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 Face Training and Nintendo DS Guide (fixes #6), calculate nand save size from header, fixed Band Brothers save initialization
This commit is contained in:
@@ -4,34 +4,51 @@
|
||||
#include "SaveList.h"
|
||||
#include "SaveListFactory.h"
|
||||
#include "fileInfo.h"
|
||||
#include "gameCode.h"
|
||||
#include "CardSaveArranger.h"
|
||||
|
||||
#define SAVE_LIST_PATH "/_pico/savelist.bin"
|
||||
#define DEFAULT_SAVE_SIZE (512 * 1024)
|
||||
#define SAVE_FILL_VALUE 0xFF
|
||||
#define NTR_NAND_BLOCK_SIZE 0x20000
|
||||
#define TWL_NAND_BLOCK_SIZE 0x80000
|
||||
#define NAND_RW_REGION_END 0x07A00000
|
||||
|
||||
static const u8 sNandSaveId[16] = { 0xEC, 0x00, 0x9E, 0xA1, 0x51, 0x65, 0x34, 0x35, 0x30, 0x35, 0x30, 0x31, 0x19, 0x19, 0x02, 0x0A };
|
||||
static const u8 sBandBrothersSaveId[12] = { 0x48, 0x8A, 0x00, 0x00, 0x42, 0x42, 0x44, 0x58, 0x31, 0x32, 0x33, 0x34 };
|
||||
static const u8 sJamWithTheBandSaveId[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
|
||||
bool CardSaveArranger::SetupCardSave(const nds_header_ntr_t* header, const TCHAR* savePath) const
|
||||
{
|
||||
SaveList* saveList = SaveListFactory().CreateFromFile(SAVE_LIST_PATH);
|
||||
u32 saveSize = DEFAULT_SAVE_SIZE;
|
||||
auto saveType = CardSaveType::None;
|
||||
if (saveList)
|
||||
u32 saveSize = DEFAULT_SAVE_SIZE;
|
||||
if (header->nandBackupRegionStart != 0)
|
||||
{
|
||||
const auto saveListEntry = saveList->FindEntry(gameCode);
|
||||
if (!saveListEntry)
|
||||
saveType = CardSaveType::Nand;
|
||||
u32 blockSize = header->IsTwlRom() ? TWL_NAND_BLOCK_SIZE : NTR_NAND_BLOCK_SIZE;
|
||||
u32 nandBackupRegionStart = header->nandBackupRegionStart * blockSize;
|
||||
saveSize = NAND_RW_REGION_END - nandBackupRegionStart;
|
||||
LOG_DEBUG("NAND save. Size: 0x%X.", saveSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveList* saveList = SaveListFactory().CreateFromFile(SAVE_LIST_PATH);
|
||||
if (saveList)
|
||||
{
|
||||
LOG_WARNING("Game code %c%c%c%c not found in save list\n",
|
||||
gameCode & 0xFF, (gameCode >> 8) & 0xFF, (gameCode >> 16) & 0xFF, gameCode >> 24);
|
||||
const auto saveListEntry = saveList->FindEntry(header->gameCode);
|
||||
if (!saveListEntry)
|
||||
{
|
||||
LOG_WARNING("Game code %c%c%c%c not found in save list\n",
|
||||
header->gameCode & 0xFF, (header->gameCode >> 8) & 0xFF,
|
||||
(header->gameCode >> 16) & 0xFF, header->gameCode >> 24);
|
||||
}
|
||||
else
|
||||
{
|
||||
saveType = saveListEntry->GetSaveType();
|
||||
saveSize = saveListEntry->GetSaveSize();
|
||||
saveListEntry->Dump();
|
||||
}
|
||||
delete saveList;
|
||||
}
|
||||
else
|
||||
{
|
||||
saveType = saveListEntry->GetSaveType();
|
||||
saveSize = saveListEntry->GetSaveSize();
|
||||
saveListEntry->Dump();
|
||||
}
|
||||
delete saveList;
|
||||
}
|
||||
if (saveSize == 0)
|
||||
{
|
||||
@@ -88,17 +105,29 @@ bool CardSaveArranger::SetupCardSave(u32 gameCode, const TCHAR* savePath) const
|
||||
offset += bytesToWrite;
|
||||
}
|
||||
|
||||
if (saveType == CardSaveType::Nand)
|
||||
// Additional save initialization
|
||||
if (header->gameCode == GAMECODE("AXBJ"))
|
||||
{
|
||||
// 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
|
||||
// Write save id for Band Brothers
|
||||
UINT bytesWritten = 0;
|
||||
if (f_lseek(file.get(), 0) != FR_OK ||
|
||||
f_write(file.get(), sBandBrothersSaveId, sizeof(sBandBrothersSaveId), &bytesWritten) != FR_OK ||
|
||||
bytesWritten != sizeof(sBandBrothersSaveId))
|
||||
{
|
||||
LOG_FATAL("Failed to write Band Brothers save id\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (header->gameCode == GAMECODE("UXBP"))
|
||||
{
|
||||
// Write save id for Jam with the Band
|
||||
// See also 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))
|
||||
f_write(file.get(), sJamWithTheBandSaveId, sizeof(sJamWithTheBandSaveId), &bytesWritten) != FR_OK ||
|
||||
bytesWritten != sizeof(sJamWithTheBandSaveId))
|
||||
{
|
||||
LOG_FATAL("Failed to write NAND save id\n");
|
||||
LOG_FATAL("Failed to write Jam with the Band save id\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#pragma once
|
||||
#include "ndsHeader.h"
|
||||
|
||||
/// @brief Class for setting up the save file for retail card roms.
|
||||
class CardSaveArranger
|
||||
{
|
||||
public:
|
||||
/// @brief Sets up the save file at \p savePath for a retail card rom with the given \p gameCode.
|
||||
/// @param gameCode The game code of the retail card rom.
|
||||
/// @param header The header of the retail card rom.
|
||||
/// @param savePath The desired save file path.
|
||||
/// @return \c true when setting up the save was successful, or \c false otherwise.
|
||||
bool SetupCardSave(u32 gameCode, const TCHAR* savePath) const;
|
||||
bool SetupCardSave(const nds_header_ntr_t* header, const TCHAR* savePath) const;
|
||||
};
|
||||
|
||||
@@ -205,7 +205,11 @@ void NdsLoader::Load(BootMode bootMode)
|
||||
{
|
||||
if (bootMode != BootMode::SdkResetSystem)
|
||||
{
|
||||
HandleCardSave();
|
||||
if (!CardSaveArranger().SetupCardSave(&_romHeader, _savePath))
|
||||
{
|
||||
ErrorDisplay().PrintError("Failed to setup save file.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HandleAntiPiracy();
|
||||
@@ -608,14 +612,6 @@ bool NdsLoader::TryLoadRomHeader(u32 romOffset)
|
||||
return true;
|
||||
}
|
||||
|
||||
void NdsLoader::HandleCardSave()
|
||||
{
|
||||
if (!CardSaveArranger().SetupCardSave(_romHeader.gameCode, _savePath))
|
||||
{
|
||||
ErrorDisplay().PrintError("Failed to setup save file.");
|
||||
}
|
||||
}
|
||||
|
||||
void NdsLoader::HandleAntiPiracy()
|
||||
{
|
||||
auto apList = ApListFactory().CreateFromFile(AP_LIST_PATH);
|
||||
|
||||
@@ -56,7 +56,6 @@ private:
|
||||
void ClearMainMemory();
|
||||
void CreateRomClusterTable();
|
||||
bool TryLoadRomHeader(u32 romOffset);
|
||||
void HandleCardSave();
|
||||
void HandleAntiPiracy();
|
||||
void RemapWram();
|
||||
bool TryLoadArm9();
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
#include "patches/arm9/OSResetSystemPatch.h"
|
||||
#include "patches/arm9/PokemonDownloaderArm9Patch.h"
|
||||
#include "patches/arm9/DSProtectArm9Patch.h"
|
||||
#include "patches/arm9/NandSave/FaceTrainingNandSavePatch.h"
|
||||
#include "patches/arm9/NandSave/JamWithTheBandNandSavePatch.h"
|
||||
#include "patches/arm9/NandSave/NintendoDSGuideNandSavePatch.h"
|
||||
#include "patches/arm9/NandSave/WarioWareDiyNandSavePatch.h"
|
||||
#include "patches/arm9/OverlayPatches/FsStartOverlayHookPatch.h"
|
||||
#include "patches/arm9/OverlayPatches/DSProtectPatches/DSProtectOverlayPatch.h"
|
||||
@@ -390,6 +392,18 @@ void Arm9Patcher::AddGameSpecificPatches(
|
||||
patchCollection.AddPatch(new JamWithTheBandNandSavePatch());
|
||||
break;
|
||||
}
|
||||
// Face Training
|
||||
case GAMECODE("USKV"):
|
||||
{
|
||||
patchCollection.AddPatch(new FaceTrainingNandSavePatch());
|
||||
break;
|
||||
}
|
||||
// Nintendo DS Guide
|
||||
case GAMECODE("UGDA"):
|
||||
{
|
||||
patchCollection.AddPatch(new NintendoDSGuideNandSavePatch());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 "FaceTrainingNandSavePatch.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 FaceTrainingNandSavePatch::FindPatchTarget(PatchContext& patchContext)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void FaceTrainingNandSavePatch::ApplyPatch(PatchContext& patchContext)
|
||||
{
|
||||
auto sectorRemapPatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<SaveOffsetToSdSectorPatchCode>
|
||||
(
|
||||
patchContext.GetPatchHeap(),
|
||||
SHARED_SAVE_FILE_INFO
|
||||
);
|
||||
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*)0x020E2AEC = 0xE3A00001; // mov r0, #1
|
||||
*(u32*)0x020E2AF0 = 0xE12FFF1E; // bx lr
|
||||
|
||||
// u32 nandResume(void)
|
||||
*(u32*)0x020E2EC0 = 0xE3A00000; // mov r0, #0
|
||||
*(u32*)0x020E2EC4 = 0xE12FFF1E; // bx lr
|
||||
|
||||
// u32 nandError(void)
|
||||
*(u32*)0x020E3150 = 0xE3A00000; // mov r0, #0
|
||||
*(u32*)0x020E3154 = 0xE12FFF1E; // bx lr
|
||||
|
||||
// u32 nandWrite(const void* memory, u32 flash, u32 size, u32 dmaChannel)
|
||||
*(u32*)0x020E2BF0 = 0xE51FF004; // ldr pc,= patch_writeNandSave
|
||||
*(u32*)0x020E2BF4 = (u32)writeNandSavePatchCode->GetWriteNandSaveFunction();
|
||||
|
||||
// u32 nandRead(void* memory, u32 flash, u32 size, u32 dmaChannel)
|
||||
*(u32*)0x020E2F3C = 0xE51FF004; // ldr pc,= patch_readNandSave
|
||||
*(u32*)0x020E2F40 = (u32)readNandSavePatchCode->GetReadNandSaveFunction();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "patches/Patch.h"
|
||||
|
||||
class FunctionSignature;
|
||||
|
||||
/// @brief Arm9 patch to redirect Face Training nand saving to the SD card.
|
||||
class FaceTrainingNandSavePatch : public Patch
|
||||
{
|
||||
public:
|
||||
bool FindPatchTarget(PatchContext& patchContext) override;
|
||||
void ApplyPatch(PatchContext& patchContext) override;
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "common.h"
|
||||
#include "gameCode.h"
|
||||
#include "patches/platform/LoaderPlatform.h"
|
||||
#include "patches/SaveOffsetToSdSectorAsm.h"
|
||||
#include "ReadNandSaveAsm.h"
|
||||
#include "WriteNandSaveAsm.h"
|
||||
#include "NintendoDSGuideNandSavePatch.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 NintendoDSGuideNandSavePatch::FindPatchTarget(PatchContext& patchContext)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void NintendoDSGuideNandSavePatch::ApplyPatch(PatchContext& patchContext)
|
||||
{
|
||||
auto sectorRemapPatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<SaveOffsetToSdSectorPatchCode>
|
||||
(
|
||||
patchContext.GetPatchHeap(),
|
||||
SHARED_SAVE_FILE_INFO
|
||||
);
|
||||
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*)0x02009298 = 0xE3A00001; // mov r0, #1
|
||||
*(u32*)0x0200929C = 0xE12FFF1E; // bx lr
|
||||
|
||||
// u32 nandResume(void)
|
||||
*(u32*)0x020098C8 = 0xE3A00000; // mov r0, #0
|
||||
*(u32*)0x020098CC = 0xE12FFF1E; // bx lr
|
||||
|
||||
// u32 nandState(void)
|
||||
*(u32*)0x02009AA8 = 0xE1A00000; //nop
|
||||
*(u32*)0x02009AB0 = 0x06600000;
|
||||
|
||||
// u32 nandError(void)
|
||||
*(u32*)0x02009AB4 = 0xE3A00000; // mov r0, #0
|
||||
*(u32*)0x02009AB8 = 0xE12FFF1E; // bx lr
|
||||
|
||||
// u32 nandWrite(const void* memory, u32 flash, u32 size, u32 dmaChannel)
|
||||
*(u32*)0x0200961C = 0xE51FF004; // ldr pc,= patch_writeNandSave
|
||||
*(u32*)0x02009620 = (u32)writeNandSavePatchCode->GetWriteNandSaveFunction();
|
||||
|
||||
// u32 nandRead(void* memory, u32 flash, u32 size, u32 dmaChannel)
|
||||
*(u32*)0x02009940 = 0xE51FF004; // ldr pc,= patch_readNandSave
|
||||
*(u32*)0x02009944 = (u32)readNandSavePatchCode->GetReadNandSaveFunction();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "patches/Patch.h"
|
||||
|
||||
class FunctionSignature;
|
||||
|
||||
/// @brief Arm9 patch to redirect Nintendo DS Guide nand saving to the SD card.
|
||||
class NintendoDSGuideNandSavePatch : public Patch
|
||||
{
|
||||
public:
|
||||
bool FindPatchTarget(PatchContext& patchContext) override;
|
||||
void ApplyPatch(PatchContext& patchContext) override;
|
||||
};
|
||||
@@ -5455,13 +5455,7 @@ UBRE;flash;0x40000
|
||||
UBRJ;flash;0x40000
|
||||
UBRP;flash;0x40000
|
||||
UEIJ;eeprom;0x2000
|
||||
UGDA;none;0x0
|
||||
UNSJ;flash;0x40000
|
||||
UORE;nand;0x1000000
|
||||
UORJ;nand;0x1000000
|
||||
UORP;nand;0x1000000
|
||||
USKV;nand;0x4000000
|
||||
UXBP;nand;0x800000
|
||||
UZCJ;flash;0x2000000
|
||||
UZPD;flash;0x20000
|
||||
UZPF;flash;0x20000
|
||||
|
||||
|
Reference in New Issue
Block a user