57 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
Gericom
151b68018d Fixed some retail games being detected as homebrew. Fixes #61, fixes #56 2025-12-22 10:57:17 +01:00
Gericom
4fd7fde362 Map slot 1 and 2 to arm7 on boot 2025-12-20 18:36:20 +01:00
Gericom
61463ca39a Added proper implementation of save verify 2025-12-20 15:46:41 +01:00
Gericom
c139eacaee Merge pull request #34 from taxicat1/dsprotect-arm9-static
Implement DSProtect patching in ARM9 static
2025-12-14 10:13:05 +01:00
taxicat1
e8b2f51497 Add some missing Rev 1 games to aplist 2025-12-10 21:52:18 -05:00
taxicat1
48d49f5df8 Fix whitespace 2025-12-10 16:02:00 -05:00
Mow
00f6407cf4 Merge branch 'LNH-team:develop' into dsprotect-arm9-static 2025-12-10 15:58:32 -05:00
taxicat1
e36e669daf Initial DSProtect for ARM9 static 2025-12-10 15:58:11 -05:00
Gericom
b0d0336e1e Merge pull request #29 from taxicat1/game-specific-dsprotect
Handle games with unusual DS Protect implementations
2025-12-10 19:21:03 +01:00
taxicat1
4416c61158 Initial game-specific DS Protect 2025-12-09 14:25:58 -05:00
Gericom
dd062bb4fa Refactored some code in Arm9Patcher 2025-12-07 11:52:53 +01:00
Gericom
656f696411 Improved handling of DSi roms and DSiWare. Fixes some system tools DSi roms being misdetected as DSiWare (related to #23) 2025-12-06 19:56:48 +01:00
Gericom
0d0fa445c9 Merge pull request #27 from taxicat1/generalize-pokemon-ir-patch
Generalize Pokemon IR sensor AP patching
2025-12-06 16:54:52 +01:00
taxicat1
e5f7283977 Add IRDJ and IREJ to switch 2025-12-06 09:53:37 -05:00
Mow
8550b36dc6 Get rid of duplicate directory 2025-12-06 09:16:48 -05:00
taxicat1
79018dee0d Fix folder name to proper case 2025-12-06 09:10:10 -05:00
taxicat1
005b8cab03 Initial generalize Pokemon IR patching 2025-12-06 08:51:58 -05:00
Gericom
b0c8dfa5bc Improved Sdk5DsiSdCardRedirectPatch to take autoload into account and to support the situation where a function is missing
Fixes WRFU Tester (#23)
2025-12-06 11:53:32 +01:00
Gericom
d12036e6a7 Added extra check to see if Sdk5DsiSdCardRedirectPatch can be applied
This ensures an error is given in roms such as wrfu
2025-12-05 19:19:58 +01:00
Gericom
e9a8c09a35 Merge pull request #22 from lifehackerhansol/develop
workflow: add release pipeline
2025-12-04 13:29:20 +01:00
lifehackerhansol
8e98796be2 workflow: add release pipeline
- Minor change in push pipeline to remove spaces from artifact name
- Add artifact move step to add all files to a single folder before
  pushing to artifacts
- Create release pipeline with the same steps that will upload a zipped
  package to every published release automatically
2025-12-03 21:00:11 -08:00
Mow
a8f5d880b2 Improved DSProtect patches 2025-12-03 16:22:06 +01:00
Gericom
7aba420201 Merge pull request #15 from lifehackerhansol/ez5n
platform: add support for the EZ-Flash Parallel
2025-12-02 19:57:14 +01:00
Gericom
8761081c73 Added more game codes to DLDI blacklist. Fixes Final Fantasy Crystal Chronicles - Ring of Fates (EU and US) and Nanashi no Geemu. Fixes #17 2025-12-02 19:14:59 +01:00
lifehackerhansol
0dd41f4220 platform: add support for the EZ-Flash Parallel 2025-12-02 08:40:36 -08:00
Gericom
485598ab79 Removed unused entrypoint_t typedef 2025-11-30 13:03:13 +01:00
Gericom
b2fabe5d97 Clear all arm9 cpu registers before jumping to the arm9 entry point. Fixes Cake Ninja DSiWare titles. 2025-11-30 13:00:35 +01:00
257 changed files with 5232 additions and 2353 deletions

View File

@@ -2,7 +2,6 @@ name: Build Pico Loader
on:
push:
branches: ["develop"]
paths-ignore:
- 'README.md'
pull_request:
@@ -19,12 +18,15 @@ jobs:
"ACE3DS",
"AK2",
"AKRPG",
"DATEL",
"DSPICO",
"DSTT",
"EZP",
"G003",
"M3DS",
"R4",
"R4iDSN",
"STARGATE",
"SUPERCARD"
]
runs-on: ubuntu-latest
@@ -46,14 +48,15 @@ jobs:
- name: Run build script
run: |
make PICO_PLATFORM=${{ matrix.platform }}
mv picoLoader7.bin data/picoLoader7.bin
mv picoLoader9_${{ matrix.platform }}.bin data/picoLoader9.bin
- name: Package artifact
run: |
mkdir Pico_Loader_${{ matrix.platform }}
mv picoLoader7.bin data/aplist.bin data/savelist.bin Pico_Loader_${{ matrix.platform }}
mv picoLoader9_${{ matrix.platform }}.bin Pico_Loader_${{ matrix.platform }}/picoLoader9.bin
- name: Publish build to GH Actions
uses: actions/upload-artifact@v4
with:
path: |
data/aplist.bin
data/savelist.bin
data/picoLoader7.bin
data/picoLoader9.bin
name: Pico Loader for ${{ matrix.platform }}
Pico_Loader_${{ matrix.platform }}
# For some reason without explicitly setting a name there is some odd conflicts
name: Pico_Loader_${{ matrix.platform }}

67
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: Build Pico Loader release
on:
release:
types: [published]
jobs:
pico_loader:
strategy:
matrix:
platform: [
"ACE3DS",
"AK2",
"AKRPG",
"DATEL",
"DSPICO",
"DSTT",
"EZP",
"G003",
"M3DS",
"R4",
"R4iDSN",
"STARGATE",
"SUPERCARD"
]
runs-on: ubuntu-latest
container: skylyrac/blocksds:slim-v1.13.1
name: Build Pico Loader
env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1
DOTNET_NOLOGO: true
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
submodules: true
- uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.x
- name: Install zip
run:
apt-get update && apt-get -y install zip
- name: Run build script
run: |
make PICO_PLATFORM=${{ matrix.platform }}
- name: Package artifact
run: |
mkdir Pico_Loader_${{ matrix.platform }}
mv picoLoader7.bin data/aplist.bin data/savelist.bin Pico_Loader_${{ matrix.platform }}
mv picoLoader9_${{ matrix.platform }}.bin Pico_Loader_${{ matrix.platform }}/picoLoader9.bin
- name: Publish build to GH Actions
uses: actions/upload-artifact@v4
with:
path: |
Pico_Loader_${{ matrix.platform }}
# For some reason without explicitly setting a name there is some odd conflicts
name: Pico_Loader_${{ matrix.platform }}
- name: Package for release
run: |
cd Pico_Loader_${{ matrix.platform }} && zip -r $PWD.zip *
- name: Release
uses: softprops/action-gh-release@v2
with:
files: |
Pico_Loader_${{ matrix.platform }}.zip

View File

@@ -25,14 +25,17 @@ 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 | ❌ |
| G003 | M3i Zero (GMP-Z003) | âś… |
| ISNITRO | Supports the IS-NITRO-EMULATOR through agb semihosting. | ❌ |
| M3DS | M3 DS Real, M3i Zero, iTouchDS, r4rts.com, r4isdhc.com RTS (black) | ❌ |
| 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>
@@ -42,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

@@ -165,9 +165,9 @@ void NdsLoader::Load(BootMode bootMode)
}
}
bool isHomebrew = (_romHeader.ntrHeader.makerCode[0] == 0 && _romHeader.ntrHeader.makerCode[1] == 0)
|| _romHeader.ntrHeader.arm9AutoLoadDoneHookAddress == 0
|| _romHeader.ntrHeader.arm7LoadAddress >= 0x03000000;
bool isHomebrew = (_romHeader.makerCode[0] == 0 && _romHeader.makerCode[1] == 0)
|| (_romHeader.arm9AutoLoadDoneHookAddress == 0 && _romHeader.arm7AutoLoadDoneHookAddress == 0)
|| _romHeader.arm7LoadAddress >= 0x03000000;
if (isHomebrew)
{
@@ -186,16 +186,7 @@ void NdsLoader::Load(BootMode bootMode)
memset(&_dsiwareSaveResult, 0, sizeof(_dsiwareSaveResult));
if (bootMode != BootMode::Multiboot)
{
if (!_romHeader.IsDsiWare())
{
if (bootMode != BootMode::SdkResetSystem)
{
HandleCardSave();
}
HandleAntiPiracy();
}
else
if (_romHeader.IsDsiWare())
{
if (bootMode == BootMode::SdkResetSystem)
{
@@ -210,12 +201,26 @@ void NdsLoader::Load(BootMode bootMode)
return;
}
}
else
{
if (bootMode != BootMode::SdkResetSystem)
{
if (!CardSaveArranger().SetupCardSave(&_romHeader, _savePath))
{
ErrorDisplay().PrintError("Failed to setup save file.");
return;
}
}
HandleAntiPiracy();
}
}
}
if (bootMode == BootMode::Normal)
{
bootType = _romHeader.IsDsiWare() ? BOOT_TYPE_NAND : BOOT_TYPE_CARD;
HandleIQueRegionFreePatching();
}
else if (bootMode == BootMode::Multiboot)
{
@@ -224,14 +229,14 @@ void NdsLoader::Load(BootMode bootMode)
SetupSharedMemory(cardId, agbMem, resetParam, romOffset, bootType);
if (Environment::IsDsiMode() && _romHeader.IsTwlRom())
{
SetupTwlConfig();
TwlAes().SetupAes(&_romHeader);
}
if (Environment::IsDsiMode())
{
if (_romHeader.IsTwlRom())
{
SetupTwlConfig();
TwlAes().SetupAes(&_romHeader);
}
RemapWram();
}
@@ -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;
@@ -321,20 +329,20 @@ void NdsLoader::Load(BootMode bootMode)
if (Environment::IsDsiMode())
{
if (!_romHeader.IsTwlRom())
{
DSMode().SwitchToDSMode(_romHeader.ntrHeader.gameCode);
}
else
if (_romHeader.IsTwlRom())
{
if (!(_romHeader.twlFlags2 & 1))
{
DSMode().SwitchToDSTouchAndSoundMode(_romHeader.ntrHeader.gameCode);
DSMode().SwitchToDSTouchAndSoundMode(_romHeader.gameCode);
}
// Set back power button handling to irq mode
mcu_writeReg(MCU_REG_MODE, 1);
}
else
{
DSMode().SwitchToDSMode(_romHeader.gameCode);
}
}
if (isHomebrew)
@@ -349,11 +357,11 @@ void NdsLoader::Load(BootMode bootMode)
bool NdsLoader::IsCloneBootRom(u32 romOffset)
{
bool isCloneBootRom = false;
if (_romHeader.ntrHeader.ntrRomSize != 0)
if (_romHeader.ntrRomSize != 0)
{
UINT bytesRead = 0;
u16 signatureHeader;
if (f_lseek(&_romFile, romOffset + _romHeader.ntrHeader.ntrRomSize) == FR_OK &&
if (f_lseek(&_romFile, romOffset + _romHeader.ntrRomSize) == FR_OK &&
f_read(&_romFile, &signatureHeader, sizeof(signatureHeader), &bytesRead) == FR_OK &&
bytesRead == 2 &&
signatureHeader == 0x6361)
@@ -371,7 +379,7 @@ void NdsLoader::InsertArgv()
u32 argSize = 0;
// Find the address to write argv
u32 argDst = ((_romHeader.ntrHeader.arm9LoadAddress + _romHeader.ntrHeader.arm9Size + 3) & ~3) + 4;
u32 argDst = ((_romHeader.arm9LoadAddress + _romHeader.arm9Size + 3) & ~3) + 4;
if (_romHeader.IsTwlRom())
{
u32 argDstTwl = ((_romHeader.arm9iLoadAddress + _romHeader.arm9iSize + 3) & ~3) + 4;
@@ -475,8 +483,8 @@ void NdsLoader::SetupSharedMemory(u32 cardId, u32 agbMem, u32 resetParam, u32 ro
memcpy(TWL_SHARED_MEMORY->ntrSharedMem.romHeader, &_romHeader, sizeof(nds_header_ntr_t));
*(vu32*)&TWL_SHARED_MEMORY->ntrSharedMem.bootCheckInfo[0] = cardId;
*(vu32*)&TWL_SHARED_MEMORY->ntrSharedMem.bootCheckInfo[4] = cardId;
*(vu16*)&TWL_SHARED_MEMORY->ntrSharedMem.bootCheckInfo[8] = _romHeader.ntrHeader.headerCrc;
*(vu16*)&TWL_SHARED_MEMORY->ntrSharedMem.bootCheckInfo[0xA] = _romHeader.ntrHeader.secureAreaCrc;
*(vu16*)&TWL_SHARED_MEMORY->ntrSharedMem.bootCheckInfo[8] = _romHeader.headerCrc;
*(vu16*)&TWL_SHARED_MEMORY->ntrSharedMem.bootCheckInfo[0xA] = _romHeader.secureAreaCrc;
*(vu32*)&TWL_SHARED_MEMORY->ntrSharedMem.bootCheckInfo[0x10] = 0x5835; // use correct card id address
*(vu32*)&TWL_SHARED_MEMORY->ntrSharedMem.isDebuggerData[0x1C] = agbMem;
TWL_SHARED_MEMORY->ntrSharedMem.resetParam = resetParam;
@@ -534,13 +542,19 @@ void NdsLoader::LoadFirmwareUserSettings()
bool NdsLoader::ShouldAttemptDldiPatch()
{
switch (_romHeader.ntrHeader.gameCode)
// Some games contain a fake dldi header to trick flashcards
// See also: https://shutterbug2000.github.io/very-clever/
switch (_romHeader.gameCode)
{
// Final Fantasy IV contains a fake dldi header to trick flashcards
// See also: https://shutterbug2000.github.io/very-clever/
// Final Fantasy IV
case GAMECODE("YF4P"):
case GAMECODE("YF4E"):
case GAMECODE("YF4J"):
// Final Fantasy Crystal Chronicles - Ring of Fates
case GAMECODE("AFXE"):
case GAMECODE("AFXP"):
// Nanashi no Geemu
case GAMECODE("YFQJ"):
{
return false;
}
@@ -602,20 +616,12 @@ bool NdsLoader::TryLoadRomHeader(u32 romOffset)
return true;
}
void NdsLoader::HandleCardSave()
{
if (!CardSaveArranger().SetupCardSave(_romHeader.ntrHeader.gameCode, _savePath))
{
ErrorDisplay().PrintError("Failed to setup save file.");
}
}
void NdsLoader::HandleAntiPiracy()
{
auto apList = ApListFactory().CreateFromFile(AP_LIST_PATH);
if (apList)
{
auto entry = apList->FindEntry(_romHeader.ntrHeader.gameCode, _romHeader.ntrHeader.softwareVersion);
auto entry = apList->FindEntry(_romHeader.gameCode, _romHeader.softwareVersion);
if (entry)
{
entry->Dump();
@@ -677,15 +683,15 @@ void NdsLoader::RemapWram()
bool NdsLoader::TryLoadArm9()
{
if (f_lseek(&_romFile, _romHeader.ntrHeader.arm9RomOffset + TWL_SHARED_MEMORY->ntrSharedMem.romOffset) != FR_OK)
if (f_lseek(&_romFile, _romHeader.arm9RomOffset + TWL_SHARED_MEMORY->ntrSharedMem.romOffset) != FR_OK)
{
LOG_FATAL("Failed to seek to arm9\n");
return false;
}
UINT bytesRead = 0;
FRESULT result = f_read(&_romFile, (void*)_romHeader.ntrHeader.arm9LoadAddress, _romHeader.ntrHeader.arm9Size, &bytesRead);
if (result != FR_OK || bytesRead != _romHeader.ntrHeader.arm9Size)
FRESULT result = f_read(&_romFile, (void*)_romHeader.arm9LoadAddress, _romHeader.arm9Size, &bytesRead);
if (result != FR_OK || bytesRead != _romHeader.arm9Size)
{
LOG_FATAL("Failed to read arm9. Result: %d, bytesRead: %d\n", result, bytesRead);
return false;
@@ -730,7 +736,7 @@ bool NdsLoader::TryLoadArm9i()
bool NdsLoader::TryDecryptArm9i()
{
if (!(_romHeader.ntrHeader.twlFlags & (1 << 1)))
if (!(_romHeader.twlFlags & (1 << 1)))
{
return true;
}
@@ -750,7 +756,7 @@ bool NdsLoader::TryDecryptArm9i()
bool NdsLoader::TryDecryptArm7i()
{
if (!(_romHeader.ntrHeader.twlFlags & (1 << 1)) || _romHeader.modcryptArea2Size == 0)
if (!(_romHeader.twlFlags & (1 << 1)) || _romHeader.modcryptArea2Size == 0)
{
return true;
}
@@ -770,15 +776,15 @@ bool NdsLoader::TryDecryptArm7i()
bool NdsLoader::TryLoadArm7()
{
if (f_lseek(&_romFile, _romHeader.ntrHeader.arm7RomOffset + TWL_SHARED_MEMORY->ntrSharedMem.romOffset) != FR_OK)
if (f_lseek(&_romFile, _romHeader.arm7RomOffset + TWL_SHARED_MEMORY->ntrSharedMem.romOffset) != FR_OK)
{
LOG_FATAL("Failed to seek to arm7\n");
return false;
}
UINT bytesRead = 0;
FRESULT result = f_read(&_romFile, (void*)_romHeader.ntrHeader.arm7LoadAddress, _romHeader.ntrHeader.arm7Size, &bytesRead);
if (result != FR_OK || bytesRead != _romHeader.ntrHeader.arm7Size)
FRESULT result = f_read(&_romFile, (void*)_romHeader.arm7LoadAddress, _romHeader.arm7Size, &bytesRead);
if (result != FR_OK || bytesRead != _romHeader.arm7Size)
{
LOG_FATAL("Failed to read arm7. Result: %d, bytesRead: %d\n", result, bytesRead);
return false;
@@ -822,19 +828,19 @@ void NdsLoader::HandleDldiPatching()
}
int arm9DldiAddr = sDldiStubMatcher.FindFirstOccurance(
(const u32*)_romHeader.ntrHeader.arm9LoadAddress, _romHeader.ntrHeader.arm9Size >> 2) << 2;
(const u32*)_romHeader.arm9LoadAddress, _romHeader.arm9Size >> 2) << 2;
if (arm9DldiAddr >= 0)
{
dldi_header_t* arm9Dldi = (dldi_header_t*)(_romHeader.ntrHeader.arm9LoadAddress + arm9DldiAddr);
dldi_header_t* arm9Dldi = (dldi_header_t*)(_romHeader.arm9LoadAddress + arm9DldiAddr);
LOG_DEBUG("Dldi found in arm9 at address %p\n", arm9Dldi);
dldi_patchTo(arm9Dldi);
}
int arm7DldiAddr = sDldiStubMatcher.FindFirstOccurance(
(const u32*)_romHeader.ntrHeader.arm7LoadAddress, _romHeader.ntrHeader.arm7Size >> 2) << 2;
(const u32*)_romHeader.arm7LoadAddress, _romHeader.arm7Size >> 2) << 2;
if (arm7DldiAddr >= 0)
{
dldi_header_t* arm7Dldi = (dldi_header_t*)(_romHeader.ntrHeader.arm7LoadAddress + arm7DldiAddr);
dldi_header_t* arm7Dldi = (dldi_header_t*)(_romHeader.arm7LoadAddress + arm7DldiAddr);
LOG_DEBUG("Dldi found in arm7 at address %p\n", arm7Dldi);
dldi_patchTo(arm7Dldi);
}
@@ -854,11 +860,15 @@ void NdsLoader::StartRom(BootMode bootMode)
REG_IF2 = ~0u;
}
((entrypoint_t)_romHeader.ntrHeader.arm7EntryAddress)();
((entrypoint_t)_romHeader.arm7EntryAddress)();
}
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;
@@ -866,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;
@@ -874,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;
@@ -900,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,
@@ -964,20 +975,20 @@ bool NdsLoader::TrySetupDsiWareSave()
bool NdsLoader::TryDecryptSecureArea()
{
if (_romHeader.ntrHeader.arm9RomOffset < 0x4000 || _romHeader.ntrHeader.arm9RomOffset >= 0x8000)
if (_romHeader.arm9RomOffset < 0x4000 || _romHeader.arm9RomOffset >= 0x8000)
{
return true;
}
if (((u32*)_romHeader.ntrHeader.arm9LoadAddress)[0] == 0xE7FFDEFF &&
((u32*)_romHeader.ntrHeader.arm9LoadAddress)[1] == 0xE7FFDEFF)
if (((u32*)_romHeader.arm9LoadAddress)[0] == 0xE7FFDEFF &&
((u32*)_romHeader.arm9LoadAddress)[1] == 0xE7FFDEFF)
{
return true;
}
u16 secureAreaCrc = swi_getCrc16(0xFFFF,
(const void*)_romHeader.ntrHeader.arm9LoadAddress, 0x8000 - _romHeader.ntrHeader.arm9RomOffset);
if (secureAreaCrc != _romHeader.ntrHeader.secureAreaCrc)
(const void*)_romHeader.arm9LoadAddress, 0x8000 - _romHeader.arm9RomOffset);
if (secureAreaCrc != _romHeader.secureAreaCrc)
{
return true;
}
@@ -994,14 +1005,122 @@ bool NdsLoader::TryDecryptSecureArea()
}
auto blowfish = std::make_unique<Blowfish>(keyTable.get());
blowfish->TransformTable(_romHeader.ntrHeader.gameCode, 3, 8);
blowfish->TransformTable(_romHeader.gameCode, 3, 8);
blowfish->Decrypt(
(const void*)_romHeader.ntrHeader.arm9LoadAddress,
(void*)_romHeader.ntrHeader.arm9LoadAddress,
0x4800 - _romHeader.ntrHeader.arm9RomOffset);
((u32*)_romHeader.ntrHeader.arm9LoadAddress)[0] = 0xE7FFDEFF;
((u32*)_romHeader.ntrHeader.arm9LoadAddress)[1] = 0xE7FFDEFF;
(const void*)_romHeader.arm9LoadAddress,
(void*)_romHeader.arm9LoadAddress,
0x4800 - _romHeader.arm9RomOffset);
((u32*)_romHeader.arm9LoadAddress)[0] = 0xE7FFDEFF;
((u32*)_romHeader.arm9LoadAddress)[1] = 0xE7FFDEFF;
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

@@ -104,7 +104,7 @@ void TwlAes::DecryptModuleAes(void* data, u32 length, const aes_u128_t* iv) cons
void TwlAes::SetupModuleKeyXY(const nds_header_twl_t* romHeader) const
{
if ((romHeader->ntrHeader.twlFlags & (1 << 2)) || (romHeader->twlFlags2 & (1 << 7)))
if ((romHeader->twlFlags & (1 << 2)) || (romHeader->twlFlags2 & (1 << 7)))
{
// debug
aes_setKey(KEY_SLOT_MODULE, (const aes_u128_t*)romHeader);
@@ -116,8 +116,8 @@ void TwlAes::SetupModuleKeyXY(const nds_header_twl_t* romHeader) const
{
MODULE_KEYX_NINT,
MODULE_KEYX_ENDO,
romHeader->ntrHeader.gameCode,
__builtin_bswap32(romHeader->ntrHeader.gameCode)
romHeader->gameCode,
__builtin_bswap32(romHeader->gameCode)
}};
aes_setKeyXY(KEY_SLOT_MODULE, &keyX, (const aes_u128_t*)romHeader->arm9iSha1Hmac);
}

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

@@ -49,15 +49,18 @@ void* Arm7Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform) const
patchCollection.AddPatch(arm7ArenaPatch);
if (romHeader->unitCode == 0) // seems only present on NITRO, not on HYBRID or LIMITED
{
patchCollection.AddPatch(new DisableArm7WramClearPatch());
}
if (sdkVersion.IsTwlSdk())
{
if (gIsDsiMode && romHeader->IsTwlRom() && twlRomHeader->IsDsiWare())
if (gIsDsiMode && (twlRomHeader->HasNandAccess() || twlRomHeader->HasSdAccess()))
{
patchCollection.AddPatch(new Sdk5DsiSdCardRedirectPatch());
}
else
if (!twlRomHeader->IsDsiWare())
{
patchCollection.AddPatch(new CardiDoTaskFromArm9Patch());
}

View File

@@ -12,10 +12,17 @@
#include "patches/arm9/CardiReadRomIdCorePatch.h"
#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/PokemonBw1/PokemonBw1IrApPatch.h"
#include "patches/arm9/OverlayPatches/PokemonBw2/PokemonBw2IrApPatch.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"
@@ -33,9 +40,9 @@
typedef void (*uncompress_func_t)(void* compressedEnd);
static const u32 sMiiUncompressBackwardPatternOld[] = { 0xE3500000, 0x0A000025, 0xE92D00F0, 0xE9100006 }; // mkds beta; version 0x2012774
static const u32 sMiiUncompressBackwardPatternOld2[] = { 0xE3500000, 0x0A00002B, 0xE92D00F0, 0xE9100006 }; // asterix & obelix xxl 2; version 0x3017531
static const u32 sMiiUncompressBackwardPattern[] = { 0xE3500000, 0x0A000027, 0xE92D00F0, 0xE9100006 }; // mkds
static const u32 sMiiUncompressBackwardPatternOld[] = { 0xE3500000, 0x0A000025, 0xE92D00F0, 0xE9100006 };
static const u32 sMiiUncompressBackwardPatternOld2[] = { 0xE3500000, 0x0A00002B, 0xE92D00F0, 0xE9100006 };
static const u32 sMiiUncompressBackwardPattern[] = { 0xE3500000, 0x0A000027, 0xE92D00F0, 0xE9100006 };
static const u32 sMiiUncompressBackwardPatternHybrid[] = { 0xE3500000, 0x0A000029, 0xE92D01F0, 0xE9100006 };
void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApListEntry* apListEntry,
@@ -43,13 +50,11 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis
{
auto romHeader = (const nds_header_ntr_t*)TWL_SHARED_MEMORY->ntrSharedMem.romHeader;
auto twlRomHeader = (const nds_header_twl_t*)TWL_SHARED_MEMORY->twlRomHeader;
ModuleParamsLocator moduleParamsLocator;
auto moduleParams = moduleParamsLocator.FindModuleParams(romHeader);
u32 arm9Size = romHeader->arm9Size;
u32 arm9iSize = romHeader->IsTwlRom() ? twlRomHeader->arm9iSize : 0;
SdkVersion sdkVersion = moduleParams ? moduleParams->sdkVersion : 0u;
u32 compressedEnd = 0;
PatchCollection patchCollection;
auto moduleParams = ModuleParamsLocator().FindModuleParams(romHeader);
SdkVersion sdkVersion = moduleParams ? moduleParams->sdkVersion : 0u;
if (moduleParams)
{
LOG_DEBUG("Module params found at 0x%p\n", moduleParams);
@@ -57,18 +62,7 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis
const u32* miiUncompressBackward = nullptr;
if (moduleParams->compressedEnd)
{
const u32* miiUncompressBackwardPattern;
if (sdkVersion <= 0x2017532)
miiUncompressBackwardPattern = sMiiUncompressBackwardPatternOld;
else
miiUncompressBackwardPattern = sMiiUncompressBackwardPattern;
miiUncompressBackward = fastSearch16((const u32*)romHeader->arm9LoadAddress, 0x1000, miiUncompressBackwardPattern);
if (!sdkVersion.IsTwlSdk() && !miiUncompressBackward)
miiUncompressBackward = fastSearch16((const u32*)romHeader->arm9LoadAddress, 0x1000, sMiiUncompressBackwardPatternOld2);
if (sdkVersion.IsTwlSdk() && !miiUncompressBackward)
miiUncompressBackward = fastSearch16((const u32*)romHeader->arm9LoadAddress, 0x1000, sMiiUncompressBackwardPatternHybrid);
miiUncompressBackward = FindMIiUncompressBackward(romHeader->arm9LoadAddress, sdkVersion);
if (miiUncompressBackward)
{
arm9Size = moduleParams->compressedEnd + *(u32*)(moduleParams->compressedEnd - 4) - romHeader->arm9LoadAddress;
@@ -86,22 +80,20 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis
{
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->magicLittleEndian == MODULE_PARAMS_TWL_MAGIC_LE &&
arm9iModuleParams->compressedEnd != 0)
{
if (arm9iModuleParams->compressedEnd)
LOG_DEBUG("Compressed arm9i found\n");
if (miiUncompressBackward)
{
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");
}
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");
}
}
}
@@ -126,6 +118,7 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis
romHeader->gameCode,
loaderPlatform
};
PatchCollection patchCollection;
if (sdkVersion != 0)
{
if (*(vu32*)0x02FFF00C == GAMECODE("ADAJ") &&
@@ -154,14 +147,13 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis
}
else
{
SecureSysCallsUnusedSpaceLocator secureSysCallsUnusedSpaceLocator;
secureSysCallsUnusedSpaceLocator.FindUnusedSpace(romHeader, patchContext.GetPatchHeap());
SecureSysCallsUnusedSpaceLocator().FindUnusedSpace(romHeader, patchContext.GetPatchHeap());
}
}
if (sdkVersion.IsTwlSdk())
{
if (!(romHeader->IsTwlRom() && twlRomHeader->IsDsiWare()))
if (!twlRomHeader->IsDsiWare())
{
// if ((romHeader->unitCode & 3) != 3)
{
@@ -169,7 +161,7 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis
}
patchCollection.AddPatch(new CardiReadRomWithCpuPatch());
if (gIsDsiMode && (romHeader->unitCode & 2))
if (gIsDsiMode && romHeader->IsTwlRom())
{
patchCollection.AddPatch(new CardiReadCardWithHashInternalAsyncPatch());
}
@@ -182,98 +174,8 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis
}
patchCollection.AddPatch(new CardiReadRomIdCorePatch());
patchCollection.AddPatch(new OSResetSystemPatch(loaderInfo));
OverlayHookPatch* overlayHookPatch;
if (romHeader->gameCode == GAMECODE("BO5P") ||
romHeader->gameCode == GAMECODE("BO5E") ||
romHeader->gameCode == GAMECODE("BO5J"))
{
overlayHookPatch = new GoldenSunDarkDawnOverlayHookPatch();
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(334, 0, DSProtectVersion::v2_01, ~0u));
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(335, 0, DSProtectVersion::v2_01s, ~0u));
}
else
{
overlayHookPatch = new FsStartOverlayHookPatch();
if (apListEntry)
{
u32 regularOverlayId = apListEntry->GetRegularOverlayId();
if (regularOverlayId != AP_LIST_OVERLAY_ID_INVALID)
{
if (regularOverlayId == AP_LIST_OVERLAY_ID_STATIC_ARM9)
{
LOG_WARNING("Patching DSProtect in main memory currently not supported\n");
}
else
{
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(
regularOverlayId, apListEntry->GetRegularOffset(),
apListEntry->GetDSProtectVersion(), apListEntry->GetDSProtectFunctionMask()));
}
}
u32 sOverlayId = apListEntry->GetSOverlayId();
if (sOverlayId != AP_LIST_OVERLAY_ID_INVALID)
{
if (sOverlayId == AP_LIST_OVERLAY_ID_STATIC_ARM9)
{
LOG_WARNING("Patching DSProtect in main memory currently not supported\n");
}
else
{
auto version = apListEntry->GetDSProtectVersion();
if (version < DSProtectVersion::v2_00s)
{
version = (DSProtectVersion)((u32)version - (u32)DSProtectVersion::v2_00 + (u32)DSProtectVersion::v2_00s);
}
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(
sOverlayId, apListEntry->GetSOffset(), version, ~0u));
}
}
}
switch (romHeader->gameCode)
{
// Pokemon Black & White
case GAMECODE("IRAD"):
case GAMECODE("IRAF"):
case GAMECODE("IRAI"):
case GAMECODE("IRAJ"):
case GAMECODE("IRAK"):
case GAMECODE("IRAO"):
case GAMECODE("IRAS"):
case GAMECODE("IRBD"):
case GAMECODE("IRBF"):
case GAMECODE("IRBI"):
case GAMECODE("IRBJ"):
case GAMECODE("IRBK"):
case GAMECODE("IRBO"):
case GAMECODE("IRBS"):
{
overlayHookPatch->AddOverlayPatch(new PokemonBw1IrApPatch());
break;
}
// Pokemon Black & White 2
// todo: IRDJ and IREJ have two revisions and the first one seems to be different
case GAMECODE("IRDD"):
case GAMECODE("IRDF"):
case GAMECODE("IRDI"):
case GAMECODE("IRDK"):
case GAMECODE("IRDO"):
case GAMECODE("IRDS"):
case GAMECODE("IRED"):
case GAMECODE("IREF"):
case GAMECODE("IREI"):
case GAMECODE("IREK"):
case GAMECODE("IREO"):
case GAMECODE("IRES"):
{
overlayHookPatch->AddOverlayPatch(new PokemonBw2IrApPatch());
break;
}
}
}
patchCollection.AddPatch(overlayHookPatch);
AddGamePatches(patchCollection, romHeader->gameCode, apListEntry);
if (moduleParams && compressedEnd != 0)
{
@@ -293,6 +195,236 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis
ic_invalidateAll();
}
const u32* Arm9Patcher::FindMIiUncompressBackward(u32 arm9LoadAddress, SdkVersion sdkVersion) const
{
const u32* miiUncompressBackwardPattern;
if (sdkVersion <= 0x2017532)
{
miiUncompressBackwardPattern = sMiiUncompressBackwardPatternOld;
}
else
{
miiUncompressBackwardPattern = sMiiUncompressBackwardPattern;
}
const u32* miiUncompressBackward = fastSearch16((const u32*)arm9LoadAddress, 0x1000, miiUncompressBackwardPattern);
if (!sdkVersion.IsTwlSdk() && !miiUncompressBackward)
{
miiUncompressBackward = fastSearch16((const u32*)arm9LoadAddress, 0x1000, sMiiUncompressBackwardPatternOld2);
}
if (sdkVersion.IsTwlSdk() && !miiUncompressBackward)
{
miiUncompressBackward = fastSearch16((const u32*)arm9LoadAddress, 0x1000, sMiiUncompressBackwardPatternHybrid);
}
return miiUncompressBackward;
}
void Arm9Patcher::AddGamePatches(PatchCollection& patchCollection, u32 gameCode, const ApListEntry* apListEntry) const
{
OverlayHookPatch* overlayHookPatch;
if (gameCode == GAMECODE("BO5P") || gameCode == GAMECODE("BO5E") || gameCode == GAMECODE("BO5J"))
{
overlayHookPatch = new GoldenSunDarkDawnOverlayHookPatch();
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(334, 0, DSProtectVersion::v2_01, ~0u));
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(335, 0, DSProtectVersion::v2_01s, ~0u));
}
else
{
overlayHookPatch = new FsStartOverlayHookPatch();
if (apListEntry)
{
AddDSProtectPatches(patchCollection, overlayHookPatch, apListEntry);
}
AddGameSpecificPatches(patchCollection, overlayHookPatch, gameCode);
}
patchCollection.AddPatch(overlayHookPatch);
}
void Arm9Patcher::AddDSProtectPatches(
PatchCollection& patchCollection, OverlayHookPatch* overlayHookPatch, const ApListEntry* apListEntry) const
{
u32 regularOverlayId = apListEntry->GetRegularOverlayId();
if (regularOverlayId != AP_LIST_OVERLAY_ID_INVALID)
{
if (regularOverlayId == AP_LIST_OVERLAY_ID_STATIC_ARM9)
{
patchCollection.AddPatch(new DSProtectArm9Patch(
apListEntry->GetRegularOffset(), apListEntry->GetDSProtectVersion(),
apListEntry->GetDSProtectFunctionMask()));
}
else
{
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(
regularOverlayId, apListEntry->GetRegularOffset(),
apListEntry->GetDSProtectVersion(), apListEntry->GetDSProtectFunctionMask()));
}
}
u32 sOverlayId = apListEntry->GetSOverlayId();
if (sOverlayId != AP_LIST_OVERLAY_ID_INVALID)
{
auto version = apListEntry->GetDSProtectVersion();
if (version < DSProtectVersion::v2_00s)
{
version = (DSProtectVersion)((u32)version - (u32)DSProtectVersion::v2_00 + (u32)DSProtectVersion::v2_00s);
}
if (sOverlayId == AP_LIST_OVERLAY_ID_STATIC_ARM9)
{
patchCollection.AddPatch(new DSProtectArm9Patch(
apListEntry->GetSOffset(), version, ~0u));
}
else
{
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(
sOverlayId, apListEntry->GetSOffset(), version, ~0u));
}
}
}
void Arm9Patcher::AddGameSpecificPatches(
PatchCollection& patchCollection, OverlayHookPatch* overlayHookPatch, u32 gameCode) const
{
switch (gameCode)
{
// Dragon Ball: Origins 2
case GAMECODE("BDBE"):
{
// BDBE;2;1.23;111111;0x1FC;-1;0x0
// BDBE;3;1.23;111111;0x47DC;-1;0x0
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(2, 0x1FC, DSProtectVersion::v1_23, ~0u));
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(3, 0x47DC, DSProtectVersion::v1_23, ~0u));
break;
}
case GAMECODE("BDBJ"):
{
// BDBJ;2;1.23;111111;0x1FC;-1;0x0
// BDBJ;3;1.23;111111;0x4C34;-1;0x0
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(2, 0x1FC, DSProtectVersion::v1_23, ~0u));
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(3, 0x4C34, DSProtectVersion::v1_23, ~0u));
break;
}
case GAMECODE("BDBP"):
{
// BDBP;2;1.23;111111;0x1FC;-1;0x0
// BDBP;3;1.23;111111;0x484C;-1;0x0
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(2, 0x1FC, DSProtectVersion::v1_23, ~0u));
overlayHookPatch->AddOverlayPatch(new DSProtectOverlayPatch(3, 0x484C, DSProtectVersion::v1_23, ~0u));
break;
}
// Puyo Puyo 7
case GAMECODE("BYOJ"):
{
// BYOJ;9;1.08;100110;1.08;0x21AC;-1;0x0
// BYOJ;12;1.08;100101;1.08;0xC568;-1;0x0
// BYOJ;14;1.08;010101;1.08;0x13AB8;-1;0x0
// BYOJ;15;1.08;010110;1.08;0x16DF0;-1;0x0
// BYOJ;19;1.08;011010;1.08;0x17F8;-1;0x0
overlayHookPatch->AddOverlayPatch(new DSProtectPuyoPuyo7Patch());
break;
}
// Pokemon HeartGold & SoulSilver
case GAMECODE("IPGD"):
case GAMECODE("IPGE"):
case GAMECODE("IPGF"):
case GAMECODE("IPGI"):
case GAMECODE("IPGJ"):
case GAMECODE("IPGK"):
case GAMECODE("IPGS"):
case GAMECODE("IPKD"):
case GAMECODE("IPKE"):
case GAMECODE("IPKF"):
case GAMECODE("IPKI"):
case GAMECODE("IPKJ"):
case GAMECODE("IPKK"):
case GAMECODE("IPKS"):
{
overlayHookPatch->AddOverlayPatch(new PokemonIrApPatch(PokemonIrVersion::Hgss));
break;
}
// Pokemon Black & White
case GAMECODE("IRAD"):
case GAMECODE("IRAF"):
case GAMECODE("IRAI"):
case GAMECODE("IRAJ"):
case GAMECODE("IRAK"):
case GAMECODE("IRAO"):
case GAMECODE("IRAS"):
case GAMECODE("IRBD"):
case GAMECODE("IRBF"):
case GAMECODE("IRBI"):
case GAMECODE("IRBJ"):
case GAMECODE("IRBK"):
case GAMECODE("IRBO"):
case GAMECODE("IRBS"):
{
overlayHookPatch->AddOverlayPatch(new PokemonIrApPatch(PokemonIrVersion::Bw1));
break;
}
// Pokemon Black & White 2
case GAMECODE("IRDD"):
case GAMECODE("IRDF"):
case GAMECODE("IRDI"):
case GAMECODE("IRDJ"):
case GAMECODE("IRDK"):
case GAMECODE("IRDO"):
case GAMECODE("IRDS"):
case GAMECODE("IRED"):
case GAMECODE("IREF"):
case GAMECODE("IREI"):
case GAMECODE("IREJ"):
case GAMECODE("IREK"):
case GAMECODE("IREO"):
case GAMECODE("IRES"):
{
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;
}
}
}
void Arm9Patcher::AddRestoreCompressedEndPatch(PatchContext& patchContext,
u32 arm9AutoLoadDoneHookAddress, u32* moduleParamsCompressedEnd, u32 originalCompressedEndValue) const
{

View File

@@ -1,9 +1,12 @@
#pragma once
#include "LoaderInfo.h"
#include "SdkVersion.h"
class ApListEntry;
class LoaderPlatform;
class PatchContext;
class PatchCollection;
class OverlayHookPatch;
/// @brief Class for patching the arm9 of retail roms.
class Arm9Patcher
@@ -18,6 +21,12 @@ public:
bool isCloneBootRom, const loader_info_t* loaderInfo) const;
private:
const u32* FindMIiUncompressBackward(u32 arm9LoadAddress, SdkVersion sdkVersion) const;
void AddGamePatches(PatchCollection& patchCollection, u32 gameCode, const ApListEntry* apListEntry) const;
void AddDSProtectPatches(PatchCollection& patchCollection,
OverlayHookPatch* overlayHookPatch, const ApListEntry* apListEntry) const;
void AddGameSpecificPatches(PatchCollection& patchCollection,
OverlayHookPatch* overlayHookPatch, u32 gameCode) const;
void AddRestoreCompressedEndPatch(PatchContext& patchContext,
u32 arm9AutoLoadDoneHookAddress, u32* moduleParamsCompressedEnd, u32 originalCompressedEndValue) const;
u32 GetAvailableParentSectionSpace() const;

