From 9b92ce87f26768d462db7318a0e10257cc2957a9 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Sun, 4 Sep 2022 02:23:29 +0530 Subject: [PATCH 01/19] bump version --- CMakeLists.txt | 4 ++-- extern/CommonLibVR | 2 +- vcpkg | 0 vcpkg.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 vcpkg diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d1b246..2ae13be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.20) set(NAME "po3_BaseObjectSwapper") -set(VERSION 1.6.0) -set(VR_VERSION 4) +set(VERSION 1.7.0) +set(VR_VERSION 1) set(AE_VERSION 1) # ---- Options ---- diff --git a/extern/CommonLibVR b/extern/CommonLibVR index e066684..0fabeda 160000 --- a/extern/CommonLibVR +++ b/extern/CommonLibVR @@ -1 +1 @@ -Subproject commit e0666849ccc88cc9e3dfb660273f2b573a0001de +Subproject commit 0fabedac02c4d97b7d54199098561be8c0c666ef diff --git a/vcpkg b/vcpkg deleted file mode 100644 index e69de29..0000000 diff --git a/vcpkg.json b/vcpkg.json index 8625992..a545009 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,6 @@ { "name": "base-object-swapper", - "version-string": "1.6.0", + "version-string": "1.7.0", "description": "Base Object Swapper", "homepage": "", "license": "MIT", From e9871657811162f66b387941bb866caaf5b4315b Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Sun, 4 Sep 2022 02:32:21 +0530 Subject: [PATCH 02/19] fix rotational transform data not being read --- src/SwapData.cpp | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/SwapData.cpp b/src/SwapData.cpp index f5ee5ed..1afa345 100644 --- a/src/SwapData.cpp +++ b/src/SwapData.cpp @@ -11,7 +11,7 @@ namespace FormSwap }; if (const auto splitNum = string::split(a_str, R"(/)"); splitNum.size() > 1) { - return { get_float(splitNum[0]), get_float(splitNum[1]) }; + return { get_float(splitNum[0]), get_float(splitNum[1]) }; } else { auto num = get_float(splitNum[0]); return { num, num }; @@ -27,18 +27,24 @@ namespace FormSwap return stl::RNG::GetSingleton()->Generate(a_min, a_max); } - static RE::NiPoint3 get_random_value(const RE::NiPoint3& a_min, const RE::NiPoint3& a_max) + static RE::NiPoint3 get_random_value(const std::pair& a_minMax) { - if (a_min == a_max) { - return a_min; + auto& [min, max] = a_minMax; + + if (min == max) { + return min; } - return { get_random_value(a_min.x, a_max.x), get_random_value(a_min.y, a_max.y), get_random_value(a_min.z, a_max.z) }; + return RE::NiPoint3{ + get_random_value(min.x, max.x), + get_random_value(min.y, max.y), + get_random_value(min.z, max.z) + }; } } Transform::relData Transform::get_transform_from_string(const std::string& a_str) - { + { bool relative = a_str.contains('R'); minMax transformData; @@ -61,7 +67,7 @@ namespace FormSwap } std::optional> Transform::get_scale_from_string(const std::string& a_str) - { + { minMax scale{ 1.0f, 1.0f }; srell::cmatch match; @@ -91,14 +97,20 @@ namespace FormSwap void Transform::SetTransform(RE::TESObjectREFR* a_refr) { if (location) { - auto [relative, minMax] = *location; - auto [min, max] = minMax; - a_refr->data.location = relative ? a_refr->data.location + detail::get_random_value(min, max) : detail::get_random_value(min, max); + auto& [relative, minMax] = *location; + if (relative) { + a_refr->data.location += detail::get_random_value(minMax); + } else { + a_refr->data.location = detail::get_random_value(minMax); + } } if (rotation) { - auto [relative, minMax] = *location; - auto [min, max] = minMax; - a_refr->data.angle = relative ? a_refr->data.angle + detail::get_random_value(min, max) : detail::get_random_value(min, max); + auto& [relative, minMax] = *rotation; + if (relative) { + a_refr->data.angle += detail::get_random_value(minMax); + } else { + a_refr->data.angle = detail::get_random_value(minMax); + } } if (refScale) { auto& [min, max] = *refScale; From 0dc48337a64214bd030057b968c4d501ed61b6e1 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Sun, 4 Sep 2022 03:42:52 +0530 Subject: [PATCH 03/19] update submodules --- extern/CommonLibSSE | 2 +- src/main.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/extern/CommonLibSSE b/extern/CommonLibSSE index def4def..04f831b 160000 --- a/extern/CommonLibSSE +++ b/extern/CommonLibSSE @@ -1 +1 @@ -Subproject commit def4def7e0821ddf320b554633382a92e47a67d0 +Subproject commit 04f831b37b7c998f04f61bfb31e16b95a276ce89 diff --git a/src/main.cpp b/src/main.cpp index f2b7c88..12dea8d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,9 +12,11 @@ namespace FormSwap auto [swapBase, transformData] = Manager::GetSingleton()->GetSwapData(a_ref, base); - if (swapBase && base != swapBase) { - a_ref->SetObjectReference(swapBase); - transformData.SetTransform(a_ref); + if (swapBase) { + if (base != swapBase) { + a_ref->SetObjectReference(swapBase); + transformData.SetTransform(a_ref); + } } } From 8d3ce50d3684816bfb2b41a48de02054acc26bc0 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Sun, 4 Sep 2022 05:44:37 +0530 Subject: [PATCH 04/19] support leveled items --- src/Manager.cpp | 11 +++++++++++ src/main.cpp | 7 ++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Manager.cpp b/src/Manager.cpp index d31f0ae..e211475 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -249,6 +249,17 @@ namespace FormSwap swapData = get_swap_base(a_base, swapForms); } + if (swapData.first) { + if (const auto swapLvlBase = swapData.first->As(); swapLvlBase) { + RE::BSScrapArray calcedObjects{}; + swapLvlBase->CalculateCurrentFormList(a_ref->GetCalcLevel(false), 1, calcedObjects, 0, true); + if (!calcedObjects.empty()) { + const auto calcedForm = calcedObjects.front().form; + swapData.first = static_cast(calcedForm); + } + } + } + return swapData; } } diff --git a/src/main.cpp b/src/main.cpp index 12dea8d..db03fff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,10 +13,8 @@ namespace FormSwap auto [swapBase, transformData] = Manager::GetSingleton()->GetSwapData(a_ref, base); if (swapBase) { - if (base != swapBase) { - a_ref->SetObjectReference(swapBase); - transformData.SetTransform(a_ref); - } + a_ref->SetObjectReference(swapBase); + transformData.SetTransform(a_ref); } } @@ -30,7 +28,6 @@ namespace FormSwap inline void Install() { stl::write_vfunc(); - logger::info("Installed form swap"sv); } } From cc3b58c88ad8b6ff71692a9d202d4506d08eae75 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Mon, 3 Oct 2022 17:20:43 +0530 Subject: [PATCH 05/19] Refactor, add character base swaps --- CMakeLists.txt | 61 ++++++++------------------------- cmake/headerlist.cmake | 1 + cmake/sourcelist.cmake | 1 + src/Hooks.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++ src/Hooks.h | 6 ++++ src/Manager.cpp | 13 ++++--- src/main.cpp | 55 ++++++------------------------ vcpkg.json | 2 +- 8 files changed, 118 insertions(+), 98 deletions(-) create mode 100644 src/Hooks.cpp create mode 100644 src/Hooks.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ae13be..7bde276 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) set(NAME "po3_BaseObjectSwapper") -set(VERSION 1.7.0) +set(VERSION 1.8.0) set(VR_VERSION 1) set(AE_VERSION 1) @@ -18,8 +18,6 @@ macro(set_from_environment VARIABLE) endif () endmacro() -set_from_environment(VCPKG_ROOT) - macro(find_commonlib_path) if (CommonLibName AND NOT ${CommonLibName} STREQUAL "") # Check extern @@ -37,6 +35,7 @@ endmacro() set_from_environment(VCPKG_ROOT) if(BUILD_SKYRIMAE) add_compile_definitions(SKYRIM_AE) + add_compile_definitions(SKYRIM_SUPPORT_AE) set(CommonLibName "CommonLibSSE") set_from_environment(SkyrimAEPath) set(SkyrimPath ${SkyrimAEPath}) @@ -59,7 +58,7 @@ endif() find_commonlib_path() message( STATUS - "Building for ${SkyrimVersion} at ${SkyrimPath} with ${CommonLibName} at ${CommonLibPath}." + "Building ${NAME} ${VERSION} for ${SkyrimVersion} at ${SkyrimPath} with ${CommonLibName} at ${CommonLibPath}." ) if (DEFINED VCPKG_ROOT) @@ -110,12 +109,6 @@ add_compile_definitions( SKSE_SUPPORT_XBYAK ) -if (BUILD_SKYRIMAE) - add_compile_definitions( - SKYRIM_SUPPORT_AE - ) -endif() - if (MSVC) if (NOT ${CMAKE_GENERATOR} STREQUAL "Ninja") add_compile_options( @@ -173,6 +166,7 @@ add_library( ${CMAKE_CURRENT_BINARY_DIR}/include/Version.h ${CMAKE_CURRENT_BINARY_DIR}/version.rc .clang-format + .editorconfig ) target_compile_features( @@ -212,39 +206,14 @@ if (MSVC) target_compile_options( ${PROJECT_NAME} PRIVATE - /sdl # Enable Additional Security Checks - /utf-8 # Set Source and Executable character sets to UTF-8 - /Zi # Debug Information Format - - /permissive- # Standards conformance - - /Zc:alignedNew # C++17 over-aligned allocation - /Zc:auto # Deduce Variable Type - /Zc:char8_t - /Zc:__cplusplus # Enable updated __cplusplus macro - /Zc:externC - /Zc:externConstexpr # Enable extern constexpr variables - /Zc:forScope # Force Conformance in for Loop Scope - /Zc:hiddenFriend - /Zc:implicitNoexcept # Implicit Exception Specifiers - /Zc:lambda - /Zc:noexceptTypes # C++17 noexcept rules - /Zc:preprocessor # Enable preprocessor conformance mode - /Zc:referenceBinding # Enforce reference binding rules - /Zc:rvalueCast # Enforce type conversion rules - /Zc:sizedDealloc # Enable Global Sized Deallocation Functions - /Zc:strictStrings # Disable string literal type conversion - /Zc:ternary # Enforce conditional operator rules - /Zc:threadSafeInit # Thread-safe Local Static Initialization - /Zc:tlsGuards - /Zc:trigraphs # Trigraphs Substitution - /Zc:wchar_t # wchar_t Is Native Type - - /external:anglebrackets - /external:W0 - - /W4 # Warning level - /WX # Warning level (warnings are errors) + /sdl # Enable Additional Security Checks + /utf-8 # Set Source and Executable character sets to UTF-8 + /Zi # Debug Information Format + + /permissive- # Standards conformance + /Zc:preprocessor # Enable preprocessor conformance mode + + /wd4200 # nonstandard extension used : zero-sized array in struct/union "$<$:>" "$<$:/Zc:inline;/JMC-;/Ob3>" @@ -253,8 +222,6 @@ if (MSVC) target_link_options( ${PROJECT_NAME} PRIVATE - /WX # Treat Linker Warnings as Errors - "$<$:/INCREMENTAL;/OPT:NOREF;/OPT:NOICF>" "$<$:/INCREMENTAL:NO;/OPT:REF;/OPT:ICF;/DEBUG:FULL>" ) @@ -267,8 +234,8 @@ if (COPY_BUILD) add_custom_command( TARGET ${PROJECT_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ ${SkyrimPath}/Data/SKSE/Plugins/ - COMMAND ${CMAKE_COMMAND} -E copy $ ${SkyrimPath}/Data/SKSE/Plugins/ + COMMAND ${CMAKE_COMMAND} -E copy $ ${SkyrimPath}/SKSE/Plugins/ + COMMAND ${CMAKE_COMMAND} -E copy $ ${SkyrimPath}/SKSE/Plugins/ ) else () message( diff --git a/cmake/headerlist.cmake b/cmake/headerlist.cmake index bc12e9b..0d4e6c8 100644 --- a/cmake/headerlist.cmake +++ b/cmake/headerlist.cmake @@ -1,4 +1,5 @@ set(headers ${headers} + src/Hooks.h src/Manager.h src/MergeMapper.h src/PCH.h diff --git a/cmake/sourcelist.cmake b/cmake/sourcelist.cmake index 694d032..ba2d0cd 100644 --- a/cmake/sourcelist.cmake +++ b/cmake/sourcelist.cmake @@ -1,4 +1,5 @@ set(sources ${sources} + src/Hooks.cpp src/Manager.cpp src/MergeMapper.cpp src/PCH.cpp diff --git a/src/Hooks.cpp b/src/Hooks.cpp new file mode 100644 index 0000000..e5b8803 --- /dev/null +++ b/src/Hooks.cpp @@ -0,0 +1,77 @@ +#include "Hooks.h" +#include "Manager.h" +#include "MergeMapper.h" + +namespace BaseObjectSwapper +{ + namespace detail + { + void swap_base(RE::TESObjectREFR* a_ref) + { + if (const auto base = a_ref->GetBaseObject(); base) { + FormSwap::Manager::GetSingleton()->LoadFormsOnce(); + + auto [swapBase, transformData] = FormSwap::Manager::GetSingleton()->GetSwapData(a_ref, base); + + if (swapBase) { + a_ref->SetObjectReference(swapBase); + transformData.SetTransform(a_ref); + } + } + } + } + + namespace TESObjectREFR + { + struct InitItemImpl + { + static void thunk(RE::TESObjectREFR* a_ref) + { + detail::swap_base(a_ref); + func(a_ref); + } + static inline REL::Relocation func; + + static inline constexpr std::size_t size = 0x13; + }; + + void Install() + { + stl::write_vfunc(); + logger::info("Installed reference form swap"sv); + } + } + + namespace Character + { + struct InitItemImpl + { + static void thunk(RE::Character* a_ref) + { + detail::swap_base(a_ref); + func(a_ref); + } + static inline REL::Relocation func; + + static inline constexpr std::size_t size = 0x13; + }; + + void Install() + { + stl::write_vfunc(); + logger::info("Installed character form swap"sv); + } + } + + void Install() + { + logger::info("{:*^30}", "MERGES"); + + MergeMapper::GetMerges(); + + logger::info("{:*^30}", "HOOKS"); + + TESObjectREFR::Install(); + Character::Install(); + } +} diff --git a/src/Hooks.h b/src/Hooks.h new file mode 100644 index 0000000..a2b9c09 --- /dev/null +++ b/src/Hooks.h @@ -0,0 +1,6 @@ +#pragma once + +namespace BaseObjectSwapper +{ + void Install(); +} diff --git a/src/Manager.cpp b/src/Manager.cpp index e211475..ac8ad63 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -123,9 +123,9 @@ namespace FormSwap processedConditions.reserve(conditions.size()); for (auto& condition : conditions) { if (const auto processedID = SwapData::GetFormID(condition); processedID != 0) { - processedConditions.push_back(processedID); + processedConditions.emplace_back(processedID); } else { - processedConditions.push_back(condition); + processedConditions.emplace_back(condition); } } @@ -192,20 +192,23 @@ namespace FormSwap if (!cell) { cell = a_ref->GetSaveParentCell(); } - const auto location = a_ref->GetCurrentLocation(); + const auto currentLocation = a_ref->GetCurrentLocation(); const auto result = std::ranges::find_if(it->second, [&](const auto& formData) { if (std::holds_alternative(formData.first)) { if (auto form = RE::TESForm::LookupByID(std::get(formData.first)); form) { switch (form->GetFormType()) { case RE::FormType::Location: - return location && location == form; + { + auto location = form->As(); + return currentLocation && (currentLocation == location || currentLocation->IsChild(location)); + } case RE::FormType::Cell: return cell && cell == form; case RE::FormType::Keyword: { auto keyword = form->As(); - return (location && location->HasKeyword(keyword)) || a_ref->HasKeyword(keyword); + return (currentLocation && currentLocation->HasKeyword(keyword)) || a_ref->HasKeyword(keyword); } default: break; diff --git a/src/main.cpp b/src/main.cpp index db03fff..48b479d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,41 +1,14 @@ +#include "Hooks.h" #include "Manager.h" -#include "MergeMapper.h" - -namespace FormSwap -{ - struct InitItemImpl - { - static void thunk(RE::TESObjectREFR* a_ref) - { - if (const auto base = a_ref->GetBaseObject(); base) { - Manager::GetSingleton()->LoadFormsOnce(); - - auto [swapBase, transformData] = Manager::GetSingleton()->GetSwapData(a_ref, base); - - if (swapBase) { - a_ref->SetObjectReference(swapBase); - transformData.SetTransform(a_ref); - } - } - - func(a_ref); - } - static inline REL::Relocation func; - - static inline constexpr std::size_t size = 0x13; - }; - - inline void Install() - { - stl::write_vfunc(); - logger::info("Installed form swap"sv); - } -} void MessageHandler(SKSE::MessagingInterface::Message* a_message) { - if (a_message->type == SKSE::MessagingInterface::kDataLoaded) { - FormSwap::Manager::GetSingleton()->PrintConflicts(); + switch (a_message->type) { + case SKSE::MessagingInterface::kPostLoad: + BaseObjectSwapper::Install(); + case SKSE::MessagingInterface::kDataLoaded: + //FormSwap::Manager::GetSingleton()->PrintConflicts(); + break; } } @@ -103,20 +76,12 @@ extern "C" DLLEXPORT bool SKSEAPI SKSEPlugin_Load(const SKSE::LoadInterface* a_s { InitializeLog(); - logger::info("loaded"); + logger::info("Game version : {}", a_skse->RuntimeVersion().string()); SKSE::Init(a_skse); - logger::info("{:*^30}", "MERGES"); - - MergeMapper::GetMerges(); - - logger::info("{:*^30}", "HOOKS"); - - FormSwap::Install(); - - /*const auto messaging = SKSE::GetMessagingInterface(); - messaging->RegisterListener(MessageHandler);*/ + const auto messaging = SKSE::GetMessagingInterface(); + messaging->RegisterListener(MessageHandler); return true; } diff --git a/vcpkg.json b/vcpkg.json index a545009..38975f0 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,6 @@ { "name": "base-object-swapper", - "version-string": "1.7.0", + "version-string": "1.8.0", "description": "Base Object Swapper", "homepage": "", "license": "MIT", From 44acee3beae136960de3b687b20fd10c614ce79d Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Mon, 3 Oct 2022 22:45:58 +0530 Subject: [PATCH 06/19] Fix location check --- src/Manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Manager.cpp b/src/Manager.cpp index ac8ad63..1f2789d 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -201,7 +201,7 @@ namespace FormSwap case RE::FormType::Location: { auto location = form->As(); - return currentLocation && (currentLocation == location || currentLocation->IsChild(location)); + return currentLocation && (currentLocation == location || currentLocation->IsParent(location)); } case RE::FormType::Cell: return cell && cell == form; From a73b1f487367f682e9a704bd080cf4c3488fda80 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Thu, 27 Oct 2022 05:56:14 +0530 Subject: [PATCH 07/19] Add randomized swaps --- cmake/headerlist.cmake | 1 + extern/CommonLibSSE | 2 +- src/Hooks.cpp | 3 +- src/Manager.cpp | 61 +- src/Manager.h | 24 +- src/PCH.h | 3 +- src/SwapData.cpp | 64 +- src/SwapData.h | 64 +- src/XoshiroCpp.hpp | 1633 ++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 7 +- 10 files changed, 1797 insertions(+), 65 deletions(-) create mode 100644 src/XoshiroCpp.hpp diff --git a/cmake/headerlist.cmake b/cmake/headerlist.cmake index 0d4e6c8..ca5a75d 100644 --- a/cmake/headerlist.cmake +++ b/cmake/headerlist.cmake @@ -4,4 +4,5 @@ set(headers ${headers} src/MergeMapper.h src/PCH.h src/SwapData.h + src/XoshiroCpp.hpp ) diff --git a/extern/CommonLibSSE b/extern/CommonLibSSE index 04f831b..59fa871 160000 --- a/extern/CommonLibSSE +++ b/extern/CommonLibSSE @@ -1 +1 @@ -Subproject commit 04f831b37b7c998f04f61bfb31e16b95a276ce89 +Subproject commit 59fa8719e0f53867bd0df1b104af43ee4d02e49d diff --git a/src/Hooks.cpp b/src/Hooks.cpp index e5b8803..094a619 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -12,7 +12,6 @@ namespace BaseObjectSwapper FormSwap::Manager::GetSingleton()->LoadFormsOnce(); auto [swapBase, transformData] = FormSwap::Manager::GetSingleton()->GetSwapData(a_ref, base); - if (swapBase) { a_ref->SetObjectReference(swapBase); transformData.SetTransform(a_ref); @@ -27,7 +26,7 @@ namespace BaseObjectSwapper { static void thunk(RE::TESObjectREFR* a_ref) { - detail::swap_base(a_ref); + detail::swap_base(a_ref); func(a_ref); } static inline REL::Relocation func; diff --git a/src/Manager.cpp b/src/Manager.cpp index 1f2789d..1b917bd 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -2,12 +2,12 @@ namespace FormSwap { - Manager::SwapMap& Manager::get_form_map(const std::string& a_str) + SwapMap& Manager::get_form_map(const std::string& a_str) { return a_str == "Forms" ? swapForms : swapRefs; } - Manager::ConflictMap& Manager::get_conflict_map(const std::string& a_str) + ConflictMap& Manager::get_conflict_map(const std::string& a_str) { return a_str == "Forms" ? conflictForms : conflictRefs; } @@ -16,35 +16,35 @@ namespace FormSwap { const auto formPair = string::split(a_str, "|"); - auto baseFormID = SwapData::GetFormID(formPair[0]); - auto swapFormID = SwapData::GetFormID(formPair[1]); - - const auto data = formPair.size() > 2 ? formPair[2] : std::string{}; - - if (swapFormID != 0 && baseFormID != 0) { - SwapData swapData{ - swapFormID, - Transform{ data } - }; - - a_func(baseFormID, swapData); - - return { true, baseFormID }; + if (auto baseFormID = SwapData::GetFormID(formPair[0]); baseFormID != 0) { + const auto swapFormID = SwapData::GetSwapFormID(formPair[1]); + const auto transform = formPair.size() > 2 ? formPair[2] : std::string{}; + const auto traits = formPair.size() > 3 ? formPair[3] : std::string{}; + + if (!swap_empty(swapFormID)) { + SwapData swapData(swapFormID, transform, traits); + a_func(baseFormID, swapData); + return { true, baseFormID }; + } else { + logger::error(" failed to process {} (SWAP formID not found)", a_str); + return { false, 0 }; + } + } else { + logger::error(" failed to process {} (BASE formID not found)", a_str); + return { false, 0 }; } - - logger::error(" failed to process {} [{:x}|{:x}|{}] (formID not found)", a_str, baseFormID, swapFormID, data); - - return { false, 0 }; } std::pair Manager::get_forms(const std::string& a_str, SwapMap& a_map) { - return get_forms_impl(a_str, [&](RE::FormID a_baseID, SwapData& a_swapData) { a_map.insert_or_assign(a_baseID, a_swapData); }); + return get_forms_impl(a_str, [&](RE::FormID a_baseID, SwapData& a_swapData) { + a_map.insert_or_assign(a_baseID, a_swapData); + }); } - std::pair Manager::get_forms(const std::string& a_str, const std::vector>& a_conditionalIDs, SwapMap& a_map) + std::pair Manager::get_forms(const std::string& a_str, const std::vector& a_conditionalIDs, SwapMap& a_map) { - return get_forms_impl(a_str, [&](RE::FormID a_baseID, SwapData& a_swapData) { + return get_forms_impl(a_str, [&](const RE::FormID a_baseID, SwapData& a_swapData) { for (auto& id : a_conditionalIDs) { a_map[a_baseID].emplace(id, a_swapData); } @@ -119,7 +119,7 @@ namespace FormSwap logger::info(" reading [Forms] : {} conditions", conditions.size()); - std::vector> processedConditions; + std::vector processedConditions; processedConditions.reserve(conditions.size()); for (auto& condition : conditions) { if (const auto processedID = SwapData::GetFormID(condition); processedID != 0) { @@ -185,7 +185,7 @@ namespace FormSwap } } - Manager::SwapResult Manager::GetSwapConditionalBase(const RE::TESObjectREFR* a_ref, const RE::TESForm* a_base) + SwapResult Manager::GetSwapConditionalBase(const RE::TESObjectREFR* a_ref, const RE::TESForm* a_base) { if (const auto it = swapFormsConditional.find(a_base->GetFormID()); it != swapFormsConditional.end()) { auto cell = a_ref->GetParentCell(); @@ -222,18 +222,18 @@ namespace FormSwap }); if (result != it->second.end()) { - return { RE::TESForm::LookupByID(result->second.formID), result->second.transform }; + return { result->second.GetSwapBase(a_ref), result->second.transform }; } } return { nullptr, Transform() }; } - Manager::SwapResult Manager::GetSwapData(const RE::TESObjectREFR* a_ref, const RE::TESForm* a_base) + SwapResult Manager::GetSwapData(const RE::TESObjectREFR* a_ref, const RE::TESForm* a_base) { - constexpr auto get_swap_base = [](const RE::TESForm* a_form, const SwapMap& a_map) { + const auto get_swap_base = [a_ref](const RE::TESForm* a_form, const SwapMap& a_map) { if (const auto it = a_map.find(a_form->GetFormID()); it != a_map.end()) { - return SwapResult{ RE::TESForm::LookupByID(it->second.formID), it->second.transform }; + return SwapResult{ it->second.GetSwapBase(a_ref), it->second.transform }; } return SwapResult{}; }; @@ -257,8 +257,7 @@ namespace FormSwap RE::BSScrapArray calcedObjects{}; swapLvlBase->CalculateCurrentFormList(a_ref->GetCalcLevel(false), 1, calcedObjects, 0, true); if (!calcedObjects.empty()) { - const auto calcedForm = calcedObjects.front().form; - swapData.first = static_cast(calcedForm); + swapData.first = static_cast(calcedObjects.front().form); } } } diff --git a/src/Manager.h b/src/Manager.h index 105449b..f761580 100644 --- a/src/Manager.h +++ b/src/Manager.h @@ -4,11 +4,16 @@ namespace FormSwap { + template + using SwapMap = Map; + using SwapDataConditional = Map; + using SwapResult = std::pair; + + using ConflictMap = Map>>; //record, path + class Manager { public: - using SwapResult = std::pair; - [[nodiscard]] static Manager* GetSingleton() { static Manager singleton; @@ -32,25 +37,16 @@ namespace FormSwap Manager& operator=(Manager&&) = delete; private: - template - using Map = robin_hood::unordered_flat_map; - - template - using SwapMap = Map; - using SwapDataConditional = Map, SwapData>; - - using ConflictMap = Map>>; //record, path - using Lock = std::mutex; using Locker = std::scoped_lock; SwapMap& get_form_map(const std::string& a_str); - ConflictMap& get_conflict_map(const std::string& a_str); + ConflictMap& get_conflict_map(const std::string& a_str); - static std::pair get_forms_impl(const std::string& a_str, std::function a_func); + static std::pair get_forms_impl(const std::string& a_str, std::function a_func); static std::pair get_forms(const std::string& a_str, SwapMap& a_map); - static std::pair get_forms(const std::string& a_str, const std::vector>& a_conditionalIDs, SwapMap& a_map); + static std::pair get_forms(const std::string& a_str, const std::vector& a_conditionalIDs, SwapMap& a_map); ConflictMap conflictForms{}; ConflictMap conflictRefs{}; diff --git a/src/PCH.h b/src/PCH.h index 0d1be24..868c0ce 100644 --- a/src/PCH.h +++ b/src/PCH.h @@ -3,11 +3,12 @@ #include "RE/Skyrim.h" #include "SKSE/SKSE.h" +#include "XoshiroCpp.hpp" #include #include #include #include -#include "srell.hpp" +#include #define DLLEXPORT __declspec(dllexport) diff --git a/src/SwapData.cpp b/src/SwapData.cpp index 1afa345..3c5ab48 100644 --- a/src/SwapData.cpp +++ b/src/SwapData.cpp @@ -2,7 +2,7 @@ namespace FormSwap { - namespace detail + namespace detail { static Transform::minMax get_min_max(const std::string& a_str) { @@ -20,7 +20,7 @@ namespace FormSwap static float get_random_value(float a_min, float a_max) { - if (a_min == a_max) { + if (stl::numeric::essentially_equal(a_min,a_max)) { return a_min; } @@ -80,7 +80,7 @@ namespace FormSwap Transform::Transform(const std::string& a_str) { - if (!a_str.empty()) { + if (!a_str.empty() && !string::icontains(a_str, "NONE")) { const auto transformStrs = string::split(a_str, ","); for (auto& transformStr : transformStrs) { if (transformStr.contains("pos")) { @@ -119,24 +119,68 @@ namespace FormSwap } } - SwapData::SwapData(RE::FormID a_id, Transform a_transform) : - formID(a_id), - transform(std::move(a_transform)) + Traits::Traits(const std::string& a_str) + { + if (!a_str.empty() && !string::icontains(a_str, "NONE")) { + if (a_str == "R") { + trueRandom = true; + } + } + } + + SwapData::SwapData(FormIDOrSet a_id, const std::string& a_transformStr, const std::string& a_traitsStr) : + formIDSet(std::move(a_id)), + transform(a_transformStr), + traits(a_traitsStr) {} RE::FormID SwapData::GetFormID(const std::string& a_str) { - if (a_str.find('~') != std::string::npos) { + if (a_str.contains('~')) { const auto formPair = string::split(a_str, "~"); - const auto [modName, formID] = MergeMapper::GetNewFormID(formPair[1], formPair[0]); - return RE::TESDataHandler::GetSingleton()->LookupFormID(formID, modName); } if (const auto form = RE::TESForm::LookupByEditorID(a_str); form) { return form->GetFormID(); } - return static_cast(0); } + + FormIDOrSet SwapData::GetSwapFormID(const std::string& a_str) + { + if (a_str.contains(",")) { + FormIDSet set; + const auto IDStrs = string::split(a_str, ","); + set.reserve(IDStrs.size()); + for (auto& IDStr : IDStrs) { + if (auto formID = GetFormID(IDStr); formID != 0) { + set.emplace(formID); + } else { + logger::error(" failed to process {} (SWAP formID not found)", IDStr); + } + } + return set; + } else { + return GetFormID(a_str); + } + } + + RE::TESBoundObject* SwapData::GetSwapBase(const RE::TESObjectREFR* a_ref) const + { + auto seededRNG = SeedRNG(a_ref->GetFormID()); + auto staticRNG = stl::RNG::GetSingleton(); + + if (const auto formID = std::get_if(&formIDSet); formID) { + return RE::TESForm::LookupByID(*formID); + } else { // return random element from set + auto& set = std::get(formIDSet); + + const auto setEnd = std::distance(set.begin(), set.end()) - 1; + const auto randIt = traits.trueRandom ? staticRNG->Generate(0, setEnd) : + seededRNG.Generate(0, setEnd); + + return RE::TESForm::LookupByID(*std::next(set.begin(), randIt)); + } + } } diff --git a/src/SwapData.h b/src/SwapData.h index f119210..81824e2 100644 --- a/src/SwapData.h +++ b/src/SwapData.h @@ -1,10 +1,54 @@ #pragma once + #include "MergeMapper.h" namespace FormSwap { - struct Transform + using FormOrEditorID = std::variant; + + template + using Map = robin_hood::unordered_flat_map; + template + using Set = robin_hood::unordered_flat_set; + + using FormIDSet = Set; + using FormIDOrSet = std::variant; + inline bool swap_empty(const FormIDOrSet& a_set) + { + if (const auto formID = std::get_if(&a_set); formID) { + return *formID == 0; + } else { + return std::get(a_set).empty(); + } + } + + class SeedRNG + { + public: + SeedRNG() = delete; + explicit SeedRNG(const RE::FormID a_seed) : + rng(a_seed) + {} + + template >> + T Generate(T a_min, T a_max) + { + if constexpr (std::is_integral_v) { + std::uniform_int_distribution distr(a_min, a_max); + return distr(rng); + } else { + std::uniform_real_distribution distr(a_min, a_max); + return distr(rng); + } + } + + private: + XoshiroCpp::Xoshiro256StarStar rng; + }; + + class Transform { + public: template using minMax = std::pair; template @@ -13,7 +57,7 @@ namespace FormSwap Transform() = default; explicit Transform(const std::string& a_str); - void SetTransform(RE::TESObjectREFR* a_refr); + void SetTransform(RE::TESObjectREFR* a_refr); private: [[nodiscard]] static relData get_transform_from_string(const std::string& a_str); @@ -27,15 +71,27 @@ namespace FormSwap static inline srell::regex scaleRegex{ R"(\((.*?)\))" }; }; + struct Traits + { + Traits() = default; + explicit Traits(const std::string& a_str); + + bool trueRandom{ false }; + }; + class SwapData { public: SwapData() = delete; - SwapData(RE::FormID a_id, Transform a_transform); + SwapData(FormIDOrSet a_id, const std::string& a_transformStr, const std::string& a_traitsStr); [[nodiscard]] static RE::FormID GetFormID(const std::string& a_str); + [[nodiscard]] static FormIDOrSet GetSwapFormID(const std::string& a_str); + + RE::TESBoundObject* GetSwapBase(const RE::TESObjectREFR* a_ref) const; - RE::FormID formID{}; + FormIDOrSet formIDSet{}; Transform transform{}; + Traits traits{}; }; } diff --git a/src/XoshiroCpp.hpp b/src/XoshiroCpp.hpp new file mode 100644 index 0000000..1ecfdfb --- /dev/null +++ b/src/XoshiroCpp.hpp @@ -0,0 +1,1633 @@ +//---------------------------------------------------------------------------------------- +// +// Xoshiro-cpp +// Xoshiro PRNG wrapper library for C++17 / C++20 +// +// Copyright (C) 2020 Ryo Suzuki +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//---------------------------------------------------------------------------------------- + +# pragma once +# include +# include +# include +# include +# if __has_cpp_attribute(nodiscard) >= 201907L +# define XOSHIROCPP_NODISCARD_CXX20 [[nodiscard]] +# else +# define XOSHIROCPP_NODISCARD_CXX20 +# endif + +namespace XoshiroCpp +{ + // A default seed value for the generators + inline constexpr std::uint64_t DefaultSeed = 1234567890ULL; + + // Converts given uint32 value `i` into a 32-bit floating + // point value in the range of [0.0f, 1.0f) + template >* = nullptr> + [[nodiscard]] + inline constexpr float FloatFromBits(Uint32 i) noexcept; + + // Converts given uint64 value `i` into a 64-bit floating + // point value in the range of [0.0, 1.0) + template >* = nullptr> + [[nodiscard]] + inline constexpr double DoubleFromBits(Uint64 i) noexcept; + + // SplitMix64 + // Output: 64 bits + // Period: 2^64 + // Footprint: 8 bytes + // Original implementation: http://prng.di.unimi.it/splitmix64.c + class SplitMix64 + { + public: + + using state_type = std::uint64_t; + using result_type = std::uint64_t; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr SplitMix64(state_type state = DefaultSeed) noexcept; + + constexpr result_type operator()() noexcept; + + template + [[nodiscard]] + constexpr std::array generateSeedSequence() noexcept; + + [[nodiscard]] + static constexpr result_type min() noexcept; + + [[nodiscard]] + static constexpr result_type max() noexcept; + + [[nodiscard]] + constexpr state_type serialize() const noexcept; + + constexpr void deserialize(state_type state) noexcept; + + [[nodiscard]] + friend bool operator ==(const SplitMix64& lhs, const SplitMix64& rhs) noexcept + { + return (lhs.m_state == rhs.m_state); + } + + [[nodiscard]] + friend bool operator !=(const SplitMix64& lhs, const SplitMix64& rhs) noexcept + { + return (lhs.m_state != rhs.m_state); + } + + private: + + state_type m_state; + }; + + // xoshiro256+ + // Output: 64 bits + // Period: 2^256 - 1 + // Footprint: 32 bytes + // Original implementation: http://prng.di.unimi.it/xoshiro256plus.c + // Version: 1.0 + class Xoshiro256Plus + { + public: + + using state_type = std::array; + using result_type = std::uint64_t; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoshiro256Plus(std::uint64_t seed = DefaultSeed) noexcept; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoshiro256Plus(state_type state) noexcept; + + constexpr result_type operator()() noexcept; + + // This is the jump function for the generator. It is equivalent + // to 2^128 calls to operator(); it can be used to generate 2^128 + // non-overlapping subsequences for parallel computations. + constexpr void jump() noexcept; + + // This is the long-jump function for the generator. It is equivalent to + // 2^192 calls to next(); it can be used to generate 2^64 starting points, + // from each of which jump() will generate 2^64 non-overlapping + // subsequences for parallel distributed computations. + constexpr void longJump() noexcept; + + [[nodiscard]] + static constexpr result_type min() noexcept; + + [[nodiscard]] + static constexpr result_type max() noexcept; + + [[nodiscard]] + constexpr state_type serialize() const noexcept; + + constexpr void deserialize(state_type state) noexcept; + + [[nodiscard]] + friend bool operator ==(const Xoshiro256Plus& lhs, const Xoshiro256Plus& rhs) noexcept + { + return (lhs.m_state == rhs.m_state); + } + + [[nodiscard]] + friend bool operator !=(const Xoshiro256Plus& lhs, const Xoshiro256Plus& rhs) noexcept + { + return (lhs.m_state != rhs.m_state); + } + + private: + + state_type m_state; + }; + + // xoshiro256++ + // Output: 64 bits + // Period: 2^256 - 1 + // Footprint: 32 bytes + // Original implementation: http://prng.di.unimi.it/xoshiro256plusplus.c + // Version: 1.0 + class Xoshiro256PlusPlus + { + public: + + using state_type = std::array; + using result_type = std::uint64_t; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoshiro256PlusPlus(std::uint64_t seed = DefaultSeed) noexcept; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoshiro256PlusPlus(state_type state) noexcept; + + constexpr result_type operator()() noexcept; + + // This is the jump function for the generator. It is equivalent + // to 2^128 calls to next(); it can be used to generate 2^128 + // non-overlapping subsequences for parallel computations. + constexpr void jump() noexcept; + + // This is the long-jump function for the generator. It is equivalent to + // 2^192 calls to next(); it can be used to generate 2^64 starting points, + // from each of which jump() will generate 2^64 non-overlapping + // subsequences for parallel distributed computations. + constexpr void longJump() noexcept; + + [[nodiscard]] + static constexpr result_type min() noexcept; + + [[nodiscard]] + static constexpr result_type max() noexcept; + + [[nodiscard]] + constexpr state_type serialize() const noexcept; + + constexpr void deserialize(state_type state) noexcept; + + [[nodiscard]] + friend bool operator ==(const Xoshiro256PlusPlus& lhs, const Xoshiro256PlusPlus& rhs) noexcept + { + return (lhs.m_state == rhs.m_state); + } + + [[nodiscard]] + friend bool operator !=(const Xoshiro256PlusPlus& lhs, const Xoshiro256PlusPlus& rhs) noexcept + { + return (lhs.m_state != rhs.m_state); + } + + private: + + state_type m_state; + }; + + // xoshiro256** + // Output: 64 bits + // Period: 2^256 - 1 + // Footprint: 32 bytes + // Original implementation: http://prng.di.unimi.it/xoshiro256starstar.c + // Version: 1.0 + class Xoshiro256StarStar + { + public: + + using state_type = std::array; + using result_type = std::uint64_t; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoshiro256StarStar(std::uint64_t seed = DefaultSeed) noexcept; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoshiro256StarStar(state_type state) noexcept; + + constexpr result_type operator()() noexcept; + + // This is the jump function for the generator. It is equivalent + // to 2^128 calls to next(); it can be used to generate 2^128 + // non-overlapping subsequences for parallel computations. + constexpr void jump() noexcept; + + // This is the long-jump function for the generator. It is equivalent to + // 2^192 calls to next(); it can be used to generate 2^64 starting points, + // from each of which jump() will generate 2^64 non-overlapping + // subsequences for parallel distributed computations. + constexpr void longJump() noexcept; + + [[nodiscard]] + static constexpr result_type min() noexcept; + + [[nodiscard]] + static constexpr result_type max() noexcept; + + [[nodiscard]] + constexpr state_type serialize() const noexcept; + + constexpr void deserialize(state_type state) noexcept; + + [[nodiscard]] + friend bool operator ==(const Xoshiro256StarStar& lhs, const Xoshiro256StarStar& rhs) noexcept + { + return (lhs.m_state == rhs.m_state); + } + + [[nodiscard]] + friend bool operator !=(const Xoshiro256StarStar& lhs, const Xoshiro256StarStar& rhs) noexcept + { + return (lhs.m_state != rhs.m_state); + } + + private: + + state_type m_state; + }; + + // xoroshiro128+ + // Output: 64 bits + // Period: 2^128 - 1 + // Footprint: 16 bytes + // Original implementation: http://prng.di.unimi.it/xoroshiro128plus.c + // Version: 1.0 + class Xoroshiro128Plus + { + public: + + using state_type = std::array; + using result_type = std::uint64_t; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoroshiro128Plus(std::uint64_t seed = DefaultSeed) noexcept; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoroshiro128Plus(state_type state) noexcept; + + constexpr result_type operator()() noexcept; + + // This is the jump function for the generator. It is equivalent + // to 2^64 calls to next(); it can be used to generate 2^64 + // non-overlapping subsequences for parallel computations. + constexpr void jump() noexcept; + + // This is the long-jump function for the generator. It is equivalent to + // 2^96 calls to next(); it can be used to generate 2^32 starting points, + // from each of which jump() will generate 2^32 non-overlapping + // subsequences for parallel distributed computations. + constexpr void longJump() noexcept; + + [[nodiscard]] + static constexpr result_type min() noexcept; + + [[nodiscard]] + static constexpr result_type max() noexcept; + + [[nodiscard]] + constexpr state_type serialize() const noexcept; + + constexpr void deserialize(state_type state) noexcept; + + [[nodiscard]] + friend bool operator ==(const Xoroshiro128Plus& lhs, const Xoroshiro128Plus& rhs) noexcept + { + return (lhs.m_state == rhs.m_state); + } + + [[nodiscard]] + friend bool operator !=(const Xoroshiro128Plus& lhs, const Xoroshiro128Plus& rhs) noexcept + { + return (lhs.m_state != rhs.m_state); + } + + private: + + state_type m_state; + }; + + // xoroshiro128++ + // Output: 64 bits + // Period: 2^128 - 1 + // Footprint: 16 bytes + // Original implementation: http://prng.di.unimi.it/xoroshiro128plusplus.c + // Version: 1.0 + class Xoroshiro128PlusPlus + { + public: + + using state_type = std::array; + using result_type = std::uint64_t; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoroshiro128PlusPlus(std::uint64_t seed = DefaultSeed) noexcept; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoroshiro128PlusPlus(state_type state) noexcept; + + constexpr result_type operator()() noexcept; + + // This is the jump function for the generator. It is equivalent + // to 2^64 calls to next(); it can be used to generate 2^64 + // non-overlapping subsequences for parallel computations. + constexpr void jump() noexcept; + + // This is the long-jump function for the generator. It is equivalent to + // 2^96 calls to next(); it can be used to generate 2^32 starting points, + // from each of which jump() will generate 2^32 non-overlapping + // subsequences for parallel distributed computations. + constexpr void longJump() noexcept; + + [[nodiscard]] + static constexpr result_type min() noexcept; + + [[nodiscard]] + static constexpr result_type max() noexcept; + + [[nodiscard]] + constexpr state_type serialize() const noexcept; + + constexpr void deserialize(state_type state) noexcept; + + [[nodiscard]] + friend bool operator ==(const Xoroshiro128PlusPlus& lhs, const Xoroshiro128PlusPlus& rhs) noexcept + { + return (lhs.m_state == rhs.m_state); + } + + [[nodiscard]] + friend bool operator !=(const Xoroshiro128PlusPlus& lhs, const Xoroshiro128PlusPlus& rhs) noexcept + { + return (lhs.m_state != rhs.m_state); + } + + private: + + state_type m_state; + }; + + // xoroshiro128** + // Output: 64 bits + // Period: 2^128 - 1 + // Footprint: 16 bytes + // Original implementation: http://prng.di.unimi.it/xoroshiro128starstar.c + // Version: 1.0 + class Xoroshiro128StarStar + { + public: + + using state_type = std::array; + using result_type = std::uint64_t; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoroshiro128StarStar(std::uint64_t seed = DefaultSeed) noexcept; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoroshiro128StarStar(state_type state) noexcept; + + constexpr result_type operator()() noexcept; + + // This is the jump function for the generator. It is equivalent + // to 2^64 calls to next(); it can be used to generate 2^64 + // non-overlapping subsequences for parallel computations. + constexpr void jump() noexcept; + + // This is the long-jump function for the generator. It is equivalent to + // 2^96 calls to next(); it can be used to generate 2^32 starting points, + // from each of which jump() will generate 2^32 non-overlapping + // subsequences for parallel distributed computations. + constexpr void longJump() noexcept; + + [[nodiscard]] + static constexpr result_type min() noexcept; + + [[nodiscard]] + static constexpr result_type max() noexcept; + + [[nodiscard]] + constexpr state_type serialize() const noexcept; + + constexpr void deserialize(state_type state) noexcept; + + [[nodiscard]] + friend bool operator ==(const Xoroshiro128StarStar& lhs, const Xoroshiro128StarStar& rhs) noexcept + { + return (lhs.m_state == rhs.m_state); + } + + [[nodiscard]] + friend bool operator !=(const Xoroshiro128StarStar& lhs, const Xoroshiro128StarStar& rhs) noexcept + { + return (lhs.m_state != rhs.m_state); + } + + private: + + state_type m_state; + }; + + // xoshiro128+ + // Output: 32 bits + // Period: 2^128 - 1 + // Footprint: 16 bytes + // Original implementation: http://prng.di.unimi.it/xoshiro128plus.c + // Version: 1.0 + class Xoshiro128Plus + { + public: + + using state_type = std::array; + using result_type = std::uint32_t; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoshiro128Plus(std::uint64_t seed = DefaultSeed) noexcept; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoshiro128Plus(state_type state) noexcept; + + constexpr result_type operator()() noexcept; + + // This is the jump function for the generator. It is equivalent + // to 2^64 calls to next(); it can be used to generate 2^64 + // non-overlapping subsequences for parallel computations. + constexpr void jump() noexcept; + + // This is the long-jump function for the generator. It is equivalent to + // 2^96 calls to next(); it can be used to generate 2^32 starting points, + // from each of which jump() will generate 2^32 non-overlapping + // subsequences for parallel distributed computations. + constexpr void longJump() noexcept; + + [[nodiscard]] + static constexpr result_type min() noexcept; + + [[nodiscard]] + static constexpr result_type max() noexcept; + + [[nodiscard]] + constexpr state_type serialize() const noexcept; + + constexpr void deserialize(state_type state) noexcept; + + [[nodiscard]] + friend bool operator ==(const Xoshiro128Plus& lhs, const Xoshiro128Plus& rhs) noexcept + { + return (lhs.m_state == rhs.m_state); + } + + [[nodiscard]] + friend bool operator !=(const Xoshiro128Plus& lhs, const Xoshiro128Plus& rhs) noexcept + { + return (lhs.m_state != rhs.m_state); + } + + private: + + state_type m_state; + }; + + // xoshiro128++ + // Output: 32 bits + // Period: 2^128 - 1 + // Footprint: 16 bytes + // Original implementation: http://prng.di.unimi.it/xoshiro128plusplus.c + // Version: 1.0 + class Xoshiro128PlusPlus + { + public: + + using state_type = std::array; + using result_type = std::uint32_t; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoshiro128PlusPlus(std::uint64_t seed = DefaultSeed) noexcept; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoshiro128PlusPlus(state_type state) noexcept; + + constexpr result_type operator()() noexcept; + + // This is the jump function for the generator. It is equivalent + // to 2^64 calls to next(); it can be used to generate 2^64 + // non-overlapping subsequences for parallel computations. + constexpr void jump() noexcept; + + // This is the long-jump function for the generator. It is equivalent to + // 2^96 calls to next(); it can be used to generate 2^32 starting points, + // from each of which jump() will generate 2^32 non-overlapping + // subsequences for parallel distributed computations. + constexpr void longJump() noexcept; + + [[nodiscard]] + static constexpr result_type min() noexcept; + + [[nodiscard]] + static constexpr result_type max() noexcept; + + [[nodiscard]] + constexpr state_type serialize() const noexcept; + + constexpr void deserialize(state_type state) noexcept; + + [[nodiscard]] + friend bool operator ==(const Xoshiro128PlusPlus& lhs, const Xoshiro128PlusPlus& rhs) noexcept + { + return (lhs.m_state == rhs.m_state); + } + + [[nodiscard]] + friend bool operator !=(const Xoshiro128PlusPlus& lhs, const Xoshiro128PlusPlus& rhs) noexcept + { + return (lhs.m_state != rhs.m_state); + } + + private: + + state_type m_state; + }; + + // xoshiro128** + // Output: 32 bits + // Period: 2^128 - 1 + // Footprint: 16 bytes + // Original implementation: http://prng.di.unimi.it/xoshiro128starstar.c + // Version: 1.1 + class Xoshiro128StarStar + { + public: + + using state_type = std::array; + using result_type = std::uint32_t; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoshiro128StarStar(std::uint64_t seed = DefaultSeed) noexcept; + + XOSHIROCPP_NODISCARD_CXX20 + explicit constexpr Xoshiro128StarStar(state_type state) noexcept; + + constexpr result_type operator()() noexcept; + + // This is the jump function for the generator. It is equivalent + // to 2^64 calls to next(); it can be used to generate 2^64 + // non-overlapping subsequences for parallel computations. + constexpr void jump() noexcept; + + // This is the long-jump function for the generator. It is equivalent to + // 2^96 calls to next(); it can be used to generate 2^32 starting points, + // from each of which jump() will generate 2^32 non-overlapping + // subsequences for parallel distributed computations. + constexpr void longJump() noexcept; + + [[nodiscard]] + static constexpr result_type min() noexcept; + + [[nodiscard]] + static constexpr result_type max() noexcept; + + [[nodiscard]] + constexpr state_type serialize() const noexcept; + + constexpr void deserialize(state_type state) noexcept; + + [[nodiscard]] + friend bool operator ==(const Xoshiro128StarStar& lhs, const Xoshiro128StarStar& rhs) noexcept + { + return (lhs.m_state == rhs.m_state); + } + + [[nodiscard]] + friend bool operator !=(const Xoshiro128StarStar& lhs, const Xoshiro128StarStar& rhs) noexcept + { + return (lhs.m_state != rhs.m_state); + } + + private: + + state_type m_state; + }; +} + +//////////////////////////////////////////////////////////////// + +namespace XoshiroCpp +{ + template >*> + inline constexpr float FloatFromBits(const Uint32 i) noexcept + { + return (i >> 8) * 0x1.0p-24f; + } + + template >*> + inline constexpr double DoubleFromBits(const Uint64 i) noexcept + { + return (i >> 11) * 0x1.0p-53; + } + + namespace detail + { + [[nodiscard]] + static constexpr std::uint64_t RotL(const std::uint64_t x, const int s) noexcept + { + return (x << s) | (x >> (64 - s)); + } + + [[nodiscard]] + static constexpr std::uint32_t RotL(const std::uint32_t x, const int s) noexcept + { + return (x << s) | (x >> (32 - s)); + } + } + + //////////////////////////////////////////////////////////////// + // + // SplitMix64 + // + inline constexpr SplitMix64::SplitMix64(const state_type state) noexcept + : m_state(state) {} + + inline constexpr SplitMix64::result_type SplitMix64::operator()() noexcept + { + std::uint64_t z = (m_state += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + return z ^ (z >> 31); + } + + template + inline constexpr std::array SplitMix64::generateSeedSequence() noexcept + { + std::array seeds = {}; + + for (auto& seed : seeds) + { + seed = operator()(); + } + + return seeds; + } + + inline constexpr SplitMix64::result_type SplitMix64::min() noexcept + { + return std::numeric_limits::lowest(); + } + + inline constexpr SplitMix64::result_type SplitMix64::max() noexcept + { + return std::numeric_limits::max(); + } + + inline constexpr SplitMix64::state_type SplitMix64::serialize() const noexcept + { + return m_state; + } + + inline constexpr void SplitMix64::deserialize(const state_type state) noexcept + { + m_state = state; + } + + //////////////////////////////////////////////////////////////// + // + // xoshiro256+ + // + inline constexpr Xoshiro256Plus::Xoshiro256Plus(const std::uint64_t seed) noexcept + : m_state(SplitMix64{ seed }.generateSeedSequence<4>()) {} + + inline constexpr Xoshiro256Plus::Xoshiro256Plus(const state_type state) noexcept + : m_state(state) {} + + inline constexpr Xoshiro256Plus::result_type Xoshiro256Plus::operator()() noexcept + { + const std::uint64_t result = m_state[0] + m_state[3]; + const std::uint64_t t = m_state[1] << 17; + m_state[2] ^= m_state[0]; + m_state[3] ^= m_state[1]; + m_state[1] ^= m_state[2]; + m_state[0] ^= m_state[3]; + m_state[2] ^= t; + m_state[3] = detail::RotL(m_state[3], 45); + return result; + } + + inline constexpr void Xoshiro256Plus::jump() noexcept + { + constexpr std::uint64_t JUMP[] = { 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c }; + + std::uint64_t s0 = 0; + std::uint64_t s1 = 0; + std::uint64_t s2 = 0; + std::uint64_t s3 = 0; + + for (std::uint64_t jump : JUMP) + { + for (int b = 0; b < 64; ++b) + { + if (jump & UINT64_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + s2 ^= m_state[2]; + s3 ^= m_state[3]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + m_state[2] = s2; + m_state[3] = s3; + } + + inline constexpr void Xoshiro256Plus::longJump() noexcept + { + constexpr std::uint64_t LONG_JUMP[] = { 0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635 }; + + std::uint64_t s0 = 0; + std::uint64_t s1 = 0; + std::uint64_t s2 = 0; + std::uint64_t s3 = 0; + + for (std::uint64_t jump : LONG_JUMP) + { + for (int b = 0; b < 64; ++b) + { + if (jump & UINT64_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + s2 ^= m_state[2]; + s3 ^= m_state[3]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + m_state[2] = s2; + m_state[3] = s3; + } + + inline constexpr Xoshiro256Plus::result_type Xoshiro256Plus::min() noexcept + { + return std::numeric_limits::lowest(); + } + + inline constexpr Xoshiro256Plus::result_type Xoshiro256Plus::max() noexcept + { + return std::numeric_limits::max(); + } + + inline constexpr Xoshiro256Plus::state_type Xoshiro256Plus::serialize() const noexcept + { + return m_state; + } + + inline constexpr void Xoshiro256Plus::deserialize(const state_type state) noexcept + { + m_state = state; + } + + //////////////////////////////////////////////////////////////// + // + // xoshiro256++ + // + inline constexpr Xoshiro256PlusPlus::Xoshiro256PlusPlus(const std::uint64_t seed) noexcept + : m_state(SplitMix64{ seed }.generateSeedSequence<4>()) {} + + inline constexpr Xoshiro256PlusPlus::Xoshiro256PlusPlus(const state_type state) noexcept + : m_state(state) {} + + inline constexpr Xoshiro256PlusPlus::result_type Xoshiro256PlusPlus::operator()() noexcept + { + const std::uint64_t result = detail::RotL(m_state[0] + m_state[3], 23) + m_state[0]; + const std::uint64_t t = m_state[1] << 17; + m_state[2] ^= m_state[0]; + m_state[3] ^= m_state[1]; + m_state[1] ^= m_state[2]; + m_state[0] ^= m_state[3]; + m_state[2] ^= t; + m_state[3] = detail::RotL(m_state[3], 45); + return result; + } + + inline constexpr void Xoshiro256PlusPlus::jump() noexcept + { + constexpr std::uint64_t JUMP[] = { 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c }; + + std::uint64_t s0 = 0; + std::uint64_t s1 = 0; + std::uint64_t s2 = 0; + std::uint64_t s3 = 0; + + for (std::uint64_t jump : JUMP) + { + for (int b = 0; b < 64; ++b) + { + if (jump & UINT64_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + s2 ^= m_state[2]; + s3 ^= m_state[3]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + m_state[2] = s2; + m_state[3] = s3; + } + + inline constexpr void Xoshiro256PlusPlus::longJump() noexcept + { + constexpr std::uint64_t LONG_JUMP[] = { 0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635 }; + + std::uint64_t s0 = 0; + std::uint64_t s1 = 0; + std::uint64_t s2 = 0; + std::uint64_t s3 = 0; + + for (std::uint64_t jump : LONG_JUMP) + { + for (int b = 0; b < 64; ++b) + { + if (jump & UINT64_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + s2 ^= m_state[2]; + s3 ^= m_state[3]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + m_state[2] = s2; + m_state[3] = s3; + } + + inline constexpr Xoshiro256PlusPlus::result_type Xoshiro256PlusPlus::min() noexcept + { + return std::numeric_limits::lowest(); + } + + inline constexpr Xoshiro256PlusPlus::result_type Xoshiro256PlusPlus::max() noexcept + { + return std::numeric_limits::max(); + } + + inline constexpr Xoshiro256PlusPlus::state_type Xoshiro256PlusPlus::serialize() const noexcept + { + return m_state; + } + + inline constexpr void Xoshiro256PlusPlus::deserialize(const state_type state) noexcept + { + m_state = state; + } + + //////////////////////////////////////////////////////////////// + // + // xoshiro256** + // + inline constexpr Xoshiro256StarStar::Xoshiro256StarStar(const std::uint64_t seed) noexcept + : m_state(SplitMix64{ seed }.generateSeedSequence<4>()) {} + + inline constexpr Xoshiro256StarStar::Xoshiro256StarStar(const state_type state) noexcept + : m_state(state) {} + + inline constexpr Xoshiro256StarStar::result_type Xoshiro256StarStar::operator()() noexcept + { + const std::uint64_t result = detail::RotL(m_state[1] * 5, 7) * 9; + const std::uint64_t t = m_state[1] << 17; + m_state[2] ^= m_state[0]; + m_state[3] ^= m_state[1]; + m_state[1] ^= m_state[2]; + m_state[0] ^= m_state[3]; + m_state[2] ^= t; + m_state[3] = detail::RotL(m_state[3], 45); + return result; + } + + inline constexpr void Xoshiro256StarStar::jump() noexcept + { + constexpr std::uint64_t JUMP[] = { 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c }; + + std::uint64_t s0 = 0; + std::uint64_t s1 = 0; + std::uint64_t s2 = 0; + std::uint64_t s3 = 0; + + for (std::uint64_t jump : JUMP) + { + for (int b = 0; b < 64; ++b) + { + if (jump & UINT64_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + s2 ^= m_state[2]; + s3 ^= m_state[3]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + m_state[2] = s2; + m_state[3] = s3; + } + + inline constexpr void Xoshiro256StarStar::longJump() noexcept + { + constexpr std::uint64_t LONG_JUMP[] = { 0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635 }; + + std::uint64_t s0 = 0; + std::uint64_t s1 = 0; + std::uint64_t s2 = 0; + std::uint64_t s3 = 0; + + for (std::uint64_t jump : LONG_JUMP) + { + for (int b = 0; b < 64; ++b) + { + if (jump & UINT64_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + s2 ^= m_state[2]; + s3 ^= m_state[3]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + m_state[2] = s2; + m_state[3] = s3; + } + + inline constexpr Xoshiro256StarStar::result_type Xoshiro256StarStar::min() noexcept + { + return std::numeric_limits::lowest(); + } + + inline constexpr Xoshiro256StarStar::result_type Xoshiro256StarStar::max() noexcept + { + return std::numeric_limits::max(); + } + + inline constexpr Xoshiro256StarStar::state_type Xoshiro256StarStar::serialize() const noexcept + { + return m_state; + } + + inline constexpr void Xoshiro256StarStar::deserialize(const state_type state) noexcept + { + m_state = state; + } + + //////////////////////////////////////////////////////////////// + // + // xoroshiro128+ + // + inline constexpr Xoroshiro128Plus::Xoroshiro128Plus(const std::uint64_t seed) noexcept + : m_state(SplitMix64{ seed }.generateSeedSequence<2>()) {} + + inline constexpr Xoroshiro128Plus::Xoroshiro128Plus(const state_type state) noexcept + : m_state(state) {} + + inline constexpr Xoroshiro128Plus::result_type Xoroshiro128Plus::operator()() noexcept + { + const std::uint64_t s0 = m_state[0]; + std::uint64_t s1 = m_state[1]; + const std::uint64_t result = s0 + s1; + s1 ^= s0; + m_state[0] = detail::RotL(s0, 24) ^ s1 ^ (s1 << 16); + m_state[1] = detail::RotL(s1, 37); + return result; + } + + inline constexpr void Xoroshiro128Plus::jump() noexcept + { + constexpr std::uint64_t JUMP[] = { 0xdf900294d8f554a5, 0x170865df4b3201fc }; + + std::uint64_t s0 = 0; + std::uint64_t s1 = 0; + + for (std::uint64_t jump : JUMP) + { + for (int b = 0; b < 64; ++b) + { + if (jump & UINT64_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + } + + inline constexpr void Xoroshiro128Plus::longJump() noexcept + { + constexpr std::uint64_t LONG_JUMP[] = { 0xd2a98b26625eee7b, 0xdddf9b1090aa7ac1 }; + + std::uint64_t s0 = 0; + std::uint64_t s1 = 0; + + for (std::uint64_t jump : LONG_JUMP) + { + for (int b = 0; b < 64; ++b) + { + if (jump & UINT64_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + } + + inline constexpr Xoroshiro128Plus::result_type Xoroshiro128Plus::min() noexcept + { + return std::numeric_limits::lowest(); + } + + inline constexpr Xoroshiro128Plus::result_type Xoroshiro128Plus::max() noexcept + { + return std::numeric_limits::max(); + } + + inline constexpr Xoroshiro128Plus::state_type Xoroshiro128Plus::serialize() const noexcept + { + return m_state; + } + + inline constexpr void Xoroshiro128Plus::deserialize(const state_type state) noexcept + { + m_state = state; + } + + //////////////////////////////////////////////////////////////// + // + // xoroshiro128++ + // + inline constexpr Xoroshiro128PlusPlus::Xoroshiro128PlusPlus(const std::uint64_t seed) noexcept + : m_state(SplitMix64{ seed }.generateSeedSequence<2>()) {} + + inline constexpr Xoroshiro128PlusPlus::Xoroshiro128PlusPlus(const state_type state) noexcept + : m_state(state) {} + + inline constexpr Xoroshiro128PlusPlus::result_type Xoroshiro128PlusPlus::operator()() noexcept + { + const std::uint64_t s0 = m_state[0]; + std::uint64_t s1 = m_state[1]; + const std::uint64_t result = detail::RotL(s0 + s1, 17) + s0; + s1 ^= s0; + m_state[0] = detail::RotL(s0, 49) ^ s1 ^ (s1 << 21); + m_state[1] = detail::RotL(s1, 28); + return result; + } + + inline constexpr void Xoroshiro128PlusPlus::jump() noexcept + { + constexpr std::uint64_t JUMP[] = { 0x2bd7a6a6e99c2ddc, 0x0992ccaf6a6fca05 }; + + std::uint64_t s0 = 0; + std::uint64_t s1 = 0; + + for (std::uint64_t jump : JUMP) + { + for (int b = 0; b < 64; ++b) + { + if (jump & UINT64_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + } + + inline constexpr void Xoroshiro128PlusPlus::longJump() noexcept + { + constexpr std::uint64_t LONG_JUMP[] = { 0x360fd5f2cf8d5d99, 0x9c6e6877736c46e3 }; + + std::uint64_t s0 = 0; + std::uint64_t s1 = 0; + + for (std::uint64_t jump : LONG_JUMP) + { + for (int b = 0; b < 64; ++b) + { + if (jump & UINT64_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + } + + inline constexpr Xoroshiro128PlusPlus::result_type Xoroshiro128PlusPlus::min() noexcept + { + return std::numeric_limits::lowest(); + } + + inline constexpr Xoroshiro128PlusPlus::result_type Xoroshiro128PlusPlus::max() noexcept + { + return std::numeric_limits::max(); + } + + inline constexpr Xoroshiro128PlusPlus::state_type Xoroshiro128PlusPlus::serialize() const noexcept + { + return m_state; + } + + inline constexpr void Xoroshiro128PlusPlus::deserialize(const state_type state) noexcept + { + m_state = state; + } + + //////////////////////////////////////////////////////////////// + // + // xoroshiro128** + // + inline constexpr Xoroshiro128StarStar::Xoroshiro128StarStar(const std::uint64_t seed) noexcept + : m_state(SplitMix64{ seed }.generateSeedSequence<2>()) {} + + inline constexpr Xoroshiro128StarStar::Xoroshiro128StarStar(const state_type state) noexcept + : m_state(state) {} + + inline constexpr Xoroshiro128StarStar::result_type Xoroshiro128StarStar::operator()() noexcept + { + const std::uint64_t s0 = m_state[0]; + std::uint64_t s1 = m_state[1]; + const std::uint64_t result = detail::RotL(s0 * 5, 7) * 9; + s1 ^= s0; + m_state[0] = detail::RotL(s0, 24) ^ s1 ^ (s1 << 16); + m_state[1] = detail::RotL(s1, 37); + return result; + } + + inline constexpr void Xoroshiro128StarStar::jump() noexcept + { + constexpr std::uint64_t JUMP[] = { 0xdf900294d8f554a5, 0x170865df4b3201fc }; + + std::uint64_t s0 = 0; + std::uint64_t s1 = 0; + + for (std::uint64_t jump : JUMP) + { + for (int b = 0; b < 64; ++b) + { + if (jump & UINT64_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + } + + inline constexpr void Xoroshiro128StarStar::longJump() noexcept + { + constexpr std::uint64_t LONG_JUMP[] = { 0xd2a98b26625eee7b, 0xdddf9b1090aa7ac1 }; + + std::uint64_t s0 = 0; + std::uint64_t s1 = 0; + + for (std::uint64_t jump : LONG_JUMP) + { + for (int b = 0; b < 64; ++b) + { + if (jump & UINT64_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + } + + inline constexpr Xoroshiro128StarStar::result_type Xoroshiro128StarStar::min() noexcept + { + return std::numeric_limits::lowest(); + } + + inline constexpr Xoroshiro128StarStar::result_type Xoroshiro128StarStar::max() noexcept + { + return std::numeric_limits::max(); + } + + inline constexpr Xoroshiro128StarStar::state_type Xoroshiro128StarStar::serialize() const noexcept + { + return m_state; + } + + inline constexpr void Xoroshiro128StarStar::deserialize(const state_type state) noexcept + { + m_state = state; + } + + //////////////////////////////////////////////////////////////// + // + // xoshiro128+ + // + inline constexpr Xoshiro128Plus::Xoshiro128Plus(const std::uint64_t seed) noexcept + : m_state() + { + SplitMix64 splitmix{ seed }; + + for (auto& state : m_state) + { + state = static_cast(splitmix()); + } + } + + inline constexpr Xoshiro128Plus::Xoshiro128Plus(const state_type state) noexcept + : m_state(state) {} + + inline constexpr Xoshiro128Plus::result_type Xoshiro128Plus::operator()() noexcept + { + const std::uint32_t result = m_state[0] + m_state[3]; + const std::uint32_t t = m_state[1] << 9; + m_state[2] ^= m_state[0]; + m_state[3] ^= m_state[1]; + m_state[1] ^= m_state[2]; + m_state[0] ^= m_state[3]; + m_state[2] ^= t; + m_state[3] = detail::RotL(m_state[3], 11); + return result; + } + + inline constexpr void Xoshiro128Plus::jump() noexcept + { + constexpr std::uint32_t JUMP[] = { 0x8764000b, 0xf542d2d3, 0x6fa035c3, 0x77f2db5b }; + + std::uint32_t s0 = 0; + std::uint32_t s1 = 0; + std::uint32_t s2 = 0; + std::uint32_t s3 = 0; + + for (std::uint32_t jump : JUMP) + { + for (int b = 0; b < 32; ++b) + { + if (jump & UINT32_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + s2 ^= m_state[2]; + s3 ^= m_state[3]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + m_state[2] = s2; + m_state[3] = s3; + } + + inline constexpr void Xoshiro128Plus::longJump() noexcept + { + constexpr std::uint32_t LONG_JUMP[] = { 0xb523952e, 0x0b6f099f, 0xccf5a0ef, 0x1c580662 }; + + std::uint32_t s0 = 0; + std::uint32_t s1 = 0; + std::uint32_t s2 = 0; + std::uint32_t s3 = 0; + + for (std::uint32_t jump : LONG_JUMP) + { + for (int b = 0; b < 32; ++b) + { + if (jump & UINT32_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + s2 ^= m_state[2]; + s3 ^= m_state[3]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + m_state[2] = s2; + m_state[3] = s3; + } + + inline constexpr Xoshiro128Plus::result_type Xoshiro128Plus::min() noexcept + { + return std::numeric_limits::lowest(); + } + + inline constexpr Xoshiro128Plus::result_type Xoshiro128Plus::max() noexcept + { + return std::numeric_limits::max(); + } + + inline constexpr Xoshiro128Plus::state_type Xoshiro128Plus::serialize() const noexcept + { + return m_state; + } + + inline constexpr void Xoshiro128Plus::deserialize(const state_type state) noexcept + { + m_state = state; + } + + //////////////////////////////////////////////////////////////// + // + // xoshiro128++ + // + inline constexpr Xoshiro128PlusPlus::Xoshiro128PlusPlus(const std::uint64_t seed) noexcept + : m_state() + { + SplitMix64 splitmix{ seed }; + + for (auto& state : m_state) + { + state = static_cast(splitmix()); + } + } + + inline constexpr Xoshiro128PlusPlus::Xoshiro128PlusPlus(const state_type state) noexcept + : m_state(state) {} + + inline constexpr Xoshiro128PlusPlus::result_type Xoshiro128PlusPlus::operator()() noexcept + { + const std::uint32_t result = detail::RotL(m_state[0] + m_state[3], 7) + m_state[0]; + const std::uint32_t t = m_state[1] << 9; + m_state[2] ^= m_state[0]; + m_state[3] ^= m_state[1]; + m_state[1] ^= m_state[2]; + m_state[0] ^= m_state[3]; + m_state[2] ^= t; + m_state[3] = detail::RotL(m_state[3], 11); + return result; + } + + inline constexpr void Xoshiro128PlusPlus::jump() noexcept + { + constexpr std::uint32_t JUMP[] = { 0x8764000b, 0xf542d2d3, 0x6fa035c3, 0x77f2db5b }; + + std::uint32_t s0 = 0; + std::uint32_t s1 = 0; + std::uint32_t s2 = 0; + std::uint32_t s3 = 0; + + for (std::uint32_t jump : JUMP) + { + for (int b = 0; b < 32; ++b) + { + if (jump & UINT32_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + s2 ^= m_state[2]; + s3 ^= m_state[3]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + m_state[2] = s2; + m_state[3] = s3; + } + + inline constexpr void Xoshiro128PlusPlus::longJump() noexcept + { + constexpr std::uint32_t LONG_JUMP[] = { 0xb523952e, 0x0b6f099f, 0xccf5a0ef, 0x1c580662 }; + + std::uint32_t s0 = 0; + std::uint32_t s1 = 0; + std::uint32_t s2 = 0; + std::uint32_t s3 = 0; + + for (std::uint32_t jump : LONG_JUMP) + { + for (int b = 0; b < 32; ++b) + { + if (jump & UINT32_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + s2 ^= m_state[2]; + s3 ^= m_state[3]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + m_state[2] = s2; + m_state[3] = s3; + } + + inline constexpr Xoshiro128PlusPlus::result_type Xoshiro128PlusPlus::min() noexcept + { + return std::numeric_limits::lowest(); + } + + inline constexpr Xoshiro128PlusPlus::result_type Xoshiro128PlusPlus::max() noexcept + { + return std::numeric_limits::max(); + } + + inline constexpr Xoshiro128PlusPlus::state_type Xoshiro128PlusPlus::serialize() const noexcept + { + return m_state; + } + + inline constexpr void Xoshiro128PlusPlus::deserialize(const state_type state) noexcept + { + m_state = state; + } + + //////////////////////////////////////////////////////////////// + // + // xoshiro128** + // + inline constexpr Xoshiro128StarStar::Xoshiro128StarStar(const std::uint64_t seed) noexcept + : m_state() + { + SplitMix64 splitmix{ seed }; + + for (auto& state : m_state) + { + state = static_cast(splitmix()); + } + } + + inline constexpr Xoshiro128StarStar::Xoshiro128StarStar(const state_type state) noexcept + : m_state(state) {} + + inline constexpr Xoshiro128StarStar::result_type Xoshiro128StarStar::operator()() noexcept + { + const std::uint32_t result = detail::RotL(m_state[1] * 5, 7) * 9; + const std::uint32_t t = m_state[1] << 9; + m_state[2] ^= m_state[0]; + m_state[3] ^= m_state[1]; + m_state[1] ^= m_state[2]; + m_state[0] ^= m_state[3]; + m_state[2] ^= t; + m_state[3] = detail::RotL(m_state[3], 11); + return result; + } + + inline constexpr void Xoshiro128StarStar::jump() noexcept + { + constexpr std::uint32_t JUMP[] = { 0x8764000b, 0xf542d2d3, 0x6fa035c3, 0x77f2db5b }; + + std::uint32_t s0 = 0; + std::uint32_t s1 = 0; + std::uint32_t s2 = 0; + std::uint32_t s3 = 0; + + for (std::uint32_t jump : JUMP) + { + for (int b = 0; b < 32; ++b) + { + if (jump & UINT32_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + s2 ^= m_state[2]; + s3 ^= m_state[3]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + m_state[2] = s2; + m_state[3] = s3; + } + + inline constexpr void Xoshiro128StarStar::longJump() noexcept + { + constexpr std::uint32_t LONG_JUMP[] = { 0xb523952e, 0x0b6f099f, 0xccf5a0ef, 0x1c580662 }; + + std::uint32_t s0 = 0; + std::uint32_t s1 = 0; + std::uint32_t s2 = 0; + std::uint32_t s3 = 0; + + for (std::uint32_t jump : LONG_JUMP) + { + for (int b = 0; b < 32; ++b) + { + if (jump & UINT32_C(1) << b) + { + s0 ^= m_state[0]; + s1 ^= m_state[1]; + s2 ^= m_state[2]; + s3 ^= m_state[3]; + } + operator()(); + } + } + + m_state[0] = s0; + m_state[1] = s1; + m_state[2] = s2; + m_state[3] = s3; + } + + inline constexpr Xoshiro128StarStar::result_type Xoshiro128StarStar::min() noexcept + { + return std::numeric_limits::lowest(); + } + + inline constexpr Xoshiro128StarStar::result_type Xoshiro128StarStar::max() noexcept + { + return std::numeric_limits::max(); + } + + inline constexpr Xoshiro128StarStar::state_type Xoshiro128StarStar::serialize() const noexcept + { + return m_state; + } + + inline constexpr void Xoshiro128StarStar::deserialize(const state_type state) noexcept + { + m_state = state; + } +} diff --git a/src/main.cpp b/src/main.cpp index 48b479d..e403747 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,7 +7,9 @@ void MessageHandler(SKSE::MessagingInterface::Message* a_message) case SKSE::MessagingInterface::kPostLoad: BaseObjectSwapper::Install(); case SKSE::MessagingInterface::kDataLoaded: - //FormSwap::Manager::GetSingleton()->PrintConflicts(); + FormSwap::Manager::GetSingleton()->PrintConflicts(); + break; + default: break; } } @@ -18,7 +20,8 @@ extern "C" DLLEXPORT constinit auto SKSEPlugin_Version = []() { v.PluginVersion(Version::MAJOR); v.PluginName("Base Object Swapper"); v.AuthorName("powerofthree"); - v.UsesAddressLibrary(true); + v.UsesAddressLibrary(); + v.UsesNoStructs(); v.CompatibleVersions({ SKSE::RUNTIME_LATEST }); return v; From cb3f1bf7baf71af70e19bf79244c45254573908c Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Thu, 27 Oct 2022 06:14:15 +0530 Subject: [PATCH 08/19] Update to MergeMapper lib --- CMakeLists.txt | 3 + CMakePresets.json | 48 +---------- cmake/headerlist.cmake | 1 - cmake/ports/mergemapper/portfile.cmake | 15 ++++ cmake/ports/mergemapper/vcpkg.json | 7 ++ cmake/sourcelist.cmake | 1 - src/Hooks.cpp | 5 -- src/MergeMapper.cpp | 105 ------------------------- src/MergeMapper.h | 17 ---- src/PCH.h | 1 + src/SwapData.cpp | 13 ++- src/SwapData.h | 2 - src/main.cpp | 13 +++ vcpkg.json | 2 +- 14 files changed, 54 insertions(+), 179 deletions(-) create mode 100644 cmake/ports/mergemapper/portfile.cmake create mode 100644 cmake/ports/mergemapper/vcpkg.json delete mode 100644 src/MergeMapper.cpp delete mode 100644 src/MergeMapper.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bde276..9dad9cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,6 +135,7 @@ endif() find_path(SIMPLEINI_INCLUDE_DIRS "ConvertUTF.c") find_path(SRELL_INCLUDE_DIRS "srell.hpp") +find_path(MERGEMAPPER_INCLUDE_DIRS "MergeMapperPluginAPI.h") # ---- Add source files ---- @@ -167,6 +168,7 @@ add_library( ${CMAKE_CURRENT_BINARY_DIR}/version.rc .clang-format .editorconfig + ${MERGEMAPPER_INCLUDE_DIRS}/MergeMapperPluginAPI.cpp ) target_compile_features( @@ -188,6 +190,7 @@ target_include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/src ${SIMPLEINI_INCLUDE_DIRS} ${SRELL_INCLUDE_DIRS} + ${MERGEMAPPER_INCLUDE_DIRS} ) target_link_libraries( diff --git a/CMakePresets.json b/CMakePresets.json index 7ecd3dc..4ace03c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -22,7 +22,7 @@ "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": { "type": "STRING", - "value": "$env{VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" + "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" }, "VCPKG_OVERLAY_PORTS": { "type": "STRING", @@ -48,47 +48,7 @@ }, { "cacheVariables": { - "CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /external:anglebrackets /external:W0 $penv{CXXFLAGS}" - }, - "generator": "Visual Studio 16 2019", - "inherits": [ - "cmake-dev", - "vcpkg", - "windows" - ], - "name": "vs2019-windows-vcpkg-se" - }, - { - "binaryDir": "${sourceDir}/buildvr", - "cacheVariables": { - "CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /external:anglebrackets /external:W0 $penv{CXXFLAGS}", - "BUILD_SKYRIMVR": true - }, - "generator": "Visual Studio 16 2019", - "inherits": [ - "cmake-dev", - "vcpkg", - "windows" - ], - "name": "vs2019-windows-vcpkg-vr" - }, - { - "binaryDir": "${sourceDir}/buildae", - "cacheVariables": { - "CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /external:anglebrackets /external:W0 $penv{CXXFLAGS}", - "BUILD_SKYRIMAE": true - }, - "generator": "Visual Studio 16 2019", - "inherits": [ - "cmake-dev", - "vcpkg", - "windows" - ], - "name": "vs2019-windows-vcpkg-ae" - }, - { - "cacheVariables": { - "CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /WX /external:W0" + "CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /WX /external:W0 $penv{CXXFLAGS}" }, "generator": "Visual Studio 17 2022", "inherits": [ @@ -102,7 +62,7 @@ { "binaryDir": "${sourceDir}/buildvr", "cacheVariables": { - "CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /WX /external:W0", + "CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /WX /external:W0 $penv{CXXFLAGS}", "BUILD_SKYRIMVR": true }, "generator": "Visual Studio 17 2022", @@ -117,7 +77,7 @@ { "binaryDir": "${sourceDir}/buildae", "cacheVariables": { - "CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /WX /external:W0", + "CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /WX /external:W0 $penv{CXXFLAGS}", "BUILD_SKYRIMAE": true }, "generator": "Visual Studio 17 2022", diff --git a/cmake/headerlist.cmake b/cmake/headerlist.cmake index ca5a75d..9655fb7 100644 --- a/cmake/headerlist.cmake +++ b/cmake/headerlist.cmake @@ -1,7 +1,6 @@ set(headers ${headers} src/Hooks.h src/Manager.h - src/MergeMapper.h src/PCH.h src/SwapData.h src/XoshiroCpp.hpp diff --git a/cmake/ports/mergemapper/portfile.cmake b/cmake/ports/mergemapper/portfile.cmake new file mode 100644 index 0000000..747af66 --- /dev/null +++ b/cmake/ports/mergemapper/portfile.cmake @@ -0,0 +1,15 @@ +# header-only library +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO alandtse/MergeMapper + REF 16f8f427b0fa54b26f7825a7f10671088bd54f34 + SHA512 0f8114cd1e441971521bc902090eae99f2eac5c8c7ab938254c7ed867510384a0afa7730d9ffbe1942348f3a6893bd1db8edea247bd8c7e269f4753b4b1413bc + HEAD_REF main +) + +# Install codes +set(MERGEMAPPER_SOURCE ${SOURCE_PATH}/src/MergeMapperPluginAPI.cpp ${SOURCE_PATH}/include/MergeMapperPluginAPI.h) + +file(INSTALL ${MERGEMAPPER_SOURCE} DESTINATION ${CURRENT_PACKAGES_DIR}/include) + +file(INSTALL ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/${PORT} RENAME copyright) diff --git a/cmake/ports/mergemapper/vcpkg.json b/cmake/ports/mergemapper/vcpkg.json new file mode 100644 index 0000000..f3ba8cc --- /dev/null +++ b/cmake/ports/mergemapper/vcpkg.json @@ -0,0 +1,7 @@ +{ + "name": "mergemapper", + "version-string": "1.2.0", + "port-version": 1, + "description": "A SKSE plugin to dynamically map zmerges.", + "homepage": "https://github.com/alandtse/MergeMapper" + } diff --git a/cmake/sourcelist.cmake b/cmake/sourcelist.cmake index ba2d0cd..20c5d5d 100644 --- a/cmake/sourcelist.cmake +++ b/cmake/sourcelist.cmake @@ -1,7 +1,6 @@ set(sources ${sources} src/Hooks.cpp src/Manager.cpp - src/MergeMapper.cpp src/PCH.cpp src/SwapData.cpp src/main.cpp diff --git a/src/Hooks.cpp b/src/Hooks.cpp index 094a619..761ef8f 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -1,6 +1,5 @@ #include "Hooks.h" #include "Manager.h" -#include "MergeMapper.h" namespace BaseObjectSwapper { @@ -64,10 +63,6 @@ namespace BaseObjectSwapper void Install() { - logger::info("{:*^30}", "MERGES"); - - MergeMapper::GetMerges(); - logger::info("{:*^30}", "HOOKS"); TESObjectREFR::Install(); diff --git a/src/MergeMapper.cpp b/src/MergeMapper.cpp deleted file mode 100644 index eeb4445..0000000 --- a/src/MergeMapper.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include "MergeMapper.h" - -namespace MergeMapper -{ - bool GetMerges() - { - using json = nlohmann::json; - logger::info("Searching for merges within the Data folder"); - auto constexpr folder = R"(Data\)"; - json json_data; - size_t total = 0; - auto constexpr mergePrefix = L"Data\\merge - "; - for (const auto& entry : std::filesystem::directory_iterator(folder)) { - // zMerge folders have name "merge - 018auri" - std::wstring path = L""; - try { - path = entry.path().wstring(); - } catch (std::exception& e) { - logger::warn(" Unable to convert path to string: {}", e.what()); - } - if (entry.exists() && entry.is_directory() && path.starts_with(mergePrefix)) { - auto file = path + L"\\merge.json"; - auto merged = path.substr(13) + L".esp"; - try { - std::ifstream json_file(file); - json_file >> json_data; - json_file.close(); - auto filename = json_data["filename"].get(); - merged = std::wstring(filename.begin(), filename.end()); - } catch (std::exception& e) { - logger::warn(" Unable to open {}, defaulting filename to {}:{}", stl::utf16_to_utf8(file).value_or(""s), stl::utf16_to_utf8(merged).value_or(""s), e.what()); - } - file = path + L"\\map.json"; - try { - std::ifstream json_file(file); - json_file >> json_data; - json_file.close(); - } catch (std::exception& e) { - logger::warn(" Unable to open {}:{}", stl::utf16_to_utf8(file).value_or(""s), e.what()); - } - auto converted_merged = stl::utf16_to_utf8(merged).value_or(""s); //json requires wstring conversion to utf encoding https://json.nlohmann.me/home/faq/#parse-errors-reading-non-ascii-characters - if (!std::filesystem::exists(folder + converted_merged)) { - logger::warn(" {} does not exist, not processing merges for this file", converted_merged); - continue; - } - if (converted_merged != "" && !json_data.empty()) { - for (auto& [esp, idmap] : json_data.items()) { - auto espkey = esp; - std::transform(espkey.begin(), espkey.end(), espkey.begin(), [](auto ch) { return static_cast(std::tolower(ch)); }); - if (idmap.size()) { - logger::info(" Found {} maps to {} with {} mappings", esp, converted_merged, idmap.size()); - total += idmap.size(); - } - if (mergeMap.contains(espkey)) - logger::warn(" Duplicate {} found in {}", esp, converted_merged); - mergeMap[espkey]["name"] = converted_merged; - if (!idmap.empty()) { - for (auto& [key, value] : idmap.items()) { - auto storedKey = std::to_string(std::stoi(key, 0, 16)); - std::transform(storedKey.begin(), storedKey.end(), storedKey.begin(), [](auto ch) { return static_cast(std::tolower(ch)); }); - auto storedValue = std::to_string(std::stoi(value.get(), 0, 16)); - std::transform(storedValue.begin(), storedValue.end(), storedValue.begin(), [](auto ch) { return static_cast(std::tolower(ch)); }); - mergeMap[espkey]["map"][storedKey] = storedValue; - } - } - } - } - } - } - if (mergeMap.empty()) { - logger::info(" No merges were found within the Data folder"); - return false; - } - logger::info(" {} merges found with {} mappings", mergeMap.size(), total); - return true; - } - - std::pair GetNewFormID(std::wstring oldName, std::string oldFormID) - { - auto converted_oldName = stl::utf16_to_utf8(oldName).value_or(""s); //json requires wstring conversion to utf https://json.nlohmann.me/home/faq/#parse-errors-reading-non-ascii-characters - if (converted_oldName == "") - logger::error(" Unable to convert oldName to UTF encoding; no mapping possible"); - return std::make_pair(converted_oldName, std::stoi(oldFormID, 0, 16)); - } - - std::pair GetNewFormID(std::string oldName, std::string oldFormID) - { - auto modName = oldName; - auto espkey = oldName; - std::transform(espkey.begin(), espkey.end(), espkey.begin(), [](auto ch) { return static_cast(std::tolower(ch)); }); - RE::FormID formID = std::stoi(oldFormID, 0, 16); - //check for merged esps - if (mergeMap.contains(espkey)) { - modName = mergeMap[espkey]["name"]; - auto storedKey = std::to_string(formID); - if (!mergeMap[espkey]["map"].empty()) { - std::transform(storedKey.begin(), storedKey.end(), storedKey.begin(), [](auto ch) { return static_cast(std::tolower(ch)); }); - if (mergeMap[espkey]["map"].contains(storedKey)) { - formID = std::stoi(mergeMap[espkey]["map"][storedKey].get()); - } - } - } - return std::make_pair(modName, formID); - } -} diff --git a/src/MergeMapper.h b/src/MergeMapper.h deleted file mode 100644 index eac9de3..0000000 --- a/src/MergeMapper.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include - -static nlohmann::json mergeMap; - -namespace MergeMapper -{ - /// @brief Search the data directory for any zmerge merges. This searches for map.json files to build a mapping table. - /// @return true if any merge mappings found - bool GetMerges(); - - /// @brief Get the new modName and formID - /// @param oldName The original modName string e.g., Dragonborn.esp - /// @param oldFormID The original formID in hex format as a string e.g., 0x134ab - /// @return a pair with string modName and uint32 FormID. If no merge is found, it will return oldName and oldFormID. - std::pair GetNewFormID(std::string oldName, std::string oldFormID); -} diff --git a/src/PCH.h b/src/PCH.h index 868c0ce..939c028 100644 --- a/src/PCH.h +++ b/src/PCH.h @@ -4,6 +4,7 @@ #include "SKSE/SKSE.h" #include "XoshiroCpp.hpp" +#include #include #include #include diff --git a/src/SwapData.cpp b/src/SwapData.cpp index 3c5ab48..d3c74c8 100644 --- a/src/SwapData.cpp +++ b/src/SwapData.cpp @@ -137,9 +137,16 @@ namespace FormSwap RE::FormID SwapData::GetFormID(const std::string& a_str) { if (a_str.contains('~')) { - const auto formPair = string::split(a_str, "~"); - const auto [modName, formID] = MergeMapper::GetNewFormID(formPair[1], formPair[0]); - return RE::TESDataHandler::GetSingleton()->LookupFormID(formID, modName); + if (const auto splitID = string::split(a_str, "~"); splitID.size() == 2) { + const auto formID = string::lexical_cast(splitID[0], true); + const auto& modName = splitID[1]; + if (g_mergeMapperInterface) { + const auto [mergedModName, mergedFormID] = g_mergeMapperInterface->GetNewFormID(modName.c_str(), formID); + return RE::TESDataHandler::GetSingleton()->LookupFormID(mergedFormID, mergedModName); + } else { + return RE::TESDataHandler::GetSingleton()->LookupFormID(formID, modName); + } + } } if (const auto form = RE::TESForm::LookupByEditorID(a_str); form) { return form->GetFormID(); diff --git a/src/SwapData.h b/src/SwapData.h index 81824e2..ef70b9d 100644 --- a/src/SwapData.h +++ b/src/SwapData.h @@ -1,7 +1,5 @@ #pragma once -#include "MergeMapper.h" - namespace FormSwap { using FormOrEditorID = std::variant; diff --git a/src/main.cpp b/src/main.cpp index e403747..ce9d2fd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,19 @@ void MessageHandler(SKSE::MessagingInterface::Message* a_message) switch (a_message->type) { case SKSE::MessagingInterface::kPostLoad: BaseObjectSwapper::Install(); + break; + case SKSE::MessagingInterface::kPostPostLoad: + { + logger::info("{:*^30}", "MERGES"); + MergeMapperPluginAPI::GetMergeMapperInterface001(); + if (g_mergeMapperInterface) { + const auto version = g_mergeMapperInterface->GetBuildNumber(); + logger::info("Got MergeMapper interface buildnumber {}", version); + } else { + logger::info("MergeMapper not detected"); + } + } + break; case SKSE::MessagingInterface::kDataLoaded: FormSwap::Manager::GetSingleton()->PrintConflicts(); break; diff --git a/vcpkg.json b/vcpkg.json index 38975f0..7f3e421 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -6,7 +6,7 @@ "license": "MIT", "dependencies": [ "boost-stl-interfaces", - "nlohmann-json", + "mergemapper", "robin-hood-hashing", "rsm-binary-io", "srell", From e8778d46716e3b9b8fb471b63685cb2a72436027 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Thu, 27 Oct 2022 22:57:15 +0530 Subject: [PATCH 09/19] Add percentage based swaps --- cmake/ports/mergemapper/vcpkg.json | 12 +-- src/Hooks.cpp | 6 +- src/Manager.cpp | 132 ++++++++++++++--------------- src/Manager.h | 28 +++--- src/SwapData.cpp | 34 +++++--- src/SwapData.h | 22 ++++- src/main.cpp | 2 +- 7 files changed, 125 insertions(+), 111 deletions(-) diff --git a/cmake/ports/mergemapper/vcpkg.json b/cmake/ports/mergemapper/vcpkg.json index f3ba8cc..39d46ac 100644 --- a/cmake/ports/mergemapper/vcpkg.json +++ b/cmake/ports/mergemapper/vcpkg.json @@ -1,7 +1,7 @@ { - "name": "mergemapper", - "version-string": "1.2.0", - "port-version": 1, - "description": "A SKSE plugin to dynamically map zmerges.", - "homepage": "https://github.com/alandtse/MergeMapper" - } + "name": "mergemapper", + "version-string": "1.2.0", + "port-version": 1, + "description": "A SKSE plugin to dynamically map zmerges.", + "homepage": "https://github.com/alandtse/MergeMapper" +} diff --git a/src/Hooks.cpp b/src/Hooks.cpp index 761ef8f..fa6ce2f 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -13,8 +13,8 @@ namespace BaseObjectSwapper auto [swapBase, transformData] = FormSwap::Manager::GetSingleton()->GetSwapData(a_ref, base); if (swapBase) { a_ref->SetObjectReference(swapBase); - transformData.SetTransform(a_ref); } + transformData.SetTransform(a_ref); } } } @@ -25,7 +25,7 @@ namespace BaseObjectSwapper { static void thunk(RE::TESObjectREFR* a_ref) { - detail::swap_base(a_ref); + detail::swap_base(a_ref); func(a_ref); } static inline REL::Relocation func; @@ -65,7 +65,7 @@ namespace BaseObjectSwapper { logger::info("{:*^30}", "HOOKS"); - TESObjectREFR::Install(); + TESObjectREFR::Install(); Character::Install(); } } diff --git a/src/Manager.cpp b/src/Manager.cpp index 1b917bd..94a54ab 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -2,51 +2,44 @@ namespace FormSwap { - SwapMap& Manager::get_form_map(const std::string& a_str) + SwapMap& Manager::get_form_map(const std::string& a_str) { return a_str == "Forms" ? swapForms : swapRefs; } - ConflictMap& Manager::get_conflict_map(const std::string& a_str) - { - return a_str == "Forms" ? conflictForms : conflictRefs; - } - - std::pair Manager::get_forms_impl(const std::string& a_str, std::function a_func) + void Manager::get_forms_impl(const std::string& a_path, const std::string& a_str, std::function a_func) { const auto formPair = string::split(a_str, "|"); - if (auto baseFormID = SwapData::GetFormID(formPair[0]); baseFormID != 0) { - const auto swapFormID = SwapData::GetSwapFormID(formPair[1]); - const auto transform = formPair.size() > 2 ? formPair[2] : std::string{}; - const auto traits = formPair.size() > 3 ? formPair[3] : std::string{}; - - if (!swap_empty(swapFormID)) { - SwapData swapData(swapFormID, transform, traits); + if (const auto baseFormID = SwapData::GetFormID(formPair[0]); baseFormID != 0) { + if (const auto swapFormID = SwapData::GetSwapFormID(formPair[1]); !swap_empty(swapFormID)) { + const SwapData::Input input( + formPair.size() > 2 ? formPair[2] : std::string{}, // transform + formPair.size() > 3 ? formPair[3] : std::string{}, // traits + a_str, + a_path); + SwapData swapData(swapFormID, input); a_func(baseFormID, swapData); - return { true, baseFormID }; } else { logger::error(" failed to process {} (SWAP formID not found)", a_str); - return { false, 0 }; } } else { logger::error(" failed to process {} (BASE formID not found)", a_str); - return { false, 0 }; } } - std::pair Manager::get_forms(const std::string& a_str, SwapMap& a_map) + void Manager::get_forms(const std::string& a_path, const std::string& a_str, SwapMap& a_map) { - return get_forms_impl(a_str, [&](RE::FormID a_baseID, SwapData& a_swapData) { - a_map.insert_or_assign(a_baseID, a_swapData); + return get_forms_impl(a_path, a_str, [&](RE::FormID a_baseID, const SwapData& a_swapData) { + a_map[a_baseID].push_back(a_swapData); }); } - std::pair Manager::get_forms(const std::string& a_str, const std::vector& a_conditionalIDs, SwapMap& a_map) + void Manager::get_forms(const std::string& a_path, const std::string& a_str, const std::vector& a_conditionalIDs, SwapMap& a_map) { - return get_forms_impl(a_str, [&](const RE::FormID a_baseID, SwapData& a_swapData) { + return get_forms_impl(a_path, a_str, [&](const RE::FormID a_baseID, const SwapData& a_swapData) { for (auto& id : a_conditionalIDs) { - a_map[a_baseID].emplace(id, a_swapData); + a_map[a_baseID][id].push_back(a_swapData); } }); } @@ -59,6 +52,8 @@ namespace FormSwap init = true; + logger::info("{:*^30}", "INI"); + std::vector configs; auto constexpr folder = R"(Data\)"; @@ -96,30 +91,22 @@ namespace FormSwap ini.GetAllSections(sections); sections.sort(CSimpleIniA::Entry::LoadOrder()); - constexpr auto map_conflicts = [](const std::string& a_str, const std::string& a_path, RE::FormID a_baseID, ConflictMap& a_conflictMap) { - a_conflictMap[a_baseID].emplace_back(std::make_pair(a_str, a_path)); + constexpr auto push_filter = [](const std::string& a_condition, std::vector& a_processedFilters) { + if (const auto processedID = SwapData::GetFormID(a_condition); processedID != 0) { + a_processedFilters.push_back(processedID); + } else { + logger::error(" Filter [{}] INFO - unable to find form, treating filter as string", a_condition); + a_processedFilters.push_back(a_condition); + } }; for (auto& [section, comment, keyOrder] : sections) { - if (!string::icontains(section, "|")) { - logger::info(" reading [{}]", section); - if (const auto values = ini.GetSection(section); values && !values->empty()) { - logger::info(" {} swaps found", values->size()); - auto& map = get_form_map(section); - auto& conflictMap = get_conflict_map(section); - for (const auto& key : *values | std::views::keys) { - std::string value(key.pItem); - if (auto [result, baseID] = get_forms(value, map); result) { - map_conflicts(value, path, baseID, conflictMap); - } - } - } - } else { + if (string::icontains(section, "|")) { auto conditions = string::split(string::split(section, "|")[1], ","); //[Forms|EditorID,EditorID2] logger::info(" reading [Forms] : {} conditions", conditions.size()); - std::vector processedConditions; + std::vector processedConditions; processedConditions.reserve(conditions.size()); for (auto& condition : conditions) { if (const auto processedID = SwapData::GetFormID(condition); processedID != 0) { @@ -132,10 +119,16 @@ namespace FormSwap if (const auto values = ini.GetSection(section); values && !values->empty()) { logger::info(" {} swaps found", values->size()); for (const auto& key : *values | std::views::keys) { - std::string value(key.pItem); - if (auto [result, baseID] = get_forms(key.pItem, processedConditions, swapFormsConditional); result) { - map_conflicts(value, path, baseID, conflictFormsConditional); - } + get_forms(path, key.pItem, processedConditions, swapFormsConditional); + } + } + } else { + logger::info(" reading [{}]", section); + if (const auto values = ini.GetSection(section); values && !values->empty()) { + logger::info(" {} swaps found", values->size()); + auto& map = get_form_map(section); + for (const auto& key : *values | std::views::keys) { + get_forms(path, key.pItem, map); } } } @@ -150,27 +143,25 @@ namespace FormSwap logger::info("{:*^30}", "CONFLICTS"); - const auto log_conflicts = [&](std::string_view a_type, const ConflictMap& a_conflictMap) { + const auto log_conflicts = [&](std::string_view a_type, const SwapMap& a_conflictMap) { logger::info("[{}]", a_type); - for (auto& [baseID, conflicts] : a_conflictMap) { - if (conflicts.size() > 1) { + for (auto& [baseID, swapDataVec] : a_conflictMap) { + if (swapDataVec.size() > 1) { hasConflicts = true; - auto winningForm = string::split(conflicts.back().first, "|"); + auto winningForm = string::split(swapDataVec.back().record, "|"); logger::warn(" {}", winningForm[0]); - logger::warn(" winning record : {} [{}]", winningForm[1], conflicts.back().second); - logger::warn(" {} conflicts", conflicts.size() - 1); - for (auto it = conflicts.begin(); it != std::prev(conflicts.end()); ++it) { - auto& [record, path] = *it; - auto conflictForm = string::split(record, "|"); - logger::warn(" {} [{}]", conflictForm[1], path); + logger::warn(" winning record : {} [{}]", winningForm[1], swapDataVec.back().path); + logger::warn(" {} conflicts", swapDataVec.size() - 1); + for (auto it = swapDataVec.begin(); it != std::prev(swapDataVec.end()); ++it) { + logger::warn(" {} [{}]", string::split(it->record, "|")[1], it->path); } } } }; - log_conflicts("Forms"sv, conflictForms); - log_conflicts("References"sv, conflictRefs); + log_conflicts("Forms"sv, swapForms); + log_conflicts("References"sv, swapRefs); logger::info("{:*^30}", "END"); @@ -180,8 +171,7 @@ namespace FormSwap void Manager::PrintConflicts() const { if (const auto console = RE::ConsoleLog::GetSingleton(); hasConflicts) { - console->Print("~BASE OBJECT SWAPPER~"); - console->Print("Conflicts detected, check po3_BaseObjectSwapper.log in %s for more details\n", logger::log_directory()->string().c_str()); + console->Print("[BOS] Conflicts detected, check po3_BaseObjectSwapper.log in %s for more details\n", logger::log_directory()->string().c_str()); } } @@ -222,7 +212,11 @@ namespace FormSwap }); if (result != it->second.end()) { - return { result->second.GetSwapBase(a_ref), result->second.transform }; + for (auto& swapData : result->second | std::ranges::views::reverse) { + if (auto swapObject = swapData.GetSwapBase(a_ref)) { + return { swapObject, swapData.transform }; + } + } } } @@ -231,9 +225,13 @@ namespace FormSwap SwapResult Manager::GetSwapData(const RE::TESObjectREFR* a_ref, const RE::TESForm* a_base) { - const auto get_swap_base = [a_ref](const RE::TESForm* a_form, const SwapMap& a_map) { + const auto get_swap_base = [a_ref](const RE::TESForm* a_form, const SwapMap& a_map) { if (const auto it = a_map.find(a_form->GetFormID()); it != a_map.end()) { - return SwapResult{ it->second.GetSwapBase(a_ref), it->second.transform }; + for (auto& swapData : it->second | std::ranges::views::reverse) { + if (auto swapObject = swapData.GetSwapBase(a_ref)) { + return SwapResult{ swapObject, swapData.transform }; + } + } } return SwapResult{}; }; @@ -252,13 +250,11 @@ namespace FormSwap swapData = get_swap_base(a_base, swapForms); } - if (swapData.first) { - if (const auto swapLvlBase = swapData.first->As(); swapLvlBase) { - RE::BSScrapArray calcedObjects{}; - swapLvlBase->CalculateCurrentFormList(a_ref->GetCalcLevel(false), 1, calcedObjects, 0, true); - if (!calcedObjects.empty()) { - swapData.first = static_cast(calcedObjects.front().form); - } + if (const auto swapLvlBase = swapData.first ? swapData.first->As() : nullptr) { + RE::BSScrapArray calcedObjects{}; + swapLvlBase->CalculateCurrentFormList(a_ref->GetCalcLevel(false), 1, calcedObjects, 0, true); + if (!calcedObjects.empty()) { + swapData.first = static_cast(calcedObjects.front().form); } } diff --git a/src/Manager.h b/src/Manager.h index f761580..c3df0ec 100644 --- a/src/Manager.h +++ b/src/Manager.h @@ -6,10 +6,10 @@ namespace FormSwap { template using SwapMap = Map; - using SwapDataConditional = Map; - using SwapResult = std::pair; - using ConflictMap = Map>>; //record, path + using SwapDataVec = std::vector; + using SwapDataConditional = Map; + using SwapResult = std::pair; class Manager { @@ -37,26 +37,18 @@ namespace FormSwap Manager& operator=(Manager&&) = delete; private: - using Lock = std::mutex; - using Locker = std::scoped_lock; - - SwapMap& get_form_map(const std::string& a_str); - ConflictMap& get_conflict_map(const std::string& a_str); + SwapMap& get_form_map(const std::string& a_str); - static std::pair get_forms_impl(const std::string& a_str, std::function a_func); + static void get_forms_impl(const std::string& a_path,const std::string& a_str, std::function a_func); - static std::pair get_forms(const std::string& a_str, SwapMap& a_map); - static std::pair get_forms(const std::string& a_str, const std::vector& a_conditionalIDs, SwapMap& a_map); + static void get_forms(const std::string& a_path, const std::string& a_str, SwapMap& a_map); + static void get_forms(const std::string& a_path, const std::string& a_str, const std::vector& a_conditionalIDs, SwapMap& a_map); - ConflictMap conflictForms{}; - ConflictMap conflictRefs{}; - ConflictMap conflictFormsConditional{}; - bool hasConflicts{ false }; - - SwapMap swapForms{}; - SwapMap swapRefs{}; + SwapMap swapForms{}; + SwapMap swapRefs{}; SwapMap swapFormsConditional{}; + bool hasConflicts{ false }; std::atomic_bool init{ false }; }; } diff --git a/src/SwapData.cpp b/src/SwapData.cpp index d3c74c8..d94586f 100644 --- a/src/SwapData.cpp +++ b/src/SwapData.cpp @@ -2,7 +2,7 @@ namespace FormSwap { - namespace detail + namespace detail { static Transform::minMax get_min_max(const std::string& a_str) { @@ -20,7 +20,7 @@ namespace FormSwap static float get_random_value(float a_min, float a_max) { - if (stl::numeric::essentially_equal(a_min,a_max)) { + if (stl::numeric::essentially_equal(a_min, a_max)) { return a_min; } @@ -68,14 +68,12 @@ namespace FormSwap std::optional> Transform::get_scale_from_string(const std::string& a_str) { - minMax scale{ 1.0f, 1.0f }; - srell::cmatch match; if (srell::regex_search(a_str.c_str(), match, scaleRegex)) { - scale = detail::get_min_max(match[1].str()); + return detail::get_min_max(match[1].str()); } - return scale; + return minMax{ 1.0f, 1.0f }; } Transform::Transform(const std::string& a_str) @@ -122,16 +120,22 @@ namespace FormSwap Traits::Traits(const std::string& a_str) { if (!a_str.empty() && !string::icontains(a_str, "NONE")) { - if (a_str == "R") { + if (a_str.contains("RAND")) { trueRandom = true; } + srell::cmatch match; + if (srell::regex_search(a_str.c_str(), match, Transform::scaleRegex)) { + chance = string::lexical_cast(match[1].str()); + } } } - SwapData::SwapData(FormIDOrSet a_id, const std::string& a_transformStr, const std::string& a_traitsStr) : + SwapData::SwapData(FormIDOrSet a_id, const Input& a_input) : formIDSet(std::move(a_id)), - transform(a_transformStr), - traits(a_traitsStr) + transform(a_input.transformStr), + traits(a_input.traitsStr), + record(a_input.record), + path(a_input.path) {} RE::FormID SwapData::GetFormID(const std::string& a_str) @@ -178,7 +182,15 @@ namespace FormSwap auto seededRNG = SeedRNG(a_ref->GetFormID()); auto staticRNG = stl::RNG::GetSingleton(); - if (const auto formID = std::get_if(&formIDSet); formID) { + if (traits.chance != 100) { + const auto rng = traits.trueRandom ? staticRNG->Generate(0, 100) : + seededRNG.Generate(0, 100); + if (rng > traits.chance) { + return nullptr; + } + } + + if (const auto formID = std::get_if(&formIDSet); formID) { return RE::TESForm::LookupByID(*formID); } else { // return random element from set auto& set = std::get(formIDSet); diff --git a/src/SwapData.h b/src/SwapData.h index ef70b9d..ec43bcf 100644 --- a/src/SwapData.h +++ b/src/SwapData.h @@ -2,7 +2,7 @@ namespace FormSwap { - using FormOrEditorID = std::variant; + using FormIDStr = std::variant; template using Map = robin_hood::unordered_flat_map; @@ -11,7 +11,7 @@ namespace FormSwap using FormIDSet = Set; using FormIDOrSet = std::variant; - inline bool swap_empty(const FormIDOrSet& a_set) + inline bool swap_empty(const FormIDOrSet& a_set) { if (const auto formID = std::get_if(&a_set); formID) { return *formID == 0; @@ -67,6 +67,8 @@ namespace FormSwap static inline srell::regex transformRegex{ R"(\((.*?),(.*?),(.*?)\))" }; static inline srell::regex scaleRegex{ R"(\((.*?)\))" }; + + friend struct Traits; }; struct Traits @@ -74,14 +76,23 @@ namespace FormSwap Traits() = default; explicit Traits(const std::string& a_str); - bool trueRandom{ false }; + bool trueRandom{ false }; + std::uint32_t chance{ 100 }; }; class SwapData { public: + struct Input + { + std::string transformStr; + std::string traitsStr; + std::string record; + std::string path; + }; + SwapData() = delete; - SwapData(FormIDOrSet a_id, const std::string& a_transformStr, const std::string& a_traitsStr); + SwapData(FormIDOrSet a_id, const Input& a_input); [[nodiscard]] static RE::FormID GetFormID(const std::string& a_str); [[nodiscard]] static FormIDOrSet GetSwapFormID(const std::string& a_str); @@ -91,5 +102,8 @@ namespace FormSwap FormIDOrSet formIDSet{}; Transform transform{}; Traits traits{}; + + std::string record{}; + std::string path{}; }; } diff --git a/src/main.cpp b/src/main.cpp index ce9d2fd..b123233 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -83,7 +83,7 @@ void InitializeLog() log->flush_on(spdlog::level::info); spdlog::set_default_logger(std::move(log)); - spdlog::set_pattern("[%l] %v"s); + spdlog::set_pattern("[%H:%M:%S:%e] %v"s); logger::info(FMT_STRING("{} v{}"), Version::PROJECT, Version::NAME); } From 2aac87b6a6afda55447be9de266cd85284fec616 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Fri, 28 Oct 2022 01:11:08 +0530 Subject: [PATCH 10/19] Add encounterZone/Level traits --- src/PCH.h | 4 ++++ src/SwapData.cpp | 48 ++++++++++++++++++++++++++++++++++++++---------- src/SwapData.h | 8 +++++++- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/PCH.h b/src/PCH.h index 939c028..e8e5086 100644 --- a/src/PCH.h +++ b/src/PCH.h @@ -1,5 +1,9 @@ #pragma once +#define WIN32_LEAN_AND_MEAN + +#undef GetForm + #include "RE/Skyrim.h" #include "SKSE/SKSE.h" diff --git a/src/SwapData.cpp b/src/SwapData.cpp index d94586f..d56a8db 100644 --- a/src/SwapData.cpp +++ b/src/SwapData.cpp @@ -69,7 +69,7 @@ namespace FormSwap std::optional> Transform::get_scale_from_string(const std::string& a_str) { srell::cmatch match; - if (srell::regex_search(a_str.c_str(), match, scaleRegex)) { + if (srell::regex_search(a_str.c_str(), match, genericRegex)) { return detail::get_min_max(match[1].str()); } @@ -120,17 +120,39 @@ namespace FormSwap Traits::Traits(const std::string& a_str) { if (!a_str.empty() && !string::icontains(a_str, "NONE")) { - if (a_str.contains("RAND")) { - trueRandom = true; - } - srell::cmatch match; - if (srell::regex_search(a_str.c_str(), match, Transform::scaleRegex)) { - chance = string::lexical_cast(match[1].str()); + const auto traitStrs = string::split(a_str, ","); + for (auto& traitStr : traitStrs) { + if (traitStr.contains("chance")) { + if (traitStr.contains("R")) { + trueRandom = true; + } + srell::cmatch match; + if (srell::regex_search(traitStr.c_str(), match, Transform::genericRegex)) { + chance = string::lexical_cast(match[1].str()); + } + } else if (traitStr.contains("zone")) { + srell::cmatch match; + if (srell::regex_search(traitStr.c_str(), match, Transform::genericRegex)) { + zone = static_cast(SwapData::GetForm(match[1].str())); + } + } else if (traitStr.contains("level")) { + srell::cmatch match; + if (srell::regex_search(traitStr.c_str(), match, Transform::genericRegex)) { + levModifier = string::lexical_cast(match[1].str()); + } + } } } } - SwapData::SwapData(FormIDOrSet a_id, const Input& a_input) : + void Traits::SetTraits(RE::TESObjectREFR* a_refr) + { + if (a_refr->Is(RE::FormType::ActorCharacter)) { + // tbd + } + } + + SwapData::SwapData(FormIDOrSet a_id, const Input& a_input) : formIDSet(std::move(a_id)), transform(a_input.transformStr), traits(a_input.traitsStr), @@ -138,7 +160,13 @@ namespace FormSwap path(a_input.path) {} - RE::FormID SwapData::GetFormID(const std::string& a_str) + RE::TESForm* SwapData::GetForm(const std::string& a_str) + { + const auto formID = GetFormID(a_str); + return formID != 0 ? RE::TESForm::LookupByID(formID) : nullptr; + } + + RE::FormID SwapData::GetFormID(const std::string& a_str) { if (a_str.contains('~')) { if (const auto splitID = string::split(a_str, "~"); splitID.size() == 2) { @@ -152,7 +180,7 @@ namespace FormSwap } } } - if (const auto form = RE::TESForm::LookupByEditorID(a_str); form) { + if (const auto form = RE::TESForm::LookupByEditorID(a_str)) { return form->GetFormID(); } return static_cast(0); diff --git a/src/SwapData.h b/src/SwapData.h index ec43bcf..74af40e 100644 --- a/src/SwapData.h +++ b/src/SwapData.h @@ -66,7 +66,7 @@ namespace FormSwap std::optional> refScale{ std::nullopt }; static inline srell::regex transformRegex{ R"(\((.*?),(.*?),(.*?)\))" }; - static inline srell::regex scaleRegex{ R"(\((.*?)\))" }; + static inline srell::regex genericRegex{ R"(\((.*?)\))" }; friend struct Traits; }; @@ -76,8 +76,13 @@ namespace FormSwap Traits() = default; explicit Traits(const std::string& a_str); + void SetTraits(RE::TESObjectREFR* a_refr); + bool trueRandom{ false }; std::uint32_t chance{ 100 }; + + RE::BGSEncounterZone* zone{ nullptr }; + RE::LEV_CREA_MODIFIER levModifier{ RE::LEV_CREA_MODIFIER::kNone }; //actors only }; class SwapData @@ -94,6 +99,7 @@ namespace FormSwap SwapData() = delete; SwapData(FormIDOrSet a_id, const Input& a_input); + [[nodiscard]] static RE::TESForm* GetForm(const std::string& a_str); [[nodiscard]] static RE::FormID GetFormID(const std::string& a_str); [[nodiscard]] static FormIDOrSet GetSwapFormID(const std::string& a_str); From 0ad9315138858553ef74599c145d8639f58da1f2 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Fri, 28 Oct 2022 01:14:21 +0530 Subject: [PATCH 11/19] Update submodule --- extern/CommonLibSSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/CommonLibSSE b/extern/CommonLibSSE index 59fa871..33979b7 160000 --- a/extern/CommonLibSSE +++ b/extern/CommonLibSSE @@ -1 +1 @@ -Subproject commit 59fa8719e0f53867bd0df1b104af43ee4d02e49d +Subproject commit 33979b75cc4f95eb694931ec4e9e3074ecc2f810 From 1ba8ca5df26588431fb31c4f29743807a22dc3e0 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Fri, 28 Oct 2022 08:40:12 +0530 Subject: [PATCH 12/19] Scrap NPC swaps Sort config sections --- src/Hooks.cpp | 53 +++++++++------------------------- src/Manager.cpp | 75 +++++++++++++++++++++++++++++------------------- src/Manager.h | 2 +- src/PCH.h | 2 -- src/SwapData.cpp | 46 +++++++---------------------- src/SwapData.h | 13 ++++----- 6 files changed, 76 insertions(+), 115 deletions(-) diff --git a/src/Hooks.cpp b/src/Hooks.cpp index fa6ce2f..295c49c 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -3,29 +3,26 @@ namespace BaseObjectSwapper { - namespace detail - { - void swap_base(RE::TESObjectREFR* a_ref) - { - if (const auto base = a_ref->GetBaseObject(); base) { - FormSwap::Manager::GetSingleton()->LoadFormsOnce(); - - auto [swapBase, transformData] = FormSwap::Manager::GetSingleton()->GetSwapData(a_ref, base); - if (swapBase) { - a_ref->SetObjectReference(swapBase); - } - transformData.SetTransform(a_ref); - } - } - } - namespace TESObjectREFR { struct InitItemImpl { static void thunk(RE::TESObjectREFR* a_ref) { - detail::swap_base(a_ref); + if (const auto base = a_ref->GetBaseObject(); base) { + FormSwap::Manager::GetSingleton()->LoadFormsOnce(); + + const auto& [swapBase, transformData] = FormSwap::Manager::GetSingleton()->GetSwapData(a_ref, base); + if (swapBase) { + if (swapBase != base) { + a_ref->SetObjectReference(swapBase); + } + if (transformData) { + transformData->SetTransform(a_ref); + } + } + } + func(a_ref); } static inline REL::Relocation func; @@ -40,32 +37,10 @@ namespace BaseObjectSwapper } } - namespace Character - { - struct InitItemImpl - { - static void thunk(RE::Character* a_ref) - { - detail::swap_base(a_ref); - func(a_ref); - } - static inline REL::Relocation func; - - static inline constexpr std::size_t size = 0x13; - }; - - void Install() - { - stl::write_vfunc(); - logger::info("Installed character form swap"sv); - } - } - void Install() { logger::info("{:*^30}", "HOOKS"); TESObjectREFR::Install(); - Character::Install(); } } diff --git a/src/Manager.cpp b/src/Manager.cpp index 94a54ab..05d291a 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -93,10 +93,10 @@ namespace FormSwap constexpr auto push_filter = [](const std::string& a_condition, std::vector& a_processedFilters) { if (const auto processedID = SwapData::GetFormID(a_condition); processedID != 0) { - a_processedFilters.push_back(processedID); + a_processedFilters.emplace_back(processedID); } else { - logger::error(" Filter [{}] INFO - unable to find form, treating filter as string", a_condition); - a_processedFilters.push_back(a_condition); + logger::error(" Filter [{}] INFO - unable to find form, treating filter as cell editorID", a_condition); + a_processedFilters.emplace_back(a_condition); } }; @@ -109,25 +109,30 @@ namespace FormSwap std::vector processedConditions; processedConditions.reserve(conditions.size()); for (auto& condition : conditions) { - if (const auto processedID = SwapData::GetFormID(condition); processedID != 0) { - processedConditions.emplace_back(processedID); - } else { - processedConditions.emplace_back(condition); - } + push_filter(condition, processedConditions); } - if (const auto values = ini.GetSection(section); values && !values->empty()) { - logger::info(" {} swaps found", values->size()); - for (const auto& key : *values | std::views::keys) { + CSimpleIniA::TNamesDepend values; + ini.GetAllKeys(section, values); + values.sort(CSimpleIniA::Entry::LoadOrder()); + + if (!values.empty()) { + logger::info(" {} swaps found", values.size()); + for (const auto& key : values) { get_forms(path, key.pItem, processedConditions, swapFormsConditional); } } } else { logger::info(" reading [{}]", section); - if (const auto values = ini.GetSection(section); values && !values->empty()) { - logger::info(" {} swaps found", values->size()); + + CSimpleIniA::TNamesDepend values; + ini.GetAllKeys(section, values); + values.sort(CSimpleIniA::Entry::LoadOrder()); + + if (!values.empty()) { + logger::info(" {} swaps found", values.size()); auto& map = get_form_map(section); - for (const auto& key : *values | std::views::keys) { + for (const auto& key : values) { get_forms(path, key.pItem, map); } } @@ -143,21 +148,33 @@ namespace FormSwap logger::info("{:*^30}", "CONFLICTS"); - const auto log_conflicts = [&](std::string_view a_type, const SwapMap& a_conflictMap) { - logger::info("[{}]", a_type); - for (auto& [baseID, swapDataVec] : a_conflictMap) { + const auto log_conflicts = [&](std::string_view a_type, const SwapMap& a_map) { + if (a_map.empty()) { + return; + } + logger::info("[{}]", a_type); + bool conflicts = false; + for (auto& [baseID, swapDataVec] : a_map) { if (swapDataVec.size() > 1) { - hasConflicts = true; - auto winningForm = string::split(swapDataVec.back().record, "|"); + if (winningForm.size() > 3 && winningForm[3].contains("chance")) { //ignore if winning record is randomized + continue; + } + conflicts = true; logger::warn(" {}", winningForm[0]); logger::warn(" winning record : {} [{}]", winningForm[1], swapDataVec.back().path); logger::warn(" {} conflicts", swapDataVec.size() - 1); - for (auto it = swapDataVec.begin(); it != std::prev(swapDataVec.end()); ++it) { - logger::warn(" {} [{}]", string::split(it->record, "|")[1], it->path); + for (auto it = swapDataVec.rbegin() + 1; it != swapDataVec.rend(); ++it) { + auto losingRecord = it->record.substr(it->record.find('|') + 1); + logger::warn(" {} [{}]", losingRecord, it->path); } } } + if (!conflicts) { + logger::info(" No conflicts found"); + } else { + hasConflicts = true; + } }; log_conflicts("Forms"sv, swapForms); @@ -171,7 +188,7 @@ namespace FormSwap void Manager::PrintConflicts() const { if (const auto console = RE::ConsoleLog::GetSingleton(); hasConflicts) { - console->Print("[BOS] Conflicts detected, check po3_BaseObjectSwapper.log in %s for more details\n", logger::log_directory()->string().c_str()); + console->Print("[BOS] Conflicts found, check po3_BaseObjectSwapper.log in %s for more info\n", logger::log_directory()->string().c_str()); } } @@ -220,7 +237,7 @@ namespace FormSwap } } - return { nullptr, Transform() }; + return { nullptr, std::nullopt }; } SwapResult Manager::GetSwapData(const RE::TESObjectREFR* a_ref, const RE::TESForm* a_base) @@ -233,28 +250,28 @@ namespace FormSwap } } } - return SwapResult{}; + return SwapResult{ nullptr, std::nullopt }; }; - SwapResult swapData{ std::make_pair(nullptr, Transform()) }; + SwapResult swapData{ nullptr, std::nullopt }; if (!a_ref->IsDynamicForm()) { swapData = get_swap_base(a_ref, swapRefs); } - if (!swapData.first) { + if (!std::get<0>(swapData)) { swapData = GetSwapConditionalBase(a_ref, a_base); } - if (!swapData.first) { + if (!std::get<0>(swapData)) { swapData = get_swap_base(a_base, swapForms); } - if (const auto swapLvlBase = swapData.first ? swapData.first->As() : nullptr) { + if (const auto swapLvlBase = std::get<0>(swapData) ? std::get<0>(swapData)->As() : nullptr) { RE::BSScrapArray calcedObjects{}; swapLvlBase->CalculateCurrentFormList(a_ref->GetCalcLevel(false), 1, calcedObjects, 0, true); if (!calcedObjects.empty()) { - swapData.first = static_cast(calcedObjects.front().form); + std::get<0>(swapData) = static_cast(calcedObjects.front().form); } } diff --git a/src/Manager.h b/src/Manager.h index c3df0ec..fd4a316 100644 --- a/src/Manager.h +++ b/src/Manager.h @@ -9,7 +9,7 @@ namespace FormSwap using SwapDataVec = std::vector; using SwapDataConditional = Map; - using SwapResult = std::pair; + using SwapResult = std::tuple>; class Manager { diff --git a/src/PCH.h b/src/PCH.h index e8e5086..23cc654 100644 --- a/src/PCH.h +++ b/src/PCH.h @@ -2,8 +2,6 @@ #define WIN32_LEAN_AND_MEAN -#undef GetForm - #include "RE/Skyrim.h" #include "SKSE/SKSE.h" diff --git a/src/SwapData.cpp b/src/SwapData.cpp index d56a8db..15d57e7 100644 --- a/src/SwapData.cpp +++ b/src/SwapData.cpp @@ -92,7 +92,7 @@ namespace FormSwap } } - void Transform::SetTransform(RE::TESObjectREFR* a_refr) + void Transform::SetTransform(RE::TESObjectREFR* a_refr) const { if (location) { auto& [relative, minMax] = *location; @@ -120,39 +120,19 @@ namespace FormSwap Traits::Traits(const std::string& a_str) { if (!a_str.empty() && !string::icontains(a_str, "NONE")) { - const auto traitStrs = string::split(a_str, ","); - for (auto& traitStr : traitStrs) { - if (traitStr.contains("chance")) { - if (traitStr.contains("R")) { - trueRandom = true; - } - srell::cmatch match; - if (srell::regex_search(traitStr.c_str(), match, Transform::genericRegex)) { - chance = string::lexical_cast(match[1].str()); - } - } else if (traitStr.contains("zone")) { - srell::cmatch match; - if (srell::regex_search(traitStr.c_str(), match, Transform::genericRegex)) { - zone = static_cast(SwapData::GetForm(match[1].str())); - } - } else if (traitStr.contains("level")) { - srell::cmatch match; - if (srell::regex_search(traitStr.c_str(), match, Transform::genericRegex)) { - levModifier = string::lexical_cast(match[1].str()); - } + if (a_str.contains("chance")) { + if (a_str.contains("R")) { + trueRandom = true; + } + srell::cmatch match; + if (srell::regex_search(a_str.c_str(), match, Transform::genericRegex)) { + chance = string::lexical_cast(match[1].str()); } } } } - void Traits::SetTraits(RE::TESObjectREFR* a_refr) - { - if (a_refr->Is(RE::FormType::ActorCharacter)) { - // tbd - } - } - - SwapData::SwapData(FormIDOrSet a_id, const Input& a_input) : + SwapData::SwapData(FormIDOrSet a_id, const Input& a_input) : formIDSet(std::move(a_id)), transform(a_input.transformStr), traits(a_input.traitsStr), @@ -160,13 +140,7 @@ namespace FormSwap path(a_input.path) {} - RE::TESForm* SwapData::GetForm(const std::string& a_str) - { - const auto formID = GetFormID(a_str); - return formID != 0 ? RE::TESForm::LookupByID(formID) : nullptr; - } - - RE::FormID SwapData::GetFormID(const std::string& a_str) + RE::FormID SwapData::GetFormID(const std::string& a_str) { if (a_str.contains('~')) { if (const auto splitID = string::split(a_str, "~"); splitID.size() == 2) { diff --git a/src/SwapData.h b/src/SwapData.h index 74af40e..2aea018 100644 --- a/src/SwapData.h +++ b/src/SwapData.h @@ -55,13 +55,14 @@ namespace FormSwap Transform() = default; explicit Transform(const std::string& a_str); - void SetTransform(RE::TESObjectREFR* a_refr); + void SetTransform(RE::TESObjectREFR* a_refr) const; private: [[nodiscard]] static relData get_transform_from_string(const std::string& a_str); [[nodiscard]] static std::optional> get_scale_from_string(const std::string& a_str); - std::optional> location{ std::nullopt }; + // members + std::optional> location{ std::nullopt }; std::optional> rotation{ std::nullopt }; std::optional> refScale{ std::nullopt }; @@ -76,13 +77,9 @@ namespace FormSwap Traits() = default; explicit Traits(const std::string& a_str); - void SetTraits(RE::TESObjectREFR* a_refr); - + // members bool trueRandom{ false }; std::uint32_t chance{ 100 }; - - RE::BGSEncounterZone* zone{ nullptr }; - RE::LEV_CREA_MODIFIER levModifier{ RE::LEV_CREA_MODIFIER::kNone }; //actors only }; class SwapData @@ -99,12 +96,12 @@ namespace FormSwap SwapData() = delete; SwapData(FormIDOrSet a_id, const Input& a_input); - [[nodiscard]] static RE::TESForm* GetForm(const std::string& a_str); [[nodiscard]] static RE::FormID GetFormID(const std::string& a_str); [[nodiscard]] static FormIDOrSet GetSwapFormID(const std::string& a_str); RE::TESBoundObject* GetSwapBase(const RE::TESObjectREFR* a_ref) const; + // members FormIDOrSet formIDSet{}; Transform transform{}; Traits traits{}; From fcd1595482bfd47b15ce876660ff77bb0497f394 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Fri, 28 Oct 2022 14:31:05 +0530 Subject: [PATCH 13/19] Check dynamic keywords Transform data supports seeded RNG --- CMakeLists.txt | 4 +- src/Manager.cpp | 14 +++++-- src/SwapData.cpp | 98 ++++++++++++++++++++++++++---------------------- src/SwapData.h | 18 +++++++-- vcpkg.json | 2 +- 5 files changed, 81 insertions(+), 55 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9dad9cc..5c7ceff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) -set(NAME "po3_BaseObjectSwapper") -set(VERSION 1.8.0) +set(NAME "po3_BaseObjectSwapper" CACHE STRING "") +set(VERSION 1.7.0 CACHE STRING "") set(VR_VERSION 1) set(AE_VERSION 1) diff --git a/src/Manager.cpp b/src/Manager.cpp index 05d291a..c352a89 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -95,7 +95,7 @@ namespace FormSwap if (const auto processedID = SwapData::GetFormID(a_condition); processedID != 0) { a_processedFilters.emplace_back(processedID); } else { - logger::error(" Filter [{}] INFO - unable to find form, treating filter as cell editorID", a_condition); + logger::error(" Filter [{}] INFO - unable to find form, treating filter as string", a_condition); a_processedFilters.emplace_back(a_condition); } }; @@ -210,8 +210,6 @@ namespace FormSwap auto location = form->As(); return currentLocation && (currentLocation == location || currentLocation->IsParent(location)); } - case RE::FormType::Cell: - return cell && cell == form; case RE::FormType::Keyword: { auto keyword = form->As(); @@ -223,7 +221,15 @@ namespace FormSwap } } else { const std::string editorID = std::get(formData.first); - return cell && cell->GetFormEditorID() == editorID; + if (cell && cell->GetFormEditorID() == editorID) { + return true; + } + if (currentLocation && currentLocation->HasKeywordString(editorID)) { + return true; + } + if (const auto keywordForm = a_base->As()) { + return keywordForm->HasKeywordString(editorID); + } } return false; }); diff --git a/src/SwapData.cpp b/src/SwapData.cpp index 15d57e7..69fdfdb 100644 --- a/src/SwapData.cpp +++ b/src/SwapData.cpp @@ -17,30 +17,6 @@ namespace FormSwap return { num, num }; } } - - static float get_random_value(float a_min, float a_max) - { - if (stl::numeric::essentially_equal(a_min, a_max)) { - return a_min; - } - - return stl::RNG::GetSingleton()->Generate(a_min, a_max); - } - - static RE::NiPoint3 get_random_value(const std::pair& a_minMax) - { - auto& [min, max] = a_minMax; - - if (min == max) { - return min; - } - - return RE::NiPoint3{ - get_random_value(min.x, max.x), - get_random_value(min.y, max.y), - get_random_value(min.z, max.z) - }; - } } Transform::relData Transform::get_transform_from_string(const std::string& a_str) @@ -76,6 +52,31 @@ namespace FormSwap return minMax{ 1.0f, 1.0f }; } + float Transform::get_random_value(const RandInput& a_input, float a_min, float a_max) + { + if (stl::numeric::essentially_equal(a_min, a_max)) { + return a_min; + } + + return a_input.trueRandom ? stl::RNG::GetSingleton()->Generate(a_min, a_max) : + SeedRNG(a_input.refSeed).Generate(a_min, a_max); + } + + RE::NiPoint3 Transform::get_random_value(const RandInput& a_input, const std::pair& a_minMax) + { + auto& [min, max] = a_minMax; + + if (min == max) { + return min; + } + + return RE::NiPoint3{ + get_random_value(a_input, min.x, max.x), + get_random_value(a_input, min.y, max.y), + get_random_value(a_input, min.z, max.z) + }; + } + Transform::Transform(const std::string& a_str) { if (!a_str.empty() && !string::icontains(a_str, "NONE")) { @@ -94,30 +95,33 @@ namespace FormSwap void Transform::SetTransform(RE::TESObjectREFR* a_refr) const { - if (location) { - auto& [relative, minMax] = *location; - if (relative) { - a_refr->data.location += detail::get_random_value(minMax); - } else { - a_refr->data.location = detail::get_random_value(minMax); + if (location || rotation || refScale) { + const RandInput input(useTrueRandom, a_refr->GetFormID()); + if (location) { + auto& [relative, minMax] = *location; + if (relative) { + a_refr->data.location += get_random_value(input, minMax); + } else { + a_refr->data.location = get_random_value(input, minMax); + } } - } - if (rotation) { - auto& [relative, minMax] = *rotation; - if (relative) { - a_refr->data.angle += detail::get_random_value(minMax); - } else { - a_refr->data.angle = detail::get_random_value(minMax); + if (rotation) { + auto& [relative, minMax] = *rotation; + if (relative) { + a_refr->data.angle += get_random_value(input, minMax); + } else { + a_refr->data.angle = get_random_value(input, minMax); + } + } + if (refScale) { + auto& [min, max] = *refScale; + const auto scale = std::clamp(get_random_value(input, min, max), 0.0f, 1000.0f); + a_refr->refScale = static_cast(a_refr->refScale * scale); } - } - if (refScale) { - auto& [min, max] = *refScale; - const auto scale = std::clamp(detail::get_random_value(min, max), 0.0f, 1000.0f); - a_refr->refScale = static_cast(a_refr->refScale * scale); } } - Traits::Traits(const std::string& a_str) + Traits::Traits(const std::string& a_str) { if (!a_str.empty() && !string::icontains(a_str, "NONE")) { if (a_str.contains("chance")) { @@ -125,7 +129,7 @@ namespace FormSwap trueRandom = true; } srell::cmatch match; - if (srell::regex_search(a_str.c_str(), match, Transform::genericRegex)) { + if (srell::regex_search(a_str.c_str(), match, genericRegex)) { chance = string::lexical_cast(match[1].str()); } } @@ -138,7 +142,11 @@ namespace FormSwap traits(a_input.traitsStr), record(a_input.record), path(a_input.path) - {} + { + if (traits.trueRandom) { + transform.useTrueRandom = true; + } + } RE::FormID SwapData::GetFormID(const std::string& a_str) { diff --git a/src/SwapData.h b/src/SwapData.h index 2aea018..9c1a5e8 100644 --- a/src/SwapData.h +++ b/src/SwapData.h @@ -20,6 +20,8 @@ namespace FormSwap } } + inline srell::regex genericRegex{ R"(\((.*?)\))" }; + class SeedRNG { public: @@ -61,15 +63,25 @@ namespace FormSwap [[nodiscard]] static relData get_transform_from_string(const std::string& a_str); [[nodiscard]] static std::optional> get_scale_from_string(const std::string& a_str); + struct RandInput + { + bool trueRandom{ false }; + RE::FormID refSeed{ 0 }; + }; + + static float get_random_value(const RandInput& a_input, float a_min, float a_max); + static RE::NiPoint3 get_random_value(const RandInput& a_input, const std::pair& a_minMax); + // members - std::optional> location{ std::nullopt }; + std::optional> location{ std::nullopt }; std::optional> rotation{ std::nullopt }; std::optional> refScale{ std::nullopt }; + bool useTrueRandom{ false }; + static inline srell::regex transformRegex{ R"(\((.*?),(.*?),(.*?)\))" }; - static inline srell::regex genericRegex{ R"(\((.*?)\))" }; - friend struct Traits; + friend class SwapData; }; struct Traits diff --git a/vcpkg.json b/vcpkg.json index 7f3e421..9f548a3 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,6 @@ { "name": "base-object-swapper", - "version-string": "1.8.0", + "version-string": "1.7.0", "description": "Base Object Swapper", "homepage": "", "license": "MIT", From 432e2e9cdb75287ba93ab766bf386092486ecb7f Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Fri, 28 Oct 2022 14:50:17 +0530 Subject: [PATCH 14/19] Enable CI --- .github/workflows/main.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..85e3489 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,24 @@ +name: Main + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + +jobs: + run: + if: github.repository_owner == 'powerof3' + uses: adya/pack-skse-mod/.github/workflows/pack.yml@main + with: + CMAKE_VR_CONFIG_PRESET: '' + CMAKE_VR_BUILD_PRESET: '' + AE_353_BRANCH: master-1.6.353 + FOMOD_INCLUDE_PDB: true + FOMOD_MOD_NAME: "Base Object Swapper" + FOMOD_MOD_AUTHOR: "powerofthree" + FOMOD_MOD_NEXUS_ID: "60805" + PUBLISH_ARCHIVE_TYPE: '7z' From 06982dc66b3e41cdc55ca6012e958a05883f7fa5 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Fri, 28 Oct 2022 15:34:11 +0530 Subject: [PATCH 15/19] Minor cleanup --- src/Manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Manager.cpp b/src/Manager.cpp index c352a89..dee4d1a 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -7,7 +7,7 @@ namespace FormSwap return a_str == "Forms" ? swapForms : swapRefs; } - void Manager::get_forms_impl(const std::string& a_path, const std::string& a_str, std::function a_func) + void Manager::get_forms_impl(const std::string& a_path, const std::string& a_str, const std::function a_func) { const auto formPair = string::split(a_str, "|"); @@ -213,7 +213,7 @@ namespace FormSwap case RE::FormType::Keyword: { auto keyword = form->As(); - return (currentLocation && currentLocation->HasKeyword(keyword)) || a_ref->HasKeyword(keyword); + return currentLocation && currentLocation->HasKeyword(keyword) || a_ref->HasKeyword(keyword); } default: break; From 1ac5da7754b2706974b40418d00a1c566f09980a Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Fri, 28 Oct 2022 16:02:47 +0530 Subject: [PATCH 16/19] Bump version to 2.0 --- CMakeLists.txt | 2 +- vcpkg.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c7ceff..7bcfcd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) set(NAME "po3_BaseObjectSwapper" CACHE STRING "") -set(VERSION 1.7.0 CACHE STRING "") +set(VERSION 2.0.0 CACHE STRING "") set(VR_VERSION 1) set(AE_VERSION 1) diff --git a/vcpkg.json b/vcpkg.json index 9f548a3..2bc652b 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,6 @@ { "name": "base-object-swapper", - "version-string": "1.7.0", + "version-string": "2.0.0", "description": "Base Object Swapper", "homepage": "", "license": "MIT", From 210da9a82b5a51e7420c559cca3a32027903b9c5 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Fri, 28 Oct 2022 16:07:06 +0530 Subject: [PATCH 17/19] Config format update --- src/Manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Manager.cpp b/src/Manager.cpp index dee4d1a..5156612 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -117,7 +117,7 @@ namespace FormSwap values.sort(CSimpleIniA::Entry::LoadOrder()); if (!values.empty()) { - logger::info(" {} swaps found", values.size()); + logger::info(" {} swaps found", values.size()); for (const auto& key : values) { get_forms(path, key.pItem, processedConditions, swapFormsConditional); } From 85779fe4f44c850f003022b3a293b7362a97be62 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 30 Oct 2022 19:46:43 -0700 Subject: [PATCH 18/19] Bump commonlibvr version --- extern/CommonLibVR | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/CommonLibVR b/extern/CommonLibVR index 0fabeda..48e8b97 160000 --- a/extern/CommonLibVR +++ b/extern/CommonLibVR @@ -1 +1 @@ -Subproject commit 0fabedac02c4d97b7d54199098561be8c0c666ef +Subproject commit 48e8b975c0913bceaa2dcd9611bd56e5865f0dbc From 726f00a872b88638a555bc609ac3c46bed1672a1 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 30 Oct 2022 19:48:10 -0700 Subject: [PATCH 19/19] Remove unnecessary include --- src/SwapData.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/SwapData.cpp b/src/SwapData.cpp index e1b3516..69fdfdb 100644 --- a/src/SwapData.cpp +++ b/src/SwapData.cpp @@ -1,5 +1,4 @@ #include "SwapData.h" -#include "MergeMapperPluginAPI.h" namespace FormSwap {