diff --git a/libzhl/functions/ASM.zhl b/libzhl/functions/ASM.zhl index c88c281da..d81d88b80 100644 --- a/libzhl/functions/ASM.zhl +++ b/libzhl/functions/ASM.zhl @@ -165,4 +165,7 @@ asm Minimap_Render_SetShaderPreImageRender "e8????????80bd????????000f84???????? // EntityPlus patches asm EntityPlus_PlayerForceCamo "8b0d????????6a03e8????????84c00f85"; -asm LostSoulHeartTypeSkipSpawnPuddle "80be????????000f85????????e8????????8b0d"; \ No newline at end of file +asm LostSoulHeartTypeSkipSpawnPuddle "80be????????000f85????????e8????????8b0d"; + +// Localization Modded Folders +asm RedirectToLocalizedResources "6a0668????????8d4d??c645??01"; \ No newline at end of file diff --git a/libzhl/functions/Manager.zhl b/libzhl/functions/Manager.zhl index 126099bd7..41eb61aa1 100644 --- a/libzhl/functions/Manager.zhl +++ b/libzhl/functions/Manager.zhl @@ -75,6 +75,9 @@ __thiscall bool Manager::IsSteamCloudEnabled(); "558bec6aff68????????64a1????????50535657a1????????33c5508d45??64a3????????8bd98db3": __thiscall void Manager::destructor(); +"a1????????8b80????????83f813": +static char* Manager::GetLanguage(); + struct Manager depends (Seeds, GameState, StringTable, PersistentGameData, NightmareScene, UnknownGameStartStruct, ModManager, KAGE_SmartPointer_ImageBase, Cutscene, OptionsConfig, AchievementOverlay, SoundEffects, ItemConfig, EntityConfig, ANM2, Font, ChallengeParam, Music, NetplayManager, DailyChallenge) { {{ inline int GetState() { return this->_state; } inline PersistentGameData* GetPersistentGameData() { return &this->_persistentGameData; } diff --git a/libzhl/functions/ModManager.zhl b/libzhl/functions/ModManager.zhl index 0a8ed797a..dbb4a49c8 100644 --- a/libzhl/functions/ModManager.zhl +++ b/libzhl/functions/ModManager.zhl @@ -33,8 +33,9 @@ __thiscall void ModManager::Reset(); "558bec83e4f851568bf1e8????????8bcee8????????8bce": __thiscall void ModManager::TriggerResize(); -struct ModManager depends (ModEntry) { +struct ModManager depends (ModEntry, RedirectedPath) { vector_ModEntryPointer _mods : 0x0; + RedirectedPath* _redirectedPathsMap[32768] : 0xc; int _modBanStatus : 0x2025c; } : 0x20260; diff --git a/libzhl/functions/RedirectedPath.zhl b/libzhl/functions/RedirectedPath.zhl new file mode 100644 index 000000000..11997d95e --- /dev/null +++ b/libzhl/functions/RedirectedPath.zhl @@ -0,0 +1,6 @@ +struct RedirectedPath depends (ModEntry) { + uint32_t _primaryHash : 0x0; + uint32_t _secondaryHash : 0x4; + ModEntry* _modEntry : 0x8; + std_string _filePath : 0xc; +} : 0x24; \ No newline at end of file diff --git a/repentogon/Patches/ASMPatches.cpp b/repentogon/Patches/ASMPatches.cpp index 5bed0ba42..c1beb95b9 100644 --- a/repentogon/Patches/ASMPatches.cpp +++ b/repentogon/Patches/ASMPatches.cpp @@ -39,6 +39,7 @@ #include "ASMPatches/ASMStatusEffects.h" #include "ASMPatches/ASMTweaks.h" #include "ASMPatches/ASMTweaks.h" +#include "ASMPatches/ASMLocalization.h" #include "ASMPatches/ASMFixes.h" #include "ASMPatches/ASMSplitTears.h" #include "ASMPatches/ASMCamera.h" @@ -283,6 +284,7 @@ void PerformASMPatches() { ASMPatches::__ItemPoolManagerExtra(); ASMPatchesForCardsExtras(); ASMPatchesForCustomModManager(); + ASMPatchRedirectToLocalizationFolders(); ASMFixes(); HookImGui(); diff --git a/repentogon/Patches/ASMPatches/ASMLocalization.cpp b/repentogon/Patches/ASMPatches/ASMLocalization.cpp new file mode 100644 index 000000000..8450dda70 --- /dev/null +++ b/repentogon/Patches/ASMPatches/ASMLocalization.cpp @@ -0,0 +1,91 @@ +#include "IsaacRepentance.h" +#include "HookSystem.h" + +#include "ASMPatcher.hpp" +#include "../ASMPatches.h" +#include "ASMDefinition.h" + +bool __stdcall TryToRedirectToLocalizedResources(std::string& resFileString, std::string& targetFile, ModEntry** modEntry, RedirectedPath* redirectPath) { + ModManager* manager = g_Manager->GetModManager(); + char* langCode = g_Manager->GetLanguage(); + + if (targetFile[0] == '\0') { + return false; + } + + auto buildAndCheckPath = [&](const std::string& postfix, bool useLangCode) -> bool { + std::string potentialPath = resFileString + postfix; + + if (useLangCode && g_Manager->_stringTable.language != 0 && langCode && langCode[0] != '\0') { + potentialPath = potentialPath + "." + langCode; + } + + potentialPath = potentialPath + "/" + targetFile; + + if (g_ContentManager.GetMountedFilePath(potentialPath.c_str()) != NULL) { + resFileString = potentialPath; + redirectPath->_modEntry = *modEntry; + redirectPath->_filePath = resFileString; + return true; + } + return false; + }; + + if (buildAndCheckPath("-repentogon", false)) return true; + + return false; +} + +void ASMPatchRedirectToLocalizedResources() { + ASMPatch::SavedRegisters savedRegisters(ASMPatch::SavedRegisters::Registers::GP_REGISTERS, true); + ASMPatch patch; + + void* addr = sASMDefinitionHolder->GetDefinition(&AsmDefinitions::RedirectToLocalizedResources); + + patch.PreserveRegisters(savedRegisters) + .Push(ASMPatch::Registers::EBP, -0xa0) // RedirectedPath* + .Push(ASMPatch::Registers::ESI) // ModEntry + .Push(ASMPatch::Registers::EBP, -0xa4) // Target file + .Push(ASMPatch::Registers::EAX) //Mod resources folder + .AddInternalCall(TryToRedirectToLocalizedResources) + .AddBytes("\x84\xC0") // test al, al + .RestoreRegisters(savedRegisters) + .AddConditionalRelativeJump(ASMPatcher::CondJumps::JNE, (char*)addr + 0x2D4) // jump for true + .AddBytes(ByteBuffer().AddAny((char*)addr, 0x7)) // Restore the commands we overwrote + .AddRelativeJump((char*)addr + 0x7); + sASMPatcher.PatchAt(addr, &patch); +} + +HOOK_METHOD(ModEntry, GetContentPath, (std::string* resFileString, const std::string* targetFile) -> void) { + super(resFileString, targetFile); + ModManager* manager = g_Manager->GetModManager(); + char* langCode = g_Manager->GetLanguage(); + + if (targetFile->empty()) { + return; + } + + auto buildAndCheckPath = [&](const std::string& postfix, bool useLangCode) -> bool { + std::string copyOfContentDirectory = _contentDirectory.substr(0, _contentDirectory.length() - 1); + std::string potentialPath = copyOfContentDirectory + postfix; + + if (useLangCode && g_Manager->_stringTable.language != 0 && langCode && langCode[0] != '\0') { + potentialPath = potentialPath + "." + langCode; + } + + potentialPath = potentialPath + "/" + *targetFile; + + if (g_ContentManager.GetMountedFilePath(potentialPath.c_str()) != NULL) { + *resFileString = potentialPath; + return true; + } + return false; + }; + + if (buildAndCheckPath("-repentogon", false)) return; + +} + +void ASMPatchRedirectToLocalizationFolders() { + ASMPatchRedirectToLocalizedResources(); +} \ No newline at end of file diff --git a/repentogon/Patches/ASMPatches/ASMLocalization.h b/repentogon/Patches/ASMPatches/ASMLocalization.h new file mode 100644 index 000000000..aa1309118 --- /dev/null +++ b/repentogon/Patches/ASMPatches/ASMLocalization.h @@ -0,0 +1,3 @@ +#pragma once + +void ASMPatchRedirectToLocalizationFolders(); \ No newline at end of file diff --git a/repentogon/Patches/NullItemsAndCostumes.cpp b/repentogon/Patches/NullItemsAndCostumes.cpp index f50c860ce..33c8e1764 100644 --- a/repentogon/Patches/NullItemsAndCostumes.cpp +++ b/repentogon/Patches/NullItemsAndCostumes.cpp @@ -26,7 +26,7 @@ void UpdateLastModId( ModEntry* mod, char* xmlpath) { else { lastModIdButCooler = "BaseGame"; } - if ((stringlower(xmlpath).find("/content/") != string::npos) || (stringlower(xmlpath).find("/content-dlc3/") != string::npos)) { + if ((stringlower(xmlpath).find("/content/") != string::npos) || (stringlower(xmlpath).find("/content-repentogon/") != string::npos) || (stringlower(xmlpath).find("/content-dlc3/") != string::npos)) { iscontent = true; } else { @@ -40,7 +40,10 @@ void UpdateLastModId( ModEntry* mod, char* xmlpath) { last = path.find("/resources"); } else if (last <= 0) { - last = path.find("/content-dlc3"); + size_t repentogonPos = path.find("/content-repentogon"); + size_t dlc3Pos = path.find("/content-dlc3"); + last = (repentogonPos != string::npos && (dlc3Pos == string::npos || repentogonPos < dlc3Pos)) + ? repentogonPos : dlc3Pos; } path = path.substr(first, last - first); //when the id is null(which it can fucking be) just use the folder name as ID... lastModIdButCooler = path; diff --git a/repentogon/Patches/XMLData.cpp b/repentogon/Patches/XMLData.cpp index fed8bb7a4..950da9604 100644 --- a/repentogon/Patches/XMLData.cpp +++ b/repentogon/Patches/XMLData.cpp @@ -275,7 +275,7 @@ void ProcessModEntry(char* xmlpath,ModEntry* mod) { else { lastmodid = "BaseGame"; } - if ((stringlower(xmlpath).find("/content/") != string::npos) || (stringlower(xmlpath).find("/content-dlc3/") != string::npos)) { + if ((stringlower(xmlpath).find("/content/") != string::npos) || (stringlower(xmlpath).find("/content-repentogon/") != string::npos) || (stringlower(xmlpath).find("/content-dlc3/") != string::npos)) { iscontent = true; } else { @@ -289,7 +289,10 @@ void ProcessModEntry(char* xmlpath,ModEntry* mod) { last = path.find("/resources"); } else if (last <= 0) { - last = path.find("/content-dlc3"); + size_t repentogonPos = path.find("/content-repentogon"); + size_t dlc3Pos = path.find("/content-dlc3"); + last = (repentogonPos != string::npos && (dlc3Pos == string::npos || repentogonPos < dlc3Pos)) + ? repentogonPos : dlc3Pos; } path = path.substr(first, last - first); //when the id is null(which it can fucking be) just use the folder name as ID... lastmodid = new char[path.length() + 1]; //this is the sort of stuff I dont like about C++ diff --git a/repentogon/Patches/XMLData.h b/repentogon/Patches/XMLData.h index 2f5052408..1b7b6cd23 100644 --- a/repentogon/Patches/XMLData.h +++ b/repentogon/Patches/XMLData.h @@ -960,7 +960,7 @@ inline bool XMLParse(xml_document* xmldoc, char* xml, const string& dir) { } inline char* GetResources(const string& dir, const string& filename) { - vector paths = { dir + "\\resources-dlc3\\" + filename, dir + "\\resources\\" + filename }; + vector paths = { dir + "\\resources-repentogon\\", dir + "\\resources-dlc3\\" + filename, dir + "\\resources\\" + filename }; for (const string& path : paths) { ifstream file(path.c_str()); if (file.is_open()) { @@ -1065,7 +1065,7 @@ inline void LoadCustomXML(CustomXML xml) { for (ModEntry* mod : g_Manager->GetModManager()->_mods) { if (mod->IsEnabled()) { string dir = filesystem::current_path().parent_path().string() + "\\mods\\" + mod->GetDir(); - vector paths = { dir + "\\resources-dlc3\\" + xml.filename, dir + "\\resources\\" + xml.filename }; + vector paths = { dir + "\\resources-repentogon\\", dir + "\\resources-dlc3\\" + xml.filename, dir + "\\resources\\" + xml.filename }; for (const string& path : paths) { if (filesystem::exists(path)) { targetresource = path; @@ -1082,7 +1082,7 @@ inline void LoadCustomXML(CustomXML xml) { for (ModEntry* mod : g_Manager->GetModManager()->_mods) { if (mod->IsEnabled()) { string dir = filesystem::current_path().parent_path().string() + "\\mods\\" + mod->GetDir(); - vector paths = { dir + "\\content-dlc3\\" + xml.filename, dir + "\\content\\" + xml.filename }; + vector paths = { dir + "\\content-repentogon\\" + xml.filename, dir + "\\content-dlc3\\" + xml.filename, dir + "\\content\\" + xml.filename }; for (const string& path : paths) { if (filesystem::exists(path)) { lastmodid = string(mod->GetId()); diff --git a/repentogon/REPENTOGONFileMap.cpp b/repentogon/REPENTOGONFileMap.cpp index 5bc307068..ac206dfd2 100644 --- a/repentogon/REPENTOGONFileMap.cpp +++ b/repentogon/REPENTOGONFileMap.cpp @@ -14,6 +14,7 @@ namespace REPENTOGONFileMap { std::vector _stringByFType = { L"resources", L"resources-dlc3", + L"resources-repentogon" L"", }; std::wstring _modsPath = L""; diff --git a/repentogon/REPENTOGONFileMap.h b/repentogon/REPENTOGONFileMap.h index ba5a45c5b..68e5dcf03 100644 --- a/repentogon/REPENTOGONFileMap.h +++ b/repentogon/REPENTOGONFileMap.h @@ -8,6 +8,7 @@ namespace REPENTOGONFileMap { enum FolderType { RESOURCES, RESOURCES_DLC3, + RESOURCES_REPENTOGON, LAST }; struct FileMapEntry {