30 Commits

Author SHA1 Message Date
Mow
fd08fd19a1 Handle BLX branches in CardiTryReadCardDmaPatch (#91) 2026-01-09 15:39:23 +01:00
TY
ef50e60ff9 Set MBK9 settings from rom header after Arm7 Patching finish (#88) 2026-01-06 07:36:35 +01:00
TY
a424b25263 Impl HandleiQueRegionFreePatching to bypass region locking on certain iQue games (#83) 2026-01-04 16:13:24 +01:00
Alex Abbatiello
f1e5f0cddc Fix typo in detectNocashPrintSupport and add support for melonDS (#85) 2026-01-02 22:01:43 +01:00
Gericom
26cec4c421 A few more renames 2026-01-02 16:17:31 +01:00
Gericom
e0e33032b9 Updated some comments 2026-01-02 16:13:56 +01:00
Gericom
9be5424f23 Changed PatchCode::GetAddressAtTarget from public to protected 2026-01-02 15:36:33 +01:00
Gericom
3e4725c99c Renamed methods in IReadSectorsDmaPatchCode 2026-01-02 15:32:48 +01:00
Gericom
dcc71ca151 Fixed casing in some includes 2026-01-02 15:27:18 +01:00
Gericom
2d4f9cb29f Fix pipeline to run on all branches 2026-01-02 15:22:14 +01:00
Gericom
231f14a783 Refactored platform code to use interfaces for patch code with a special feature and improved patch code file names 2026-01-02 15:18:45 +01:00
Mow
d6080984d1 Add overlay patch for hombrew AP in Rabbids Go Homeh (#82) 2025-12-31 07:51:40 +01:00
Mow
6fb34c75f8 Add overlay patch to fix race condition in Kirby Super Star Ultra (#81) 2025-12-30 22:23:04 +01:00
Edoardo Lolletti
992e2d1053 DATEL: Make read sector function vram safe (#80)
* DATEL: Make read sector function vram safe

The platform code was performing reads in single byte units, breaking in case the destination buffer was in vram.
This caused issues with some games (e.g. Castelvania Order of Ecclesia), and also broke soft resetting.

* Halve wait timeout on sector read since we read 2 bytes at the time
2025-12-30 20:10:02 +01:00
Max
842dd8e63a Update BlocksDS installation link in README (#79)
The /options/ path does not exist anymore
2025-12-30 08:01:19 +01:00
Alex Abbatiello
92a6c6ecdc Save list cleanup (#78) 2025-12-29 12:37:48 +00:00
Edoardo Lolletti
c380041241 DATEL: Put back the full spi cycle routine when sending commands (#76)
Perform again the full cycle spi routine that was removed as optimization since some SDs appeared that require that to work properly
2025-12-29 09:46:00 +00:00
lifehackerhansol
06c1f22cd1 platform: add support for the Stargate 3DS (#71) 2025-12-28 20:22:56 +00:00
Edoardo Lolletti
26f27a4138 Add support for DATEL devices (GAMES n' MUSIC and Action Replay DS(i) Media Edition) (#64) 2025-12-28 19:25:22 +00:00
Edoardo Lolletti
7134c4b330 Optimize space usage of supercard platform (#74) 2025-12-28 16:38:28 +00:00
Gericom
9f6311014d 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 2025-12-28 17:34:06 +01:00
Gericom
6d12399ba4 Added support for NAND saving in WarioWare D.I.Y. and Jam with the Band (#6)
Also improved speed of creating save files
2025-12-28 13:19:23 +01:00
Gericom
64e020182a Added check to CardiTaskThreadPatch that the required slot is actually mapped to arm7. Fixes #60 2025-12-27 12:44:00 +01:00
Gericom
8036004e5a Merge remote-tracking branch 'origin/develop' into feature/refactor-cardi-task-thread-patch 2025-12-26 20:59:06 +01:00
Gericom
31d6c63e3b Removed redundant forward definition 2025-12-26 13:12:49 +01:00
Gericom
9fea5f7a51 More refactoring for CardiTaskThreadPatch 2025-12-26 13:11:49 +01:00
Gericom
c8898ff13c Attempt to improve handling of thumb signatures in CardiTaskThreadPatch 2025-12-26 12:39:10 +01:00
Mow
497fdca384 Add missing signature for Tetris DS (#66) 2025-12-25 23:01:44 +01:00
Gericom
e58a55b81c Attempt to improve handling of arm signatures in CardiTaskThreadPatch 2025-12-25 13:26:09 +01:00
TY
19cce5960b Properly set the supported language, user language, and region based on the ROM's region (#38) 2025-12-23 14:08:55 +00:00
217 changed files with 3536 additions and 1311 deletions

View File

@@ -2,7 +2,6 @@ name: Build Pico Loader
on:
push:
branches: ["develop"]
paths-ignore:
- 'README.md'
pull_request:
@@ -19,6 +18,7 @@ jobs:
"ACE3DS",
"AK2",
"AKRPG",
"DATEL",
"DSPICO",
"DSTT",
"EZP",
@@ -26,6 +26,7 @@ jobs:
"M3DS",
"R4",
"R4iDSN",
"STARGATE",
"SUPERCARD"
]
runs-on: ubuntu-latest

View File

@@ -12,6 +12,7 @@ jobs:
"ACE3DS",
"AK2",
"AKRPG",
"DATEL",
"DSPICO",
"DSTT",
"EZP",
@@ -19,6 +20,7 @@ jobs:
"M3DS",
"R4",
"R4iDSN",
"STARGATE",
"SUPERCARD"
]
runs-on: ubuntu-latest

View File

@@ -25,6 +25,7 @@ Note that there can be some game compatibility differences between different pla
| ACE3DS | Ace3DS+, Gateway 3DS (blue), r4isdhc.com.cn carts, r4isdhc.hk carts 2020+, various derivatives | ✅ |
| AK2 | Acekard 2, 2.1, 2i, r4ids.cn, various derivatives | ❌ |
| AKRPG | Acekard RPG SD card | ❌ |
| DATEL | DATEL devices consisting of GAMES n' MUSIC and Action Replay DS(i) Media Edition | ❌ |
| DSPICO | DSpico | ✅ |
| DSTT | DSTT, SuperCard DSONE SDHC, r4isdhc.com carts 2014+, r4i-sdhc.com carts, various derivatives | ❌ |
| EZP | EZ-Flash Parallel | ❌ |
@@ -34,6 +35,7 @@ Note that there can be some game compatibility differences between different pla
| MELONDS | Melon DS support for testing purposes only. | ❌ |
| R4 | Original R4DS (non-SDHC), M3 DS Simply | ❌ |
| R4iDSN | r4idsn.com | ❌ |
| STARGATE | Stargate 3DS DS-mode | ✅ |
| SUPERCARD | SuperCard (Slot-2 flashcart) | ❌ |
The DMA column indicates whether DMA card reads are implemented for the platform . Without DMA card reads, some games can have cache related issues.<br>
@@ -43,7 +45,7 @@ Note that there are still SDK versions and variants for which Pico Loader does n
We recommend using WSL (Windows Subsystem for Linux), or MSYS2 to compile this repository.
The steps provided will assume you already have one of those environments set up.
1. Install [BlocksDS](https://blocksds.skylyrac.net/docs/setup/options/)
1. Install [BlocksDS](https://blocksds.skylyrac.net/docs/setup/)
2. Install [.NET 9.0](https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu-install?tabs=dotnet9&pivots=os-linux-ubuntu-2404) for your system (note: this link points to the instructions for Ubuntu, but links for most OS'es are available on the same page)
## Compiling

View File

@@ -2,6 +2,9 @@
#include "picoAgbAdapter.h"
#include "Environment.h"
#define NOCASH_ID 0x67246F6E
#define NOCASH_ID_MELONDS 0x6F6C656D
u32 Environment::_flags;
static bool detectIsNitroEmulator()
@@ -15,10 +18,10 @@ static bool detectIsNitroEmulator()
return true;
}
static bool detectNocashPrintSuppport()
static bool detectNocashPrintSupport()
{
u32 nocashIdentifier = *(vu32*)0x04FFFA00;
return nocashIdentifier == 0x67246F6E; //no$g
return nocashIdentifier == NOCASH_ID || nocashIdentifier == NOCASH_ID_MELONDS;
}
static bool detectPicoAgbAdapter()
@@ -55,7 +58,7 @@ void Environment::Initialize(bool dsiMode)
}
if (!(_flags & ENVIRONMENT_FLAGS_IS_NITRO_EMULATOR))
{
if (detectNocashPrintSuppport())
if (detectNocashPrintSupport())
_flags |= ENVIRONMENT_FLAGS_NOCASH_PRINT;
}
}
}

View File

@@ -1,32 +1,54 @@
#include "common.h"
#include <algorithm>
#include <string.h>
#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
bool CardSaveArranger::SetupCardSave(u32 gameCode, const TCHAR* savePath) const
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(const nds_header_ntr_t* header, const TCHAR* savePath) const
{
SaveList* saveList = SaveListFactory().CreateFromFile(SAVE_LIST_PATH);
auto saveType = CardSaveType::None;
u32 saveSize = DEFAULT_SAVE_SIZE;
if (saveList)
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
{
saveSize = saveListEntry->GetSaveSize();
saveListEntry->Dump();
}
delete saveList;
}
if (saveSize == 0)
{
@@ -50,8 +72,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 +91,45 @@ 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;
}
// Additional save initialization
if (header->gameCode == GAMECODE("AXBJ"))
{
// 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(), sJamWithTheBandSaveId, sizeof(sJamWithTheBandSaveId), &bytesWritten) != FR_OK ||
bytesWritten != sizeof(sJamWithTheBandSaveId))
{
LOG_FATAL("Failed to write Jam with the Band save id\n");
return false;
}
}
}

View File

@@ -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;
};

View File

@@ -0,0 +1,12 @@
#pragma once
/// @brief Enum for DSi console region.
enum class ConsoleRegion
{
Japan,
America,
Europe,
Australia,
China,
Korea
};

View File

@@ -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;
}
}

View File

@@ -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();
@@ -216,6 +220,7 @@ void NdsLoader::Load(BootMode bootMode)
if (bootMode == BootMode::Normal)
{
bootType = _romHeader.IsDsiWare() ? BOOT_TYPE_NAND : BOOT_TYPE_CARD;
HandleIQueRegionFreePatching();
}
else if (bootMode == BootMode::Multiboot)
{
@@ -292,6 +297,9 @@ void NdsLoader::Load(BootMode bootMode)
{
SetupDsiDeviceList();
// Set twl wram locking (REG_MBK9) settings from rom header
REG_MBK9 = _romHeader.mbk9Setting[0] | (_romHeader.mbk9Setting[1] << 8) | (_romHeader.mbk9Setting[2] << 16);
u32 scfgExt7 = 0x93FBFB00 | (_romHeader.arm7ScfgExt7 & 0x40407);
REG_SCFG_EXT = scfgExt7;
REG_SCFG_CLK = 0x187;
@@ -608,14 +616,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);
@@ -865,6 +865,10 @@ void NdsLoader::StartRom(BootMode bootMode)
void NdsLoader::SetupTwlConfig()
{
ConsoleRegion romRegion = GetRomRegion(_romHeader.gameCode);
// Set language based on rom region, TODO: allow user to override this via some config or so
UserLanguage userLang = GetLanguageByRomRegion(romRegion);
twl_config_t* twlConfig = (twl_config_t*)0x02000400;
*(twl_config_t**)0x02FFFDFC = twlConfig;
*(vu8*)0x02FFFDFA = 0x80;
@@ -872,7 +876,7 @@ void NdsLoader::SetupTwlConfig()
memset(twlConfig, 0, sizeof(twl_config_t));
twlConfig->configFlags = 0x0100000F;
twlConfig->country = 0x4E;
twlConfig->language = TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x64];
twlConfig->language = static_cast<u8>(userLang);
twlConfig->rtcYear = TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x66];
twlConfig->rtcOffset = *(s64*)&TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x68];
twlConfig->eulaAgreeVersion[0] = 1;
@@ -880,7 +884,7 @@ void NdsLoader::SetupTwlConfig()
twlConfig->alarmMinute = TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x53];
twlConfig->alarmEnable = TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x56];
twlConfig->systemMenuUsedTitleSlots = 9;
twlConfig->systemMenuUsedTitleSlots = 30;
twlConfig->systemMenuFreeTitleSlots = 30;
twlConfig->field24 = 3;
memcpy(&twlConfig->touchCalibrationX1Adc, &TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x58], 0xC);
twlConfig->field3C = 0x0201209C;
@@ -906,9 +910,10 @@ void NdsLoader::SetupTwlConfig()
}
*(vu16*)0x020005E2 = swi_getCrc16(0xFFFF, (void*)0x020005E4, 0xC);
*(vu32*)0x02FFFD68 = 0x3E; // supported languages
// Set bitmask for supported languages
*(vu32*)0x02FFFD68 = GetSupportedLanguagesByRegion(romRegion);
*(vu32*)0x02FFFD6C = 0;
*(vu8*)0x02FFFD70 = 2; // region, 2 = europe
*(vu8*)0x02FFFD70 = static_cast<u8>(romRegion); // region
}
void NdsLoader::SetDeviceListEntry(dsi_devicelist_entry_t& deviceListEntry,
@@ -1011,3 +1016,111 @@ bool NdsLoader::TryDecryptSecureArea()
LOG_DEBUG("Decrypted secure area\n");
return true;
}
void NdsLoader::HandleIQueRegionFreePatching()
{
if ((_romHeader.flags & 0x80) == 0x80)
{
_romHeader.flags &= ~0x80;
_romHeader.headerCrc = swi_getCrc16(0xFFFF, (void*)&_romHeader, 0x15E);
}
}
ConsoleRegion NdsLoader::GetRomRegion(u32 gameCode)
{
u8 gameRegionCode = (gameCode >> 24) & 0xFF;
if (gameRegionCode != 'A' && gameRegionCode != 'O')
{
// Determine region by TID
if (gameRegionCode == 'J')
{
return ConsoleRegion::Japan;
}
else if (gameRegionCode == 'E' || gameRegionCode == 'T')
{
return ConsoleRegion::America;
}
else if (gameRegionCode == 'P' || gameRegionCode == 'V')
{
return ConsoleRegion::Europe;
}
else if (gameRegionCode == 'U')
{
return ConsoleRegion::Australia;
}
else if (gameRegionCode == 'C')
{
return ConsoleRegion::China;
}
else if (gameRegionCode== 'K')
{
return ConsoleRegion::Korea;
}
}
return ConsoleRegion::Europe; // Default to EUR
}
UserLanguage NdsLoader::GetLanguageByRomRegion(ConsoleRegion romRegion)
{
UserLanguage userLang = static_cast<UserLanguage>(TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x64] & 0x07);
if (romRegion == ConsoleRegion::Japan)
{
return UserLanguage::Japanese;
}
else if (romRegion == ConsoleRegion::America &&
(userLang != UserLanguage::English ||
userLang != UserLanguage::French ||
userLang != UserLanguage::Spanish))
{
return UserLanguage::English;
}
else if (romRegion == ConsoleRegion::Europe && userLang < UserLanguage::English && userLang > UserLanguage::Spanish)
{
return UserLanguage::English;
}
else if (romRegion == ConsoleRegion::China)
{
return UserLanguage::Chinese;
}
else if (romRegion == ConsoleRegion::Korea)
{
return UserLanguage::Korean;
}
return userLang;
}
u32 NdsLoader::GetSupportedLanguagesByRegion(ConsoleRegion region)
{
switch (region)
{
case ConsoleRegion::Japan:
{
return 0x01;
}
case ConsoleRegion::America:
{
return 0x26;
}
case ConsoleRegion::Europe:
{
return 0x3E;
}
case ConsoleRegion::Australia:
{
return 0x02;
}
case ConsoleRegion::China:
{
return 0x40;
}
case ConsoleRegion::Korea:
{
return 0x80;
}
}
return 0x3E;
}

