Added support for NAND saving in WarioWare D.I.Y. and Jam with the Band (#6)

Also improved speed of creating save files
This commit is contained in:
Gericom
2025-12-28 13:18:30 +01:00
parent 64e020182a
commit 6d12399ba4
17 changed files with 409 additions and 27 deletions

View File

@@ -13,6 +13,8 @@
#include "patches/arm9/OSResetSystemPatch.h"
#include "patches/arm9/PokemonDownloaderArm9Patch.h"
#include "patches/arm9/DSProtectArm9Patch.h"
#include "patches/arm9/NandSave/JamWithTheBandNandSavePatch.h"
#include "patches/arm9/NandSave/WarioWareDiyNandSavePatch.h"
#include "patches/arm9/OverlayPatches/FsStartOverlayHookPatch.h"
#include "patches/arm9/OverlayPatches/DSProtectPatches/DSProtectOverlayPatch.h"
#include "patches/arm9/OverlayPatches/DSProtectPatches/DSProtectPuyoPuyo7Patch.h"
@@ -374,6 +376,20 @@ void Arm9Patcher::AddGameSpecificPatches(
overlayHookPatch->AddOverlayPatch(new PokemonIrApPatch(PokemonIrVersion::Bw2));
break;
}
// WarioWare: D.I.Y.
case GAMECODE("UORE"):
case GAMECODE("UORP"):
case GAMECODE("UORJ"):
{
patchCollection.AddPatch(new WarioWareDiyNandSavePatch());
break;
}
// Jam with the Band
case GAMECODE("UXBP"):
{
patchCollection.AddPatch(new JamWithTheBandNandSavePatch());
break;
}
}
}

View File

@@ -1,6 +1,6 @@
#pragma once
#include "sections.h"
#include "../SectorRemapPatchCode.h"
#include "SectorRemapPatchCode.h"
#include "fileInfo.h"
DEFINE_SECTION_SYMBOLS(saveoffsettosdsector);

View File

