/* Copyright (C) 2024 lifehackerhansol SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include #include "../cheatwnd.h" #include "../dsrom.h" #include "../flags.h" #include "../mainlist.h" #include "../ui/progresswnd.h" #include "ILauncher.h" #include "TopToyLauncher.h" static void resetAndLoop() { DC_FlushAll(); DC_InvalidateAll(); fifoSendValue32(FIFO_USER_01, MENU_MSG_ARM7_REBOOT_TT); *((vu32*)0x02FFFE04) = 0; // Interrupt REG_IME = 0; REG_IE = 0; REG_IF = ~0; // wait for arm7 while (*((vu32*)0x02FFFE04) == 0) ; swiSoftReset(); } typedef struct { char TTARMagic[4]; // usually "TTAR" char gameCode[4]; // gameCode of title as seen in usrcheat.dat u32 crc32; // crc32 of title header as seen in usrcheat.dat u32 unk1; // TODO: what does this mean? u32 cheatOffset; // relative from 0x1100 of file u32 cheatSize; // size in bytes u8 reserved[235]; } PACKED TTARHeader; bool TopToyLauncher::prepareCheats() { u32 gameCode, crc32; if (cCheatWnd::romData(mRomPath, gameCode, crc32)) { FILE* cheatDb = fopen("/__rpg/cheats/usrcheat.dat", "rb"); if (!cheatDb) goto cheat_failed; long cheatOffset; size_t cheatSize; if (cCheatWnd::searchCheatData(cheatDb, gameCode, crc32, cheatOffset, cheatSize)) { // Read cheat codes cCheatWnd chtwnd((256) / 2, (192) / 2, 100, 100, NULL, mRomPath); chtwnd.parse(mRomPath); std::vector cheats(chtwnd.getCheats()); // YSMENU.ARP file creation ALIGN(4) TTARHeader arHeader = {}; arHeader.TTARMagic[0] = 'T'; arHeader.TTARMagic[1] = 'T'; arHeader.TTARMagic[2] = 'A'; arHeader.TTARMagic[3] = 'R'; memcpy(&arHeader.gameCode, &gameCode, 4); arHeader.crc32 = crc32; // We will write the cheat codes directly to 0x1100. arHeader.cheatOffset = 0; arHeader.cheatSize = cheats.size() << 2; FILE* cheatFile = fopen("/YSMENU.ARP", "wb"); fseek(cheatFile, 0, SEEK_SET); // memdump. Actually just expanding the file seems to crash, but this works totally // fine... fwrite((void*)0x02400000, 4, 0x1100 >> 2, cheatFile); fflush(cheatFile); fseek(cheatFile, 0, SEEK_SET); fwrite(&arHeader, 1, sizeof(TTARHeader), cheatFile); fseek(cheatFile, 0x1100, SEEK_SET); fwrite(cheats.data(), 4, cheats.size(), cheatFile); fflush(cheatFile); fclose(cheatFile); } else { fclose(cheatDb); goto cheat_failed; } fclose(cheatDb); } return true; cheat_failed: // Remove cheat bin if exists if (access("/YSMENU.ARP", F_OK) == 0) { remove("/YSMENU.ARP"); } return false; } // TODO: what are the unknowns? typedef struct { char TTSYSMagic[4]; u32 unk1; u32 unk2; // always set to 1? u32 softReset; u32 useCheats; u32 DMA; u8 reserved[232]; } PACKED TTSYSHeader; // TTMENU.SYS file creation. Sets up parameters for ttpatch.dat bool TopToyLauncher::prepareTTSYS(void) { TTSYSHeader* ttsys_header = (TTSYSHeader*)malloc(sizeof(TTSYSHeader)); ttsys_header->TTSYSMagic[0] = 't'; ttsys_header->TTSYSMagic[1] = 't'; ttsys_header->TTSYSMagic[2] = 'd'; ttsys_header->TTSYSMagic[3] = 's'; ttsys_header->unk1 = 0; ttsys_header->unk2 = 1; ttsys_header->softReset = mFlags & PATCH_SOFT_RESET ? 1 : 0; ttsys_header->useCheats = mFlags & PATCH_CHEATS ? 1 : 0; ttsys_header->DMA = mFlags & PATCH_DMA ? 1 : 0; FILE* TTSYSFile = fopen("fat:/TTMENU.SYS", "rb+"); fseek(TTSYSFile, 0, SEEK_SET); fwrite(ttsys_header, sizeof(TTSYSHeader), 1, TTSYSFile); free(ttsys_header); fseek(TTSYSFile, 0x100, SEEK_SET); fwrite(mRomPath.c_str() + 4, 1, 0x1000, TTSYSFile); fseek(TTSYSFile, 0x1100, SEEK_SET); fwrite(mSavePath.c_str() + 4, 1, 0x1000, TTSYSFile); if (mFlags & PATCH_CHEATS) { fseek(TTSYSFile, 0x2100, SEEK_SET); fwrite("/YSMENU.ARP", 1, 0x12, TTSYSFile); } fflush(TTSYSFile); fclose(TTSYSFile); return true; } bool TopToyLauncher::launchRom(std::string romPath, std::string savePath, u32 flags, u32 cheatOffset, u32 cheatSize) { #ifdef __TTLAUNCHER_M3__ if (access("fat:/TTMenu/m3patch.dat", F_OK) != 0) #else if (access("fat:/TTMenu/ttpatch.dat", F_OK) != 0) #endif return false; mRomPath = romPath; mSavePath = savePath; mFlags = flags; // Create TTMENU.SYS if it don't exist if (access("fat:/TTMENU.SYS", F_OK) != 0) { progressWnd().setTipText("Generating TTMENU.SYS..."); progressWnd().show(); progressWnd().setPercent(0); FILE* TTSYSFile = fopen("fat:/TTMENU.SYS", "wb"); fseek(TTSYSFile, 0, SEEK_SET); // memdump. Actually just expanding the file seems to crash, but this works totally fine... fwrite((void*)0x02400000, 1, 0x400000, TTSYSFile); fflush(TTSYSFile); fclose(TTSYSFile); progressWnd().setPercent(100); progressWnd().hide(); } // Prepare cheat codes if enabled if (flags & PATCH_CHEATS) { if (!prepareCheats()) return false; } // Setup TTMenu system parameters if (!prepareTTSYS()) return false; #ifdef __TTLAUNCHER_M3__ FILE* loader = fopen("fat:/TTMenu/m3patch.dat", "rb"); #else FILE* loader = fopen("fat:/TTMenu/ttpatch.dat", "rb"); #endif tNDSHeader* header = (tNDSHeader*)malloc(sizeof(tNDSHeader)); u8* loader_arm7 = NULL; fseek(loader, 0, SEEK_SET); fread(header, 1, sizeof(tNDSHeader), loader); fseek(loader, header->arm9romOffset, SEEK_SET); fread((void*)header->arm9destination, 1, header->arm9binarySize, loader); fseek(loader, header->arm7romOffset, SEEK_SET); loader_arm7 = (u8*)memalign(32, header->arm7binarySize); fread(loader_arm7, 1, header->arm7binarySize, loader); fclose(loader); memcpy((void*)__NDSHeader, header, sizeof(tNDSHeader)); free(header); *(u32*)0x02FFFE00 = (u32)loader_arm7; // patch a loop in ARM9 // not sure why it's there. Some sort of obfuscation mechanism? if (*((vu32*)0x023200EC) == 0xEAFFFFFE) // b #0; bad *((vu32*)0x023200EC) = 0xE3A00000; // mov r0, #0 // ttpatch checks this for some reason *((vu32*)0x02FFFC20) = 0x5555AAAA; // set SD/SDHC flag to SDHC, right now our DLDI only does SDHC *((vu32*)0x02FFFC24) = ~0; // this int seems to be a flag to reinitialize the SD card in ttpatch // if this is *not* -1, ttpatch sends an SDIO CMD12 (STOP_TRANSMISSION) // other frontends set this to -1 by default, so let's do it too *((vu32*)0x02FFFC28) = ~0; resetAndLoop(); free(loader_arm7); return false; }