#include "constants.h" #include "helpers.h" #include "patches.h" #include #include #include extern GameVersion gameVersion; bool chsPatch = false; #define MIN(a, b) ((a)<(b)?(a):(b)) namespace patches::TestMode { bool hookInstallFinished = false; std::vector> hooks = {}; std::thread *hookInstall; std::mutex hooksMutex; std::condition_variable hooksCV; std::vector patches = {}; std::vector values = {}; std::map valueMap = {}; std::wstring mergeCondition (std::wstring original, std::wstring addition, std::wstring value) { size_t pos = original.find (L"/"); if (pos < 0) return original; std::wstring b = original.substr (0, pos - 1); if (b == L"False") { if (value.starts_with (L"!")) value = value.substr (1, value.size () - 1); else value = L"!" + value; } if (original.ends_with(L")")) return replace (original, L")", std::format (L" or {}:{})", addition, value)); else return std::format (L"{}/({} or {}:{})", b, original.substr (pos + 1, original.size () - pos - 1), addition, value); } void addConditionValue (pugi::xml_node *node, std::wstring key, std::wstring addition, std::wstring value) { pugi::xml_attribute attr = node->attribute (key.c_str ()); if (attr != nullptr) { attr.set_value (mergeCondition(attr.value (), addition, value).c_str ()); } else node->append_attribute (key.c_str ()) = std::format(L"True/{}:{}", addition, value).c_str (); } class RegisteredItem : public Applicable { public: std::wstring selectItem; std::function registerInit; pugi::xml_node temp; RegisteredItem (const std::wstring &selectItem, const std::function &initMethod) { this->selectItem = selectItem; this->registerInit = initMethod; } pugi::xml_node * Apply (pugi::xml_node *doc, pugi::xml_node *node) override { pugi::xml_document itemDoc; std::wstring itemLine = L"" + this->selectItem + L""; bool success = itemDoc.load_string (itemLine.c_str ()); if (!success) { LogMessage (LogLevel::ERROR, L"Failed to parse item: {}\n", this->selectItem); itemDoc.load_string (L""); } // if (success) AddHookInLoop (this->registerInit); std::wstring layoutName = L"layout"; if (layoutName == node->name ()) { temp = node->append_copy (itemDoc.first_child ().first_child ()); return &temp; } else { pugi::xml_node breakItem = node->parent ().insert_child_after (L"break-item", *node); temp = node->parent ().insert_copy_after (itemDoc.first_child ().first_child (), breakItem); return &temp; } } }; class RegisteredMenu : public Menu { public: std::wstring menuName; std::wstring menuId; std::vector items; pugi::xml_node temp; RegisteredMenu (const std::wstring &menuName, const std::wstring &menuId, std::vector items = {}) { this->menuName = menuName; this->menuId = menuId; this->items = items; } void RegisterItem (Applicable *item) override { this->items.push_back (item); } pugi::xml_node * Apply (pugi::xml_node *doc, pugi::xml_node *node) override { size_t size = items.size (); if (size == 0) return node; size_t maxPage = size <= 14 ? 1 : (size / 13 + (size % 13 > 0)); LogMessage (LogLevel::DEBUG, "Render testmode menu with items(size={}, page={})", size, maxPage); std::wstring menuName = L"menu"; pugi::xml_node parentNode = node->parent (); while (parentNode.name() != menuName) parentNode = parentNode.parent (); std::wstring parentId = parentNode.attribute (L"id").value (); pugi::xml_document menuItem; menuItem.load_string (std::format (L"", this->menuName, this->menuId).c_str ()); std::wstring layoutName = L"layout"; if (layoutName == node->name ()) { temp = node->append_copy (menuItem.first_child ().first_child ()); } else { pugi::xml_node menuBreakItem = node->parent ().insert_child_after (L"break-item", *node); temp = node->parent ().insert_copy_after (menuItem.first_child ().first_child (), menuBreakItem); } pugi::xml_document menu; menu.load_string (std::format ( L"" L"" L"" L"" L"", this->menuId, this->menuName ).c_str ()); pugi::xpath_query query = pugi::xpath_query (L"/root/menu/layout[@type='Center']"); pugi::xml_node passing = menu.select_node (query).node (); std::vector refs = {}; for (size_t page = 0; page < maxPage; page ++) { LogMessage (LogLevel::DEBUG, "Render testmode menu page ({}/{})", page + 1, maxPage); pugi::xml_node ref = passing; size_t maxIndex = MIN(size - page * 13, 13); for (size_t index = 0; index < maxIndex; index ++) { LogMessage (LogLevel::DEBUG, "Render testmode menu page ({}/{}) item ({}/{})", page + 1, maxPage, index + 1, maxIndex); if (index >= refs.size ()) { passing = *(items[page * 13 + index]->Apply (doc, &passing)); if (size > 14) { addConditionValue (&passing, L"invisible", std::format (L"{}Pagenation", this->menuId), std::format(L"!{}", page + 1)); addConditionValue (&passing, L"disable", std::format (L"{}Pagenation", this->menuId), std::format(L"!{}", page + 1)); } refs.push_back (passing); } else { pugi::xml_node ref = refs[index]; ref = *(items[page * 13 + index]->Apply (doc, &ref)); addConditionValue (&ref, L"invisible", std::format (L"{}Pagenation", this->menuId), std::format(L"!{}", page + 1)); addConditionValue (&ref, L"disable", std::format (L"{}Pagenation", this->menuId), std::format(L"!{}", page + 1)); ref.parent ().remove_child (ref.previous_sibling ()); refs[index] = ref; } } if (size == 14) passing = *(items[13]->Apply (doc, &passing)); } passing = passing.parent ().last_child (); LogMessage (LogLevel::DEBUG, "Begin render testmode footer"); size_t maxIndex = size == 14 ? 14 : MIN(size, 13); for (size_t i = 0; i < 15 - maxIndex; i++) passing = passing.parent ().insert_child_after (L"break-item", passing); if (maxPage > 1) { pugi::xml_document pagenation; pagenation.load_string (std::format (L"", maxPage, this->menuId, maxPage).c_str ()); passing = passing.parent ().insert_copy_after (pagenation.first_child ().first_child (), passing); } passing = passing.parent ().insert_child_after (L"break-item", passing); pugi::xml_document exit; exit.load_string (std::format (L"", parentId).c_str ()); passing = passing.parent ().insert_copy_after (exit.first_child ().first_child (), passing); LogMessage (LogLevel::DEBUG, L"Insert testmode menu menuId: {}, fromId: {}", this->menuId, parentId); pugi::xml_node root = doc->first_child (); root.insert_copy_after (menu.first_child ().first_child (), root.first_child ()); return &temp; } }; class RegisteredSingleItem : Applicable { public: std::wstring query; Applicable *item; RegisteredSingleItem (const std::wstring query, Applicable *item) { this->query = query; this->item = item; } pugi::xml_node * Apply (pugi::xml_node *doc, pugi::xml_node *node) override { pugi::xpath_query insertQuery = pugi::xpath_query (this->query.c_str ()); try { if (pugi::xml_node insertNode = doc->select_node (insertQuery).node ()) return this->item->Apply (doc, &insertNode); } catch ([[maybe_unused]] std::exception &e) { LogMessage (LogLevel::ERROR, L"Failed to find node by xpath: {}\n", this->query); } return nullptr; } }; class RegisteredModify : Applicable { public: std::wstring query; std::function nodeModify; std::function registerInit; RegisteredModify (const std::wstring &query, const std::function &nodeModify, const std::function &initMethod) { this->query = query; this->nodeModify = nodeModify; this->registerInit = initMethod; } pugi::xml_node * Apply (pugi::xml_node *doc, pugi::xml_node *node) override { pugi::xpath_query modifyQuery = pugi::xpath_query (this->query.c_str ()); try { if (pugi::xml_node modifyNode = node->select_node (modifyQuery).node ()) { this->nodeModify (modifyNode); // AddHookInLoop (this->registerInit); } } catch ([[maybe_unused]] std::exception &e) { LogMessage (LogLevel::ERROR, L"Failed to find node by xpath: {}\n", this->query); } return nullptr; } }; class TestModeValue : Value { public: std::wstring key; int value; boolean reset; TestModeValue (std::wstring key) { this->key = key; this->value = -1; this->reset = true; } int Read (int value) override { if (reset) { this->value = ReadTestModeValue (key.c_str ()); if (this->value != -1) { LogMessage (LogLevel::DEBUG, L"TestMode Value({}) got val: {}", this->key, this->value); reset = false; } } if (this->value == -1) return value; else return this->value; } void Write (int value) override { SetTestModeValue (this->key.c_str (), value); this->reset = true; } void Reset () override { this->reset = true; } }; Menu *modManager = new RegisteredMenu (L"MOD MANAGER", L"ModManagerMenu"); std::vector registeredSingleItems = {}; std::vector registeredModifies = {}; std::wstring moddedInitial = L""; std::wstring modded = L""; std::wstring usingDeviceInitialize = L""; std::wstring moddedDeviceInitialize = L"DeviceInitialize_mod.xml"; std::wstring usingTestMode = L""; std::wstring moddedTestMode = L"TestMode_mod.xml"; u64 appAccessor = 0; RefTestModeMain refTestMode = nullptr; FAST_HOOK_DYNAMIC (char, SceneTestModeLoading, u64 a1, u64 a2, u64 a3) { char result = originalSceneTestModeLoading.fastcall (a1, a2, a3); return result; } FAST_HOOK_DYNAMIC (char, SceneTestModeFinalize, u64 a1, u64 a2, u64 a3) { char result = originalSceneTestModeFinalize.fastcall (a1, a2, a3); std::thread ([](){for (auto value : values) { value->Reset (); value->Read (); }}).detach (); return result; } FAST_HOOK_DYNAMIC (void, SceneFirstInitialize, u64 a1, u64 a2, u64 a3) { std::thread ([](){for (auto value : values) { value->Reset (); value->Read (); }}).detach (); originalSceneFirstInitialize.fastcall (a1, a2, a3); } std::wstring replaceFileName (std::wstring fullFileName, std::wstring usingLastFileName) { size_t lastPosition = fullFileName.find_last_of (L"/"); if (lastPosition == -1) return usingLastFileName; else return fullFileName.substr(0, lastPosition) + usingLastFileName; } FAST_HOOK_DYNAMIC (void, TestModeSetMenuHook, u64 testModeLibrary, const wchar_t *lFileName) { auto start = std::chrono::high_resolution_clock::now(); const auto originalFileName = std::wstring (lFileName); LogMessage (LogLevel::DEBUG, L"Begin Loading {}", originalFileName); std::wstring fileName = originalFileName; if (fileName.find (L"/DeviceInitialize") != -1) { if (moddedInitial == L"") { if (usingDeviceInitialize != L"") { fileName = replaceFileName (originalFileName, usingDeviceInitialize); } else fileName = originalFileName; LogMessage (LogLevel::DEBUG, L"final path: {}", fileName); if (pugi::xml_document doc; !doc.load_file (fileName.c_str ())) { LogMessage (LogLevel::ERROR, L"Loading DeviceInitialize structure failed! path: " + fileName); moddedInitial = originalFileName; fileName = originalFileName; } else { const std::wstring modFileName = replaceFileName (originalFileName, moddedDeviceInitialize); const pugi::xpath_query dongleQuery = pugi::xpath_query (L"/root/menu[@id='TopMenu']/layout[@type='Center']/select-item[@id='DongleItem']"); const pugi::xml_node dongleItem = doc.select_node (dongleQuery).node (); pugi::xml_node talItem = dongleItem.parent ().append_copy (dongleItem); talItem.attribute (L"label").set_value (L"TAIKOARCADELOADER"); talItem.attribute (L"id").set_value (L"TaikoArcadeLoader"); talItem.append_attribute (L"default") = L"1"; dongleItem.parent ().append_child (L"break-item"); [[maybe_unused]] auto ignored = doc.save_file (modFileName.c_str ()); moddedInitial = modFileName; fileName = modFileName; } } else fileName = moddedInitial; } else if (fileName.find (L"/TestMode") != -1) { if (modded == L"") { if (!((RegisteredMenu*)modManager)->items.empty () || registeredSingleItems.empty () || !registeredModifies.empty ()) { if (usingTestMode != L"") { fileName = replaceFileName (originalFileName, usingTestMode); } else fileName = originalFileName; LogMessage (LogLevel::DEBUG, L"final path: {}", fileName); if (pugi::xml_document doc; !doc.load_file (fileName.c_str ())) { LogMessage (LogLevel::ERROR, L"Loading TestMode structure failed! path: " + fileName); modded = originalFileName; fileName = originalFileName; } else { const std::wstring modFileName = replaceFileName (originalFileName, moddedTestMode); if (!((RegisteredMenu*)modManager)->items.empty ()) { LogMessage (LogLevel::DEBUG, "Begin generate Mod Manager menu"); pugi::xpath_query menuQuery = pugi::xpath_query (L"/root/menu[@id='TopMenu']/layout[@type='Center']/menu-item[@menu='GameOptionsMenu']"); pugi::xml_node menuItem = doc.select_node (menuQuery).node (); modManager->Apply (&doc, &menuItem); LogMessage (LogLevel::DEBUG, "End generate Mod Manager menu"); } if (!registeredSingleItems.empty()) { LogMessage (LogLevel::DEBUG, "Begin Apply Single Items"); for (RegisteredSingleItem *item : registeredSingleItems) item->Apply (&doc, &doc); LogMessage (LogLevel::DEBUG, "End Apply Single Items"); } if (!registeredModifies.empty ()) { LogMessage (LogLevel::DEBUG, "Begin Apply Modify Items"); for (RegisteredModify *modify : registeredModifies) modify->Apply (&doc, &doc); LogMessage (LogLevel::DEBUG, "End Apply Modify Items"); } [[maybe_unused]] auto _ = doc.save_file (modFileName.c_str ()); modded = modFileName; fileName = modFileName; } } else modded = fileName; } else fileName = modded; } std::chrono::duration duration = std::chrono::high_resolution_clock::now() - start; LogMessage (LogLevel::DEBUG, L"TestModeLibrary load: {}, spend: {:.2f}ms", fileName, duration.count () * 1000); originalTestModeSetMenuHook.fastcall (testModeLibrary, fileName.c_str ()); } void CommonModify () { // Default off Close time RegisterModify ( L"/root/menu[@id='CloseTimeSettingMenu']/layout[@type='Center']/select-item[@id='ScheduleTypeItem']", [&] (const pugi::xml_node &node) { node.attribute (L"default").set_value (L"0"); }, [&] {}); } void LocalizationCHS () { TestMode::RegisterModify ( L"/root/menu[@id='TopMenu']/layout[@type='Center']/menu-item[@menu='ModManagerMenu']", [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"模组管理"); }, [](){}); TestMode::RegisterModify ( L"/root/menu[@id='ModManagerMenu']/layout[@type='Header']/text-item", [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"模组管理"); }, [](){}); TestMode::RegisterModify ( L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModUnlockSongs']", [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"解锁歌曲"); node.attribute (L"replace-text").set_value (L"0:禁用, 1:启用, 2:强制"); }, [](){}); TestMode::RegisterModify ( L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModFreezeTimer']", [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"冻结计时器"); node.attribute (L"replace-text").set_value (L"0:禁用, 1:启用"); }, [](){}); TestMode::RegisterModify ( L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeCollabo024']", [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"鬼灭之刃模式"); node.attribute (L"replace-text").set_value (L"0:默认, 1:启用, 2:仅刷卡"); }, [](){}); TestMode::RegisterModify ( L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeCollabo025']", [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"航海王模式"); node.attribute (L"replace-text").set_value (L"0:默认, 1:启用, 2:仅刷卡"); }, [](){}); TestMode::RegisterModify ( L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeCollabo026']", [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"AI粗品模式"); node.attribute (L"replace-text").set_value (L"0:默认, 1:启用, 2:仅刷卡"); }, [](){}); TestMode::RegisterModify( L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModModeAprilFool001']", [&](pugi::xml_node &node) { node.attribute(L"label").set_value(L"青春之达人模式"); node.attribute(L"replace-text").set_value(L"0:默认, 1:启用, 2:仅刷卡"); }, [](){}); TestMode::RegisterModify ( L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModInstantResult']", [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"即时上传成绩"); node.attribute (L"replace-text").set_value (L"0:禁用, 1:启用"); }, [](){}); TestMode::RegisterModify ( L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/select-item[@id='ModTheme']", [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"界面主题"); node.attribute (L"replace-text").set_value (L"0:默认, 1:四季(仅中文), 2:秋叶原女仆"); }, [](){}); TestMode::RegisterModify ( L"/root/menu[@id='ModManagerMenu']/layout[@type='Center']/menu-item[@menu='TopMenu']", [&] (const pugi::xml_node &node) { node.attribute (L"label").set_value (L"离开"); }, [](){}); TestMode::RegisterModify( L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='AttractDemoItem']", [&](pugi::xml_node &node) { node.attribute(L"label").set_value(L"演示游玩影片"); node.attribute(L"replace-text").set_value(L"0:关闭, 1:开启"); }, [](){}); TestMode::RegisterModify( L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='LanguageItem']", [&](pugi::xml_node &node) { node.attribute(L"label").set_value(L"文本语言"); const wchar_t *vals = node.attribute(L"replace-text").value (); std::wstring langaugeReplace = std::wstring (vals); langaugeReplace = replace (langaugeReplace, L"JPN", L"日语"); langaugeReplace = replace (langaugeReplace, L"ENG", L"英语"); langaugeReplace = replace (langaugeReplace, L"zh-tw", L"繁体中文"); langaugeReplace = replace (langaugeReplace, L"KOR", L"韩语"); langaugeReplace = replace (langaugeReplace, L"zh-cn", L"简体中文"); node.attribute(L"replace-text").set_value(langaugeReplace.c_str ()); }, [](){}); TestMode::RegisterModify( L"/root/menu[@id='OthersMenu']/layout[@type='Center']/select-item[@id='VoiceLanguageItem']", [&](pugi::xml_node &node) { node.attribute(L"label").set_value(L"语音语言"); node.attribute(L"replace-text").set_value(L"0:日语, 1:中文"); }, [](){}); } void Init () { LogMessage (LogLevel::INFO, "Init TestMode patches"); const u64 testModeLibrary = (u64)GetModuleHandle ("TestModeLibrary.dll"); const u64 testModeSetMenu = testModeLibrary + 0x99D0; CommonModify (); for (auto method : hooks) method (); switch (gameVersion) { case GameVersion::JPN00: break; case GameVersion::JPN08: break; case GameVersion::JPN39: { INSTALL_FAST_HOOK_DYNAMIC (SceneTestModeLoading, ASLR (0x1404793D0)); INSTALL_FAST_HOOK_DYNAMIC (SceneTestModeFinalize, ASLR (0x140479600)); INSTALL_FAST_HOOK_DYNAMIC (SceneFirstInitialize, ASLR (0x1404574B0)); if (Language::CnFontPatches () && std::filesystem::exists ("..\\..\\Data\\x64\\testmode\\DeviceInitialize_china.xml")) { usingDeviceInitialize = L"DeviceInitialize_china.xml"; patches.push_back (safetyhook::create_mid (ASLR (0x140465549), [](SafetyHookContext &ctx) { ctx.r8 = 2; ctx.rdx = (uintptr_t)"cn"; ctx.rip = ASLR (0x140465556); })); // DeviceCheck = Loading Font patches.push_back (safetyhook::create_mid (ASLR (0x1404676E7), [](SafetyHookContext &ctx) { ctx.r8 = 2; ctx.rdx = (uintptr_t)"cn"; ctx.rip = ASLR (0x1404676F4); })); // ?? WRITE_MEMORY (ASLR (0x140CD1E40), wchar_t, L"加载中\0"); WRITE_MEMORY (ASLR (0x140CD1E28), wchar_t, L"加载中.\0"); WRITE_MEMORY (ASLR (0x140CD1E68), wchar_t, L"加载中..\0"); WRITE_MEMORY (ASLR (0x140CD1E50), wchar_t, L"加载中...\0"); } if (Language::CnFontPatches () && std::filesystem::exists ("..\\..\\Data\\x64\\testmode\\TestMode_china.xml")) { usingTestMode = L"TestMode_china.xml"; patches.push_back (safetyhook::create_mid (ASLR (0x14047C603), [](SafetyHookContext &ctx) { ctx.r8 = 2; ctx.rdx = (uintptr_t)"cn"; ctx.rip = ASLR (0x14047C610); })); // TestMode = DeviceInitialize LocalizationCHS (); } } break; case GameVersion::CHN00: break; } INSTALL_FAST_HOOK_DYNAMIC (TestModeSetMenuHook, testModeSetMenu); } void SetupAccessor (const u64 appAccessor, const RefTestModeMain refTestMode) { if (TestMode::appAccessor != appAccessor) { LogMessage (LogLevel::DEBUG, "TestMode setup!"); TestMode::appAccessor = appAccessor; TestMode::refTestMode = refTestMode; // std::thread ([](){for (auto value : values) { value->Reset (); value->Read (); }}).detach (); } } int ReadTestModeValue (const wchar_t *itemId) { if (appAccessor) { try { if (const u64 testModeMain = refTestMode (appAccessor)) { int value = 0; u64 *reader = *reinterpret_cast (testModeMain + 16); (*reinterpret_cast (*reader + 256)) (reader, itemId, &value); return value; } } catch ([[maybe_unused]] std::exception &e) { LogMessage (LogLevel::ERROR, L"Reading Value({}) Error!", itemId); } } return -1; } void SetTestModeValue (const wchar_t *itemId, int value) { if (appAccessor) { if (const u64 testModeMain = refTestMode (appAccessor)) { u64 *reader = *reinterpret_cast (testModeMain + 16); (*reinterpret_cast (*reader + 176)) (reader, itemId, value); } } } Menu * CreateMenu (const std::wstring &menuName, const std::wstring &menuId) { LogMessage (LogLevel::DEBUG, L"Create MenuName: {} MenuId: {}", menuName, menuId); return new RegisteredMenu (menuName, menuId); } Value * CreateValue (const std::wstring &key) { LogMessage (LogLevel::DEBUG, L"Create TestMode Value key: {}", key); // if (valueMap.count (key)) return valueMap[key]; Value *value = (Value *)(new TestModeValue (key)); values.push_back ((Value *)value); // valueMap[key] = value; return value; } void RegisterItem (const std::wstring &item, const std::function &initMethod, Menu *menu) { LogMessage (LogLevel::DEBUG, L"Register \nItem: {}", item); hooks.push_back (initMethod); menu->RegisterItem (new RegisteredItem (item, [](){})); } void RegisterItem (const std::wstring &item, const std::function &initMethod) { LogMessage (LogLevel::DEBUG, L"Register \nItem: {}", item); hooks.push_back (initMethod); modManager->RegisterItem (new RegisteredItem (item, [](){})); } void RegisterItem (const std::wstring &item, Menu *menu) { LogMessage (LogLevel::DEBUG, L"Register \nItem: {}", item); menu->RegisterItem (new RegisteredItem (item, [](){})); } void RegisterItem (const std::wstring &item) { LogMessage (LogLevel::DEBUG, L"Register \nItem: {}", item); modManager->RegisterItem (new RegisteredItem (item, [](){})); } void RegisterItem (Applicable *item, Menu *menu) { LogMessage (LogLevel::DEBUG, L"Register Item"); menu->RegisterItem (item); } void RegisterItem (Applicable *item) { LogMessage (LogLevel::DEBUG, L"Register Item"); modManager->RegisterItem (item); } void RegisterItemAfter (const std::wstring &query, const std::wstring &item, const std::function &initMethod) { LogMessage (LogLevel::DEBUG, L"Register \nQuery: {} \nItem: {}", query, item); hooks.push_back (initMethod); registeredSingleItems.push_back (new RegisteredSingleItem (query, new RegisteredItem (item, [](){}))); } void RegisterItemAfter (const std::wstring &query, const std::wstring &item) { LogMessage (LogLevel::DEBUG, L"Register \nQuery: {} \nItem: {}", query, item); registeredSingleItems.push_back (new RegisteredSingleItem (query, new RegisteredItem (item, [](){}))); } void RegisterItemAfter (const std::wstring &query, Applicable *item) { LogMessage (LogLevel::DEBUG, L"Register \nQuery: {} \nItem: ptr", query); registeredSingleItems.push_back (new RegisteredSingleItem (query, item)); } void RegisterModify (const std::wstring &query, const std::function &nodeModify, const std::function &initMethod) { LogMessage (LogLevel::DEBUG, L"Register \nModify: {}", query); hooks.push_back (initMethod); registeredModifies.push_back (new RegisteredModify (query, nodeModify, [](){})); } void RegisterModify (const std::wstring &query, const std::function &nodeModify) { LogMessage (LogLevel::DEBUG, L"Register \nModify: {}", query); registeredModifies.push_back (new RegisteredModify (query, nodeModify, [](){})); } void RegisterHook (const std::function &initMethod) { LogMessage (LogLevel::DEBUG, L"Register Hook"); hooks.push_back (initMethod); } void Append (const pugi::xml_node &node, const wchar_t *attr, const std::wstring &append) { pugi::xml_attribute attribute = node.attribute (attr); const std::wstring attrValue = std::wstring (attribute.value ()) + append; attribute.set_value (attrValue.c_str ()); } }