From f5a8498e08ee402e873f56917f53798587a3f5bb Mon Sep 17 00:00:00 2001 From: Mow <32942550+taxicat1@users.noreply.github.com> Date: Sat, 31 Jan 2026 14:54:37 -0500 Subject: [PATCH] Add system for adjusting addresses for autoload (#118) Fixes #41 --- arm9/source/Arm7Patcher.cpp | 12 ++++ arm9/source/Arm9Patcher.cpp | 54 +++++++++++++---- arm9/source/AutoloadAdjuster.h | 59 +++++++++++++++++++ arm9/source/IAutoloadAdjuster.h | 21 +++++++ arm9/source/patches/PatchContext.h | 17 +++++- .../arm7/sdk5/Sdk5DsiSdCardRedirectPatch.cpp | 26 +------- .../arm9/sdk2to4/CardiTryReadCardDmaPatch.cpp | 48 ++++++++++++--- common/moduleParams.h | 7 +++ 8 files changed, 197 insertions(+), 47 deletions(-) create mode 100644 arm9/source/AutoloadAdjuster.h create mode 100644 arm9/source/IAutoloadAdjuster.h diff --git a/arm9/source/Arm7Patcher.cpp b/arm9/source/Arm7Patcher.cpp index 718d8b8..3aeca80 100644 --- a/arm9/source/Arm7Patcher.cpp +++ b/arm9/source/Arm7Patcher.cpp @@ -29,14 +29,26 @@ void* Arm7Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform) const // Spider-Man 2 (USA) is probably the only game without module params sdkVersion = 0x02004F50; } + std::unique_ptr arm7Autoload; // TODO unused + std::unique_ptr arm7iAutoload; + if (gIsDsiMode && romHeader->SupportsDsiMode()) + { + auto arm7iModuleParams = (const module_params_twl_t*)(twlRomHeader->arm7LoadAddress + twlRomHeader->arm7iModuleParamsAddress); + arm7iAutoload = std::make_unique>( + (autoload_list_entry_sdk5_t*)arm7iModuleParams->autoloadListStart, + (autoload_list_entry_sdk5_t*)arm7iModuleParams->autoloadListEnd, + arm7iModuleParams->autoloadStart); + } PatchCollection patchCollection; LOG_DEBUG("Arm7 region: 0x%x - 0x%x\n", romHeader->arm7LoadAddress, romHeader->arm7LoadAddress + romHeader->arm7Size); PatchContext patchContext { (void*)romHeader->arm7LoadAddress, romHeader->arm7Size, + std::move(arm7Autoload), (romHeader->SupportsDsiMode()) ? (void*)twlRomHeader->arm7iLoadAddress : nullptr, (romHeader->SupportsDsiMode()) ? twlRomHeader->arm7iSize : 0, + std::move(arm7iAutoload), sdkVersion, romHeader->gameCode, romHeader->softwareVersion, diff --git a/arm9/source/Arm9Patcher.cpp b/arm9/source/Arm9Patcher.cpp index cbb3839..1d679af 100644 --- a/arm9/source/Arm9Patcher.cpp +++ b/arm9/source/Arm9Patcher.cpp @@ -1,5 +1,6 @@ #include "common.h" #include "ModuleParamsLocator.h" +#include "AutoloadAdjuster.h" #include "SdkVersion.h" #include "patches/PatchCollection.h" #include "patches/PatchContext.h" @@ -54,6 +55,8 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis u32 arm9Size = romHeader->arm9Size; u32 arm9iSize = romHeader->SupportsDsiMode() ? twlRomHeader->arm9iSize : 0; u32 compressedEnd = 0; + std::unique_ptr arm9Autoload; + std::unique_ptr arm9iAutoload; auto moduleParams = ModuleParamsLocator().FindModuleParams(romHeader); SdkVersion sdkVersion = moduleParams ? moduleParams->sdkVersion : 0u; if (moduleParams) @@ -77,25 +80,46 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis } } + if (sdkVersion.IsTwlSdk()) + { + arm9Autoload = std::make_unique>( + (autoload_list_entry_sdk5_t*)moduleParams->autoloadListStart, + (autoload_list_entry_sdk5_t*)moduleParams->autoloadListEnd, + moduleParams->autoloadStart); + } + else + { + arm9Autoload = std::make_unique>( + (autoload_list_entry_t*)moduleParams->autoloadListStart, + (autoload_list_entry_t*)moduleParams->autoloadListEnd, + moduleParams->autoloadStart); + } + if (gIsDsiMode && romHeader->SupportsDsiMode()) { auto arm9iModuleParams = (module_params_twl_t*)(romHeader->arm9LoadAddress + twlRomHeader->arm9iModuleParamsAddress); if (arm9iModuleParams->magicBigEndian == MODULE_PARAMS_TWL_MAGIC_BE && - arm9iModuleParams->magicLittleEndian == MODULE_PARAMS_TWL_MAGIC_LE && - arm9iModuleParams->compressedEnd != 0) + arm9iModuleParams->magicLittleEndian == MODULE_PARAMS_TWL_MAGIC_LE) { - LOG_DEBUG("Compressed arm9i found\n"); - if (miiUncompressBackward) + if (arm9iModuleParams->compressedEnd != 0) { - arm9iSize = arm9iModuleParams->compressedEnd + *(u32*)(arm9iModuleParams->compressedEnd - 4) - twlRomHeader->arm9iLoadAddress; - ((uncompress_func_t)miiUncompressBackward)((void*)arm9iModuleParams->compressedEnd); - arm9iModuleParams->compressedEnd = 0; - LOG_DEBUG("Decompressed arm9i\n"); - } - else - { - LOG_DEBUG("Could not decompress arm9i\n"); + LOG_DEBUG("Compressed arm9i found\n"); + if (miiUncompressBackward) + { + arm9iSize = arm9iModuleParams->compressedEnd + *(u32*)(arm9iModuleParams->compressedEnd - 4) - twlRomHeader->arm9iLoadAddress; + ((uncompress_func_t)miiUncompressBackward)((void*)arm9iModuleParams->compressedEnd); + arm9iModuleParams->compressedEnd = 0; + LOG_DEBUG("Decompressed arm9i\n"); + } + else + { + LOG_DEBUG("Could not decompress arm9i\n"); + } } + arm9iAutoload = std::make_unique>( + (autoload_list_entry_sdk5_t*)arm9iModuleParams->autoloadListStart, + (autoload_list_entry_sdk5_t*)arm9iModuleParams->autoloadListEnd, + arm9iModuleParams->autoloadStart); } } } @@ -106,6 +130,10 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis { // Spider-Man 2 (USA) is probably the only game without module params sdkVersion = 0x02004F50; + arm9Autoload = std::make_unique>( + (autoload_list_entry_t*)0x0215DBA0, + (autoload_list_entry_t*)0x0215DBB8, + 0x02157CC0); } } LOG_DEBUG("Arm9 region: 0x%x - 0x%x\n", romHeader->arm9LoadAddress, romHeader->arm9LoadAddress + arm9Size); @@ -113,8 +141,10 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis { (void*)romHeader->arm9LoadAddress, arm9Size, + std::move(arm9Autoload), romHeader->SupportsDsiMode() ? (void*)twlRomHeader->arm9iLoadAddress : nullptr, arm9iSize, + std::move(arm9iAutoload), sdkVersion, romHeader->gameCode, romHeader->softwareVersion, diff --git a/arm9/source/AutoloadAdjuster.h b/arm9/source/AutoloadAdjuster.h new file mode 100644 index 0000000..7df1677 --- /dev/null +++ b/arm9/source/AutoloadAdjuster.h @@ -0,0 +1,59 @@ +#pragma once +#include "common.h" +#include "moduleParams.h" +#include "IAutoloadAdjuster.h" + +/// @brief Class for adjusting addresses for autoload from autoload list entries. +/// @tparam T The type of autoload list entry. +template +class AutoloadAdjuster : public IAutoloadAdjuster +{ +public: + AutoloadAdjuster(const T* autoloadListStart, const T* autoloadListEnd, u32 autoloadStartAddress) + : _autoloadListStart(autoloadListStart), _autoloadListEnd(autoloadListEnd), _autoloadStartAddress(autoloadStartAddress) { } + + u32 AdjustInitialToFinal(u32 initialAddress) const override + { + u32 finalAddress = initialAddress; + u32 currentAddress = _autoloadStartAddress; + for (auto autoloadListCurr = _autoloadListStart; autoloadListCurr != _autoloadListEnd; autoloadListCurr++) + { + if (initialAddress >= currentAddress && initialAddress < currentAddress + autoloadListCurr->size) + { + finalAddress -= currentAddress; + finalAddress += autoloadListCurr->targetAddress; + break; + } + else + { + currentAddress += autoloadListCurr->size; + } + } + return finalAddress; + } + + u32 AdjustFinalToInitial(u32 finalAddress) const override + { + u32 initialAddress = finalAddress; + u32 currentAddress = _autoloadStartAddress; + for (auto autoloadListCurr = _autoloadListStart; autoloadListCurr != _autoloadListEnd; autoloadListCurr++) + { + if (finalAddress >= autoloadListCurr->targetAddress && finalAddress < autoloadListCurr->targetAddress + autoloadListCurr->size) + { + initialAddress -= autoloadListCurr->targetAddress; + initialAddress += currentAddress; + break; + } + else + { + currentAddress += autoloadListCurr->size; + } + } + return initialAddress; + } + +private: + const T* _autoloadListStart; + const T* _autoloadListEnd; + u32 _autoloadStartAddress; +}; diff --git a/arm9/source/IAutoloadAdjuster.h b/arm9/source/IAutoloadAdjuster.h new file mode 100644 index 0000000..3ec0f70 --- /dev/null +++ b/arm9/source/IAutoloadAdjuster.h @@ -0,0 +1,21 @@ +#pragma once + +/// @brief Interface for an autoload address adjuster. +class IAutoloadAdjuster +{ +public: + virtual ~IAutoloadAdjuster() { } + + /// @brief Adjust an initial address (pre-autoload) to its final location after autoload. + /// @param initialAddress The address to adjust for autoloading. + /// @return The final location of the address. + virtual u32 AdjustInitialToFinal(u32 initialAddress) const = 0; + + /// @brief Adjust a final address (post-autoload) to its initial location before autoload. + /// @param finalAddress The address to adjust for autoloading. + /// @return The initial location of the address. + virtual u32 AdjustFinalToInitial(u32 finalAddress) const = 0; + +protected: + IAutoloadAdjuster() { } +}; diff --git a/arm9/source/patches/PatchContext.h b/arm9/source/patches/PatchContext.h index 2aad29c..1e5e156 100644 --- a/arm9/source/patches/PatchContext.h +++ b/arm9/source/patches/PatchContext.h @@ -1,6 +1,7 @@ #pragma once #include #include "SdkVersion.h" +#include "AutoloadAdjuster.h" #include "PatchHeap.h" #include "PatchCodeCollection.h" @@ -10,9 +11,11 @@ class LoaderPlatform; class PatchContext { public: - PatchContext(void* data, u32 dataSize, void* twlData, u32 twlDataSize, + PatchContext(void* data, u32 dataSize, std::unique_ptr autoloadAdjuster, + void* twlData, u32 twlDataSize, std::unique_ptr twlAutoloadAdjuster, SdkVersion sdkVersion, u32 gameCode, u8 gameRevision, const LoaderPlatform* loaderPlatform) - : _data(data), _dataSize(dataSize), _twlData(twlData), _twlDataSize(twlDataSize) + : _data(data), _dataSize(dataSize), _autoloadAdjuster(std::move(autoloadAdjuster)) + , _twlData(twlData), _twlDataSize(twlDataSize), _twlAutoloadAdjuster(std::move(twlAutoloadAdjuster)) , _sdkVersion(sdkVersion), _gameCode(gameCode), _gameRevision(gameRevision), _loaderPlatform(loaderPlatform) { } /// @brief Tries to find the given \p pattern of the given \p byteLength in the ntr region. @@ -27,6 +30,14 @@ public: /// @return A pointer to the first location where the pattern was found, or \c nullptr if the pattern was not found. u32* FindPattern32Twl(const u32* pattern, u32 byteLength) const; + /// @brief Returns the ntr autoload adjuster of this context. + /// @return The ntr autoload adjuster of this context. + constexpr const IAutoloadAdjuster* GetAutoloadAdjuster() const { return _autoloadAdjuster.get(); } + + /// @brief Returns the twl autoload adjuster of this context. + /// @return The twl autoload adjuster of this context. + constexpr const IAutoloadAdjuster* GetAutoloadAdjusterTwl() const { return _twlAutoloadAdjuster.get(); } + /// @brief Returns the patch heap of this context. /// @return The patch heap of this context. constexpr PatchHeap& GetPatchHeap() { return _patchHeap; } @@ -54,8 +65,10 @@ public: private: void* _data; u32 _dataSize; + std::unique_ptr _autoloadAdjuster; void* _twlData; u32 _twlDataSize; + std::unique_ptr _twlAutoloadAdjuster; SdkVersion _sdkVersion; u32 _gameCode; u8 _gameRevision; diff --git a/arm9/source/patches/arm7/sdk5/Sdk5DsiSdCardRedirectPatch.cpp b/arm9/source/patches/arm7/sdk5/Sdk5DsiSdCardRedirectPatch.cpp index d7774e2..a2634c8 100644 --- a/arm9/source/patches/arm7/sdk5/Sdk5DsiSdCardRedirectPatch.cpp +++ b/arm9/source/patches/arm7/sdk5/Sdk5DsiSdCardRedirectPatch.cpp @@ -63,29 +63,6 @@ static u32 getThumbBlAddress(const u32* instructionPointer) return (u32)instructionPointer + 5 + ((int)((((blInstruction1 & 0x7FF) << 11) | (blInstruction2 & 0x7FF)) << 10) >> 9); } -static u32 correctAddressForArm7iAutoLoad(u32 address) -{ - auto romHeader = (const nds_header_twl_t*)TWL_SHARED_MEMORY->twlRomHeader; - auto arm7iModuleParams = (const module_params_twl_t*)( - romHeader->arm7LoadAddress + romHeader->arm7iModuleParamsAddress); - - auto autoLoadListEntry = (autoload_list_entry_sdk5_t*)arm7iModuleParams->autoloadListStart; - u32 currentAddress = arm7iModuleParams->autoloadStart; - while (autoLoadListEntry != (autoload_list_entry_sdk5_t*)arm7iModuleParams->autoloadListEnd) - { - if (address >= currentAddress && address < currentAddress + autoLoadListEntry->size) - { - address -= currentAddress; - address += autoLoadListEntry->targetAddress; - break; - } - currentAddress += autoLoadListEntry->size; - autoLoadListEntry++; - } - - return address; -} - void Sdk5DsiSdCardRedirectPatch::ApplyPatch(PatchContext& patchContext) { if (!_attachFunction) @@ -101,7 +78,8 @@ void Sdk5DsiSdCardRedirectPatch::ApplyPatch(PatchContext& patchContext) getDriveStructAddress = getArmBlAddress((u32*)((u8*)_attachFunction + _blToGetDriveStructOffset)); } - getDriveStructAddress = correctAddressForArm7iAutoLoad(getDriveStructAddress); + auto arm7iAutoload = patchContext.GetAutoloadAdjusterTwl(); + getDriveStructAddress = arm7iAutoload->AdjustInitialToFinal(getDriveStructAddress); auto sdRead = patchContext.GetLoaderPlatform()->CreateSdReadPatchCode( patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap()); diff --git a/arm9/source/patches/arm9/sdk2to4/CardiTryReadCardDmaPatch.cpp b/arm9/source/patches/arm9/sdk2to4/CardiTryReadCardDmaPatch.cpp index eeb8840..5619d6d 100644 --- a/arm9/source/patches/arm9/sdk2to4/CardiTryReadCardDmaPatch.cpp +++ b/arm9/source/patches/arm9/sdk2to4/CardiTryReadCardDmaPatch.cpp @@ -169,6 +169,11 @@ bool CardiTryReadCardDmaPatch::FindPatchTarget(PatchContext& patchContext) return true; //_cardiTryReadCardDma != nullptr; } +static s32 getArmBlOffset(u32 blInstruction) +{ + return 8 + ((int)((blInstruction & 0xFFFFFF) << 8) >> 6); +} + static u32 getArmBlAddress(const u32* instructionPointer) { u32 blInstruction = *instructionPointer; @@ -193,7 +198,7 @@ void CardiTryReadCardDmaPatch::ApplyPatch(PatchContext& patchContext) u32 cardiCommon; u32 cardiOnReadCard; u32 cardiSetCardDma; - u32 miiCardDmaCopy32; + u32 cardiSetCardDmaDmaCopyCallOffset; u32 cardiOnReadCardOffset; if (_foundPattern == sCARDiTryReadCardDmaPattern20029A7) { @@ -202,7 +207,7 @@ void CardiTryReadCardDmaPatch::ApplyPatch(PatchContext& patchContext) cardiSetCardDma = getArmBlAddress((u32*)((u8*)_cardiTryReadCardDma + 0x124)); if (*(u32*)cardiOnReadCard == 0xE92D40F0u) { - miiCardDmaCopy32 = getArmBlAddress((u32*)(cardiSetCardDma + 0x18)); + cardiSetCardDmaDmaCopyCallOffset = 0x18; cardiOnReadCardOffset = 0x40; } else @@ -218,7 +223,7 @@ void CardiTryReadCardDmaPatch::ApplyPatch(PatchContext& patchContext) cardiCommon = *(u32*)((u8*)_cardiTryReadCardDma + 0x140); cardiOnReadCard = *(u32*)((u8*)_cardiTryReadCardDma + 0x150); cardiSetCardDma = getArmBlAddress((u32*)((u8*)_cardiTryReadCardDma + 0x130)); - miiCardDmaCopy32 = getArmBlAddress((u32*)(cardiSetCardDma + 0x18)); + cardiSetCardDmaDmaCopyCallOffset = 0x18; cardiOnReadCardOffset = 0x40; } else if (_foundPattern == sCARDiTryReadCardDmaPattern) @@ -226,7 +231,7 @@ void CardiTryReadCardDmaPatch::ApplyPatch(PatchContext& patchContext) cardiCommon = *(u32*)((u8*)_cardiTryReadCardDma + 0x148) + 4; cardiOnReadCard = *(u32*)((u8*)_cardiTryReadCardDma + 0x158); cardiSetCardDma = getArmBlAddress((u32*)((u8*)_cardiTryReadCardDma + 0x138)); - miiCardDmaCopy32 = getArmBlAddress((u32*)(cardiSetCardDma + 0x18)); + cardiSetCardDmaDmaCopyCallOffset = 0x18; cardiOnReadCardOffset = 0x40; } else if (_foundPattern == sCARDiTryReadCardDmaPatternSdk3017530) @@ -241,7 +246,7 @@ void CardiTryReadCardDmaPatch::ApplyPatch(PatchContext& patchContext) { cardiSetCardDma = getArmBlAddress((u32*)((u8*)_cardiTryReadCardDma + 0x14C)); } - miiCardDmaCopy32 = getArmBlAddress((u32*)(cardiSetCardDma + 0x18)); + cardiSetCardDmaDmaCopyCallOffset = 0x18; cardiOnReadCardOffset = 0x40; } else if (_foundPattern == sCARDiTryReadCardDmaPatternSdk3027530) @@ -249,7 +254,7 @@ void CardiTryReadCardDmaPatch::ApplyPatch(PatchContext& patchContext) cardiCommon = *(u32*)((u8*)_cardiTryReadCardDma + 0x160) + 4; cardiOnReadCard = *(u32*)((u8*)_cardiTryReadCardDma + 0x170); cardiSetCardDma = getArmBlAddress((u32*)((u8*)_cardiTryReadCardDma + 0x14C)); - miiCardDmaCopy32 = getArmBlAddress((u32*)(cardiSetCardDma + 0x18)); + cardiSetCardDmaDmaCopyCallOffset = 0x18; cardiOnReadCardOffset = 0x40; } else if (_foundPattern == sCARDiTryReadCardDmaPatternSdk4007532) @@ -257,7 +262,7 @@ void CardiTryReadCardDmaPatch::ApplyPatch(PatchContext& patchContext) cardiCommon = *(u32*)((u8*)_cardiTryReadCardDma + 0x178) + 4; cardiOnReadCard = *(u32*)((u8*)_cardiTryReadCardDma + 0x188); cardiSetCardDma = getArmBlAddress((u32*)((u8*)_cardiTryReadCardDma + 0x16C)); - miiCardDmaCopy32 = getArmBlAddress((u32*)(cardiSetCardDma + 0x1C)); + cardiSetCardDmaDmaCopyCallOffset = 0x1C; cardiOnReadCardOffset = 0x48; } else if (_foundPattern == sCARDiTryReadCardDmaPatternSdk4027539SpiritTracks) @@ -265,7 +270,7 @@ void CardiTryReadCardDmaPatch::ApplyPatch(PatchContext& patchContext) cardiCommon = *(u32*)((u8*)_cardiTryReadCardDma + 0x180) + 4; cardiOnReadCard = *(u32*)((u8*)_cardiTryReadCardDma + 0x190); cardiSetCardDma = getArmBlAddress((u32*)((u8*)_cardiTryReadCardDma + 0x174)); - miiCardDmaCopy32 = getArmBlAddress((u32*)(cardiSetCardDma + 0x1C)); + cardiSetCardDmaDmaCopyCallOffset = 0x1C; cardiOnReadCardOffset = 0x48; } else @@ -275,7 +280,32 @@ void CardiTryReadCardDmaPatch::ApplyPatch(PatchContext& patchContext) return; } - u32 osDisableIrqMask = getArmBlAddress((u32*)(cardiOnReadCard + cardiOnReadCardOffset + 4)); + // correct all addresses for autoload, if libcard is in an autoload block (New Super Mario Bros.) + auto autoloadAdjuster = patchContext.GetAutoloadAdjuster(); + + // CARDi_OnReadCard address is the final location, but we need its initial location to patch it now + if (autoloadAdjuster) + { + cardiOnReadCard = autoloadAdjuster->AdjustFinalToInitial(cardiOnReadCard); + } + + // MIi_CardDmaCopy32 is relative, but we need its final location to call it later + u32 miiCardDmaCopy32CallLocation = cardiSetCardDma + cardiSetCardDmaDmaCopyCallOffset; + s32 miiCardDmaCopy32CallOffset = getArmBlOffset(*(u32*)miiCardDmaCopy32CallLocation); + if (autoloadAdjuster) + { + miiCardDmaCopy32CallLocation = autoloadAdjuster->AdjustInitialToFinal(miiCardDmaCopy32CallLocation); + } + u32 miiCardDmaCopy32 = miiCardDmaCopy32CallLocation + miiCardDmaCopy32CallOffset; + + // same as above with OS_DisableIrqMask + u32 osDisableIrqMaskCallLocation = cardiOnReadCard + cardiOnReadCardOffset + 4; + s32 osDisableIrqMaskCallOffset = getArmBlOffset(*(u32*)osDisableIrqMaskCallLocation); + if (autoloadAdjuster) + { + osDisableIrqMaskCallLocation = autoloadAdjuster->AdjustInitialToFinal(osDisableIrqMaskCallLocation); + } + u32 osDisableIrqMask = osDisableIrqMaskCallLocation + osDisableIrqMaskCallOffset; // patch CARDi_SetCardDma auto sdReadDmaPatchCode = patchContext.GetLoaderPlatform()->CreateSdReadDmaPatchCode( diff --git a/common/moduleParams.h b/common/moduleParams.h index d968e9e..df2c2bd 100644 --- a/common/moduleParams.h +++ b/common/moduleParams.h @@ -43,6 +43,13 @@ struct build_params_t u32 magicLittleEndian; //0x3381C0DE }; +struct autoload_list_entry_t +{ + u32 targetAddress; + u32 size; + u32 bssSize; +}; + struct autoload_list_entry_sdk5_t { u32 targetAddress;