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
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd08fd19a1 | ||
|
|
ef50e60ff9 | ||
|
|
a424b25263 | ||
|
|
f1e5f0cddc | ||
|
|
26cec4c421 | ||
|
|
e0e33032b9 | ||
|
|
9be5424f23 | ||
|
|
3e4725c99c | ||
|
|
dcc71ca151 | ||
|
|
2d4f9cb29f | ||
|
|
231f14a783 | ||
|
|
d6080984d1 | ||
|
|
6fb34c75f8 | ||
|
|
992e2d1053 | ||
|
|
842dd8e63a | ||
|
|
92a6c6ecdc | ||
|
|
c380041241 | ||
|
|
06c1f22cd1 | ||
|
|
26f27a4138 | ||
|
|
7134c4b330 | ||
|
|
9f6311014d | ||
|
|
6d12399ba4 | ||
|
|
64e020182a | ||
|
|
8036004e5a | ||
|
|
31d6c63e3b | ||
|
|
9fea5f7a51 | ||
|
|
c8898ff13c | ||
|
|
497fdca384 | ||
|
|
e58a55b81c | ||
|
|
19cce5960b | ||
|
|
151b68018d | ||
|
|
4fd7fde362 | ||
|
|
61463ca39a | ||
|
|
c139eacaee | ||
|
|
e8b2f51497 | ||
|
|
48d49f5df8 | ||
|
|
00f6407cf4 | ||
|
|
e36e669daf | ||
|
|
b0d0336e1e | ||
|
|
4416c61158 | ||
|
|
dd062bb4fa | ||
|
|
656f696411 | ||
|
|
0d0fa445c9 | ||
|
|
e5f7283977 | ||
|
|
8550b36dc6 | ||
|
|
79018dee0d | ||
|
|
005b8cab03 | ||
|
|
b0c8dfa5bc | ||
|
|
d12036e6a7 | ||
|
|
e9a8c09a35 | ||
|
|
8e98796be2 | ||
|
|
a8f5d880b2 | ||
|
|
7aba420201 | ||
|
|
8761081c73 | ||
|
|
0dd41f4220 | ||
|
|
485598ab79 | ||
|
|
b2fabe5d97 |
19
.github/workflows/nightly.yml
vendored
19
.github/workflows/nightly.yml
vendored
@@ -2,7 +2,6 @@ name: Build Pico Loader
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["develop"]
|
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -19,12 +18,15 @@ jobs:
|
|||||||
"ACE3DS",
|
"ACE3DS",
|
||||||
"AK2",
|
"AK2",
|
||||||
"AKRPG",
|
"AKRPG",
|
||||||
|
"DATEL",
|
||||||
"DSPICO",
|
"DSPICO",
|
||||||
"DSTT",
|
"DSTT",
|
||||||
|
"EZP",
|
||||||
"G003",
|
"G003",
|
||||||
"M3DS",
|
"M3DS",
|
||||||
"R4",
|
"R4",
|
||||||
"R4iDSN",
|
"R4iDSN",
|
||||||
|
"STARGATE",
|
||||||
"SUPERCARD"
|
"SUPERCARD"
|
||||||
]
|
]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -46,14 +48,15 @@ jobs:
|
|||||||
- name: Run build script
|
- name: Run build script
|
||||||
run: |
|
run: |
|
||||||
make PICO_PLATFORM=${{ matrix.platform }}
|
make PICO_PLATFORM=${{ matrix.platform }}
|
||||||
mv picoLoader7.bin data/picoLoader7.bin
|
- name: Package artifact
|
||||||
mv picoLoader9_${{ matrix.platform }}.bin data/picoLoader9.bin
|
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
|
- name: Publish build to GH Actions
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
data/aplist.bin
|
Pico_Loader_${{ matrix.platform }}
|
||||||
data/savelist.bin
|
# For some reason without explicitly setting a name there is some odd conflicts
|
||||||
data/picoLoader7.bin
|
name: Pico_Loader_${{ matrix.platform }}
|
||||||
data/picoLoader9.bin
|
|
||||||
name: Pico Loader for ${{ matrix.platform }}
|
|
||||||
|
|||||||
67
.github/workflows/release.yml
vendored
Normal file
67
.github/workflows/release.yml
vendored
Normal 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
|
||||||
@@ -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 | âś… |
|
| 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 | ❌ |
|
| AK2 | Acekard 2, 2.1, 2i, r4ids.cn, various derivatives | ❌ |
|
||||||
| AKRPG | Acekard RPG SD card | ❌ |
|
| AKRPG | Acekard RPG SD card | ❌ |
|
||||||
|
| DATEL | DATEL devices consisting of GAMES n' MUSIC and Action Replay DS(i) Media Edition | ❌ |
|
||||||
| DSPICO | DSpico | âś… |
|
| DSPICO | DSpico | âś… |
|
||||||
| DSTT | DSTT, SuperCard DSONE SDHC, r4isdhc.com carts 2014+, r4i-sdhc.com carts, various derivatives | ❌ |
|
| DSTT | DSTT, SuperCard DSONE SDHC, r4isdhc.com carts 2014+, r4i-sdhc.com carts, various derivatives | ❌ |
|
||||||
|
| EZP | EZ-Flash Parallel | ❌ |
|
||||||
| G003 | M3i Zero (GMP-Z003) | âś… |
|
| G003 | M3i Zero (GMP-Z003) | âś… |
|
||||||
| ISNITRO | Supports the IS-NITRO-EMULATOR through agb semihosting. | ❌ |
|
| ISNITRO | Supports the IS-NITRO-EMULATOR through agb semihosting. | ❌ |
|
||||||
| M3DS | M3 DS Real, M3i Zero, iTouchDS, r4rts.com, r4isdhc.com RTS (black) | ❌ |
|
| M3DS | M3 DS Real, M3i Zero, iTouchDS, r4rts.com, r4isdhc.com RTS (black) | ❌ |
|
||||||
| MELONDS | Melon DS support for testing purposes only. | ❌ |
|
| MELONDS | Melon DS support for testing purposes only. | ❌ |
|
||||||
| R4 | Original R4DS (non-SDHC), M3 DS Simply | ❌ |
|
| R4 | Original R4DS (non-SDHC), M3 DS Simply | ❌ |
|
||||||
| R4iDSN | r4idsn.com | ❌ |
|
| R4iDSN | r4idsn.com | ❌ |
|
||||||
|
| STARGATE | Stargate 3DS DS-mode | âś… |
|
||||||
| SUPERCARD | SuperCard (Slot-2 flashcart) | ❌ |
|
| 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>
|
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.
|
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.
|
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)
|
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
|
## Compiling
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
#include "picoAgbAdapter.h"
|
#include "picoAgbAdapter.h"
|
||||||
#include "Environment.h"
|
#include "Environment.h"
|
||||||
|
|
||||||
|
#define NOCASH_ID 0x67246F6E
|
||||||
|
#define NOCASH_ID_MELONDS 0x6F6C656D
|
||||||
|
|
||||||
u32 Environment::_flags;
|
u32 Environment::_flags;
|
||||||
|
|
||||||
static bool detectIsNitroEmulator()
|
static bool detectIsNitroEmulator()
|
||||||
@@ -15,10 +18,10 @@ static bool detectIsNitroEmulator()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool detectNocashPrintSuppport()
|
static bool detectNocashPrintSupport()
|
||||||
{
|
{
|
||||||
u32 nocashIdentifier = *(vu32*)0x04FFFA00;
|
u32 nocashIdentifier = *(vu32*)0x04FFFA00;
|
||||||
return nocashIdentifier == 0x67246F6E; //no$g
|
return nocashIdentifier == NOCASH_ID || nocashIdentifier == NOCASH_ID_MELONDS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool detectPicoAgbAdapter()
|
static bool detectPicoAgbAdapter()
|
||||||
@@ -55,7 +58,7 @@ void Environment::Initialize(bool dsiMode)
|
|||||||
}
|
}
|
||||||
if (!(_flags & ENVIRONMENT_FLAGS_IS_NITRO_EMULATOR))
|
if (!(_flags & ENVIRONMENT_FLAGS_IS_NITRO_EMULATOR))
|
||||||
{
|
{
|
||||||
if (detectNocashPrintSuppport())
|
if (detectNocashPrintSupport())
|
||||||
_flags |= ENVIRONMENT_FLAGS_NOCASH_PRINT;
|
_flags |= ENVIRONMENT_FLAGS_NOCASH_PRINT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,32 +1,54 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include <algorithm>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "SaveList.h"
|
#include "SaveList.h"
|
||||||
#include "SaveListFactory.h"
|
#include "SaveListFactory.h"
|
||||||
#include "fileInfo.h"
|
#include "fileInfo.h"
|
||||||
|
#include "gameCode.h"
|
||||||
#include "CardSaveArranger.h"
|
#include "CardSaveArranger.h"
|
||||||
|
|
||||||
#define SAVE_LIST_PATH "/_pico/savelist.bin"
|
#define SAVE_LIST_PATH "/_pico/savelist.bin"
|
||||||
#define DEFAULT_SAVE_SIZE (512 * 1024)
|
#define DEFAULT_SAVE_SIZE (512 * 1024)
|
||||||
#define SAVE_FILL_VALUE 0xFF
|
#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;
|
u32 saveSize = DEFAULT_SAVE_SIZE;
|
||||||
if (saveList)
|
if (header->nandBackupRegionStart != 0)
|
||||||
{
|
{
|
||||||
const auto saveListEntry = saveList->FindEntry(gameCode);
|
saveType = CardSaveType::Nand;
|
||||||
if (!saveListEntry)
|
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",
|
const auto saveListEntry = saveList->FindEntry(header->gameCode);
|
||||||
gameCode & 0xFF, (gameCode >> 8) & 0xFF, (gameCode >> 16) & 0xFF, gameCode >> 24);
|
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)
|
if (saveSize == 0)
|
||||||
{
|
{
|
||||||
@@ -50,8 +72,9 @@ bool CardSaveArranger::SetupCardSave(u32 gameCode, const TCHAR* savePath) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ffBuffer = std::make_unique<u8[]>(512);
|
const u32 ffBufferSize = 32 * 1024;
|
||||||
memset(ffBuffer.get(), SAVE_FILL_VALUE, 512);
|
auto ffBuffer = std::make_unique<u8[]>(ffBufferSize);
|
||||||
|
memset(ffBuffer.get(), SAVE_FILL_VALUE, ffBufferSize);
|
||||||
|
|
||||||
u32 offset = initialSize;
|
u32 offset = initialSize;
|
||||||
// Align to 512 bytes
|
// Align to 512 bytes
|
||||||
@@ -68,17 +91,45 @@ bool CardSaveArranger::SetupCardSave(u32 gameCode, const TCHAR* savePath) const
|
|||||||
offset += remainingTo512;
|
offset += remainingTo512;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write in 512-byte blocks
|
// Write in up to 32kb blocks
|
||||||
while (offset < saveSize)
|
while (offset < saveSize)
|
||||||
{
|
{
|
||||||
|
u32 bytesToWrite = std::min<u32>(saveSize - offset, ffBufferSize);
|
||||||
UINT bytesWritten = 0;
|
UINT bytesWritten = 0;
|
||||||
if (f_write(file.get(), ffBuffer.get(), 512, &bytesWritten) != FR_OK ||
|
if (f_write(file.get(), ffBuffer.get(), bytesToWrite, &bytesWritten) != FR_OK ||
|
||||||
bytesWritten != 512)
|
bytesWritten != bytesToWrite)
|
||||||
{
|
{
|
||||||
LOG_FATAL("Failed to expand save file\n");
|
LOG_FATAL("Failed to expand save file\n");
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "ndsHeader.h"
|
||||||
|
|
||||||
/// @brief Class for setting up the save file for retail card roms.
|
/// @brief Class for setting up the save file for retail card roms.
|
||||||
class CardSaveArranger
|
class CardSaveArranger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// @brief Sets up the save file at \p savePath for a retail card rom with the given \p gameCode.
|
/// @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.
|
/// @param savePath The desired save file path.
|
||||||
/// @return \c true when setting up the save was successful, or \c false otherwise.
|
/// @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;
|
||||||
};
|
};
|
||||||
|
|||||||
12
arm7/source/loader/ConsoleRegion.h
Normal file
12
arm7/source/loader/ConsoleRegion.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/// @brief Enum for DSi console region.
|
||||||
|
enum class ConsoleRegion
|
||||||
|
{
|
||||||
|
Japan,
|
||||||
|
America,
|
||||||
|
Europe,
|
||||||
|
Australia,
|
||||||
|
China,
|
||||||
|
Korea
|
||||||
|
};
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "DsiWareSaveArranger.h"
|
#include "DsiWareSaveArranger.h"
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ bool DsiWareSaveArranger::SetupDsiWareSaveFile(const TCHAR* savePath, u32 saveSi
|
|||||||
if (f_lseek(file.get(), saveSize) != FR_OK ||
|
if (f_lseek(file.get(), saveSize) != FR_OK ||
|
||||||
f_lseek(file.get(), 0) != 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;
|
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 ||
|
if (f_write(file.get(), fatHeader.get(), sizeof(fat_header_t), &bytesWritten) != FR_OK ||
|
||||||
bytesWritten != sizeof(fat_header_t))
|
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;
|
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;
|
bytesWritten = 0;
|
||||||
if (f_write(file.get(), fatHeader.get(), 512, &bytesWritten) != FR_OK ||
|
if (f_write(file.get(), clearBuffer.get(), bytesToWrite, &bytesWritten) != FR_OK ||
|
||||||
bytesWritten != 512)
|
bytesWritten != bytesToWrite)
|
||||||
{
|
{
|
||||||
LOG_FATAL("Failed to format private save file\n");
|
LOG_FATAL("Failed to format DSiWare save file\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
offset += bytesToWrite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -165,9 +165,9 @@ void NdsLoader::Load(BootMode bootMode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isHomebrew = (_romHeader.ntrHeader.makerCode[0] == 0 && _romHeader.ntrHeader.makerCode[1] == 0)
|
bool isHomebrew = (_romHeader.makerCode[0] == 0 && _romHeader.makerCode[1] == 0)
|
||||||
|| _romHeader.ntrHeader.arm9AutoLoadDoneHookAddress == 0
|
|| (_romHeader.arm9AutoLoadDoneHookAddress == 0 && _romHeader.arm7AutoLoadDoneHookAddress == 0)
|
||||||
|| _romHeader.ntrHeader.arm7LoadAddress >= 0x03000000;
|
|| _romHeader.arm7LoadAddress >= 0x03000000;
|
||||||
|
|
||||||
if (isHomebrew)
|
if (isHomebrew)
|
||||||
{
|
{
|
||||||
@@ -186,16 +186,7 @@ void NdsLoader::Load(BootMode bootMode)
|
|||||||
memset(&_dsiwareSaveResult, 0, sizeof(_dsiwareSaveResult));
|
memset(&_dsiwareSaveResult, 0, sizeof(_dsiwareSaveResult));
|
||||||
if (bootMode != BootMode::Multiboot)
|
if (bootMode != BootMode::Multiboot)
|
||||||
{
|
{
|
||||||
if (!_romHeader.IsDsiWare())
|
if (_romHeader.IsDsiWare())
|
||||||
{
|
|
||||||
if (bootMode != BootMode::SdkResetSystem)
|
|
||||||
{
|
|
||||||
HandleCardSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
HandleAntiPiracy();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (bootMode == BootMode::SdkResetSystem)
|
if (bootMode == BootMode::SdkResetSystem)
|
||||||
{
|
{
|
||||||
@@ -210,12 +201,26 @@ void NdsLoader::Load(BootMode bootMode)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (bootMode != BootMode::SdkResetSystem)
|
||||||
|
{
|
||||||
|
if (!CardSaveArranger().SetupCardSave(&_romHeader, _savePath))
|
||||||
|
{
|
||||||
|
ErrorDisplay().PrintError("Failed to setup save file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleAntiPiracy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bootMode == BootMode::Normal)
|
if (bootMode == BootMode::Normal)
|
||||||
{
|
{
|
||||||
bootType = _romHeader.IsDsiWare() ? BOOT_TYPE_NAND : BOOT_TYPE_CARD;
|
bootType = _romHeader.IsDsiWare() ? BOOT_TYPE_NAND : BOOT_TYPE_CARD;
|
||||||
|
HandleIQueRegionFreePatching();
|
||||||
}
|
}
|
||||||
else if (bootMode == BootMode::Multiboot)
|
else if (bootMode == BootMode::Multiboot)
|
||||||
{
|
{
|
||||||
@@ -224,14 +229,14 @@ void NdsLoader::Load(BootMode bootMode)
|
|||||||
|
|
||||||
SetupSharedMemory(cardId, agbMem, resetParam, romOffset, bootType);
|
SetupSharedMemory(cardId, agbMem, resetParam, romOffset, bootType);
|
||||||
|
|
||||||
if (Environment::IsDsiMode() && _romHeader.IsTwlRom())
|
|
||||||
{
|
|
||||||
SetupTwlConfig();
|
|
||||||
TwlAes().SetupAes(&_romHeader);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Environment::IsDsiMode())
|
if (Environment::IsDsiMode())
|
||||||
{
|
{
|
||||||
|
if (_romHeader.IsTwlRom())
|
||||||
|
{
|
||||||
|
SetupTwlConfig();
|
||||||
|
TwlAes().SetupAes(&_romHeader);
|
||||||
|
}
|
||||||
|
|
||||||
RemapWram();
|
RemapWram();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,6 +297,9 @@ void NdsLoader::Load(BootMode bootMode)
|
|||||||
{
|
{
|
||||||
SetupDsiDeviceList();
|
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);
|
u32 scfgExt7 = 0x93FBFB00 | (_romHeader.arm7ScfgExt7 & 0x40407);
|
||||||
REG_SCFG_EXT = scfgExt7;
|
REG_SCFG_EXT = scfgExt7;
|
||||||
REG_SCFG_CLK = 0x187;
|
REG_SCFG_CLK = 0x187;
|
||||||
@@ -321,20 +329,20 @@ void NdsLoader::Load(BootMode bootMode)
|
|||||||
|
|
||||||
if (Environment::IsDsiMode())
|
if (Environment::IsDsiMode())
|
||||||
{
|
{
|
||||||
if (!_romHeader.IsTwlRom())
|
if (_romHeader.IsTwlRom())
|
||||||
{
|
|
||||||
DSMode().SwitchToDSMode(_romHeader.ntrHeader.gameCode);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (!(_romHeader.twlFlags2 & 1))
|
if (!(_romHeader.twlFlags2 & 1))
|
||||||
{
|
{
|
||||||
DSMode().SwitchToDSTouchAndSoundMode(_romHeader.ntrHeader.gameCode);
|
DSMode().SwitchToDSTouchAndSoundMode(_romHeader.gameCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set back power button handling to irq mode
|
// Set back power button handling to irq mode
|
||||||
mcu_writeReg(MCU_REG_MODE, 1);
|
mcu_writeReg(MCU_REG_MODE, 1);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DSMode().SwitchToDSMode(_romHeader.gameCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isHomebrew)
|
if (isHomebrew)
|
||||||
@@ -349,11 +357,11 @@ void NdsLoader::Load(BootMode bootMode)
|
|||||||
bool NdsLoader::IsCloneBootRom(u32 romOffset)
|
bool NdsLoader::IsCloneBootRom(u32 romOffset)
|
||||||
{
|
{
|
||||||
bool isCloneBootRom = false;
|
bool isCloneBootRom = false;
|
||||||
if (_romHeader.ntrHeader.ntrRomSize != 0)
|
if (_romHeader.ntrRomSize != 0)
|
||||||
{
|
{
|
||||||
UINT bytesRead = 0;
|
UINT bytesRead = 0;
|
||||||
u16 signatureHeader;
|
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 &&
|
f_read(&_romFile, &signatureHeader, sizeof(signatureHeader), &bytesRead) == FR_OK &&
|
||||||
bytesRead == 2 &&
|
bytesRead == 2 &&
|
||||||
signatureHeader == 0x6361)
|
signatureHeader == 0x6361)
|
||||||
@@ -371,7 +379,7 @@ void NdsLoader::InsertArgv()
|
|||||||
u32 argSize = 0;
|
u32 argSize = 0;
|
||||||
|
|
||||||
// Find the address to write argv
|
// 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())
|
if (_romHeader.IsTwlRom())
|
||||||
{
|
{
|
||||||
u32 argDstTwl = ((_romHeader.arm9iLoadAddress + _romHeader.arm9iSize + 3) & ~3) + 4;
|
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));
|
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[0] = cardId;
|
||||||
*(vu32*)&TWL_SHARED_MEMORY->ntrSharedMem.bootCheckInfo[4] = 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[8] = _romHeader.headerCrc;
|
||||||
*(vu16*)&TWL_SHARED_MEMORY->ntrSharedMem.bootCheckInfo[0xA] = _romHeader.ntrHeader.secureAreaCrc;
|
*(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.bootCheckInfo[0x10] = 0x5835; // use correct card id address
|
||||||
*(vu32*)&TWL_SHARED_MEMORY->ntrSharedMem.isDebuggerData[0x1C] = agbMem;
|
*(vu32*)&TWL_SHARED_MEMORY->ntrSharedMem.isDebuggerData[0x1C] = agbMem;
|
||||||
TWL_SHARED_MEMORY->ntrSharedMem.resetParam = resetParam;
|
TWL_SHARED_MEMORY->ntrSharedMem.resetParam = resetParam;
|
||||||
@@ -534,13 +542,19 @@ void NdsLoader::LoadFirmwareUserSettings()
|
|||||||
|
|
||||||
bool NdsLoader::ShouldAttemptDldiPatch()
|
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
|
// Final Fantasy IV
|
||||||
// See also: https://shutterbug2000.github.io/very-clever/
|
|
||||||
case GAMECODE("YF4P"):
|
case GAMECODE("YF4P"):
|
||||||
case GAMECODE("YF4E"):
|
case GAMECODE("YF4E"):
|
||||||
case GAMECODE("YF4J"):
|
case GAMECODE("YF4J"):
|
||||||
|
// Final Fantasy Crystal Chronicles - Ring of Fates
|
||||||
|
case GAMECODE("AFXE"):
|
||||||
|
case GAMECODE("AFXP"):
|
||||||
|
// Nanashi no Geemu
|
||||||
|
case GAMECODE("YFQJ"):
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -602,20 +616,12 @@ bool NdsLoader::TryLoadRomHeader(u32 romOffset)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NdsLoader::HandleCardSave()
|
|
||||||
{
|
|
||||||
if (!CardSaveArranger().SetupCardSave(_romHeader.ntrHeader.gameCode, _savePath))
|
|
||||||
{
|
|
||||||
ErrorDisplay().PrintError("Failed to setup save file.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NdsLoader::HandleAntiPiracy()
|
void NdsLoader::HandleAntiPiracy()
|
||||||
{
|
{
|
||||||
auto apList = ApListFactory().CreateFromFile(AP_LIST_PATH);
|
auto apList = ApListFactory().CreateFromFile(AP_LIST_PATH);
|
||||||
if (apList)
|
if (apList)
|
||||||
{
|
{
|
||||||
auto entry = apList->FindEntry(_romHeader.ntrHeader.gameCode, _romHeader.ntrHeader.softwareVersion);
|
auto entry = apList->FindEntry(_romHeader.gameCode, _romHeader.softwareVersion);
|
||||||
if (entry)
|
if (entry)
|
||||||
{
|
{
|
||||||
entry->Dump();
|
entry->Dump();
|
||||||
@@ -677,15 +683,15 @@ void NdsLoader::RemapWram()
|
|||||||
|
|
||||||
bool NdsLoader::TryLoadArm9()
|
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");
|
LOG_FATAL("Failed to seek to arm9\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
UINT bytesRead = 0;
|
UINT bytesRead = 0;
|
||||||
FRESULT result = f_read(&_romFile, (void*)_romHeader.ntrHeader.arm9LoadAddress, _romHeader.ntrHeader.arm9Size, &bytesRead);
|
FRESULT result = f_read(&_romFile, (void*)_romHeader.arm9LoadAddress, _romHeader.arm9Size, &bytesRead);
|
||||||
if (result != FR_OK || bytesRead != _romHeader.ntrHeader.arm9Size)
|
if (result != FR_OK || bytesRead != _romHeader.arm9Size)
|
||||||
{
|
{
|
||||||
LOG_FATAL("Failed to read arm9. Result: %d, bytesRead: %d\n", result, bytesRead);
|
LOG_FATAL("Failed to read arm9. Result: %d, bytesRead: %d\n", result, bytesRead);
|
||||||
return false;
|
return false;
|
||||||
@@ -730,7 +736,7 @@ bool NdsLoader::TryLoadArm9i()
|
|||||||
|
|
||||||
bool NdsLoader::TryDecryptArm9i()
|
bool NdsLoader::TryDecryptArm9i()
|
||||||
{
|
{
|
||||||
if (!(_romHeader.ntrHeader.twlFlags & (1 << 1)))
|
if (!(_romHeader.twlFlags & (1 << 1)))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -750,7 +756,7 @@ bool NdsLoader::TryDecryptArm9i()
|
|||||||
|
|
||||||
bool NdsLoader::TryDecryptArm7i()
|
bool NdsLoader::TryDecryptArm7i()
|
||||||
{
|
{
|
||||||
if (!(_romHeader.ntrHeader.twlFlags & (1 << 1)) || _romHeader.modcryptArea2Size == 0)
|
if (!(_romHeader.twlFlags & (1 << 1)) || _romHeader.modcryptArea2Size == 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -770,15 +776,15 @@ bool NdsLoader::TryDecryptArm7i()
|
|||||||
|
|
||||||
bool NdsLoader::TryLoadArm7()
|
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");
|
LOG_FATAL("Failed to seek to arm7\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
UINT bytesRead = 0;
|
UINT bytesRead = 0;
|
||||||
FRESULT result = f_read(&_romFile, (void*)_romHeader.ntrHeader.arm7LoadAddress, _romHeader.ntrHeader.arm7Size, &bytesRead);
|
FRESULT result = f_read(&_romFile, (void*)_romHeader.arm7LoadAddress, _romHeader.arm7Size, &bytesRead);
|
||||||
if (result != FR_OK || bytesRead != _romHeader.ntrHeader.arm7Size)
|
if (result != FR_OK || bytesRead != _romHeader.arm7Size)
|
||||||
{
|
{
|
||||||
LOG_FATAL("Failed to read arm7. Result: %d, bytesRead: %d\n", result, bytesRead);
|
LOG_FATAL("Failed to read arm7. Result: %d, bytesRead: %d\n", result, bytesRead);
|
||||||
return false;
|
return false;
|
||||||
@@ -822,19 +828,19 @@ void NdsLoader::HandleDldiPatching()
|
|||||||
}
|
}
|
||||||
|
|
||||||
int arm9DldiAddr = sDldiStubMatcher.FindFirstOccurance(
|
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)
|
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);
|
LOG_DEBUG("Dldi found in arm9 at address %p\n", arm9Dldi);
|
||||||
dldi_patchTo(arm9Dldi);
|
dldi_patchTo(arm9Dldi);
|
||||||
}
|
}
|
||||||
|
|
||||||
int arm7DldiAddr = sDldiStubMatcher.FindFirstOccurance(
|
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)
|
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);
|
LOG_DEBUG("Dldi found in arm7 at address %p\n", arm7Dldi);
|
||||||
dldi_patchTo(arm7Dldi);
|
dldi_patchTo(arm7Dldi);
|
||||||
}
|
}
|
||||||
@@ -854,11 +860,15 @@ void NdsLoader::StartRom(BootMode bootMode)
|
|||||||
REG_IF2 = ~0u;
|
REG_IF2 = ~0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
((entrypoint_t)_romHeader.ntrHeader.arm7EntryAddress)();
|
((entrypoint_t)_romHeader.arm7EntryAddress)();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NdsLoader::SetupTwlConfig()
|
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* twlConfig = (twl_config_t*)0x02000400;
|
||||||
*(twl_config_t**)0x02FFFDFC = twlConfig;
|
*(twl_config_t**)0x02FFFDFC = twlConfig;
|
||||||
*(vu8*)0x02FFFDFA = 0x80;
|
*(vu8*)0x02FFFDFA = 0x80;
|
||||||
@@ -866,7 +876,7 @@ void NdsLoader::SetupTwlConfig()
|
|||||||
memset(twlConfig, 0, sizeof(twl_config_t));
|
memset(twlConfig, 0, sizeof(twl_config_t));
|
||||||
twlConfig->configFlags = 0x0100000F;
|
twlConfig->configFlags = 0x0100000F;
|
||||||
twlConfig->country = 0x4E;
|
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->rtcYear = TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x66];
|
||||||
twlConfig->rtcOffset = *(s64*)&TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x68];
|
twlConfig->rtcOffset = *(s64*)&TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x68];
|
||||||
twlConfig->eulaAgreeVersion[0] = 1;
|
twlConfig->eulaAgreeVersion[0] = 1;
|
||||||
@@ -874,7 +884,7 @@ void NdsLoader::SetupTwlConfig()
|
|||||||
twlConfig->alarmMinute = TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x53];
|
twlConfig->alarmMinute = TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x53];
|
||||||
twlConfig->alarmEnable = TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x56];
|
twlConfig->alarmEnable = TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x56];
|
||||||
twlConfig->systemMenuUsedTitleSlots = 9;
|
twlConfig->systemMenuUsedTitleSlots = 9;
|
||||||
twlConfig->systemMenuUsedTitleSlots = 30;
|
twlConfig->systemMenuFreeTitleSlots = 30;
|
||||||
twlConfig->field24 = 3;
|
twlConfig->field24 = 3;
|
||||||
memcpy(&twlConfig->touchCalibrationX1Adc, &TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x58], 0xC);
|
memcpy(&twlConfig->touchCalibrationX1Adc, &TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x58], 0xC);
|
||||||
twlConfig->field3C = 0x0201209C;
|
twlConfig->field3C = 0x0201209C;
|
||||||
@@ -900,9 +910,10 @@ void NdsLoader::SetupTwlConfig()
|
|||||||
}
|
}
|
||||||
*(vu16*)0x020005E2 = swi_getCrc16(0xFFFF, (void*)0x020005E4, 0xC);
|
*(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;
|
*(vu32*)0x02FFFD6C = 0;
|
||||||
*(vu8*)0x02FFFD70 = 2; // region, 2 = europe
|
*(vu8*)0x02FFFD70 = static_cast<u8>(romRegion); // region
|
||||||
}
|
}
|
||||||
|
|
||||||
void NdsLoader::SetDeviceListEntry(dsi_devicelist_entry_t& deviceListEntry,
|
void NdsLoader::SetDeviceListEntry(dsi_devicelist_entry_t& deviceListEntry,
|
||||||
@@ -964,20 +975,20 @@ bool NdsLoader::TrySetupDsiWareSave()
|
|||||||
|
|
||||||
bool NdsLoader::TryDecryptSecureArea()
|
bool NdsLoader::TryDecryptSecureArea()
|
||||||
{
|
{
|
||||||
if (_romHeader.ntrHeader.arm9RomOffset < 0x4000 || _romHeader.ntrHeader.arm9RomOffset >= 0x8000)
|
if (_romHeader.arm9RomOffset < 0x4000 || _romHeader.arm9RomOffset >= 0x8000)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((u32*)_romHeader.ntrHeader.arm9LoadAddress)[0] == 0xE7FFDEFF &&
|
if (((u32*)_romHeader.arm9LoadAddress)[0] == 0xE7FFDEFF &&
|
||||||
((u32*)_romHeader.ntrHeader.arm9LoadAddress)[1] == 0xE7FFDEFF)
|
((u32*)_romHeader.arm9LoadAddress)[1] == 0xE7FFDEFF)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 secureAreaCrc = swi_getCrc16(0xFFFF,
|
u16 secureAreaCrc = swi_getCrc16(0xFFFF,
|
||||||
(const void*)_romHeader.ntrHeader.arm9LoadAddress, 0x8000 - _romHeader.ntrHeader.arm9RomOffset);
|
(const void*)_romHeader.arm9LoadAddress, 0x8000 - _romHeader.arm9RomOffset);
|
||||||
if (secureAreaCrc != _romHeader.ntrHeader.secureAreaCrc)
|
if (secureAreaCrc != _romHeader.secureAreaCrc)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -994,14 +1005,122 @@ bool NdsLoader::TryDecryptSecureArea()
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto blowfish = std::make_unique<Blowfish>(keyTable.get());
|
auto blowfish = std::make_unique<Blowfish>(keyTable.get());
|
||||||
blowfish->TransformTable(_romHeader.ntrHeader.gameCode, 3, 8);
|
blowfish->TransformTable(_romHeader.gameCode, 3, 8);
|
||||||
blowfish->Decrypt(
|
blowfish->Decrypt(
|
||||||
(const void*)_romHeader.ntrHeader.arm9LoadAddress,
|
(const void*)_romHeader.arm9LoadAddress,
|
||||||
(void*)_romHeader.ntrHeader.arm9LoadAddress,
|
(void*)_romHeader.arm9LoadAddress,
|
||||||
0x4800 - _romHeader.ntrHeader.arm9RomOffset);
|
0x4800 - _romHeader.arm9RomOffset);
|
||||||
((u32*)_romHeader.ntrHeader.arm9LoadAddress)[0] = 0xE7FFDEFF;
|
((u32*)_romHeader.arm9LoadAddress)[0] = 0xE7FFDEFF;
|
||||||
((u32*)_romHeader.ntrHeader.arm9LoadAddress)[1] = 0xE7FFDEFF;
|
((u32*)_romHeader.arm9LoadAddress)[1] = 0xE7FFDEFF;
|
||||||
|
|
||||||
LOG_DEBUG("Decrypted secure area\n");
|
LOG_DEBUG("Decrypted secure area\n");
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include "ndsHeader.h"
|
#include "ndsHeader.h"
|
||||||
#include "DsiWareSaveArranger.h"
|
#include "DsiWareSaveArranger.h"
|
||||||
#include "BootMode.h"
|
#include "BootMode.h"
|
||||||
|
#include "ConsoleRegion.h"
|
||||||
|
#include "UserLanguage.h"
|
||||||
|
|
||||||
struct dsi_devicelist_entry_t;
|
struct dsi_devicelist_entry_t;
|
||||||
|
|
||||||
@@ -54,7 +56,6 @@ private:
|
|||||||
void ClearMainMemory();
|
void ClearMainMemory();
|
||||||
void CreateRomClusterTable();
|
void CreateRomClusterTable();
|
||||||
bool TryLoadRomHeader(u32 romOffset);
|
bool TryLoadRomHeader(u32 romOffset);
|
||||||
void HandleCardSave();
|
|
||||||
void HandleAntiPiracy();
|
void HandleAntiPiracy();
|
||||||
void RemapWram();
|
void RemapWram();
|
||||||
bool TryLoadArm9();
|
bool TryLoadArm9();
|
||||||
@@ -72,4 +73,8 @@ private:
|
|||||||
void InsertArgv();
|
void InsertArgv();
|
||||||
bool TrySetupDsiWareSave();
|
bool TrySetupDsiWareSave();
|
||||||
bool TryDecryptSecureArea();
|
bool TryDecryptSecureArea();
|
||||||
|
void HandleIQueRegionFreePatching();
|
||||||
|
ConsoleRegion GetRomRegion(u32 gameCode);
|
||||||
|
UserLanguage GetLanguageByRomRegion(ConsoleRegion romRegion);
|
||||||
|
u32 GetSupportedLanguagesByRegion(ConsoleRegion region);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,13 +7,13 @@
|
|||||||
class SaveListEntry
|
class SaveListEntry
|
||||||
{
|
{
|
||||||
u32 gameCode;
|
u32 gameCode;
|
||||||
u8 saveType; // see CardSaveType
|
CardSaveType saveType;
|
||||||
u8 saveSize; // 0 or 1 << x
|
u8 saveSize; // 0 or 1 << x
|
||||||
u8 reserved[2]; // for possible future use
|
u8 reserved[2]; // for possible future use
|
||||||
|
|
||||||
public:
|
public:
|
||||||
u32 GetGameCode() const { return gameCode; }
|
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); }
|
u32 GetSaveSize() const { return saveSize == 0 ? 0 : (1u << saveSize); }
|
||||||
|
|
||||||
void Dump() const
|
void Dump() const
|
||||||
|
|||||||
@@ -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
|
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
|
// debug
|
||||||
aes_setKey(KEY_SLOT_MODULE, (const aes_u128_t*)romHeader);
|
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_NINT,
|
||||||
MODULE_KEYX_ENDO,
|
MODULE_KEYX_ENDO,
|
||||||
romHeader->ntrHeader.gameCode,
|
romHeader->gameCode,
|
||||||
__builtin_bswap32(romHeader->ntrHeader.gameCode)
|
__builtin_bswap32(romHeader->gameCode)
|
||||||
}};
|
}};
|
||||||
aes_setKeyXY(KEY_SLOT_MODULE, &keyX, (const aes_u128_t*)romHeader->arm9iSha1Hmac);
|
aes_setKeyXY(KEY_SLOT_MODULE, &keyX, (const aes_u128_t*)romHeader->arm9iSha1Hmac);
|
||||||
}
|
}
|
||||||
|
|||||||
14
arm7/source/loader/UserLanguage.h
Normal file
14
arm7/source/loader/UserLanguage.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/// @brief Enum for DSi user language.
|
||||||
|
enum class UserLanguage
|
||||||
|
{
|
||||||
|
Japanese,
|
||||||
|
English,
|
||||||
|
French,
|
||||||
|
German,
|
||||||
|
Italian,
|
||||||
|
Spanish,
|
||||||
|
Chinese,
|
||||||
|
Korean
|
||||||
|
};
|
||||||
@@ -49,15 +49,18 @@ void* Arm7Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform) const
|
|||||||
patchCollection.AddPatch(arm7ArenaPatch);
|
patchCollection.AddPatch(arm7ArenaPatch);
|
||||||
|
|
||||||
if (romHeader->unitCode == 0) // seems only present on NITRO, not on HYBRID or LIMITED
|
if (romHeader->unitCode == 0) // seems only present on NITRO, not on HYBRID or LIMITED
|
||||||
|
{
|
||||||
patchCollection.AddPatch(new DisableArm7WramClearPatch());
|
patchCollection.AddPatch(new DisableArm7WramClearPatch());
|
||||||
|
}
|
||||||
|
|
||||||
if (sdkVersion.IsTwlSdk())
|
if (sdkVersion.IsTwlSdk())
|
||||||
{
|
{
|
||||||
if (gIsDsiMode && romHeader->IsTwlRom() && twlRomHeader->IsDsiWare())
|
if (gIsDsiMode && (twlRomHeader->HasNandAccess() || twlRomHeader->HasSdAccess()))
|
||||||
{
|
{
|
||||||
patchCollection.AddPatch(new Sdk5DsiSdCardRedirectPatch());
|
patchCollection.AddPatch(new Sdk5DsiSdCardRedirectPatch());
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (!twlRomHeader->IsDsiWare())
|
||||||
{
|
{
|
||||||
patchCollection.AddPatch(new CardiDoTaskFromArm9Patch());
|
patchCollection.AddPatch(new CardiDoTaskFromArm9Patch());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,17 @@
|
|||||||
#include "patches/arm9/CardiReadRomIdCorePatch.h"
|
#include "patches/arm9/CardiReadRomIdCorePatch.h"
|
||||||
#include "patches/arm9/OSResetSystemPatch.h"
|
#include "patches/arm9/OSResetSystemPatch.h"
|
||||||
#include "patches/arm9/PokemonDownloaderArm9Patch.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/FsStartOverlayHookPatch.h"
|
||||||
#include "patches/arm9/OverlayPatches/DSProtectPatches/DSProtectOverlayPatch.h"
|
#include "patches/arm9/OverlayPatches/DSProtectPatches/DSProtectOverlayPatch.h"
|
||||||
#include "patches/arm9/OverlayPatches/PokemonBw1/PokemonBw1IrApPatch.h"
|
#include "patches/arm9/OverlayPatches/DSProtectPatches/DSProtectPuyoPuyo7Patch.h"
|
||||||
#include "patches/arm9/OverlayPatches/PokemonBw2/PokemonBw2IrApPatch.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 "patches/arm9/OverlayPatches/GoldenSunDarkDawn/GoldenSunDarkDawnOverlayHookPatch.h"
|
||||||
#include "SecureSysCallsUnusedSpaceLocator.h"
|
#include "SecureSysCallsUnusedSpaceLocator.h"
|
||||||
#include "fastSearch.h"
|
#include "fastSearch.h"
|
||||||
@@ -33,9 +40,9 @@
|
|||||||
|
|
||||||
typedef void (*uncompress_func_t)(void* compressedEnd);
|
typedef void (*uncompress_func_t)(void* compressedEnd);
|
||||||
|
|
||||||
static const u32 sMiiUncompressBackwardPatternOld[] = { 0xE3500000, 0x0A000025, 0xE92D00F0, 0xE9100006 }; // mkds beta; version 0x2012774
|
static const u32 sMiiUncompressBackwardPatternOld[] = { 0xE3500000, 0x0A000025, 0xE92D00F0, 0xE9100006 };
|
||||||
static const u32 sMiiUncompressBackwardPatternOld2[] = { 0xE3500000, 0x0A00002B, 0xE92D00F0, 0xE9100006 }; // asterix & obelix xxl 2; version 0x3017531
|
static const u32 sMiiUncompressBackwardPatternOld2[] = { 0xE3500000, 0x0A00002B, 0xE92D00F0, 0xE9100006 };
|
||||||
static const u32 sMiiUncompressBackwardPattern[] = { 0xE3500000, 0x0A000027, 0xE92D00F0, 0xE9100006 }; // mkds
|
static const u32 sMiiUncompressBackwardPattern[] = { 0xE3500000, 0x0A000027, 0xE92D00F0, 0xE9100006 };
|
||||||
static const u32 sMiiUncompressBackwardPatternHybrid[] = { 0xE3500000, 0x0A000029, 0xE92D01F0, 0xE9100006 };
|
static const u32 sMiiUncompressBackwardPatternHybrid[] = { 0xE3500000, 0x0A000029, 0xE92D01F0, 0xE9100006 };
|
||||||
|
|
||||||
void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApListEntry* apListEntry,
|
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 romHeader = (const nds_header_ntr_t*)TWL_SHARED_MEMORY->ntrSharedMem.romHeader;
|
||||||
auto twlRomHeader = (const nds_header_twl_t*)TWL_SHARED_MEMORY->twlRomHeader;
|
auto twlRomHeader = (const nds_header_twl_t*)TWL_SHARED_MEMORY->twlRomHeader;
|
||||||
ModuleParamsLocator moduleParamsLocator;
|
|
||||||
auto moduleParams = moduleParamsLocator.FindModuleParams(romHeader);
|
|
||||||
u32 arm9Size = romHeader->arm9Size;
|
u32 arm9Size = romHeader->arm9Size;
|
||||||
u32 arm9iSize = romHeader->IsTwlRom() ? twlRomHeader->arm9iSize : 0;
|
u32 arm9iSize = romHeader->IsTwlRom() ? twlRomHeader->arm9iSize : 0;
|
||||||
SdkVersion sdkVersion = moduleParams ? moduleParams->sdkVersion : 0u;
|
|
||||||
u32 compressedEnd = 0;
|
u32 compressedEnd = 0;
|
||||||
PatchCollection patchCollection;
|
auto moduleParams = ModuleParamsLocator().FindModuleParams(romHeader);
|
||||||
|
SdkVersion sdkVersion = moduleParams ? moduleParams->sdkVersion : 0u;
|
||||||
if (moduleParams)
|
if (moduleParams)
|
||||||
{
|
{
|
||||||
LOG_DEBUG("Module params found at 0x%p\n", 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;
|
const u32* miiUncompressBackward = nullptr;
|
||||||
if (moduleParams->compressedEnd)
|
if (moduleParams->compressedEnd)
|
||||||
{
|
{
|
||||||
const u32* miiUncompressBackwardPattern;
|
miiUncompressBackward = FindMIiUncompressBackward(romHeader->arm9LoadAddress, sdkVersion);
|
||||||
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);
|
|
||||||
|
|
||||||
if (miiUncompressBackward)
|
if (miiUncompressBackward)
|
||||||
{
|
{
|
||||||
arm9Size = moduleParams->compressedEnd + *(u32*)(moduleParams->compressedEnd - 4) - romHeader->arm9LoadAddress;
|
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);
|
auto arm9iModuleParams = (module_params_twl_t*)(romHeader->arm9LoadAddress + twlRomHeader->arm9iModuleParamsAddress);
|
||||||
if (arm9iModuleParams->magicBigEndian == MODULE_PARAMS_TWL_MAGIC_BE &&
|
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");
|
arm9iSize = arm9iModuleParams->compressedEnd + *(u32*)(arm9iModuleParams->compressedEnd - 4) - twlRomHeader->arm9iLoadAddress;
|
||||||
if (miiUncompressBackward)
|
((uncompress_func_t)miiUncompressBackward)((void*)arm9iModuleParams->compressedEnd);
|
||||||
{
|
arm9iModuleParams->compressedEnd = 0;
|
||||||
arm9iSize = arm9iModuleParams->compressedEnd + *(u32*)(arm9iModuleParams->compressedEnd - 4) - twlRomHeader->arm9iLoadAddress;
|
LOG_DEBUG("Decompressed arm9i\n");
|
||||||
((uncompress_func_t)miiUncompressBackward)((void*)arm9iModuleParams->compressedEnd);
|
}
|
||||||
arm9iModuleParams->compressedEnd = 0;
|
else
|
||||||
LOG_DEBUG("Decompressed arm9i\n");
|
{
|
||||||
}
|
LOG_DEBUG("Could not decompress arm9i\n");
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG_DEBUG("Could not decompress arm9i\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,6 +118,7 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis
|
|||||||
romHeader->gameCode,
|
romHeader->gameCode,
|
||||||
loaderPlatform
|
loaderPlatform
|
||||||
};
|
};
|
||||||
|
PatchCollection patchCollection;
|
||||||
if (sdkVersion != 0)
|
if (sdkVersion != 0)
|
||||||
{
|
{
|
||||||
if (*(vu32*)0x02FFF00C == GAMECODE("ADAJ") &&
|
if (*(vu32*)0x02FFF00C == GAMECODE("ADAJ") &&
|
||||||
@@ -154,14 +147,13 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SecureSysCallsUnusedSpaceLocator secureSysCallsUnusedSpaceLocator;
|
SecureSysCallsUnusedSpaceLocator().FindUnusedSpace(romHeader, patchContext.GetPatchHeap());
|
||||||
secureSysCallsUnusedSpaceLocator.FindUnusedSpace(romHeader, patchContext.GetPatchHeap());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sdkVersion.IsTwlSdk())
|
if (sdkVersion.IsTwlSdk())
|
||||||
{
|
{
|
||||||
if (!(romHeader->IsTwlRom() && twlRomHeader->IsDsiWare()))
|
if (!twlRomHeader->IsDsiWare())
|
||||||
{
|
{
|
||||||
// if ((romHeader->unitCode & 3) != 3)
|
// if ((romHeader->unitCode & 3) != 3)
|
||||||
{
|
{
|
||||||
@@ -169,7 +161,7 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis
|
|||||||
}
|
}
|
||||||
patchCollection.AddPatch(new CardiReadRomWithCpuPatch());
|
patchCollection.AddPatch(new CardiReadRomWithCpuPatch());
|
||||||
|
|
||||||
if (gIsDsiMode && (romHeader->unitCode & 2))
|
if (gIsDsiMode && romHeader->IsTwlRom())
|
||||||
{
|
{
|
||||||
patchCollection.AddPatch(new CardiReadCardWithHashInternalAsyncPatch());
|
patchCollection.AddPatch(new CardiReadCardWithHashInternalAsyncPatch());
|
||||||
}
|
}
|
||||||
@@ -182,98 +174,8 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis
|
|||||||
}
|
}
|
||||||
|
|
||||||
patchCollection.AddPatch(new CardiReadRomIdCorePatch());
|
patchCollection.AddPatch(new CardiReadRomIdCorePatch());
|
||||||
|
|
||||||
patchCollection.AddPatch(new OSResetSystemPatch(loaderInfo));
|
patchCollection.AddPatch(new OSResetSystemPatch(loaderInfo));
|
||||||
|
AddGamePatches(patchCollection, romHeader->gameCode, apListEntry);
|
||||||
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);
|
|
||||||
|
|
||||||
if (moduleParams && compressedEnd != 0)
|
if (moduleParams && compressedEnd != 0)
|
||||||
{
|
{
|
||||||
@@ -293,6 +195,236 @@ void Arm9Patcher::ApplyPatches(const LoaderPlatform* loaderPlatform, const ApLis
|
|||||||
ic_invalidateAll();
|
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,
|
void Arm9Patcher::AddRestoreCompressedEndPatch(PatchContext& patchContext,
|
||||||
u32 arm9AutoLoadDoneHookAddress, u32* moduleParamsCompressedEnd, u32 originalCompressedEndValue) const
|
u32 arm9AutoLoadDoneHookAddress, u32* moduleParamsCompressedEnd, u32 originalCompressedEndValue) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "LoaderInfo.h"
|
#include "LoaderInfo.h"
|
||||||
|
#include "SdkVersion.h"
|
||||||
|
|
||||||
class ApListEntry;
|
class ApListEntry;
|
||||||
class LoaderPlatform;
|
class LoaderPlatform;
|
||||||
class PatchContext;
|
class PatchContext;
|
||||||
|
class PatchCollection;
|
||||||
|
class OverlayHookPatch;
|
||||||
|
|
||||||
/// @brief Class for patching the arm9 of retail roms.
|
/// @brief Class for patching the arm9 of retail roms.
|
||||||
class Arm9Patcher
|
class Arm9Patcher
|
||||||
@@ -18,6 +21,12 @@ public:
|
|||||||
bool isCloneBootRom, const loader_info_t* loaderInfo) const;
|
bool isCloneBootRom, const loader_info_t* loaderInfo) const;
|
||||||
|
|
||||||
private:
|
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,
|
void AddRestoreCompressedEndPatch(PatchContext& patchContext,
|
||||||
u32 arm9AutoLoadDoneHookAddress, u32* moduleParamsCompressedEnd, u32 originalCompressedEndValue) const;
|
u32 arm9AutoLoadDoneHookAddress, u32* moduleParamsCompressedEnd, u32 originalCompressedEndValue) const;
|
||||||
u32 GetAvailableParentSectionSpace() const;
|
u32 GetAvailableParentSectionSpace() const;
|
||||||
|
|||||||
5
arm9/source/jumpToArm9EntryPoint.h
Normal file
5
arm9/source/jumpToArm9EntryPoint.h
Normal 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);
|
||||||
29
arm9/source/jumpToArm9EntryPoint.s
Normal file
29
arm9/source/jumpToArm9EntryPoint.s
Normal 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
|
||||||
@@ -26,8 +26,7 @@
|
|||||||
#include "arm9Clock.h"
|
#include "arm9Clock.h"
|
||||||
#include "errorDisplay/ErrorDisplay.h"
|
#include "errorDisplay/ErrorDisplay.h"
|
||||||
#include "LoaderInfo.h"
|
#include "LoaderInfo.h"
|
||||||
|
#include "jumpToArm9EntryPoint.h"
|
||||||
typedef void (*entrypoint_t)(void);
|
|
||||||
|
|
||||||
#define HANDSHAKE_PART0 0xA
|
#define HANDSHAKE_PART0 0xA
|
||||||
#define HANDSHAKE_PART1 0xB
|
#define HANDSHAKE_PART1 0xB
|
||||||
@@ -109,7 +108,7 @@ static void bootArm9()
|
|||||||
while (gfx_getVCount() == 191);
|
while (gfx_getVCount() == 191);
|
||||||
REG_IF = ~0u; // final clear of REG_IF bits
|
REG_IF = ~0u; // final clear of REG_IF bits
|
||||||
auto romHeader = (const nds_header_ntr_t*)TWL_SHARED_MEMORY->ntrSharedMem.romHeader;
|
auto romHeader = (const nds_header_ntr_t*)TWL_SHARED_MEMORY->ntrSharedMem.romHeader;
|
||||||
((entrypoint_t)romHeader->arm9EntryAddress)();
|
jumpToArm9EntryPoint((void*)romHeader->arm9EntryAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleInitializeSdCardCommand()
|
static void handleInitializeSdCardCommand()
|
||||||
@@ -193,8 +192,8 @@ static void handleGetSdFunctionsCommand()
|
|||||||
{
|
{
|
||||||
auto sdReadPatchCode = sLoaderPlatform->CreateSdReadPatchCode(patchCodeCollection, patchHeap);
|
auto sdReadPatchCode = sLoaderPlatform->CreateSdReadPatchCode(patchCodeCollection, patchHeap);
|
||||||
auto sdWritePatchCode = sLoaderPlatform->CreateSdWritePatchCode(patchCodeCollection, patchHeap);
|
auto sdWritePatchCode = sLoaderPlatform->CreateSdWritePatchCode(patchCodeCollection, patchHeap);
|
||||||
*(vu32*)0x037F8000 = (u32)sdReadPatchCode->GetSdReadFunction();
|
*(vu32*)0x037F8000 = (u32)sdReadPatchCode->GetReadSectorsFunction();
|
||||||
*(vu32*)0x037F8004 = (u32)sdWritePatchCode->GetSdWriteFunction();
|
*(vu32*)0x037F8004 = (u32)sdWritePatchCode->GetWriteSectorFunction();
|
||||||
patchCodeCollection.CopyAllToTarget();
|
patchCodeCollection.CopyAllToTarget();
|
||||||
}
|
}
|
||||||
dc_flushAll();
|
dc_flushAll();
|
||||||
@@ -219,22 +218,15 @@ static void handleBootCommand()
|
|||||||
REG_EXMEMCNT &= ~0x0880; // map ds and gba slot to arm9
|
REG_EXMEMCNT &= ~0x0880; // map ds and gba slot to arm9
|
||||||
sLoaderPlatform->PrepareRomBoot(sRomDirSector, sRomDirSectorOffset);
|
sLoaderPlatform->PrepareRomBoot(sRomDirSector, sRomDirSectorOffset);
|
||||||
Arm9IoRegisterClearer().ClearNtrIoRegisters(isSdkResetSystem);
|
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;
|
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;
|
||||||
Arm9IoRegisterClearer().ClearTwlIoRegisters();
|
scfg_setArm9Clock(ScfgArm9Clock::Twl134MHz);
|
||||||
REG_SCFG_EXT = 0x8307F100;
|
REG_SCFG_CLK = 0x87;
|
||||||
scfg_setArm9Clock(ScfgArm9Clock::Twl134MHz);
|
REG_SCFG_RST = 1;
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
bootArm9();
|
bootArm9();
|
||||||
}
|
}
|
||||||
|
|||||||
13
arm9/source/patches/ISectorRemapPatchCode.h
Normal file
13
arm9/source/patches/ISectorRemapPatchCode.h
Normal 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
Reference in New Issue
Block a user