mirror of
https://github.com/rvtr/unlaunch-installer_dev.git
synced 2026-01-26 13:43:08 -05:00
Supports patching unlaunch with custom backgrounds
This commit is contained in:
parent
80866c08ea
commit
94cb35df6f
75
arm9/src/bgMenu.cpp
Normal file
75
arm9/src/bgMenu.cpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#include "bgMenu.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "menu.h"
|
||||||
|
|
||||||
|
#include <nds.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
static const auto& getBackgroundList()
|
||||||
|
{
|
||||||
|
static auto bgs = []{
|
||||||
|
std::vector<std::pair<std::string,std::string>> bgs;
|
||||||
|
static const std::string bgstr{"nitro:/backgrounds/"};
|
||||||
|
auto* pdir = opendir("nitro:/backgrounds");
|
||||||
|
if(!pdir) return bgs;
|
||||||
|
dirent* pent;
|
||||||
|
while((pent = readdir(pdir))) {
|
||||||
|
if(pent->d_type == DT_DIR)
|
||||||
|
continue;
|
||||||
|
std::string name{pent->d_name};
|
||||||
|
if(!name.ends_with(".gif") && !name.ends_with(".GIF"))
|
||||||
|
continue;
|
||||||
|
bgs.emplace_back(name.substr(0, name.size() - 4), bgstr + name);
|
||||||
|
}
|
||||||
|
closedir(pdir);
|
||||||
|
return bgs;
|
||||||
|
}();
|
||||||
|
return bgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* backgroundMenu()
|
||||||
|
{
|
||||||
|
//top screen
|
||||||
|
clearScreen(&topScreen);
|
||||||
|
|
||||||
|
//menu
|
||||||
|
Menu* m = newMenu();
|
||||||
|
setMenuHeader(m, "BACKGROUNDS");
|
||||||
|
|
||||||
|
const auto& bgs = getBackgroundList();
|
||||||
|
|
||||||
|
for(const auto& [bgName, bgPath] : bgs)
|
||||||
|
{
|
||||||
|
addMenuItem(m, bgName.data(), nullptr, 0);
|
||||||
|
}
|
||||||
|
addMenuItem(m, "Default", nullptr, 0);
|
||||||
|
addMenuItem(m, "Cancel", nullptr, 0);
|
||||||
|
|
||||||
|
m->cursor = 0;
|
||||||
|
|
||||||
|
//bottom screen
|
||||||
|
printMenu(m);
|
||||||
|
|
||||||
|
while (!programEnd)
|
||||||
|
{
|
||||||
|
swiWaitForVBlank();
|
||||||
|
scanKeys();
|
||||||
|
|
||||||
|
if (moveCursor(m))
|
||||||
|
printMenu(m);
|
||||||
|
|
||||||
|
if (keysDown() & KEY_A)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* result = nullptr;
|
||||||
|
if(static_cast<size_t>(m->cursor) < bgs.size())
|
||||||
|
result = bgs[m->cursor].second.data();
|
||||||
|
else if(static_cast<size_t>(m->cursor) == bgs.size())
|
||||||
|
result = "default";
|
||||||
|
freeMenu(m);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
14
arm9/src/bgMenu.h
Normal file
14
arm9/src/bgMenu.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef BGMENU_H
|
||||||
|
#define BGMENU_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char* backgroundMenu();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
#include "bgMenu.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
@ -17,6 +18,7 @@ static UNLAUNCH_VERSION foundUnlaunchInstallerVersion = INVALID;
|
|||||||
static bool disableAllPatches = false;
|
static bool disableAllPatches = false;
|
||||||
static bool enableSoundAndSplash = false;
|
static bool enableSoundAndSplash = false;
|
||||||
static const char* splashSoundBinaryPatchPath = NULL;
|
static const char* splashSoundBinaryPatchPath = NULL;
|
||||||
|
static const char* customBgPath = NULL;
|
||||||
bool charging = false;
|
bool charging = false;
|
||||||
u8 batteryLevel = 0;
|
u8 batteryLevel = 0;
|
||||||
|
|
||||||
@ -25,6 +27,7 @@ PrintConsole bottomScreen;
|
|||||||
|
|
||||||
enum {
|
enum {
|
||||||
MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL,
|
MAIN_MENU_SAFE_UNLAUNCH_UNINSTALL,
|
||||||
|
MAIN_MENU_CUSTOM_BG,
|
||||||
MAIN_MENU_TID_PATCHES,
|
MAIN_MENU_TID_PATCHES,
|
||||||
MAIN_MENU_SOUND_SPLASH_PATCHES,
|
MAIN_MENU_SOUND_SPLASH_PATCHES,
|
||||||
MAIN_MENU_SAFE_UNLAUNCH_INSTALL,
|
MAIN_MENU_SAFE_UNLAUNCH_INSTALL,
|
||||||
@ -69,8 +72,9 @@ static int mainMenu(int cursor)
|
|||||||
Menu* m = newMenu();
|
Menu* m = newMenu();
|
||||||
setMenuHeader(m, "MAIN MENU");
|
setMenuHeader(m, "MAIN MENU");
|
||||||
|
|
||||||
char uninstallStr[32], installStr[32], soundPatchesStr[64], tidPatchesStr[32];
|
char uninstallStr[32], installStr[32], soundPatchesStr[64], tidPatchesStr[32], customBgStr[32];
|
||||||
sprintf(uninstallStr, "\x1B[%02omUninstall unlaunch", unlaunchFound ? 047 : 037);
|
sprintf(uninstallStr, "\x1B[%02omUninstall unlaunch", unlaunchFound ? 047 : 037);
|
||||||
|
sprintf(customBgStr, "\x1B[%02omCustom background", (foundUnlaunchInstallerVersion != INVALID) ? 047 : 037);
|
||||||
sprintf(tidPatchesStr, "\x1B[%02omDisable all patches: %s",
|
sprintf(tidPatchesStr, "\x1B[%02omDisable all patches: %s",
|
||||||
(foundUnlaunchInstallerVersion == v1_9 || foundUnlaunchInstallerVersion == v2_0) ? 047 : 037,
|
(foundUnlaunchInstallerVersion == v1_9 || foundUnlaunchInstallerVersion == v2_0) ? 047 : 037,
|
||||||
disableAllPatches ? "On" : "Off");
|
disableAllPatches ? "On" : "Off");
|
||||||
@ -79,6 +83,7 @@ static int mainMenu(int cursor)
|
|||||||
enableSoundAndSplash ? "On" : "Off");
|
enableSoundAndSplash ? "On" : "Off");
|
||||||
sprintf(installStr, "\x1B[%02omInstall unlaunch", (foundUnlaunchInstallerVersion != INVALID && !unlaunchFound) ? 047 : 037);
|
sprintf(installStr, "\x1B[%02omInstall unlaunch", (foundUnlaunchInstallerVersion != INVALID && !unlaunchFound) ? 047 : 037);
|
||||||
addMenuItem(m, uninstallStr, NULL, 0);
|
addMenuItem(m, uninstallStr, NULL, 0);
|
||||||
|
addMenuItem(m, customBgStr, NULL, true);
|
||||||
addMenuItem(m, tidPatchesStr, NULL, 0);
|
addMenuItem(m, tidPatchesStr, NULL, 0);
|
||||||
addMenuItem(m, soundPatchesStr, NULL, 0);
|
addMenuItem(m, soundPatchesStr, NULL, 0);
|
||||||
addMenuItem(m, installStr, NULL, 0);
|
addMenuItem(m, installStr, NULL, 0);
|
||||||
@ -257,6 +262,22 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MAIN_MENU_CUSTOM_BG:
|
||||||
|
if(foundUnlaunchInstallerVersion != INVALID) {
|
||||||
|
const char* customBg = backgroundMenu();
|
||||||
|
if(!customBg)
|
||||||
|
break;
|
||||||
|
if(strcmp(customBg, "default") == 0)
|
||||||
|
{
|
||||||
|
customBgPath = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
customBgPath = customBg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case MAIN_MENU_TID_PATCHES:
|
case MAIN_MENU_TID_PATCHES:
|
||||||
if(foundUnlaunchInstallerVersion == v1_9 || foundUnlaunchInstallerVersion == v2_0) {
|
if(foundUnlaunchInstallerVersion == v1_9 || foundUnlaunchInstallerVersion == v2_0) {
|
||||||
disableAllPatches = !disableAllPatches;
|
disableAllPatches = !disableAllPatches;
|
||||||
@ -279,7 +300,8 @@ int main(int argc, char **argv)
|
|||||||
if(installUnlaunch(retailConsole,
|
if(installUnlaunch(retailConsole,
|
||||||
retailLauncherTmdPresentAndToBePatched ? retailLauncherTmdPath : NULL,
|
retailLauncherTmdPresentAndToBePatched ? retailLauncherTmdPath : NULL,
|
||||||
disableAllPatches,
|
disableAllPatches,
|
||||||
enableSoundAndSplash ? splashSoundBinaryPatchPath : NULL))
|
enableSoundAndSplash ? splashSoundBinaryPatchPath : NULL,
|
||||||
|
customBgPath))
|
||||||
{
|
{
|
||||||
messageBox("Install successful!\n");
|
messageBox("Install successful!\n");
|
||||||
unlaunchFound = true;
|
unlaunchFound = true;
|
||||||
|
|||||||
@ -77,7 +77,7 @@ void sortMenuItems(Menu* m)
|
|||||||
qsort(m->items, m->itemCount, sizeof(Item), alphabeticalCompare);
|
qsort(m->items, m->itemCount, sizeof(Item), alphabeticalCompare);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMenuHeader(Menu* m, char* str)
|
void setMenuHeader(Menu* m, const char* str)
|
||||||
{
|
{
|
||||||
if (!m) return;
|
if (!m) return;
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ void setMenuHeader(Menu* m, char* str)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* strPtr = str;
|
const char* strPtr = str;
|
||||||
|
|
||||||
if (strlen(strPtr) > 30)
|
if (strlen(strPtr) > 30)
|
||||||
strPtr = str + (strlen(strPtr) - 30);
|
strPtr = str + (strlen(strPtr) - 30);
|
||||||
|
|||||||
@ -30,7 +30,7 @@ void freeMenu(Menu* m);
|
|||||||
|
|
||||||
void addMenuItem(Menu* m, char const* label, char const* value, bool directory);
|
void addMenuItem(Menu* m, char const* label, char const* value, bool directory);
|
||||||
void sortMenuItems(Menu* m);
|
void sortMenuItems(Menu* m);
|
||||||
void setMenuHeader(Menu* m, char* str);
|
void setMenuHeader(Menu* m, const char* str);
|
||||||
|
|
||||||
void resetMenu(Menu* m);
|
void resetMenu(Menu* m);
|
||||||
void clearMenu(Menu* m);
|
void clearMenu(Menu* m);
|
||||||
|
|||||||
43
arm9/src/tonccpy.h
Normal file
43
arm9/src/tonccpy.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
//# Stuff you may not have yet.
|
||||||
|
|
||||||
|
#ifndef TONCCPY_H
|
||||||
|
#define TONCCPY_H
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <nds/ndstypes.h>
|
||||||
|
|
||||||
|
typedef unsigned int uint;
|
||||||
|
#define BIT_MASK(len) ( (1<<(len))-1 )
|
||||||
|
static inline u32 quad8(u16 x) { x |= x<<8; return x | x<<16; }
|
||||||
|
|
||||||
|
|
||||||
|
//# Declarations and inlines.
|
||||||
|
|
||||||
|
void tonccpy(void *dst, const void *src, uint size);
|
||||||
|
|
||||||
|
void __toncset(void *dst, u32 fill, uint size);
|
||||||
|
static inline void toncset(void *dst, u8 src, uint size);
|
||||||
|
static inline void toncset16(void *dst, u16 src, uint size);
|
||||||
|
static inline void toncset32(void *dst, u32 src, uint size);
|
||||||
|
|
||||||
|
|
||||||
|
//! VRAM-safe memset, byte version. Size in bytes.
|
||||||
|
static inline void toncset(void *dst, u8 src, uint size)
|
||||||
|
{ __toncset(dst, quad8(src), size); }
|
||||||
|
|
||||||
|
//! VRAM-safe memset, halfword version. Size in hwords.
|
||||||
|
static inline void toncset16(void *dst, u16 src, uint size)
|
||||||
|
{ __toncset(dst, src|src<<16, size*2); }
|
||||||
|
|
||||||
|
//! VRAM-safe memset, word version. Size in words.
|
||||||
|
static inline void toncset32(void *dst, u32 src, uint size)
|
||||||
|
{ __toncset(dst, src, size*4); }
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
136
arm9/src/tonccpy.itcm.c
Normal file
136
arm9/src/tonccpy.itcm.c
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#include "tonccpy.h"
|
||||||
|
//# tonccpy.c
|
||||||
|
|
||||||
|
//! VRAM-safe cpy.
|
||||||
|
/*! This version mimics memcpy in functionality, with
|
||||||
|
the benefit of working for VRAM as well. It is also
|
||||||
|
slightly faster than the original memcpy, but faster
|
||||||
|
implementations can be made.
|
||||||
|
\param dst Destination pointer.
|
||||||
|
\param src Source pointer.
|
||||||
|
\param size Fill-length in bytes.
|
||||||
|
\note The pointers and size need not be word-aligned.
|
||||||
|
*/
|
||||||
|
void tonccpy(void *dst, const void *src, uint size)
|
||||||
|
{
|
||||||
|
if(size==0 || dst==0 || src==0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint count;
|
||||||
|
u16 *dst16; // hword destination
|
||||||
|
u8 *src8; // byte source
|
||||||
|
|
||||||
|
// Ideal case: copy by 4x words. Leaves tail for later.
|
||||||
|
if( ((u32)src|(u32)dst)%4==0 && size>=4)
|
||||||
|
{
|
||||||
|
u32 *src32= (u32*)src, *dst32= (u32*)dst;
|
||||||
|
|
||||||
|
count= size/4;
|
||||||
|
uint tmp= count&3;
|
||||||
|
count /= 4;
|
||||||
|
|
||||||
|
// Duff's Device, good friend!
|
||||||
|
switch(tmp) {
|
||||||
|
do { *dst32++ = *src32++;
|
||||||
|
case 3: *dst32++ = *src32++;
|
||||||
|
case 2: *dst32++ = *src32++;
|
||||||
|
case 1: *dst32++ = *src32++;
|
||||||
|
case 0: ; } while(count--);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for tail
|
||||||
|
size &= 3;
|
||||||
|
if(size == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
src8= (u8*)src32;
|
||||||
|
dst16= (u16*)dst32;
|
||||||
|
}
|
||||||
|
else // Unaligned.
|
||||||
|
{
|
||||||
|
uint dstOfs= (u32)dst&1;
|
||||||
|
src8= (u8*)src;
|
||||||
|
dst16= (u16*)(dst-dstOfs);
|
||||||
|
|
||||||
|
// Head: 1 byte.
|
||||||
|
if(dstOfs != 0)
|
||||||
|
{
|
||||||
|
*dst16= (*dst16 & 0xFF) | *src8++<<8;
|
||||||
|
dst16++;
|
||||||
|
if(--size==0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unaligned main: copy by 2x byte.
|
||||||
|
count= size/2;
|
||||||
|
while(count--)
|
||||||
|
{
|
||||||
|
*dst16++ = src8[0] | src8[1]<<8;
|
||||||
|
src8 += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tail: 1 byte.
|
||||||
|
if(size&1)
|
||||||
|
*dst16= (*dst16 &~ 0xFF) | *src8;
|
||||||
|
}
|
||||||
|
//# toncset.c
|
||||||
|
|
||||||
|
//! VRAM-safe memset, internal routine.
|
||||||
|
/*! This version mimics memset in functionality, with
|
||||||
|
the benefit of working for VRAM as well. It is also
|
||||||
|
slightly faster than the original memset.
|
||||||
|
\param dst Destination pointer.
|
||||||
|
\param fill Word to fill with.
|
||||||
|
\param size Fill-length in bytes.
|
||||||
|
\note The \a dst pointer and \a size need not be
|
||||||
|
word-aligned. In the case of unaligned fills, \a fill
|
||||||
|
will be masked off to match the situation.
|
||||||
|
*/
|
||||||
|
void __toncset(void *dst, u32 fill, uint size)
|
||||||
|
{
|
||||||
|
if(size==0 || dst==0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint left= (u32)dst&3;
|
||||||
|
u32 *dst32= (u32*)(dst-left);
|
||||||
|
u32 count, mask;
|
||||||
|
|
||||||
|
// Unaligned head.
|
||||||
|
if(left != 0)
|
||||||
|
{
|
||||||
|
// Adjust for very small stint.
|
||||||
|
if(left+size<4)
|
||||||
|
{
|
||||||
|
mask= BIT_MASK(size*8)<<(left*8);
|
||||||
|
*dst32= (*dst32 &~ mask) | (fill & mask);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mask= BIT_MASK(left*8);
|
||||||
|
*dst32= (*dst32 & mask) | (fill&~mask);
|
||||||
|
dst32++;
|
||||||
|
size -= 4-left;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main stint.
|
||||||
|
count= size/4;
|
||||||
|
uint tmp= count&3;
|
||||||
|
count /= 4;
|
||||||
|
|
||||||
|
switch(tmp) {
|
||||||
|
do { *dst32++ = fill;
|
||||||
|
case 3: *dst32++ = fill;
|
||||||
|
case 2: *dst32++ = fill;
|
||||||
|
case 1: *dst32++ = fill;
|
||||||
|
case 0: ; } while(count--);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tail
|
||||||
|
size &= 3;
|
||||||
|
if(size)
|
||||||
|
{
|
||||||
|
mask= BIT_MASK(size*8);
|
||||||
|
*dst32= (*dst32 &~ mask) | (fill & mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,15 +1,16 @@
|
|||||||
#include "message.h"
|
#include "message.h"
|
||||||
#include "sha1digest.h"
|
#include "sha1digest.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
|
#include "tonccpy.h"
|
||||||
#include "unlaunch.h"
|
#include "unlaunch.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
|
||||||
#include <nds/sha1.h>
|
#include <nds/sha1.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
static char unlaunchInstallerBuffer[0x30000];
|
static char unlaunchInstallerBuffer[0x30000];
|
||||||
|
static char ogUnlaunchInstallerBuffer[0x30000];
|
||||||
static const char* hnaaTmdPath = "nand:/title/00030017/484e4141/content/title.tmd";
|
static const char* hnaaTmdPath = "nand:/title/00030017/484e4141/content/title.tmd";
|
||||||
static const char* hnaaBackupTmdPath = "nand:/title/00030017/484e4141/content/title.tmd.bak";
|
static const char* hnaaBackupTmdPath = "nand:/title/00030017/484e4141/content/title.tmd.bak";
|
||||||
|
|
||||||
@ -32,6 +33,12 @@ constexpr std::array knownUnlaunchHashes{
|
|||||||
"15f4a36251d1408d71114019b2825fe8f5b4c8cc"_sha1, // v2.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(0x48f0, 0x855c), /* 2.0 */
|
||||||
|
};
|
||||||
|
|
||||||
constexpr std::array blockAllPatchesOffset{
|
constexpr std::array blockAllPatchesOffset{
|
||||||
0xae74, /* 1.9 */
|
0xae74, /* 1.9 */
|
||||||
0xae91, /* 2.0 */
|
0xae91, /* 2.0 */
|
||||||
@ -366,12 +373,12 @@ static bool readUnlaunchInstaller(const char* path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int toRead = unlaunchInstallerSize;
|
int toRead = unlaunchInstallerSize;
|
||||||
char* buff = unlaunchInstallerBuffer;
|
auto* buff = unlaunchInstallerBuffer;
|
||||||
// Pad the installer with 520 bytes, those being the size of a valid tmd
|
// Pad the installer with 520 bytes, those being the size of a valid tmd
|
||||||
buff += 520;
|
buff += 520;
|
||||||
|
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
while (toRead != 0 && (n = fread(buff, sizeof(char), toRead, unlaunchInstaller)) > 0)
|
while (toRead != 0 && (n = fread(buff, sizeof(uint8_t), toRead, unlaunchInstaller)) > 0)
|
||||||
{
|
{
|
||||||
toRead -= n;
|
toRead -= n;
|
||||||
buff += n;
|
buff += n;
|
||||||
@ -402,8 +409,64 @@ static bool verifyUnlaunchInstaller(void)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool patchUnlaunchInstaller(bool disableAllPatches, const char* splashSoundBinaryPatchPath)
|
static bool patchCustomBackground(const char* customBackgroundPath)
|
||||||
{
|
{
|
||||||
|
if(!customBackgroundPath)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto bgGif = fopen(customBackgroundPath, "rb");
|
||||||
|
if(!bgGif)
|
||||||
|
{
|
||||||
|
messageBox("\x1B[31mError:\x1B[33m Failed to open custom bg gif.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto size = getFileSize(bgGif);
|
||||||
|
if(size < 7 || size > 0x3C70)
|
||||||
|
{
|
||||||
|
messageBox("\x1B[31mError:\x1B[33m Invalid gif file.\n");
|
||||||
|
fclose(bgGif);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
u16 gifWidth;
|
||||||
|
u16 gifHeight;
|
||||||
|
if((fseek(bgGif, 6, SEEK_SET) != 0) || (fread(&gifWidth, 1, sizeof(u16), bgGif) != sizeof(u16)) || (fread(&gifHeight, 1, sizeof(u16), bgGif) != sizeof(u16)))
|
||||||
|
{
|
||||||
|
messageBox("\x1B[31mError:\x1B[33m Failed to parse gif file.\n");
|
||||||
|
fclose(bgGif);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (gifWidth != 256 || gifHeight != 192) {
|
||||||
|
messageBox("\x1B[31mError:\x1B[33m Gif file has invalid dimensions.\n");
|
||||||
|
fclose(bgGif);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const u32 gifSignatureStart = 0x38464947;
|
||||||
|
const u32 gifSignatureEnd = 0x3B000044;
|
||||||
|
|
||||||
|
auto [gifOffsetStart, gifOffsetEnd] = gifOffsets[installerVersion];
|
||||||
|
|
||||||
|
auto* gifStart = reinterpret_cast<uint32_t*>((unlaunchInstallerBuffer + 520) + gifOffsetStart);
|
||||||
|
auto* gifEnd = reinterpret_cast<uint32_t*>((unlaunchInstallerBuffer + 520) + gifOffsetEnd);
|
||||||
|
|
||||||
|
if(*gifStart != gifSignatureStart || *gifEnd != gifSignatureEnd)
|
||||||
|
{
|
||||||
|
messageBox("\x1B[31mError:\x1B[33m Gif offsets not matching.\n");
|
||||||
|
fclose(bgGif);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(bgGif, 0, SEEK_SET);
|
||||||
|
|
||||||
|
//read the whole file, could be less than 0x3C70, but unlaunch should then just ignore the leftover data
|
||||||
|
fread(gifStart, 1, 0x3C70, bgGif);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool patchUnlaunchInstaller(bool disableAllPatches, const char* splashSoundBinaryPatchPath, const char* customBackgroundPath)
|
||||||
|
{
|
||||||
|
tonccpy(unlaunchInstallerBuffer, ogUnlaunchInstallerBuffer, sizeof(unlaunchInstallerBuffer));
|
||||||
if(disableAllPatches)
|
if(disableAllPatches)
|
||||||
{
|
{
|
||||||
if(installerVersion == v1_8)
|
if(installerVersion == v1_8)
|
||||||
@ -450,6 +513,10 @@ static bool patchUnlaunchInstaller(bool disableAllPatches, const char* splashSou
|
|||||||
}
|
}
|
||||||
fclose(patch);
|
fclose(patch);
|
||||||
}
|
}
|
||||||
|
if(!patchCustomBackground(customBackgroundPath))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,14 +524,15 @@ UNLAUNCH_VERSION loadUnlaunchInstaller(const char* path)
|
|||||||
{
|
{
|
||||||
if(readUnlaunchInstaller(path) && verifyUnlaunchInstaller())
|
if(readUnlaunchInstaller(path) && verifyUnlaunchInstaller())
|
||||||
{
|
{
|
||||||
|
tonccpy(ogUnlaunchInstallerBuffer, unlaunchInstallerBuffer, sizeof(unlaunchInstallerBuffer));
|
||||||
return installerVersion;
|
return installerVersion;
|
||||||
}
|
}
|
||||||
return INVALID;
|
return INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool installUnlaunch(bool retailConsole, const char* retailLauncherTmdPath, bool disableAllPatches, const char* splashSoundBinaryPatchPath)
|
bool installUnlaunch(bool retailConsole, const char* retailLauncherTmdPath, bool disableAllPatches, const char* splashSoundBinaryPatchPath, const char* customBackgroundPath)
|
||||||
{
|
{
|
||||||
if (installerVersion == INVALID || !patchUnlaunchInstaller(disableAllPatches, splashSoundBinaryPatchPath))
|
if (installerVersion == INVALID || !patchUnlaunchInstaller(disableAllPatches, splashSoundBinaryPatchPath, customBackgroundPath))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Treat protos differently
|
// Treat protos differently
|
||||||
|
|||||||
@ -14,7 +14,7 @@ typedef enum UNLAUNCH_VERSION {
|
|||||||
} UNLAUNCH_VERSION;
|
} UNLAUNCH_VERSION;
|
||||||
|
|
||||||
bool uninstallUnlaunch(bool notProto, bool hasHNAABackup, const char* retailLauncherTmdPath);
|
bool uninstallUnlaunch(bool notProto, bool hasHNAABackup, const char* retailLauncherTmdPath);
|
||||||
bool installUnlaunch(bool retailConsole, const char* retailLauncherTmdPath, bool disableAllPatches, const char* splashSoundBinaryPatchPath);
|
bool installUnlaunch(bool retailConsole, const char* retailLauncherTmdPath, bool disableAllPatches, const char* splashSoundBinaryPatchPath, const char* customBackgroundPath);
|
||||||
|
|
||||||
bool isLauncherTmdPatched(const char* path);
|
bool isLauncherTmdPatched(const char* path);
|
||||||
|
|
||||||
|
|||||||
BIN
nitrofiles/backgrounds/black.gif
Normal file
BIN
nitrofiles/backgrounds/black.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 351 B |
BIN
nitrofiles/backgrounds/desert.gif
Normal file
BIN
nitrofiles/backgrounds/desert.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
nitrofiles/backgrounds/desert_gray.gif
Normal file
BIN
nitrofiles/backgrounds/desert_gray.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Loading…
Reference in New Issue
Block a user