Replace TEMP_BUFFER & MAIN_BUFFER with heap

This commit is contained in:
d0k3 2018-01-24 23:32:06 +01:00
parent e9dc378b7a
commit a0d2d27b06
23 changed files with 1006 additions and 606 deletions

View File

@ -31,52 +31,37 @@
#define countof(x) \ #define countof(x) \
(sizeof(x) / sizeof((x)[0])) (sizeof(x) / sizeof((x)[0]))
#define bkpt \
asm("bkpt\n\t")
#define STATIC_ASSERT(...) \ #define STATIC_ASSERT(...) \
_Static_assert((__VA_ARGS__), #__VA_ARGS__) _Static_assert((__VA_ARGS__), #__VA_ARGS__)
// standard output path (support file paths are in support.h) // standard output path (support file paths are in support.h)
#define OUTPUT_PATH "0:/gm9/out" #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) // buffer area defines (in use by godmode.c)
#define DIR_BUFFER (0x20000000) #define DIR_BUFFER (0x20000000)
#define DIR_BUFFER_SIZE (0x100000) #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) // 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 #define NAND_BUFFER_SIZE (0x100000) // must be multiple of 0x200
// buffer area defines (in use by sddata.c) // buffer area defines (in use by sddata.c)
#define SDCRYPT_BUFFER ((u8*)0x20300000) #define SDCRYPT_BUFFER ((u8*)0x20200000)
#define SDCRYPT_BUFFER_SIZE (0x100000) #define SDCRYPT_BUFFER_SIZE (0x100000)
// buffer area defines (in use by scripting.c) // buffer area defines (in use by scripting.c)
#define SCRIPT_BUFFER ((u8*)0x20400000) #define SCRIPT_BUFFER ((u8*)0x20300000)
#define SCRIPT_BUFFER_SIZE (0x100000) #define SCRIPT_BUFFER_SIZE (0x100000)
// buffer area defines (in use by vgame.c) // 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 #define VGAME_BUFFER_SIZE (0x200000) // 2MB, big RomFS
// buffer area defines (in use by vcart.c) // 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 #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) // buffer area defines (in use by image.c, for RAMdrive)
#define RAMDRV_BUFFER ((u8*)0x22800000) // top of STACK #define RAMDRV_BUFFER ((u8*)0x22800000) // top of STACK
#define RAMDRV_SIZE_O3DS (0x5800000) // 88MB #define RAMDRV_SIZE_O3DS (0x5800000) // 88MB

View File

@ -14,6 +14,9 @@
#define SCREEN_WIDTH(s) ((s == TOP_SCREEN) ? SCREEN_WIDTH_TOP : SCREEN_WIDTH_BOT) #define SCREEN_WIDTH(s) ((s == TOP_SCREEN) ? SCREEN_WIDTH_TOP : SCREEN_WIDTH_BOT)
#define SCREEN_WIDTH_TOP 400 #define SCREEN_WIDTH_TOP 400
#define SCREEN_WIDTH_BOT 320 #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_WIDTH_EXT GetFontWidth()
#define FONT_HEIGHT_EXT GetFontHeight() #define FONT_HEIGHT_EXT GetFontHeight()

View File

@ -131,8 +131,9 @@ u32 LoadKeyFromFile(void* key, u32 keyslot, char type, char* id)
if (!key) key = keystore; if (!key) key = keystore;
// try to get key from 'aeskeydb.bin' file // try to get key from 'aeskeydb.bin' file
AesKeyInfo* keydb = (AesKeyInfo*) TEMP_BUFFER; AesKeyInfo* keydb = (AesKeyInfo*) malloc(STD_BUFFER_SIZE);
u32 nkeys = LoadKeyDb(NULL, keydb, TEMP_BUFFER_SIZE); u32 nkeys = (keydb) ? LoadKeyDb(NULL, keydb, STD_BUFFER_SIZE) : 0;
for (u32 i = 0; i < nkeys; i++) { for (u32 i = 0; i < nkeys; i++) {
AesKeyInfo* info = &(keydb[i]); AesKeyInfo* info = &(keydb[i]);
if (!((info->slot == keyslot) && (info->type == type) && if (!((info->slot == keyslot) && (info->type == type) &&
@ -146,6 +147,8 @@ u32 LoadKeyFromFile(void* key, u32 keyslot, char type, char* id)
break; break;
} }
free(keydb);
// load legacy slot0x??Key?.bin file instead // load legacy slot0x??Key?.bin file instead
if (!found && (type != 'I')) { if (!found && (type != 'I')) {
char fname[64]; 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); (1ull<<0x1C)|(1ull<<0x1D)|(1ull<<0x1E)|(1ull<<0x1F)|(1ull<<0x24)|(1ull<<0x25)|(1ull<<0x2F);
// try to load aeskeydb.bin file // try to load aeskeydb.bin file
AesKeyInfo* keydb = (AesKeyInfo*) (void*) TEMP_BUFFER; AesKeyInfo* keydb = (AesKeyInfo*) malloc(STD_BUFFER_SIZE);
u32 nkeys = LoadKeyDb(path, keydb, TEMP_BUFFER_SIZE); u32 nkeys = (keydb) ? LoadKeyDb(path, keydb, STD_BUFFER_SIZE) : 0;
if (!nkeys) return 1;
// apply all applicable keys // apply all applicable keys
for (u32 i = 0; i < nkeys; i++) { for (u32 i = 0; i < nkeys; i++) {
AesKeyInfo* info = &(keydb[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 return 1; // looks faulty, better stop right here
}
if (!path && !((1ull<<info->slot)&keyslot_whitelist)) continue; // not in keyslot whitelist if (!path && !((1ull<<info->slot)&keyslot_whitelist)) continue; // not in keyslot whitelist
if ((info->type == 'I') || (*(info->id)) || (info->keyUnitType && (info->keyUnitType != GetUnitKeysType())) || 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 (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); use_aeskey(keyslot);
} }
return 0; free(keydb);
return (nkeys) ? 0 : 1;
} }
// creates dependency to "sha.h", not required for base keydb functions // 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 // try to load aeskeydb.bin file
AesKeyInfo* keydb = (AesKeyInfo*) (void*) TEMP_BUFFER; AesKeyInfo* keydb = (AesKeyInfo*) malloc(STD_BUFFER_SIZE);
u32 nkeys = LoadKeyDb(path, keydb, TEMP_BUFFER_SIZE); if (!keydb) return 1;
if (!nkeys) return 1; u32 nkeys = LoadKeyDb(path, keydb, STD_BUFFER_SIZE);
// compare with recommended SHA // 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;
} }

View File

@ -133,7 +133,7 @@ u64 IdentifyFileType(const char* path) {
u64 type = 0; u64 type = 0;
if ((fsize <= SCRIPT_MAX_SIZE) && ext && (strncasecmp(ext, SCRIPT_EXT, strnlen(SCRIPT_EXT, 16) + 1) == 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) 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; return type;
} else if ((strncmp(path + 2, "/Nintendo DSiWare/", 18) == 0) && } else if ((strncmp(path + 2, "/Nintendo DSiWare/", 18) == 0) &&
(sscanf(fname, "%08lx.bin", &id) == 1) && (strncasecmp(ext, "bin", 4) == 0)) { (sscanf(fname, "%08lx.bin", &id) == 1) && (strncasecmp(ext, "bin", 4) == 0)) {

View File

@ -23,7 +23,10 @@ bool InitExtFS() {
if (fs_mounted[i]) continue; if (fs_mounted[i]) continue;
fs_mounted[i] = (f_mount(fs + i, fsname, 1) == FR_OK); fs_mounted[i] = (f_mount(fs + i, fsname, 1) == FR_OK);
if (!fs_mounted[i] && (i == NORM_FS - 1) && !(GetMountState() & IMG_NAND)) { 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); f_mount(NULL, fsname, 1);
fs_mounted[i] = (f_mount(fs + i, fsname, 1) == FR_OK); fs_mounted[i] = (f_mount(fs + i, fsname, 1) == FR_OK);
} }

View File

@ -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 VolToPart[0].pt = 1; // workaround to prevent FatFS rebuilding the MBR
InitSDCardFS(); InitSDCardFS();
UINT c_size = cluster_size; 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); (f_setlabel((label) ? label : "0:GM9SD") == FR_OK);
free(buffer);
DeinitSDCardFS(); DeinitSDCardFS();
VolToPart[0].pt = 0; // revert workaround to prevent SD mount problems VolToPart[0].pt = 0; // revert workaround to prevent SD mount problems
@ -91,7 +96,12 @@ bool SetupBonusDrive(void) {
return false; return false;
ShowString("Formatting drive, please wait..."); ShowString("Formatting drive, please wait...");
if (GetMountState() & IMG_NAND) InitImgFS(NULL); 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) { if (ret) {
f_setlabel("8:BONUS"); f_setlabel("8:BONUS");
InitExtFS(); 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) if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return false; return false;
fsize = fvx_size(&file); fsize = fvx_size(&file);
if (offset + size > fsize) return false; if (offset + size > fsize) return false;
if (!size) size = fsize - offset; if (!size) size = fsize - offset;
fvx_lseek(&file, 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); ShowProgress(0, 0, path);
sha_init(SHA256_MODE); sha_init(SHA256_MODE);
for (u64 pos = 0; (pos < size) && ret; pos += MAIN_BUFFER_SIZE) { for (u64 pos = 0; (pos < size) && ret; pos += bufsiz) {
UINT read_bytes = min(MAIN_BUFFER_SIZE, size - pos); UINT read_bytes = min(bufsiz, size - pos);
UINT bytes_read = 0; 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; ret = false;
if (!ShowProgress(pos + bytes_read, size, path)) if (!ShowProgress(pos + bytes_read, size, path))
ret = false; ret = false;
sha_update(MAIN_BUFFER, bytes_read); sha_update(buffer, bytes_read);
} }
sha_get(sha256); sha_get(sha256);
fvx_close(&file); fvx_close(&file);
free(buffer);
ShowProgress(1, 1, path); ShowProgress(1, 1, path);
return ret; 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) if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return found; return found;
u8* buffer = (u8*) malloc(STD_BUFFER_SIZE);
if (!buffer) return false;
// main routine // main routine
for (u32 pass = 0; pass < 2; pass++) { for (u32 pass = 0; pass < 2; pass++) {
bool show_progress = false; bool show_progress = false;
u64 pos = (pass == 0) ? offset_file : 0; u64 pos = (pass == 0) ? offset_file : 0;
u64 search_end = (pass == 0) ? fsize : offset_file + size_data; u64 search_end = (pass == 0) ? fsize : offset_file + size_data;
search_end = (search_end > fsize) ? fsize : search_end; search_end = (search_end > fsize) ? fsize : search_end;
for (; (pos < search_end) && (found == (u64) -1); pos += MAIN_BUFFER_SIZE - (size_data - 1)) { for (; (pos < search_end) && (found == (u64) -1); pos += STD_BUFFER_SIZE - (size_data - 1)) {
UINT read_bytes = min(MAIN_BUFFER_SIZE, search_end - pos); UINT read_bytes = min(STD_BUFFER_SIZE, search_end - pos);
UINT btr; UINT btr;
fvx_lseek(&file, pos); 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; break;
for (u32 i = 0; i + size_data <= read_bytes; i++) { 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; found = pos + i;
break; break;
} }
@ -202,6 +224,8 @@ u32 FileFindData(const char* path, u8* data, u32 size_data, u32 offset_file) {
break; break;
} }
} }
free(buffer);
fvx_close(&file); fvx_close(&file);
return found; return found;
@ -244,14 +268,17 @@ bool FileInjectFile(const char* dest, const char* orig, u64 off_dest, u64 off_or
return false; return false;
} }
u8* buffer = (u8*) malloc(STD_BUFFER_SIZE);
if (!buffer) return false;
bool ret = true; bool ret = true;
ShowProgress(0, 0, orig); ShowProgress(0, 0, orig);
for (u64 pos = 0; (pos < size) && ret; pos += MAIN_BUFFER_SIZE) { for (u64 pos = 0; (pos < size) && ret; pos += STD_BUFFER_SIZE) {
UINT read_bytes = min(MAIN_BUFFER_SIZE, size - pos); UINT read_bytes = min(STD_BUFFER_SIZE, size - pos);
UINT bytes_read = read_bytes; UINT bytes_read = read_bytes;
UINT bytes_written = read_bytes; UINT bytes_written = read_bytes;
if ((fvx_read(&ofile, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK) || if ((fvx_read(&ofile, buffer, read_bytes, &bytes_read) != FR_OK) ||
(fvx_write(&dfile, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK) || (fvx_write(&dfile, buffer, read_bytes, &bytes_written) != FR_OK) ||
(bytes_read != bytes_written)) (bytes_read != bytes_written))
ret = false; ret = false;
if (ret && !ShowProgress(pos + bytes_read, size, orig)) { 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); ShowProgress(1, 1, orig);
free(buffer);
fvx_close(&dfile); fvx_close(&dfile);
fvx_close(&ofile); fvx_close(&ofile);
@ -290,13 +318,17 @@ bool FileSetByte(const char* dest, u64 offset, u64 size, u8 fillbyte, u32* flags
return false; 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; bool ret = true;
memset(MAIN_BUFFER, fillbyte, size);
ShowProgress(0, 0, dest); ShowProgress(0, 0, dest);
for (u64 pos = 0; (pos < size) && ret; pos += MAIN_BUFFER_SIZE) { for (u64 pos = 0; (pos < size) && ret; pos += bufsiz) {
UINT write_bytes = min(MAIN_BUFFER_SIZE, size - pos); UINT write_bytes = min(bufsiz, size - pos);
UINT bytes_written = write_bytes; 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)) (write_bytes != bytes_written))
ret = false; ret = false;
if (ret && !ShowProgress(pos + bytes_written, size, dest)) { 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); ShowProgress(1, 1, dest);
free(buffer);
fvx_close(&dfile); fvx_close(&dfile);
return ret; return ret;
@ -396,7 +429,7 @@ bool PathExist(const char* path) {
return (fvx_stat(path, NULL) == FR_OK); 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 to_virtual = GetVirtualSource(dest);
bool silent = (flags && (*flags & SILENT)); bool silent = (flags && (*flags & SILENT));
bool ret = false; 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 if (oname == NULL) return false; // not a proper origin path
*(dname++) = '/'; *(dname++) = '/';
strncpy(dname, oname++, 256 - (dname - dest)); strncpy(dname, oname++, 256 - (dname - dest));
bool res = PathMoveCopyRec(dest, orig, flags, move); bool res = PathMoveCopyRec(dest, orig, flags, move, buffer, bufsiz);
*(--dname) = '\0'; *(--dname) = '\0';
if (!res) break; if (!res) break;
} }
} }
fvx_closedir(&pdir); fvx_closedir(&pdir);
*(--fname) = '\0';
} else if (move) { // moving if destination exists } else if (move) { // moving if destination exists
if (fvx_stat(dest, &fno) != FR_OK) return false; if (fvx_stat(dest, &fno) != FR_OK) return false;
if (fno.fattrib & AM_DIR) { if (fno.fattrib & AM_DIR) {
@ -495,11 +530,11 @@ bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move) {
fvx_sync(&ofile); fvx_sync(&ofile);
if (flags && (*flags & CALC_SHA)) sha_init(SHA256_MODE); 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_read = 0;
UINT bytes_written = 0; UINT bytes_written = 0;
if ((fvx_read(&ofile, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK) || if ((fvx_read(&ofile, buffer, bufsiz, &bytes_read) != FR_OK) ||
(fvx_write(&dfile, MAIN_BUFFER, bytes_read, &bytes_written) != FR_OK) || (fvx_write(&dfile, buffer, bytes_read, &bytes_written) != FR_OK) ||
(bytes_read != bytes_written)) (bytes_read != bytes_written))
ret = false; ret = false;
if (ret && !ShowProgress(pos + bytes_read, fsize, orig)) { 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); ShowProgress(pos + bytes_read, fsize, orig);
} }
if (flags && (*flags & CALC_SHA)) if (flags && (*flags & CALC_SHA))
sha_update(MAIN_BUFFER, bytes_read); sha_update(buffer, bytes_read);
} }
ShowProgress(1, 1, orig); 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 // ensure the destination path exists
if (flags && (*flags & BUILD_PATH)) fvx_rmkpath(ldest); 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 // actual move / copy operation
bool same_drv = (strncasecmp(lorig, ldest, 2) == 0); 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); if (move && res && (!flags || !(*flags&SKIP_CUR))) PathDelete(lorig);
free(buffer);
return res; return res;
} else { // virtual destination handling } else { // virtual destination handling
// can't write an SHA file to a virtual destination // 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 // handle NAND image unmounts
if (ddrvtype & (DRV_SYSNAND|DRV_EMUNAND|DRV_IMAGE)) { if (ddrvtype & (DRV_SYSNAND|DRV_EMUNAND|DRV_IMAGE)) {
FILINFO fno; 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)) if ((fvx_stat(ldest, &fno) == FR_OK) && (fno.fsize > 4 * 1024 * 1024))
force_unmount = true; force_unmount = true;
} }
@ -637,10 +678,16 @@ bool PathMoveCopy(const char* dest, const char* orig, u32* flags, bool move) {
return false; return false;
} }
// setup buffer
u8* buffer = (u8*) malloc(STD_BUFFER_SIZE);
if (!buffer) return false;
// actual virtual copy operation // actual virtual copy operation
if (force_unmount) DismountDriveType(DriveType(ldest)&(DRV_SYSNAND|DRV_EMUNAND|DRV_IMAGE)); 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(); if (force_unmount) InitExtFS();
free(buffer);
return res; return res;
} }
} }
@ -719,8 +766,8 @@ bool PathAttr(const char* path, u8 attr, u8 mask) {
return (f_chmod(path, attr, mask) == FR_OK); return (f_chmod(path, attr, mask) == FR_OK);
} }
bool FileSelector(char* result, const char* text, const char* path, const char* pattern, u32 flags) { bool FileSelectorWorker(char* result, const char* text, const char* path, const char* pattern, u32 flags, void* buffer) {
DirStruct* contents = (DirStruct*) (void*) TEMP_BUFFER; DirStruct* contents = (DirStruct*) buffer;
char path_local[256]; char path_local[256];
strncpy(path_local, path, 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) { if (select_dirs) {
strncpy(result, res_local->path, 256); strncpy(result, res_local->path, 256);
return true; 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; return true;
} }
break; 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() { void CreateScreenshot() {
const u32 snap_size = 54 + (400 * 240 * 3 * 2);
const u8 bmp_header[54] = { const u8 bmp_header[54] = {
0x42, 0x4D, 0x36, 0xCA, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, 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, 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, 0xCA, 0x08, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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]; char filename[64];
static u32 n = 0; static u32 n = 0;
@ -810,13 +866,21 @@ void CreateScreenshot() {
} }
if (n >= 1000) return; if (n >= 1000) return;
memcpy(MAIN_BUFFER, bmp_header, 54); u8* buffer = (u8*) malloc(snap_size);
memset(buffer, 0x1F, 400 * 240 * 3 * 2); 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 x = 0; x < 400; x++)
for (u32 y = 0; y < 240; y++) for (u32 y = 0; y < 240; y++)
memcpy(buffer_t + (y*400 + x) * 3, TOP_SCREEN + (x*240 + y) * 3, 3); memcpy(buffer_t + (y*400 + x) * 3, TOP_SCREEN + (x*240 + y) * 3, 3);
for (u32 x = 0; x < 320; x++) for (u32 x = 0; x < 320; x++)
for (u32 y = 0; y < 240; y++) for (u32 y = 0; y < 240; y++)
memcpy(buffer + (y*400 + x + 40) * 3, BOT_SCREEN + (x*240 + y) * 3, 3); memcpy(buffer_b + (y*400 + x + 40) * 3, BOT_SCREEN + (x*240 + y) * 3, 3);
FileSetData(filename, MAIN_BUFFER, 54 + (400 * 240 * 3 * 2), 0, true); FileSetData(filename, buffer, snap_size, 0, true);
free(buffer);
} }

View File

@ -194,6 +194,11 @@ FRESULT fvx_qwrite (const TCHAR* path, const void* buff, FSIZE_t ofs, UINT btw,
return res; 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 #if !_LFN_UNICODE // this will not work for unicode
FRESULT worker_fvx_rmkdir (TCHAR* tpath) { FRESULT worker_fvx_rmkdir (TCHAR* tpath) {
DIR tmp_dir; DIR tmp_dir;

View File

@ -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_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); 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 // additional recursive functions
FRESULT fvx_rmkdir (const TCHAR* path); FRESULT fvx_rmkdir (const TCHAR* path);
FRESULT fvx_rmkpath (const TCHAR* path); FRESULT fvx_rmkpath (const TCHAR* path);

View File

@ -63,6 +63,10 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) {
return 0; 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 // try to grab the seed from NAND database
const u32 seed_offset[2] = {SEEDSAVE_AREA_OFFSETS}; const u32 seed_offset[2] = {SEEDSAVE_AREA_OFFSETS};
const char* nand_drv[] = {"1:", "4:"}; // SysNAND and EmuNAND const char* nand_drv[] = {"1:", "4:"}; // SysNAND and EmuNAND
@ -87,8 +91,8 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) {
// check seedsave for seed // check seedsave for seed
u8* seeddb[2]; u8* seeddb[2];
seeddb[0] = (u8*) (TEMP_BUFFER + (TEMP_BUFFER_SIZE/2)); seeddb[0] = buffer;
seeddb[1] = seeddb[0] + (SEEDSAVE_MAX_ENTRIES*(8+16)); seeddb[1] = buffer + (SEEDSAVE_MAX_ENTRIES*(8+16));
if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
continue; continue;
f_read(&file, seeddb[0], 0x200, &btr); f_read(&file, seeddb[0], 0x200, &btr);
@ -110,6 +114,7 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) {
if (hash_seed == sha256sum[0]) { if (hash_seed == sha256sum[0]) {
memcpy(seed, lseed, 16); memcpy(seed, lseed, 16);
f_close(&file); f_close(&file);
free(buffer);
return 0; // found! return 0; // found!
} }
} }
@ -119,8 +124,8 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) {
} }
// not found -> try seeddb.bin // not found -> try seeddb.bin
SeedInfo* seeddb = (SeedInfo*) (TEMP_BUFFER + (TEMP_BUFFER_SIZE/2)); SeedInfo* seeddb = (SeedInfo*) (void*) buffer;
size_t len = LoadSupportFile(SEEDDB_NAME, seeddb, (TEMP_BUFFER_SIZE/2)); size_t len = LoadSupportFile(SEEDDB_NAME, seeddb, STD_BUFFER_SIZE);
if (len && (seeddb->n_entries <= (len - 16) / 32)) { // check filesize / seeddb size if (len && (seeddb->n_entries <= (len - 16) / 32)) { // check filesize / seeddb size
for (u32 s = 0; s < seeddb->n_entries; s++) { for (u32 s = 0; s < seeddb->n_entries; s++) {
if (titleId != seeddb->entries[s].titleId) if (titleId != seeddb->entries[s].titleId)
@ -129,12 +134,14 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) {
sha_quick(sha256sum, lseed, 16 + 8, SHA256_MODE); sha_quick(sha256sum, lseed, 16 + 8, SHA256_MODE);
if (hash_seed == sha256sum[0]) { if (hash_seed == sha256sum[0]) {
memcpy(seed, lseed, 16); memcpy(seed, lseed, 16);
free(buffer);
return 0; // found! return 0; // found!
} }
} }
} }
// out of options -> failed! // out of options -> failed!
free(buffer);
return 1; return 1;
} }

