From 7501d4073267de0eb4b1a88bdf9a36305d1e246b Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Fri, 1 Mar 2024 00:04:34 -0600 Subject: [PATCH 01/13] Minor changes --- .gitignore | 7 ++++--- CMakeLists.txt | 1 + include/RE/B/BGSEntryPoint.h | 2 +- include/RE/B/BSStringPool.h | 4 ++-- include/RE/Offsets.h | 2 +- include/RE/Offsets_NiRTTI.h | 2 +- include/RE/Offsets_RTTI.h | 2 +- include/RE/Offsets_VTABLE.h | 2 +- include/RE/T/TESForm.h | 3 ++- include/RE/T/TESFullName.h | 2 +- include/SKSE/Version.h | 2 ++ 11 files changed, 17 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 50a52675a..ad8f1c8f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,12 @@ # dot folders -.vs/ +.vs*/ .vscode/ .xmake/ # folders -build/ -out/ +build*/ +out*/ +vs*/ # files *.zip diff --git a/CMakeLists.txt b/CMakeLists.txt index 5582f6c17..95607da82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,6 +135,7 @@ install( DIRECTORY "include/RE" "include/REL" + "include/REX" "include/SKSE" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" ) diff --git a/include/RE/B/BGSEntryPoint.h b/include/RE/B/BGSEntryPoint.h index 48f6861a8..5fd132bf0 100644 --- a/include/RE/B/BGSEntryPoint.h +++ b/include/RE/B/BGSEntryPoint.h @@ -113,7 +113,7 @@ namespace RE { using func_t = decltype(&BGSEntryPoint::HandleEntryPoint); REL::Relocation func{ RELOCATION_ID(23073, 23526) }; - return func(a_entryPoint, a_perkOwner, a_args...); + func(a_entryPoint, a_perkOwner, a_args...); } }; } diff --git a/include/RE/B/BSStringPool.h b/include/RE/B/BSStringPool.h index e7537cca3..7345da9c3 100644 --- a/include/RE/B/BSStringPool.h +++ b/include/RE/B/BSStringPool.h @@ -27,14 +27,14 @@ namespace RE { using func_t = decltype(&Entry::release8); REL::Relocation func{ RELOCATION_ID(67847, 69192) }; - return func(a_entry); + func(a_entry); } static inline void release16(const wchar_t*& a_entry) { using func_t = decltype(&Entry::release16); REL::Relocation func{ RELOCATION_ID(67848, 69193) }; - return func(a_entry); + func(a_entry); } inline void acquire() diff --git a/include/RE/Offsets.h b/include/RE/Offsets.h index ad7c5ca45..45fa117f2 100644 --- a/include/RE/Offsets.h +++ b/include/RE/Offsets.h @@ -1,6 +1,6 @@ #pragma once -#include "REL/Relocation.h" +#include "REL/ID.h" namespace RE { diff --git a/include/RE/Offsets_NiRTTI.h b/include/RE/Offsets_NiRTTI.h index ac36c2132..65a28f384 100644 --- a/include/RE/Offsets_NiRTTI.h +++ b/include/RE/Offsets_NiRTTI.h @@ -1,6 +1,6 @@ #pragma once -#include "REL/Relocation.h" +#include "REL/ID.h" namespace RE { diff --git a/include/RE/Offsets_RTTI.h b/include/RE/Offsets_RTTI.h index 9b94d7118..096df1b09 100644 --- a/include/RE/Offsets_RTTI.h +++ b/include/RE/Offsets_RTTI.h @@ -1,6 +1,6 @@ #pragma once -#include "REL/Relocation.h" +#include "REL/ID.h" namespace RE { diff --git a/include/RE/Offsets_VTABLE.h b/include/RE/Offsets_VTABLE.h index ed17f3c40..44bb38776 100644 --- a/include/RE/Offsets_VTABLE.h +++ b/include/RE/Offsets_VTABLE.h @@ -1,6 +1,6 @@ #pragma once -#include "REL/Relocation.h" +#include "REL/ID.h" namespace RE { diff --git a/include/RE/T/TESForm.h b/include/RE/T/TESForm.h index 7884cb423..8a743190d 100644 --- a/include/RE/T/TESForm.h +++ b/include/RE/T/TESForm.h @@ -1,6 +1,7 @@ #pragma once #include "RE/B/BSAtomic.h" +#include "RE/B/BSCoreTypes.h" #include "RE/B/BSFixedString.h" #include "RE/B/BSTArray.h" #include "RE/B/BSTHashMap.h" @@ -183,7 +184,7 @@ namespace RE { using func_t = decltype(&TESForm::AddCompileIndex); REL::Relocation func{ RELOCATION_ID(14509, 14667) }; - return func(a_id, a_file); + func(a_id, a_file); } [[nodiscard]] static auto GetAllForms() diff --git a/include/RE/T/TESFullName.h b/include/RE/T/TESFullName.h index b6113f0d6..0cd10c7a0 100644 --- a/include/RE/T/TESFullName.h +++ b/include/RE/T/TESFullName.h @@ -25,7 +25,7 @@ namespace RE { using func_t = decltype(&TESFullName::SetFullName); REL::Relocation func{ RELOCATION_ID(22318, 22791) }; - return func(this, a_name); + func(this, a_name); } // members diff --git a/include/SKSE/Version.h b/include/SKSE/Version.h index 7db68d41f..a8a1c7598 100644 --- a/include/SKSE/Version.h +++ b/include/SKSE/Version.h @@ -1,5 +1,7 @@ #pragma once +#include "REL/Version.h" + namespace SKSE { inline constexpr REL::Version RUNTIME_1_1_47(1, 1, 47, 0); From dc71983b0d2f0e31aa7542c3d5376f8c674ea0bd Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Fri, 1 Mar 2024 00:08:37 -0600 Subject: [PATCH 02/13] Minor changes again --- include/SKSE/Interfaces.h | 23 +++++++++++++++++------ include/SKSE/Logger.h | 3 --- src/SKSE/Logger.cpp | 6 ++++-- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/include/SKSE/Interfaces.h b/include/SKSE/Interfaces.h index c17d7e5be..fa9d4d9a3 100644 --- a/include/SKSE/Interfaces.h +++ b/include/SKSE/Interfaces.h @@ -1,14 +1,25 @@ #pragma once -#include "RE/G/GFxMovieView.h" -#include "RE/G/GFxValue.h" -#include "RE/I/IVirtualMachine.h" -#include "RE/I/InventoryEntryData.h" -#include "RE/V/VirtualMachine.h" - #include "SKSE/Impl/Stubs.h" #include "SKSE/Version.h" +namespace RE +{ + class GFxMovieView; + class GFxValue; + class InventoryEntryData; + + namespace BSScript + { + namespace Internal + { + class VirtualMachine; + } + + class IVirtualMachine; + } +} + namespace SKSE { struct PluginInfo; diff --git a/include/SKSE/Logger.h b/include/SKSE/Logger.h index 1fcc425db..338367225 100644 --- a/include/SKSE/Logger.h +++ b/include/SKSE/Logger.h @@ -1,8 +1,5 @@ #pragma once -#include "RE/B/BSTEvent.h" -#include "RE/L/LogEvent.h" - #define SKSE_MAKE_SOURCE_LOGGER(a_func, a_type) \ \ template \ diff --git a/src/SKSE/Logger.cpp b/src/SKSE/Logger.cpp index 9170e9068..424ba6fed 100644 --- a/src/SKSE/Logger.cpp +++ b/src/SKSE/Logger.cpp @@ -1,12 +1,14 @@ #include "SKSE/Logger.h" +#include "RE/B/BSTEvent.h" +#include "RE/L/LogEvent.h" #include "RE/V/VirtualMachine.h" -#include "SKSE/API.h" - #include "REX/W32/OLE32.h" #include "REX/W32/SHELL32.h" +#include "SKSE/API.h" + namespace SKSE { namespace Impl From e228f3e5e078d30dca6366335722ac6918b45201 Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Fri, 1 Mar 2024 00:11:16 -0600 Subject: [PATCH 03/13] Improve Trampoline --- include/SKSE/API.h | 3 +- include/SKSE/Trampoline.h | 171 ++------------------------------------ src/SKSE/API.cpp | 6 -- src/SKSE/Trampoline.cpp | 156 ++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 170 deletions(-) diff --git a/include/SKSE/API.h b/include/SKSE/API.h index 551934952..f84eb7f48 100644 --- a/include/SKSE/API.h +++ b/include/SKSE/API.h @@ -36,6 +36,5 @@ namespace SKSE const SKSEObjectRegistry* GetObjectRegistry() noexcept; const SKSEPersistentObjectStorage* GetPersistentObjectStorage() noexcept; - Trampoline& GetTrampoline(); - void AllocTrampoline(std::size_t a_size, bool a_trySKSEReserve = true); + void AllocTrampoline(std::size_t a_size, bool a_trySKSEReserve = true); } diff --git a/include/SKSE/Trampoline.h b/include/SKSE/Trampoline.h index 3285b9a68..a630896da 100644 --- a/include/SKSE/Trampoline.h +++ b/include/SKSE/Trampoline.h @@ -1,7 +1,5 @@ #pragma once -#include "REX/W32/KERNEL32.h" - #if defined(SKSE_SUPPORT_XBYAK) namespace Xbyak { @@ -11,33 +9,6 @@ namespace Xbyak namespace SKSE { - namespace detail - { - [[nodiscard]] constexpr std::size_t roundup(std::size_t a_number, std::size_t a_multiple) noexcept - { - if (a_multiple == 0) { - return 0; - } - - const auto remainder = a_number % a_multiple; - return remainder == 0 ? - a_number : - a_number + a_multiple - remainder; - } - - [[nodiscard]] constexpr std::size_t rounddown(std::size_t a_number, std::size_t a_multiple) noexcept - { - if (a_multiple == 0) { - return 0; - } - - const auto remainder = a_number % a_multiple; - return remainder == 0 ? - a_number : - a_number - remainder; - } - } - class Trampoline { public: @@ -46,7 +17,7 @@ namespace SKSE Trampoline() = default; Trampoline(const Trampoline&) = delete; - Trampoline(Trampoline&& a_rhs) { move_from(std::move(a_rhs)); } + Trampoline(Trampoline&& a_rhs) noexcept { move_from(std::move(a_rhs)); } explicit Trampoline(std::string_view a_name) : _name(a_name) @@ -56,7 +27,7 @@ namespace SKSE Trampoline& operator=(const Trampoline&) = delete; - Trampoline& operator=(Trampoline&& a_rhs) + Trampoline& operator=(Trampoline&& a_rhs) noexcept { if (this != std::addressof(a_rhs)) { move_from(std::move(a_rhs)); @@ -65,31 +36,9 @@ namespace SKSE } void create(std::size_t a_size) { return create(a_size, nullptr); } + void create(std::size_t a_size, void* a_module); - void create(std::size_t a_size, void* a_module) - { - if (a_size == 0) { - stl::report_and_fail("cannot create a trampoline with a zero size"sv); - } - - if (!a_module) { - const auto text = REL::Module::get().segment(REL::Segment::textx); - a_module = text.pointer() + text.size(); - } - - auto mem = do_create(a_size, reinterpret_cast(a_module)); - if (!mem) { - stl::report_and_fail("failed to create trampoline"sv); - } - - set_trampoline(mem, a_size, [](void* a_mem, std::size_t) { - REX::W32::VirtualFree(a_mem, 0, REX::W32::MEM_RELEASE); - }); - } - - void set_trampoline(void* a_trampoline, std::size_t a_size) { set_trampoline(a_trampoline, a_size, {}); } - - void set_trampoline(void* a_trampoline, std::size_t a_size, deleter_type a_deleter) + void set_trampoline(void* a_trampoline, std::size_t a_size, deleter_type a_deleter = {}) { auto trampoline = static_cast(a_trampoline); if (trampoline) { @@ -181,114 +130,10 @@ namespace SKSE private: [[nodiscard]] void* do_create(std::size_t a_size, std::uintptr_t a_address); + [[nodiscard]] void* do_allocate(std::size_t a_size); - [[nodiscard]] void* do_allocate(std::size_t a_size) - { - if (a_size > free_size()) { - stl::report_and_fail("Failed to handle allocation request"sv); - } - - auto mem = _data + _size; - _size += a_size; - - return mem; - } - - void write_5branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_opcode) - { -#pragma pack(push, 1) - struct SrcAssembly - { - // jmp/call [rip + imm32] - std::uint8_t opcode; // 0 - 0xE9/0xE8 - std::int32_t disp; // 1 - }; - static_assert(offsetof(SrcAssembly, opcode) == 0x0); - static_assert(offsetof(SrcAssembly, disp) == 0x1); - static_assert(sizeof(SrcAssembly) == 0x5); - - // FF /4 - // JMP r/m64 - struct TrampolineAssembly - { - // jmp [rip] - std::uint8_t jmp; // 0 - 0xFF - std::uint8_t modrm; // 1 - 0x25 - std::int32_t disp; // 2 - 0x00000000 - std::uint64_t addr; // 6 - [rip] - }; - static_assert(offsetof(TrampolineAssembly, jmp) == 0x0); - static_assert(offsetof(TrampolineAssembly, modrm) == 0x1); - static_assert(offsetof(TrampolineAssembly, disp) == 0x2); - static_assert(offsetof(TrampolineAssembly, addr) == 0x6); - static_assert(sizeof(TrampolineAssembly) == 0xE); -#pragma pack(pop) - - TrampolineAssembly* mem = nullptr; - if (const auto it = _5branches.find(a_dst); it != _5branches.end()) { - mem = reinterpret_cast(it->second); - } else { - mem = allocate(); - _5branches.emplace(a_dst, reinterpret_cast(mem)); - } - - const auto disp = - reinterpret_cast(mem) - - reinterpret_cast(a_src + sizeof(SrcAssembly)); - if (!in_range(disp)) { // the trampoline should already be in range, so this should never happen - stl::report_and_fail("displacement is out of range"sv); - } - - SrcAssembly assembly; - assembly.opcode = a_opcode; - assembly.disp = static_cast(disp); - REL::safe_write(a_src, &assembly, sizeof(assembly)); - - mem->jmp = static_cast(0xFF); - mem->modrm = static_cast(0x25); - mem->disp = static_cast(0); - mem->addr = static_cast(a_dst); - } - - void write_6branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_modrm) - { -#pragma pack(push, 1) - struct Assembly - { - // jmp/call [rip + imm32] - std::uint8_t opcode; // 0 - 0xFF - std::uint8_t modrm; // 1 - 0x25/0x15 - std::int32_t disp; // 2 - }; - static_assert(offsetof(Assembly, opcode) == 0x0); - static_assert(offsetof(Assembly, modrm) == 0x1); - static_assert(offsetof(Assembly, disp) == 0x2); - static_assert(sizeof(Assembly) == 0x6); -#pragma pack(pop) - - std::uintptr_t* mem = nullptr; - if (const auto it = _6branches.find(a_dst); it != _6branches.end()) { - mem = reinterpret_cast(it->second); - } else { - mem = allocate(); - _6branches.emplace(a_dst, reinterpret_cast(mem)); - } - - const auto disp = - reinterpret_cast(mem) - - reinterpret_cast(a_src + sizeof(Assembly)); - if (!in_range(disp)) { // the trampoline should already be in range, so this should never happen - stl::report_and_fail("displacement is out of range"sv); - } - - Assembly assembly; - assembly.opcode = static_cast(0xFF); - assembly.modrm = a_modrm; - assembly.disp = static_cast(disp); - REL::safe_write(a_src, &assembly, sizeof(assembly)); - - *mem = a_dst; - } + void write_5branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_opcode); + void write_6branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_modrm); template [[nodiscard]] std::uintptr_t write_branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_data) @@ -357,4 +202,6 @@ namespace SKSE std::size_t _capacity{ 0 }; std::size_t _size{ 0 }; }; + + Trampoline& GetTrampoline(); } diff --git a/src/SKSE/API.cpp b/src/SKSE/API.cpp index 79a765f4e..c157b8283 100644 --- a/src/SKSE/API.cpp +++ b/src/SKSE/API.cpp @@ -218,12 +218,6 @@ namespace SKSE return detail::APIStorage::get().persistentObjectStorage; } - Trampoline& GetTrampoline() - { - static Trampoline trampoline; - return trampoline; - } - void AllocTrampoline(std::size_t a_size, bool a_trySKSEReserve) { auto& trampoline = GetTrampoline(); diff --git a/src/SKSE/Trampoline.cpp b/src/SKSE/Trampoline.cpp index ddd808712..daa1849d1 100644 --- a/src/SKSE/Trampoline.cpp +++ b/src/SKSE/Trampoline.cpp @@ -16,6 +16,48 @@ namespace SKSE { + namespace detail + { + [[nodiscard]] constexpr std::size_t roundup(std::size_t a_number, std::size_t a_multiple) noexcept + { + if (a_multiple == 0) + return 0; + + const auto remainder = a_number % a_multiple; + return (remainder == 0) ? a_number : (a_number + a_multiple - remainder); + } + + [[nodiscard]] constexpr std::size_t rounddown(std::size_t a_number, std::size_t a_multiple) noexcept + { + if (a_multiple == 0) + return 0; + + const auto remainder = a_number % a_multiple; + return (remainder == 0) ? a_number : (a_number - remainder); + } + } + + void Trampoline::create(std::size_t a_size, void* a_module) + { + if (a_size == 0) { + stl::report_and_fail("cannot create a trampoline with a zero size"sv); + } + + if (!a_module) { + const auto text = REL::Module::get().segment(REL::Segment::textx); + a_module = text.pointer() + text.size(); + } + + auto mem = do_create(a_size, reinterpret_cast(a_module)); + if (!mem) { + stl::report_and_fail("failed to create trampoline"sv); + } + + set_trampoline(mem, a_size, [](void* a_mem, std::size_t) { + REX::W32::VirtualFree(a_mem, 0, REX::W32::MEM_RELEASE); + }); + } + #ifdef SKSE_SUPPORT_XBYAK void* Trampoline::allocate(Xbyak::CodeGenerator& a_code) { @@ -68,9 +110,123 @@ namespace SKSE return nullptr; } + void* Trampoline::do_allocate(std::size_t a_size) + { + if (a_size > free_size()) { + stl::report_and_fail("Failed to handle allocation request"sv); + } + + auto mem = _data + _size; + _size += a_size; + + return mem; + } + + void Trampoline::write_5branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_opcode) + { +#pragma pack(push, 1) + struct SrcAssembly + { + // jmp/call [rip + imm32] + std::uint8_t opcode; // 0 - 0xE9/0xE8 + std::int32_t disp; // 1 + }; + static_assert(offsetof(SrcAssembly, opcode) == 0x0); + static_assert(offsetof(SrcAssembly, disp) == 0x1); + static_assert(sizeof(SrcAssembly) == 0x5); + + // FF /4 + // JMP r/m64 + struct TrampolineAssembly + { + // jmp [rip] + std::uint8_t jmp; // 0 - 0xFF + std::uint8_t modrm; // 1 - 0x25 + std::int32_t disp; // 2 - 0x00000000 + std::uint64_t addr; // 6 - [rip] + }; + static_assert(offsetof(TrampolineAssembly, jmp) == 0x0); + static_assert(offsetof(TrampolineAssembly, modrm) == 0x1); + static_assert(offsetof(TrampolineAssembly, disp) == 0x2); + static_assert(offsetof(TrampolineAssembly, addr) == 0x6); + static_assert(sizeof(TrampolineAssembly) == 0xE); +#pragma pack(pop) + + TrampolineAssembly* mem = nullptr; + if (const auto it = _5branches.find(a_dst); it != _5branches.end()) { + mem = reinterpret_cast(it->second); + } else { + mem = allocate(); + _5branches.emplace(a_dst, reinterpret_cast(mem)); + } + + const auto disp = + reinterpret_cast(mem) - + reinterpret_cast(a_src + sizeof(SrcAssembly)); + if (!in_range(disp)) { // the trampoline should already be in range, so this should never happen + stl::report_and_fail("displacement is out of range"sv); + } + + SrcAssembly assembly; + assembly.opcode = a_opcode; + assembly.disp = static_cast(disp); + REL::safe_write(a_src, &assembly, sizeof(assembly)); + + mem->jmp = static_cast(0xFF); + mem->modrm = static_cast(0x25); + mem->disp = static_cast(0); + mem->addr = static_cast(a_dst); + } + + void Trampoline::write_6branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_modrm) + { +#pragma pack(push, 1) + struct Assembly + { + // jmp/call [rip + imm32] + std::uint8_t opcode; // 0 - 0xFF + std::uint8_t modrm; // 1 - 0x25/0x15 + std::int32_t disp; // 2 + }; + static_assert(offsetof(Assembly, opcode) == 0x0); + static_assert(offsetof(Assembly, modrm) == 0x1); + static_assert(offsetof(Assembly, disp) == 0x2); + static_assert(sizeof(Assembly) == 0x6); +#pragma pack(pop) + + std::uintptr_t* mem = nullptr; + if (const auto it = _6branches.find(a_dst); it != _6branches.end()) { + mem = reinterpret_cast(it->second); + } else { + mem = allocate(); + _6branches.emplace(a_dst, reinterpret_cast(mem)); + } + + const auto disp = + reinterpret_cast(mem) - + reinterpret_cast(a_src + sizeof(Assembly)); + if (!in_range(disp)) { // the trampoline should already be in range, so this should never happen + stl::report_and_fail("displacement is out of range"sv); + } + + Assembly assembly; + assembly.opcode = static_cast(0xFF); + assembly.modrm = a_modrm; + assembly.disp = static_cast(disp); + REL::safe_write(a_src, &assembly, sizeof(assembly)); + + *mem = a_dst; + } + void Trampoline::log_stats() const { const auto pct = (static_cast(_size) / static_cast(_capacity)) * 100.0; log::debug("{} => {}B / {}B ({:05.2f}%)"sv, _name, _size, _capacity, pct); } + + Trampoline& GetTrampoline() + { + static Trampoline trampoline; + return trampoline; + } } From a0fee55a598ad1859b4b9884ac322bcc9a4f7c6c Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Fri, 1 Mar 2024 00:15:22 -0600 Subject: [PATCH 04/13] Improve Version --- cmake/sourcelist.cmake | 1 + include/REL/Module.h | 2 +- include/REL/Version.h | 40 +++++++++++----------------------------- src/REL/Version.cpp | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 30 deletions(-) create mode 100644 src/REL/Version.cpp diff --git a/cmake/sourcelist.cmake b/cmake/sourcelist.cmake index 516b10289..1d0c24c93 100644 --- a/cmake/sourcelist.cmake +++ b/cmake/sourcelist.cmake @@ -1989,6 +1989,7 @@ set(SOURCES src/RE/Z/ZeroFunctionArguments.cpp src/REL/ID.cpp src/REL/Module.cpp + src/REL/Version.cpp src/REX/W32/ADVAPI32.cpp src/REX/W32/D3D11.cpp src/REX/W32/D3DCOMPILER.cpp diff --git a/include/REL/Module.h b/include/REL/Module.h index 52b220b3a..a7c461013 100644 --- a/include/REL/Module.h +++ b/include/REL/Module.h @@ -120,7 +120,7 @@ namespace REL void load_version() { - const auto version = get_file_version(_filename); + const auto version = GetFileVersion(_filename); if (version) { _version = *version; } else { diff --git a/include/REL/Version.h b/include/REL/Version.h index f72ebeb62..d24462e5a 100644 --- a/include/REL/Version.h +++ b/include/REL/Version.h @@ -1,7 +1,5 @@ #pragma once -#include "REX/W32/VERSION.h" - namespace REL { class Version @@ -70,6 +68,16 @@ namespace REL return result; } + [[nodiscard]] static constexpr Version unpack(std::uint32_t a_packedVersion) noexcept + { + return REL::Version{ + static_cast((a_packedVersion >> 24) & 0x0FF), + static_cast((a_packedVersion >> 16) & 0x0FF), + static_cast((a_packedVersion >> 4) & 0xFFF), + static_cast(a_packedVersion & 0x0F) + }; + } + private: std::array _impl{ 0, 0, 0, 0 }; }; @@ -77,31 +85,5 @@ namespace REL [[nodiscard]] constexpr bool operator==(const Version& a_lhs, const Version& a_rhs) noexcept { return a_lhs.compare(a_rhs) == 0; } [[nodiscard]] constexpr std::strong_ordering operator<=>(const Version& a_lhs, const Version& a_rhs) noexcept { return a_lhs.compare(a_rhs); } - [[nodiscard]] inline std::optional get_file_version(stl::zwstring a_filename) - { - std::uint32_t dummy; - std::vector buf(REX::W32::GetFileVersionInfoSizeW(a_filename.data(), std::addressof(dummy))); - if (buf.empty()) { - return std::nullopt; - } - - if (!REX::W32::GetFileVersionInfoW(a_filename.data(), 0, static_cast(buf.size()), buf.data())) { - return std::nullopt; - } - - void* verBuf{ nullptr }; - std::uint32_t verLen{ 0 }; - if (!REX::W32::VerQueryValueW(buf.data(), L"\\StringFileInfo\\040904B0\\ProductVersion", std::addressof(verBuf), std::addressof(verLen))) { - return std::nullopt; - } - - Version version; - std::wistringstream ss(std::wstring(static_cast(verBuf), verLen)); - std::wstring token; - for (std::size_t i = 0; i < 4 && std::getline(ss, token, L'.'); ++i) { - version[i] = static_cast(std::stoi(token)); - } - - return version; - } + [[nodiscard]] std::optional GetFileVersion(stl::zwstring a_filename); } diff --git a/src/REL/Version.cpp b/src/REL/Version.cpp new file mode 100644 index 000000000..c204adc02 --- /dev/null +++ b/src/REL/Version.cpp @@ -0,0 +1,34 @@ +#include "REL/Version.h" + +#include "REX/W32/VERSION.h" + +namespace REL +{ + std::optional GetFileVersion(stl::zwstring a_filename) + { + std::uint32_t dummy; + std::vector buf(REX::W32::GetFileVersionInfoSizeW(a_filename.data(), std::addressof(dummy))); + if (buf.empty()) { + return std::nullopt; + } + + if (!REX::W32::GetFileVersionInfoW(a_filename.data(), 0, static_cast(buf.size()), buf.data())) { + return std::nullopt; + } + + void* verBuf{ nullptr }; + std::uint32_t verLen{ 0 }; + if (!REX::W32::VerQueryValueW(buf.data(), L"\\StringFileInfo\\040904B0\\ProductVersion", std::addressof(verBuf), std::addressof(verLen))) { + return std::nullopt; + } + + Version version; + std::wistringstream ss(std::wstring(static_cast(verBuf), verLen)); + std::wstring token; + for (std::size_t i = 0; i < 4 && std::getline(ss, token, L'.'); ++i) { + version[i] = static_cast(std::stoi(token)); + } + + return version; + } +} From 923bbe5c0a5e524eb65198cce6b99d09418682ef Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Fri, 1 Mar 2024 00:18:59 -0600 Subject: [PATCH 05/13] Improve Relocation --- cmake/sourcelist.cmake | 1 + include/REL/Relocation.h | 96 +++++++++++++++++++++++++--------------- src/REL/Relocation.cpp | 34 ++++++++++++++ 3 files changed, 96 insertions(+), 35 deletions(-) create mode 100644 src/REL/Relocation.cpp diff --git a/cmake/sourcelist.cmake b/cmake/sourcelist.cmake index 1d0c24c93..38c8cd4d1 100644 --- a/cmake/sourcelist.cmake +++ b/cmake/sourcelist.cmake @@ -1989,6 +1989,7 @@ set(SOURCES src/RE/Z/ZeroFunctionArguments.cpp src/REL/ID.cpp src/REL/Module.cpp + src/REL/Relocation.cpp src/REL/Version.cpp src/REX/W32/ADVAPI32.cpp src/REX/W32/D3D11.cpp diff --git a/include/REL/Relocation.h b/include/REL/Relocation.h index a96e1c48a..1b5aa5fde 100644 --- a/include/REL/Relocation.h +++ b/include/REL/Relocation.h @@ -1,6 +1,8 @@ #pragma once -#include "REX/W32/KERNEL32.h" +#include "REL/Module.h" + +#include "SKSE/Trampoline.h" #define REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL(a_nopropQual, a_propQual, ...) \ template < \ @@ -187,19 +189,7 @@ namespace REL } } - inline void safe_write(std::uintptr_t a_dst, const void* a_src, std::size_t a_count) - { - std::uint32_t old{ 0 }; - bool success = REX::W32::VirtualProtect( - reinterpret_cast(a_dst), a_count, REX::W32::PAGE_EXECUTE_READWRITE, std::addressof(old)); - if (success) { - std::memcpy(reinterpret_cast(a_dst), a_src, a_count); - success = REX::W32::VirtualProtect( - reinterpret_cast(a_dst), a_count, old, std::addressof(old)); - } - - assert(success); - } + void safe_write(std::uintptr_t a_dst, const void* a_src, std::size_t a_count); template void safe_write(std::uintptr_t a_dst, const T& a_data) @@ -213,21 +203,9 @@ namespace REL safe_write(a_dst, a_data.data(), a_data.size_bytes()); } - inline void safe_fill(std::uintptr_t a_dst, std::uint8_t a_value, std::size_t a_count) - { - std::uint32_t old{ 0 }; - bool success = REX::W32::VirtualProtect( - reinterpret_cast(a_dst), a_count, REX::W32::PAGE_EXECUTE_READWRITE, std::addressof(old)); - if (success) { - std::fill_n(reinterpret_cast(a_dst), a_count, a_value); - success = REX::W32::VirtualProtect( - reinterpret_cast(a_dst), a_count, old, std::addressof(old)); - } + void safe_fill(std::uintptr_t a_dst, std::uint8_t a_value, std::size_t a_count); - assert(success); - } - - template + template class Relocation { public: @@ -274,22 +252,22 @@ namespace REL } template - [[nodiscard]] decltype(auto) operator*() const noexcept // + [[nodiscard]] decltype(auto) operator*() const noexcept requires(std::is_pointer_v) { return *get(); } template - [[nodiscard]] auto operator->() const noexcept // + [[nodiscard]] auto operator->() const noexcept requires(std::is_pointer_v) { return get(); } template - std::invoke_result_t operator()(Args&&... a_args) const // - noexcept(std::is_nothrow_invocable_v) // + std::invoke_result_t operator()(Args&&... a_args) const + noexcept(std::is_nothrow_invocable_v) requires(std::invocable) { return REL::invoke(get(), std::forward(a_args)...); @@ -298,15 +276,63 @@ namespace REL [[nodiscard]] constexpr std::uintptr_t address() const noexcept { return _impl; } [[nodiscard]] std::size_t offset() const { return _impl - base(); } - [[nodiscard]] value_type get() const // + [[nodiscard]] value_type get() const noexcept(std::is_nothrow_copy_constructible_v) { assert(_impl != 0); return stl::unrestricted_cast(_impl); } + template + void write(const U& a_data) + requires(std::same_as) + { + safe_write(address(), std::addressof(a_data), sizeof(T)); + } + + template + void write(const std::span a_data) + requires(std::same_as) + { + safe_write(address(), a_data.data(), a_data.size_bytes()); + } + + template + std::uintptr_t write_branch(const std::uintptr_t a_dst) + requires(std::same_as) + { + return SKSE::GetTrampoline().write_branch(address(), a_dst); + } + + template + std::uintptr_t write_branch(const F a_dst) + requires(std::same_as) + { + return SKSE::GetTrampoline().write_branch(address(), stl::unrestricted_cast(a_dst)); + } + + template + std::uintptr_t write_call(const std::uintptr_t a_dst) + requires(std::same_as) + { + return SKSE::GetTrampoline().write_call(address(), a_dst); + } + + template + std::uintptr_t write_call(const F a_dst) + requires(std::same_as) + { + return SKSE::GetTrampoline().write_call(address(), stl::unrestricted_cast(a_dst)); + } + + void write_fill(const std::uint8_t a_value, const std::size_t a_count) + requires(std::same_as) + { + safe_fill(address(), a_value, a_count); + } + template - std::uintptr_t write_vfunc(std::size_t a_idx, std::uintptr_t a_newFunc) // + std::uintptr_t write_vfunc(const std::size_t a_idx, const std::uintptr_t a_newFunc) requires(std::same_as) { const auto addr = address() + (sizeof(void*) * a_idx); @@ -316,7 +342,7 @@ namespace REL } template - std::uintptr_t write_vfunc(std::size_t a_idx, F a_newFunc) // + std::uintptr_t write_vfunc(const std::size_t a_idx, const F a_newFunc) requires(std::same_as) { return write_vfunc(a_idx, stl::unrestricted_cast(a_newFunc)); diff --git a/src/REL/Relocation.cpp b/src/REL/Relocation.cpp new file mode 100644 index 000000000..4ce831bde --- /dev/null +++ b/src/REL/Relocation.cpp @@ -0,0 +1,34 @@ +#include "REL/Relocation.h" + +#include "REX/W32/KERNEL32.h" + +namespace REL +{ + void safe_write(std::uintptr_t a_dst, const void* a_src, std::size_t a_count) + { + std::uint32_t old{ 0 }; + bool success = REX::W32::VirtualProtect( + reinterpret_cast(a_dst), a_count, REX::W32::PAGE_EXECUTE_READWRITE, std::addressof(old)); + if (success) { + std::memcpy(reinterpret_cast(a_dst), a_src, a_count); + success = REX::W32::VirtualProtect( + reinterpret_cast(a_dst), a_count, old, std::addressof(old)); + } + + assert(success); + } + + void safe_fill(std::uintptr_t a_dst, std::uint8_t a_value, std::size_t a_count) + { + std::uint32_t old{ 0 }; + bool success = REX::W32::VirtualProtect( + reinterpret_cast(a_dst), a_count, REX::W32::PAGE_EXECUTE_READWRITE, std::addressof(old)); + if (success) { + std::fill_n(reinterpret_cast(a_dst), a_count, a_value); + success = REX::W32::VirtualProtect( + reinterpret_cast(a_dst), a_count, old, std::addressof(old)); + } + + assert(success); + } +} From 44969e9246386cf0e9044764c6c12c9b8264253c Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Fri, 1 Mar 2024 00:22:07 -0600 Subject: [PATCH 06/13] Add xmake support --- xmake.lua | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 xmake.lua diff --git a/xmake.lua b/xmake.lua new file mode 100644 index 000000000..64a89959d --- /dev/null +++ b/xmake.lua @@ -0,0 +1,127 @@ +-- set minimum xmake version +set_xmakever("2.8.2") + +-- set project +set_project("commonlibsse") +set_arch("x64") +set_languages("c++20") +set_warnings("allextra") +set_encodings("utf-8") + +-- add rules +add_rules("mode.debug", "mode.release") + +-- define options +option("skyrim_ae") + set_default(false) + set_description("Enable support for Skyrim AE") + add_defines("SKYRIM_SUPPORT_AE=1") +option_end() + +option("skse_xbyak") + set_default(false) + set_description("Enable trampoline support for Xbyak") + add_defines("SKSE_SUPPORT_XBYAK=1") +option_end() + +-- require packages +add_requires("rsm-binary-io") +add_requires("spdlog", { configs = { header_only = false, wchar = true, std_format = true } }) + +if has_config("skse_xbyak") then + add_requires("xbyak") +end + +-- define targets +target("commonlibsse") + -- set target kind + set_kind("static") + + -- add packages + add_packages("rsm-binary-io", "spdlog", { public = true }) + + if has_config("skse_xbyak") then + add_packages("xbyak", { public = true }) + end + + -- add options + add_options("skyrim_ae", "skse_xbyak", { public = true }) + + -- add system links + add_syslinks("advapi32", "d3d11", "d3dcompiler", "dbghelp", "dxgi", "ole32", "shell32", "user32", "version") + + -- add source files + add_files("src/**.cpp") + + -- add header files + add_includedirs("include", { public = true }) + add_headerfiles( + "include/(RE/**.h)", + "include/(REL/**.h)", + "include/(REX/**.h)", + "include/(SKSE/**.h)" + ) + + -- set precompiled header + set_pcxxheader("include/SKSE/Impl/PCH.h") + + -- add flags + add_cxxflags("/EHsc", "/permissive-", { public = true }) + + -- add flags (cl) + add_cxxflags( + "cl::/bigobj", + "cl::/cgthreads8", + "cl::/diagnostics:caret", + "cl::/external:W0", + "cl::/fp:contract", + "cl::/fp:except-", + "cl::/guard:cf-", + "cl::/Zc:enumTypes", + "cl::/Zc:preprocessor", + "cl::/Zc:templateScope" + ) + + -- add flags (cl: warnings -> errors) + add_cxxflags("cl::/we4715") -- `function` : not all control paths return a value + + -- add flags (cl: disable warnings) + add_cxxflags( + "cl::/wd4005", -- macro redefinition + "cl::/wd4061", -- enumerator `identifier` in switch of enum `enumeration` is not explicitly handled by a case label + "cl::/wd4068", -- unknown pragma 'clang' + "cl::/wd4200", -- nonstandard extension used : zero-sized array in struct/union + "cl::/wd4201", -- nonstandard extension used : nameless struct/union + "cl::/wd4264", -- 'virtual_function' : no override available for virtual member function from base 'class'; function is hidden + "cl::/wd4265", -- 'type': class has virtual functions, but its non-trivial destructor is not virtual; instances of this class may not be destructed correctly + "cl::/wd4266", -- 'function' : no override available for virtual member function from base 'type'; function is hidden + "cl::/wd4324", -- 'struct_name' : structure was padded due to __declspec(align()) + "cl::/wd4371", -- 'classname': layout of class may have changed from a previous version of the compiler due to better packing of member 'member' + "cl::/wd4514", -- 'function' : unreferenced inline function has been removed + "cl::/wd4582", -- 'type': constructor is not implicitly called + "cl::/wd4583", -- 'type': destructor is not implicitly called + "cl::/wd4623", -- 'derived class' : default constructor was implicitly defined as deleted because a base class default constructor is inaccessible or deleted + "cl::/wd4625", -- 'derived class' : copy constructor was implicitly defined as deleted because a base class copy constructor is inaccessible or deleted + "cl::/wd4626", -- 'derived class' : assignment operator was implicitly defined as deleted because a base class assignment operator is inaccessible or deleted + "cl::/wd4686", -- 'user-defined type' : possible change in behavior, change in UDT return calling convention + "cl::/wd4710", -- 'function' : function not inlined + "cl::/wd4711", -- function 'function' selected for inline expansion + "cl::/wd4820", -- 'bytes' bytes padding added after construct 'member_name' + "cl::/wd5082", -- second argument to 'va_start' is not the last named parameter + "cl::/wd5026", -- 'type': move constructor was implicitly defined as deleted + "cl::/wd5027", -- 'type': move assignment operator was implicitly defined as deleted + "cl::/wd5045", -- compiler will insert Spectre mitigation for memory load if /Qspectre switch specified + "cl::/wd5053", -- support for 'explicit()' in C++17 and earlier is a vendor extension + "cl::/wd5105", -- macro expansion producing 'defined' has undefined behavior (workaround for older msvc bug) + "cl::/wd5204", -- 'type-name': class has virtual functions, but its trivial destructor is not virtual; instances of objects derived from this class may not be destructed correctly + "cl::/wd5220" -- 'member': a non-static data member with a volatile qualified type no longer implies that compiler generated copy / move constructors and copy / move assignment operators are not trivial + ) + + -- add flags (clang-cl: disable warnings) + add_cxxflags( + "clang_cl::-Wno-delete-non-abstract-non-virtual-dtor", + "clang_cl::-Wno-inconsistent-missing-override", + "clang_cl::-Wno-overloaded-virtual", + "clang_cl::-Wno-reinterpret-base-class" + ) +target_end() From d910f48e831064427a5908e98ee914dc41410c83 Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:50:39 -0600 Subject: [PATCH 07/13] Improve xmake --- xmake-extra.lua | 99 +++++++++++++++++++++++++++++++++++++++++++++++++ xmake.lua | 3 ++ 2 files changed, 102 insertions(+) create mode 100644 xmake-extra.lua diff --git a/xmake-extra.lua b/xmake-extra.lua new file mode 100644 index 000000000..7ab6b9419 --- /dev/null +++ b/xmake-extra.lua @@ -0,0 +1,99 @@ +local PLUGIN_VERSION_FILE = [[ +#include + +1 VERSIONINFO +FILEVERSION ${PLUGIN_VERSION_MAJOR}, ${PLUGIN_VERSION_MINOR}, ${PLUGIN_VERSION_PATCH}, 0 +PRODUCTVERSION ${PROJECT_VERSION_MAJOR}, ${PROJECT_VERSION_MINOR}, ${PROJECT_VERSION_PATCH}, 0 +FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif +FILEOS 0x4L +FILETYPE 0x1L +FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "${PLUGIN_DESCRIPTION}" + VALUE "FileVersion", "${PLUGIN_VERSION}.0" + VALUE "InternalName", "${PLUGIN_NAME}" + VALUE "LegalCopyright", "${PLUGIN_AUTHOR} | ${PLUGIN_LICENSE}" + VALUE "ProductName", "${PROJECT_NAME}" + VALUE "ProductVersion", "${PROJECT_VERSION}.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END]] + +-- Usage: +-- add_deps("commonlibsse") +-- add_rules("commonlibsse.plugin", { +-- name = "PluginName", +-- author = "Author Name", +-- description = "Plugin Description", +-- email = "example@site.com" +-- }) + +rule("commonlibsse.plugin") + add_deps("win.sdk.resource") + + on_config(function(target) + import("core.base.semver") + import("core.project.depend") + import("core.project.project") + + target:set("arch", "x64") + target:set("kind", "shared") + + local conf = target:extraconf("rules", "commonlibsse.plugin") + local conf_dir = path.join(target:autogendir(), "rules", "commonlibsse", "plugin") + + local conf_map = { + PLUGIN_AUTHOR = conf.author or "", + PLUGIN_DESCRIPTION = conf.description or "", + PLUGIN_EMAIL = conf.email or "", + PLUGIN_LICENSE = (target:license() or "Unknown") .. " License", + PLUGIN_NAME = conf.name or target:name(), + PLUGIN_VERSION = target:version() or "0.0.0", + PLUGIN_VERSION_MAJOR = semver.new(target:version() or "0.0.0"):major(), + PLUGIN_VERSION_MINOR = semver.new(target:version() or "0.0.0"):minor(), + PLUGIN_VERSION_PATCH = semver.new(target:version() or "0.0.0"):patch(), + PROJECT_NAME = project.name() or "", + PROJECT_VERSION = project.version() or "0.0.0", + PROJECT_VERSION_MAJOR = semver.new(project.version() or "0.0.0"):major(), + PROJECT_VERSION_MINOR = semver.new(project.version() or "0.0.0"):minor(), + PROJECT_VERSION_PATCH = semver.new(project.version() or "0.0.0"):patch(), + } + + local conf_parse = function(a_str) + return a_str:gsub("(%${([^\n]-)})", function(_, a_var) + local result = conf_map[a_var:trim()] + if type(result) ~= "string" then + result = tostring(result) + end + assert(result ~= nil, "cannot get variable(%s)", a_var) + return result + end) + end + + local add_file = function(a_path, a_data) + local file_path = path.join(conf_dir, a_path) + depend.on_changed(function() + local file = io.open(file_path, "w") + if file then + file:write(conf_parse(a_data), "\n") + file:close() + end + end, { dependfile = target:dependfile(file_path), files = project.allfiles()}) + target:add("files", file_path) + end + + add_file("version.rc", PLUGIN_VERSION_FILE) + end) diff --git a/xmake.lua b/xmake.lua index 64a89959d..2fc70316d 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,6 +1,9 @@ -- set minimum xmake version set_xmakever("2.8.2") +-- make extras available +includes("xmake-extra.lua") + -- set project set_project("commonlibsse") set_arch("x64") From e184250b7d885ce72799fea1c810d216f1ab5c48 Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Fri, 1 Mar 2024 20:28:57 -0600 Subject: [PATCH 08/13] Improve xmake again --- xmake-extra.lua | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/xmake-extra.lua b/xmake-extra.lua index 7ab6b9419..406fa926a 100644 --- a/xmake-extra.lua +++ b/xmake-extra.lua @@ -1,3 +1,20 @@ +local PLUGIN_FILE = [[ +#include +#ifdef SKYRIM_SUPPORT_AE +extern "C" __declspec(dllexport) constinit auto SKSEPlugin_Version = []() { + SKSE::PluginVersionData v; + v.PluginVersion({ ${PLUGIN_VERSION_MAJOR}, ${PLUGIN_VERSION_MINOR}, ${PLUGIN_VERSION_PATCH}, 0 }); + v.PluginName("${PLUGIN_NAME}"); + v.AuthorName("${PLUGIN_AUTHOR}"); + v.AuthorEmail("${PLUGIN_EMAIL}"); + v.UsesAddressLibrary(); + v.UsesUpdatedStructs(); + v.CompatibleVersions({ SKSE::RUNTIME_LATEST }); + return v; +}(); +#endif +]] + local PLUGIN_VERSION_FILE = [[ #include @@ -95,5 +112,6 @@ rule("commonlibsse.plugin") target:add("files", file_path) end + add_file("plugin.cpp", PLUGIN_FILE) add_file("version.rc", PLUGIN_VERSION_FILE) end) From f9197d35fb83f74664d4cfce13ee382eae214df9 Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Sat, 2 Mar 2024 12:59:51 -0600 Subject: [PATCH 09/13] Fix Trampoline when Xbyak is enabled --- src/SKSE/Trampoline.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SKSE/Trampoline.cpp b/src/SKSE/Trampoline.cpp index daa1849d1..a6a8347e2 100644 --- a/src/SKSE/Trampoline.cpp +++ b/src/SKSE/Trampoline.cpp @@ -10,6 +10,7 @@ # undef max # undef MEM_COMMIT # undef MEM_FREE +# undef MEM_RELEASE # undef MEM_RESERVE # undef PAGE_EXECUTE_READWRITE #endif From 185e7902ff2fdcaa81c93ff1186b44478a4a1da2 Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Sat, 2 Mar 2024 13:03:56 -0600 Subject: [PATCH 10/13] Improve xmake (for SE) --- xmake-extra.lua | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/xmake-extra.lua b/xmake-extra.lua index 406fa926a..e2ec0fff6 100644 --- a/xmake-extra.lua +++ b/xmake-extra.lua @@ -1,7 +1,8 @@ local PLUGIN_FILE = [[ #include #ifdef SKYRIM_SUPPORT_AE -extern "C" __declspec(dllexport) constinit auto SKSEPlugin_Version = []() { +extern "C" __declspec(dllexport) +constinit auto SKSEPlugin_Version = []() { SKSE::PluginVersionData v; v.PluginVersion({ ${PLUGIN_VERSION_MAJOR}, ${PLUGIN_VERSION_MINOR}, ${PLUGIN_VERSION_PATCH}, 0 }); v.PluginName("${PLUGIN_NAME}"); @@ -12,6 +13,27 @@ extern "C" __declspec(dllexport) constinit auto SKSEPlugin_Version = []() { v.CompatibleVersions({ SKSE::RUNTIME_LATEST }); return v; }(); +#else +extern "C" __declspec(dllexport) +bool SKSEPlugin_Query(const SKSE::QueryInterface* a_skse, SKSE::PluginInfo* a_info) +{ + a_info->infoVersion = SKSE::PluginInfo::kVersion; + a_info->name = "${PLUGIN_NAME}"; + a_info->version = REL::Version{ ${PLUGIN_VERSION_MAJOR}, ${PLUGIN_VERSION_MINOR}, ${PLUGIN_VERSION_PATCH}, 0 }.pack(); + + if (a_skse->IsEditor()) { + SKSE::log::critical("Loaded in editor, marking as incompatible"); + return false; + } + + const auto ver = a_skse->RuntimeVersion(); + if (ver < SKSE::RUNTIME_1_5_39) { + SKSE::log::critical("Unsupported runtime version {}", ver.string()); + return false; + } + + return true; +} #endif ]] From 48d0e917547bcbbe59d51aed4d24dd6514a3769d Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Sat, 2 Mar 2024 14:54:56 -0600 Subject: [PATCH 11/13] c++23 --- CMakeLists.txt | 2 +- README.md | 2 +- include/RE/A/ActorValueList.h | 2 +- include/RE/B/BGSDefaultObjectManager.h | 6 +++--- include/RE/G/GFxTask.h | 2 +- include/RE/M/MiddleHighProcessData.h | 2 +- include/RE/T/TESDataHandler.h | 2 +- include/SKSE/Impl/PCH.h | 7 ------- src/RE/A/Actor.cpp | 2 +- src/RE/A/ActorValueList.cpp | 4 ++-- src/RE/B/BSInputDeviceManager.cpp | 12 ++++++------ src/RE/B/BSXFlags.cpp | 2 +- src/RE/G/GFxResource.cpp | 8 ++++---- src/RE/I/IFormFactory.cpp | 2 +- src/RE/N/NiAlphaProperty.cpp | 4 ++-- src/RE/T/TESAIForm.cpp | 6 +++--- src/RE/T/TESDataHandler.cpp | 2 +- src/RE/T/TypeInfo.cpp | 4 ++-- xmake.lua | 2 +- 19 files changed, 33 insertions(+), 40 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 95607da82..29d1637ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,7 +46,7 @@ target_compile_definitions( target_compile_features( "${PROJECT_NAME}" PUBLIC - cxx_std_20 + cxx_std_23 ) if (MSVC) diff --git a/README.md b/README.md index ed3755510..23baf2388 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # `CommonLibSSE` -[![C++20](https://img.shields.io/static/v1?label=standard&message=C%2B%2B20&color=blue&logo=c%2B%2B&&logoColor=white&style=flat)](https://en.cppreference.com/w/cpp/compiler_support) +[![C++23](https://img.shields.io/static/v1?label=standard&message=C%2B%2B20&color=blue&logo=c%2B%2B&&logoColor=white&style=flat)](https://en.cppreference.com/w/cpp/compiler_support) [![Platform](https://img.shields.io/static/v1?label=platform&message=windows&color=dimgray&style=flat)](#) [![Main CI](https://github.com/dann1/CommonLibSSE/actions/workflows/main_ci.yml/badge.svg?branch=dev)](https://github.com/dann1/CommonLibSSE/actions/workflows/main_ci.yml?branch=dev) diff --git a/include/RE/A/ActorValueList.h b/include/RE/A/ActorValueList.h index 9baf8fded..c8cd07c3f 100644 --- a/include/RE/A/ActorValueList.h +++ b/include/RE/A/ActorValueList.h @@ -20,7 +20,7 @@ namespace RE // members std::uint32_t unk00; // 00 std::uint32_t pad04; // 04 - ActorValueInfo* actorValues[stl::to_underlying(ActorValue::kTotal)]; // 08 + ActorValueInfo* actorValues[std::to_underlying(ActorValue::kTotal)]; // 08 }; } diff --git a/include/RE/B/BGSDefaultObjectManager.h b/include/RE/B/BGSDefaultObjectManager.h index 57b9525fd..3cdf81c05 100644 --- a/include/RE/B/BGSDefaultObjectManager.h +++ b/include/RE/B/BGSDefaultObjectManager.h @@ -442,17 +442,17 @@ namespace RE return func(); } - [[nodiscard]] TESForm* GetObject(DefaultObject a_object) const noexcept { return GetObject(stl::to_underlying(a_object)); } + [[nodiscard]] TESForm* GetObject(DefaultObject a_object) const noexcept { return GetObject(std::to_underlying(a_object)); } template [[nodiscard]] T* GetObject(DefaultObject a_object) const noexcept { - return GetObject(stl::to_underlying(a_object)); + return GetObject(std::to_underlying(a_object)); } [[nodiscard]] TESForm* GetObject(std::size_t a_idx) const noexcept { - assert(a_idx < stl::to_underlying(DefaultObject::kTotal)); + assert(a_idx < std::to_underlying(DefaultObject::kTotal)); return objectInit[a_idx] ? objects[a_idx] : nullptr; } diff --git a/include/RE/G/GFxTask.h b/include/RE/G/GFxTask.h index f3725597b..d7e174fb5 100644 --- a/include/RE/G/GFxTask.h +++ b/include/RE/G/GFxTask.h @@ -51,7 +51,7 @@ namespace RE inline TaskId GetTaskType() const { - return static_cast(stl::to_underlying(thisTaskId) & stl::to_underlying(TaskId::kType_Mask)); + return static_cast(std::to_underlying(thisTaskId) & std::to_underlying(TaskId::kType_Mask)); } inline TaskState GetTaskState() const diff --git a/include/RE/M/MiddleHighProcessData.h b/include/RE/M/MiddleHighProcessData.h index dd1be6302..dce381b3b 100644 --- a/include/RE/M/MiddleHighProcessData.h +++ b/include/RE/M/MiddleHighProcessData.h @@ -120,7 +120,7 @@ namespace RE BSTArray& operator[](BGSEntryPoint::ENTRY_POINT a_pos) { assert(a_pos < BGSEntryPoint::ENTRY_POINT::kTotal); - return perkEntryArrays[stl::to_underlying(a_pos)]; + return perkEntryArrays[std::to_underlying(a_pos)]; } // members diff --git a/include/RE/T/TESDataHandler.h b/include/RE/T/TESDataHandler.h index f0983c85a..64132cb68 100644 --- a/include/RE/T/TESDataHandler.h +++ b/include/RE/T/TESDataHandler.h @@ -72,7 +72,7 @@ namespace RE std::uint16_t pad002; // 002 std::uint32_t pad004; // 004 TESObjectList* objectList; // 008 - BSTArray formArrays[stl::to_underlying(FormType::Max)]; // 010 + BSTArray formArrays[std::to_underlying(FormType::Max)]; // 010 TESRegionList* regionList; // D00 NiTPrimitiveArray interiorCells; // D08 NiTPrimitiveArray addonNodes; // D20 diff --git a/include/SKSE/Impl/PCH.h b/include/SKSE/Impl/PCH.h index e82ba3469..4a87ff47b 100644 --- a/include/SKSE/Impl/PCH.h +++ b/include/SKSE/Impl/PCH.h @@ -637,13 +637,6 @@ namespace SKSE REX::W32::TerminateProcess(REX::W32::GetCurrentProcess(), EXIT_FAILURE); } - template - [[nodiscard]] constexpr auto to_underlying(Enum a_val) noexcept // - requires(std::is_enum_v) - { - return static_cast>(a_val); - } - template [[nodiscard]] To unrestricted_cast(From a_from) noexcept { diff --git a/src/RE/A/Actor.cpp b/src/RE/A/Actor.cpp index d4c77ca0a..e0a6abe8b 100644 --- a/src/RE/A/Actor.cpp +++ b/src/RE/A/Actor.cpp @@ -1225,7 +1225,7 @@ namespace RE if (auto magicCaster = magicCasters[i]) { auto castingSource = magicCaster->GetCastingSource(); if (magicCaster->currentSpell) { - result |= 1 << stl::to_underlying(castingSource); + result |= 1 << std::to_underlying(castingSource); } } } diff --git a/src/RE/A/ActorValueList.cpp b/src/RE/A/ActorValueList.cpp index 533f622fb..af7cacc33 100644 --- a/src/RE/A/ActorValueList.cpp +++ b/src/RE/A/ActorValueList.cpp @@ -7,13 +7,13 @@ namespace RE ActorValueInfo* ActorValueList::GetActorValue(ActorValue a_actorValue) const { return a_actorValue < ActorValue::kTotal ? - actorValues[stl::to_underlying(a_actorValue)] : + actorValues[std::to_underlying(a_actorValue)] : nullptr; } ActorValue ActorValueList::LookupActorValueByName(std::string_view a_enumName) const { - for (std::size_t i = 0; i < stl::to_underlying(ActorValue::kTotal); ++i) { + for (std::size_t i = 0; i < std::to_underlying(ActorValue::kTotal); ++i) { if (a_enumName.size() == strlen(actorValues[i]->enumName) && _strnicmp(actorValues[i]->enumName, a_enumName.data(), a_enumName.size()) == 0) { return static_cast(i); diff --git a/src/RE/B/BSInputDeviceManager.cpp b/src/RE/B/BSInputDeviceManager.cpp index 2851b7977..0b021d3a1 100644 --- a/src/RE/B/BSInputDeviceManager.cpp +++ b/src/RE/B/BSInputDeviceManager.cpp @@ -23,22 +23,22 @@ namespace RE BSPCGamepadDeviceHandler* BSInputDeviceManager::GetGamepadHandler() { - return static_cast(devices[stl::to_underlying(INPUT_DEVICE::kGamepad)]); + return static_cast(devices[std::to_underlying(INPUT_DEVICE::kGamepad)]); } BSWin32KeyboardDevice* BSInputDeviceManager::GetKeyboard() { - return static_cast(devices[stl::to_underlying(INPUT_DEVICE::kKeyboard)]); + return static_cast(devices[std::to_underlying(INPUT_DEVICE::kKeyboard)]); } BSWin32MouseDevice* BSInputDeviceManager::GetMouse() { - return static_cast(devices[stl::to_underlying(INPUT_DEVICE::kMouse)]); + return static_cast(devices[std::to_underlying(INPUT_DEVICE::kMouse)]); } BSWin32VirtualKeyboardDevice* BSInputDeviceManager::GetVirtualKeyboard() { - return static_cast(devices[stl::to_underlying(INPUT_DEVICE::kVirtualKeyboard)]); + return static_cast(devices[std::to_underlying(INPUT_DEVICE::kVirtualKeyboard)]); } bool BSInputDeviceManager::IsGamepadConnected() @@ -61,13 +61,13 @@ namespace RE bool BSInputDeviceManager::GetDeviceKeyMapping(INPUT_DEVICE a_device, std::uint32_t a_key, BSFixedString& a_mapping) { - auto device = devices[stl::to_underlying(a_device)]; + auto device = devices[std::to_underlying(a_device)]; return device && device->GetKeyMapping(a_key, a_mapping); } bool BSInputDeviceManager::GetDeviceMappedKeycode(INPUT_DEVICE a_device, std::uint32_t a_key, uint32_t& a_outKeyCode) { - auto device = devices[stl::to_underlying(a_device)]; + auto device = devices[std::to_underlying(a_device)]; return device && device->GetMappedKeycode(a_key, a_outKeyCode); } diff --git a/src/RE/B/BSXFlags.cpp b/src/RE/B/BSXFlags.cpp index 1d6377951..7ec1130d5 100644 --- a/src/RE/B/BSXFlags.cpp +++ b/src/RE/B/BSXFlags.cpp @@ -10,6 +10,6 @@ namespace RE void BSXFlags::SetFlags(Flag a_flags) { - value = stl::to_underlying(a_flags); + value = std::to_underlying(a_flags); } } diff --git a/src/RE/G/GFxResource.cpp b/src/RE/G/GFxResource.cpp index f5575f39c..f7321bb5e 100644 --- a/src/RE/G/GFxResource.cpp +++ b/src/RE/G/GFxResource.cpp @@ -71,19 +71,19 @@ namespace RE { return static_cast( GetResourceTypeCode() >> - stl::to_underlying(ResourceType::kTypeCode_Shift)); + std::to_underlying(ResourceType::kTypeCode_Shift)); } GFxResource::ResourceUse GFxResource::GetResourceUse() const { return static_cast( GetResourceTypeCode() & - stl::to_underlying(ResourceUse::kTypeCode_Mask)); + std::to_underlying(ResourceUse::kTypeCode_Mask)); } std::uint32_t GFxResource::MakeTypeCode(ResourceType a_resourceType, ResourceUse a_resourceUse) { - return (stl::to_underlying(a_resourceType) << stl::to_underlying(ResourceType::kTypeCode_Shift)) | - stl::to_underlying(a_resourceUse); + return (std::to_underlying(a_resourceType) << std::to_underlying(ResourceType::kTypeCode_Shift)) | + std::to_underlying(a_resourceUse); } } diff --git a/src/RE/I/IFormFactory.cpp b/src/RE/I/IFormFactory.cpp index a8418dcb1..eb986c89a 100644 --- a/src/RE/I/IFormFactory.cpp +++ b/src/RE/I/IFormFactory.cpp @@ -6,7 +6,7 @@ namespace RE { struct Factories { - IFormFactory* data[stl::to_underlying(FormType::Max)]; + IFormFactory* data[std::to_underlying(FormType::Max)]; }; REL::Relocation formFactories{ RELOCATION_ID(514355, 400508) }; diff --git a/src/RE/N/NiAlphaProperty.cpp b/src/RE/N/NiAlphaProperty.cpp index dfe706c02..4ea82c3f4 100644 --- a/src/RE/N/NiAlphaProperty.cpp +++ b/src/RE/N/NiAlphaProperty.cpp @@ -43,12 +43,12 @@ namespace RE void NiAlphaProperty::SetDestBlendMode(AlphaFunction a_mode) { alphaFlags &= ~480; - alphaFlags |= 32 * stl::to_underlying(a_mode); + alphaFlags |= 32 * std::to_underlying(a_mode); } void NiAlphaProperty::SetSrcBlendMode(AlphaFunction a_mode) { alphaFlags &= ~30; - alphaFlags |= 2 * stl::to_underlying(a_mode); + alphaFlags |= 2 * std::to_underlying(a_mode); } } diff --git a/src/RE/T/TESAIForm.cpp b/src/RE/T/TESAIForm.cpp index 1295e6e6c..5e331c64f 100644 --- a/src/RE/T/TESAIForm.cpp +++ b/src/RE/T/TESAIForm.cpp @@ -44,21 +44,21 @@ namespace RE void TESAIForm::SetAggressionLevel(ACTOR_AGGRESSION a_level) { - const auto level = stl::to_underlying(a_level); + const auto level = std::to_underlying(a_level); aiData.aggression1 = (level & 1) != 0; aiData.aggression2 = (level & 2) != 0; } void TESAIForm::SetAssistanceLevel(ACTOR_ASSISTANCE a_level) { - const auto level = stl::to_underlying(a_level); + const auto level = std::to_underlying(a_level); aiData.assistance1 = (level & 1) != 0; aiData.assistance2 = (level & 2) != 0; } void TESAIForm::SetConfidenceLevel(ACTOR_CONFIDENCE a_level) { - const auto level = stl::to_underlying(a_level); + const auto level = std::to_underlying(a_level); aiData.confidence1 = (level & 1) != 0; aiData.confidence2 = (level & 2) != 0; aiData.confidence3 = (level & 4) != 0; diff --git a/src/RE/T/TESDataHandler.cpp b/src/RE/T/TESDataHandler.cpp index c16c1341f..70db58a93 100644 --- a/src/RE/T/TESDataHandler.cpp +++ b/src/RE/T/TESDataHandler.cpp @@ -131,7 +131,7 @@ namespace RE BSTArray& TESDataHandler::GetFormArray(FormType a_formType) { - return formArrays[stl::to_underlying(a_formType)]; + return formArrays[std::to_underlying(a_formType)]; } ObjectRefHandle TESDataHandler::CreateReferenceAtLocation(TESBoundObject* a_base, const NiPoint3& a_location, const NiPoint3& a_rotation, TESObjectCELL* a_targetCell, TESWorldSpace* a_selfWorldSpace, TESObjectREFR* a_alreadyCreatedRef, BGSPrimitive* a_primitive, const ObjectRefHandle& a_linkedRoomRefHandle, bool a_forcePersist, bool a_arg11) diff --git a/src/RE/T/TypeInfo.cpp b/src/RE/T/TypeInfo.cpp index a6a61884f..8ec2c1601 100644 --- a/src/RE/T/TypeInfo.cpp +++ b/src/RE/T/TypeInfo.cpp @@ -82,9 +82,9 @@ namespace RE { assert(IsObject() || IsObjectArray()); if (IsObject()) { - return reinterpret_cast(stl::to_underlying(GetRawType()) & ~stl::to_underlying(RawType::kObject)); + return reinterpret_cast(std::to_underlying(GetRawType()) & ~std::to_underlying(RawType::kObject)); } else { - return reinterpret_cast(stl::to_underlying(GetRawType()) & ~stl::to_underlying(RawType::kObjectArray)); + return reinterpret_cast(std::to_underlying(GetRawType()) & ~std::to_underlying(RawType::kObjectArray)); } } diff --git a/xmake.lua b/xmake.lua index 2fc70316d..5830912bf 100644 --- a/xmake.lua +++ b/xmake.lua @@ -7,7 +7,7 @@ includes("xmake-extra.lua") -- set project set_project("commonlibsse") set_arch("x64") -set_languages("c++20") +set_languages("c++23") set_warnings("allextra") set_encodings("utf-8") From 475c51d80543dd8f1d652f9f765e76198ff4c2c4 Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Sat, 2 Mar 2024 18:12:33 -0600 Subject: [PATCH 12/13] Fix SerializationInterface return types --- include/SKSE/Interfaces.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/SKSE/Interfaces.h b/include/SKSE/Interfaces.h index fa9d4d9a3..a519212cd 100644 --- a/include/SKSE/Interfaces.h +++ b/include/SKSE/Interfaces.h @@ -106,7 +106,7 @@ namespace SKSE std::negation_v< std::is_pointer>, int> = 0> - inline std::uint32_t WriteRecord(std::uint32_t a_type, std::uint32_t a_version, const T& a_buf) const + inline bool WriteRecord(std::uint32_t a_type, std::uint32_t a_version, const T& a_buf) const { return WriteRecord(a_type, a_version, std::addressof(a_buf), sizeof(T)); } @@ -117,7 +117,7 @@ namespace SKSE std::enable_if_t< std::is_array_v, int> = 0> - inline std::uint32_t WriteRecord(std::uint32_t a_type, std::uint32_t a_version, const T (&a_buf)[N]) const + inline bool WriteRecord(std::uint32_t a_type, std::uint32_t a_version, const T (&a_buf)[N]) const { return WriteRecord(a_type, a_version, std::addressof(a_buf), sizeof(T) * N); } @@ -132,7 +132,7 @@ namespace SKSE std::negation_v< std::is_pointer>, int> = 0> - inline std::uint32_t WriteRecordData(const T& a_buf) const + inline bool WriteRecordData(const T& a_buf) const { return WriteRecordData(std::addressof(a_buf), sizeof(T)); } @@ -143,7 +143,7 @@ namespace SKSE std::enable_if_t< std::is_array_v, int> = 0> - inline std::uint32_t WriteRecordData(const T (&a_buf)[N]) const + inline bool WriteRecordData(const T (&a_buf)[N]) const { return WriteRecordData(std::addressof(a_buf), sizeof(T) * N); } From bbdfa49cc02c588354bb9c861522e7cda6a87615 Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Sun, 3 Mar 2024 18:17:19 -0600 Subject: [PATCH 13/13] Add TESObjectREFR::OpenContainer --- include/RE/T/TESObjectREFR.h | 1 + src/RE/T/TESObjectREFR.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/include/RE/T/TESObjectREFR.h b/include/RE/T/TESObjectREFR.h index a26df60ae..bb0abfbfb 100644 --- a/include/RE/T/TESObjectREFR.h +++ b/include/RE/T/TESObjectREFR.h @@ -450,6 +450,7 @@ namespace RE bool MoveToNode(TESObjectREFR* a_target, const BSFixedString& a_nodeName); bool MoveToNode(TESObjectREFR* a_target, NiAVObject* a_node); bool NameIncludes(std::string a_word); + void OpenContainer(std::int32_t a_openType) const; NiPointer PlaceObjectAtMe(TESBoundObject* a_baseToPlace, bool a_forcePersist) const; void PlayAnimation(stl::zstring a_from, stl::zstring a_to); void PlayAnimation(NiControllerManager* a_manager, NiControllerSequence* a_toSeq, NiControllerSequence* a_fromSeq); diff --git a/src/RE/T/TESObjectREFR.cpp b/src/RE/T/TESObjectREFR.cpp index 9d83840dd..9fa6af0f6 100644 --- a/src/RE/T/TESObjectREFR.cpp +++ b/src/RE/T/TESObjectREFR.cpp @@ -790,6 +790,13 @@ namespace RE return name.find(a_word) != std::string::npos; } + void TESObjectREFR::OpenContainer(std::int32_t a_openType) const + { + using func_t = decltype(&TESObjectREFR::OpenContainer); + REL::Relocation func{ RELOCATION_ID(50211, 51140) }; + func(this, a_openType); + } + NiPointer TESObjectREFR::PlaceObjectAtMe(TESBoundObject* a_baseToPlace, bool a_forcePersist) const { const auto handle = TESDataHandler::GetSingleton()->CreateReferenceAtLocation(a_baseToPlace, GetPosition(), GetAngle(), GetParentCell(), GetWorldspace(), nullptr, nullptr, ObjectRefHandle(), a_forcePersist, true);