@@ -8,8 +8,7 @@
#include "patches/arm7/VerifySaveAsm.h"
#include "patches/FunctionSignature.h"
#include "patches/platform/LoaderPlatform.h"
#include "patches/arm7/SaveOffsetToSdSectorAsm.h"
#include "patches/OffsetToSectorRemapAsm.h"
#include "patches/SaveOffsetToSdSectorAsm.h"
#include "patches/arm7/CardiTaskThreadPatchAsm.h"
#include "thumbInstructions.h"
#include "CardiTaskThreadPatch.h"
@@ -95,14 +94,11 @@ void CardiTaskThreadPatch::ApplyPatch(PatchContext& patchContext)
u32 patch1Size = SECTION_SIZE(patch_carditaskthread);
void* patch1Address = patchContext.GetPatchHeap().Alloc(patch1Size);
auto loaderPlatform = patchContext.GetLoaderPlatform();
const SdReadPatchCode* readPatchCode;
const SdWritePatchCode* writePatchCode;
const SectorRemapPatchCode* sectorRemapPatchCode;
readPatchCode = loaderPlatform->CreateSdReadPatchCode(
auto readPatchCode = loaderPlatform->CreateSdReadPatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
writePatchCode = loaderPlatform->CreateSdWritePatchCode(
auto writePatchCode = loaderPlatform->CreateSdWritePatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
sectorRemapPatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<SaveOffsetToSdSectorPatchCode>
auto sectorRemapPatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<SaveOffsetToSdSectorPatchCode>
(
patchContext.GetPatchHeap(),
(const save_file_info_t*)((u32)SHARED_SAVE_FILE_INFO - 0x02F00000 + 0x02700000)

View File

@@ -5,9 +5,8 @@
#include "patches/arm7/ReadSaveAsm.h"
#include "patches/arm7/WriteSaveAsm.h"
#include "patches/arm7/VerifySaveAsm.h"
#include "patches/arm7/SaveOffsetToSdSectorAsm.h"
#include "patches/SaveOffsetToSdSectorAsm.h"
#include "patches/platform/LoaderPlatform.h"
#include "patches/OffsetToSectorRemapAsm.h"
#include "patches/arm7/CardiTaskThreadPatchAsm.h"
#include "CardiDoTaskFromArm9Patch.h"

View File

@@ -0,0 +1,57 @@
#include "common.h"
#include "gameCode.h"
#include "patches/platform/LoaderPlatform.h"
#include "patches/SaveOffsetToSdSectorAsm.h"
#include "ReadNandSaveAsm.h"
#include "WriteNandSaveAsm.h"
#include "JamWithTheBandNandSavePatch.h"
// This code was based on nds-bootstrap:
// https://github.com/DS-Homebrew/nds-bootstrap/blob/89f27d1392a68436695d0050992ee84258ef41bc/retail/bootloader/source/arm7/patch_arm9.c#L2531
bool JamWithTheBandNandSavePatch::FindPatchTarget(PatchContext& patchContext)
{
return true;
}
void JamWithTheBandNandSavePatch::ApplyPatch(PatchContext& patchContext)
{
auto sectorRemapPatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<SaveOffsetToSdSectorPatchCode>
(
patchContext.GetPatchHeap(),
(const save_file_info_t*)((u32)SHARED_SAVE_FILE_INFO - 0x02F00000 + 0x02700000)
);
auto loaderPlatform = patchContext.GetLoaderPlatform();
auto readNandSavePatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<ReadNandSavePatchCode>
(
patchContext.GetPatchHeap(),
sectorRemapPatchCode,
loaderPlatform->CreateSdReadPatchCode(patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap())
);
auto writeNandSavePatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<WriteNandSavePatchCode>
(
patchContext.GetPatchHeap(),
sectorRemapPatchCode,
loaderPlatform->CreateSdWritePatchCode(patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap())
);
// u32 nandInit(void* data)
*(u32*)0x020613CC = 0xE3A00001; // mov r0, #1
*(u32*)0x020613D0 = 0xE12FFF1E; // bx lr
// u32 nandResume(void)
*(u32*)0x02061A4C = 0xE3A00000; // mov r0, #0
*(u32*)0x02061A50 = 0xE12FFF1E; // bx lr
// u32 nandError(void)
*(u32*)0x02061C24 = 0xE3A00000; // mov r0, #0
*(u32*)0x02061C28 = 0xE12FFF1E; // bx lr
// u32 nandWrite(const void* memory, u32 flash, u32 size, u32 dmaChannel)
*(u32*)0x0206176C = 0xE51FF004; // ldr pc,= patch_writeNandSave
*(u32*)0x02061770 = (u32)writeNandSavePatchCode->GetWriteNandSaveFunction();
// u32 nandRead(void* memory, u32 flash, u32 size, u32 dmaChannel)
*(u32*)0x02061AC4 = 0xE51FF004; // ldr pc,= patch_readNandSave
*(u32*)0x02061AC8 = (u32)readNandSavePatchCode->GetReadNandSaveFunction();
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include "patches/Patch.h"
class FunctionSignature;
/// @brief Arm9 patch to redirect Jam with the Band nand saving to the SD card.
class JamWithTheBandNandSavePatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include "patches/PatchCode.h"
#include "sections.h"
#include "patches/SaveOffsetToSdSectorAsm.h"
#include "patches/platform/SdReadPatchCode.h"
DEFINE_SECTION_SYMBOLS(patch_readnandsave);
extern "C" bool patch_readNandSave(void* dst, u32 nandByteOffset, u32 byteLength, u32 dmaChannel);
extern u32 patch_readNandSave_save_offset_to_sd_sector_asm_address;
extern u32 patch_readNandSave_sdread_asm_address;
class ReadNandSavePatchCode : public PatchCode
{
public:
ReadNandSavePatchCode(PatchHeap& patchHeap, const SaveOffsetToSdSectorPatchCode* saveOffsetToSdSectorPatchCode,
const SdReadPatchCode* sdReadPatchCode)
: PatchCode(SECTION_START(patch_readnandsave), SECTION_SIZE(patch_readnandsave), patchHeap)
{
patch_readNandSave_save_offset_to_sd_sector_asm_address = (u32)saveOffsetToSdSectorPatchCode->GetRemapFunction();
patch_readNandSave_sdread_asm_address = (u32)sdReadPatchCode->GetSdReadFunction();
}
const void* GetReadNandSaveFunction() const
{
return GetAddressAtTarget((void*)patch_readNandSave);
}
};

View File

@@ -0,0 +1,57 @@
.cpu arm946e-s
.section "patch_readnandsave", "ax"
.syntax unified
.thumb
// r0 = memory dst
// r1 = nand byte offset
// r2 = byte length
// returns r0 = bool success
.global patch_readNandSave
.type patch_readNandSave, %function
patch_readNandSave:
push {r0,r1,r2,r4,r5,r6,lr}
pop {r4,r5,r6}
lsrs r6, r6, #9 // remaining number of sectors = byte length / 512
loop:
// while remaining number of sectors > 0
cmp r6, #0
beq end
movs r0, r5 // nand byte offset
ldr r3, patch_readNandSave_save_offset_to_sd_sector_asm_address
blx r3
mov r2, lr // sectors to read
// if sectors to read > remaining number of sectors
cmp r2, r6
bls 1f
movs r2, r6 // sectors to read = remaining number of sectors
1:
subs r6, r2 // remaining number of sectors -= sectors to read
movs r1, r4 // memory dst
lsls r3, r2, #9
adds r4, r3 // memory dst += sectors to read * 512
adds r5, r3 // nand byte offset += sectors to read * 512
ldr r3, patch_readNandSave_sdread_asm_address
blx r3
b loop
end:
movs r0, #1
pop {r4,r5,r6,pc}
.balign 4
.pool
.global patch_readNandSave_save_offset_to_sd_sector_asm_address
patch_readNandSave_save_offset_to_sd_sector_asm_address:
.word 0
.global patch_readNandSave_sdread_asm_address
patch_readNandSave_sdread_asm_address:
.word 0
.end

View File

@@ -0,0 +1,85 @@
#include "common.h"
#include "gameCode.h"
#include "patches/platform/LoaderPlatform.h"
#include "patches/SaveOffsetToSdSectorAsm.h"
#include "ReadNandSaveAsm.h"
#include "WriteNandSaveAsm.h"
#include "WarioWareDiyNandSavePatch.h"
// This code was based on nds-bootstrap:
// https://github.com/DS-Homebrew/nds-bootstrap/blob/89f27d1392a68436695d0050992ee84258ef41bc/retail/bootloader/source/arm7/patch_arm9.c#L2531
bool WarioWareDiyNandSavePatch::FindPatchTarget(PatchContext& patchContext)
{
_sdPatchEntry = nullptr;
switch (patchContext.GetGameCode())
{
case GAMECODE("UORE"):
{
_sdPatchEntry = (u8*)0x02002C04;
break;
}
case GAMECODE("UORP"):
{
_sdPatchEntry = (u8*)0x02002CA4;
break;
}
case GAMECODE("UORJ"):
{
_sdPatchEntry = (u8*)0x02002BE4;
break;
}
}
return _sdPatchEntry != nullptr;
}
void WarioWareDiyNandSavePatch::ApplyPatch(PatchContext& patchContext)
{
if (_sdPatchEntry == nullptr)
{
return;
}
auto sectorRemapPatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<SaveOffsetToSdSectorPatchCode>
(
patchContext.GetPatchHeap(),
(const save_file_info_t*)((u32)SHARED_SAVE_FILE_INFO - 0x02F00000 + 0x02700000)
);
auto loaderPlatform = patchContext.GetLoaderPlatform();
auto readNandSavePatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<ReadNandSavePatchCode>
(
patchContext.GetPatchHeap(),
sectorRemapPatchCode,
loaderPlatform->CreateSdReadPatchCode(patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap())
);
auto writeNandSavePatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<WriteNandSavePatchCode>
(
patchContext.GetPatchHeap(),
sectorRemapPatchCode,
loaderPlatform->CreateSdWritePatchCode(patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap())
);
// u32 nandInit(void* data)
*(u32*)(_sdPatchEntry + 0x50C) = 0xE3A00001; // mov r0, #1
*(u32*)(_sdPatchEntry + 0x510) = 0xE12FFF1E; // bx lr
// u32 nandWait(void)
*(u32*)(_sdPatchEntry + 0xC9C) = 0xE12FFF1E; // bx lr
// u32 nandState(void)
*(u32*)(_sdPatchEntry + 0xEB0) = 0xE3A00003; // mov r0, #3
*(u32*)(_sdPatchEntry + 0xEB4) = 0xE12FFF1E; // bx lr
// u32 nandError(void)
*(u32*)(_sdPatchEntry + 0xEC8) = 0xE3A00000; // mov r0, #0
*(u32*)(_sdPatchEntry + 0xECC) = 0xE12FFF1E; // bx lr
// u32 nandWrite(void* memory, u32 flash, u32 size, u32 dmaChannel)
*(u32*)(_sdPatchEntry + 0x958) = 0xE51FF004; // ldr pc,= patch_writeNandSave
*(u32*)(_sdPatchEntry + 0x95C) = (u32)writeNandSavePatchCode->GetWriteNandSaveFunction();
// u32 nandRead(void* memory, u32 flash, u32 size, u32 dmaChannel)
*(u32*)(_sdPatchEntry + 0xD24) = 0xE51FF004; // ldr pc,= patch_readNandSave
*(u32*)(_sdPatchEntry + 0xD28) = (u32)readNandSavePatchCode->GetReadNandSaveFunction();
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include "patches/Patch.h"
class FunctionSignature;
/// @brief Arm9 patch to redirect WarioWare D.I.Y. nand saving to the SD card.
class WarioWareDiyNandSavePatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u8* _sdPatchEntry = nullptr;
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include "patches/PatchCode.h"
#include "sections.h"
#include "patches/SaveOffsetToSdSectorAsm.h"
#include "patches/platform/SdWritePatchCode.h"
DEFINE_SECTION_SYMBOLS(patch_writenandsave);
extern "C" bool patch_writeNandSave(const void* src, u32 nandByteOffset, u32 byteLength, u32 dmaChannel);
extern u32 patch_writeNandSave_save_offset_to_sd_sector_asm_address;
extern u32 patch_writeNandSave_sdwrite_asm_address;
class WriteNandSavePatchCode : public PatchCode
{
public:
WriteNandSavePatchCode(PatchHeap& patchHeap, const SaveOffsetToSdSectorPatchCode* saveOffsetToSdSectorPatchCode,
const SdWritePatchCode* sdWritePatchCode)
: PatchCode(SECTION_START(patch_writenandsave), SECTION_SIZE(patch_writenandsave), patchHeap)
{
patch_writeNandSave_save_offset_to_sd_sector_asm_address = (u32)saveOffsetToSdSectorPatchCode->GetRemapFunction();
patch_writeNandSave_sdwrite_asm_address = (u32)sdWritePatchCode->GetSdWriteFunction();
}
const void* GetWriteNandSaveFunction() const
{
return GetAddressAtTarget((void*)patch_writeNandSave);
}
};

View File

@@ -0,0 +1,57 @@
.cpu arm946e-s
.section "patch_writenandsave", "ax"
.syntax unified
.thumb
// r0 = memory src
// r1 = nand byte offset
// r2 = byte length
// returns r0 = bool success
.global patch_writeNandSave
.type patch_writeNandSave, %function
patch_writeNandSave:
push {r0,r1,r2,r4,r5,r6,lr}
pop {r4,r5,r6}
lsrs r6, r6, #9 // remaining number of sectors = byte length / 512
loop:
// while remaining number of sectors > 0
cmp r6, #0
beq end
movs r0, r5 // nand byte offset
ldr r3, patch_writeNandSave_save_offset_to_sd_sector_asm_address
blx r3
mov r2, lr // sectors to read
// if sectors to read > remaining number of sectors
cmp r2, r6
bls 1f
movs r2, r6 // sectors to read = remaining number of sectors
1:
subs r6, r2 // remaining number of sectors -= sectors to read
movs r1, r4 // memory src
lsls r3, r2, #9
adds r4, r3 // memory src += sectors to read * 512
adds r5, r3 // nand byte offset += sectors to read * 512
ldr r3, patch_writeNandSave_sdwrite_asm_address
blx r3
b loop
end:
movs r0, #1
pop {r4,r5,r6,pc}
.balign 4
.pool
.global patch_writeNandSave_save_offset_to_sd_sector_asm_address
patch_writeNandSave_save_offset_to_sd_sector_asm_address:
.word 0
.global patch_writeNandSave_sdwrite_asm_address
patch_writeNandSave_sdwrite_asm_address:
.word 0
.end