mirror of
https://github.com/rvtr/unlaunch-installer_dev.git
synced 2026-01-26 13:43:08 -05:00
Add binary patch to make unalunch work properly with HNAA tmd
This commit is contained in:
parent
28dee555e1
commit
c4f861b4aa
99
README.md
99
README.md
@ -48,6 +48,105 @@ protection since it won't interfere with the system. As a bonus, if you
|
||||
sell/trade you console in the future and the new owner uses the official
|
||||
installer, they'll be protected from bricks.
|
||||
|
||||
## Patches applied to unlaunch
|
||||
The installer ships with 2 binary patches, one is an "ahestetic" one to enable
|
||||
the dsi H&S screen and sound.
|
||||
The other is a mandatory one, required to make unlaunch properly use the tmd that
|
||||
was installed in the HNAA folder, otherwise it would attempt to save its settings
|
||||
to random fat blocks, since it assumes that unlaunch is installed in the blocks right
|
||||
after the title.tmd associated with the launcher read from HWINFO_S.
|
||||
More specifically, the sound and splash patch modifies the arm7 instruction of unlaunch at
|
||||
address 0x1308 (in the then relocated code it is run at 0x23fe038). The patched instruction
|
||||
is a `bl` to the function patching a second binary blob of the launcher, and is replaced with
|
||||
a nop.
|
||||
The other patch, modifies the code responsible for reading the launcher title id from HWINFO.
|
||||
The original code is as follow
|
||||
```
|
||||
@ this loads in r0 the buffer containing the contents read
|
||||
@ from HWINFO_S, offsetted at the position of the title id
|
||||
FC 05 9F E5 ldr r0, [pc, #0x5fc]
|
||||
FC 15 9F E5 ldr r1, [pc, #0x5fc]
|
||||
@ This reads the title id and then stores it to the address
|
||||
@ pointed by r1, which is then used across the whole program
|
||||
00 00 90 E5 ldr r0, [r0]
|
||||
00 00 81 E5 str r0, [r1]
|
||||
F4 05 9F E5 ldr r0, [pc, #0x5f4]
|
||||
9d 01 00 EB bl #0x602a3c4
|
||||
C5 FD FF EB bl #0x602946c
|
||||
EC 05 9F E5 ldr r0, [pc, #0x5ec]
|
||||
00 00 90 E5 ldr r0, [r0]
|
||||
E8 15 9F E5 ldr r1, [pc, #0x5e8]
|
||||
66 00 00 EB bl #0x6029f00
|
||||
E4 05 9F E5 ldr r0, [pc, #0x5e4]
|
||||
E4 15 9F E5 ldr r1, [pc, #0x5e4]
|
||||
14 20 A0 E3 mov r2, #0x14
|
||||
0C 09 00 EB bl #0x602c1a8
|
||||
DC 05 9F E5 ldr r0, [pc, #0x5dc]
|
||||
91 01 00 EB bl #0x602a3c4
|
||||
BA FD FF EB bl #0x602946c
|
||||
D4 05 9F E5 ldr r0, [pc, #0x5d4]
|
||||
00 00 90 E5 ldr r0, [r0]
|
||||
D0 15 9F E5 ldr r1, [pc, #0x5d0]
|
||||
02 2C A0 E3 mov r2, #0x200
|
||||
5B 00 00 EB bl #0x6029f04
|
||||
C8 15 9F E5 ldr r1, [pc, #0x5c8]
|
||||
E4 01 91 E5 ldr r0, [r1, #0x1e4]
|
||||
72 08 00 EB bl #0x602bf6c
|
||||
C0 15 9F E5 ldr r1, [pc, #0x5c0]
|
||||
19 00 00 EB bl #0x6029e10
|
||||
00 00 A0 E3 mov r0, #0
|
||||
01 00 C1 E4 strb r0, [r1], #1
|
||||
B4 05 9F E5 ldr r0, [pc, #0x5b4]
|
||||
93 01 D0 E5 ldrb r0, [r0, #0x193]
|
||||
01 00 C1 E4 strb r0, [r1], #1
|
||||
FF 9F BD E8 pop {r0, r1, r2, r3, r4, r5, r6, r7, r8, sb, sl, fp, ip, pc}
|
||||
```
|
||||
The patched code is
|
||||
```
|
||||
@ Instead of reading the title id from the HWINFO_S buffer,
|
||||
@ read the hardcoded value placed at the bottom.
|
||||
@ all the relative addresses have been incremented by 4 to account
|
||||
@ for the shift of the instructions (same for the relative bl instructions)
|
||||
7C 00 9F E5 ldr r0, [pc, #0x7c]
|
||||
FC 15 9F E5 ldr r1, [pc, #0x5fc]
|
||||
00 00 81 E5 str r0, [r1]
|
||||
F8 05 9F E5 ldr r0, [pc, #0x5f8]
|
||||
9D 01 00 EB bl #0x602a3c4
|
||||
C6 FD FF EB bl #0x602946c
|
||||
F0 05 9F E5 ldr r0, [pc, #0x5f0]
|
||||
00 00 90 E5 ldr r0, [r0]
|
||||
EC 15 9F E5 ldr r1, [pc, #0x5ec]
|
||||
67 00 00 EB bl #0x6029f00
|
||||
E8 05 9F E5 ldr r0, [pc, #0x5e8]
|
||||
E8 15 9F E5 ldr r1, [pc, #0x5e8]
|
||||
14 20 A0 E3 mov r2, #0x14
|
||||
0D 09 00 EB bl #0x602c1a8
|
||||
E0 05 9F E5 ldr r0, [pc, #0x5e0]
|
||||
92 01 00 EB bl #0x602a3c4
|
||||
BB FD FF EB bl #0x602946c
|
||||
D8 05 9F E5 ldr r0, [pc, #0x5d8]
|
||||
00 00 90 E5 ldr r0, [r0]
|
||||
D4 15 9F E5 ldr r1, [pc, #0x5d4]
|
||||
02 2C A0 E3 mov r2, #0x200
|
||||
5C 00 00 EB bl #0x6029f04
|
||||
CC 15 9F E5 ldr r1, [pc, #0x5cc]
|
||||
E8 01 91 E5 ldr r0, [r1, #0x1e8]
|
||||
73 08 00 EB bl #0x602bf6c
|
||||
C4 15 9F E5 ldr r1, [pc, #0x5c4]
|
||||
1A 00 00 EB bl #0x6029e10
|
||||
00 00 A0 E3 mov r0, #0
|
||||
01 00 C1 E4 strb r0, [r1], #1
|
||||
B8 05 9F E5 ldr r0, [pc, #0x5b8]
|
||||
97 01 D0 E5 ldrb r0, [r0, #0x197]
|
||||
01 00 C1 E4 strb r0, [r1], #1
|
||||
FF 9F BD E8 pop {r0, r1, r2, r3, r4, r5, r6, r7, r8, sb, sl, fp, ip, pc}
|
||||
41 41 4E 48 .word 0x484e4141
|
||||
```
|
||||
The patched code is found at address `0x60a7` in the decompressed binary, and is loaded into
|
||||
ram at address `0x6029d38`.
|
||||
|
||||
The lzss compressed arm7 payload is found at offset `0x8580` for unlaunch 2.0 with a length of `0x67FD`.
|
||||
|
||||
## Credits
|
||||
- [DevkitPro](https://devkitpro.org/): devkitARM and libnds
|
||||
- [Martin Korth (nocash)](https://problemkaputt.de):
|
||||
|
||||
@ -90,7 +90,7 @@ static void setupScreens()
|
||||
|
||||
static int mainMenu(const consoleInfo& info, int cursor)
|
||||
{
|
||||
const auto tidPatchesSupported = (foundUnlaunchInstallerVersion == v1_9 || foundUnlaunchInstallerVersion == v2_0) && isLauncherVersionSupported;
|
||||
const auto tidPatchesSupported = (foundUnlaunchInstallerVersion == v2_0) && isLauncherVersionSupported;
|
||||
//top screen
|
||||
clearScreen(&topScreen);
|
||||
|
||||
@ -352,12 +352,13 @@ void loadUnlaunchInstaller() {
|
||||
}
|
||||
|
||||
void loadUnlaunchInstallerPatch() {
|
||||
if (fileExists("sd:/unlaunch-patch.bin")) {
|
||||
splashSoundBinaryPatchPath = "sd:/unlaunch-patch.bin";
|
||||
if (fileExists("sd:/sound-and-splash-patch.bin")) {
|
||||
splashSoundBinaryPatchPath = "sd:/sound-and-splash-patch.bin";
|
||||
} else if(fileExists("nitro:/sound-and-splash-patch.bin")) {
|
||||
splashSoundBinaryPatchPath = "nitro:/sound-and-splash-patch.bin";
|
||||
}
|
||||
else if(fileExists("nitro:/unlaunch-patch.bin"))
|
||||
{
|
||||
splashSoundBinaryPatchPath = "nitro:/unlaunch-patch.bin";
|
||||
if(!fileExists("nitro:/force-hnaa-patch.bin")) {
|
||||
throw std::runtime_error(std::format("Failed to find hnaa patch ({})", "nitro:/force-hnaa-patch.bin"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -657,7 +658,7 @@ void doMainMenu(consoleInfo& info) {
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(advancedOptionsUnlocked && (foundUnlaunchInstallerVersion == v1_9 || foundUnlaunchInstallerVersion == v2_0)) {
|
||||
if(advancedOptionsUnlocked && (foundUnlaunchInstallerVersion == v2_0)) {
|
||||
disableAllPatches = !disableAllPatches;
|
||||
}
|
||||
break;
|
||||
@ -700,11 +701,12 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
loadUnlaunchInstaller();
|
||||
|
||||
try {
|
||||
loadUnlaunchInstallerPatch();
|
||||
|
||||
consoleInfo info;
|
||||
|
||||
try {
|
||||
retrieveInstalledLauncherInfo(info);
|
||||
|
||||
checkNocashFooter(info);
|
||||
|
||||
@ -30,19 +30,19 @@ constexpr std::array knownUnlaunchHashes{
|
||||
/*"0525b28cc59b6f7fc00ad592aebadd7257bf7efb"_sha1, // v1.5: blacklisted, doesn't like this install method*/
|
||||
/*"9470a51fde188235052b119f6bfabf6689cb2343"_sha1, // v1.6: blacklisted, doesn't like this install method*/
|
||||
/*"672c11eb535b97b0d32ff580d314a2ad6411d5fe"_sha1, // v1.7: blacklisted, doesn't like this install method*/
|
||||
"b76c2b1722e769c6c0b4b3d4bc73250e41993229"_sha1, // v1.8
|
||||
"f3eb41cba136a3477523155f8b05df14917c55f4"_sha1, // v1.9
|
||||
/*"b76c2b1722e769c6c0b4b3d4bc73250e41993229"_sha1, // v1.8: blacklisted, the HNAA patch is only done for 2.0 */
|
||||
/*"f3eb41cba136a3477523155f8b05df14917c55f4"_sha1, // v1.9: blacklisted, the HNAA patch is only done for 2.0 */
|
||||
"15f4a36251d1408d71114019b2825fe8f5b4c8cc"_sha1, // v2.0
|
||||
};
|
||||
|
||||
constexpr std::array gifOffsets{
|
||||
std::make_pair(0x48d4, 0x8540), /* 1.8 */
|
||||
std::make_pair(0x48c8, 0x8534), /* 1.9 */
|
||||
/* std::make_pair(0x48d4, 0x8540),*/ /* 1.8 */
|
||||
/* std::make_pair(0x48c8, 0x8534),*/ /* 1.9 */
|
||||
std::make_pair(0x48f0, 0x855c), /* 2.0 */
|
||||
};
|
||||
|
||||
constexpr std::array blockAllPatchesOffset{
|
||||
0xae74, /* 1.9 */
|
||||
/* 0xae74, */ /* 1.9 */
|
||||
0xae91, /* 2.0 */
|
||||
};
|
||||
|
||||
@ -476,54 +476,55 @@ static bool patchCustomBackground(const char* customBackgroundPath)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool patchUnlaunchInstaller(bool disableAllPatches, const char* splashSoundBinaryPatchPath, const char* customBackgroundPath)
|
||||
static bool applyBinaryPatch(const char* path)
|
||||
{
|
||||
tonccpy(unlaunchInstallerBuffer, ogUnlaunchInstallerBuffer, sizeof(unlaunchInstallerBuffer));
|
||||
if(disableAllPatches)
|
||||
{
|
||||
if(installerVersion == v1_8)
|
||||
{
|
||||
messageBox("\x1B[31mError:\x1B[33m Unlaunch 1.8 can't be patched\n");
|
||||
return false;
|
||||
}
|
||||
// change launcher TID from ANH to SAN so that unlaunch doesn't realize it's booting the launcher
|
||||
auto patchOffset = blockAllPatchesOffset[installerVersion - 1];
|
||||
const char newID[]{'S','A','N'};
|
||||
memcpy((unlaunchInstallerBuffer + 520) + patchOffset, newID, 3);
|
||||
}
|
||||
else if (splashSoundBinaryPatchPath)
|
||||
{
|
||||
if(installerVersion != v2_0)
|
||||
{
|
||||
messageBox("\x1B[31mError:\x1B[33m The splash and sound patch is\n"
|
||||
"only for unlaunch 2.0\n");
|
||||
return false;
|
||||
}
|
||||
static constexpr auto patchOffset = 0x8580;
|
||||
static constexpr auto patchSectionSize = 0x67FD;
|
||||
auto* patch = fopen(splashSoundBinaryPatchPath, "rb");
|
||||
static constexpr auto lzssCompressedBinaryOffset = 0x8580;
|
||||
static constexpr auto lzssCompressedBinarySize = 0x67FD;
|
||||
auto* patch = fopen(path, "rb");
|
||||
if(!patch)
|
||||
{
|
||||
messageBox("\x1B[31mError:\x1B[33m Failed to open the splash and\n"
|
||||
"sound patch is.\n");
|
||||
messageBox("\x1B[31mError:\x1B[33m Failed to open the patch.\n");
|
||||
return false;
|
||||
}
|
||||
auto patchSize = getFileSize(patch);
|
||||
if(patchSize > patchSectionSize)
|
||||
if(patchSize > lzssCompressedBinarySize)
|
||||
{
|
||||
messageBox("\x1B[31mError:\x1B[33m Splash and sound patch is too\n"
|
||||
"big.\n");
|
||||
messageBox("\x1B[31mError:\x1B[33m Patch is too big.\n");
|
||||
fclose(patch);
|
||||
return false;
|
||||
}
|
||||
if (fread((unlaunchInstallerBuffer + 520) + patchOffset, 1, patchSize, patch) != patchSize)
|
||||
if (fread((unlaunchInstallerBuffer + 520) + lzssCompressedBinaryOffset, 1, patchSize, patch) != patchSize)
|
||||
{
|
||||
messageBox("\x1B[31mError:\x1B[33m Failed to read splash and sound\n"
|
||||
"patch.\n");
|
||||
messageBox("\x1B[31mError:\x1B[33m Failed to read patch.\n");
|
||||
fclose(patch);
|
||||
return false;
|
||||
}
|
||||
fclose(patch);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool patchUnlaunchInstaller(bool disableAllPatches, const char* splashSoundBinaryPatchPath, const char* customBackgroundPath)
|
||||
{
|
||||
tonccpy(unlaunchInstallerBuffer, ogUnlaunchInstallerBuffer, sizeof(unlaunchInstallerBuffer));
|
||||
if (splashSoundBinaryPatchPath)
|
||||
{
|
||||
iprintf("Applying splash and sound patch\n");
|
||||
if(!applyBinaryPatch(splashSoundBinaryPatchPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if(disableAllPatches) {
|
||||
// change launcher TID from ANH to SAN so that unlaunch doesn't realize it's booting the launcher
|
||||
auto patchOffset = blockAllPatchesOffset[installerVersion];
|
||||
const char newID[]{'S','A','N'};
|
||||
memcpy((unlaunchInstallerBuffer + 520) + patchOffset, newID, 3);
|
||||
}
|
||||
iprintf("Applying HNAA patch\n");
|
||||
if(!applyBinaryPatch("nitro:/force-hnaa-patch.bin"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(!patchCustomBackground(customBackgroundPath))
|
||||
{
|
||||
@ -543,8 +544,6 @@ UNLAUNCH_VERSION loadUnlaunchInstaller(std::string_view path)
|
||||
}
|
||||
|
||||
std::array unlaunchVersionStrings{
|
||||
"v1.8",
|
||||
"v1.9",
|
||||
"v2.0",
|
||||
"INVALID",
|
||||
};
|
||||
|
||||
@ -5,8 +5,6 @@
|
||||
#include "consoleInfo.h"
|
||||
|
||||
typedef enum UNLAUNCH_VERSION {
|
||||
v1_8,
|
||||
v1_9,
|
||||
v2_0,
|
||||
INVALID,
|
||||
} UNLAUNCH_VERSION;
|
||||
|
||||
BIN
nitrofiles/force-hnaa-patch.bin
Normal file
BIN
nitrofiles/force-hnaa-patch.bin
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user