2015-08-05 03:57:37 +02:00
|
|
|
/*
|
|
|
|
* firm.c
|
2016-03-23 02:27:53 +01:00
|
|
|
* by Reisyukaku / Aurora Wright
|
|
|
|
* Copyright (c) 2016 All Rights Reserved
|
2015-08-05 03:57:37 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "firm.h"
|
2016-04-05 05:27:28 +02:00
|
|
|
#include "config.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "fs.h"
|
2015-08-05 03:57:37 +02:00
|
|
|
#include "patches.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "emunand.h"
|
2015-08-21 20:11:23 +02:00
|
|
|
#include "crypto.h"
|
2016-02-29 16:28:43 +01:00
|
|
|
#include "draw.h"
|
2016-03-23 02:27:53 +01:00
|
|
|
#include "screeninit.h"
|
2016-03-08 15:13:55 +01:00
|
|
|
#include "loader.h"
|
2016-03-23 02:27:53 +01:00
|
|
|
#include "buttons.h"
|
2016-03-23 14:59:52 +01:00
|
|
|
#include "../build/patches.h"
|
2016-03-23 02:27:53 +01:00
|
|
|
|
2016-03-27 19:19:35 +02:00
|
|
|
static firmHeader *const firm = (firmHeader *)0x24000000;
|
2016-03-10 14:58:11 +01:00
|
|
|
static const firmSectionHeader *section;
|
2016-04-02 17:58:06 +02:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
u32 config,
|
|
|
|
console,
|
2016-04-14 17:10:55 +02:00
|
|
|
firmSource,
|
|
|
|
emuOffset;
|
2016-04-11 05:15:44 +02:00
|
|
|
|
|
|
|
void main(void)
|
|
|
|
{
|
|
|
|
u32 bootType,
|
|
|
|
firmType,
|
|
|
|
nandType,
|
|
|
|
a9lhInstalled,
|
|
|
|
updatedSys,
|
|
|
|
needConfig,
|
|
|
|
newConfig,
|
|
|
|
emuHeader;
|
2016-04-02 17:58:06 +02:00
|
|
|
|
2016-02-19 21:32:07 +01:00
|
|
|
//Detect the console being used
|
2016-03-27 16:26:09 +02:00
|
|
|
console = (PDN_MPCORE_CFG == 1) ? 0 : 1;
|
2016-04-02 17:58:06 +02:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Mount filesystems. CTRNAND will be mounted only if/when needed
|
|
|
|
mountFs();
|
2016-03-23 02:27:53 +01:00
|
|
|
|
|
|
|
//Attempt to read the configuration file
|
2016-04-03 18:13:05 +02:00
|
|
|
const char configPath[] = "/aurei/config.bin";
|
2016-04-12 23:18:07 +02:00
|
|
|
if(fileRead(&config, configPath, 4)) needConfig = 1;
|
2016-04-04 01:08:42 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
config = 0;
|
|
|
|
needConfig = 2;
|
|
|
|
}
|
2016-02-25 20:19:20 +01:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Determine if this is a firmlaunch boot
|
|
|
|
if(*(vu8 *)0x23F00005)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-04-11 05:15:44 +02:00
|
|
|
bootType = 1;
|
2016-03-24 17:27:02 +01:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
|
2016-04-11 14:32:38 +02:00
|
|
|
firmType = *(vu8 *)0x23F00005 - '0';
|
2016-04-02 17:58:06 +02:00
|
|
|
|
2016-04-12 23:18:07 +02:00
|
|
|
nandType = BOOTCONFIG(0, 3);
|
|
|
|
firmSource = BOOTCONFIG(2, 1);
|
|
|
|
a9lhInstalled = BOOTCONFIG(3, 1);
|
|
|
|
updatedSys = (a9lhInstalled && CONFIG(1)) ? 1 : 0;
|
2016-04-02 17:58:06 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-04-11 05:15:44 +02:00
|
|
|
bootType = 0;
|
2016-04-11 14:32:38 +02:00
|
|
|
firmType = 0;
|
2016-02-19 21:32:07 +01:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Determine if booting with A9LH
|
|
|
|
u32 a9lhBoot = !PDN_SPI_CNT ? 1 : 0;
|
2016-03-31 18:20:36 +02:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Retrieve the last booted FIRM
|
|
|
|
u32 previousFirm = CFG_BOOTENV;
|
|
|
|
|
|
|
|
//Get pressed buttons
|
|
|
|
u32 pressed = HID_PAD;
|
|
|
|
|
|
|
|
//Determine if we need to autoboot sysNAND
|
2016-04-12 23:18:07 +02:00
|
|
|
u32 autoBootSys = CONFIG(0);
|
2016-04-11 05:15:44 +02:00
|
|
|
|
|
|
|
//Determine if A9LH is installed and the user has an updated sysNAND
|
2016-04-12 23:18:07 +02:00
|
|
|
if(a9lhBoot || CONFIG(2))
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-04-11 05:15:44 +02:00
|
|
|
if(pressed == SAFE_MODE)
|
|
|
|
error("Using Safe Mode would brick you, or remove A9LH!");
|
|
|
|
|
|
|
|
a9lhInstalled = 1;
|
2016-04-02 17:58:06 +02:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Check setting for > 9.2 sysNAND
|
2016-04-12 23:18:07 +02:00
|
|
|
updatedSys = CONFIG(1);
|
2016-04-02 17:58:06 +02:00
|
|
|
}
|
2016-04-11 05:15:44 +02:00
|
|
|
else
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-04-11 05:15:44 +02:00
|
|
|
a9lhInstalled = 0;
|
|
|
|
updatedSys = 0;
|
2016-03-05 15:35:06 +01:00
|
|
|
}
|
|
|
|
|
2016-04-12 23:18:07 +02:00
|
|
|
newConfig = a9lhInstalled << 3;
|
2016-04-11 05:15:44 +02:00
|
|
|
|
|
|
|
/* If booting with A9LH, it's a MCU reboot and a previous configuration exists,
|
|
|
|
try to force boot options */
|
|
|
|
if(a9lhBoot && previousFirm && needConfig == 1)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-04-11 05:15:44 +02:00
|
|
|
//Always force a sysNAND boot when quitting AGB_FIRM
|
|
|
|
if(previousFirm == 7)
|
|
|
|
{
|
|
|
|
nandType = 0;
|
2016-04-12 23:18:07 +02:00
|
|
|
firmSource = updatedSys ? 0 : BOOTCONFIG(0, 3);
|
2016-04-11 05:15:44 +02:00
|
|
|
needConfig = 0;
|
|
|
|
|
|
|
|
//Flag to prevent multiple boot options-forcing
|
2016-04-12 23:18:07 +02:00
|
|
|
newConfig |= 1 << 4;
|
2016-04-11 05:15:44 +02:00
|
|
|
}
|
|
|
|
/* Else, force the last used boot options unless a payload button or A/L/R are pressed
|
|
|
|
or the no-forcing flag is set */
|
2016-04-12 23:18:07 +02:00
|
|
|
else if(!(pressed & OVERRIDE_BUTTONS) && !BOOTCONFIG(4, 1))
|
2016-04-11 05:15:44 +02:00
|
|
|
{
|
2016-04-12 23:18:07 +02:00
|
|
|
nandType = BOOTCONFIG(0, 3);
|
|
|
|
firmSource = BOOTCONFIG(2, 1);
|
2016-04-11 05:15:44 +02:00
|
|
|
needConfig = 0;
|
|
|
|
}
|
2016-04-02 18:48:31 +02:00
|
|
|
}
|
2016-03-05 15:35:06 +01:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Boot options aren't being forced
|
|
|
|
if(needConfig)
|
|
|
|
{
|
|
|
|
/* If L and R/Select or one of the single payload buttons are pressed and, if not using A9LH,
|
|
|
|
the Safe Mode combo is not pressed, chainload an external payload */
|
|
|
|
if(((pressed & SINGLE_PAYLOAD_BUTTONS) || ((pressed & BUTTON_L1) && (pressed & L_PAYLOAD_BUTTONS)))
|
|
|
|
&& pressed != SAFE_MODE)
|
|
|
|
loadPayload();
|
2016-03-31 01:38:28 +02:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//If no configuration file exists or SELECT is held, load configuration menu
|
|
|
|
if(needConfig == 2 || (pressed & BUTTON_SELECT))
|
|
|
|
configureCFW(configPath);
|
2016-03-31 15:57:02 +02:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//If screens are inited or the corresponding option is set, load splash screen
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
if(PDN_GPU_CNT != 1 || CONFIG(8)) loadSplash();
|
2016-04-11 05:15:44 +02:00
|
|
|
|
|
|
|
//Determine if we need to boot an emuNAND or sysNAND
|
|
|
|
nandType = (pressed & BUTTON_L1) ? autoBootSys : ((pressed & BUTTON_R1) ? updatedSys : !autoBootSys);
|
|
|
|
|
|
|
|
/* If we're booting emuNAND the second emuNAND is set as default and B isn't pressed,
|
|
|
|
or vice-versa, boot the second emuNAND */
|
2016-04-12 23:18:07 +02:00
|
|
|
if(nandType && ((!(pressed & BUTTON_B)) == CONFIG(3))) nandType++;
|
2016-04-11 05:15:44 +02:00
|
|
|
|
|
|
|
//Determine the NAND we should take the FIRM from
|
|
|
|
firmSource = (pressed & BUTTON_R1) ? !nandType : (nandType != 0);
|
2016-03-05 15:35:06 +01:00
|
|
|
}
|
2016-04-11 05:15:44 +02:00
|
|
|
}
|
2016-04-04 18:19:00 +02:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//If we need to boot emuNAND, make sure it exists
|
|
|
|
if(nandType)
|
|
|
|
{
|
|
|
|
getEmunandSect(&emuOffset, &emuHeader, &nandType);
|
|
|
|
if(!nandType) firmSource = 0;
|
2016-03-05 15:35:06 +01:00
|
|
|
}
|
2016-03-04 23:43:22 +01:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Same if we're using emuNAND as the FIRM source
|
|
|
|
else if(firmSource)
|
|
|
|
getEmunandSect(&emuOffset, &emuHeader, &firmSource);
|
2016-03-04 23:43:22 +01:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
if(!bootType)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-04-12 23:18:07 +02:00
|
|
|
newConfig |= nandType | (firmSource << 2);
|
2016-04-02 17:58:06 +02:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
/* If the boot configuration is different from previously, overwrite it.
|
|
|
|
Just the no-forcing flag being set is not enough */
|
2016-04-13 01:08:13 +02:00
|
|
|
if((newConfig & 0x2F) != (config & 0x3F))
|
2016-04-11 05:15:44 +02:00
|
|
|
{
|
2016-04-14 17:10:55 +02:00
|
|
|
//Preserve user settings (last 26 bits)
|
2016-04-12 23:18:07 +02:00
|
|
|
newConfig |= config & 0xFFFFFFC0;
|
2016-04-11 05:15:44 +02:00
|
|
|
|
2016-04-12 23:18:07 +02:00
|
|
|
fileWrite(&newConfig, configPath, 4);
|
2016-04-11 05:15:44 +02:00
|
|
|
}
|
2016-03-31 15:57:02 +02:00
|
|
|
}
|
2016-04-11 05:15:44 +02:00
|
|
|
|
2016-04-12 14:32:38 +02:00
|
|
|
loadFirm(firmType, !firmType && (nandType == 2 || updatedSys == !firmSource));
|
2016-04-11 05:15:44 +02:00
|
|
|
|
2016-04-11 14:32:38 +02:00
|
|
|
if(!firmType) patchNativeFirm(nandType, emuHeader, a9lhInstalled);
|
2016-04-11 05:15:44 +02:00
|
|
|
else patchTwlAgbFirm(firmType);
|
|
|
|
|
|
|
|
launchFirm(bootType);
|
2016-02-25 20:19:20 +01:00
|
|
|
}
|
|
|
|
|
2016-03-21 18:56:41 +01:00
|
|
|
//Load FIRM into FCRAM
|
2016-04-11 05:15:44 +02:00
|
|
|
static inline void loadFirm(u32 firmType, u32 externalFirm)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-04-11 05:15:44 +02:00
|
|
|
section = firm->section;
|
2016-04-04 18:19:00 +02:00
|
|
|
|
2016-04-11 14:32:38 +02:00
|
|
|
u32 firmSize;
|
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
if(externalFirm)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-04-11 05:15:44 +02:00
|
|
|
const char *path = "/aurei/firmware.bin";
|
2016-03-04 23:43:22 +01:00
|
|
|
firmSize = fileSize(path);
|
2016-02-25 20:19:20 +01:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
if(firmSize)
|
|
|
|
{
|
|
|
|
fileRead(firm, path, firmSize);
|
2016-02-08 03:37:03 +01:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Check that the loaded FIRM matches the console
|
|
|
|
if((((u32)section[2].address >> 8) & 0xFF) != (console ? 0x60 : 0x68))
|
|
|
|
error("aurei/firmware.bin doesn't match this console,\nor it's encrypted");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else firmSize = 0;
|
2016-03-21 18:56:41 +01:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
if(!firmSize)
|
2016-04-04 18:07:13 +02:00
|
|
|
{
|
2016-04-14 17:10:55 +02:00
|
|
|
const char *firmFolders[3][2] = {{ "00000002", "20000002" },
|
|
|
|
{ "00000102", "20000102" },
|
|
|
|
{ "00000202", "20000202" }};
|
|
|
|
|
2016-04-11 14:32:38 +02:00
|
|
|
firmRead(firm, firmFolders[firmType][console]);
|
2016-04-11 05:15:44 +02:00
|
|
|
decryptExeFs((u8 *)firm);
|
2016-04-04 18:07:13 +02:00
|
|
|
}
|
2015-08-05 11:54:00 +02:00
|
|
|
}
|
|
|
|
|
2016-04-11 14:32:38 +02:00
|
|
|
static inline void patchNativeFirm(u32 nandType, u32 emuHeader, u32 a9lhInstalled)
|
2016-04-03 17:56:09 +02:00
|
|
|
{
|
2016-04-11 05:15:44 +02:00
|
|
|
u8 *arm9Section = (u8 *)firm + section[2].offset;
|
2016-04-03 17:56:09 +02:00
|
|
|
|
2016-04-11 14:32:38 +02:00
|
|
|
u32 nativeFirmType;
|
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
if(console)
|
|
|
|
{
|
|
|
|
//Determine if we're booting the 9.0 FIRM
|
2016-04-11 14:32:38 +02:00
|
|
|
nativeFirmType = (arm9Section[0x51] == 0xFF) ? 0 : 1;
|
2016-04-03 17:56:09 +02:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader
|
2016-04-12 14:32:38 +02:00
|
|
|
arm9Loader(arm9Section, nativeFirmType);
|
2016-04-11 05:15:44 +02:00
|
|
|
firm->arm9Entry = (u8 *)0x801B01C;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Determine if we're booting the 9.0 FIRM
|
|
|
|
u8 firm90Hash[0x10] = {0x27, 0x2D, 0xFE, 0xEB, 0xAF, 0x3F, 0x6B, 0x3B, 0xF5, 0xDE, 0x4C, 0x41, 0xDE, 0x95, 0x27, 0x6A};
|
2016-04-11 14:32:38 +02:00
|
|
|
nativeFirmType = (memcmp(section[2].hash, firm90Hash, 0x10) == 0) ? 0 : 1;
|
2016-04-11 05:15:44 +02:00
|
|
|
}
|
2016-04-03 17:56:09 +02:00
|
|
|
|
2016-04-11 14:32:38 +02:00
|
|
|
if(nativeFirmType || nandType)
|
2016-04-11 05:15:44 +02:00
|
|
|
{
|
|
|
|
//Find the Process9 NCCH location
|
|
|
|
u8 *proc9Offset = getProc9(arm9Section, section[2].size);
|
2016-04-03 17:56:09 +02:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Apply emuNAND patches
|
|
|
|
if(nandType) patchEmuNAND(arm9Section, proc9Offset, emuHeader);
|
2016-04-03 17:56:09 +02:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Apply FIRM reboot patches, not on 9.0 FIRM as it breaks firmlaunchhax
|
2016-04-11 14:32:38 +02:00
|
|
|
if(nativeFirmType) patchReboots(arm9Section, proc9Offset);
|
2016-04-11 05:15:44 +02:00
|
|
|
}
|
2016-04-03 17:56:09 +02:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Apply FIRM0/1 writes patches on sysNAND to protect A9LH
|
|
|
|
if(a9lhInstalled && !nandType)
|
2016-04-04 18:07:13 +02:00
|
|
|
{
|
2016-04-11 05:15:44 +02:00
|
|
|
u16 *writeOffset = getFirmWrite(arm9Section, section[2].size);
|
|
|
|
*writeOffset = writeBlock[0];
|
|
|
|
*(writeOffset + 1) = writeBlock[1];
|
2016-04-04 18:07:13 +02:00
|
|
|
}
|
2016-04-03 17:56:09 +02:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Apply signature checks patches
|
|
|
|
u32 sigOffset,
|
|
|
|
sigOffset2;
|
2016-04-03 17:56:09 +02:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
getSigChecks(arm9Section, section[2].size, &sigOffset, &sigOffset2);
|
|
|
|
*(u16 *)sigOffset = sigPatch[0];
|
|
|
|
*(u16 *)sigOffset2 = sigPatch[0];
|
|
|
|
*((u16 *)sigOffset2 + 1) = sigPatch[1];
|
2016-04-03 17:56:09 +02:00
|
|
|
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
if(CONFIG(5))
|
2016-04-11 21:14:51 +02:00
|
|
|
{
|
|
|
|
//Apply UNITINFO patch
|
2016-04-12 14:32:38 +02:00
|
|
|
u8 *unitInfoOffset = getUnitInfoValueSet(arm9Section, section[2].size);
|
|
|
|
*unitInfoOffset = unitInfoPatch;
|
2016-04-11 21:14:51 +02:00
|
|
|
}
|
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Replace the FIRM loader with the injector
|
|
|
|
injectLoader();
|
2016-04-03 17:56:09 +02:00
|
|
|
}
|
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
static inline void patchEmuNAND(u8 *arm9Section, u8 *proc9Offset, u32 emuHeader)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-04-11 22:16:51 +02:00
|
|
|
//Copy emuNAND code
|
2016-03-29 17:43:53 +02:00
|
|
|
void *emuCodeOffset = getEmuCode(proc9Offset);
|
2016-03-23 15:48:59 +01:00
|
|
|
memcpy(emuCodeOffset, emunand, emunand_size);
|
|
|
|
|
2016-04-11 22:16:51 +02:00
|
|
|
//Add the data of the found emuNAND
|
2016-03-23 14:59:52 +01:00
|
|
|
u32 *pos_offset = (u32 *)memsearch(emuCodeOffset, "NAND", emunand_size, 4);
|
|
|
|
u32 *pos_header = (u32 *)memsearch(emuCodeOffset, "NCSD", emunand_size, 4);
|
2016-02-08 03:37:03 +01:00
|
|
|
*pos_offset = emuOffset;
|
|
|
|
*pos_header = emuHeader;
|
2016-02-25 20:19:20 +01:00
|
|
|
|
2016-03-23 15:48:59 +01:00
|
|
|
//Find and add the SDMMC struct
|
2016-03-23 14:59:52 +01:00
|
|
|
u32 *pos_sdmmc = (u32 *)memsearch(emuCodeOffset, "SDMC", emunand_size, 4);
|
2016-03-21 18:56:41 +01:00
|
|
|
*pos_sdmmc = getSDMMC(arm9Section, section[2].size);
|
|
|
|
|
2016-03-21 03:20:15 +01:00
|
|
|
//Calculate offset for the hooks
|
2016-03-27 19:19:35 +02:00
|
|
|
u32 branchOffset = (u32)emuCodeOffset - (u32)firm -
|
2016-03-26 17:24:16 +01:00
|
|
|
section[2].offset + (u32)section[2].address;
|
2016-02-08 03:37:03 +01:00
|
|
|
|
2016-04-11 22:16:51 +02:00
|
|
|
//Add emuNAND hooks
|
2016-03-31 15:57:02 +02:00
|
|
|
u32 emuRead,
|
|
|
|
emuWrite;
|
|
|
|
|
2016-03-21 18:56:41 +01:00
|
|
|
getEmuRW(arm9Section, section[2].size, &emuRead, &emuWrite);
|
2016-03-26 17:24:16 +01:00
|
|
|
*(u16 *)emuRead = nandRedir[0];
|
|
|
|
*((u16 *)emuRead + 1) = nandRedir[1];
|
|
|
|
*((u32 *)emuRead + 1) = branchOffset;
|
|
|
|
*(u16 *)emuWrite = nandRedir[0];
|
|
|
|
*((u16 *)emuWrite + 1) = nandRedir[1];
|
|
|
|
*((u32 *)emuWrite + 1) = branchOffset;
|
2016-02-08 03:37:03 +01:00
|
|
|
|
2016-02-06 00:54:24 +01:00
|
|
|
//Set MPU for emu code region
|
2016-03-26 17:24:16 +01:00
|
|
|
u32 *mpuOffset = getMPU(arm9Section, section[2].size);
|
|
|
|
*mpuOffset = mpuPatch[0];
|
|
|
|
*(mpuOffset + 6) = mpuPatch[1];
|
|
|
|
*(mpuOffset + 9) = mpuPatch[2];
|
2015-08-07 09:01:42 +02:00
|
|
|
}
|
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
static inline void patchReboots(u8 *arm9Section, u8 *proc9Offset)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
|
|
|
//Calculate offset for the firmlaunch code
|
|
|
|
void *rebootOffset = getReboot(arm9Section, section[2].size);
|
|
|
|
|
|
|
|
//Calculate offset for the fOpen function
|
|
|
|
u32 fOpenOffset = getfOpen(proc9Offset, rebootOffset);
|
|
|
|
|
|
|
|
//Copy firmlaunch code
|
|
|
|
memcpy(rebootOffset, reboot, reboot_size);
|
|
|
|
|
|
|
|
//Put the fOpen offset in the right location
|
|
|
|
u32 *pos_fopen = (u32 *)memsearch(rebootOffset, "OPEN", reboot_size, 4);
|
|
|
|
*pos_fopen = fOpenOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void injectLoader(void)
|
|
|
|
{
|
2016-04-13 14:23:08 +02:00
|
|
|
u32 loaderSize;
|
2016-04-02 17:58:06 +02:00
|
|
|
|
2016-04-13 14:23:08 +02:00
|
|
|
void *loaderOffset = getLoader((u8 *)firm + section[0].offset, section[0].size, &loaderSize);
|
2016-04-02 17:58:06 +02:00
|
|
|
|
|
|
|
//Check that the injector CXI isn't larger than the original
|
2016-04-13 14:23:08 +02:00
|
|
|
if((u32)injector_size <= loaderSize)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-04-13 14:23:08 +02:00
|
|
|
memcpy(loaderOffset, injector, injector_size);
|
2016-04-02 17:58:06 +02:00
|
|
|
|
|
|
|
//Patch content size and ExeFS size to match the repaced loader's ones
|
|
|
|
*((u32 *)loaderOffset + 0x41) = loaderSize / 0x200;
|
|
|
|
*((u32 *)loaderOffset + 0x69) = loaderSize / 0x200 - 5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
static inline void patchTwlAgbFirm(u32 firmType)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-04-11 05:15:44 +02:00
|
|
|
//On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader
|
|
|
|
if(console)
|
2016-04-04 18:07:13 +02:00
|
|
|
{
|
2016-04-11 05:15:44 +02:00
|
|
|
arm9Loader((u8 *)firm + section[3].offset, 0);
|
|
|
|
firm->arm9Entry = (u8 *)0x801301C;
|
2016-04-04 18:07:13 +02:00
|
|
|
}
|
2016-04-03 17:56:09 +02:00
|
|
|
|
2016-04-14 17:10:55 +02:00
|
|
|
const patchData twlPatches[] = {
|
2016-04-11 05:15:44 +02:00
|
|
|
{{0x1650C0, 0x165D64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
|
|
|
|
{{0x173A0E, 0x17474A}, { .type1 = 0x2001 }, 1},
|
|
|
|
{{0x174802, 0x17553E}, { .type1 = 0x2000 }, 2},
|
|
|
|
{{0x174964, 0x1756A0}, { .type1 = 0x2000 }, 2},
|
|
|
|
{{0x174D52, 0x175A8E}, { .type1 = 0x2001 }, 2},
|
|
|
|
{{0x174D5E, 0x175A9A}, { .type1 = 0x2001 }, 2},
|
|
|
|
{{0x174D6A, 0x175AA6}, { .type1 = 0x2001 }, 2},
|
|
|
|
{{0x174E56, 0x175B92}, { .type1 = 0x2001 }, 1},
|
|
|
|
{{0x174E58, 0x175B94}, { .type1 = 0x4770 }, 1}
|
|
|
|
},
|
|
|
|
agbPatches[] = {
|
|
|
|
{{0x9D2A8, 0x9DF64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
|
|
|
|
{{0xD7A12, 0xD8B8A}, { .type1 = 0xEF26 }, 1}
|
|
|
|
};
|
2016-04-02 17:58:06 +02:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
/* Calculate the amount of patches to apply. Only count the boot screen patch for AGB_FIRM
|
|
|
|
if the matching option was enabled (keep it as last) */
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
u32 numPatches = firmType == 1 ? (sizeof(twlPatches) / sizeof(patchData)) : (sizeof(agbPatches) / sizeof(patchData) - !CONFIG(7));
|
2016-04-11 18:34:37 +02:00
|
|
|
const patchData *patches = firmType == 1 ? twlPatches : agbPatches;
|
2016-03-21 18:56:41 +01:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Patch
|
|
|
|
for(u32 i = 0; i < numPatches; i++)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-04-11 05:15:44 +02:00
|
|
|
switch(patches[i].type)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
memcpy((u8 *)firm + patches[i].offset[console], patches[i].patch.type0 + 1, patches[i].patch.type0[0]);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
*(u16 *)((u8 *)firm + patches[i].offset[console] + 2) = 0;
|
|
|
|
case 1:
|
|
|
|
*(u16 *)((u8 *)firm + patches[i].offset[console]) = patches[i].patch.type1;
|
|
|
|
break;
|
|
|
|
}
|
2016-02-19 21:32:07 +01:00
|
|
|
}
|
2015-08-05 03:57:37 +02:00
|
|
|
}
|
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
static inline void launchFirm(u32 bootType)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2015-08-05 03:57:37 +02:00
|
|
|
//Copy firm partitions to respective memory locations
|
2016-04-11 05:15:44 +02:00
|
|
|
for(u32 i = 0; i < 4 && section[i].size; i++)
|
|
|
|
memcpy(section[i].address, (u8 *)firm + section[i].offset, section[i].size);
|
2016-02-29 16:28:43 +01:00
|
|
|
|
2016-04-11 05:15:44 +02:00
|
|
|
//Determine the ARM11 entry to use
|
|
|
|
vu32 *arm11;
|
|
|
|
if(bootType) arm11 = (u32 *)0x1FFFFFFC;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
deinitScreens();
|
|
|
|
arm11 = (u32 *)0x1FFFFFF8;
|
|
|
|
}
|
2016-03-11 15:08:05 +01:00
|
|
|
|
2016-03-23 02:27:53 +01:00
|
|
|
//Set ARM11 kernel entrypoint
|
2016-04-11 05:15:44 +02:00
|
|
|
*arm11 = (u32)firm->arm11Entry;
|
2016-03-11 15:08:05 +01:00
|
|
|
|
2016-03-23 02:27:53 +01:00
|
|
|
//Final jump to arm9 kernel
|
2016-03-27 19:19:35 +02:00
|
|
|
((void (*)())firm->arm9Entry)();
|
2016-04-11 22:16:51 +02:00
|
|
|
}
|