diff --git a/arm9/source/common/common.h b/arm9/source/common/common.h index 583fb92..bb8defe 100644 --- a/arm9/source/common/common.h +++ b/arm9/source/common/common.h @@ -30,6 +30,9 @@ (((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v)) #define countof(x) \ (sizeof(x) / sizeof((x)[0])) + +#define bkpt \ + asm("bkpt\n\t") #define STATIC_ASSERT(...) \ _Static_assert((__VA_ARGS__), #__VA_ARGS__) @@ -37,46 +40,28 @@ // standard output path (support file paths are in support.h) #define OUTPUT_PATH "0:/gm9/out" +// used in several places +#define STD_BUFFER_SIZE 0x100000 // must be a multiple of 0x200 + // buffer area defines (in use by godmode.c) #define DIR_BUFFER (0x20000000) #define DIR_BUFFER_SIZE (0x100000) -// buffer area defines (in use by fsutil.c, fsinit.c and gameutil.c) -#define MAIN_BUFFER ((u8*)0x20100000) -#define MAIN_BUFFER_SIZE (0x100000) // must be multiple of 0x200 // buffer area defines (in use by nand.c) -#define NAND_BUFFER ((u8*)0x20200000) +#define NAND_BUFFER ((u8*)0x20100000) #define NAND_BUFFER_SIZE (0x100000) // must be multiple of 0x200 // buffer area defines (in use by sddata.c) -#define SDCRYPT_BUFFER ((u8*)0x20300000) +#define SDCRYPT_BUFFER ((u8*)0x20200000) #define SDCRYPT_BUFFER_SIZE (0x100000) // buffer area defines (in use by scripting.c) -#define SCRIPT_BUFFER ((u8*)0x20400000) +#define SCRIPT_BUFFER ((u8*)0x20300000) #define SCRIPT_BUFFER_SIZE (0x100000) // buffer area defines (in use by vgame.c) -#define VGAME_BUFFER ((u8*)0x20500000) +#define VGAME_BUFFER ((u8*)0x20400000) #define VGAME_BUFFER_SIZE (0x200000) // 2MB, big RomFS // buffer area defines (in use by vcart.c) -#define VCART_BUFFER ((u8*)0x20700000) +#define VCART_BUFFER ((u8*)0x20600000) #define VCART_BUFFER_SIZE (0x20000) // 128kB, this is more than enough -// buffer area defines (temporary, in use by various functions) -// -> godmode.c hexviewer -// -> godmode.c loading payloads -// -> ncch.c seed setup -// -> cia.c ticket / titlekey setup -// -> gameutil.c various temporary stuff -// -> nandcmac.c for processing agbsave -// -> nandutil.c for storing essential backup -// -> ctrtransfer.c for SecureInfo (temporary) -// -> vgame.c for handling FIRMs -// -> vtickdb.c for parsing ticket.db -// -> qlzcomp.c for temporary compression stuff -// -> codelzss.c for decompressing .code -// meaning: careful when using this! -#define TEMP_BUFFER ((u8*)0x20800000) -#define TEMP_BUFFER_SIZE (0x400000) // 4MB -#define TEMP_BUFFER_EXTSIZE (0x1800000) // 24MB(!) (only used by codelzss.c right now) - // buffer area defines (in use by image.c, for RAMdrive) #define RAMDRV_BUFFER ((u8*)0x22800000) // top of STACK #define RAMDRV_SIZE_O3DS (0x5800000) // 88MB diff --git a/arm9/source/common/ui.h b/arm9/source/common/ui.h index ab22014..dcded0d 100644 --- a/arm9/source/common/ui.h +++ b/arm9/source/common/ui.h @@ -14,6 +14,9 @@ #define SCREEN_WIDTH(s) ((s == TOP_SCREEN) ? SCREEN_WIDTH_TOP : SCREEN_WIDTH_BOT) #define SCREEN_WIDTH_TOP 400 #define SCREEN_WIDTH_BOT 320 +#define SCREEN_SIZE(s) ((s == TOP_SCREEN) ? SCREEN_SIZE_TOP : SCREEN_SIZE_BOT) +#define SCREEN_SIZE_TOP (SCREEN_WIDTH_TOP * SCREEN_HEIGHT * BYTES_PER_PIXEL) +#define SCREEN_SIZE_BOT (SCREEN_WIDTH_BOT * SCREEN_HEIGHT * BYTES_PER_PIXEL) #define FONT_WIDTH_EXT GetFontWidth() #define FONT_HEIGHT_EXT GetFontHeight() diff --git a/arm9/source/crypto/keydb.c b/arm9/source/crypto/keydb.c index e10d2a7..7f92396 100644 --- a/arm9/source/crypto/keydb.c +++ b/arm9/source/crypto/keydb.c @@ -131,8 +131,9 @@ u32 LoadKeyFromFile(void* key, u32 keyslot, char type, char* id) if (!key) key = keystore; // try to get key from 'aeskeydb.bin' file - AesKeyInfo* keydb = (AesKeyInfo*) TEMP_BUFFER; - u32 nkeys = LoadKeyDb(NULL, keydb, TEMP_BUFFER_SIZE); + AesKeyInfo* keydb = (AesKeyInfo*) malloc(STD_BUFFER_SIZE); + u32 nkeys = (keydb) ? LoadKeyDb(NULL, keydb, STD_BUFFER_SIZE) : 0; + for (u32 i = 0; i < nkeys; i++) { AesKeyInfo* info = &(keydb[i]); if (!((info->slot == keyslot) && (info->type == type) && @@ -146,6 +147,8 @@ u32 LoadKeyFromFile(void* key, u32 keyslot, char type, char* id) break; } + free(keydb); + // load legacy slot0x??Key?.bin file instead if (!found && (type != 'I')) { char fname[64]; @@ -185,15 +188,16 @@ u32 InitKeyDb(const char* path) (1ull<<0x1C)|(1ull<<0x1D)|(1ull<<0x1E)|(1ull<<0x1F)|(1ull<<0x24)|(1ull<<0x25)|(1ull<<0x2F); // try to load aeskeydb.bin file - AesKeyInfo* keydb = (AesKeyInfo*) (void*) TEMP_BUFFER; - u32 nkeys = LoadKeyDb(path, keydb, TEMP_BUFFER_SIZE); - if (!nkeys) return 1; + AesKeyInfo* keydb = (AesKeyInfo*) malloc(STD_BUFFER_SIZE); + u32 nkeys = (keydb) ? LoadKeyDb(path, keydb, STD_BUFFER_SIZE) : 0; // apply all applicable keys for (u32 i = 0; i < nkeys; i++) { AesKeyInfo* info = &(keydb[i]); - if ((info->slot >= 0x40) || ((info->type != 'X') && (info->type != 'Y') && (info->type != 'N') && (info->type != 'I'))) + if ((info->slot >= 0x40) || ((info->type != 'X') && (info->type != 'Y') && (info->type != 'N') && (info->type != 'I'))) { + free(keydb); return 1; // looks faulty, better stop right here + } if (!path && !((1ull<slot)&keyslot_whitelist)) continue; // not in keyslot whitelist if ((info->type == 'I') || (*(info->id)) || (info->keyUnitType && (info->keyUnitType != GetUnitKeysType())) || (CheckKeySlot(info->slot, info->type) == 0)) continue; // most likely valid, but not applicable or already set @@ -219,7 +223,8 @@ u32 InitKeyDb(const char* path) use_aeskey(keyslot); } - return 0; + free(keydb); + return (nkeys) ? 0 : 1; } // creates dependency to "sha.h", not required for base keydb functions @@ -233,10 +238,13 @@ u32 CheckRecommendedKeyDb(const char* path) }; // try to load aeskeydb.bin file - AesKeyInfo* keydb = (AesKeyInfo*) (void*) TEMP_BUFFER; - u32 nkeys = LoadKeyDb(path, keydb, TEMP_BUFFER_SIZE); - if (!nkeys) return 1; + AesKeyInfo* keydb = (AesKeyInfo*) malloc(STD_BUFFER_SIZE); + if (!keydb) return 1; + u32 nkeys = LoadKeyDb(path, keydb, STD_BUFFER_SIZE); // compare with recommended SHA - return sha_cmp(recommended_sha, keydb, nkeys * sizeof(AesKeyInfo), SHA256_MODE); + bool res = (nkeys && (sha_cmp(recommended_sha, keydb, nkeys * sizeof(AesKeyInfo), SHA256_MODE) == 0)); + + free(keydb); + return res ? 0 : 1; } diff --git a/arm9/source/filesys/filetype.c b/arm9/source/filesys/filetype.c index 102e63a..2e8760b 100644 --- a/arm9/source/filesys/filetype.c +++ b/arm9/source/filesys/filetype.c @@ -133,7 +133,7 @@ u64 IdentifyFileType(const char* path) { u64 type = 0; if ((fsize <= SCRIPT_MAX_SIZE) && ext && (strncasecmp(ext, SCRIPT_EXT, strnlen(SCRIPT_EXT, 16) + 1) == 0)) type |= TXT_SCRIPT; // should be a script (which is also generic text) - if (fsize < TEMP_BUFFER_SIZE) type |= TXT_GENERIC; + if (fsize < STD_BUFFER_SIZE) type |= TXT_GENERIC; return type; } else if ((strncmp(path + 2, "/Nintendo DSiWare/", 18) == 0) && (sscanf(fname, "%08lx.bin", &id) == 1) && (strncasecmp(ext, "bin", 4) == 0)) { diff --git a/arm9/source/filesys/fsinit.c b/arm9/source/filesys/fsinit.c index 893c368..f1a0632 100644 --- a/arm9/source/filesys/fsinit.c +++ b/arm9/source/filesys/fsinit.c @@ -23,7 +23,10 @@ bool InitExtFS() { if (fs_mounted[i]) continue; fs_mounted[i] = (f_mount(fs + i, fsname, 1) == FR_OK); if (!fs_mounted[i] && (i == NORM_FS - 1) && !(GetMountState() & IMG_NAND)) { - f_mkfs(fsname, FM_ANY, 0, MAIN_BUFFER, MAIN_BUFFER_SIZE); // format ramdrive if required + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) bkpt; // whatever, this won't go wrong anyways + f_mkfs(fsname, FM_ANY, 0, buffer, STD_BUFFER_SIZE); // format ramdrive if required + free(buffer); f_mount(NULL, fsname, 1); fs_mounted[i] = (f_mount(fs + i, fsname, 1) == FR_OK); } diff --git a/arm9/source/filesys/fsutil.c b/arm9/source/filesys/fsutil.c index 9d01045..fa91841 100644 --- a/arm9/source/filesys/fsutil.c +++ b/arm9/source/filesys/fsutil.c @@ -77,9 +77,14 @@ bool FormatSDCard(u64 hidden_mb, u32 cluster_size, const char* label) { VolToPart[0].pt = 1; // workaround to prevent FatFS rebuilding the MBR InitSDCardFS(); UINT c_size = cluster_size; - bool ret = ((f_mkfs("0:", FM_FAT32, c_size, MAIN_BUFFER, MAIN_BUFFER_SIZE) == FR_OK) || - (f_mkfs("0:", FM_FAT32, 0, MAIN_BUFFER, MAIN_BUFFER_SIZE) == FR_OK)) && + + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) bkpt; // will not happen + bool ret = ((f_mkfs("0:", FM_FAT32, c_size, buffer, STD_BUFFER_SIZE) == FR_OK) || + (f_mkfs("0:", FM_FAT32, 0, buffer, STD_BUFFER_SIZE) == FR_OK)) && (f_setlabel((label) ? label : "0:GM9SD") == FR_OK); + free(buffer); + DeinitSDCardFS(); VolToPart[0].pt = 0; // revert workaround to prevent SD mount problems @@ -91,7 +96,12 @@ bool SetupBonusDrive(void) { return false; ShowString("Formatting drive, please wait..."); if (GetMountState() & IMG_NAND) InitImgFS(NULL); - bool ret = (f_mkfs("8:", FM_ANY, 0, MAIN_BUFFER, MAIN_BUFFER_SIZE) == FR_OK); + + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) bkpt; + bool ret = (f_mkfs("8:", FM_ANY, 0, buffer, STD_BUFFER_SIZE) == FR_OK); + free(buffer); + if (ret) { f_setlabel("8:BONUS"); InitExtFS(); @@ -146,23 +156,32 @@ bool FileGetSha256(const char* path, u8* sha256, u64 offset, u64 size) { if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) return false; + fsize = fvx_size(&file); if (offset + size > fsize) return false; if (!size) size = fsize - offset; fvx_lseek(&file, offset); + + u32 bufsiz = min(STD_BUFFER_SIZE, fsize); + u8* buffer = (u8*) malloc(bufsiz); + if (!buffer) return false; + ShowProgress(0, 0, path); sha_init(SHA256_MODE); - for (u64 pos = 0; (pos < size) && ret; pos += MAIN_BUFFER_SIZE) { - UINT read_bytes = min(MAIN_BUFFER_SIZE, size - pos); + for (u64 pos = 0; (pos < size) && ret; pos += bufsiz) { + UINT read_bytes = min(bufsiz, size - pos); UINT bytes_read = 0; - if (fvx_read(&file, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK) + if (fvx_read(&file, buffer, read_bytes, &bytes_read) != FR_OK) ret = false; if (!ShowProgress(pos + bytes_read, size, path)) ret = false; - sha_update(MAIN_BUFFER, bytes_read); + sha_update(buffer, bytes_read); } + sha_get(sha256); fvx_close(&file); + free(buffer); + ShowProgress(1, 1, path); return ret; @@ -176,20 +195,23 @@ u32 FileFindData(const char* path, u8* data, u32 size_data, u32 offset_file) { if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) return found; + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) return false; + // main routine for (u32 pass = 0; pass < 2; pass++) { bool show_progress = false; u64 pos = (pass == 0) ? offset_file : 0; u64 search_end = (pass == 0) ? fsize : offset_file + size_data; search_end = (search_end > fsize) ? fsize : search_end; - for (; (pos < search_end) && (found == (u64) -1); pos += MAIN_BUFFER_SIZE - (size_data - 1)) { - UINT read_bytes = min(MAIN_BUFFER_SIZE, search_end - pos); + for (; (pos < search_end) && (found == (u64) -1); pos += STD_BUFFER_SIZE - (size_data - 1)) { + UINT read_bytes = min(STD_BUFFER_SIZE, search_end - pos); UINT btr; fvx_lseek(&file, pos); - if ((fvx_read(&file, MAIN_BUFFER, read_bytes, &btr) != FR_OK) || (btr != read_bytes)) + if ((fvx_read(&file, buffer, read_bytes, &btr) != FR_OK) || (btr != read_bytes)) break; for (u32 i = 0; i + size_data <= read_bytes; i++) { - if (memcmp(MAIN_BUFFER + i, data, size_data) == 0) { + if (memcmp(buffer + i, data, size_data) == 0) { found = pos + i; break; } @@ -202,6 +224,8 @@ u32 FileFindData(const char* path, u8* data, u32 size_data, u32 offset_file) { break; } } + + free(buffer); fvx_close(&file); return found; @@ -244,14 +268,17 @@ bool FileInjectFile(const char* dest, const char* orig, u64 off_dest, u64 off_or return false; } + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) return false; + bool ret = true; ShowProgress(0, 0, orig); - for (u64 pos = 0; (pos < size) && ret; pos += MAIN_BUFFER_SIZE) { - UINT read_bytes = min(MAIN_BUFFER_SIZE, size - pos); + for (u64 pos = 0; (pos < size) && ret; pos += STD_BUFFER_SIZE) { + UINT read_bytes = min(STD_BUFFER_SIZE, size - pos); UINT bytes_read = read_bytes; UINT bytes_written = read_bytes; - if ((fvx_read(&ofile, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK) || - (fvx_write(&dfile, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK) || + if ((fvx_read(&ofile, buffer, read_bytes, &bytes_read) != FR_OK) || + (fvx_write(&dfile, buffer, read_bytes, &bytes_written) != FR_OK) || (bytes_read != bytes_written)) ret = false; if (ret && !ShowProgress(pos + bytes_read, size, orig)) { @@ -264,6 +291,7 @@ bool FileInjectFile(const char* dest, const char* orig, u64 off_dest, u64 off_or } ShowProgress(1, 1, orig); + free(buffer); fvx_close(&dfile); fvx_close(&ofile); @@ -290,13 +318,17 @@ bool FileSetByte(const char* dest, u64 offset, u64 size, u8 fillbyte, u32* flags return false; } + u32 bufsiz = min(STD_BUFFER_SIZE, size); + u8* buffer = (u8*) malloc(bufsiz); + if (!buffer) return false; + memset(buffer, fillbyte, bufsiz); + bool ret = true; - memset(MAIN_BUFFER, fillbyte, size); ShowProgress(0, 0, dest); - for (u64 pos = 0; (pos < size) && ret; pos += MAIN_BUFFER_SIZE) { - UINT write_bytes = min(MAIN_BUFFER_SIZE, size - pos); + for (u64 pos = 0; (pos < size) && ret; pos += bufsiz) { + UINT write_bytes = min(bufsiz, size - pos); UINT bytes_written = write_bytes; - if ((fvx_write(&dfile, MAIN_BUFFER, write_bytes, &bytes_written) != FR_OK) || + if ((fvx_write(&dfile, buffer, write_bytes, &bytes_written) != FR_OK) || (write_bytes != bytes_written)) ret = false; if (ret && !ShowProgress(pos + bytes_written, size, dest)) { @@ -309,6 +341,7 @@ bool FileSetByte(const char* dest, u64 offset, u64 size, u8 fillbyte, u32* flags } ShowProgress(1, 1, dest); + free(buffer); fvx_close(&dfile); return ret; @@ -396,7 +429,7 @@ bool PathExist(const char* path) { return (fvx_stat(path, NULL) == FR_OK); } -bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move) { +bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move, u8* buffer, u32 bufsiz) { bool to_virtual = GetVirtualSource(dest); bool silent = (flags && (*flags & SILENT)); bool ret = false; @@ -451,12 +484,14 @@ bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move) { if (oname == NULL) return false; // not a proper origin path *(dname++) = '/'; strncpy(dname, oname++, 256 - (dname - dest)); - bool res = PathMoveCopyRec(dest, orig, flags, move); + bool res = PathMoveCopyRec(dest, orig, flags, move, buffer, bufsiz); *(--dname) = '\0'; if (!res) break; } } + fvx_closedir(&pdir); + *(--fname) = '\0'; } else if (move) { // moving if destination exists if (fvx_stat(dest, &fno) != FR_OK) return false; if (fno.fattrib & AM_DIR) { @@ -495,11 +530,11 @@ bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move) { fvx_sync(&ofile); if (flags && (*flags & CALC_SHA)) sha_init(SHA256_MODE); - for (u64 pos = 0; (pos < fsize) && ret; pos += MAIN_BUFFER_SIZE) { + for (u64 pos = 0; (pos < fsize) && ret; pos += bufsiz) { UINT bytes_read = 0; UINT bytes_written = 0; - if ((fvx_read(&ofile, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK) || - (fvx_write(&dfile, MAIN_BUFFER, bytes_read, &bytes_written) != FR_OK) || + if ((fvx_read(&ofile, buffer, bufsiz, &bytes_read) != FR_OK) || + (fvx_write(&dfile, buffer, bytes_read, &bytes_written) != FR_OK) || (bytes_read != bytes_written)) ret = false; if (ret && !ShowProgress(pos + bytes_read, fsize, orig)) { @@ -510,7 +545,7 @@ bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move) { ShowProgress(pos + bytes_read, fsize, orig); } if (flags && (*flags & CALC_SHA)) - sha_update(MAIN_BUFFER, bytes_read); + sha_update(buffer, bytes_read); } ShowProgress(1, 1, orig); @@ -607,10 +642,16 @@ bool PathMoveCopy(const char* dest, const char* orig, u32* flags, bool move) { // ensure the destination path exists if (flags && (*flags & BUILD_PATH)) fvx_rmkpath(ldest); + // setup buffer + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) return false; + // actual move / copy operation bool same_drv = (strncasecmp(lorig, ldest, 2) == 0); - bool res = PathMoveCopyRec(ldest, lorig, flags, move && same_drv); + bool res = PathMoveCopyRec(ldest, lorig, flags, move && same_drv, buffer, STD_BUFFER_SIZE); if (move && res && (!flags || !(*flags&SKIP_CUR))) PathDelete(lorig); + + free(buffer); return res; } else { // virtual destination handling // can't write an SHA file to a virtual destination @@ -620,7 +661,7 @@ bool PathMoveCopy(const char* dest, const char* orig, u32* flags, bool move) { // handle NAND image unmounts if (ddrvtype & (DRV_SYSNAND|DRV_EMUNAND|DRV_IMAGE)) { FILINFO fno; - // virtual NAND files over 4 MB require unomunt, totally arbitrary limit (hacky!) + // virtual NAND files over 4 MB require unmount, totally arbitrary limit (hacky!) if ((fvx_stat(ldest, &fno) == FR_OK) && (fno.fsize > 4 * 1024 * 1024)) force_unmount = true; } @@ -637,10 +678,16 @@ bool PathMoveCopy(const char* dest, const char* orig, u32* flags, bool move) { return false; } + // setup buffer + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) return false; + // actual virtual copy operation if (force_unmount) DismountDriveType(DriveType(ldest)&(DRV_SYSNAND|DRV_EMUNAND|DRV_IMAGE)); - bool res = PathMoveCopyRec(ldest, lorig, flags, false); + bool res = PathMoveCopyRec(ldest, lorig, flags, false, buffer, STD_BUFFER_SIZE); if (force_unmount) InitExtFS(); + + free(buffer); return res; } } @@ -719,8 +766,8 @@ bool PathAttr(const char* path, u8 attr, u8 mask) { return (f_chmod(path, attr, mask) == FR_OK); } -bool FileSelector(char* result, const char* text, const char* path, const char* pattern, u32 flags) { - DirStruct* contents = (DirStruct*) (void*) TEMP_BUFFER; +bool FileSelectorWorker(char* result, const char* text, const char* path, const char* pattern, u32 flags, void* buffer) { + DirStruct* contents = (DirStruct*) buffer; char path_local[256]; strncpy(path_local, path, 256); @@ -773,7 +820,7 @@ bool FileSelector(char* result, const char* text, const char* path, const char* if (select_dirs) { strncpy(result, res_local->path, 256); return true; - } else if (FileSelector(result, text, res_local->path, pattern, flags)) { + } else if (FileSelectorWorker(result, text, res_local->path, pattern, flags, buffer)) { return true; } break; @@ -791,15 +838,24 @@ bool FileSelector(char* result, const char* text, const char* path, const char* } } +bool FileSelector(char* result, const char* text, const char* path, const char* pattern, u32 flags) { + void* buffer = (void*) malloc(sizeof(DirStruct)); + if (!buffer) return false; + + bool ret = FileSelectorWorker(result, text, path, pattern, flags, buffer); + free(buffer); + return ret; +} + void CreateScreenshot() { + const u32 snap_size = 54 + (400 * 240 * 3 * 2); const u8 bmp_header[54] = { 0x42, 0x4D, 0x36, 0xCA, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x08, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - u8* buffer = MAIN_BUFFER + 54; - u8* buffer_t = buffer + (400 * 240 * 3); + char filename[64]; static u32 n = 0; @@ -810,13 +866,21 @@ void CreateScreenshot() { } if (n >= 1000) return; - memcpy(MAIN_BUFFER, bmp_header, 54); - memset(buffer, 0x1F, 400 * 240 * 3 * 2); + u8* buffer = (u8*) malloc(snap_size); + if (!buffer) return; + + u8* buffer_b = buffer + 54; + u8* buffer_t = buffer_b + (400 * 240 * 3); + + memset(buffer, 0x1F, snap_size); // gray background + memcpy(buffer, bmp_header, 54); for (u32 x = 0; x < 400; x++) for (u32 y = 0; y < 240; y++) memcpy(buffer_t + (y*400 + x) * 3, TOP_SCREEN + (x*240 + y) * 3, 3); for (u32 x = 0; x < 320; x++) for (u32 y = 0; y < 240; y++) - memcpy(buffer + (y*400 + x + 40) * 3, BOT_SCREEN + (x*240 + y) * 3, 3); - FileSetData(filename, MAIN_BUFFER, 54 + (400 * 240 * 3 * 2), 0, true); + memcpy(buffer_b + (y*400 + x + 40) * 3, BOT_SCREEN + (x*240 + y) * 3, 3); + FileSetData(filename, buffer, snap_size, 0, true); + + free(buffer); } diff --git a/arm9/source/filesys/vff.c b/arm9/source/filesys/vff.c index 245f6d4..dc97116 100644 --- a/arm9/source/filesys/vff.c +++ b/arm9/source/filesys/vff.c @@ -194,6 +194,11 @@ FRESULT fvx_qwrite (const TCHAR* path, const void* buff, FSIZE_t ofs, UINT btw, return res; } +FSIZE_t fvx_qsize (const TCHAR* path) { + FILINFO fno; + return (fvx_stat(path, &fno) == FR_OK) ? fno.fsize : 0; +} + #if !_LFN_UNICODE // this will not work for unicode FRESULT worker_fvx_rmkdir (TCHAR* tpath) { DIR tmp_dir; diff --git a/arm9/source/filesys/vff.h b/arm9/source/filesys/vff.h index 406e100..0fa1e6b 100644 --- a/arm9/source/filesys/vff.h +++ b/arm9/source/filesys/vff.h @@ -32,6 +32,9 @@ FRESULT fvx_readdir (DIR* dp, FILINFO* fno); FRESULT fvx_qread (const TCHAR* path, void* buff, FSIZE_t ofs, UINT btr, UINT* br); FRESULT fvx_qwrite (const TCHAR* path, const void* buff, FSIZE_t ofs, UINT btw, UINT* bw); +// additional quick file info functions +FSIZE_t fvx_qsize (const TCHAR* path); + // additional recursive functions FRESULT fvx_rmkdir (const TCHAR* path); FRESULT fvx_rmkpath (const TCHAR* path); diff --git a/arm9/source/game/ncch.c b/arm9/source/game/ncch.c index 6311f13..84df277 100644 --- a/arm9/source/game/ncch.c +++ b/arm9/source/game/ncch.c @@ -63,6 +63,10 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) { return 0; } + // setup a large enough buffer + u8* buffer = (u8*) malloc(max(STD_BUFFER_SIZE, SEEDSAVE_MAX_ENTRIES*(8+16)*2)); + if (!buffer) return 1; + // try to grab the seed from NAND database const u32 seed_offset[2] = {SEEDSAVE_AREA_OFFSETS}; const char* nand_drv[] = {"1:", "4:"}; // SysNAND and EmuNAND @@ -87,8 +91,8 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) { // check seedsave for seed u8* seeddb[2]; - seeddb[0] = (u8*) (TEMP_BUFFER + (TEMP_BUFFER_SIZE/2)); - seeddb[1] = seeddb[0] + (SEEDSAVE_MAX_ENTRIES*(8+16)); + seeddb[0] = buffer; + seeddb[1] = buffer + (SEEDSAVE_MAX_ENTRIES*(8+16)); if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) continue; f_read(&file, seeddb[0], 0x200, &btr); @@ -110,6 +114,7 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) { if (hash_seed == sha256sum[0]) { memcpy(seed, lseed, 16); f_close(&file); + free(buffer); return 0; // found! } } @@ -119,8 +124,8 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) { } // not found -> try seeddb.bin - SeedInfo* seeddb = (SeedInfo*) (TEMP_BUFFER + (TEMP_BUFFER_SIZE/2)); - size_t len = LoadSupportFile(SEEDDB_NAME, seeddb, (TEMP_BUFFER_SIZE/2)); + SeedInfo* seeddb = (SeedInfo*) (void*) buffer; + size_t len = LoadSupportFile(SEEDDB_NAME, seeddb, STD_BUFFER_SIZE); if (len && (seeddb->n_entries <= (len - 16) / 32)) { // check filesize / seeddb size for (u32 s = 0; s < seeddb->n_entries; s++) { if (titleId != seeddb->entries[s].titleId) @@ -129,12 +134,14 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) { sha_quick(sha256sum, lseed, 16 + 8, SHA256_MODE); if (hash_seed == sha256sum[0]) { memcpy(seed, lseed, 16); + free(buffer); return 0; // found! } } } // out of options -> failed! + free(buffer); return 1; } diff --git a/arm9/source/game/ticket.c b/arm9/source/game/ticket.c index 2cd680e..3ffa598 100644 --- a/arm9/source/game/ticket.c +++ b/arm9/source/game/ticket.c @@ -131,11 +131,13 @@ u32 FindTicket(Ticket* ticket, u8* title_id, bool force_legit, bool emunand) { u32 FindTitleKey(Ticket* ticket, u8* title_id) { bool found = false; + TitleKeysInfo* tikdb = (TitleKeysInfo*) malloc(STD_BUFFER_SIZE); // more than enough + if (!tikdb) return 1; + // search for a titlekey inside encTitleKeys.bin / decTitleKeys.bin // when found, add it to the ticket for (u32 enc = 0; (enc <= 1) && !found; enc++) { - TitleKeysInfo* tikdb = (TitleKeysInfo*) (TEMP_BUFFER + (TEMP_BUFFER_SIZE/2)); - u32 len = LoadSupportFile((enc) ? TIKDB_NAME_ENC : TIKDB_NAME_DEC, tikdb, (TEMP_BUFFER_SIZE/2)); + u32 len = LoadSupportFile((enc) ? TIKDB_NAME_ENC : TIKDB_NAME_DEC, tikdb, STD_BUFFER_SIZE); if (len == 0) continue; // file not found if (tikdb->n_entries > (len - 16) / 32) @@ -153,6 +155,7 @@ u32 FindTitleKey(Ticket* ticket, u8* title_id) { } } + free(tikdb); return (found) ? 0 : 1; } diff --git a/arm9/source/game/tmd.c b/arm9/source/game/tmd.c index 963182c..42d2d5e 100644 --- a/arm9/source/game/tmd.c +++ b/arm9/source/game/tmd.c @@ -35,7 +35,7 @@ u32 FixTmdHashes(TitleMetaData* tmd) { u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents, u32 save_size) { const u8 sig_type[4] = { TMD_SIG_TYPE }; // safety check: number of contents - if (n_contents > TMD_MAX_CONTENTS) return 1; // !!! + if (n_contents > TMD_MAX_CONTENTS) return 1; // potential incompatibility here (!) // set TMD all zero for a clean start memset(tmd, 0x00, TMD_SIZE_N(n_contents)); // file TMD values diff --git a/arm9/source/godmode.c b/arm9/source/godmode.c index 78ae499..9bd4801 100644 --- a/arm9/source/godmode.c +++ b/arm9/source/godmode.c @@ -46,6 +46,7 @@ typedef struct { u32 SplashInit(const char* modestr) { u64 splash_size; u8* splash = FindVTarFileInfo(VRAM0_SPLASH_PCX, &splash_size); + u8* bitmap = (u8*) malloc(SCREEN_SIZE_TOP); const char* namestr = FLAVOR " " VERSION; const char* loadstr = "booting..."; const u32 pos_xb = 10; @@ -55,9 +56,9 @@ u32 SplashInit(const char* modestr) { ClearScreenF(true, true, COLOR_STD_BG); - if (splash && PCX_Decompress(TEMP_BUFFER, TEMP_BUFFER_SIZE, splash, splash_size)) { + if (splash && bitmap && PCX_Decompress(bitmap, SCREEN_SIZE_TOP, splash, splash_size)) { PCXHdr* hdr = (PCXHdr*) (void*) splash; - DrawBitmap(TOP_SCREEN, -1, -1, PCX_Width(hdr), PCX_Height(hdr), TEMP_BUFFER); + DrawBitmap(TOP_SCREEN, -1, -1, PCX_Width(hdr), PCX_Height(hdr), bitmap); } else DrawStringF(TOP_SCREEN, 10, 10, COLOR_STD_FONT, COLOR_TRANSPARENT, "(" VRAM0_SPLASH_PCX " not found)"); if (modestr) DrawStringF(TOP_SCREEN, SCREEN_WIDTH_TOP - 10 - GetDrawStringWidth(modestr), SCREEN_HEIGHT - 10 - GetDrawStringHeight(modestr), COLOR_STD_FONT, COLOR_TRANSPARENT, modestr); @@ -70,6 +71,7 @@ u32 SplashInit(const char* modestr) { DrawStringF(BOT_SCREEN, pos_xu, pos_yu, COLOR_STD_FONT, COLOR_STD_BG, loadstr); DrawStringF(BOT_SCREEN, pos_xb, pos_yu, COLOR_STD_FONT, COLOR_STD_BG, "built: " DBUILTL); + if (bitmap) free(bitmap); return 0; } @@ -391,40 +393,50 @@ u32 SdFormatMenu(void) { } u32 FileGraphicsViewer(const char* path) { + const u32 max_size = SCREEN_SIZE(ALT_SCREEN); u64 filetype = IdentifyFileType(path); - u8* bitmap = TEMP_BUFFER; - u32 buffer_size = TEMP_BUFFER_SIZE / 2; + u8* bitmap = (u8*) malloc(max_size); + u8* input = (u8*) malloc(max_size); u32 w = 0; u32 h = 0; + u32 ret = 1; - if (filetype & GFX_PCX) { - u8* pcx = TEMP_BUFFER + TEMP_BUFFER_SIZE / 2; - u32 pcx_size = FileGetData(path, pcx, TEMP_BUFFER_SIZE / 2, 0); - if ((pcx_size > 0) && (pcx_size < TEMP_BUFFER_SIZE / 2) && - (PCX_Decompress(bitmap, buffer_size, pcx, pcx_size))) { - PCXHdr* hdr = (PCXHdr*) (void*) pcx; - w = PCX_Width(hdr); - h = PCX_Height(hdr); + if (!bitmap || !input) { + if (bitmap) free(bitmap); + if (input) free(input); + return 1; + } + + u32 input_size = FileGetData(path, input, max_size, 0); + if (input_size && (input_size < max_size)) { + if (filetype & GFX_PCX) { + if (PCX_Decompress(bitmap, max_size, input, input_size)) { + PCXHdr* hdr = (PCXHdr*) (void*) input; + w = PCX_Width(hdr); + h = PCX_Height(hdr); + ret = 0; + } } } - if (w && h && (w < SCREEN_WIDTH(ALT_SCREEN)) && (h < SCREEN_HEIGHT)) { + if ((ret == 0) && w && h && (w < SCREEN_WIDTH(ALT_SCREEN)) && (h < SCREEN_HEIGHT)) { ClearScreenF(true, true, COLOR_STD_BG); DrawBitmap(ALT_SCREEN, -1, -1, w, h, bitmap); ShowString("Press to continue"); InputWait(0); ClearScreenF(true, true, COLOR_STD_BG); - return 0; - } + } else ret = 1; - return 1; + free(bitmap); + free(input); + return ret; } u32 FileHexViewer(const char* path) { const u32 max_data = (SCREEN_HEIGHT / FONT_HEIGHT_EXT) * 16 * ((FONT_WIDTH_EXT > 4) ? 1 : 2); static u32 mode = 0; - u8* data = TEMP_BUFFER; - u8* bottom_cpy = TEMP_BUFFER + 0xC0000; // a copy of the bottom screen framebuffer + u8* data = NULL; + u8* bottom_cpy = (u8*) malloc(SCREEN_SIZE_BOT); // a copy of the bottom screen framebuffer u32 fsize = FileGetSize(path); bool dual_screen = 0; @@ -443,11 +455,18 @@ u32 FileHexViewer(const char* path) { static const u32 edit_bsize = 0x4000; // should be multiple of 0x200 * 2 bool edit_mode = false; - u8* edit_buffer = TEMP_BUFFER; - u8* edit_buffer_cpy = TEMP_BUFFER + edit_bsize; + u8* buffer = (u8*) malloc(edit_bsize); + u8* buffer_cpy = (u8*) malloc(edit_bsize); u32 edit_start = 0; int cursor = 0; + if (!bottom_cpy || !buffer || !buffer_cpy) { + if (bottom_cpy) free(bottom_cpy); + if (buffer) free(buffer); + if (buffer_cpy) free(buffer_cpy); + return 1; + } + static bool show_instr = true; static const char* instr = "Hexeditor Controls:\n \n\x18\x19\x1A\x1B(+R) - Scroll\nR+Y - Switch view\nX - Search / goto...\nA - Enter edit mode\nA+\x18\x19\x1A\x1B - Edit value\nB - Exit\n"; if (show_instr) { // show one time instructions @@ -458,6 +477,7 @@ u32 FileHexViewer(const char* path) { if (MAIN_SCREEN != TOP_SCREEN) ShowString(instr); memcpy(bottom_cpy, BOT_SCREEN, (SCREEN_HEIGHT * SCREEN_WIDTH_BOT * 3)); + data = buffer; while (true) { if (mode != last_mode) { if (FONT_WIDTH_EXT <= 5) { @@ -547,7 +567,7 @@ u32 FileHexViewer(const char* path) { if ((offset < edit_start) || (offset + max_data > edit_start + edit_bsize)) offset = last_offset; // we don't expect this to happen total_data = (fsize - offset >= max_data) ? max_data : fsize - offset; - data = edit_buffer + (offset - edit_start); + data = buffer + (offset - edit_start); } last_offset = offset; } @@ -674,20 +694,20 @@ u32 FileHexViewer(const char* path) { cursor = 0; edit_start = ((offset - (offset % 0x200) <= (edit_bsize / 2)) || (fsize < edit_bsize)) ? 0 : offset - (offset % 0x200) - (edit_bsize / 2); - FileGetData(path, edit_buffer, edit_bsize, edit_start); - memcpy(edit_buffer_cpy, edit_buffer, edit_bsize); - data = edit_buffer + (offset - edit_start); + FileGetData(path, buffer, edit_bsize, edit_start); + memcpy(buffer_cpy, buffer, edit_bsize); + data = buffer + (offset - edit_start); } else edit_mode = false; } else { // editor mode if (pad_state & (BUTTON_B|BUTTON_START)) { edit_mode = false; // check for user edits u32 diffs = 0; - for (u32 i = 0; i < edit_bsize; i++) if (edit_buffer[i] != edit_buffer_cpy[i]) diffs++; + for (u32 i = 0; i < edit_bsize; i++) if (buffer[i] != buffer_cpy[i]) diffs++; if (diffs && ShowPrompt(true, "You made edits in %i place(s).\nWrite changes to file?", diffs)) - if (!FileSetData(path, edit_buffer, min(edit_bsize, (fsize - edit_start)), edit_start, false)) + if (!FileSetData(path, buffer, min(edit_bsize, (fsize - edit_start)), edit_start, false)) ShowPrompt(false, "Failed writing to file!"); - data = TEMP_BUFFER; + data = buffer; last_offset = (u32) -1; // force reload from file } else if (pad_state & BUTTON_A) { if (pad_state & BUTTON_DOWN) data[cursor]--; @@ -720,9 +740,12 @@ u32 FileHexViewer(const char* path) { } ClearScreen(TOP_SCREEN, COLOR_STD_BG); - if (MAIN_SCREEN == TOP_SCREEN) memcpy(BOT_SCREEN, bottom_cpy, (SCREEN_HEIGHT * SCREEN_WIDTH_BOT * 3)); + if (MAIN_SCREEN == TOP_SCREEN) memcpy(BOT_SCREEN, bottom_cpy, (SCREEN_SIZE_BOT)); else ClearScreen(BOT_SCREEN, COLOR_STD_BG); + free(bottom_cpy); + free(buffer); + free(buffer_cpy); return 0; } @@ -828,7 +851,7 @@ u32 BootFirmHandler(const char* bootpath, bool verbose, bool delete) { size_t firm_size = FileGetSize(bootpath); if (!firm_size) return 1; - if (firm_size > TEMP_BUFFER_SIZE) { + if (firm_size > FIRM_MAX_SIZE) { if (verbose) ShowPrompt(false, "%s\nFIRM too big, can't boot", pathstr); // unlikely return 1; } @@ -837,10 +860,12 @@ u32 BootFirmHandler(const char* bootpath, bool verbose, bool delete) { pathstr, firm_size / 1024)) return 1; - void* firm = (void*) TEMP_BUFFER; + void* firm = (void*) malloc(FIRM_MAX_SIZE); + if (!firm) return 1; if ((FileGetData(bootpath, firm, firm_size, 0) != firm_size) || !IsBootableFirm(firm, firm_size)) { if (verbose) ShowPrompt(false, "%s\nNot a bootable FIRM.", pathstr); + free(firm); return 1; } @@ -849,8 +874,10 @@ u32 BootFirmHandler(const char* bootpath, bool verbose, bool delete) { FirmA9LHeader* a9l = (FirmA9LHeader*)(void*) ((u8*) firm + arm9s->offset); if (verbose && (ValidateFirmA9LHeader(a9l) == 0) && ShowPrompt(true, "%s\nFIRM is encrypted.\n \nDecrypt before boot?", pathstr) && - (DecryptFirmFull(firm, firm_size) != 0)) + (DecryptFirmFull(firm, firm_size) != 0)) { + free(firm); return 1; + } // unsupported location handling char fixpath[256] = { 0 }; @@ -876,6 +903,7 @@ u32 BootFirmHandler(const char* bootpath, bool verbose, bool delete) { } // a return was not intended + free(firm); return 1; } @@ -1640,9 +1668,12 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan return 0; } else if (user_select == font) { // set font - u32 pbm_size = FileGetData(file_path, TEMP_BUFFER, TEMP_BUFFER_SIZE, 0); - if (pbm_size) SetFontFromPbm(TEMP_BUFFER, pbm_size); + u8* pbm = (u8*) malloc(0x10000); // arbitrary, should be enough by far + if (!pbm) return 1; + u32 pbm_size = FileGetData(file_path, pbm, 0x10000, 0); + if (pbm_size) SetFontFromPbm(pbm, pbm_size); ClearScreenF(true, true, COLOR_STD_BG); + free(pbm); return 0; } else if (user_select == view) { // view gfx @@ -1652,15 +1683,18 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan } else if (user_select == agbexport) { // export GBA VC save if (DumpGbaVcSavegame(file_path) == 0) - ShowPrompt(false, "Savegame dumped to " OUTPUT_PATH); + ShowPrompt(false, "Savegame dumped to " OUTPUT_PATH "."); else ShowPrompt(false, "Savegame dump failed!"); return 0; } else if (user_select == agbimport) { // import GBA VC save if (clipboard->n_entries != 1) { ShowPrompt(false, "GBA VC savegame has to\nbe in the clipboard."); - } else ShowPrompt(false, "Savegame inject %s", - (InjectGbaVcSavegame(file_path, clipboard->entry[0].path) == 0) ? "success" : "failed!"); + } else { + ShowPrompt(false, "Savegame inject %s.", + (InjectGbaVcSavegame(file_path, clipboard->entry[0].path) == 0) ? "success" : "failed!"); + clipboard->n_entries = 0; + } return 0; } @@ -1789,9 +1823,11 @@ u32 HomeMoreMenu(char* current_path) { return 0; } else if (user_select == sysinfo) { // Myria's system info - char* sysinfo_txt = (char*) TEMP_BUFFER; + char* sysinfo_txt = (char*) malloc(STD_BUFFER_SIZE); + if (!sysinfo_txt) return 1; MyriaSysinfo(sysinfo_txt); - MemTextViewer(sysinfo_txt, strnlen(sysinfo_txt, TEMP_BUFFER_SIZE), 1, false); + MemTextViewer(sysinfo_txt, strnlen(sysinfo_txt, STD_BUFFER_SIZE), 1, false); + free(sysinfo_txt); return 0; } else if (user_select == readme) { // Display GodMode9 readme @@ -1821,7 +1857,7 @@ u32 GodMode(int entrypoint) { bool bootloader = IS_SIGHAX && (entrypoint == ENTRY_NANDBOOT); bool bootmenu = bootloader && (BOOTMENU_KEY != BUTTON_START) && CheckButton(BOOTMENU_KEY); bool godmode9 = !bootloader; - FirmHeader* firm_in_mem = (FirmHeader*) (void*) (TEMP_BUFFER + TEMP_BUFFER_SIZE); // should be safe here + /*FirmHeader* firm_in_mem = (FirmHeader*) (void*) (TEMP_BUFFER + TEMP_BUFFER_SIZE); // should be safe here memcpy(firm_in_mem, "NOPE", 4); // to prevent bootloops if (bootloader) { // check for FIRM in FCRAM, but prevent bootloops for (u8* addr = (u8*) 0x20000200; addr < (u8*) 0x22000000; addr += 0x400000) { @@ -1831,7 +1867,7 @@ u32 GodMode(int entrypoint) { if (memcmp(firm_in_mem, "FIRM", 4) != 0) memmove(firm_in_mem, addr, firm_size); if (memcmp(addr, "FIRM", 4) == 0) memcpy(addr, "NOPE", 4); // prevent bootloops } - } + }*/ // get mode string for splash screen const char* disp_mode = NULL; @@ -1931,7 +1967,7 @@ u32 GodMode(int entrypoint) { // bootloader handler if (bootloader) { const char* bootfirm_paths[] = { BOOTFIRM_PATHS }; - if (IsBootableFirm(firm_in_mem, FIRM_MAX_SIZE)) BootFirm(firm_in_mem, "sdmc:/bootonce.firm"); + // if (IsBootableFirm(firm_in_mem, FIRM_MAX_SIZE)) BootFirm(firm_in_mem, "sdmc:/bootonce.firm"); for (u32 i = 0; i < sizeof(bootfirm_paths) / sizeof(char*); i++) { BootFirmHandler(bootfirm_paths[i], false, (BOOTFIRM_TEMPS >> i) & 0x1); } @@ -2016,7 +2052,7 @@ u32 GodMode(int entrypoint) { } } else if (user_select == fixcmac) { RecursiveFixFileCmac(curr_entry->path); - ClearScreenF(true, false, COLOR_STD_BG); + ShowPrompt(false, "Fix CMACs for drive finished."); } else if (user_select == dirnfo) { bool is_drive = (!*current_path); FILINFO fno; @@ -2129,6 +2165,7 @@ u32 GodMode(int entrypoint) { GetDirContents(current_dir, current_path); } else if (switched && (pad_state & BUTTON_DOWN)) { // force reload file list GetDirContents(current_dir, current_path); + ClearScreenF(true, true, COLOR_STD_BG); } else if ((pad_state & BUTTON_RIGHT) && !(pad_state & BUTTON_L1)) { // cursor down (quick) cursor += quick_stp; } else if ((pad_state & BUTTON_LEFT) && !(pad_state & BUTTON_L1)) { // cursor up (quick) diff --git a/arm9/source/start.s b/arm9/source/start.s index d6b60cc..67e9643 100644 --- a/arm9/source/start.s +++ b/arm9/source/start.s @@ -5,6 +5,7 @@ #include #include #include +#include .global _start _start: @@ -71,6 +72,15 @@ _start: mov r0, #0x10000000 mov r1, #0x340 str r1, [r0, #0x20] + + @ Setup heap + ldr r0, =fake_heap_start + ldr r1, =__HEAP_ADDR + str r1, [r0] + + ldr r0, =fake_heap_end + ldr r1, =__HEAP_END + str r1, [r0] @ Install exception handlers ldr r0, =XRQ_Start diff --git a/arm9/source/system/memmap.h b/arm9/source/system/memmap.h new file mode 100644 index 0000000..77c0a26 --- /dev/null +++ b/arm9/source/system/memmap.h @@ -0,0 +1,12 @@ +# pragma once + +// not complete! (!!!) + +#define __RAMDRV_ADDR 0x22800000 +#define __RAMDRV_END 0x28000000 + +#define __STACK_ADDR (__RAMDRV_ADDR - 0x800000) +#define __STACK_END __RAMDRV_ADDR + +#define __HEAP_ADDR 0x20700000 +#define __HEAP_END __STACK_ADDR diff --git a/arm9/source/utils/ctrtransfer.c b/arm9/source/utils/ctrtransfer.c index c4a60db..dd63d5a 100644 --- a/arm9/source/utils/ctrtransfer.c +++ b/arm9/source/utils/ctrtransfer.c @@ -53,13 +53,14 @@ u32 TransferCtrNandImage(const char* path_img, const char* drv) { } // CTRNAND preparations - SecureInfo* secnfo_img = (SecureInfo*) TEMP_BUFFER; - SecureInfo* secnfo_loc = (SecureInfo*) (TEMP_BUFFER + 0x200); - char* path_secnfo_a = (char*) (TEMP_BUFFER + 0x400); - char* path_secnfo_b = (char*) (TEMP_BUFFER + 0x420); - char* path_secnfo_c = (char*) (TEMP_BUFFER + 0x440); - char* path_tickdb = (char*) (TEMP_BUFFER + 0x460); - char* path_tickdb_bak = (char*) (TEMP_BUFFER + 0x480); + SecureInfo secnfo_img; + SecureInfo secnfo_loc; + char path_secnfo_a[32]; + char path_secnfo_b[32]; + char path_secnfo_c[32]; + char path_tickdb[32]; + char path_tickdb_bak[32]; + snprintf(path_secnfo_a, 32, "%s/rw/sys/SecureInfo_A", drv); snprintf(path_secnfo_b, 32, "%s/rw/sys/SecureInfo_B", drv); snprintf(path_secnfo_c, 32, "%s/rw/sys/SecureInfo_C", drv); @@ -68,13 +69,13 @@ u32 TransferCtrNandImage(const char* path_img, const char* drv) { // special handling for out of region images (create SecureInfo_C) PathDelete(path_secnfo_c); // not required when transfering back to original region - if (((FileGetData("7:/rw/sys/SecureInfo_A", (u8*) secnfo_img, sizeof(SecureInfo), 0) == sizeof(SecureInfo)) || - (FileGetData("7:/rw/sys/SecureInfo_B", (u8*) secnfo_img, sizeof(SecureInfo), 0) == sizeof(SecureInfo))) && - ((FileGetData(path_secnfo_a, (u8*) secnfo_loc, sizeof(SecureInfo), 0) == sizeof(SecureInfo)) || - (FileGetData(path_secnfo_b, (u8*) secnfo_loc, sizeof(SecureInfo), 0) == sizeof(SecureInfo))) && - (secnfo_img->region != secnfo_loc->region)) { - secnfo_loc->region = secnfo_img->region; - FileSetData(path_secnfo_c, (u8*) secnfo_loc, sizeof(SecureInfo), 0, true); + if (((FileGetData("7:/rw/sys/SecureInfo_A", (u8*) &secnfo_img, sizeof(SecureInfo), 0) == sizeof(SecureInfo)) || + (FileGetData("7:/rw/sys/SecureInfo_B", (u8*) &secnfo_img, sizeof(SecureInfo), 0) == sizeof(SecureInfo))) && + ((FileGetData(path_secnfo_a, (u8*) &secnfo_loc, sizeof(SecureInfo), 0) == sizeof(SecureInfo)) || + (FileGetData(path_secnfo_b, (u8*) &secnfo_loc, sizeof(SecureInfo), 0) == sizeof(SecureInfo))) && + (secnfo_img.region != secnfo_loc.region)) { + secnfo_loc.region = secnfo_img.region; + FileSetData(path_secnfo_c, (u8*) &secnfo_loc, sizeof(SecureInfo), 0, true); } // make a backup of ticket.db PathDelete(path_tickdb_bak); diff --git a/arm9/source/utils/gameutil.c b/arm9/source/utils/gameutil.c index 366ffa1..5550d34 100644 --- a/arm9/source/utils/gameutil.c +++ b/arm9/source/utils/gameutil.c @@ -44,16 +44,21 @@ u32 CheckNcchHash(u8* expected, FIL* file, u32 size_data, u32 offset_ncch, NcchH u32 offset_data = fvx_tell(file) - offset_ncch; u8 hash[32]; + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) return 1; + sha_init(SHA256_MODE); - for (u32 i = 0; i < size_data; i += MAIN_BUFFER_SIZE) { - u32 read_bytes = min(MAIN_BUFFER_SIZE, (size_data - i)); + for (u32 i = 0; i < size_data; i += STD_BUFFER_SIZE) { + u32 read_bytes = min(STD_BUFFER_SIZE, (size_data - i)); UINT bytes_read; - fvx_read(file, MAIN_BUFFER, read_bytes, &bytes_read); - DecryptNcch(MAIN_BUFFER, offset_data + i, read_bytes, ncch, exefs); - sha_update(MAIN_BUFFER, read_bytes); + fvx_read(file, buffer, read_bytes, &bytes_read); + DecryptNcch(buffer, offset_data + i, read_bytes, ncch, exefs); + sha_update(buffer, read_bytes); } sha_get(hash); + free(buffer); + return (memcmp(hash, expected, 32) == 0) ? 0 : 1; } @@ -211,8 +216,6 @@ u32 LoadCdnTicketFile(Ticket* ticket, const char* path_cnt) { u32 GetTmdContentPath(char* path_content, const char* path_tmd) { // get path to TMD first content const u8 dlc_tid_high[] = { DLC_TID_HIGH }; - TitleMetaData* tmd = (TitleMetaData*) TEMP_BUFFER; - TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1); // content path string char* name_content; @@ -222,11 +225,17 @@ u32 GetTmdContentPath(char* path_content, const char* path_tmd) { name_content++; // load TMD file - if ((LoadTmdFile(tmd, path_tmd) != 0) || !getbe16(tmd->content_count)) + TitleMetaData* tmd = (TitleMetaData*) malloc(TMD_SIZE_MAX); + TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1); + if (!tmd) return 1; + if ((LoadTmdFile(tmd, path_tmd) != 0) || !getbe16(tmd->content_count)) { + free(tmd); return 1; + } snprintf(name_content, 256 - (name_content - path_content), (memcmp(tmd->title_id, dlc_tid_high, sizeof(dlc_tid_high)) == 0) ? "00000000/%08lx.app" : "%08lx.app", getbe32(chunk->id)); + free(tmd); return 0; } @@ -268,17 +277,24 @@ u32 VerifyTmdContent(const char* path, u64 offset, TmdContentChunk* chunk, const } fvx_lseek(&file, offset); + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) { + fvx_close(&file); + return 1; + } + GetTmdCtr(ctr, chunk); sha_init(SHA256_MODE); - for (u32 i = 0; i < size; i += MAIN_BUFFER_SIZE) { - u32 read_bytes = min(MAIN_BUFFER_SIZE, (size - i)); + for (u32 i = 0; i < size; i += STD_BUFFER_SIZE) { + u32 read_bytes = min(STD_BUFFER_SIZE, (size - i)); UINT bytes_read; - fvx_read(&file, MAIN_BUFFER, read_bytes, &bytes_read); - if (encrypted) DecryptCiaContentSequential(MAIN_BUFFER, read_bytes, ctr, titlekey); - sha_update(MAIN_BUFFER, read_bytes); + fvx_read(&file, buffer, read_bytes, &bytes_read); + if (encrypted) DecryptCiaContentSequential(buffer, read_bytes, ctr, titlekey); + sha_update(buffer, read_bytes); if (!ShowProgress(i + read_bytes, size, path)) break; } sha_get(hash); + free(buffer); fvx_close(&file); return memcmp(hash, expected, 32); @@ -399,10 +415,12 @@ u32 VerifyNcsdFile(const char* path) { } u32 VerifyCiaFile(const char* path) { - CiaStub* cia = (CiaStub*) TEMP_BUFFER; + CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub)); CiaInfo info; u8 titlekey[16]; + if (!cia) return 1; + // path string char pathstr[32 + 1]; TruncateString(pathstr, path, 32, 8); @@ -412,6 +430,7 @@ u32 VerifyCiaFile(const char* path) { (GetCiaInfo(&info, &(cia->header)) != 0) || (GetTitleKey(titlekey, &(cia->ticket)) != 0)) { ShowPrompt(false, "%s\nError: Probably not a CIA file", pathstr); + free(cia); return 1; } @@ -423,20 +442,18 @@ u32 VerifyCiaFile(const char* path) { if (VerifyTmdContent(path, next_offset, chunk, titlekey) != 0) { ShowPrompt(false, "%s\nID %08lX (%08llX@%08llX)\nVerification failed", pathstr, getbe32(chunk->id), getbe64(chunk->size), next_offset, i); + free(cia); return 1; } next_offset += getbe64(chunk->size); } + free(cia); return 0; } u32 VerifyTmdFile(const char* path, bool cdn) { const u8 dlc_tid_high[] = { DLC_TID_HIGH }; - TitleMetaData* tmd = (TitleMetaData*) TEMP_BUFFER; - TmdContentChunk* content_list = (TmdContentChunk*) (tmd + 1); - Ticket* ticket = (Ticket*) (TEMP_BUFFER + TMD_SIZE_MAX); - u8 titlekey[0x10] = { 0xFF }; // path string char pathstr[32 + 1]; @@ -451,17 +468,23 @@ u32 VerifyTmdFile(const char* path, bool cdn) { name_content++; // load TMD file + TitleMetaData* tmd = (TitleMetaData*) malloc(TMD_SIZE_MAX); + TmdContentChunk* content_list = (TmdContentChunk*) (tmd + 1); if (LoadTmdFile(tmd, path) != 0) { ShowPrompt(false, "%s\nError: TMD probably corrupted", pathstr); + free(tmd); return 1; } + u8 titlekey[0x10] = { 0xFF }; if (cdn) { // load / build ticket (for titlekey / CDN only) - if (!((LoadCdnTicketFile(ticket, path) == 0) || - ((BuildFakeTicket(ticket, tmd->title_id) == 0) && - (FindTitleKey(ticket, tmd->title_id) == 0))) || - (GetTitleKey(titlekey, ticket) != 0)) { + Ticket ticket; + if (!((LoadCdnTicketFile(&ticket, path) == 0) || + ((BuildFakeTicket(&ticket, tmd->title_id) == 0) && + (FindTitleKey(&ticket, tmd->title_id) == 0))) || + (GetTitleKey(titlekey, &ticket) != 0)) { ShowPrompt(false, "%s\nError: CDN titlekey not found", pathstr); + free(tmd); return 1; } } @@ -477,66 +500,59 @@ u32 VerifyTmdFile(const char* path, bool cdn) { TruncateString(pathstr, path_content, 32, 8); if (VerifyTmdContent(path_content, 0, chunk, titlekey) != 0) { ShowPrompt(false, "%s\nVerification failed", pathstr); + free(tmd); return 1; } } + free(tmd); return 0; } u32 VerifyFirmFile(const char* path) { - FirmHeader header; - FIL file; - UINT btr; - char pathstr[32 + 1]; TruncateString(pathstr, path, 32, 8); - // open file, get FIRM header - if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) - return 1; - fvx_lseek(&file, 0); - if ((fvx_read(&file, &header, sizeof(FirmHeader), &btr) != FR_OK) || - (ValidateFirmHeader(&header, fvx_size(&file)) != 0)) { - fvx_close(&file); + void* firm_buffer = (void*) malloc(FIRM_MAX_SIZE); + if (!firm_buffer) return 1; + + // load the whole FIRM into memory + u32 firm_size = fvx_qsize(path); + if ((firm_size > FIRM_MAX_SIZE) || (fvx_qread(path, firm_buffer, 0, firm_size, NULL) != FR_OK) || + (ValidateFirmHeader(firm_buffer, firm_size) != 0)) { + free(firm_buffer); return 1; } // hash verify all available sections - for (u32 i = 0; i < 4; i++) { - FirmSectionHeader* section = header.sections + i; - u32 size = section->size; - if (!size) continue; - fvx_lseek(&file, section->offset); - sha_init(SHA256_MODE); - for (u32 i = 0; i < size; i += MAIN_BUFFER_SIZE) { - u32 read_bytes = min(MAIN_BUFFER_SIZE, (size - i)); - fvx_read(&file, MAIN_BUFFER, read_bytes, &btr); - sha_update(MAIN_BUFFER, read_bytes); - } - u8 hash[0x20]; - sha_get(hash); - if (memcmp(hash, section->hash, 0x20) != 0) { + FirmHeader header; + memcpy(&header, firm_buffer, sizeof(FirmHeader)); + for (u32 i = 0; i < 4; i++) { + FirmSectionHeader* sct = header.sections + i; + void* section = ((u8*) firm_buffer) + sct->offset; + if (!(sct->size)) continue; + if (sha_cmp(sct->hash, section, sct->size, SHA256_MODE) != 0) { ShowPrompt(false, "%s\nSection %u hash mismatch", pathstr, i); - fvx_close(&file); + free(firm_buffer); return 1; } } - fvx_close(&file); // no arm11 / arm9 entrypoints? if (!header.entry_arm9) { ShowPrompt(false, "%s\nARM9 entrypoint is missing", pathstr); + free(firm_buffer); return 1; } else if (!header.entry_arm11) { ShowPrompt(false, "%s\nWarning: ARM11 entrypoint is missing", pathstr); } + free(firm_buffer); return 0; } u32 VerifyBossFile(const char* path) { - BossHeader* boss = (BossHeader*) TEMP_BUFFER; + BossHeader boss; u32 payload_size; bool encrypted = false; FIL file; @@ -550,41 +566,53 @@ u32 VerifyBossFile(const char* path) { if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) return 1; fvx_lseek(&file, 0); - if ((fvx_read(&file, boss, sizeof(BossHeader), &btr) != FR_OK) || - (btr != sizeof(BossHeader)) || (ValidateBossHeader(boss, 0) != 0)) { + if ((fvx_read(&file, &boss, sizeof(BossHeader), &btr) != FR_OK) || + (btr != sizeof(BossHeader)) || (ValidateBossHeader(&boss, 0) != 0)) { ShowPrompt(false, "%s\nError: Not a BOSS file", pathstr); fvx_close(&file); return 1; } // get / check size - payload_size = getbe32(boss->filesize) - sizeof(BossHeader); + payload_size = getbe32(boss.filesize) - sizeof(BossHeader); if (!payload_size) { fvx_close(&file); return 1; } // check if encrypted, decrypt if required - encrypted = (CheckBossEncrypted(boss) == 0); - if (encrypted) CryptBoss((u8*) boss, 0, sizeof(BossHeader), boss); + encrypted = (CheckBossEncrypted(&boss) == 0); + if (encrypted) CryptBoss((void*) &boss, 0, sizeof(BossHeader), &boss); + + // set up a buffer + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) { + fvx_close(&file); + return 1; + } // actual hash calculation & compare u8 hash[32]; sha_init(SHA256_MODE); - GetBossPayloadHashHeader(MAIN_BUFFER, boss); - u32 read_bytes = min((MAIN_BUFFER_SIZE - BOSS_SIZE_PAYLOAD_HEADER), payload_size); - fvx_read(&file, MAIN_BUFFER + BOSS_SIZE_PAYLOAD_HEADER, read_bytes, &btr); - if (encrypted) CryptBoss(MAIN_BUFFER + BOSS_SIZE_PAYLOAD_HEADER, sizeof(BossHeader), read_bytes, boss); - sha_update(MAIN_BUFFER, read_bytes + BOSS_SIZE_PAYLOAD_HEADER); - for (u32 i = read_bytes; i < payload_size; i += MAIN_BUFFER_SIZE) { - read_bytes = min(MAIN_BUFFER_SIZE, (payload_size - i)); - fvx_read(&file, MAIN_BUFFER, read_bytes, &btr); - if (encrypted) CryptBoss(MAIN_BUFFER, sizeof(BossHeader) + i, read_bytes, boss); - sha_update(MAIN_BUFFER, read_bytes); + + GetBossPayloadHashHeader(buffer, &boss); + u32 read_bytes = min((STD_BUFFER_SIZE - BOSS_SIZE_PAYLOAD_HEADER), payload_size); + fvx_read(&file, buffer + BOSS_SIZE_PAYLOAD_HEADER, read_bytes, &btr); + if (encrypted) CryptBoss(buffer + BOSS_SIZE_PAYLOAD_HEADER, sizeof(BossHeader), read_bytes, &boss); + sha_update(buffer, read_bytes + BOSS_SIZE_PAYLOAD_HEADER); + + for (u32 i = read_bytes; i < payload_size; i += STD_BUFFER_SIZE) { + read_bytes = min(STD_BUFFER_SIZE, (payload_size - i)); + fvx_read(&file, buffer, read_bytes, &btr); + if (encrypted) CryptBoss(buffer, sizeof(BossHeader) + i, read_bytes, &boss); + sha_update(buffer, read_bytes); } - fvx_close(&file); + sha_get(hash); - if (memcmp(hash, boss->hash_payload, 0x20) != 0) { + fvx_close(&file); + free(buffer); + + if (memcmp(hash, boss.hash_payload, 0x20) != 0) { ShowPrompt(false, "%s\nBOSS payload hash mismatch", pathstr); return 1; } @@ -636,24 +664,31 @@ u32 CheckEncryptedNcsdFile(const char* path) { } u32 CheckEncryptedCiaFile(const char* path) { - CiaStub* cia = (CiaStub*) TEMP_BUFFER; + CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub)); CiaInfo info; + if (!cia) return 1; + // load CIA stub if ((LoadCiaStub(cia, path) != 0) || - (GetCiaInfo(&info, &(cia->header)) != 0)) + (GetCiaInfo(&info, &(cia->header)) != 0)) { + free(cia); return 1; + } // check for encryption in CIA contents u32 content_count = getbe16(cia->tmd.content_count); u64 next_offset = info.offset_content; for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++) { TmdContentChunk* chunk = &(cia->content_list[i]); - if ((getbe16(chunk->type) & 0x1) || (CheckEncryptedNcchFile(path, next_offset) == 0)) + if ((getbe16(chunk->type) & 0x1) || (CheckEncryptedNcchFile(path, next_offset) == 0)) { + free(cia); return 0; // encryption found + } next_offset += getbe64(chunk->size); } + free(cia); return 1; } @@ -689,16 +724,10 @@ u32 CheckEncryptedFirmFile(const char* path) { } u32 CheckEncryptedBossFile(const char* path) { - BossHeader* boss = (BossHeader*) TEMP_BUFFER; - UINT btr; - - // get boss header - if ((fvx_qread(path, boss, 0, sizeof(BossHeader), &btr) != FR_OK) || - (btr != sizeof(BossHeader))) { - return 1; - } - - return CheckBossEncrypted(boss); + // get boss header, check if encrypted + BossHeader boss; + if (fvx_qread(path, &boss, 0, sizeof(BossHeader), NULL) != FR_OK) return 1; + return CheckBossEncrypted(&boss); } u32 CheckEncryptedGameFile(const char* path) { @@ -767,20 +796,28 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 } } + // set up buffer + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) { + fvx_close(ofp); + fvx_close(dfp); + return 1; + } + u32 ret = 0; if (!ShowProgress(offset, fsize, dest)) ret = 1; if (mode & (GAME_NCCH|GAME_NCSD|GAME_BOSS|SYS_FIRM|GAME_NDS)) { // for NCCH / NCSD / BOSS / FIRM files - for (u64 i = 0; (i < size) && (ret == 0); i += MAIN_BUFFER_SIZE) { - u32 read_bytes = min(MAIN_BUFFER_SIZE, (size - i)); + for (u64 i = 0; (i < size) && (ret == 0); i += STD_BUFFER_SIZE) { + u32 read_bytes = min(STD_BUFFER_SIZE, (size - i)); UINT bytes_read, bytes_written; - if (fvx_read(ofp, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK) ret = 1; - if (((mode & GAME_NCCH) && (CryptNcchSequential(MAIN_BUFFER, i, read_bytes, crypto) != 0)) || - ((mode & GAME_NCSD) && (CryptNcsdSequential(MAIN_BUFFER, i, read_bytes, crypto) != 0)) || - ((mode & GAME_BOSS) && crypt_boss && (CryptBossSequential(MAIN_BUFFER, i, read_bytes) != 0)) || - ((mode & SYS_FIRM) && (DecryptFirmSequential(MAIN_BUFFER, i, read_bytes) != 0))) + if (fvx_read(ofp, buffer, read_bytes, &bytes_read) != FR_OK) ret = 1; + if (((mode & GAME_NCCH) && (CryptNcchSequential(buffer, i, read_bytes, crypto) != 0)) || + ((mode & GAME_NCSD) && (CryptNcsdSequential(buffer, i, read_bytes, crypto) != 0)) || + ((mode & GAME_BOSS) && crypt_boss && (CryptBossSequential(buffer, i, read_bytes) != 0)) || + ((mode & SYS_FIRM) && (DecryptFirmSequential(buffer, i, read_bytes) != 0))) ret = 1; if (inplace) fvx_lseek(ofp, fvx_tell(ofp) - read_bytes); - if (fvx_write(dfp, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK) ret = 1; + if (fvx_write(dfp, buffer, read_bytes, &bytes_written) != FR_OK) ret = 1; if ((read_bytes != bytes_read) || (bytes_read != bytes_written)) ret = 1; if (!ShowProgress(offset + i + read_bytes, fsize, dest)) ret = 1; } @@ -790,10 +827,10 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 UINT bytes_read, bytes_written; u8 ctr[16]; - NcchHeader* ncch = (NcchHeader*) (void*) MAIN_BUFFER; + NcchHeader* ncch = (NcchHeader*) (void*) buffer; GetTmdCtr(ctr, chunk); // NCCH crypto? - if (fvx_read(ofp, MAIN_BUFFER, sizeof(NcchHeader), &bytes_read) != FR_OK) ret = 1; - if (cia_crypto) DecryptCiaContentSequential(MAIN_BUFFER, sizeof(NcchHeader), ctr, titlekey); + if (fvx_read(ofp, buffer, sizeof(NcchHeader), &bytes_read) != FR_OK) ret = 1; + if (cia_crypto) DecryptCiaContentSequential(buffer, sizeof(NcchHeader), ctr, titlekey); ncch_crypto = ((ValidateNcchHeader(ncch) == 0) && (NCCH_ENCRYPTED(ncch) || !(crypto & NCCH_NOCRYPTO))); if (ncch_crypto && (SetupNcchCrypto(ncch, crypto) != 0)) ret = 1; @@ -801,14 +838,14 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 GetTmdCtr(ctr, chunk); fvx_lseek(ofp, offset); sha_init(SHA256_MODE); - for (u64 i = 0; (i < size) && (ret == 0); i += MAIN_BUFFER_SIZE) { - u32 read_bytes = min(MAIN_BUFFER_SIZE, (size - i)); - if (fvx_read(ofp, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK) ret = 1; - if (cia_crypto && (DecryptCiaContentSequential(MAIN_BUFFER, read_bytes, ctr, titlekey) != 0)) ret = 1; - if (ncch_crypto && (CryptNcchSequential(MAIN_BUFFER, i, read_bytes, crypto) != 0)) ret = 1; + for (u64 i = 0; (i < size) && (ret == 0); i += STD_BUFFER_SIZE) { + u32 read_bytes = min(STD_BUFFER_SIZE, (size - i)); + if (fvx_read(ofp, buffer, read_bytes, &bytes_read) != FR_OK) ret = 1; + if (cia_crypto && (DecryptCiaContentSequential(buffer, read_bytes, ctr, titlekey) != 0)) ret = 1; + if (ncch_crypto && (CryptNcchSequential(buffer, i, read_bytes, crypto) != 0)) ret = 1; if (inplace) fvx_lseek(ofp, fvx_tell(ofp) - read_bytes); - if (fvx_write(dfp, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK) ret = 1; - sha_update(MAIN_BUFFER, read_bytes); + if (fvx_write(dfp, buffer, read_bytes, &bytes_written) != FR_OK) ret = 1; + sha_update(buffer, read_bytes); if ((read_bytes != bytes_read) || (bytes_read != bytes_written)) ret = 1; if (!ShowProgress(offset + i + read_bytes, fsize, dest)) ret = 1; } @@ -818,13 +855,13 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 fvx_close(ofp); if (!inplace) fvx_close(dfp); + if (buffer) free(buffer); return ret; } u32 CryptCiaFile(const char* orig, const char* dest, u16 crypto) { bool inplace = (strncmp(orig, dest, 256) == 0); - CiaStub* cia = (CiaStub*) TEMP_BUFFER; CiaInfo info; u8 titlekey[16]; @@ -835,9 +872,12 @@ u32 CryptCiaFile(const char* orig, const char* dest, u16 crypto) { if (!inplace) f_unlink(dest); // load CIA stub from origin + CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub)); + if (!cia) return 1; if ((LoadCiaStub(cia, orig) != 0) || (GetCiaInfo(&info, &(cia->header)) != 0) || (GetTitleKey(titlekey, &(cia->ticket)) != 0)) { + free(cia); return 1; } @@ -847,8 +887,10 @@ u32 CryptCiaFile(const char* orig, const char* dest, u16 crypto) { for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++) { TmdContentChunk* chunk = &(cia->content_list[i]); u64 size = getbe64(chunk->size); - if (CryptNcchNcsdBossFirmFile(orig, dest, GAME_CIA, crypto, next_offset, size, chunk, titlekey) != 0) + if (CryptNcchNcsdBossFirmFile(orig, dest, GAME_CIA, crypto, next_offset, size, chunk, titlekey) != 0) { + free(cia); return 1; + } next_offset += size; } @@ -856,83 +898,53 @@ u32 CryptCiaFile(const char* orig, const char* dest, u16 crypto) { if (!inplace && (info.size_meta == CIA_META_SIZE)) { CiaMeta* meta = (CiaMeta*) (void*) (cia + 1); if ((fvx_qread(orig, meta, info.offset_meta, CIA_META_SIZE, NULL) != FR_OK) || - (fvx_qwrite(dest, meta, info.offset_meta, CIA_META_SIZE, NULL) != FR_OK)) + (fvx_qwrite(dest, meta, info.offset_meta, CIA_META_SIZE, NULL) != FR_OK)) { + free(cia); return 1; + } } // fix TMD hashes, write CIA stub to destination - if ((FixTmdHashes(&(cia->tmd)) != 0) || - (WriteCiaStub(cia, dest) != 0)) return 1; + if ((FixTmdHashes(&(cia->tmd)) != 0) || (WriteCiaStub(cia, dest) != 0)) { + free(cia); + return 1; + } + free(cia); return 0; } u32 DecryptFirmFile(const char* orig, const char* dest) { const u8 dec_magic[] = { 'D', 'E', 'C', '\0' }; // insert to decrypted firms - FirmHeader firm; - FIL file; - UINT btr; + void* firm_buffer = (void*) malloc(FIRM_MAX_SIZE); + if (!firm_buffer) return 1; - // actual decryption - if (CryptNcchNcsdBossFirmFile(orig, dest, SYS_FIRM, CRYPTO_DECRYPT, 0, 0, NULL, NULL) != 0) - return 1; - - // open destination file, get FIRM header - if (fvx_open(&file, dest, FA_READ | FA_WRITE | FA_OPEN_EXISTING) != FR_OK) - return 1; - fvx_lseek(&file, 0); - if ((fvx_read(&file, &firm, sizeof(FirmHeader), &btr) != FR_OK) || - (ValidateFirmHeader(&firm, fvx_size(&file)) != 0)) { - fvx_close(&file); + // load the whole FIRM into memory & decrypt it + u32 firm_size = fvx_qsize(orig); + if ((firm_size > FIRM_MAX_SIZE) || (fvx_qread(orig, firm_buffer, 0, firm_size, NULL) != FR_OK) || + (DecryptFirmFull(firm_buffer, firm_size) != 0)) { + free(firm_buffer); return 1; } - // find ARM9 section - FirmSectionHeader* arm9s = FindFirmArm9Section(&firm); - if (!arm9s || !arm9s->size) return 1; + // add the decrypted magic + FirmHeader* firm = (FirmHeader*) firm_buffer; + memcpy(firm->dec_magic, dec_magic, sizeof(dec_magic)); - // decrypt ARM9 loader header - FirmA9LHeader a9l; - fvx_lseek(&file, arm9s->offset); - if ((fvx_read(&file, &a9l, sizeof(FirmA9LHeader), &btr) != FR_OK) || - (DecryptA9LHeader(&a9l) != 0) || (fvx_lseek(&file, arm9s->offset) != FR_OK) || - (fvx_write(&file, &a9l, sizeof(FirmA9LHeader), &btr) != FR_OK)) { - fvx_close(&file); + // write decrypted FIRM to the destination file + if (fvx_qwrite(dest, firm_buffer, 0, firm_size, NULL) != FR_OK) { + free(firm_buffer); return 1; } - // calculate new hash for ARM9 section - fvx_lseek(&file, arm9s->offset); - sha_init(SHA256_MODE); - for (u32 i = 0; i < arm9s->size; i += MAIN_BUFFER_SIZE) { - u32 read_bytes = min(MAIN_BUFFER_SIZE, (arm9s->size - i)); - if ((fvx_read(&file, MAIN_BUFFER, read_bytes, &btr) != FR_OK) || (btr != read_bytes)) { - fvx_close(&file); - return 1; - } - sha_update(MAIN_BUFFER, read_bytes); - } - sha_get(arm9s->hash); - - // write back FIRM header - fvx_lseek(&file, 0); - memcpy(firm.dec_magic, dec_magic, sizeof(dec_magic)); - firm.entry_arm9 = ARM9ENTRY_FIX(&firm); - if (fvx_write(&file, &firm, sizeof(FirmHeader), &btr) != FR_OK) { - fvx_close(&file); - return 1; - } - - fvx_close(&file); + free(firm_buffer); return 0; } -u32 CryptCdnFile(const char* orig, const char* dest, u16 crypto) { +u32 CryptCdnFileBuffered(const char* orig, const char* dest, u16 crypto, void* buffer) { bool inplace = (strncmp(orig, dest, 256) == 0); - TitleMetaData* tmd = (TitleMetaData*) TEMP_BUFFER; + TitleMetaData* tmd = (TitleMetaData*) buffer; TmdContentChunk* content_list = (TmdContentChunk*) (tmd + 1); - Ticket* ticket = (Ticket*) (TEMP_BUFFER + TMD_SIZE_MAX); - u8 titlekey[0x10] = { 0xFF }; // get name char* fname; @@ -953,13 +965,15 @@ u32 CryptCdnFile(const char* orig, const char* dest, u16 crypto) { } else tmd = NULL; // load or build ticket - if (LoadCdnTicketFile(ticket, orig) != 0) { - if (!tmd || (BuildFakeTicket(ticket, tmd->title_id) != 0)) return 1; - if (FindTitleKey(ticket, tmd->title_id) != 0) return 1; + Ticket ticket; + if (LoadCdnTicketFile(&ticket, orig) != 0) { + if (!tmd || (BuildFakeTicket(&ticket, tmd->title_id) != 0)) return 1; + if (FindTitleKey(&ticket, tmd->title_id) != 0) return 1; } // get titlekey - if (GetTitleKey(titlekey, ticket) != 0) + u8 titlekey[0x10] = { 0xFF }; + if (GetTitleKey(titlekey, &ticket) != 0) return 1; // find (build fake) content chunk @@ -984,15 +998,24 @@ u32 CryptCdnFile(const char* orig, const char* dest, u16 crypto) { if (CryptNcchNcsdBossFirmFile(orig, dest, GAME_NUSCDN, crypto, 0, 0, chunk, titlekey) != 0) return 1; - if (inplace && tmd) { - UINT bw; // in that case, write the change to the TMD file, too + if (inplace && tmd) { // in that case, write the change to the TMD file, too u32 offset = ((u8*) chunk) - ((u8*) tmd); - fvx_qwrite(path_tmd, chunk, offset, sizeof(TmdContentChunk), &bw); + fvx_qwrite(path_tmd, chunk, offset, sizeof(TmdContentChunk), NULL); } return 0; } +u32 CryptCdnFile(const char* orig, const char* dest, u16 crypto) { + void* buffer = (void*) malloc(TMD_SIZE_MAX); + if (!buffer) return 1; + + u32 ret = CryptCdnFileBuffered(orig, dest, crypto, buffer); + + free(buffer); + return ret; +} + u32 CryptGameFile(const char* path, bool inplace, bool encrypt) { u64 filetype = IdentifyFileType(path); u16 crypto = encrypt ? CRYPTO_ENCRYPT : CRYPTO_DECRYPT; @@ -1082,6 +1105,14 @@ u32 InsertCiaContent(const char* path_cia, const char* path_content, u32 offset, fvx_lseek(&ofile, offset); } + // allocate buffer + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) { + fvx_close(&ofile); + fvx_close(&dfile); + return 1; + } + // main loop starts here u8 ctr_in[16]; u8 ctr_out[16]; @@ -1089,22 +1120,23 @@ u32 InsertCiaContent(const char* path_cia, const char* path_content, u32 offset, GetTmdCtr(ctr_in, chunk); GetTmdCtr(ctr_out, chunk); if (!ShowProgress(0, 0, path_content)) ret = 1; - for (u32 i = 0; (i < size) && (ret == 0); i += MAIN_BUFFER_SIZE) { - u32 read_bytes = min(MAIN_BUFFER_SIZE, (size - i)); - if (fvx_read(&ofile, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK) ret = 1; - if (cdn_decrypt && (DecryptCiaContentSequential(MAIN_BUFFER, read_bytes, ctr_in, titlekey) != 0)) ret = 1; - if (ncch_decrypt && (DecryptNcchSequential(MAIN_BUFFER, i, read_bytes) != 0)) ret = 1; - if ((i == 0) && cxi_fix && (SetNcchSdFlag(MAIN_BUFFER) != 0)) ret = 1; + for (u32 i = 0; (i < size) && (ret == 0); i += STD_BUFFER_SIZE) { + u32 read_bytes = min(STD_BUFFER_SIZE, (size - i)); + if (fvx_read(&ofile, buffer, read_bytes, &bytes_read) != FR_OK) ret = 1; + if (cdn_decrypt && (DecryptCiaContentSequential(buffer, read_bytes, ctr_in, titlekey) != 0)) ret = 1; + if (ncch_decrypt && (DecryptNcchSequential(buffer, i, read_bytes) != 0)) ret = 1; + if ((i == 0) && cxi_fix && (SetNcchSdFlag(buffer) != 0)) ret = 1; if (i == 0) sha_init(SHA256_MODE); - sha_update(MAIN_BUFFER, read_bytes); - if (cia_encrypt && (EncryptCiaContentSequential(MAIN_BUFFER, read_bytes, ctr_out, titlekey) != 0)) ret = 1; - if (fvx_write(&dfile, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK) ret = 1; + sha_update(buffer, read_bytes); + if (cia_encrypt && (EncryptCiaContentSequential(buffer, read_bytes, ctr_out, titlekey) != 0)) ret = 1; + if (fvx_write(&dfile, buffer, read_bytes, &bytes_written) != FR_OK) ret = 1; if ((read_bytes != bytes_read) || (bytes_read != bytes_written)) ret = 1; if (!ShowProgress(offset + i + read_bytes, fsize, path_content)) ret = 1; } u8 hash[0x20]; sha_get(hash); + free(buffer); fvx_close(&ofile); fvx_close(&dfile); @@ -1129,10 +1161,9 @@ u32 InsertCiaMeta(const char* path_cia, CiaMeta* meta) { return (res) ? 0 : 1; } -u32 BuildCiaFromTmdFile(const char* path_tmd, const char* path_cia, bool force_legit, bool cdn) { +u32 BuildCiaFromTmdFileBuffered(const char* path_tmd, const char* path_cia, bool force_legit, bool cdn, void* buffer) { const u8 dlc_tid_high[] = { DLC_TID_HIGH }; - CiaStub* cia = (CiaStub*) TEMP_BUFFER; - CiaMeta* meta = (CiaMeta*) (TEMP_BUFFER + sizeof(CiaStub)); + CiaStub* cia = (CiaStub*) buffer; // Init progress bar if (!ShowProgress(0, 0, path_tmd)) return 1; @@ -1224,17 +1255,21 @@ u32 BuildCiaFromTmdFile(const char* path_tmd, const char* path_cia, bool force_l } // try to build & insert meta, but ignore result - if (content_count && cdn) { - if (!force_legit || !(getbe16(content_list->type) & 0x01)) { - CiaInfo info; - GetCiaInfo(&info, &(cia->header)); - if ((LoadNcchMeta(meta, path_cia, info.offset_content) == 0) && (InsertCiaMeta(path_cia, meta) == 0)) + CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta)); + if (meta) { + if (content_count && cdn) { + if (!force_legit || !(getbe16(content_list->type) & 0x01)) { + CiaInfo info; + GetCiaInfo(&info, &(cia->header)); + if ((LoadNcchMeta(meta, path_cia, info.offset_content) == 0) && (InsertCiaMeta(path_cia, meta) == 0)) + cia->header.size_meta = CIA_META_SIZE; + } + } else if (content_count) { + snprintf(name_content, 256 - (name_content - path_content), "%08lx.app", getbe32(content_list->id)); + if ((LoadNcchMeta(meta, path_content, 0) == 0) && (InsertCiaMeta(path_cia, meta) == 0)) cia->header.size_meta = CIA_META_SIZE; } - } else if (content_count) { - snprintf(name_content, 256 - (name_content - path_content), "%08lx.app", getbe32(content_list->id)); - if ((LoadNcchMeta(meta, path_content, 0) == 0) && (InsertCiaMeta(path_cia, meta) == 0)) - cia->header.size_meta = CIA_META_SIZE; + free(meta); } // write the CIA stub (take #2) @@ -1244,28 +1279,39 @@ u32 BuildCiaFromTmdFile(const char* path_tmd, const char* path_cia, bool force_l return 0; } +u32 BuildCiaFromTmdFile(const char* path_tmd, const char* path_cia, bool force_legit, bool cdn) { + void* buffer = (void*) malloc(sizeof(CiaStub)); + if (!buffer) return 1; + + u32 ret = BuildCiaFromTmdFileBuffered(path_tmd, path_cia, force_legit, cdn, buffer); + + free(buffer); + return ret; +} + u32 BuildCiaFromNcchFile(const char* path_ncch, const char* path_cia) { - CiaStub* cia = (CiaStub*) TEMP_BUFFER; - CiaMeta* meta = (CiaMeta*) (void*) (cia + 1); - NcchExtHeader* exthdr = (NcchExtHeader*) (void*) (meta + 1); + NcchExtHeader exthdr; NcchHeader ncch; u8 title_id[8]; u32 save_size = 0; + bool has_exthdr = false; // Init progress bar if (!ShowProgress(0, 0, path_ncch)) return 1; // load NCCH header / extheader, get save size && title id - if (LoadNcchHeaders(&ncch, exthdr, NULL, path_ncch, 0) == 0) { - save_size = getle32(exthdr->sys_info); - } else { - exthdr = NULL; - if (LoadNcchHeaders(&ncch, NULL, NULL, path_ncch, 0) != 0) return 1; + if (LoadNcchHeaders(&ncch, &exthdr, NULL, path_ncch, 0) == 0) { + save_size = getle32(exthdr.sys_info); + has_exthdr = true; + } else if (LoadNcchHeaders(&ncch, NULL, NULL, path_ncch, 0) != 0) { + return 1; } for (u32 i = 0; i < 8; i++) title_id[i] = (ncch.programId >> ((7-i)*8)) & 0xFF; // build the CIA stub + CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub)); + if (!cia) return 1; memset(cia, 0, sizeof(CiaStub)); if ((BuildCiaHeader(&(cia->header)) != 0) || (BuildCiaCert(cia->cert) != 0) || @@ -1273,35 +1319,41 @@ u32 BuildCiaFromNcchFile(const char* path_ncch, const char* path_cia) { (BuildFakeTmd(&(cia->tmd), title_id, 1, save_size)) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || (WriteCiaStub(cia, path_cia) != 0)) { + free(cia); return 1; } // insert NCCH content TmdContentChunk* chunk = cia->content_list; memset(chunk, 0, sizeof(TmdContentChunk)); // nothing else to do - if (InsertCiaContent(path_cia, path_ncch, 0, 0, chunk, NULL, false, true, false) != 0) + if (InsertCiaContent(path_cia, path_ncch, 0, 0, chunk, NULL, false, true, false) != 0) { + free(cia); return 1; + } // optional stuff (proper titlekey / meta data) - FindTitleKey((&cia->ticket), title_id); - if (exthdr && (BuildCiaMeta(meta, exthdr, NULL) == 0) && + CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta)); + if (meta && has_exthdr && (BuildCiaMeta(meta, &exthdr, NULL) == 0) && (LoadExeFsFile(meta->smdh, path_ncch, 0, "icon", sizeof(meta->smdh), NULL) == 0) && (InsertCiaMeta(path_cia, meta) == 0)) cia->header.size_meta = CIA_META_SIZE; + free(meta); // write the CIA stub (take #2) + FindTitleKey((&cia->ticket), title_id); if ((FixTmdHashes(&(cia->tmd)) != 0) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || - (WriteCiaStub(cia, path_cia) != 0)) + (WriteCiaStub(cia, path_cia) != 0)) { + free(cia); return 1; + } + free(cia); return 0; } u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) { - CiaStub* cia = (CiaStub*) TEMP_BUFFER; - CiaMeta* meta = (CiaMeta*) (void*) (cia + 1); - NcchExtHeader* exthdr = (NcchExtHeader*) (void*) (meta + 1); + NcchExtHeader exthdr; NcsdHeader ncsd; NcchHeader ncch; u8 title_id[8]; @@ -1319,11 +1371,13 @@ u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) { title_id[i] = (ncsd.mediaId >> ((7-i)*8)) & 0xFF; // load first content NCCH / extheader - if (LoadNcchHeaders(&ncch, exthdr, NULL, path_ncsd, NCSD_CNT0_OFFSET) != 0) + if (LoadNcchHeaders(&ncch, &exthdr, NULL, path_ncsd, NCSD_CNT0_OFFSET) != 0) return 1; - save_size = getle32(exthdr->sys_info); + save_size = getle32(exthdr.sys_info); // build the CIA stub + CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub)); + if (!cia) return 1; memset(cia, 0, sizeof(CiaStub)); if ((BuildCiaHeader(&(cia->header)) != 0) || (BuildCiaCert(cia->cert) != 0) || @@ -1331,6 +1385,7 @@ u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) { (BuildFakeTmd(&(cia->tmd), title_id, content_count, save_size)) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || (WriteCiaStub(cia, path_cia) != 0)) { + free(cia); return 1; } @@ -1343,23 +1398,30 @@ u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) { if (!size) continue; memset(chunk, 0, sizeof(TmdContentChunk)); chunk->id[3] = chunk->index[1] = i; - if (InsertCiaContent(path_cia, path_ncsd, offset, size, chunk++, NULL, false, (i == 0), false) != 0) + if (InsertCiaContent(path_cia, path_ncsd, offset, size, chunk++, NULL, false, (i == 0), false) != 0) { + free(cia); return 1; + } } // optional stuff (proper titlekey / meta data) - FindTitleKey(&(cia->ticket), title_id); - if ((BuildCiaMeta(meta, exthdr, NULL) == 0) && + CiaMeta* meta = (CiaMeta*) malloc(sizeof(CiaMeta)); + if (meta && (BuildCiaMeta(meta, &exthdr, NULL) == 0) && (LoadExeFsFile(meta->smdh, path_ncsd, NCSD_CNT0_OFFSET, "icon", sizeof(meta->smdh), NULL) == 0) && (InsertCiaMeta(path_cia, meta) == 0)) cia->header.size_meta = CIA_META_SIZE; + if (meta) free(meta); // write the CIA stub (take #2) + FindTitleKey(&(cia->ticket), title_id); if ((FixTmdHashes(&(cia->tmd)) != 0) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || - (WriteCiaStub(cia, path_cia) != 0)) + (WriteCiaStub(cia, path_cia) != 0)) { + free(cia); return 1; + } + free(cia); return 0; } @@ -1434,47 +1496,54 @@ u32 DumpCxiSrlFromTmdFile(const char* path) { } u32 ExtractCodeFromCxiFile(const char* path, const char* path_out, char* extstr) { - u8* code = (u8*) TEMP_BUFFER; - u32 code_max_size = TEMP_BUFFER_EXTSIZE; // uses the extended temp buffer size + const u32 code_max_size = 24 * 1024 * 1024; // arbitrary / this may not suffice (!) + + char dest[256]; + if (!path_out && (fvx_rmkdir(OUTPUT_PATH) != FR_OK)) return 1; + strncpy(dest, path_out ? path_out : OUTPUT_PATH, 256); + if (!CheckWritePermissions(dest)) return 1; NcchHeader ncch; NcchExtHeader exthdr; + u8* code = (u8*) malloc(code_max_size); + if (!code) { + ShowPrompt(false, "Out of memory."); + return 1; + } // load ncch, exthdr, .code u32 code_size; if ((LoadNcchHeaders(&ncch, &exthdr, NULL, path, 0) != 0) || ((LoadExeFsFile(code, path, 0, EXEFS_CODE_NAME, code_max_size, &code_size) != 0) && - (LoadExeFsFile(code, path, 0, ".firm", code_max_size, &code_size) != 0))) + (LoadExeFsFile(code, path, 0, ".firm", code_max_size, &code_size) != 0))) { + free(code); return 1; + } // decompress code (only if required) - if ((exthdr.flag & 0x1) && (DecompressCodeLzss(code, &code_size, code_max_size) != 0)) + if ((exthdr.flag & 0x1) && (DecompressCodeLzss(code, &code_size, code_max_size) != 0)) { + free(code); return 1; + } - // decide extension + // finalize output path (if not already final) char* ext = EXEFS_CODE_NAME; if (code_size >= 0x200) { if (ValidateFirmHeader((FirmHeader*)(void*) code, code_size) == 0) ext = ".firm"; else if (ValidateAgbHeader((AgbHeader*)(void*) code) == 0) ext = ".gba"; } if (extstr) strncpy(extstr, ext, 7); - - // build or take over output path - char dest[256]; - if (!path_out) { - // ensure the output dir exists - if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) return 1; - snprintf(dest, 256, OUTPUT_PATH "/%016llX%s%s", ncch.programId, (exthdr.flag & 0x1) ? ".dec" : "", ext); - } else strncpy(dest, path_out, 256); - if (!CheckWritePermissions(dest)) return 1; + if (!path_out) snprintf(dest, 256, OUTPUT_PATH "/%016llX%s%s", ncch.programId, (exthdr.flag & 0x1) ? ".dec" : "", ext); // write output file fvx_unlink(dest); if (fvx_qwrite(dest, code, 0, code_size, NULL) != FR_OK) { fvx_unlink(dest); + free(code); return 1; } - + + free(code); return 0; } @@ -1514,10 +1583,10 @@ u32 LoadSmdhFromGameFile(const char* path, Smdh* smdh) { u32 ShowSmdhTitleInfo(Smdh* smdh) { const u8 smdh_magic[] = { SMDH_MAGIC }; const u32 lwrap = 24; - u8* icon = (u8*) (TEMP_BUFFER + sizeof(Smdh)); - char* desc_l = (char*) icon + SMDH_SIZE_ICON_BIG; - char* desc_s = (char*) desc_l + SMDH_SIZE_DESC_LONG; - char* pub = (char*) desc_s + SMDH_SIZE_DESC_SHORT; + u8 icon[SMDH_SIZE_ICON_BIG]; + char desc_l[SMDH_SIZE_DESC_LONG]; + char desc_s[SMDH_SIZE_DESC_SHORT]; + char pub[SMDH_SIZE_PUBLISHER]; if ((memcmp(smdh->magic, smdh_magic, 4) != 0) || (GetSmdhIconBig(icon, smdh) != 0) || (GetSmdhDescLong(desc_l, smdh) != 0) || @@ -1535,8 +1604,8 @@ u32 ShowSmdhTitleInfo(Smdh* smdh) { u32 ShowTwlIconTitleInfo(TwlIconData* twl_icon) { const u32 lwrap = 24; - u8* icon = (u8*) (TEMP_BUFFER + sizeof(TwlIconData)); - char* desc = (char*) icon + TWLICON_SIZE_ICON; + u8 icon[TWLICON_SIZE_ICON]; + char desc[TWLICON_SIZE_DESC]; if ((GetTwlIcon(icon, twl_icon) != 0) || (GetTwlTitle(desc, twl_icon) != 0)) return 1; @@ -1559,9 +1628,6 @@ u32 ShowGbaFileTitleInfo(const char* path) { } u32 ShowGameFileTitleInfo(const char* path) { - Smdh* smdh = (Smdh*) (void*) TEMP_BUFFER; - TwlIconData* twl_icon = (TwlIconData*) (void*) TEMP_BUFFER; - char path_content[256]; u64 itype = IdentifyFileType(path); // initial type if (itype & GAME_TMD) { @@ -1569,13 +1635,21 @@ u32 ShowGameFileTitleInfo(const char* path) { path = path_content; } + void* buffer = (void*) malloc(max(sizeof(Smdh), sizeof(TwlIconData))); + Smdh* smdh = (Smdh*) buffer; + TwlIconData* twl_icon = (TwlIconData*) buffer; + // try loading SMDH, then try NDS / GBA + u32 ret = 1; if (LoadSmdhFromGameFile(path, smdh) == 0) - return ShowSmdhTitleInfo(smdh); + ret = ShowSmdhTitleInfo(smdh); else if ((LoadTwlMetaData(path, NULL, twl_icon) == 0) || ((itype & GAME_TAD) && (fvx_qread(path, twl_icon, TAD_BANNER_OFFSET, sizeof(TwlIconData), NULL) == FR_OK))) - return ShowTwlIconTitleInfo(twl_icon); - else return ShowGbaFileTitleInfo(path); + ret = ShowTwlIconTitleInfo(twl_icon); + else ret = ShowGbaFileTitleInfo(path); + + free(buffer); + return ret; } u32 BuildNcchInfoXorpads(const char* destdir, const char* path) { @@ -1584,7 +1658,7 @@ u32 BuildNcchInfoXorpads(const char* destdir, const char* path) { UINT bt; if (!CheckWritePermissions(destdir)) return 1; - // warning: this will only build output dirs in the root dir (!!!) + // warning: this will only build output dirs in the root dir (!) if ((f_stat(destdir, NULL) != FR_OK) && (f_mkdir(destdir) != FR_OK)) return 1; @@ -1603,6 +1677,9 @@ u32 BuildNcchInfoXorpads(const char* destdir, const char* path) { version = GetNcchInfoVersion(&info); entry_size = (version == 3) ? NCCHINFO_V3_SIZE : sizeof(NcchInfoEntry); if (!version) ret = 1; + + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) ret = 1; for (u32 i = 0; (i < info.n_entries) && (ret == 0); i++) { NcchInfoEntry entry; if ((fvx_read(&fp_info, &entry, entry_size, &bt) != FR_OK) || @@ -1612,19 +1689,20 @@ u32 BuildNcchInfoXorpads(const char* destdir, const char* path) { char dest[256]; // 256 is the maximum length of a full path snprintf(dest, 256, "%s/%s", destdir, entry.filename); - if (fvx_open(&fp_xorpad, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) - ret = 1; - if (!ShowProgress(0, 0, entry.filename)) ret = 1; - for (u64 p = 0; (p < entry.size_b) && (ret == 0); p += MAIN_BUFFER_SIZE) { - UINT create_bytes = min(MAIN_BUFFER_SIZE, entry.size_b - p); - if (BuildNcchInfoXorpad(MAIN_BUFFER, &entry, create_bytes, p) != 0) ret = 1; - if (fvx_write(&fp_xorpad, MAIN_BUFFER, create_bytes, &bt) != FR_OK) ret = 1; - if (!ShowProgress(p + create_bytes, entry.size_b, entry.filename)) ret = 1; - } - fvx_close(&fp_xorpad); + if (fvx_open(&fp_xorpad, dest, FA_WRITE | FA_CREATE_ALWAYS) == FR_OK) { + if (!ShowProgress(0, 0, entry.filename)) ret = 1; + for (u64 p = 0; (p < entry.size_b) && (ret == 0); p += STD_BUFFER_SIZE) { + UINT create_bytes = min(STD_BUFFER_SIZE, entry.size_b - p); + if (BuildNcchInfoXorpad(buffer, &entry, create_bytes, p) != 0) ret = 1; + if (fvx_write(&fp_xorpad, buffer, create_bytes, &bt) != FR_OK) ret = 1; + if (!ShowProgress(p + create_bytes, entry.size_b, entry.filename)) ret = 1; + } + fvx_close(&fp_xorpad); + } else ret = 1; if (ret != 0) f_unlink(dest); // get rid of the borked file } + if (buffer) free(buffer); fvx_close(&fp_info); return ret; } @@ -1655,17 +1733,18 @@ u32 GetHealthAndSafetyPaths(const char* drv, char* path_cxi, char* path_bak) { // build paths if (path_cxi) *path_cxi = '\0'; if (path_bak) *path_bak = '\0'; + TitleMetaData* tmd = (TitleMetaData*) malloc(TMD_SIZE_MAX); + TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1); for (u32 i = 0; i < 8; i++) { // 8 is an arbitrary number - TitleMetaData* tmd = (TitleMetaData*) TEMP_BUFFER; - TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1); char path_tmd[64]; snprintf(path_tmd, 64, "%s/title/00040010/%08lx/content/%08lx.tmd", drv, tidlow_hs, i); if (LoadTmdFile(tmd, path_tmd) != 0) continue; - if (!getbe16(tmd->content_count)) return 1; + if (!getbe16(tmd->content_count)) break; if (path_cxi) snprintf(path_cxi, 64, "%s/title/00040010/%08lx/content/%08lx.app", drv, tidlow_hs, getbe32(chunk->id)); if (path_bak) snprintf(path_bak, 64, "%s/title/00040010/%08lx/content/%08lx.bak", drv, tidlow_hs, getbe32(chunk->id)); break; } + free(tmd); return ((path_cxi && !*path_cxi) || (path_bak && !*path_bak)) ? 1 : 0; } @@ -1748,17 +1827,19 @@ u32 InjectHealthAndSafety(const char* path, const char* destdrv) { } u32 BuildTitleKeyInfo(const char* path, bool dec, bool dump) { - TitleKeysInfo* tik_info = (TitleKeysInfo*) MAIN_BUFFER; + static TitleKeysInfo* tik_info = NULL; const char* path_out = (dec) ? OUTPUT_PATH "/" TIKDB_NAME_DEC : OUTPUT_PATH "/" TIKDB_NAME_ENC; const char* path_in = path; - UINT br; // write permissions if (!CheckWritePermissions(path_out)) return 1; if (!path_in && !dump) { // no input path given - initialize + if (!tik_info) tik_info = (TitleKeysInfo*) malloc(STD_BUFFER_SIZE); + if (!tik_info) return 1; memset(tik_info, 0, 16); + if ((fvx_stat(path_out, NULL) == FR_OK) && (ShowPrompt(true, "%s\nOutput file already exists.\nUpdate this?", path_out))) path_in = path_out; @@ -1767,71 +1848,102 @@ u32 BuildTitleKeyInfo(const char* path, bool dec, bool dump) { u64 filetype = path_in ? IdentifyFileType(path_in) : 0; if (filetype & GAME_TICKET) { - Ticket* ticket = (Ticket*) TEMP_BUFFER; - if ((fvx_qread(path_in, ticket, 0, TICKET_SIZE, &br) != FR_OK) || (br != TICKET_SIZE) || - (TIKDB_SIZE(tik_info) + 32 > MAIN_BUFFER_SIZE) || (AddTicketToInfo(tik_info, ticket, dec) != 0)) return 1; + Ticket ticket; + if ((fvx_qread(path_in, &ticket, 0, TICKET_SIZE, NULL) != FR_OK) || + (TIKDB_SIZE(tik_info) + 32 > STD_BUFFER_SIZE) || + (AddTicketToInfo(tik_info, &ticket, dec) != 0)) { + return 1; + } } else if (filetype & SYS_TICKDB) { const u32 area_offsets[] = { TICKDB_AREA_OFFSETS }; FIL file; + if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) return 1; + u8* data = (u8*) malloc(STD_BUFFER_SIZE); + if (!data) { + fvx_close(&file); + return 1; + } + // parse file, sector by sector for (u32 p = 0; p < sizeof(area_offsets) / sizeof(u32); p++) { fvx_lseek(&file, area_offsets[p]); fvx_sync(&file); - for (u32 i = 0; i < TICKDB_AREA_SIZE; i += (TEMP_BUFFER_SIZE - 0x200)) { - u32 read_bytes = min(TEMP_BUFFER_SIZE, TICKDB_AREA_SIZE - i); - u8* data = (u8*) TEMP_BUFFER; - if ((fvx_read(&file, data, read_bytes, &br) != FR_OK) || (br != read_bytes)) { + for (u32 i = 0; i < TICKDB_AREA_SIZE; i += (STD_BUFFER_SIZE - 0x200)) { + u32 read_bytes = min(STD_BUFFER_SIZE, TICKDB_AREA_SIZE - i); + + if (fvx_read(&file, data, read_bytes, NULL) != FR_OK) { fvx_close(&file); + free(data); return 1; } - for (; data + TICKET_SIZE < ((u8*) TEMP_BUFFER) + read_bytes; data += 0x200) { + + for (; data + TICKET_SIZE < data + read_bytes; data += 0x200) { Ticket* ticket = TicketFromTickDbChunk(data, NULL, true); if (!ticket || (ticket->commonkey_idx >= 2) || !getbe64(ticket->ticket_id)) continue; - if (TIKDB_SIZE(tik_info) + 32 > MAIN_BUFFER_SIZE) return 1; + if (TIKDB_SIZE(tik_info) + 32 > STD_BUFFER_SIZE) break; // no error message AddTicketToInfo(tik_info, ticket, dec); // ignore result } } } + fvx_close(&file); + free(data); } else if (filetype & BIN_TIKDB) { - TitleKeysInfo* tik_info_merge = (TitleKeysInfo*) TEMP_BUFFER; - if ((fvx_qread(path_in, tik_info_merge, 0, TEMP_BUFFER_SIZE, &br) != FR_OK) || - (TIKDB_SIZE(tik_info_merge) != br)) return 1; + TitleKeysInfo* tik_info_merge = (TitleKeysInfo*) malloc(STD_BUFFER_SIZE); + if (!tik_info_merge) return 1; + + UINT br; + if ((fvx_qread(path_in, tik_info_merge, 0, STD_BUFFER_SIZE, &br) != FR_OK) || + (TIKDB_SIZE(tik_info_merge) != br)) { + free(tik_info_merge); + return 1; + } + // merge and rebuild TitleKeyInfo u32 n_entries = tik_info_merge->n_entries; TitleKeyEntry* tik = tik_info_merge->entries; for (u32 i = 0; i < n_entries; i++, tik++) { - if (TIKDB_SIZE(tik_info) + 32 > MAIN_BUFFER_SIZE) return 1; + if (TIKDB_SIZE(tik_info) + 32 > STD_BUFFER_SIZE) break; // no error message AddTitleKeyToInfo(tik_info, tik, !(filetype & FLAG_ENC), dec, false); // ignore result } + + free(tik_info_merge); } if (dump) { u32 dump_size = TIKDB_SIZE(tik_info); - if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) // ensure the output dir exists - return 1; - f_unlink(path_out); - if ((dump_size <= 16) || (fvx_qwrite(path_out, tik_info, 0, dump_size, &br) != FR_OK) || (br != dump_size)) - return 1; + + if (dump_size > 16) { + if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) // ensure the output dir exists + return 1; + f_unlink(path_out); + if ((dump_size <= 16) || (fvx_qwrite(path_out, tik_info, 0, dump_size, NULL) != FR_OK)) + return 1; + } + + free(tik_info); + tik_info = NULL; } return 0; } u32 BuildSeedInfo(const char* path, bool dump) { - SeedInfo* seed_info = (SeedInfo*) MAIN_BUFFER; + static SeedInfo* seed_info = NULL; const char* path_out = OUTPUT_PATH "/" SEEDDB_NAME; const char* path_in = path; u32 inputtype = 0; // 0 -> none, 1 -> seeddb.bin, 2 -> seed system save - UINT br; // write permissions if (!CheckWritePermissions(path_out)) return 1; if (!path_in && !dump) { // no input path given - initialize + if (!seed_info) seed_info = (SeedInfo*) malloc(STD_BUFFER_SIZE); + if (!seed_info) return 1; memset(seed_info, 0, 16); + if ((fvx_stat(path_out, NULL) == FR_OK) && (ShowPrompt(true, "%s\nOutput file already exists.\nUpdate this?", path_out))) { path_in = path_out; @@ -1839,12 +1951,15 @@ u32 BuildSeedInfo(const char* path, bool dump) { } else return 0; } + // seed info has to be allocated at this point + if (!seed_info) return 1; + char path_str[128]; if (path_in && (strnlen(path_in, 16) == 2)) { // when only a drive is given... // grab the key Y from movable.sed u8 movable_keyy[16]; snprintf(path_str, 128, "%s/private/movable.sed", path_in); - if ((fvx_qread(path_str, movable_keyy, 0x110, 0x10, &br) != FR_OK) || (br != 0x10)) + if (fvx_qread(path_str, movable_keyy, 0x110, 0x10, NULL) != FR_OK) return 1; // build the seed save path u32 sha256sum[8]; @@ -1856,45 +1971,70 @@ u32 BuildSeedInfo(const char* path, bool dump) { } if (inputtype == 1) { // seeddb.bin input - SeedInfo* seed_info_merge = (SeedInfo*) TEMP_BUFFER; - if ((fvx_qread(path_in, seed_info_merge, 0, TEMP_BUFFER_SIZE, &br) != FR_OK) || - (SEEDDB_SIZE(seed_info_merge) != br)) return 1; + SeedInfo* seed_info_merge = (SeedInfo*) malloc(STD_BUFFER_SIZE); + if (!seed_info_merge) return 1; + + UINT br; + if ((fvx_qread(path_in, seed_info_merge, 0, STD_BUFFER_SIZE, &br) != FR_OK) || + (SEEDDB_SIZE(seed_info_merge) != br)) { + free(seed_info_merge); + return 1; + } + // merge and rebuild SeedInfo u32 n_entries = seed_info_merge->n_entries; SeedInfoEntry* seed = seed_info_merge->entries; for (u32 i = 0; i < n_entries; i++, seed++) { - if (SEEDDB_SIZE(seed_info) + 32 > MAIN_BUFFER_SIZE) return 1; + if (SEEDDB_SIZE(seed_info) + 32 > STD_BUFFER_SIZE) break; // no error message AddSeedToDb(seed_info, seed); // ignore result } + + free(seed_info_merge); } else if (inputtype == 2) { // seed system save input static const u32 seed_offset[2] = {SEEDSAVE_AREA_OFFSETS}; - u8* seedsave = (u8*) TEMP_BUFFER; - if ((fvx_qread(path_in, seedsave, 0, 0x200, &br) != FR_OK) || (br != 0x200)) + u8* seedsave = (u8*) malloc(SEEDSAVE_MAX_ENTRIES*(8+16)); + if (!seedsave) return 1; + + if (fvx_qread(path_in, seedsave, 0, 0x200, NULL) != FR_OK) { + free(seedsave); return 1; + } + u32 p_active = (getle32(seedsave + 0x168)) ? 1 : 0; for (u32 p = 0; p < 2; p++) { SeedInfoEntry seed = { 0 }; - if ((fvx_qread(path_in, seedsave, seed_offset[(p + p_active) % 2], SEEDSAVE_MAX_ENTRIES*(8+16), &br) != FR_OK) || - (br != SEEDSAVE_MAX_ENTRIES*(8+16))) + + if (fvx_qread(path_in, seedsave, seed_offset[(p + p_active) % 2], SEEDSAVE_MAX_ENTRIES*(8+16), NULL) != FR_OK) { + free(seedsave); return 1; + } + for (u32 s = 0; s < SEEDSAVE_MAX_ENTRIES; s++) { seed.titleId = getle64(seedsave + (s*8)); memcpy(seed.seed, seedsave + (SEEDSAVE_MAX_ENTRIES*8) + (s*16), 16); if (((seed.titleId >> 32) != 0x00040000) || (!getle64(seed.seed) && !getle64(seed.seed + 8))) continue; - if (SEEDDB_SIZE(seed_info) + 32 > MAIN_BUFFER_SIZE) return 1; + if (SEEDDB_SIZE(seed_info) + 32 > STD_BUFFER_SIZE) break; // no error message AddSeedToDb(seed_info, &seed); // ignore result } } + + free(seedsave); } if (dump) { u32 dump_size = SEEDDB_SIZE(seed_info); - if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) // ensure the output dir exists - return 1; - f_unlink(path_out); - if ((dump_size <= 16) || (fvx_qwrite(path_out, seed_info, 0, dump_size, &br) != FR_OK) || (br != dump_size)) - return 1; + + if (dump_size > 16) { + if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) // ensure the output dir exists + return 1; + f_unlink(path_out); + if (fvx_qwrite(path_out, seed_info, 0, dump_size, NULL) != FR_OK) + return 1; + } + + free(seed_info); + seed_info = NULL; } return 0; @@ -1902,39 +2042,48 @@ u32 BuildSeedInfo(const char* path, bool dump) { u32 LoadNcchFromGameFile(const char* path, NcchHeader* ncch) { u64 filetype = IdentifyFileType(path); - UINT br; if (filetype & GAME_NCCH) { - if ((fvx_qread(path, ncch, 0, sizeof(NcchHeader), &br) == FR_OK) && - (br == sizeof(NcchHeader)) && (ValidateNcchHeader(ncch) == 0)) return 0; + if ((fvx_qread(path, ncch, 0, sizeof(NcchHeader), NULL) == FR_OK) && + (ValidateNcchHeader(ncch) == 0)) return 0; } else if (filetype & GAME_NCSD) { - if ((fvx_qread(path, ncch, NCSD_CNT0_OFFSET, sizeof(NcchHeader), &br) == FR_OK) && - (br == sizeof(NcchHeader)) && (ValidateNcchHeader(ncch) == 0)) return 0; + if ((fvx_qread(path, ncch, NCSD_CNT0_OFFSET, sizeof(NcchHeader), NULL) == FR_OK) && + (ValidateNcchHeader(ncch) == 0)) return 0; } else if (filetype & GAME_CIA) { - CiaStub* cia = (CiaStub*) TEMP_BUFFER; + CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub)); CiaInfo info; // load CIA stub from path if ((LoadCiaStub(cia, path) != 0) || (GetCiaInfo(&info, &(cia->header)) != 0)) { + free(cia); return 1; } // decrypt / load NCCH header from first CIA content + u32 ret = 1; if (getbe16(cia->tmd.content_count)) { TmdContentChunk* chunk = cia->content_list; if ((getbe64(chunk->size) < sizeof(NcchHeader)) || - (fvx_qread(path, ncch, info.offset_content, sizeof(NcchHeader), &br) != FR_OK) || - (br != sizeof(NcchHeader))) return 1; + (fvx_qread(path, ncch, info.offset_content, sizeof(NcchHeader), NULL) != FR_OK)) { + free(cia); + return 1; + } if (getbe16(chunk->type) & 0x1) { // decrypt first content header u8 titlekey[16]; u8 ctr[16]; GetTmdCtr(ctr, chunk); - if (GetTitleKey(titlekey, &(cia->ticket)) != 0) return 1; + if (GetTitleKey(titlekey, &(cia->ticket)) != 0) { + free(cia); + return 1; + } DecryptCiaContentSequential((void*) ncch, sizeof(NcchHeader), ctr, titlekey); } - if (ValidateNcchHeader(ncch) == 0) return 0; + if (ValidateNcchHeader(ncch) == 0) ret = 0; } + + free(cia); + return ret; } return 1; @@ -1977,68 +2126,68 @@ u32 GetGoodName(char* name, const char* path, bool quick) { } if (type_donor & GAME_GBA) { // AGB - AgbHeader* agb = (AgbHeader*) TEMP_BUFFER; - if (fvx_qread(path_donor, agb, 0, sizeof(AgbHeader), NULL) != FR_OK) return 1; - snprintf(name, 128, "%.12s (AGB-%.4s).%s", agb->game_title, agb->game_code, ext); + AgbHeader agb; + if (fvx_qread(path_donor, &agb, 0, sizeof(AgbHeader), NULL) != FR_OK) return 1; + snprintf(name, 128, "%.12s (AGB-%.4s).%s", agb.game_title, agb.game_code, ext); } else if (type_donor & GAME_NDS) { // NTR or TWL - TwlHeader* twl = (TwlHeader*) TEMP_BUFFER; - TwlIconData* icon = (TwlIconData*) (TEMP_BUFFER + sizeof(TwlHeader)); - if (LoadTwlMetaData(path_donor, twl, quick ? NULL : icon) != 0) return 1; + TwlHeader twl; + TwlIconData icon; + if (LoadTwlMetaData(path_donor, &twl, quick ? NULL : &icon) != 0) return 1; if (quick) { - if (twl->unit_code & 0x02) { // TWL - snprintf(name, 128, "%016llX (TWL-%.4s).%s", twl->title_id, twl->game_code, ext); + if (twl.unit_code & 0x02) { // TWL + snprintf(name, 128, "%016llX (TWL-%.4s).%s", twl.title_id, twl.game_code, ext); } else { // NTR - snprintf(name, 128, "%.12s (NTR-%.4s).%s", twl->game_title, twl->game_code, ext); + snprintf(name, 128, "%.12s (NTR-%.4s).%s", twl.game_title, twl.game_code, ext); } } else { char title_name[0x80+1] = { 0 }; - if (GetTwlTitle(title_name, icon) != 0) return 1; + if (GetTwlTitle(title_name, &icon) != 0) return 1; char* linebrk = strchr(title_name, '\n'); if (linebrk) *linebrk = '\0'; - if (twl->unit_code & 0x02) { // TWL + if (twl.unit_code & 0x02) { // TWL char region[8] = { 0 }; - if (twl->region_flags == TWL_REGION_FREE) snprintf(region, 8, "W"); + if (twl.region_flags == TWL_REGION_FREE) snprintf(region, 8, "W"); snprintf(region, 8, "%s%s%s%s%s", - (twl->region_flags & REGION_MASK_JPN) ? "J" : "", - (twl->region_flags & REGION_MASK_USA) ? "U" : "", - (twl->region_flags & REGION_MASK_EUR) ? "E" : "", - (twl->region_flags & REGION_MASK_CHN) ? "C" : "", - (twl->region_flags & REGION_MASK_KOR) ? "K" : ""); + (twl.region_flags & REGION_MASK_JPN) ? "J" : "", + (twl.region_flags & REGION_MASK_USA) ? "U" : "", + (twl.region_flags & REGION_MASK_EUR) ? "E" : "", + (twl.region_flags & REGION_MASK_CHN) ? "C" : "", + (twl.region_flags & REGION_MASK_KOR) ? "K" : ""); if (strncmp(region, "JUECK", 8) == 0) snprintf(region, 8, "W"); if (!*region) snprintf(region, 8, "UNK"); - char* unit_str = (twl->unit_code == TWL_UNITCODE_TWLNTR) ? "DSi Enhanced" : "DSi Exclusive"; + char* unit_str = (twl.unit_code == TWL_UNITCODE_TWLNTR) ? "DSi Enhanced" : "DSi Exclusive"; snprintf(name, 128, "%016llX %s (TWL-%.4s) (%s) (%s).%s", - twl->title_id, title_name, twl->game_code, unit_str, region, ext); + twl.title_id, title_name, twl.game_code, unit_str, region, ext); } else { // NTR - snprintf(name, 128, "%s (NTR-%.4s).%s", title_name, twl->game_code, ext); + snprintf(name, 128, "%s (NTR-%.4s).%s", title_name, twl.game_code, ext); } } } else if (type_donor & (GAME_CIA|GAME_NCSD|GAME_NCCH)) { // CTR (data from NCCH) - NcchHeader* ncch = (NcchHeader*) (void*) TEMP_BUFFER; - Smdh* smdh = (Smdh*) (TEMP_BUFFER + sizeof(NcchHeader)); - if (LoadNcchFromGameFile(path_donor, ncch) != 0) return 1; - if (quick || (LoadSmdhFromGameFile(path_donor, smdh) != 0)) { - snprintf(name, 128, "%016llX%s (%.16s).%s", ncch->programId, appid_str, ncch->productcode, ext); + NcchHeader ncch; + Smdh smdh; // pretty big for the stack, but should be okay here + if (LoadNcchFromGameFile(path_donor, &ncch) != 0) return 1; + if (quick || (LoadSmdhFromGameFile(path_donor, &smdh) != 0)) { + snprintf(name, 128, "%016llX%s (%.16s).%s", ncch.programId, appid_str, ncch.productcode, ext); } else { char title_name[0x40+1] = { 0 }; - if (GetSmdhDescShort(title_name, smdh) != 0) return 1; + if (GetSmdhDescShort(title_name, &smdh) != 0) return 1; char region[8] = { 0 }; - if (smdh->region_lockout == SMDH_REGION_FREE) snprintf(region, 8, "W"); - snprintf(region, 8, "%s%s%s%s%s%s", - (smdh->region_lockout & REGION_MASK_JPN) ? "J" : "", - (smdh->region_lockout & REGION_MASK_USA) ? "U" : "", - (smdh->region_lockout & REGION_MASK_EUR) ? "E" : "", - (smdh->region_lockout & REGION_MASK_CHN) ? "C" : "", - (smdh->region_lockout & REGION_MASK_KOR) ? "K" : "", - (smdh->region_lockout & REGION_MASK_TWN) ? "T" : ""); + if (smdh.region_lockout == SMDH_REGION_FREE) snprintf(region, 8, "W"); + else snprintf(region, 8, "%s%s%s%s%s%s", + (smdh.region_lockout & REGION_MASK_JPN) ? "J" : "", + (smdh.region_lockout & REGION_MASK_USA) ? "U" : "", + (smdh.region_lockout & REGION_MASK_EUR) ? "E" : "", + (smdh.region_lockout & REGION_MASK_CHN) ? "C" : "", + (smdh.region_lockout & REGION_MASK_KOR) ? "K" : "", + (smdh.region_lockout & REGION_MASK_TWN) ? "T" : ""); if (strncmp(region, "JUECKT", 8) == 0) snprintf(region, 8, "W"); if (!*region) snprintf(region, 8, "UNK"); snprintf(name, 128, "%016llX%s %s (%.16s) (%s).%s", - ncch->programId, appid_str, title_name, ncch->productcode, region, ext); + ncch.programId, appid_str, title_name, ncch.productcode, region, ext); } } else return 1; diff --git a/arm9/source/utils/keydbutil.c b/arm9/source/utils/keydbutil.c index baad89c..c1d80ff 100644 --- a/arm9/source/utils/keydbutil.c +++ b/arm9/source/utils/keydbutil.c @@ -3,13 +3,11 @@ #include "ui.h" #include "unittype.h" -#define MAX_KEYDB_SIZE (TEMP_BUFFER_SIZE) +#define MAX_KEYDB_SIZE (STD_BUFFER_SIZE) u32 CryptAesKeyDb(const char* path, bool inplace, bool encrypt) { - AesKeyInfo* keydb = (AesKeyInfo*) MAIN_BUFFER; + AesKeyInfo* keydb = NULL; const char* path_out = (inplace) ? path : OUTPUT_PATH "/" KEYDB_NAME; - u32 n_keys; - UINT bt, btw; // write permissions if (!CheckWritePermissions(path_out)) @@ -21,13 +19,22 @@ u32 CryptAesKeyDb(const char* path, bool inplace, bool encrypt) { return 1; } - // load key database - if ((fvx_qread(path, keydb, 0, MAIN_BUFFER_SIZE, &bt) != FR_OK) || - (bt % sizeof(AesKeyInfo)) || (bt >= MAIN_BUFFER_SIZE)) + // check key database size + u32 fsize = fvx_qsize(path); + if (!fsize || (fsize % sizeof(AesKeyInfo)) || (fsize > MAX_KEYDB_SIZE)) return 1; + keydb = (AesKeyInfo*) malloc(fsize); + if (!keydb) return 1; + + // load key database + if (fvx_qread(path, keydb, 0, fsize, NULL) != FR_OK) { + free(keydb); + return 1; + } + // en-/decrypt keys - n_keys = bt / sizeof(AesKeyInfo); + u32 n_keys = fsize / sizeof(AesKeyInfo); for (u32 i = 0; i < n_keys; i++) { if ((bool) keydb[i].isEncrypted == !encrypt) CryptAesKeyInfo(&(keydb[i])); @@ -35,9 +42,12 @@ u32 CryptAesKeyDb(const char* path, bool inplace, bool encrypt) { // dump key database if (!inplace) f_unlink(path_out); - if ((fvx_qwrite(path_out, keydb, 0, bt, &btw) != FR_OK) || (bt != btw)) + if (fvx_qwrite(path_out, keydb, 0, fsize, NULL) != FR_OK) { + free(keydb); return 1; + } + free(keydb); return 0; } @@ -59,16 +69,19 @@ u32 AddKeyToDb(AesKeyInfo* key_info, AesKeyInfo* key_entry) { } u32 BuildKeyDb(const char* path, bool dump) { - AesKeyInfo* key_info = (AesKeyInfo*) MAIN_BUFFER; + static AesKeyInfo* key_info = NULL; const char* path_out = OUTPUT_PATH "/" KEYDB_NAME; const char* path_in = path; - UINT br; // write permissions if (!CheckWritePermissions(path_out)) return 1; if (!path_in && !dump) { // no input path given - initialize + if (!key_info) key_info = (AesKeyInfo*) malloc(STD_BUFFER_SIZE); + if (!key_info) return 1; + memset(key_info, 0xFF, sizeof(AesKeyInfo)); + AddKeyToDb(key_info, NULL); if ((fvx_stat(path_out, NULL) == FR_OK) && (ShowPrompt(true, "%s\nOutput file already exists.\nUpdate this?", path_out))) @@ -76,30 +89,46 @@ u32 BuildKeyDb(const char* path, bool dump) { else return 0; } + // key info has to be allocated at this point + if (!key_info) return 1; + u64 filetype = path_in ? IdentifyFileType(path_in) : 0; if (filetype & BIN_KEYDB) { // AES key database - AesKeyInfo* key_info_merge = (AesKeyInfo*) TEMP_BUFFER; - if ((fvx_qread(path_in, key_info_merge, 0, TEMP_BUFFER_SIZE, &br) != FR_OK) || - (br % sizeof(AesKeyInfo)) || (br >= MAIN_BUFFER_SIZE)) return 1; - u32 n_keys = br / sizeof(AesKeyInfo); - for (u32 i = 0; i < n_keys; i++) { - if (key_info_merge[i].isEncrypted) // build an unencrypted db - CryptAesKeyInfo(&(key_info_merge[i])); - if (AddKeyToDb(key_info, key_info_merge + i) != 0) return 1; + u32 fsize = fvx_qsize(path_in); + if ((fsize % sizeof(AesKeyInfo)) || (fsize > MAX_KEYDB_SIZE)) + return 1; + + u32 n_keys = fsize / sizeof(AesKeyInfo); + u32 merged_keys = 0; + + AesKeyInfo* key_info_merge = (AesKeyInfo*) malloc(fsize); + if (fvx_qread(path_in, key_info_merge, 0, fsize, NULL) == FR_OK) { + for (u32 i = 0; i < n_keys; i++) { + if (key_info_merge[i].isEncrypted) // build an unencrypted db + CryptAesKeyInfo(&(key_info_merge[i])); + if (AddKeyToDb(key_info, key_info_merge + i) != 0) break; + merged_keys++; + } } + + free(key_info_merge); + if (merged_keys < n_keys) return 1; } else if (filetype & BIN_LEGKEY) { // legacy key file AesKeyInfo key; unsigned int keyslot = 0xFF; char typestr[32] = { 0 }; char* name_in = strrchr(path_in, '/'); + memset(&key, 0, sizeof(AesKeyInfo)); key.type = 'N'; if (!name_in || (strnlen(++name_in, 32) > 24)) return 1; // safety if ((sscanf(name_in, "slot0x%02XKey%s", &keyslot, typestr) != 2) && (sscanf(name_in, "slot0x%02Xkey%s", &keyslot, typestr) != 2)) return 1; + char* ext = strchr(typestr, '.'); if (!ext) return 1; *(ext++) = '\0'; + if ((*typestr == 'X') || (*typestr == 'Y')) { key.type = *typestr; strncpy(key.id, typestr + 1, 10); @@ -110,21 +139,28 @@ u32 BuildKeyDb(const char* path, bool dump) { key.slot = keyslot; key.keyUnitType = (strncasecmp(ext, "ret.bin", 10) == 0) ? KEYS_RETAIL : (strncasecmp(ext, "dev.bin", 10) == 0) ? KEYS_DEVKIT : 0; - if ((fvx_qread(path_in, key.key, 0, 16, &br) != FR_OK) || (br != 16)) return 1; + if (fvx_qread(path_in, key.key, 0, 16, NULL) != FR_OK) return 1; if (AddKeyToDb(key_info, &key) != 0) return 1; } if (dump) { u32 dump_size = 0; for (AesKeyInfo* key = key_info; key->slot <= 0x40; key++) { - dump_size += sizeof(AesKeyInfo); - if (dump_size >= MAX_KEYDB_SIZE) return 1; + u32 dump_size_next = dump_size + sizeof(AesKeyInfo); + if (dump_size_next > MAX_KEYDB_SIZE) break; + dump_size = dump_size_next; } - if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) // ensure the output dir exists - return 1; - f_unlink(path_out); - if (!dump_size || (fvx_qwrite(path_out, key_info, 0, dump_size, &br) != FR_OK) || (br != dump_size)) - return 1; + + if (dump_size) { + if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) // ensure the output dir exists + return 1; + f_unlink(path_out); + if (fvx_qwrite(path_out, key_info, 0, dump_size, NULL) != FR_OK) + return 1; + } + + free(key_info); + key_info = NULL; } return 0; diff --git a/arm9/source/utils/nandcmac.c b/arm9/source/utils/nandcmac.c index faa75f7..5b4c80d 100644 --- a/arm9/source/utils/nandcmac.c +++ b/arm9/source/utils/nandcmac.c @@ -160,11 +160,9 @@ u32 CalculateFileCmac(const char* path, u8* cmac) { else if (!cmac_type) return 1; const u32 cmac_keyslot[] = { CMAC_KEYSLOT }; - u8* data = (u8*) TEMP_BUFFER; + u8 hashdata[0x200]; u32 keyslot = cmac_keyslot[cmac_type]; - u8* hashdata = NULL; u32 hashsize = 0; - UINT br; // setup slot 0x30 via movable.sed if ((keyslot == 0x30) && (SetupSlot0x30(drv) != 0)) @@ -172,23 +170,29 @@ u32 CalculateFileCmac(const char* path, u8* cmac) { // build hash data block, get size if ((cmac_type == CMAC_AGBSAVE) || (cmac_type == CMAC_AGBSAVE_SD)) { // agbsaves - AgbSaveHeader* agbsave = (AgbSaveHeader*) (void*) data; - if ((TEMP_BUFFER_SIZE < AGBSAVE_MAX_SIZE) || (fvx_qread(path, agbsave, 0, AGBSAVE_MAX_SIZE, &br) != FR_OK) || - (br < 0x200) || (ValidateAgbSaveHeader(agbsave) != 0) || (0x200 + agbsave->save_size > br)) + AgbSaveHeader* agbsave = (AgbSaveHeader*) malloc(AGBSAVE_MAX_SIZE); + UINT br; + + if (!agbsave) return 1; + if ((fvx_qread(path, agbsave, 0, AGBSAVE_MAX_SIZE, &br) != FR_OK) || (br < 0x200) || + (ValidateAgbSaveHeader(agbsave) != 0) || (0x200 + agbsave->save_size > br)) { + free(agbsave); return 1; - return FixAgbSaveCmac(data, cmac, (cmac_type == CMAC_AGBSAVE) ? NULL : path); + } + + u32 ret = FixAgbSaveCmac(agbsave, cmac, (cmac_type == CMAC_AGBSAVE) ? NULL : path); + free(agbsave); + return ret; } else if (cmac_type == CMAC_MOVABLE) { // movable.sed // see: https://3dbrew.org/wiki/Nand/private/movable.sed - if ((fvx_qread(path, data, 0, 0x140, &br) != FR_OK) || (br != 0x140)) + if (fvx_qread(path, hashdata, 0, 0x140, NULL) != FR_OK) return 1; hashsize = 0x130; - hashdata = data; } else { // "savegame" CMACs // see: https://3dbrew.org/wiki/Savegames const char* cmac_savetype[] = { CMAC_SAVETYPE }; - u8* disa = data; - hashdata = data + 0x400; - if ((fvx_qread(path, disa, 0x100, 0x100, &br) != FR_OK) || (br != 0x100)) + u8 disa[0x100]; + if (fvx_qread(path, disa, 0x100, 0x100, NULL) != FR_OK) return 1; memcpy(hashdata, cmac_savetype[cmac_type], 8); if ((cmac_type == CMAC_EXTDATA_SD) || (cmac_type == CMAC_EXTDATA_SYS)) { @@ -205,7 +209,7 @@ u32 CalculateFileCmac(const char* path, u8* cmac) { memcpy(hashdata + 0x10, disa, 0x100); hashsize = 0x110; } else if (cmac_type == CMAC_SAVEDATA_SD) { - u8* hashdata0 = data + 0x200; + u8* hashdata0 = hashdata + 0x30; memcpy(hashdata0 + 0x00, cmac_savetype[CMAC_SAVEGAME], 8); memcpy(hashdata0 + 0x08, disa, 0x100); memcpy(hashdata + 0x08, &tid_low, 4); @@ -221,7 +225,7 @@ u32 CalculateFileCmac(const char* path, u8* cmac) { // calculate CMAC u8 shasum[32]; - if (!hashdata || !hashsize) return 1; + if (!hashsize) return 1; sha_quick(shasum, hashdata, hashsize, SHA256_MODE); use_aeskey(keyslot); aes_cmac(shasum, cmac, 2); diff --git a/arm9/source/utils/nandutil.c b/arm9/source/utils/nandutil.c index 4fb6f7d..385a0a9 100644 --- a/arm9/source/utils/nandutil.c +++ b/arm9/source/utils/nandutil.c @@ -96,31 +96,40 @@ u32 BuildEssentialBackup(const char* path, EssentialBackup* essential) { } u32 CheckEmbeddedBackup(const char* path) { - EssentialBackup* essential = (EssentialBackup*) TEMP_BUFFER; - EssentialBackup* embedded = (EssentialBackup*) (TEMP_BUFFER + sizeof(EssentialBackup)); - UINT btr; - if ((BuildEssentialBackup(path, essential) != 0) || - (fvx_qread(path, embedded, SECTOR_D0K3 * 0x200, sizeof(EssentialBackup), &btr) != FR_OK) || - (memcmp(embedded, essential, sizeof(EssentialBackup)) != 0)) + EssentialBackup* essential = (EssentialBackup*) malloc(sizeof(EssentialBackup)); + EssentialBackup* embedded = (EssentialBackup*) malloc(sizeof(EssentialBackup)); + + if (!essential || !embedded || (BuildEssentialBackup(path, essential) != 0) || + (fvx_qread(path, embedded, SECTOR_D0K3 * 0x200, sizeof(EssentialBackup), NULL) != FR_OK) || + (memcmp(embedded, essential, sizeof(EssentialBackup)) != 0)) { + free(essential); + free(embedded); return 1; + } + + free(essential); + free(embedded); return 0; } u32 EmbedEssentialBackup(const char* path) { - EssentialBackup* essential = (EssentialBackup*) TEMP_BUFFER; - UINT btw; + EssentialBackup* essential = (EssentialBackup*) malloc(sizeof(EssentialBackup)); + if (!essential) return 1; + // leaving out the write permissions check here, it's okay if ((BuildEssentialBackup(path, essential) != 0) || (ValidateNandNcsdHeader((NandNcsdHeader*) essential->nand_hdr) != 0) || - (fvx_qwrite(path, essential, SECTOR_D0K3 * 0x200, sizeof(EssentialBackup), &btw) != FR_OK) || - (btw != sizeof(EssentialBackup))) + (fvx_qwrite(path, essential, SECTOR_D0K3 * 0x200, sizeof(EssentialBackup), NULL) != FR_OK)) { + free(essential); return 1; + } + + free(essential); return 0; } -u32 DumpGbaVcSavegame(const char* path) { - if (TEMP_BUFFER_SIZE < AGBSAVE_MAX_SIZE) return 1; - AgbSaveHeader* agbsave = (AgbSaveHeader*) (void*) TEMP_BUFFER; +u32 DumpGbaVcSavegameBuffered(const char* path, void* buffer) { + AgbSaveHeader* agbsave = (AgbSaveHeader*) buffer; u8* savegame = (u8*) (agbsave + 1); // read full AGBsave to memory @@ -145,9 +154,20 @@ u32 DumpGbaVcSavegame(const char* path) { return 0; } -u32 InjectGbaVcSavegame(const char* path, const char* path_vcsave) { - if (TEMP_BUFFER_SIZE < AGBSAVE_MAX_SIZE) return 1; - AgbSaveHeader* agbsave = (AgbSaveHeader*) (void*) TEMP_BUFFER; +u32 DumpGbaVcSavegame(const char* path) { + u8* buffer = (u8*) malloc(AGBSAVE_MAX_SIZE); + if (!buffer) { + ShowPrompt(false, "Out of memory."); + return 1; + } + + u32 ret = DumpGbaVcSavegameBuffered(path, buffer); + free(buffer); + return ret; +} + +u32 InjectGbaVcSavegameBuffered(const char* path, const char* path_vcsave, void* buffer) { + AgbSaveHeader* agbsave = (AgbSaveHeader*) buffer; u8* savegame = (u8*) (agbsave + 1); // basic sanity checks for path_vcsave @@ -183,7 +203,6 @@ u32 InjectGbaVcSavegame(const char* path, const char* path_vcsave) { snprintf(path_sd, 64, "A:/title/%08lx/%08lx/data/00000001.sav", getle32((u8*) &(agbsave->title_id) + 4), getle32((u8*) &(agbsave->title_id))); if (FixAgbSaveCmac(agbsave, NULL, path_sd) != 0) return 1; - ShowPrompt(false, path_sd); // check SD save size, then write both partitions if ((fvx_stat(path_sd, &fno) != FR_OK) || (fno.fsize != max(AGBSAVE_MAX_SIZE, 2 * data_size))) @@ -191,7 +210,7 @@ u32 InjectGbaVcSavegame(const char* path, const char* path_vcsave) { if (fvx_qwrite(path_sd, agbsave, 0, data_size, NULL) != FR_OK) return 1; // write fail (#0) if (fvx_qwrite(path_sd, agbsave, data_size, data_size, NULL) != FR_OK) return 1; // write fail (#1) } - + // set CFG_BOOTENV to 0x7 so the save is taken over (not needed anymore) // https://www.3dbrew.org/wiki/CONFIG9_Registers#CFG9_BOOTENV // if (strncasecmp(path, "S:/agbsave.bin", 256) == 0) *(u32*) 0x10010000 = 0x7; @@ -199,6 +218,18 @@ u32 InjectGbaVcSavegame(const char* path, const char* path_vcsave) { return 0; } +u32 InjectGbaVcSavegame(const char* path, const char* path_vcsave) { + u8* buffer = (u8*) malloc(AGBSAVE_MAX_SIZE); + if (!buffer) { + ShowPrompt(false, "Out of memory."); + return 1; + } + + u32 ret = InjectGbaVcSavegameBuffered(path, path_vcsave, buffer); + free(buffer); + return ret; +} + u32 RebuildNandNcsdHeader(NandNcsdHeader* ncsd) { // signature (retail or dev) u8* signature = (IS_DEVKIT) ? sig_nand_ncsd_dev : sig_nand_ncsd_retail; @@ -324,38 +355,26 @@ u32 ValidateNandDump(const char* path) { } // check FIRMs (at least one FIRM must be valid) + u8* firm = (u8*) malloc(FIRM_MAX_SIZE); + if (!firm) return 1; + // check all 8 firms, also check if ARM9 & ARM11 entrypoints are available for (u32 f = 0; f <= 8; f++) { - FirmHeader firm; if (GetNandNcsdPartitionInfo(&info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, f, &ncsd) != 0) { ShowPrompt(false, "%s\nNo valid FIRM found", pathstr); fvx_close(&file); + free(firm); return 1; } - if ((ReadNandFile(&file, &firm, info.sector, 1, info.keyslot) != 0) || - (ValidateFirmHeader(&firm, 0) != 0) || (getbe32(firm.dec_magic) != 0) || // decrypted firms are not allowed - (!firm.entry_arm9) || (!firm.entry_arm11)) // arm9 / arm11 entry points must be there - continue; - // hash verify all available sections - u32 s; - for (s = 0; s < 4; s++) { - FirmSectionHeader* section = firm.sections + s; - u32 sector = info.sector + (section->offset / 0x200); - u32 count = section->size / 0x200; - if (!count) continue; - sha_init(SHA256_MODE); - // relies on sections being aligned to sectors - for (u32 c = 0; c < count; c += MAIN_BUFFER_SIZE / 0x200) { - u32 read_sectors = min(MAIN_BUFFER_SIZE / 0x200, (count - c)); - ReadNandFile(&file, MAIN_BUFFER, sector + c, read_sectors, info.keyslot); - sha_update(MAIN_BUFFER, read_sectors * 0x200); - } - u8 hash[0x20]; - sha_get(hash); - if (memcmp(hash, section->hash, 0x20) != 0) break; - } - if (s >= 4) break; // valid FIRM found + + u32 firm_size = info.count * 0x200; + if ((firm_size <= FIRM_MAX_SIZE) && + (ReadNandFile(&file, firm, info.sector, info.count, info.keyslot) == 0) && + (ValidateFirm(firm, firm_size, true) == 0)) + break; } + + free(firm); fvx_close(&file); return 0; @@ -430,6 +449,12 @@ u32 SafeRestoreNandDump(const char* path) { } } + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) { + fvx_close(&file); + return 1; + } + // main processing loop u32 ret = 0; u32 sector0 = SECTOR_SECRET + COUNT_SECRET; // start at the sector after secret sector @@ -440,15 +465,17 @@ u32 SafeRestoreNandDump(const char* path) { u32 subtype = NP_SUBTYPE_CTR; u32 sector1 = (GetNandNcsdPartitionInfo(&np_info, type, subtype, p, &ncsd_loc) == 0) ? np_info.sector : fsize / 0x200; if (sector1 < sector0) ret = 1; // safety check - for (u32 s = sector0; (s < sector1) && (ret == 0); s += MAIN_BUFFER_SIZE / 0x200) { - u32 count = min(MAIN_BUFFER_SIZE / 0x200, (sector1 - s)); - if (ReadNandFile(&file, MAIN_BUFFER, s, count, 0xFF)) ret = 1; - if (WriteNandSectors(MAIN_BUFFER, s, count, 0xFF, NAND_SYSNAND)) ret = 1; + for (u32 s = sector0; (s < sector1) && (ret == 0); s += STD_BUFFER_SIZE / 0x200) { + u32 count = min(STD_BUFFER_SIZE / 0x200, (sector1 - s)); + if (ReadNandFile(&file, buffer, s, count, 0xFF)) ret = 1; + if (WriteNandSectors(buffer, s, count, 0xFF, NAND_SYSNAND)) ret = 1; if (!ShowProgress(s + count, fsize / 0x200, path)) ret = 1; } if (sector1 == fsize / 0x200) break; // at file end sector0 = np_info.sector + np_info.count; // skip partition } + + free(buffer); fvx_close(&file); // NCSD header inject, should only be required with 2.1 local NANDs on N3DS @@ -459,14 +486,14 @@ u32 SafeRestoreNandDump(const char* path) { return ret; } -u32 SafeInstallFirm(const char* path, u32 slots) { +u32 SafeInstallFirmBuffered(const char* path, u32 slots, u8* buffer, u32 bufsiz) { char pathstr[32 + 1]; // truncated path string TruncateString(pathstr, path, 32, 8); // load / check FIRM - u8* firm = (u8*) TEMP_BUFFER; + u8* firm = buffer; UINT firm_size; - if ((fvx_qread(path, firm, 0, TEMP_BUFFER_SIZE, &firm_size) != FR_OK) || + if ((fvx_qread(path, firm, 0, bufsiz, &firm_size) != FR_OK) || !firm_size || !IsInstallableFirm(firm, firm_size)) { ShowPrompt(false, IsBootableFirm(firm, firm_size) ? "%s\nNot a installable FIRM." : "%s\nFIRM load/verify error.", pathstr); @@ -560,8 +587,19 @@ u32 SafeInstallFirm(const char* path, u32 slots) { return 0; } -u32 SafeInstallKeyDb(const char* path) -{ +u32 SafeInstallFirm(const char* path, u32 slots) { + u8* buffer = (u8*) malloc(FIRM_MAX_SIZE); + if (!buffer) { + ShowPrompt(false, "Out of memory."); + return 1; + } + + u32 ret = SafeInstallFirmBuffered(path, slots, buffer, FIRM_MAX_SIZE); + free(buffer); + return ret; +} + +u32 SafeInstallKeyDb(const char* path) { const u8 perfect_sha[] = { KEYDB_PERFECT_HASH }; u8 keydb[KEYDB_PERFECT_SIZE]; diff --git a/arm9/source/utils/scripting.c b/arm9/source/utils/scripting.c index 59a5f48..bdea3c1 100644 --- a/arm9/source/utils/scripting.c +++ b/arm9/source/utils/scripting.c @@ -925,16 +925,19 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) { ShowPrompt(false, argv[0]); } else if (id == CMD_ID_QR) { + const u32 screen_size = SCREEN_SIZE(ALT_SCREEN); + u8* screen_copy = (u8*) malloc(screen_size); u8 qrcode[qrcodegen_BUFFER_LEN_MAX]; u8 temp[qrcodegen_BUFFER_LEN_MAX]; - ret = qrcodegen_encodeText(argv[1], temp, qrcode, qrcodegen_Ecc_LOW, + ret = screen_copy && qrcodegen_encodeText(argv[1], temp, qrcode, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); if (ret) { - memcpy(TEMP_BUFFER, ALT_SCREEN, (SCREEN_HEIGHT * SCREEN_WIDTH_ALT * 3)); + memcpy(screen_copy, ALT_SCREEN, screen_size); DrawQrCode(ALT_SCREEN, qrcode); ShowPrompt(false, argv[0]); - memcpy(ALT_SCREEN, TEMP_BUFFER, (SCREEN_HEIGHT * SCREEN_WIDTH_ALT * 3)); - } + memcpy(ALT_SCREEN, screen_copy, screen_size); + } else if (err_str) snprintf(err_str, _ERR_STR_LEN, "out of memory"); + free(screen_copy); } else if (id == CMD_ID_ASK) { ret = ShowPrompt(true, argv[0]); @@ -1211,16 +1214,23 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) { } } else if (id == CMD_ID_BOOT) { - size_t firm_size = FileGetData(argv[0], TEMP_BUFFER, TEMP_BUFFER_SIZE, 0); - ret = firm_size && IsBootableFirm(TEMP_BUFFER, firm_size); - if (ret) { - char fixpath[256] = { 0 }; - if ((*argv[0] == '0') || (*argv[0] == '1')) - snprintf(fixpath, 256, "%s%s", (*argv[0] == '0') ? "sdmc" : "nand", argv[0] + 1); - else strncpy(fixpath, argv[0], 256); - BootFirm((FirmHeader*)(void*)TEMP_BUFFER, fixpath); - while(1); - } else if (err_str) snprintf(err_str, _ERR_STR_LEN, "not a bootable firm"); + u8* firm = (u8*) malloc(FIRM_MAX_SIZE); + if (!firm) { + ret = false; + if (err_str) snprintf(err_str, _ERR_STR_LEN, "out of memory"); + } else { + size_t firm_size = FileGetData(argv[0], firm, FIRM_MAX_SIZE, 0); + ret = firm_size && IsBootableFirm(firm, firm_size); + if (ret) { + char fixpath[256] = { 0 }; + if ((*argv[0] == '0') || (*argv[0] == '1')) + snprintf(fixpath, 256, "%s%s", (*argv[0] == '0') ? "sdmc" : "nand", argv[0] + 1); + else strncpy(fixpath, argv[0], 256); + BootFirm((FirmHeader*)(void*)firm, fixpath); + while(1); + } else if (err_str) snprintf(err_str, _ERR_STR_LEN, "not a bootable firm"); + free(firm); + } } else if (id == CMD_ID_SWITCHSD) { DeinitExtFS(); @@ -1255,7 +1265,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) { PowerOff(); } else if (id == CMD_ID_BKPT) { - asm("bkpt\n\t"); + bkpt; while(1); } else { // command not recognized / bad number of arguments @@ -1566,13 +1576,19 @@ bool MemToCViewer(const char* text, u32 len, const char* title) { bool FileTextViewer(const char* path, bool as_script) { // load text file (completely into memory) - char* text = (char*) TEMP_BUFFER; - u32 flen = FileGetData(path, text, TEMP_BUFFER_SIZE, 0); + // text file needs to fit inside the STD_BUFFER_SIZE + char* text = (char*) malloc(STD_BUFFER_SIZE); + if (!text) return false; + + u32 flen = FileGetData(path, text, STD_BUFFER_SIZE, 0); u32 len = 0; // actual length may be shorter due to zero symbol for (len = 0; (len < flen) && text[len]; len++); // let MemTextViewer take over - return MemTextViewer(text, len, 1, as_script); + bool result = MemTextViewer(text, len, 1, as_script); + + free(text); + return result; } bool ExecuteGM9Script(const char* path_script) { @@ -1624,16 +1640,20 @@ bool ExecuteGM9Script(const char* path_script) { ClearScreen(TOP_SCREEN, COLOR_STD_BG); if (preview_mode > 2) { char* preview_str = get_var("PREVIEW_MODE", NULL); - u8* pcx = TEMP_BUFFER + TEMP_BUFFER_SIZE / 2; - u32 pcx_size = FileGetData(preview_str, pcx, TEMP_BUFFER_SIZE / 2, 0); - if ((pcx_size > 0) && (pcx_size < TEMP_BUFFER_SIZE / 2) && - (PCX_Decompress(TEMP_BUFFER, TEMP_BUFFER_SIZE / 2, pcx, pcx_size))) { + u32 pcx_size = fvx_qsize(preview_str); + u8* pcx = (u8*) malloc(SCREEN_SIZE_TOP); + u8* bitmap = (u8*) malloc(SCREEN_SIZE_TOP); + if (pcx && bitmap && pcx_size && (pcx_size < SCREEN_SIZE_TOP) && + (pcx_size == FileGetData(preview_str, pcx, pcx_size, 0)) && + (PCX_Decompress(bitmap, SCREEN_SIZE_TOP, pcx, pcx_size))) { PCXHdr* hdr = (PCXHdr*) (void*) pcx; - DrawBitmap(TOP_SCREEN, -1, -1, PCX_Width(hdr), PCX_Height(hdr), TEMP_BUFFER); + DrawBitmap(TOP_SCREEN, -1, -1, PCX_Width(hdr), PCX_Height(hdr), bitmap); } else { if (strncmp(preview_str, "off", _VAR_CNT_LEN) == 0) preview_str = "(preview disabled)"; DrawStringCenter(TOP_SCREEN, COLOR_STD_FONT, COLOR_STD_BG, preview_str); } + if (pcx) free(pcx); + if (bitmap) free(bitmap); preview_mode = 0; } preview_mode_local = preview_mode; diff --git a/arm9/source/virtual/vgame.c b/arm9/source/virtual/vgame.c index 25e6850..765cc3f 100644 --- a/arm9/source/virtual/vgame.c +++ b/arm9/source/virtual/vgame.c @@ -602,31 +602,32 @@ bool BuildVGameFirmDir(void) { templates[n].flags = VFLAG_NO_CRYPTO; n++; if (section->method == FIRM_NDMA_CPY) { // ARM9 section, search for Process9 - u8* buffer = (u8*) (TEMP_BUFFER + (TEMP_BUFFER_SIZE/2)); - u32 buffer_size = TEMP_BUFFER_SIZE/2; - NcchHeader* p9_ncch; + NcchHeader p9_ncch; char name[8]; u32 offset_p9 = 0; - for (u32 p = 0; (p < section->size) && (!offset_p9); p += buffer_size) { - u32 btr = min(buffer_size, (section->size - p)); - if (ReadGameImageBytes(buffer, section->offset + p, btr) != 0) break; - for (u32 s = 0; (s < btr) && (!offset_p9); s += 0x10) { - p9_ncch = (NcchHeader*) (void*) (buffer + s); - if ((ValidateNcchHeader(p9_ncch) == 0) && - (ReadGameImageBytes((u8*) name, section->offset + p + s + 0x200, 8) == 0)) - offset_p9 = section->offset + p + s; + + u8* buffer = (u8*) malloc(section->size); + if (buffer) { + if (ReadGameImageBytes(buffer, section->offset, section->size) != 0) break; + for (u32 s = 0; (s < section->size - 0x400) && (!offset_p9); s += 0x10) { + if ((ValidateNcchHeader((NcchHeader*) (void*) (buffer + s)) == 0) && + (ReadGameImageBytes((u8*) name, section->offset + s + 0x200, 8) == 0)) { + offset_p9 = section->offset + s; + memcpy(&p9_ncch, buffer + s, sizeof(NcchHeader)); + } } + free(buffer); } if (offset_p9) { - snprintf(templates[n].name, 32, NAME_FIRM_NCCH, p9_ncch->programId, name, ".app"); + snprintf(templates[n].name, 32, NAME_FIRM_NCCH, p9_ncch.programId, name, ".app"); templates[n].offset = offset_p9; - templates[n].size = p9_ncch->size * NCCH_MEDIA_UNIT; + templates[n].size = p9_ncch.size * NCCH_MEDIA_UNIT; templates[n].keyslot = (offset_a9bin == (u64) -1) ? 0xFF : 0x15; templates[n].flags = 0; n++; memcpy(templates + n, templates + n - 1, sizeof(VirtualFile)); - snprintf(templates[n].name, 32, NAME_FIRM_NCCH, p9_ncch->programId, name, ""); + snprintf(templates[n].name, 32, NAME_FIRM_NCCH, p9_ncch.programId, name, ""); templates[n].flags |= (VFLAG_NCCH | VFLAG_DIR); n++; } diff --git a/arm9/source/virtual/virtual.c b/arm9/source/virtual/virtual.c index 16ccdd4..3061d2b 100644 --- a/arm9/source/virtual/virtual.c +++ b/arm9/source/virtual/virtual.c @@ -192,18 +192,22 @@ int WriteVirtualFile(const VirtualFile* vfile, const void* buffer, u64 offset, u } int DeleteVirtualFile(const VirtualFile* vfile) { - u8* zeroes = (u8*) TEMP_BUFFER; - u32 zeroes_size = TEMP_BUFFER_SIZE; - if (!(vfile->flags & VFLAG_DELETABLE)) return -1; - memset(zeroes, 0x00, TEMP_BUFFER_SIZE); + + u32 zeroes_size = STD_BUFFER_SIZE; + u8* zeroes = (u8*) malloc(zeroes_size); + if (!zeroes) return -1; + memset(zeroes, 0x00, zeroes_size); + + int result = 0; for (u64 pos = 0; pos < vfile->size; pos += zeroes_size) { u64 wipe_bytes = min(zeroes_size, vfile->size - pos); - if (WriteVirtualFile(vfile, zeroes, pos, wipe_bytes, NULL) != 0) - return -1; + result = WriteVirtualFile(vfile, zeroes, pos, wipe_bytes, NULL); + if (result != 0) break; } - return 0; + free(zeroes); + return result; } u64 GetVirtualDriveSize(const char* path) { diff --git a/arm9/source/virtual/vtickdb.c b/arm9/source/virtual/vtickdb.c index 44ecb66..9804045 100644 --- a/arm9/source/virtual/vtickdb.c +++ b/arm9/source/virtual/vtickdb.c @@ -69,24 +69,31 @@ u32 InitVTickDbDrive(void) { // prerequisite: ticket.db mounted as image // reset internal db memset(tick_info, 0, 16); + // set up buffer + u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); + if (!buffer) return 0; + // parse file, sector by sector for (u32 p = 0; p < sizeof(area_offsets) / sizeof(u32); p++) { u32 offset_area = area_offsets[p]; - for (u32 i = 0; i < TICKDB_AREA_SIZE; i += (TEMP_BUFFER_SIZE - 0x200)) { - u32 read_bytes = min(TEMP_BUFFER_SIZE, TICKDB_AREA_SIZE - i); - u8* data = (u8*) TEMP_BUFFER; + for (u32 i = 0; i < TICKDB_AREA_SIZE; i += (STD_BUFFER_SIZE - 0x200)) { + u32 read_bytes = min(STD_BUFFER_SIZE, TICKDB_AREA_SIZE - i); + u8* data = buffer; if (ReadImageBytes(data, offset_area + i, read_bytes) != 0) { tick_info->n_entries = 0; + free(buffer); return 0; } - for (; data + TICKET_SIZE < ((u8*) TEMP_BUFFER) + read_bytes; data += 0x200) { + // likely bug here (!!!) (not a new one) + for (; data + TICKET_SIZE < buffer + read_bytes; data += 0x200) { Ticket* ticket = TicketFromTickDbChunk(data, NULL, true); if (!ticket) continue; - AddTickDbInfo(tick_info, ticket, offset_area + i + (data - ((u8*) TEMP_BUFFER)) + 0x18); + AddTickDbInfo(tick_info, ticket, offset_area + i + (data - buffer) + 0x18); } } } + free(buffer); return (tick_info->n_entries) ? SYS_TICKDB : 0; }