diff --git a/CMakeLists.txt b/CMakeLists.txt index ed7c50dc..b4f03423 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,9 @@ if(TARGET CommonLibSF) endif() # options if not defined +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) option(SFSE_SUPPORT_XBYAK "Enables trampoline support for xbyak." OFF) option(SFSE_BUILD_TESTS "Builds the tests." OFF) @@ -55,6 +58,9 @@ function(configure_target TARGET_NAME) PUBLIC WINVER=0x0A00 # windows 10, minimum supported version by starfield _WIN32_WINNT=0x0A00 + "$<$:REX_OPTION_INI=1>" + "$<$:REX_OPTION_JSON=1>" + "$<$:REX_OPTION_TOML=1>" $<$:SFSE_SUPPORT_XBYAK=1> WIN32_LEAN_AND_MEAN NOMINMAX diff --git a/include/REX/REX.h b/include/REX/REX.h index c57c8307..43a000af 100644 --- a/include/REX/REX.h +++ b/include/REX/REX.h @@ -1,273 +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& 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>>; -} - -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/include/REX/REX/Enum.h b/include/REX/REX/Enum.h new file mode 100644 index 00000000..cc048275 --- /dev/null +++ b/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/include/REX/REX/EnumSet.h b/include/REX/REX/EnumSet.h new file mode 100644 index 00000000..29670345 --- /dev/null +++ b/include/REX/REX/EnumSet.h @@ -0,0 +1,182 @@ +#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/include/REX/REX/INI.h b/include/REX/REX/INI.h new file mode 100644 index 00000000..315dfb89 --- /dev/null +++ b/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/include/REX/REX/JSON.h b/include/REX/REX/JSON.h new file mode 100644 index 00000000..14b2e4ae --- /dev/null +++ b/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/include/REX/REX/Setting.h b/include/REX/REX/Setting.h new file mode 100644 index 00000000..5d26eb57 --- /dev/null +++ b/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/include/REX/REX/Singleton.h b/include/REX/REX/Singleton.h new file mode 100644 index 00000000..836ddb1e --- /dev/null +++ b/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/include/REX/REX/TOML.h b/include/REX/REX/TOML.h new file mode 100644 index 00000000..8516479f --- /dev/null +++ b/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/include/SFSE/Impl/PCH.h b/include/SFSE/Impl/PCH.h index bf471d9a..fe37c402 100644 --- a/include/SFSE/Impl/PCH.h +++ b/include/SFSE/Impl/PCH.h @@ -106,14 +106,15 @@ 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) #include #pragma warning(pop) -#include "REX/REX.h" -#include "REX/W32/KERNEL32.h" -#include "REX/W32/USER32.h" - #define AsAddress(ptr) std::bit_cast(ptr) #define AsPointer(addr) std::bit_cast(addr) #define stl_assert(cond, ...) \ diff --git a/src/REX/REX.cpp b/src/REX/REX.cpp new file mode 100644 index 00000000..b12b48dc --- /dev/null +++ b/src/REX/REX.cpp @@ -0,0 +1,375 @@ +#include "REX/REX/INI.h" +#include "REX/REX/JSON.h" +#include "REX/REX/TOML.h" + +#include "SFSE/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) { + SFSE::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) { + SFSE::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/xmake.lua b/xmake.lua index ab4d5dbe..3df6bd93 100644 --- a/xmake.lua +++ b/xmake.lua @@ -15,6 +15,24 @@ add_rules("mode.debug", "mode.releasedbg") includes("xmake-extra.lua") -- define options +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) + option("sfse_xbyak", function() set_default(false) set_description("Enable trampoline support for Xbyak") @@ -24,6 +42,18 @@ end) -- require packages add_requires("spdlog", { configs = { header_only = false, std_format = true } }) +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 + if has_config("sfse_xbyak") then add_requires("xbyak") end @@ -39,12 +69,24 @@ target("commonlibsf") -- add packages add_packages("spdlog", { public = true }) + 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 + if has_config("sfse_xbyak") then add_packages("xbyak", { public = true }) end -- add options - add_options("sfse_xbyak", { public = true }) + add_options("rex_ini", "rex_json", "rex_toml", "sfse_xbyak", { public = true }) -- add system links add_syslinks("advapi32", "bcrypt", "dbghelp", "dxgi", "ole32", "shell32", "user32", "version", "ws2_32")