Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "extern/CommonLibSSE"]
path = extern/CommonLibSSE
url = https://github.com/Ryan-rsm-McKenzie/CommonLibSSE
url = https://github.com/powerof3/CommonLibSSE.git
[submodule "extern/CommonLibVR"]
path = extern/CommonLibVR
url = https://github.com/alandtse/CommonLibVR.git
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.20)
set(NAME "po3_BaseObjectSwapper")
set(VERSION 1.2.0)
set(VR_VERSION 1)
set(VR_VERSION 4)

# ---- Options ----

Expand Down
25 changes: 22 additions & 3 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@
"hidden": true,
"name": "windows"
},
{
"binaryDir": "${sourceDir}/buildvr",
"cacheVariables": {
"BUILD_SKYRIMVR": true
},
"hidden": true,
"name": "vr"
},
{
"cacheVariables": {
"CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /external:anglebrackets /external:W0 $penv{CXXFLAGS}"
Expand All @@ -72,13 +80,24 @@
"toolset": "v143"
},
{
"binaryDir": "${sourceDir}/buildvr",
"cacheVariables": {
"CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /WX $penv{CXXFLAGS}",
"BUILD_SKYRIMVR": true
"CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /external:anglebrackets /external:W0 $penv{CXXFLAGS}"
},
"generator": "Visual Studio 16 2019",
"inherits": [
"vr",
"cmake-dev",
"vcpkg",
"windows"
],
"name": "vs2019-windows-vcpkg-vr"
}, {
"cacheVariables": {
"CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /external:anglebrackets /external:W0 $penv{CXXFLAGS}"
},
"generator": "Visual Studio 17 2022",
"inherits": [
"vr",
"cmake-dev",
"vcpkg",
"windows"
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ SKSE/SKSEVR plugin and framework that allows swapping base forms at runtime
```
git clone https://github.com/powerof3/BaseObjectSwapper.git
cd BaseObjectSwapper
# pull commonlib /extern to override the path settings
git submodule init
# to update submodules to checked in build
git submodule update
```

### SSE
Expand Down
2 changes: 1 addition & 1 deletion cmake/headerlist.cmake
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
set(headers ${headers}
src/Cache.h
src/FormSwapManager.h
src/MergeMapper.h
src/PCH.h
)
1 change: 1 addition & 0 deletions cmake/sourcelist.cmake
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
set(sources ${sources}
src/FormSwapManager.cpp
src/main.cpp
src/MergeMapper.cpp
src/PCH.cpp
)
1 change: 1 addition & 0 deletions extern/CommonLibSSE
Submodule CommonLibSSE added at 87100a
1 change: 1 addition & 0 deletions extern/CommonLibVR
Submodule CommonLibVR added at 6b5ce0
2 changes: 1 addition & 1 deletion src/FormSwapManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ bool FormSwapManager::LoadFormsOnce()
a_map.insert_or_assign(baseFormID, data);
a_conflictMap[baseFormID].emplace_back(std::make_pair(a_str, a_path));
} else {
logger::error("failed to process [{}|{}|{}] (formID not found)", baseFormID, swapFormID, flags);
logger::error("{} failed to process {} [{:x}|{:x}|{}] (formID not found)", a_path, a_str, baseFormID, swapFormID, static_cast<int>(flags));
}
};

Expand Down
6 changes: 3 additions & 3 deletions src/FormSwapManager.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#pragma once
#include "MergeMapper.h"

enum class SWAP_FLAGS
{
Expand Down Expand Up @@ -67,10 +68,9 @@ class FormSwapManager
if (a_str.find('~') != std::string::npos) {
const auto formPair = string::split(a_str, "~");

const auto processedFormPair = std::make_pair(
string::lexical_cast<RE::FormID>(formPair[0], true), formPair[1]);
const auto processedFormPair = MergeMapper::GetNewFormID(formPair[1], formPair[0]);

return RE::TESDataHandler::GetSingleton()->LookupFormID(processedFormPair.first, processedFormPair.second);
return RE::TESDataHandler::GetSingleton()->LookupFormID(processedFormPair.second, processedFormPair.first);
}
if (const auto form = RE::TESForm::LookupByEditorID(a_str); form) {
return form->GetFormID();
Expand Down
91 changes: 91 additions & 0 deletions src/MergeMapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#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"\\map.json";
auto merged = path.substr(13) + L".esp";
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("<unicode conversion error>"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 (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<char>(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<char>(std::tolower(ch)); });
auto storedValue = std::to_string(std::stoi(value.get<std::string>(), 0, 16));
std::transform(storedValue.begin(), storedValue.end(), storedValue.begin(), [](auto ch) { return static_cast<char>(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<std::string, RE::FormID> 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<std::string, RE::FormID> 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<char>(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<char>(std::tolower(ch)); });
if (mergeMap[espkey]["map"].contains(storedKey)) {
formID = std::stoi(mergeMap[espkey]["map"][storedKey].get<std::string>());
}
}
}
return std::make_pair(modName, formID);
}
}
17 changes: 17 additions & 0 deletions src/MergeMapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once
#include <nlohmann/json.hpp>

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<std::string, RE::FormID> GetNewFormID(std::string oldName, std::string oldFormID);
}
4 changes: 4 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "FormSwapManager.h"
#include "MergeMapper.h"

namespace FormSwap
{
Expand Down Expand Up @@ -128,6 +129,9 @@ extern "C" DLLEXPORT bool SKSEAPI SKSEPlugin_Load(const SKSE::LoadInterface* a_s

SKSE::Init(a_skse);

logger::info("{:*^30}", "MERGES");
MergeMapper::GetMerges();

logger::info("{:*^30}", "HOOKS");

FormSwap::Install();
Expand Down
1 change: 1 addition & 0 deletions vcpkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"license": "MIT",
"dependencies": [
"boost-stl-interfaces",
"nlohmann-json",
"robin-hood-hashing",
"rsm-binary-io",
"simpleini",
Expand Down