View File

@@ -3,6 +3,8 @@
#include "ndsHeader.h"
#include "DsiWareSaveArranger.h"
#include "BootMode.h"
#include "ConsoleRegion.h"
#include "UserLanguage.h"
struct dsi_devicelist_entry_t;
@@ -54,7 +56,6 @@ private:
void ClearMainMemory();
void CreateRomClusterTable();
bool TryLoadRomHeader(u32 romOffset);
void HandleCardSave();
void HandleAntiPiracy();
void RemapWram();
bool TryLoadArm9();
@@ -72,4 +73,8 @@ private:
void InsertArgv();
bool TrySetupDsiWareSave();
bool TryDecryptSecureArea();
void HandleIQueRegionFreePatching();
ConsoleRegion GetRomRegion(u32 gameCode);
UserLanguage GetLanguageByRomRegion(ConsoleRegion romRegion);
u32 GetSupportedLanguagesByRegion(ConsoleRegion region);
};

View File

@@ -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

View File

@@ -0,0 +1,14 @@
#pragma once
/// @brief Enum for DSi user language.
enum class UserLanguage
{
Japanese,
English,
French,
German,
Italian,
Spanish,
Chinese,
Korean
};

View File

@@ -13,10 +13,16 @@
#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"
#include "patches/arm9/OverlayPatches/DSProtectPatches/DSProtectPuyoPuyo7Patch.h"
#include "patches/arm9/OverlayPatches/PokemonIr/PokemonIrApPatch.h"
#include "patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatch.h"
#include "patches/arm9/OverlayPatches/RabbidsGoHome/RabbidsGoHomePatch.h"
#include "patches/arm9/OverlayPatches/GoldenSunDarkDawn/GoldenSunDarkDawnOverlayHookPatch.h"
#include "SecureSysCallsUnusedSpaceLocator.h"
#include "fastSearch.h"
@@ -374,6 +380,48 @@ 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;
}
// Face Training
case GAMECODE("USKV"):
{
patchCollection.AddPatch(new FaceTrainingNandSavePatch());
break;
}
// Nintendo DS Guide
case GAMECODE("UGDA"):
{
patchCollection.AddPatch(new NintendoDSGuideNandSavePatch());
break;
}
// Rabbids Go Home
case GAMECODE("VRGE"):
case GAMECODE("VRGV"):
{
overlayHookPatch->AddOverlayPatch(new RabbidsGoHomePatch());
break;
}
// Kirby Super Star Ultra
case GAMECODE("YKWE"):
case GAMECODE("YKWJ"):
case GAMECODE("YKWK"):
case GAMECODE("YKWP"):
{
overlayHookPatch->AddOverlayPatch(new KirbySuperStarUltraPatch());
break;
}
}
}