View File

@@ -0,0 +1,5 @@
#pragma once
/// @brief Clears all cpu registers and jumps to the specified \p arm9EntryPoint.
/// @param arm9EntryPoint The arm9 entry point to jump to.
extern "C" void jumpToArm9EntryPoint(void* arm9EntryPoint);

View File

@@ -0,0 +1,29 @@
.section ".itcm", "ax"
.arm
// r0: arm9EntryPoint
.global jumpToArm9EntryPoint
.type jumpToArm9EntryPoint, %function
jumpToArm9EntryPoint:
str r0, entry_point
// Clear all registers
mov r0, #0
mov r1, #0
mov r2, #0
mov r3, #0
mov r4, #0
mov r5, #0
mov r6, #0
mov r7, #0
mov r8, #0
mov r9, #0
mov r10, #0
mov r11, #0
mov r12, #0
mov sp, #0
mov lr, #0
// Jump to the arm9 entry point
ldr pc, entry_point
entry_point:
.word 0

View File

@@ -26,8 +26,7 @@
#include "arm9Clock.h"
#include "errorDisplay/ErrorDisplay.h"
#include "LoaderInfo.h"
typedef void (*entrypoint_t)(void);
#include "jumpToArm9EntryPoint.h"
#define HANDSHAKE_PART0 0xA
#define HANDSHAKE_PART1 0xB
@@ -109,7 +108,7 @@ static void bootArm9()
while (gfx_getVCount() == 191);
REG_IF = ~0u; // final clear of REG_IF bits
auto romHeader = (const nds_header_ntr_t*)TWL_SHARED_MEMORY->ntrSharedMem.romHeader;
((entrypoint_t)romHeader->arm9EntryAddress)();
jumpToArm9EntryPoint((void*)romHeader->arm9EntryAddress);
}
static void handleInitializeSdCardCommand()
@@ -193,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();
@@ -219,22 +218,15 @@ static void handleBootCommand()
REG_EXMEMCNT &= ~0x0880; // map ds and gba slot to arm9
sLoaderPlatform->PrepareRomBoot(sRomDirSector, sRomDirSectorOffset);
Arm9IoRegisterClearer().ClearNtrIoRegisters(isSdkResetSystem);
REG_EXMEMCNT |= 0x0880; // map ds and gba slot to arm7
auto ntrRomHeader = (const nds_header_ntr_t*)TWL_SHARED_MEMORY->ntrSharedMem.romHeader;
if (ntrRomHeader->IsTwlRom())
if (gIsDsiMode && ntrRomHeader->IsTwlRom())
{
if (gIsDsiMode)
{
Arm9IoRegisterClearer().ClearTwlIoRegisters();
REG_SCFG_EXT = 0x8307F100;
scfg_setArm9Clock(ScfgArm9Clock::Twl134MHz);
REG_SCFG_CLK = 0x87;
REG_SCFG_RST = 1;
}
auto twlRomHeader = (const nds_header_twl_t*)TWL_SHARED_MEMORY->twlRomHeader;
if (twlRomHeader->IsDsiWare())
{
REG_EXMEMCNT |= 0x0880; // map ds and gba slot to arm7
}
Arm9IoRegisterClearer().ClearTwlIoRegisters();
REG_SCFG_EXT = 0x8307F100;
scfg_setArm9Clock(ScfgArm9Clock::Twl134MHz);
REG_SCFG_CLK = 0x87;
REG_SCFG_RST = 1;
}
bootArm9();
}

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

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