Skip to content
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
013dd2a
Added modded localization resources folder support
epfly6 Oct 27, 2024
126f0ca
Localized content folder support
epfly6 Oct 30, 2024
a73e91a
Merge branch 'main' into feat-moddedLocalizationFolders
epfly6 Oct 30, 2024
4ce8e20
Merge branch 'main' into feat-moddedLocalizationFolders
epfly6 Nov 3, 2024
9f45914
Merge branch 'main' into feat-moddedLocalizationFolders
epfly6 Nov 5, 2024
f994d9e
Merge branch 'main' into feat-moddedLocalizationFolders
epfly6 Nov 6, 2024
e7f1eaa
Merge branch 'main' into feat-moddedLocalizationFolders
epfly6 Nov 10, 2024
d90dba1
Merge branch 'main' into feat-moddedLocalizationFolders
epfly6 Nov 11, 2024
912066d
Merge branch 'main' into feat-moddedLocalizationFolders
epfly6 Nov 17, 2024
bb25eec
Isaac.GetStringByNum
epfly6 Nov 17, 2024
ea02f3f
Merge branch 'main' into feat-moddedLocalizationFolders
epfly6 Nov 18, 2024
af24ba9
Merge branch 'repentance-plus' into feat-moddedLocalizationFolders
epfly6 Dec 17, 2024
4c49394
Update Manager.zhl
epfly6 Dec 17, 2024
92d7558
Merge branch 'repentance-plus' into feat-moddedLocalizationFolders
epfly6 Dec 17, 2024
8e8d7c2
Update ModManager.zhl
epfly6 Dec 17, 2024
ba8abc2
Merge branch 'repentance-plus' into feat-moddedLocalizationFolders
epfly6 Jan 17, 2025
6ba563a
Merge branch 'repentance-plus' into feat-moddedLocalizationFolders
epfly6 Jan 18, 2025
a4134a5
modded -repentogon folders and -dlc3 local supprot
epfly6 Jan 18, 2025
ecdedb8
Merge branch 'repentance-plus' into feat-moddedLocalizationFolders
epfly6 Feb 8, 2025
ffa0bf0
switch to GetMountedFilePath
epfly6 Feb 8, 2025
832deaf
Merge branch 'main' into feat-moddedLocalizationFolders
epfly6 Feb 8, 2025
3ea3ad1
Merge branch 'main' into feat-moddedLocalizationFolders
epfly6 Mar 18, 2025
f40d513
Merge branch 'main' into feat-moddedLocalizationFolders
epfly6 Apr 17, 2025
910cb9d
Merge branch 'feat-moddedLocalizationFolders' of https://github.com/T…
epfly6 Nov 30, 2025
f1263fa
modded repentogon folders is 2025
epfly6 Nov 30, 2025
4fea8b5
stripping away localization support for pr
epfly6 Dec 1, 2025
5ce89a7
search for -repentogon folders
epfly6 Dec 2, 2025
eb42923
res-repentogon:changes from feedback
epfly6 Dec 5, 2025
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 libzhl/functions/ASM.zhl
Original file line number Diff line number Diff line change
Expand Up @@ -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";
asm LostSoulHeartTypeSkipSpawnPuddle "80be????????000f85????????e8????????8b0d";

// Localization Modded Folders
asm RedirectToLocalizedResources "6a0668????????8d4d??c645??01";
3 changes: 3 additions & 0 deletions libzhl/functions/Manager.zhl
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
3 changes: 2 additions & 1 deletion libzhl/functions/ModManager.zhl
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wacky looking thing. So the game can "only" remember 32768 redirected paths?

Is there something we will need to access this directly for?

int _modBanStatus : 0x2025c;
} : 0x20260;