View File

@ -131,11 +131,13 @@ u32 FindTicket(Ticket* ticket, u8* title_id, bool force_legit, bool emunand) {
u32 FindTitleKey(Ticket* ticket, u8* title_id) { u32 FindTitleKey(Ticket* ticket, u8* title_id) {
bool found = false; 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 // search for a titlekey inside encTitleKeys.bin / decTitleKeys.bin
// when found, add it to the ticket // when found, add it to the ticket
for (u32 enc = 0; (enc <= 1) && !found; enc++) { 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, STD_BUFFER_SIZE);
u32 len = LoadSupportFile((enc) ? TIKDB_NAME_ENC : TIKDB_NAME_DEC, tikdb, (TEMP_BUFFER_SIZE/2));
if (len == 0) continue; // file not found if (len == 0) continue; // file not found
if (tikdb->n_entries > (len - 16) / 32) if (tikdb->n_entries > (len - 16) / 32)
@ -153,6 +155,7 @@ u32 FindTitleKey(Ticket* ticket, u8* title_id) {
} }
} }
free(tikdb);
return (found) ? 0 : 1; return (found) ? 0 : 1;
} }

View File

@ -35,7 +35,7 @@ u32 FixTmdHashes(TitleMetaData* tmd) {
u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents, u32 save_size) { u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents, u32 save_size) {
const u8 sig_type[4] = { TMD_SIG_TYPE }; const u8 sig_type[4] = { TMD_SIG_TYPE };
// safety check: number of contents // 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 // set TMD all zero for a clean start
memset(tmd, 0x00, TMD_SIZE_N(n_contents)); memset(tmd, 0x00, TMD_SIZE_N(n_contents));
// file TMD values // file TMD values

View File

@ -46,6 +46,7 @@ typedef struct {
u32 SplashInit(const char* modestr) { u32 SplashInit(const char* modestr) {
u64 splash_size; u64 splash_size;
u8* splash = FindVTarFileInfo(VRAM0_SPLASH_PCX, &splash_size); u8* splash = FindVTarFileInfo(VRAM0_SPLASH_PCX, &splash_size);
u8* bitmap = (u8*) malloc(SCREEN_SIZE_TOP);
const char* namestr = FLAVOR " " VERSION; const char* namestr = FLAVOR " " VERSION;
const char* loadstr = "booting..."; const char* loadstr = "booting...";
const u32 pos_xb = 10; const u32 pos_xb = 10;
@ -55,9 +56,9 @@ u32 SplashInit(const char* modestr) {
ClearScreenF(true, true, COLOR_STD_BG); 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; 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)"); } 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), if (modestr) DrawStringF(TOP_SCREEN, SCREEN_WIDTH_TOP - 10 - GetDrawStringWidth(modestr),
SCREEN_HEIGHT - 10 - GetDrawStringHeight(modestr), COLOR_STD_FONT, COLOR_TRANSPARENT, 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_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); DrawStringF(BOT_SCREEN, pos_xb, pos_yu, COLOR_STD_FONT, COLOR_STD_BG, "built: " DBUILTL);
if (bitmap) free(bitmap);
return 0; return 0;
} }
@ -391,40 +393,50 @@ u32 SdFormatMenu(void) {
} }
u32 FileGraphicsViewer(const char* path) { u32 FileGraphicsViewer(const char* path) {
const u32 max_size = SCREEN_SIZE(ALT_SCREEN);
u64 filetype = IdentifyFileType(path); u64 filetype = IdentifyFileType(path);
u8* bitmap = TEMP_BUFFER; u8* bitmap = (u8*) malloc(max_size);
u32 buffer_size = TEMP_BUFFER_SIZE / 2; u8* input = (u8*) malloc(max_size);
u32 w = 0; u32 w = 0;
u32 h = 0; u32 h = 0;
u32 ret = 1;
if (filetype & GFX_PCX) { if (!bitmap || !input) {
u8* pcx = TEMP_BUFFER + TEMP_BUFFER_SIZE / 2; if (bitmap) free(bitmap);
u32 pcx_size = FileGetData(path, pcx, TEMP_BUFFER_SIZE / 2, 0); if (input) free(input);
if ((pcx_size > 0) && (pcx_size < TEMP_BUFFER_SIZE / 2) && return 1;
(PCX_Decompress(bitmap, buffer_size, pcx, pcx_size))) { }
PCXHdr* hdr = (PCXHdr*) (void*) pcx;
w = PCX_Width(hdr); u32 input_size = FileGetData(path, input, max_size, 0);
h = PCX_Height(hdr); 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); ClearScreenF(true, true, COLOR_STD_BG);
DrawBitmap(ALT_SCREEN, -1, -1, w, h, bitmap); DrawBitmap(ALT_SCREEN, -1, -1, w, h, bitmap);
ShowString("Press <A> to continue"); ShowString("Press <A> to continue");
InputWait(0); InputWait(0);
ClearScreenF(true, true, COLOR_STD_BG); ClearScreenF(true, true, COLOR_STD_BG);
return 0; } else ret = 1;
}
return 1; free(bitmap);
free(input);
return ret;
} }
u32 FileHexViewer(const char* path) { u32 FileHexViewer(const char* path) {
const u32 max_data = (SCREEN_HEIGHT / FONT_HEIGHT_EXT) * 16 * ((FONT_WIDTH_EXT > 4) ? 1 : 2); const u32 max_data = (SCREEN_HEIGHT / FONT_HEIGHT_EXT) * 16 * ((FONT_WIDTH_EXT > 4) ? 1 : 2);
static u32 mode = 0; static u32 mode = 0;
u8* data = TEMP_BUFFER; u8* data = NULL;
u8* bottom_cpy = TEMP_BUFFER + 0xC0000; // a copy of the bottom screen framebuffer u8* bottom_cpy = (u8*) malloc(SCREEN_SIZE_BOT); // a copy of the bottom screen framebuffer
u32 fsize = FileGetSize(path); u32 fsize = FileGetSize(path);
bool dual_screen = 0; 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 static const u32 edit_bsize = 0x4000; // should be multiple of 0x200 * 2
bool edit_mode = false; bool edit_mode = false;
u8* edit_buffer = TEMP_BUFFER; u8* buffer = (u8*) malloc(edit_bsize);
u8* edit_buffer_cpy = TEMP_BUFFER + edit_bsize; u8* buffer_cpy = (u8*) malloc(edit_bsize);
u32 edit_start = 0; u32 edit_start = 0;
int cursor = 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 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"; 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 if (show_instr) { // show one time instructions
@ -458,6 +477,7 @@ u32 FileHexViewer(const char* path) {
if (MAIN_SCREEN != TOP_SCREEN) ShowString(instr); if (MAIN_SCREEN != TOP_SCREEN) ShowString(instr);
memcpy(bottom_cpy, BOT_SCREEN, (SCREEN_HEIGHT * SCREEN_WIDTH_BOT * 3)); memcpy(bottom_cpy, BOT_SCREEN, (SCREEN_HEIGHT * SCREEN_WIDTH_BOT * 3));
data = buffer;
while (true) { while (true) {
if (mode != last_mode) { if (mode != last_mode) {
if (FONT_WIDTH_EXT <= 5) { 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)) if ((offset < edit_start) || (offset + max_data > edit_start + edit_bsize))
offset = last_offset; // we don't expect this to happen offset = last_offset; // we don't expect this to happen
total_data = (fsize - offset >= max_data) ? max_data : fsize - offset; total_data = (fsize - offset >= max_data) ? max_data : fsize - offset;
data = edit_buffer + (offset - edit_start); data = buffer + (offset - edit_start);
} }
last_offset = offset; last_offset = offset;
} }
@ -674,20 +694,20 @@ u32 FileHexViewer(const char* path) {
cursor = 0; cursor = 0;
edit_start = ((offset - (offset % 0x200) <= (edit_bsize / 2)) || (fsize < edit_bsize)) ? 0 : edit_start = ((offset - (offset % 0x200) <= (edit_bsize / 2)) || (fsize < edit_bsize)) ? 0 :
offset - (offset % 0x200) - (edit_bsize / 2); offset - (offset % 0x200) - (edit_bsize / 2);
FileGetData(path, edit_buffer, edit_bsize, edit_start); FileGetData(path, buffer, edit_bsize, edit_start);
memcpy(edit_buffer_cpy, edit_buffer, edit_bsize); memcpy(buffer_cpy, buffer, edit_bsize);
data = edit_buffer + (offset - edit_start); data = buffer + (offset - edit_start);
} else edit_mode = false; } else edit_mode = false;
} else { // editor mode } else { // editor mode
if (pad_state & (BUTTON_B|BUTTON_START)) { if (pad_state & (BUTTON_B|BUTTON_START)) {
edit_mode = false; edit_mode = false;
// check for user edits // check for user edits
u32 diffs = 0; 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 (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!"); ShowPrompt(false, "Failed writing to file!");
data = TEMP_BUFFER; data = buffer;
last_offset = (u32) -1; // force reload from file last_offset = (u32) -1; // force reload from file
} else if (pad_state & BUTTON_A) { } else if (pad_state & BUTTON_A) {
if (pad_state & BUTTON_DOWN) data[cursor]--; if (pad_state & BUTTON_DOWN) data[cursor]--;
@ -720,9 +740,12 @@ u32 FileHexViewer(const char* path) {
} }
ClearScreen(TOP_SCREEN, COLOR_STD_BG); 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); else ClearScreen(BOT_SCREEN, COLOR_STD_BG);
free(bottom_cpy);
free(buffer);
free(buffer_cpy);
return 0; return 0;
} }
@ -828,7 +851,7 @@ u32 BootFirmHandler(const char* bootpath, bool verbose, bool delete) {
size_t firm_size = FileGetSize(bootpath); size_t firm_size = FileGetSize(bootpath);
if (!firm_size) return 1; 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 if (verbose) ShowPrompt(false, "%s\nFIRM too big, can't boot", pathstr); // unlikely
return 1; return 1;
} }
@ -837,10 +860,12 @@ u32 BootFirmHandler(const char* bootpath, bool verbose, bool delete) {
pathstr, firm_size / 1024)) pathstr, firm_size / 1024))
return 1; 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) || if ((FileGetData(bootpath, firm, firm_size, 0) != firm_size) ||
!IsBootableFirm(firm, firm_size)) { !IsBootableFirm(firm, firm_size)) {
if (verbose) ShowPrompt(false, "%s\nNot a bootable FIRM.", pathstr); if (verbose) ShowPrompt(false, "%s\nNot a bootable FIRM.", pathstr);
free(firm);
return 1; return 1;
} }
@ -849,8 +874,10 @@ u32 BootFirmHandler(const char* bootpath, bool verbose, bool delete) {
FirmA9LHeader* a9l = (FirmA9LHeader*)(void*) ((u8*) firm + arm9s->offset); FirmA9LHeader* a9l = (FirmA9LHeader*)(void*) ((u8*) firm + arm9s->offset);
if (verbose && (ValidateFirmA9LHeader(a9l) == 0) && if (verbose && (ValidateFirmA9LHeader(a9l) == 0) &&
ShowPrompt(true, "%s\nFIRM is encrypted.\n \nDecrypt before boot?", pathstr) && 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; return 1;
}
// unsupported location handling // unsupported location handling
char fixpath[256] = { 0 }; char fixpath[256] = { 0 };
@ -876,6 +903,7 @@ u32 BootFirmHandler(const char* bootpath, bool verbose, bool delete) {
} }
// a return was not intended // a return was not intended
free(firm);
return 1; return 1;
} }
@ -1640,9 +1668,12 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
return 0; return 0;
} }
else if (user_select == font) { // set font else if (user_select == font) { // set font
u32 pbm_size = FileGetData(file_path, TEMP_BUFFER, TEMP_BUFFER_SIZE, 0); u8* pbm = (u8*) malloc(0x10000); // arbitrary, should be enough by far
if (pbm_size) SetFontFromPbm(TEMP_BUFFER, pbm_size); 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); ClearScreenF(true, true, COLOR_STD_BG);
free(pbm);
return 0; return 0;
} }
else if (user_select == view) { // view gfx 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 else if (user_select == agbexport) { // export GBA VC save
if (DumpGbaVcSavegame(file_path) == 0) 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!"); else ShowPrompt(false, "Savegame dump failed!");
return 0; return 0;
} }
else if (user_select == agbimport) { // import GBA VC save else if (user_select == agbimport) { // import GBA VC save
if (clipboard->n_entries != 1) { if (clipboard->n_entries != 1) {
ShowPrompt(false, "GBA VC savegame has to\nbe in the clipboard."); ShowPrompt(false, "GBA VC savegame has to\nbe in the clipboard.");
} else ShowPrompt(false, "Savegame inject %s", } else {
(InjectGbaVcSavegame(file_path, clipboard->entry[0].path) == 0) ? "success" : "failed!"); ShowPrompt(false, "Savegame inject %s.",
(InjectGbaVcSavegame(file_path, clipboard->entry[0].path) == 0) ? "success" : "failed!");
clipboard->n_entries = 0;
}
return 0; return 0;
} }
@ -1789,9 +1823,11 @@ u32 HomeMoreMenu(char* current_path) {
return 0; return 0;
} }
else if (user_select == sysinfo) { // Myria's system info 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); 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; return 0;
} }
else if (user_select == readme) { // Display GodMode9 readme else if (user_select == readme) { // Display GodMode9 readme
@ -1821,7 +1857,7 @@ u32 GodMode(int entrypoint) {
bool bootloader = IS_SIGHAX && (entrypoint == ENTRY_NANDBOOT); bool bootloader = IS_SIGHAX && (entrypoint == ENTRY_NANDBOOT);
bool bootmenu = bootloader && (BOOTMENU_KEY != BUTTON_START) && CheckButton(BOOTMENU_KEY); bool bootmenu = bootloader && (BOOTMENU_KEY != BUTTON_START) && CheckButton(BOOTMENU_KEY);
bool godmode9 = !bootloader; 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 memcpy(firm_in_mem, "NOPE", 4); // to prevent bootloops
if (bootloader) { // check for FIRM in FCRAM, but prevent bootloops if (bootloader) { // check for FIRM in FCRAM, but prevent bootloops
for (u8* addr = (u8*) 0x20000200; addr < (u8*) 0x22000000; addr += 0x400000) { 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(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 if (memcmp(addr, "FIRM", 4) == 0) memcpy(addr, "NOPE", 4); // prevent bootloops
} }
} }*/
// get mode string for splash screen // get mode string for splash screen
const char* disp_mode = NULL; const char* disp_mode = NULL;
@ -1931,7 +1967,7 @@ u32 GodMode(int entrypoint) {
// bootloader handler // bootloader handler
if (bootloader) { if (bootloader) {
const char* bootfirm_paths[] = { BOOTFIRM_PATHS }; 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++) { for (u32 i = 0; i < sizeof(bootfirm_paths) / sizeof(char*); i++) {
BootFirmHandler(bootfirm_paths[i], false, (BOOTFIRM_TEMPS >> i) & 0x1); BootFirmHandler(bootfirm_paths[i], false, (BOOTFIRM_TEMPS >> i) & 0x1);
} }
@ -2016,7 +2052,7 @@ u32 GodMode(int entrypoint) {
} }
} else if (user_select == fixcmac) { } else if (user_select == fixcmac) {
RecursiveFixFileCmac(curr_entry->path); RecursiveFixFileCmac(curr_entry->path);
ClearScreenF(true, false, COLOR_STD_BG); ShowPrompt(false, "Fix CMACs for drive finished.");
} else if (user_select == dirnfo) { } else if (user_select == dirnfo) {
bool is_drive = (!*current_path); bool is_drive = (!*current_path);
FILINFO fno; FILINFO fno;
@ -2129,6 +2165,7 @@ u32 GodMode(int entrypoint) {
GetDirContents(current_dir, current_path); GetDirContents(current_dir, current_path);
} else if (switched && (pad_state & BUTTON_DOWN)) { // force reload file list } else if (switched && (pad_state & BUTTON_DOWN)) { // force reload file list
GetDirContents(current_dir, current_path); GetDirContents(current_dir, current_path);
ClearScreenF(true, true, COLOR_STD_BG);
} else if ((pad_state & BUTTON_RIGHT) && !(pad_state & BUTTON_L1)) { // cursor down (quick) } else if ((pad_state & BUTTON_RIGHT) && !(pad_state & BUTTON_L1)) { // cursor down (quick)
cursor += quick_stp; cursor += quick_stp;
} else if ((pad_state & BUTTON_LEFT) && !(pad_state & BUTTON_L1)) { // cursor up (quick) } else if ((pad_state & BUTTON_LEFT) && !(pad_state & BUTTON_L1)) { // cursor up (quick)

View File

@ -5,6 +5,7 @@
#include <arm.h> #include <arm.h>
#include <brf.h> #include <brf.h>
#include <entrypoints.h> #include <entrypoints.h>
#include <memmap.h>
.global _start .global _start
_start: _start:
@ -72,6 +73,15 @@ _start:
mov r1, #0x340 mov r1, #0x340
str r1, [r0, #0x20] 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 @ Install exception handlers
ldr r0, =XRQ_Start ldr r0, =XRQ_Start
ldr r1, =XRQ_End ldr r1, =XRQ_End

View File

@ -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

View File

@ -53,13 +53,14 @@ u32 TransferCtrNandImage(const char* path_img, const char* drv) {
} }
// CTRNAND preparations // CTRNAND preparations
SecureInfo* secnfo_img = (SecureInfo*) TEMP_BUFFER; SecureInfo secnfo_img;
SecureInfo* secnfo_loc = (SecureInfo*) (TEMP_BUFFER + 0x200); SecureInfo secnfo_loc;
char* path_secnfo_a = (char*) (TEMP_BUFFER + 0x400); char path_secnfo_a[32];
char* path_secnfo_b = (char*) (TEMP_BUFFER + 0x420); char path_secnfo_b[32];
char* path_secnfo_c = (char*) (TEMP_BUFFER + 0x440); char path_secnfo_c[32];
char* path_tickdb = (char*) (TEMP_BUFFER + 0x460); char path_tickdb[32];
char* path_tickdb_bak = (char*) (TEMP_BUFFER + 0x480); char path_tickdb_bak[32];
snprintf(path_secnfo_a, 32, "%s/rw/sys/SecureInfo_A", drv); 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_b, 32, "%s/rw/sys/SecureInfo_B", drv);
snprintf(path_secnfo_c, 32, "%s/rw/sys/SecureInfo_C", 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) // special handling for out of region images (create SecureInfo_C)
PathDelete(path_secnfo_c); // not required when transfering back to original region 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)) || 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("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_a, (u8*) &secnfo_loc, sizeof(SecureInfo), 0) == sizeof(SecureInfo)) ||
(FileGetData(path_secnfo_b, (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_img.region != secnfo_loc.region)) {
secnfo_loc->region = secnfo_img->region; secnfo_loc.region = secnfo_img.region;
FileSetData(path_secnfo_c, (u8*) secnfo_loc, sizeof(SecureInfo), 0, true); FileSetData(path_secnfo_c, (u8*) &secnfo_loc, sizeof(SecureInfo), 0, true);
} }
// make a backup of ticket.db // make a backup of ticket.db
PathDelete(path_tickdb_bak); PathDelete(path_tickdb_bak);

File diff suppressed because it is too large Load Diff

View File

@ -3,13 +3,11 @@
#include "ui.h" #include "ui.h"
#include "unittype.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) { 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; const char* path_out = (inplace) ? path : OUTPUT_PATH "/" KEYDB_NAME;
u32 n_keys;
UINT bt, btw;
// write permissions // write permissions
if (!CheckWritePermissions(path_out)) if (!CheckWritePermissions(path_out))
@ -21,13 +19,22 @@ u32 CryptAesKeyDb(const char* path, bool inplace, bool encrypt) {
return 1; return 1;
} }
// load key database // check key database size
if ((fvx_qread(path, keydb, 0, MAIN_BUFFER_SIZE, &bt) != FR_OK) || u32 fsize = fvx_qsize(path);
(bt % sizeof(AesKeyInfo)) || (bt >= MAIN_BUFFER_SIZE)) if (!fsize || (fsize % sizeof(AesKeyInfo)) || (fsize > MAX_KEYDB_SIZE))
return 1; 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 // en-/decrypt keys
n_keys = bt / sizeof(AesKeyInfo); u32 n_keys = fsize / sizeof(AesKeyInfo);
for (u32 i = 0; i < n_keys; i++) { for (u32 i = 0; i < n_keys; i++) {
if ((bool) keydb[i].isEncrypted == !encrypt) if ((bool) keydb[i].isEncrypted == !encrypt)
CryptAesKeyInfo(&(keydb[i])); CryptAesKeyInfo(&(keydb[i]));
@ -35,9 +42,12 @@ u32 CryptAesKeyDb(const char* path, bool inplace, bool encrypt) {
// dump key database // dump key database
if (!inplace) f_unlink(path_out); 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; return 1;
}
free(keydb);
return 0; return 0;
} }
@ -59,16 +69,19 @@ u32 AddKeyToDb(AesKeyInfo* key_info, AesKeyInfo* key_entry) {
} }
u32 BuildKeyDb(const char* path, bool dump) { 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_out = OUTPUT_PATH "/" KEYDB_NAME;
const char* path_in = path; const char* path_in = path;
UINT br;
// write permissions // write permissions
if (!CheckWritePermissions(path_out)) if (!CheckWritePermissions(path_out))
return 1; return 1;
if (!path_in && !dump) { // no input path given - initialize 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); AddKeyToDb(key_info, NULL);
if ((fvx_stat(path_out, NULL) == FR_OK) && if ((fvx_stat(path_out, NULL) == FR_OK) &&
(ShowPrompt(true, "%s\nOutput file already exists.\nUpdate this?", path_out))) (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; 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; u64 filetype = path_in ? IdentifyFileType(path_in) : 0;
if (filetype & BIN_KEYDB) { // AES key database if (filetype & BIN_KEYDB) { // AES key database
AesKeyInfo* key_info_merge = (AesKeyInfo*) TEMP_BUFFER; u32 fsize = fvx_qsize(path_in);
if ((fvx_qread(path_in, key_info_merge, 0, TEMP_BUFFER_SIZE, &br) != FR_OK) || if ((fsize % sizeof(AesKeyInfo)) || (fsize > MAX_KEYDB_SIZE))
(br % sizeof(AesKeyInfo)) || (br >= MAIN_BUFFER_SIZE)) return 1; return 1;
u32 n_keys = br / sizeof(AesKeyInfo);
for (u32 i = 0; i < n_keys; i++) { u32 n_keys = fsize / sizeof(AesKeyInfo);
if (key_info_merge[i].isEncrypted) // build an unencrypted db u32 merged_keys = 0;
CryptAesKeyInfo(&(key_info_merge[i]));
if (AddKeyToDb(key_info, key_info_merge + i) != 0) return 1; 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 } else if (filetype & BIN_LEGKEY) { // legacy key file
AesKeyInfo key; AesKeyInfo key;
unsigned int keyslot = 0xFF; unsigned int keyslot = 0xFF;
char typestr[32] = { 0 }; char typestr[32] = { 0 };
char* name_in = strrchr(path_in, '/'); char* name_in = strrchr(path_in, '/');
memset(&key, 0, sizeof(AesKeyInfo)); memset(&key, 0, sizeof(AesKeyInfo));
key.type = 'N'; key.type = 'N';
if (!name_in || (strnlen(++name_in, 32) > 24)) return 1; // safety if (!name_in || (strnlen(++name_in, 32) > 24)) return 1; // safety
if ((sscanf(name_in, "slot0x%02XKey%s", &keyslot, typestr) != 2) && if ((sscanf(name_in, "slot0x%02XKey%s", &keyslot, typestr) != 2) &&
(sscanf(name_in, "slot0x%02Xkey%s", &keyslot, typestr) != 2)) return 1; (sscanf(name_in, "slot0x%02Xkey%s", &keyslot, typestr) != 2)) return 1;
char* ext = strchr(typestr, '.'); char* ext = strchr(typestr, '.');
if (!ext) return 1; if (!ext) return 1;
*(ext++) = '\0'; *(ext++) = '\0';
if ((*typestr == 'X') || (*typestr == 'Y')) { if ((*typestr == 'X') || (*typestr == 'Y')) {
key.type = *typestr; key.type = *typestr;
strncpy(key.id, typestr + 1, 10); strncpy(key.id, typestr + 1, 10);
@ -110,21 +139,28 @@ u32 BuildKeyDb(const char* path, bool dump) {
key.slot = keyslot; key.slot = keyslot;
key.keyUnitType = (strncasecmp(ext, "ret.bin", 10) == 0) ? KEYS_RETAIL : key.keyUnitType = (strncasecmp(ext, "ret.bin", 10) == 0) ? KEYS_RETAIL :
(strncasecmp(ext, "dev.bin", 10) == 0) ? KEYS_DEVKIT : 0; (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 (AddKeyToDb(key_info, &key) != 0) return 1;
} }
if (dump) { if (dump) {
u32 dump_size = 0; u32 dump_size = 0;
for (AesKeyInfo* key = key_info; key->slot <= 0x40; key++) { for (AesKeyInfo* key = key_info; key->slot <= 0x40; key++) {
dump_size += sizeof(AesKeyInfo); u32 dump_size_next = dump_size + sizeof(AesKeyInfo);
if (dump_size >= MAX_KEYDB_SIZE) return 1; 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; if (dump_size) {
f_unlink(path_out); if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) // ensure the output dir exists
if (!dump_size || (fvx_qwrite(path_out, key_info, 0, dump_size, &br) != FR_OK) || (br != dump_size)) return 1;
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; return 0;

View File

@ -160,11 +160,9 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
else if (!cmac_type) return 1; else if (!cmac_type) return 1;
const u32 cmac_keyslot[] = { CMAC_KEYSLOT }; const u32 cmac_keyslot[] = { CMAC_KEYSLOT };
u8* data = (u8*) TEMP_BUFFER; u8 hashdata[0x200];
u32 keyslot = cmac_keyslot[cmac_type]; u32 keyslot = cmac_keyslot[cmac_type];
u8* hashdata = NULL;
u32 hashsize = 0; u32 hashsize = 0;
UINT br;
// setup slot 0x30 via movable.sed // setup slot 0x30 via movable.sed
if ((keyslot == 0x30) && (SetupSlot0x30(drv) != 0)) if ((keyslot == 0x30) && (SetupSlot0x30(drv) != 0))
@ -172,23 +170,29 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
// build hash data block, get size // build hash data block, get size
if ((cmac_type == CMAC_AGBSAVE) || (cmac_type == CMAC_AGBSAVE_SD)) { // agbsaves if ((cmac_type == CMAC_AGBSAVE) || (cmac_type == CMAC_AGBSAVE_SD)) { // agbsaves
AgbSaveHeader* agbsave = (AgbSaveHeader*) (void*) data; AgbSaveHeader* agbsave = (AgbSaveHeader*) malloc(AGBSAVE_MAX_SIZE);
if ((TEMP_BUFFER_SIZE < AGBSAVE_MAX_SIZE) || (fvx_qread(path, agbsave, 0, AGBSAVE_MAX_SIZE, &br) != FR_OK) || UINT br;
(br < 0x200) || (ValidateAgbSaveHeader(agbsave) != 0) || (0x200 + agbsave->save_size > 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 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 } else if (cmac_type == CMAC_MOVABLE) { // movable.sed
// see: https://3dbrew.org/wiki/Nand/private/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; return 1;
hashsize = 0x130; hashsize = 0x130;
hashdata = data;
} else { // "savegame" CMACs } else { // "savegame" CMACs
// see: https://3dbrew.org/wiki/Savegames // see: https://3dbrew.org/wiki/Savegames
const char* cmac_savetype[] = { CMAC_SAVETYPE }; const char* cmac_savetype[] = { CMAC_SAVETYPE };
u8* disa = data; u8 disa[0x100];
hashdata = data + 0x400; if (fvx_qread(path, disa, 0x100, 0x100, NULL) != FR_OK)
if ((fvx_qread(path, disa, 0x100, 0x100, &br) != FR_OK) || (br != 0x100))
return 1; return 1;
memcpy(hashdata, cmac_savetype[cmac_type], 8); memcpy(hashdata, cmac_savetype[cmac_type], 8);
if ((cmac_type == CMAC_EXTDATA_SD) || (cmac_type == CMAC_EXTDATA_SYS)) { 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); memcpy(hashdata + 0x10, disa, 0x100);
hashsize = 0x110; hashsize = 0x110;
} else if (cmac_type == CMAC_SAVEDATA_SD) { } 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 + 0x00, cmac_savetype[CMAC_SAVEGAME], 8);
memcpy(hashdata0 + 0x08, disa, 0x100); memcpy(hashdata0 + 0x08, disa, 0x100);
memcpy(hashdata + 0x08, &tid_low, 4); memcpy(hashdata + 0x08, &tid_low, 4);
@ -221,7 +225,7 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
// calculate CMAC // calculate CMAC
u8 shasum[32]; u8 shasum[32];
if (!hashdata || !hashsize) return 1; if (!hashsize) return 1;
sha_quick(shasum, hashdata, hashsize, SHA256_MODE); sha_quick(shasum, hashdata, hashsize, SHA256_MODE);
use_aeskey(keyslot); use_aeskey(keyslot);
aes_cmac(shasum, cmac, 2); aes_cmac(shasum, cmac, 2);

View File

@ -96,31 +96,40 @@ u32 BuildEssentialBackup(const char* path, EssentialBackup* essential) {
} }
u32 CheckEmbeddedBackup(const char* path) { u32 CheckEmbeddedBackup(const char* path) {
EssentialBackup* essential = (EssentialBackup*) TEMP_BUFFER; EssentialBackup* essential = (EssentialBackup*) malloc(sizeof(EssentialBackup));
EssentialBackup* embedded = (EssentialBackup*) (TEMP_BUFFER + sizeof(EssentialBackup)); EssentialBackup* embedded = (EssentialBackup*) malloc(sizeof(EssentialBackup));
UINT btr;
if ((BuildEssentialBackup(path, essential) != 0) || if (!essential || !embedded || (BuildEssentialBackup(path, essential) != 0) ||
(fvx_qread(path, embedded, SECTOR_D0K3 * 0x200, sizeof(EssentialBackup), &btr) != FR_OK) || (fvx_qread(path, embedded, SECTOR_D0K3 * 0x200, sizeof(EssentialBackup), NULL) != FR_OK) ||
(memcmp(embedded, essential, sizeof(EssentialBackup)) != 0)) (memcmp(embedded, essential, sizeof(EssentialBackup)) != 0)) {
free(essential);
free(embedded);
return 1; return 1;
}
free(essential);
free(embedded);
return 0; return 0;
} }
u32 EmbedEssentialBackup(const char* path) { u32 EmbedEssentialBackup(const char* path) {
EssentialBackup* essential = (EssentialBackup*) TEMP_BUFFER; EssentialBackup* essential = (EssentialBackup*) malloc(sizeof(EssentialBackup));
UINT btw; if (!essential) return 1;
// leaving out the write permissions check here, it's okay // leaving out the write permissions check here, it's okay
if ((BuildEssentialBackup(path, essential) != 0) || if ((BuildEssentialBackup(path, essential) != 0) ||
(ValidateNandNcsdHeader((NandNcsdHeader*) essential->nand_hdr) != 0) || (ValidateNandNcsdHeader((NandNcsdHeader*) essential->nand_hdr) != 0) ||
(fvx_qwrite(path, essential, SECTOR_D0K3 * 0x200, sizeof(EssentialBackup), &btw) != FR_OK) || (fvx_qwrite(path, essential, SECTOR_D0K3 * 0x200, sizeof(EssentialBackup), NULL) != FR_OK)) {
(btw != sizeof(EssentialBackup))) free(essential);
return 1; return 1;
}
free(essential);
return 0; return 0;
} }
u32 DumpGbaVcSavegame(const char* path) { u32 DumpGbaVcSavegameBuffered(const char* path, void* buffer) {
if (TEMP_BUFFER_SIZE < AGBSAVE_MAX_SIZE) return 1; AgbSaveHeader* agbsave = (AgbSaveHeader*) buffer;
AgbSaveHeader* agbsave = (AgbSaveHeader*) (void*) TEMP_BUFFER;
u8* savegame = (u8*) (agbsave + 1); u8* savegame = (u8*) (agbsave + 1);
// read full AGBsave to memory // read full AGBsave to memory
@ -145,9 +154,20 @@ u32 DumpGbaVcSavegame(const char* path) {
return 0; return 0;
} }
u32 InjectGbaVcSavegame(const char* path, const char* path_vcsave) { u32 DumpGbaVcSavegame(const char* path) {
if (TEMP_BUFFER_SIZE < AGBSAVE_MAX_SIZE) return 1; u8* buffer = (u8*) malloc(AGBSAVE_MAX_SIZE);
AgbSaveHeader* agbsave = (AgbSaveHeader*) (void*) TEMP_BUFFER; 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); u8* savegame = (u8*) (agbsave + 1);
// basic sanity checks for path_vcsave // 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", snprintf(path_sd, 64, "A:/title/%08lx/%08lx/data/00000001.sav",
getle32((u8*) &(agbsave->title_id) + 4), getle32((u8*) &(agbsave->title_id))); getle32((u8*) &(agbsave->title_id) + 4), getle32((u8*) &(agbsave->title_id)));
if (FixAgbSaveCmac(agbsave, NULL, path_sd) != 0) return 1; if (FixAgbSaveCmac(agbsave, NULL, path_sd) != 0) return 1;
ShowPrompt(false, path_sd);
// check SD save size, then write both partitions // 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))) if ((fvx_stat(path_sd, &fno) != FR_OK) || (fno.fsize != max(AGBSAVE_MAX_SIZE, 2 * data_size)))
@ -199,6 +218,18 @@ u32 InjectGbaVcSavegame(const char* path, const char* path_vcsave) {
return 0; 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) { u32 RebuildNandNcsdHeader(NandNcsdHeader* ncsd) {
// signature (retail or dev) // signature (retail or dev)
u8* signature = (IS_DEVKIT) ? sig_nand_ncsd_dev : sig_nand_ncsd_retail; 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) // 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 // check all 8 firms, also check if ARM9 & ARM11 entrypoints are available
for (u32 f = 0; f <= 8; f++) { for (u32 f = 0; f <= 8; f++) {
FirmHeader firm;
if (GetNandNcsdPartitionInfo(&info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, f, &ncsd) != 0) { if (GetNandNcsdPartitionInfo(&info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, f, &ncsd) != 0) {
ShowPrompt(false, "%s\nNo valid FIRM found", pathstr); ShowPrompt(false, "%s\nNo valid FIRM found", pathstr);
fvx_close(&file); fvx_close(&file);
free(firm);
return 1; 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 u32 firm_size = info.count * 0x200;
(!firm.entry_arm9) || (!firm.entry_arm11)) // arm9 / arm11 entry points must be there if ((firm_size <= FIRM_MAX_SIZE) &&
continue; (ReadNandFile(&file, firm, info.sector, info.count, info.keyslot) == 0) &&
// hash verify all available sections (ValidateFirm(firm, firm_size, true) == 0))
u32 s; break;
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
} }
free(firm);
fvx_close(&file); fvx_close(&file);
return 0; 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 // main processing loop
u32 ret = 0; u32 ret = 0;
u32 sector0 = SECTOR_SECRET + COUNT_SECRET; // start at the sector after secret sector 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 subtype = NP_SUBTYPE_CTR;
u32 sector1 = (GetNandNcsdPartitionInfo(&np_info, type, subtype, p, &ncsd_loc) == 0) ? np_info.sector : fsize / 0x200; u32 sector1 = (GetNandNcsdPartitionInfo(&np_info, type, subtype, p, &ncsd_loc) == 0) ? np_info.sector : fsize / 0x200;
if (sector1 < sector0) ret = 1; // safety check if (sector1 < sector0) ret = 1; // safety check
for (u32 s = sector0; (s < sector1) && (ret == 0); s += MAIN_BUFFER_SIZE / 0x200) { for (u32 s = sector0; (s < sector1) && (ret == 0); s += STD_BUFFER_SIZE / 0x200) {
u32 count = min(MAIN_BUFFER_SIZE / 0x200, (sector1 - s)); u32 count = min(STD_BUFFER_SIZE / 0x200, (sector1 - s));
if (ReadNandFile(&file, MAIN_BUFFER, s, count, 0xFF)) ret = 1; if (ReadNandFile(&file, buffer, s, count, 0xFF)) ret = 1;
if (WriteNandSectors(MAIN_BUFFER, s, count, 0xFF, NAND_SYSNAND)) ret = 1; if (WriteNandSectors(buffer, s, count, 0xFF, NAND_SYSNAND)) ret = 1;
if (!ShowProgress(s + count, fsize / 0x200, path)) ret = 1; if (!ShowProgress(s + count, fsize / 0x200, path)) ret = 1;
} }
if (sector1 == fsize / 0x200) break; // at file end if (sector1 == fsize / 0x200) break; // at file end
sector0 = np_info.sector + np_info.count; // skip partition sector0 = np_info.sector + np_info.count; // skip partition
} }
free(buffer);
fvx_close(&file); fvx_close(&file);
// NCSD header inject, should only be required with 2.1 local NANDs on N3DS // 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; 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 char pathstr[32 + 1]; // truncated path string
TruncateString(pathstr, path, 32, 8); TruncateString(pathstr, path, 32, 8);
// load / check FIRM // load / check FIRM
u8* firm = (u8*) TEMP_BUFFER; u8* firm = buffer;
UINT firm_size; 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)) { !firm_size || !IsInstallableFirm(firm, firm_size)) {
ShowPrompt(false, IsBootableFirm(firm, firm_size) ? ShowPrompt(false, IsBootableFirm(firm, firm_size) ?
"%s\nNot a installable FIRM." : "%s\nFIRM load/verify error.", pathstr); "%s\nNot a installable FIRM." : "%s\nFIRM load/verify error.", pathstr);
@ -560,8 +587,19 @@ u32 SafeInstallFirm(const char* path, u32 slots) {
return 0; 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 }; const u8 perfect_sha[] = { KEYDB_PERFECT_HASH };
u8 keydb[KEYDB_PERFECT_SIZE]; u8 keydb[KEYDB_PERFECT_SIZE];

View File

@ -925,16 +925,19 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
ShowPrompt(false, argv[0]); ShowPrompt(false, argv[0]);
} }
else if (id == CMD_ID_QR) { 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 qrcode[qrcodegen_BUFFER_LEN_MAX];
u8 temp[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); qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true);
if (ret) { if (ret) {
memcpy(TEMP_BUFFER, ALT_SCREEN, (SCREEN_HEIGHT * SCREEN_WIDTH_ALT * 3)); memcpy(screen_copy, ALT_SCREEN, screen_size);
DrawQrCode(ALT_SCREEN, qrcode); DrawQrCode(ALT_SCREEN, qrcode);
ShowPrompt(false, argv[0]); 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) { else if (id == CMD_ID_ASK) {
ret = ShowPrompt(true, argv[0]); 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) { else if (id == CMD_ID_BOOT) {
size_t firm_size = FileGetData(argv[0], TEMP_BUFFER, TEMP_BUFFER_SIZE, 0); u8* firm = (u8*) malloc(FIRM_MAX_SIZE);
ret = firm_size && IsBootableFirm(TEMP_BUFFER, firm_size); if (!firm) {
if (ret) { ret = false;
char fixpath[256] = { 0 }; if (err_str) snprintf(err_str, _ERR_STR_LEN, "out of memory");
if ((*argv[0] == '0') || (*argv[0] == '1')) } else {
snprintf(fixpath, 256, "%s%s", (*argv[0] == '0') ? "sdmc" : "nand", argv[0] + 1); size_t firm_size = FileGetData(argv[0], firm, FIRM_MAX_SIZE, 0);
else strncpy(fixpath, argv[0], 256); ret = firm_size && IsBootableFirm(firm, firm_size);
BootFirm((FirmHeader*)(void*)TEMP_BUFFER, fixpath); if (ret) {
while(1); char fixpath[256] = { 0 };
} else if (err_str) snprintf(err_str, _ERR_STR_LEN, "not a bootable firm"); 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) { else if (id == CMD_ID_SWITCHSD) {
DeinitExtFS(); DeinitExtFS();
@ -1255,7 +1265,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
PowerOff(); PowerOff();
} }
else if (id == CMD_ID_BKPT) { else if (id == CMD_ID_BKPT) {
asm("bkpt\n\t"); bkpt;
while(1); while(1);
} }
else { // command not recognized / bad number of arguments 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) { bool FileTextViewer(const char* path, bool as_script) {
// load text file (completely into memory) // load text file (completely into memory)
char* text = (char*) TEMP_BUFFER; // text file needs to fit inside the STD_BUFFER_SIZE
u32 flen = FileGetData(path, text, TEMP_BUFFER_SIZE, 0); 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 u32 len = 0; // actual length may be shorter due to zero symbol
for (len = 0; (len < flen) && text[len]; len++); for (len = 0; (len < flen) && text[len]; len++);
// let MemTextViewer take over // 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) { bool ExecuteGM9Script(const char* path_script) {
@ -1624,16 +1640,20 @@ bool ExecuteGM9Script(const char* path_script) {
ClearScreen(TOP_SCREEN, COLOR_STD_BG); ClearScreen(TOP_SCREEN, COLOR_STD_BG);
if (preview_mode > 2) { if (preview_mode > 2) {
char* preview_str = get_var("PREVIEW_MODE", NULL); char* preview_str = get_var("PREVIEW_MODE", NULL);
u8* pcx = TEMP_BUFFER + TEMP_BUFFER_SIZE / 2; u32 pcx_size = fvx_qsize(preview_str);
u32 pcx_size = FileGetData(preview_str, pcx, TEMP_BUFFER_SIZE / 2, 0); u8* pcx = (u8*) malloc(SCREEN_SIZE_TOP);
if ((pcx_size > 0) && (pcx_size < TEMP_BUFFER_SIZE / 2) && u8* bitmap = (u8*) malloc(SCREEN_SIZE_TOP);
(PCX_Decompress(TEMP_BUFFER, TEMP_BUFFER_SIZE / 2, pcx, pcx_size))) { 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; 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 { } else {
if (strncmp(preview_str, "off", _VAR_CNT_LEN) == 0) preview_str = "(preview disabled)"; if (strncmp(preview_str, "off", _VAR_CNT_LEN) == 0) preview_str = "(preview disabled)";
DrawStringCenter(TOP_SCREEN, COLOR_STD_FONT, COLOR_STD_BG, preview_str); DrawStringCenter(TOP_SCREEN, COLOR_STD_FONT, COLOR_STD_BG, preview_str);
} }
if (pcx) free(pcx);
if (bitmap) free(bitmap);
preview_mode = 0; preview_mode = 0;
} }
preview_mode_local = preview_mode; preview_mode_local = preview_mode;

View File

@ -602,31 +602,32 @@ bool BuildVGameFirmDir(void) {
templates[n].flags = VFLAG_NO_CRYPTO; templates[n].flags = VFLAG_NO_CRYPTO;
n++; n++;
if (section->method == FIRM_NDMA_CPY) { // ARM9 section, search for Process9 if (section->method == FIRM_NDMA_CPY) { // ARM9 section, search for Process9
u8* buffer = (u8*) (TEMP_BUFFER + (TEMP_BUFFER_SIZE/2)); NcchHeader p9_ncch;
u32 buffer_size = TEMP_BUFFER_SIZE/2;
NcchHeader* p9_ncch;
char name[8]; char name[8];
u32 offset_p9 = 0; u32 offset_p9 = 0;
for (u32 p = 0; (p < section->size) && (!offset_p9); p += buffer_size) {
u32 btr = min(buffer_size, (section->size - p)); u8* buffer = (u8*) malloc(section->size);
if (ReadGameImageBytes(buffer, section->offset + p, btr) != 0) break; if (buffer) {
for (u32 s = 0; (s < btr) && (!offset_p9); s += 0x10) { if (ReadGameImageBytes(buffer, section->offset, section->size) != 0) break;
p9_ncch = (NcchHeader*) (void*) (buffer + s); for (u32 s = 0; (s < section->size - 0x400) && (!offset_p9); s += 0x10) {
if ((ValidateNcchHeader(p9_ncch) == 0) && if ((ValidateNcchHeader((NcchHeader*) (void*) (buffer + s)) == 0) &&
(ReadGameImageBytes((u8*) name, section->offset + p + s + 0x200, 8) == 0)) (ReadGameImageBytes((u8*) name, section->offset + s + 0x200, 8) == 0)) {
offset_p9 = section->offset + p + s; offset_p9 = section->offset + s;
memcpy(&p9_ncch, buffer + s, sizeof(NcchHeader));
}
} }
free(buffer);
} }
if (offset_p9) { 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].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].keyslot = (offset_a9bin == (u64) -1) ? 0xFF : 0x15;
templates[n].flags = 0; templates[n].flags = 0;
n++; n++;
memcpy(templates + n, templates + n - 1, sizeof(VirtualFile)); 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); templates[n].flags |= (VFLAG_NCCH | VFLAG_DIR);
n++; n++;
} }

View File

@ -192,18 +192,22 @@ int WriteVirtualFile(const VirtualFile* vfile, const void* buffer, u64 offset, u
} }
int DeleteVirtualFile(const VirtualFile* vfile) { int DeleteVirtualFile(const VirtualFile* vfile) {
u8* zeroes = (u8*) TEMP_BUFFER;
u32 zeroes_size = TEMP_BUFFER_SIZE;
if (!(vfile->flags & VFLAG_DELETABLE)) return -1; 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) { for (u64 pos = 0; pos < vfile->size; pos += zeroes_size) {
u64 wipe_bytes = min(zeroes_size, vfile->size - pos); u64 wipe_bytes = min(zeroes_size, vfile->size - pos);
if (WriteVirtualFile(vfile, zeroes, pos, wipe_bytes, NULL) != 0) result = WriteVirtualFile(vfile, zeroes, pos, wipe_bytes, NULL);
return -1; if (result != 0) break;
} }
return 0; free(zeroes);
return result;
} }
u64 GetVirtualDriveSize(const char* path) { u64 GetVirtualDriveSize(const char* path) {

View File

@ -69,24 +69,31 @@ u32 InitVTickDbDrive(void) { // prerequisite: ticket.db mounted as image
// reset internal db // reset internal db
memset(tick_info, 0, 16); memset(tick_info, 0, 16);
// set up buffer
u8* buffer = (u8*) malloc(STD_BUFFER_SIZE);
if (!buffer) return 0;
// parse file, sector by sector // parse file, sector by sector
for (u32 p = 0; p < sizeof(area_offsets) / sizeof(u32); p++) { for (u32 p = 0; p < sizeof(area_offsets) / sizeof(u32); p++) {
u32 offset_area = area_offsets[p]; u32 offset_area = area_offsets[p];
for (u32 i = 0; i < TICKDB_AREA_SIZE; i += (TEMP_BUFFER_SIZE - 0x200)) { for (u32 i = 0; i < TICKDB_AREA_SIZE; i += (STD_BUFFER_SIZE - 0x200)) {
u32 read_bytes = min(TEMP_BUFFER_SIZE, TICKDB_AREA_SIZE - i); u32 read_bytes = min(STD_BUFFER_SIZE, TICKDB_AREA_SIZE - i);
u8* data = (u8*) TEMP_BUFFER; u8* data = buffer;
if (ReadImageBytes(data, offset_area + i, read_bytes) != 0) { if (ReadImageBytes(data, offset_area + i, read_bytes) != 0) {
tick_info->n_entries = 0; tick_info->n_entries = 0;
free(buffer);
return 0; 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); Ticket* ticket = TicketFromTickDbChunk(data, NULL, true);
if (!ticket) continue; 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; return (tick_info->n_entries) ? SYS_TICKDB : 0;
} }