mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 05:32:47 +00:00
Replace TEMP_BUFFER & MAIN_BUFFER with heap
This commit is contained in:
parent
e9dc378b7a
commit
a0d2d27b06
@ -30,6 +30,9 @@
|
|||||||
(((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v))
|
(((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v))
|
||||||
#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__)
|
||||||
@ -37,46 +40,28 @@
|
|||||||
// 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
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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)) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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:
|
||||||
@ -71,6 +72,15 @@ _start:
|
|||||||
mov r0, #0x10000000
|
mov r0, #0x10000000
|
||||||
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
|
||||||
|
12
arm9/source/system/memmap.h
Normal file
12
arm9/source/system/memmap.h
Normal 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
|
@ -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
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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)))
|
||||||
@ -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, 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)
|
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)
|
// set CFG_BOOTENV to 0x7 so the save is taken over (not needed anymore)
|
||||||
// https://www.3dbrew.org/wiki/CONFIG9_Registers#CFG9_BOOTENV
|
// https://www.3dbrew.org/wiki/CONFIG9_Registers#CFG9_BOOTENV
|
||||||
// if (strncasecmp(path, "S:/agbsave.bin", 256) == 0) *(u32*) 0x10010000 = 0x7;
|
// 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;
|
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];
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user