View File

@@ -192,8 +192,8 @@ static void handleGetSdFunctionsCommand()
{
auto sdReadPatchCode = sLoaderPlatform->CreateSdReadPatchCode(patchCodeCollection, patchHeap);
auto sdWritePatchCode = sLoaderPlatform->CreateSdWritePatchCode(patchCodeCollection, patchHeap);
*(vu32*)0x037F8000 = (u32)sdReadPatchCode->GetSdReadFunction();
*(vu32*)0x037F8004 = (u32)sdWritePatchCode->GetSdWriteFunction();
*(vu32*)0x037F8000 = (u32)sdReadPatchCode->GetReadSectorsFunction();
*(vu32*)0x037F8004 = (u32)sdWritePatchCode->GetWriteSectorFunction();
patchCodeCollection.CopyAllToTarget();
}
dc_flushAll();

View File

@@ -0,0 +1,13 @@
#pragma once
/// @brief Interface for sector remapping patch code.
class ISectorRemapPatchCode
{
protected:
ISectorRemapPatchCode() { }
public:
/// @brief Gets a pointer to the sector remap function in the patch code.
/// @return The pointer to the sector remap function.
virtual const void* GetRemapFunction() const = 0;
};

View File

@@ -1,17 +1,17 @@
#pragma once
#include "sections.h"
#include "SectorRemapPatchCode.h"
#include "PatchCode.h"
#include "ISectorRemapPatchCode.h"
DEFINE_SECTION_SYMBOLS(offsettosectorremap);
extern "C" u32 offset_to_sector_remap(u32 romOffset);
class OffsetToSectorRemapPatchCode : public SectorRemapPatchCode
class OffsetToSectorRemapPatchCode : public PatchCode, public ISectorRemapPatchCode
{
public:
explicit OffsetToSectorRemapPatchCode(PatchHeap& patchHeap)
: SectorRemapPatchCode(SECTION_START(offsettosectorremap), SECTION_SIZE(offsettosectorremap), patchHeap)
{ }
: PatchCode(SECTION_START(offsettosectorremap), SECTION_SIZE(offsettosectorremap), patchHeap) { }
const void* GetRemapFunction() const override
{

View File

@@ -20,15 +20,6 @@ public:
while (1);
}
/// @brief Converts a pointer inside the original code block
/// to a pointer at the target location in the patch heap.
/// @param ptr The pointer to convert.
/// @return The converted pointer.
const void* GetAddressAtTarget(const void* ptr) const
{
return (const void*)((u32)ptr - (u32)_code + (u32)_targetAddress);
}
/// @brief Copies the patch code to the target address.
void CopyToTarget() const
{
@@ -44,4 +35,13 @@ protected:
/// @brief The target address for the code block in the patch heap.
void* const _targetAddress;
/// @brief Converts a pointer inside the original code block
/// to a pointer at the target location in the patch heap.
/// @param ptr The pointer to convert.
/// @return The converted pointer.
const void* GetAddressAtTarget(const void* ptr) const
{
return (const void*)((u32)ptr - (u32)_code + (u32)_targetAddress);
}
};

View File

@@ -1,6 +1,7 @@
#pragma once
#include "sections.h"
#include "../SectorRemapPatchCode.h"
#include "PatchCode.h"
#include "ISectorRemapPatchCode.h"
#include "fileInfo.h"
DEFINE_SECTION_SYMBOLS(saveoffsettosdsector);
@@ -9,11 +10,11 @@ extern "C" u32 save_offset_to_sd_sector_asm(u32 saveOffset);
extern u32 saveoffsettosdsector_fatDataPtr;
class SaveOffsetToSdSectorPatchCode : public SectorRemapPatchCode
class SaveOffsetToSdSectorPatchCode : public PatchCode, public ISectorRemapPatchCode
{
public:
SaveOffsetToSdSectorPatchCode(PatchHeap& patchHeap, const save_file_info_t* fatDataPtr)
: SectorRemapPatchCode(SECTION_START(saveoffsettosdsector), SECTION_SIZE(saveoffsettosdsector), patchHeap)
: PatchCode(SECTION_START(saveoffsettosdsector), SECTION_SIZE(saveoffsettosdsector), patchHeap)
{
saveoffsettosdsector_fatDataPtr = (u32)fatDataPtr;
}

Some files were not shown because too many files have changed in this diff Show More