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
30 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 |
3
.github/workflows/nightly.yml
vendored
3
.github/workflows/nightly.yml
vendored
@@ -2,7 +2,6 @@ name: Build Pico Loader
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["develop"]
|
||||
paths-ignore:
|
||||
- 'README.md'
|
||||
pull_request:
|
||||
@@ -19,6 +18,7 @@ jobs:
|
||||
"ACE3DS",
|
||||
"AK2",
|
||||
"AKRPG",
|
||||
"DATEL",
|
||||
"DSPICO",
|
||||
"DSTT",
|
||||
"EZP",
|
||||
@@ -26,6 +26,7 @@ jobs:
|
||||
"M3DS",
|
||||
"R4",
|
||||
"R4iDSN",
|
||||
"STARGATE",
|
||||
"SUPERCARD"
|
||||
]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -12,6 +12,7 @@ jobs:
|
||||
"ACE3DS",
|
||||
"AK2",
|
||||
"AKRPG",
|
||||
"DATEL",
|
||||
"DSPICO",
|
||||
"DSTT",
|
||||
"EZP",
|
||||
@@ -19,6 +20,7 @@ jobs:
|
||||
"M3DS",
|
||||
"R4",
|
||||
"R4iDSN",
|
||||
"STARGATE",
|
||||
"SUPERCARD"
|
||||
]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -25,6 +25,7 @@ Note that there can be some game compatibility differences between different pla
|
||||
| ACE3DS | Ace3DS+, Gateway 3DS (blue), r4isdhc.com.cn carts, r4isdhc.hk carts 2020+, various derivatives | ✅ |
|
||||
| AK2 | Acekard 2, 2.1, 2i, r4ids.cn, various derivatives | ❌ |
|
||||
| AKRPG | Acekard RPG SD card | ❌ |
|
||||
| DATEL | DATEL devices consisting of GAMES n' MUSIC and Action Replay DS(i) Media Edition | ❌ |
|
||||
| DSPICO | DSpico | ✅ |
|
||||
| DSTT | DSTT, SuperCard DSONE SDHC, r4isdhc.com carts 2014+, r4i-sdhc.com carts, various derivatives | ❌ |
|
||||
| EZP | EZ-Flash Parallel | ❌ |
|
||||
@@ -34,6 +35,7 @@ Note that there can be some game compatibility differences between different pla
|
||||
| MELONDS | Melon DS support for testing purposes only. | ❌ |
|
||||
| R4 | Original R4DS (non-SDHC), M3 DS Simply | ❌ |
|
||||
| R4iDSN | r4idsn.com | ❌ |
|
||||
| STARGATE | Stargate 3DS DS-mode | ✅ |
|
||||
| SUPERCARD | SuperCard (Slot-2 flashcart) | ❌ |
|
||||
|
||||
The DMA column indicates whether DMA card reads are implemented for the platform . Without DMA card reads, some games can have cache related issues.<br>
|
||||
@@ -43,7 +45,7 @@ Note that there are still SDK versions and variants for which Pico Loader does n
|
||||
We recommend using WSL (Windows Subsystem for Linux), or MSYS2 to compile this repository.
|
||||
The steps provided will assume you already have one of those environments set up.
|
||||
|
||||
1. Install [BlocksDS](https://blocksds.skylyrac.net/docs/setup/options/)
|
||||
1. Install [BlocksDS](https://blocksds.skylyrac.net/docs/setup/)
|
||||
2. Install [.NET 9.0](https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu-install?tabs=dotnet9&pivots=os-linux-ubuntu-2404) for your system (note: this link points to the instructions for Ubuntu, but links for most OS'es are available on the same page)
|
||||
|
||||
## Compiling
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
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 <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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -205,7 +205,11 @@ void NdsLoader::Load(BootMode bootMode)
|
||||
{
|
||||
if (bootMode != BootMode::SdkResetSystem)
|
||||
{
|
||||
HandleCardSave();
|
||||
if (!CardSaveArranger().SetupCardSave(&_romHeader, _savePath))
|
||||
{
|
||||
ErrorDisplay().PrintError("Failed to setup save file.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HandleAntiPiracy();
|
||||
@@ -216,6 +220,7 @@ void NdsLoader::Load(BootMode bootMode)
|
||||
if (bootMode == BootMode::Normal)
|
||||
{
|
||||
bootType = _romHeader.IsDsiWare() ? BOOT_TYPE_NAND : BOOT_TYPE_CARD;
|
||||
HandleIQueRegionFreePatching();
|
||||
}
|
||||
else if (bootMode == BootMode::Multiboot)
|
||||
{
|
||||
@@ -292,6 +297,9 @@ void NdsLoader::Load(BootMode bootMode)
|
||||
{
|
||||
SetupDsiDeviceList();
|
||||
|
||||
// Set twl wram locking (REG_MBK9) settings from rom header
|
||||
REG_MBK9 = _romHeader.mbk9Setting[0] | (_romHeader.mbk9Setting[1] << 8) | (_romHeader.mbk9Setting[2] << 16);
|
||||
|
||||
u32 scfgExt7 = 0x93FBFB00 | (_romHeader.arm7ScfgExt7 & 0x40407);
|
||||
REG_SCFG_EXT = scfgExt7;
|
||||
REG_SCFG_CLK = 0x187;
|
||||
@@ -608,14 +616,6 @@ bool NdsLoader::TryLoadRomHeader(u32 romOffset)
|
||||
return true;
|
||||
}
|
||||
|
||||
void NdsLoader::HandleCardSave()
|
||||
{
|
||||
if (!CardSaveArranger().SetupCardSave(_romHeader.gameCode, _savePath))
|
||||
{
|
||||
ErrorDisplay().PrintError("Failed to setup save file.");
|
||||
}
|
||||
}
|
||||
|
||||
void NdsLoader::HandleAntiPiracy()
|
||||
{
|
||||
auto apList = ApListFactory().CreateFromFile(AP_LIST_PATH);
|
||||
@@ -865,6 +865,10 @@ void NdsLoader::StartRom(BootMode bootMode)
|
||||
|
||||
void NdsLoader::SetupTwlConfig()
|
||||
{
|
||||
ConsoleRegion romRegion = GetRomRegion(_romHeader.gameCode);
|
||||
// Set language based on rom region, TODO: allow user to override this via some config or so
|
||||
UserLanguage userLang = GetLanguageByRomRegion(romRegion);
|
||||
|
||||
twl_config_t* twlConfig = (twl_config_t*)0x02000400;
|
||||
*(twl_config_t**)0x02FFFDFC = twlConfig;
|
||||
*(vu8*)0x02FFFDFA = 0x80;
|
||||
@@ -872,7 +876,7 @@ void NdsLoader::SetupTwlConfig()
|
||||
memset(twlConfig, 0, sizeof(twl_config_t));
|
||||
twlConfig->configFlags = 0x0100000F;
|
||||
twlConfig->country = 0x4E;
|
||||
twlConfig->language = TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x64];
|
||||
twlConfig->language = static_cast<u8>(userLang);
|
||||
twlConfig->rtcYear = TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x66];
|
||||
twlConfig->rtcOffset = *(s64*)&TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x68];
|
||||
twlConfig->eulaAgreeVersion[0] = 1;
|
||||
@@ -880,7 +884,7 @@ void NdsLoader::SetupTwlConfig()
|
||||
twlConfig->alarmMinute = TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x53];
|
||||
twlConfig->alarmEnable = TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x56];
|
||||
twlConfig->systemMenuUsedTitleSlots = 9;
|
||||
twlConfig->systemMenuUsedTitleSlots = 30;
|
||||
twlConfig->systemMenuFreeTitleSlots = 30;
|
||||
twlConfig->field24 = 3;
|
||||
memcpy(&twlConfig->touchCalibrationX1Adc, &TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x58], 0xC);
|
||||
twlConfig->field3C = 0x0201209C;
|
||||
@@ -906,9 +910,10 @@ void NdsLoader::SetupTwlConfig()
|
||||
}
|
||||
*(vu16*)0x020005E2 = swi_getCrc16(0xFFFF, (void*)0x020005E4, 0xC);
|
||||
|
||||
*(vu32*)0x02FFFD68 = 0x3E; // supported languages
|
||||
// Set bitmask for supported languages
|
||||
*(vu32*)0x02FFFD68 = GetSupportedLanguagesByRegion(romRegion);
|
||||
*(vu32*)0x02FFFD6C = 0;
|
||||
*(vu8*)0x02FFFD70 = 2; // region, 2 = europe
|
||||
*(vu8*)0x02FFFD70 = static_cast<u8>(romRegion); // region
|
||||
}
|
||||
|
||||
void NdsLoader::SetDeviceListEntry(dsi_devicelist_entry_t& deviceListEntry,
|
||||
@@ -1011,3 +1016,111 @@ bool NdsLoader::TryDecryptSecureArea()
|
||||
LOG_DEBUG("Decrypted secure area\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
void NdsLoader::HandleIQueRegionFreePatching()
|
||||
{
|
||||
if ((_romHeader.flags & 0x80) == 0x80)
|
||||
{
|
||||
_romHeader.flags &= ~0x80;
|
||||
_romHeader.headerCrc = swi_getCrc16(0xFFFF, (void*)&_romHeader, 0x15E);
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleRegion NdsLoader::GetRomRegion(u32 gameCode)
|
||||
{
|
||||
u8 gameRegionCode = (gameCode >> 24) & 0xFF;
|
||||
if (gameRegionCode != 'A' && gameRegionCode != 'O')
|
||||
{
|
||||
// Determine region by TID
|
||||
if (gameRegionCode == 'J')
|
||||
{
|
||||
return ConsoleRegion::Japan;
|
||||
}
|
||||
else if (gameRegionCode == 'E' || gameRegionCode == 'T')
|
||||
{
|
||||
return ConsoleRegion::America;
|
||||
}
|
||||
else if (gameRegionCode == 'P' || gameRegionCode == 'V')
|
||||
{
|
||||
return ConsoleRegion::Europe;
|
||||
}
|
||||
else if (gameRegionCode == 'U')
|
||||
{
|
||||
return ConsoleRegion::Australia;
|
||||
}
|
||||
else if (gameRegionCode == 'C')
|
||||
{
|
||||
return ConsoleRegion::China;
|
||||
}
|
||||
else if (gameRegionCode== 'K')
|
||||
{
|
||||
return ConsoleRegion::Korea;
|
||||
}
|
||||
}
|
||||
|
||||
return ConsoleRegion::Europe; // Default to EUR
|
||||
}
|
||||
|
||||
UserLanguage NdsLoader::GetLanguageByRomRegion(ConsoleRegion romRegion)
|
||||
{
|
||||
UserLanguage userLang = static_cast<UserLanguage>(TWL_SHARED_MEMORY->ntrSharedMem.firmwareUserData[0x64] & 0x07);
|
||||
|
||||
if (romRegion == ConsoleRegion::Japan)
|
||||
{
|
||||
return UserLanguage::Japanese;
|
||||
}
|
||||
else if (romRegion == ConsoleRegion::America &&
|
||||
(userLang != UserLanguage::English ||
|
||||
userLang != UserLanguage::French ||
|
||||
userLang != UserLanguage::Spanish))
|
||||
{
|
||||
return UserLanguage::English;
|
||||
}
|
||||
else if (romRegion == ConsoleRegion::Europe && userLang < UserLanguage::English && userLang > UserLanguage::Spanish)
|
||||
{
|
||||
return UserLanguage::English;
|
||||
}
|
||||
else if (romRegion == ConsoleRegion::China)
|
||||
{
|
||||
return UserLanguage::Chinese;
|
||||
}
|
||||
else if (romRegion == ConsoleRegion::Korea)
|
||||
{
|
||||
return UserLanguage::Korean;
|
||||
}
|
||||
|
||||
return userLang;
|
||||
}
|
||||
|
||||
u32 NdsLoader::GetSupportedLanguagesByRegion(ConsoleRegion region)
|
||||
{
|
||||
switch (region)
|
||||
{
|
||||
case ConsoleRegion::Japan:
|
||||
{
|
||||
return 0x01;
|
||||
}
|
||||
case ConsoleRegion::America:
|
||||
{
|
||||
return 0x26;
|
||||
}
|
||||
case ConsoleRegion::Europe:
|
||||
{
|
||||
return 0x3E;
|
||||
}
|
||||
case ConsoleRegion::Australia:
|
||||
{
|
||||
return 0x02;
|
||||
}
|
||||
case ConsoleRegion::China:
|
||||
{
|
||||
return 0x40;
|
||||
}
|
||||
case ConsoleRegion::Korea:
|
||||
{
|
||||
return 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
return 0x3E;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
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
|
||||
};
|
||||
@@ -13,10 +13,16 @@
|
||||
#include "patches/arm9/OSResetSystemPatch.h"
|
||||
#include "patches/arm9/PokemonDownloaderArm9Patch.h"
|
||||
#include "patches/arm9/DSProtectArm9Patch.h"
|
||||
#include "patches/arm9/NandSave/FaceTrainingNandSavePatch.h"
|
||||
#include "patches/arm9/NandSave/JamWithTheBandNandSavePatch.h"
|
||||
#include "patches/arm9/NandSave/NintendoDSGuideNandSavePatch.h"
|
||||
#include "patches/arm9/NandSave/WarioWareDiyNandSavePatch.h"
|
||||
#include "patches/arm9/OverlayPatches/FsStartOverlayHookPatch.h"
|
||||
#include "patches/arm9/OverlayPatches/DSProtectPatches/DSProtectOverlayPatch.h"
|
||||
#include "patches/arm9/OverlayPatches/DSProtectPatches/DSProtectPuyoPuyo7Patch.h"
|
||||
#include "patches/arm9/OverlayPatches/PokemonIr/PokemonIrApPatch.h"
|
||||
#include "patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatch.h"
|
||||
#include "patches/arm9/OverlayPatches/RabbidsGoHome/RabbidsGoHomePatch.h"
|
||||
#include "patches/arm9/OverlayPatches/GoldenSunDarkDawn/GoldenSunDarkDawnOverlayHookPatch.h"
|
||||
#include "SecureSysCallsUnusedSpaceLocator.h"
|
||||
#include "fastSearch.h"
|
||||
@@ -374,6 +380,48 @@ void Arm9Patcher::AddGameSpecificPatches(
|
||||
overlayHookPatch->AddOverlayPatch(new PokemonIrApPatch(PokemonIrVersion::Bw2));
|
||||
break;
|
||||
}
|
||||
// WarioWare: D.I.Y.
|
||||
case GAMECODE("UORE"):
|
||||
case GAMECODE("UORP"):
|
||||
case GAMECODE("UORJ"):
|
||||
{
|
||||
patchCollection.AddPatch(new WarioWareDiyNandSavePatch());
|
||||
break;
|
||||
}
|
||||
// Jam with the Band
|
||||
case GAMECODE("UXBP"):
|
||||
{
|
||||
patchCollection.AddPatch(new JamWithTheBandNandSavePatch());
|
||||
break;
|
||||
}
|
||||
// Face Training
|
||||
case GAMECODE("USKV"):
|
||||
{
|
||||
patchCollection.AddPatch(new FaceTrainingNandSavePatch());
|
||||
break;
|
||||
}
|
||||
// Nintendo DS Guide
|
||||
case GAMECODE("UGDA"):
|
||||
{
|
||||
patchCollection.AddPatch(new NintendoDSGuideNandSavePatch());
|
||||
break;
|
||||
}
|
||||
// Rabbids Go Home
|
||||
case GAMECODE("VRGE"):
|
||||
case GAMECODE("VRGV"):
|
||||
{
|
||||
overlayHookPatch->AddOverlayPatch(new RabbidsGoHomePatch());
|
||||
break;
|
||||
}
|
||||
// Kirby Super Star Ultra
|
||||
case GAMECODE("YKWE"):
|
||||
case GAMECODE("YKWJ"):
|
||||
case GAMECODE("YKWK"):
|
||||
case GAMECODE("YKWP"):
|
||||
{
|
||||
overlayHookPatch->AddOverlayPatch(new KirbySuperStarUltraPatch());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -192,8 +192,8 @@ static void handleGetSdFunctionsCommand()
|
||||
{
|
||||
auto sdReadPatchCode = sLoaderPlatform->CreateSdReadPatchCode(patchCodeCollection, patchHeap);
|
||||
auto sdWritePatchCode = sLoaderPlatform->CreateSdWritePatchCode(patchCodeCollection, patchHeap);
|
||||
*(vu32*)0x037F8000 = (u32)sdReadPatchCode->GetSdReadFunction();
|
||||
*(vu32*)0x037F8004 = (u32)sdWritePatchCode->GetSdWriteFunction();
|
||||
*(vu32*)0x037F8000 = (u32)sdReadPatchCode->GetReadSectorsFunction();
|
||||
*(vu32*)0x037F8004 = (u32)sdWritePatchCode->GetWriteSectorFunction();
|
||||
patchCodeCollection.CopyAllToTarget();
|
||||
}
|
||||
dc_flushAll();
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -1,17 +1,17 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "SectorRemapPatchCode.h"
|
||||
#include "PatchCode.h"
|
||||
#include "ISectorRemapPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(offsettosectorremap);
|
||||
|
||||
extern "C" u32 offset_to_sector_remap(u32 romOffset);
|
||||
|
||||
class OffsetToSectorRemapPatchCode : public SectorRemapPatchCode
|
||||
class OffsetToSectorRemapPatchCode : public PatchCode, public ISectorRemapPatchCode
|
||||
{
|
||||
public:
|
||||
explicit OffsetToSectorRemapPatchCode(PatchHeap& patchHeap)
|
||||
: SectorRemapPatchCode(SECTION_START(offsettosectorremap), SECTION_SIZE(offsettosectorremap), patchHeap)
|
||||
{ }
|
||||
: PatchCode(SECTION_START(offsettosectorremap), SECTION_SIZE(offsettosectorremap), patchHeap) { }
|
||||
|
||||
const void* GetRemapFunction() const override
|
||||
{
|
||||
@@ -20,15 +20,6 @@ public:
|
||||
while (1);
|
||||
}
|
||||
|
||||
/// @brief Converts a pointer inside the original code block
|
||||
/// to a pointer at the target location in the patch heap.
|
||||
/// @param ptr The pointer to convert.
|
||||
/// @return The converted pointer.
|
||||
const void* GetAddressAtTarget(const void* ptr) const
|
||||
{
|
||||
return (const void*)((u32)ptr - (u32)_code + (u32)_targetAddress);
|
||||
}
|
||||
|
||||
/// @brief Copies the patch code to the target address.
|
||||
void CopyToTarget() const
|
||||
{
|
||||
@@ -44,4 +35,13 @@ protected:
|
||||
|
||||
/// @brief The target address for the code block in the patch heap.
|
||||
void* const _targetAddress;
|
||||
|
||||
/// @brief Converts a pointer inside the original code block
|
||||
/// to a pointer at the target location in the patch heap.
|
||||
/// @param ptr The pointer to convert.
|
||||
/// @return The converted pointer.
|
||||
const void* GetAddressAtTarget(const void* ptr) const
|
||||
{
|
||||
return (const void*)((u32)ptr - (u32)_code + (u32)_targetAddress);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SectorRemapPatchCode.h"
|
||||
#include "PatchCode.h"
|
||||
#include "ISectorRemapPatchCode.h"
|
||||
#include "fileInfo.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(saveoffsettosdsector);
|
||||
@@ -9,11 +10,11 @@ extern "C" u32 save_offset_to_sd_sector_asm(u32 saveOffset);
|
||||
|
||||
extern u32 saveoffsettosdsector_fatDataPtr;
|
||||
|
||||
class SaveOffsetToSdSectorPatchCode : public SectorRemapPatchCode
|
||||
class SaveOffsetToSdSectorPatchCode : public PatchCode, public ISectorRemapPatchCode
|
||||
{
|
||||
public:
|
||||
SaveOffsetToSdSectorPatchCode(PatchHeap& patchHeap, const save_file_info_t* fatDataPtr)
|
||||
: SectorRemapPatchCode(SECTION_START(saveoffsettosdsector), SECTION_SIZE(saveoffsettosdsector), patchHeap)
|
||||
: PatchCode(SECTION_START(saveoffsettosdsector), SECTION_SIZE(saveoffsettosdsector), patchHeap)
|
||||
{
|
||||
saveoffsettosdsector_fatDataPtr = (u32)fatDataPtr;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user