From c7295d014284a346d7dc2a2546e3eb284fadafff Mon Sep 17 00:00:00 2001 From: shad0wshayd3 Date: Thu, 14 Nov 2024 15:01:17 -0700 Subject: [PATCH 1/7] fix: msvc::unique_ptr --- CommonLibF4/include/RE/msvc/memory.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CommonLibF4/include/RE/msvc/memory.h b/CommonLibF4/include/RE/msvc/memory.h index 083d178c..f22c87bc 100644 --- a/CommonLibF4/include/RE/msvc/memory.h +++ b/CommonLibF4/include/RE/msvc/memory.h @@ -270,7 +270,7 @@ namespace RE::msvc // 3c unique_ptr(pointer a_ptr, const deleter_type& a_dtor) noexcept // - requires((std::is_lvalue_reference && + requires((std::is_lvalue_reference_v && std::is_const_v)) : super(a_ptr, a_dtor) {} @@ -484,7 +484,7 @@ namespace RE::msvc // 3c template unique_ptr(U a_ptr, const deleter_type& a_dtor) noexcept // - requires((std::is_lvalue_reference && + requires((std::is_lvalue_reference_v && std::is_const_v && detail::unique_ptr_array_convertible)) : super(a_ptr, a_dtor) From 552fa87bd93fcbb7e55e139ff972219c0a90b971 Mon Sep 17 00:00:00 2001 From: shad0wshayd3 Date: Thu, 14 Nov 2024 15:52:27 -0700 Subject: [PATCH 2/7] fix: workflows --- .github/workflows/main_ci.yml | 56 +++++---- .github/workflows/maintenance.yml | 24 ++-- CMakePresets.json | 112 +++++++----------- .../{glob_files.py => cmake_generate.py} | 0 4 files changed, 84 insertions(+), 108 deletions(-) rename CommonLibF4/scripts/{glob_files.py => cmake_generate.py} (100%) diff --git a/.github/workflows/main_ci.yml b/.github/workflows/main_ci.yml index 172821bb..dfabfd9d 100644 --- a/.github/workflows/main_ci.yml +++ b/.github/workflows/main_ci.yml @@ -1,46 +1,52 @@ -name: build +name: Main CI on: push: branches: [ master ] + paths: + - '.github/workflows/main_ci.yml' + - 'cmake/**' + - 'include/**' + - 'src/**' + - 'CMakeLists.txt' + - 'CMakePresets.json' + - 'vcpkg.json' pull_request: branches: [ master ] workflow_dispatch: env: + BUILD_TYPE: Debug VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/vcpkg-cache jobs: windows: - runs-on: windows-2022 - strategy: - fail-fast: false - matrix: - build-type: - - debug - - release - compiler: - - msvc + runs-on: windows-latest steps: - - name: Checkout - uses: actions/checkout@v2 + - uses: actions/checkout@v2 with: path: main - - - name: Setup cmake - uses: lukka/get-cmake@latest - - - name: Setup msvc - uses: ilammy/msvc-dev-cmd@v1.10.0 - name: Setup vcpkg - uses: friendlyanon/setup-vcpkg@v1 + run: | + mkdir -p ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} + cd $env:VCPKG_INSTALLATION_ROOT + ./bootstrap-vcpkg.bat + ./vcpkg --version > ${{ github.workspace }}/vcpkg-version.txt + + - name: Cache vcpkg + uses: actions/cache@v2 + id: vcpkg-cache + env: + cache-name: vcpkg-cache with: - committish: 943c5ef1c8f6b5e6ced092b242c8299caae2ff01 + path: ${{ env.VCPKG_DEFAULT_BINARY_CACHE }}/* + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('main/**/vcpkg.json', 'vcpkg-version.txt') }} + + - name: Configure CMake + working-directory: ${{ github.workspace }}/main + run: cmake --preset vs2022-windows-vcpkg - name: Build - uses: lukka/run-cmake@v10.0 - with: - cmakeListsTxtPath: ${{ github.workspace }}/main/CMakeLists.txt - configurePreset: build-${{ matrix.build-type }}-${{ matrix.compiler }}-vcpkg - buildPreset: ${{ matrix.build-type }}-${{ matrix.compiler }}-vcpkg + working-directory: ${{ github.workspace }}/main + run: cmake --build build --config ${{ env.BUILD_TYPE }} diff --git a/.github/workflows/maintenance.yml b/.github/workflows/maintenance.yml index fdd2b381..f2ca0eb9 100644 --- a/.github/workflows/maintenance.yml +++ b/.github/workflows/maintenance.yml @@ -1,24 +1,24 @@ name: maintenance -on: [push] +on: push jobs: maintenance: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: '3.9' + - uses: actions/setup-python@v2 + with: + python-version: '3.9' - - name: Run clang-format - run: find -type f \( -name *.h -o -name *.cpp \) | xargs clang-format-14 -style=file -i + - name: Run clang-format + run: find -type f \( -name *.h -o -name *.cpp \) | xargs clang-format-14 -style=file -i - - name: Glob files - run: python ${{ github.workspace }}/CommonLibF4/scripts/glob_files.py + - name: Glob files + run: python ${{ github.workspace }}/CommonLibF4/scripts/cmake_generate.py - - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: maintenance + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: maintenance diff --git a/CMakePresets.json b/CMakePresets.json index c5cb2b39..db5296f8 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,102 +1,72 @@ { "configurePresets": [ { - "name": "common", - "hidden": true, + "binaryDir": "${sourceDir}/build", "cacheVariables": { - "CMAKE_CXX_FLAGS": "$env{PROJECT_PLATFORM_FLAGS} $env{PROJECT_TEXT_FLAGS} $env{PROJECT_COMPILER_FLAGS} $penv{CXX_FLAGS}", - "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$:Debug>DLL" - }, - "environment": { - "PROJECT_ARCHITECTURE": "x64", - "PROJECT_PLATFORM": "windows", - "PROJECT_TEXT_FLAGS": "-DUNICODE -D_UNICODE" + "CMAKE_BUILD_TYPE": { + "type": "STRING", + "value": "Debug" + } }, "errors": { "deprecated": true }, + "hidden": true, + "name": "cmake-dev", "warnings": { "deprecated": true, "dev": true - }, - "vendor": { - "microsoft.com/VisualStudioSettings/CMake/1.0": { - "intelliSenseMode": "$env{PROJECT_PLATFORM}-$env{PROJECT_COMPILER}-$env{PROJECT_ARCHITECTURE}", - "enableMicrosoftCodeAnalysis": true, - "enableClangTidyCodeAnalysis": true - } } }, { - "name": "buildtype-debug", - "hidden": true, "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug" - } - }, - { - "name": "buildtype-release", + "CMAKE_TOOLCHAIN_FILE": { + "type": "STRING", + "value": "$env{VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" + } + }, "hidden": true, - "cacheVariables": { - "CMAKE_BUILD_TYPE": "RelWithDebInfo" - } + "name": "vcpkg" }, { - "name": "packaging-vcpkg", - "hidden": true, - "toolchainFile": "$env{VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake", "cacheVariables": { - "VCPKG_HOST_TRIPLET": "$env{PROJECT_ARCHITECTURE}-$env{PROJECT_PLATFORM}-static-md", - "VCPKG_TARGET_TRIPLET": "$env{PROJECT_ARCHITECTURE}-$env{PROJECT_PLATFORM}-static-md" - } - }, - { - "name": "generator-ninja", - "hidden": true, - "generator": "Ninja" - }, - { - "name": "compiler-msvc", + "CMAKE_MSVC_RUNTIME_LIBRARY": { + "type": "STRING", + "value": "MultiThreaded$<$:Debug>DLL" + }, + "VCPKG_TARGET_TRIPLET": { + "type": "STRING", + "value": "x64-windows-static-md" + } + }, "hidden": true, - "environment": { - "PROJECT_COMPILER_FLAGS": "/EHsc /MP /W4 /WX /external:W0 /bigobj", - "PROJECT_COMPILER": "msvc" - } + "name": "windows" }, { - "name": "build-debug-msvc-vcpkg", + "cacheVariables": { + "CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /WX /external:anglebrackets /external:W0" + }, + "generator": "Visual Studio 16 2019", "inherits": [ - "common", - "buildtype-debug", - "packaging-vcpkg", - "generator-ninja", - "compiler-msvc" + "cmake-dev", + "vcpkg", + "windows" ], - "binaryDir": "${sourceDir}/build/debug-msvc-vcpkg" + "name": "vs2019-windows-vcpkg" }, { - "name": "build-release-msvc-vcpkg", + "cacheVariables": { + "CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /WX /external:W0" + }, + "generator": "Visual Studio 17 2022", "inherits": [ - "common", - "buildtype-release", - "packaging-vcpkg", - "generator-ninja", - "compiler-msvc" + "cmake-dev", + "vcpkg", + "windows" ], - "binaryDir": "${sourceDir}/build/release-msvc-vcpkg" - } - ], - "buildPresets": [ - { - "name": "debug-msvc-vcpkg", - "configurePreset": "build-debug-msvc-vcpkg", - "displayName": "Debug (MSVC, Vcpkg)" - }, - { - "name": "release-msvc-vcpkg", - "configurePreset": "build-release-msvc-vcpkg", - "displayName": "Release (MSVC, Vcpkg)" + "name": "vs2022-windows-vcpkg", + "toolset": "v143" } ], - "version": 3 + "version": 2 } diff --git a/CommonLibF4/scripts/glob_files.py b/CommonLibF4/scripts/cmake_generate.py similarity index 100% rename from CommonLibF4/scripts/glob_files.py rename to CommonLibF4/scripts/cmake_generate.py From 4c8baa484dd92694f84dae8c7114ab5dcf2bed8d Mon Sep 17 00:00:00 2001 From: shad0wshayd3 Date: Thu, 14 Nov 2024 16:00:42 -0700 Subject: [PATCH 3/7] feat: add xmake workflow --- .github/workflows/main_ci_xmake.yml | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/main_ci_xmake.yml diff --git a/.github/workflows/main_ci_xmake.yml b/.github/workflows/main_ci_xmake.yml new file mode 100644 index 00000000..c7095ae7 --- /dev/null +++ b/.github/workflows/main_ci_xmake.yml @@ -0,0 +1,38 @@ +name: Main CI [XMake] + +on: + push: + branches: [ master ] + paths: + - ".github/workflows/main_ci_xmake.yml" + - "include/**" + - "src/**" + - "xmake.lua" + - "xmake-extra.lua" + pull_request: + branches: [ master ] + workflow_dispatch: + +jobs: + build-xmake: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + mode: + - debug + - release + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup XMake + uses: xmake-io/github-action-setup-xmake@v1 + with: + xmake-version: "latest" + + - name: Configure + run: xmake config -y --mode=${{ matrix.mode }} --vs_toolset=14.41 + + - name: Build + run: xmake build -y -vD From dd664ad8681d32989866f60e5abeb8419704820a Mon Sep 17 00:00:00 2001 From: shad0wshayd3 Date: Thu, 14 Nov 2024 17:24:41 -0700 Subject: [PATCH 4/7] feat: Relocation::write_func requires xbyak --- CommonLibF4/include/REL/Relocation.h | 10 +++++++++ CommonLibF4/src/REL/Relocation.cpp | 31 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/CommonLibF4/include/REL/Relocation.h b/CommonLibF4/include/REL/Relocation.h index b58f124a..cac01367 100644 --- a/CommonLibF4/include/REL/Relocation.h +++ b/CommonLibF4/include/REL/Relocation.h @@ -329,6 +329,16 @@ namespace REL safe_fill(address(), a_value, a_count); } +#ifdef F4SE_SUPPORT_XBYAK + void write_func(const std::size_t a_count, const std::uintptr_t a_dst) requires(std::same_as); + + template + void write_func(const std::size_t a_count, const F a_dst) requires(std::same_as) + { + write_func(a_count, stl::unrestricted_cast(a_dst)); + } +#endif + template std::uintptr_t write_vfunc(std::size_t a_idx, std::uintptr_t a_newFunc) requires(std::same_as) { diff --git a/CommonLibF4/src/REL/Relocation.cpp b/CommonLibF4/src/REL/Relocation.cpp index 4ce831bd..3b8b39c6 100644 --- a/CommonLibF4/src/REL/Relocation.cpp +++ b/CommonLibF4/src/REL/Relocation.cpp @@ -32,3 +32,34 @@ namespace REL assert(success); } } + +#ifdef F4SE_SUPPORT_XBYAK +# include + +namespace REL +{ + struct write_func_impl : Xbyak::CodeGenerator + { + write_func_impl(std::uintptr_t a_dst) + { + Xbyak::Label dst; + jmp(ptr[rip + dst]); + L(dst); + dq(a_dst); + } + }; + + template + void Relocation::write_func(const std::size_t a_count, const std::uintptr_t a_dst) + requires(std::same_as) + { + safe_fill(address(), INT3, a_count); + auto patch = write_func_impl{ a_dst }; + patch.ready(); + assert(patch.getSize() <= a_count); + safe_write(address(), std::span{ patch.getCode(), patch.getSize() }); + } + + template void Relocation::write_func(const std::size_t, const std::uintptr_t); +} +#endif From 327cd150fa19013d808e1d4ccb3fe044ae6d6628 Mon Sep 17 00:00:00 2001 From: shad0wshayd3 Date: Fri, 15 Nov 2024 00:25:10 +0000 Subject: [PATCH 5/7] maintenance --- CommonLibF4/src/REL/Relocation.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CommonLibF4/src/REL/Relocation.cpp b/CommonLibF4/src/REL/Relocation.cpp index 3b8b39c6..2266b261 100644 --- a/CommonLibF4/src/REL/Relocation.cpp +++ b/CommonLibF4/src/REL/Relocation.cpp @@ -50,8 +50,7 @@ namespace REL }; template - void Relocation::write_func(const std::size_t a_count, const std::uintptr_t a_dst) - requires(std::same_as) + void Relocation::write_func(const std::size_t a_count, const std::uintptr_t a_dst) requires(std::same_as) { safe_fill(address(), INT3, a_count); auto patch = write_func_impl{ a_dst }; From d3504612612ece0374135975e63e30ea7c15708d Mon Sep 17 00:00:00 2001 From: shad0wshayd3 Date: Thu, 14 Nov 2024 16:20:53 -0700 Subject: [PATCH 6/7] feat: REX 2 split REX defs into separate files, add config library --- CommonLibF4/CMakeLists.txt | 6 + CommonLibF4/cmake/sourcelist.cmake | 8 + CommonLibF4/include/F4SE/Impl/PCH.h | 10 +- CommonLibF4/include/REX/REX.h | 263 +---------------- CommonLibF4/include/REX/REX/Enum.h | 68 +++++ CommonLibF4/include/REX/REX/EnumSet.h | 181 ++++++++++++ CommonLibF4/include/REX/REX/INI.h | 100 +++++++ CommonLibF4/include/REX/REX/JSON.h | 97 ++++++ CommonLibF4/include/REX/REX/Setting.h | 71 +++++ CommonLibF4/include/REX/REX/Singleton.h | 25 ++ CommonLibF4/include/REX/REX/TOML.h | 116 ++++++++ CommonLibF4/src/REX/REX.cpp | 375 ++++++++++++++++++++++++ CommonLibF4/xmake.lua | 44 ++- 13 files changed, 1103 insertions(+), 261 deletions(-) create mode 100644 CommonLibF4/include/REX/REX/Enum.h create mode 100644 CommonLibF4/include/REX/REX/EnumSet.h create mode 100644 CommonLibF4/include/REX/REX/INI.h create mode 100644 CommonLibF4/include/REX/REX/JSON.h create mode 100644 CommonLibF4/include/REX/REX/Setting.h create mode 100644 CommonLibF4/include/REX/REX/Singleton.h create mode 100644 CommonLibF4/include/REX/REX/TOML.h create mode 100644 CommonLibF4/src/REX/REX.cpp diff --git a/CommonLibF4/CMakeLists.txt b/CommonLibF4/CMakeLists.txt index d975f29e..baa5d29d 100644 --- a/CommonLibF4/CMakeLists.txt +++ b/CommonLibF4/CMakeLists.txt @@ -1,6 +1,9 @@ cmake_minimum_required(VERSION 3.21) option(F4SE_SUPPORT_XBYAK "Enables trampoline support for Xbyak." OFF) +option(REX_OPTION_INI "Enables ini config support for REX." OFF) +option(REX_OPTION_JSON "Enables json config support for REX." OFF) +option(REX_OPTION_TOML "Enables toml config support for REX." OFF) project( CommonLibF4 @@ -39,6 +42,9 @@ target_compile_definitions( WINVER=0x0601 # windows 7, minimum supported version by fallout 4 _WIN32_WINNT=0x0601 "$<$:F4SE_SUPPORT_XBYAK=1>" + "$<$:REX_OPTION_INI=1>" + "$<$:REX_OPTION_JSON=1>" + "$<$:REX_OPTION_TOML=1>" ) target_compile_features( diff --git a/CommonLibF4/cmake/sourcelist.cmake b/CommonLibF4/cmake/sourcelist.cmake index abcf6a9d..07698bae 100644 --- a/CommonLibF4/cmake/sourcelist.cmake +++ b/CommonLibF4/cmake/sourcelist.cmake @@ -370,6 +370,13 @@ set(SOURCES include/REX/PS4.h include/REX/PS4/SCEPAD.h include/REX/REX.h + include/REX/REX/Enum.h + include/REX/REX/EnumSet.h + include/REX/REX/INI.h + include/REX/REX/JSON.h + include/REX/REX/Setting.h + include/REX/REX/Singleton.h + include/REX/REX/TOML.h include/REX/W32.h include/REX/W32/ADVAPI32.h include/REX/W32/BASE.h @@ -446,5 +453,6 @@ set(SOURCES src/REL/Module.cpp src/REL/Relocation.cpp src/REL/Version.cpp + src/REX/REX.cpp src/REX/W32.cpp ) diff --git a/CommonLibF4/include/F4SE/Impl/PCH.h b/CommonLibF4/include/F4SE/Impl/PCH.h index e7b72e1c..335f088a 100644 --- a/CommonLibF4/include/F4SE/Impl/PCH.h +++ b/CommonLibF4/include/F4SE/Impl/PCH.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -41,15 +42,16 @@ static_assert( std::is_integral_v && sizeof(std::time_t) == sizeof(std::size_t), "wrap std::time_t instead"); +#include "REX/REX/Enum.h" +#include "REX/REX/EnumSet.h" +#include "REX/W32/KERNEL32.h" +#include "REX/W32/USER32.h" + #pragma warning(push, 0) #include #include #pragma warning(pop) -#include "REX/REX.h" -#include "REX/W32/KERNEL32.h" -#include "REX/W32/USER32.h" - namespace F4SE { using namespace std::literals; diff --git a/CommonLibF4/include/REX/REX.h b/CommonLibF4/include/REX/REX.h index e16baae6..43a000af 100644 --- a/CommonLibF4/include/REX/REX.h +++ b/CommonLibF4/include/REX/REX.h @@ -1,258 +1,9 @@ #pragma once -namespace REX -{ - template < - class E, - class U = std::underlying_type_t> - class Enum - { - public: - using enum_type = E; - using underlying_type = U; - - static_assert(std::is_enum_v, "Enum must be an enum"); - static_assert(std::is_integral_v, "Enum<..., U> must be an integral"); - - constexpr Enum() noexcept = default; - constexpr Enum(const Enum&) noexcept = default; - constexpr Enum(Enum&&) noexcept = default; - - template // NOLINTNEXTLINE(google-explicit-constructor) - constexpr Enum(Enum a_rhs) noexcept : - _impl(static_cast(a_rhs.get())) - {} - - constexpr Enum(E a_value) noexcept : - _impl(static_cast(a_value)) - {} - - ~Enum() noexcept = default; - - constexpr Enum& operator=(const Enum&) noexcept = default; - constexpr Enum& operator=(Enum&&) noexcept = default; - - template - constexpr Enum& operator=(Enum a_rhs) noexcept - { - _impl = static_cast(a_rhs.get()); - } - - constexpr Enum& operator=(E a_value) noexcept - { - _impl = static_cast(a_value); - return *this; - } - - public: - [[nodiscard]] explicit constexpr operator bool() const noexcept { return _impl != static_cast(0); } - [[nodiscard]] constexpr E operator*() const noexcept { return get(); } - [[nodiscard]] constexpr E get() const noexcept { return static_cast(_impl); } - [[nodiscard]] constexpr U underlying() const noexcept { return _impl; } - - public: - friend constexpr bool operator==(Enum a_lhs, Enum a_rhs) noexcept { return a_lhs.underlying() == a_rhs.underlying(); } - friend constexpr bool operator==(Enum a_lhs, E a_rhs) noexcept { return a_lhs.underlying() == static_cast(a_rhs); } - friend constexpr bool operator==(E a_lhs, Enum a_rhs) noexcept { return static_cast(a_lhs) == a_rhs.underlying(); } - - private: - U _impl{ 0 }; - }; - template - Enum(Args...) -> Enum< - std::common_type_t, - std::underlying_type_t< - std::common_type_t>>; -} - -namespace REX -{ - template < - class E, - class U = std::underlying_type_t> - class EnumSet - { - public: - using enum_type = E; - using underlying_type = U; - - static_assert(std::is_enum_v, "EnumSet must be an enum"); - static_assert(std::is_integral_v, "EnumSet<..., U> must be an integral"); - - constexpr EnumSet() noexcept = default; - constexpr EnumSet(const EnumSet&) noexcept = default; - constexpr EnumSet(EnumSet&&) noexcept = default; - - template // NOLINTNEXTLINE(google-explicit-constructor) - constexpr EnumSet(EnumSet a_rhs) noexcept : - _impl(static_cast(a_rhs.get())) - {} - - template - constexpr EnumSet(Args... a_values) noexcept - requires(std::same_as&&...) : - _impl((static_cast(a_values) | ...)) - {} - - ~EnumSet() noexcept = default; - - constexpr EnumSet& operator=(const EnumSet&) noexcept = default; - constexpr EnumSet& operator=(EnumSet&&) noexcept = default; - - template - constexpr EnumSet& operator=(EnumSet a_rhs) noexcept - { - _impl = static_cast(a_rhs.get()); - } - - constexpr EnumSet& operator=(E a_value) noexcept - { - _impl = static_cast(a_value); - return *this; - } - - public: - [[nodiscard]] explicit constexpr operator bool() const noexcept { return _impl != static_cast(0); } - - [[nodiscard]] constexpr E operator*() const noexcept { return get(); } - [[nodiscard]] constexpr E get() const noexcept { return static_cast(_impl); } - [[nodiscard]] constexpr U underlying() const noexcept { return _impl; } - - public: - template - constexpr EnumSet& set(Args... a_args) noexcept - requires(std::same_as&&...) - { - _impl |= (static_cast(a_args) | ...); - return *this; - } - - template - constexpr EnumSet& reset(Args... a_args) noexcept - requires(std::same_as&&...) - { - _impl &= ~(static_cast(a_args) | ...); - return *this; - } - - constexpr EnumSet& reset() noexcept - { - _impl = 0; - return *this; - } - - template - [[nodiscard]] constexpr bool any(Args... a_args) const noexcept - requires(std::same_as&&...) - { - return (_impl & (static_cast(a_args) | ...)) != static_cast(0); - } - - template - [[nodiscard]] constexpr bool all(Args... a_args) const noexcept - requires(std::same_as&&...) - { - return (_impl & (static_cast(a_args) | ...)) == (static_cast(a_args) | ...); - } - - template - [[nodiscard]] constexpr bool none(Args... a_args) const noexcept - requires(std::same_as&&...) - { - return (_impl & (static_cast(a_args) | ...)) == static_cast(0); - } - - public: - friend constexpr bool operator==(EnumSet a_lhs, EnumSet a_rhs) noexcept { return a_lhs.underlying() == a_rhs.underlying(); } - friend constexpr bool operator==(EnumSet a_lhs, E a_rhs) noexcept { return a_lhs.underlying() == static_cast(a_rhs); } - friend constexpr bool operator==(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs) == a_rhs.underlying(); } - - friend constexpr std::strong_ordering operator<=>(EnumSet a_lhs, EnumSet a_rhs) noexcept { return a_lhs.underlying() <=> a_rhs.underlying(); } - friend constexpr std::strong_ordering operator<=>(EnumSet a_lhs, E a_rhs) noexcept { return a_lhs.underlying() <=> static_cast(a_rhs); } - friend constexpr std::strong_ordering operator<=>(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs) <=> a_rhs.underlying(); } - - friend constexpr EnumSet operator&(EnumSet a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs.underlying() & a_rhs.underlying()); } - friend constexpr EnumSet operator&(EnumSet a_lhs, E a_rhs) noexcept { return static_cast(a_lhs.underlying() & static_cast(a_rhs)); } - friend constexpr EnumSet operator&(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(static_cast(a_lhs) & a_rhs.underlying()); } - - friend constexpr EnumSet& operator&=(EnumSet& a_lhs, EnumSet a_rhs) noexcept { return a_lhs = a_lhs & a_rhs; } - friend constexpr EnumSet& operator&=(EnumSet& a_lhs, E a_rhs) noexcept { return a_lhs = a_lhs & a_rhs; } - - friend constexpr EnumSet operator|(EnumSet a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs.underlying() | a_rhs.underlying()); } - friend constexpr EnumSet operator|(EnumSet a_lhs, E a_rhs) noexcept { return static_cast(a_lhs.underlying() | static_cast(a_rhs)); } - friend constexpr EnumSet operator|(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(static_cast(a_lhs) | a_rhs.underlying()); } - - friend constexpr EnumSet& operator|=(EnumSet& a_lhs, EnumSet a_rhs) noexcept { return a_lhs = a_lhs | a_rhs; } - friend constexpr EnumSet& operator|=(EnumSet& a_lhs, E a_rhs) noexcept { return a_lhs = a_lhs | a_rhs; } - - friend constexpr EnumSet operator^(EnumSet a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs.underlying() ^ a_rhs.underlying()); } - friend constexpr EnumSet operator^(EnumSet a_lhs, E a_rhs) noexcept { return static_cast(a_lhs.underlying() ^ static_cast(a_rhs)); } - friend constexpr EnumSet operator^(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(static_cast(a_lhs) ^ a_rhs.underlying()); } - - friend constexpr EnumSet& operator^=(EnumSet& a_lhs, EnumSet a_rhs) noexcept { return a_lhs = a_lhs ^ a_rhs; } - friend constexpr EnumSet& operator^=(EnumSet& a_lhs, E a_rhs) noexcept { return a_lhs = a_lhs ^ a_rhs; } - - friend constexpr EnumSet operator+(EnumSet a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs.underlying() + a_rhs.underlying()); } - friend constexpr EnumSet operator+(EnumSet a_lhs, E a_rhs) noexcept { return static_cast(a_lhs.underlying() + static_cast(a_rhs)); } - friend constexpr EnumSet operator+(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(static_cast(a_lhs) + a_rhs.underlying()); } - - friend constexpr EnumSet& operator+=(EnumSet& a_lhs, EnumSet a_rhs) noexcept { return a_lhs = a_lhs + a_rhs; } - friend constexpr EnumSet& operator+=(EnumSet& a_lhs, E a_rhs) noexcept { return a_lhs = a_lhs + a_rhs; } - - friend constexpr EnumSet operator-(EnumSet a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs.underlying() - a_rhs.underlying()); } - friend constexpr EnumSet operator-(EnumSet a_lhs, E a_rhs) noexcept { return static_cast(a_lhs.underlying() - static_cast(a_rhs)); } - friend constexpr EnumSet operator-(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(static_cast(a_lhs) - a_rhs.underlying()); } - - friend constexpr EnumSet& operator-=(EnumSet& a_lhs, EnumSet a_rhs) noexcept { return a_lhs = a_lhs - a_rhs; } - friend constexpr EnumSet& operator-=(EnumSet& a_lhs, E a_rhs) noexcept { return a_lhs = a_lhs - a_rhs; } - - friend constexpr EnumSet operator<<(EnumSet a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs.underlying() << a_rhs.underlying()); } - friend constexpr EnumSet operator<<(EnumSet a_lhs, E a_rhs) noexcept { return static_cast(a_lhs.underlying() << static_cast(a_rhs)); } - friend constexpr EnumSet operator<<(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(static_cast(a_lhs) << a_rhs.underlying()); } - - friend constexpr EnumSet& operator<<=(EnumSet& a_lhs, EnumSet a_rhs) noexcept { return a_lhs = a_lhs << a_rhs; } - friend constexpr EnumSet& operator<<=(EnumSet& a_lhs, E a_rhs) noexcept { return a_lhs = a_lhs << a_rhs; } - - friend constexpr EnumSet operator>>(EnumSet a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs.underlying() >> a_rhs.underlying()); } - friend constexpr EnumSet operator>>(EnumSet a_lhs, E a_rhs) noexcept { return static_cast(a_lhs.underlying() >> static_cast(a_rhs)); } - friend constexpr EnumSet operator>>(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(static_cast(a_lhs) >> a_rhs.underlying()); } - - friend constexpr EnumSet& operator>>=(EnumSet& a_lhs, EnumSet a_rhs) noexcept { return a_lhs = a_lhs >> a_rhs; } - friend constexpr EnumSet& operator>>=(EnumSet& a_lhs, E a_rhs) noexcept { return a_lhs = a_lhs >> a_rhs; } - - friend constexpr EnumSet& operator~(EnumSet& a_lhs) noexcept { return a_lhs = ~a_lhs.underlying(); } - - private: - U _impl{ 0 }; - }; - - template - EnumSet(Args...) -> EnumSet< - std::common_type_t, - std::underlying_type_t< - std::common_type_t>>; -} - -namespace REX -{ - template - class Singleton - { - public: - static T* GetSingleton() - { - static T singleton; - return std::addressof(singleton); - } - - protected: - Singleton() = default; - ~Singleton() = default; - - Singleton(const Singleton&) = delete; - Singleton(Singleton&&) = delete; - - Singleton& operator=(const Singleton&) = delete; - Singleton& operator=(Singleton&&) = delete; - }; -} +#include "REX/REX/Enum.h" +#include "REX/REX/EnumSet.h" +#include "REX/REX/INI.h" +#include "REX/REX/JSON.h" +#include "REX/REX/Setting.h" +#include "REX/REX/Singleton.h" +#include "REX/REX/TOML.h" diff --git a/CommonLibF4/include/REX/REX/Enum.h b/CommonLibF4/include/REX/REX/Enum.h new file mode 100644 index 00000000..cc048275 --- /dev/null +++ b/CommonLibF4/include/REX/REX/Enum.h @@ -0,0 +1,68 @@ +#pragma once + +namespace REX +{ + template < + class E, + class U = std::underlying_type_t> + class Enum + { + public: + using enum_type = E; + using underlying_type = U; + + static_assert(std::is_enum_v, "Enum must be an enum"); + static_assert(std::is_integral_v, "Enum<..., U> must be an integral"); + + constexpr Enum() noexcept = default; + constexpr Enum(const Enum&) noexcept = default; + constexpr Enum(Enum&&) noexcept = default; + + template // NOLINTNEXTLINE(google-explicit-constructor) + constexpr Enum(Enum a_rhs) noexcept : + _impl(static_cast(a_rhs.get())) + {} + + constexpr Enum(E a_value) noexcept : + _impl(static_cast(a_value)) + {} + + ~Enum() noexcept = default; + + constexpr Enum& operator=(const Enum&) noexcept = default; + constexpr Enum& operator=(Enum&&) noexcept = default; + + template + constexpr Enum& operator=(Enum a_rhs) noexcept + { + _impl = static_cast(a_rhs.get()); + } + + constexpr Enum& operator=(E a_value) noexcept + { + _impl = static_cast(a_value); + return *this; + } + + public: + [[nodiscard]] explicit constexpr operator bool() const noexcept { return _impl != static_cast(0); } + + [[nodiscard]] constexpr E operator*() const noexcept { return get(); } + [[nodiscard]] constexpr E get() const noexcept { return static_cast(_impl); } + [[nodiscard]] constexpr U underlying() const noexcept { return _impl; } + + public: + friend constexpr bool operator==(Enum a_lhs, Enum a_rhs) noexcept { return a_lhs.underlying() == a_rhs.underlying(); } + friend constexpr bool operator==(Enum a_lhs, E a_rhs) noexcept { return a_lhs.underlying() == static_cast(a_rhs); } + friend constexpr bool operator==(E a_lhs, Enum a_rhs) noexcept { return static_cast(a_lhs) == a_rhs.underlying(); } + + private: + U _impl{ 0 }; + }; + + template + Enum(Args...) -> Enum< + std::common_type_t, + std::underlying_type_t< + std::common_type_t>>; +} diff --git a/CommonLibF4/include/REX/REX/EnumSet.h b/CommonLibF4/include/REX/REX/EnumSet.h new file mode 100644 index 00000000..2bae12f3 --- /dev/null +++ b/CommonLibF4/include/REX/REX/EnumSet.h @@ -0,0 +1,181 @@ +#pragma once + +namespace REX +{ + template < + class E, + class U = std::underlying_type_t> + class EnumSet + { + public: + using enum_type = E; + using underlying_type = U; + + static_assert(std::is_enum_v, "EnumSet must be an enum"); + static_assert(std::is_integral_v, "EnumSet<..., U> must be an integral"); + + constexpr EnumSet() noexcept = default; + constexpr EnumSet(const EnumSet&) noexcept = default; + constexpr EnumSet(EnumSet&&) noexcept = default; + + template // NOLINTNEXTLINE(google-explicit-constructor) + constexpr EnumSet(EnumSet a_rhs) noexcept : + _impl(static_cast(a_rhs.get())) + {} + + template + constexpr EnumSet(Args... a_values) noexcept + requires(std::same_as&&...) : + _impl((static_cast(a_values) | ...)) + {} + + ~EnumSet() noexcept = default; + + constexpr EnumSet& operator=(const EnumSet&) noexcept = default; + constexpr EnumSet& operator=(EnumSet&&) noexcept = default; + + template + constexpr EnumSet& operator=(EnumSet a_rhs) noexcept + { + _impl = static_cast(a_rhs.get()); + } + + constexpr EnumSet& operator=(E a_value) noexcept + { + _impl = static_cast(a_value); + return *this; + } + + public: + [[nodiscard]] explicit constexpr operator bool() const noexcept { return _impl != static_cast(0); } + + [[nodiscard]] constexpr E operator*() const noexcept { return get(); } + [[nodiscard]] constexpr E get() const noexcept { return static_cast(_impl); } + [[nodiscard]] constexpr U underlying() const noexcept { return _impl; } + + public: + template + constexpr EnumSet& set(Args... a_args) noexcept + requires(std::same_as&&...) + { + _impl |= (static_cast(a_args) | ...); + return *this; + } + + template + constexpr EnumSet& set(bool a_set, Args... a_args) noexcept + requires(std::same_as&&...) + { + if (a_set) + _impl |= (static_cast(a_args) | ...); + else + _impl &= ~(static_cast(a_args) | ...); + + return *this; + } + + template + constexpr EnumSet& reset(Args... a_args) noexcept + requires(std::same_as&&...) + { + _impl &= ~(static_cast(a_args) | ...); + return *this; + } + + constexpr EnumSet& reset() noexcept + { + _impl = 0; + return *this; + } + + template + [[nodiscard]] constexpr bool any(Args... a_args) const noexcept + requires(std::same_as&&...) + { + return (_impl & (static_cast(a_args) | ...)) != static_cast(0); + } + + template + [[nodiscard]] constexpr bool all(Args... a_args) const noexcept + requires(std::same_as&&...) + { + return (_impl & (static_cast(a_args) | ...)) == (static_cast(a_args) | ...); + } + + template + [[nodiscard]] constexpr bool none(Args... a_args) const noexcept + requires(std::same_as&&...) + { + return (_impl & (static_cast(a_args) | ...)) == static_cast(0); + } + + public: + friend constexpr bool operator==(EnumSet a_lhs, EnumSet a_rhs) noexcept { return a_lhs.underlying() == a_rhs.underlying(); } + friend constexpr bool operator==(EnumSet a_lhs, E a_rhs) noexcept { return a_lhs.underlying() == static_cast(a_rhs); } + friend constexpr bool operator==(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs) == a_rhs.underlying(); } + + friend constexpr std::strong_ordering operator<=>(EnumSet a_lhs, EnumSet a_rhs) noexcept { return a_lhs.underlying() <=> a_rhs.underlying(); } + friend constexpr std::strong_ordering operator<=>(EnumSet a_lhs, E a_rhs) noexcept { return a_lhs.underlying() <=> static_cast(a_rhs); } + friend constexpr std::strong_ordering operator<=>(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs) <=> a_rhs.underlying(); } + + friend constexpr EnumSet operator&(EnumSet a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs.underlying() & a_rhs.underlying()); } + friend constexpr EnumSet operator&(EnumSet a_lhs, E a_rhs) noexcept { return static_cast(a_lhs.underlying() & static_cast(a_rhs)); } + friend constexpr EnumSet operator&(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(static_cast(a_lhs) & a_rhs.underlying()); } + + friend constexpr EnumSet& operator&=(EnumSet& a_lhs, EnumSet a_rhs) noexcept { return a_lhs = a_lhs & a_rhs; } + friend constexpr EnumSet& operator&=(EnumSet& a_lhs, E a_rhs) noexcept { return a_lhs = a_lhs & a_rhs; } + + friend constexpr EnumSet operator|(EnumSet a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs.underlying() | a_rhs.underlying()); } + friend constexpr EnumSet operator|(EnumSet a_lhs, E a_rhs) noexcept { return static_cast(a_lhs.underlying() | static_cast(a_rhs)); } + friend constexpr EnumSet operator|(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(static_cast(a_lhs) | a_rhs.underlying()); } + + friend constexpr EnumSet& operator|=(EnumSet& a_lhs, EnumSet a_rhs) noexcept { return a_lhs = a_lhs | a_rhs; } + friend constexpr EnumSet& operator|=(EnumSet& a_lhs, E a_rhs) noexcept { return a_lhs = a_lhs | a_rhs; } + + friend constexpr EnumSet operator^(EnumSet a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs.underlying() ^ a_rhs.underlying()); } + friend constexpr EnumSet operator^(EnumSet a_lhs, E a_rhs) noexcept { return static_cast(a_lhs.underlying() ^ static_cast(a_rhs)); } + friend constexpr EnumSet operator^(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(static_cast(a_lhs) ^ a_rhs.underlying()); } + + friend constexpr EnumSet& operator^=(EnumSet& a_lhs, EnumSet a_rhs) noexcept { return a_lhs = a_lhs ^ a_rhs; } + friend constexpr EnumSet& operator^=(EnumSet& a_lhs, E a_rhs) noexcept { return a_lhs = a_lhs ^ a_rhs; } + + friend constexpr EnumSet operator+(EnumSet a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs.underlying() + a_rhs.underlying()); } + friend constexpr EnumSet operator+(EnumSet a_lhs, E a_rhs) noexcept { return static_cast(a_lhs.underlying() + static_cast(a_rhs)); } + friend constexpr EnumSet operator+(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(static_cast(a_lhs) + a_rhs.underlying()); } + + friend constexpr EnumSet& operator+=(EnumSet& a_lhs, EnumSet a_rhs) noexcept { return a_lhs = a_lhs + a_rhs; } + friend constexpr EnumSet& operator+=(EnumSet& a_lhs, E a_rhs) noexcept { return a_lhs = a_lhs + a_rhs; } + + friend constexpr EnumSet operator-(EnumSet a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs.underlying() - a_rhs.underlying()); } + friend constexpr EnumSet operator-(EnumSet a_lhs, E a_rhs) noexcept { return static_cast(a_lhs.underlying() - static_cast(a_rhs)); } + friend constexpr EnumSet operator-(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(static_cast(a_lhs) - a_rhs.underlying()); } + + friend constexpr EnumSet& operator-=(EnumSet& a_lhs, EnumSet a_rhs) noexcept { return a_lhs = a_lhs - a_rhs; } + friend constexpr EnumSet& operator-=(EnumSet& a_lhs, E a_rhs) noexcept { return a_lhs = a_lhs - a_rhs; } + + friend constexpr EnumSet operator<<(EnumSet a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs.underlying() << a_rhs.underlying()); } + friend constexpr EnumSet operator<<(EnumSet a_lhs, E a_rhs) noexcept { return static_cast(a_lhs.underlying() << static_cast(a_rhs)); } + friend constexpr EnumSet operator<<(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(static_cast(a_lhs) << a_rhs.underlying()); } + + friend constexpr EnumSet& operator<<=(EnumSet& a_lhs, EnumSet a_rhs) noexcept { return a_lhs = a_lhs << a_rhs; } + friend constexpr EnumSet& operator<<=(EnumSet& a_lhs, E a_rhs) noexcept { return a_lhs = a_lhs << a_rhs; } + + friend constexpr EnumSet operator>>(EnumSet a_lhs, EnumSet a_rhs) noexcept { return static_cast(a_lhs.underlying() >> a_rhs.underlying()); } + friend constexpr EnumSet operator>>(EnumSet a_lhs, E a_rhs) noexcept { return static_cast(a_lhs.underlying() >> static_cast(a_rhs)); } + friend constexpr EnumSet operator>>(E a_lhs, EnumSet a_rhs) noexcept { return static_cast(static_cast(a_lhs) >> a_rhs.underlying()); } + + friend constexpr EnumSet& operator>>=(EnumSet& a_lhs, EnumSet a_rhs) noexcept { return a_lhs = a_lhs >> a_rhs; } + friend constexpr EnumSet& operator>>=(EnumSet& a_lhs, E a_rhs) noexcept { return a_lhs = a_lhs >> a_rhs; } + + friend constexpr EnumSet& operator~(EnumSet& a_lhs) noexcept { return a_lhs = ~a_lhs.underlying(); } + + private: + U _impl{ 0 }; + }; + + template + EnumSet(Args...) -> EnumSet< + std::common_type_t, + std::underlying_type_t< + std::common_type_t>>; +} diff --git a/CommonLibF4/include/REX/REX/INI.h b/CommonLibF4/include/REX/REX/INI.h new file mode 100644 index 00000000..315dfb89 --- /dev/null +++ b/CommonLibF4/include/REX/REX/INI.h @@ -0,0 +1,100 @@ +#pragma once + +#include "REX/REX/Setting.h" + +#ifdef REX_OPTION_INI +namespace REX::INI +{ + using sec_t = std::string_view; + using key_t = std::string_view; + + namespace Impl + { + template + void SettingLoad(void* a_file, sec_t a_section, key_t a_key, T& a_value, T& a_valueDefault); + + template + void SettingSave(void* a_file, sec_t a_section, key_t a_key, T& a_value); + } + + class SettingStore : + public TSettingStore + { + public: + virtual void Load() override; + virtual void Save() override; + }; + + template + class Setting : + public TSetting + { + public: + Setting(sec_t a_section, key_t a_key, T a_default) : + TSetting(a_default), + m_section(a_section), + m_key(a_key) + {} + + public: + virtual void Load(void* a_data, bool a_isBase) override + { + if (a_isBase) { + Impl::SettingLoad(a_data, m_section, m_key, this->m_valueDefault, this->m_valueDefault); + this->SetValue(this->m_valueDefault); + } else { + Impl::SettingLoad(a_data, m_section, m_key, this->m_value, this->m_valueDefault); + } + } + + virtual void Save(void* a_data) override + { + Impl::SettingSave(a_data, m_section, m_key, this->m_value); + } + + private: + sec_t m_section; + key_t m_key; + }; + + template + using Bool = Setting; + + template + using F32 = Setting; + + template + using F64 = Setting; + + template + using I8 = Setting; + + template + using I16 = Setting; + + template + using I32 = Setting; + + template + using U8 = Setting; + + template + using U16 = Setting; + + template + using U32 = Setting; + + template + using Str = Setting; +} + +template +struct std::formatter, CharT> : std::formatter +{ + template + auto format(const REX::INI::Setting& a_setting, FormatContext& a_ctx) const + { + return std::formatter::format(a_setting.GetValue(), a_ctx); + } +}; +#endif diff --git a/CommonLibF4/include/REX/REX/JSON.h b/CommonLibF4/include/REX/REX/JSON.h new file mode 100644 index 00000000..14b2e4ae --- /dev/null +++ b/CommonLibF4/include/REX/REX/JSON.h @@ -0,0 +1,97 @@ +#pragma once + +#include "REX/REX/Setting.h" + +#ifdef REX_OPTION_JSON +namespace REX::JSON +{ + using path_t = std::string_view; + + namespace Impl + { + template + void SettingLoad(void* a_file, path_t a_path, T& a_value, T& a_valueDefault); + + template + void SettingSave(void* a_file, path_t a_path, T& a_value); + } + + class SettingStore : + public TSettingStore + { + public: + virtual void Load() override; + virtual void Save() override; + }; + + template + class Setting : + public TSetting + { + public: + Setting(path_t a_path, T a_default) : + TSetting(a_default), + m_path(a_path) + {} + + public: + virtual void Load(void* a_data, bool a_isBase) override + { + if (a_isBase) { + Impl::SettingLoad(a_data, m_path, this->m_valueDefault, this->m_valueDefault); + this->SetValue(this->m_valueDefault); + } else { + Impl::SettingLoad(a_data, m_path, this->m_value, this->m_valueDefault); + } + } + + virtual void Save(void* a_data) override + { + Impl::SettingSave(a_data, m_path, this->m_value); + } + + private: + path_t m_path; + }; + + template + using Bool = Setting; + + template + using F32 = Setting; + + template + using F64 = Setting; + + template + using I8 = Setting; + + template + using I16 = Setting; + + template + using I32 = Setting; + + template + using U8 = Setting; + + template + using U16 = Setting; + + template + using U32 = Setting; + + template + using Str = Setting; +} + +template +struct std::formatter, CharT> : std::formatter +{ + template + auto format(const REX::JSON::Setting& a_setting, FormatContext& a_ctx) const + { + return std::formatter::format(a_setting.GetValue(), a_ctx); + } +}; +#endif diff --git a/CommonLibF4/include/REX/REX/Setting.h b/CommonLibF4/include/REX/REX/Setting.h new file mode 100644 index 00000000..5d26eb57 --- /dev/null +++ b/CommonLibF4/include/REX/REX/Setting.h @@ -0,0 +1,71 @@ +#pragma once + +#include "REX/REX/Singleton.h" + +namespace REX +{ + class ISetting + { + public: + virtual void Load(void* a_data, bool a_isBase) = 0; + virtual void Save(void* a_data) = 0; + }; + + class ISettingStore + { + public: + virtual void Init(const char* a_file, const char* a_fileCustom) = 0; + virtual void Load() = 0; + virtual void Save() = 0; + virtual void Register(ISetting* a_setting) = 0; + }; + + template + class TSetting : + public ISetting + { + public: + TSetting() = delete; + + TSetting(T a_default) : + m_value(a_default), + m_valueDefault(a_default) + { + Store::GetSingleton()->Register(this); + } + + public: + T GetValue() const { return m_value; } + + T GetValueDefault() const { return m_valueDefault; } + + void SetValue(T a_value) { m_value = a_value; } + + protected: + T m_value; + T m_valueDefault; + }; + + template + class TSettingStore : + public ISettingStore, + public Singleton + { + public: + virtual void Init(const char* a_fileBase, const char* a_fileUser) override + { + m_fileBase = a_fileBase; + m_fileUser = a_fileUser; + } + + virtual void Register(ISetting* a_setting) override + { + m_settings.emplace_back(a_setting); + } + + protected: + std::string_view m_fileBase; + std::string_view m_fileUser; + std::vector m_settings; + }; +} diff --git a/CommonLibF4/include/REX/REX/Singleton.h b/CommonLibF4/include/REX/REX/Singleton.h new file mode 100644 index 00000000..836ddb1e --- /dev/null +++ b/CommonLibF4/include/REX/REX/Singleton.h @@ -0,0 +1,25 @@ +#pragma once + +namespace REX +{ + template + class Singleton + { + public: + static T* GetSingleton() + { + static T singleton; + return std::addressof(singleton); + } + + protected: + Singleton() = default; + ~Singleton() = default; + + Singleton(const Singleton&) = delete; + Singleton(Singleton&&) = delete; + + Singleton& operator=(const Singleton&) = delete; + Singleton& operator=(Singleton&&) = delete; + }; +} diff --git a/CommonLibF4/include/REX/REX/TOML.h b/CommonLibF4/include/REX/REX/TOML.h new file mode 100644 index 00000000..8516479f --- /dev/null +++ b/CommonLibF4/include/REX/REX/TOML.h @@ -0,0 +1,116 @@ +#pragma once + +#include "REX/REX/Setting.h" + +#ifdef REX_OPTION_TOML +namespace REX::TOML +{ + using sec_t = std::vector; + using key_t = std::string_view; + + namespace Impl + { + template + void SettingLoad(void* a_file, sec_t a_section, key_t a_key, T& a_value, T& a_valueDefault); + + template + void SettingSave(void* a_file, sec_t a_section, key_t a_key, T& a_value); + } + + class SettingStore : + public TSettingStore + { + public: + virtual void Load() override; + virtual void Save() override; + }; + + template + class Setting : + public TSetting + { + public: + Setting(key_t a_key, T a_default) : + TSetting(a_default), + m_section(), + m_key(a_key) + {} + + Setting(std::string_view a_section, key_t a_key, T a_default) : + TSetting(a_default), + m_section(), + m_key(a_key) + { + for (const auto token : std::ranges::split_view{ a_section, std::string_view{ "." } }) { + m_section.emplace_back(token.data(), token.size()); + } + } + + Setting(std::initializer_list a_section, key_t a_key, T a_default) : + TSetting(a_default), + m_section(a_section), + m_key(a_key) + {} + + public: + virtual void Load(void* a_data, bool a_isBase) override + { + if (a_isBase) { + Impl::SettingLoad(a_data, m_section, m_key, this->m_valueDefault, this->m_valueDefault); + this->SetValue(this->m_valueDefault); + } else { + Impl::SettingLoad(a_data, m_section, m_key, this->m_value, this->m_valueDefault); + } + } + + virtual void Save(void* a_data) override + { + Impl::SettingSave(a_data, m_section, m_key, this->m_value); + } + + private: + sec_t m_section; + key_t m_key; + }; + + template + using Bool = Setting; + + template + using F32 = Setting; + + template + using F64 = Setting; + + template + using I8 = Setting; + + template + using I16 = Setting; + + template + using I32 = Setting; + + template + using U8 = Setting; + + template + using U16 = Setting; + + template + using U32 = Setting; + + template + using Str = Setting; +} + +template +struct std::formatter, CharT> : std::formatter +{ + template + auto format(const REX::TOML::Setting& a_setting, FormatContext& a_ctx) const + { + return std::formatter::format(a_setting.GetValue(), a_ctx); + } +}; +#endif diff --git a/CommonLibF4/src/REX/REX.cpp b/CommonLibF4/src/REX/REX.cpp new file mode 100644 index 00000000..9f3c16d4 --- /dev/null +++ b/CommonLibF4/src/REX/REX.cpp @@ -0,0 +1,375 @@ +#include "REX/REX/INI.h" +#include "REX/REX/JSON.h" +#include "REX/REX/TOML.h" + +#include "F4SE/Logger.h" + +#ifdef REX_OPTION_INI +# include + +namespace REX::INI +{ + namespace Impl + { + template + constexpr bool is_long_integral_v = std::_Is_any_of_v, std::uint8_t, std::uint16_t, std::uint32_t, std::int8_t, std::int16_t, std::int32_t>; + + template + void SettingLoad( + void* a_data, + sec_t a_section, + key_t a_key, + T& a_value, + T& a_valueDefault) + { + const auto file = static_cast(a_data); + if constexpr (std::is_same_v) { + a_value = file->GetBoolValue(a_section.data(), a_key.data(), a_valueDefault); + } else if constexpr (std::is_floating_point_v) { + a_value = static_cast(file->GetDoubleValue(a_section.data(), a_key.data(), a_valueDefault)); + } else if constexpr (is_long_integral_v) { + a_value = static_cast(file->GetLongValue(a_section.data(), a_key.data(), a_valueDefault)); + } else if constexpr (std::is_same_v) { + a_value = file->GetValue(a_section.data(), a_key.data(), a_valueDefault.c_str()); + } + } + + template void SettingLoad(void*, sec_t, key_t, bool&, bool&); + template void SettingLoad(void*, sec_t, key_t, float&, float&); + template void SettingLoad(void*, sec_t, key_t, double&, double&); + template void SettingLoad(void*, sec_t, key_t, std::uint8_t&, std::uint8_t&); + template void SettingLoad(void*, sec_t, key_t, std::uint16_t&, std::uint16_t&); + template void SettingLoad(void*, sec_t, key_t, std::uint32_t&, std::uint32_t&); + template void SettingLoad(void*, sec_t, key_t, std::int8_t&, std::int8_t&); + template void SettingLoad(void*, sec_t, key_t, std::int16_t&, std::int16_t&); + template void SettingLoad(void*, sec_t, key_t, std::int32_t&, std::int32_t&); + template void SettingLoad(void*, sec_t, key_t, std::string&, std::string&); + + template + void SettingSave( + void* a_data, + sec_t a_section, + key_t a_key, + T& a_value) + { + auto& file = *static_cast(a_data); + if constexpr (std::is_same_v) { + file.SetBoolValue(a_section.data(), a_key.data(), a_value); + } else if constexpr (std::is_floating_point_v) { + file.SetDoubleValue(a_section.data(), a_key.data(), a_value); + } else if constexpr (is_long_integral_v) { + file.SetLongValue(a_section.data(), a_key.data(), a_value); + } else if constexpr (std::is_same_v) { + file.SetValue(a_section.data(), a_key.data(), a_value.c_str()); + } + } + + template void SettingSave(void*, sec_t, key_t, bool&); + template void SettingSave(void*, sec_t, key_t, float&); + template void SettingSave(void*, sec_t, key_t, double&); + template void SettingSave(void*, sec_t, key_t, std::uint8_t&); + template void SettingSave(void*, sec_t, key_t, std::uint16_t&); + template void SettingSave(void*, sec_t, key_t, std::uint32_t&); + template void SettingSave(void*, sec_t, key_t, std::int8_t&); + template void SettingSave(void*, sec_t, key_t, std::int16_t&); + template void SettingSave(void*, sec_t, key_t, std::int32_t&); + template void SettingSave(void*, sec_t, key_t, std::string&); + } + + void SettingStore::Load() + { + CSimpleIniA file; + file.SetUnicode(true); + file.SetQuotes(true); + + if (std::filesystem::exists(m_fileBase)) { + file.LoadFile(m_fileBase.data()); + for (auto& setting : m_settings) { + setting->Load(&file, true); + } + } + + if (std::filesystem::exists(m_fileUser)) { + file.LoadFile(m_fileUser.data()); + for (auto& setting : m_settings) { + setting->Load(&file, false); + } + } + } + + void SettingStore::Save() + { + CSimpleIniA file; + file.SetUnicode(true); + file.SetQuotes(true); + + file.LoadFile(m_fileBase.data()); + for (auto& setting : m_settings) { + setting->Save(&file); + } + + file.SaveFile(m_fileBase.data()); + } +} +#endif + +#ifdef REX_OPTION_JSON +# include + +namespace REX::JSON +{ + namespace Impl + { + template + void SettingLoad( + void* a_data, + path_t a_path, + T& a_value, + T& a_valueDefault) + { + const auto& json = *static_cast(a_data); + if (a_path[0] == '/') { + a_value = json.value(nlohmann::json::json_pointer(a_path.data()), a_valueDefault); + } else { + a_value = json.value(a_path, a_valueDefault); + } + } + + template void SettingLoad(void*, path_t, bool&, bool&); + template void SettingLoad(void*, path_t, float&, float&); + template void SettingLoad(void*, path_t, double&, double&); + template void SettingLoad(void*, path_t, std::uint8_t&, std::uint8_t&); + template void SettingLoad(void*, path_t, std::uint16_t&, std::uint16_t&); + template void SettingLoad(void*, path_t, std::uint32_t&, std::uint32_t&); + template void SettingLoad(void*, path_t, std::int8_t&, std::int8_t&); + template void SettingLoad(void*, path_t, std::int16_t&, std::int16_t&); + template void SettingLoad(void*, path_t, std::int32_t&, std::int32_t&); + template void SettingLoad(void*, path_t, std::string&, std::string&); + + template + void SettingSave( + void* a_data, + path_t a_path, + T& a_value) + { + auto& json = *static_cast(a_data); + if (a_path[0] == '/') { + json[nlohmann::json::json_pointer(a_path.data())] = a_value; + } else { + json[a_path] = a_value; + } + } + + template void SettingSave(void*, path_t, bool&); + template void SettingSave(void*, path_t, float&); + template void SettingSave(void*, path_t, double&); + template void SettingSave(void*, path_t, std::uint8_t&); + template void SettingSave(void*, path_t, std::uint16_t&); + template void SettingSave(void*, path_t, std::uint32_t&); + template void SettingSave(void*, path_t, std::int8_t&); + template void SettingSave(void*, path_t, std::int16_t&); + template void SettingSave(void*, path_t, std::int32_t&); + template void SettingSave(void*, path_t, std::string&); + } + + void SettingStore::Load() + { + if (std::filesystem::exists(m_fileBase)) { + std::ifstream file{ m_fileBase.data() }; + try { + auto result = nlohmann::json::parse(file); + for (auto setting : m_settings) { + setting->Load(&result, true); + } + } catch (const std::exception& e) { + F4SE::log::error("{}", e.what()); + } + } + + if (std::filesystem::exists(m_fileUser)) { + std::ifstream file{ m_fileUser.data() }; + try { + auto result = nlohmann::json::parse(file); + for (auto setting : m_settings) { + setting->Load(&result, false); + } + } catch (const std::exception& e) { + F4SE::log::error("{}", e.what()); + } + } + } + + void SettingStore::Save() + { + nlohmann::json output{}; + if (std::filesystem::exists(m_fileBase)) { + std::ifstream file{ m_fileBase.data() }; + output = nlohmann::json::parse(file); + } + + for (auto& setting : m_settings) { + setting->Save(&output); + } + + std::ofstream file{ m_fileBase.data(), std::ios::trunc }; + file << std::setw(4) << output; + } +} +#endif + +#ifdef REX_OPTION_TOML +# include + +namespace REX::TOML +{ + namespace Impl + { + static toml::value* recurse_table(toml::value* a_result, toml::value& a_value, const std::string& a_section, bool a_create) + { + if (a_result && a_result->is_table()) { + for (auto& value : a_result->as_table()) { + if (value.first == a_section) { + return std::addressof(value.second); + } + } + if (a_create) { + (*a_result)[a_section] = toml::table{}; + return std::addressof((*a_result)[a_section]); + } + } else if (a_value.is_table()) { + for (auto& value : a_value.as_table()) { + if (value.first == a_section) { + return std::addressof(value.second); + } + } + if (a_create) { + a_value[a_section] = toml::table{}; + return std::addressof(a_value[a_section]); + } + } + return a_result; + } + + static bool recurse_mark_implicit(toml::value& a_value) + { + for (auto& kv : a_value.as_table()) { + if (kv.second.is_table()) { + if (!recurse_mark_implicit(kv.second)) { + continue; + } + kv.second.as_table_fmt().fmt = toml::table_format::implicit; + } else { + return false; + } + } + return true; + } + + template + void SettingLoad( + void* a_data, + sec_t a_section, + key_t a_key, + T& a_value, + T& a_valueDefault) + { + const auto& data = static_cast(a_data); + if (a_section.empty()) { + auto& path = (*data); + a_value = toml::find_or(path, a_key.data(), a_valueDefault); + return; + } else if (a_section.size() == 1) { + auto& path = (*data)[a_section.front()]; + a_value = toml::find_or(path, a_key.data(), a_valueDefault); + return; + } else { + toml::value* path{ nullptr }; + for (auto& section : a_section) { + path = recurse_table(path, *data, section, false); + } + if (path) { + a_value = toml::find_or(*path, a_key.data(), a_valueDefault); + return; + } + } + a_value = a_valueDefault; + } + + template void SettingLoad(void*, sec_t, key_t, bool&, bool&); + template void SettingLoad(void*, sec_t, key_t, float&, float&); + template void SettingLoad(void*, sec_t, key_t, double&, double&); + template void SettingLoad(void*, sec_t, key_t, std::uint8_t&, std::uint8_t&); + template void SettingLoad(void*, sec_t, key_t, std::uint16_t&, std::uint16_t&); + template void SettingLoad(void*, sec_t, key_t, std::uint32_t&, std::uint32_t&); + template void SettingLoad(void*, sec_t, key_t, std::int8_t&, std::int8_t&); + template void SettingLoad(void*, sec_t, key_t, std::int16_t&, std::int16_t&); + template void SettingLoad(void*, sec_t, key_t, std::int32_t&, std::int32_t&); + template void SettingLoad(void*, sec_t, key_t, std::string&, std::string&); + + template + void SettingSave( + void* a_data, + sec_t a_section, + key_t a_key, + T& a_value) + { + auto& data = *static_cast(a_data); + if (a_section.empty()) { + data[a_key.data()] = a_value; + } else if (a_section.size() == 1) { + data[a_section.front()][a_key.data()] = a_value; + } else { + toml::value* path{ nullptr }; + for (auto& section : a_section) { + path = recurse_table(path, data, section, true); + } + if (path) { + (*path)[a_key.data()] = a_value; + } + } + } + + template void SettingSave(void*, sec_t, key_t, bool&); + template void SettingSave(void*, sec_t, key_t, float&); + template void SettingSave(void*, sec_t, key_t, double&); + template void SettingSave(void*, sec_t, key_t, std::uint8_t&); + template void SettingSave(void*, sec_t, key_t, std::uint16_t&); + template void SettingSave(void*, sec_t, key_t, std::uint32_t&); + template void SettingSave(void*, sec_t, key_t, std::int8_t&); + template void SettingSave(void*, sec_t, key_t, std::int16_t&); + template void SettingSave(void*, sec_t, key_t, std::int32_t&); + template void SettingSave(void*, sec_t, key_t, std::string&); + } + + void SettingStore::Load() + { + if (auto result = toml::try_parse(m_fileBase.data()); result.is_ok()) { + for (auto& setting : m_settings) { + setting->Load(&result.unwrap(), true); + } + } + + if (auto result = toml::try_parse(m_fileUser.data()); result.is_ok()) { + for (auto& setting : m_settings) { + setting->Load(&result.unwrap(), false); + } + } + } + + void SettingStore::Save() + { + toml::value output{}; + if (auto result = toml::try_parse(m_fileBase.data()); result.is_ok()) { + output = result.unwrap(); + } + + for (auto setting : m_settings) { + setting->Save(&output); + } + + Impl::recurse_mark_implicit(output); + std::ofstream file{ m_fileBase.data(), std::ios::trunc }; + file << toml::format(output); + } +} +#endif diff --git a/CommonLibF4/xmake.lua b/CommonLibF4/xmake.lua index c262d388..7bb2da3e 100644 --- a/CommonLibF4/xmake.lua +++ b/CommonLibF4/xmake.lua @@ -5,11 +5,41 @@ option("f4se_xbyak", function() add_defines("F4SE_SUPPORT_XBYAK=1") end) +option("rex_ini", function() + set_default(false) + set_description("Enable ini config support for REX") + add_defines("REX_OPTION_INI=1") +end) + +option("rex_json", function() + set_default(false) + set_description("Enable json config support for REX") + add_defines("REX_OPTION_JSON=1") +end) + +option("rex_toml", function() + set_default(false) + set_description("Enable toml config support for REX") + add_defines("REX_OPTION_TOML=1") +end) + -- require packages if has_config("f4se_xbyak") then add_requires("xbyak") end +if has_config("rex_ini") then + add_requires("simpleini") +end + +if has_config("rex_json") then + add_requires("nlohmann_json") +end + +if has_config("rex_toml") then + add_requires("toml11") +end + -- define targets target("commonlibf4", function() set_kind("static") @@ -27,8 +57,20 @@ target("commonlibf4", function() add_packages("xbyak", { public = true }) end + if has_config("rex_ini") then + add_packages("simpleini", { public = true }) + end + + if has_config("rex_json") then + add_packages("nlohmann_json", { public = true }) + end + + if has_config("rex_toml") then + add_packages("toml11", { public = true }) + end + -- add options - add_options("f4se_xbyak", { public = true }) + add_options("f4se_xbyak", "rex_ini", "rex_json", "rex_toml", { public = true }) -- add system links add_syslinks("advapi32", "bcrypt", "d3d11", "d3dcompiler", "dbghelp", "dxgi", "ole32", "shell32", "user32", "version") From 997ce59b8cea6550ba781c6a5f21fc7f1a6d01be Mon Sep 17 00:00:00 2001 From: shad0wshayd3 Date: Sat, 16 Nov 2024 13:13:59 -0700 Subject: [PATCH 7/7] refactor: Relocation::replace_func renamed from write_func, removed xbyak dependency --- CommonLibF4/include/REL/Relocation.h | 41 +++++++++++++++++++++------- CommonLibF4/src/REL/Relocation.cpp | 30 -------------------- 2 files changed, 31 insertions(+), 40 deletions(-) diff --git a/CommonLibF4/include/REL/Relocation.h b/CommonLibF4/include/REL/Relocation.h index cac01367..fed982fb 100644 --- a/CommonLibF4/include/REL/Relocation.h +++ b/CommonLibF4/include/REL/Relocation.h @@ -278,6 +278,37 @@ namespace REL return stl::unrestricted_cast(_impl); } + template + void replace_func(const std::size_t a_count, const std::uintptr_t a_dst) requires(std::same_as) + { +#pragma pack(push, 1) + struct Assembly + { + std::uint8_t jmp; + std::uint8_t modrm; + std::int32_t disp; + std::uint64_t addr; + }; + static_assert(sizeof(Assembly) == 0xE); +#pragma pack(pop) + + Assembly assembly{ + .jmp = static_cast(0xFF), + .modrm = static_cast(0x25), + .disp = static_cast(0), + .addr = static_cast(a_dst), + }; + + safe_fill(address() + O, INT3, a_count); + safe_write(address() + O, &assembly, sizeof(assembly)); + } + + template + void replace_func(const std::size_t a_count, const F a_dst) requires(std::same_as) + { + replace_func(a_count, stl::unrestricted_cast(a_dst)); + } + void write(const void* a_src, std::size_t a_count) requires(std::same_as) { safe_write(address(), a_src, a_count); @@ -329,16 +360,6 @@ namespace REL safe_fill(address(), a_value, a_count); } -#ifdef F4SE_SUPPORT_XBYAK - void write_func(const std::size_t a_count, const std::uintptr_t a_dst) requires(std::same_as); - - template - void write_func(const std::size_t a_count, const F a_dst) requires(std::same_as) - { - write_func(a_count, stl::unrestricted_cast(a_dst)); - } -#endif - template std::uintptr_t write_vfunc(std::size_t a_idx, std::uintptr_t a_newFunc) requires(std::same_as) { diff --git a/CommonLibF4/src/REL/Relocation.cpp b/CommonLibF4/src/REL/Relocation.cpp index 2266b261..4ce831bd 100644 --- a/CommonLibF4/src/REL/Relocation.cpp +++ b/CommonLibF4/src/REL/Relocation.cpp @@ -32,33 +32,3 @@ namespace REL assert(success); } } - -#ifdef F4SE_SUPPORT_XBYAK -# include - -namespace REL -{ - struct write_func_impl : Xbyak::CodeGenerator - { - write_func_impl(std::uintptr_t a_dst) - { - Xbyak::Label dst; - jmp(ptr[rip + dst]); - L(dst); - dq(a_dst); - } - }; - - template - void Relocation::write_func(const std::size_t a_count, const std::uintptr_t a_dst) requires(std::same_as) - { - safe_fill(address(), INT3, a_count); - auto patch = write_func_impl{ a_dst }; - patch.ready(); - assert(patch.getSize() <= a_count); - safe_write(address(), std::span{ patch.getCode(), patch.getSize() }); - } - - template void Relocation::write_func(const std::size_t, const std::uintptr_t); -} -#endif