Expand Down
6 changes: 6 additions & 0 deletions libzhl/functions/RedirectedPath.zhl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
struct RedirectedPath depends (ModEntry) {
uint32_t _primaryHash : 0x0;
uint32_t _secondaryHash : 0x4;
ModEntry* _modEntry : 0x8;
std_string _filePath : 0xc;
} : 0x24;
2 changes: 2 additions & 0 deletions repentogon/Patches/ASMPatches.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -283,6 +284,7 @@ void PerformASMPatches() {
ASMPatches::__ItemPoolManagerExtra();
ASMPatchesForCardsExtras();
ASMPatchesForCustomModManager();
ASMPatchRedirectToLocalizationFolders();
ASMFixes();
HookImGui();

Expand Down
94 changes: 94 additions & 0 deletions repentogon/Patches/ASMPatches/ASMLocalization.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#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) {
auto* manager = g_Manager->GetModManager();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nitpick but I would prefer to skip the autos in this file unless the type is something wacky

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gonna change it to Manager* then

auto* langCode = g_Manager->GetLanguage();

if (targetFile[0] == '\0') {
return false;
}

auto buildAndCheckPath = [&](const std::string& postfix, bool useLangCode) -> bool {
std::string potentialPath = resFileString + postfix;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resFileString always ends in "/resources" at this stage?


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;
std::cout << "[REPENTOGON] Patched " << resFileString << std::endl;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this end up in the same place as printf (ie, external console)?

I support logging the redirections, unless it's spammy (like if theres anything that would cause it to be written every frame).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe print should be disabled

return true;
}
return false;
};

if (buildAndCheckPath("-repentogon", false)) return true;

return false;
}

void ASMPatchRedirectToLocalizedResources() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this trigger every time the file is accessed, or does the game do this once, cache the path in _redirectedPathsMap, and then it wont trigger redirection again?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I remember correctly, once the game successfully finds resources file, it will cache it to redirected path and it won't look for other mods

ASMPatch::SavedRegisters savedRegisters(ASMPatch::SavedRegisters::Registers::GP_REGISTERS, true);
ASMPatch patch;

void* addr = sASMDefinitionHolder->GetDefinition(&AsmDefinitions::RedirectToLocalizedResources);
printf("[REPENTOGON] Patching ModManager::TryRedirectPath for resources folder redirect at %p\n", addr);

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);
auto* manager = g_Manager->GetModManager();
auto* langCode = g_Manager->GetLanguage();

if (targetFile->empty()) {
return;
}

auto buildAndCheckPath = [&](const std::string& postfix, bool useLangCode) -> bool {
auto copyOfContentDirectory = _contentDirectory.substr(0, _contentDirectory.length() - 1);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this chopping a / off of .../content/ or something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, for suffixes...

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;
std::cout << "[REPENTOGON] Patched path: " << *resFileString << std::endl;
return true;
}
return false;
};

if (buildAndCheckPath("-repentogon", false)) return;

}

void ASMPatchRedirectToLocalizationFolders() {
ASMPatchRedirectToLocalizedResources();
}
3 changes: 3 additions & 0 deletions repentogon/Patches/ASMPatches/ASMLocalization.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

void ASMPatchRedirectToLocalizationFolders();
7 changes: 5 additions & 2 deletions repentogon/Patches/NullItemsAndCostumes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down
7 changes: 5 additions & 2 deletions repentogon/Patches/XMLData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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++
Expand Down
6 changes: 3 additions & 3 deletions repentogon/Patches/XMLData.h
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,7 @@ inline bool XMLParse(xml_document<char>* xmldoc, char* xml, const string& dir) {
}

inline char* GetResources(const string& dir, const string& filename) {
vector<string> paths = { dir + "\\resources-dlc3\\" + filename, dir + "\\resources\\" + filename };
vector<string> 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()) {
Expand Down Expand Up @@ -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<string> paths = { dir + "\\resources-dlc3\\" + xml.filename, dir + "\\resources\\" + xml.filename };
vector<string> paths = { dir + "\\resources-repentogon\\", dir + "\\resources-dlc3\\" + xml.filename, dir + "\\resources\\" + xml.filename };
for (const string& path : paths) {
if (filesystem::exists(path)) {
targetresource = path;
Expand All @@ -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<string> paths = { dir + "\\content-dlc3\\" + xml.filename, dir + "\\content\\" + xml.filename };
vector<string> 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());
Expand Down
1 change: 1 addition & 0 deletions repentogon/REPENTOGONFileMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace REPENTOGONFileMap {
std::vector<std::wstring> _stringByFType = {
L"resources",
L"resources-dlc3",
L"resources-repentogon"
L"",
};
std::wstring _modsPath = L"";
Expand Down
1 change: 1 addition & 0 deletions repentogon/REPENTOGONFileMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace REPENTOGONFileMap {
enum FolderType {
RESOURCES,
RESOURCES_DLC3,
RESOURCES_REPENTOGON,
LAST
};
struct FileMapEntry {
Expand Down