You've already forked pico-loader
mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-01-09 16:28:35 -08:00
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:
@@ -1,4 +1,5 @@
|
||||
#include "common.h"
|
||||
#include <algorithm>
|
||||
#include <string.h>
|
||||
#include "SaveList.h"
|
||||
#include "SaveListFactory.h"
|
||||
@@ -9,10 +10,13 @@
|
||||
#define DEFAULT_SAVE_SIZE (512 * 1024)
|
||||
#define SAVE_FILL_VALUE 0xFF
|
||||
|
||||
static const u8 sNandSaveId[16] = { 0xEC, 0x00, 0x9E, 0xA1, 0x51, 0x65, 0x34, 0x35, 0x30, 0x35, 0x30, 0x31, 0x19, 0x19, 0x02, 0x0A };
|
||||
|
||||
bool CardSaveArranger::SetupCardSave(u32 gameCode, const TCHAR* savePath) const
|
||||
{
|
||||
SaveList* saveList = SaveListFactory().CreateFromFile(SAVE_LIST_PATH);
|
||||
u32 saveSize = DEFAULT_SAVE_SIZE;
|
||||
auto saveType = CardSaveType::None;
|
||||
if (saveList)
|
||||
{
|
||||
const auto saveListEntry = saveList->FindEntry(gameCode);
|
||||
@@ -23,6 +27,7 @@ bool CardSaveArranger::SetupCardSave(u32 gameCode, const TCHAR* savePath) const
|
||||
}
|
||||
else
|
||||
{
|
||||
saveType = saveListEntry->GetSaveType();
|
||||
saveSize = saveListEntry->GetSaveSize();
|
||||
saveListEntry->Dump();
|
||||
}
|
||||
@@ -50,8 +55,9 @@ bool CardSaveArranger::SetupCardSave(u32 gameCode, const TCHAR* savePath) const
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ffBuffer = std::make_unique<u8[]>(512);
|
||||
memset(ffBuffer.get(), SAVE_FILL_VALUE, 512);
|
||||
const u32 ffBufferSize = 32 * 1024;
|
||||
auto ffBuffer = std::make_unique<u8[]>(ffBufferSize);
|
||||
memset(ffBuffer.get(), SAVE_FILL_VALUE, ffBufferSize);
|
||||
|
||||
u32 offset = initialSize;
|
||||
// Align to 512 bytes
|
||||
@@ -68,17 +74,33 @@ bool CardSaveArranger::SetupCardSave(u32 gameCode, const TCHAR* savePath) const
|
||||
offset += remainingTo512;
|
||||
}
|
||||
|
||||
// Write in 512-byte blocks
|
||||
// Write in up to 32kb blocks
|
||||
while (offset < saveSize)
|
||||
{
|
||||
u32 bytesToWrite = std::min<u32>(saveSize - offset, ffBufferSize);
|
||||
UINT bytesWritten = 0;
|
||||
if (f_write(file.get(), ffBuffer.get(), 512, &bytesWritten) != FR_OK ||
|
||||
bytesWritten != 512)
|
||||
if (f_write(file.get(), ffBuffer.get(), bytesToWrite, &bytesWritten) != FR_OK ||
|
||||
bytesWritten != bytesToWrite)
|
||||
{
|
||||
LOG_FATAL("Failed to expand save file\n");
|
||||
return false;
|
||||
}
|
||||
offset += 512;
|
||||
offset += bytesToWrite;
|
||||
}
|
||||
|
||||
if (saveType == CardSaveType::Nand)
|
||||
{
|
||||
// NAND save games have a read-only NAND save id at the end of their save area.
|
||||
// Jam with the Band checks this id and errors if it cannot find it.
|
||||
// See https://github.com/melonDS-emu/melonDS/blob/3a3388c4c50e8735af125c1af4d89e457f5e9035/src/NDSCart.cpp#L1067
|
||||
UINT bytesWritten = 0;
|
||||
if (f_lseek(file.get(), saveSize - 0x800) != FR_OK ||
|
||||
f_write(file.get(), sNandSaveId, sizeof(sNandSaveId), &bytesWritten) != FR_OK ||
|
||||
bytesWritten != sizeof(sNandSaveId))
|
||||
{
|
||||
LOG_FATAL("Failed to write NAND save id\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "common.h"
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include "DsiWareSaveArranger.h"
|
||||
|
||||
@@ -70,7 +71,7 @@ bool DsiWareSaveArranger::SetupDsiWareSaveFile(const TCHAR* savePath, u32 saveSi
|
||||
if (f_lseek(file.get(), saveSize) != FR_OK ||
|
||||
f_lseek(file.get(), 0) != FR_OK)
|
||||
{
|
||||
LOG_FATAL("Failed to create private save file\n");
|
||||
LOG_FATAL("Failed to create DSiWare save file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -80,21 +81,28 @@ bool DsiWareSaveArranger::SetupDsiWareSaveFile(const TCHAR* savePath, u32 saveSi
|
||||
if (f_write(file.get(), fatHeader.get(), sizeof(fat_header_t), &bytesWritten) != FR_OK ||
|
||||
bytesWritten != sizeof(fat_header_t))
|
||||
{
|
||||
LOG_FATAL("Failed to format private save file\n");
|
||||
LOG_FATAL("Failed to format DSiWare save file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(fatHeader.get(), 0, 512);
|
||||
fatHeader.reset();
|
||||
|
||||
while (f_tell(file.get()) < saveSize)
|
||||
const u32 clearBufferSize = 32 * 1024;
|
||||
auto clearBuffer = std::make_unique<u8[]>(clearBufferSize);
|
||||
memset(clearBuffer.get(), 0, clearBufferSize);
|
||||
|
||||
u32 offset = f_tell(file.get());
|
||||
while (offset < saveSize)
|
||||
{
|
||||
u32 bytesToWrite = std::min<u32>(saveSize - offset, clearBufferSize);
|
||||
bytesWritten = 0;
|
||||
if (f_write(file.get(), fatHeader.get(), 512, &bytesWritten) != FR_OK ||
|
||||
bytesWritten != 512)
|
||||
if (f_write(file.get(), clearBuffer.get(), bytesToWrite, &bytesWritten) != FR_OK ||
|
||||
bytesWritten != bytesToWrite)
|
||||
{
|
||||
LOG_FATAL("Failed to format private save file\n");
|
||||
LOG_FATAL("Failed to format DSiWare save file\n");
|
||||
return false;
|
||||
}
|
||||
offset += bytesToWrite;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
class SaveListEntry
|
||||
{
|
||||
u32 gameCode;
|
||||
u8 saveType; // see CardSaveType
|
||||
CardSaveType saveType;
|
||||
u8 saveSize; // 0 or 1 << x
|
||||
u8 reserved[2]; // for possible future use
|
||||
|
||||
public:
|
||||
u32 GetGameCode() const { return gameCode; }
|
||||
CardSaveType GetSaveType() const { return static_cast<CardSaveType>(saveType); }
|
||||
CardSaveType GetSaveType() const { return saveType; }
|
||||
u32 GetSaveSize() const { return saveSize == 0 ? 0 : (1u << saveSize); }
|
||||
|
||||
void Dump() const
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SectorRemapPatchCode.h"
|
||||
#include "SectorRemapPatchCode.h"
|
||||
#include "fileInfo.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(saveoffsettosdsector);
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
29
arm9/source/patches/arm9/NandSave/ReadNandSaveAsm.h
Normal file
29
arm9/source/patches/arm9/NandSave/ReadNandSaveAsm.h
Normal 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);
|
||||
}
|
||||
};
|
||||
57
arm9/source/patches/arm9/NandSave/ReadNandSaveAsm.s
Normal file
57
arm9/source/patches/arm9/NandSave/ReadNandSaveAsm.s
Normal 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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
29
arm9/source/patches/arm9/NandSave/WriteNandSaveAsm.h
Normal file
29
arm9/source/patches/arm9/NandSave/WriteNandSaveAsm.h
Normal 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);
|
||||
}
|
||||
};
|
||||
57
arm9/source/patches/arm9/NandSave/WriteNandSaveAsm.s
Normal file
57
arm9/source/patches/arm9/NandSave/WriteNandSaveAsm.s
Normal 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
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief Enum representing the save type.
|
||||
enum class CardSaveType
|
||||
enum class CardSaveType : u8
|
||||
{
|
||||
/// @brief The game has no save.
|
||||
None = 0,
|
||||
|
||||
Reference in New Issue
Block a user