Remove trailing white space

- Removed trailing whitespace from all source code files (.c, .h. and
   .s) and the README.md
This commit is contained in:
Gabriel Marcano 2020-08-24 21:27:19 -07:00 committed by d0k3
parent d682a65df6
commit d010f2858b
81 changed files with 1888 additions and 1888 deletions

View File

@ -113,7 +113,7 @@ With the possibilites GodMode9 provides, not everything may be obvious at first
* __Hexview and hexedit any file__: Press the A button on a file and select `Show in Hexeditor`. A button again enables edit mode, hold the A button and press arrow buttons to edit bytes. You will get an additional confirmation prompt to take over changes. Take note that for certain files, write permissions can't be enabled. * __Hexview and hexedit any file__: Press the A button on a file and select `Show in Hexeditor`. A button again enables edit mode, hold the A button and press arrow buttons to edit bytes. You will get an additional confirmation prompt to take over changes. Take note that for certain files, write permissions can't be enabled.
* __View text files in a text viewer__: Press the A button on a file and select `Show in Textviewer` (only shows up for actual text files). You can enable wordwrapped mode via R+Y, and navigate around the file via R+X and the dpad. * __View text files in a text viewer__: Press the A button on a file and select `Show in Textviewer` (only shows up for actual text files). You can enable wordwrapped mode via R+Y, and navigate around the file via R+X and the dpad.
* __Chainload FIRM payloads__: Press the A button on a FIRM file, select `FIRM options` -> `Boot FIRM`. Keep in mind you should not run FIRMs from dubious sources and that the write permissions system is no longer in place after booting a payload. * __Chainload FIRM payloads__: Press the A button on a FIRM file, select `FIRM options` -> `Boot FIRM`. Keep in mind you should not run FIRMs from dubious sources and that the write permissions system is no longer in place after booting a payload.
* __Chainload FIRM payloads from a neat menu__: The `payloads` menu is found inside the HOME button menu. It provides any FIRM found in `0:/gm9/payloads` for quick chainloading. * __Chainload FIRM payloads from a neat menu__: The `payloads` menu is found inside the HOME button menu. It provides any FIRM found in `0:/gm9/payloads` for quick chainloading.
* __Inject a file to another file__: Put exactly one file (the file to be injected from) into the clipboard (via the Y button). Press A on the file to be injected to. There will be an option to inject the first file into it. * __Inject a file to another file__: Put exactly one file (the file to be injected from) into the clipboard (via the Y button). Press A on the file to be injected to. There will be an option to inject the first file into it.
### Scripting functionality ### Scripting functionality

View File

@ -31,7 +31,7 @@ enum {
GIC_RISINGEDGE_1N = 3 GIC_RISINGEDGE_1N = 3
// With the 1-N model, an interrupt that is taken on any CPU clears the Pending // With the 1-N model, an interrupt that is taken on any CPU clears the Pending
// status on all CPUs. // status on all CPUs.
// With the N-N model, all CPUs receive the interrupt independently. The Pending // With the N-N model, all CPUs receive the interrupt independently. The Pending
// status is cleared only for the CPU that takes it, not for the other CPUs // status is cleared only for the CPU that takes it, not for the other CPUs
}; };

View File

@ -7,23 +7,23 @@ bool is_valid_dstime(DsTime* dstime) {
(DSTIMEGET(dstime, bcd_m) >= 60) || (DSTIMEGET(dstime, bcd_m) >= 60) ||
(DSTIMEGET(dstime, bcd_s) >= 60)) (DSTIMEGET(dstime, bcd_s) >= 60))
return false; return false;
// check the date... // check the date...
u32 year = 2000 + DSTIMEGET(dstime, bcd_Y); u32 year = 2000 + DSTIMEGET(dstime, bcd_Y);
u32 month = DSTIMEGET(dstime, bcd_M); u32 month = DSTIMEGET(dstime, bcd_M);
u32 day = DSTIMEGET(dstime, bcd_D); u32 day = DSTIMEGET(dstime, bcd_D);
// date: year & month // date: year & month
if ((year >= 2100) || (month == 0) || (month > 12)) if ((year >= 2100) || (month == 0) || (month > 12))
return false; return false;
// date: day // date: day
// see: https://github.com/devkitPro/libnds/blob/9678bf09389cb1fcdc99dfa0357ec0cbe51dd0b7/source/arm7/clock.c#L224-L262 // see: https://github.com/devkitPro/libnds/blob/9678bf09389cb1fcdc99dfa0357ec0cbe51dd0b7/source/arm7/clock.c#L224-L262
u32 months_lastday[1+12] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; u32 months_lastday[1+12] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
u32 leap = (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) ? 1 : 0; u32 leap = (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) ? 1 : 0;
u32 days_in_month = months_lastday[month] + ((month == 2) ? leap : 0); u32 days_in_month = months_lastday[month] + ((month == 2) ? leap : 0);
if (day > days_in_month) return false; if (day > days_in_month) return false;
return true; return true;
} }

View File

@ -73,7 +73,7 @@ static bool BuildKeyboard(TouchBox* swkbd, const char* keys, const u8* layout) {
static void DrawKey(const TouchBox* key, const bool pressed, const u32 uppercase) { static void DrawKey(const TouchBox* key, const bool pressed, const u32 uppercase) {
const char* keystrs[] = { SWKBD_KEYSTR }; const char* keystrs[] = { SWKBD_KEYSTR };
const u32 color = (pressed) ? COLOR_SWKBD_PRESSED : const u32 color = (pressed) ? COLOR_SWKBD_PRESSED :
(key->id == KEY_ENTER) ? COLOR_SWKBD_ENTER : (key->id == KEY_ENTER) ? COLOR_SWKBD_ENTER :
((key->id == KEY_CAPS) && (uppercase > 1)) ? COLOR_SWKBD_CAPS : ((key->id == KEY_CAPS) && (uppercase > 1)) ? COLOR_SWKBD_CAPS :
COLOR_SWKBD_NORMAL; COLOR_SWKBD_NORMAL;
@ -125,12 +125,12 @@ static void DrawTextBox(const TouchBox* txtbox, const char* inputstr, const u32
const u32 input_shown = (txtbox->w / FONT_WIDTH_EXT) - 2; const u32 input_shown = (txtbox->w / FONT_WIDTH_EXT) - 2;
const u32 inputstr_size = strlen(inputstr); // we rely on a zero terminated string const u32 inputstr_size = strlen(inputstr); // we rely on a zero terminated string
const u16 x = txtbox->x; const u16 x = txtbox->x;
const u16 y = txtbox->y; const u16 y = txtbox->y;
// fix scroll // fix scroll
if (cursor < *scroll) *scroll = cursor; if (cursor < *scroll) *scroll = cursor;
else if (cursor - *scroll > input_shown) *scroll = cursor - input_shown; else if (cursor - *scroll > input_shown) *scroll = cursor - input_shown;
// draw input string // draw input string
DrawStringF(BOT_SCREEN, x, y, COLOR_STD_FONT, COLOR_STD_BG, "%c%-*.*s%-*.*s%c", DrawStringF(BOT_SCREEN, x, y, COLOR_STD_FONT, COLOR_STD_BG, "%c%-*.*s%-*.*s%c",
(*scroll) ? '<' : '|', (*scroll) ? '<' : '|',
@ -142,7 +142,7 @@ static void DrawTextBox(const TouchBox* txtbox, const char* inputstr, const u32
"", "",
(inputstr_size - (s32) *scroll > input_shown) ? '>' : '|' (inputstr_size - (s32) *scroll > input_shown) ? '>' : '|'
); );
// draw cursor // draw cursor
DrawStringF(BOT_SCREEN, x-(FONT_WIDTH_EXT/2), y+10, COLOR_STD_FONT, COLOR_STD_BG, "%-*.*s^%-*.*s", DrawStringF(BOT_SCREEN, x-(FONT_WIDTH_EXT/2), y+10, COLOR_STD_FONT, COLOR_STD_BG, "%-*.*s^%-*.*s",
1 + cursor - *scroll, 1 + cursor - *scroll,

View File

@ -7,7 +7,7 @@ u64 timer_start( void ) {
if (!(*TIMER_CNT0 & *TIMER_CNT1 & *TIMER_CNT2 & *TIMER_CNT3 & TIMER_ACTIVE) || if (!(*TIMER_CNT0 & *TIMER_CNT1 & *TIMER_CNT2 & *TIMER_CNT3 & TIMER_ACTIVE) ||
!(*TIMER_CNT1 & *TIMER_CNT2 & *TIMER_CNT3 & TIMER_COUNT_UP)) !(*TIMER_CNT1 & *TIMER_CNT2 & *TIMER_CNT3 & TIMER_COUNT_UP))
timer_init = true; timer_init = true;
if (timer_init) { if (timer_init) {
// deactivate, then reset timers // deactivate, then reset timers
*TIMER_CNT0 = 0; *TIMER_CNT0 = 0;
@ -17,7 +17,7 @@ u64 timer_start( void ) {
// start timers // start timers
*TIMER_CNT0 = TIMER_ACTIVE; *TIMER_CNT0 = TIMER_ACTIVE;
*TIMER_CNT1 = *TIMER_CNT2 = *TIMER_CNT3 = TIMER_ACTIVE | TIMER_COUNT_UP; *TIMER_CNT1 = *TIMER_CNT2 = *TIMER_CNT3 = TIMER_ACTIVE | TIMER_COUNT_UP;
// timer initialized // timer initialized
timer_init = false; timer_init = false;
} }

View File

@ -89,7 +89,7 @@ bool CalibrateTouchFromSupportFile(void) {
size_t size = LoadSupportFile(TOUCH_CALIB_FILENAME, calibrations, sizeof(calibrations)); size_t size = LoadSupportFile(TOUCH_CALIB_FILENAME, calibrations, sizeof(calibrations));
u32 n_dots = size / sizeof(HID_CalibrationData); u32 n_dots = size / sizeof(HID_CalibrationData);
is_calibrated = (n_dots == 0) ? false : is_calibrated = (n_dots == 0) ? false :
HID_SetCalibrationData(calibrations, n_dots, SCREEN_WIDTH_BOT, SCREEN_HEIGHT); HID_SetCalibrationData(calibrations, n_dots, SCREEN_WIDTH_BOT, SCREEN_HEIGHT);
return is_calibrated; return is_calibrated;
} }
@ -99,11 +99,11 @@ bool CalibrateTouchFromFlash(void) {
// set calibration defaults // set calibration defaults
SetCalibrationDefaults(); SetCalibrationDefaults();
// check SPIflash status // check SPIflash status
if (!spiflash_get_status()) if (!spiflash_get_status())
return false; return false;
// check NVRAM console ID // check NVRAM console ID
u32 console_id = 0; u32 console_id = 0;
if (!spiflash_read(0x001C, 4, (u8*)&console_id)) if (!spiflash_read(0x001C, 4, (u8*)&console_id))

View File

@ -19,7 +19,7 @@
#define STRBUF_SIZE 512 // maximum size of the string buffer #define STRBUF_SIZE 512 // maximum size of the string buffer
#define FONT_MAX_WIDTH 8 #define FONT_MAX_WIDTH 8
#define FONT_MAX_HEIGHT 10 #define FONT_MAX_HEIGHT 10
#define PROGRESS_REFRESH_RATE 30 // the progress bar is only allowed to draw to screen every X milliseconds #define PROGRESS_REFRESH_RATE 30 // the progress bar is only allowed to draw to screen every X milliseconds
static u32 font_width = 0; static u32 font_width = 0;
static u32 font_height = 0; static u32 font_height = 0;
@ -247,7 +247,7 @@ void DrawString(u16 *screen, const char *str, int x, int y, u32 color, u32 bgcol
size_t len = (strlen(str) > max_len) ? max_len : strlen(str); size_t len = (strlen(str) > max_len) ? max_len : strlen(str);
for (size_t i = 0; i < len; i++) { for (size_t i = 0; i < len; i++) {
char c = (char) (fix_utf8 && str[i] >= 0x80) ? '?' : str[i]; char c = (char) (fix_utf8 && str[i] >= 0x80) ? '?' : str[i];
DrawCharacter(screen, c, x + i * font_width, y, color, bgcolor); DrawCharacter(screen, c, x + i * font_width, y, color, bgcolor);
} }
} }
@ -445,17 +445,17 @@ void ShowIconString(u16* icon, int w, int h, const char *format, ...)
bool ShowPrompt(bool ask, const char *format, ...) bool ShowPrompt(bool ask, const char *format, ...)
{ {
bool ret = true; bool ret = true;
char str[STRBUF_SIZE]; char str[STRBUF_SIZE];
va_list va; va_list va;
va_start(va, format); va_start(va, format);
vsnprintf(str, STRBUF_SIZE, format, va); vsnprintf(str, STRBUF_SIZE, format, va);
va_end(va); va_end(va);
ClearScreenF(true, false, COLOR_STD_BG); ClearScreenF(true, false, COLOR_STD_BG);
DrawStringCenter(MAIN_SCREEN, COLOR_STD_FONT, COLOR_STD_BG, "%s\n \n%s", str, DrawStringCenter(MAIN_SCREEN, COLOR_STD_FONT, COLOR_STD_BG, "%s\n \n%s", str,
(ask) ? "(<A> yes, <B> no)" : "(<A> to continue)"); (ask) ? "(<A> yes, <B> no)" : "(<A> to continue)");
while (true) { while (true) {
u32 pad_state = InputWait(0); u32 pad_state = InputWait(0);
if (pad_state & BUTTON_A) break; if (pad_state & BUTTON_A) break;
@ -464,9 +464,9 @@ bool ShowPrompt(bool ask, const char *format, ...)
break; break;
} }
} }
ClearScreenF(true, false, COLOR_STD_BG); ClearScreenF(true, false, COLOR_STD_BG);
return ret; return ret;
} }
@ -661,7 +661,7 @@ u32 ShowFileScrollPrompt(u32 n, const DirEntry** options, bool hide_ext, const c
DrawStringF(MAIN_SCREEN, x, yopt + ((line_height+2)*(i-scroll)), DrawStringF(MAIN_SCREEN, x, yopt + ((line_height+2)*(i-scroll)),
(sel == (int)i) ? COLOR_STD_FONT : COLOR_ENTRY(options[i]), COLOR_STD_BG, "%2.2s %s", (sel == (int)i) ? COLOR_STD_FONT : COLOR_ENTRY(options[i]), COLOR_STD_BG, "%2.2s %s",
(sel == (int)i) ? "->" : "", content_str); (sel == (int)i) ? "->" : "", content_str);
DrawStringF(MAIN_SCREEN, x + item_width - font_width * 11, yopt + ((line_height+2)*(i-scroll)), DrawStringF(MAIN_SCREEN, x + item_width - font_width * 11, yopt + ((line_height+2)*(i-scroll)),
(sel == (int)i) ? COLOR_STD_FONT : COLOR_ENTRY(options[i]), COLOR_STD_BG, "%10.10s", (sel == (int)i) ? COLOR_STD_FONT : COLOR_ENTRY(options[i]), COLOR_STD_BG, "%10.10s",
(options[i]->type == T_DIR) ? "(dir)" : (options[i]->type == T_DOTDOT) ? "(..)" : bytestr); (options[i]->type == T_DIR) ? "(dir)" : (options[i]->type == T_DOTDOT) ? "(..)" : bytestr);
@ -768,7 +768,7 @@ bool ShowInputPrompt(char* inputstr, u32 max_size, u32 resize, const char* alpha
if (str_width < (24 * font_width)) str_width = 24 * font_width; if (str_width < (24 * font_width)) str_width = 24 * font_width;
x = (str_width >= SCREEN_WIDTH_MAIN) ? 0 : (SCREEN_WIDTH_MAIN - str_width) / 2; x = (str_width >= SCREEN_WIDTH_MAIN) ? 0 : (SCREEN_WIDTH_MAIN - str_width) / 2;
y = (str_height >= SCREEN_HEIGHT) ? 0 : (SCREEN_HEIGHT - str_height) / 2; y = (str_height >= SCREEN_HEIGHT) ? 0 : (SCREEN_HEIGHT - str_height) / 2;
ClearScreenF(true, false, COLOR_STD_BG); ClearScreenF(true, false, COLOR_STD_BG);
DrawStringF(MAIN_SCREEN, x, y, COLOR_STD_FONT, COLOR_STD_BG, "%s", str); DrawStringF(MAIN_SCREEN, x, y, COLOR_STD_FONT, COLOR_STD_BG, "%s", str);
DrawStringF(MAIN_SCREEN, x + 8, y + str_height - 40, COLOR_STD_FONT, COLOR_STD_BG, DrawStringF(MAIN_SCREEN, x + 8, y + str_height - 40, COLOR_STD_FONT, COLOR_STD_BG,
@ -901,7 +901,7 @@ bool ShowStringPrompt(char* inputstr, u32 max_size, const char *format, ...) {
ret = ShowInputPrompt(inputstr, max_size, 1, alphabet, format, va); ret = ShowInputPrompt(inputstr, max_size, 1, alphabet, format, va);
va_end(va); va_end(va);
return ret; return ret;
} }
u64 ShowHexPrompt(u64 start_val, u32 n_digits, const char *format, ...) { u64 ShowHexPrompt(u64 start_val, u32 n_digits, const char *format, ...) {
@ -919,7 +919,7 @@ u64 ShowHexPrompt(u64 start_val, u32 n_digits, const char *format, ...) {
} else ret = (u64) -1; } else ret = (u64) -1;
va_end(va); va_end(va);
return ret; return ret;
} }
u64 ShowNumberPrompt(u64 start_val, const char *format, ...) { u64 ShowNumberPrompt(u64 start_val, const char *format, ...) {
@ -936,7 +936,7 @@ u64 ShowNumberPrompt(u64 start_val, const char *format, ...) {
} else ret = (u64) -1; } else ret = (u64) -1;
va_end(va); va_end(va);
return ret; return ret;
} }
bool ShowDataPrompt(u8* data, u32* size, const char *format, ...) { bool ShowDataPrompt(u8* data, u32* size, const char *format, ...) {
@ -965,7 +965,7 @@ bool ShowDataPrompt(u8* data, u32* size, const char *format, ...) {
} }
va_end(va); va_end(va);
return ret; return ret;
} }

View File

@ -189,7 +189,7 @@ int utf16_to_utf8(u8 *out, const u16 *in, int len_out, int len_in)
units = decode_utf16(&code, in); units = decode_utf16(&code, in);
if(units == -1) if(units == -1)
return -1; return -1;
if (len_in >= units) if (len_in >= units)
len_in -= units; len_in -= units;
else return -1; else return -1;
@ -238,7 +238,7 @@ int utf8_to_utf16(u16 *out, const u8 *in, int len_out, int len_in)
units = decode_utf8(&code, in); units = decode_utf8(&code, in);
if(units == -1) if(units == -1)
return -1; return -1;
if (len_in >= units) if (len_in >= units)
len_in -= units; len_in -= units;
else return -1; else return -1;

View File

@ -213,11 +213,11 @@ void ctr_decrypt_byte(void *inbuf, void *outbuf, size_t size, size_t off, uint32
uint8_t *in = inbuf; uint8_t *in = inbuf;
uint8_t *out = outbuf; uint8_t *out = outbuf;
uint32_t i; uint32_t i;
for (i=0; i<AES_BLOCK_SIZE; i++) // setup local ctr for (i=0; i<AES_BLOCK_SIZE; i++) // setup local ctr
ctr_local[i] = ctr[i]; ctr_local[i] = ctr[i];
add_ctr(ctr_local, off / AES_BLOCK_SIZE); add_ctr(ctr_local, off / AES_BLOCK_SIZE);
if (off_fix) // handle misaligned offset (at beginning) if (off_fix) // handle misaligned offset (at beginning)
{ {
size_t last_byte = ((off_fix + bytes_left) >= AES_BLOCK_SIZE) ? size_t last_byte = ((off_fix + bytes_left) >= AES_BLOCK_SIZE) ?
@ -229,7 +229,7 @@ void ctr_decrypt_byte(void *inbuf, void *outbuf, size_t size, size_t off, uint32
*(out++) = temp[i]; *(out++) = temp[i];
bytes_left -= last_byte - off_fix; bytes_left -= last_byte - off_fix;
} }
if (bytes_left >= AES_BLOCK_SIZE) if (bytes_left >= AES_BLOCK_SIZE)
{ {
size_t blocks = bytes_left / AES_BLOCK_SIZE; size_t blocks = bytes_left / AES_BLOCK_SIZE;
@ -238,7 +238,7 @@ void ctr_decrypt_byte(void *inbuf, void *outbuf, size_t size, size_t off, uint32
out += AES_BLOCK_SIZE * blocks; out += AES_BLOCK_SIZE * blocks;
bytes_left -= AES_BLOCK_SIZE * blocks; bytes_left -= AES_BLOCK_SIZE * blocks;
} }
if (bytes_left) // handle misaligned offset (at end) if (bytes_left) // handle misaligned offset (at end)
{ {
for (i=0; i<bytes_left; i++) for (i=0; i<bytes_left; i++)
@ -300,7 +300,7 @@ void aes_cmac(void* inbuf, void* outbuf, size_t size)
AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN; AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN;
uint32_t* out = (uint32_t*) outbuf; uint32_t* out = (uint32_t*) outbuf;
uint32_t* in = (uint32_t*) inbuf; uint32_t* in = (uint32_t*) inbuf;
// create xorpad for last block // create xorpad for last block
set_ctr(zeroes); set_ctr(zeroes);
aes_decrypt(xorpad, xorpad, 1, mode); aes_decrypt(xorpad, xorpad, 1, mode);
@ -312,7 +312,7 @@ void aes_cmac(void* inbuf, void* outbuf, size_t size)
} }
xorpadb[15] <<= 1; xorpadb[15] <<= 1;
xorpadb[15] ^= finalxor; xorpadb[15] ^= finalxor;
// process blocks // process blocks
for (uint32_t i = 0; i < 4; i++) for (uint32_t i = 0; i < 4; i++)
out[i] = 0; out[i] = 0;

View File

@ -10,7 +10,7 @@ u16 crc16_quick(const void* src, u32 len) {
static const u16 tabval[] = { CRC16_TABVAL }; static const u16 tabval[] = { CRC16_TABVAL };
u16* data = (u16*) src; u16* data = (u16*) src;
u16 crc = 0xFFFF; u16 crc = 0xFFFF;
for (len >>= 1; len; len--) { for (len >>= 1; len; len--) {
u16 curr = *(data++); u16 curr = *(data++);
for (u32 i = 0; i < 4; i++) { for (u32 i = 0; i < 4; i++) {
@ -21,6 +21,6 @@ u16 crc16_quick(const void* src, u32 len) {
crc ^= tval; crc ^= tval;
} }
} }
return crc; return crc;
} }

View File

@ -73,7 +73,7 @@ u32 crc32_calculate_from_file(const char* fileName, u32 offset, u32 length) {
return crc32; return crc32;
} }
fvx_lseek(&inputFile, offset); fvx_lseek(&inputFile, offset);
bool ret = true; bool ret = true;
for (u64 pos = 0; (pos < length) && ret; pos += bufsiz) { for (u64 pos = 0; (pos < length) && ret; pos += bufsiz) {
UINT read_bytes = min(bufsiz, length - pos); UINT read_bytes = min(bufsiz, length - pos);
@ -83,7 +83,7 @@ u32 crc32_calculate_from_file(const char* fileName, u32 offset, u32 length) {
ret = false; ret = false;
if (ret) crc32 = crc32_calculate(crc32, buffer, read_bytes); if (ret) crc32 = crc32_calculate(crc32, buffer, read_bytes);
} }
fvx_close(&inputFile); fvx_close(&inputFile);
free(buffer); free(buffer);
return ~crc32; return ~crc32;

View File

@ -17,7 +17,7 @@ static u64 keyYState = 0;
u32 GetUnitKeysType(void) u32 GetUnitKeysType(void)
{ {
static u32 keys_type = KEYS_UNKNOWN; static u32 keys_type = KEYS_UNKNOWN;
if (keys_type == KEYS_UNKNOWN) { if (keys_type == KEYS_UNKNOWN) {
static const u8 slot0x2CSampleRetail[16] = { static const u8 slot0x2CSampleRetail[16] = {
0xBC, 0xC4, 0x16, 0x2C, 0x2A, 0x06, 0x91, 0xEE, 0x47, 0x18, 0x86, 0xB8, 0xEB, 0x2F, 0xB5, 0x48 }; 0xBC, 0xC4, 0x16, 0x2C, 0x2A, 0x06, 0x91, 0xEE, 0x47, 0x18, 0x86, 0xB8, 0xEB, 0x2F, 0xB5, 0x48 };
@ -35,7 +35,7 @@ u32 GetUnitKeysType(void)
keys_type = KEYS_DEVKIT; keys_type = KEYS_DEVKIT;
} }
} }
return keys_type; return keys_type;
} }
@ -67,15 +67,15 @@ u32 CheckKeySlot(u32 keyslot, char type)
{ 0xBC, 0x83, 0x7C, 0xC9, 0x99, 0xC8, 0x80, 0x9E, 0x8A, 0xDE, 0x4A, 0xFA, 0xAA, 0x72, 0x08, 0x28 } } { 0xBC, 0x83, 0x7C, 0xC9, 0x99, 0xC8, 0x80, 0x9E, 0x8A, 0xDE, 0x4A, 0xFA, 0xAA, 0x72, 0x08, 0x28 } }
}; };
u64* state = (type == 'X') ? &keyXState : (type == 'Y') ? &keyYState : (type == 'N') ? &keyState : NULL; u64* state = (type == 'X') ? &keyXState : (type == 'Y') ? &keyYState : (type == 'N') ? &keyState : NULL;
// just to be safe... // just to be safe...
if (keyslot >= 0x40) if (keyslot >= 0x40)
return 1; return 1;
// check if key is already loaded // check if key is already loaded
if ((type != 'I') && ((*state >> keyslot) & 1)) if ((type != 'I') && ((*state >> keyslot) & 1))
return 0; return 0;
// if is not, we may still be able to verify the currently set one (for NCCH keys) // if is not, we may still be able to verify the currently set one (for NCCH keys)
for (u32 p = 0; (type == 'X') && (p < sizeof(keyNcchSamples) / sizeof(AesNcchSampleInfo)); p++) { for (u32 p = 0; (type == 'X') && (p < sizeof(keyNcchSamples) / sizeof(AesNcchSampleInfo)); p++) {
if (keyNcchSamples[p].slot != keyslot) // only for keyslots in the keyNcchSamples table! if (keyNcchSamples[p].slot != keyslot) // only for keyslots in the keyNcchSamples table!
@ -93,7 +93,7 @@ u32 CheckKeySlot(u32 keyslot, char type)
return 0; return 0;
} }
} }
// not set up if getting here // not set up if getting here
return 1; return 1;
} }
@ -102,13 +102,13 @@ u32 CheckKeySlot(u32 keyslot, char type)
// to get rid of these, you may replace this function with anything that works for you // to get rid of these, you may replace this function with anything that works for you
u32 LoadKeyDb(const char* path_db, AesKeyInfo* keydb, u32 bsize) { u32 LoadKeyDb(const char* path_db, AesKeyInfo* keydb, u32 bsize) {
UINT fsize = 0; UINT fsize = 0;
if (path_db) { if (path_db) {
if (fvx_qread (path_db, keydb, 0, bsize, &fsize) != FR_OK) fsize = 0; if (fvx_qread (path_db, keydb, 0, bsize, &fsize) != FR_OK) fsize = 0;
} else if (fvx_qread ("S:/" KEYDB_NAME, keydb, 0, bsize, &fsize) != FR_OK) { } else if (fvx_qread ("S:/" KEYDB_NAME, keydb, 0, bsize, &fsize) != FR_OK) {
fsize = LoadSupportFile(KEYDB_NAME, keydb, bsize); // load key database support file fsize = LoadSupportFile(KEYDB_NAME, keydb, bsize); // load key database support file
} }
u32 nkeys = 0; u32 nkeys = 0;
if (fsize && !(fsize % sizeof(AesKeyInfo))) if (fsize && !(fsize % sizeof(AesKeyInfo)))
nkeys = fsize / sizeof(AesKeyInfo); nkeys = fsize / sizeof(AesKeyInfo);
@ -119,24 +119,24 @@ u32 LoadKeyFromFile(void* key, u32 keyslot, char type, char* id)
{ {
u8 keystore[16] __attribute__((aligned(32))) = {0}; u8 keystore[16] __attribute__((aligned(32))) = {0};
bool found = false; bool found = false;
// checking the obvious // checking the obvious
if ((keyslot >= 0x40) || ((type != 'X') && (type != 'Y') && (type != 'N') && (type != 'I'))) if ((keyslot >= 0x40) || ((type != 'X') && (type != 'Y') && (type != 'N') && (type != 'I')))
return 1; return 1;
// check if already loaded // check if already loaded
if (!key && !id && (CheckKeySlot(keyslot, type) == 0)) return 0; if (!key && !id && (CheckKeySlot(keyslot, type) == 0)) return 0;
// use keystore if key == NULL // use keystore if key == NULL
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*) malloc(STD_BUFFER_SIZE); AesKeyInfo* keydb = (AesKeyInfo*) malloc(STD_BUFFER_SIZE);
u32 nkeys = (keydb) ? LoadKeyDb(NULL, keydb, STD_BUFFER_SIZE) : 0; 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) &&
((!id && !(info->id[0])) || (id && (strncmp(id, info->id, 10) == 0))) && ((!id && !(info->id[0])) || (id && (strncmp(id, info->id, 10) == 0))) &&
(!info->keyUnitType || (info->keyUnitType == GetUnitKeysType())))) (!info->keyUnitType || (info->keyUnitType == GetUnitKeysType()))))
continue; continue;
@ -146,9 +146,9 @@ u32 LoadKeyFromFile(void* key, u32 keyslot, char type, char* id)
memcpy(key, info->key, 16); memcpy(key, info->key, 16);
break; break;
} }
free(keydb); 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];
@ -156,13 +156,13 @@ u32 LoadKeyFromFile(void* key, u32 keyslot, char type, char* id)
(type == 'X') ? "X" : (type == 'Y') ? "Y" : (type == 'I') ? "IV" : "", (id) ? id : ""); (type == 'X') ? "X" : (type == 'Y') ? "Y" : (type == 'I') ? "IV" : "", (id) ? id : "");
found = (LoadSupportFile(fname, key, 16) == 16); found = (LoadSupportFile(fname, key, 16) == 16);
} }
// key still not found (duh) // key still not found (duh)
if (!found) return 1; // out of options here if (!found) return 1; // out of options here
// done if this is an IV // done if this is an IV
if (type == 'I') return 0; if (type == 'I') return 0;
// now, setup the key // now, setup the key
if (type == 'X') { if (type == 'X') {
setup_aeskeyX(keyslot, key); setup_aeskeyX(keyslot, key);
@ -177,7 +177,7 @@ u32 LoadKeyFromFile(void* key, u32 keyslot, char type, char* id)
keyYState |= 1ull << keyslot; keyYState |= 1ull << keyslot;
} }
use_aeskey(keyslot); use_aeskey(keyslot);
return 0; return 0;
} }
@ -186,11 +186,11 @@ u32 InitKeyDb(const char* path)
// use this to quickly initialize all applicable keys in aeskeydb.bin // use this to quickly initialize all applicable keys in aeskeydb.bin
static const u64 keyslot_whitelist = (1ull<<0x02)|(1ull<<0x03)|(1ull<<0x05)|(1ull<<0x18)|(1ull<<0x19)|(1ull<<0x1A)|(1ull<<0x1B)| static const u64 keyslot_whitelist = (1ull<<0x02)|(1ull<<0x03)|(1ull<<0x05)|(1ull<<0x18)|(1ull<<0x19)|(1ull<<0x1A)|(1ull<<0x1B)|
(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*) malloc(STD_BUFFER_SIZE); AesKeyInfo* keydb = (AesKeyInfo*) malloc(STD_BUFFER_SIZE);
u32 nkeys = (keydb) ? LoadKeyDb(path, keydb, STD_BUFFER_SIZE) : 0; u32 nkeys = (keydb) ? LoadKeyDb(path, keydb, STD_BUFFER_SIZE) : 0;
// 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]);
@ -202,7 +202,7 @@ u32 InitKeyDb(const char* path)
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
if (info->isEncrypted) CryptAesKeyInfo(info); // decrypt key if (info->isEncrypted) CryptAesKeyInfo(info); // decrypt key
// apply key // apply key
u8 key[16] __attribute__((aligned(32))) = {0}; u8 key[16] __attribute__((aligned(32))) = {0};
char type = info->type; char type = info->type;
@ -222,7 +222,7 @@ u32 InitKeyDb(const char* path)
} }
use_aeskey(keyslot); use_aeskey(keyslot);
} }
free(keydb); free(keydb);
return (nkeys) ? 0 : 1; return (nkeys) ? 0 : 1;
} }
@ -236,15 +236,15 @@ u32 CheckRecommendedKeyDb(const char* path)
0x40, 0x76, 0x54, 0x3D, 0xA3, 0xFF, 0x91, 0x1C, 0xE1, 0xCC, 0x4E, 0xC7, 0x2F, 0x92, 0xE4, 0xB7, 0x40, 0x76, 0x54, 0x3D, 0xA3, 0xFF, 0x91, 0x1C, 0xE1, 0xCC, 0x4E, 0xC7, 0x2F, 0x92, 0xE4, 0xB7,
0x2B, 0x24, 0x00, 0x15, 0xBE, 0x9B, 0xFC, 0xDE, 0x7F, 0xED, 0x95, 0x1D, 0xD5, 0xAB, 0x2D, 0xCB 0x2B, 0x24, 0x00, 0x15, 0xBE, 0x9B, 0xFC, 0xDE, 0x7F, 0xED, 0x95, 0x1D, 0xD5, 0xAB, 0x2D, 0xCB
}; };
// try to load aeskeydb.bin file // try to load aeskeydb.bin file
AesKeyInfo* keydb = (AesKeyInfo*) malloc(STD_BUFFER_SIZE); AesKeyInfo* keydb = (AesKeyInfo*) malloc(STD_BUFFER_SIZE);
if (!keydb) return 1; if (!keydb) return 1;
u32 nkeys = LoadKeyDb(path, keydb, STD_BUFFER_SIZE); u32 nkeys = LoadKeyDb(path, keydb, STD_BUFFER_SIZE);
// compare with recommended SHA // compare with recommended SHA
bool res = (nkeys && (sha_cmp(recommended_sha, keydb, nkeys * sizeof(AesKeyInfo), SHA256_MODE) == 0)); bool res = (nkeys && (sha_cmp(recommended_sha, keydb, nkeys * sizeof(AesKeyInfo), SHA256_MODE) == 0));
free(keydb); free(keydb);
return res ? 0 : 1; return res ? 0 : 1;
} }

View File

@ -14,10 +14,10 @@
#define KEYS_UNKNOWN 0 #define KEYS_UNKNOWN 0
#define KEYS_DEVKIT 1 #define KEYS_DEVKIT 1
#define KEYS_RETAIL 2 #define KEYS_RETAIL 2
typedef struct { typedef struct {
u8 slot; // keyslot, 0x00...0x3F u8 slot; // keyslot, 0x00...0x3F
char type; // type 'X' / 'Y' / 'N' for normalKey / 'I' for IV char type; // type 'X' / 'Y' / 'N' for normalKey / 'I' for IV
char id[10]; // key ID for special keys, all zero for standard keys char id[10]; // key ID for special keys, all zero for standard keys
u8 reserved[2]; // reserved space u8 reserved[2]; // reserved space

View File

@ -13,9 +13,9 @@ void sha_init(u32 mode)
} }
void sha_update(const void* src, u32 size) void sha_update(const void* src, u32 size)
{ {
const u32* src32 = (const u32*)src; const u32* src32 = (const u32*)src;
while(size >= 0x40) { while(size >= 0x40) {
while(*REG_SHACNT & 1); while(*REG_SHACNT & 1);
*((volatile _sha_block*)REG_SHAINFIFO) = *((const _sha_block*)src32); *((volatile _sha_block*)REG_SHAINFIFO) = *((const _sha_block*)src32);

View File

@ -63,7 +63,7 @@ FATpartition DriveInfo[13] = {
{ TYPE_RAMDRV, SUBTYPE_NONE, 0, 0, 0xFF } // Z - RAMDRIVE { TYPE_RAMDRV, SUBTYPE_NONE, 0, 0, 0xFF } // Z - RAMDRIVE
}; };
static BYTE imgnand_mode = 0x00; static BYTE imgnand_mode = 0x00;
@ -81,7 +81,7 @@ DWORD get_fattime( void ) {
((DSTIMEGET(&dstime, bcd_D)&0x1F) << 16) | ((DSTIMEGET(&dstime, bcd_D)&0x1F) << 16) |
((DSTIMEGET(&dstime, bcd_M)&0x0F) << 21) | ((DSTIMEGET(&dstime, bcd_M)&0x0F) << 21) |
(((DSTIMEGET(&dstime, bcd_Y)+(2000-1980))&0x7F) << 25); (((DSTIMEGET(&dstime, bcd_Y)+(2000-1980))&0x7F) << 25);
return fattime; return fattime;
} }
@ -113,10 +113,10 @@ DSTATUS disk_initialize (
imgnand_mode = (GetMountState() & IMG_NAND) ? 0x01 : 0x00; imgnand_mode = (GetMountState() & IMG_NAND) ? 0x01 : 0x00;
FATpartition* fat_info = PART_INFO(pdrv); FATpartition* fat_info = PART_INFO(pdrv);
BYTE type = PART_TYPE(pdrv); BYTE type = PART_TYPE(pdrv);
fat_info->offset = fat_info->size = 0; fat_info->offset = fat_info->size = 0;
fat_info->keyslot = 0xFF; fat_info->keyslot = 0xFF;
if (type == TYPE_SDCARD) { if (type == TYPE_SDCARD) {
if (sdmmc_sdcard_init() != 0) return STA_NOINIT|STA_NODISK; if (sdmmc_sdcard_init() != 0) return STA_NOINIT|STA_NODISK;
fat_info->size = getMMCDevice(1)->total_size; fat_info->size = getMMCDevice(1)->total_size;
@ -149,7 +149,7 @@ DSTATUS disk_initialize (
InitRamDrive(); InitRamDrive();
fat_info->size = (GetRamDriveSize() + 0x1FF) / 0x200; fat_info->size = (GetRamDriveSize() + 0x1FF) / 0x200;
} }
return RES_OK; return RES_OK;
} }
@ -166,9 +166,9 @@ DRESULT disk_read (
DWORD sector, /* Sector address in LBA */ DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to read */ UINT count /* Number of sectors to read */
) )
{ {
BYTE type = PART_TYPE(pdrv); BYTE type = PART_TYPE(pdrv);
if (type == TYPE_NONE) { if (type == TYPE_NONE) {
return RES_PARERR; return RES_PARERR;
} else if (type == TYPE_SDCARD) { } else if (type == TYPE_SDCARD) {
@ -205,7 +205,7 @@ DRESULT disk_write (
) )
{ {
BYTE type = PART_TYPE(pdrv); BYTE type = PART_TYPE(pdrv);
if (type == TYPE_NONE) { if (type == TYPE_NONE) {
return RES_PARERR; return RES_PARERR;
} else if (type == TYPE_SDCARD) { } else if (type == TYPE_SDCARD) {
@ -245,7 +245,7 @@ DRESULT disk_ioctl (
{ {
BYTE type = PART_TYPE(pdrv); BYTE type = PART_TYPE(pdrv);
FATpartition* fat_info = PART_INFO(pdrv); FATpartition* fat_info = PART_INFO(pdrv);
switch (cmd) { switch (cmd) {
case GET_SECTOR_SIZE: case GET_SECTOR_SIZE:
*((DWORD*) buff) = 0x200; *((DWORD*) buff) = 0x200;
@ -262,7 +262,7 @@ DRESULT disk_ioctl (
// nothing else to do here - sdmmc.c handles the rest // nothing else to do here - sdmmc.c handles the rest
return RES_OK; return RES_OK;
} }
return RES_PARERR; return RES_PARERR;
} }
#endif #endif

View File

@ -5,7 +5,7 @@ u32 ValidateMbrHeader(MbrHeader* mbr) {
u32 sector = 1; // check partitions u32 sector = 1; // check partitions
for (u32 i = 0; i < 4; i++) { for (u32 i = 0; i < 4; i++) {
MbrPartitionInfo* partition = mbr->partitions + i; MbrPartitionInfo* partition = mbr->partitions + i;
if (!partition->count && i) continue; if (!partition->count && i) continue;
else if (!partition->count) return 1; // first partition can't be empty else if (!partition->count) return 1; // first partition can't be empty
if ((partition->type != 0x1) && (partition->type != 0x4) && (partition->type != 0x6) && if ((partition->type != 0x1) && (partition->type != 0x4) && (partition->type != 0x6) &&
(partition->type != 0xB) && (partition->type != 0xC) && (partition->type != 0xE)) (partition->type != 0xB) && (partition->type != 0xC) && (partition->type != 0xE))
@ -22,7 +22,7 @@ u32 ValidateFatHeader(void* fat) {
if (strncmp(fat32->fs_type, "FAT32 ", 8) == 0) if (strncmp(fat32->fs_type, "FAT32 ", 8) == 0)
return 0; // is FAT32 header return 0; // is FAT32 header
Fat16Header* fat16 = (Fat16Header*) fat; Fat16Header* fat16 = (Fat16Header*) fat;
if ((strncmp(fat16->fs_type, "FAT16 ", 8) == 0) || if ((strncmp(fat16->fs_type, "FAT16 ", 8) == 0) ||
(strncmp(fat16->fs_type, "FAT12 ", 8) == 0) || (strncmp(fat16->fs_type, "FAT12 ", 8) == 0) ||
(strncmp(fat16->fs_type, "FAT ", 8) == 0)) (strncmp(fat16->fs_type, "FAT ", 8) == 0))
return 0; // is FAT16 / FAT12 header return 0; // is FAT16 / FAT12 header

View File

@ -15,7 +15,7 @@ typedef struct {
typedef struct { typedef struct {
char text[446]; char text[446];
MbrPartitionInfo partitions[4]; MbrPartitionInfo partitions[4];
u16 magic; // 0xAA55 u16 magic; // 0xAA55
} PACKED_ALIGN(2) MbrHeader; } PACKED_ALIGN(2) MbrHeader;
@ -50,7 +50,7 @@ typedef struct {
u32 clr_root; // 0x02 u32 clr_root; // 0x02
u16 sct_fsinfo; // 0x01 u16 sct_fsinfo; // 0x01
u16 sct_backup; // 0x06 u16 sct_backup; // 0x06
u8 reserved3[12]; u8 reserved3[12];
u8 ndrive; // 0x80 u8 ndrive; // 0x80
u8 head_cur; // 0x00 u8 head_cur; // 0x00
u8 boot_sig; // 0x29 u8 boot_sig; // 0x29

View File

@ -112,7 +112,7 @@ u64 IdentifyFileType(const char* path) {
} }
} }
} }
if (fsize == sizeof(TitleInfoEntry) && (strncasecmp(path, "T:/", 3) == 0)) { if (fsize == sizeof(TitleInfoEntry) && (strncasecmp(path, "T:/", 3) == 0)) {
const char* mntpath = GetMountPath(); const char* mntpath = GetMountPath();
if (mntpath && *mntpath) { if (mntpath && *mntpath) {
@ -183,6 +183,6 @@ u64 IdentifyFileType(const char* path) {
if (FileGetSize(path_cdn) > 0) if (FileGetSize(path_cdn) > 0)
return GAME_NUSCDN; // NUS/CDN type 1 return GAME_NUSCDN; // NUS/CDN type 1
} }
return 0; return 0;
} }

View File

@ -16,7 +16,7 @@ static bool search_title_mode = false;
int DriveType(const char* path) { int DriveType(const char* path) {
int type = DRV_UNKNOWN; int type = DRV_UNKNOWN;
int pdrv = GetMountedFSNum(path); int pdrv = GetMountedFSNum(path);
if (CheckAliasDrive(path)) { if (CheckAliasDrive(path)) {
type = DRV_FAT | DRV_ALIAS | ((*path == 'A') ? DRV_SYSNAND : DRV_EMUNAND); type = DRV_FAT | DRV_ALIAS | ((*path == 'A') ? DRV_SYSNAND : DRV_EMUNAND);
} else if (*search_pattern && *search_path && (strncmp(path, "Z:", 3) == 0)) { } else if (*search_pattern && *search_path && (strncmp(path, "Z:", 3) == 0)) {
@ -39,7 +39,7 @@ int DriveType(const char* path) {
} else if ((pdrv >= 7) && (pdrv <= 9) && } else if ((pdrv >= 7) && (pdrv <= 9) &&
(GetMountState() & (IMG_FAT|IMG_NAND))) { (GetMountState() & (IMG_FAT|IMG_NAND))) {
type = DRV_FAT | DRV_IMAGE | DRV_STDFAT; type = DRV_FAT | DRV_IMAGE | DRV_STDFAT;
} }
} else if (CheckVirtualDrive(path)) { } else if (CheckVirtualDrive(path)) {
int vsrc = GetVirtualSource(path); int vsrc = GetVirtualSource(path);
if (vsrc == VRT_SYSNAND) { if (vsrc == VRT_SYSNAND) {
@ -58,9 +58,9 @@ int DriveType(const char* path) {
type = DRV_VIRTUAL | DRV_CART; type = DRV_VIRTUAL | DRV_CART;
} else if (vsrc == VRT_VRAM) { } else if (vsrc == VRT_VRAM) {
type = DRV_VIRTUAL | DRV_VRAM; type = DRV_VIRTUAL | DRV_VRAM;
} }
} }
return type; return type;
} }
@ -82,14 +82,14 @@ bool GetRootDirContentsWorker(DirStruct* contents) {
static const char* drvname[] = { FS_DRVNAME }; static const char* drvname[] = { FS_DRVNAME };
static const char* drvnum[] = { FS_DRVNUM }; static const char* drvnum[] = { FS_DRVNUM };
u32 n_entries = 0; u32 n_entries = 0;
char sdlabel[DRV_LABEL_LEN]; char sdlabel[DRV_LABEL_LEN];
if (!GetFATVolumeLabel("0:", sdlabel) || !(*sdlabel)) if (!GetFATVolumeLabel("0:", sdlabel) || !(*sdlabel))
strcpy(sdlabel, "NOLABEL"); strcpy(sdlabel, "NOLABEL");
char carttype[16]; char carttype[16];
GetVCartTypeString(carttype); GetVCartTypeString(carttype);
// virtual root objects hacked in // virtual root objects hacked in
for (u32 i = 0; (i < countof(drvnum)) && (n_entries < MAX_DIR_ENTRIES); i++) { for (u32 i = 0; (i < countof(drvnum)) && (n_entries < MAX_DIR_ENTRIES); i++) {
DirEntry* entry = &(contents->entry[n_entries]); DirEntry* entry = &(contents->entry[n_entries]);
@ -124,7 +124,7 @@ bool GetRootDirContentsWorker(DirStruct* contents) {
n_entries++; n_entries++;
} }
contents->n_entries = n_entries; contents->n_entries = n_entries;
return contents->n_entries; return contents->n_entries;
} }
@ -133,11 +133,11 @@ bool GetDirContentsWorker(DirStruct* contents, char* fpath, int fnsize, const ch
FILINFO fno; FILINFO fno;
char* fname = fpath + strnlen(fpath, fnsize - 1); char* fname = fpath + strnlen(fpath, fnsize - 1);
bool ret = false; bool ret = false;
if (fvx_opendir(&pdir, fpath) != FR_OK) if (fvx_opendir(&pdir, fpath) != FR_OK)
return false; return false;
if (*(fname-1) != '/') *(fname++) = '/'; if (*(fname-1) != '/') *(fname++) = '/';
while (fvx_readdir(&pdir, &fno) == FR_OK) { while (fvx_readdir(&pdir, &fno) == FR_OK) {
if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0)) if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0))
continue; // filter out virtual entries continue; // filter out virtual entries
@ -176,7 +176,7 @@ bool GetDirContentsWorker(DirStruct* contents, char* fpath, int fnsize, const ch
} }
} }
fvx_closedir(&pdir); fvx_closedir(&pdir);
return ret; return ret;
} }
@ -222,7 +222,7 @@ uint64_t GetFreeSpace(const char* path)
int pdrv = GetMountedFSNum(path); int pdrv = GetMountedFSNum(path);
FATFS* fsobj = GetMountedFSObject(path); FATFS* fsobj = GetMountedFSObject(path);
if ((pdrv < 0) || !fsobj) return 0; if ((pdrv < 0) || !fsobj) return 0;
snprintf(fsname, 3, "%i:", pdrv); snprintf(fsname, 3, "%i:", pdrv);
if (f_getfree(fsname, &free_clusters, &fsptr) != FR_OK) if (f_getfree(fsname, &free_clusters, &fsptr) != FR_OK)
return 0; return 0;

View File

@ -40,7 +40,7 @@
"MEMORY VIRTUAL", \ "MEMORY VIRTUAL", \
"VRAM VIRTUAL", \ "VRAM VIRTUAL", \
"LAST SEARCH" \ "LAST SEARCH" \
#define FS_DRVNUM \ #define FS_DRVNUM \
"0:", "1:", "2:", "3:", "A:", "S:", "4:", "5:", "6:", "B:", "E:", "7:", "8:", "9:", "I:", "C:", "G:", "K:", "T:", "D:", "M:", "V:", "Z:" "0:", "1:", "2:", "3:", "A:", "S:", "4:", "5:", "6:", "B:", "E:", "7:", "8:", "9:", "I:", "C:", "G:", "K:", "T:", "D:", "M:", "V:", "Z:"

View File

@ -25,7 +25,7 @@ bool GoodRenamer(DirEntry* entry, bool ask) {
if ((GetGoodName(goodname, entry->path, false) != 0) || if ((GetGoodName(goodname, entry->path, false) != 0) ||
(strncmp(goodname + strnlen(goodname, 256) - 4, ".tmd", 4) == 0)) // no TMD, please (strncmp(goodname + strnlen(goodname, 256) - 4, ".tmd", 4) == 0)) // no TMD, please
return false; return false;
if (ask) { // ask for confirmatiom if (ask) { // ask for confirmatiom
char oldname_tr[32+1]; char oldname_tr[32+1];
char newname_ww[256]; char newname_ww[256];
@ -35,7 +35,7 @@ bool GoodRenamer(DirEntry* entry, bool ask) {
if (!ShowPrompt(true, "%s\nRename to good name?\n \n%s", oldname_tr, newname_ww)) if (!ShowPrompt(true, "%s\nRename to good name?\n \n%s", oldname_tr, newname_ww))
return true; // call it a success because user choice return true; // call it a success because user choice
} }
char npath[256]; // get new path char npath[256]; // get new path
strncpy(npath, entry->path, 256); strncpy(npath, entry->path, 256);
char* nname = strrchr(npath, '/'); char* nname = strrchr(npath, '/');
@ -47,6 +47,6 @@ bool GoodRenamer(DirEntry* entry, bool ask) {
if (f_rename(entry->path, npath) != FR_OK) return false; if (f_rename(entry->path, npath) != FR_OK) return false;
strncpy(entry->path, npath, 256); strncpy(entry->path, npath, 256);
entry->name = entry->path + (nname - npath); entry->name = entry->path + (nname - npath);
return true; return true;
} }

View File

@ -18,7 +18,7 @@ bool InitSDCardFS() {
bool InitExtFS() { bool InitExtFS() {
static bool ramdrv_ready = false; static bool ramdrv_ready = false;
for (u32 i = 1; i < NORM_FS; i++) { for (u32 i = 1; i < NORM_FS; i++) {
char fsname[8]; char fsname[8];
snprintf(fsname, 7, "%lu:", i); snprintf(fsname, 7, "%lu:", i);

View File

@ -6,7 +6,7 @@
// init SD card filesystem - required(?) for everything else // init SD card filesystem - required(?) for everything else
bool InitSDCardFS(); bool InitSDCardFS();
// init fill external fileystem // init fill external fileystem
bool InitExtFS(); bool InitExtFS();
// mount and init image file system // mount and init image file system
@ -21,7 +21,7 @@ void DeinitSDCardFS();
// dismount drives of a certain type // dismount drives of a certain type
void DismountDriveType(u32 type); void DismountDriveType(u32 type);
// returns the mount state of the SD card // returns the mount state of the SD card
bool CheckSDMountState(void); bool CheckSDMountState(void);
// get number of mounted file system (only for FATFS filesystems) // get number of mounted file system (only for FATFS filesystems)

View File

@ -7,7 +7,7 @@
#include "ui.h" #include "ui.h"
#include "sdmmc.h" #include "sdmmc.h"
#define PATH_SYS_LVL1 "S:/twln.bin", "S:/twlp.bin" #define PATH_SYS_LVL1 "S:/twln.bin", "S:/twlp.bin"
#define PATH_SYS_LVL2 "1:/rw/sys/LocalFriendCodeSeed_B", "1:/rw/sys/LocalFriendCodeSeed_A", \ #define PATH_SYS_LVL2 "1:/rw/sys/LocalFriendCodeSeed_B", "1:/rw/sys/LocalFriendCodeSeed_A", \
"1:/rw/sys/SecureInfo_A", "1:/rw/sys/SecureInfo_B", \ "1:/rw/sys/SecureInfo_A", "1:/rw/sys/SecureInfo_B", \
"1:/private/movable.sed", "1:/ro/sys/HWCAL0.dat", "1:/ro/sys/HWCAL1.dat", \ "1:/private/movable.sed", "1:/ro/sys/HWCAL0.dat", "1:/ro/sys/HWCAL1.dat", \
@ -23,7 +23,7 @@ bool CheckWritePermissions(const char* path) {
char area_name[16]; char area_name[16];
int drvtype = DriveType(path); int drvtype = DriveType(path);
u32 perm; u32 perm;
// create a standardized path string // create a standardized path string
char path_f[256]; char path_f[256];
char* p = (char*) path; char* p = (char*) path;
@ -37,13 +37,13 @@ bool CheckWritePermissions(const char* path) {
// check mounted image write permissions // check mounted image write permissions
if ((drvtype & DRV_IMAGE) && !CheckWritePermissions(GetMountPath())) if ((drvtype & DRV_IMAGE) && !CheckWritePermissions(GetMountPath()))
return false; // endless loop when mounted file inside image, but not possible return false; // endless loop when mounted file inside image, but not possible
// SD card write protection check // SD card write protection check
if ((drvtype & (DRV_SDCARD | DRV_EMUNAND | DRV_ALIAS)) && SD_WRITE_PROTECTED) { if ((drvtype & (DRV_SDCARD | DRV_EMUNAND | DRV_ALIAS)) && SD_WRITE_PROTECTED) {
ShowPrompt(false, "SD card is write protected!\nCan't continue."); ShowPrompt(false, "SD card is write protected!\nCan't continue.");
return false; return false;
} }
// check drive type, get permission type // check drive type, get permission type
if (drvtype & DRV_SYSNAND) { if (drvtype & DRV_SYSNAND) {
static const u32 perms[] = { PERM_SYS_LVL0, PERM_SYS_LVL1, PERM_SYS_LVL2, PERM_SYS_LVL3 }; static const u32 perms[] = { PERM_SYS_LVL0, PERM_SYS_LVL1, PERM_SYS_LVL2, PERM_SYS_LVL3 };
@ -104,20 +104,20 @@ bool CheckWritePermissions(const char* path) {
} else { } else {
return false; return false;
} }
// check permission, return if already set // check permission, return if already set
if ((write_permissions & perm) == perm) if ((write_permissions & perm) == perm)
return true; return true;
// offer unlock if possible // offer unlock if possible
if (!(perm & (PERM_VRAM|PERM_GAME|PERM_XORPAD))) { if (!(perm & (PERM_VRAM|PERM_GAME|PERM_XORPAD))) {
// ask the user // ask the user
if (!ShowPrompt(true, "Writing to %s is locked!\nUnlock it now?", area_name)) if (!ShowPrompt(true, "Writing to %s is locked!\nUnlock it now?", area_name))
return false; return false;
return SetWritePermissions(perm, true); return SetWritePermissions(perm, true);
} }
// unlock not possible // unlock not possible
ShowPrompt(false, "Unlock write permission for\n%s is not allowed.", area_name); ShowPrompt(false, "Unlock write permission for\n%s is not allowed.", area_name);
return false; return false;
@ -141,7 +141,7 @@ bool SetWritePermissions(u32 perm, bool add_perm) {
if (!add_perm) write_permissions = perm; if (!add_perm) write_permissions = perm;
return true; return true;
} }
switch (perm) { switch (perm) {
case PERM_BASE: case PERM_BASE:
if (!ShowUnlockSequence(1, "You want to enable base\nwriting permissions.")) if (!ShowUnlockSequence(1, "You want to enable base\nwriting permissions."))
@ -207,9 +207,9 @@ bool SetWritePermissions(u32 perm, bool add_perm) {
break; break;
#endif #endif
} }
write_permissions = add_perm ? write_permissions | perm : perm; write_permissions = add_perm ? write_permissions | perm : perm;
return true; return true;
} }

View File

@ -43,19 +43,19 @@ bool FormatSDCard(u64 hidden_mb, u32 cluster_size, const char* label) {
u32 emu_size = (u32) ((hidden_mb * 1024 * 1024) / 512); u32 emu_size = (u32) ((hidden_mb * 1024 * 1024) / 512);
u32 fat_sector = align(emu_sector + emu_size, 0x2000); // align to 4MB u32 fat_sector = align(emu_sector + emu_size, 0x2000); // align to 4MB
u32 fat_size = (fat_sector < sd_size) ? sd_size - fat_sector : 0; u32 fat_size = (fat_sector < sd_size) ? sd_size - fat_sector : 0;
// FAT size check // FAT size check
if (fat_size < 0x80000) { // minimum free space: 256MB if (fat_size < 0x80000) { // minimum free space: 256MB
ShowPrompt(false, "Error: SD card is too small"); ShowPrompt(false, "Error: SD card is too small");
return false; return false;
} }
// Write protection check // Write protection check
if (SD_WRITE_PROTECTED) { if (SD_WRITE_PROTECTED) {
ShowPrompt(false, "SD card is write protected!\nCan't continue."); ShowPrompt(false, "SD card is write protected!\nCan't continue.");
return false; return false;
} }
// build the MBR // build the MBR
memcpy(mbrdata + 0x08, &fat_sector, 4); memcpy(mbrdata + 0x08, &fat_sector, 4);
memcpy(mbrdata + 0x0C, &fat_size, 4); memcpy(mbrdata + 0x0C, &fat_size, 4);
@ -64,13 +64,13 @@ bool FormatSDCard(u64 hidden_mb, u32 cluster_size, const char* label) {
memcpy(mbr + 0x1BE, mbrdata, 0x42); memcpy(mbr + 0x1BE, mbrdata, 0x42);
if (hidden_mb) memcpy(mbr, "GATEWAYNAND", 12); // legacy if (hidden_mb) memcpy(mbr, "GATEWAYNAND", 12); // legacy
else memset(mbr + 0x1CE, 0, 0x10); else memset(mbr + 0x1CE, 0, 0x10);
// one last warning.... // one last warning....
// 0:/Nintendo 3DS/ write permission is ignored here, this warning is enough // 0:/Nintendo 3DS/ write permission is ignored here, this warning is enough
if (!ShowUnlockSequence(5, "!WARNING!\n \nProceeding will format this SD.\nThis will irreversibly delete\nALL data on it.")) if (!ShowUnlockSequence(5, "!WARNING!\n \nProceeding will format this SD.\nThis will irreversibly delete\nALL data on it."))
return false; return false;
ShowString("Formatting SD, please wait..."); ShowString("Formatting SD, please wait...");
// write the MBR to disk // write the MBR to disk
// !this assumes a fully deinitialized file system! // !this assumes a fully deinitialized file system!
if ((sdmmc_sdcard_init() != 0) || (sdmmc_sdcard_writesectors(0, 1, mbr) != 0) || if ((sdmmc_sdcard_init() != 0) || (sdmmc_sdcard_writesectors(0, 1, mbr) != 0) ||
@ -78,11 +78,11 @@ bool FormatSDCard(u64 hidden_mb, u32 cluster_size, const char* label) {
ShowPrompt(false, "Error: SD card i/o failure"); ShowPrompt(false, "Error: SD card i/o failure");
return false; return false;
} }
// format the SD card // format the SD card
VolToPart[0].pt = 1; // workaround to prevent FatFS rebuilding the MBR VolToPart[0].pt = 1; // workaround to prevent FatFS rebuilding the MBR
InitSDCardFS(); InitSDCardFS();
u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); u8* buffer = (u8*) malloc(STD_BUFFER_SIZE);
if (!buffer) bkpt; // will not happen if (!buffer) bkpt; // will not happen
MKFS_PARM opt0, opt1; MKFS_PARM opt0, opt1;
@ -92,14 +92,14 @@ bool FormatSDCard(u64 hidden_mb, u32 cluster_size, const char* label) {
opt0.align = opt1.align = 0; opt0.align = opt1.align = 0;
opt0.n_fat = opt1.n_fat = 1; opt0.n_fat = opt1.n_fat = 1;
opt0.n_root = opt1.n_root = 0; opt0.n_root = opt1.n_root = 0;
bool ret = ((f_mkfs("0:", &opt0, buffer, STD_BUFFER_SIZE) == FR_OK) || bool ret = ((f_mkfs("0:", &opt0, buffer, STD_BUFFER_SIZE) == FR_OK) ||
(f_mkfs("0:", &opt1, buffer, STD_BUFFER_SIZE) == FR_OK)) && (f_mkfs("0:", &opt1, buffer, STD_BUFFER_SIZE) == FR_OK)) &&
(f_setlabel((label) ? label : "0:GM9SD") == FR_OK); (f_setlabel((label) ? label : "0:GM9SD") == FR_OK);
free(buffer); 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
return ret; return ret;
} }
@ -108,12 +108,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);
u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); u8* buffer = (u8*) malloc(STD_BUFFER_SIZE);
if (!buffer) bkpt; if (!buffer) bkpt;
bool ret = (f_mkfs("8:", NULL, buffer, STD_BUFFER_SIZE) == FR_OK); bool ret = (f_mkfs("8:", NULL, buffer, STD_BUFFER_SIZE) == FR_OK);
free(buffer); free(buffer);
if (ret) { if (ret) {
f_setlabel("8:BONUS"); f_setlabel("8:BONUS");
InitExtFS(); InitExtFS();
@ -124,12 +124,12 @@ bool SetupBonusDrive(void) {
bool FileUnlock(const char* path) { bool FileUnlock(const char* path) {
FIL file; FIL file;
FRESULT res; FRESULT res;
if (!(DriveType(path) & DRV_FAT)) return true; // can't really check this if (!(DriveType(path) & DRV_FAT)) return true; // can't really check this
if ((res = fx_open(&file, path, FA_READ | FA_OPEN_EXISTING)) != FR_OK) { if ((res = fx_open(&file, path, FA_READ | FA_OPEN_EXISTING)) != FR_OK) {
char pathstr[32 + 1]; char pathstr[32 + 1];
TruncateString(pathstr, path, 32, 8); TruncateString(pathstr, path, 32, 8);
if (GetMountState() && (res == FR_LOCKED) && if (GetMountState() && (res == FR_LOCKED) &&
(ShowPrompt(true, "%s\nFile is currently mounted.\nUnmount to unlock?", pathstr))) { (ShowPrompt(true, "%s\nFile is currently mounted.\nUnmount to unlock?", pathstr))) {
InitImgFS(NULL); InitImgFS(NULL);
if (fx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) if (fx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
@ -137,7 +137,7 @@ bool FileUnlock(const char* path) {
} else return false; } else return false;
} }
fx_close(&file); fx_close(&file);
return true; return true;
} }
@ -165,19 +165,19 @@ bool FileGetSha256(const char* path, u8* sha256, u64 offset, u64 size) {
bool ret = true; bool ret = true;
FIL file; FIL file;
u64 fsize; u64 fsize;
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); u32 bufsiz = min(STD_BUFFER_SIZE, fsize);
u8* buffer = (u8*) malloc(bufsiz); u8* buffer = (u8*) malloc(bufsiz);
if (!buffer) return false; 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 += bufsiz) { for (u64 pos = 0; (pos < size) && ret; pos += bufsiz) {
@ -189,13 +189,13 @@ bool FileGetSha256(const char* path, u8* sha256, u64 offset, u64 size) {
ret = false; ret = false;
sha_update(buffer, bytes_read); sha_update(buffer, bytes_read);
} }
sha_get(sha256); sha_get(sha256);
fvx_close(&file); fvx_close(&file);
free(buffer); free(buffer);
ShowProgress(1, 1, path); ShowProgress(1, 1, path);
return ret; return ret;
} }
@ -203,13 +203,13 @@ u32 FileFindData(const char* path, u8* data, u32 size_data, u32 offset_file) {
FIL file; // used for FAT & virtual FIL file; // used for FAT & virtual
u64 found = (u64) -1; u64 found = (u64) -1;
u64 fsize = FileGetSize(path); u64 fsize = FileGetSize(path);
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); u8* buffer = (u8*) malloc(STD_BUFFER_SIZE);
if (!buffer) return false; 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;
@ -236,10 +236,10 @@ u32 FileFindData(const char* path, u8* data, u32 size_data, u32 offset_file) {
break; break;
} }
} }
free(buffer); free(buffer);
fvx_close(&file); fvx_close(&file);
return found; return found;
} }
@ -247,13 +247,13 @@ bool FileInjectFile(const char* dest, const char* orig, u64 off_dest, u64 off_or
FIL ofile; FIL ofile;
FIL dfile; FIL dfile;
bool allow_expand = (flags && (*flags & ALLOW_EXPAND)); bool allow_expand = (flags && (*flags & ALLOW_EXPAND));
if (!CheckWritePermissions(dest)) return false; if (!CheckWritePermissions(dest)) return false;
if (strncasecmp(dest, orig, 256) == 0) { if (strncasecmp(dest, orig, 256) == 0) {
ShowPrompt(false, "Error: Can't inject file into itself"); ShowPrompt(false, "Error: Can't inject file into itself");
return false; return false;
} }
// open destination / origin // open destination / origin
if (fvx_open(&dfile, dest, FA_WRITE | ((allow_expand) ? FA_OPEN_ALWAYS : FA_OPEN_EXISTING)) != FR_OK) if (fvx_open(&dfile, dest, FA_WRITE | ((allow_expand) ? FA_OPEN_ALWAYS : FA_OPEN_EXISTING)) != FR_OK)
return false; return false;
@ -266,7 +266,7 @@ bool FileInjectFile(const char* dest, const char* orig, u64 off_dest, u64 off_or
fvx_lseek(&ofile, off_orig); fvx_lseek(&ofile, off_orig);
if (!size && (off_orig < fvx_size(&ofile))) if (!size && (off_orig < fvx_size(&ofile)))
size = fvx_size(&ofile) - off_orig; size = fvx_size(&ofile) - off_orig;
// check file limits // check file limits
if (!allow_expand && (off_dest + size > fvx_size(&dfile))) { if (!allow_expand && (off_dest + size > fvx_size(&dfile))) {
ShowPrompt(false, "Operation would write beyond end of file"); ShowPrompt(false, "Operation would write beyond end of file");
@ -279,10 +279,10 @@ bool FileInjectFile(const char* dest, const char* orig, u64 off_dest, u64 off_or
fvx_close(&ofile); fvx_close(&ofile);
return false; return false;
} }
u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); u8* buffer = (u8*) malloc(STD_BUFFER_SIZE);
if (!buffer) return false; 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 += STD_BUFFER_SIZE) { for (u64 pos = 0; (pos < size) && ret; pos += STD_BUFFER_SIZE) {
@ -302,39 +302,39 @@ bool FileInjectFile(const char* dest, const char* orig, u64 off_dest, u64 off_or
} }
} }
ShowProgress(1, 1, orig); ShowProgress(1, 1, orig);
free(buffer); free(buffer);
fvx_close(&dfile); fvx_close(&dfile);
fvx_close(&ofile); fvx_close(&ofile);
return ret; return ret;
} }
bool FileSetByte(const char* dest, u64 offset, u64 size, u8 fillbyte, u32* flags) { bool FileSetByte(const char* dest, u64 offset, u64 size, u8 fillbyte, u32* flags) {
FIL dfile; FIL dfile;
bool allow_expand = (flags && (*flags & ALLOW_EXPAND)); bool allow_expand = (flags && (*flags & ALLOW_EXPAND));
if (!CheckWritePermissions(dest)) return false; if (!CheckWritePermissions(dest)) return false;
// open destination // open destination
if (fvx_open(&dfile, dest, FA_WRITE | ((allow_expand) ? FA_OPEN_ALWAYS : FA_OPEN_EXISTING)) != FR_OK) if (fvx_open(&dfile, dest, FA_WRITE | ((allow_expand) ? FA_OPEN_ALWAYS : FA_OPEN_EXISTING)) != FR_OK)
return false; return false;
fvx_lseek(&dfile, offset); fvx_lseek(&dfile, offset);
if (!size && (offset < fvx_size(&dfile))) if (!size && (offset < fvx_size(&dfile)))
size = fvx_size(&dfile) - offset; size = fvx_size(&dfile) - offset;
// check file limits // check file limits
if (!allow_expand && (offset + size > fvx_size(&dfile))) { if (!allow_expand && (offset + size > fvx_size(&dfile))) {
ShowPrompt(false, "Operation would write beyond end of file"); ShowPrompt(false, "Operation would write beyond end of file");
fvx_close(&dfile); fvx_close(&dfile);
return false; return false;
} }
u32 bufsiz = min(STD_BUFFER_SIZE, size); u32 bufsiz = min(STD_BUFFER_SIZE, size);
u8* buffer = (u8*) malloc(bufsiz); u8* buffer = (u8*) malloc(bufsiz);
if (!buffer) return false; if (!buffer) return false;
memset(buffer, fillbyte, bufsiz); memset(buffer, fillbyte, bufsiz);
bool ret = true; bool ret = true;
ShowProgress(0, 0, dest); ShowProgress(0, 0, dest);
for (u64 pos = 0; (pos < size) && ret; pos += bufsiz) { for (u64 pos = 0; (pos < size) && ret; pos += bufsiz) {
@ -352,10 +352,10 @@ bool FileSetByte(const char* dest, u64 offset, u64 size, u8 fillbyte, u32* flags
} }
} }
ShowProgress(1, 1, dest); ShowProgress(1, 1, dest);
free(buffer); free(buffer);
fvx_close(&dfile); fvx_close(&dfile);
return ret; return ret;
} }
@ -364,7 +364,7 @@ bool FileCreateDummy(const char* cpath, const char* filename, u64 size) {
if (!CheckWritePermissions(cpath)) return false; if (!CheckWritePermissions(cpath)) return false;
if (filename) snprintf(npath, 255, "%s/%s", cpath, filename); if (filename) snprintf(npath, 255, "%s/%s", cpath, filename);
else snprintf(npath, 255, "%s", cpath); else snprintf(npath, 255, "%s", cpath);
// create dummy file (fail if already existing) // create dummy file (fail if already existing)
// then, expand the file size via cluster preallocation // then, expand the file size via cluster preallocation
FIL dfile; FIL dfile;
@ -373,7 +373,7 @@ bool FileCreateDummy(const char* cpath, const char* filename, u64 size) {
f_lseek(&dfile, size > 0xFFFFFFFF ? 0xFFFFFFFF : (FSIZE_t) size); f_lseek(&dfile, size > 0xFFFFFFFF ? 0xFFFFFFFF : (FSIZE_t) size);
f_sync(&dfile); f_sync(&dfile);
fx_close(&dfile); fx_close(&dfile);
return (fa_stat(npath, NULL) == FR_OK); return (fa_stat(npath, NULL) == FR_OK);
} }
@ -425,7 +425,7 @@ bool DirInfoWorker(char* fpath, bool virtual, u64* tsize, u32* tdirs, u32* tfile
} }
f_closedir(&pdir); f_closedir(&pdir);
} }
return ret; return ret;
} }
@ -449,20 +449,20 @@ bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move, u8* buffer,
bool append = (flags && (*flags & APPEND_ALL)); bool append = (flags && (*flags & APPEND_ALL));
bool calcsha = (flags && (*flags & CALC_SHA) && !append); bool calcsha = (flags && (*flags & CALC_SHA) && !append);
bool ret = false; bool ret = false;
// check destination write permission (special paths only) // check destination write permission (special paths only)
if (((*dest == '1') || (strncmp(dest, "0:/Nintendo 3DS", 16) == 0)) && if (((*dest == '1') || (strncmp(dest, "0:/Nintendo 3DS", 16) == 0)) &&
(!flags || !(*flags & OVERRIDE_PERM)) && (!flags || !(*flags & OVERRIDE_PERM)) &&
!CheckWritePermissions(dest)) return false; !CheckWritePermissions(dest)) return false;
FILINFO fno; FILINFO fno;
if (fvx_stat(orig, &fno) != FR_OK) return false; // origin does not exist if (fvx_stat(orig, &fno) != FR_OK) return false; // origin does not exist
if (move && (to_virtual || fno.fattrib & AM_VRT)) return false; // trying to move a virtual file if (move && (to_virtual || fno.fattrib & AM_VRT)) return false; // trying to move a virtual file
// path string (for output) // path string (for output)
char deststr[36 + 1]; char deststr[36 + 1];
TruncateString(deststr, dest, 36, 8); TruncateString(deststr, dest, 36, 8);
// the copy process takes place here // the copy process takes place here
if (!ShowProgress(0, 0, orig) && !(flags && (*flags & NO_CANCEL))) { if (!ShowProgress(0, 0, orig) && !(flags && (*flags & NO_CANCEL))) {
if (ShowPrompt(true, "%s\nB button detected. Cancel?", deststr)) return false; if (ShowPrompt(true, "%s\nB button detected. Cancel?", deststr)) return false;
@ -473,12 +473,12 @@ bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move, u8* buffer,
} else if (fno.fattrib & AM_DIR) { // processing folders (same for move & copy) } else if (fno.fattrib & AM_DIR) { // processing folders (same for move & copy)
DIR pdir; DIR pdir;
char* fname = orig + strnlen(orig, 256); char* fname = orig + strnlen(orig, 256);
if (append) { if (append) {
if (!silent) ShowPrompt(false, "%s\nError: Cannot append a folder", deststr); if (!silent) ShowPrompt(false, "%s\nError: Cannot append a folder", deststr);
return false; return false;
} }
// create the destination folder if it does not already exist // create the destination folder if it does not already exist
if (fvx_opendir(&pdir, dest) != FR_OK) { if (fvx_opendir(&pdir, dest) != FR_OK) {
if (fvx_mkdir(dest) != FR_OK) { if (fvx_mkdir(dest) != FR_OK) {
@ -486,11 +486,11 @@ bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move, u8* buffer,
return false; return false;
} }
} else fvx_closedir(&pdir); } else fvx_closedir(&pdir);
if (fvx_opendir(&pdir, orig) != FR_OK) if (fvx_opendir(&pdir, orig) != FR_OK)
return false; return false;
*(fname++) = '/'; *(fname++) = '/';
while (fvx_readdir(&pdir, &fno) == FR_OK) { while (fvx_readdir(&pdir, &fno) == FR_OK) {
if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0)) if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0))
continue; // filter out virtual entries continue; // filter out virtual entries
@ -509,7 +509,7 @@ bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move, u8* buffer,
if (!res) break; if (!res) break;
} }
} }
fvx_closedir(&pdir); fvx_closedir(&pdir);
*(--fname) = '\0'; *(--fname) = '\0';
} else if (move) { // moving if destination exists } else if (move) { // moving if destination exists
@ -525,20 +525,20 @@ bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move, u8* buffer,
FIL dfile; FIL dfile;
u64 osize; u64 osize;
u64 dsize; u64 dsize;
if (fvx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) { if (fvx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK) {
if (!FileUnlock(orig) || (fvx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK)) if (!FileUnlock(orig) || (fvx_open(&ofile, orig, FA_READ | FA_OPEN_EXISTING) != FR_OK))
return false; return false;
ShowProgress(0, 0, orig); // reinit progress bar ShowProgress(0, 0, orig); // reinit progress bar
} }
if ((!append || (fvx_open(&dfile, dest, FA_WRITE | FA_OPEN_EXISTING) != FR_OK)) && if ((!append || (fvx_open(&dfile, dest, FA_WRITE | FA_OPEN_EXISTING) != FR_OK)) &&
(fvx_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK)) { (fvx_open(&dfile, dest, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK)) {
if (!silent) ShowPrompt(false, "%s\nError: Cannot open destination file", deststr); if (!silent) ShowPrompt(false, "%s\nError: Cannot open destination file", deststr);
fvx_close(&ofile); fvx_close(&ofile);
return false; return false;
} }
ret = true; // destination file exists by now, so we need to handle deletion ret = true; // destination file exists by now, so we need to handle deletion
osize = fvx_size(&ofile); osize = fvx_size(&ofile);
dsize = append ? fvx_size(&dfile) : 0; // always 0 if not appending to file dsize = append ? fvx_size(&dfile) : 0; // always 0 if not appending to file
@ -546,16 +546,16 @@ bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move, u8* buffer,
if (!silent) ShowPrompt(false, "%s\nError: Not enough space available", deststr); if (!silent) ShowPrompt(false, "%s\nError: Not enough space available", deststr);
ret = false; ret = false;
} }
fvx_lseek(&dfile, dsize); fvx_lseek(&dfile, dsize);
fvx_sync(&dfile); fvx_sync(&dfile);
fvx_lseek(&ofile, 0); fvx_lseek(&ofile, 0);
fvx_sync(&ofile); fvx_sync(&ofile);
if (calcsha) sha_init(SHA256_MODE); if (calcsha) sha_init(SHA256_MODE);
for (u64 pos = 0; (pos < osize) && ret; pos += bufsiz) { for (u64 pos = 0; (pos < osize) && ret; pos += bufsiz) {
UINT bytes_read = 0; UINT bytes_read = 0;
UINT bytes_written = 0; UINT bytes_written = 0;
if ((fvx_read(&ofile, buffer, bufsiz, &bytes_read) != FR_OK) || if ((fvx_read(&ofile, buffer, bufsiz, &bytes_read) != FR_OK) ||
(fvx_write(&dfile, 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))
@ -574,7 +574,7 @@ bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move, u8* buffer,
sha_update(buffer, bytes_read); sha_update(buffer, bytes_read);
} }
ShowProgress(1, 1, orig); ShowProgress(1, 1, orig);
fvx_close(&ofile); fvx_close(&ofile);
fvx_close(&dfile); fvx_close(&dfile);
if (!ret && ((dsize == 0) || (fvx_lseek(&dfile, dsize) != FR_OK) || (f_truncate(&dfile) != FR_OK))) { if (!ret && ((dsize == 0) || (fvx_lseek(&dfile, dsize) != FR_OK) || (f_truncate(&dfile) != FR_OK))) {
@ -587,7 +587,7 @@ bool PathMoveCopyRec(char* dest, char* orig, u32* flags, bool move, u8* buffer,
FileSetData(dest, sha256, 0x20, 0, true); FileSetData(dest, sha256, 0x20, 0, true);
} }
} }
return ret; return ret;
} }
@ -597,10 +597,10 @@ bool PathMoveCopy(const char* dest, const char* orig, u32* flags, bool move) {
if (!CheckWritePermissions(dest)) return false; if (!CheckWritePermissions(dest)) return false;
if (move && !CheckDirWritePermissions(orig)) return false; if (move && !CheckDirWritePermissions(orig)) return false;
} }
// reset local flags // reset local flags
if (flags) *flags = *flags & ~(SKIP_CUR|OVERWRITE_CUR); if (flags) *flags = *flags & ~(SKIP_CUR|OVERWRITE_CUR);
// preparations // preparations
int ddrvtype = DriveType(dest); int ddrvtype = DriveType(dest);
int odrvtype = DriveType(orig); int odrvtype = DriveType(orig);
@ -610,32 +610,32 @@ bool PathMoveCopy(const char* dest, const char* orig, u32* flags, bool move) {
strncpy(lorig, orig, 255); strncpy(lorig, orig, 255);
char deststr[36 + 1]; char deststr[36 + 1];
TruncateString(deststr, ldest, 36, 8); TruncateString(deststr, ldest, 36, 8);
// moving only for regular FAT drives (= not alias drives) // moving only for regular FAT drives (= not alias drives)
if (move && !(ddrvtype & odrvtype & DRV_STDFAT)) { if (move && !(ddrvtype & odrvtype & DRV_STDFAT)) {
ShowPrompt(false, "Error: Only FAT files can be moved"); ShowPrompt(false, "Error: Only FAT files can be moved");
return false; return false;
} }
// is destination part of origin? // is destination part of origin?
u32 olen = strnlen(lorig, 255); u32 olen = strnlen(lorig, 255);
if ((strncasecmp(ldest, lorig, olen) == 0) && (ldest[olen] == '/')) { if ((strncasecmp(ldest, lorig, olen) == 0) && (ldest[olen] == '/')) {
ShowPrompt(false, "%s\nError: Destination is part of origin", deststr); ShowPrompt(false, "%s\nError: Destination is part of origin", deststr);
return false; return false;
} }
if (!(ddrvtype & DRV_VIRTUAL)) { // FAT destination handling if (!(ddrvtype & DRV_VIRTUAL)) { // FAT destination handling
// get destination name // get destination name
char* dname = strrchr(ldest, '/'); char* dname = strrchr(ldest, '/');
if (!dname) return false; if (!dname) return false;
dname++; dname++;
// check & fix destination == origin // check & fix destination == origin
while (strncasecmp(ldest, lorig, 255) == 0) { while (strncasecmp(ldest, lorig, 255) == 0) {
if (!ShowKeyboardOrPrompt(dname, 255 - (dname - ldest), "%s\nDestination equals origin\nChoose another name?", deststr)) if (!ShowKeyboardOrPrompt(dname, 255 - (dname - ldest), "%s\nDestination equals origin\nChoose another name?", deststr))
return false; return false;
} }
// check if destination exists // check if destination exists
if (flags && !(*flags & (OVERWRITE_CUR|OVERWRITE_ALL|APPEND_ALL)) && (fa_stat(ldest, NULL) == FR_OK)) { if (flags && !(*flags & (OVERWRITE_CUR|OVERWRITE_ALL|APPEND_ALL)) && (fa_stat(ldest, NULL) == FR_OK)) {
if (*flags & SKIP_ALL) { if (*flags & SKIP_ALL) {
@ -665,29 +665,29 @@ bool PathMoveCopy(const char* dest, const char* orig, u32* flags, bool move) {
return false; return false;
} }
} }
// 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 // setup buffer
u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); u8* buffer = (u8*) malloc(STD_BUFFER_SIZE);
if (!buffer) { if (!buffer) {
ShowPrompt(false, "Out of memory."); ShowPrompt(false, "Out of memory.");
return false; 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, buffer, STD_BUFFER_SIZE); 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); 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
if (flags) *flags &= ~CALC_SHA; if (flags) *flags &= ~CALC_SHA;
bool force_unmount = false; bool force_unmount = false;
// handle NAND image unmounts // handle NAND image unmounts
if ((ddrvtype & (DRV_SYSNAND|DRV_EMUNAND|DRV_IMAGE)) && !(GetVirtualSource(dest) & (VRT_DISADIFF | VRT_BDRI))) { if ((ddrvtype & (DRV_SYSNAND|DRV_EMUNAND|DRV_IMAGE)) && !(GetVirtualSource(dest) & (VRT_DISADIFF | VRT_BDRI))) {
FILINFO fno; FILINFO fno;
@ -695,31 +695,31 @@ bool PathMoveCopy(const char* dest, const char* orig, u32* flags, bool move) {
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;
} }
// prevent illegal operations // prevent illegal operations
if (force_unmount && (odrvtype & ddrvtype & (DRV_SYSNAND|DRV_EMUNAND|DRV_IMAGE))) { if (force_unmount && (odrvtype & ddrvtype & (DRV_SYSNAND|DRV_EMUNAND|DRV_IMAGE))) {
ShowPrompt(false, "Copy operation is not allowed"); ShowPrompt(false, "Copy operation is not allowed");
return false; return false;
} }
// check destination == origin // check destination == origin
if (strncasecmp(ldest, lorig, 255) == 0) { if (strncasecmp(ldest, lorig, 255) == 0) {
ShowPrompt(false, "%s\nDestination equals origin", deststr); ShowPrompt(false, "%s\nDestination equals origin", deststr);
return false; return false;
} }
// setup buffer // setup buffer
u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); u8* buffer = (u8*) malloc(STD_BUFFER_SIZE);
if (!buffer) { if (!buffer) {
ShowPrompt(false, "Out of memory."); ShowPrompt(false, "Out of memory.");
return false; 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, buffer, STD_BUFFER_SIZE); bool res = PathMoveCopyRec(ldest, lorig, flags, false, buffer, STD_BUFFER_SIZE);
if (force_unmount) InitExtFS(); if (force_unmount) InitExtFS();
free(buffer); free(buffer);
return res; return res;
} }
@ -731,7 +731,7 @@ bool PathCopy(const char* destdir, const char* orig, u32* flags) {
char* oname = strrchr(orig, '/'); char* oname = strrchr(orig, '/');
if (oname == NULL) return false; // not a proper origin path if (oname == NULL) return false; // not a proper origin path
snprintf(dest, 255, "%s/%s", destdir, (++oname)); snprintf(dest, 255, "%s/%s", destdir, (++oname));
// virtual destination special handling // virtual destination special handling
if (GetVirtualSource(destdir) & ~VRT_BDRI) { if (GetVirtualSource(destdir) & ~VRT_BDRI) {
u64 osize = FileGetSize(orig); u64 osize = FileGetSize(orig);
@ -762,7 +762,7 @@ bool PathCopy(const char* destdir, const char* orig, u32* flags) {
} }
} }
} }
return PathMoveCopy(dest, orig, flags, false); return PathMoveCopy(dest, orig, flags, false);
} }
@ -772,7 +772,7 @@ bool PathMove(const char* destdir, const char* orig, u32* flags) {
char* oname = strrchr(orig, '/'); char* oname = strrchr(orig, '/');
if (oname == NULL) return false; // not a proper origin path if (oname == NULL) return false; // not a proper origin path
snprintf(dest, 255, "%s/%s", destdir, (++oname)); snprintf(dest, 255, "%s/%s", destdir, (++oname));
return PathMoveCopy(dest, orig, flags, true); return PathMoveCopy(dest, orig, flags, true);
} }
@ -784,13 +784,13 @@ bool PathDelete(const char* path) {
bool PathRename(const char* path, const char* newname) { bool PathRename(const char* path, const char* newname) {
char npath[256]; // 256 is the maximum length of a full path char npath[256]; // 256 is the maximum length of a full path
char* oldname = strrchr(path, '/'); char* oldname = strrchr(path, '/');
if (!CheckDirWritePermissions(path)) return false; if (!CheckDirWritePermissions(path)) return false;
if (!oldname) return false; if (!oldname) return false;
oldname++; oldname++;
strncpy(npath, path, oldname - path); strncpy(npath, path, oldname - path);
strncpy(npath + (oldname - path), newname, 255 - (oldname - path)); strncpy(npath + (oldname - path), newname, 255 - (oldname - path));
if (fvx_rename(path, npath) != FR_OK) return false; if (fvx_rename(path, npath) != FR_OK) return false;
if ((strncasecmp(path, npath, 256) != 0) && if ((strncasecmp(path, npath, 256) != 0) &&
((fvx_stat(path, NULL) == FR_OK) || (fvx_stat(npath, NULL) != FR_OK))) ((fvx_stat(path, NULL) == FR_OK) || (fvx_stat(npath, NULL) != FR_OK)))
@ -808,18 +808,18 @@ bool FileSelectorWorker(char* result, const char* text, const char* path, const
char path_local[256]; char path_local[256];
strncpy(path_local, path, 256); strncpy(path_local, path, 256);
path_local[255] = '\0'; path_local[255] = '\0';
bool no_dirs = flags & NO_DIRS; bool no_dirs = flags & NO_DIRS;
bool no_files = flags & NO_FILES; bool no_files = flags & NO_FILES;
bool hide_ext = flags & HIDE_EXT; bool hide_ext = flags & HIDE_EXT;
bool select_dirs = flags & SELECT_DIRS; bool select_dirs = flags & SELECT_DIRS;
// main loop // main loop
while (true) { while (true) {
u32 n_found = 0; u32 n_found = 0;
u32 pos = 0; u32 pos = 0;
GetDirContents(contents, path_local); GetDirContents(contents, path_local);
while (pos < contents->n_entries) { while (pos < contents->n_entries) {
char opt_names[_MAX_FS_OPT+1][32+1]; char opt_names[_MAX_FS_OPT+1][32+1];
DirEntry* res_entry[MAX_DIR_ENTRIES+1] = { NULL }; DirEntry* res_entry[MAX_DIR_ENTRIES+1] = { NULL };
@ -834,7 +834,7 @@ bool FileSelectorWorker(char* result, const char* text, const char* path, const
snprintf(opt_names[n_opt++], 32, "[more...]"); snprintf(opt_names[n_opt++], 32, "[more...]");
break; break;
} }
if (!new_style) { if (!new_style) {
char temp_str[256]; char temp_str[256];
snprintf(temp_str, 256, "%s", entry->name); snprintf(temp_str, 256, "%s", entry->name);
@ -850,7 +850,7 @@ bool FileSelectorWorker(char* result, const char* text, const char* path, const
if ((pos >= contents->n_entries) && (n_opt < n_found) && !new_style) if ((pos >= contents->n_entries) && (n_opt < n_found) && !new_style)
snprintf(opt_names[n_opt++], 32, "[more...]"); snprintf(opt_names[n_opt++], 32, "[more...]");
if (!n_opt) break; if (!n_opt) break;
const char* optionstr[_MAX_FS_OPT+1] = { NULL }; const char* optionstr[_MAX_FS_OPT+1] = { NULL };
for (u32 i = 0; i <= _MAX_FS_OPT; i++) optionstr[i] = opt_names[i]; for (u32 i = 0; i <= _MAX_FS_OPT; i++) optionstr[i] = opt_names[i];
u32 user_select = new_style ? ShowFileScrollPrompt(n_opt, (const DirEntry**)res_entry, hide_ext, "%s", text) u32 user_select = new_style ? ShowFileScrollPrompt(n_opt, (const DirEntry**)res_entry, hide_ext, "%s", text)
@ -874,7 +874,7 @@ bool FileSelectorWorker(char* result, const char* text, const char* path, const
char pathstr[32+1]; char pathstr[32+1];
TruncateString(pathstr, path_local, 32, 8); TruncateString(pathstr, path_local, 32, 8);
ShowPrompt(false, "%s\nNo usable entries found.", pathstr); ShowPrompt(false, "%s\nNo usable entries found.", pathstr);
return false; return false;
} }
} }
} }
@ -882,7 +882,7 @@ bool FileSelectorWorker(char* result, const char* text, const char* path, const
bool FileSelector(char* result, const char* text, const char* path, const char* pattern, u32 flags, bool new_style) { bool FileSelector(char* result, const char* text, const char* path, const char* pattern, u32 flags, bool new_style) {
void* buffer = (void*) malloc(sizeof(DirStruct)); void* buffer = (void*) malloc(sizeof(DirStruct));
if (!buffer) return false; if (!buffer) return false;
bool ret = FileSelectorWorker(result, text, path, pattern, flags, buffer, new_style); bool ret = FileSelectorWorker(result, text, path, pattern, flags, buffer, new_style);
free(buffer); free(buffer);
return ret; return ret;

View File

@ -17,7 +17,7 @@ int ReadImageBytes(void* buffer, u64 offset, u64 count) {
if (!mount_state) return FR_INVALID_OBJECT; if (!mount_state) return FR_INVALID_OBJECT;
if (fvx_tell(&mount_file) != offset) { if (fvx_tell(&mount_file) != offset) {
if (fvx_size(&mount_file) < offset) return -1; if (fvx_size(&mount_file) < offset) return -1;
fvx_lseek(&mount_file, offset); fvx_lseek(&mount_file, offset);
} }
ret = fvx_read(&mount_file, buffer, count, &bytes_read); ret = fvx_read(&mount_file, buffer, count, &bytes_read);
return (ret != 0) ? (int) ret : (bytes_read != count) ? -1 : 0; return (ret != 0) ? (int) ret : (bytes_read != count) ? -1 : 0;

View File

@ -42,7 +42,7 @@ void dealias_path (TCHAR* alias, const TCHAR* path) {
FilCryptInfo* fx_find_cryptinfo(FIL* fptr) { FilCryptInfo* fx_find_cryptinfo(FIL* fptr) {
FilCryptInfo* info = NULL; FilCryptInfo* info = NULL;
for (u32 i = 0; i < NUM_FILCRYPTINFO; i++) { for (u32 i = 0; i < NUM_FILCRYPTINFO; i++) {
if (!info && !filcrypt[i].fptr) // use first free if (!info && !filcrypt[i].fptr) // use first free
info = &filcrypt[i]; info = &filcrypt[i];
@ -51,7 +51,7 @@ FilCryptInfo* fx_find_cryptinfo(FIL* fptr) {
break; break;
} }
} }
return info; return info;
} }
@ -59,27 +59,27 @@ FRESULT fx_decrypt_dsiware (FIL* fp, void* buff, FSIZE_t ofs, UINT len) {
const u32 mode = AES_CNT_TITLEKEY_DECRYPT_MODE; const u32 mode = AES_CNT_TITLEKEY_DECRYPT_MODE;
const u32 num_tbl = sizeof(TadContentTable) / sizeof(u32); const u32 num_tbl = sizeof(TadContentTable) / sizeof(u32);
const FSIZE_t ofs0 = f_tell(fp); const FSIZE_t ofs0 = f_tell(fp);
u8 __attribute__((aligned(16))) iv[AES_BLOCK_SIZE]; u8 __attribute__((aligned(16))) iv[AES_BLOCK_SIZE];
u32 tbl[num_tbl]; u32 tbl[num_tbl];
u8 hdr[TAD_HEADER_LEN]; u8 hdr[TAD_HEADER_LEN];
FRESULT res; FRESULT res;
UINT br; UINT br;
// read and decrypt header // read and decrypt header
if ((res = f_lseek(fp, TAD_HEADER_OFFSET)) != FR_OK) return res; if ((res = f_lseek(fp, TAD_HEADER_OFFSET)) != FR_OK) return res;
if ((res = f_read(fp, hdr, TAD_HEADER_LEN, &br)) != FR_OK) return res; if ((res = f_read(fp, hdr, TAD_HEADER_LEN, &br)) != FR_OK) return res;
if (br != TAD_HEADER_LEN) return FR_DENIED; if (br != TAD_HEADER_LEN) return FR_DENIED;
memcpy(iv, hdr + TAD_HEADER_LEN - AES_BLOCK_SIZE, AES_BLOCK_SIZE); memcpy(iv, hdr + TAD_HEADER_LEN - AES_BLOCK_SIZE, AES_BLOCK_SIZE);
cbc_decrypt(hdr, hdr, sizeof(TadHeader) / AES_BLOCK_SIZE, mode, iv); cbc_decrypt(hdr, hdr, sizeof(TadHeader) / AES_BLOCK_SIZE, mode, iv);
// setup the table // setup the table
if (BuildTadContentTable(tbl, hdr) != 0) return FR_DENIED; if (BuildTadContentTable(tbl, hdr) != 0) return FR_DENIED;
if (tbl[num_tbl-1] > f_size(fp)) return FR_DENIED; // obviously missing data if (tbl[num_tbl-1] > f_size(fp)) return FR_DENIED; // obviously missing data
// process sections // process sections
u32 sct_start = 0; u32 sct_start = 0;
u32 sct_end = 0; u32 sct_end = 0;
@ -87,21 +87,21 @@ FRESULT fx_decrypt_dsiware (FIL* fp, void* buff, FSIZE_t ofs, UINT len) {
sct_end = tbl[i]; sct_end = tbl[i];
if (sct_start == sct_end) continue; // nothing in section if (sct_start == sct_end) continue; // nothing in section
if ((ofs + len <= sct_start) || (ofs >= sct_end)) continue; // section not in data if ((ofs + len <= sct_start) || (ofs >= sct_end)) continue; // section not in data
const u32 crypt_end = sct_end - (AES_BLOCK_SIZE * 2); const u32 crypt_end = sct_end - (AES_BLOCK_SIZE * 2);
const u32 data_end = min(crypt_end, ofs + len); const u32 data_end = min(crypt_end, ofs + len);
u32 data_pos = max(ofs, sct_start); u32 data_pos = max(ofs, sct_start);
if (ofs >= crypt_end) continue; // nothing to do if (ofs >= crypt_end) continue; // nothing to do
if ((sct_start < ofs) || (sct_end > ofs + len)) { // incomplete section, ugh if ((sct_start < ofs) || (sct_end > ofs + len)) { // incomplete section, ugh
u8 __attribute__((aligned(16))) block[AES_BLOCK_SIZE]; u8 __attribute__((aligned(16))) block[AES_BLOCK_SIZE];
// load iv0 // load iv0
FSIZE_t block0_ofs = data_pos - (data_pos % AES_BLOCK_SIZE); FSIZE_t block0_ofs = data_pos - (data_pos % AES_BLOCK_SIZE);
FSIZE_t iv0_ofs = ((block0_ofs > sct_start) ? block0_ofs : sct_end) - AES_BLOCK_SIZE; FSIZE_t iv0_ofs = ((block0_ofs > sct_start) ? block0_ofs : sct_end) - AES_BLOCK_SIZE;
if ((res = f_lseek(fp, iv0_ofs)) != FR_OK) return res; if ((res = f_lseek(fp, iv0_ofs)) != FR_OK) return res;
if ((res = f_read(fp, iv, AES_BLOCK_SIZE, &br)) != FR_OK) return res; if ((res = f_read(fp, iv, AES_BLOCK_SIZE, &br)) != FR_OK) return res;
// load and decrypt block0 (if misaligned) // load and decrypt block0 (if misaligned)
if (data_pos % AES_BLOCK_SIZE) { if (data_pos % AES_BLOCK_SIZE) {
if ((res = f_lseek(fp, block0_ofs)) != FR_OK) return res; if ((res = f_lseek(fp, block0_ofs)) != FR_OK) return res;
@ -110,7 +110,7 @@ FRESULT fx_decrypt_dsiware (FIL* fp, void* buff, FSIZE_t ofs, UINT len) {
data_pos = min(block0_ofs + AES_BLOCK_SIZE, data_end); data_pos = min(block0_ofs + AES_BLOCK_SIZE, data_end);
memcpy(buff, block + (ofs - block0_ofs), data_pos - ofs); memcpy(buff, block + (ofs - block0_ofs), data_pos - ofs);
} }
// decrypt blocks in between // decrypt blocks in between
u32 num_blocks = (data_end - data_pos) / AES_BLOCK_SIZE; u32 num_blocks = (data_end - data_pos) / AES_BLOCK_SIZE;
if (num_blocks) { if (num_blocks) {
@ -118,7 +118,7 @@ FRESULT fx_decrypt_dsiware (FIL* fp, void* buff, FSIZE_t ofs, UINT len) {
cbc_decrypt(blocks, blocks, num_blocks, mode, iv); cbc_decrypt(blocks, blocks, num_blocks, mode, iv);
data_pos += num_blocks * AES_BLOCK_SIZE; data_pos += num_blocks * AES_BLOCK_SIZE;
} }
// decrypt last block // decrypt last block
if (data_pos < data_end) { if (data_pos < data_end) {
u8* lbuff = (u8*) buff + (data_pos - ofs); u8* lbuff = (u8*) buff + (data_pos - ofs);
@ -137,7 +137,7 @@ FRESULT fx_decrypt_dsiware (FIL* fp, void* buff, FSIZE_t ofs, UINT len) {
cbc_decrypt(blocks, blocks, num_blocks, mode, iv); cbc_decrypt(blocks, blocks, num_blocks, mode, iv);
} }
} }
return f_lseek(fp, ofs0); return f_lseek(fp, ofs0);
} }
@ -145,7 +145,7 @@ FRESULT fx_open (FIL* fp, const TCHAR* path, BYTE mode) {
int num = alias_num(path); int num = alias_num(path);
FilCryptInfo* info = fx_find_cryptinfo(fp); FilCryptInfo* info = fx_find_cryptinfo(fp);
if (info) info->fptr = NULL; if (info) info->fptr = NULL;
if (info && (num >= 0)) { if (info && (num >= 0)) {
// DSIWare Export, mark with the magic number // DSIWare Export, mark with the magic number
if (strncmp(path + 2, "/" DSIWARE_MAGIC, 1 + 16) == 0) { if (strncmp(path + 2, "/" DSIWARE_MAGIC, 1 + 16) == 0) {
@ -172,7 +172,7 @@ FRESULT fx_open (FIL* fp, const TCHAR* path, BYTE mode) {
memcpy(info->keyy, sd_keyy[num], 16); memcpy(info->keyy, sd_keyy[num], 16);
info->fptr = fp; info->fptr = fp;
} }
return fa_open(fp, path, mode); return fa_open(fp, path, mode);
} }
@ -193,12 +193,12 @@ FRESULT fx_write (FIL* fp, const void* buff, UINT btw, UINT* bw) {
FilCryptInfo* info = fx_find_cryptinfo(fp); FilCryptInfo* info = fx_find_cryptinfo(fp);
FSIZE_t off = f_tell(fp); FSIZE_t off = f_tell(fp);
FRESULT res = FR_OK; FRESULT res = FR_OK;
if (info && info->fptr) { if (info && info->fptr) {
if (memcmp(info->ctr, DSIWARE_MAGIC, 16) == 0) return FR_DENIED; if (memcmp(info->ctr, DSIWARE_MAGIC, 16) == 0) return FR_DENIED;
void* crypt_buff = (void*) malloc(min(btw, STD_BUFFER_SIZE)); void* crypt_buff = (void*) malloc(min(btw, STD_BUFFER_SIZE));
if (!crypt_buff) return FR_DENIED; if (!crypt_buff) return FR_DENIED;
setup_aeskeyY(0x34, info->keyy); setup_aeskeyY(0x34, info->keyy);
use_aeskey(0x34); use_aeskey(0x34);
*bw = 0; *bw = 0;
@ -210,7 +210,7 @@ FRESULT fx_write (FIL* fp, const void* buff, UINT btw, UINT* bw) {
res = f_write(fp, (const void*) crypt_buff, pcount, &bwl); res = f_write(fp, (const void*) crypt_buff, pcount, &bwl);
*bw += bwl; *bw += bwl;
} }
free(crypt_buff); free(crypt_buff);
} else res = f_write(fp, buff, btw, bw); } else res = f_write(fp, buff, btw, bw);
return res; return res;
@ -255,12 +255,12 @@ FRESULT fa_unlink (const TCHAR* path) {
// special functions for access of virtual NAND SD drives // special functions for access of virtual NAND SD drives
bool SetupNandSdDrive(const char* path, const char* sd_path, const char* movable, int num) { bool SetupNandSdDrive(const char* path, const char* sd_path, const char* movable, int num) {
char alias[128]; char alias[128];
// initial checks // initial checks
if ((num >= NUM_ALIAS_DRV) || (num < 0)) return false; if ((num >= NUM_ALIAS_DRV) || (num < 0)) return false;
alias_drv[num] = 0; alias_drv[num] = 0;
if (!sd_path || !movable || !path) return true; if (!sd_path || !movable || !path) return true;
// grab the key Y from movable.sed // grab the key Y from movable.sed
UINT bytes_read = 0; UINT bytes_read = 0;
FIL file; FIL file;
@ -272,13 +272,13 @@ bool SetupNandSdDrive(const char* path, const char* sd_path, const char* movable
return false; return false;
} }
f_close(&file); f_close(&file);
// build the alias path (id0) // build the alias path (id0)
u32 sha256sum[8]; u32 sha256sum[8];
sha_quick(sha256sum, sd_keyy[num], 0x10, SHA256_MODE); sha_quick(sha256sum, sd_keyy[num], 0x10, SHA256_MODE);
snprintf(alias, 127, "%s/Nintendo 3DS/%08lX%08lX%08lX%08lX", snprintf(alias, 127, "%s/Nintendo 3DS/%08lX%08lX%08lX%08lX",
sd_path, sha256sum[0], sha256sum[1], sha256sum[2], sha256sum[3]); sd_path, sha256sum[0], sha256sum[1], sha256sum[2], sha256sum[3]);
// find the alias path (id1) // find the alias path (id1)
char* id1 = alias + strnlen(alias, 127); char* id1 = alias + strnlen(alias, 127);
DIR pdir; DIR pdir;
@ -297,7 +297,7 @@ bool SetupNandSdDrive(const char* path, const char* sd_path, const char* movable
} }
f_closedir(&pdir); f_closedir(&pdir);
if (!(*id1)) return false; if (!(*id1)) return false;
// create the alias drive // create the alias drive
return SetupAliasDrive(path, alias, num); return SetupAliasDrive(path, alias, num);
} }
@ -307,12 +307,12 @@ bool SetupAliasDrive(const char* path, const char* alias, int num) {
if ((num >= NUM_ALIAS_DRV) || (num < 0)) return false; if ((num >= NUM_ALIAS_DRV) || (num < 0)) return false;
alias_drv[num] = 0; alias_drv[num] = 0;
if (!alias || !path) return true; if (!alias || !path) return true;
// take over drive path and alias // take over drive path and alias
strncpy(alias_path[num], alias, 128); strncpy(alias_path[num], alias, 128);
if (path[1] != ':') return false; if (path[1] != ':') return false;
alias_drv[num] = path[0]; alias_drv[num] = path[0];
return true; return true;
} }

View File

@ -12,7 +12,7 @@ bool CheckSupportFile(const char* fname)
// try VRAM0 first // try VRAM0 first
if (FindVTarFileInfo(fname, NULL)) if (FindVTarFileInfo(fname, NULL))
return true; return true;
// try support file paths // try support file paths
const char* base_paths[] = { SUPPORT_FILE_PATHS }; const char* base_paths[] = { SUPPORT_FILE_PATHS };
for (u32 i = 0; i < countof(base_paths); i++) { for (u32 i = 0; i < countof(base_paths); i++) {
@ -21,7 +21,7 @@ bool CheckSupportFile(const char* fname)
if (fvx_stat(path, NULL) == FR_OK) if (fvx_stat(path, NULL) == FR_OK)
return true; return true;
} }
return false; return false;
} }
@ -34,7 +34,7 @@ size_t LoadSupportFile(const char* fname, void* buffer, size_t max_len)
memcpy(buffer, data, len64); memcpy(buffer, data, len64);
return (size_t) len64; return (size_t) len64;
} }
// try support file paths // try support file paths
const char* base_paths[] = { SUPPORT_FILE_PATHS }; const char* base_paths[] = { SUPPORT_FILE_PATHS };
for (u32 i = 0; i < countof(base_paths); i++) { for (u32 i = 0; i < countof(base_paths); i++) {
@ -44,7 +44,7 @@ size_t LoadSupportFile(const char* fname, void* buffer, size_t max_len)
if (fvx_qread(path, buffer, 0, max_len, &len32) == FR_OK) if (fvx_qread(path, buffer, 0, max_len, &len32) == FR_OK)
return len32; return len32;
} }
return 0; return 0;
} }
@ -73,7 +73,7 @@ bool SaveSupportFile(const char* fname, void* buffer, size_t len)
if (fvx_qwrite(path, buffer, 0, len, NULL) == FR_OK) if (fvx_qwrite(path, buffer, 0, len, NULL) == FR_OK)
return true; return true;
} }
return false; return false;
} }
@ -102,7 +102,7 @@ bool GetSupportDir(char* path, const char* dname)
if ((fvx_stat(path, &fno) == FR_OK) && (fno.fattrib & AM_DIR)) if ((fvx_stat(path, &fno) == FR_OK) && (fno.fattrib & AM_DIR))
return true; return true;
} }
return false; return false;
} }

View File

@ -151,22 +151,22 @@ FRESULT fvx_qread (const TCHAR* path, void* buff, FSIZE_t ofs, UINT btr, UINT* b
FIL fp; FIL fp;
FRESULT res; FRESULT res;
UINT brt = 0; UINT brt = 0;
res = fvx_open(&fp, path, FA_READ | FA_OPEN_EXISTING); res = fvx_open(&fp, path, FA_READ | FA_OPEN_EXISTING);
if (res != FR_OK) return res; if (res != FR_OK) return res;
res = fvx_lseek(&fp, ofs); res = fvx_lseek(&fp, ofs);
if (res != FR_OK) { if (res != FR_OK) {
fvx_close(&fp); fvx_close(&fp);
return res; return res;
} }
res = fvx_read(&fp, buff, btr, &brt); res = fvx_read(&fp, buff, btr, &brt);
fvx_close(&fp); fvx_close(&fp);
if (br) *br = brt; if (br) *br = brt;
else if ((res == FR_OK) && (brt != btr)) res = FR_DENIED; else if ((res == FR_OK) && (brt != btr)) res = FR_DENIED;
return res; return res;
} }
@ -174,29 +174,29 @@ FRESULT fvx_qwrite (const TCHAR* path, const void* buff, FSIZE_t ofs, UINT btw,
FIL fp; FIL fp;
FRESULT res; FRESULT res;
UINT bwt = 0; UINT bwt = 0;
res = fvx_open(&fp, path, FA_WRITE | FA_OPEN_ALWAYS); res = fvx_open(&fp, path, FA_WRITE | FA_OPEN_ALWAYS);
if (res != FR_OK) return res; if (res != FR_OK) return res;
res = fvx_lseek(&fp, ofs); res = fvx_lseek(&fp, ofs);
if (res != FR_OK) { if (res != FR_OK) {
fvx_close(&fp); fvx_close(&fp);
return res; return res;
} }
res = fvx_write(&fp, buff, btw, &bwt); res = fvx_write(&fp, buff, btw, &bwt);
fvx_close(&fp); fvx_close(&fp);
if (bw) *bw = bwt; if (bw) *bw = bwt;
else if ((res == FR_OK) && (bwt != btw)) res = FR_DENIED; else if ((res == FR_OK) && (bwt != btw)) res = FR_DENIED;
return res; return res;
} }
FRESULT fvx_qcreate (const TCHAR* path, UINT btc) { FRESULT fvx_qcreate (const TCHAR* path, UINT btc) {
FIL fp; FIL fp;
FRESULT res; FRESULT res;
res = fvx_open(&fp, path, FA_WRITE | FA_CREATE_ALWAYS); res = fvx_open(&fp, path, FA_WRITE | FA_CREATE_ALWAYS);
if (res != FR_OK) return res; if (res != FR_OK) return res;
@ -212,7 +212,7 @@ FRESULT fvx_qfill (const TCHAR* path, const void* buff, UINT btb) {
FRESULT res; FRESULT res;
UINT bwtt = 0; UINT bwtt = 0;
UINT fsiz = 0; UINT fsiz = 0;
res = fvx_open(&fp, path, FA_WRITE | FA_OPEN_EXISTING); res = fvx_open(&fp, path, FA_WRITE | FA_OPEN_EXISTING);
if (res != FR_OK) return res; if (res != FR_OK) return res;
@ -281,17 +281,17 @@ FRESULT fvx_rmkpath (const TCHAR* path) {
FRESULT worker_fvx_runlink (TCHAR* tpath) { FRESULT worker_fvx_runlink (TCHAR* tpath) {
FILINFO fno; FILINFO fno;
FRESULT res; FRESULT res;
// this code handles directory content deletion // this code handles directory content deletion
if ((res = fvx_stat(tpath, &fno)) != FR_OK) return res; // tpath does not exist if ((res = fvx_stat(tpath, &fno)) != FR_OK) return res; // tpath does not exist
if (fno.fattrib & AM_DIR) { // process folder contents if (fno.fattrib & AM_DIR) { // process folder contents
DIR pdir; DIR pdir;
TCHAR* fname = tpath + strnlen(tpath, 255); TCHAR* fname = tpath + strnlen(tpath, 255);
if ((res = fa_opendir(&pdir, tpath)) != FR_OK) return res; if ((res = fa_opendir(&pdir, tpath)) != FR_OK) return res;
*(fname++) = '/'; *(fname++) = '/';
while (fvx_readdir(&pdir, &fno) == FR_OK) { while (fvx_readdir(&pdir, &fno) == FR_OK) {
if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0)) if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0))
continue; // filter out virtual entries continue; // filter out virtual entries
@ -305,7 +305,7 @@ FRESULT worker_fvx_runlink (TCHAR* tpath) {
fvx_closedir(&pdir); fvx_closedir(&pdir);
*(--fname) = '\0'; *(--fname) = '\0';
} }
return fvx_unlink( tpath ); return fvx_unlink( tpath );
} }
#endif #endif
@ -344,7 +344,7 @@ FRESULT fvx_match_name(const TCHAR* path, const TCHAR* pattern) {
if (fvx_match_name(path, pattern + 1) == FR_OK) return FR_OK; if (fvx_match_name(path, pattern + 1) == FR_OK) return FR_OK;
} }
} }
return FR_NO_FILE; return FR_NO_FILE;
} }
@ -360,19 +360,19 @@ FRESULT fvx_findpath (TCHAR* path, const TCHAR* pattern, BYTE mode) {
TCHAR* fname = strrchr(path, '/'); TCHAR* fname = strrchr(path, '/');
if (!fname) return FR_DENIED; if (!fname) return FR_DENIED;
*fname = '\0'; *fname = '\0';
TCHAR* npattern = strrchr(pattern, '/'); TCHAR* npattern = strrchr(pattern, '/');
if (!npattern) return FR_DENIED; if (!npattern) return FR_DENIED;
npattern++; npattern++;
DIR pdir; DIR pdir;
FILINFO fno; FILINFO fno;
FRESULT res; FRESULT res;
if ((res = fvx_opendir(&pdir, path)) != FR_OK) return res; if ((res = fvx_opendir(&pdir, path)) != FR_OK) return res;
*(fname++) = '/'; *(fname++) = '/';
*fname = '\0'; *fname = '\0';
while ((fvx_preaddir(&pdir, &fno, npattern) == FR_OK) && *(fno.fname)) { while ((fvx_preaddir(&pdir, &fno, npattern) == FR_OK) && *(fno.fname)) {
int cmp = strncmp(fno.fname, fname, _MAX_FN_LEN); int cmp = strncmp(fno.fname, fname, _MAX_FN_LEN);
if (((mode & FN_HIGHEST) && (cmp > 0)) || ((mode & FN_LOWEST) && (cmp < 0)) || !(*fname)) if (((mode & FN_HIGHEST) && (cmp > 0)) || ((mode & FN_LOWEST) && (cmp < 0)) || !(*fname))
@ -380,7 +380,7 @@ FRESULT fvx_findpath (TCHAR* path, const TCHAR* pattern, BYTE mode) {
if (!(mode & (FN_HIGHEST|FN_LOWEST))) break; if (!(mode & (FN_HIGHEST|FN_LOWEST))) break;
} }
fvx_closedir( &pdir ); fvx_closedir( &pdir );
return (*fname) ? FR_OK : FR_NO_PATH; return (*fname) ? FR_OK : FR_NO_PATH;
} }
@ -389,7 +389,7 @@ FRESULT fvx_findnopath (TCHAR* path, const TCHAR* pattern) {
TCHAR* fname = strrchr(path, '/'); TCHAR* fname = strrchr(path, '/');
if (!fname) return FR_DENIED; if (!fname) return FR_DENIED;
fname++; fname++;
TCHAR* rep[16]; TCHAR* rep[16];
u32 n_rep = 0; u32 n_rep = 0;
for (u32 i = 0; fname[i]; i++) { for (u32 i = 0; fname[i]; i++) {
@ -400,7 +400,7 @@ FRESULT fvx_findnopath (TCHAR* path, const TCHAR* pattern) {
if (n_rep >= 16) return FR_DENIED; if (n_rep >= 16) return FR_DENIED;
} }
if (!n_rep) return (fvx_stat(path, NULL) == FR_OK) ? FR_NO_PATH : FR_OK; if (!n_rep) return (fvx_stat(path, NULL) == FR_OK) ? FR_NO_PATH : FR_OK;
while (fvx_stat(path, NULL) == FR_OK) { while (fvx_stat(path, NULL) == FR_OK) {
for (int i = n_rep - 1; (i >= 0); i--) { for (int i = n_rep - 1; (i >= 0); i--) {
if (*(rep[i]) == '9') { if (*(rep[i]) == '9') {
@ -412,7 +412,7 @@ FRESULT fvx_findnopath (TCHAR* path, const TCHAR* pattern) {
} }
} }
} }
return FR_OK; return FR_OK;
} }

View File

@ -105,8 +105,8 @@ static FRESULT BDRIWrite(UINT ofs, UINT btw, const void* buf) {
bool CheckDBMagic(const u8* pre_header, bool tickdb) { bool CheckDBMagic(const u8* pre_header, bool tickdb) {
const TitleDBPreHeader* title = (TitleDBPreHeader*) pre_header; const TitleDBPreHeader* title = (TitleDBPreHeader*) pre_header;
const TickDBPreHeader* tick = (TickDBPreHeader*) pre_header; const TickDBPreHeader* tick = (TickDBPreHeader*) pre_header;
return (tickdb ? ((strncmp(tick->magic, "TICK", 4) == 0) && (tick->unknown1 == 1)) : return (tickdb ? ((strncmp(tick->magic, "TICK", 4) == 0) && (tick->unknown1 == 1)) :
((strcmp(title->magic, "NANDIDB") == 0) || (strcmp(title->magic, "NANDTDB") == 0) || ((strcmp(title->magic, "NANDIDB") == 0) || (strcmp(title->magic, "NANDTDB") == 0) ||
(strcmp(title->magic, "TEMPIDB") == 0) || (strcmp(title->magic, "TEMPTDB") == 0))) && (strcmp(title->magic, "TEMPIDB") == 0) || (strcmp(title->magic, "TEMPTDB") == 0))) &&
(strcmp((tickdb ? tick->fs_header : title->fs_header).magic, "BDRI") == 0) && (strcmp((tickdb ? tick->fs_header : title->fs_header).magic, "BDRI") == 0) &&
@ -133,13 +133,13 @@ static u32 GetBDRIEntrySize(const BDRIFsHeader* fs_header, const u32 fs_header_o
const u32 data_offset = fs_header_offset + fs_header->data_offset; const u32 data_offset = fs_header_offset + fs_header->data_offset;
const u32 fet_offset = data_offset + fs_header->fet_start_block * fs_header->data_block_size; const u32 fet_offset = data_offset + fs_header->fet_start_block * fs_header->data_block_size;
const u32 fht_offset = fs_header_offset + fs_header->fht_offset; const u32 fht_offset = fs_header_offset + fs_header->fht_offset;
u32 index = 0; u32 index = 0;
TdbFileEntry file_entry; TdbFileEntry file_entry;
u64 tid_be = getbe64(title_id); u64 tid_be = getbe64(title_id);
u8* title_id_be = (u8*) &tid_be; u8* title_id_be = (u8*) &tid_be;
const u32 hash_bucket = GetHashBucket(title_id_be, 1, fs_header->fht_bucket_count); const u32 hash_bucket = GetHashBucket(title_id_be, 1, fs_header->fht_bucket_count);
if (BDRIRead(fht_offset + hash_bucket * sizeof(u32), sizeof(u32), &(file_entry.hash_bucket_next_index)) != FR_OK) if (BDRIRead(fht_offset + hash_bucket * sizeof(u32), sizeof(u32), &(file_entry.hash_bucket_next_index)) != FR_OK)
return 1; return 1;
@ -147,13 +147,13 @@ static u32 GetBDRIEntrySize(const BDRIFsHeader* fs_header, const u32 fs_header_o
do { do {
if (file_entry.hash_bucket_next_index == 0) if (file_entry.hash_bucket_next_index == 0)
return 1; return 1;
index = file_entry.hash_bucket_next_index; index = file_entry.hash_bucket_next_index;
if (BDRIRead(fet_offset + index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &file_entry) != FR_OK) if (BDRIRead(fet_offset + index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &file_entry) != FR_OK)
return 1; return 1;
} while (memcmp(title_id_be, file_entry.title_id, 8) != 0); } while (memcmp(title_id_be, file_entry.title_id, 8) != 0);
*size = file_entry.size; *size = file_entry.size;
return 0; return 0;
@ -162,18 +162,18 @@ static u32 GetBDRIEntrySize(const BDRIFsHeader* fs_header, const u32 fs_header_o
static u32 ReadBDRIEntry(const BDRIFsHeader* fs_header, const u32 fs_header_offset, const u8* title_id, u8* entry, const u32 expected_size) { static u32 ReadBDRIEntry(const BDRIFsHeader* fs_header, const u32 fs_header_offset, const u8* title_id, u8* entry, const u32 expected_size) {
if ((fs_header->info_offset != 0x20) || (fs_header->fat_entry_count != fs_header->data_block_count)) // Could be more thorough if ((fs_header->info_offset != 0x20) || (fs_header->fat_entry_count != fs_header->data_block_count)) // Could be more thorough
return 1; return 1;
const u32 data_offset = fs_header_offset + fs_header->data_offset; const u32 data_offset = fs_header_offset + fs_header->data_offset;
const u32 fet_offset = data_offset + fs_header->fet_start_block * fs_header->data_block_size; const u32 fet_offset = data_offset + fs_header->fet_start_block * fs_header->data_block_size;
const u32 fht_offset = fs_header_offset + fs_header->fht_offset; const u32 fht_offset = fs_header_offset + fs_header->fht_offset;
const u32 fat_offset = fs_header_offset + fs_header->fat_offset; const u32 fat_offset = fs_header_offset + fs_header->fat_offset;
u32 index = 0; u32 index = 0;
TdbFileEntry file_entry; TdbFileEntry file_entry;
u64 tid_be = getbe64(title_id); u64 tid_be = getbe64(title_id);
u8* title_id_be = (u8*) &tid_be; u8* title_id_be = (u8*) &tid_be;
const u32 hash_bucket = GetHashBucket(title_id_be, 1, fs_header->fht_bucket_count); const u32 hash_bucket = GetHashBucket(title_id_be, 1, fs_header->fht_bucket_count);
if (BDRIRead(fht_offset + hash_bucket * sizeof(u32), sizeof(u32), &(file_entry.hash_bucket_next_index)) != FR_OK) if (BDRIRead(fht_offset + hash_bucket * sizeof(u32), sizeof(u32), &(file_entry.hash_bucket_next_index)) != FR_OK)
return 1; return 1;
@ -181,108 +181,108 @@ static u32 ReadBDRIEntry(const BDRIFsHeader* fs_header, const u32 fs_header_offs
do { do {
if (file_entry.hash_bucket_next_index == 0) if (file_entry.hash_bucket_next_index == 0)
return 1; return 1;
index = file_entry.hash_bucket_next_index; index = file_entry.hash_bucket_next_index;
if (BDRIRead(fet_offset + index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &file_entry) != FR_OK) if (BDRIRead(fet_offset + index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &file_entry) != FR_OK)
return 1; return 1;
} while (memcmp(title_id_be, file_entry.title_id, 8) != 0); } while (memcmp(title_id_be, file_entry.title_id, 8) != 0);
if (expected_size && (file_entry.size != expected_size)) if (expected_size && (file_entry.size != expected_size))
return 1; return 1;
index = file_entry.start_block_index + 1; // FAT entry index index = file_entry.start_block_index + 1; // FAT entry index
u32 bytes_read = 0; u32 bytes_read = 0;
u32 fat_entry[2]; u32 fat_entry[2];
while (bytes_read < file_entry.size) { // Read the full entry, walking the FAT node chain while (bytes_read < file_entry.size) { // Read the full entry, walking the FAT node chain
u32 read_start = index - 1; // Data region block index u32 read_start = index - 1; // Data region block index
u32 read_count = 0; u32 read_count = 0;
if (BDRIRead(fat_offset + index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK) if (BDRIRead(fat_offset + index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK)
return 1; return 1;
if ((bytes_read == 0) && !getfatflag(fat_entry[0])) if ((bytes_read == 0) && !getfatflag(fat_entry[0]))
return 1; return 1;
u32 next_index = getfatindex(fat_entry[1]); u32 next_index = getfatindex(fat_entry[1]);
if (getfatflag(fat_entry[1])) { // Multi-entry node if (getfatflag(fat_entry[1])) { // Multi-entry node
if (BDRIRead(fat_offset + (index + 1) * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK) if (BDRIRead(fat_offset + (index + 1) * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK)
return 1; return 1;
if (!getfatflag(fat_entry[0]) || getfatflag(fat_entry[1]) || (getfatindex(fat_entry[0]) != index) || (getfatindex(fat_entry[0]) >= getfatindex(fat_entry[1]))) if (!getfatflag(fat_entry[0]) || getfatflag(fat_entry[1]) || (getfatindex(fat_entry[0]) != index) || (getfatindex(fat_entry[0]) >= getfatindex(fat_entry[1])))
return 1; return 1;
read_count = getfatindex(fat_entry[1]) + 1 - index; read_count = getfatindex(fat_entry[1]) + 1 - index;
} else { // Single-entry node } else { // Single-entry node
read_count = 1; read_count = 1;
} }
index = next_index; index = next_index;
u32 btr = min(file_entry.size - bytes_read, read_count * fs_header->data_block_size); u32 btr = min(file_entry.size - bytes_read, read_count * fs_header->data_block_size);
if (entry && (BDRIRead(data_offset + read_start * fs_header->data_block_size, btr, entry + bytes_read) != FR_OK)) if (entry && (BDRIRead(data_offset + read_start * fs_header->data_block_size, btr, entry + bytes_read) != FR_OK))
return 1; return 1;
bytes_read += btr; bytes_read += btr;
} }
return 0; return 0;
} }
static u32 RemoveBDRIEntry(const BDRIFsHeader* fs_header, const u32 fs_header_offset, const u8* title_id) { static u32 RemoveBDRIEntry(const BDRIFsHeader* fs_header, const u32 fs_header_offset, const u8* title_id) {
if ((fs_header->info_offset != 0x20) || (fs_header->fat_entry_count != fs_header->data_block_count)) // Could be more thorough if ((fs_header->info_offset != 0x20) || (fs_header->fat_entry_count != fs_header->data_block_count)) // Could be more thorough
return 1; return 1;
const u32 data_offset = fs_header_offset + fs_header->data_offset; const u32 data_offset = fs_header_offset + fs_header->data_offset;
const u32 det_offset = data_offset + fs_header->det_start_block * fs_header->data_block_size; const u32 det_offset = data_offset + fs_header->det_start_block * fs_header->data_block_size;
const u32 fet_offset = data_offset + fs_header->fet_start_block * fs_header->data_block_size; const u32 fet_offset = data_offset + fs_header->fet_start_block * fs_header->data_block_size;
const u32 fht_offset = fs_header_offset + fs_header->fht_offset; const u32 fht_offset = fs_header_offset + fs_header->fht_offset;
const u32 fat_offset = fs_header_offset + fs_header->fat_offset; const u32 fat_offset = fs_header_offset + fs_header->fat_offset;
u32 index = 0, previous_index = 0; u32 index = 0, previous_index = 0;
TdbFileEntry file_entry; TdbFileEntry file_entry;
u64 tid_be = getbe64(title_id); u64 tid_be = getbe64(title_id);
u8* title_id_be = (u8*) &tid_be; u8* title_id_be = (u8*) &tid_be;
// Read the index of the first file entry from the directory entry table // Read the index of the first file entry from the directory entry table
if (BDRIRead(det_offset + 0x2C, sizeof(u32), &(file_entry.next_sibling_index)) != FR_OK) if (BDRIRead(det_offset + 0x2C, sizeof(u32), &(file_entry.next_sibling_index)) != FR_OK)
return 1; return 1;
// Find the file entry for the tid specified, fail if it doesn't exist // Find the file entry for the tid specified, fail if it doesn't exist
do { do {
previous_index = index; previous_index = index;
index = file_entry.next_sibling_index; index = file_entry.next_sibling_index;
if (index == 0) if (index == 0)
return 1; return 1;
if (BDRIRead(fet_offset + index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &file_entry) != FR_OK) if (BDRIRead(fet_offset + index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &file_entry) != FR_OK)
return 1; return 1;
} while (memcmp(title_id_be, file_entry.title_id, 8) != 0); } while (memcmp(title_id_be, file_entry.title_id, 8) != 0);
DummyFileEntry dummy_entry; DummyFileEntry dummy_entry;
// Read the 0th entry in the FET, which is always a dummy entry // Read the 0th entry in the FET, which is always a dummy entry
if (BDRIRead(fet_offset, sizeof(DummyFileEntry), &dummy_entry) != FR_OK) if (BDRIRead(fet_offset, sizeof(DummyFileEntry), &dummy_entry) != FR_OK)
return 1; return 1;
if (dummy_entry.max_entry_count != fs_header->max_file_count + 1) if (dummy_entry.max_entry_count != fs_header->max_file_count + 1)
return 1; return 1;
if ((BDRIWrite(fet_offset + index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &dummy_entry) != FR_OK) || if ((BDRIWrite(fet_offset + index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &dummy_entry) != FR_OK) ||
(BDRIWrite(fet_offset + 0x28, sizeof(u32), &index) != FR_OK) || (BDRIWrite(fet_offset + 0x28, sizeof(u32), &index) != FR_OK) ||
(BDRIWrite((previous_index == 0) ? det_offset + 0x2C : fet_offset + previous_index * sizeof(TdbFileEntry) + 0xC, sizeof(u32), &(file_entry.next_sibling_index)) != FR_OK)) (BDRIWrite((previous_index == 0) ? det_offset + 0x2C : fet_offset + previous_index * sizeof(TdbFileEntry) + 0xC, sizeof(u32), &(file_entry.next_sibling_index)) != FR_OK))
return 1; return 1;
const u32 hash_bucket = GetHashBucket(file_entry.title_id, file_entry.parent_index, fs_header->fht_bucket_count); const u32 hash_bucket = GetHashBucket(file_entry.title_id, file_entry.parent_index, fs_header->fht_bucket_count);
u32 index_hash = 0; u32 index_hash = 0;
if (BDRIRead(fht_offset + hash_bucket * sizeof(u32), sizeof(u32), &index_hash) != FR_OK) if (BDRIRead(fht_offset + hash_bucket * sizeof(u32), sizeof(u32), &index_hash) != FR_OK)
return 1; return 1;
if (index_hash == index) { if (index_hash == index) {
if (BDRIWrite(fht_offset + hash_bucket * sizeof(u32), sizeof(u32), &(file_entry.hash_bucket_next_index)) != FR_OK) if (BDRIWrite(fht_offset + hash_bucket * sizeof(u32), sizeof(u32), &(file_entry.hash_bucket_next_index)) != FR_OK)
return 1; return 1;
@ -290,83 +290,83 @@ static u32 RemoveBDRIEntry(const BDRIFsHeader* fs_header, const u32 fs_header_of
do { do {
if (index_hash == 0) // This shouldn't happen if the entry was properly added if (index_hash == 0) // This shouldn't happen if the entry was properly added
break; break;
if (BDRIRead(fet_offset + index_hash * sizeof(TdbFileEntry) + 0x28, sizeof(u32), &index_hash) != FR_OK) if (BDRIRead(fet_offset + index_hash * sizeof(TdbFileEntry) + 0x28, sizeof(u32), &index_hash) != FR_OK)
return 1; return 1;
} while (index_hash != index); } while (index_hash != index);
if ((index_hash != 0) && BDRIWrite(fet_offset + index_hash * sizeof(TdbFileEntry) + 0x28, sizeof(u32), &(file_entry.hash_bucket_next_index)) != FR_OK) if ((index_hash != 0) && BDRIWrite(fet_offset + index_hash * sizeof(TdbFileEntry) + 0x28, sizeof(u32), &(file_entry.hash_bucket_next_index)) != FR_OK)
return 1; return 1;
} }
u32 fat_entry[2]; u32 fat_entry[2];
if (BDRIRead(fat_offset, FAT_ENTRY_SIZE, fat_entry) != FR_OK) if (BDRIRead(fat_offset, FAT_ENTRY_SIZE, fat_entry) != FR_OK)
return 1; return 1;
if (getfatflag(fat_entry[1]) || (fat_entry[0] != 0)) if (getfatflag(fat_entry[1]) || (fat_entry[0] != 0))
return 1; return 1;
u32 next_free_index = getfatindex(fat_entry[1]), fat_index = file_entry.start_block_index + 1; u32 next_free_index = getfatindex(fat_entry[1]), fat_index = file_entry.start_block_index + 1;
if (BDRIWrite(fat_offset + sizeof(u32), sizeof(u32), &fat_index) != FR_OK) if (BDRIWrite(fat_offset + sizeof(u32), sizeof(u32), &fat_index) != FR_OK)
return 1; return 1;
fat_entry[1] = fat_index; fat_entry[1] = fat_index;
do { do {
fat_index = getfatindex(fat_entry[1]); fat_index = getfatindex(fat_entry[1]);
if (BDRIRead(fat_offset + FAT_ENTRY_SIZE * fat_index, FAT_ENTRY_SIZE, fat_entry) != FR_OK) if (BDRIRead(fat_offset + FAT_ENTRY_SIZE * fat_index, FAT_ENTRY_SIZE, fat_entry) != FR_OK)
return 1; return 1;
} while (getfatindex(fat_entry[1]) != 0); } while (getfatindex(fat_entry[1]) != 0);
fat_entry[1] |= next_free_index; fat_entry[1] |= next_free_index;
if ((BDRIWrite(fat_offset + fat_index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK) || if ((BDRIWrite(fat_offset + fat_index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK) ||
(BDRIRead(fat_offset + next_free_index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK)) (BDRIRead(fat_offset + next_free_index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK))
return 1; return 1;
fat_entry[0] = buildfatuv(fat_index, false); fat_entry[0] = buildfatuv(fat_index, false);
if (BDRIWrite(fat_offset + next_free_index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK) if (BDRIWrite(fat_offset + next_free_index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK)
return 1; return 1;
return 0; return 0;
} }
static u32 AddBDRIEntry(const BDRIFsHeader* fs_header, const u32 fs_header_offset, const u8* title_id, const u8* entry, const u32 size, bool replace) { static u32 AddBDRIEntry(const BDRIFsHeader* fs_header, const u32 fs_header_offset, const u8* title_id, const u8* entry, const u32 size, bool replace) {
if ((fs_header->info_offset != 0x20) || (fs_header->fat_entry_count != fs_header->data_block_count)) // Could be more thorough if ((fs_header->info_offset != 0x20) || (fs_header->fat_entry_count != fs_header->data_block_count)) // Could be more thorough
return 1; return 1;
if (!entry || !size) if (!entry || !size)
return 1; return 1;
const u32 data_offset = fs_header_offset + fs_header->data_offset; const u32 data_offset = fs_header_offset + fs_header->data_offset;
const u32 det_offset = data_offset + fs_header->det_start_block * fs_header->data_block_size; const u32 det_offset = data_offset + fs_header->det_start_block * fs_header->data_block_size;
const u32 fet_offset = data_offset + fs_header->fet_start_block * fs_header->data_block_size; const u32 fet_offset = data_offset + fs_header->fet_start_block * fs_header->data_block_size;
const u32 fht_offset = fs_header_offset + fs_header->fht_offset; const u32 fht_offset = fs_header_offset + fs_header->fht_offset;
const u32 fat_offset = fs_header_offset + fs_header->fat_offset; const u32 fat_offset = fs_header_offset + fs_header->fat_offset;
const u32 size_blocks = (size / fs_header->data_block_size) + (((size % fs_header->data_block_size) == 0) ? 0 : 1); const u32 size_blocks = (size / fs_header->data_block_size) + (((size % fs_header->data_block_size) == 0) ? 0 : 1);
u32 index = 0, max_index = 0; u32 index = 0, max_index = 0;
TdbFileEntry file_entry; TdbFileEntry file_entry;
u64 tid_be = getbe64(title_id); u64 tid_be = getbe64(title_id);
u8* title_id_be = (u8*) &tid_be; u8* title_id_be = (u8*) &tid_be;
bool do_replace = false; bool do_replace = false;
// Read the index of the first file entry from the directory entry table // Read the index of the first file entry from the directory entry table
if (BDRIRead(det_offset + 0x2C, sizeof(u32), &(file_entry.next_sibling_index)) != FR_OK) if (BDRIRead(det_offset + 0x2C, sizeof(u32), &(file_entry.next_sibling_index)) != FR_OK)
return 1; return 1;
// Try to find the file entry for the tid specified // Try to find the file entry for the tid specified
while (file_entry.next_sibling_index != 0) { while (file_entry.next_sibling_index != 0) {
index = file_entry.next_sibling_index; index = file_entry.next_sibling_index;
max_index = max(index, max_index); max_index = max(index, max_index);
if (BDRIRead(fet_offset + index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &file_entry) != FR_OK) if (BDRIRead(fet_offset + index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &file_entry) != FR_OK)
return 1; return 1;
// If an entry for the tid already existed that is already the specified size and replace was specified, just replace the existing entry // If an entry for the tid already existed that is already the specified size and replace was specified, just replace the existing entry
if (memcmp(title_id_be, file_entry.title_id, 8) == 0) { if (memcmp(title_id_be, file_entry.title_id, 8) == 0) {
if (!replace || (file_entry.size != size)) return 1; if (!replace || (file_entry.size != size)) return 1;
@ -376,10 +376,10 @@ static u32 AddBDRIEntry(const BDRIFsHeader* fs_header, const u32 fs_header_offse
} }
} }
} }
u32 fat_entry[2]; u32 fat_entry[2];
u32 fat_index = 0; u32 fat_index = 0;
if (!do_replace) { if (!do_replace) {
if (BDRIRead(fat_offset, FAT_ENTRY_SIZE, fat_entry) != FR_OK) if (BDRIRead(fat_offset, FAT_ENTRY_SIZE, fat_entry) != FR_OK)
return 1; return 1;
@ -476,48 +476,48 @@ static u32 AddBDRIEntry(const BDRIFsHeader* fs_header, const u32 fs_header_offse
if (BDRIWrite(fat_offset + previous_free_index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK) if (BDRIWrite(fat_offset + previous_free_index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK)
return 1; return 1;
} else fat_index = file_entry.start_block_index + 1; } else fat_index = file_entry.start_block_index + 1;
u32 bytes_written = 0, fat_index_write = fat_index; u32 bytes_written = 0, fat_index_write = fat_index;
while (bytes_written < size) { // Write the full entry, walking the FAT node chain while (bytes_written < size) { // Write the full entry, walking the FAT node chain
// Can't assume contiguity here, because we might be replacing an existing entry // Can't assume contiguity here, because we might be replacing an existing entry
u32 write_start = fat_index_write - 1; // Data region block index u32 write_start = fat_index_write - 1; // Data region block index
u32 write_count = 0; u32 write_count = 0;
if (BDRIRead(fat_offset + fat_index_write * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK) if (BDRIRead(fat_offset + fat_index_write * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK)
return 1; return 1;
if ((bytes_written == 0) && !getfatflag(fat_entry[0])) if ((bytes_written == 0) && !getfatflag(fat_entry[0]))
return 1; return 1;
u32 next_index = getfatindex(fat_entry[1]); u32 next_index = getfatindex(fat_entry[1]);
if (getfatflag(fat_entry[1])) { // Multi-entry node if (getfatflag(fat_entry[1])) { // Multi-entry node
if (BDRIRead(fat_offset + (fat_index_write + 1) * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK) if (BDRIRead(fat_offset + (fat_index_write + 1) * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK)
return 1; return 1;
if (!getfatflag(fat_entry[0]) || getfatflag(fat_entry[1]) || (getfatindex(fat_entry[0]) != fat_index_write) || (getfatindex(fat_entry[0]) >= getfatindex(fat_entry[1]))) if (!getfatflag(fat_entry[0]) || getfatflag(fat_entry[1]) || (getfatindex(fat_entry[0]) != fat_index_write) || (getfatindex(fat_entry[0]) >= getfatindex(fat_entry[1])))
return 1; return 1;
write_count = getfatindex(fat_entry[1]) + 1 - fat_index_write; write_count = getfatindex(fat_entry[1]) + 1 - fat_index_write;
} else { // Single-entry node } else { // Single-entry node
write_count = 1; write_count = 1;
} }
fat_index_write = next_index; fat_index_write = next_index;
u32 btw = min(size - bytes_written, write_count * fs_header->data_block_size); u32 btw = min(size - bytes_written, write_count * fs_header->data_block_size);
if (BDRIWrite(data_offset + write_start * fs_header->data_block_size, btw, entry + bytes_written) != FR_OK) if (BDRIWrite(data_offset + write_start * fs_header->data_block_size, btw, entry + bytes_written) != FR_OK)
return 1; return 1;
bytes_written += btw; bytes_written += btw;
} }
if (!do_replace) { if (!do_replace) {
DummyFileEntry dummy_entry; DummyFileEntry dummy_entry;
// Read the 0th entry in the FET, which is always a dummy entry // Read the 0th entry in the FET, which is always a dummy entry
if (BDRIRead(fet_offset, sizeof(DummyFileEntry), &dummy_entry) != FR_OK) if (BDRIRead(fet_offset, sizeof(DummyFileEntry), &dummy_entry) != FR_OK)
return 1; return 1;
if (dummy_entry.max_entry_count != fs_header->max_file_count + 1) if (dummy_entry.max_entry_count != fs_header->max_file_count + 1)
@ -559,83 +559,83 @@ static u32 AddBDRIEntry(const BDRIFsHeader* fs_header, const u32 fs_header_offse
if (BDRIWrite(fet_offset + index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &file_entry) != FR_OK) if (BDRIWrite(fet_offset + index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &file_entry) != FR_OK)
return 1; return 1;
} }
return 0; return 0;
} }
static u32 GetNumBDRIEntries(const BDRIFsHeader* fs_header, const u32 fs_header_offset) { static u32 GetNumBDRIEntries(const BDRIFsHeader* fs_header, const u32 fs_header_offset) {
if ((fs_header->info_offset != 0x20) || (fs_header->fat_entry_count != fs_header->data_block_count)) // Could be more thorough if ((fs_header->info_offset != 0x20) || (fs_header->fat_entry_count != fs_header->data_block_count)) // Could be more thorough
return 0; return 0;
const u32 data_offset = fs_header_offset + fs_header->data_offset; const u32 data_offset = fs_header_offset + fs_header->data_offset;
const u32 det_offset = data_offset + fs_header->det_start_block * fs_header->data_block_size; const u32 det_offset = data_offset + fs_header->det_start_block * fs_header->data_block_size;
const u32 fet_offset = data_offset + fs_header->fet_start_block * fs_header->data_block_size; const u32 fet_offset = data_offset + fs_header->fet_start_block * fs_header->data_block_size;
u32 num_entries = 0; u32 num_entries = 0;
TdbFileEntry file_entry; TdbFileEntry file_entry;
// Read the index of the first file entry from the directory entry table // Read the index of the first file entry from the directory entry table
if (BDRIRead(det_offset + 0x2C, sizeof(u32), &(file_entry.next_sibling_index)) != FR_OK) if (BDRIRead(det_offset + 0x2C, sizeof(u32), &(file_entry.next_sibling_index)) != FR_OK)
return 0; return 0;
while (file_entry.next_sibling_index != 0) { while (file_entry.next_sibling_index != 0) {
num_entries++; num_entries++;
if (BDRIRead(fet_offset + file_entry.next_sibling_index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &file_entry) != FR_OK) if (BDRIRead(fet_offset + file_entry.next_sibling_index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &file_entry) != FR_OK)
return 0; return 0;
} }
return num_entries; return num_entries;
} }
static u32 ListBDRIEntryTitleIDs(const BDRIFsHeader* fs_header, const u32 fs_header_offset, u8* title_ids, u32 max_title_ids) { static u32 ListBDRIEntryTitleIDs(const BDRIFsHeader* fs_header, const u32 fs_header_offset, u8* title_ids, u32 max_title_ids) {
if ((fs_header->info_offset != 0x20) || (fs_header->fat_entry_count != fs_header->data_block_count)) if ((fs_header->info_offset != 0x20) || (fs_header->fat_entry_count != fs_header->data_block_count))
return 0; return 0;
const u32 data_offset = fs_header_offset + fs_header->data_offset; const u32 data_offset = fs_header_offset + fs_header->data_offset;
const u32 det_offset = data_offset + fs_header->det_start_block * fs_header->data_block_size; const u32 det_offset = data_offset + fs_header->det_start_block * fs_header->data_block_size;
const u32 fet_offset = data_offset + fs_header->fet_start_block * fs_header->data_block_size; const u32 fet_offset = data_offset + fs_header->fet_start_block * fs_header->data_block_size;
u32 num_entries = 0; u32 num_entries = 0;
TdbFileEntry file_entry; TdbFileEntry file_entry;
for (u32 i = 0; i < max_title_ids; i++) for (u32 i = 0; i < max_title_ids; i++)
memset(title_ids, 0, max_title_ids * 8); memset(title_ids, 0, max_title_ids * 8);
// Read the index of the first file entry from the directory entry table // Read the index of the first file entry from the directory entry table
if (BDRIRead(det_offset + 0x2C, sizeof(u32), &(file_entry.next_sibling_index)) != FR_OK) if (BDRIRead(det_offset + 0x2C, sizeof(u32), &(file_entry.next_sibling_index)) != FR_OK)
return 1; return 1;
while ((file_entry.next_sibling_index != 0) && (num_entries < max_title_ids)) { while ((file_entry.next_sibling_index != 0) && (num_entries < max_title_ids)) {
if (BDRIRead(fet_offset + file_entry.next_sibling_index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &file_entry) != FR_OK) if (BDRIRead(fet_offset + file_entry.next_sibling_index * sizeof(TdbFileEntry), sizeof(TdbFileEntry), &file_entry) != FR_OK)
return 1; return 1;
u64 tid_be = getbe64(file_entry.title_id); u64 tid_be = getbe64(file_entry.title_id);
memcpy(title_ids + num_entries * 8, (u8*) &tid_be, 8); memcpy(title_ids + num_entries * 8, (u8*) &tid_be, 8);
num_entries++; num_entries++;
} }
return 0; return 0;
} }
u32 GetNumTitleInfoEntries(const char* path) { u32 GetNumTitleInfoEntries(const char* path) {
FIL file; FIL file;
TitleDBPreHeader pre_header; TitleDBPreHeader pre_header;
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 0; return 0;
bdrifp = &file; bdrifp = &file;
if ((BDRIRead(0, sizeof(TitleDBPreHeader), &pre_header) != FR_OK) || if ((BDRIRead(0, sizeof(TitleDBPreHeader), &pre_header) != FR_OK) ||
!CheckDBMagic((u8*) &pre_header, false)) { !CheckDBMagic((u8*) &pre_header, false)) {
fvx_close(bdrifp); fvx_close(bdrifp);
bdrifp = NULL; bdrifp = NULL;
return 0; return 0;
} }
u32 num = GetNumBDRIEntries(&(pre_header.fs_header), sizeof(TitleDBPreHeader) - sizeof(BDRIFsHeader)); u32 num = GetNumBDRIEntries(&(pre_header.fs_header), sizeof(TitleDBPreHeader) - sizeof(BDRIFsHeader));
fvx_close(bdrifp); fvx_close(bdrifp);
bdrifp = NULL; bdrifp = NULL;
return num; return num;
@ -644,12 +644,12 @@ u32 GetNumTitleInfoEntries(const char* path) {
u32 GetNumTickets(const char* path) { u32 GetNumTickets(const char* path) {
FIL file; FIL file;
TickDBPreHeader pre_header; TickDBPreHeader pre_header;
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 0; return 0;
bdrifp = &file; bdrifp = &file;
if ((BDRIRead(0, sizeof(TickDBPreHeader), &pre_header) != FR_OK) || if ((BDRIRead(0, sizeof(TickDBPreHeader), &pre_header) != FR_OK) ||
!CheckDBMagic((u8*) &pre_header, true)) { !CheckDBMagic((u8*) &pre_header, true)) {
fvx_close(bdrifp); fvx_close(bdrifp);
@ -658,7 +658,7 @@ u32 GetNumTickets(const char* path) {
} }
u32 num = GetNumBDRIEntries(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader)); u32 num = GetNumBDRIEntries(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader));
fvx_close(bdrifp); fvx_close(bdrifp);
bdrifp = NULL; bdrifp = NULL;
return num; return num;
@ -667,12 +667,12 @@ u32 GetNumTickets(const char* path) {
u32 ListTitleInfoEntryTitleIDs(const char* path, u8* title_ids, u32 max_title_ids) { u32 ListTitleInfoEntryTitleIDs(const char* path, u8* title_ids, u32 max_title_ids) {
FIL file; FIL file;
TitleDBPreHeader pre_header; TitleDBPreHeader pre_header;
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 1; return 1;
bdrifp = &file; bdrifp = &file;
if ((BDRIRead(0, sizeof(TitleDBPreHeader), &pre_header) != FR_OK) || if ((BDRIRead(0, sizeof(TitleDBPreHeader), &pre_header) != FR_OK) ||
!CheckDBMagic((u8*) &pre_header, false) || !CheckDBMagic((u8*) &pre_header, false) ||
(ListBDRIEntryTitleIDs(&(pre_header.fs_header), sizeof(TitleDBPreHeader) - sizeof(BDRIFsHeader), title_ids, max_title_ids) != 0)) { (ListBDRIEntryTitleIDs(&(pre_header.fs_header), sizeof(TitleDBPreHeader) - sizeof(BDRIFsHeader), title_ids, max_title_ids) != 0)) {
@ -680,7 +680,7 @@ u32 ListTitleInfoEntryTitleIDs(const char* path, u8* title_ids, u32 max_title_id
bdrifp = NULL; bdrifp = NULL;
return 1; return 1;
} }
fvx_close(bdrifp); fvx_close(bdrifp);
bdrifp = NULL; bdrifp = NULL;
return 0; return 0;
@ -689,12 +689,12 @@ u32 ListTitleInfoEntryTitleIDs(const char* path, u8* title_ids, u32 max_title_id
u32 ListTicketTitleIDs(const char* path, u8* title_ids, u32 max_title_ids) { u32 ListTicketTitleIDs(const char* path, u8* title_ids, u32 max_title_ids) {
FIL file; FIL file;
TickDBPreHeader pre_header; TickDBPreHeader pre_header;
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 1; return 1;
bdrifp = &file; bdrifp = &file;
if ((BDRIRead(0, sizeof(TickDBPreHeader), &pre_header) != FR_OK) || if ((BDRIRead(0, sizeof(TickDBPreHeader), &pre_header) != FR_OK) ||
!CheckDBMagic((u8*) &pre_header, true) || !CheckDBMagic((u8*) &pre_header, true) ||
(ListBDRIEntryTitleIDs(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader), title_ids, max_title_ids) != 0)) { (ListBDRIEntryTitleIDs(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader), title_ids, max_title_ids) != 0)) {
@ -702,7 +702,7 @@ u32 ListTicketTitleIDs(const char* path, u8* title_ids, u32 max_title_ids) {
bdrifp = NULL; bdrifp = NULL;
return 1; return 1;
} }
fvx_close(bdrifp); fvx_close(bdrifp);
bdrifp = NULL; bdrifp = NULL;
return 0; return 0;
@ -711,12 +711,12 @@ u32 ListTicketTitleIDs(const char* path, u8* title_ids, u32 max_title_ids) {
u32 ReadTitleInfoEntryFromDB(const char* path, const u8* title_id, TitleInfoEntry* tie) { u32 ReadTitleInfoEntryFromDB(const char* path, const u8* title_id, TitleInfoEntry* tie) {
FIL file; FIL file;
TitleDBPreHeader pre_header; TitleDBPreHeader pre_header;
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 1; return 1;
bdrifp = &file; bdrifp = &file;
if ((BDRIRead(0, sizeof(TitleDBPreHeader), &pre_header) != FR_OK) || if ((BDRIRead(0, sizeof(TitleDBPreHeader), &pre_header) != FR_OK) ||
!CheckDBMagic((u8*) &pre_header, false) || !CheckDBMagic((u8*) &pre_header, false) ||
(ReadBDRIEntry(&(pre_header.fs_header), sizeof(TitleDBPreHeader) - sizeof(BDRIFsHeader), title_id, (u8*) tie, (ReadBDRIEntry(&(pre_header.fs_header), sizeof(TitleDBPreHeader) - sizeof(BDRIFsHeader), title_id, (u8*) tie,
@ -725,7 +725,7 @@ u32 ReadTitleInfoEntryFromDB(const char* path, const u8* title_id, TitleInfoEntr
bdrifp = NULL; bdrifp = NULL;
return 1; return 1;
} }
fvx_close(bdrifp); fvx_close(bdrifp);
bdrifp = NULL; bdrifp = NULL;
return 0; return 0;
@ -739,13 +739,13 @@ u32 ReadTicketFromDB(const char* path, const u8* title_id, Ticket** ticket) {
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 1; return 1;
bdrifp = &file; bdrifp = &file;
if ((BDRIRead(0, sizeof(TickDBPreHeader), &pre_header) != FR_OK) || if ((BDRIRead(0, sizeof(TickDBPreHeader), &pre_header) != FR_OK) ||
!CheckDBMagic((u8*) &pre_header, true) || !CheckDBMagic((u8*) &pre_header, true) ||
(GetBDRIEntrySize(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader), title_id, &entry_size) != 0) || (GetBDRIEntrySize(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader), title_id, &entry_size) != 0) ||
entry_size < sizeof(TicketEntry) + 0x14 || entry_size < sizeof(TicketEntry) + 0x14 ||
(te = (TicketEntry*)malloc(entry_size), te == NULL) || (te = (TicketEntry*)malloc(entry_size), te == NULL) ||
(ReadBDRIEntry(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader), title_id, (u8*) te, (ReadBDRIEntry(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader), title_id, (u8*) te,
entry_size) != 0)) { entry_size) != 0)) {
@ -754,15 +754,15 @@ u32 ReadTicketFromDB(const char* path, const u8* title_id, Ticket** ticket) {
bdrifp = NULL; bdrifp = NULL;
return 1; return 1;
} }
fvx_close(bdrifp); fvx_close(bdrifp);
bdrifp = NULL; bdrifp = NULL;
if (te->ticket_size != GetTicketSize(&te->ticket)) { if (te->ticket_size != GetTicketSize(&te->ticket)) {
free(te); free(te);
return 1; return 1;
} }
if (ticket) { if (ticket) {
u32 size = te->ticket_size; u32 size = te->ticket_size;
memmove(te, &te->ticket, size); // recycle this memory, instead of allocating another memmove(te, &te->ticket, size); // recycle this memory, instead of allocating another
@ -771,7 +771,7 @@ u32 ReadTicketFromDB(const char* path, const u8* title_id, Ticket** ticket) {
*ticket = tik; *ticket = tik;
return 0; return 0;
} }
free(te); free(te);
return 0; return 0;
} }
@ -779,12 +779,12 @@ u32 ReadTicketFromDB(const char* path, const u8* title_id, Ticket** ticket) {
u32 RemoveTitleInfoEntryFromDB(const char* path, const u8* title_id) { u32 RemoveTitleInfoEntryFromDB(const char* path, const u8* title_id) {
FIL file; FIL file;
TitleDBPreHeader pre_header; TitleDBPreHeader pre_header;
if (fvx_open(&file, path, FA_READ | FA_WRITE | FA_OPEN_EXISTING) != FR_OK) if (fvx_open(&file, path, FA_READ | FA_WRITE | FA_OPEN_EXISTING) != FR_OK)
return 1; return 1;
bdrifp = &file; bdrifp = &file;
if ((BDRIRead(0, sizeof(TitleDBPreHeader), &pre_header) != FR_OK) || if ((BDRIRead(0, sizeof(TitleDBPreHeader), &pre_header) != FR_OK) ||
!CheckDBMagic((u8*) &pre_header, false) || !CheckDBMagic((u8*) &pre_header, false) ||
(RemoveBDRIEntry(&(pre_header.fs_header), sizeof(TitleDBPreHeader) - sizeof(BDRIFsHeader), title_id) != 0)) { (RemoveBDRIEntry(&(pre_header.fs_header), sizeof(TitleDBPreHeader) - sizeof(BDRIFsHeader), title_id) != 0)) {
@ -792,7 +792,7 @@ u32 RemoveTitleInfoEntryFromDB(const char* path, const u8* title_id) {
bdrifp = NULL; bdrifp = NULL;
return 1; return 1;
} }
fvx_close(bdrifp); fvx_close(bdrifp);
bdrifp = NULL; bdrifp = NULL;
return 0; return 0;
@ -801,12 +801,12 @@ u32 RemoveTitleInfoEntryFromDB(const char* path, const u8* title_id) {
u32 RemoveTicketFromDB(const char* path, const u8* title_id) { u32 RemoveTicketFromDB(const char* path, const u8* title_id) {
FIL file; FIL file;
TickDBPreHeader pre_header; TickDBPreHeader pre_header;
if (fvx_open(&file, path, FA_READ | FA_WRITE | FA_OPEN_EXISTING) != FR_OK) if (fvx_open(&file, path, FA_READ | FA_WRITE | FA_OPEN_EXISTING) != FR_OK)
return 1; return 1;
bdrifp = &file; bdrifp = &file;
if ((BDRIRead(0, sizeof(TickDBPreHeader), &pre_header) != FR_OK) || if ((BDRIRead(0, sizeof(TickDBPreHeader), &pre_header) != FR_OK) ||
!CheckDBMagic((u8*) &pre_header, true) || !CheckDBMagic((u8*) &pre_header, true) ||
(RemoveBDRIEntry(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader), title_id) != 0)) { (RemoveBDRIEntry(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader), title_id) != 0)) {
@ -814,7 +814,7 @@ u32 RemoveTicketFromDB(const char* path, const u8* title_id) {
bdrifp = NULL; bdrifp = NULL;
return 1; return 1;
} }
fvx_close(&file); fvx_close(&file);
bdrifp = NULL; bdrifp = NULL;
return 0; return 0;
@ -823,13 +823,13 @@ u32 RemoveTicketFromDB(const char* path, const u8* title_id) {
u32 AddTitleInfoEntryToDB(const char* path, const u8* title_id, const TitleInfoEntry* tie, bool replace) { u32 AddTitleInfoEntryToDB(const char* path, const u8* title_id, const TitleInfoEntry* tie, bool replace) {
FIL file; FIL file;
TitleDBPreHeader pre_header; TitleDBPreHeader pre_header;
if (fvx_open(&file, path, FA_READ | FA_WRITE | FA_OPEN_EXISTING) != FR_OK) if (fvx_open(&file, path, FA_READ | FA_WRITE | FA_OPEN_EXISTING) != FR_OK)
return 1; return 1;
bdrifp = &file; bdrifp = &file;
if ((BDRIRead(0, sizeof(TitleDBPreHeader), &pre_header) != FR_OK) || if ((BDRIRead(0, sizeof(TitleDBPreHeader), &pre_header) != FR_OK) ||
!CheckDBMagic((u8*) &pre_header, false) || !CheckDBMagic((u8*) &pre_header, false) ||
(AddBDRIEntry(&(pre_header.fs_header), sizeof(TitleDBPreHeader) - sizeof(BDRIFsHeader), title_id, (AddBDRIEntry(&(pre_header.fs_header), sizeof(TitleDBPreHeader) - sizeof(BDRIFsHeader), title_id,
(const u8*) tie, sizeof(TitleInfoEntry), replace) != 0)) { (const u8*) tie, sizeof(TitleInfoEntry), replace) != 0)) {
@ -837,7 +837,7 @@ u32 AddTitleInfoEntryToDB(const char* path, const u8* title_id, const TitleInfoE
bdrifp = NULL; bdrifp = NULL;
return 1; return 1;
} }
fvx_close(bdrifp); fvx_close(bdrifp);
bdrifp = NULL; bdrifp = NULL;
return 0; return 0;
@ -847,7 +847,7 @@ u32 AddTicketToDB(const char* path, const u8* title_id, const Ticket* ticket, bo
FIL file; FIL file;
TickDBPreHeader pre_header; TickDBPreHeader pre_header;
u32 entry_size = sizeof(TicketEntry) + GetTicketContentIndexSize(ticket); u32 entry_size = sizeof(TicketEntry) + GetTicketContentIndexSize(ticket);
TicketEntry* te = (TicketEntry*)malloc(entry_size); TicketEntry* te = (TicketEntry*)malloc(entry_size);
if (!te) { if (!te) {
return 1; return 1;
@ -860,9 +860,9 @@ u32 AddTicketToDB(const char* path, const u8* title_id, const Ticket* ticket, bo
free(te); free(te);
return 1; return 1;
} }
bdrifp = &file; bdrifp = &file;
if ((BDRIRead(0, sizeof(TickDBPreHeader), &pre_header) != FR_OK) || if ((BDRIRead(0, sizeof(TickDBPreHeader), &pre_header) != FR_OK) ||
!CheckDBMagic((u8*) &pre_header, true) || !CheckDBMagic((u8*) &pre_header, true) ||
(AddBDRIEntry(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader), title_id, (AddBDRIEntry(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader), title_id,

View File

@ -7,7 +7,7 @@ u32 CheckBossHash(BossHeader* boss, bool encrypted) {
u8 hash_area[0x14] __attribute__((aligned(4))) = { 0 }; u8 hash_area[0x14] __attribute__((aligned(4))) = { 0 };
u8 boss_sha256[0x20]; u8 boss_sha256[0x20];
u8 l_sha256[0x20]; u8 l_sha256[0x20];
// calculate hash // calculate hash
memcpy(hash_area, ((u8*) boss) + 0x28, 0x12); memcpy(hash_area, ((u8*) boss) + 0x28, 0x12);
memcpy(boss_sha256, boss->hash_header, 0x20); memcpy(boss_sha256, boss->hash_header, 0x20);
@ -16,13 +16,13 @@ u32 CheckBossHash(BossHeader* boss, bool encrypted) {
CryptBoss(boss_sha256, 0x28 + 0x12, 0x20, boss); CryptBoss(boss_sha256, 0x28 + 0x12, 0x20, boss);
} }
sha_quick(l_sha256, hash_area, 0x14, SHA256_MODE); sha_quick(l_sha256, hash_area, 0x14, SHA256_MODE);
return (memcmp(boss_sha256, l_sha256, 0x20) == 0) ? 0 : 1; return (memcmp(boss_sha256, l_sha256, 0x20) == 0) ? 0 : 1;
} }
u32 ValidateBossHeader(BossHeader* header, u32 fsize) { u32 ValidateBossHeader(BossHeader* header, u32 fsize) {
u8 boss_magic[] = { BOSS_MAGIC }; u8 boss_magic[] = { BOSS_MAGIC };
// base checks // base checks
if ((memcmp(header->magic, boss_magic, sizeof(boss_magic)) != 0) || if ((memcmp(header->magic, boss_magic, sizeof(boss_magic)) != 0) ||
(fsize && (fsize != getbe32(header->filesize))) || (fsize && (fsize != getbe32(header->filesize))) ||
@ -31,12 +31,12 @@ u32 ValidateBossHeader(BossHeader* header, u32 fsize) {
(getbe16(header->cnthdr_hash_type) != 0x0002) || (getbe16(header->cnthdr_hash_type) != 0x0002) ||
(getbe16(header->cnthdr_rsa_size) != 0x0002)) (getbe16(header->cnthdr_rsa_size) != 0x0002))
return 1; return 1;
// hash check // hash check
if ((CheckBossHash(header, false) != 0) && if ((CheckBossHash(header, false) != 0) &&
(CheckBossHash(header, true) != 0)) (CheckBossHash(header, true) != 0))
return 1; return 1;
return 0; return 0;
} }
@ -59,14 +59,14 @@ u32 CryptBoss(void* data, u32 offset, u32 size, BossHeader* boss) {
size -= 0x28 - offset; size -= 0x28 - offset;
offset = 0x28; offset = 0x28;
} }
// decrypt BOSS data // decrypt BOSS data
u8 ctr[16] = { 0 }; u8 ctr[16] = { 0 };
memcpy(ctr, boss->ctr12, 12); memcpy(ctr, boss->ctr12, 12);
ctr[15] = 0x01; ctr[15] = 0x01;
use_aeskey(0x38); use_aeskey(0x38);
ctr_decrypt_byte(data, data, size, offset - 0x28, AES_CNT_CTRNAND_MODE, ctr); ctr_decrypt_byte(data, data, size, offset - 0x28, AES_CNT_CTRNAND_MODE, ctr);
return 0; return 0;
} }
@ -76,7 +76,7 @@ u32 CryptBossSequential(void* data, u32 offset, u32 size) {
// unexpected results otherwise // unexpected results otherwise
static BossHeader boss = { 0 }; static BossHeader boss = { 0 };
static BossHeader* bossptr = NULL; static BossHeader* bossptr = NULL;
// fetch boss header from data // fetch boss header from data
if ((offset == 0) && (size >= sizeof(BossHeader))) { if ((offset == 0) && (size >= sizeof(BossHeader))) {
bossptr = NULL; bossptr = NULL;
@ -87,9 +87,9 @@ u32 CryptBossSequential(void* data, u32 offset, u32 size) {
return 1; return 1;
bossptr = &boss; bossptr = &boss;
} }
// safety check, boss pointer // safety check, boss pointer
if (!bossptr) return 1; if (!bossptr) return 1;
return CryptBoss(data, offset, size, bossptr); return CryptBoss(data, offset, size, bossptr);
} }

View File

@ -14,7 +14,7 @@
typedef struct { typedef struct {
// actual BOSS header // actual BOSS header
u8 magic[8]; // "boss" + 0x00010001, see above u8 magic[8]; // "boss" + 0x00010001, see above
u8 filesize[4]; // big endian u8 filesize[4]; // big endian
u8 release_date[8]; u8 release_date[8];
u8 unknown0[2]; // always 0x0001 u8 unknown0[2]; // always 0x0001
u8 padding[2]; u8 padding[2];
@ -27,7 +27,7 @@ typedef struct {
u8 hash_header[0x20]; u8 hash_header[0x20];
u8 signature_header[0x100]; u8 signature_header[0x100];
// payload header, first 0x1C byte used for hash (0x15A) // payload header, first 0x1C byte used for hash (0x15A)
u8 programId[8]; u8 programId[8];
u8 unknown2[4]; // typically zero u8 unknown2[4]; // typically zero
u8 data_type[4]; u8 data_type[4];
u8 size_payload[4]; u8 size_payload[4];

View File

@ -18,20 +18,20 @@ u32 ValidateCiaHeader(CiaHeader* header) {
u32 GetCiaInfo(CiaInfo* info, CiaHeader* header) { u32 GetCiaInfo(CiaInfo* info, CiaHeader* header) {
if ((u8*) info != (u8*) header) memcpy(info, header, 0x20); // take over first 0x20 byte if ((u8*) info != (u8*) header) memcpy(info, header, 0x20); // take over first 0x20 byte
info->offset_cert = align(header->size_header, 64); info->offset_cert = align(header->size_header, 64);
info->offset_ticket = info->offset_cert + align(header->size_cert, 64); info->offset_ticket = info->offset_cert + align(header->size_cert, 64);
info->offset_tmd = info->offset_ticket + align(header->size_ticket, 64); info->offset_tmd = info->offset_ticket + align(header->size_ticket, 64);
info->offset_content = info->offset_tmd + align(header->size_tmd, 64); info->offset_content = info->offset_tmd + align(header->size_tmd, 64);
info->offset_meta = (header->size_meta) ? info->offset_content + align(header->size_content, 64) : 0; info->offset_meta = (header->size_meta) ? info->offset_content + align(header->size_content, 64) : 0;
info->offset_content_list = info->offset_tmd + sizeof(TitleMetaData); info->offset_content_list = info->offset_tmd + sizeof(TitleMetaData);
info->size_content_list = info->size_tmd - sizeof(TitleMetaData); info->size_content_list = info->size_tmd - sizeof(TitleMetaData);
info->size_cia = (header->size_meta) ? info->offset_meta + info->size_meta : info->size_cia = (header->size_meta) ? info->offset_meta + info->size_meta :
info->offset_content + info->size_content; info->offset_content + info->size_content;
info->max_contents = (info->size_tmd - sizeof(TitleMetaData)) / sizeof(TmdContentChunk); info->max_contents = (info->size_tmd - sizeof(TitleMetaData)) / sizeof(TmdContentChunk);
return 0; return 0;
} }
@ -58,7 +58,7 @@ u32 BuildCiaCert(u8* ciacert) {
0xFB, 0xD2, 0xC0, 0x47, 0x95, 0xB9, 0x4C, 0xC8, 0x0B, 0x64, 0x58, 0x96, 0xF6, 0x61, 0x0F, 0x52, 0xFB, 0xD2, 0xC0, 0x47, 0x95, 0xB9, 0x4C, 0xC8, 0x0B, 0x64, 0x58, 0x96, 0xF6, 0x61, 0x0F, 0x52,
0x18, 0x83, 0xAF, 0xE0, 0xF4, 0xE5, 0x62, 0xBA, 0x69, 0xEE, 0x72, 0x2A, 0xC2, 0x4E, 0x95, 0xB3 0x18, 0x83, 0xAF, 0xE0, 0xF4, 0xE5, 0x62, 0xBA, 0x69, 0xEE, 0x72, 0x2A, 0xC2, 0x4E, 0x95, 0xB3
}; };
// open certs.db file on SysNAND // open certs.db file on SysNAND
FIL db; FIL db;
UINT bytes_read; UINT bytes_read;
@ -74,13 +74,13 @@ u32 BuildCiaCert(u8* ciacert) {
f_lseek(&db, 0x3C10); f_lseek(&db, 0x3C10);
f_read(&db, ciacert + 0x700, 0x300, &bytes_read); f_read(&db, ciacert + 0x700, 0x300, &bytes_read);
f_close(&db); f_close(&db);
// check the certificate hash // check the certificate hash
u8 cert_hash[0x20]; u8 cert_hash[0x20];
sha_quick(cert_hash, ciacert, CIA_CERT_SIZE, SHA256_MODE); sha_quick(cert_hash, ciacert, CIA_CERT_SIZE, SHA256_MODE);
if (memcmp(cert_hash, IS_DEVKIT ? cert_hash_expected_dev : cert_hash_expected, 0x20) != 0) if (memcmp(cert_hash, IS_DEVKIT ? cert_hash_expected_dev : cert_hash_expected, 0x20) != 0)
return 1; return 1;
return 0; return 0;
} }

View File

@ -16,10 +16,10 @@ typedef struct {
u32 GetCodeLzssUncompressedSize(void* footer, u32 comp_size) { u32 GetCodeLzssUncompressedSize(void* footer, u32 comp_size) {
if (comp_size < sizeof(CodeLzssFooter)) return 0; if (comp_size < sizeof(CodeLzssFooter)) return 0;
CodeLzssFooter* f = (CodeLzssFooter*) footer; CodeLzssFooter* f = (CodeLzssFooter*) footer;
if ((CODE_COMP_SIZE(f) > comp_size) || (CODE_COMP_END(f) < 0)) return 0; if ((CODE_COMP_SIZE(f) > comp_size) || (CODE_COMP_END(f) < 0)) return 0;
return CODE_DEC_SIZE(f) + (comp_size - CODE_COMP_SIZE(f)); return CODE_DEC_SIZE(f) + (comp_size - CODE_COMP_SIZE(f));
} }
@ -27,22 +27,22 @@ u32 GetCodeLzssUncompressedSize(void* footer, u32 comp_size) {
u32 DecompressCodeLzss(u8* code, u32* code_size, u32 max_size) { u32 DecompressCodeLzss(u8* code, u32* code_size, u32 max_size) {
u8* data_start = code; u8* data_start = code;
u8* comp_start = data_start; u8* comp_start = data_start;
// get footer, fix comp_start offset // get footer, fix comp_start offset
if ((*code_size < sizeof(CodeLzssFooter)) || (*code_size > max_size)) return 1; if ((*code_size < sizeof(CodeLzssFooter)) || (*code_size > max_size)) return 1;
CodeLzssFooter* footer = (CodeLzssFooter*) (void*) (data_start + *code_size - sizeof(CodeLzssFooter)); CodeLzssFooter* footer = (CodeLzssFooter*) (void*) (data_start + *code_size - sizeof(CodeLzssFooter));
if (CODE_COMP_SIZE(footer) <= *code_size) comp_start += *code_size - CODE_COMP_SIZE(footer); if (CODE_COMP_SIZE(footer) <= *code_size) comp_start += *code_size - CODE_COMP_SIZE(footer);
else return 1; else return 1;
// more sanity checks // more sanity checks
if ((CODE_COMP_END(footer) < 0) || (CODE_DEC_SIZE(footer) > max_size)) if ((CODE_COMP_END(footer) < 0) || (CODE_DEC_SIZE(footer) > max_size))
return 1; // not reverse LZSS compressed code or too big uncompressed return 1; // not reverse LZSS compressed code or too big uncompressed
// set pointers // set pointers
u8* data_end = (u8*) comp_start + CODE_DEC_SIZE(footer); u8* data_end = (u8*) comp_start + CODE_DEC_SIZE(footer);
u8* ptr_in = (u8*) comp_start + CODE_COMP_END(footer); u8* ptr_in = (u8*) comp_start + CODE_COMP_END(footer);
u8* ptr_out = data_end; u8* ptr_out = data_end;
// main decompression loop // main decompression loop
while ((ptr_in > comp_start) && (ptr_out > comp_start)) { while ((ptr_in > comp_start) && (ptr_out > comp_start)) {
if (!ShowProgress(data_end - ptr_out, data_end - data_start, "Decompressing .code...")) { if (!ShowProgress(data_end - ptr_out, data_end - data_start, "Decompressing .code...")) {
@ -50,17 +50,17 @@ u32 DecompressCodeLzss(u8* code, u32* code_size, u32 max_size) {
ShowProgress(0, data_end - data_start, "Decompressing .code..."); ShowProgress(0, data_end - data_start, "Decompressing .code...");
ShowProgress(data_end - ptr_out, data_end - data_start, "Decompressing .code..."); ShowProgress(data_end - ptr_out, data_end - data_start, "Decompressing .code...");
} }
// sanity check // sanity check
if (ptr_out < ptr_in) return 1; if (ptr_out < ptr_in) return 1;
// read and process control byte // read and process control byte
u8 ctrlbyte = *(--ptr_in); u8 ctrlbyte = *(--ptr_in);
for (int i = 7; i >= 0; i--) { for (int i = 7; i >= 0; i--) {
// end conditions met? // end conditions met?
if ((ptr_in <= comp_start) || (ptr_out <= comp_start)) if ((ptr_in <= comp_start) || (ptr_out <= comp_start))
break; break;
// process control byte // process control byte
if ((ctrlbyte >> i) & 0x1) { if ((ctrlbyte >> i) & 0x1) {
// control bit set, read segment code // control bit set, read segment code
@ -69,11 +69,11 @@ u32 DecompressCodeLzss(u8* code, u32* code_size, u32 max_size) {
if (ptr_in < comp_start) return 1; // corrupted code if (ptr_in < comp_start) return 1; // corrupted code
u32 seg_off = CODE_SEG_OFFSET(seg_code); u32 seg_off = CODE_SEG_OFFSET(seg_code);
u32 seg_len = CODE_SEG_SIZE(seg_code); u32 seg_len = CODE_SEG_SIZE(seg_code);
// sanity check for output pointer // sanity check for output pointer
if ((ptr_out - seg_len < comp_start) || (ptr_out + seg_off >= data_end)) if ((ptr_out - seg_len < comp_start) || (ptr_out + seg_off >= data_end))
return 1; return 1;
// copy data to the correct place // copy data to the correct place
for (u32 c = 0; c < seg_len; c++) { for (u32 c = 0; c < seg_len; c++) {
u8 byte = *(ptr_out + seg_off); u8 byte = *(ptr_out + seg_off);
@ -83,17 +83,17 @@ u32 DecompressCodeLzss(u8* code, u32* code_size, u32 max_size) {
// sanity check for both pointers // sanity check for both pointers
if ((ptr_out == comp_start) || (ptr_in == comp_start)) if ((ptr_out == comp_start) || (ptr_in == comp_start))
return 1; return 1;
// control bit not set, copy byte verbatim // control bit not set, copy byte verbatim
*(--ptr_out) = *(--ptr_in); *(--ptr_out) = *(--ptr_in);
} }
} }
} }
// check pointers // check pointers
if ((ptr_in != comp_start) || (ptr_out != comp_start)) if ((ptr_in != comp_start) || (ptr_out != comp_start))
return 1; return 1;
// all fine if arriving here - return the result // all fine if arriving here - return the result
*code_size = data_end - data_start; *code_size = data_end - data_start;
return 0; return 0;
@ -116,7 +116,7 @@ void initTable(sCompressInfo* a_pInfo, void* a_pWork) {
a_pInfo->ReversedOffsetTable = (s16*)(a_pWork) + 4098; a_pInfo->ReversedOffsetTable = (s16*)(a_pWork) + 4098;
a_pInfo->ByteTable = (s16*)(a_pWork) + 4098 + 4098; a_pInfo->ByteTable = (s16*)(a_pWork) + 4098 + 4098;
a_pInfo->EndTable = (s16*)(a_pWork) + 4098 + 4098 + 256; a_pInfo->EndTable = (s16*)(a_pWork) + 4098 + 4098 + 256;
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
a_pInfo->ByteTable[i] = -1; a_pInfo->ByteTable[i] = -1;
a_pInfo->EndTable[i] = -1; a_pInfo->EndTable[i] = -1;
@ -127,35 +127,35 @@ int search(sCompressInfo* a_pInfo, const u8* a_pSrc, int* a_nOffset, int a_nMaxS
if (a_nMaxSize < 3) { if (a_nMaxSize < 3) {
return 0; return 0;
} }
const u8* pSearch = NULL; const u8* pSearch = NULL;
int nSize = 2; int nSize = 2;
const u16 uWindowPos = a_pInfo->WindowPos; const u16 uWindowPos = a_pInfo->WindowPos;
const u16 uWindowLen = a_pInfo->WindowLen; const u16 uWindowLen = a_pInfo->WindowLen;
s16* pReversedOffsetTable = a_pInfo->ReversedOffsetTable; s16* pReversedOffsetTable = a_pInfo->ReversedOffsetTable;
for (s16 nOffset = a_pInfo->EndTable[*(a_pSrc - 1)]; nOffset != -1; nOffset = pReversedOffsetTable[nOffset]) { for (s16 nOffset = a_pInfo->EndTable[*(a_pSrc - 1)]; nOffset != -1; nOffset = pReversedOffsetTable[nOffset]) {
if (nOffset < uWindowPos) { if (nOffset < uWindowPos) {
pSearch = a_pSrc + uWindowPos - nOffset; pSearch = a_pSrc + uWindowPos - nOffset;
} else { } else {
pSearch = a_pSrc + uWindowLen + uWindowPos - nOffset; pSearch = a_pSrc + uWindowLen + uWindowPos - nOffset;
} }
if (pSearch - a_pSrc < 3) { if (pSearch - a_pSrc < 3) {
continue; continue;
} }
if (*(pSearch - 2) != *(a_pSrc - 2) || *(pSearch - 3) != *(a_pSrc - 3)) { if (*(pSearch - 2) != *(a_pSrc - 2) || *(pSearch - 3) != *(a_pSrc - 3)) {
continue; continue;
} }
int nMaxSize = (int)((s64)min(a_nMaxSize, pSearch - a_pSrc)); int nMaxSize = (int)((s64)min(a_nMaxSize, pSearch - a_pSrc));
int nCurrentSize = 3; int nCurrentSize = 3;
while (nCurrentSize < nMaxSize && *(pSearch - nCurrentSize - 1) == *(a_pSrc - nCurrentSize - 1)) { while (nCurrentSize < nMaxSize && *(pSearch - nCurrentSize - 1) == *(a_pSrc - nCurrentSize - 1)) {
nCurrentSize++; nCurrentSize++;
} }
if (nCurrentSize > nSize) { if (nCurrentSize > nSize) {
nSize = nCurrentSize; nSize = nCurrentSize;
*a_nOffset = (int)(pSearch - a_pSrc); *a_nOffset = (int)(pSearch - a_pSrc);
@ -164,11 +164,11 @@ int search(sCompressInfo* a_pInfo, const u8* a_pSrc, int* a_nOffset, int a_nMaxS
} }
} }
} }
if (nSize < 3) { if (nSize < 3) {
return 0; return 0;
} }
return nSize; return nSize;
} }
@ -181,33 +181,33 @@ void slideByte(sCompressInfo* a_pInfo, const u8* a_pSrc) {
s16* pReversedOffsetTable = a_pInfo->ReversedOffsetTable; s16* pReversedOffsetTable = a_pInfo->ReversedOffsetTable;
s16* pByteTable = a_pInfo->ByteTable; s16* pByteTable = a_pInfo->ByteTable;
s16* pEndTable = a_pInfo->EndTable; s16* pEndTable = a_pInfo->EndTable;
if (uWindowLen == 4098) { if (uWindowLen == 4098) {
u8 uOutData = *(a_pSrc + 4097); u8 uOutData = *(a_pSrc + 4097);
if ((pByteTable[uOutData] = pOffsetTable[pByteTable[uOutData]]) == -1) { if ((pByteTable[uOutData] = pOffsetTable[pByteTable[uOutData]]) == -1) {
pEndTable[uOutData] = -1; pEndTable[uOutData] = -1;
} else { } else {
pReversedOffsetTable[pByteTable[uOutData]] = -1; pReversedOffsetTable[pByteTable[uOutData]] = -1;
} }
uInsertOffset = uWindowPos; uInsertOffset = uWindowPos;
} else { } else {
uInsertOffset = uWindowLen; uInsertOffset = uWindowLen;
} }
s16 nOffset = pEndTable[uInData]; s16 nOffset = pEndTable[uInData];
if (nOffset == -1) { if (nOffset == -1) {
pByteTable[uInData] = uInsertOffset; pByteTable[uInData] = uInsertOffset;
} else { } else {
pOffsetTable[nOffset] = uInsertOffset; pOffsetTable[nOffset] = uInsertOffset;
} }
pEndTable[uInData] = uInsertOffset; pEndTable[uInData] = uInsertOffset;
pOffsetTable[uInsertOffset] = -1; pOffsetTable[uInsertOffset] = -1;
pReversedOffsetTable[uInsertOffset] = nOffset; pReversedOffsetTable[uInsertOffset] = nOffset;
if (uWindowLen == 4098) { if (uWindowLen == 4098) {
a_pInfo->WindowPos = (uWindowPos + 1) % 4098; a_pInfo->WindowPos = (uWindowPos + 1) % 4098;
} else { } else {
@ -228,19 +228,19 @@ s64 alignBytes(s64 a_nData, s64 a_nAlignment) {
bool CompressCodeLzss(const u8* a_pUncompressed, u32 a_uUncompressedSize, u8* a_pCompressed, u32* a_uCompressedSize) { bool CompressCodeLzss(const u8* a_pUncompressed, u32 a_uUncompressedSize, u8* a_pCompressed, u32* a_uCompressedSize) {
const int s_nCompressWorkSize = (4098 + 4098 + 256 + 256) * sizeof(s16); const int s_nCompressWorkSize = (4098 + 4098 + 256 + 256) * sizeof(s16);
bool bResult = true; bool bResult = true;
if (a_uUncompressedSize > sizeof(CodeLzssFooter) && *a_uCompressedSize >= a_uUncompressedSize) { if (a_uUncompressedSize > sizeof(CodeLzssFooter) && *a_uCompressedSize >= a_uUncompressedSize) {
u8* pWork = malloc(s_nCompressWorkSize * sizeof(u8)); u8* pWork = malloc(s_nCompressWorkSize * sizeof(u8));
if (!pWork) return false; if (!pWork) return false;
do { do {
sCompressInfo info; sCompressInfo info;
initTable(&info, pWork); initTable(&info, pWork);
const int nMaxSize = 0xF + 3; const int nMaxSize = 0xF + 3;
const u8* pSrc = a_pUncompressed + a_uUncompressedSize; const u8* pSrc = a_pUncompressed + a_uUncompressedSize;
u8* pDest = a_pCompressed + a_uUncompressedSize; u8* pDest = a_pCompressed + a_uUncompressedSize;
while (pSrc - a_pUncompressed > 0 && pDest - a_pCompressed > 0) { while (pSrc - a_pUncompressed > 0 && pDest - a_pCompressed > 0) {
if (!ShowProgress((u32)(a_pUncompressed + a_uUncompressedSize - pSrc), a_uUncompressedSize, "Compressing .code...")) { if (!ShowProgress((u32)(a_pUncompressed + a_uUncompressedSize - pSrc), a_uUncompressedSize, "Compressing .code...")) {
if (ShowPrompt(true, "Compressing .code...\nB button detected. Cancel?")) { if (ShowPrompt(true, "Compressing .code...\nB button detected. Cancel?")) {
@ -250,20 +250,20 @@ bool CompressCodeLzss(const u8* a_pUncompressed, u32 a_uUncompressedSize, u8* a_
ShowProgress(0, a_uUncompressedSize, "Compressing .code..."); ShowProgress(0, a_uUncompressedSize, "Compressing .code...");
ShowProgress((u32)(a_pUncompressed + a_uUncompressedSize - pSrc), a_uUncompressedSize, "Compressing .code..."); ShowProgress((u32)(a_pUncompressed + a_uUncompressedSize - pSrc), a_uUncompressedSize, "Compressing .code...");
} }
u8* pFlag = --pDest; u8* pFlag = --pDest;
*pFlag = 0; *pFlag = 0;
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
int nOffset = 0; int nOffset = 0;
int nSize = search(&info, pSrc, &nOffset, (int)((s64)min((s64)min(nMaxSize, pSrc - a_pUncompressed), a_pUncompressed + a_uUncompressedSize - pSrc))); int nSize = search(&info, pSrc, &nOffset, (int)((s64)min((s64)min(nMaxSize, pSrc - a_pUncompressed), a_pUncompressed + a_uUncompressedSize - pSrc)));
if (nSize < 3) { if (nSize < 3) {
if (pDest - a_pCompressed < 1) { if (pDest - a_pCompressed < 1) {
bResult = false; bResult = false;
break; break;
} }
slide(&info, pSrc, 1); slide(&info, pSrc, 1);
*--pDest = *--pSrc; *--pDest = *--pSrc;
} else { } else {
@ -271,7 +271,7 @@ bool CompressCodeLzss(const u8* a_pUncompressed, u32 a_uUncompressedSize, u8* a_
bResult = false; bResult = false;
break; break;
} }
*pFlag |= 0x80 >> i; *pFlag |= 0x80 >> i;
slide(&info, pSrc, nSize); slide(&info, pSrc, nSize);
pSrc -= nSize; pSrc -= nSize;
@ -279,29 +279,29 @@ bool CompressCodeLzss(const u8* a_pUncompressed, u32 a_uUncompressedSize, u8* a_
*--pDest = (nSize << 4 & 0xF0) | ((nOffset - 3) >> 8 & 0x0F); *--pDest = (nSize << 4 & 0xF0) | ((nOffset - 3) >> 8 & 0x0F);
*--pDest = (nOffset - 3) & 0xFF; *--pDest = (nOffset - 3) & 0xFF;
} }
if (pSrc - a_pUncompressed <= 0) { if (pSrc - a_pUncompressed <= 0) {
break; break;
} }
} }
if (!bResult) { if (!bResult) {
break; break;
} }
} }
if (!bResult) { if (!bResult) {
break; break;
} }
*a_uCompressedSize = (u32)(a_pCompressed + a_uUncompressedSize - pDest); *a_uCompressedSize = (u32)(a_pCompressed + a_uUncompressedSize - pDest);
} while (false); } while (false);
free(pWork); free(pWork);
} else { } else {
bResult = false; bResult = false;
} }
if (bResult) { if (bResult) {
u32 uOrigSize = a_uUncompressedSize; u32 uOrigSize = a_uUncompressedSize;
u8* pCompressBuffer = a_pCompressed + a_uUncompressedSize - *a_uCompressedSize; u8* pCompressBuffer = a_pCompressed + a_uUncompressedSize - *a_uCompressedSize;
@ -309,10 +309,10 @@ bool CompressCodeLzss(const u8* a_pUncompressed, u32 a_uUncompressedSize, u8* a_
u32 uOrigSafe = 0; u32 uOrigSafe = 0;
u32 uCompressSafe = 0; u32 uCompressSafe = 0;
bool bOver = false; bool bOver = false;
while (uOrigSize > 0) { while (uOrigSize > 0) {
u8 uFlag = pCompressBuffer[--uCompressBufferSize]; u8 uFlag = pCompressBuffer[--uCompressBufferSize];
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
if ((uFlag << i & 0x80) == 0) { if ((uFlag << i & 0x80) == 0) {
uCompressBufferSize--; uCompressBufferSize--;
@ -321,7 +321,7 @@ bool CompressCodeLzss(const u8* a_pUncompressed, u32 a_uUncompressedSize, u8* a_
int nSize = (pCompressBuffer[--uCompressBufferSize] >> 4 & 0x0F) + 3; int nSize = (pCompressBuffer[--uCompressBufferSize] >> 4 & 0x0F) + 3;
uCompressBufferSize--; uCompressBufferSize--;
uOrigSize -= nSize; uOrigSize -= nSize;
if (uOrigSize < uCompressBufferSize) { if (uOrigSize < uCompressBufferSize) {
uOrigSafe = uOrigSize; uOrigSafe = uOrigSize;
uCompressSafe = uCompressBufferSize; uCompressSafe = uCompressBufferSize;
@ -329,24 +329,24 @@ bool CompressCodeLzss(const u8* a_pUncompressed, u32 a_uUncompressedSize, u8* a_
break; break;
} }
} }
if (uOrigSize <= 0) { if (uOrigSize <= 0) {
break; break;
} }
} }
if (bOver) { if (bOver) {
break; break;
} }
} }
u32 uCompressedSize = *a_uCompressedSize - uCompressSafe; u32 uCompressedSize = *a_uCompressedSize - uCompressSafe;
u32 uPadOffset = uOrigSafe + uCompressedSize; u32 uPadOffset = uOrigSafe + uCompressedSize;
u32 uCompFooterOffset = (u32)(alignBytes(uPadOffset, 4)); u32 uCompFooterOffset = (u32)(alignBytes(uPadOffset, 4));
*a_uCompressedSize = uCompFooterOffset + sizeof(CodeLzssFooter); *a_uCompressedSize = uCompFooterOffset + sizeof(CodeLzssFooter);
u32 uTop = *a_uCompressedSize - uOrigSafe; u32 uTop = *a_uCompressedSize - uOrigSafe;
u32 uBottom = *a_uCompressedSize - uPadOffset; u32 uBottom = *a_uCompressedSize - uPadOffset;
if (*a_uCompressedSize >= a_uUncompressedSize || uTop > 0xFFFFFF) { if (*a_uCompressedSize >= a_uUncompressedSize || uTop > 0xFFFFFF) {
bResult = false; bResult = false;
} else { } else {
@ -358,6 +358,6 @@ bool CompressCodeLzss(const u8* a_pUncompressed, u32 a_uUncompressedSize, u8* a_
pCompFooter->addsize_dec = a_uUncompressedSize - *a_uCompressedSize; pCompFooter->addsize_dec = a_uUncompressedSize - *a_uCompressedSize;
} }
} }
return bResult; return bResult;
} }

View File

@ -107,13 +107,13 @@ inline static u32 DisaDiffSize(const TCHAR* path) {
inline static FRESULT DisaDiffOpen(const TCHAR* path) { inline static FRESULT DisaDiffOpen(const TCHAR* path) {
FRESULT res = FR_OK; FRESULT res = FR_OK;
ddfp = NULL; ddfp = NULL;
if (path) { if (path) {
res = fvx_open(&ddfile, path, FA_READ | FA_WRITE | FA_OPEN_EXISTING); res = fvx_open(&ddfile, path, FA_READ | FA_WRITE | FA_OPEN_EXISTING);
if (res == FR_OK) ddfp = &ddfile; if (res == FR_OK) ddfp = &ddfile;
} else if (!GetMountState()) res = FR_DENIED; } else if (!GetMountState()) res = FR_DENIED;
return res; return res;
} }
@ -164,16 +164,16 @@ u32 GetDisaDiffRWInfo(const char* path, DisaDiffRWInfo* info, bool partitionB) {
static const u8 ivfc_magic[] = { IVFC_MAGIC }; static const u8 ivfc_magic[] = { IVFC_MAGIC };
static const u8 dpfs_magic[] = { DPFS_MAGIC }; static const u8 dpfs_magic[] = { DPFS_MAGIC };
static const u8 difi_magic[] = { DIFI_MAGIC }; static const u8 difi_magic[] = { DIFI_MAGIC };
// reset reader info // reset reader info
memset(info, 0x00, sizeof(DisaDiffRWInfo)); memset(info, 0x00, sizeof(DisaDiffRWInfo));
// get file size, header at header offset // get file size, header at header offset
u32 file_size = DisaDiffSize(path); u32 file_size = DisaDiffSize(path);
u8 header[0x100]; u8 header[0x100];
if (DisaDiffQRead(path, header, 0x100, 0x100) != FR_OK) if (DisaDiffQRead(path, header, 0x100, 0x100) != FR_OK)
return 1; return 1;
// DISA/DIFF header: find partition offset & size and DIFI descriptor // DISA/DIFF header: find partition offset & size and DIFI descriptor
u32 offset_partition = 0; u32 offset_partition = 0;
u32 size_partition = 0; u32 size_partition = 0;
@ -207,22 +207,22 @@ u32 GetDisaDiffRWInfo(const char* path, DisaDiffRWInfo* info, bool partitionB) {
} else { } else {
return 1; return 1;
} }
// check the output so far // check the output so far
if (!offset_difi || (offset_difi + sizeof(DifiStruct) > file_size) || (offset_partition + size_partition > file_size)) if (!offset_difi || (offset_difi + sizeof(DifiStruct) > file_size) || (offset_partition + size_partition > file_size))
return 1; return 1;
info->offset_difi = offset_difi; info->offset_difi = offset_difi;
// read DIFI struct from file // read DIFI struct from file
const DifiStruct difis; const DifiStruct difis;
if (DisaDiffQRead(path, (DifiStruct*) &difis, offset_difi, sizeof(DifiStruct)) != FR_OK) if (DisaDiffQRead(path, (DifiStruct*) &difis, offset_difi, sizeof(DifiStruct)) != FR_OK)
return 1; return 1;
if ((memcmp(difis.difi.magic, difi_magic, 8) != 0) || if ((memcmp(difis.difi.magic, difi_magic, 8) != 0) ||
(memcmp(difis.ivfc.magic, ivfc_magic, 8) != 0) || (memcmp(difis.ivfc.magic, ivfc_magic, 8) != 0) ||
(memcmp(difis.dpfs.magic, dpfs_magic, 8) != 0)) (memcmp(difis.dpfs.magic, dpfs_magic, 8) != 0))
return 1; return 1;
// check & get data from DIFI header // check & get data from DIFI header
const DifiHeader* difi = &(difis.difi); const DifiHeader* difi = &(difis.difi);
if ((difi->offset_ivfc != sizeof(DifiHeader)) || if ((difi->offset_ivfc != sizeof(DifiHeader)) ||
@ -232,12 +232,12 @@ u32 GetDisaDiffRWInfo(const char* path, DisaDiffRWInfo* info, bool partitionB) {
(difi->offset_hash != difi->offset_dpfs + difi->size_dpfs) || (difi->offset_hash != difi->offset_dpfs + difi->size_dpfs) ||
(difi->size_hash < 0x20)) (difi->size_hash < 0x20))
return 1; return 1;
info->dpfs_lvl1_selector = difi->dpfs_lvl1_selector; info->dpfs_lvl1_selector = difi->dpfs_lvl1_selector;
info->ivfc_use_extlvl4 = difi->ivfc_use_extlvl4; info->ivfc_use_extlvl4 = difi->ivfc_use_extlvl4;
info->offset_ivfc_lvl4 = (u32) (offset_partition + difi->ivfc_offset_extlvl4); info->offset_ivfc_lvl4 = (u32) (offset_partition + difi->ivfc_offset_extlvl4);
info->offset_master_hash = (u32) difi->offset_hash; info->offset_master_hash = (u32) difi->offset_hash;
// check & get data from DPFS descriptor // check & get data from DPFS descriptor
const DpfsDescriptor* dpfs = &(difis.dpfs); const DpfsDescriptor* dpfs = &(difis.dpfs);
if ((dpfs->offset_lvl1 + dpfs->size_lvl1 > dpfs->offset_lvl2) || if ((dpfs->offset_lvl1 + dpfs->size_lvl1 > dpfs->offset_lvl2) ||
@ -246,7 +246,7 @@ u32 GetDisaDiffRWInfo(const char* path, DisaDiffRWInfo* info, bool partitionB) {
(2 > dpfs->log_lvl2) || (dpfs->log_lvl2 > dpfs->log_lvl3) || (2 > dpfs->log_lvl2) || (dpfs->log_lvl2 > dpfs->log_lvl3) ||
!dpfs->size_lvl1 || !dpfs->size_lvl2 || !dpfs->size_lvl3) !dpfs->size_lvl1 || !dpfs->size_lvl2 || !dpfs->size_lvl3)
return 1; return 1;
info->offset_dpfs_lvl1 = (u32) (offset_partition + dpfs->offset_lvl1); info->offset_dpfs_lvl1 = (u32) (offset_partition + dpfs->offset_lvl1);
info->offset_dpfs_lvl2 = (u32) (offset_partition + dpfs->offset_lvl2); info->offset_dpfs_lvl2 = (u32) (offset_partition + dpfs->offset_lvl2);
info->offset_dpfs_lvl3 = (u32) (offset_partition + dpfs->offset_lvl3); info->offset_dpfs_lvl3 = (u32) (offset_partition + dpfs->offset_lvl3);
@ -255,7 +255,7 @@ u32 GetDisaDiffRWInfo(const char* path, DisaDiffRWInfo* info, bool partitionB) {
info->size_dpfs_lvl3 = (u32) dpfs->size_lvl3; info->size_dpfs_lvl3 = (u32) dpfs->size_lvl3;
info->log_dpfs_lvl2 = (u32) dpfs->log_lvl2; info->log_dpfs_lvl2 = (u32) dpfs->log_lvl2;
info->log_dpfs_lvl3 = (u32) dpfs->log_lvl3; info->log_dpfs_lvl3 = (u32) dpfs->log_lvl3;
// check & get data from IVFC descriptor // check & get data from IVFC descriptor
const IvfcDescriptor* ivfc = &(difis.ivfc); const IvfcDescriptor* ivfc = &(difis.ivfc);
if ((ivfc->size_hash != difi->size_hash) || if ((ivfc->size_hash != difi->size_hash) ||
@ -264,16 +264,16 @@ u32 GetDisaDiffRWInfo(const char* path, DisaDiffRWInfo* info, bool partitionB) {
(ivfc->offset_lvl2 + ivfc->size_lvl2 > ivfc->offset_lvl3) || (ivfc->offset_lvl2 + ivfc->size_lvl2 > ivfc->offset_lvl3) ||
(ivfc->offset_lvl3 + ivfc->size_lvl3 > dpfs->size_lvl3)) (ivfc->offset_lvl3 + ivfc->size_lvl3 > dpfs->size_lvl3))
return 1; return 1;
if (!info->ivfc_use_extlvl4) { if (!info->ivfc_use_extlvl4) {
if ((ivfc->offset_lvl3 + ivfc->size_lvl3 > ivfc->offset_lvl4) || if ((ivfc->offset_lvl3 + ivfc->size_lvl3 > ivfc->offset_lvl4) ||
(ivfc->offset_lvl4 + ivfc->size_lvl4 > dpfs->size_lvl3)) (ivfc->offset_lvl4 + ivfc->size_lvl4 > dpfs->size_lvl3))
return 1; return 1;
info->offset_ivfc_lvl4 = (u32) ivfc->offset_lvl4; info->offset_ivfc_lvl4 = (u32) ivfc->offset_lvl4;
} else if (info->offset_ivfc_lvl4 + ivfc->size_lvl4 > offset_partition + size_partition) } else if (info->offset_ivfc_lvl4 + ivfc->size_lvl4 > offset_partition + size_partition)
return 1; return 1;
info->log_ivfc_lvl1 = (u32) ivfc->log_lvl1; info->log_ivfc_lvl1 = (u32) ivfc->log_lvl1;
info->log_ivfc_lvl2 = (u32) ivfc->log_lvl2; info->log_ivfc_lvl2 = (u32) ivfc->log_lvl2;
info->log_ivfc_lvl3 = (u32) ivfc->log_lvl3; info->log_ivfc_lvl3 = (u32) ivfc->log_lvl3;
@ -285,7 +285,7 @@ u32 GetDisaDiffRWInfo(const char* path, DisaDiffRWInfo* info, bool partitionB) {
info->size_ivfc_lvl2 = (u32) ivfc->size_lvl2; info->size_ivfc_lvl2 = (u32) ivfc->size_lvl2;
info->size_ivfc_lvl3 = (u32) ivfc->size_lvl3; info->size_ivfc_lvl3 = (u32) ivfc->size_lvl3;
info->size_ivfc_lvl4 = (u32) ivfc->size_lvl4; info->size_ivfc_lvl4 = (u32) ivfc->size_lvl4;
return 0; return 0;
} }
@ -293,31 +293,31 @@ u32 BuildDisaDiffDpfsLvl2Cache(const char* path, const DisaDiffRWInfo* info, u8*
const u32 min_cache_bits = (info->size_dpfs_lvl3 + (1 << info->log_dpfs_lvl3) - 1) >> info->log_dpfs_lvl3; const u32 min_cache_bits = (info->size_dpfs_lvl3 + (1 << info->log_dpfs_lvl3) - 1) >> info->log_dpfs_lvl3;
const u32 min_cache_size = ((min_cache_bits + 31) >> (3 + 2)) << 2; const u32 min_cache_size = ((min_cache_bits + 31) >> (3 + 2)) << 2;
const u32 offset_lvl1 = info->offset_dpfs_lvl1 + ((info->dpfs_lvl1_selector) ? info->size_dpfs_lvl1 : 0); const u32 offset_lvl1 = info->offset_dpfs_lvl1 + ((info->dpfs_lvl1_selector) ? info->size_dpfs_lvl1 : 0);
// safety (this still assumes all the checks from GetDisaDiffRWInfo()) // safety (this still assumes all the checks from GetDisaDiffRWInfo())
if ((cache_size < min_cache_size) || if ((cache_size < min_cache_size) ||
(min_cache_size > info->size_dpfs_lvl2) || (min_cache_size > info->size_dpfs_lvl2) ||
(min_cache_size > (info->size_dpfs_lvl1 << (3 + info->log_dpfs_lvl2)))) { (min_cache_size > (info->size_dpfs_lvl1 << (3 + info->log_dpfs_lvl2)))) {
return 1; return 1;
} }
// allocate memory // allocate memory
u8* lvl1 = (u8*) malloc(info->size_dpfs_lvl1); u8* lvl1 = (u8*) malloc(info->size_dpfs_lvl1);
if (!lvl1) return 1; // this is never more than 8 byte in reality -___- if (!lvl1) return 1; // this is never more than 8 byte in reality -___-
// open file pointer // open file pointer
if (DisaDiffOpen(path) != FR_OK) { if (DisaDiffOpen(path) != FR_OK) {
free(lvl1); free(lvl1);
return 1; return 1;
} }
// read lvl1 // read lvl1
u32 ret = 0; u32 ret = 0;
if ((ret != 0) || DisaDiffRead(lvl1, info->size_dpfs_lvl1, offset_lvl1)) ret = 1; if ((ret != 0) || DisaDiffRead(lvl1, info->size_dpfs_lvl1, offset_lvl1)) ret = 1;
// read full lvl2_0 to cache // read full lvl2_0 to cache
if ((ret != 0) || DisaDiffRead(cache, info->size_dpfs_lvl2, info->offset_dpfs_lvl2)) ret = 1; if ((ret != 0) || DisaDiffRead(cache, info->size_dpfs_lvl2, info->offset_dpfs_lvl2)) ret = 1;
// cherry-pick lvl2_1 // cherry-pick lvl2_1
u32 log_lvl2 = info->log_dpfs_lvl2; u32 log_lvl2 = info->log_dpfs_lvl2;
u32 offset_lvl2_1 = info->offset_dpfs_lvl2 + info->size_dpfs_lvl2; u32 offset_lvl2_1 = info->offset_dpfs_lvl2 + info->size_dpfs_lvl2;
@ -330,7 +330,7 @@ u32 BuildDisaDiffDpfsLvl2Cache(const char* path, const DisaDiffRWInfo* info, u8*
} }
} }
} }
((DisaDiffRWInfo*) info)->dpfs_lvl2_cache = cache; ((DisaDiffRWInfo*) info)->dpfs_lvl2_cache = cache;
free(lvl1); free(lvl1);
DisaDiffClose(); DisaDiffClose();
@ -344,11 +344,11 @@ static u32 ReadDisaDiffDpfsLvl3(const DisaDiffRWInfo* info, u32 offset, u32 size
const u32 offset_lvl3_0 = info->offset_dpfs_lvl3; const u32 offset_lvl3_0 = info->offset_dpfs_lvl3;
const u32 offset_lvl3_1 = offset_lvl3_0 + info->size_dpfs_lvl3; const u32 offset_lvl3_1 = offset_lvl3_0 + info->size_dpfs_lvl3;
const u32 log_lvl3 = info->log_dpfs_lvl3; const u32 log_lvl3 = info->log_dpfs_lvl3;
u32 read_start = offset_start; u32 read_start = offset_start;
u32 read_end = read_start; u32 read_end = read_start;
u32 bit_state = 0; u32 bit_state = 0;
// full reading below // full reading below
while (size && (read_start < offset_end)) { while (size && (read_start < offset_end)) {
// read bits until bit_state does not match // read bits until bit_state does not match
@ -370,7 +370,7 @@ static u32 ReadDisaDiffDpfsLvl3(const DisaDiffRWInfo* info, u32 offset, u32 size
// flip the bit_state // flip the bit_state
bit_state = ~bit_state & 0x1; bit_state = ~bit_state & 0x1;
} }
return size; return size;
} }
@ -381,11 +381,11 @@ static u32 WriteDisaDiffDpfsLvl3(const DisaDiffRWInfo* info, u32 offset, u32 siz
const u32 offset_lvl3_0 = info->offset_dpfs_lvl3; const u32 offset_lvl3_0 = info->offset_dpfs_lvl3;
const u32 offset_lvl3_1 = offset_lvl3_0 + info->size_dpfs_lvl3; const u32 offset_lvl3_1 = offset_lvl3_0 + info->size_dpfs_lvl3;
const u32 log_lvl3 = info->log_dpfs_lvl3; const u32 log_lvl3 = info->log_dpfs_lvl3;
u32 write_start = offset_start; u32 write_start = offset_start;
u32 write_end = write_start; u32 write_end = write_start;
u32 bit_state = 0; u32 bit_state = 0;
// full reading below // full reading below
while (size && (write_start < offset_end)) { while (size && (write_start < offset_end)) {
// write bits until bit_state does not match // write bits until bit_state does not match
@ -407,40 +407,40 @@ static u32 WriteDisaDiffDpfsLvl3(const DisaDiffRWInfo* info, u32 offset, u32 siz
// flip the bit_state // flip the bit_state
bit_state = ~bit_state & 0x1; bit_state = ~bit_state & 0x1;
} }
return size; return size;
} }
u32 FixDisaDiffPartitionHash(const DisaDiffRWInfo* info) { u32 FixDisaDiffPartitionHash(const DisaDiffRWInfo* info) {
const u32 size = info->size_table; const u32 size = info->size_table;
u8 sha_buf[0x20]; u8 sha_buf[0x20];
u8* buf; u8* buf;
if (!(buf = malloc(size))) if (!(buf = malloc(size)))
return 1; return 1;
if (DisaDiffRead(buf, size, info->offset_table) != FR_OK) { if (DisaDiffRead(buf, size, info->offset_table) != FR_OK) {
free(buf); free(buf);
return 1; return 1;
} }
sha_quick(sha_buf, buf, size, SHA256_MODE); sha_quick(sha_buf, buf, size, SHA256_MODE);
free(buf); free(buf);
if (DisaDiffWrite(sha_buf, 0x20, info->offset_partition_hash) != FR_OK) if (DisaDiffWrite(sha_buf, 0x20, info->offset_partition_hash) != FR_OK)
return 1; return 1;
return 0; return 0;
} }
u32 FixDisaDiffIvfcLevel(const DisaDiffRWInfo* info, u32 level, u32 offset, u32 size, u32* next_offset, u32* next_size) { u32 FixDisaDiffIvfcLevel(const DisaDiffRWInfo* info, u32 level, u32 offset, u32 size, u32* next_offset, u32* next_size) {
if (level == 0) if (level == 0)
return FixDisaDiffPartitionHash(info); return FixDisaDiffPartitionHash(info);
if (level > 4) if (level > 4)
return 1; return 1;
const u32 offset_ivfc_lvl = (&(info->offset_ivfc_lvl1))[level - 1]; const u32 offset_ivfc_lvl = (&(info->offset_ivfc_lvl1))[level - 1];
const u32 size_ivfc_lvl = (&(info->size_ivfc_lvl1))[level - 1]; const u32 size_ivfc_lvl = (&(info->size_ivfc_lvl1))[level - 1];
const u32 log_ivfc_lvl = (&(info->log_ivfc_lvl1))[level - 1]; const u32 log_ivfc_lvl = (&(info->log_ivfc_lvl1))[level - 1];
@ -448,48 +448,48 @@ u32 FixDisaDiffIvfcLevel(const DisaDiffRWInfo* info, u32 level, u32 offset, u32
u32 read_size = block_size; u32 read_size = block_size;
u32 align_offset = (offset >> log_ivfc_lvl) << log_ivfc_lvl; // align starting offset u32 align_offset = (offset >> log_ivfc_lvl) << log_ivfc_lvl; // align starting offset
u32 align_size = size + offset - align_offset; // increase size by the amount starting offset decreased when aligned u32 align_size = size + offset - align_offset; // increase size by the amount starting offset decreased when aligned
if (level != 1) { if (level != 1) {
if (next_offset) *next_offset = (align_offset >> log_ivfc_lvl) * 0x20; if (next_offset) *next_offset = (align_offset >> log_ivfc_lvl) * 0x20;
if (next_size) *next_size = ((align_size >> log_ivfc_lvl) + (((align_size % block_size) == 0) ? 0 : 1)) * 0x20; if (next_size) *next_size = ((align_size >> log_ivfc_lvl) + (((align_size % block_size) == 0) ? 0 : 1)) * 0x20;
} }
u8 sha_buf[0x20]; u8 sha_buf[0x20];
u8* buf; u8* buf;
if (!(buf = malloc(block_size))) if (!(buf = malloc(block_size)))
return 1; return 1;
while (align_size > 0) { while (align_size > 0) {
if (align_offset + block_size > size_ivfc_lvl) { if (align_offset + block_size > size_ivfc_lvl) {
memset(buf, 0, block_size); memset(buf, 0, block_size);
read_size -= (align_offset + block_size - size_ivfc_lvl); read_size -= (align_offset + block_size - size_ivfc_lvl);
} }
if (((level == 4) && info->ivfc_use_extlvl4) ? (DisaDiffRead(buf, read_size, align_offset + offset_ivfc_lvl) != FR_OK) : if (((level == 4) && info->ivfc_use_extlvl4) ? (DisaDiffRead(buf, read_size, align_offset + offset_ivfc_lvl) != FR_OK) :
(ReadDisaDiffDpfsLvl3(info, align_offset + offset_ivfc_lvl, read_size, buf) != read_size)) { (ReadDisaDiffDpfsLvl3(info, align_offset + offset_ivfc_lvl, read_size, buf) != read_size)) {
free(buf); free(buf);
return 1; return 1;
} }
sha_quick(sha_buf, buf, block_size, SHA256_MODE); sha_quick(sha_buf, buf, block_size, SHA256_MODE);
if ((level == 1) ? (DisaDiffWrite(sha_buf, 0x20, info->offset_difi + info->offset_master_hash + ((align_offset >> log_ivfc_lvl) * 0x20)) != FR_OK) : if ((level == 1) ? (DisaDiffWrite(sha_buf, 0x20, info->offset_difi + info->offset_master_hash + ((align_offset >> log_ivfc_lvl) * 0x20)) != FR_OK) :
(WriteDisaDiffDpfsLvl3(info, (&(info->offset_ivfc_lvl1))[level - 2] + ((align_offset >> log_ivfc_lvl) * 0x20), 0x20, sha_buf) != 0x20)) { (WriteDisaDiffDpfsLvl3(info, (&(info->offset_ivfc_lvl1))[level - 2] + ((align_offset >> log_ivfc_lvl) * 0x20), 0x20, sha_buf) != 0x20)) {
free(buf); free(buf);
return 1; return 1;
} }
align_offset += block_size; align_offset += block_size;
align_size = ((align_size < block_size) ? 0 : (align_size - block_size)); align_size = ((align_size < block_size) ? 0 : (align_size - block_size));
} }
free(buf); free(buf);
return 0; return 0;
} }
u32 ReadDisaDiffIvfcLvl4(const char* path, const DisaDiffRWInfo* info, u32 offset, u32 size, void* buffer) { // offset: offset inside IVFC lvl4 u32 ReadDisaDiffIvfcLvl4(const char* path, const DisaDiffRWInfo* info, u32 offset, u32 size, void* buffer) { // offset: offset inside IVFC lvl4
// DisaDiffRWInfo not provided? // DisaDiffRWInfo not provided?
DisaDiffRWInfo info_l; DisaDiffRWInfo info_l;
@ -504,15 +504,15 @@ u32 ReadDisaDiffIvfcLvl4(const char* path, const DisaDiffRWInfo* info, u32 offse
return 0; return 0;
} }
} }
// open file pointer // open file pointer
if (DisaDiffOpen(path) != FR_OK) if (DisaDiffOpen(path) != FR_OK)
size = 0; size = 0;
// sanity checks - offset & size // sanity checks - offset & size
if (offset > info->size_ivfc_lvl4) return 0; if (offset > info->size_ivfc_lvl4) return 0;
else if (offset + size > info->size_ivfc_lvl4) size = info->size_ivfc_lvl4 - offset; else if (offset + size > info->size_ivfc_lvl4) size = info->size_ivfc_lvl4 - offset;
if (info->ivfc_use_extlvl4) { if (info->ivfc_use_extlvl4) {
if (DisaDiffRead(buffer, size, info->offset_ivfc_lvl4 + offset) != FR_OK) if (DisaDiffRead(buffer, size, info->offset_ivfc_lvl4 + offset) != FR_OK)
size = 0; size = 0;
@ -541,22 +541,22 @@ u32 WriteDisaDiffIvfcLvl4(const char* path, const DisaDiffRWInfo* info, u32 offs
return 0; return 0;
} }
} }
// sanity check - offset & size // sanity check - offset & size
if (offset + size > info->size_ivfc_lvl4) if (offset + size > info->size_ivfc_lvl4)
return 0; return 0;
// open file pointer // open file pointer
if (DisaDiffOpen(path) != FR_OK) if (DisaDiffOpen(path) != FR_OK)
size = 0; size = 0;
if (info->ivfc_use_extlvl4) { if (info->ivfc_use_extlvl4) {
if (DisaDiffWrite(buffer, size, info->offset_ivfc_lvl4 + offset) != FR_OK) if (DisaDiffWrite(buffer, size, info->offset_ivfc_lvl4 + offset) != FR_OK)
size = 0; size = 0;
} else { } else {
size = WriteDisaDiffDpfsLvl3(info, info->offset_ivfc_lvl4 + offset, size, buffer); size = WriteDisaDiffDpfsLvl3(info, info->offset_ivfc_lvl4 + offset, size, buffer);
} }
if ((size != 0) && ddfp) { // if we're writing to a mounted image, the hash chain will be handled later by vdisadiff if ((size != 0) && ddfp) { // if we're writing to a mounted image, the hash chain will be handled later by vdisadiff
u32 hashfix_offset = offset, hashfix_size = size; u32 hashfix_offset = offset, hashfix_size = size;
for (int i = 4; i >= 0; i--) { for (int i = 4; i >= 0; i--) {
@ -566,7 +566,7 @@ u32 WriteDisaDiffIvfcLvl4(const char* path, const DisaDiffRWInfo* info, u32 offs
} }
} }
} }
DisaDiffClose(); DisaDiffClose();
if (cache) free(cache); if (cache) free(cache);
return size; return size;

View File

@ -169,10 +169,10 @@ u32 SetupSecretKey(u32 keynum) {
static u8 __attribute__((aligned(32))) sector[0x200]; static u8 __attribute__((aligned(32))) sector[0x200];
static u32 got_keys = 0; static u32 got_keys = 0;
u8* key = sector + (keynum*0x10); u8* key = sector + (keynum*0x10);
if (keynum >= 0x200/0x10) if (keynum >= 0x200/0x10)
return 1; // safety return 1; // safety
// try to load full secret sector // try to load full secret sector
if (!got_keys) { if (!got_keys) {
ReadNandSectors(sector, 0x96, 1, 0x11, NAND_SYSNAND); ReadNandSectors(sector, 0x96, 1, 0x11, NAND_SYSNAND);
@ -185,34 +185,34 @@ u32 SetupSecretKey(u32 keynum) {
if (LoadKeyFromFile(key, 0x11, 'N', (keynum == 0) ? "95" : "96") == 0) if (LoadKeyFromFile(key, 0x11, 'N', (keynum == 0) ? "95" : "96") == 0)
got_keys |= (0x1<<keynum); // got at least this key got_keys |= (0x1<<keynum); // got at least this key
} }
// setup the key // setup the key
if (got_keys & (0x1<<keynum)) { if (got_keys & (0x1<<keynum)) {
setup_aeskey(0x11, key); setup_aeskey(0x11, key);
use_aeskey(0x11); use_aeskey(0x11);
return 0; return 0;
} }
// out of options // out of options
return 1; return 1;
} }
u32 DecryptA9LHeader(FirmA9LHeader* header) { u32 DecryptA9LHeader(FirmA9LHeader* header) {
u32 type = A9L_CRYPTO_TYPE(header); u32 type = A9L_CRYPTO_TYPE(header);
if (SetupSecretKey(0) != 0) return 1; if (SetupSecretKey(0) != 0) return 1;
aes_decrypt(header->keyX0x15, header->keyX0x15, 1, AES_CNT_ECB_DECRYPT_MODE); aes_decrypt(header->keyX0x15, header->keyX0x15, 1, AES_CNT_ECB_DECRYPT_MODE);
if (type) { if (type) {
if (SetupSecretKey((type == 1) ? 0 : 1) != 0) return 1; if (SetupSecretKey((type == 1) ? 0 : 1) != 0) return 1;
aes_decrypt(header->keyX0x16, header->keyX0x16, 1, AES_CNT_ECB_DECRYPT_MODE); aes_decrypt(header->keyX0x16, header->keyX0x16, 1, AES_CNT_ECB_DECRYPT_MODE);
} }
return 0; return 0;
} }
u32 SetupArm9BinaryCrypto(FirmA9LHeader* header) { u32 SetupArm9BinaryCrypto(FirmA9LHeader* header) {
u32 type = A9L_CRYPTO_TYPE(header); u32 type = A9L_CRYPTO_TYPE(header);
if (type == 0) { if (type == 0) {
u8 __attribute__((aligned(32))) keyX0x15[0x10]; u8 __attribute__((aligned(32))) keyX0x15[0x10];
memcpy(keyX0x15, header->keyX0x15, 0x10); memcpy(keyX0x15, header->keyX0x15, 0x10);
@ -230,24 +230,24 @@ u32 SetupArm9BinaryCrypto(FirmA9LHeader* header) {
setup_aeskeyY(0x16, header->keyY0x150x16); setup_aeskeyY(0x16, header->keyY0x150x16);
use_aeskey(0x16); use_aeskey(0x16);
} }
return 0; return 0;
} }
u32 DecryptArm9Binary(void* data, u32 offset, u32 size, FirmA9LHeader* a9l) { u32 DecryptArm9Binary(void* data, u32 offset, u32 size, FirmA9LHeader* a9l) {
// offset == offset inside ARM9 binary // offset == offset inside ARM9 binary
// ARM9 binary begins 0x800 byte after the ARM9 loader header // ARM9 binary begins 0x800 byte after the ARM9 loader header
// only process actual ARM9 binary // only process actual ARM9 binary
u32 size_bin = GetArm9BinarySize(a9l); u32 size_bin = GetArm9BinarySize(a9l);
if (offset >= size_bin) return 0; if (offset >= size_bin) return 0;
else if (size >= size_bin - offset) else if (size >= size_bin - offset)
size = size_bin - offset; size = size_bin - offset;
// decrypt data // decrypt data
if (SetupArm9BinaryCrypto(a9l) != 0) return 1; if (SetupArm9BinaryCrypto(a9l) != 0) return 1;
ctr_decrypt_byte(data, data, size, offset, AES_CNT_CTRNAND_MODE, a9l->ctr); ctr_decrypt_byte(data, data, size, offset, AES_CNT_CTRNAND_MODE, a9l->ctr);
return 0; return 0;
} }
@ -256,16 +256,16 @@ u32 DecryptFirm(void* data, u32 offset, u32 size, FirmHeader* firm, FirmA9LHeade
FirmSectionHeader* arm9s = FindFirmArm9Section(firm); FirmSectionHeader* arm9s = FindFirmArm9Section(firm);
u32 offset_arm9bin = arm9s->offset + ARM9BIN_OFFSET; u32 offset_arm9bin = arm9s->offset + ARM9BIN_OFFSET;
u32 size_arm9bin = GetArm9BinarySize(a9l); u32 size_arm9bin = GetArm9BinarySize(a9l);
// sanity checks // sanity checks
if (!size_arm9bin || (size_arm9bin + ARM9BIN_OFFSET > arm9s->size)) if (!size_arm9bin || (size_arm9bin + ARM9BIN_OFFSET > arm9s->size))
return 1; // bad header / data return 1; // bad header / data
// check if ARM9 binary in data // check if ARM9 binary in data
if ((offset_arm9bin >= offset + size) || if ((offset_arm9bin >= offset + size) ||
(offset >= offset_arm9bin + size_arm9bin)) (offset >= offset_arm9bin + size_arm9bin))
return 0; // section not in data return 0; // section not in data
// determine data / offset / size // determine data / offset / size
u8* data8 = (u8*)data; u8* data8 = (u8*)data;
u8* data_i = data8; u8* data_i = data8;
@ -277,7 +277,7 @@ u32 DecryptFirm(void* data, u32 offset, u32 size, FirmHeader* firm, FirmA9LHeade
size_i = size_arm9bin - offset_i; size_i = size_arm9bin - offset_i;
if (size_i > size - (data_i - data8)) if (size_i > size - (data_i - data8))
size_i = size - (data_i - data8); size_i = size - (data_i - data8);
return DecryptArm9Binary(data_i, offset_i, size_i, a9l); return DecryptArm9Binary(data_i, offset_i, size_i, a9l);
} }
@ -290,7 +290,7 @@ u32 DecryptFirmSequential(void* data, u32 offset, u32 size) {
static FirmHeader* firmptr = NULL; static FirmHeader* firmptr = NULL;
static FirmA9LHeader* a9lptr = NULL; static FirmA9LHeader* a9lptr = NULL;
static FirmSectionHeader* arm9s = NULL; static FirmSectionHeader* arm9s = NULL;
// fetch firm header from data // fetch firm header from data
if ((offset == 0) && (size >= sizeof(FirmHeader))) { if ((offset == 0) && (size >= sizeof(FirmHeader))) {
memcpy(&firm, data, sizeof(FirmHeader)); memcpy(&firm, data, sizeof(FirmHeader));
@ -298,17 +298,17 @@ u32 DecryptFirmSequential(void* data, u32 offset, u32 size) {
arm9s = (firmptr) ? FindFirmArm9Section(firmptr) : NULL; arm9s = (firmptr) ? FindFirmArm9Section(firmptr) : NULL;
a9lptr = NULL; a9lptr = NULL;
} }
// safety check, firm header pointer // safety check, firm header pointer
if (!firmptr) return 1; if (!firmptr) return 1;
// fetch ARM9 loader header from data // fetch ARM9 loader header from data
if (arm9s && !a9lptr && (offset <= arm9s->offset) && if (arm9s && !a9lptr && (offset <= arm9s->offset) &&
((offset + size) >= arm9s->offset + sizeof(FirmA9LHeader))) { ((offset + size) >= arm9s->offset + sizeof(FirmA9LHeader))) {
memcpy(&a9l, (u8*)data + arm9s->offset - offset, sizeof(FirmA9LHeader)); memcpy(&a9l, (u8*)data + arm9s->offset - offset, sizeof(FirmA9LHeader));
a9lptr = (ValidateFirmA9LHeader(&a9l) == 0) ? &a9l : NULL; a9lptr = (ValidateFirmA9LHeader(&a9l) == 0) ? &a9l : NULL;
} }
return (a9lptr) ? DecryptFirm(data, offset, size, firmptr, a9lptr) : 0; return (a9lptr) ? DecryptFirm(data, offset, size, firmptr, a9lptr) : 0;
} }
@ -318,17 +318,17 @@ u32 DecryptFirmFull(void* data, u32 size) {
FirmSectionHeader* arm9s = FindFirmArm9Section(firm); FirmSectionHeader* arm9s = FindFirmArm9Section(firm);
if (ValidateFirmHeader(firm, size) != 0) return 1; // not a proper firm if (ValidateFirmHeader(firm, size) != 0) return 1; // not a proper firm
if (!arm9s) return 0; // no ARM9 section -> not encrypted -> done if (!arm9s) return 0; // no ARM9 section -> not encrypted -> done
FirmA9LHeader* a9l = (FirmA9LHeader*)(void*) ((u8*) data + arm9s->offset); FirmA9LHeader* a9l = (FirmA9LHeader*)(void*) ((u8*) data + arm9s->offset);
if (ValidateFirmA9LHeader(a9l) != 0) return 0; // no ARM9bin -> not encrypted -> done if (ValidateFirmA9LHeader(a9l) != 0) return 0; // no ARM9bin -> not encrypted -> done
// decrypt FIRM and ARM9loader header // decrypt FIRM and ARM9loader header
if ((DecryptFirm(data, 0, size, firm, a9l) != 0) || (DecryptA9LHeader(a9l) != 0)) if ((DecryptFirm(data, 0, size, firm, a9l) != 0) || (DecryptA9LHeader(a9l) != 0))
return 1; return 1;
// fix ARM9 section SHA and ARM9 entrypoint // fix ARM9 section SHA and ARM9 entrypoint
sha_quick(arm9s->hash, (u8*) data + arm9s->offset, arm9s->size, SHA256_MODE); sha_quick(arm9s->hash, (u8*) data + arm9s->offset, arm9s->size, SHA256_MODE);
firm->entry_arm9 = ARM9ENTRY_FIX(firm); firm->entry_arm9 = ARM9ENTRY_FIX(firm);
return 0; return 0;
} }

View File

@ -8,20 +8,20 @@
u32 ValidateAgbSaveHeader(AgbSaveHeader* header) { u32 ValidateAgbSaveHeader(AgbSaveHeader* header) {
static u8 magic[] = { AGBSAVE_MAGIC }; static u8 magic[] = { AGBSAVE_MAGIC };
// basic checks // basic checks
if ((memcmp(header->magic, magic, sizeof(magic)) != 0) || if ((memcmp(header->magic, magic, sizeof(magic)) != 0) ||
(header->unknown0 != 1) || (header->save_start != 0x200) || (header->unknown0 != 1) || (header->save_start != 0x200) ||
(header->save_size > AGBSAVE_MAX_SSIZE) || !(GBASAVE_VALID(header->save_size))) (header->save_size > AGBSAVE_MAX_SSIZE) || !(GBASAVE_VALID(header->save_size)))
return 1; return 1;
// reserved area checks // reserved area checks
// disabled due to a weird quirk https://github.com/d0k3/GodMode9/issues/412 // disabled due to a weird quirk https://github.com/d0k3/GodMode9/issues/412
// for (u32 i = 0; i < sizeof(header->reserved0); i++) if (header->reserved0[i] != 0xFF) return 1; // for (u32 i = 0; i < sizeof(header->reserved0); i++) if (header->reserved0[i] != 0xFF) return 1;
// for (u32 i = 0; i < sizeof(header->reserved1); i++) if (header->reserved1[i] != 0xFF) return 1; // for (u32 i = 0; i < sizeof(header->reserved1); i++) if (header->reserved1[i] != 0xFF) return 1;
// for (u32 i = 0; i < sizeof(header->reserved2); i++) if (header->reserved2[i] != 0xFF) return 1; // for (u32 i = 0; i < sizeof(header->reserved2); i++) if (header->reserved2[i] != 0xFF) return 1;
// for (u32 i = 0; i < sizeof(header->reserved3); i++) if (header->reserved3[i] != 0xFF) return 1; // for (u32 i = 0; i < sizeof(header->reserved3); i++) if (header->reserved3[i] != 0xFF) return 1;
// all fine if arriving here // all fine if arriving here
return 0; return 0;
} }
@ -30,60 +30,60 @@ u32 ValidateAgbSaveHeader(AgbSaveHeader* header) {
u32 ValidateAgbHeader(AgbHeader* agb) { u32 ValidateAgbHeader(AgbHeader* agb) {
static const u8 logo_sha[0x20] = { AGBLOGO_SHA256 }; static const u8 logo_sha[0x20] = { AGBLOGO_SHA256 };
u8 logo[0x9C] __attribute__((aligned(4))); u8 logo[0x9C] __attribute__((aligned(4)));
// check fixed value // check fixed value
if (agb->fixed != 0x96) return 1; if (agb->fixed != 0x96) return 1;
// header checksum // header checksum
u8* hdr = (u8*) agb; u8* hdr = (u8*) agb;
u8 checksum = 0x00 - 0x19; u8 checksum = 0x00 - 0x19;
for (u32 i = 0xA0; i < 0xBD; i++) for (u32 i = 0xA0; i < 0xBD; i++)
checksum -= hdr[i]; checksum -= hdr[i];
if (agb->checksum != checksum) return 1; if (agb->checksum != checksum) return 1;
// logo SHA check // logo SHA check
memcpy(logo, agb->logo, 0x9C); memcpy(logo, agb->logo, 0x9C);
logo[0x98] &= ~0x84; logo[0x98] &= ~0x84;
logo[0x9A] &= ~0x03; logo[0x9A] &= ~0x03;
if (sha_cmp(logo_sha, logo, 0x9C, SHA256_MODE) != 0) if (sha_cmp(logo_sha, logo, 0x9C, SHA256_MODE) != 0)
return 1; return 1;
return 0; return 0;
} }
/* u32 ValidateAgbVc(void* data, u32 len) { /* u32 ValidateAgbVc(void* data, u32 len) {
const u8 magic[] = { GBAVC_MAGIC }; const u8 magic[] = { GBAVC_MAGIC };
if (len < sizeof(AgbHeader) + sizeof(AgbVcFooter)) if (len < sizeof(AgbHeader) + sizeof(AgbVcFooter))
return 1; return 1;
AgbHeader* header = (AgbHeader*) data; AgbHeader* header = (AgbHeader*) data;
AgbVcFooter* footer = (AgbVcFooter*) (((u8*) data) + len - sizeof(AgbVcFooter)); AgbVcFooter* footer = (AgbVcFooter*) (((u8*) data) + len - sizeof(AgbVcFooter));
if ((ValidateAgbHeader(header) != 0) || (memcmp(footer->magic, magic, sizeof(magic)) != 0) || if ((ValidateAgbHeader(header) != 0) || (memcmp(footer->magic, magic, sizeof(magic)) != 0) ||
(footer->rom_size != len - sizeof(AgbVcFooter))) (footer->rom_size != len - sizeof(AgbVcFooter)))
return 1; return 1;
return 0; return 0;
} */ } */
// basically reverse ValidateAgbSaveHeader() // basically reverse ValidateAgbSaveHeader()
/* u32 BuildAgbSaveHeader(AgbSaveHeader* header, u64 title_id, u32 save_size) { /* u32 BuildAgbSaveHeader(AgbSaveHeader* header, u64 title_id, u32 save_size) {
const u8 magic[] = { AGBSAVE_MAGIC }; const u8 magic[] = { AGBSAVE_MAGIC };
memset(header, 0x00, sizeof(AgbSaveHeader)); memset(header, 0x00, sizeof(AgbSaveHeader));
memset(header->reserved0, 0xFF, sizeof(header->reserved0)); memset(header->reserved0, 0xFF, sizeof(header->reserved0));
memset(header->reserved1, 0xFF, sizeof(header->reserved1)); memset(header->reserved1, 0xFF, sizeof(header->reserved1));
memset(header->reserved2, 0xFF, sizeof(header->reserved2)); memset(header->reserved2, 0xFF, sizeof(header->reserved2));
memset(header->reserved3, 0xFF, sizeof(header->reserved3)); memset(header->reserved3, 0xFF, sizeof(header->reserved3));
memcpy(header->magic, magic, sizeof(magic)); memcpy(header->magic, magic, sizeof(magic));
header->unknown0 = 0x01; header->unknown0 = 0x01;
header->title_id = title_id; header->title_id = title_id;
header->save_start = 0x200; header->save_start = 0x200;
header->save_size = save_size; header->save_size = save_size;
sdmmc_get_cid(0, (u32*) (void*) &(header->sd_cid)); sdmmc_get_cid(0, (u32*) (void*) &(header->sd_cid));
return 0; return 0;
} */ } */

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "common.h" #include "common.h"
#define GBAVC_MAGIC '.', 'C', 'A', 'A' #define GBAVC_MAGIC '.', 'C', 'A', 'A'
#define AGBSAVE_MAGIC '.', 'S', 'A', 'V' #define AGBSAVE_MAGIC '.', 'S', 'A', 'V'
#define AGBSAVE_MAX_SIZE (0x000180 * 0x200) // standard size of the NAND partition #define AGBSAVE_MAX_SIZE (0x000180 * 0x200) // standard size of the NAND partition
@ -20,14 +20,14 @@
((tp >= 0x4) && (tp <= 0x9)) ? GBASAVE_FLASH_64K : \ ((tp >= 0x4) && (tp <= 0x9)) ? GBASAVE_FLASH_64K : \
((tp >= 0xA) && (tp <= 0xD)) ? GBASAVE_FLASH_128K : \ ((tp >= 0xA) && (tp <= 0xD)) ? GBASAVE_FLASH_128K : \
(tp == 0xE) ? GBASAVE_SRAM_32K : 0); // last one means invalid (tp == 0xE) ? GBASAVE_SRAM_32K : 0); // last one means invalid
#define GBASAVE_VALID(size) \ #define GBASAVE_VALID(size) \
(((size) == GBASAVE_EEPROM_512) || \ (((size) == GBASAVE_EEPROM_512) || \
((size) == GBASAVE_EEPROM_8K) || \ ((size) == GBASAVE_EEPROM_8K) || \
((size) == GBASAVE_SRAM_32K) || \ ((size) == GBASAVE_SRAM_32K) || \
((size) == GBASAVE_FLASH_64K) || \ ((size) == GBASAVE_FLASH_64K) || \
((size) == GBASAVE_FLASH_128K)) ((size) == GBASAVE_FLASH_128K))
// see: http://problemkaputt.de/gbatek.htm#gbacartridgeheader // see: http://problemkaputt.de/gbatek.htm#gbacartridgeheader
#define AGB_DESTSTR(code) \ #define AGB_DESTSTR(code) \
(((code)[3] == 'J') ? "Japan" : \ (((code)[3] == 'J') ? "Japan" : \
@ -36,9 +36,9 @@
((code)[3] == 'D') ? "German" : \ ((code)[3] == 'D') ? "German" : \
((code)[3] == 'F') ? "French" : \ ((code)[3] == 'F') ? "French" : \
((code)[3] == 'I') ? "Italian" : \ ((code)[3] == 'I') ? "Italian" : \
((code)[3] == 'S') ? "Spanish" : "Unknown") ((code)[3] == 'S') ? "Spanish" : "Unknown")
// see: http://3dbrew.org/wiki/3DS_Virtual_Console#Footer // see: http://3dbrew.org/wiki/3DS_Virtual_Console#Footer
// still a lot of unknowns in here, also redundant stuff left out // still a lot of unknowns in here, also redundant stuff left out
typedef struct { typedef struct {
@ -73,7 +73,7 @@ typedef struct {
// see: http://problemkaputt.de/gbatek.htm#gbacartridgeheader // see: http://problemkaputt.de/gbatek.htm#gbacartridgeheader
typedef struct { typedef struct {
u32 arm7_rom_entry; u32 arm7_rom_entry;
u8 logo[0x9C]; u8 logo[0x9C];
char game_title[12]; char game_title[12];
char game_code[4]; char game_code[4];

View File

@ -113,14 +113,14 @@ int ApplyIPSPatch(const char* patchName, const char* inName, const char* outName
int error = IPS_INVALID; int error = IPS_INVALID;
UINT outlen_min, outlen_max, outlen_min_mem; UINT outlen_min, outlen_max, outlen_min_mem;
snprintf(errName, 256, "%s", patchName); snprintf(errName, 256, "%s", patchName);
if (fvx_open(&patchFile, patchName, FA_READ) != FR_OK) return displayError(IPS_INVALID_FILE_PATH); if (fvx_open(&patchFile, patchName, FA_READ) != FR_OK) return displayError(IPS_INVALID_FILE_PATH);
patchSize = fvx_size(&patchFile); patchSize = fvx_size(&patchFile);
ShowProgress(0, patchSize, patchName); ShowProgress(0, patchSize, patchName);
patch = malloc(patchSize); patch = malloc(patchSize);
if (!patch || fvx_read(&patchFile, patch, patchSize, NULL) != FR_OK) return displayError(IPS_MEMORY); if (!patch || fvx_read(&patchFile, patch, patchSize, NULL) != FR_OK) return displayError(IPS_MEMORY);
// Check validity of patch // Check validity of patch
if (patchSize < 8) return displayError(IPS_INVALID); if (patchSize < 8) return displayError(IPS_INVALID);
if (read8() != 'P' || if (read8() != 'P' ||
@ -131,7 +131,7 @@ int ApplyIPSPatch(const char* patchName, const char* inName, const char* outName
{ {
return displayError(IPS_INVALID); return displayError(IPS_INVALID);
} }
unsigned int offset = read24(); unsigned int offset = read24();
unsigned int outlen = 0; unsigned int outlen = 0;
unsigned int thisout = 0; unsigned int thisout = 0;
@ -144,7 +144,7 @@ int ApplyIPSPatch(const char* patchName, const char* inName, const char* outName
ShowProgress(0, patchSize, patchName); ShowProgress(0, patchSize, patchName);
ShowProgress(patchOffset, patchSize, patchName); ShowProgress(patchOffset, patchSize, patchName);
} }
unsigned int size = read16(); unsigned int size = read16();
if (size == 0) if (size == 0)
{ {
@ -180,7 +180,7 @@ int ApplyIPSPatch(const char* patchName, const char* inName, const char* outName
outlen_min = outlen; outlen_min = outlen;
error = IPS_OK; error = IPS_OK;
if (w_scrambled) error = IPS_SCRAMBLED; if (w_scrambled) error = IPS_SCRAMBLED;
// start applying patch // start applying patch
bool inPlace = false; bool inPlace = false;
if (!CheckWritePermissions(outName)) return displayError(IPS_INVALID_FILE_PATH); if (!CheckWritePermissions(outName)) return displayError(IPS_INVALID_FILE_PATH);
@ -193,19 +193,19 @@ int ApplyIPSPatch(const char* patchName, const char* inName, const char* outName
else if ((fvx_open(&inFile, inName, FA_READ) != FR_OK) || else if ((fvx_open(&inFile, inName, FA_READ) != FR_OK) ||
(fvx_open(&outFile, outName, FA_CREATE_ALWAYS | FA_WRITE | FA_READ) != FR_OK)) (fvx_open(&outFile, outName, FA_CREATE_ALWAYS | FA_WRITE | FA_READ) != FR_OK))
return displayError(IPS_INVALID_FILE_PATH); return displayError(IPS_INVALID_FILE_PATH);
size_t inSize = fvx_size(&inFile); size_t inSize = fvx_size(&inFile);
outlen = max(outlen_min, min(inSize, outlen_max)); outlen = max(outlen_min, min(inSize, outlen_max));
fvx_lseek(&outFile, max(outlen, outlen_min_mem)); fvx_lseek(&outFile, max(outlen, outlen_min_mem));
fvx_lseek(&outFile, 0); fvx_lseek(&outFile, 0);
size_t outSize = outlen; size_t outSize = outlen;
ShowProgress(0, outSize, outName); ShowProgress(0, outSize, outName);
fvx_lseek(&inFile, 0); fvx_lseek(&inFile, 0);
if (!inPlace && !IPScopy(COPY_IN, min(inSize, outlen), 0)) return displayError(IPS_MEMORY); if (!inPlace && !IPScopy(COPY_IN, min(inSize, outlen), 0)) return displayError(IPS_MEMORY);
fvx_lseek(&outFile, inSize); fvx_lseek(&outFile, inSize);
if (outSize > inSize && !IPScopy(COPY_RLE, outSize - inSize, 0)) return displayError(IPS_MEMORY); if (outSize > inSize && !IPScopy(COPY_RLE, outSize - inSize, 0)) return displayError(IPS_MEMORY);
fvx_lseek(&patchFile, 5); fvx_lseek(&patchFile, 5);
offset = read24(); offset = read24();
while (offset != 0x454F46) while (offset != 0x454F46)
@ -215,14 +215,14 @@ int ApplyIPSPatch(const char* patchName, const char* inName, const char* outName
ShowProgress(0, outSize, outName); ShowProgress(0, outSize, outName);
ShowProgress(offset, outSize, outName); ShowProgress(offset, outSize, outName);
} }
fvx_lseek(&outFile, offset); fvx_lseek(&outFile, offset);
unsigned int size = read16(); unsigned int size = read16();
if (size == 0 && !IPScopy(COPY_RLE, read16(), read8())) return displayError(IPS_MEMORY); if (size == 0 && !IPScopy(COPY_RLE, read16(), read8())) return displayError(IPS_MEMORY);
else if (size != 0 && !IPScopy(COPY_PATCH, size, 0)) return displayError(IPS_MEMORY); else if (size != 0 && !IPScopy(COPY_PATCH, size, 0)) return displayError(IPS_MEMORY);
offset = read24(); offset = read24();
} }
fvx_lseek(&outFile, outSize); fvx_lseek(&outFile, outSize);
f_truncate(&outFile); f_truncate(&outFile);
return displayError(error); return displayError(error);

View File

@ -11,7 +11,7 @@
u32 ValidateNcchHeader(NcchHeader* header) { u32 ValidateNcchHeader(NcchHeader* header) {
if (memcmp(header->magic, "NCCH", 4) != 0) // check magic number if (memcmp(header->magic, "NCCH", 4) != 0) // check magic number
return 1; return 1;
u32 ncch_units = (NCCH_EXTHDR_OFFSET + header->size_exthdr) / NCCH_MEDIA_UNIT; // exthdr u32 ncch_units = (NCCH_EXTHDR_OFFSET + header->size_exthdr) / NCCH_MEDIA_UNIT; // exthdr
if (header->size_plain) { // plain region if (header->size_plain) { // plain region
if (header->offset_plain < ncch_units) return 1; // overlapping plain region if (header->offset_plain < ncch_units) return 1; // overlapping plain region
@ -26,8 +26,8 @@ u32 ValidateNcchHeader(NcchHeader* header) {
ncch_units = (header->offset_romfs + header->size_romfs); ncch_units = (header->offset_romfs + header->size_romfs);
} }
// size check // size check
if (ncch_units > header->size) return 1; if (ncch_units > header->size) return 1;
return 0; return 0;
} }
@ -36,7 +36,7 @@ u32 GetNcchCtr(u8* ctr, NcchHeader* ncch, u8 section) {
if (ncch->version == 1) { if (ncch->version == 1) {
memcpy(ctr, &(ncch->partitionId), 8); memcpy(ctr, &(ncch->partitionId), 8);
if (section == 1) { // ExtHeader ctr if (section == 1) { // ExtHeader ctr
add_ctr(ctr, NCCH_EXTHDR_OFFSET); add_ctr(ctr, NCCH_EXTHDR_OFFSET);
} else if (section == 2) { // ExeFS ctr } else if (section == 2) { // ExeFS ctr
add_ctr(ctr, ncch->offset_exefs * NCCH_MEDIA_UNIT); add_ctr(ctr, ncch->offset_exefs * NCCH_MEDIA_UNIT);
} else if (section == 3) { // RomFS ctr } else if (section == 3) { // RomFS ctr
@ -47,7 +47,7 @@ u32 GetNcchCtr(u8* ctr, NcchHeader* ncch, u8 section) {
ctr[i] = ((u8*) &(ncch->partitionId))[7-i]; ctr[i] = ((u8*) &(ncch->partitionId))[7-i];
ctr[8] = section; ctr[8] = section;
} }
return 0; return 0;
} }
@ -56,25 +56,25 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) {
u64 titleId = ncch->programId; u64 titleId = ncch->programId;
u32 hash_seed = ncch->hash_seed; u32 hash_seed = ncch->hash_seed;
u32 sha256sum[8]; u32 sha256sum[8];
memcpy(lseed+16, &(ncch->programId), 8); memcpy(lseed+16, &(ncch->programId), 8);
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);
return 0; return 0;
} }
// setup a large enough buffer // setup a large enough buffer
u8* buffer = (u8*) malloc(max(STD_BUFFER_SIZE, SEEDSAVE_AREA_SIZE)); u8* buffer = (u8*) malloc(max(STD_BUFFER_SIZE, SEEDSAVE_AREA_SIZE));
if (!buffer) return 1; if (!buffer) return 1;
// try to grab the seed from NAND database // try to grab the seed from NAND database
const char* nand_drv[] = {"1:", "4:"}; // SysNAND and EmuNAND const char* nand_drv[] = {"1:", "4:"}; // SysNAND and EmuNAND
for (u32 i = 0; i < countof(nand_drv); i++) { for (u32 i = 0; i < countof(nand_drv); i++) {
UINT btr = 0; UINT btr = 0;
FIL file; FIL file;
char path[128]; char path[128];
// grab the key Y from movable.sed // grab the key Y from movable.sed
u8 movable_keyy[16]; u8 movable_keyy[16];
snprintf(path, 128, "%s/private/movable.sed", nand_drv[i]); snprintf(path, 128, "%s/private/movable.sed", nand_drv[i]);
@ -83,17 +83,17 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) {
f_lseek(&file, 0x110); f_lseek(&file, 0x110);
f_read(&file, movable_keyy, 0x10, &btr); f_read(&file, movable_keyy, 0x10, &btr);
f_close(&file); f_close(&file);
// build the seed save path // build the seed save path
sha_quick(sha256sum, movable_keyy, 0x10, SHA256_MODE); sha_quick(sha256sum, movable_keyy, 0x10, SHA256_MODE);
snprintf(path, 128, "%s/data/%08lX%08lX%08lX%08lX/sysdata/0001000F/00000000", snprintf(path, 128, "%s/data/%08lX%08lX%08lX%08lX/sysdata/0001000F/00000000",
nand_drv[i], sha256sum[0], sha256sum[1], sha256sum[2], sha256sum[3]); nand_drv[i], sha256sum[0], sha256sum[1], sha256sum[2], sha256sum[3]);
// check seedsave for seed // check seedsave for seed
u8* seeddb = buffer; u8* seeddb = buffer;
if (ReadDisaDiffIvfcLvl4(path, NULL, SEEDSAVE_AREA_OFFSET, SEEDSAVE_AREA_SIZE, seeddb) != SEEDSAVE_AREA_SIZE) if (ReadDisaDiffIvfcLvl4(path, NULL, SEEDSAVE_AREA_OFFSET, SEEDSAVE_AREA_SIZE, seeddb) != SEEDSAVE_AREA_SIZE)
continue; continue;
// search for the seed // search for the seed
for (u32 s = 0; s < SEEDSAVE_MAX_ENTRIES; s++) { for (u32 s = 0; s < SEEDSAVE_MAX_ENTRIES; s++) {
if (titleId != getle64(seeddb + (s*8))) continue; if (titleId != getle64(seeddb + (s*8))) continue;
@ -106,10 +106,10 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) {
} }
} }
} }
// not found -> try seeddb.bin // not found -> try seeddb.bin
SeedInfo* seeddb = (SeedInfo*) (void*) buffer; SeedInfo* seeddb = (SeedInfo*) (void*) buffer;
size_t len = LoadSupportFile(SEEDDB_NAME, seeddb, STD_BUFFER_SIZE); 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)
@ -123,7 +123,7 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) {
} }
} }
} }
// out of options -> failed! // out of options -> failed!
free(buffer); free(buffer);
return 1; return 1;
@ -150,10 +150,10 @@ u32 SetNcchKey(NcchHeader* ncch, u16 crypto, u32 keyid) {
u8 flags7 = crypto & 0xFF; u8 flags7 = crypto & 0xFF;
u32 keyslot = (!keyid || !flags3) ? 0x2C : // standard / secure3 / secure4 / 7.x crypto u32 keyslot = (!keyid || !flags3) ? 0x2C : // standard / secure3 / secure4 / 7.x crypto
(flags3 == 0x0A) ? 0x18 : (flags3 == 0x0B) ? 0x1B : 0x25; (flags3 == 0x0A) ? 0x18 : (flags3 == 0x0B) ? 0x1B : 0x25;
if (flags7 & 0x04) if (flags7 & 0x04)
return 1; return 1;
if (flags7 & 0x01) { // fixed key crypto if (flags7 & 0x01) { // fixed key crypto
// from https://github.com/profi200/Project_CTR/blob/master/makerom/pki/dev.h // from https://github.com/profi200/Project_CTR/blob/master/makerom/pki/dev.h
u8 zeroKey[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, u8 zeroKey[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -164,11 +164,11 @@ u32 SetNcchKey(NcchHeader* ncch, u16 crypto, u32 keyid) {
use_aeskey(0x11); use_aeskey(0x11);
return 0; return 0;
} }
// load key X from file if required // load key X from file if required
if ((keyslot != 0x2C) && (LoadKeyFromFile(NULL, keyslot, 'X', NULL) != 0)) if ((keyslot != 0x2C) && (LoadKeyFromFile(NULL, keyslot, 'X', NULL) != 0))
return 1; return 1;
// key Y for seed and non seed // key Y for seed and non seed
if (keyid && (flags7 & 0x20)) { // seed crypto if (keyid && (flags7 & 0x20)) { // seed crypto
static u8 seedkeyY[16+16] __attribute__((aligned(32))) = { 0 }; static u8 seedkeyY[16+16] __attribute__((aligned(32))) = { 0 };
@ -188,12 +188,12 @@ u32 SetNcchKey(NcchHeader* ncch, u16 crypto, u32 keyid) {
setup_aeskeyY(keyslot, ncch->signature); setup_aeskeyY(keyslot, ncch->signature);
} }
use_aeskey(keyslot); use_aeskey(keyslot);
return 0; return 0;
} }
// this is used to force and check crypto setup // this is used to force and check crypto setup
// (also prevents SHA register usage later on) // (also prevents SHA register usage later on)
u32 SetupNcchCrypto(NcchHeader* ncch, u16 crypt_to) { u32 SetupNcchCrypto(NcchHeader* ncch, u16 crypt_to) {
u16 crypt_from = NCCH_GET_CRYPTO(ncch); u16 crypt_from = NCCH_GET_CRYPTO(ncch);
u32 res_from = ((crypt_from & NCCH_NOCRYPTO) || u32 res_from = ((crypt_from & NCCH_NOCRYPTO) ||
@ -207,14 +207,14 @@ u32 CryptNcchSection(void* data, u32 offset_data, u32 size_data, u32 offset_sect
u32 offset_ctr, NcchHeader* ncch, u32 snum, u16 crypt_to, u32 keyid) { u32 offset_ctr, NcchHeader* ncch, u32 snum, u16 crypt_to, u32 keyid) {
u16 crypt_from = NCCH_GET_CRYPTO(ncch); u16 crypt_from = NCCH_GET_CRYPTO(ncch);
const u32 mode = AES_CNT_CTRNAND_MODE; const u32 mode = AES_CNT_CTRNAND_MODE;
// check if section in data // check if section in data
if ((offset_section >= offset_data + size_data) || if ((offset_section >= offset_data + size_data) ||
(offset_data >= offset_section + size_section) || (offset_data >= offset_section + size_section) ||
!size_section) { !size_section) {
return 0; // section not in data return 0; // section not in data
} }
// determine data / offset / size // determine data / offset / size
u8* data8 = (u8*)data; u8* data8 = (u8*)data;
u8* data_i = data8; u8* data_i = data8;
@ -226,7 +226,7 @@ u32 CryptNcchSection(void* data, u32 offset_data, u32 size_data, u32 offset_sect
size_i = size_section - offset_i; size_i = size_section - offset_i;
if (size_i > size_data - (data_i - data8)) if (size_i > size_data - (data_i - data8))
size_i = size_data - (data_i - data8); size_i = size_data - (data_i - data8);
// actual decryption stuff // actual decryption stuff
u8 ctr[16]; u8 ctr[16];
GetNcchCtr(ctr, ncch, snum); GetNcchCtr(ctr, ncch, snum);
@ -238,7 +238,7 @@ u32 CryptNcchSection(void* data, u32 offset_data, u32 size_data, u32 offset_sect
if (SetNcchKey(ncch, crypt_to, keyid) != 0) return 1; if (SetNcchKey(ncch, crypt_to, keyid) != 0) return 1;
ctr_decrypt_byte(data_i, data_i, size_i, offset_i + offset_ctr, mode, ctr); ctr_decrypt_byte(data_i, data_i, size_i, offset_i + offset_ctr, mode, ctr);
} }
return 0; return 0;
} }
@ -247,11 +247,11 @@ u32 CryptNcch(void* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* e
const u32 offset_flag3 = 0x188 + 3; const u32 offset_flag3 = 0x188 + 3;
const u32 offset_flag7 = 0x188 + 7; const u32 offset_flag7 = 0x188 + 7;
u16 crypt_from = NCCH_GET_CRYPTO(ncch); u16 crypt_from = NCCH_GET_CRYPTO(ncch);
// check for encryption // check for encryption
if ((crypt_to & crypt_from & NCCH_NOCRYPTO) || (crypt_to == crypt_from)) if ((crypt_to & crypt_from & NCCH_NOCRYPTO) || (crypt_to == crypt_from))
return 0; // desired end result already met return 0; // desired end result already met
// ncch flags handling // ncch flags handling
if ((offset <= offset_flag3) && (offset + size > offset_flag3)) if ((offset <= offset_flag3) && (offset + size > offset_flag3))
((u8*)data)[offset_flag3 - offset] = (crypt_to >> 8); ((u8*)data)[offset_flag3 - offset] = (crypt_to >> 8);
@ -259,7 +259,7 @@ u32 CryptNcch(void* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* e
((u8*)data)[offset_flag7 - offset] &= ~(0x01|0x20|0x04); ((u8*)data)[offset_flag7 - offset] &= ~(0x01|0x20|0x04);
((u8*)data)[offset_flag7 - offset] |= (crypt_to & (0x01|0x20|0x04)); ((u8*)data)[offset_flag7 - offset] |= (crypt_to & (0x01|0x20|0x04));
} }
// exthdr handling // exthdr handling
if (ncch->size_exthdr) { if (ncch->size_exthdr) {
if (CryptNcchSection(data, offset, size, if (CryptNcchSection(data, offset, size,
@ -267,14 +267,14 @@ u32 CryptNcch(void* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* e
NCCH_EXTHDR_SIZE, NCCH_EXTHDR_SIZE,
0, ncch, 1, crypt_to, 0) != 0) return 1; 0, ncch, 1, crypt_to, 0) != 0) return 1;
} }
// exefs handling // exefs handling
if (ncch->size_exefs) { if (ncch->size_exefs) {
// exefs header handling // exefs header handling
if (CryptNcchSection(data, offset, size, if (CryptNcchSection(data, offset, size,
ncch->offset_exefs * NCCH_MEDIA_UNIT, ncch->offset_exefs * NCCH_MEDIA_UNIT,
0x200, 0, ncch, 2, crypt_to, 0) != 0) return 1; 0x200, 0, ncch, 2, crypt_to, 0) != 0) return 1;
// exefs file handling // exefs file handling
if (exefs) for (u32 i = 0; i < 10; i++) { if (exefs) for (u32 i = 0; i < 10; i++) {
ExeFsFileHeader* file = exefs->files + i; ExeFsFileHeader* file = exefs->files + i;
@ -291,7 +291,7 @@ u32 CryptNcch(void* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* e
ncch, 2, crypt_to, 0) != 0) return 1; ncch, 2, crypt_to, 0) != 0) return 1;
} }
} }
// romfs handling // romfs handling
if (ncch->size_romfs) { if (ncch->size_romfs) {
if (CryptNcchSection(data, offset, size, if (CryptNcchSection(data, offset, size,
@ -299,7 +299,7 @@ u32 CryptNcch(void* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* e
ncch->size_romfs * NCCH_MEDIA_UNIT, ncch->size_romfs * NCCH_MEDIA_UNIT,
0, ncch, 3, crypt_to, 1) != 0) return 1; 0, ncch, 3, crypt_to, 1) != 0) return 1;
} }
return 0; return 0;
} }
@ -311,31 +311,31 @@ u32 CryptNcchSequential(void* data, u32 offset, u32 size, u16 crypt_to) {
static ExeFsHeader exefs = { 0 }; static ExeFsHeader exefs = { 0 };
static NcchHeader* ncchptr = NULL; static NcchHeader* ncchptr = NULL;
static ExeFsHeader* exefsptr = NULL; static ExeFsHeader* exefsptr = NULL;
// fetch ncch header from data // fetch ncch header from data
if ((offset == 0) && (size >= sizeof(NcchHeader))) { if ((offset == 0) && (size >= sizeof(NcchHeader))) {
memcpy(&ncch, data, sizeof(NcchHeader)); memcpy(&ncch, data, sizeof(NcchHeader));
ncchptr = (ValidateNcchHeader(&ncch) == 0) ? &ncch : NULL; ncchptr = (ValidateNcchHeader(&ncch) == 0) ? &ncch : NULL;
exefsptr = NULL; exefsptr = NULL;
} }
// safety check, ncch pointer // safety check, ncch pointer
if (!ncchptr) return 1; if (!ncchptr) return 1;
// fetch exefs header from data // fetch exefs header from data
if (ncchptr->offset_exefs && !exefsptr) { if (ncchptr->offset_exefs && !exefsptr) {
u32 offset_exefs = ncchptr->offset_exefs * NCCH_MEDIA_UNIT; u32 offset_exefs = ncchptr->offset_exefs * NCCH_MEDIA_UNIT;
if ((offset <= offset_exefs) && if ((offset <= offset_exefs) &&
((offset + size) >= offset_exefs + sizeof(ExeFsHeader))) { ((offset + size) >= offset_exefs + sizeof(ExeFsHeader))) {
memcpy(&exefs, (u8*)data + offset_exefs - offset, sizeof(ExeFsHeader)); memcpy(&exefs, (u8*)data + offset_exefs - offset, sizeof(ExeFsHeader));
if ((NCCH_ENCRYPTED(ncchptr)) && if ((NCCH_ENCRYPTED(ncchptr)) &&
(DecryptNcch((u8*) &exefs, offset_exefs, sizeof(ExeFsHeader), ncchptr, NULL) != 0)) (DecryptNcch((u8*) &exefs, offset_exefs, sizeof(ExeFsHeader), ncchptr, NULL) != 0))
return 1; return 1;
if (ValidateExeFsHeader(&exefs, 0) != 0) return 1; if (ValidateExeFsHeader(&exefs, 0) != 0) return 1;
exefsptr = &exefs; exefsptr = &exefs;
} }
} }
return CryptNcch(data, offset, size, ncchptr, exefsptr, crypt_to); return CryptNcch(data, offset, size, ncchptr, exefsptr, crypt_to);
} }
@ -343,17 +343,17 @@ u32 SetNcchSdFlag(void* data) { // data must be at least 0x600 byte and start wi
NcchHeader* ncch = (NcchHeader*) data; NcchHeader* ncch = (NcchHeader*) data;
NcchExtHeader* exthdr = (NcchExtHeader*) (void*) ((u8*)data + NCCH_EXTHDR_OFFSET); NcchExtHeader* exthdr = (NcchExtHeader*) (void*) ((u8*)data + NCCH_EXTHDR_OFFSET);
NcchExtHeader exthdr_dec; NcchExtHeader exthdr_dec;
if ((ValidateNcchHeader(ncch) != 0) || (!ncch->size_exthdr)) if ((ValidateNcchHeader(ncch) != 0) || (!ncch->size_exthdr))
return 0; // no extheader return 0; // no extheader
memcpy(&exthdr_dec, exthdr, sizeof(NcchExtHeader)); memcpy(&exthdr_dec, exthdr, sizeof(NcchExtHeader));
if (DecryptNcch((u8*) &exthdr_dec, NCCH_EXTHDR_OFFSET, sizeof(NcchExtHeader), ncch, NULL) != 0) if (DecryptNcch((u8*) &exthdr_dec, NCCH_EXTHDR_OFFSET, sizeof(NcchExtHeader), ncch, NULL) != 0)
return 1; return 1;
if (exthdr_dec.flag & (1<<1)) return 0; // flag already set if (exthdr_dec.flag & (1<<1)) return 0; // flag already set
exthdr_dec.flag |= (1<<1); exthdr_dec.flag |= (1<<1);
exthdr->flag ^= (1<<1); exthdr->flag ^= (1<<1);
sha_quick(ncch->hash_exthdr, &exthdr_dec, 0x400, SHA256_MODE); sha_quick(ncch->hash_exthdr, &exthdr_dec, 0x400, SHA256_MODE);
return 0; return 0;
} }

View File

@ -18,25 +18,25 @@ u32 FixNcchInfoEntry(NcchInfoEntry* entry, u32 version) {
} else if (version != 4) { // !ncchinfo v4.0/v4.1/v4.2 } else if (version != 4) { // !ncchinfo v4.0/v4.1/v4.2
return 1; return 1;
} }
// poor man's UTF-16 -> UTF-8 // poor man's UTF-16 -> UTF-8
if (entry->filename[1] == 0x00) { if (entry->filename[1] == 0x00) {
for (u32 i = 1; i < (sizeof(entry->filename) / 2); i++) for (u32 i = 1; i < (sizeof(entry->filename) / 2); i++)
entry->filename[i] = entry->filename[i*2]; entry->filename[i] = entry->filename[i*2];
} }
// fix sdmc: prefix // fix sdmc: prefix
if (memcmp(entry->filename, "sdmc:", 5) == 0) if (memcmp(entry->filename, "sdmc:", 5) == 0)
memmove(entry->filename, entry->filename + 5, 112 - 5); memmove(entry->filename, entry->filename + 5, 112 - 5);
// workaround (1) for older (v4.0) ncchinfo.bin // workaround (1) for older (v4.0) ncchinfo.bin
// this combination means seed crypto rather than FixedKey // this combination means seed crypto rather than FixedKey
if ((entry->ncchFlag7 == 0x01) && entry->ncchFlag3) if ((entry->ncchFlag7 == 0x01) && entry->ncchFlag3)
entry->ncchFlag7 = 0x20; entry->ncchFlag7 = 0x20;
// workaround (2) for older (v4.1) ncchinfo.bin // workaround (2) for older (v4.1) ncchinfo.bin
if (!entry->size_b) entry->size_b = entry->size_mb * 1024 * 1024; if (!entry->size_b) entry->size_b = entry->size_mb * 1024 * 1024;
return 0; return 0;
} }
@ -50,10 +50,10 @@ u32 BuildNcchInfoXorpad(void* buffer, NcchInfoEntry* entry, u32 size, u32 offset
ncch.programId = ncch.partitionId = entry->titleId; ncch.programId = ncch.partitionId = entry->titleId;
if (SetNcchKey(&ncch, NCCH_GET_CRYPTO(&ncch), 1) != 0) if (SetNcchKey(&ncch, NCCH_GET_CRYPTO(&ncch), 1) != 0)
return 1; return 1;
// write xorpad // write xorpad
memset(buffer, 0, size); memset(buffer, 0, size);
ctr_decrypt_byte(buffer, buffer, size, offset, AES_CNT_CTRNAND_MODE, entry->ctr); ctr_decrypt_byte(buffer, buffer, size, offset, AES_CNT_CTRNAND_MODE, entry->ctr);
return 0; return 0;
} }

View File

@ -6,7 +6,7 @@ u32 ValidateNcsdHeader(NcsdHeader* header) {
if ((memcmp(header->magic, "NCSD", 4) != 0) || // check magic number if ((memcmp(header->magic, "NCSD", 4) != 0) || // check magic number
(memcmp(header->partitions_fs_type, zeroes, 8) != 0) || !header->mediaId) // prevent detection of NAND images (memcmp(header->partitions_fs_type, zeroes, 8) != 0) || !header->mediaId) // prevent detection of NAND images
return 1; return 1;
u32 data_units = 0; u32 data_units = 0;
for (u32 i = 0; i < 8; i++) { for (u32 i = 0; i < 8; i++) {
NcchPartition* partition = header->partitions + i; NcchPartition* partition = header->partitions + i;
@ -18,7 +18,7 @@ u32 ValidateNcsdHeader(NcsdHeader* header) {
} }
if (data_units > header->size) if (data_units > header->size)
return 1; return 1;
return 0; return 0;
} }
@ -30,7 +30,7 @@ u64 GetNcsdTrimmedSize(NcsdHeader* header) {
if (!partition->size) continue; if (!partition->size) continue;
data_units = (partition_end > data_units) ? partition_end : data_units; data_units = (partition_end > data_units) ? partition_end : data_units;
} }
return data_units * NCSD_MEDIA_UNIT; return data_units * NCSD_MEDIA_UNIT;
} }
@ -38,11 +38,11 @@ u64 GetNcsdTrimmedSize(NcsdHeader* header) {
u32 CryptNcsdSequential(void* data, u32 offset_data, u32 size_data, u16 crypto) { u32 CryptNcsdSequential(void* data, u32 offset_data, u32 size_data, u16 crypto) {
// warning: this will only work for sequential processing // warning: this will only work for sequential processing
static NcsdHeader ncsd; static NcsdHeader ncsd;
// fetch ncsd header from data // fetch ncsd header from data
if ((offset_data == 0) && (size_data >= sizeof(NcsdHeader))) if ((offset_data == 0) && (size_data >= sizeof(NcsdHeader)))
memcpy(&ncsd, data, sizeof(NcsdHeader)); memcpy(&ncsd, data, sizeof(NcsdHeader));
for (u32 i = 0; i < 8; i++) { for (u32 i = 0; i < 8; i++) {
NcchPartition* partition = ncsd.partitions + i; NcchPartition* partition = ncsd.partitions + i;
u32 offset_p = partition->offset * NCSD_MEDIA_UNIT; u32 offset_p = partition->offset * NCSD_MEDIA_UNIT;
@ -68,7 +68,7 @@ u32 CryptNcsdSequential(void* data, u32 offset_data, u32 size_data, u16 crypto)
if (CryptNcchSequential(data_i, offset_i, size_i, crypto) != 0) if (CryptNcchSequential(data_i, offset_i, size_i, crypto) != 0)
return 1; return 1;
} }
return 0; return 0;
} }

View File

@ -32,7 +32,7 @@ u32 BuildTwlSaveHeader(void* sav, u32 size) {
if (size / (u32) sct_size > 0xFFFF) if (size / (u32) sct_size > 0xFFFF)
return 1; return 1;
// fit max number of sectors into size // fit max number of sectors into size
// that's how Nintendo does it ¯\_(ツ)_/¯ // that's how Nintendo does it ¯\_(ツ)_/¯
const u16 n_sct_max = size / sct_size; const u16 n_sct_max = size / sct_size;
u16 n_sct = 1; u16 n_sct = 1;
@ -143,15 +143,15 @@ u32 FindNitroRomDir(u32 dirid, u32* fileid, u8** fnt_entry, TwlHeader* hdr, u8*
NitroFntBaseEntry* fnt_base = (NitroFntBaseEntry*) fnt; NitroFntBaseEntry* fnt_base = (NitroFntBaseEntry*) fnt;
NitroFntBaseEntry* fnt_dir = &((NitroFntBaseEntry*) fnt)[dirid]; NitroFntBaseEntry* fnt_dir = &((NitroFntBaseEntry*) fnt)[dirid];
NitroFatEntry* fat_lut = (NitroFatEntry*) fat; NitroFatEntry* fat_lut = (NitroFatEntry*) fat;
// base sanity checks // base sanity checks
if (fnt_base->parent_id*sizeof(NitroFntBaseEntry) > fnt_base->subtable_offset) return 1; // invalid FNT if (fnt_base->parent_id*sizeof(NitroFntBaseEntry) > fnt_base->subtable_offset) return 1; // invalid FNT
if (dirid >= fnt_base->parent_id) return 1; // dir ID out of bounds if (dirid >= fnt_base->parent_id) return 1; // dir ID out of bounds
// set first FNT entry / fileid // set first FNT entry / fileid
*fnt_entry = fnt + fnt_dir->subtable_offset; *fnt_entry = fnt + fnt_dir->subtable_offset;
*fileid = fnt_dir->file0_id; *fileid = fnt_dir->file0_id;
// check subtable / directory validity // check subtable / directory validity
u32 fid = *fileid; u32 fid = *fileid;
for (u8* entry = *fnt_entry;; entry = FNT_ENTRY_NEXT(entry)) { for (u8* entry = *fnt_entry;; entry = FNT_ENTRY_NEXT(entry)) {
@ -161,29 +161,29 @@ u32 FindNitroRomDir(u32 dirid, u32* fileid, u8** fnt_entry, TwlHeader* hdr, u8*
if (!FNT_ENTRY_ISDIR(entry)) fid++; if (!FNT_ENTRY_ISDIR(entry)) fid++;
} }
if (fid*sizeof(NitroFatEntry) > hdr->fat_size) return 1; // corrupt fnt / fat if (fid*sizeof(NitroFatEntry) > hdr->fat_size) return 1; // corrupt fnt / fat
return 0; return 0;
} }
u32 NextNitroRomEntry(u32* fileid, u8** fnt_entry) { u32 NextNitroRomEntry(u32* fileid, u8** fnt_entry) {
// check for end of subtable // check for end of subtable
if (!*fnt_entry || !**fnt_entry) return 1; if (!*fnt_entry || !**fnt_entry) return 1;
// advance to next entry // advance to next entry
if (!FNT_ENTRY_ISDIR(*fnt_entry)) (*fileid)++; if (!FNT_ENTRY_ISDIR(*fnt_entry)) (*fileid)++;
*fnt_entry += FNT_ENTRY_LEN(*fnt_entry); *fnt_entry += FNT_ENTRY_LEN(*fnt_entry);
// check for end of subtable // check for end of subtable
if (!**fnt_entry) return 1; if (!**fnt_entry) return 1;
return 0; return 0;
} }
u32 ReadNitroRomEntry(u64* offset, u64* size, bool* is_dir, u32 fileid, u8* fnt_entry, u8* fat) { u32 ReadNitroRomEntry(u64* offset, u64* size, bool* is_dir, u32 fileid, u8* fnt_entry, u8* fat) {
// check for end of subtable // check for end of subtable
if (!fnt_entry || !*fnt_entry) return 1; if (!fnt_entry || !*fnt_entry) return 1;
// decipher FNT entry // decipher FNT entry
*is_dir = FNT_ENTRY_ISDIR(fnt_entry); *is_dir = FNT_ENTRY_ISDIR(fnt_entry);
if (!(*is_dir)) { // for files if (!(*is_dir)) { // for files
@ -195,6 +195,6 @@ u32 ReadNitroRomEntry(u64* offset, u64* size, bool* is_dir, u32 fileid, u8* fnt_
*offset = (u64) (fnt_entry[1+fnlen]|(fnt_entry[1+fnlen+1]<<8)) & 0xFFF; // dir ID goes in offset *offset = (u64) (fnt_entry[1+fnlen]|(fnt_entry[1+fnlen+1]<<8)) & 0xFFF; // dir ID goes in offset
*size = 0; *size = 0;
} }
return 0; return 0;
} }

View File

@ -7,7 +7,7 @@
// see: http://problemkaputt.de/gbatek.htm#dscartridgeicontitle // see: http://problemkaputt.de/gbatek.htm#dscartridgeicontitle
// v0x0001 -> 0x0840 byte (contains JPN, USA, FRE, GER, ITA, ESP titles) // v0x0001 -> 0x0840 byte (contains JPN, USA, FRE, GER, ITA, ESP titles)
// v0x0002 -> 0x0940 byte (adds CHN title) // v0x0002 -> 0x0940 byte (adds CHN title)
// v0x0003 -> 0x0A40 byte (adds KOR title) // v0x0003 -> 0x0A40 byte (adds KOR title)
// v0x0103 -> 0x23C0 byte (adds TWL animated icon data) // v0x0103 -> 0x23C0 byte (adds TWL animated icon data)
#define TWLICON_SIZE_DATA(v) ((v == 0x0001) ? 0x0840 : (v == 0x0002) ? 0x0940 : \ #define TWLICON_SIZE_DATA(v) ((v == 0x0001) ? 0x0840 : (v == 0x0002) ? 0x0940 : \
(v == 0x0003) ? 0x1240 : (v == 0x0103) ? 0x23C0 : 0x0000) (v == 0x0003) ? 0x1240 : (v == 0x0103) ? 0x23C0 : 0x0000)

View File

@ -58,12 +58,12 @@ u32 BuildLv3Index(RomFsLv3Index* index, u8* lv3) {
index->filehash = (u32*) (void*) (lv3 + hdr->offset_filehash); index->filehash = (u32*) (void*) (lv3 + hdr->offset_filehash);
index->filemeta = lv3 + hdr->offset_filemeta; index->filemeta = lv3 + hdr->offset_filemeta;
index->filedata = NULL; index->filedata = NULL;
index->mod_dir = (hdr->size_dirhash / sizeof(u32)); index->mod_dir = (hdr->size_dirhash / sizeof(u32));
index->mod_file = (hdr->size_filehash / sizeof(u32)); index->mod_file = (hdr->size_filehash / sizeof(u32));
index->size_dirmeta = hdr->size_dirmeta; index->size_dirmeta = hdr->size_dirmeta;
index->size_filemeta = hdr->size_filemeta; index->size_filemeta = hdr->size_filemeta;
return 0; return 0;
} }
@ -77,13 +77,13 @@ u32 HashLv3Path(u16* wname, u32 name_len, u32 offset_parent) {
RomFsLv3DirMeta* GetLv3DirMeta(const char* name, u32 offset_parent, RomFsLv3Index* index) { RomFsLv3DirMeta* GetLv3DirMeta(const char* name, u32 offset_parent, RomFsLv3Index* index) {
RomFsLv3DirMeta* meta; RomFsLv3DirMeta* meta;
// wide (UTF-16) name // wide (UTF-16) name
u16 wname[256]; u16 wname[256];
int name_len = utf8_to_utf16(wname, (u8*) name, 255, 255); int name_len = utf8_to_utf16(wname, (u8*) name, 255, 255);
if (name_len <= 0) return NULL; if (name_len <= 0) return NULL;
wname[name_len] = 0; wname[name_len] = 0;
// hashing, first offset // hashing, first offset
u32 hash = HashLv3Path(wname, name_len, offset_parent); u32 hash = HashLv3Path(wname, name_len, offset_parent);
u32 offset = index->dirhash[hash % index->mod_dir]; u32 offset = index->dirhash[hash % index->mod_dir];
@ -96,19 +96,19 @@ RomFsLv3DirMeta* GetLv3DirMeta(const char* name, u32 offset_parent, RomFsLv3Inde
(memcmp(wname, meta->wname, name_len * 2) == 0)) (memcmp(wname, meta->wname, name_len * 2) == 0))
break; break;
} }
return (offset >= index->size_dirmeta) ? NULL : meta; return (offset >= index->size_dirmeta) ? NULL : meta;
} }
RomFsLv3FileMeta* GetLv3FileMeta(const char* name, u32 offset_parent, RomFsLv3Index* index) { RomFsLv3FileMeta* GetLv3FileMeta(const char* name, u32 offset_parent, RomFsLv3Index* index) {
RomFsLv3FileMeta* meta; RomFsLv3FileMeta* meta;
// wide (UTF-16) name // wide (UTF-16) name
u16 wname[256]; u16 wname[256];
int name_len = utf8_to_utf16(wname, (u8*) name, 255, 255); int name_len = utf8_to_utf16(wname, (u8*) name, 255, 255);
if (name_len <= 0) return NULL; if (name_len <= 0) return NULL;
wname[name_len] = 0; wname[name_len] = 0;
// hashing, first offset // hashing, first offset
u32 hash = HashLv3Path(wname, name_len, offset_parent); u32 hash = HashLv3Path(wname, name_len, offset_parent);
u32 offset = index->filehash[hash % index->mod_file]; u32 offset = index->filehash[hash % index->mod_file];
@ -121,6 +121,6 @@ RomFsLv3FileMeta* GetLv3FileMeta(const char* name, u32 offset_parent, RomFsLv3In
(memcmp(wname, meta->wname, name_len * 2) == 0)) (memcmp(wname, meta->wname, name_len * 2) == 0))
break; break;
} }
return (offset >= index->size_filemeta) ? NULL : meta; return (offset >= index->size_filemeta) ? NULL : meta;
} }

View File

@ -4,14 +4,14 @@
u32 BuildTadContentTable(void* table, void* header) { u32 BuildTadContentTable(void* table, void* header) {
TadHeader* hdr = (TadHeader*) header; TadHeader* hdr = (TadHeader*) header;
TadContentTable* tbl = (TadContentTable*) table; TadContentTable* tbl = (TadContentTable*) table;
if (strncmp(hdr->magic, TAD_HEADER_MAGIC, strlen(TAD_HEADER_MAGIC)) != 0) if (strncmp(hdr->magic, TAD_HEADER_MAGIC, strlen(TAD_HEADER_MAGIC)) != 0)
return 1; return 1;
tbl->banner_end = 0 + sizeof(TadBanner) + sizeof(TadBlockMetaData); tbl->banner_end = 0 + sizeof(TadBanner) + sizeof(TadBlockMetaData);
tbl->header_end = tbl->banner_end + sizeof(TadHeader) + sizeof(TadBlockMetaData); tbl->header_end = tbl->banner_end + sizeof(TadHeader) + sizeof(TadBlockMetaData);
tbl->footer_end = tbl->header_end + sizeof(TadFooter) + sizeof(TadBlockMetaData); tbl->footer_end = tbl->header_end + sizeof(TadFooter) + sizeof(TadBlockMetaData);
u32 content_end_last = tbl->footer_end; u32 content_end_last = tbl->footer_end;
for (u32 i = 0; i < TAD_NUM_CONTENT; i++) { for (u32 i = 0; i < TAD_NUM_CONTENT; i++) {
tbl->content_end[i] = content_end_last; tbl->content_end[i] = content_end_last;
@ -19,6 +19,6 @@ u32 BuildTadContentTable(void* table, void* header) {
tbl->content_end[i] += align(hdr->content_size[i], 0x10) + sizeof(TadBlockMetaData); tbl->content_end[i] += align(hdr->content_size[i], 0x10) + sizeof(TadBlockMetaData);
content_end_last = tbl->content_end[i]; content_end_last = tbl->content_end[i];
} }
return 0; return 0;
} }

View File

@ -23,18 +23,18 @@ u32 ValidateTicketSignature(Ticket* ticket) {
static bool got_modexp = false; static bool got_modexp = false;
static u32 mod[0x100 / 0x4] = { 0 }; static u32 mod[0x100 / 0x4] = { 0 };
static u32 exp = 0; static u32 exp = 0;
if (!got_modexp) { if (!got_modexp) {
// grab mod/exp from cert from cert.db // grab mod/exp from cert from cert.db
if (LoadCertFromCertDb(0x3F10, NULL, mod, &exp) == 0) if (LoadCertFromCertDb(0x3F10, NULL, mod, &exp) == 0)
got_modexp = true; got_modexp = true;
else return 1; else return 1;
} }
if (!RSA_setKey2048(3, mod, exp) || if (!RSA_setKey2048(3, mod, exp) ||
!RSA_verify2048((void*) &(ticket->signature), (void*) &(ticket->issuer), GetTicketSize(ticket) - 0x140)) !RSA_verify2048((void*) &(ticket->signature), (void*) &(ticket->issuer), GetTicketSize(ticket) - 0x140))
return 1; return 1;
return 0; return 0;
} }
@ -59,7 +59,7 @@ u32 BuildFakeTicket(Ticket* ticket, u8* title_id) {
ticket->audit = 0x01; // whatever ticket->audit = 0x01; // whatever
memcpy(ticket->content_index, ticket_cnt_index, sizeof(ticket_cnt_index)); memcpy(ticket->content_index, ticket_cnt_index, sizeof(ticket_cnt_index));
memset(&ticket->content_index[sizeof(ticket_cnt_index)], 0xFF, 0x80); // 1024 content indexes memset(&ticket->content_index[sizeof(ticket_cnt_index)], 0xFF, 0x80); // 1024 content indexes
return 0; return 0;
} }
@ -73,14 +73,14 @@ u32 GetTicketSize(const Ticket* ticket) {
u32 BuildTicketCert(u8* tickcert) { u32 BuildTicketCert(u8* tickcert) {
static const u8 cert_hash_expected[0x20] = { static const u8 cert_hash_expected[0x20] = {
0xDC, 0x15, 0x3C, 0x2B, 0x8A, 0x0A, 0xC8, 0x74, 0xA9, 0xDC, 0x78, 0x61, 0x0E, 0x6A, 0x8F, 0xE3, 0xDC, 0x15, 0x3C, 0x2B, 0x8A, 0x0A, 0xC8, 0x74, 0xA9, 0xDC, 0x78, 0x61, 0x0E, 0x6A, 0x8F, 0xE3,
0xE6, 0xB1, 0x34, 0xD5, 0x52, 0x88, 0x73, 0xC9, 0x61, 0xFB, 0xC7, 0x95, 0xCB, 0x47, 0xE6, 0x97 0xE6, 0xB1, 0x34, 0xD5, 0x52, 0x88, 0x73, 0xC9, 0x61, 0xFB, 0xC7, 0x95, 0xCB, 0x47, 0xE6, 0x97
}; };
static const u8 cert_hash_expected_dev[0x20] = { static const u8 cert_hash_expected_dev[0x20] = {
0x97, 0x2A, 0x32, 0xFF, 0x9D, 0x4B, 0xAA, 0x2F, 0x1A, 0x24, 0xCF, 0x21, 0x13, 0x87, 0xF5, 0x38, 0x97, 0x2A, 0x32, 0xFF, 0x9D, 0x4B, 0xAA, 0x2F, 0x1A, 0x24, 0xCF, 0x21, 0x13, 0x87, 0xF5, 0x38,
0xC6, 0x4B, 0xD4, 0x8F, 0xDF, 0x13, 0x21, 0x3D, 0xFC, 0x72, 0xFC, 0x8D, 0x9F, 0xDD, 0x01, 0x0E 0xC6, 0x4B, 0xD4, 0x8F, 0xDF, 0x13, 0x21, 0x3D, 0xFC, 0x72, 0xFC, 0x8D, 0x9F, 0xDD, 0x01, 0x0E
}; };
// open certs.db file on SysNAND // open certs.db file on SysNAND
FIL db; FIL db;
UINT bytes_read; UINT bytes_read;
@ -94,13 +94,13 @@ u32 BuildTicketCert(u8* tickcert) {
f_lseek(&db, 0x3A00); f_lseek(&db, 0x3A00);
f_read(&db, tickcert + 0x4F0, 0x210, &bytes_read); f_read(&db, tickcert + 0x4F0, 0x210, &bytes_read);
f_close(&db); f_close(&db);
// check the certificate hash // check the certificate hash
u8 cert_hash[0x20]; u8 cert_hash[0x20];
sha_quick(cert_hash, tickcert, TICKET_CDNCERT_SIZE, SHA256_MODE); sha_quick(cert_hash, tickcert, TICKET_CDNCERT_SIZE, SHA256_MODE);
if (memcmp(cert_hash, IS_DEVKIT ? cert_hash_expected_dev : cert_hash_expected, 0x20) != 0) if (memcmp(cert_hash, IS_DEVKIT ? cert_hash_expected_dev : cert_hash_expected, 0x20) != 0)
return 1; return 1;
return 0; return 0;
} }

View File

@ -32,10 +32,10 @@ u32 CryptTitleKey(TitleKeyEntry* tik, bool encrypt, bool devkit) {
{0x75, 0x05, 0x52, 0xBF, 0xAA, 0x1C, 0x04, 0x07, 0x55, 0xC8, 0xD5, 0x9A, 0x55, 0xF9, 0xAD, 0x1F} , // 4 {0x75, 0x05, 0x52, 0xBF, 0xAA, 0x1C, 0x04, 0x07, 0x55, 0xC8, 0xD5, 0x9A, 0x55, 0xF9, 0xAD, 0x1F} , // 4
{0xAA, 0xDA, 0x4C, 0xA8, 0xF6, 0xE5, 0xA9, 0x77, 0xE0, 0xA0, 0xF9, 0xE4, 0x76, 0xCF, 0x0D, 0x63} , // 5 {0xAA, 0xDA, 0x4C, 0xA8, 0xF6, 0xE5, 0xA9, 0x77, 0xE0, 0xA0, 0xF9, 0xE4, 0x76, 0xCF, 0x0D, 0x63} , // 5
}; };
u32 mode = (encrypt) ? AES_CNT_TITLEKEY_ENCRYPT_MODE : AES_CNT_TITLEKEY_DECRYPT_MODE; u32 mode = (encrypt) ? AES_CNT_TITLEKEY_ENCRYPT_MODE : AES_CNT_TITLEKEY_DECRYPT_MODE;
u8 ctr[16] = { 0 }; u8 ctr[16] = { 0 };
// setup key 0x3D // ctr // setup key 0x3D // ctr
if (tik->commonkey_idx >= 6) return 1; if (tik->commonkey_idx >= 6) return 1;
if (!devkit) setup_aeskeyY(0x3D, (void*) common_keyy[tik->commonkey_idx]); if (!devkit) setup_aeskeyY(0x3D, (void*) common_keyy[tik->commonkey_idx]);
@ -43,7 +43,7 @@ u32 CryptTitleKey(TitleKeyEntry* tik, bool encrypt, bool devkit) {
use_aeskey(0x3D); use_aeskey(0x3D);
memcpy(ctr, tik->title_id, 8); memcpy(ctr, tik->title_id, 8);
set_ctr(ctr); set_ctr(ctr);
// decrypt / encrypt the titlekey // decrypt / encrypt the titlekey
aes_decrypt(tik->titlekey, tik->titlekey, 1, mode); aes_decrypt(tik->titlekey, tik->titlekey, 1, mode);
return 0; return 0;
@ -54,7 +54,7 @@ u32 GetTitleKey(u8* titlekey, Ticket* ticket) {
memcpy(tik.title_id, ticket->title_id, 8); memcpy(tik.title_id, ticket->title_id, 8);
memcpy(tik.titlekey, ticket->titlekey, 16); memcpy(tik.titlekey, ticket->titlekey, 16);
tik.commonkey_idx = ticket->commonkey_idx; tik.commonkey_idx = ticket->commonkey_idx;
if (CryptTitleKey(&tik, false, TICKET_DEVKIT(ticket)) != 0) return 1; if (CryptTitleKey(&tik, false, TICKET_DEVKIT(ticket)) != 0) return 1;
memcpy(titlekey, tik.titlekey, 16); memcpy(titlekey, tik.titlekey, 16);
return 0; return 0;
@ -64,7 +64,7 @@ u32 FindTicket(Ticket** ticket, u8* title_id, bool force_legit, bool emunand) {
const char* path_db = TICKDB_PATH(emunand); // EmuNAND / SysNAND const char* path_db = TICKDB_PATH(emunand); // EmuNAND / SysNAND
char path_store[256] = { 0 }; char path_store[256] = { 0 };
char* path_bak = NULL; char* path_bak = NULL;
strncpy(path_store, GetMountPath(), 256); strncpy(path_store, GetMountPath(), 256);
if (*path_store) path_bak = path_store; if (*path_store) path_bak = path_store;
if (!InitImgFS(path_db)) if (!InitImgFS(path_db))
@ -81,37 +81,37 @@ u32 FindTicket(Ticket** ticket, u8* title_id, bool force_legit, bool emunand) {
for (u32 i = force_legit ? 1 : 0; i < 4; i++) { for (u32 i = force_legit ? 1 : 0; i < 4; i++) {
snprintf(dir_path, 12, "T:/%s", virtual_tickdb_dirs[i]); snprintf(dir_path, 12, "T:/%s", virtual_tickdb_dirs[i]);
if (fvx_opendir(&dir, dir_path) != FR_OK) { if (fvx_opendir(&dir, dir_path) != FR_OK) {
InitImgFS(path_bak); InitImgFS(path_bak);
return 1; return 1;
} }
while ((fvx_readdir(&dir, &fno) == FR_OK) && *(fno.fname)) { while ((fvx_readdir(&dir, &fno) == FR_OK) && *(fno.fname)) {
if (strncmp(tid_string, fno.fname, 16) == 0) { if (strncmp(tid_string, fno.fname, 16) == 0) {
snprintf(tik_path, 64, "%s/%s", dir_path, fno.fname); snprintf(tik_path, 64, "%s/%s", dir_path, fno.fname);
u32 size = fvx_qsize(tik_path); u32 size = fvx_qsize(tik_path);
if (!(*ticket = malloc(size))) { if (!(*ticket = malloc(size))) {
InitImgFS(path_bak); InitImgFS(path_bak);
return 1; return 1;
} }
if ((fvx_qread(tik_path, *ticket, 0, size, NULL) != FR_OK) || if ((fvx_qread(tik_path, *ticket, 0, size, NULL) != FR_OK) ||
(force_legit && (ValidateTicketSignature(*ticket) != 0))) { (force_legit && (ValidateTicketSignature(*ticket) != 0))) {
free(*ticket); free(*ticket);
InitImgFS(path_bak); InitImgFS(path_bak);
return 1; return 1;
} }
InitImgFS(path_bak); InitImgFS(path_bak);
return 0; return 0;
} }
} }
fvx_closedir(&dir); fvx_closedir(&dir);
} }
InitImgFS(path_bak); InitImgFS(path_bak);
return 1; return 1;
} }
@ -120,12 +120,12 @@ u32 FindTitleKey(Ticket* ticket, u8* title_id) {
bool found = false; bool found = false;
TitleKeysInfo* tikdb = (TitleKeysInfo*) malloc(STD_BUFFER_SIZE); // more than enough TitleKeysInfo* tikdb = (TitleKeysInfo*) malloc(STD_BUFFER_SIZE); // more than enough
if (!tikdb) return 1; 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++) {
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, STD_BUFFER_SIZE);
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)
continue; // filesize / titlekey db size mismatch continue; // filesize / titlekey db size mismatch
@ -141,7 +141,7 @@ u32 FindTitleKey(Ticket* ticket, u8* title_id) {
break; break;
} }
} }
free(tikdb); free(tikdb);
return (found) ? 0 : 1; return (found) ? 0 : 1;
} }

View File

@ -8,7 +8,7 @@ u32 BuildTitleInfoEntryTmd(TitleInfoEntry* tie, TitleMetaData* tmd, bool sd) {
u64 title_id = getbe64(tmd->title_id); u64 title_id = getbe64(tmd->title_id);
bool has_idx1 = false; bool has_idx1 = false;
bool has_idx2 = false; bool has_idx2 = false;
// set basic values // set basic values
memset(tie, 0x00, sizeof(TitleInfoEntry)); memset(tie, 0x00, sizeof(TitleInfoEntry));
tie->title_type = 0x40; tie->title_type = 0x40;
@ -34,7 +34,7 @@ u32 BuildTitleInfoEntryTmd(TitleInfoEntry* tie, TitleMetaData* tmd, bool sd) {
((tmd->twl_flag & 0x2) ? align(sizeof(TwlIconData), align_size) : 0); ((tmd->twl_flag & 0x2) ? align(sizeof(TwlIconData), align_size) : 0);
} }
// contents title size + some additional stuff // contents title size + some additional stuff
TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1); TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1);
tie->content0_id = getbe32(chunk->id); tie->content0_id = getbe32(chunk->id);
for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++, chunk++) { for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++, chunk++) {
@ -55,7 +55,7 @@ u32 BuildTitleInfoEntryTmd(TitleInfoEntry* tie, TitleMetaData* tmd, bool sd) {
u32 BuildTitleInfoEntryTwl(TitleInfoEntry* tie, TitleMetaData* tmd, TwlHeader* twl) { u32 BuildTitleInfoEntryTwl(TitleInfoEntry* tie, TitleMetaData* tmd, TwlHeader* twl) {
u64 title_id = getbe64(tmd->title_id); u64 title_id = getbe64(tmd->title_id);
// build the basic titledb entry // build the basic titledb entry
if (BuildTitleInfoEntryTmd(tie, tmd, false) != 0) return 1; if (BuildTitleInfoEntryTmd(tie, tmd, false) != 0) return 1;
@ -90,7 +90,7 @@ u32 BuildTitleInfoEntryNcch(TitleInfoEntry* tie, TitleMetaData* tmd, NcchHeader*
// NCCH titles need no content0 ID // NCCH titles need no content0 ID
tie->content0_id = 0; tie->content0_id = 0;
// specific flags // specific flags
// see: http://3dbrew.org/wiki/Titles // see: http://3dbrew.org/wiki/Titles
if (!((title_id >> 32) & 0x10)) // not a system title if (!((title_id >> 32) & 0x10)) // not a system title

View File

@ -18,18 +18,18 @@ u32 ValidateTmdSignature(TitleMetaData* tmd) {
static bool got_modexp = false; static bool got_modexp = false;
static u32 mod[0x100 / 4] = { 0 }; static u32 mod[0x100 / 4] = { 0 };
static u32 exp = 0; static u32 exp = 0;
if (!got_modexp) { if (!got_modexp) {
// grab mod/exp from cert from cert.db // grab mod/exp from cert from cert.db
if (LoadCertFromCertDb(0x3C10, NULL, mod, &exp) == 0) if (LoadCertFromCertDb(0x3C10, NULL, mod, &exp) == 0)
got_modexp = true; got_modexp = true;
else return 1; else return 1;
} }
if (!RSA_setKey2048(3, mod, exp) || if (!RSA_setKey2048(3, mod, exp) ||
!RSA_verify2048((void*) &(tmd->signature), (void*) &(tmd->issuer), 0xC4)) !RSA_verify2048((void*) &(tmd->signature), (void*) &(tmd->issuer), 0xC4))
return 1; return 1;
return 0; return 0;
} }
@ -98,20 +98,20 @@ u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents, u32 save_size
memcpy(tmd->contentinfo[0].cmd_count, tmd->content_count, 2); memcpy(tmd->contentinfo[0].cmd_count, tmd->content_count, 2);
memset(tmd->contentinfo[0].hash, 0xFF, 0x20); // placeholder (hash) memset(tmd->contentinfo[0].hash, 0xFF, 0x20); // placeholder (hash)
// nothing to do for content list (yet) // nothing to do for content list (yet)
return 0; return 0;
} }
u32 BuildTmdCert(u8* tmdcert) { u32 BuildTmdCert(u8* tmdcert) {
static const u8 cert_hash_expected[0x20] = { static const u8 cert_hash_expected[0x20] = {
0x91, 0x5F, 0x77, 0x3A, 0x07, 0x82, 0xD4, 0x27, 0xC4, 0xCE, 0xF5, 0x49, 0x25, 0x33, 0xE8, 0xEC, 0x91, 0x5F, 0x77, 0x3A, 0x07, 0x82, 0xD4, 0x27, 0xC4, 0xCE, 0xF5, 0x49, 0x25, 0x33, 0xE8, 0xEC,
0xF6, 0xFE, 0xA1, 0xEB, 0x8C, 0xCF, 0x59, 0x6E, 0x69, 0xBA, 0x2A, 0x38, 0x8D, 0x73, 0x8A, 0xE1 0xF6, 0xFE, 0xA1, 0xEB, 0x8C, 0xCF, 0x59, 0x6E, 0x69, 0xBA, 0x2A, 0x38, 0x8D, 0x73, 0x8A, 0xE1
}; };
static const u8 cert_hash_expected_dev[0x20] = { static const u8 cert_hash_expected_dev[0x20] = {
0x49, 0xC9, 0x41, 0x56, 0xCA, 0x86, 0xBD, 0x1F, 0x36, 0x51, 0x51, 0x6A, 0x4A, 0x9F, 0x54, 0xA1, 0x49, 0xC9, 0x41, 0x56, 0xCA, 0x86, 0xBD, 0x1F, 0x36, 0x51, 0x51, 0x6A, 0x4A, 0x9F, 0x54, 0xA1,
0xC2, 0xE9, 0xCA, 0x93, 0x94, 0xF4, 0x29, 0xA0, 0x38, 0x54, 0x75, 0xFF, 0xAB, 0x6E, 0x8E, 0x71 0xC2, 0xE9, 0xCA, 0x93, 0x94, 0xF4, 0x29, 0xA0, 0x38, 0x54, 0x75, 0xFF, 0xAB, 0x6E, 0x8E, 0x71
}; };
// open certs.db file on SysNAND // open certs.db file on SysNAND
FIL db; FIL db;
UINT bytes_read; UINT bytes_read;
@ -125,12 +125,12 @@ u32 BuildTmdCert(u8* tmdcert) {
f_lseek(&db, 0x3A00); f_lseek(&db, 0x3A00);
f_read(&db, tmdcert + 0x4F0, 0x210, &bytes_read); f_read(&db, tmdcert + 0x4F0, 0x210, &bytes_read);
f_close(&db); f_close(&db);
// check the certificate hash // check the certificate hash
u8 cert_hash[0x20]; u8 cert_hash[0x20];
sha_quick(cert_hash, tmdcert, TMD_CDNCERT_SIZE, SHA256_MODE); sha_quick(cert_hash, tmdcert, TMD_CDNCERT_SIZE, SHA256_MODE);
if (memcmp(cert_hash, IS_DEVKIT ? cert_hash_expected_dev : cert_hash_expected, 0x20) != 0) if (memcmp(cert_hash, IS_DEVKIT ? cert_hash_expected_dev : cert_hash_expected, 0x20) != 0)
return 1; return 1;
return 0; return 0;
} }

View File

@ -30,7 +30,7 @@
#define SPI_512B_EEPROM_CMD_RDLO 3 #define SPI_512B_EEPROM_CMD_RDLO 3
#define SPI_512B_EEPROM_CMD_RDHI 11 #define SPI_512B_EEPROM_CMD_RDHI 11
#define SPI_EEPROM_CMD_WRITE 2 #define SPI_EEPROM_CMD_WRITE 2
#define SPI_CMD_READ 3 #define SPI_CMD_READ 3
@ -119,7 +119,7 @@ int CardSPIWriteRead(CardSPIType type, const void* cmd, u32 cmdSize, void* answe
SPI_DoXfer(SPI_DEV_CART_FLASH, transfers, 3, true); SPI_DoXfer(SPI_DEV_CART_FLASH, transfers, 3, true);
REG_CFG9_CARDCTL &= ~CARDCTL_SPICARD; REG_CFG9_CARDCTL &= ~CARDCTL_SPICARD;
return 0; return 0;
} }
@ -148,12 +148,12 @@ int CardSPIEnableWriting_regular(CardSPIType type) {
if (res) return res; if (res) return res;
cmd = SPI_CMD_RDSR; cmd = SPI_CMD_RDSR;
do { do {
res = CardSPIWriteRead(type, &cmd, 1, &statusReg, 1, 0, 0); res = CardSPIWriteRead(type, &cmd, 1, &statusReg, 1, 0, 0);
if (res) return res; if (res) return res;
} while(statusReg & ~SPI_FLG_WEL); } while(statusReg & ~SPI_FLG_WEL);
return 0; return 0;
} }
@ -176,17 +176,17 @@ int CardSPIReadJEDECIDAndStatusReg(CardSPIType type, u32* id, u8* statusReg) {
u32 id_ = 0; u32 id_ = 0;
int res = CardSPIWaitWriteEnd(type, 0); int res = CardSPIWaitWriteEnd(type, 0);
if (res) return res; if (res) return res;
if ((res = CardSPIWriteRead(type, &cmd, 1, idbuf, 3, 0, 0))) return res; if ((res = CardSPIWriteRead(type, &cmd, 1, idbuf, 3, 0, 0))) return res;
id_ = (idbuf[0] << 16) | (idbuf[1] << 8) | idbuf[2]; id_ = (idbuf[0] << 16) | (idbuf[1] << 8) | idbuf[2];
cmd = SPI_CMD_RDSR; cmd = SPI_CMD_RDSR;
if ((res = CardSPIWriteRead(type, &cmd, 1, &reg, 1, 0, 0))) return res; if ((res = CardSPIWriteRead(type, &cmd, 1, &reg, 1, 0, 0))) return res;
if (id) *id = id_; if (id) *id = id_;
if (statusReg) *statusReg = reg; if (statusReg) *statusReg = reg;
return 0; return 0;
} }
@ -207,19 +207,19 @@ u32 CardSPIGetCapacity(CardSPIType type) {
int CardSPIWriteSaveData_9bit(CardSPIType type, u32 offset, const void* data, u32 size) { int CardSPIWriteSaveData_9bit(CardSPIType type, u32 offset, const void* data, u32 size) {
u8 cmd[2] = { (offset >= 0x100) ? SPI_512B_EEPROM_CMD_WRHI : SPI_512B_EEPROM_CMD_WRLO, (u8) offset }; u8 cmd[2] = { (offset >= 0x100) ? SPI_512B_EEPROM_CMD_WRHI : SPI_512B_EEPROM_CMD_WRLO, (u8) offset };
return _SPIWriteTransaction(type, cmd, 2, (void*) ((u8*) data), size); return _SPIWriteTransaction(type, cmd, 2, (void*) ((u8*) data), size);
} }
int CardSPIWriteSaveData_16bit(CardSPIType type, u32 offset, const void* data, u32 size) { int CardSPIWriteSaveData_16bit(CardSPIType type, u32 offset, const void* data, u32 size) {
u8 cmd[3] = { type.chip->writeCommand, (u8)(offset >> 8), (u8) offset }; u8 cmd[3] = { type.chip->writeCommand, (u8)(offset >> 8), (u8) offset };
return _SPIWriteTransaction(type, cmd, 3, (void*) ((u8*) data), size); return _SPIWriteTransaction(type, cmd, 3, (void*) ((u8*) data), size);
} }
int CardSPIWriteSaveData_24bit_write(CardSPIType type, u32 offset, const void* data, u32 size) { int CardSPIWriteSaveData_24bit_write(CardSPIType type, u32 offset, const void* data, u32 size) {
u8 cmd[4] = { type.chip->writeCommand, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset }; u8 cmd[4] = { type.chip->writeCommand, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset };
return _SPIWriteTransaction(type, cmd, 4, (void*) ((u8*) data), size); return _SPIWriteTransaction(type, cmd, 4, (void*) ((u8*) data), size);
} }
@ -270,60 +270,60 @@ int CardSPIWriteSaveData_24bit_erase_program(CardSPIType type, u32 offset, const
int CardSPIWriteSaveData(CardSPIType type, u32 offset, const void* data, u32 size) { int CardSPIWriteSaveData(CardSPIType type, u32 offset, const void* data, u32 size) {
if (type.chip == NO_CHIP) return 1; if (type.chip == NO_CHIP) return 1;
if (size == 0) return 0; if (size == 0) return 0;
size = min(size, CardSPIGetCapacity(type) - offset); size = min(size, CardSPIGetCapacity(type) - offset);
u32 end = offset + size; u32 end = offset + size;
u32 pos = offset; u32 pos = offset;
u32 writeSize = type.chip->writeSize; u32 writeSize = type.chip->writeSize;
if (writeSize == 0) return 0xC8E13404; if (writeSize == 0) return 0xC8E13404;
int res = CardSPIWaitWriteEnd(type, 1000); int res = CardSPIWaitWriteEnd(type, 1000);
if (res) return res; if (res) return res;
while(pos < end) { while(pos < end) {
u32 remaining = end - pos; u32 remaining = end - pos;
u32 nb = writeSize - (pos % writeSize); u32 nb = writeSize - (pos % writeSize);
u32 dataSize = (remaining < nb) ? remaining : nb; u32 dataSize = (remaining < nb) ? remaining : nb;
if ((res = type.chip->writeSaveData(type, pos, (void*) ((u8*) data - offset + pos), dataSize))) return res; if ((res = type.chip->writeSaveData(type, pos, (void*) ((u8*) data - offset + pos), dataSize))) return res;
pos = ((pos / writeSize) + 1) * writeSize; // truncate pos = ((pos / writeSize) + 1) * writeSize; // truncate
} }
return 0; return 0;
} }
int CardSPIReadSaveData_9bit(CardSPIType type, u32 pos, void* data, u32 size) { int CardSPIReadSaveData_9bit(CardSPIType type, u32 pos, void* data, u32 size) {
u8 cmd[4]; u8 cmd[4];
u32 cmdSize = 2; u32 cmdSize = 2;
u32 end = pos + size; u32 end = pos + size;
u32 read = 0; u32 read = 0;
if (pos < 0x100) { if (pos < 0x100) {
u32 len = 0x100 - pos; u32 len = 0x100 - pos;
cmd[0] = SPI_512B_EEPROM_CMD_RDLO; cmd[0] = SPI_512B_EEPROM_CMD_RDLO;
cmd[1] = (u8) pos; cmd[1] = (u8) pos;
int res = CardSPIWriteRead(type, cmd, cmdSize, data, len, NULL, 0); int res = CardSPIWriteRead(type, cmd, cmdSize, data, len, NULL, 0);
if (res) return res; if (res) return res;
read += len; read += len;
} }
if (end >= 0x100) { if (end >= 0x100) {
u32 len = end - 0x100; u32 len = end - 0x100;
cmd[0] = SPI_512B_EEPROM_CMD_RDHI; cmd[0] = SPI_512B_EEPROM_CMD_RDHI;
cmd[1] = (u8)(pos + read); cmd[1] = (u8)(pos + read);
int res = CardSPIWriteRead(type, cmd, cmdSize, (void*)((u8*)data + read), len, NULL, 0); int res = CardSPIWriteRead(type, cmd, cmdSize, (void*)((u8*)data + read), len, NULL, 0);
if (res) return res; if (res) return res;
} }
return 0; return 0;
} }
@ -335,7 +335,7 @@ int CardSPIReadSaveData_16bit(CardSPIType type, u32 offset, void* data, u32 size
int CardSPIReadSaveData_24bit(CardSPIType type, u32 offset, void* data, u32 size) { int CardSPIReadSaveData_24bit(CardSPIType type, u32 offset, void* data, u32 size) {
u8 cmd[4] = { SPI_CMD_READ, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset }; u8 cmd[4] = { SPI_CMD_READ, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset };
return CardSPIWriteRead(type, cmd, 4, data, size, NULL, 0); return CardSPIWriteRead(type, cmd, 4, data, size, NULL, 0);
} }
@ -343,10 +343,10 @@ int CardSPIReadSaveData(CardSPIType type, u32 offset, void* data, u32 size) {
if (type.chip == NO_CHIP) return 1; if (type.chip == NO_CHIP) return 1;
if (size == 0) return 0; if (size == 0) return 0;
int res = CardSPIWaitWriteEnd(type, 1000); int res = CardSPIWaitWriteEnd(type, 1000);
if (res) return res; if (res) return res;
size = (size <= CardSPIGetCapacity(type) - offset) ? size : CardSPIGetCapacity(type) - offset; size = (size <= CardSPIGetCapacity(type) - offset) ? size : CardSPIGetCapacity(type) - offset;
return type.chip->readSaveData(type, offset, data, size); return type.chip->readSaveData(type, offset, data, size);
@ -358,7 +358,7 @@ int CardSPIEraseSector_emulated(CardSPIType type, u32 offset) {
if (!fill_buf) return 1; if (!fill_buf) return 1;
memset(fill_buf, 0xff, blockSize); memset(fill_buf, 0xff, blockSize);
offset = (offset / blockSize) * blockSize; offset = (offset / blockSize) * blockSize;
int res = CardSPIWriteSaveData(type, offset, fill_buf, blockSize); int res = CardSPIWriteSaveData(type, offset, fill_buf, blockSize);
free(fill_buf); free(fill_buf);
return res; return res;
@ -366,10 +366,10 @@ int CardSPIEraseSector_emulated(CardSPIType type, u32 offset) {
int CardSPIEraseSector_real(CardSPIType type, u32 offset) { int CardSPIEraseSector_real(CardSPIType type, u32 offset) {
u8 cmd[4] = { type.chip->eraseCommand, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset }; u8 cmd[4] = { type.chip->eraseCommand, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset };
int res = CardSPIWaitWriteEnd(type, 10000); int res = CardSPIWaitWriteEnd(type, 10000);
if (res) return res; if (res) return res;
return _SPIWriteTransaction(type, cmd, 4, NULL, 0); return _SPIWriteTransaction(type, cmd, 4, NULL, 0);
} }
@ -399,41 +399,41 @@ int CardSPIErase(CardSPIType type) {
* *
* Copyright (C) Pokedoc (2010) * Copyright (C) Pokedoc (2010)
*/ */
/* /*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, but * This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details. * for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., * with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
int _SPIIsDataMirrored(CardSPIType type, int size, bool* mirrored) { int _SPIIsDataMirrored(CardSPIType type, int size, bool* mirrored) {
u32 offset0 = (size-1); // n KB u32 offset0 = (size-1); // n KB
u32 offset1 = (2*size-1); // 2n KB u32 offset1 = (2*size-1); // 2n KB
u8 buf1; // +0k data read -> write u8 buf1; // +0k data read -> write
u8 buf2; // +n k data read -> read u8 buf2; // +n k data read -> read
u8 buf3; // +0k ~data write u8 buf3; // +0k ~data write
u8 buf4; // +n k data new comp buf2 u8 buf4; // +n k data new comp buf2
int res; int res;
if ((res = CardSPIReadSaveData(type, offset0, &buf1, 1))) return res; if ((res = CardSPIReadSaveData(type, offset0, &buf1, 1))) return res;
if ((res = CardSPIReadSaveData(type, offset1, &buf2, 1))) return res; if ((res = CardSPIReadSaveData(type, offset1, &buf2, 1))) return res;
buf3=~buf1; buf3=~buf1;
if ((res = CardSPIWriteSaveData(type, offset0, &buf3, 1))) return res; if ((res = CardSPIWriteSaveData(type, offset0, &buf3, 1))) return res;
if ((res = CardSPIReadSaveData(type, offset1, &buf4, 1))) return res; if ((res = CardSPIReadSaveData(type, offset1, &buf4, 1))) return res;
if ((res = CardSPIWriteSaveData(type, offset0, &buf1, 1))) return res; if ((res = CardSPIWriteSaveData(type, offset0, &buf1, 1))) return res;
*mirrored = buf2 != buf4; *mirrored = buf2 != buf4;
return 0; return 0;
} }
@ -442,8 +442,8 @@ int CardSPIGetCardSPIType(CardSPIType* type, bool infrared) {
u8 sr = 0; u8 sr = 0;
u32 jedec = 0; u32 jedec = 0;
CardSPIType t = {NO_CHIP, infrared}; CardSPIType t = {NO_CHIP, infrared};
int res; int res;
if(infrared) { if(infrared) {
// Infrared carts currently not supported, need additional handling! // Infrared carts currently not supported, need additional handling!
*type = (CardSPIType) {NO_CHIP, true}; *type = (CardSPIType) {NO_CHIP, true};
@ -452,20 +452,20 @@ int CardSPIGetCardSPIType(CardSPIType* type, bool infrared) {
res = CardSPIReadJEDECIDAndStatusReg(t, &jedec, &sr); res = CardSPIReadJEDECIDAndStatusReg(t, &jedec, &sr);
if (res) return res; if (res) return res;
if ((sr & 0xfd) == 0x00 && (jedec != 0x00ffffff)) { t.chip = &FLASH_DUMMY; } if ((sr & 0xfd) == 0x00 && (jedec != 0x00ffffff)) { t.chip = &FLASH_DUMMY; }
if ((sr & 0xfd) == 0xF0 && (jedec == 0x00ffffff)) { *type = (CardSPIType) { EEPROM_512B, false }; return 0; } if ((sr & 0xfd) == 0xF0 && (jedec == 0x00ffffff)) { *type = (CardSPIType) { EEPROM_512B, false }; return 0; }
if ((sr & 0xfd) == 0x00 && (jedec == 0x00ffffff)) { t = (CardSPIType) { &EEPROM_DUMMY, false }; } if ((sr & 0xfd) == 0x00 && (jedec == 0x00ffffff)) { t = (CardSPIType) { &EEPROM_DUMMY, false }; }
if(t.chip == NO_CHIP) { if(t.chip == NO_CHIP) {
*type = (CardSPIType) {NO_CHIP, false}; *type = (CardSPIType) {NO_CHIP, false};
return 0; return 0;
} }
if (t.chip == &EEPROM_DUMMY) { if (t.chip == &EEPROM_DUMMY) {
bool mirrored = false; bool mirrored = false;
size_t i; size_t i;
for(i = 0; i < sizeof(EEPROMTypes) / sizeof(CardSPITypeData) - 1; i++) { for(i = 0; i < sizeof(EEPROMTypes) / sizeof(CardSPITypeData) - 1; i++) {
if ((res = _SPIIsDataMirrored(t, CardSPIGetCapacity((CardSPIType) {EEPROMTypes + i, false}), &mirrored))) return res; if ((res = _SPIIsDataMirrored(t, CardSPIGetCapacity((CardSPIType) {EEPROMTypes + i, false}), &mirrored))) return res;
if (mirrored) { if (mirrored) {
@ -476,15 +476,15 @@ int CardSPIGetCardSPIType(CardSPIType* type, bool infrared) {
*type = (CardSPIType) { EEPROMTypes + i, false }; *type = (CardSPIType) { EEPROMTypes + i, false };
return 0; return 0;
} }
for(size_t i = 0; i < sizeof(flashTypes) / sizeof(CardSPITypeData); i++) { for(size_t i = 0; i < sizeof(flashTypes) / sizeof(CardSPITypeData); i++) {
if (flashTypes[i].jedecId == jedec) { if (flashTypes[i].jedecId == jedec) {
*type = (CardSPIType) { flashTypes + i, infrared }; *type = (CardSPIType) { flashTypes + i, infrared };
return 0; return 0;
} }
} }
*type = (CardSPIType) { NO_CHIP, infrared }; *type = (CardSPIType) { NO_CHIP, infrared };
return 0; return 0;
} }

View File

@ -1,7 +1,7 @@
/* /*
* This file is based on SPI.h from TWLSaveTool. Its copyright notice is * This file is based on SPI.h from TWLSaveTool. Its copyright notice is
* reproduced below. * reproduced below.
* *
* Copyright (C) 2015-2016 TuxSH * Copyright (C) 2015-2016 TuxSH
* *
* TWLSaveTool is free software: you can redistribute it and/or modify * TWLSaveTool is free software: you can redistribute it and/or modify

View File

@ -42,9 +42,9 @@ void NTR_CmdReadHeader (u8* buffer)
while(REG_NTRCARDROMCNT&NTRCARD_BUSY) ; while(REG_NTRCARDROMCNT&NTRCARD_BUSY) ;
u32 iCardId=cardReadID(NTRCARD_CLK_SLOW); u32 iCardId=cardReadID(NTRCARD_CLK_SLOW);
while(REG_NTRCARDROMCNT&NTRCARD_BUSY) ; while(REG_NTRCARDROMCNT&NTRCARD_BUSY) ;
u32 iCheapCard=iCardId&0x80000000; u32 iCheapCard=iCardId&0x80000000;
if(iCheapCard) if(iCheapCard)
{ {
//this is magic of wood goblins //this is magic of wood goblins

View File

@ -68,18 +68,18 @@ u32 InitCartRead(CartData* cdata) {
cdata->cart_type = (cdata->cart_id & 0x10000000) ? CART_CTR : CART_NTR; cdata->cart_type = (cdata->cart_id & 0x10000000) ? CART_CTR : CART_NTR;
if (cdata->cart_type & CART_CTR) { // CTR cartridges if (cdata->cart_type & CART_CTR) { // CTR cartridges
memset(cdata, 0xFF, 0x4000 + PRIV_HDR_SIZE); // switch the padding to 0xFF memset(cdata, 0xFF, 0x4000 + PRIV_HDR_SIZE); // switch the padding to 0xFF
// init, NCCH header // init, NCCH header
static u32 sec_keys[4]; static u32 sec_keys[4];
u8* ncch_header = cdata->header + 0x1000; u8* ncch_header = cdata->header + 0x1000;
CTR_CmdReadHeader(ncch_header); CTR_CmdReadHeader(ncch_header);
Cart_Secure_Init((u32*) (void*) ncch_header, sec_keys); Cart_Secure_Init((u32*) (void*) ncch_header, sec_keys);
// NCSD header and CINFO // NCSD header and CINFO
// Cart_Dummy(); // Cart_Dummy();
// Cart_Dummy(); // Cart_Dummy();
CTR_CmdReadData(0, 0x200, 8, cdata->header); CTR_CmdReadData(0, 0x200, 8, cdata->header);
// safety checks, cart size // safety checks, cart size
NcsdHeader* ncsd = (NcsdHeader*) (void*) cdata->header; NcsdHeader* ncsd = (NcsdHeader*) (void*) cdata->header;
NcchHeader* ncch = (NcchHeader*) (void*) ncch_header; NcchHeader* ncch = (NcchHeader*) (void*) ncch_header;
@ -90,14 +90,14 @@ u32 InitCartRead(CartData* cdata) {
if (cdata->cart_size > 0x100000000) return 1; // carts > 4GB don't exist if (cdata->cart_size > 0x100000000) return 1; // carts > 4GB don't exist
// else if (cdata->cart_size == 0x100000000) cdata->cart_size -= 0x200; // silent 4GB fix // else if (cdata->cart_size == 0x100000000) cdata->cart_size -= 0x200; // silent 4GB fix
if (cdata->data_size > cdata->cart_size) return 1; if (cdata->data_size > cdata->cart_size) return 1;
// private header // private header
u8* priv_header = cdata->header + 0x4000; u8* priv_header = cdata->header + 0x4000;
CTR_CmdReadUniqueID(priv_header); CTR_CmdReadUniqueID(priv_header);
memcpy(priv_header + 0x40, &(cdata->cart_id), 4); memcpy(priv_header + 0x40, &(cdata->cart_id), 4);
memset(priv_header + 0x44, 0x00, 4); memset(priv_header + 0x44, 0x00, 4);
memset(priv_header + 0x48, 0xFF, 8); memset(priv_header + 0x48, 0xFF, 8);
// save data // save data
u32 card2_offset = getle32(cdata->header + 0x200); u32 card2_offset = getle32(cdata->header + 0x200);
if ((card2_offset != 0xFFFFFFFF) || (CardSPIGetCardSPIType(&(cdata->save_type), 0) != 0)) { if ((card2_offset != 0xFFFFFFFF) || (CardSPIGetCardSPIType(&(cdata->save_type), 0) != 0)) {
@ -110,13 +110,13 @@ u32 InitCartRead(CartData* cdata) {
NTR_CmdReadHeader(cdata->header); NTR_CmdReadHeader(cdata->header);
if (!(*(cdata->header))) return 1; // error reading the header if (!(*(cdata->header))) return 1; // error reading the header
if (!NTR_Secure_Init(cdata->header, Cart_GetID(), 0)) return 1; if (!NTR_Secure_Init(cdata->header, Cart_GetID(), 0)) return 1;
// cartridge size, trimmed size, twl presets // cartridge size, trimmed size, twl presets
if (nds_header->device_capacity >= 15) return 1; // too big, not valid if (nds_header->device_capacity >= 15) return 1; // too big, not valid
cdata->cart_size = (128 * 1024) << nds_header->device_capacity; cdata->cart_size = (128 * 1024) << nds_header->device_capacity;
cdata->data_size = nds_header->ntr_rom_size; cdata->data_size = nds_header->ntr_rom_size;
cdata->arm9i_rom_offset = 0; cdata->arm9i_rom_offset = 0;
// TWL header // TWL header
if (nds_header->unit_code != 0x00) { // DSi or NDS+DSi if (nds_header->unit_code != 0x00) { // DSi or NDS+DSi
cdata->cart_type |= CART_TWL; cdata->cart_type |= CART_TWL;
@ -129,10 +129,10 @@ u32 InitCartRead(CartData* cdata) {
NTR_CmdReadHeader(cdata->twl_header); NTR_CmdReadHeader(cdata->twl_header);
if (!NTR_Secure_Init(cdata->twl_header, Cart_GetID(), 1)) return 1; if (!NTR_Secure_Init(cdata->twl_header, Cart_GetID(), 1)) return 1;
} }
// last safety check // last safety check
if (cdata->data_size > cdata->cart_size) return 1; if (cdata->data_size > cdata->cart_size) return 1;
// save data // save data
bool infrared = *(nds_header->game_code) == 'I'; bool infrared = *(nds_header->game_code) == 'I';
if (CardSPIGetCardSPIType(&(cdata->save_type), infrared) != 0) { if (CardSPIGetCardSPIType(&(cdata->save_type), infrared) != 0) {
@ -203,7 +203,7 @@ u32 ReadCartSectors(void* buffer, u32 sector, u32 count, CartData* cdata) {
} }
u32 ReadCartBytes(void* buffer, u64 offset, u64 count, CartData* cdata) { u32 ReadCartBytes(void* buffer, u64 offset, u64 count, CartData* cdata) {
if (!(offset % 0x200) && !(count % 0x200)) { // aligned data -> simple case if (!(offset % 0x200) && !(count % 0x200)) { // aligned data -> simple case
// simple wrapper function for ReadCartSectors(...) // simple wrapper function for ReadCartSectors(...)
return ReadCartSectors(buffer, offset / 0x200, count / 0x200, cdata); return ReadCartSectors(buffer, offset / 0x200, count / 0x200, cdata);
} else { // misaligned data -> -___- } else { // misaligned data -> -___-

View File

@ -129,7 +129,7 @@ void NTR_InitKey (u32 aGameCode, u32* pCardHash, int nCardHash, u32* pKeyCode, i
const u8* BlowfishNtr = (const u8*)0x01FFE428; const u8* BlowfishNtr = (const u8*)0x01FFE428;
memcpy (pCardHash, BlowfishNtr, 0x1048); memcpy (pCardHash, BlowfishNtr, 0x1048);
} }
pKeyCode[0] = aGameCode; pKeyCode[0] = aGameCode;
pKeyCode[1] = aGameCode/2; pKeyCode[1] = aGameCode/2;
pKeyCode[2] = aGameCode*2; pKeyCode[2] = aGameCode*2;
@ -202,7 +202,7 @@ void NTR_SecureDelay(u16 aTimeout)
//TIMER0_CR=TIMER_DIV_256|TIMER_ENABLE; //TIMER0_CR=TIMER_DIV_256|TIMER_ENABLE;
TIMER0_CR=TIMER_DIV_1024|TIMER_ENABLE; TIMER0_CR=TIMER_DIV_1024|TIMER_ENABLE;
while(TIMER0_DATA!=0xFFFF); while(TIMER0_DATA!=0xFFFF);
// Clear out the timer registers // Clear out the timer registers
TIMER0_CR=0; TIMER0_CR=0;
TIMER0_DATA=0; TIMER0_DATA=0;

View File

@ -183,7 +183,7 @@ void CheckBattery(u32* battery, bool* is_charging) {
} }
*battery = battery_l; *battery = battery_l;
} }
if (is_charging) { if (is_charging) {
static bool is_charging_l = false; static bool is_charging_l = false;
static u64 timer_c = (u64) -1; static u64 timer_c = (u64) -1;
@ -200,19 +200,19 @@ void DrawBatteryBitmap(u16* screen, u32 b_x, u32 b_y, u32 width, u32 height) {
const u16 color_inline = COLOR_LIGHTGREY; const u16 color_inline = COLOR_LIGHTGREY;
const u16 color_inside = COLOR_LIGHTERGREY; const u16 color_inside = COLOR_LIGHTERGREY;
const u16 color_bg = COLOR_TRANSPARENT; const u16 color_bg = COLOR_TRANSPARENT;
if ((width < 8) || (height < 6)) return; if ((width < 8) || (height < 6)) return;
u32 battery; u32 battery;
bool is_charging; bool is_charging;
CheckBattery(&battery, &is_charging); CheckBattery(&battery, &is_charging);
u16 color_battery = (is_charging) ? COLOR_BATTERY_CHARGING : u16 color_battery = (is_charging) ? COLOR_BATTERY_CHARGING :
(battery > 70) ? COLOR_BATTERY_FULL : (battery > 30) ? COLOR_BATTERY_MEDIUM : COLOR_BATTERY_LOW; (battery > 70) ? COLOR_BATTERY_FULL : (battery > 30) ? COLOR_BATTERY_MEDIUM : COLOR_BATTERY_LOW;
u32 nub_size = (height < 12) ? 1 : 2; u32 nub_size = (height < 12) ? 1 : 2;
u32 width_inside = width - 4 - nub_size; u32 width_inside = width - 4 - nub_size;
u32 width_battery = (battery >= 100) ? width_inside : ((battery * width_inside) + 50) / 100; u32 width_battery = (battery >= 100) ? width_inside : ((battery * width_inside) + 50) / 100;
for (u32 y = 0; y < height; y++) { for (u32 y = 0; y < height; y++) {
const u32 mirror_y = (y >= (height+1) / 2) ? height - 1 - y : y; const u32 mirror_y = (y >= (height+1) / 2) ? height - 1 - y : y;
for (u32 x = 0; x < width; x++) { for (u32 x = 0; x < width; x++) {
@ -233,14 +233,14 @@ void DrawTopBar(const char* curr_path) {
const u32 bartxt_x = 2; const u32 bartxt_x = 2;
const u32 len_path = SCREEN_WIDTH_TOP - 120; const u32 len_path = SCREEN_WIDTH_TOP - 120;
char tempstr[64]; char tempstr[64];
// top bar - current path // top bar - current path
DrawRectangle(TOP_SCREEN, 0, 0, SCREEN_WIDTH_TOP, 12, COLOR_TOP_BAR); DrawRectangle(TOP_SCREEN, 0, 0, SCREEN_WIDTH_TOP, 12, COLOR_TOP_BAR);
if (*curr_path) TruncateString(tempstr, curr_path, min(63, len_path / FONT_WIDTH_EXT), 8); if (*curr_path) TruncateString(tempstr, curr_path, min(63, len_path / FONT_WIDTH_EXT), 8);
else snprintf(tempstr, 16, "[root]"); else snprintf(tempstr, 16, "[root]");
DrawStringF(TOP_SCREEN, bartxt_x, bartxt_start, COLOR_STD_BG, COLOR_TOP_BAR, "%s", tempstr); DrawStringF(TOP_SCREEN, bartxt_x, bartxt_start, COLOR_STD_BG, COLOR_TOP_BAR, "%s", tempstr);
bool show_time = true; bool show_time = true;
#ifdef SHOW_FREE #ifdef SHOW_FREE
if (*curr_path) { // free & total storage if (*curr_path) { // free & total storage
const u32 bartxt_rx = SCREEN_WIDTH_TOP - (19*FONT_WIDTH_EXT) - bartxt_x; const u32 bartxt_rx = SCREEN_WIDTH_TOP - (19*FONT_WIDTH_EXT) - bartxt_x;
@ -262,14 +262,14 @@ void DrawTopBar(const char* curr_path) {
show_time = false; show_time = false;
} }
#endif #endif
if (show_time) { // clock & battery if (show_time) { // clock & battery
const u32 battery_width = 16; const u32 battery_width = 16;
const u32 battery_height = 9; const u32 battery_height = 9;
const u32 battery_x = SCREEN_WIDTH_TOP - battery_width - bartxt_x; const u32 battery_x = SCREEN_WIDTH_TOP - battery_width - bartxt_x;
const u32 battery_y = (12 - battery_height) / 2; const u32 battery_y = (12 - battery_height) / 2;
const u32 clock_x = battery_x - (15*FONT_WIDTH_EXT); const u32 clock_x = battery_x - (15*FONT_WIDTH_EXT);
char timestr[32]; char timestr[32];
GetTimeString(timestr, false, false); GetTimeString(timestr, false, false);
DrawStringF(TOP_SCREEN, clock_x, bartxt_start, COLOR_STD_BG, COLOR_TOP_BAR, "%14.14s", timestr); DrawStringF(TOP_SCREEN, clock_x, bartxt_start, COLOR_STD_BG, COLOR_TOP_BAR, "%14.14s", timestr);
@ -284,7 +284,7 @@ void DrawUserInterface(const char* curr_path, DirEntry* curr_entry, u32 curr_pan
const u32 len_info = (SCREEN_WIDTH_MAIN - ((SCREEN_WIDTH_MAIN >= 400) ? 80 : 20)) / 2; const u32 len_info = (SCREEN_WIDTH_MAIN - ((SCREEN_WIDTH_MAIN >= 400) ? 80 : 20)) / 2;
const u32 str_len_info = min(63, len_info / FONT_WIDTH_EXT); const u32 str_len_info = min(63, len_info / FONT_WIDTH_EXT);
char tempstr[64]; char tempstr[64];
static u32 state_prev = 0xFFFFFFFF; static u32 state_prev = 0xFFFFFFFF;
u32 state_curr = u32 state_curr =
((*curr_path) ? (1<<0) : 0) | ((*curr_path) ? (1<<0) : 0) |
@ -293,12 +293,12 @@ void DrawUserInterface(const char* curr_path, DirEntry* curr_entry, u32 curr_pan
((GetMountState()) ? (1<<3) : 0) | ((GetMountState()) ? (1<<3) : 0) |
((GetWritePermissions() > PERM_BASE) ? (1<<4) : 0) | ((GetWritePermissions() > PERM_BASE) ? (1<<4) : 0) |
(curr_pane<<5); (curr_pane<<5);
if (state_prev != state_curr) { if (state_prev != state_curr) {
ClearScreenF(true, false, COLOR_STD_BG); ClearScreenF(true, false, COLOR_STD_BG);
state_prev = state_curr; state_prev = state_curr;
} }
// left top - current file info // left top - current file info
if (curr_pane) snprintf(tempstr, 63, "PANE #%lu", curr_pane); if (curr_pane) snprintf(tempstr, 63, "PANE #%lu", curr_pane);
else snprintf(tempstr, 63, "CURRENT"); else snprintf(tempstr, 63, "CURRENT");
@ -315,7 +315,7 @@ void DrawUserInterface(const char* curr_path, DirEntry* curr_entry, u32 curr_pan
} else if (curr_entry->type == T_ROOT) { } else if (curr_entry->type == T_ROOT) {
int drvtype = DriveType(curr_entry->path); int drvtype = DriveType(curr_entry->path);
char drvstr[32]; char drvstr[32];
snprintf(drvstr, 31, "(%s%s)", snprintf(drvstr, 31, "(%s%s)",
((drvtype & DRV_SDCARD) ? "SD" : (drvtype & DRV_RAMDRIVE) ? "RAMdrive" : (drvtype & DRV_GAME) ? "Game" : ((drvtype & DRV_SDCARD) ? "SD" : (drvtype & DRV_RAMDRIVE) ? "RAMdrive" : (drvtype & DRV_GAME) ? "Game" :
(drvtype & DRV_SYSNAND) ? "SysNAND" : (drvtype & DRV_EMUNAND) ? "EmuNAND" : (drvtype & DRV_IMAGE) ? "Image" : (drvtype & DRV_SYSNAND) ? "SysNAND" : (drvtype & DRV_EMUNAND) ? "EmuNAND" : (drvtype & DRV_IMAGE) ? "Image" :
(drvtype & DRV_XORPAD) ? "XORpad" : (drvtype & DRV_MEMORY) ? "Memory" : (drvtype & DRV_ALIAS) ? "Alias" : (drvtype & DRV_XORPAD) ? "XORpad" : (drvtype & DRV_MEMORY) ? "Memory" : (drvtype & DRV_ALIAS) ? "Alias" :
@ -341,7 +341,7 @@ void DrawUserInterface(const char* curr_path, DirEntry* curr_entry, u32 curr_pan
ResizeString(tempstr, "", str_len_info, 8, false); ResizeString(tempstr, "", str_len_info, 8, false);
DrawStringF(MAIN_SCREEN, 4, info_start + 12 + 10 + 10, color_current, COLOR_STD_BG, "%s", tempstr); DrawStringF(MAIN_SCREEN, 4, info_start + 12 + 10 + 10, color_current, COLOR_STD_BG, "%s", tempstr);
} }
// right top - clipboard // right top - clipboard
DrawStringF(MAIN_SCREEN, SCREEN_WIDTH_MAIN - len_info, info_start, COLOR_STD_FONT, COLOR_STD_BG, "%*s", DrawStringF(MAIN_SCREEN, SCREEN_WIDTH_MAIN - len_info, info_start, COLOR_STD_FONT, COLOR_STD_BG, "%*s",
len_info / FONT_WIDTH_EXT, (clipboard->n_entries) ? "[CLIPBOARD]" : ""); len_info / FONT_WIDTH_EXT, (clipboard->n_entries) ? "[CLIPBOARD]" : "");
@ -354,7 +354,7 @@ void DrawUserInterface(const char* curr_path, DirEntry* curr_entry, u32 curr_pan
if (clipboard->n_entries > n_cb_show) snprintf(tempstr, 60, "+ %lu more", clipboard->n_entries - n_cb_show); if (clipboard->n_entries > n_cb_show) snprintf(tempstr, 60, "+ %lu more", clipboard->n_entries - n_cb_show);
DrawStringF(MAIN_SCREEN, SCREEN_WIDTH_MAIN - len_info - 4, info_start + 12 + (n_cb_show*10), COLOR_DARKGREY, COLOR_STD_BG, DrawStringF(MAIN_SCREEN, SCREEN_WIDTH_MAIN - len_info - 4, info_start + 12 + (n_cb_show*10), COLOR_DARKGREY, COLOR_STD_BG,
"%*s", len_info / FONT_WIDTH_EXT, tempstr); "%*s", len_info / FONT_WIDTH_EXT, tempstr);
// bottom: instruction block // bottom: instruction block
char instr[512]; char instr[512];
snprintf(instr, 512, "%s\n%s%s%s%s%s%s%s%s", snprintf(instr, 512, "%s\n%s%s%s%s%s%s%s%s",
@ -364,7 +364,7 @@ void DrawUserInterface(const char* curr_path, DirEntry* curr_entry, u32 curr_pan
((GetWritePermissions() > PERM_BASE) ? "R+Y - Relock write permissions\n" : ""), ((GetWritePermissions() > PERM_BASE) ? "R+Y - Relock write permissions\n" : ""),
(*curr_path) ? "" : (GetMountState()) ? "R+X - Unmount image\n" : "", (*curr_path) ? "" : (GetMountState()) ? "R+X - Unmount image\n" : "",
(*curr_path) ? "" : (CheckSDMountState()) ? "R+B - Unmount SD card\n" : "R+B - Remount SD card\n", (*curr_path) ? "" : (CheckSDMountState()) ? "R+B - Unmount SD card\n" : "R+B - Remount SD card\n",
(*curr_path) ? "R+A - Directory options\n" : "R+A - Drive options\n", (*curr_path) ? "R+A - Directory options\n" : "R+A - Drive options\n",
"R+L - Make a Screenshot\n", "R+L - Make a Screenshot\n",
"R+\x1B\x1A - Switch to prev/next pane\n", "R+\x1B\x1A - Switch to prev/next pane\n",
(clipboard->n_entries) ? "SELECT - Clear Clipboard\n" : "SELECT - Restore Clipboard\n", // only if clipboard is full (clipboard->n_entries) ? "SELECT - Clear Clipboard\n" : "SELECT - Restore Clipboard\n", // only if clipboard is full
@ -379,12 +379,12 @@ void DrawDirContents(DirStruct* contents, u32 cursor, u32* scroll) {
const u32 pos_x = 0; const u32 pos_x = 0;
const u32 lines = (SCREEN_HEIGHT-(start_y+2)+(stp_y-1)) / stp_y; const u32 lines = (SCREEN_HEIGHT-(start_y+2)+(stp_y-1)) / stp_y;
u32 pos_y = start_y + 2; u32 pos_y = start_y + 2;
if (*scroll > cursor) *scroll = cursor; if (*scroll > cursor) *scroll = cursor;
else if (*scroll + lines <= cursor) *scroll = cursor - lines + 1; else if (*scroll + lines <= cursor) *scroll = cursor - lines + 1;
if (*scroll + lines > contents->n_entries) if (*scroll + lines > contents->n_entries)
*scroll = (contents->n_entries > lines) ? contents->n_entries - lines : 0; *scroll = (contents->n_entries > lines) ? contents->n_entries - lines : 0;
for (u32 i = 0; pos_y < SCREEN_HEIGHT; i++) { for (u32 i = 0; pos_y < SCREEN_HEIGHT; i++) {
char tempstr[str_width + 1]; char tempstr[str_width + 1];
u32 offset_i = *scroll + i; u32 offset_i = *scroll + i;
@ -402,7 +402,7 @@ void DrawDirContents(DirStruct* contents, u32 cursor, u32* scroll) {
DrawStringF(ALT_SCREEN, pos_x, pos_y, color_font, COLOR_STD_BG, "%s", tempstr); DrawStringF(ALT_SCREEN, pos_x, pos_y, color_font, COLOR_STD_BG, "%s", tempstr);
pos_y += stp_y; pos_y += stp_y;
} }
const u32 flist_height = (SCREEN_HEIGHT - start_y); const u32 flist_height = (SCREEN_HEIGHT - start_y);
const u32 bar_width = 2; const u32 bar_width = 2;
if (contents->n_entries > lines) { // draw position bar at the right if (contents->n_entries > lines) { // draw position bar at the right
@ -410,7 +410,7 @@ void DrawDirContents(DirStruct* contents, u32 cursor, u32* scroll) {
u32 bar_height = (lines * flist_height) / contents->n_entries; u32 bar_height = (lines * flist_height) / contents->n_entries;
if (bar_height < bar_height_min) bar_height = bar_height_min; if (bar_height < bar_height_min) bar_height = bar_height_min;
const u32 bar_pos = ((u64) *scroll * (flist_height - bar_height)) / (contents->n_entries - lines) + start_y; const u32 bar_pos = ((u64) *scroll * (flist_height - bar_height)) / (contents->n_entries - lines) + start_y;
DrawRectangle(ALT_SCREEN, SCREEN_WIDTH_ALT - bar_width, start_y, bar_width, (bar_pos - start_y), COLOR_STD_BG); DrawRectangle(ALT_SCREEN, SCREEN_WIDTH_ALT - bar_width, start_y, bar_width, (bar_pos - start_y), COLOR_STD_BG);
DrawRectangle(ALT_SCREEN, SCREEN_WIDTH_ALT - bar_width, bar_pos + bar_height, bar_width, SCREEN_HEIGHT - (bar_pos + bar_height), COLOR_STD_BG); DrawRectangle(ALT_SCREEN, SCREEN_WIDTH_ALT - bar_width, bar_pos + bar_height, bar_width, SCREEN_HEIGHT - (bar_pos + bar_height), COLOR_STD_BG);
DrawRectangle(ALT_SCREEN, SCREEN_WIDTH_ALT - bar_width, bar_pos, bar_width, bar_height, COLOR_SIDE_BAR); DrawRectangle(ALT_SCREEN, SCREEN_WIDTH_ALT - bar_width, bar_pos, bar_width, bar_height, COLOR_SIDE_BAR);
@ -431,14 +431,14 @@ u32 SdFormatMenu(const char* slabel) {
u64 sdcard_size_mb = 0; u64 sdcard_size_mb = 0;
u64 emunand_size_mb = (u64) -1; u64 emunand_size_mb = (u64) -1;
u32 user_select; u32 user_select;
// check actual SD card size // check actual SD card size
sdcard_size_mb = GetSDCardSize() / 0x100000; sdcard_size_mb = GetSDCardSize() / 0x100000;
if (!sdcard_size_mb) { if (!sdcard_size_mb) {
ShowPrompt(false, "Error: SD card not detected."); ShowPrompt(false, "Error: SD card not detected.");
return 1; return 1;
} }
user_select = ShowSelectPrompt(7, option_emunand_size, "Format SD card (%lluMB)?\nChoose EmuNAND size:", sdcard_size_mb); user_select = ShowSelectPrompt(7, option_emunand_size, "Format SD card (%lluMB)?\nChoose EmuNAND size:", sdcard_size_mb);
if (user_select && (user_select < 4)) { if (user_select && (user_select < 4)) {
emunand_size_mb = (user_select == 2) ? sysnand_min_size_mb : (user_select == 3) ? sysnand_size_mb : 0; emunand_size_mb = (user_select == 2) ? sysnand_min_size_mb : (user_select == 3) ? sysnand_size_mb : 0;
@ -450,20 +450,20 @@ u32 SdFormatMenu(const char* slabel) {
if (emunand_size_mb == (u64) -1) break; if (emunand_size_mb == (u64) -1) break;
} while (emunand_size_mb > sdcard_size_mb); } while (emunand_size_mb > sdcard_size_mb);
if (emunand_size_mb == (u64) -1) return 1; if (emunand_size_mb == (u64) -1) return 1;
user_select = ShowSelectPrompt(4, option_cluster_size, "Format SD card (%lluMB)?\nChoose cluster size:", sdcard_size_mb); user_select = ShowSelectPrompt(4, option_cluster_size, "Format SD card (%lluMB)?\nChoose cluster size:", sdcard_size_mb);
if (!user_select) return 1; if (!user_select) return 1;
else cluster_size = cluster_size_table[user_select]; else cluster_size = cluster_size_table[user_select];
snprintf(label, DRV_LABEL_LEN + 4, "0:%s", (slabel && *slabel) ? slabel : "GM9SD"); snprintf(label, DRV_LABEL_LEN + 4, "0:%s", (slabel && *slabel) ? slabel : "GM9SD");
if (!ShowKeyboardOrPrompt(label + 2, 11 + 1, "Format SD card (%lluMB)?\nEnter label:", sdcard_size_mb)) if (!ShowKeyboardOrPrompt(label + 2, 11 + 1, "Format SD card (%lluMB)?\nEnter label:", sdcard_size_mb))
return 1; return 1;
if (!FormatSDCard(emunand_size_mb, cluster_size, label)) { if (!FormatSDCard(emunand_size_mb, cluster_size, label)) {
ShowPrompt(false, "Format SD: failed!"); ShowPrompt(false, "Format SD: failed!");
return 1; return 1;
} }
if (emunand_size_mb >= sysnand_min_size_mb) { if (emunand_size_mb >= sysnand_min_size_mb) {
u32 emunand_offset = 1; u32 emunand_offset = 1;
u32 n_emunands = 1; u32 n_emunands = 1;
@ -480,13 +480,13 @@ u32 SdFormatMenu(const char* slabel) {
emunand_offset = (user_select == 2) ? 0 : 1; // 0 -> GW EmuNAND emunand_offset = (user_select == 2) ? 0 : 1; // 0 -> GW EmuNAND
} else user_select = ShowPrompt(true, "Clone SysNAND to RedNAND?") ? 1 : 0; } else user_select = ShowPrompt(true, "Clone SysNAND to RedNAND?") ? 1 : 0;
if (!user_select) return 0; if (!user_select) return 0;
u8 ncsd[0x200]; u8 ncsd[0x200];
u32 flags = OVERRIDE_PERM; u32 flags = OVERRIDE_PERM;
InitSDCardFS(); // this has to be initialized for EmuNAND to work InitSDCardFS(); // this has to be initialized for EmuNAND to work
for (u32 i = 0; i < n_emunands; i++) { for (u32 i = 0; i < n_emunands; i++) {
if ((i * sysnand_multi_size_mb) + sysnand_min_size_mb > emunand_size_mb) break; if ((i * sysnand_multi_size_mb) + sysnand_min_size_mb > emunand_size_mb) break;
SetEmuNandBase((i * sysnand_multi_size_mb * 0x100000 / 0x200) + emunand_offset); SetEmuNandBase((i * sysnand_multi_size_mb * 0x100000 / 0x200) + emunand_offset);
if ((ReadNandSectors(ncsd, 0, 1, 0xFF, NAND_SYSNAND) != 0) || if ((ReadNandSectors(ncsd, 0, 1, 0xFF, NAND_SYSNAND) != 0) ||
(WriteNandSectors(ncsd, 0, 1, 0xFF, NAND_EMUNAND) != 0) || (WriteNandSectors(ncsd, 0, 1, 0xFF, NAND_EMUNAND) != 0) ||
(!PathCopy("E:", "S:/nand_minsize.bin", &flags))) { (!PathCopy("E:", "S:/nand_minsize.bin", &flags))) {
@ -496,7 +496,7 @@ u32 SdFormatMenu(const char* slabel) {
} }
DeinitSDCardFS(); DeinitSDCardFS();
} }
return 0; return 0;
} }
@ -539,51 +539,51 @@ u32 FileHexViewer(const char* path) {
u8* data = NULL; u8* data = NULL;
u8* bottom_cpy = (u8*) malloc(SCREEN_SIZE_BOT); // 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;
int x_off = 0, x_hex = 0, x_ascii = 0; int x_off = 0, x_hex = 0, x_ascii = 0;
u32 vpad = 0, hlpad = 0, hrpad = 0; u32 vpad = 0, hlpad = 0, hrpad = 0;
u32 rows = 0, cols = 0; u32 rows = 0, cols = 0;
u32 total_shown = 0; u32 total_shown = 0;
u32 total_data = 0; u32 total_data = 0;
u32 last_mode = 0xFF; u32 last_mode = 0xFF;
u32 last_offset = (u32) -1; u32 last_offset = (u32) -1;
u32 offset = 0; u32 offset = 0;
u8 found_data[64 + 1] = { 0 }; u8 found_data[64 + 1] = { 0 };
u32 found_offset = (u32) -1; u32 found_offset = (u32) -1;
u32 found_size = 0; u32 found_size = 0;
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* buffer = (u8*) malloc(edit_bsize); u8* buffer = (u8*) malloc(edit_bsize);
u8* buffer_cpy = (u8*) malloc(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 || !buffer || !buffer_cpy) {
if (bottom_cpy) free(bottom_cpy); if (bottom_cpy) free(bottom_cpy);
if (buffer) free(buffer); if (buffer) free(buffer);
if (buffer_cpy) free(buffer_cpy); if (buffer_cpy) free(buffer_cpy);
return 1; 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
ShowPrompt(false, instr); ShowPrompt(false, instr);
show_instr = false; show_instr = false;
} }
if (MAIN_SCREEN != TOP_SCREEN) ShowString(instr); if (MAIN_SCREEN != TOP_SCREEN) ShowString(instr);
memcpy(bottom_cpy, BOT_SCREEN, SCREEN_SIZE_BOT); memcpy(bottom_cpy, BOT_SCREEN, SCREEN_SIZE_BOT);
data = buffer; data = buffer;
while (true) { while (true) {
if (mode != last_mode) { if (mode != last_mode) {
if (FONT_WIDTH_EXT <= 5) { if (FONT_WIDTH_EXT <= 5) {
mode = 0; mode = 0;
vpad = 1; vpad = 1;
hlpad = hrpad = 2; hlpad = hrpad = 2;
cols = 16; cols = 16;
@ -602,7 +602,7 @@ u32 FileHexViewer(const char* path) {
x_hex = x_off + (8*FONT_WIDTH_EXT) + 16; x_hex = x_off + (8*FONT_WIDTH_EXT) + 16;
dual_screen = false; dual_screen = false;
} else { } else {
mode = 0; mode = 0;
vpad = 0; vpad = 0;
hlpad = hrpad = 3; hlpad = hrpad = 3;
cols = 8; cols = 8;
@ -641,7 +641,7 @@ u32 FileHexViewer(const char* path) {
dual_screen = false; dual_screen = false;
break; break;
default: default:
mode = 0; mode = 0;
vpad = hlpad = hrpad = 2; vpad = hlpad = hrpad = 2;
cols = 8; cols = 8;
x_off = (SCREEN_WIDTH_TOP - SCREEN_WIDTH_BOT) / 2; x_off = (SCREEN_WIDTH_TOP - SCREEN_WIDTH_BOT) / 2;
@ -673,7 +673,7 @@ u32 FileHexViewer(const char* path) {
} }
last_offset = offset; last_offset = offset;
} }
// display data on screen // display data on screen
for (u32 row = 0; row < rows; row++) { for (u32 row = 0; row < rows; row++) {
char ascii[16 + 1] = { 0 }; char ascii[16 + 1] = { 0 };
@ -682,29 +682,29 @@ u32 FileHexViewer(const char* path) {
u32 cutoff = (curr_pos >= total_data) ? 0 : (total_data >= curr_pos + cols) ? cols : total_data - curr_pos; u32 cutoff = (curr_pos >= total_data) ? 0 : (total_data >= curr_pos + cols) ? cols : total_data - curr_pos;
u16* screen = TOP_SCREEN; u16* screen = TOP_SCREEN;
u32 x0 = 0; u32 x0 = 0;
// marked offsets handling // marked offsets handling
s32 marked0 = 0, marked1 = 0; s32 marked0 = 0, marked1 = 0;
if ((found_size > 0) && if ((found_size > 0) &&
(found_offset + found_size > offset + curr_pos) && (found_offset + found_size > offset + curr_pos) &&
(found_offset < offset + curr_pos + cols)) { (found_offset < offset + curr_pos + cols)) {
marked0 = (s32) found_offset - (offset + curr_pos); marked0 = (s32) found_offset - (offset + curr_pos);
marked1 = marked0 + found_size; marked1 = marked0 + found_size;
if (marked0 < 0) marked0 = 0; if (marked0 < 0) marked0 = 0;
if (marked1 > cols) marked1 = cols; if (marked1 > cols) marked1 = cols;
} }
// switch to bottom screen // switch to bottom screen
if (y >= SCREEN_HEIGHT) { if (y >= SCREEN_HEIGHT) {
y -= SCREEN_HEIGHT; y -= SCREEN_HEIGHT;
screen = BOT_SCREEN; screen = BOT_SCREEN;
x0 = 40; x0 = 40;
} }
memcpy(ascii, data + curr_pos, cols); memcpy(ascii, data + curr_pos, cols);
for (u32 col = 0; col < cols; col++) for (u32 col = 0; col < cols; col++)
if ((col >= cutoff) || (ascii[col] == 0x00)) ascii[col] = ' '; if ((col >= cutoff) || (ascii[col] == 0x00)) ascii[col] = ' ';
// draw offset / ASCII representation // draw offset / ASCII representation
if (x_off >= 0) DrawStringF(screen, x_off - x0, y, cutoff ? COLOR_HVOFFS : COLOR_HVOFFSI, if (x_off >= 0) DrawStringF(screen, x_off - x0, y, cutoff ? COLOR_HVOFFS : COLOR_HVOFFSI,
COLOR_STD_BG, "%08X", (unsigned int) offset + curr_pos); COLOR_STD_BG, "%08X", (unsigned int) offset + curr_pos);
@ -715,7 +715,7 @@ u32 FileHexViewer(const char* path) {
if (edit_mode && ((u32) cursor / cols == row)) DrawCharacter(screen, ascii[cursor % cols], if (edit_mode && ((u32) cursor / cols == row)) DrawCharacter(screen, ascii[cursor % cols],
x_ascii - x0 + FONT_WIDTH_EXT * (cursor % cols), y, COLOR_RED, COLOR_STD_BG); x_ascii - x0 + FONT_WIDTH_EXT * (cursor % cols), y, COLOR_RED, COLOR_STD_BG);
} }
// draw HEX values // draw HEX values
for (u32 col = 0; (col < cols) && (x_hex >= 0); col++) { for (u32 col = 0; (col < cols) && (x_hex >= 0); col++) {
u32 x = (x_hex + hlpad) + (((2*FONT_WIDTH_EXT) + hrpad + hlpad) * col) - x0; u32 x = (x_hex + hlpad) + (((2*FONT_WIDTH_EXT) + hrpad + hlpad) * col) - x0;
@ -726,7 +726,7 @@ u32 FileHexViewer(const char* path) {
else DrawStringF(screen, x, y, hex_color, COLOR_STD_BG, " "); else DrawStringF(screen, x, y, hex_color, COLOR_STD_BG, " ");
} }
} }
// handle user input // handle user input
u32 pad_state = InputWait(0); u32 pad_state = InputWait(0);
if (!edit_mode) { // standard viewer mode if (!edit_mode) { // standard viewer mode
@ -750,7 +750,7 @@ u32 FileHexViewer(const char* path) {
else memcpy(BOT_SCREEN, bottom_cpy, SCREEN_SIZE_BOT); else memcpy(BOT_SCREEN, bottom_cpy, SCREEN_SIZE_BOT);
} else if (pad_state & BUTTON_X) { } else if (pad_state & BUTTON_X) {
static const char* optionstr[3] = { "Go to offset", "Search for string", "Search for data" }; static const char* optionstr[3] = { "Go to offset", "Search for string", "Search for data" };
u32 user_select = ShowSelectPrompt(3, optionstr, "Current offset: %08X\nSelect action:", u32 user_select = ShowSelectPrompt(3, optionstr, "Current offset: %08X\nSelect action:",
(unsigned int) offset); (unsigned int) offset);
if (user_select == 1) { // -> goto offset if (user_select == 1) { // -> goto offset
u64 new_offset = ShowHexPrompt(offset, 8, "Current offset: %08X\nEnter new offset below.", u64 new_offset = ShowHexPrompt(offset, 8, "Current offset: %08X\nEnter new offset below.",
@ -785,7 +785,7 @@ u32 FileHexViewer(const char* path) {
found_size = 0; found_size = 0;
found_offset = (u32) -1; found_offset = (u32) -1;
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, buffer, edit_bsize, edit_start); FileGetData(path, buffer, edit_bsize, edit_start);
memcpy(buffer_cpy, buffer, edit_bsize); memcpy(buffer_cpy, buffer, edit_bsize);
@ -831,11 +831,11 @@ 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_SIZE_BOT); 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(bottom_cpy);
free(buffer); free(buffer);
free(buffer_cpy); free(buffer_cpy);
@ -855,7 +855,7 @@ u32 Sha256Calculator(const char* path) {
static u8 sha256_prev[32] = { 0 }; static u8 sha256_prev[32] = { 0 };
char sha_path[256]; char sha_path[256];
u8 sha256_file[32]; u8 sha256_file[32];
snprintf(sha_path, 256, "%s.sha", path); snprintf(sha_path, 256, "%s.sha", path);
bool have_sha = (FileGetData(sha_path, sha256_file, 32, 0) == 32); bool have_sha = (FileGetData(sha_path, sha256_file, 32, 0) == 32);
bool match_sha = have_sha && (memcmp(sha256, sha256_file, 32) == 0); bool match_sha = have_sha && (memcmp(sha256, sha256_file, 32) == 0);
@ -870,11 +870,11 @@ u32 Sha256Calculator(const char* path) {
(write_sha) ? "\n \nWrite .SHA file?" : "") && write_sha) { (write_sha) ? "\n \nWrite .SHA file?" : "") && write_sha) {
FileSetData(sha_path, sha256, 32, 0, true); FileSetData(sha_path, sha256, 32, 0, true);
} }
strncpy(pathstr_prev, pathstr, 32 + 1); strncpy(pathstr_prev, pathstr, 32 + 1);
memcpy(sha256_prev, sha256, 32); memcpy(sha256_prev, sha256, 32);
} }
return 0; return 0;
} }
@ -906,8 +906,8 @@ u32 CmacCalculator(const char* path) {
ShowPrompt(false, "Fixing CMAC: failed!"); ShowPrompt(false, "Fixing CMAC: failed!");
} }
} }
return 0; return 0;
} }
@ -915,16 +915,16 @@ u32 StandardCopy(u32* cursor, u32* scroll) {
DirEntry* curr_entry = &(current_dir->entry[*cursor]); DirEntry* curr_entry = &(current_dir->entry[*cursor]);
u32 n_marked = 0; u32 n_marked = 0;
if (curr_entry->marked) { if (curr_entry->marked) {
for (u32 i = 0; i < current_dir->n_entries; i++) for (u32 i = 0; i < current_dir->n_entries; i++)
if (current_dir->entry[i].marked) n_marked++; if (current_dir->entry[i].marked) n_marked++;
} }
u32 flags = BUILD_PATH; u32 flags = BUILD_PATH;
if ((n_marked > 1) && ShowPrompt(true, "Copy all %lu selected items?", n_marked)) { if ((n_marked > 1) && ShowPrompt(true, "Copy all %lu selected items?", n_marked)) {
u32 n_success = 0; u32 n_success = 0;
for (u32 i = 0; i < current_dir->n_entries; i++) { for (u32 i = 0; i < current_dir->n_entries; i++) {
const char* path = current_dir->entry[i].path; const char* path = current_dir->entry[i].path;
if (!current_dir->entry[i].marked) if (!current_dir->entry[i].marked)
continue; continue;
flags |= ASK_ALL; flags |= ASK_ALL;
DrawDirContents(current_dir, (*cursor = i), scroll); DrawDirContents(current_dir, (*cursor = i), scroll);
@ -945,7 +945,7 @@ u32 StandardCopy(u32* cursor, u32* scroll) {
ShowPrompt(false, "%s\nFailed copying item", pathstr); ShowPrompt(false, "%s\nFailed copying item", pathstr);
else ShowPrompt(false, "%s\nCopied to %s", pathstr, OUTPUT_PATH); else ShowPrompt(false, "%s\nCopied to %s", pathstr, OUTPUT_PATH);
} }
return 0; return 0;
} }
@ -960,7 +960,7 @@ u32 DirFileAttrMenu(const char* path, const char *name) {
TruncateString(namestr, name, 31, 8); TruncateString(namestr, name, 31, 8);
// preparations: create file info, date string // preparations: create file info, date string
if (!drv) { if (!drv) {
if (fvx_stat(path, &fno) != FR_OK) return 1; if (fvx_stat(path, &fno) != FR_OK) return 1;
vrt = (fno.fattrib & AM_VRT); vrt = (fno.fattrib & AM_VRT);
new_attrib = fno.fattrib; new_attrib = fno.fattrib;
@ -1071,18 +1071,18 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
const char* file_path = (&(current_dir->entry[*cursor]))->path; const char* file_path = (&(current_dir->entry[*cursor]))->path;
const char* file_name = (&(current_dir->entry[*cursor]))->name; const char* file_name = (&(current_dir->entry[*cursor]))->name;
const char* optionstr[16]; const char* optionstr[16];
// check for file lock // check for file lock
if (!FileUnlock(file_path)) return 1; if (!FileUnlock(file_path)) return 1;
u64 filetype = IdentifyFileType(file_path); u64 filetype = IdentifyFileType(file_path);
u32 drvtype = DriveType(file_path); u32 drvtype = DriveType(file_path);
bool in_output_path = (strncasecmp(current_path, OUTPUT_PATH, 256) == 0); bool in_output_path = (strncasecmp(current_path, OUTPUT_PATH, 256) == 0);
// don't handle TMDs inside the game drive, won't work properly anyways // don't handle TMDs inside the game drive, won't work properly anyways
if ((filetype & GAME_TMD) && (drvtype & DRV_GAME)) filetype &= ~GAME_TMD; if ((filetype & GAME_TMD) && (drvtype & DRV_GAME)) filetype &= ~GAME_TMD;
// special stuff, only available for known filetypes (see int special below) // special stuff, only available for known filetypes (see int special below)
bool mountable = (FTYPE_MOUNTABLE(filetype) && !(drvtype & DRV_IMAGE) && bool mountable = (FTYPE_MOUNTABLE(filetype) && !(drvtype & DRV_IMAGE) &&
!((drvtype & (DRV_SYSNAND|DRV_EMUNAND)) && (drvtype & DRV_VIRTUAL) && (filetype & IMG_FAT))); !((drvtype & (DRV_SYSNAND|DRV_EMUNAND)) && (drvtype & DRV_VIRTUAL) && (filetype & IMG_FAT)));
@ -1100,7 +1100,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
bool key_buildable = (FTYPE_KEYBUILD(filetype)) && !in_output_path && bool key_buildable = (FTYPE_KEYBUILD(filetype)) && !in_output_path &&
!((drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND)); !((drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND));
bool titleinfo = (FTYPE_TITLEINFO(filetype)); bool titleinfo = (FTYPE_TITLEINFO(filetype));
bool ciacheckable = (FTYPE_CIACHECK(filetype)); bool ciacheckable = (FTYPE_CIACHECK(filetype));
bool renamable = (FTYPE_RENAMABLE(filetype)) && !(drvtype & DRV_VIRTUAL) && !(drvtype & DRV_ALIAS) && bool renamable = (FTYPE_RENAMABLE(filetype)) && !(drvtype & DRV_VIRTUAL) && !(drvtype & DRV_ALIAS) &&
!(drvtype & DRV_CTRNAND) && !(drvtype & DRV_TWLNAND) && !(drvtype & DRV_IMAGE); !(drvtype & DRV_CTRNAND) && !(drvtype & DRV_TWLNAND) && !(drvtype & DRV_IMAGE);
bool trimable = (FTYPE_TRIMABLE(filetype)) && !(drvtype & DRV_VIRTUAL) && !(drvtype & DRV_ALIAS) && bool trimable = (FTYPE_TRIMABLE(filetype)) && !(drvtype & DRV_VIRTUAL) && !(drvtype & DRV_ALIAS) &&
@ -1122,7 +1122,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
bool installable = (FTYPE_INSTALLABLE(filetype)); bool installable = (FTYPE_INSTALLABLE(filetype));
bool agbexportable = (FTYPE_AGBSAVE(filetype) && (drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND)); bool agbexportable = (FTYPE_AGBSAVE(filetype) && (drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND));
bool agbimportable = (FTYPE_AGBSAVE(filetype) && (drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND)); bool agbimportable = (FTYPE_AGBSAVE(filetype) && (drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND));
char cxi_path[256] = { 0 }; // special options for TMD char cxi_path[256] = { 0 }; // special options for TMD
if ((filetype & GAME_TMD) && !(filetype & FLAG_NUSCDN) && if ((filetype & GAME_TMD) && !(filetype & FLAG_NUSCDN) &&
(GetTmdContentPath(cxi_path, file_path) == 0) && (GetTmdContentPath(cxi_path, file_path) == 0) &&
@ -1131,18 +1131,18 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
mountable = (FTYPE_MOUNTABLE(filetype_cxi) && !(drvtype & DRV_IMAGE)); mountable = (FTYPE_MOUNTABLE(filetype_cxi) && !(drvtype & DRV_IMAGE));
extrcodeable = (FTYPE_HASCODE(filetype_cxi)); extrcodeable = (FTYPE_HASCODE(filetype_cxi));
} }
bool special_opt = mountable || verificable || decryptable || encryptable || cia_buildable || cia_buildable_legit || cxi_dumpable || tik_buildable || key_buildable || titleinfo || renamable || trimable || transferable || hsinjectable || restorable || xorpadable || ebackupable || ncsdfixable || extrcodeable || keyinitable || keyinstallable || bootable || scriptable || fontable || viewable || installable || agbexportable || agbimportable; bool special_opt = mountable || verificable || decryptable || encryptable || cia_buildable || cia_buildable_legit || cxi_dumpable || tik_buildable || key_buildable || titleinfo || renamable || trimable || transferable || hsinjectable || restorable || xorpadable || ebackupable || ncsdfixable || extrcodeable || keyinitable || keyinstallable || bootable || scriptable || fontable || viewable || installable || agbexportable || agbimportable;
char pathstr[32+1]; char pathstr[32+1];
TruncateString(pathstr, file_path, 32, 8); TruncateString(pathstr, file_path, 32, 8);
u32 n_marked = 0; u32 n_marked = 0;
if ((&(current_dir->entry[*cursor]))->marked) { if ((&(current_dir->entry[*cursor]))->marked) {
for (u32 i = 0; i < current_dir->n_entries; i++) for (u32 i = 0; i < current_dir->n_entries; i++)
if (current_dir->entry[i].marked) n_marked++; if (current_dir->entry[i].marked) n_marked++;
} }
// main menu processing // main menu processing
int n_opt = 0; int n_opt = 0;
int special = (special_opt) ? ++n_opt : -1; int special = (special_opt) ? ++n_opt : -1;
@ -1199,7 +1199,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
if (inject > 0) optionstr[inject-1] = "Inject data @offset"; if (inject > 0) optionstr[inject-1] = "Inject data @offset";
if (searchdrv > 0) optionstr[searchdrv-1] = "Open containing folder"; if (searchdrv > 0) optionstr[searchdrv-1] = "Open containing folder";
if (titleman > 0) optionstr[titleman-1] = "Open title folder"; if (titleman > 0) optionstr[titleman-1] = "Open title folder";
int user_select = ShowSelectPrompt(n_opt, optionstr, (n_marked > 1) ? int user_select = ShowSelectPrompt(n_opt, optionstr, (n_marked > 1) ?
"%s\n%(%lu files selected)" : "%s", pathstr, n_marked); "%s\n%(%lu files selected)" : "%s", pathstr, n_marked);
if (user_select == hexviewer) { // -> show in hex viewer if (user_select == hexviewer) { // -> show in hex viewer
@ -1307,7 +1307,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
else if (user_select != special) { else if (user_select != special) {
return 1; return 1;
} }
// stuff for special menu starts here // stuff for special menu starts here
n_opt = 0; n_opt = 0;
int show_info = (titleinfo) ? ++n_opt : -1; int show_info = (titleinfo) ? ++n_opt : -1;
@ -1378,7 +1378,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
if (agbexport > 0) optionstr[agbexport-1] = "Dump GBA VC save"; if (agbexport > 0) optionstr[agbexport-1] = "Dump GBA VC save";
if (agbimport > 0) optionstr[agbimport-1] = "Inject GBA VC save"; if (agbimport > 0) optionstr[agbimport-1] = "Inject GBA VC save";
if (setup > 0) optionstr[setup-1] = "Set as default"; if (setup > 0) optionstr[setup-1] = "Set as default";
// auto select when there is only one option // auto select when there is only one option
user_select = (n_opt <= 1) ? n_opt : (int) ShowSelectPrompt(n_opt, optionstr, (n_marked > 1) ? user_select = (n_opt <= 1) ? n_opt : (int) ShowSelectPrompt(n_opt, optionstr, (n_marked > 1) ?
"%s\n%(%lu files selected)" : "%s", pathstr, n_marked); "%s\n%(%lu files selected)" : "%s", pathstr, n_marked);
@ -1387,13 +1387,13 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
if (clipboard->n_entries && (DriveType(clipboard->entry[0].path) & DRV_IMAGE)) if (clipboard->n_entries && (DriveType(clipboard->entry[0].path) & DRV_IMAGE))
clipboard->n_entries = 0; // remove last mounted image clipboard entries clipboard->n_entries = 0; // remove last mounted image clipboard entries
InitImgFS((filetype & GAME_TMD) ? cxi_path : file_path); InitImgFS((filetype & GAME_TMD) ? cxi_path : file_path);
const char* drv_path = NULL; // find path of mounted drive const char* drv_path = NULL; // find path of mounted drive
for (u32 i = 0; i < (sizeof(mnt_drv_paths) / sizeof(const char*)); i++) { for (u32 i = 0; i < (sizeof(mnt_drv_paths) / sizeof(const char*)); i++) {
if (DriveType((drv_path = mnt_drv_paths[i]))) break; if (DriveType((drv_path = mnt_drv_paths[i]))) break;
drv_path = NULL; drv_path = NULL;
} }
if (!drv_path) { if (!drv_path) {
ShowPrompt(false, "Mounting image: failed"); ShowPrompt(false, "Mounting image: failed");
InitImgFS(NULL); InitImgFS(NULL);
@ -1405,7 +1405,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
(*pane)->scroll = *scroll; (*pane)->scroll = *scroll;
if (++*pane >= panedata + N_PANES) *pane -= N_PANES; if (++*pane >= panedata + N_PANES) *pane -= N_PANES;
} }
strncpy(current_path, drv_path, 256); strncpy(current_path, drv_path, 256);
GetDirContents(current_dir, current_path); GetDirContents(current_dir, current_path);
*cursor = 1; *cursor = 1;
@ -1430,7 +1430,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
ShowString("Trying to decrypt %lu files...", n_marked); ShowString("Trying to decrypt %lu files...", n_marked);
for (u32 i = 0; i < current_dir->n_entries; i++) { for (u32 i = 0; i < current_dir->n_entries; i++) {
const char* path = current_dir->entry[i].path; const char* path = current_dir->entry[i].path;
if (!current_dir->entry[i].marked) if (!current_dir->entry[i].marked)
continue; continue;
if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) { if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) {
n_other++; n_other++;
@ -1483,7 +1483,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
ShowString("Trying to encrypt %lu files...", n_marked); ShowString("Trying to encrypt %lu files...", n_marked);
for (u32 i = 0; i < current_dir->n_entries; i++) { for (u32 i = 0; i < current_dir->n_entries; i++) {
const char* path = current_dir->entry[i].path; const char* path = current_dir->entry[i].path;
if (!current_dir->entry[i].marked) if (!current_dir->entry[i].marked)
continue; continue;
if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) { if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) {
n_other++; n_other++;
@ -1518,10 +1518,10 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
bool force_legit = (user_select == cia_build_legit); bool force_legit = (user_select == cia_build_legit);
if ((n_marked > 1) && ShowPrompt(true, "Try to process all %lu selected files?", n_marked)) { if ((n_marked > 1) && ShowPrompt(true, "Try to process all %lu selected files?", n_marked)) {
u32 n_success = 0; u32 n_success = 0;
u32 n_other = 0; u32 n_other = 0;
for (u32 i = 0; i < current_dir->n_entries; i++) { for (u32 i = 0; i < current_dir->n_entries; i++) {
const char* path = current_dir->entry[i].path; const char* path = current_dir->entry[i].path;
if (!current_dir->entry[i].marked) if (!current_dir->entry[i].marked)
continue; continue;
if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) { if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) {
n_other++; n_other++;
@ -1579,7 +1579,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
ShowString("Trying to install %lu files...", n_marked); ShowString("Trying to install %lu files...", n_marked);
for (u32 i = 0; i < current_dir->n_entries; i++) { for (u32 i = 0; i < current_dir->n_entries; i++) {
const char* path = current_dir->entry[i].path; const char* path = current_dir->entry[i].path;
if (!current_dir->entry[i].marked) if (!current_dir->entry[i].marked)
continue; continue;
if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) { if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) {
n_other++; n_other++;
@ -1617,7 +1617,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
optionstr[0] = "Keep ticket & savegame"; optionstr[0] = "Keep ticket & savegame";
optionstr[1] = "Uninstall everything"; optionstr[1] = "Uninstall everything";
optionstr[2] = "Abort uninstall"; optionstr[2] = "Abort uninstall";
user_select = (int) (n_marked > 1) ? user_select = (int) (n_marked > 1) ?
ShowSelectPrompt(3, optionstr, "Uninstall %lu selected titles?", n_marked) : ShowSelectPrompt(3, optionstr, "Uninstall %lu selected titles?", n_marked) :
ShowSelectPrompt(3, optionstr, "%s\nUninstall selected title?", pathstr); ShowSelectPrompt(3, optionstr, "%s\nUninstall selected title?", pathstr);
full_uninstall = (user_select == 2); full_uninstall = (user_select == 2);
@ -1655,7 +1655,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
u32 n_processed = 0; u32 n_processed = 0;
for (u32 i = 0; i < current_dir->n_entries; i++) { for (u32 i = 0; i < current_dir->n_entries; i++) {
const char* path = current_dir->entry[i].path; const char* path = current_dir->entry[i].path;
if (!current_dir->entry[i].marked) if (!current_dir->entry[i].marked)
continue; continue;
if (!(filetype & (GAME_CIA|GAME_TMD|GAME_NCSD|GAME_NCCH)) && if (!(filetype & (GAME_CIA|GAME_TMD|GAME_NCSD|GAME_NCCH)) &&
!ShowProgress(n_processed++, n_marked, path)) break; !ShowProgress(n_processed++, n_marked, path)) break;
@ -1679,7 +1679,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
} }
if (n_other) ShowPrompt(false, "%lu/%lu files verified ok\n%lu/%lu not of same type", if (n_other) ShowPrompt(false, "%lu/%lu files verified ok\n%lu/%lu not of same type",
n_success, n_marked, n_other, n_marked); n_success, n_marked, n_other, n_marked);
else ShowPrompt(false, "%lu/%lu files verified ok", n_success, n_marked); else ShowPrompt(false, "%lu/%lu files verified ok", n_success, n_marked);
} else { } else {
ShowString("%s\nVerifying file, please wait...", pathstr); ShowString("%s\nVerifying file, please wait...", pathstr);
if (filetype & IMG_NAND) { if (filetype & IMG_NAND) {
@ -1712,9 +1712,9 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
if (BuildTitleKeyInfo(NULL, dec, true) == 0) { if (BuildTitleKeyInfo(NULL, dec, true) == 0) {
if (n_other) ShowPrompt(false, "%s\n%lu/%lu files processed\n%lu/%lu files ignored", if (n_other) ShowPrompt(false, "%s\n%lu/%lu files processed\n%lu/%lu files ignored",
path_out, n_success, n_marked, n_other, n_marked); path_out, n_success, n_marked, n_other, n_marked);
else ShowPrompt(false, "%s\n%lu/%lu files processed", path_out, n_success, n_marked); else ShowPrompt(false, "%s\n%lu/%lu files processed", path_out, n_success, n_marked);
} else ShowPrompt(false, "%s\nBuild database failed.", path_out); } else ShowPrompt(false, "%s\nBuild database failed.", path_out);
} else ShowPrompt(false, "%s\nBuild database %s.", path_out, } else ShowPrompt(false, "%s\nBuild database %s.", path_out,
(BuildTitleKeyInfo(file_path, dec, true) == 0) ? "success" : "failed"); (BuildTitleKeyInfo(file_path, dec, true) == 0) ? "success" : "failed");
return 0; return 0;
} }
@ -1739,9 +1739,9 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
if (BuildKeyDb(NULL, true) == 0) { if (BuildKeyDb(NULL, true) == 0) {
if (n_other) ShowPrompt(false, "%s\n%lu/%lu files processed\n%lu/%lu files ignored", if (n_other) ShowPrompt(false, "%s\n%lu/%lu files processed\n%lu/%lu files ignored",
path_out, n_success, n_marked, n_other, n_marked); path_out, n_success, n_marked, n_other, n_marked);
else ShowPrompt(false, "%s\n%lu/%lu files processed", path_out, n_success, n_marked); else ShowPrompt(false, "%s\n%lu/%lu files processed", path_out, n_success, n_marked);
} else ShowPrompt(false, "%s\nBuild database failed.", path_out); } else ShowPrompt(false, "%s\nBuild database failed.", path_out);
} else ShowPrompt(false, "%s\nBuild database %s.", path_out, } else ShowPrompt(false, "%s\nBuild database %s.", path_out,
(BuildKeyDb(file_path, true) == 0) ? "success" : "failed"); (BuildKeyDb(file_path, true) == 0) ? "success" : "failed");
return 0; return 0;
} }
@ -1755,7 +1755,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
for (u32 i = 0; i < current_dir->n_entries; i++) { for (u32 i = 0; i < current_dir->n_entries; i++) {
const char* path = current_dir->entry[i].path; const char* path = current_dir->entry[i].path;
u64 prevsize = 0; u64 prevsize = 0;
if (!current_dir->entry[i].marked) if (!current_dir->entry[i].marked)
continue; continue;
if (!ShowProgress(n_processed++, n_marked, path)) break; if (!ShowProgress(n_processed++, n_marked, path)) break;
if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) { if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) {
@ -1858,7 +1858,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
u32 n_processed = 0; u32 n_processed = 0;
for (u32 i = 0; i < current_dir->n_entries; i++) { for (u32 i = 0; i < current_dir->n_entries; i++) {
const char* path = current_dir->entry[i].path; const char* path = current_dir->entry[i].path;
if (!current_dir->entry[i].marked) if (!current_dir->entry[i].marked)
continue; continue;
if (!ShowProgress(n_processed++, n_marked, path)) break; if (!ShowProgress(n_processed++, n_marked, path)) break;
if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) { if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) {
@ -1868,7 +1868,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
DrawDirContents(current_dir, (*cursor = i), scroll); DrawDirContents(current_dir, (*cursor = i), scroll);
if (filetype & GAME_TMD) { if (filetype & GAME_TMD) {
char cxi_pathl[256] = { 0 }; char cxi_pathl[256] = { 0 };
if ((GetTmdContentPath(cxi_pathl, path) == 0) && PathExist(cxi_pathl) && if ((GetTmdContentPath(cxi_pathl, path) == 0) && PathExist(cxi_pathl) &&
(ExtractCodeFromCxiFile(cxi_pathl, NULL, NULL) == 0)) { (ExtractCodeFromCxiFile(cxi_pathl, NULL, NULL) == 0)) {
n_success++; n_success++;
} else continue; } else continue;
@ -1880,7 +1880,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
} }
if (n_other) ShowPrompt(false, "%lu/%lu files extracted ok\n%lu/%lu not of same type", if (n_other) ShowPrompt(false, "%lu/%lu files extracted ok\n%lu/%lu not of same type",
n_success, n_marked, n_other, n_marked); n_success, n_marked, n_other, n_marked);
else ShowPrompt(false, "%lu/%lu files extracted ok", n_success, n_marked); else ShowPrompt(false, "%lu/%lu files extracted ok", n_success, n_marked);
} else { } else {
char extstr[8] = { 0 }; char extstr[8] = { 0 };
ShowString("%s\nExtracting .code, please wait...", pathstr); ShowString("%s\nExtracting .code, please wait...", pathstr);
@ -1926,7 +1926,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
bool inplace = (user_select == xorpad_inplace); bool inplace = (user_select == xorpad_inplace);
bool success = (BuildNcchInfoXorpads((inplace) ? current_path : OUTPUT_PATH, file_path) == 0); bool success = (BuildNcchInfoXorpads((inplace) ? current_path : OUTPUT_PATH, file_path) == 0);
ShowPrompt(false, "%s\nNCCHinfo padgen %s%s", pathstr, ShowPrompt(false, "%s\nNCCHinfo padgen %s%s", pathstr,
(success) ? "success" : "failed", (success) ? "success" : "failed",
(!success || inplace) ? "" : "\nOutput dir: " OUTPUT_PATH); (!success || inplace) ? "" : "\nOutput dir: " OUTPUT_PATH);
GetDirContents(current_dir, current_path); GetDirContents(current_dir, current_path);
for (; *cursor < current_dir->n_entries; (*cursor)++) { for (; *cursor < current_dir->n_entries; (*cursor)++) {
@ -2019,14 +2019,14 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
} }
return 0; return 0;
} }
return FileHandlerMenu(current_path, cursor, scroll, pane); return FileHandlerMenu(current_path, cursor, scroll, pane);
} }
u32 HomeMoreMenu(char* current_path) { u32 HomeMoreMenu(char* current_path) {
NandPartitionInfo np_info; NandPartitionInfo np_info;
if (GetNandPartitionInfo(&np_info, NP_TYPE_BONUS, NP_SUBTYPE_CTR, 0, NAND_SYSNAND) != 0) np_info.count = 0; if (GetNandPartitionInfo(&np_info, NP_TYPE_BONUS, NP_SUBTYPE_CTR, 0, NAND_SYSNAND) != 0) np_info.count = 0;
const char* optionstr[8]; const char* optionstr[8];
const char* promptstr = "HOME more... menu.\nSelect action:"; const char* promptstr = "HOME more... menu.\nSelect action:";
u32 n_opt = 0; u32 n_opt = 0;
@ -2040,7 +2040,7 @@ u32 HomeMoreMenu(char* current_path) {
int calib = ++n_opt; int calib = ++n_opt;
int sysinfo = ++n_opt; int sysinfo = ++n_opt;
int readme = (FindVTarFileInfo(VRAM0_README_MD, NULL)) ? (int) ++n_opt : -1; int readme = (FindVTarFileInfo(VRAM0_README_MD, NULL)) ? (int) ++n_opt : -1;
if (sdformat > 0) optionstr[sdformat - 1] = "SD format menu"; if (sdformat > 0) optionstr[sdformat - 1] = "SD format menu";
if (bonus > 0) optionstr[bonus - 1] = "Bonus drive setup"; if (bonus > 0) optionstr[bonus - 1] = "Bonus drive setup";
if (multi > 0) optionstr[multi - 1] = "Switch EmuNAND"; if (multi > 0) optionstr[multi - 1] = "Switch EmuNAND";
@ -2051,7 +2051,7 @@ u32 HomeMoreMenu(char* current_path) {
if (calib > 0) optionstr[calib - 1] = "Calibrate touchscreen"; if (calib > 0) optionstr[calib - 1] = "Calibrate touchscreen";
if (sysinfo > 0) optionstr[sysinfo - 1] = "System info"; if (sysinfo > 0) optionstr[sysinfo - 1] = "System info";
if (readme > 0) optionstr[readme - 1] = "Show ReadMe"; if (readme > 0) optionstr[readme - 1] = "Show ReadMe";
int user_select = ShowSelectPrompt(n_opt, optionstr, promptstr); int user_select = ShowSelectPrompt(n_opt, optionstr, promptstr);
if (user_select == sdformat) { // format SD card if (user_select == sdformat) { // format SD card
bool sd_state = CheckSDMountState(); bool sd_state = CheckSDMountState();
@ -2178,27 +2178,27 @@ u32 HomeMoreMenu(char* current_path) {
MemToCViewer(README_md, README_md_size, "GodMode9 ReadMe Table of Contents"); MemToCViewer(README_md, README_md_size, "GodMode9 ReadMe Table of Contents");
return 0; return 0;
} else return 1; } else return 1;
return HomeMoreMenu(current_path); return HomeMoreMenu(current_path);
} }
u32 GodMode(int entrypoint) { u32 GodMode(int entrypoint) {
const u32 quick_stp = (MAIN_SCREEN == TOP_SCREEN) ? 20 : 19; const u32 quick_stp = (MAIN_SCREEN == TOP_SCREEN) ? 20 : 19;
u32 exit_mode = GODMODE_EXIT_POWEROFF; u32 exit_mode = GODMODE_EXIT_POWEROFF;
char current_path[256] = { 0x00 }; char current_path[256] = { 0x00 };
u32 cursor = 0; u32 cursor = 0;
u32 scroll = 0; u32 scroll = 0;
int mark_next = -1; int mark_next = -1;
u32 last_write_perm = GetWritePermissions(); u32 last_write_perm = GetWritePermissions();
u32 last_clipboard_size = 0; u32 last_clipboard_size = 0;
bool bootloader = IS_UNLOCKED && (entrypoint == ENTRY_NANDBOOT); bool bootloader = IS_UNLOCKED && (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;
// FIRM from FCRAM handling // FIRM from FCRAM handling
FirmHeader* firm_in_mem = (FirmHeader*) __FIRMTMP_ADDR; // should be safe here FirmHeader* firm_in_mem = (FirmHeader*) __FIRMTMP_ADDR; // should be safe here
if (bootloader) { // check for FIRM in FCRAM, but prevent bootloops if (bootloader) { // check for FIRM in FCRAM, but prevent bootloops
@ -2210,27 +2210,27 @@ u32 GodMode(int entrypoint) {
memcpy(addr, "NOPE", 4); // to prevent bootloops memcpy(addr, "NOPE", 4); // to prevent bootloops
} }
} }
// get mode string for splash screen // get mode string for splash screen
const char* disp_mode = NULL; const char* disp_mode = NULL;
if (bootloader) disp_mode = "bootloader mode\nR+LEFT for menu"; if (bootloader) disp_mode = "bootloader mode\nR+LEFT for menu";
else if (!IS_UNLOCKED && (entrypoint == ENTRY_NANDBOOT)) disp_mode = "oldloader mode"; else if (!IS_UNLOCKED && (entrypoint == ENTRY_NANDBOOT)) disp_mode = "oldloader mode";
else if (entrypoint == ENTRY_NTRBOOT) disp_mode = "ntrboot mode"; else if (entrypoint == ENTRY_NTRBOOT) disp_mode = "ntrboot mode";
else if (entrypoint == ENTRY_UNKNOWN) disp_mode = "unknown mode"; else if (entrypoint == ENTRY_UNKNOWN) disp_mode = "unknown mode";
bool show_splash = true; bool show_splash = true;
#ifdef SALTMODE #ifdef SALTMODE
show_splash = !bootloader; show_splash = !bootloader;
#endif #endif
// init font // init font
if (!SetFontFromPbm(NULL, 0)) return exit_mode; if (!SetFontFromPbm(NULL, 0)) return exit_mode;
// show splash screen (if enabled) // show splash screen (if enabled)
ClearScreenF(true, true, COLOR_STD_BG); ClearScreenF(true, true, COLOR_STD_BG);
if (show_splash) SplashInit(disp_mode); if (show_splash) SplashInit(disp_mode);
u64 timer = timer_start(); // for splash delay u64 timer = timer_start(); // for splash delay
InitSDCardFS(); InitSDCardFS();
AutoEmuNandBase(true); AutoEmuNandBase(true);
InitNandCrypto(true); // (entrypoint != ENTRY_B9S); InitNandCrypto(true); // (entrypoint != ENTRY_B9S);
@ -2252,7 +2252,7 @@ u32 GodMode(int entrypoint) {
free(pbm); free(pbm);
} }
} }
// check for embedded essential backup // check for embedded essential backup
if (((entrypoint == ENTRY_NANDBOOT) || (entrypoint == ENTRY_B9S)) && if (((entrypoint == ENTRY_NANDBOOT) || (entrypoint == ENTRY_B9S)) &&
!PathExist("S:/essential.exefs") && CheckGenuineNandNcsd() && !PathExist("S:/essential.exefs") && CheckGenuineNandNcsd() &&
@ -2263,7 +2263,7 @@ u32 GodMode(int entrypoint) {
ShowPrompt(false, "Backup embedded in SysNAND\nand written to " OUTPUT_PATH "."); ShowPrompt(false, "Backup embedded in SysNAND\nand written to " OUTPUT_PATH ".");
} }
} }
// check internal clock // check internal clock
if (IS_UNLOCKED) { // we could actually do this on any entrypoint if (IS_UNLOCKED) { // we could actually do this on any entrypoint
DsTime dstime; DsTime dstime;
@ -2277,12 +2277,12 @@ u32 GodMode(int entrypoint) {
ShowPrompt(false, "New RTC date&time is:\n%s\n \nHint: HOMEMENU time needs\nmanual adjustment after\nsetting the RTC.", timestr); ShowPrompt(false, "New RTC date&time is:\n%s\n \nHint: HOMEMENU time needs\nmanual adjustment after\nsetting the RTC.", timestr);
} }
} }
// check aeskeydb.bin / key state // check aeskeydb.bin / key state
if ((entrypoint != ENTRY_B9S) && (CheckRecommendedKeyDb(NULL) != 0)) { if ((entrypoint != ENTRY_B9S) && (CheckRecommendedKeyDb(NULL) != 0)) {
ShowPrompt(false, "WARNING:\nNot running from a boot9strap\ncompatible entrypoint. Not\neverything may work as expected.\n \nProvide the recommended\naeskeydb.bin file to make this\nwarning go away."); ShowPrompt(false, "WARNING:\nNot running from a boot9strap\ncompatible entrypoint. Not\neverything may work as expected.\n \nProvide the recommended\naeskeydb.bin file to make this\nwarning go away.");
} }
#if defined(SALTMODE) #if defined(SALTMODE)
show_splash = bootmenu = (bootloader && CheckButton(BOOTMENU_KEY)); show_splash = bootmenu = (bootloader && CheckButton(BOOTMENU_KEY));
if (show_splash) SplashInit("saltmode"); if (show_splash) SplashInit("saltmode");
@ -2317,7 +2317,7 @@ u32 GodMode(int entrypoint) {
break; break;
} }
} }
// bootloader handler // bootloader handler
if (bootloader) { if (bootloader) {
const char* bootfirm_paths[] = { BOOTFIRM_PATHS }; const char* bootfirm_paths[] = { BOOTFIRM_PATHS };
@ -2331,7 +2331,7 @@ u32 GodMode(int entrypoint) {
ShowPrompt(false, "No bootable FIRM found.\nNow resuming GodMode9..."); ShowPrompt(false, "No bootable FIRM found.\nNow resuming GodMode9...");
godmode9 = true; godmode9 = true;
} }
if (godmode9) { if (godmode9) {
current_dir = (DirStruct*) malloc(sizeof(DirStruct)); current_dir = (DirStruct*) malloc(sizeof(DirStruct));
clipboard = (DirStruct*) malloc(sizeof(DirStruct)); clipboard = (DirStruct*) malloc(sizeof(DirStruct));
@ -2340,13 +2340,13 @@ u32 GodMode(int entrypoint) {
ShowPrompt(false, "Out of memory."); // just to be safe ShowPrompt(false, "Out of memory."); // just to be safe
return exit_mode; return exit_mode;
} }
GetDirContents(current_dir, ""); GetDirContents(current_dir, "");
clipboard->n_entries = 0; clipboard->n_entries = 0;
memset(panedata, 0x00, N_PANES * sizeof(PaneData)); memset(panedata, 0x00, N_PANES * sizeof(PaneData));
ClearScreenF(true, true, COLOR_STD_BG); // clear splash ClearScreenF(true, true, COLOR_STD_BG); // clear splash
} }
PaneData* pane = panedata; PaneData* pane = panedata;
while (godmode9) { // this is the main loop while (godmode9) { // this is the main loop
// basic sanity checking // basic sanity checking
@ -2364,7 +2364,7 @@ u32 GodMode(int entrypoint) {
} }
if (cursor >= current_dir->n_entries) // cursor beyond allowed range if (cursor >= current_dir->n_entries) // cursor beyond allowed range
cursor = current_dir->n_entries - 1; cursor = current_dir->n_entries - 1;
int curr_drvtype = DriveType(current_path); int curr_drvtype = DriveType(current_path);
DirEntry* curr_entry = &(current_dir->entry[cursor]); DirEntry* curr_entry = &(current_dir->entry[cursor]);
if ((mark_next >= 0) && (curr_entry->type != T_DOTDOT)) { if ((mark_next >= 0) && (curr_entry->type != T_DOTDOT)) {
@ -2374,7 +2374,7 @@ u32 GodMode(int entrypoint) {
DrawDirContents(current_dir, cursor, &scroll); DrawDirContents(current_dir, cursor, &scroll);
DrawUserInterface(current_path, curr_entry, N_PANES ? pane - panedata + 1 : 0); DrawUserInterface(current_path, curr_entry, N_PANES ? pane - panedata + 1 : 0);
DrawTopBar(current_path); DrawTopBar(current_path);
// check write permissions // check write permissions
if (~last_write_perm & GetWritePermissions()) { if (~last_write_perm & GetWritePermissions()) {
if (ShowPrompt(true, "Write permissions were changed.\nRelock them?")) SetWritePermissions(last_write_perm, false); if (ShowPrompt(true, "Write permissions were changed.\nRelock them?")) SetWritePermissions(last_write_perm, false);
@ -2385,7 +2385,7 @@ u32 GodMode(int entrypoint) {
// handle user input // handle user input
u32 pad_state = InputWait(3); u32 pad_state = InputWait(3);
bool switched = (pad_state & BUTTON_R1); bool switched = (pad_state & BUTTON_R1);
// basic navigation commands // basic navigation commands
if ((pad_state & BUTTON_A) && (curr_entry->type != T_FILE) && (curr_entry->type != T_DOTDOT)) { // for dirs if ((pad_state & BUTTON_A) && (curr_entry->type != T_FILE) && (curr_entry->type != T_DOTDOT)) { // for dirs
if (switched && !(DriveType(curr_entry->path) & DRV_SEARCH)) { // search directory if (switched && !(DriveType(curr_entry->path) & DRV_SEARCH)) { // search directory
@ -2444,8 +2444,8 @@ u32 GodMode(int entrypoint) {
current_path[255] = '\0'; current_path[255] = '\0';
if (user_select == 2) { if (user_select == 2) {
char* last_slash = strrchr(current_path, '/'); char* last_slash = strrchr(current_path, '/');
if (last_slash) *last_slash = '\0'; if (last_slash) *last_slash = '\0';
} }
GetDirContents(current_dir, current_path); GetDirContents(current_dir, current_path);
if (*current_path && (current_dir->n_entries > 1)) { if (*current_path && (current_dir->n_entries > 1)) {
cursor = 1; cursor = 1;
@ -2465,7 +2465,7 @@ u32 GodMode(int entrypoint) {
char old_path[256]; char old_path[256];
char* last_slash = strrchr(current_path, '/'); char* last_slash = strrchr(current_path, '/');
strncpy(old_path, current_path, 256); strncpy(old_path, current_path, 256);
if (last_slash) *last_slash = '\0'; if (last_slash) *last_slash = '\0';
else *current_path = '\0'; else *current_path = '\0';
GetDirContents(current_dir, current_path); GetDirContents(current_dir, current_path);
if (*old_path && current_dir->n_entries) { if (*old_path && current_dir->n_entries) {
@ -2545,7 +2545,7 @@ u32 GodMode(int entrypoint) {
} else if (!switched) { // standard unswitched command set } else if (!switched) { // standard unswitched command set
if ((curr_drvtype & DRV_VIRTUAL) && (pad_state & BUTTON_X) && (*current_path != 'T')) { if ((curr_drvtype & DRV_VIRTUAL) && (pad_state & BUTTON_X) && (*current_path != 'T')) {
ShowPrompt(false, "Not allowed in virtual path"); ShowPrompt(false, "Not allowed in virtual path");
} else if (pad_state & BUTTON_X) { // delete a file } else if (pad_state & BUTTON_X) { // delete a file
u32 n_marked = 0; u32 n_marked = 0;
if (curr_entry->marked) { if (curr_entry->marked) {
for (u32 c = 0; c < current_dir->n_entries; c++) for (u32 c = 0; c < current_dir->n_entries; c++)
@ -2612,16 +2612,16 @@ u32 GodMode(int entrypoint) {
TruncateString(namestr, clipboard->entry[c].name, 36, 12); TruncateString(namestr, clipboard->entry[c].name, 36, 12);
flags &= ~ASK_ALL; flags &= ~ASK_ALL;
if (c < clipboard->n_entries - 1) flags |= ASK_ALL; if (c < clipboard->n_entries - 1) flags |= ASK_ALL;
if ((user_select == 1) && !PathCopy(current_path, clipboard->entry[c].path, &flags)) { if ((user_select == 1) && !PathCopy(current_path, clipboard->entry[c].path, &flags)) {
if (c + 1 < clipboard->n_entries) { if (c + 1 < clipboard->n_entries) {
if (!ShowPrompt(true, "Failed copying path:\n%s\nProcess remaining?", namestr)) break; if (!ShowPrompt(true, "Failed copying path:\n%s\nProcess remaining?", namestr)) break;
} else ShowPrompt(false, "Failed copying path:\n%s", namestr); } else ShowPrompt(false, "Failed copying path:\n%s", namestr);
} else if ((user_select == 2) && !PathMove(current_path, clipboard->entry[c].path, &flags)) { } else if ((user_select == 2) && !PathMove(current_path, clipboard->entry[c].path, &flags)) {
if (c + 1 < clipboard->n_entries) { if (c + 1 < clipboard->n_entries) {
if (!ShowPrompt(true, "Failed moving path:\n%s\nProcess remaining?", namestr)) break; if (!ShowPrompt(true, "Failed moving path:\n%s\nProcess remaining?", namestr)) break;
} else ShowPrompt(false, "Failed moving path:\n%s", namestr); } else ShowPrompt(false, "Failed moving path:\n%s", namestr);
} }
} }
clipboard->n_entries = 0; clipboard->n_entries = 0;
GetDirContents(current_dir, current_path); GetDirContents(current_dir, current_path);
} }
@ -2670,7 +2670,7 @@ u32 GodMode(int entrypoint) {
} }
} }
} }
if (pad_state & BUTTON_START) { if (pad_state & BUTTON_START) {
exit_mode = (switched || (pad_state & BUTTON_LEFT)) ? GODMODE_EXIT_POWEROFF : GODMODE_EXIT_REBOOT; exit_mode = (switched || (pad_state & BUTTON_LEFT)) ? GODMODE_EXIT_POWEROFF : GODMODE_EXIT_REBOOT;
break; break;
@ -2690,7 +2690,7 @@ u32 GodMode(int entrypoint) {
if (scripts > 0) optionstr[scripts - 1] = "Scripts..."; if (scripts > 0) optionstr[scripts - 1] = "Scripts...";
if (payloads > 0) optionstr[payloads - 1] = "Payloads..."; if (payloads > 0) optionstr[payloads - 1] = "Payloads...";
if (more > 0) optionstr[more - 1] = "More..."; if (more > 0) optionstr[more - 1] = "More...";
int user_select = 0; int user_select = 0;
while ((user_select = ShowSelectPrompt(n_opt, optionstr, "%s button pressed.\nSelect action:", buttonstr)) && while ((user_select = ShowSelectPrompt(n_opt, optionstr, "%s button pressed.\nSelect action:", buttonstr)) &&
(user_select != poweroff) && (user_select != reboot)) { (user_select != poweroff) && (user_select != reboot)) {
@ -2715,11 +2715,11 @@ u32 GodMode(int entrypoint) {
break; break;
} }
} }
if (user_select == poweroff) { if (user_select == poweroff) {
exit_mode = GODMODE_EXIT_POWEROFF; exit_mode = GODMODE_EXIT_POWEROFF;
break; break;
} else if (user_select == reboot) { } else if (user_select == reboot) {
exit_mode = GODMODE_EXIT_REBOOT; exit_mode = GODMODE_EXIT_REBOOT;
break; break;
} }
@ -2746,15 +2746,15 @@ u32 GodMode(int entrypoint) {
GetDirContents(current_dir, current_path); GetDirContents(current_dir, current_path);
} }
} }
DeinitExtFS(); DeinitExtFS();
DeinitSDCardFS(); DeinitSDCardFS();
if (current_dir) free(current_dir); if (current_dir) free(current_dir);
if (clipboard) free(clipboard); if (clipboard) free(clipboard);
if (panedata) free(panedata); if (panedata) free(panedata);
return exit_mode; return exit_mode;
} }
@ -2764,7 +2764,7 @@ u32 ScriptRunner(int entrypoint) {
if (!SetFontFromPbm(NULL, 0)) return GODMODE_EXIT_POWEROFF; if (!SetFontFromPbm(NULL, 0)) return GODMODE_EXIT_POWEROFF;
SplashInit("scriptrunner mode"); SplashInit("scriptrunner mode");
u64 timer = timer_start(); u64 timer = timer_start();
InitSDCardFS(); InitSDCardFS();
AutoEmuNandBase(true); AutoEmuNandBase(true);
InitNandCrypto(entrypoint != ENTRY_B9S); InitNandCrypto(entrypoint != ENTRY_B9S);
@ -2776,14 +2776,14 @@ u32 ScriptRunner(int entrypoint) {
s32 brightness = -1; s32 brightness = -1;
if (LoadSupportFile("gm9bright.cfg", &brightness, 0x4)) if (LoadSupportFile("gm9bright.cfg", &brightness, 0x4))
SetScreenBrightness(brightness); SetScreenBrightness(brightness);
while (CheckButton(BOOTPAUSE_KEY)); // don't continue while these keys are held while (CheckButton(BOOTPAUSE_KEY)); // don't continue while these keys are held
while (timer_msec( timer ) < 500); // show splash for at least 0.5 sec while (timer_msec( timer ) < 500); // show splash for at least 0.5 sec
// you didn't really install a scriptrunner to NAND, did you? // you didn't really install a scriptrunner to NAND, did you?
if (IS_UNLOCKED && (entrypoint == ENTRY_NANDBOOT)) if (IS_UNLOCKED && (entrypoint == ENTRY_NANDBOOT))
BootFirmHandler("0:/iderped.firm", false, false); BootFirmHandler("0:/iderped.firm", false, false);
if (PathExist("V:/" VRAM0_AUTORUN_GM9)) { if (PathExist("V:/" VRAM0_AUTORUN_GM9)) {
ClearScreenF(true, true, COLOR_STD_BG); // clear splash ClearScreenF(true, true, COLOR_STD_BG); // clear splash
ExecuteGM9Script("V:/" VRAM0_AUTORUN_GM9); ExecuteGM9Script("V:/" VRAM0_AUTORUN_GM9);
@ -2792,11 +2792,11 @@ u32 ScriptRunner(int entrypoint) {
if (FileSelector(loadpath, FLAVOR " scripts menu.\nSelect script:", "V:/" VRAM0_SCRIPTS, "*.gm9", HIDE_EXT, false)) if (FileSelector(loadpath, FLAVOR " scripts menu.\nSelect script:", "V:/" VRAM0_SCRIPTS, "*.gm9", HIDE_EXT, false))
ExecuteGM9Script(loadpath); ExecuteGM9Script(loadpath);
} else ShowPrompt(false, "Compiled as script autorunner\nbut no script provided.\n \nDerp!"); } else ShowPrompt(false, "Compiled as script autorunner\nbut no script provided.\n \nDerp!");
// deinit // deinit
DeinitExtFS(); DeinitExtFS();
DeinitSDCardFS(); DeinitSDCardFS();
return GODMODE_EXIT_REBOOT; return GODMODE_EXIT_REBOOT;
} }
#endif #endif

View File

@ -23,7 +23,7 @@ typedef struct {
// see: http://3dbrew.org/wiki/Nand/private/movable.sed // see: http://3dbrew.org/wiki/Nand/private/movable.sed
typedef struct { typedef struct {
u8 magic[0x4]; // "SEED" u8 magic[0x4]; // "SEED"
u8 indicator[0x4]; // uninitialized all zero, otherwise u8[1] nonzero u8 indicator[0x4]; // uninitialized all zero, otherwise u8[1] nonzero
LocalFriendCodeSeed codeseed_data; LocalFriendCodeSeed codeseed_data;
u8 keyy_high[8]; u8 keyy_high[8];
u8 unknown[0x10]; u8 unknown[0x10];

View File

@ -33,7 +33,7 @@ static const u8 ALIGN(4) slot0x05KeyY_sha256[0x20] = { // hash for slot0x05KeyY
}; };
static const u8 ALIGN(4) slot0x11Key95_sha256[0x20] = { // slot0x11Key95 hash (first 16 byte of sector0x96) static const u8 ALIGN(4) slot0x11Key95_sha256[0x20] = { // slot0x11Key95 hash (first 16 byte of sector0x96)
0xBA, 0xC1, 0x40, 0x9C, 0x6E, 0xE4, 0x1F, 0x04, 0xAA, 0xC4, 0xE2, 0x09, 0x5C, 0xE9, 0x4F, 0x78, 0xBA, 0xC1, 0x40, 0x9C, 0x6E, 0xE4, 0x1F, 0x04, 0xAA, 0xC4, 0xE2, 0x09, 0x5C, 0xE9, 0x4F, 0x78,
0x6C, 0x78, 0x5F, 0xAC, 0xEC, 0x7E, 0xC0, 0x11, 0x26, 0x9D, 0x4E, 0x47, 0xB3, 0x64, 0xC4, 0xA5 0x6C, 0x78, 0x5F, 0xAC, 0xEC, 0x7E, 0xC0, 0x11, 0x26, 0x9D, 0x4E, 0x47, 0xB3, 0x64, 0xC4, 0xA5
}; };
@ -78,12 +78,12 @@ bool GetOtp0x90(void* otp0x90, u32 len)
cbc_encrypt(otp0x90, otp0x90, len / 0x10, AES_CNT_TITLEKEY_ENCRYPT_MODE, otp_iv); cbc_encrypt(otp0x90, otp0x90, len / 0x10, AES_CNT_TITLEKEY_ENCRYPT_MODE, otp_iv);
return true; return true;
} }
return false; return false;
} }
bool InitNandCrypto(bool init_full) bool InitNandCrypto(bool init_full)
{ {
// part #0: KeyX / KeyY for secret sector 0x96 // part #0: KeyX / KeyY for secret sector 0x96
if (IS_UNLOCKED) { // if OTP is unlocked if (IS_UNLOCKED) { // if OTP is unlocked
// see: https://www.3dbrew.org/wiki/OTP_Registers // see: https://www.3dbrew.org/wiki/OTP_Registers
@ -95,11 +95,11 @@ bool InitNandCrypto(bool init_full)
if (GetOtp0x90(otp0x90, 0x90)) if (GetOtp0x90(otp0x90, 0x90))
sha_quick(OtpSha256, otp0x90, 0x90, SHA256_MODE); sha_quick(OtpSha256, otp0x90, 0x90, SHA256_MODE);
} }
// part #1: Get NAND CID, set up TWL/CTR counter // part #1: Get NAND CID, set up TWL/CTR counter
u32 NandCid[4]; u32 NandCid[4];
u8 shasum[32]; u8 shasum[32];
sdmmc_sdcard_init(); sdmmc_sdcard_init();
sdmmc_get_cid(1, NandCid); sdmmc_get_cid(1, NandCid);
sha_quick(shasum, (u8*) NandCid, 16, SHA256_MODE); sha_quick(shasum, (u8*) NandCid, 16, SHA256_MODE);
@ -107,7 +107,7 @@ bool InitNandCrypto(bool init_full)
sha_quick(shasum, (u8*) NandCid, 16, SHA1_MODE); sha_quick(shasum, (u8*) NandCid, 16, SHA1_MODE);
for(u32 i = 0; i < 16; i++) // little endian and reversed order for(u32 i = 0; i < 16; i++) // little endian and reversed order
TwlNandCtr[i] = shasum[15-i]; TwlNandCtr[i] = shasum[15-i];
// part #2: TWL KEY (if not already set up) // part #2: TWL KEY (if not already set up)
// see: https://www.3dbrew.org/wiki/Memory_layout#ARM9_ITCM // see: https://www.3dbrew.org/wiki/Memory_layout#ARM9_ITCM
if (GetNandPartitionInfo(NULL, NP_TYPE_FAT, NP_SUBTYPE_TWL, 0, NAND_SYSNAND) != 0) { if (GetNandPartitionInfo(NULL, NP_TYPE_FAT, NP_SUBTYPE_TWL, 0, NAND_SYSNAND) != 0) {
@ -118,11 +118,11 @@ bool InitNandCrypto(bool init_full)
u64 ALIGN(32) otp0x10[2]; u64 ALIGN(32) otp0x10[2];
if (GetOtp0x90(otp0x10, 0x10)) TwlCustId = *otp0x10; if (GetOtp0x90(otp0x10, 0x10)) TwlCustId = *otp0x10;
} }
if (TwlCustId) { // give up if TwlCustId not found if (TwlCustId) { // give up if TwlCustId not found
u32 ALIGN(32) TwlKey0x03Y[4]; u32 ALIGN(32) TwlKey0x03Y[4];
u32 ALIGN(32) TwlKey0x03X[4]; u32 ALIGN(32) TwlKey0x03X[4];
if (IS_DEVKIT) { if (IS_DEVKIT) {
TwlKey0x03X[1] = 0xEE7A4B1E; TwlKey0x03X[1] = 0xEE7A4B1E;
TwlKey0x03X[2] = 0xAF42C08B; TwlKey0x03X[2] = 0xAF42C08B;
@ -132,20 +132,20 @@ bool InitNandCrypto(bool init_full)
TwlKey0x03X[2] = *(vu32*)0x01FFD3AC; // "ENDO" TwlKey0x03X[2] = *(vu32*)0x01FFD3AC; // "ENDO"
memcpy(TwlKey0x03Y, (u8*) 0x01FFD3C8, 16); memcpy(TwlKey0x03Y, (u8*) 0x01FFD3C8, 16);
} }
TwlKey0x03X[0] = (u32) (TwlCustId>>0); TwlKey0x03X[0] = (u32) (TwlCustId>>0);
TwlKey0x03X[3] = (u32) (TwlCustId>>32); TwlKey0x03X[3] = (u32) (TwlCustId>>32);
TwlKey0x03Y[3] = 0xE1A00005; TwlKey0x03Y[3] = 0xE1A00005;
setup_aeskeyX(0x03, TwlKey0x03X); setup_aeskeyX(0x03, TwlKey0x03X);
setup_aeskeyY(0x03, TwlKey0x03Y); setup_aeskeyY(0x03, TwlKey0x03Y);
use_aeskey(0x03); use_aeskey(0x03);
if (init_full) { // full init if (init_full) { // full init
vu32 *RegKey0x01X = &REG_AESKEY0123[((0x30u * 0x01) + 0x10u)/4u]; vu32 *RegKey0x01X = &REG_AESKEY0123[((0x30u * 0x01) + 0x10u)/4u];
RegKey0x01X[2] = (u32) (TwlCustId>>32); RegKey0x01X[2] = (u32) (TwlCustId>>32);
RegKey0x01X[3] = (u32) (TwlCustId>>0); RegKey0x01X[3] = (u32) (TwlCustId>>0);
setup_aeskeyX(0x02, (u8*)0x01FFD398); setup_aeskeyX(0x02, (u8*)0x01FFD398);
if (IS_DEVKIT) { if (IS_DEVKIT) {
u32 ALIGN(32) TwlKey0x02Y[4]; u32 ALIGN(32) TwlKey0x02Y[4];
@ -153,24 +153,24 @@ bool InitNandCrypto(bool init_full)
setup_aeskeyY(0x02, TwlKey0x02Y); setup_aeskeyY(0x02, TwlKey0x02Y);
} else setup_aeskeyY(0x02, (u8*)0x01FFD220); } else setup_aeskeyY(0x02, (u8*)0x01FFD220);
use_aeskey(0x02); use_aeskey(0x02);
if (IS_UNLOCKED) if (IS_UNLOCKED)
(*(vu64*)0x10012100) = TwlCustId; (*(vu64*)0x10012100) = TwlCustId;
} }
} }
} }
// part #3: CTRNAND N3DS KEY (if not set up) // part #3: CTRNAND N3DS KEY (if not set up)
if (GetNandPartitionInfo(NULL, NP_TYPE_FAT, NP_SUBTYPE_CTR, 0, NAND_SYSNAND) != 0) if (GetNandPartitionInfo(NULL, NP_TYPE_FAT, NP_SUBTYPE_CTR, 0, NAND_SYSNAND) != 0)
LoadKeyFromFile(slot0x05KeyY, 0x05, 'Y', NULL); LoadKeyFromFile(slot0x05KeyY, 0x05, 'Y', NULL);
// part #4: AGBSAVE CMAC KEY (set up on unlocked systems) // part #4: AGBSAVE CMAC KEY (set up on unlocked systems)
if (init_full && IS_UNLOCKED) if (init_full && IS_UNLOCKED)
LoadKeyFromFile(NULL, 0x24, 'Y', NULL); LoadKeyFromFile(NULL, 0x24, 'Y', NULL);
// part #5: FULL INIT // part #5: FULL INIT
if (init_full) InitKeyDb(NULL); if (init_full) InitKeyDb(NULL);
return true; return true;
} }
@ -179,11 +179,11 @@ bool CheckSlot0x05Crypto(void)
// step #1 - check the slot0x05KeyY SHA-256 // step #1 - check the slot0x05KeyY SHA-256
if (sha_cmp(slot0x05KeyY_sha256, slot0x05KeyY, 16, SHA256_MODE) == 0) if (sha_cmp(slot0x05KeyY_sha256, slot0x05KeyY, 16, SHA256_MODE) == 0)
return true; return true;
// step #2 - check actual presence of CTRNAND FAT // step #2 - check actual presence of CTRNAND FAT
if (GetNandPartitionInfo(NULL, NP_TYPE_STD, NP_SUBTYPE_CTR_N, 0, NAND_SYSNAND) == 0) if (GetNandPartitionInfo(NULL, NP_TYPE_STD, NP_SUBTYPE_CTR_N, 0, NAND_SYSNAND) == 0)
return true; return true;
// failed if we arrive here // failed if we arrive here
return false; return false;
} }
@ -206,14 +206,14 @@ bool CheckGenuineNandNcsd(void)
}; };
u8 ALIGN(4) gen_n3ds_hash[0x20] = { u8 ALIGN(4) gen_n3ds_hash[0x20] = {
0x49, 0xB7, 0x4A, 0xF1, 0xFD, 0xB7, 0xCF, 0x5B, 0x76, 0x8F, 0xA2, 0x94, 0x0D, 0xB2, 0xB3, 0xE2, 0x49, 0xB7, 0x4A, 0xF1, 0xFD, 0xB7, 0xCF, 0x5B, 0x76, 0x8F, 0xA2, 0x94, 0x0D, 0xB2, 0xB3, 0xE2,
0xA4, 0xBD, 0x25, 0x03, 0x06, 0x03, 0x47, 0x0B, 0x24, 0x5A, 0x86, 0x6A, 0x43, 0x60, 0xBC, 0x84, 0xA4, 0xBD, 0x25, 0x03, 0x06, 0x03, 0x47, 0x0B, 0x24, 0x5A, 0x86, 0x6A, 0x43, 0x60, 0xBC, 0x84,
}; };
u8 ALIGN(4) gen_hdr[0x100]; u8 ALIGN(4) gen_hdr[0x100];
if ((ReadNandBytes(gen_hdr, 0x100, 0x100, 0xFF, NAND_SYSNAND) != 0) || if ((ReadNandBytes(gen_hdr, 0x100, 0x100, 0xFF, NAND_SYSNAND) != 0) ||
(ReadNandBytes(gen_hdr + 0xBE, 0x1BE, 0x42, 0x03, NAND_SYSNAND) != 0)) (ReadNandBytes(gen_hdr + 0xBE, 0x1BE, 0x42, 0x03, NAND_SYSNAND) != 0))
return false; return false;
return (sha_cmp((IS_O3DS) ? gen_o3ds_hash : gen_n3ds_hash, gen_hdr, 0x100, SHA256_MODE) == 0); return (sha_cmp((IS_O3DS) ? gen_o3ds_hash : gen_n3ds_hash, gen_hdr, 0x100, SHA256_MODE) == 0);
} }
@ -226,7 +226,7 @@ void CryptNand(void* buffer, u32 sector, u32 count, u32 keyslot)
// copy NAND CTR and increment it // copy NAND CTR and increment it
memcpy(ctr, (keyslot != 0x03) ? CtrNandCtr : TwlNandCtr, 16); // hacky again memcpy(ctr, (keyslot != 0x03) ? CtrNandCtr : TwlNandCtr, 16); // hacky again
add_ctr(ctr, sector * (0x200 / 0x10)); add_ctr(ctr, sector * (0x200 / 0x10));
// decrypt the data // decrypt the data
use_aeskey(keyslot); use_aeskey(keyslot);
ctr_decrypt((void*) buffer, (void*) buffer, blocks, mode, ctr); ctr_decrypt((void*) buffer, (void*) buffer, blocks, mode, ctr);
@ -235,11 +235,11 @@ void CryptNand(void* buffer, u32 sector, u32 count, u32 keyslot)
void CryptSector0x96(void* buffer, bool encrypt) void CryptSector0x96(void* buffer, bool encrypt)
{ {
u32 mode = encrypt ? AES_CNT_ECB_ENCRYPT_MODE : AES_CNT_ECB_DECRYPT_MODE; u32 mode = encrypt ? AES_CNT_ECB_ENCRYPT_MODE : AES_CNT_ECB_DECRYPT_MODE;
// setup the key // setup the key
setup_aeskeyX(0x11, OtpSha256); setup_aeskeyX(0x11, OtpSha256);
setup_aeskeyY(0x11, OtpSha256 + 16); setup_aeskeyY(0x11, OtpSha256 + 16);
// decrypt the sector // decrypt the sector
use_aeskey(0x11); use_aeskey(0x11);
ecb_decrypt((void*) buffer, (void*) buffer, 0x200 / AES_BLOCK_SIZE, mode); ecb_decrypt((void*) buffer, (void*) buffer, 0x200 / AES_BLOCK_SIZE, mode);
@ -247,7 +247,7 @@ void CryptSector0x96(void* buffer, bool encrypt)
int ReadNandBytes(void* buffer, u64 offset, u64 count, u32 keyslot, u32 nand_src) int ReadNandBytes(void* buffer, u64 offset, u64 count, u32 keyslot, u32 nand_src)
{ {
if (!(offset % 0x200) && !(count % 0x200)) { // aligned data -> simple case if (!(offset % 0x200) && !(count % 0x200)) { // aligned data -> simple case
// simple wrapper function for ReadNandSectors(...) // simple wrapper function for ReadNandSectors(...)
return ReadNandSectors(buffer, offset / 0x200, count / 0x200, keyslot, nand_src); return ReadNandSectors(buffer, offset / 0x200, count / 0x200, keyslot, nand_src);
} else { // misaligned data -> -___- } else { // misaligned data -> -___-
@ -280,7 +280,7 @@ int ReadNandBytes(void* buffer, u64 offset, u64 count, u32 keyslot, u32 nand_src
int WriteNandBytes(const void* buffer, u64 offset, u64 count, u32 keyslot, u32 nand_dst) int WriteNandBytes(const void* buffer, u64 offset, u64 count, u32 keyslot, u32 nand_dst)
{ {
if (!(offset % 0x200) && !(count % 0x200)) { // aligned data -> simple case if (!(offset % 0x200) && !(count % 0x200)) { // aligned data -> simple case
// simple wrapper function for WriteNandSectors(...) // simple wrapper function for WriteNandSectors(...)
return WriteNandSectors(buffer, offset / 0x200, count / 0x200, keyslot, nand_dst); return WriteNandSectors(buffer, offset / 0x200, count / 0x200, keyslot, nand_dst);
} else { // misaligned data -> -___- } else { // misaligned data -> -___-
@ -316,7 +316,7 @@ int WriteNandBytes(const void* buffer, u64 offset, u64 count, u32 keyslot, u32 n
} }
int ReadNandSectors(void* buffer, u32 sector, u32 count, u32 keyslot, u32 nand_src) int ReadNandSectors(void* buffer, u32 sector, u32 count, u32 keyslot, u32 nand_src)
{ {
u8* buffer8 = (u8*) buffer; u8* buffer8 = (u8*) buffer;
if (!count) return 0; // <--- just to be safe if (!count) return 0; // <--- just to be safe
if (nand_src == NAND_EMUNAND) { // EmuNAND if (nand_src == NAND_EMUNAND) { // EmuNAND
@ -335,7 +335,7 @@ int ReadNandSectors(void* buffer, u32 sector, u32 count, u32 keyslot, u32 nand_s
if (errorcode) return errorcode; if (errorcode) return errorcode;
} else if (nand_src == NAND_SYSNAND) { // SysNAND } else if (nand_src == NAND_SYSNAND) { // SysNAND
int errorcode = sdmmc_nand_readsectors(sector, count, buffer8); int errorcode = sdmmc_nand_readsectors(sector, count, buffer8);
if (errorcode) return errorcode; if (errorcode) return errorcode;
} else if (nand_src == NAND_ZERONAND) { // zero NAND (good for XORpads) } else if (nand_src == NAND_ZERONAND) { // zero NAND (good for XORpads)
memset(buffer8, 0, count * 0x200); memset(buffer8, 0, count * 0x200);
} else { } else {
@ -343,7 +343,7 @@ int ReadNandSectors(void* buffer, u32 sector, u32 count, u32 keyslot, u32 nand_s
} }
if ((keyslot == 0x11) && (sector == SECTOR_SECRET)) CryptSector0x96(buffer8, false); if ((keyslot == 0x11) && (sector == SECTOR_SECRET)) CryptSector0x96(buffer8, false);
else if (keyslot < 0x40) CryptNand(buffer8, sector, count, keyslot); else if (keyslot < 0x40) CryptNand(buffer8, sector, count, keyslot);
return 0; return 0;
} }
@ -353,7 +353,7 @@ int WriteNandSectors(const void* buffer, u32 sector, u32 count, u32 keyslot, u32
void* nand_buffer = (void*) malloc(min(STD_BUFFER_SIZE, count * 0x200)); void* nand_buffer = (void*) malloc(min(STD_BUFFER_SIZE, count * 0x200));
if (!nand_buffer) return -1; if (!nand_buffer) return -1;
int errorcode = 0; int errorcode = 0;
for (u32 s = 0; s < count; s += (STD_BUFFER_SIZE / 0x200)) { for (u32 s = 0; s < count; s += (STD_BUFFER_SIZE / 0x200)) {
u32 pcount = min((STD_BUFFER_SIZE/0x200), (count - s)); u32 pcount = min((STD_BUFFER_SIZE/0x200), (count - s));
memcpy(nand_buffer, ((u8*) buffer) + (s*0x200), pcount * 0x200); memcpy(nand_buffer, ((u8*) buffer) + (s*0x200), pcount * 0x200);
@ -372,7 +372,7 @@ int WriteNandSectors(const void* buffer, u32 sector, u32 count, u32 keyslot, u32
errorcode = -1; errorcode = -1;
} }
} }
free(nand_buffer); free(nand_buffer);
return errorcode; return errorcode;
} }
@ -390,7 +390,7 @@ u32 ValidateNandNcsdHeader(NandNcsdHeader* header)
if ((memcmp(header->magic, "NCSD", 4) != 0) || // check magic number if ((memcmp(header->magic, "NCSD", 4) != 0) || // check magic number
(memcmp(header->partitions_fs_type, zeroes, 8) == 0) || header->mediaId) // prevent detection of cart NCSD images (memcmp(header->partitions_fs_type, zeroes, 8) == 0) || header->mediaId) // prevent detection of cart NCSD images
return 1; return 1;
u32 data_units = 0; u32 data_units = 0;
u32 firm_count = 0; u32 firm_count = 0;
for (u32 i = 0; i < 8; i++) { for (u32 i = 0; i < 8; i++) {
@ -406,7 +406,7 @@ u32 ValidateNandNcsdHeader(NandNcsdHeader* header)
} }
if (data_units > header->size) return 1; if (data_units > header->size) return 1;
if (!firm_count) return 1; // at least one firm is required if (!firm_count) return 1; // at least one firm is required
return 0; return 0;
} }
@ -417,7 +417,7 @@ u32 GetNandNcsdMinSizeSectors(NandNcsdHeader* ncsd)
u32 prt_end = ncsd->partitions[prt_idx].offset + ncsd->partitions[prt_idx].size; u32 prt_end = ncsd->partitions[prt_idx].offset + ncsd->partitions[prt_idx].size;
if (prt_end > nand_minsize) nand_minsize = prt_end; if (prt_end > nand_minsize) nand_minsize = prt_end;
} }
return nand_minsize; return nand_minsize;
} }
@ -426,7 +426,7 @@ u32 GetNandMinSizeSectors(u32 nand_src)
NandNcsdHeader ncsd; NandNcsdHeader ncsd;
if ((ReadNandSectors((u8*) &ncsd, 0, 1, 0xFF, nand_src) != 0) || if ((ReadNandSectors((u8*) &ncsd, 0, 1, 0xFF, nand_src) != 0) ||
(ValidateNandNcsdHeader(&ncsd) != 0)) return 0; (ValidateNandNcsdHeader(&ncsd) != 0)) return 0;
return GetNandNcsdMinSizeSectors(&ncsd); return GetNandNcsdMinSizeSectors(&ncsd);
} }
@ -434,7 +434,7 @@ u32 GetNandSizeSectors(u32 nand_src)
{ {
u32 sysnand_sectors = getMMCDevice(0)->total_size; u32 sysnand_sectors = getMMCDevice(0)->total_size;
if (nand_src == NAND_SYSNAND) return sysnand_sectors; // for SysNAND if (nand_src == NAND_SYSNAND) return sysnand_sectors; // for SysNAND
u32 min_sectors = GetNandMinSizeSectors(nand_src); u32 min_sectors = GetNandMinSizeSectors(nand_src);
if (nand_src == NAND_EMUNAND) { // for EmuNAND if (nand_src == NAND_EMUNAND) { // for EmuNAND
u32 partition_offset = GetPartitionOffsetSector("0:"); u32 partition_offset = GetPartitionOffsetSector("0:");
@ -447,7 +447,7 @@ u32 GetNandSizeSectors(u32 nand_src)
u32 img_sectors = (GetMountState() & IMG_NAND) ? GetMountSize() / 0x200 : 0; u32 img_sectors = (GetMountState() & IMG_NAND) ? GetMountSize() / 0x200 : 0;
return (img_sectors >= min_sectors) ? img_sectors : 0; return (img_sectors >= min_sectors) ? img_sectors : 0;
} }
return 0; return 0;
} }
@ -456,14 +456,14 @@ u32 GetNandNcsdPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32
// safety / set keyslot // safety / set keyslot
if ((type == NP_TYPE_FAT) || (type > NP_TYPE_BONUS) || (subtype > NP_SUBTYPE_CTR_N)) return 1; if ((type == NP_TYPE_FAT) || (type > NP_TYPE_BONUS) || (subtype > NP_SUBTYPE_CTR_N)) return 1;
info->keyslot = np_keyslots[type][subtype]; info->keyslot = np_keyslots[type][subtype];
// full (minimum) NAND "partition" // full (minimum) NAND "partition"
if (type == NP_TYPE_NONE) { if (type == NP_TYPE_NONE) {
info->sector = 0x00; info->sector = 0x00;
info->count = GetNandNcsdMinSizeSectors(ncsd); info->count = GetNandNcsdMinSizeSectors(ncsd);
return 0; return 0;
} }
// special, custom partition types, not in NCSD // special, custom partition types, not in NCSD
if (type >= NP_TYPE_NCSD) { if (type >= NP_TYPE_NCSD) {
if (type == NP_TYPE_NCSD) { if (type == NP_TYPE_NCSD) {
@ -484,7 +484,7 @@ u32 GetNandNcsdPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32
} else return 1; } else return 1;
return 0; return 0;
} }
u32 prt_idx = 8; u32 prt_idx = 8;
for (prt_idx = 0; prt_idx < 8; prt_idx++) { for (prt_idx = 0; prt_idx < 8; prt_idx++) {
if ((ncsd->partitions_fs_type[prt_idx] != type) || if ((ncsd->partitions_fs_type[prt_idx] != type) ||
@ -492,11 +492,11 @@ u32 GetNandNcsdPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32
if (index == 0) break; if (index == 0) break;
index--; index--;
} }
if (prt_idx >= 8) return 1; // not found if (prt_idx >= 8) return 1; // not found
info->sector = ncsd->partitions[prt_idx].offset; info->sector = ncsd->partitions[prt_idx].offset;
info->count = ncsd->partitions[prt_idx].size; info->count = ncsd->partitions[prt_idx].size;
return 0; return 0;
} }
@ -546,7 +546,7 @@ u32 AutoEmuNandBase(bool reset)
if (!reset) { if (!reset) {
u32 last_valid = emunand_base_sector; u32 last_valid = emunand_base_sector;
u32 emunand_min_sectors = GetNandMinSizeSectors(NAND_EMUNAND); u32 emunand_min_sectors = GetNandMinSizeSectors(NAND_EMUNAND);
// legacy type multiNAND // legacy type multiNAND
u32 legacy_sectors = (getMMCDevice(0)->total_size > 0x200000) ? 0x400000 : 0x200000; u32 legacy_sectors = (getMMCDevice(0)->total_size > 0x200000) ? 0x400000 : 0x200000;
emunand_base_sector += legacy_sectors - (emunand_base_sector % legacy_sectors); emunand_base_sector += legacy_sectors - (emunand_base_sector % legacy_sectors);
@ -555,7 +555,7 @@ u32 AutoEmuNandBase(bool reset)
emunand_base_sector++; emunand_base_sector++;
if (GetNandSizeSectors(NAND_EMUNAND) && (GetNandPartitionInfo(NULL, NP_TYPE_NCSD, NP_SUBTYPE_CTR, 0, NAND_EMUNAND) == 0)) if (GetNandSizeSectors(NAND_EMUNAND) && (GetNandPartitionInfo(NULL, NP_TYPE_NCSD, NP_SUBTYPE_CTR, 0, NAND_EMUNAND) == 0))
return emunand_base_sector; // RedNAND type EmuNAND return emunand_base_sector; // RedNAND type EmuNAND
// compact type multiNAND // compact type multiNAND
if (emunand_min_sectors && (last_valid % 0x2000 <= 1)) { if (emunand_min_sectors && (last_valid % 0x2000 <= 1)) {
u32 compact_sectors = align(emunand_min_sectors + 1, 0x2000); u32 compact_sectors = align(emunand_min_sectors + 1, 0x2000);
@ -564,7 +564,7 @@ u32 AutoEmuNandBase(bool reset)
return emunand_base_sector; return emunand_base_sector;
} }
} }
emunand_base_sector = 0x000001; // RedNAND type EmuNAND, default emunand_base_sector = 0x000001; // RedNAND type EmuNAND, default
if (GetNandPartitionInfo(NULL, NP_TYPE_NCSD, NP_SUBTYPE_CTR, 0, NAND_EMUNAND) != 0) { if (GetNandPartitionInfo(NULL, NP_TYPE_NCSD, NP_SUBTYPE_CTR, 0, NAND_EMUNAND) != 0) {
emunand_base_sector = 0x000000; // GW type EmuNAND emunand_base_sector = 0x000000; // GW type EmuNAND
@ -573,7 +573,7 @@ u32 AutoEmuNandBase(bool reset)
// still nothing found? revert to default (RedNAND offset) // still nothing found? revert to default (RedNAND offset)
emunand_base_sector = 0x000001; emunand_base_sector = 0x000001;
} }
return emunand_base_sector; return emunand_base_sector;
} }

View File

@ -1,9 +1,9 @@
/* /*
* QR Code generator library (C) * QR Code generator library (C)
* *
* Copyright (c) Project Nayuki. (MIT License) * Copyright (c) Project Nayuki. (MIT License)
* https://www.nayuki.io/page/qr-code-generator-library * https://www.nayuki.io/page/qr-code-generator-library
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of * Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in * this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to * the Software without restriction, including without limitation the rights to
@ -128,12 +128,12 @@ static const int PENALTY_N4 = 10;
// Public function - see documentation comment in header file. // Public function - see documentation comment in header file.
bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[],
enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) {
size_t textLen = strlen(text); size_t textLen = strlen(text);
if (textLen == 0) if (textLen == 0)
return qrcodegen_encodeSegmentsAdvanced(NULL, 0, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); return qrcodegen_encodeSegmentsAdvanced(NULL, 0, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode);
size_t bufLen = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); size_t bufLen = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion);
struct qrcodegen_Segment seg; struct qrcodegen_Segment seg;
if (qrcodegen_isNumeric(text)) { if (qrcodegen_isNumeric(text)) {
if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, textLen) > bufLen) if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, textLen) > bufLen)
@ -156,7 +156,7 @@ bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode
seg.data = tempBuffer; seg.data = tempBuffer;
} }
return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode);
fail: fail:
qrcode[0] = 0; // Set size to invalid value for safety qrcode[0] = 0; // Set size to invalid value for safety
return false; return false;
@ -166,7 +166,7 @@ fail:
// Public function - see documentation comment in header file. // Public function - see documentation comment in header file.
bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[],
enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) {
struct qrcodegen_Segment seg; struct qrcodegen_Segment seg;
seg.mode = qrcodegen_Mode_BYTE; seg.mode = qrcodegen_Mode_BYTE;
seg.bitLength = calcSegmentBitLength(seg.mode, dataLen); seg.bitLength = calcSegmentBitLength(seg.mode, dataLen);
@ -204,7 +204,7 @@ testable void appendErrorCorrection(uint8_t data[], int version, enum qrcodegen_
int dataLen = rawCodewords - blockEccLen * numBlocks; int dataLen = rawCodewords - blockEccLen * numBlocks;
int numShortBlocks = numBlocks - rawCodewords % numBlocks; int numShortBlocks = numBlocks - rawCodewords % numBlocks;
int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen; int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen;
// Split data into blocks and append ECC after all data // Split data into blocks and append ECC after all data
uint8_t generator[30]; uint8_t generator[30];
calcReedSolomonGenerator(blockEccLen, generator); calcReedSolomonGenerator(blockEccLen, generator);
@ -216,7 +216,7 @@ testable void appendErrorCorrection(uint8_t data[], int version, enum qrcodegen_
j += blockEccLen; j += blockEccLen;
k += blockLen; k += blockLen;
} }
// Interleave (not concatenate) the bytes from every block into a single sequence // Interleave (not concatenate) the bytes from every block into a single sequence
for (int i = 0, k = 0; i < numBlocks; i++) { for (int i = 0, k = 0; i < numBlocks; i++) {
for (int j = 0, l = i; j < shortBlockDataLen; j++, k++, l += numBlocks) for (int j = 0, l = i; j < shortBlockDataLen; j++, k++, l += numBlocks)
@ -268,7 +268,7 @@ testable void calcReedSolomonGenerator(int degree, uint8_t result[]) {
assert(1 <= degree && degree <= 30); assert(1 <= degree && degree <= 30);
memset(result, 0, degree * sizeof(result[0])); memset(result, 0, degree * sizeof(result[0]));
result[degree - 1] = 1; result[degree - 1] = 1;
// Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
// drop the highest term, and store the rest of the coefficients in order of descending powers. // drop the highest term, and store the rest of the coefficients in order of descending powers.
// Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
@ -289,7 +289,7 @@ testable void calcReedSolomonGenerator(int degree, uint8_t result[]) {
// polynomials are in big endian and the generator has an implicit leading 1 term, storing the result in result[0 : degree]. // polynomials are in big endian and the generator has an implicit leading 1 term, storing the result in result[0 : degree].
testable void calcReedSolomonRemainder(const uint8_t data[], int dataLen, testable void calcReedSolomonRemainder(const uint8_t data[], int dataLen,
const uint8_t generator[], int degree, uint8_t result[]) { const uint8_t generator[], int degree, uint8_t result[]) {
// Perform polynomial division // Perform polynomial division
assert(1 <= degree && degree <= 30); assert(1 <= degree && degree <= 30);
memset(result, 0, degree * sizeof(result[0])); memset(result, 0, degree * sizeof(result[0]));
@ -326,16 +326,16 @@ testable void initializeFunctionModules(int version, uint8_t qrcode[]) {
int qrsize = version * 4 + 17; int qrsize = version * 4 + 17;
memset(qrcode, 0, ((qrsize * qrsize + 7) / 8 + 1) * sizeof(qrcode[0])); memset(qrcode, 0, ((qrsize * qrsize + 7) / 8 + 1) * sizeof(qrcode[0]));
qrcode[0] = (uint8_t)qrsize; qrcode[0] = (uint8_t)qrsize;
// Fill horizontal and vertical timing patterns // Fill horizontal and vertical timing patterns
fillRectangle(6, 0, 1, qrsize, qrcode); fillRectangle(6, 0, 1, qrsize, qrcode);
fillRectangle(0, 6, qrsize, 1, qrcode); fillRectangle(0, 6, qrsize, 1, qrcode);
// Fill 3 finder patterns (all corners except bottom right) and format bits // Fill 3 finder patterns (all corners except bottom right) and format bits
fillRectangle(0, 0, 9, 9, qrcode); fillRectangle(0, 0, 9, 9, qrcode);
fillRectangle(qrsize - 8, 0, 8, 9, qrcode); fillRectangle(qrsize - 8, 0, 8, 9, qrcode);
fillRectangle(0, qrsize - 8, 9, 8, qrcode); fillRectangle(0, qrsize - 8, 9, 8, qrcode);
// Fill numerous alignment patterns // Fill numerous alignment patterns
uint8_t alignPatPos[7] = {0}; uint8_t alignPatPos[7] = {0};
int numAlign = getAlignmentPatternPositions(version, alignPatPos); int numAlign = getAlignmentPatternPositions(version, alignPatPos);
@ -347,7 +347,7 @@ testable void initializeFunctionModules(int version, uint8_t qrcode[]) {
fillRectangle(alignPatPos[i] - 2, alignPatPos[j] - 2, 5, 5, qrcode); fillRectangle(alignPatPos[i] - 2, alignPatPos[j] - 2, 5, 5, qrcode);
} }
} }
// Fill version blocks // Fill version blocks
if (version >= 7) { if (version >= 7) {
fillRectangle(qrsize - 11, 0, 3, 6, qrcode); fillRectangle(qrsize - 11, 0, 3, 6, qrcode);
@ -366,7 +366,7 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version) {
setModule(qrcode, 6, i, false); setModule(qrcode, 6, i, false);
setModule(qrcode, i, 6, false); setModule(qrcode, i, 6, false);
} }
// Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
for (int i = -4; i <= 4; i++) { for (int i = -4; i <= 4; i++) {
for (int j = -4; j <= 4; j++) { for (int j = -4; j <= 4; j++) {
@ -380,7 +380,7 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version) {
} }
} }
} }
// Draw numerous alignment patterns // Draw numerous alignment patterns
uint8_t alignPatPos[7] = {0}; uint8_t alignPatPos[7] = {0};
int numAlign = getAlignmentPatternPositions(version, alignPatPos); int numAlign = getAlignmentPatternPositions(version, alignPatPos);
@ -396,7 +396,7 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version) {
} }
} }
} }
// Draw version blocks // Draw version blocks
if (version >= 7) { if (version >= 7) {
// Calculate error correction code and pack bits // Calculate error correction code and pack bits
@ -405,7 +405,7 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version) {
rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); rem = (rem << 1) ^ ((rem >> 11) * 0x1F25);
long data = (long)version << 12 | rem; // uint18 long data = (long)version << 12 | rem; // uint18
assert(data >> 18 == 0); assert(data >> 18 == 0);
// Draw two copies // Draw two copies
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
for (int j = 0; j < 3; j++) { for (int j = 0; j < 3; j++) {
@ -440,7 +440,7 @@ static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uin
data = data << 10 | rem; data = data << 10 | rem;
data ^= 0x5412; // uint15 data ^= 0x5412; // uint15
assert(data >> 15 == 0); assert(data >> 15 == 0);
// Draw first copy // Draw first copy
for (int i = 0; i <= 5; i++) for (int i = 0; i <= 5; i++)
setModule(qrcode, 8, i, ((data >> i) & 1) != 0); setModule(qrcode, 8, i, ((data >> i) & 1) != 0);
@ -449,7 +449,7 @@ static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uin
setModule(qrcode, 7, 8, ((data >> 8) & 1) != 0); setModule(qrcode, 7, 8, ((data >> 8) & 1) != 0);
for (int i = 9; i < 15; i++) for (int i = 9; i < 15; i++)
setModule(qrcode, 14 - i, 8, ((data >> i) & 1) != 0); setModule(qrcode, 14 - i, 8, ((data >> i) & 1) != 0);
// Draw second copy // Draw second copy
int qrsize = qrcodegen_getSize(qrcode); int qrsize = qrcodegen_getSize(qrcode);
for (int i = 0; i <= 7; i++) for (int i = 0; i <= 7; i++)
@ -554,7 +554,7 @@ static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qr
static long getPenaltyScore(const uint8_t qrcode[]) { static long getPenaltyScore(const uint8_t qrcode[]) {
int qrsize = qrcodegen_getSize(qrcode); int qrsize = qrcodegen_getSize(qrcode);
long result = 0; long result = 0;
// Adjacent modules in row having same color // Adjacent modules in row having same color
for (int y = 0; y < qrsize; y++) { for (int y = 0; y < qrsize; y++) {
bool colorX; bool colorX;
@ -587,7 +587,7 @@ static long getPenaltyScore(const uint8_t qrcode[]) {
} }
} }
} }
// 2*2 blocks of modules having same color // 2*2 blocks of modules having same color
for (int y = 0; y < qrsize - 1; y++) { for (int y = 0; y < qrsize - 1; y++) {
for (int x = 0; x < qrsize - 1; x++) { for (int x = 0; x < qrsize - 1; x++) {
@ -598,7 +598,7 @@ static long getPenaltyScore(const uint8_t qrcode[]) {
result += PENALTY_N2; result += PENALTY_N2;
} }
} }
// Finder-like pattern in rows // Finder-like pattern in rows
for (int y = 0; y < qrsize; y++) { for (int y = 0; y < qrsize; y++) {
for (int x = 0, bits = 0; x < qrsize; x++) { for (int x = 0, bits = 0; x < qrsize; x++) {
@ -615,7 +615,7 @@ static long getPenaltyScore(const uint8_t qrcode[]) {
result += PENALTY_N3; result += PENALTY_N3;
} }
} }
// Balance of black and white modules // Balance of black and white modules
int black = 0; int black = 0;
for (int y = 0; y < qrsize; y++) { for (int y = 0; y < qrsize; y++) {
@ -734,7 +734,7 @@ testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars) {
if (numChars > (unsigned int)LIMIT) if (numChars > (unsigned int)LIMIT)
return -1; return -1;
int n = (int)numChars; int n = (int)numChars;
int result = -2; int result = -2;
if (mode == qrcodegen_Mode_NUMERIC) { if (mode == qrcodegen_Mode_NUMERIC) {
// n * 3 + ceil(n / 3) // n * 3 + ceil(n / 3)
@ -798,7 +798,7 @@ struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]
if (bitLen > 0) if (bitLen > 0)
memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0]));
result.bitLength = 0; result.bitLength = 0;
unsigned int accumData = 0; unsigned int accumData = 0;
int accumCount = 0; int accumCount = 0;
for (; *digits != '\0'; digits++) { for (; *digits != '\0'; digits++) {
@ -832,7 +832,7 @@ struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t bu
if (bitLen > 0) if (bitLen > 0)
memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0]));
result.bitLength = 0; result.bitLength = 0;
unsigned int accumData = 0; unsigned int accumData = 0;
int accumCount = 0; int accumCount = 0;
for (; *text != '\0'; text++) { for (; *text != '\0'; text++) {
@ -893,7 +893,7 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz
assert(segs != NULL || len == 0); assert(segs != NULL || len == 0);
assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX); assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX);
// assert(0 <= (int)ecl && (int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7); // assert(0 <= (int)ecl && (int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7);
// Find the minimal version number to use // Find the minimal version number to use
int version, dataUsedBits; int version, dataUsedBits;
for (version = minVersion; ; version++) { for (version = minVersion; ; version++) {
@ -907,13 +907,13 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz
} }
} }
assert(dataUsedBits != -1); assert(dataUsedBits != -1);
// Increase the error correction level while the data still fits in the current version number // Increase the error correction level while the data still fits in the current version number
for (int i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) { for (int i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) {
if (boostEcl && dataUsedBits <= getNumDataCodewords(version, (enum qrcodegen_Ecc)i) * 8) if (boostEcl && dataUsedBits <= getNumDataCodewords(version, (enum qrcodegen_Ecc)i) * 8)
ecl = (enum qrcodegen_Ecc)i; ecl = (enum qrcodegen_Ecc)i;
} }
// Create the data bit string by concatenating all segments // Create the data bit string by concatenating all segments
int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; int dataCapacityBits = getNumDataCodewords(version, ecl) * 8;
memset(qrcode, 0, qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); memset(qrcode, 0, qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0]));
@ -934,26 +934,26 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz
for (int j = 0; j < seg->bitLength; j++) for (int j = 0; j < seg->bitLength; j++)
appendBitsToBuffer((seg->data[j >> 3] >> (7 - (j & 7))) & 1, 1, qrcode, &bitLen); appendBitsToBuffer((seg->data[j >> 3] >> (7 - (j & 7))) & 1, 1, qrcode, &bitLen);
} }
// Add terminator and pad up to a byte if applicable // Add terminator and pad up to a byte if applicable
int terminatorBits = dataCapacityBits - bitLen; int terminatorBits = dataCapacityBits - bitLen;
if (terminatorBits > 4) if (terminatorBits > 4)
terminatorBits = 4; terminatorBits = 4;
appendBitsToBuffer(0, terminatorBits, qrcode, &bitLen); appendBitsToBuffer(0, terminatorBits, qrcode, &bitLen);
appendBitsToBuffer(0, (8 - bitLen % 8) % 8, qrcode, &bitLen); appendBitsToBuffer(0, (8 - bitLen % 8) % 8, qrcode, &bitLen);
// Pad with alternate bytes until data capacity is reached // Pad with alternate bytes until data capacity is reached
for (uint8_t padByte = 0xEC; bitLen < dataCapacityBits; padByte ^= 0xEC ^ 0x11) for (uint8_t padByte = 0xEC; bitLen < dataCapacityBits; padByte ^= 0xEC ^ 0x11)
appendBitsToBuffer(padByte, 8, qrcode, &bitLen); appendBitsToBuffer(padByte, 8, qrcode, &bitLen);
assert(bitLen % 8 == 0); assert(bitLen % 8 == 0);
// Draw function and data codeword modules // Draw function and data codeword modules
appendErrorCorrection(qrcode, version, ecl, tempBuffer); appendErrorCorrection(qrcode, version, ecl, tempBuffer);
initializeFunctionModules(version, qrcode); initializeFunctionModules(version, qrcode);
drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode); drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode);
drawWhiteFunctionModules(qrcode, version); drawWhiteFunctionModules(qrcode, version);
initializeFunctionModules(version, tempBuffer); initializeFunctionModules(version, tempBuffer);
// Handle masking // Handle masking
if (mask == qrcodegen_Mask_AUTO) { // Automatically choose best mask if (mask == qrcodegen_Mask_AUTO) { // Automatically choose best mask
long minPenalty = LONG_MAX; long minPenalty = LONG_MAX;
@ -1012,7 +1012,7 @@ static int numCharCountBits(enum qrcodegen_Mode mode, int version) {
else if (10 <= version && version <= 26) i = 1; else if (10 <= version && version <= 26) i = 1;
else if (27 <= version && version <= 40) i = 2; else if (27 <= version && version <= 40) i = 2;
else assert(false); else assert(false);
switch (mode) { switch (mode) {
case qrcodegen_Mode_NUMERIC : { const int temp[] = {10, 12, 14}; return temp[i]; } case qrcodegen_Mode_NUMERIC : { const int temp[] = {10, 12, 14}; return temp[i]; }
case qrcodegen_Mode_ALPHANUMERIC: { const int temp[] = { 9, 11, 13}; return temp[i]; } case qrcodegen_Mode_ALPHANUMERIC: { const int temp[] = { 9, 11, 13}; return temp[i]; }

View File

@ -1,9 +1,9 @@
/* /*
* QR Code generator library (C) * QR Code generator library (C)
* *
* Copyright (c) Project Nayuki. (MIT License) * Copyright (c) Project Nayuki. (MIT License)
* https://www.nayuki.io/page/qr-code-generator-library * https://www.nayuki.io/page/qr-code-generator-library
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of * Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in * this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to * the Software without restriction, including without limitation the rights to
@ -30,7 +30,7 @@
/*---- Enum and struct types----*/ /*---- Enum and struct types----*/
/* /*
* The error correction level used in a QR Code symbol. * The error correction level used in a QR Code symbol.
*/ */
enum qrcodegen_Ecc { enum qrcodegen_Ecc {
@ -41,7 +41,7 @@ enum qrcodegen_Ecc {
}; };
/* /*
* The mask pattern used in a QR Code symbol. * The mask pattern used in a QR Code symbol.
*/ */
enum qrcodegen_Mask { enum qrcodegen_Mask {
@ -60,7 +60,7 @@ enum qrcodegen_Mask {
}; };
/* /*
* The mode field of a segment. * The mode field of a segment.
*/ */
enum qrcodegen_Mode { enum qrcodegen_Mode {
@ -72,7 +72,7 @@ enum qrcodegen_Mode {
}; };
/* /*
* A segment of user/application data that a QR Code symbol can convey. * A segment of user/application data that a QR Code symbol can convey.
* Each segment has a mode, a character count, and character/general data that is * Each segment has a mode, a character count, and character/general data that is
* already encoded as a sequence of bits. The maximum allowed bit length is 32767, * already encoded as a sequence of bits. The maximum allowed bit length is 32767,
@ -81,17 +81,17 @@ enum qrcodegen_Mode {
struct qrcodegen_Segment { struct qrcodegen_Segment {
// The mode indicator for this segment. // The mode indicator for this segment.
enum qrcodegen_Mode mode; enum qrcodegen_Mode mode;
// The length of this segment's unencoded data. Always in the range [0, 32767]. // The length of this segment's unencoded data. Always in the range [0, 32767].
// For numeric, alphanumeric, and kanji modes, this measures in Unicode code points. // For numeric, alphanumeric, and kanji modes, this measures in Unicode code points.
// For byte mode, this measures in bytes (raw binary data, text in UTF-8, or other encodings). // For byte mode, this measures in bytes (raw binary data, text in UTF-8, or other encodings).
// For ECI mode, this is always zero. // For ECI mode, this is always zero.
int numChars; int numChars;
// The data bits of this segment, packed in bitwise big endian. // The data bits of this segment, packed in bitwise big endian.
// Can be null if the bit length is zero. // Can be null if the bit length is zero.
uint8_t *data; uint8_t *data;
// The number of valid data bits used in the buffer. Requires // The number of valid data bits used in the buffer. Requires
// 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8. // 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8.
int bitLength; int bitLength;
@ -120,7 +120,7 @@ struct qrcodegen_Segment {
/*---- Functions to generate QR Codes ----*/ /*---- Functions to generate QR Codes ----*/
/* /*
* Encodes the given text string to a QR Code symbol, returning true if encoding succeeded. * Encodes the given text string to a QR Code symbol, returning true if encoding succeeded.
* If the data is too long to fit in any version in the given range * If the data is too long to fit in any version in the given range
* at the given ECC level, then false is returned. * at the given ECC level, then false is returned.
@ -143,7 +143,7 @@ bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode
enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl);
/* /*
* Encodes the given binary data to a QR Code symbol, returning true if encoding succeeded. * Encodes the given binary data to a QR Code symbol, returning true if encoding succeeded.
* If the data is too long to fit in any version in the given range * If the data is too long to fit in any version in the given range
* at the given ECC level, then false is returned. * at the given ECC level, then false is returned.
@ -165,19 +165,19 @@ bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcod
enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl);
/* /*
* Tests whether the given string can be encoded as a segment in alphanumeric mode. * Tests whether the given string can be encoded as a segment in alphanumeric mode.
*/ */
bool qrcodegen_isAlphanumeric(const char *text); bool qrcodegen_isAlphanumeric(const char *text);
/* /*
* Tests whether the given string can be encoded as a segment in numeric mode. * Tests whether the given string can be encoded as a segment in numeric mode.
*/ */
bool qrcodegen_isNumeric(const char *text); bool qrcodegen_isNumeric(const char *text);
/* /*
* Returns the number of bytes (uint8_t) needed for the data buffer of a segment * Returns the number of bytes (uint8_t) needed for the data buffer of a segment
* containing the given number of characters using the given mode. Notes: * containing the given number of characters using the given mode. Notes:
* - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or * - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or
@ -191,19 +191,19 @@ bool qrcodegen_isNumeric(const char *text);
size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars); size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars);
/* /*
* Returns a segment representing the given binary data encoded in byte mode. * Returns a segment representing the given binary data encoded in byte mode.
*/ */
struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]); struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]);
/* /*
* Returns a segment representing the given string of decimal digits encoded in numeric mode. * Returns a segment representing the given string of decimal digits encoded in numeric mode.
*/ */
struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]); struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]);
/* /*
* Returns a segment representing the given text string encoded in alphanumeric mode. * Returns a segment representing the given text string encoded in alphanumeric mode.
* The characters allowed are: 0 to 9, A to Z (uppercase only), space, * The characters allowed are: 0 to 9, A to Z (uppercase only), space,
* dollar, percent, asterisk, plus, hyphen, period, slash, colon. * dollar, percent, asterisk, plus, hyphen, period, slash, colon.
@ -211,14 +211,14 @@ struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]
struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]); struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]);
/* /*
* Returns a segment representing an Extended Channel Interpretation * Returns a segment representing an Extended Channel Interpretation
* (ECI) designator with the given assignment value. * (ECI) designator with the given assignment value.
*/ */
struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]); struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]);
/* /*
* Renders a QR Code symbol representing the given data segments at the given error correction * Renders a QR Code symbol representing the given data segments at the given error correction
* level or higher. The smallest possible QR Code version is automatically chosen for the output. * level or higher. The smallest possible QR Code version is automatically chosen for the output.
* Returns true if QR Code creation succeeded, or false if the data is too long to fit in any version. * Returns true if QR Code creation succeeded, or false if the data is too long to fit in any version.
@ -233,7 +233,7 @@ bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len,
enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]); enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]);
/* /*
* Renders a QR Code symbol representing the given data segments with the given encoding parameters. * Renders a QR Code symbol representing the given data segments with the given encoding parameters.
* Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions. * Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions.
* The smallest possible QR Code version within the given range is automatically chosen for the output. * The smallest possible QR Code version within the given range is automatically chosen for the output.
@ -250,7 +250,7 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz
/*---- Functions to extract raw data from QR Codes ----*/ /*---- Functions to extract raw data from QR Codes ----*/
/* /*
* Returns the side length of the given QR Code, assuming that encoding succeeded. * Returns the side length of the given QR Code, assuming that encoding succeeded.
* The result is in the range [21, 177]. Note that the length of the array buffer * The result is in the range [21, 177]. Note that the length of the array buffer
* is related to the side length - every 'uint8_t qrcode[]' must have length at least * is related to the side length - every 'uint8_t qrcode[]' must have length at least
@ -259,7 +259,7 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz
int qrcodegen_getSize(const uint8_t qrcode[]); int qrcodegen_getSize(const uint8_t qrcode[]);
/* /*
* Returns the color of the module (pixel) at the given coordinates, which is either * Returns the color of the module (pixel) at the given coordinates, which is either
* false for white or true for black. The top left corner has the coordinates (x=0, y=0). * false for white or true for black. The top left corner has the coordinates (x=0, y=0).
* If the given coordinates are out of bounds, then false (white) is returned. * If the given coordinates are out of bounds, then false (white) is returned.

View File

@ -63,7 +63,7 @@
#define __STACK_ABT_LEN 0x10000 #define __STACK_ABT_LEN 0x10000
#define __STACK_TOP (__STACK_ABT_TOP - __STACK_ABT_LEN) #define __STACK_TOP (__STACK_ABT_TOP - __STACK_ABT_LEN)
#define __STACK_LEN 0x7F0000 #define __STACK_LEN 0x7F0000
#define __HEAP_ADDR (__FCRAM0_ADDR + 0x0001000) #define __HEAP_ADDR (__FCRAM0_ADDR + 0x0001000)
#define __HEAP_END (__STACK_TOP - __STACK_LEN) #define __HEAP_END (__STACK_TOP - __STACK_LEN)

View File

@ -20,7 +20,7 @@ bool spiflash_read(u32 offset, u32 size, u8 *buf)
ARM_WbDC_Range(ARM_GetSHMEM()->spiBuffer, blksz); ARM_WbDC_Range(ARM_GetSHMEM()->spiBuffer, blksz);
PXI_DoCMD(PXI_NVRAM_READ, args, 2); PXI_DoCMD(PXI_NVRAM_READ, args, 2);
ARM_InvDC_Range(ARM_GetSHMEM()->spiBuffer, blksz); ARM_InvDC_Range(ARM_GetSHMEM()->spiBuffer, blksz);
ARM_DSB(); ARM_DSB();
memcpy(buf, ARM_GetSHMEM()->spiBuffer, blksz); memcpy(buf, ARM_GetSHMEM()->spiBuffer, blksz);

View File

@ -3,38 +3,38 @@
u64 ReadAsciiOctal(char* num, u32 len) { u64 ReadAsciiOctal(char* num, u32 len) {
u64 res = 0; u64 res = 0;
if ((num[len-1] != '\0') && (num[len-1] != ' ')) if ((num[len-1] != '\0') && (num[len-1] != ' '))
return (u64) -1; return (u64) -1;
for (u32 i = 0; i < (len-1); i++) { for (u32 i = 0; i < (len-1); i++) {
res <<= 3; res <<= 3;
if ((num[i] >= '0') && (num[i] < '8')) res |= (num[i] - '0'); if ((num[i] >= '0') && (num[i] < '8')) res |= (num[i] - '0');
else return (u64) -1; else return (u64) -1;
} }
return res; return res;
} }
u32 ValidateTarHeader(void* tardata, void* tardata_end) { u32 ValidateTarHeader(void* tardata, void* tardata_end) {
TarHeader* tar = tardata; TarHeader* tar = tardata;
// available space // available space
if ((u8*) tardata_end < (u8*) tardata + sizeof(TarHeader)) if ((u8*) tardata_end < (u8*) tardata + sizeof(TarHeader))
return 1; return 1;
// filename prefix is not supported here // filename prefix is not supported here
if (*(tar->fname_prefix)) return 1; if (*(tar->fname_prefix)) return 1;
// check filesize // check filesize
u64 fsize_max = ((u8*) tardata_end - (u8*) tardata) - sizeof(TarHeader); u64 fsize_max = ((u8*) tardata_end - (u8*) tardata) - sizeof(TarHeader);
if (ReadAsciiOctal(tar->fsize, 12) > fsize_max) if (ReadAsciiOctal(tar->fsize, 12) > fsize_max)
return 1; return 1;
// type can only be standard file or dir // type can only be standard file or dir
if (tar->ftype && (tar->ftype != '0') && (tar->ftype != '5')) if (tar->ftype && (tar->ftype != '0') && (tar->ftype != '5'))
return 1; return 1;
// checksum // checksum
u8* data = (u8*) tardata; u8* data = (u8*) tardata;
u64 checksum = 0; u64 checksum = 0;
@ -42,18 +42,18 @@ u32 ValidateTarHeader(void* tardata, void* tardata_end) {
checksum += ((i < 148) || (i >= 156)) ? data[i] : (u64) ' '; checksum += ((i < 148) || (i >= 156)) ? data[i] : (u64) ' ';
if (checksum != ReadAsciiOctal(tar->checksum, 7)) if (checksum != ReadAsciiOctal(tar->checksum, 7))
return 1; return 1;
return 0; return 0;
} }
void* GetTarFileInfo(void* tardata, char* fname, u64* fsize, bool* is_dir) { void* GetTarFileInfo(void* tardata, char* fname, u64* fsize, bool* is_dir) {
// this assumes a valid TAR header // this assumes a valid TAR header
TarHeader* tar = tardata; TarHeader* tar = tardata;
if (fname) snprintf(fname, 101, "%.100s", tar->fname); if (fname) snprintf(fname, 101, "%.100s", tar->fname);
if (fsize) *fsize = ReadAsciiOctal(tar->fsize, 12); if (fsize) *fsize = ReadAsciiOctal(tar->fsize, 12);
if (is_dir) *is_dir = (tar->ftype == '5'); if (is_dir) *is_dir = (tar->ftype == '5');
return (void*) (tar + 1); return (void*) (tar + 1);
} }
@ -62,23 +62,23 @@ void* NextTarEntry(void* tardata, void* tardata_end) {
TarHeader* tar = tardata; TarHeader* tar = tardata;
u8* data = (u8*) tardata; u8* data = (u8*) tardata;
u64 fsize = ReadAsciiOctal(tar->fsize, 12); u64 fsize = ReadAsciiOctal(tar->fsize, 12);
data += sizeof(TarHeader) + align(fsize, 512); data += sizeof(TarHeader) + align(fsize, 512);
if (ValidateTarHeader(data, tardata_end) != 0) if (ValidateTarHeader(data, tardata_end) != 0)
return NULL; return NULL;
return data; return data;
} }
void* FindTarFileInfo(void* tardata, void* tardata_end, const char* fname, u64* fsize) { void* FindTarFileInfo(void* tardata, void* tardata_end, const char* fname, u64* fsize) {
while (tardata && (tardata < tardata_end)) { while (tardata && (tardata < tardata_end)) {
TarHeader* tar = tardata; TarHeader* tar = tardata;
if (ValidateTarHeader(tardata, tardata_end) != 0) break; if (ValidateTarHeader(tardata, tardata_end) != 0) break;
if ((strncasecmp(tar->fname, fname, 100) == 0) && (!tar->ftype || (tar->ftype == '0'))) if ((strncasecmp(tar->fname, fname, 100) == 0) && (!tar->ftype || (tar->ftype == '0')))
return GetTarFileInfo(tardata, NULL, fsize, NULL); return GetTarFileInfo(tardata, NULL, fsize, NULL);
tardata = ((u8*) tardata) + sizeof(TarHeader) + align(ReadAsciiOctal(tar->fsize, 12), 512); tardata = ((u8*) tardata) + sizeof(TarHeader) + align(ReadAsciiOctal(tar->fsize, 12), 512);
} }
return NULL; return NULL;
} }

View File

@ -31,15 +31,15 @@
#define FirstVTarEntry() \ #define FirstVTarEntry() \
TARDATA TARDATA
#define OffsetVTarEntry(off) \ #define OffsetVTarEntry(off) \
TARDATA_(off) TARDATA_(off)
#define NextVTarEntry(tardata) \ #define NextVTarEntry(tardata) \
NextTarEntry(tardata, TARDATA_END) NextTarEntry(tardata, TARDATA_END)
#define GetVTarFileInfo(tardata, fname, fsize, is_dir) \ #define GetVTarFileInfo(tardata, fname, fsize, is_dir) \
GetTarFileInfo(tardata, fname, fsize, is_dir) GetTarFileInfo(tardata, fname, fsize, is_dir)
#define FindVTarFileInfo(fname, fsize) \ #define FindVTarFileInfo(fname, fsize) \
FindTarFileInfo(TARDATA, TARDATA_END, fname, fsize) FindTarFileInfo(TARDATA, TARDATA_END, fname, fsize)

View File

@ -42,7 +42,7 @@ u32 CheckTransferableMbr(void* data) { // strict checks, custom partitions not a
u32 TransferCtrNandImage(const char* path_img, const char* drv) { u32 TransferCtrNandImage(const char* path_img, const char* drv) {
if (!CheckWritePermissions(drv)) return 1; if (!CheckWritePermissions(drv)) return 1;
// backup current mount path, mount new path // backup current mount path, mount new path
char path_store[256] = { 0 }; char path_store[256] = { 0 };
char* path_bak = NULL; char* path_bak = NULL;
@ -53,7 +53,7 @@ u32 TransferCtrNandImage(const char* path_img, const char* drv) {
InitImgFS(path_bak); InitImgFS(path_bak);
return 1; return 1;
} }
// CTRNAND preparations // CTRNAND preparations
SecureInfo secnfo_img; SecureInfo secnfo_img;
SecureInfo secnfo_loc; SecureInfo secnfo_loc;
@ -62,19 +62,19 @@ u32 TransferCtrNandImage(const char* path_img, const char* drv) {
char path_secnfo_c[32]; char path_secnfo_c[32];
char path_tickdb[32]; char path_tickdb[32];
char path_tickdb_bak[32]; 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);
snprintf(path_tickdb, 32, "%s/dbs/ticket.db", drv); snprintf(path_tickdb, 32, "%s/dbs/ticket.db", drv);
snprintf(path_tickdb_bak, 32, "%s/dbs/ticket.bak", drv); snprintf(path_tickdb_bak, 32, "%s/dbs/ticket.bak", 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);
@ -95,7 +95,7 @@ u32 TransferCtrNandImage(const char* path_img, const char* drv) {
drv, sha256sum[0], sha256sum[1], sha256sum[2], sha256sum[3]); drv, sha256sum[0], sha256sum[1], sha256sum[2], sha256sum[3]);
PathDelete(path_asr); PathDelete(path_asr);
} }
// actual transfer - db files / titles // actual transfer - db files / titles
static const char* dbnames[] = { "ticket.db", "certs.db", "title.db", "import.db", "tmp_t.db", "tmp_i.db" }; static const char* dbnames[] = { "ticket.db", "certs.db", "title.db", "import.db", "tmp_t.db", "tmp_i.db" };
char path_to[32]; char path_to[32];
@ -115,7 +115,7 @@ u32 TransferCtrNandImage(const char* path_img, const char* drv) {
snprintf(path_from, 32, "7:/title"); snprintf(path_from, 32, "7:/title");
PathDelete(path_to); PathDelete(path_to);
PathCopy(drv, path_from, &flags); PathCopy(drv, path_from, &flags);
InitImgFS(path_bak); InitImgFS(path_bak);
return 0; return 0;
} }

File diff suppressed because it is too large Load Diff

View File

@ -8,45 +8,45 @@
u32 CryptAesKeyDb(const char* path, bool inplace, bool encrypt) { u32 CryptAesKeyDb(const char* path, bool inplace, bool encrypt) {
AesKeyInfo* keydb = NULL; AesKeyInfo* keydb = NULL;
const char* path_out = (inplace) ? path : OUTPUT_PATH "/" KEYDB_NAME; const char* path_out = (inplace) ? path : OUTPUT_PATH "/" KEYDB_NAME;
// write permissions // write permissions
if (!CheckWritePermissions(path_out)) if (!CheckWritePermissions(path_out))
return 1; return 1;
if (!inplace) { if (!inplace) {
// ensure the output dir exists // ensure the output dir exists
if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) if (fvx_rmkdir(OUTPUT_PATH) != FR_OK)
return 1; return 1;
} }
// check key database size // check key database size
u32 fsize = fvx_qsize(path); u32 fsize = fvx_qsize(path);
if (!fsize || (fsize % sizeof(AesKeyInfo)) || (fsize > MAX_KEYDB_SIZE)) if (!fsize || (fsize % sizeof(AesKeyInfo)) || (fsize > MAX_KEYDB_SIZE))
return 1; return 1;
keydb = (AesKeyInfo*) malloc(fsize); keydb = (AesKeyInfo*) malloc(fsize);
if (!keydb) return 1; if (!keydb) return 1;
// load key database // load key database
if (fvx_qread(path, keydb, 0, fsize, NULL) != FR_OK) { if (fvx_qread(path, keydb, 0, fsize, NULL) != FR_OK) {
free(keydb); free(keydb);
return 1; return 1;
} }
// en-/decrypt keys // en-/decrypt keys
u32 n_keys = fsize / 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]));
} }
// 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, fsize, NULL) != FR_OK) { if (fvx_qwrite(path_out, keydb, 0, fsize, NULL) != FR_OK) {
free(keydb); free(keydb);
return 1; return 1;
} }
free(keydb); free(keydb);
return 0; return 0;
} }
@ -72,35 +72,35 @@ u32 BuildKeyDb(const char* path, bool dump) {
static AesKeyInfo* key_info = NULL; 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;
// 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) key_info = (AesKeyInfo*) malloc(STD_BUFFER_SIZE);
if (!key_info) return 1; if (!key_info) return 1;
memset(key_info, 0xFF, sizeof(AesKeyInfo)); 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)))
path_in = path_out; path_in = path_out;
else return 0; else return 0;
} }
// key info has to be allocated at this point // key info has to be allocated at this point
if (!key_info) return 1; 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
u32 fsize = fvx_qsize(path_in); u32 fsize = fvx_qsize(path_in);
if ((fsize % sizeof(AesKeyInfo)) || (fsize > MAX_KEYDB_SIZE)) if ((fsize % sizeof(AesKeyInfo)) || (fsize > MAX_KEYDB_SIZE))
return 1; return 1;
u32 n_keys = fsize / sizeof(AesKeyInfo); u32 n_keys = fsize / sizeof(AesKeyInfo);
u32 merged_keys = 0; u32 merged_keys = 0;
AesKeyInfo* key_info_merge = (AesKeyInfo*) malloc(fsize); AesKeyInfo* key_info_merge = (AesKeyInfo*) malloc(fsize);
if (fvx_qread(path_in, key_info_merge, 0, fsize, NULL) == FR_OK) { if (fvx_qread(path_in, key_info_merge, 0, fsize, NULL) == FR_OK) {
for (u32 i = 0; i < n_keys; i++) { for (u32 i = 0; i < n_keys; i++) {
@ -110,7 +110,7 @@ u32 BuildKeyDb(const char* path, bool dump) {
merged_keys++; merged_keys++;
} }
} }
free(key_info_merge); free(key_info_merge);
if (merged_keys < n_keys) return 1; if (merged_keys < n_keys) return 1;
} else if (filetype & BIN_LEGKEY) { // legacy key file } else if (filetype & BIN_LEGKEY) { // legacy key file
@ -118,17 +118,17 @@ u32 BuildKeyDb(const char* path, bool dump) {
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%31s", &keyslot, typestr) != 2) && if ((sscanf(name_in, "slot0x%02XKey%31s", &keyslot, typestr) != 2) &&
(sscanf(name_in, "slot0x%02Xkey%31s", &keyslot, typestr) != 2)) return 1; (sscanf(name_in, "slot0x%02Xkey%31s", &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);
@ -143,7 +143,7 @@ u32 BuildKeyDb(const char* path, bool dump) {
if (fvx_qread(path_in, key.key, 0, 16, NULL) != FR_OK) 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++) {
@ -151,7 +151,7 @@ u32 BuildKeyDb(const char* path, bool dump) {
if (dump_size_next > MAX_KEYDB_SIZE) break; if (dump_size_next > MAX_KEYDB_SIZE) break;
dump_size = dump_size_next; dump_size = dump_size_next;
} }
if (dump_size) { if (dump_size) {
if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) // ensure the output dir exists if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) // ensure the output dir exists
return 1; return 1;
@ -159,10 +159,10 @@ u32 BuildKeyDb(const char* path, bool dump) {
if (fvx_qwrite(path_out, key_info, 0, dump_size, NULL) != FR_OK) if (fvx_qwrite(path_out, key_info, 0, dump_size, NULL) != FR_OK)
return 1; return 1;
} }
free(key_info); free(key_info);
key_info = NULL; key_info = NULL;
} }
return 0; return 0;
} }

View File

@ -55,15 +55,15 @@
u32 SetupSlot0x30(char drv) { u32 SetupSlot0x30(char drv) {
u8 keyy[16] __attribute__((aligned(32))); u8 keyy[16] __attribute__((aligned(32)));
char movable_path[32]; char movable_path[32];
if ((drv == 'A') || (drv == 'S')) drv = '1'; if ((drv == 'A') || (drv == 'S')) drv = '1';
else if ((drv == 'B') || (drv == 'E')) drv = '4'; else if ((drv == 'B') || (drv == 'E')) drv = '4';
snprintf(movable_path, 32, "%c:/private/movable.sed", drv); snprintf(movable_path, 32, "%c:/private/movable.sed", drv);
if (fvx_qread(movable_path, keyy, 0x110, 0x10, NULL) != FR_OK) return 1; if (fvx_qread(movable_path, keyy, 0x110, 0x10, NULL) != FR_OK) return 1;
setup_aeskeyY(0x30, keyy); setup_aeskeyY(0x30, keyy);
use_aeskey(0x30); use_aeskey(0x30);
return 0; return 0;
} }
@ -117,12 +117,12 @@ u32 LocateAgbSaveSdCurrentSlot(const char* path, AgbSaveHeader* agbsave) {
u32 CheckCmacHeader(const char* path) { u32 CheckCmacHeader(const char* path) {
u8 cmac_hdr[0x100]; u8 cmac_hdr[0x100];
UINT br; UINT br;
if ((fvx_qread(path, cmac_hdr, 0, 0x100, &br) != FR_OK) || (br != 0x100)) if ((fvx_qread(path, cmac_hdr, 0, 0x100, &br) != FR_OK) || (br != 0x100))
return 1; return 1;
for (u32 i = 0x10; i < 0x100; i++) for (u32 i = 0x10; i < 0x100; i++)
if (cmac_hdr[i] != 0x00) return 1; if (cmac_hdr[i] != 0x00) return 1;
return 0; return 0;
} }
@ -133,14 +133,14 @@ u32 CheckCmacPath(const char* path) {
u32 ReadWriteFileCmac(const char* path, u8* cmac, bool do_write, bool check_perms) { u32 ReadWriteFileCmac(const char* path, u8* cmac, bool do_write, bool check_perms) {
u32 cmac_type = CalculateFileCmac(path, NULL); u32 cmac_type = CalculateFileCmac(path, NULL);
u32 offset = 0; u32 offset = 0;
if (!cmac_type) return 1; if (!cmac_type) return 1;
else if (cmac_type == CMAC_MOVABLE) offset = 0x130; else if (cmac_type == CMAC_MOVABLE) offset = 0x130;
else if (cmac_type == CMAC_AGBSAVE) offset = 0x010; else if (cmac_type == CMAC_AGBSAVE) offset = 0x010;
else if (cmac_type == CMAC_AGBSAVE_SD) offset = LocateAgbSaveSdCurrentSlot(path, NULL) + 0x10; else if (cmac_type == CMAC_AGBSAVE_SD) offset = LocateAgbSaveSdCurrentSlot(path, NULL) + 0x10;
else if ((cmac_type == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) return 1; // can't do that here else if ((cmac_type == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) return 1; // can't do that here
else offset = 0x000; else offset = 0x000;
if (do_write && check_perms && !CheckWritePermissions(path)) return 1; if (do_write && check_perms && !CheckWritePermissions(path)) return 1;
if (!do_write) return (fvx_qread(path, cmac, offset, 0x10, NULL) != FR_OK) ? 1 : 0; if (!do_write) return (fvx_qread(path, cmac, offset, 0x10, NULL) != FR_OK) ? 1 : 0;
else return (fvx_qwrite(path, cmac, offset, 0x10, NULL) != FR_OK) ? 1 : 0; else return (fvx_qwrite(path, cmac, offset, 0x10, NULL) != FR_OK) ? 1 : 0;
@ -155,13 +155,13 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
u32 sid; // save ID / various uses u32 sid; // save ID / various uses
char* name; char* name;
char* ext; char* ext;
name = strrchr(path, '/'); // filename name = strrchr(path, '/'); // filename
if (!name) return 0; // will not happen if (!name) return 0; // will not happen
name++; name++;
ext = strrchr(name, '.'); // extension ext = strrchr(name, '.'); // extension
if (ext) ext++; if (ext) ext++;
if ((drv == 'A') || (drv == 'B')) { // data installed on SD if ((drv == 'A') || (drv == 'B')) { // data installed on SD
if (sscanf(path, "%c:/extdata/%08lx/%08lx/%08lx/%08lx", &drv, &xid_high, &xid_low, &fid_high, &fid_low) == 5) { if (sscanf(path, "%c:/extdata/%08lx/%08lx/%08lx/%08lx", &drv, &xid_high, &xid_low, &fid_high, &fid_low) == 5) {
sid = 1; sid = 1;
@ -191,7 +191,7 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
cmac_type = CMAC_CMD_TWLN; // this is not supported (yet), it's in here just for detection cmac_type = CMAC_CMD_TWLN; // this is not supported (yet), it's in here just for detection
} }
} }
if (!cmac_type) { // path independent stuff if (!cmac_type) { // path independent stuff
const char* db_names[] = { SYS_DB_NAMES }; const char* db_names[] = { SYS_DB_NAMES };
for (sid = 0; sid < sizeof(db_names) / sizeof(char*); sid++) for (sid = 0; sid < sizeof(db_names) / sizeof(char*); sid++)
@ -203,28 +203,28 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
else if (strncasecmp(name, "agbsave.bin", 16) == 0) else if (strncasecmp(name, "agbsave.bin", 16) == 0)
cmac_type = CMAC_AGBSAVE; cmac_type = CMAC_AGBSAVE;
} }
// exit with cmac_type if (u8*) cmac is NULL // exit with cmac_type if (u8*) cmac is NULL
// somewhat hacky, but can be used to check if file has a CMAC // somewhat hacky, but can be used to check if file has a CMAC
if (!cmac) return cmac_type; if (!cmac) return cmac_type;
else if ((cmac_type == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) return 1; else if ((cmac_type == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) return 1;
else if (!cmac_type) return 1; else if (!cmac_type) return 1;
static const u32 cmac_keyslot[] = { CMAC_KEYSLOT }; static const u32 cmac_keyslot[] = { CMAC_KEYSLOT };
u8 hashdata[0x200] __attribute__((aligned(4))); u8 hashdata[0x200] __attribute__((aligned(4)));
u32 keyslot = cmac_keyslot[cmac_type]; u32 keyslot = cmac_keyslot[cmac_type];
u32 hashsize = 0; u32 hashsize = 0;
// setup slot 0x30 via movable.sed // setup slot 0x30 via movable.sed
if ((keyslot == 0x30) && (SetupSlot0x30(drv) != 0)) if ((keyslot == 0x30) && (SetupSlot0x30(drv) != 0))
return 1; return 1;
// 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*) malloc(AGBSAVE_MAX_SIZE); AgbSaveHeader* agbsave = (AgbSaveHeader*) malloc(AGBSAVE_MAX_SIZE);
u32 offset = 0; u32 offset = 0;
UINT br; UINT br;
if (!agbsave) return 1; if (!agbsave) return 1;
if (cmac_type == CMAC_AGBSAVE_SD) offset = LocateAgbSaveSdCurrentSlot(path, NULL); if (cmac_type == CMAC_AGBSAVE_SD) offset = LocateAgbSaveSdCurrentSlot(path, NULL);
if ((fvx_qread(path, agbsave, offset, AGBSAVE_MAX_SIZE, &br) != FR_OK) || (br < 0x200) || if ((fvx_qread(path, agbsave, offset, AGBSAVE_MAX_SIZE, &br) != FR_OK) || (br < 0x200) ||
@ -232,7 +232,7 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
free(agbsave); free(agbsave);
return 1; return 1;
} }
u32 ret = FixAgbSaveCmac(agbsave, cmac, (cmac_type == CMAC_AGBSAVE) ? NULL : path); u32 ret = FixAgbSaveCmac(agbsave, cmac, (cmac_type == CMAC_AGBSAVE) ? NULL : path);
free(agbsave); free(agbsave);
return ret; return ret;
@ -275,14 +275,14 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
hashsize = 0x10C; hashsize = 0x10C;
} }
} }
// calculate CMAC // calculate CMAC
u8 shasum[32]; u8 shasum[32];
if (!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);
return 0; return 0;
} }
@ -311,11 +311,11 @@ u32 FixFileCmac(const char* path, bool check_perms) {
u32 FixAgbSaveCmac(void* data, u8* cmac, const char* sddrv) { u32 FixAgbSaveCmac(void* data, u8* cmac, const char* sddrv) {
AgbSaveHeader* agbsave = (AgbSaveHeader*) (void*) data; AgbSaveHeader* agbsave = (AgbSaveHeader*) (void*) data;
u8 temp[0x30] __attribute__((aligned(4))); // final hash @temp+0x00 u8 temp[0x30] __attribute__((aligned(4))); // final hash @temp+0x00
// safety check // safety check
if (ValidateAgbSaveHeader(agbsave) != 0) if (ValidateAgbSaveHeader(agbsave) != 0)
return 1; return 1;
if (!sddrv) { // NAND partition mode if (!sddrv) { // NAND partition mode
sha_quick(temp + 0x00, (u8*) data + 0x30, (0x200 - 0x30) + agbsave->save_size, SHA256_MODE); sha_quick(temp + 0x00, (u8*) data + 0x30, (0x200 - 0x30) + agbsave->save_size, SHA256_MODE);
} else { } else {
@ -325,7 +325,7 @@ u32 FixAgbSaveCmac(void* data, u8* cmac, const char* sddrv) {
// this won't work on devkits(!!!) // this won't work on devkits(!!!)
const char* cmac_savetype[] = { CMAC_SAVETYPE }; const char* cmac_savetype[] = { CMAC_SAVETYPE };
if (SetupSlot0x30(*sddrv) != 0) return 1; if (SetupSlot0x30(*sddrv) != 0) return 1;
// first hash (hash0 = AGBSAVE_hash) // first hash (hash0 = AGBSAVE_hash)
sha_quick(temp + 0x08, (u8*) data + 0x30, (0x200 - 0x30) + agbsave->save_size, SHA256_MODE); sha_quick(temp + 0x08, (u8*) data + 0x30, (0x200 - 0x30) + agbsave->save_size, SHA256_MODE);
// second hash (hash1 = CTR-SAV0 + hash0) // second hash (hash1 = CTR-SAV0 + hash0)
@ -336,11 +336,11 @@ u32 FixAgbSaveCmac(void* data, u8* cmac, const char* sddrv) {
memcpy(temp + 0x08, &(agbsave->title_id), 8); memcpy(temp + 0x08, &(agbsave->title_id), 8);
sha_quick(temp + 0x00, temp, 0x30, SHA256_MODE); sha_quick(temp + 0x00, temp, 0x30, SHA256_MODE);
} }
use_aeskey((sddrv) ? 0x30 : 0x24); use_aeskey((sddrv) ? 0x30 : 0x24);
aes_cmac(temp, &(agbsave->cmac), 2); aes_cmac(temp, &(agbsave->cmac), 2);
if (cmac) memcpy(cmac, &(agbsave->cmac), 0x10); if (cmac) memcpy(cmac, &(agbsave->cmac), 0x10);
return 0; return 0;
} }
@ -368,7 +368,7 @@ u32 CheckFixCmdCmac(const char* path, bool fix, bool check_perms) {
u64 cmd_size = fvx_qsize(path); u64 cmd_size = fvx_qsize(path);
u8* cmd_data = malloc(cmd_size); u8* cmd_data = malloc(cmd_size);
CmdHeader* cmd = (CmdHeader*) (void*) cmd_data; CmdHeader* cmd = (CmdHeader*) (void*) cmd_data;
// check for out of memory // check for out of memory
if (cmd_data == NULL) return 1; if (cmd_data == NULL) return 1;
@ -454,13 +454,13 @@ u32 RecursiveFixFileCmacWorker(char* path) {
FILINFO fno; FILINFO fno;
DIR pdir; DIR pdir;
u32 err = 0; u32 err = 0;
if (fvx_opendir(&pdir, path) == FR_OK) { // process folder contents if (fvx_opendir(&pdir, path) == FR_OK) { // process folder contents
char pathstr[32 + 1]; char pathstr[32 + 1];
TruncateString(pathstr, path, 32, 8); TruncateString(pathstr, path, 32, 8);
char* fname = path + strnlen(path, 255); char* fname = path + strnlen(path, 255);
*(fname++) = '/'; *(fname++) = '/';
ShowString("%s\nFixing CMACs, please wait...", pathstr); ShowString("%s\nFixing CMACs, please wait...", pathstr);
while (f_readdir(&pdir, &fno) == FR_OK) { while (f_readdir(&pdir, &fno) == FR_OK) {
if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0)) if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0))
@ -479,16 +479,16 @@ u32 RecursiveFixFileCmacWorker(char* path) {
*(--fname) = '\0'; *(--fname) = '\0';
} else if (CheckCmacPath(path) == 0) // fix single file CMAC } else if (CheckCmacPath(path) == 0) // fix single file CMAC
return FixFileCmac(path, true); return FixFileCmac(path, true);
return err; return err;
} }
u32 RecursiveFixFileCmac(const char* path) { u32 RecursiveFixFileCmac(const char* path) {
// create a fixed up local path // create a fixed up local path
// (this is highly path sensitive) // (this is highly path sensitive)
char lpath[256]; char lpath[256];
char* p = (char*) path; char* p = (char*) path;
lpath[255] = '\0'; lpath[255] = '\0';
for (u32 i = 0; i < 255; i++) { for (u32 i = 0; i < 255; i++) {
lpath[i] = *(p++); lpath[i] = *(p++);
while ((lpath[i] == '/') && (*p == '/')) p++; while ((lpath[i] == '/') && (*p == '/')) p++;

View File

@ -50,7 +50,7 @@ u32 BuildEssentialBackup(const char* path, EssentialBackup* essential) {
}; };
memset(essential, 0, sizeof(EssentialBackup)); memset(essential, 0, sizeof(EssentialBackup));
memcpy(essential, filelist, sizeof(filelist)); memcpy(essential, filelist, sizeof(filelist));
// backup current mount path, mount new path // backup current mount path, mount new path
char path_store[256] = { 0 }; char path_store[256] = { 0 };
char* path_bak = NULL; char* path_bak = NULL;
@ -61,7 +61,7 @@ u32 BuildEssentialBackup(const char* path, EssentialBackup* essential) {
InitImgFS(path_bak); InitImgFS(path_bak);
return 1; return 1;
} }
// read four files // read four files
ExeFsFileHeader* files = essential->header.files; ExeFsFileHeader* files = essential->header.files;
if ((fvx_qread("I:/nand_hdr.bin", &(essential->nand_hdr), 0, 0x200, (UINT*) &(files[0].size)) != FR_OK) || if ((fvx_qread("I:/nand_hdr.bin", &(essential->nand_hdr), 0, 0x200, (UINT*) &(files[0].size)) != FR_OK) ||
@ -73,34 +73,34 @@ u32 BuildEssentialBackup(const char* path, EssentialBackup* essential) {
InitImgFS(path_bak); InitImgFS(path_bak);
return 1; return 1;
} }
// HWCAL0.dat / HWCAL1.dat // HWCAL0.dat / HWCAL1.dat
if ((fvx_qread("7:/ro/sys/HWCAL0.dat", &(essential->hwcal0), 0, 0x1000, (UINT*) &(files[6].size)) != FR_OK) || if ((fvx_qread("7:/ro/sys/HWCAL0.dat", &(essential->hwcal0), 0, 0x1000, (UINT*) &(files[6].size)) != FR_OK) ||
(fvx_qread("7:/ro/sys/HWCAL1.dat", &(essential->hwcal1), 0, 0x1000, (UINT*) &(files[7].size)) != FR_OK)) { (fvx_qread("7:/ro/sys/HWCAL1.dat", &(essential->hwcal1), 0, 0x1000, (UINT*) &(files[7].size)) != FR_OK)) {
memset(&(filelist[6]), 0, 2 * sizeof(ExeFsFileHeader)); memset(&(filelist[6]), 0, 2 * sizeof(ExeFsFileHeader));
} }
// mount original file // mount original file
InitImgFS(path_bak); InitImgFS(path_bak);
// fill nand cid / otp hash // fill nand cid / otp hash
sdmmc_get_cid(1, (u32*) (void*) &(essential->nand_cid)); sdmmc_get_cid(1, (u32*) (void*) &(essential->nand_cid));
if (!IS_UNLOCKED) memset(&(filelist[5]), 0, 3 * sizeof(ExeFsFileHeader)); if (!IS_UNLOCKED) memset(&(filelist[5]), 0, 3 * sizeof(ExeFsFileHeader));
else memcpy(&(essential->otp), (u8*) __OTP_ADDR, 0x100); else memcpy(&(essential->otp), (u8*) __OTP_ADDR, 0x100);
// calculate hashes // calculate hashes
for (u32 i = 0; i < 8 && *(filelist[i].name); i++) for (u32 i = 0; i < 8 && *(filelist[i].name); i++)
sha_quick(essential->header.hashes[9-i], sha_quick(essential->header.hashes[9-i],
((u8*) essential) + files[i].offset + sizeof(ExeFsHeader), ((u8*) essential) + files[i].offset + sizeof(ExeFsHeader),
files[i].size, SHA256_MODE); files[i].size, SHA256_MODE);
return 0; return 0;
} }
u32 CheckEmbeddedBackup(const char* path) { u32 CheckEmbeddedBackup(const char* path) {
EssentialBackup* essential = (EssentialBackup*) malloc(sizeof(EssentialBackup)); EssentialBackup* essential = (EssentialBackup*) malloc(sizeof(EssentialBackup));
EssentialBackup* embedded = (EssentialBackup*) malloc(sizeof(EssentialBackup)); EssentialBackup* embedded = (EssentialBackup*) malloc(sizeof(EssentialBackup));
if (!essential || !embedded || (BuildEssentialBackup(path, essential) != 0) || if (!essential || !embedded || (BuildEssentialBackup(path, essential) != 0) ||
(fvx_qread(path, embedded, SECTOR_D0K3 * 0x200, sizeof(EssentialBackup), NULL) != 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)) {
@ -108,7 +108,7 @@ u32 CheckEmbeddedBackup(const char* path) {
free(embedded); free(embedded);
return 1; return 1;
} }
free(essential); free(essential);
free(embedded); free(embedded);
return 0; return 0;
@ -117,7 +117,7 @@ u32 CheckEmbeddedBackup(const char* path) {
u32 EmbedEssentialBackup(const char* path) { u32 EmbedEssentialBackup(const char* path) {
EssentialBackup* essential = (EssentialBackup*) malloc(sizeof(EssentialBackup)); EssentialBackup* essential = (EssentialBackup*) malloc(sizeof(EssentialBackup));
if (!essential) return 1; 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((void*)essential->nand_hdr) != 0) || (ValidateNandNcsdHeader((void*)essential->nand_hdr) != 0) ||
@ -125,7 +125,7 @@ u32 EmbedEssentialBackup(const char* path) {
free(essential); free(essential);
return 1; return 1;
} }
free(essential); free(essential);
return 0; return 0;
} }
@ -133,27 +133,27 @@ u32 EmbedEssentialBackup(const char* path) {
u32 DumpGbaVcSavegameBuffered(const char* path, void* buffer) { u32 DumpGbaVcSavegameBuffered(const char* path, void* buffer) {
AgbSaveHeader* agbsave = (AgbSaveHeader*) buffer; AgbSaveHeader* agbsave = (AgbSaveHeader*) buffer;
u8* savegame = (u8*) (agbsave + 1); u8* savegame = (u8*) (agbsave + 1);
// read full AGBsave to memory // read full AGBsave to memory
if ((fvx_qread(path, agbsave, 0, sizeof(AgbSaveHeader), NULL) != FR_OK) || (ValidateAgbSaveHeader(agbsave) != 0) || if ((fvx_qread(path, agbsave, 0, sizeof(AgbSaveHeader), NULL) != FR_OK) || (ValidateAgbSaveHeader(agbsave) != 0) ||
(fvx_qread(path, savegame, sizeof(AgbSaveHeader), agbsave->save_size, NULL) != FR_OK)) return 1; // not a proper AGBSAVE file (fvx_qread(path, savegame, sizeof(AgbSaveHeader), agbsave->save_size, NULL) != FR_OK)) return 1; // not a proper AGBSAVE file
// byteswap for eeprom type saves (512 byte / 8 kB) // byteswap for eeprom type saves (512 byte / 8 kB)
if ((agbsave->save_size == GBASAVE_EEPROM_512) || (agbsave->save_size == GBASAVE_EEPROM_8K)) { if ((agbsave->save_size == GBASAVE_EEPROM_512) || (agbsave->save_size == GBASAVE_EEPROM_8K)) {
for (u8* ptr = savegame; (ptr - savegame) < (int) agbsave->save_size; ptr += 8) for (u8* ptr = savegame; (ptr - savegame) < (int) agbsave->save_size; ptr += 8)
*(u64*) (void*) ptr = getbe64(ptr); *(u64*) (void*) ptr = getbe64(ptr);
} }
// ensure the output dir exists // ensure the output dir exists
if (fvx_rmkdir(OUTPUT_PATH) != FR_OK) if (fvx_rmkdir(OUTPUT_PATH) != FR_OK)
return 1; return 1;
// generate output path // generate output path
char path_vcsav[64]; char path_vcsav[64];
snprintf(path_vcsav, 64, OUTPUT_PATH "/%016llX.gbavc.sav", agbsave->title_id); snprintf(path_vcsav, 64, OUTPUT_PATH "/%016llX.gbavc.sav", agbsave->title_id);
if (fvx_qwrite(path_vcsav, savegame, 0, agbsave->save_size, NULL) != FR_OK) return 1; // write fail if (fvx_qwrite(path_vcsav, savegame, 0, agbsave->save_size, NULL) != FR_OK) return 1; // write fail
return 0; return 0;
} }
u32 DumpGbaVcSavegame(const char* path) { u32 DumpGbaVcSavegame(const char* path) {
@ -162,7 +162,7 @@ u32 DumpGbaVcSavegame(const char* path) {
ShowPrompt(false, "Out of memory."); ShowPrompt(false, "Out of memory.");
return 1; return 1;
} }
u32 ret = DumpGbaVcSavegameBuffered(path, buffer); u32 ret = DumpGbaVcSavegameBuffered(path, buffer);
free(buffer); free(buffer);
return ret; return ret;
@ -171,7 +171,7 @@ u32 DumpGbaVcSavegame(const char* path) {
u32 InjectGbaVcSavegameBuffered(const char* path, const char* path_vcsave, void* buffer) { u32 InjectGbaVcSavegameBuffered(const char* path, const char* path_vcsave, void* buffer) {
AgbSaveHeader* agbsave = (AgbSaveHeader*) 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
FILINFO fno; FILINFO fno;
char* ext = strrchr(path_vcsave, '.'); char* ext = strrchr(path_vcsave, '.');
@ -179,27 +179,27 @@ u32 InjectGbaVcSavegameBuffered(const char* path, const char* path_vcsave, void*
(strncasecmp(ext+1, "SaveRAM", 8) != 0))) return 1; // bad extension (strncasecmp(ext+1, "SaveRAM", 8) != 0))) return 1; // bad extension
if ((fvx_stat(path_vcsave, &fno) != FR_OK) || !GBASAVE_VALID(fno.fsize)) if ((fvx_stat(path_vcsave, &fno) != FR_OK) || !GBASAVE_VALID(fno.fsize))
return 1; // bad size return 1; // bad size
// read AGBsave header to memory // read AGBsave header to memory
if ((fvx_qread(path, agbsave, 0, sizeof(AgbSaveHeader), NULL) != FR_OK) || if ((fvx_qread(path, agbsave, 0, sizeof(AgbSaveHeader), NULL) != FR_OK) ||
(ValidateAgbSaveHeader(agbsave) != 0)) return 1; // not a proper header (ValidateAgbSaveHeader(agbsave) != 0)) return 1; // not a proper header
// read savegame to memory // read savegame to memory
u32 inject_save_size = min(agbsave->save_size, fno.fsize); u32 inject_save_size = min(agbsave->save_size, fno.fsize);
memset(savegame, 0xFF, agbsave->save_size); // pad with 0xFF memset(savegame, 0xFF, agbsave->save_size); // pad with 0xFF
if (fvx_qread(path_vcsave, savegame, 0, inject_save_size, NULL) != FR_OK) return 1; if (fvx_qread(path_vcsave, savegame, 0, inject_save_size, NULL) != FR_OK) return 1;
// byteswap for eeprom type saves (512 byte / 8 kB) // byteswap for eeprom type saves (512 byte / 8 kB)
if ((agbsave->save_size == GBASAVE_EEPROM_512) || (agbsave->save_size == GBASAVE_EEPROM_8K)) { if ((agbsave->save_size == GBASAVE_EEPROM_512) || (agbsave->save_size == GBASAVE_EEPROM_8K)) {
for (u8* ptr = savegame; (ptr - savegame) < (int) inject_save_size; ptr += 8) for (u8* ptr = savegame; (ptr - savegame) < (int) inject_save_size; ptr += 8)
*(u64*) (void*) ptr = getbe64(ptr); *(u64*) (void*) ptr = getbe64(ptr);
} }
// fix CMAC for NAND partition, rewrite AGBSAVE file // fix CMAC for NAND partition, rewrite AGBSAVE file
u32 data_size = sizeof(AgbSaveHeader) + agbsave->save_size; u32 data_size = sizeof(AgbSaveHeader) + agbsave->save_size;
if (FixAgbSaveCmac(agbsave, NULL, NULL) != 0) return 1; if (FixAgbSaveCmac(agbsave, NULL, NULL) != 0) return 1;
if (fvx_qwrite(path, agbsave, 0, data_size, NULL) != FR_OK) return 1; // write fail if (fvx_qwrite(path, agbsave, 0, data_size, NULL) != FR_OK) return 1; // write fail
// fix CMAC for SD partition, take it over to SD // fix CMAC for SD partition, take it over to SD
if (strncasecmp(path, "S:/agbsave.bin", 256) == 0) { if (strncasecmp(path, "S:/agbsave.bin", 256) == 0) {
AgbSaveHeader agbsave_sd; AgbSaveHeader agbsave_sd;
@ -217,18 +217,18 @@ u32 InjectGbaVcSavegameBuffered(const char* path, const char* path_vcsave, void*
(ValidateAgbSaveHeader(&agbsave_sd) == 0) && (ValidateAgbSaveHeader(&agbsave_sd) == 0) &&
(agbsave->times_saved == agbsave_sd.times_saved)) (agbsave->times_saved == agbsave_sd.times_saved))
slot = data_size; // proper slot is bottom slot (otherwise it's the top slot) slot = data_size; // proper slot is bottom slot (otherwise it's the top slot)
// inject next slot // inject next slot
agbsave->times_saved++; // increase # of times saved agbsave->times_saved++; // increase # of times saved
if (FixAgbSaveCmac(agbsave, NULL, path_sd) != 0) return 1; if (FixAgbSaveCmac(agbsave, NULL, path_sd) != 0) return 1;
if (fvx_qwrite(path_sd, agbsave, slot, data_size, NULL) != FR_OK) return 1; // write fail if (fvx_qwrite(path_sd, agbsave, slot, data_size, NULL) != FR_OK) return 1; // write fail
} }
// 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;
return 0; return 0;
} }
u32 InjectGbaVcSavegame(const char* path, const char* path_vcsave) { u32 InjectGbaVcSavegame(const char* path, const char* path_vcsave) {
@ -237,7 +237,7 @@ u32 InjectGbaVcSavegame(const char* path, const char* path_vcsave) {
ShowPrompt(false, "Out of memory."); ShowPrompt(false, "Out of memory.");
return 1; return 1;
} }
u32 ret = InjectGbaVcSavegameBuffered(path, path_vcsave, buffer); u32 ret = InjectGbaVcSavegameBuffered(path, path_vcsave, buffer);
free(buffer); free(buffer);
return ret; return ret;
@ -246,54 +246,54 @@ u32 InjectGbaVcSavegame(const char* path, const char* path_vcsave) {
u32 RebuildNandNcsdHeader(NandNcsdHeader* ncsd) { u32 RebuildNandNcsdHeader(NandNcsdHeader* ncsd) {
// signature (retail or dev) // signature (retail or dev)
const u8* signature = (IS_DEVKIT) ? sig_nand_ncsd_dev : sig_nand_ncsd_retail; const u8* signature = (IS_DEVKIT) ? sig_nand_ncsd_dev : sig_nand_ncsd_retail;
// encrypted TWL MBR // encrypted TWL MBR
u8 twl_mbr_data[0x200] = { 0 }; u8 twl_mbr_data[0x200] = { 0 };
u8* twl_mbr = twl_mbr_data + (0x200 - sizeof(twl_mbr_std)); u8* twl_mbr = twl_mbr_data + (0x200 - sizeof(twl_mbr_std));
memcpy(twl_mbr, twl_mbr_std, sizeof(twl_mbr_std)); memcpy(twl_mbr, twl_mbr_std, sizeof(twl_mbr_std));
CryptNand(twl_mbr_data, 0, 1, 0x03); CryptNand(twl_mbr_data, 0, 1, 0x03);
// rebuild NAND header for console // rebuild NAND header for console
memset(ncsd, 0x00, sizeof(NandNcsdHeader)); memset(ncsd, 0x00, sizeof(NandNcsdHeader));
memcpy(ncsd->signature, signature, 0x100); // signature memcpy(ncsd->signature, signature, 0x100); // signature
memcpy(ncsd->twl_mbr, twl_mbr, 0x42); // TWL MBR memcpy(ncsd->twl_mbr, twl_mbr, 0x42); // TWL MBR
memcpy(ncsd->magic, "NCSD", 0x4); // magic number memcpy(ncsd->magic, "NCSD", 0x4); // magic number
ncsd->size = (IS_O3DS) ? 0x200000 : 0x280000; // total size ncsd->size = (IS_O3DS) ? 0x200000 : 0x280000; // total size
// TWL partition (0) // TWL partition (0)
ncsd->partitions_fs_type[0] = 0x01; ncsd->partitions_fs_type[0] = 0x01;
ncsd->partitions_crypto_type[0] = 0x01; ncsd->partitions_crypto_type[0] = 0x01;
ncsd->partitions[0].offset = 0x000000; ncsd->partitions[0].offset = 0x000000;
ncsd->partitions[0].size = 0x058800; ncsd->partitions[0].size = 0x058800;
// AGBSAVE partition (1) // AGBSAVE partition (1)
ncsd->partitions_fs_type[1] = 0x04; ncsd->partitions_fs_type[1] = 0x04;
ncsd->partitions_crypto_type[1] = 0x02; ncsd->partitions_crypto_type[1] = 0x02;
ncsd->partitions[1].offset = 0x058800; ncsd->partitions[1].offset = 0x058800;
ncsd->partitions[1].size = 0x000180; ncsd->partitions[1].size = 0x000180;
// FIRM0 partition (2) // FIRM0 partition (2)
ncsd->partitions_fs_type[2] = 0x03; ncsd->partitions_fs_type[2] = 0x03;
ncsd->partitions_crypto_type[2] = 0x02; ncsd->partitions_crypto_type[2] = 0x02;
ncsd->partitions[2].offset = 0x058980; ncsd->partitions[2].offset = 0x058980;
ncsd->partitions[2].size = 0x002000; ncsd->partitions[2].size = 0x002000;
// FIRM1 partition (3) // FIRM1 partition (3)
ncsd->partitions_fs_type[3] = 0x03; ncsd->partitions_fs_type[3] = 0x03;
ncsd->partitions_crypto_type[3] = 0x02; ncsd->partitions_crypto_type[3] = 0x02;
ncsd->partitions[3].offset = 0x05A980; ncsd->partitions[3].offset = 0x05A980;
ncsd->partitions[3].size = 0x002000; ncsd->partitions[3].size = 0x002000;
// CTR partition (4) // CTR partition (4)
ncsd->partitions_fs_type[4] = 0x01; ncsd->partitions_fs_type[4] = 0x01;
ncsd->partitions_crypto_type[4] = (IS_O3DS) ? 0x02 : 0x03; ncsd->partitions_crypto_type[4] = (IS_O3DS) ? 0x02 : 0x03;
ncsd->partitions[4].offset = 0x05C980; ncsd->partitions[4].offset = 0x05C980;
ncsd->partitions[4].size = (IS_O3DS) ? 0x17AE80 : 0x20F680; ncsd->partitions[4].size = (IS_O3DS) ? 0x17AE80 : 0x20F680;
// unknown stuff - whatever this is ¯\_(ツ)_/¯ // unknown stuff - whatever this is ¯\_(ツ)_/¯
ncsd->unknown[0x25] = 0x04; ncsd->unknown[0x25] = 0x04;
ncsd->unknown[0x2C] = 0x01; ncsd->unknown[0x2C] = 0x01;
// done // done
return 0; return 0;
} }
@ -301,13 +301,13 @@ u32 RebuildNandNcsdHeader(NandNcsdHeader* ncsd) {
u32 FixNandHeader(const char* path, bool check_size) { u32 FixNandHeader(const char* path, bool check_size) {
NandNcsdHeader ncsd; NandNcsdHeader ncsd;
if (RebuildNandNcsdHeader(&ncsd) != 0) return 1; if (RebuildNandNcsdHeader(&ncsd) != 0) return 1;
// safety check // safety check
FILINFO fno; FILINFO fno;
FSIZE_t min_size = check_size ? GetNandNcsdMinSizeSectors(&ncsd) * 0x200 : 0x200; FSIZE_t min_size = check_size ? GetNandNcsdMinSizeSectors(&ncsd) * 0x200 : 0x200;
if ((fvx_stat(path, &fno) != FR_OK) || (min_size > fno.fsize)) if ((fvx_stat(path, &fno) != FR_OK) || (min_size > fno.fsize))
return 1; return 1;
// inject to path // inject to path
if (!CheckWritePermissions(path)) return 1; if (!CheckWritePermissions(path)) return 1;
return (fvx_qwrite(path, &ncsd, 0x0, 0x200, NULL) == FR_OK) ? 0 : 1; return (fvx_qwrite(path, &ncsd, 0x0, 0x200, NULL) == FR_OK) ? 0 : 1;
@ -316,15 +316,15 @@ u32 FixNandHeader(const char* path, bool check_size) {
u32 ValidateNandDump(const char* path) { u32 ValidateNandDump(const char* path) {
NandPartitionInfo info; NandPartitionInfo info;
FIL file; FIL file;
// truncated path string // truncated path string
char pathstr[32 + 1]; char pathstr[32 + 1];
TruncateString(pathstr, path, 32, 8); TruncateString(pathstr, path, 32, 8);
// open file // open 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 1; return 1;
// check NAND header // check NAND header
NandNcsdHeader ncsd; NandNcsdHeader ncsd;
if ((ReadNandFile(&file, &ncsd, 0, 1, 0xFF) != 0) || (ValidateNandNcsdHeader(&ncsd) != 0)) { if ((ReadNandFile(&file, &ncsd, 0, 1, 0xFF) != 0) || (ValidateNandNcsdHeader(&ncsd) != 0)) {
@ -332,14 +332,14 @@ u32 ValidateNandDump(const char* path) {
fvx_close(&file); fvx_close(&file);
return 1; return 1;
} }
// check size // check size
if (fvx_size(&file) < (GetNandNcsdMinSizeSectors(&ncsd) * 0x200)) { if (fvx_size(&file) < (GetNandNcsdMinSizeSectors(&ncsd) * 0x200)) {
ShowPrompt(false, "%s\nNAND dump misses data", pathstr); ShowPrompt(false, "%s\nNAND dump misses data", pathstr);
fvx_close(&file); fvx_close(&file);
return 1; return 1;
} }
// check TWL & CTR FAT partitions // check TWL & CTR FAT partitions
for (u32 i = 0; i < 2; i++) { for (u32 i = 0; i < 2; i++) {
char* section_type = (i) ? "CTR" : "MBR"; char* section_type = (i) ? "CTR" : "MBR";
@ -366,11 +366,11 @@ 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); u8* firm = (u8*) malloc(FIRM_MAX_SIZE);
if (!firm) return 1; 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++) {
if (GetNandNcsdPartitionInfo(&info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, f, &ncsd) != 0) { if (GetNandNcsdPartitionInfo(&info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, f, &ncsd) != 0) {
@ -379,17 +379,17 @@ u32 ValidateNandDump(const char* path) {
free(firm); free(firm);
return 1; return 1;
} }
u32 firm_size = info.count * 0x200; u32 firm_size = info.count * 0x200;
if ((firm_size <= FIRM_MAX_SIZE) && if ((firm_size <= FIRM_MAX_SIZE) &&
(ReadNandFile(&file, firm, info.sector, info.count, info.keyslot) == 0) && (ReadNandFile(&file, firm, info.sector, info.count, info.keyslot) == 0) &&
(ValidateFirm(firm, firm_size, true) == 0)) (ValidateFirm(firm, firm_size, true) == 0))
break; break;
} }
free(firm); free(firm);
fvx_close(&file); fvx_close(&file);
return 0; return 0;
} }
@ -406,17 +406,17 @@ u32 SafeRestoreNandDump(const char* path) {
EmbedEssentialBackup("S:/nand.bin"); EmbedEssentialBackup("S:/nand.bin");
else return 1; else return 1;
} }
if (!ShowUnlockSequence(5, "!WARNING!\n \nProceeding will overwrite the\nSysNAND with the provided dump.\n \n(B9S/A9LH will be left intact.)")) if (!ShowUnlockSequence(5, "!WARNING!\n \nProceeding will overwrite the\nSysNAND with the provided dump.\n \n(B9S/A9LH will be left intact.)"))
return 1; return 1;
if (!SetWritePermissions(PERM_SYS_LVL1, true)) return 1; if (!SetWritePermissions(PERM_SYS_LVL1, true)) return 1;
// open file, get size // open file, get size
FIL file; FIL 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 1; return 1;
u32 fsize = fvx_size(&file); u32 fsize = fvx_size(&file);
// get NCSD headers from image and SysNAND // get NCSD headers from image and SysNAND
NandNcsdHeader ncsd_loc, ncsd_img; NandNcsdHeader ncsd_loc, ncsd_img;
MbrHeader twl_mbr_img; MbrHeader twl_mbr_img;
@ -426,7 +426,7 @@ u32 SafeRestoreNandDump(const char* path) {
fvx_close(&file); fvx_close(&file);
return 1; return 1;
} }
// compare NCSD header partitioning // compare NCSD header partitioning
// FIRMS must be at the same place for image and local NAND // FIRMS must be at the same place for image and local NAND
bool header_inject = false; bool header_inject = false;
@ -452,7 +452,7 @@ u32 SafeRestoreNandDump(const char* path) {
return 1; return 1;
} }
} }
// additional warning for elevated write permissions // additional warning for elevated write permissions
if (header_inject) { if (header_inject) {
if (!ShowPrompt(true, "!WARNING!\n \nNCSD differs between image and local,\nelevated write permissions required\n \nProceed on your own risk?") || if (!ShowPrompt(true, "!WARNING!\n \nNCSD differs between image and local,\nelevated write permissions required\n \nProceed on your own risk?") ||
@ -461,13 +461,13 @@ u32 SafeRestoreNandDump(const char* path) {
return 1; return 1;
} }
} }
u8* buffer = (u8*) malloc(STD_BUFFER_SIZE); u8* buffer = (u8*) malloc(STD_BUFFER_SIZE);
if (!buffer) { if (!buffer) {
fvx_close(&file); fvx_close(&file);
return 1; 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
@ -487,22 +487,22 @@ u32 SafeRestoreNandDump(const char* path) {
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); 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
if (header_inject && (ret == 0) && if (header_inject && (ret == 0) &&
(WriteNandSectors((u8*) &ncsd_img, 0, 1, 0xFF, NAND_SYSNAND) != 0)) (WriteNandSectors((u8*) &ncsd_img, 0, 1, 0xFF, NAND_SYSNAND) != 0))
ret = 1; ret = 1;
return ret; return ret;
} }
u32 SafeInstallFirmBuffered(const char* path, u32 slots, u8* buffer, u32 bufsiz) { 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 = buffer; u8* firm = buffer;
UINT firm_size; UINT firm_size;
@ -512,12 +512,12 @@ u32 SafeInstallFirmBuffered(const char* path, u32 slots, u8* buffer, u32 bufsiz)
"%s\nNot a installable FIRM." : "%s\nFIRM load/verify error.", pathstr); "%s\nNot a installable FIRM." : "%s\nFIRM load/verify error.", pathstr);
return 1; return 1;
} }
// inject sighax signature, get hash // inject sighax signature, get hash
u8 firm_sha[0x20]; u8 firm_sha[0x20];
memcpy(firm + 0x100, (IS_DEVKIT) ? sig_nand_firm_dev : sig_nand_firm_retail, 0x100); memcpy(firm + 0x100, (IS_DEVKIT) ? sig_nand_firm_dev : sig_nand_firm_retail, 0x100);
sha_quick(firm_sha, firm, firm_size, SHA256_MODE); sha_quick(firm_sha, firm, firm_size, SHA256_MODE);
// check install slots // check install slots
for (u32 s = 0; s < 8; s++) { for (u32 s = 0; s < 8; s++) {
NandPartitionInfo info; NandPartitionInfo info;
@ -528,7 +528,7 @@ u32 SafeInstallFirmBuffered(const char* path, u32 slots, u8* buffer, u32 bufsiz)
return 1; return 1;
} }
} }
// check sector 0x96 on N3DS, offer fix if required // check sector 0x96 on N3DS, offer fix if required
u8 sector0x96[0x200] __attribute__((aligned(4))); u8 sector0x96[0x200] __attribute__((aligned(4)));
bool fix_sector0x96 = false; bool fix_sector0x96 = false;
@ -552,11 +552,11 @@ u32 SafeInstallFirmBuffered(const char* path, u32 slots, u8* buffer, u32 bufsiz)
fix_sector0x96 = true; fix_sector0x96 = true;
} else return 1; } else return 1;
} }
// all checked, ready to go // all checked, ready to go
if (!ShowUnlockSequence(6, "!WARNING!\n \nProceeding will install the\nprovided FIRM to the SysNAND\nand inject sighax.\n \nInstalling an unsupported FIRM\nwill BRICK your console!")) return 1; if (!ShowUnlockSequence(6, "!WARNING!\n \nProceeding will install the\nprovided FIRM to the SysNAND\nand inject sighax.\n \nInstalling an unsupported FIRM\nwill BRICK your console!")) return 1;
// if (!SetWritePermissions(PERM_SYS_LVL3, true)) return 1; // one unlock sequence is enough // if (!SetWritePermissions(PERM_SYS_LVL3, true)) return 1; // one unlock sequence is enough
// point of no return // point of no return
ShowString(false, "Installing FIRM, please wait..."); ShowString(false, "Installing FIRM, please wait...");
if (fix_sector0x96 && (WriteNandSectors(sector0x96, 0x96, 1, 0x11, NAND_SYSNAND) != 0)) { if (fix_sector0x96 && (WriteNandSectors(sector0x96, 0x96, 1, 0x11, NAND_SYSNAND) != 0)) {
@ -572,7 +572,7 @@ u32 SafeInstallFirmBuffered(const char* path, u32 slots, u8* buffer, u32 bufsiz)
return 1; return 1;
} }
} }
// done, now check the installation // done, now check the installation
ShowString(false, "Checking installation, please wait..."); ShowString(false, "Checking installation, please wait...");
if (fix_sector0x96 && ((ReadNandSectors(sector0x96, 0x96, 1, 0x11, NAND_SYSNAND) != 0) || if (fix_sector0x96 && ((ReadNandSectors(sector0x96, 0x96, 1, 0x11, NAND_SYSNAND) != 0) ||
@ -590,7 +590,7 @@ u32 SafeInstallFirmBuffered(const char* path, u32 slots, u8* buffer, u32 bufsiz)
return 1; return 1;
} }
} }
return 0; return 0;
} }
@ -600,7 +600,7 @@ u32 SafeInstallFirm(const char* path, u32 slots) {
ShowPrompt(false, "Out of memory."); ShowPrompt(false, "Out of memory.");
return 1; return 1;
} }
u32 ret = SafeInstallFirmBuffered(path, slots, buffer, FIRM_MAX_SIZE); u32 ret = SafeInstallFirmBuffered(path, slots, buffer, FIRM_MAX_SIZE);
free(buffer); free(buffer);
return ret; return ret;
@ -609,29 +609,29 @@ u32 SafeInstallFirm(const char* path, u32 slots) {
u32 SafeInstallKeyDb(const char* path) { u32 SafeInstallKeyDb(const char* path) {
static const u8 perfect_sha[] = { KEYDB_PERFECT_HASH }; static const u8 perfect_sha[] = { KEYDB_PERFECT_HASH };
u8 keydb[KEYDB_PERFECT_SIZE] __attribute__((aligned(4))); u8 keydb[KEYDB_PERFECT_SIZE] __attribute__((aligned(4)));
char pathstr[32 + 1]; // truncated path string char pathstr[32 + 1]; // truncated path string
TruncateString(pathstr, path, 32, 8); TruncateString(pathstr, path, 32, 8);
// already installed? // already installed?
if ((ReadNandBytes(keydb, SECTOR_KEYDB*0x200, KEYDB_PERFECT_SIZE, 0xFF, NAND_SYSNAND) == 0) && if ((ReadNandBytes(keydb, SECTOR_KEYDB*0x200, KEYDB_PERFECT_SIZE, 0xFF, NAND_SYSNAND) == 0) &&
(sha_cmp(perfect_sha, keydb, KEYDB_PERFECT_SIZE, SHA256_MODE) == 0)) { (sha_cmp(perfect_sha, keydb, KEYDB_PERFECT_SIZE, SHA256_MODE) == 0)) {
ShowPrompt(false, "Perfect " KEYDB_NAME " is already installed!"); ShowPrompt(false, "Perfect " KEYDB_NAME " is already installed!");
return 1; return 1;
} }
// check input path... // check input path...
if ((fvx_qread(path, keydb, 0, KEYDB_PERFECT_SIZE, NULL) != FR_OK) || if ((fvx_qread(path, keydb, 0, KEYDB_PERFECT_SIZE, NULL) != FR_OK) ||
(sha_cmp(perfect_sha, keydb, KEYDB_PERFECT_SIZE, SHA256_MODE) != 0)) { (sha_cmp(perfect_sha, keydb, KEYDB_PERFECT_SIZE, SHA256_MODE) != 0)) {
ShowPrompt(false, "%s\nNot a perfect " KEYDB_NAME " image.\nCannot install to NAND!", pathstr); ShowPrompt(false, "%s\nNot a perfect " KEYDB_NAME " image.\nCannot install to NAND!", pathstr);
return 1; return 1;
} }
// point of no return, install key database // point of no return, install key database
if (WriteNandBytes(keydb, SECTOR_KEYDB*0x200, KEYDB_PERFECT_SIZE, 0xFF, NAND_SYSNAND) != 0) { if (WriteNandBytes(keydb, SECTOR_KEYDB*0x200, KEYDB_PERFECT_SIZE, 0xFF, NAND_SYSNAND) != 0) {
ShowPrompt(false, "%s\nFailed writing " KEYDB_NAME " to NAND!", pathstr); ShowPrompt(false, "%s\nFailed writing " KEYDB_NAME " to NAND!", pathstr);
return 1; return 1;
} }
return 0; return 0;
} }

View File

@ -81,7 +81,7 @@ void Paint9_DrawBrush(u16 px, u16 py, u32 color_fg, u32 color_bg, u32 id) {
// https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#C // https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#C
void Paint9_DrawLine(u16 px0, u16 py0, u16 px1, u16 py1, u32 color_fg, u32 id) { void Paint9_DrawLine(u16 px0, u16 py0, u16 px1, u16 py1, u32 color_fg, u32 id) {
const int dx = abs((s16) px1-px0), sx = (px0 < px1) ? 1 : -1; const int dx = abs((s16) px1-px0), sx = (px0 < px1) ? 1 : -1;
const int dy = abs((s16) py1-py0), sy = (py0 < py1) ? 1 : -1; const int dy = abs((s16) py1-py0), sy = (py0 < py1) ? 1 : -1;
int err = ((dx > dy) ? dx : -dy) / 2; int err = ((dx > dy) ? dx : -dy) / 2;
while (true) { while (true) {

View File

@ -232,7 +232,7 @@ static inline bool isntrboot(void) {
static inline u32 strntohex(const char* str, u8* hex, u32 len) { static inline u32 strntohex(const char* str, u8* hex, u32 len) {
if (!len) { if (!len) {
len = strlen(str); len = strlen(str);
if (len%1) return 0; if (len%1) return 0;
else len >>= 1; else len >>= 1;
} else if (len*2 != strnlen(str, (len*2)+1)) { } else if (len*2 != strnlen(str, (len*2)+1)) {
@ -285,28 +285,28 @@ static inline u32 line_len(const char* text, u32 len, u32 ww, const char* line,
} }
static inline char* line_seek(const char* text, u32 len, u32 ww, const char* line, int add) { static inline char* line_seek(const char* text, u32 len, u32 ww, const char* line, int add) {
// safety checks / // safety checks /
if (line < text) return NULL; if (line < text) return NULL;
if ((line >= (text + len)) && (add >= 0)) return (char*) line; if ((line >= (text + len)) && (add >= 0)) return (char*) line;
if (!ww) { // non wordwrapped mode if (!ww) { // non wordwrapped mode
char* lf = ((char*) line - 1); char* lf = ((char*) line - 1);
// ensure we are at the start of the line // ensure we are at the start of the line
while ((lf > text) && (*lf != '\n')) lf--; while ((lf > text) && (*lf != '\n')) lf--;
// handle backwards search // handle backwards search
for (; (add < 0) && (lf >= text); add++) for (; (add < 0) && (lf >= text); add++)
for (lf--; (lf >= text) && (*lf != '\n'); lf--); for (lf--; (lf >= text) && (*lf != '\n'); lf--);
// handle forwards search // handle forwards search
for (; (add > 0) && (lf < text + len); add--) for (; (add > 0) && (lf < text + len); add--)
for (lf++; (lf < text + len) && (*lf != '\n'); lf++); for (lf++; (lf < text + len) && (*lf != '\n'); lf++);
return lf + 1; return lf + 1;
} else { // wordwrapped mode } else { // wordwrapped mode
char* l0 = (char*) line; char* l0 = (char*) line;
// handle forwards wordwrapped search // handle forwards wordwrapped search
for (; (add > 0) && (l0 < text + len); add--) { for (; (add > 0) && (l0 < text + len); add--) {
char* eol = NULL; char* eol = NULL;
@ -314,7 +314,7 @@ static inline char* line_seek(const char* text, u32 len, u32 ww, const char* lin
if (eol || !llenww) l0 = line_seek(text, len, 0, l0, 1); if (eol || !llenww) l0 = line_seek(text, len, 0, l0, 1);
else l0 += llenww; else l0 += llenww;
} }
// handle backwards wordwrapped search // handle backwards wordwrapped search
while ((add < 0) && (l0 > text)) { while ((add < 0) && (l0 > text)) {
char* l1 = line_seek(text, len, 0, l0, -1); char* l1 = line_seek(text, len, 0, l0, -1);
@ -331,19 +331,19 @@ static inline char* line_seek(const char* text, u32 len, u32 ww, const char* lin
} }
} }
return l0; return l0;
} }
} }
static inline u32 get_lno(const char* text, u32 len, const char* line) { static inline u32 get_lno(const char* text, u32 len, const char* line) {
u32 lno = 1; u32 lno = 1;
for (u32 i = 0; i < len; i++) { for (u32 i = 0; i < len; i++) {
if (line <= text + i) return lno; if (line <= text + i) return lno;
else if (text[i] == '\n') lno++; else if (text[i] == '\n') lno++;
} }
return 0; return 0;
} }
@ -369,11 +369,11 @@ void set_preview(const char* name, const char* content) {
char* set_var(const char* name, const char* content) { char* set_var(const char* name, const char* content) {
Gm9ScriptVar* vars = (Gm9ScriptVar*) var_buffer; Gm9ScriptVar* vars = (Gm9ScriptVar*) var_buffer;
if ((strnlen(name, _VAR_NAME_LEN) > (_VAR_NAME_LEN-1)) || (strnlen(content, _VAR_CNT_LEN) > (_VAR_CNT_LEN-1)) || if ((strnlen(name, _VAR_NAME_LEN) > (_VAR_NAME_LEN-1)) || (strnlen(content, _VAR_CNT_LEN) > (_VAR_CNT_LEN-1)) ||
(strchr(name, '[') || strchr(name, ']'))) (strchr(name, '[') || strchr(name, ']')))
return NULL; return NULL;
u32 n_var = 0; u32 n_var = 0;
for (Gm9ScriptVar* var = vars; n_var < _VAR_MAX_BUFF; n_var++, var++) for (Gm9ScriptVar* var = vars; n_var < _VAR_MAX_BUFF; n_var++, var++)
if (!*(var->name) || (strncmp(var->name, name, _VAR_NAME_LEN) == 0)) break; if (!*(var->name) || (strncmp(var->name, name, _VAR_NAME_LEN) == 0)) break;
@ -383,10 +383,10 @@ char* set_var(const char* name, const char* content) {
strncpy(vars[n_var].content, content, _VAR_CNT_LEN); strncpy(vars[n_var].content, content, _VAR_CNT_LEN);
vars[n_var].content[_VAR_CNT_LEN - 1] = '\0'; vars[n_var].content[_VAR_CNT_LEN - 1] = '\0';
if (!n_var) *(vars[n_var].content) = '\0'; // NULL var if (!n_var) *(vars[n_var].content) = '\0'; // NULL var
// update preview stuff // update preview stuff
set_preview(name, content); set_preview(name, content);
return vars[n_var].content; return vars[n_var].content;
} }
@ -397,18 +397,18 @@ void upd_var(const char* name) {
u8 secinfo_data[1 + 1 + 16] = { 0 }; u8 secinfo_data[1 + 1 + 16] = { 0 };
char* env_serial = (char*) secinfo_data + 2; char* env_serial = (char*) secinfo_data + 2;
char env_region[3 + 1] = { 0 }; char env_region[3 + 1] = { 0 };
snprintf(env_region, 0x4, "UNK"); snprintf(env_region, 0x4, "UNK");
if ((FileGetData("1:/rw/sys/SecureInfo_A", secinfo_data, 0x11, 0x100) != 0x11) && if ((FileGetData("1:/rw/sys/SecureInfo_A", secinfo_data, 0x11, 0x100) != 0x11) &&
(FileGetData("1:/rw/sys/SecureInfo_B", secinfo_data, 0x11, 0x100) != 0x11)) (FileGetData("1:/rw/sys/SecureInfo_B", secinfo_data, 0x11, 0x100) != 0x11))
snprintf(env_serial, 0xF, "UNKNOWN"); snprintf(env_serial, 0xF, "UNKNOWN");
else if (*secinfo_data < SMDH_NUM_REGIONS) else if (*secinfo_data < SMDH_NUM_REGIONS)
strncpy(env_region, g_regionNamesShort[*secinfo_data], countof(env_region) - 1); strncpy(env_region, g_regionNamesShort[*secinfo_data], countof(env_region) - 1);
set_var("SERIAL", env_serial); set_var("SERIAL", env_serial);
set_var("REGION", env_region); set_var("REGION", env_region);
} }
// device sysnand / emunand id0 // device sysnand / emunand id0
for (u32 emu = 0; emu <= 1; emu++) { for (u32 emu = 0; emu <= 1; emu++) {
const char* env_id0_name = (emu) ? "EMUID0" : "SYSID0"; const char* env_id0_name = (emu) ? "EMUID0" : "SYSID0";
@ -425,7 +425,7 @@ void upd_var(const char* name) {
set_var(env_id0_name, env_id0); set_var(env_id0_name, env_id0);
} }
} }
// datestamp & timestamp // datestamp & timestamp
if (!name || (strncmp(name, "DATESTAMP", _VAR_NAME_LEN) == 0) || (strncmp(name, "TIMESTAMP", _VAR_NAME_LEN) == 0)) { if (!name || (strncmp(name, "DATESTAMP", _VAR_NAME_LEN) == 0) || (strncmp(name, "TIMESTAMP", _VAR_NAME_LEN) == 0)) {
DsTime dstime; DsTime dstime;
@ -449,7 +449,7 @@ void upd_var(const char* name) {
char* get_var(const char* name, char** endptr) { char* get_var(const char* name, char** endptr) {
Gm9ScriptVar* vars = (Gm9ScriptVar*) var_buffer; Gm9ScriptVar* vars = (Gm9ScriptVar*) var_buffer;
u32 name_len = 0; u32 name_len = 0;
char* pname = NULL; char* pname = NULL;
if (!endptr) { // no endptr, varname is verbatim if (!endptr) { // no endptr, varname is verbatim
@ -462,26 +462,26 @@ char* get_var(const char* name, char** endptr) {
if ((name_len >= _VAR_NAME_LEN) || !pname[name_len]) return NULL; if ((name_len >= _VAR_NAME_LEN) || !pname[name_len]) return NULL;
*endptr = pname + name_len + 1; *endptr = pname + name_len + 1;
} }
char vname[_VAR_NAME_LEN]; char vname[_VAR_NAME_LEN];
strncpy(vname, pname, name_len); strncpy(vname, pname, name_len);
vname[name_len] = '\0'; vname[name_len] = '\0';
upd_var(vname); // handle dynamic env vars upd_var(vname); // handle dynamic env vars
u32 n_var = 0; u32 n_var = 0;
for (Gm9ScriptVar* var = vars; n_var < _VAR_MAX_BUFF; n_var++, var++) { for (Gm9ScriptVar* var = vars; n_var < _VAR_MAX_BUFF; n_var++, var++) {
if (!*(var->name) || (strncmp(var->name, vname, _VAR_NAME_LEN) == 0)) break; if (!*(var->name) || (strncmp(var->name, vname, _VAR_NAME_LEN) == 0)) break;
} }
if (n_var >= _VAR_MAX_BUFF || !*(vars[n_var].name)) n_var = 0; if (n_var >= _VAR_MAX_BUFF || !*(vars[n_var].name)) n_var = 0;
return vars[n_var].content; return vars[n_var].content;
} }
bool init_vars(const char* path_script) { bool init_vars(const char* path_script) {
// reset var buffer // reset var buffer
memset(var_buffer, 0x00, sizeof(Gm9ScriptVar) * _VAR_MAX_BUFF); memset(var_buffer, 0x00, sizeof(Gm9ScriptVar) * _VAR_MAX_BUFF);
// current path // current path
char curr_dir[_VAR_CNT_LEN]; char curr_dir[_VAR_CNT_LEN];
if (path_script) { if (path_script) {
@ -490,9 +490,9 @@ bool init_vars(const char* path_script) {
char* slash = strrchr(curr_dir, '/'); char* slash = strrchr(curr_dir, '/');
if (slash) *slash = '\0'; if (slash) *slash = '\0';
} else strncpy(curr_dir, "(null)", _VAR_CNT_LEN - 1); } else strncpy(curr_dir, "(null)", _VAR_CNT_LEN - 1);
// set env vars // set env vars
set_var("NULL", ""); // this one is special and should not be changed later set_var("NULL", ""); // this one is special and should not be changed later
set_var("CURRDIR", curr_dir); // script path, never changes set_var("CURRDIR", curr_dir); // script path, never changes
set_var("GM9OUT", OUTPUT_PATH); // output path, never changes set_var("GM9OUT", OUTPUT_PATH); // output path, never changes
set_var("HAX", IS_UNLOCKED ? (isntrboot() ? "ntrboot" : "sighax") : ""); // type of hax running from set_var("HAX", IS_UNLOCKED ? (isntrboot() ? "ntrboot" : "sighax") : ""); // type of hax running from
@ -501,17 +501,17 @@ bool init_vars(const char* path_script) {
char* ptr = set_var("GM9VER", VERSION); // GodMode9 version, truncated below char* ptr = set_var("GM9VER", VERSION); // GodMode9 version, truncated below
while (*(ptr++) != '\0') if (*ptr == '-') *ptr = '\0'; while (*(ptr++) != '\0') if (*ptr == '-') *ptr = '\0';
upd_var(NULL); // set all dynamic environment vars upd_var(NULL); // set all dynamic environment vars
return true; return true;
} }
bool expand_arg(char* argex, const char* arg, u32 len) { bool expand_arg(char* argex, const char* arg, u32 len) {
char* out = argex; char* out = argex;
for (char* in = (char*) arg; in - arg < (int) len; in++) { for (char* in = (char*) arg; in - arg < (int) len; in++) {
u32 out_len = out - argex; u32 out_len = out - argex;
if (out_len >= (_ARG_MAX_LEN-1)) return false; // maximum arglen reached if (out_len >= (_ARG_MAX_LEN-1)) return false; // maximum arglen reached
if (*in == '\\') { // escape line breaks if (*in == '\\') { // escape line breaks
if (*(++in) == 'n') *(out++) = '\n'; if (*(++in) == 'n') *(out++) = '\n';
else { else {
@ -529,20 +529,20 @@ bool expand_arg(char* argex, const char* arg, u32 len) {
} else *(out++) = *in; } else *(out++) = *in;
} }
*out = '\0'; *out = '\0';
return true; return true;
} }
cmd_id get_cmd_id(char* cmd, u32 len, u32 flags, u32 argc, char* err_str) { cmd_id get_cmd_id(char* cmd, u32 len, u32 flags, u32 argc, char* err_str) {
const Gm9ScriptCmd* cmd_entry = NULL; const Gm9ScriptCmd* cmd_entry = NULL;
for (u32 i = 0; i < (sizeof(cmd_list)/sizeof(Gm9ScriptCmd)); i++) { for (u32 i = 0; i < (sizeof(cmd_list)/sizeof(Gm9ScriptCmd)); i++) {
if (strncmp(cmd_list[i].cmd, cmd, len) == 0) { if (strncmp(cmd_list[i].cmd, cmd, len) == 0) {
cmd_entry = cmd_list + i; cmd_entry = cmd_list + i;
break; break;
} }
} }
if (!cmd_entry) { if (!cmd_entry) {
if (err_str) snprintf(err_str, _ERR_STR_LEN, "unknown cmd"); if (err_str) snprintf(err_str, _ERR_STR_LEN, "unknown cmd");
} else if (cmd_entry->n_args != argc) { } else if (cmd_entry->n_args != argc) {
@ -550,13 +550,13 @@ cmd_id get_cmd_id(char* cmd, u32 len, u32 flags, u32 argc, char* err_str) {
} else if (~(cmd_entry->allowed_flags|_FLG('o')|_FLG('s')) & flags) { } else if (~(cmd_entry->allowed_flags|_FLG('o')|_FLG('s')) & flags) {
if (err_str) snprintf(err_str, _ERR_STR_LEN, "unrecognized flags"); if (err_str) snprintf(err_str, _ERR_STR_LEN, "unrecognized flags");
} else return cmd_entry->id; } else return cmd_entry->id;
return CMD_ID_NONE; return CMD_ID_NONE;
} }
u32 get_flag(char* str, u32 len, char* err_str) { u32 get_flag(char* str, u32 len, char* err_str) {
char flag_char = '\0'; char flag_char = '\0';
if ((len < 2) || (*str != '-')) flag_char = '\0'; if ((len < 2) || (*str != '-')) flag_char = '\0';
else if (len == 2) flag_char = str[1]; else if (len == 2) flag_char = str[1];
else if (strncmp(str, "--all", len) == 0) flag_char = 'a'; else if (strncmp(str, "--all", len) == 0) flag_char = 'a';
@ -577,23 +577,23 @@ u32 get_flag(char* str, u32 len, char* err_str) {
else if (strncmp(str, "--unequal", len) == 0) flag_char = 'u'; else if (strncmp(str, "--unequal", len) == 0) flag_char = 'u';
else if (strncmp(str, "--overwrite", len) == 0) flag_char = 'w'; else if (strncmp(str, "--overwrite", len) == 0) flag_char = 'w';
else if (strncmp(str, "--explorer", len) == 0) flag_char = 'x'; else if (strncmp(str, "--explorer", len) == 0) flag_char = 'x';
if ((flag_char < 'a') && (flag_char > 'z')) { if ((flag_char < 'a') && (flag_char > 'z')) {
if (err_str) snprintf(err_str, _ERR_STR_LEN, "illegal flag"); if (err_str) snprintf(err_str, _ERR_STR_LEN, "illegal flag");
return 0; return 0;
} }
return _FLG(flag_char); return _FLG(flag_char);
} }
char* get_string(char* ptr, const char* line_end, u32* len, char** next, char* err_str) { char* get_string(char* ptr, const char* line_end, u32* len, char** next, char* err_str) {
char* str = NULL; char* str = NULL;
*len = 0; *len = 0;
// skip whitespaces // skip whitespaces
for (; IS_WHITESPACE(*ptr) && (ptr < line_end); ptr++); for (; IS_WHITESPACE(*ptr) && (ptr < line_end); ptr++);
if (ptr >= line_end) return (*next = (char*) line_end); // end reached, all whitespaces if (ptr >= line_end) return (*next = (char*) line_end); // end reached, all whitespaces
// handle string // handle string
if (*ptr == '\"') { // quotes if (*ptr == '\"') { // quotes
str = ++ptr; str = ++ptr;
@ -604,11 +604,11 @@ char* get_string(char* ptr, const char* line_end, u32* len, char** next, char* e
} }
*next = ptr + 1; *next = ptr + 1;
} else { // no quotes, no whitespace } else { // no quotes, no whitespace
str = ptr; str = ptr;
for (; !IS_WHITESPACE(*ptr) && (ptr < line_end); ptr++, (*len)++); for (; !IS_WHITESPACE(*ptr) && (ptr < line_end); ptr++, (*len)++);
*next = ptr; *next = ptr;
} }
return str; return str;
} }
@ -618,16 +618,16 @@ char* skip_block(char* ptr, bool ignore_else, bool stop_after_end) {
char* line_start = ptr; char* line_start = ptr;
char* line_end = strchr(ptr, '\n'); char* line_end = strchr(ptr, '\n');
if (!line_end) line_end = ptr + strlen(ptr); if (!line_end) line_end = ptr + strlen(ptr);
// grab first string // grab first string
char* str = NULL; char* str = NULL;
u32 str_len = 0; u32 str_len = 0;
if (!(str = get_string(ptr, line_end, &str_len, &ptr, NULL)) || (str >= line_end)) { if (!(str = get_string(ptr, line_end, &str_len, &ptr, NULL)) || (str >= line_end)) {
// string error or empty line // string error or empty line
ptr = line_end + 1; ptr = line_end + 1;
continue; continue;
} }
// check string // check string
if (MATCH_STR(str, str_len, _CMD_END)) { // stop at end if (MATCH_STR(str, str_len, _CMD_END)) { // stop at end
return line_start; // end of block found return line_start; // end of block found
@ -638,19 +638,19 @@ char* skip_block(char* ptr, bool ignore_else, bool stop_after_end) {
} else if (MATCH_STR(str, str_len, _CMD_IF)) { } else if (MATCH_STR(str, str_len, _CMD_IF)) {
ptr = line_start = skip_block(line_end + 1, true, false); ptr = line_start = skip_block(line_end + 1, true, false);
if (ptr == NULL) return NULL; if (ptr == NULL) return NULL;
line_end = strchr(ptr, '\n'); line_end = strchr(ptr, '\n');
if (!line_end) line_end = ptr + strlen(ptr); if (!line_end) line_end = ptr + strlen(ptr);
str = get_string(ptr, line_end, &str_len, &ptr, NULL); str = get_string(ptr, line_end, &str_len, &ptr, NULL);
if (!(MATCH_STR(str, str_len, _CMD_END))) return NULL; if (!(MATCH_STR(str, str_len, _CMD_END))) return NULL;
if (stop_after_end) return line_end + 1; if (stop_after_end) return line_end + 1;
} }
// move on to the next line // move on to the next line
ptr = line_end + 1; ptr = line_end + 1;
} }
// end of block not found // end of block not found
return NULL; return NULL;
} }
@ -661,16 +661,16 @@ char* find_next(char* ptr) {
char* line_start = ptr; char* line_start = ptr;
char* line_end = strchr(ptr, '\n'); char* line_end = strchr(ptr, '\n');
if (!line_end) line_end = ptr + strlen(ptr); if (!line_end) line_end = ptr + strlen(ptr);
// grab first string // grab first string
char* str = NULL; char* str = NULL;
u32 str_len = 0; u32 str_len = 0;
if (!(str = get_string(ptr, line_end, &str_len, &ptr, NULL)) || (str >= line_end)) { if (!(str = get_string(ptr, line_end, &str_len, &ptr, NULL)) || (str >= line_end)) {
// string error or empty line // string error or empty line
ptr = line_end + 1; ptr = line_end + 1;
continue; continue;
} }
// check string // check string
if (MATCH_STR(str, str_len, _CMD_IF)) { // skip 'if' blocks if (MATCH_STR(str, str_len, _CMD_IF)) { // skip 'if' blocks
ptr = skip_block(ptr, true, true); ptr = skip_block(ptr, true, true);
@ -679,11 +679,11 @@ char* find_next(char* ptr) {
} else if (MATCH_STR(str, str_len, _CMD_NEXT)) { } else if (MATCH_STR(str, str_len, _CMD_NEXT)) {
return line_start; return line_start;
} }
// move on to the next line // move on to the next line
ptr = line_end + 1; ptr = line_end + 1;
} }
// 'next' not found // 'next' not found
return NULL; return NULL;
} }
@ -692,13 +692,13 @@ char* find_label(const char* label, const char* last_found) {
char* script = (char*) script_buffer; char* script = (char*) script_buffer;
char* ptr = script; char* ptr = script;
u32 label_len = strnlen(label, _ARG_MAX_LEN); u32 label_len = strnlen(label, _ARG_MAX_LEN);
if (last_found) { if (last_found) {
ptr = strchr(last_found, '\n'); ptr = strchr(last_found, '\n');
if (!ptr) return NULL; if (!ptr) return NULL;
ptr++; ptr++;
} }
char* next = ptr; char* next = ptr;
for (; next && *ptr; ptr = next) { for (; next && *ptr; ptr = next) {
// store line start / get line end // store line start / get line end
@ -706,27 +706,27 @@ char* find_label(const char* label, const char* last_found) {
char* line_end = strchr(ptr, '\n'); char* line_end = strchr(ptr, '\n');
if (!line_end) line_end = ptr + strlen(ptr); if (!line_end) line_end = ptr + strlen(ptr);
next = line_end + 1; next = line_end + 1;
// search for label // search for label
char* str = NULL; char* str = NULL;
u32 str_len = 0; u32 str_len = 0;
if (!(str = get_string(ptr, line_end, &str_len, &ptr, NULL))) continue; // string error, ignore line if (!(str = get_string(ptr, line_end, &str_len, &ptr, NULL))) continue; // string error, ignore line
else if (str >= line_end) continue; // empty line else if (str >= line_end) continue; // empty line
if (*str == '@') { if (*str == '@') {
// label found // label found
str++; str_len--; str++; str_len--;
// compare it manually (also check for '*' at end) // compare it manually (also check for '*' at end)
u32 pdiff = 0; u32 pdiff = 0;
for (; (pdiff < str_len) && (label[pdiff] == str[pdiff]); pdiff++); for (; (pdiff < str_len) && (label[pdiff] == str[pdiff]); pdiff++);
if ((pdiff < label_len) && (label[pdiff] != '*')) continue; // no match if ((pdiff < label_len) && (label[pdiff] != '*')) continue; // no match
// otherwise: potential regular or wildcard match // otherwise: potential regular or wildcard match
// may be a match, see if there are more strings after it // may be a match, see if there are more strings after it
if (!(str = get_string(ptr, line_end, &str_len, &ptr, NULL))) continue; // string error, ignore line if (!(str = get_string(ptr, line_end, &str_len, &ptr, NULL))) continue; // string error, ignore line
else if ((str < line_end) && (*str != '#')) continue; // neither end of line nor comment else if ((str < line_end) && (*str != '#')) continue; // neither end of line nor comment
return line_start; // match found return line_start; // match found
} else if (MATCH_STR(str, str_len, _CMD_IF)) { } else if (MATCH_STR(str, str_len, _CMD_IF)) {
next = skip_block(line_start, true, true); next = skip_block(line_start, true, true);
@ -734,7 +734,7 @@ char* find_label(const char* label, const char* last_found) {
next = find_next(line_start); next = find_next(line_start);
} // otherwise: irrelevant line } // otherwise: irrelevant line
} }
return NULL; return NULL;
} }
@ -744,13 +744,13 @@ bool for_handler(char* path, const char* dir, const char* pattern, bool recursiv
static char ldir[256]; static char ldir[256];
static char lpattern[64]; static char lpattern[64];
static bool rec = false; static bool rec = false;
if (!path && !dir && !pattern) { // close all dirs if (!path && !dir && !pattern) { // close all dirs
while (dp >= fdir) fvx_closedir(dp--); while (dp >= fdir) fvx_closedir(dp--);
dp = NULL; dp = NULL;
return true; return true;
} }
if (dir) { // open a dir if (dir) { // open a dir
snprintf(lpattern, 64, "%s", pattern); snprintf(lpattern, 64, "%s", pattern);
snprintf(ldir, 256, "%s", dir); snprintf(ldir, 256, "%s", dir);
@ -769,14 +769,14 @@ bool for_handler(char* path, const char* dir, const char* pattern, bool recursiv
if (!slash) return false; if (!slash) return false;
*slash = '\0'; *slash = '\0';
} }
snprintf(path, 256, "%s/%.254s", ldir, fno.fname); snprintf(path, 256, "%s/%.254s", ldir, fno.fname);
if (rec && (fno.fattrib & AM_DIR) && (dp - fdir < _MAX_FOR_DEPTH - 1)) { if (rec && (fno.fattrib & AM_DIR) && (dp - fdir < _MAX_FOR_DEPTH - 1)) {
if (fvx_opendir(++dp, path) != FR_OK) dp--; if (fvx_opendir(++dp, path) != FR_OK) dp--;
else strncpy(ldir, path, 255); else strncpy(ldir, path, 255);
} }
} else return false; } else return false;
return true; return true;
} }
@ -784,18 +784,18 @@ bool parse_line(const char* line_start, const char* line_end, cmd_id* cmdid, u32
char* ptr = (char*) line_start; char* ptr = (char*) line_start;
char* str; char* str;
u32 len; u32 len;
// set everything to initial values // set everything to initial values
*cmdid = 0; *cmdid = 0;
*flags = 0; *flags = 0;
*argc = 0; *argc = 0;
// search for cmd // search for cmd
char* cmd = NULL; char* cmd = NULL;
u32 cmd_len = 0; u32 cmd_len = 0;
if (!(cmd = get_string(ptr, line_end, &cmd_len, &ptr, err_str))) return false; // string error if (!(cmd = get_string(ptr, line_end, &cmd_len, &ptr, err_str))) return false; // string error
if ((cmd >= line_end) || (*cmd == '#') || (*cmd == '@')) return true; // empty line or comment or label if ((cmd >= line_end) || (*cmd == '#') || (*cmd == '@')) return true; // empty line or comment or label
// special handling for "if", "elif" and "not" // special handling for "if", "elif" and "not"
if (MATCH_STR(cmd, cmd_len, _CMD_NOT)) { if (MATCH_STR(cmd, cmd_len, _CMD_NOT)) {
*cmdid = CMD_ID_NOT; *cmdid = CMD_ID_NOT;
@ -807,7 +807,7 @@ bool parse_line(const char* line_start, const char* line_end, cmd_id* cmdid, u32
*cmdid = CMD_ID_ELIF; *cmdid = CMD_ID_ELIF;
return true; return true;
} }
// got cmd, now parse flags & args // got cmd, now parse flags & args
while ((str = get_string(ptr, line_end, &len, &ptr, err_str))) { while ((str = get_string(ptr, line_end, &len, &ptr, err_str))) {
bool in_quotes = ((ptr - str) != (int) len); // hacky bool in_quotes = ((ptr - str) != (int) len); // hacky
@ -825,14 +825,14 @@ bool parse_line(const char* line_start, const char* line_end, cmd_id* cmdid, u32
return false; // arg expand failed return false; // arg expand failed
} }
} }
// end reached with a failed get_string() // end reached with a failed get_string()
return false; return false;
} }
bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) { bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
bool ret = true; // true unless some cmd messes up bool ret = true; // true unless some cmd messes up
// process arg0 @string // process arg0 @string
u64 at_org = 0; u64 at_org = 0;
u64 sz_org = 0; u64 sz_org = 0;
@ -846,7 +846,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
} }
} }
} }
// perform command // perform command
if (id == CMD_ID_NOT) { if (id == CMD_ID_NOT) {
// check the argument // check the argument
@ -859,7 +859,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
// "if true" or "if false" // "if true" or "if false"
skip_state = (strncmp(argv[0], _ARG_TRUE, _ARG_MAX_LEN) == 0) ? 0 : _SKIP_BLOCK; skip_state = (strncmp(argv[0], _ARG_TRUE, _ARG_MAX_LEN) == 0) ? 0 : _SKIP_BLOCK;
ifcnt++; ifcnt++;
if (syntax_error && err_str) if (syntax_error && err_str)
snprintf(err_str, _ERR_STR_LEN, "syntax error after 'if'"); snprintf(err_str, _ERR_STR_LEN, "syntax error after 'if'");
ret = !syntax_error; ret = !syntax_error;
@ -871,12 +871,12 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
syntax_error = true; syntax_error = true;
return false; return false;
} }
// skip state handling, check the argument if required // skip state handling, check the argument if required
// "if true" or "if false" // "if true" or "if false"
skip_state = !skip_state ? _SKIP_TILL_END : skip_state = !skip_state ? _SKIP_TILL_END :
((strncmp(argv[0], _ARG_TRUE, _ARG_MAX_LEN) == 0) ? 0 : _SKIP_BLOCK); ((strncmp(argv[0], _ARG_TRUE, _ARG_MAX_LEN) == 0) ? 0 : _SKIP_BLOCK);
if (syntax_error && err_str) if (syntax_error && err_str)
snprintf(err_str, _ERR_STR_LEN, "syntax error after 'elif'"); snprintf(err_str, _ERR_STR_LEN, "syntax error after 'elif'");
ret = !syntax_error; ret = !syntax_error;
@ -888,10 +888,10 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
syntax_error = true; syntax_error = true;
return false; return false;
} }
// turn the skip state // turn the skip state
skip_state = skip_state ? 0 : _SKIP_TILL_END; skip_state = skip_state ? 0 : _SKIP_TILL_END;
ret = true; ret = true;
} }
else if (id == CMD_ID_END) { else if (id == CMD_ID_END) {
@ -901,11 +901,11 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
syntax_error = true; syntax_error = true;
return false; return false;
} }
// close last "if" // close last "if"
skip_state = 0; skip_state = 0;
ifcnt--; ifcnt--;
ret = true; ret = true;
} }
else if (id == CMD_ID_FOR) { else if (id == CMD_ID_FOR) {
@ -959,16 +959,16 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
char* options_jmp[_CHOICE_MAX_N] = { NULL }; char* options_jmp[_CHOICE_MAX_N] = { NULL };
char options_str[_CHOICE_MAX_N][_CHOICE_STR_LEN+1]; char options_str[_CHOICE_MAX_N][_CHOICE_STR_LEN+1];
u32 options_keys[_CHOICE_MAX_N] = { 0 }; u32 options_keys[_CHOICE_MAX_N] = { 0 };
char* ast = strchr(argv[1], '*'); char* ast = strchr(argv[1], '*');
char* ptr = NULL; char* ptr = NULL;
u32 n_opt = 0; u32 n_opt = 0;
while ((ptr = find_label(argv[1], ptr))) { while ((ptr = find_label(argv[1], ptr))) {
options_jmp[n_opt] = ptr; options_jmp[n_opt] = ptr;
while (*(ptr++) != '@'); while (*(ptr++) != '@');
if (ast) ptr += (ast - argv[1]); if (ast) ptr += (ast - argv[1]);
char* choice = options_str[n_opt]; char* choice = options_str[n_opt];
for (u32 i = 0; i < _CHOICE_STR_LEN; choice[++i] = '\0') { for (u32 i = 0; i < _CHOICE_STR_LEN; choice[++i] = '\0') {
if (IS_WHITESPACE(ptr[i])) break; if (IS_WHITESPACE(ptr[i])) break;
@ -980,13 +980,13 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
for (; *choice != ' ' && *choice != '\0'; choice++); for (; *choice != ' ' && *choice != '\0'; choice++);
if (*choice != '\0') *(choice++) = '\0'; if (*choice != '\0') *(choice++) = '\0';
options_keys[n_opt] = StringToButton(keystr); options_keys[n_opt] = StringToButton(keystr);
if (!options_keys[n_opt]) continue; if (!options_keys[n_opt]) continue;
} }
options[n_opt] = choice; options[n_opt] = choice;
if (++n_opt >= _CHOICE_MAX_N) break; if (++n_opt >= _CHOICE_MAX_N) break;
} }
u32 result = (flags & _FLG('k')) ? ShowHotkeyPrompt(n_opt, options, options_keys, "%s", argv[0]) : u32 result = (flags & _FLG('k')) ? ShowHotkeyPrompt(n_opt, options, options_keys, "%s", argv[0]) :
ShowSelectPrompt(n_opt, options, "%s", argv[0]); ShowSelectPrompt(n_opt, options, "%s", argv[0]);
@ -1039,7 +1039,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
char* var = get_var(argv[2], NULL); char* var = get_var(argv[2], NULL);
strncpy(choice, var, _VAR_CNT_LEN); strncpy(choice, var, _VAR_CNT_LEN);
choice[_VAR_CNT_LEN - 1] = '\0'; choice[_VAR_CNT_LEN - 1] = '\0';
char path[_VAR_CNT_LEN]; char path[_VAR_CNT_LEN];
strncpy(path, argv[1], _VAR_CNT_LEN); strncpy(path, argv[1], _VAR_CNT_LEN);
path[_VAR_CNT_LEN - 1] = '\0'; path[_VAR_CNT_LEN - 1] = '\0';
@ -1061,7 +1061,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
ret = FileSelector(choice, argv[0], path, NULL, NO_FILES | SELECT_DIRS, (flags & _FLG('x'))); ret = FileSelector(choice, argv[0], path, NULL, NO_FILES | SELECT_DIRS, (flags & _FLG('x')));
if (err_str) snprintf(err_str, _ERR_STR_LEN, "dirselect abort"); if (err_str) snprintf(err_str, _ERR_STR_LEN, "dirselect abort");
} }
if (ret) { if (ret) {
ret = set_var(argv[2], choice); ret = set_var(argv[2], choice);
if (err_str) snprintf(err_str, _ERR_STR_LEN, "var fail"); if (err_str) snprintf(err_str, _ERR_STR_LEN, "var fail");
@ -1075,14 +1075,14 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
char str[_ARG_MAX_LEN]; char str[_ARG_MAX_LEN];
strncpy(str, argv[1], _ARG_MAX_LEN); strncpy(str, argv[1], _ARG_MAX_LEN);
str[_ARG_MAX_LEN - 1] = '\0'; str[_ARG_MAX_LEN - 1] = '\0';
ret = false; ret = false;
if (strlen(argv[2]) == 1) { // argv[2] must be one char if (strlen(argv[2]) == 1) { // argv[2] must be one char
char* found; char* found;
if (flags & _FLG('f')) found = strchr(str, *argv[2]); if (flags & _FLG('f')) found = strchr(str, *argv[2]);
else found = strrchr(str, *argv[2]); else found = strrchr(str, *argv[2]);
if (!found && err_str) snprintf(err_str, _ERR_STR_LEN, "char not found"); if (!found && err_str) snprintf(err_str, _ERR_STR_LEN, "char not found");
if (found) { if (found) {
if (flags & _FLG('b')) { if (flags & _FLG('b')) {
*found = '\0'; *found = '\0';
@ -1096,7 +1096,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
char str[_ARG_MAX_LEN]; char str[_ARG_MAX_LEN];
strncpy(str, argv[1], _ARG_MAX_LEN); strncpy(str, argv[1], _ARG_MAX_LEN);
str[_ARG_MAX_LEN - 1] = '\0'; str[_ARG_MAX_LEN - 1] = '\0';
if (strnlen(argv[2], _ARG_MAX_LEN) != 2) { if (strnlen(argv[2], _ARG_MAX_LEN) != 2) {
if (err_str) snprintf(err_str, _ERR_STR_LEN, "argv[2] must be 2 chars"); if (err_str) snprintf(err_str, _ERR_STR_LEN, "argv[2] must be 2 chars");
ret = false; ret = false;
@ -1480,7 +1480,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
ret = false; ret = false;
if (err_str) snprintf(err_str, _ERR_STR_LEN, "unknown error"); if (err_str) snprintf(err_str, _ERR_STR_LEN, "unknown error");
} }
if (ret && err_str) snprintf(err_str, _ERR_STR_LEN, "command success"); if (ret && err_str) snprintf(err_str, _ERR_STR_LEN, "command success");
return ret; return ret;
} }
@ -1494,18 +1494,18 @@ bool run_line(const char* line_start, const char* line_end, u32* flags, char* er
// set up argv array // set up argv array
for (u32 i = 0; i < _MAX_ARGS; i++) for (u32 i = 0; i < _MAX_ARGS; i++)
argv[i] = args[i]; argv[i] = args[i];
// flags handling (if no pointer given) // flags handling (if no pointer given)
u32 lflags; u32 lflags;
if (!flags) flags = &lflags; if (!flags) flags = &lflags;
*flags = 0; *flags = 0;
// parse current line, grab cmd / flags / args // parse current line, grab cmd / flags / args
if (!parse_line(line_start, line_end, &cmdid, flags, &argc, argv, err_str)) { if (!parse_line(line_start, line_end, &cmdid, flags, &argc, argv, err_str)) {
syntax_error = true; syntax_error = true;
return false; return false;
} }
// control flow command handling // control flow command handling
// block out of control flow commands // block out of control flow commands
if (if_cond && IS_CTRLFLOW_CMD(cmdid)) { if (if_cond && IS_CTRLFLOW_CMD(cmdid)) {
@ -1513,36 +1513,36 @@ bool run_line(const char* line_start, const char* line_end, u32* flags, char* er
syntax_error = true; syntax_error = true;
return false; return false;
} }
// shortcuts for "elif" / "else" // shortcuts for "elif" / "else"
if (((cmdid == CMD_ID_ELIF) || (cmdid == CMD_ID_ELSE)) && !skip_state) { if (((cmdid == CMD_ID_ELIF) || (cmdid == CMD_ID_ELSE)) && !skip_state) {
skip_state = _SKIP_TILL_END; skip_state = _SKIP_TILL_END;
cmdid = 0; cmdid = 0;
} }
// handle "if" / "elif" / "not" // handle "if" / "elif" / "not"
if ((cmdid == CMD_ID_IF) || (cmdid == CMD_ID_ELIF) || (cmdid == CMD_ID_NOT)) { if ((cmdid == CMD_ID_IF) || (cmdid == CMD_ID_ELIF) || (cmdid == CMD_ID_NOT)) {
// set defaults // set defaults
argc = 1; argc = 1;
strncpy(argv[0], _ARG_FALSE, _ARG_MAX_LEN - 1); strncpy(argv[0], _ARG_FALSE, _ARG_MAX_LEN - 1);
// skip to behind the command // skip to behind the command
char* line_start_next = (char*) line_start; char* line_start_next = (char*) line_start;
for (; IS_WHITESPACE(*line_start_next); line_start_next++); for (; IS_WHITESPACE(*line_start_next); line_start_next++);
for (; *line_start_next && !IS_WHITESPACE(*line_start_next); line_start_next++); for (; *line_start_next && !IS_WHITESPACE(*line_start_next); line_start_next++);
// run condition, take over result // run condition, take over result
if (run_line(line_start_next, line_end, flags, err_str, true)) if (run_line(line_start_next, line_end, flags, err_str, true))
strncpy(argv[0], _ARG_TRUE, _ARG_MAX_LEN - 1); strncpy(argv[0], _ARG_TRUE, _ARG_MAX_LEN - 1);
} }
// run the command (if available) // run the command (if available)
if (cmdid && !run_cmd(cmdid, *flags, argv, err_str)) { if (cmdid && !run_cmd(cmdid, *flags, argv, err_str)) {
char* msg_fail = get_var("ERRORMSG", NULL); char* msg_fail = get_var("ERRORMSG", NULL);
if (msg_fail && *msg_fail) *err_str = '\0'; // use custom error message if (msg_fail && *msg_fail) *err_str = '\0'; // use custom error message
return false; return false;
} }
// success if we arrive here // success if we arrive here
return true; return true;
} }
@ -1581,7 +1581,7 @@ void MemTextView(const char* text, u32 len, char* line0, int off_disp, int lno,
if (ncpy > TV_LLEN_DISP) ncpy = TV_LLEN_DISP; if (ncpy > TV_LLEN_DISP) ncpy = TV_LLEN_DISP;
bool al = !ww && off_disp && (ptr != ptr_next); bool al = !ww && off_disp && (ptr != ptr_next);
bool ar = !ww && (llen > off_disp + TV_LLEN_DISP); bool ar = !ww && (llen > off_disp + TV_LLEN_DISP);
// set text color / find start of comment of scripts // set text color / find start of comment of scripts
u32 color_text = (nln == mno) ? script_color_active : (is_script) ? script_color_code : (u32) COLOR_TVTEXT; u32 color_text = (nln == mno) ? script_color_active : (is_script) ? script_color_code : (u32) COLOR_TVTEXT;
int cmt_start = TV_LLEN_DISP; // start of comment in current displayed line (may be negative) int cmt_start = TV_LLEN_DISP; // start of comment in current displayed line (may be negative)
@ -1591,14 +1591,14 @@ void MemTextView(const char* text, u32 len, char* line0, int off_disp, int lno,
cmt_start = (hash - ptr) - off_disp; cmt_start = (hash - ptr) - off_disp;
} }
if (cmt_start <= 0) color_text = script_color_comment; if (cmt_start <= 0) color_text = script_color_comment;
// build text string // build text string
snprintf(txtstr, TV_LLEN_DISP + 1, "%-*.*s", (int) TV_LLEN_DISP, (int) TV_LLEN_DISP, ""); snprintf(txtstr, TV_LLEN_DISP + 1, "%-*.*s", (int) TV_LLEN_DISP, (int) TV_LLEN_DISP, "");
if (ncpy) memcpy(txtstr, ptr + off_disp, ncpy); if (ncpy) memcpy(txtstr, ptr + off_disp, ncpy);
for (char* d = txtstr; *d; d++) if (*d < ' ') *d = ' '; for (char* d = txtstr; *d; d++) if (*d < ' ') *d = ' ';
if (al) memcpy(txtstr + p_al, al_str, strnlen(al_str, 16)); if (al) memcpy(txtstr + p_al, al_str, strnlen(al_str, 16));
if (ar) memcpy(txtstr + p_ar, ar_str, strnlen(ar_str, 16)); if (ar) memcpy(txtstr + p_ar, ar_str, strnlen(ar_str, 16));
// draw line number & text // draw line number & text
DrawString(TOP_SCREEN, txtstr, x_txt, y, color_text, COLOR_STD_BG, false); DrawString(TOP_SCREEN, txtstr, x_txt, y, color_text, COLOR_STD_BG, false);
if (TV_LNOS > 0) { // line number if (TV_LNOS > 0) { // line number
@ -1606,17 +1606,17 @@ void MemTextView(const char* text, u32 len, char* line0, int off_disp, int lno,
DrawStringF(TOP_SCREEN, x_lno, y, ((ptr == text) || (*(ptr-1) == '\n')) ? COLOR_TVOFFS : COLOR_TVOFFSL, COLOR_STD_BG, "%0*lu", TV_LNOS, nln); DrawStringF(TOP_SCREEN, x_lno, y, ((ptr == text) || (*(ptr-1) == '\n')) ? COLOR_TVOFFS : COLOR_TVOFFSL, COLOR_STD_BG, "%0*lu", TV_LNOS, nln);
else DrawStringF(TOP_SCREEN, x_lno, y, COLOR_TVOFFSL, COLOR_STD_BG, "%*.*s", TV_LNOS, TV_LNOS, " "); else DrawStringF(TOP_SCREEN, x_lno, y, COLOR_TVOFFSL, COLOR_STD_BG, "%*.*s", TV_LNOS, TV_LNOS, " ");
} }
// colorize comment if is_script // colorize comment if is_script
if ((cmt_start > 0) && ((u32) cmt_start < TV_LLEN_DISP)) { if ((cmt_start > 0) && ((u32) cmt_start < TV_LLEN_DISP)) {
memset(txtstr, ' ', cmt_start); memset(txtstr, ' ', cmt_start);
DrawString(TOP_SCREEN, txtstr, x_txt, y, script_color_comment, COLOR_TRANSPARENT, false); DrawString(TOP_SCREEN, txtstr, x_txt, y, script_color_comment, COLOR_TRANSPARENT, false);
} }
// colorize arrows // colorize arrows
if (al) DrawStringF(TOP_SCREEN, x_al, y, COLOR_TVOFFS, COLOR_TRANSPARENT, al_str); if (al) DrawStringF(TOP_SCREEN, x_al, y, COLOR_TVOFFS, COLOR_TRANSPARENT, al_str);
if (ar) DrawStringF(TOP_SCREEN, x_ar, y, COLOR_TVOFFS, COLOR_TRANSPARENT, ar_str); if (ar) DrawStringF(TOP_SCREEN, x_ar, y, COLOR_TVOFFS, COLOR_TRANSPARENT, ar_str);
// advance pointer / line number // advance pointer / line number
for (char* c = ptr; c < ptr_next; c++) if (*c == '\n') ++nln; for (char* c = ptr; c < ptr_next; c++) if (*c == '\n') ++nln;
ptr = ptr_next; ptr = ptr_next;
@ -1664,7 +1664,7 @@ bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script) {
while (true) { while (true) {
// display text on screen // display text on screen
MemTextView(text, len, line0, off_disp, lcurr, ww, 0, as_script); MemTextView(text, len, line0, off_disp, lcurr, ww, 0, as_script);
// handle user input // handle user input
u32 pad_state = InputWait(0); u32 pad_state = InputWait(0);
char* line0_next = line0; char* line0_next = line0;
@ -1683,7 +1683,7 @@ bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script) {
ww = ww ? 0 : TV_LLEN_DISP; ww = ww ? 0 : TV_LLEN_DISP;
line0_next = line_seek(text, len, ww, line0, 0); line0_next = line_seek(text, len, ww, line0, 0);
} else if (pad_state & (BUTTON_B|BUTTON_START)) break; } else if (pad_state & (BUTTON_B|BUTTON_START)) break;
// check for problems, apply changes // check for problems, apply changes
if (!ww && (line0_next > llast_nww)) line0_next = llast_nww; if (!ww && (line0_next > llast_nww)) line0_next = llast_nww;
else if (ww && (line0_next > llast_ww)) line0_next = llast_ww; else if (ww && (line0_next > llast_ww)) line0_next = llast_ww;
@ -1697,10 +1697,10 @@ bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script) {
if (off_disp + TV_LLEN_DISP > llen_max) off_disp = llen_max - TV_LLEN_DISP; if (off_disp + TV_LLEN_DISP > llen_max) off_disp = llen_max - TV_LLEN_DISP;
if ((off_disp < 0) || ww) off_disp = 0; if ((off_disp < 0) || ww) off_disp = 0;
} }
// clear screens // clear screens
ClearScreenF(true, true, COLOR_STD_BG); ClearScreenF(true, true, COLOR_STD_BG);
return true; return true;
} }
@ -1711,17 +1711,17 @@ bool MemToCViewer(const char* text, u32 len, const char* title) {
char* captions[max_captions]; char* captions[max_captions];
u32 lineno[max_captions]; u32 lineno[max_captions];
u32 ww = TV_LLEN_DISP; u32 ww = TV_LLEN_DISP;
// check if this really is text // check if this really is text
if (!ValidateText(text, len)) { if (!ValidateText(text, len)) {
ShowPrompt(false, "Error: Invalid text data"); ShowPrompt(false, "Error: Invalid text data");
return false; return false;
} }
// clear screens / view start of readme on top // clear screens / view start of readme on top
ClearScreenF(true, true, COLOR_STD_BG); ClearScreenF(true, true, COLOR_STD_BG);
MemTextView(text, len, (char*) text, 0, 1, ww, 0, false); MemTextView(text, len, (char*) text, 0, 1, ww, 0, false);
// parse text for markdown captions // parse text for markdown captions
u32 n_captions = 0; u32 n_captions = 0;
char* ptr = (char*) text; char* ptr = (char*) text;
@ -1735,7 +1735,7 @@ bool MemToCViewer(const char* text, u32 len, const char* title) {
} }
ptr = ptr_next; ptr = ptr_next;
} }
int cursor = -1; int cursor = -1;
while (true) { while (true) {
// display ToC // display ToC
@ -1756,7 +1756,7 @@ bool MemToCViewer(const char* text, u32 len, const char* title) {
"%*.*s", len, len, caption); "%*.*s", len, len, caption);
y0 += FONT_HEIGHT_EXT + (2*TV_VPAD); y0 += FONT_HEIGHT_EXT + (2*TV_VPAD);
} }
// handle user input // handle user input
u32 pad_state = InputWait(0); u32 pad_state = InputWait(0);
if ((cursor >= 0) && (pad_state & BUTTON_A)) { if ((cursor >= 0) && (pad_state & BUTTON_A)) {
@ -1772,10 +1772,10 @@ bool MemToCViewer(const char* text, u32 len, const char* title) {
MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false); MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false);
} }
} }
// clear screens // clear screens
ClearScreenF(true, true, COLOR_STD_BG); ClearScreenF(true, true, COLOR_STD_BG);
return true; return true;
} }
@ -1802,29 +1802,29 @@ bool FileTextViewer(const char* path, bool as_script) {
bool ExecuteGM9Script(const char* path_script) { bool ExecuteGM9Script(const char* path_script) {
char path_str[32+1]; char path_str[32+1];
TruncateString(path_str, path_script, 32, 12); TruncateString(path_str, path_script, 32, 12);
// reset control flow global vars // reset control flow global vars
ifcnt = 0; ifcnt = 0;
jump_ptr = NULL; jump_ptr = NULL;
for_ptr = NULL; for_ptr = NULL;
skip_state = 0; skip_state = 0;
syntax_error = false; syntax_error = false;
// allocate && check memory // allocate && check memory
var_buffer = (void*) malloc(sizeof(Gm9ScriptVar) * _VAR_MAX_BUFF); var_buffer = (void*) malloc(sizeof(Gm9ScriptVar) * _VAR_MAX_BUFF);
script_buffer = (void*) malloc(SCRIPT_MAX_SIZE); script_buffer = (void*) malloc(SCRIPT_MAX_SIZE);
char* script = (char*) script_buffer; char* script = (char*) script_buffer;
char* ptr = script; char* ptr = script;
if (!var_buffer || !script_buffer) { if (!var_buffer || !script_buffer) {
if (var_buffer) free(var_buffer); if (var_buffer) free(var_buffer);
if (script_buffer) free(script_buffer); if (script_buffer) free(script_buffer);
ShowPrompt(false, "Out of memory."); ShowPrompt(false, "Out of memory.");
return false; return false;
} }
// fetch script from path // fetch script from path
u32 script_size = FileGetData(path_script, (u8*) script, SCRIPT_MAX_SIZE, 0); u32 script_size = FileGetData(path_script, (u8*) script, SCRIPT_MAX_SIZE, 0);
if (!script_size || (script_size >= SCRIPT_MAX_SIZE)) { if (!script_size || (script_size >= SCRIPT_MAX_SIZE)) {
@ -1832,13 +1832,13 @@ bool ExecuteGM9Script(const char* path_script) {
free(script_buffer); free(script_buffer);
return false; return false;
} }
char* end = script + script_size; char* end = script + script_size;
*end = '\0'; *end = '\0';
// initialise variables // initialise variables
init_vars(path_script); init_vars(path_script);
// setup script preview (only if used) // setup script preview (only if used)
u32 preview_mode_local = 0; u32 preview_mode_local = 0;
if (MAIN_SCREEN != TOP_SCREEN) { if (MAIN_SCREEN != TOP_SCREEN) {
@ -1848,7 +1848,7 @@ bool ExecuteGM9Script(const char* path_script) {
script_color_comment = COLOR_TVCMT; script_color_comment = COLOR_TVCMT;
script_color_code = COLOR_TVCMD; script_color_code = COLOR_TVCMD;
} }
// script execute loop // script execute loop
u32 lno = 1; u32 lno = 1;
bool result = true; bool result = true;
@ -1905,17 +1905,17 @@ bool ExecuteGM9Script(const char* path_script) {
MemTextView(script, script_size, script, 0, 1, 0, lno, true); MemTextView(script, script_size, script, 0, 1, 0, lno, true);
} else { } else {
char* ptr_view = line_seek(script, script_size, 0, ptr, -(TV_NLIN_DISP/2)); char* ptr_view = line_seek(script, script_size, 0, ptr, -(TV_NLIN_DISP/2));
u32 lno_view = lno - (TV_NLIN_DISP/2); u32 lno_view = lno - (TV_NLIN_DISP/2);
MemTextView(script, script_size, ptr_view, 0, lno_view, 0, lno, true); MemTextView(script, script_size, ptr_view, 0, lno_view, 0, lno, true);
} }
} }
} }
// run command // run command
char err_str[_ERR_STR_LEN+1] = { 0 }; char err_str[_ERR_STR_LEN+1] = { 0 };
result = run_line(ptr, line_end, &flags, err_str, false); result = run_line(ptr, line_end, &flags, err_str, false);
// skip state handling // skip state handling
char* skip_ptr = ptr; char* skip_ptr = ptr;
if ((skip_state == _SKIP_BLOCK) || (skip_state == _SKIP_TILL_END)) { if ((skip_state == _SKIP_BLOCK) || (skip_state == _SKIP_TILL_END)) {
@ -1942,12 +1942,12 @@ bool ExecuteGM9Script(const char* path_script) {
} }
skip_state = 0; skip_state = 0;
} }
if (!result) { // error handling if (!result) { // error handling
if (syntax_error) // severe error, can't continue if (syntax_error) // severe error, can't continue
flags &= ~(_FLG('o')|_FLG('s')); // never silent or optional flags &= ~(_FLG('o')|_FLG('s')); // never silent or optional
if (!(flags & _FLG('s'))) { // not silent if (!(flags & _FLG('s'))) { // not silent
if (!*err_str) { if (!*err_str) {
char* msg_fail = get_var("ERRORMSG", NULL); char* msg_fail = get_var("ERRORMSG", NULL);
@ -1970,7 +1970,7 @@ bool ExecuteGM9Script(const char* path_script) {
break; break;
} else result = true; // set back the result otherwise } else result = true; // set back the result otherwise
} }
// reposition pointer // reposition pointer
if (skip_ptr != ptr) { if (skip_ptr != ptr) {
ptr = skip_ptr; ptr = skip_ptr;
@ -1987,8 +1987,8 @@ bool ExecuteGM9Script(const char* path_script) {
lno++; lno++;
} }
} }
if (result) { // all fine(?) up to this point if (result) { // all fine(?) up to this point
if (ifcnt) { // check for unresolved 'if' if (ifcnt) { // check for unresolved 'if'
ShowPrompt(false, "%s\nend of script: unresolved 'if'", path_str); ShowPrompt(false, "%s\nend of script: unresolved 'if'", path_str);
@ -2000,13 +2000,13 @@ bool ExecuteGM9Script(const char* path_script) {
result = false; result = false;
} }
} }
if (result) { // success message if applicable if (result) { // success message if applicable
char* msg_okay = get_var("SUCCESSMSG", NULL); char* msg_okay = get_var("SUCCESSMSG", NULL);
if (msg_okay && *msg_okay) ShowPrompt(false, "%s", msg_okay); if (msg_okay && *msg_okay) ShowPrompt(false, "%s", msg_okay);
} }
free(var_buffer); free(var_buffer);
free(script_buffer); free(script_buffer);
return result; return result;

View File

@ -276,16 +276,16 @@ void GetSysInfo_Movable(SysInfo* info, char nand_drive) {
strncpy(info->friendcodeseed, "<unknown>", countof("<unknown>")); strncpy(info->friendcodeseed, "<unknown>", countof("<unknown>"));
strncpy(info->movablekeyy, "<unknown>", countof("<unknown>")); strncpy(info->movablekeyy, "<unknown>", countof("<unknown>"));
strncpy(info->nand_id0, "<unknown>", countof("<unknown>")); strncpy(info->nand_id0, "<unknown>", countof("<unknown>"));
if (fvx_qread(path, &data, 0, 0x120 /* sizeof(data) */, NULL) != FR_OK) // whatever, we don't need the last 0x20 byte here if (fvx_qread(path, &data, 0, 0x120 /* sizeof(data) */, NULL) != FR_OK) // whatever, we don't need the last 0x20 byte here
return; return;
// The LocalFriendCodeSeed. // The LocalFriendCodeSeed.
snprintf(info->friendcodeseed, 16 + 1, "%016llX", getbe64(data.codeseed_data.codeseed)); snprintf(info->friendcodeseed, 16 + 1, "%016llX", getbe64(data.codeseed_data.codeseed));
// The Movable KeyY // The Movable KeyY
snprintf(info->movablekeyy, 32 + 1, "%s%016llX", info->friendcodeseed, getbe64(data.keyy_high)); snprintf(info->movablekeyy, 32 + 1, "%s%016llX", info->friendcodeseed, getbe64(data.keyy_high));
// SysNAND ID0 // SysNAND ID0
unsigned int sha256sum[8]; unsigned int sha256sum[8];
sha_quick(sha256sum, data.codeseed_data.codeseed, 16, SHA256_MODE); sha_quick(sha256sum, data.codeseed_data.codeseed, 16, SHA256_MODE);
@ -296,17 +296,17 @@ void GetSysInfo_Movable(SysInfo* info, char nand_drive) {
// Read sdmmc. // Read sdmmc.
void GetSysInfo_SDMMC(SysInfo* info, char nand_drive) { void GetSysInfo_SDMMC(SysInfo* info, char nand_drive) {
(void) nand_drive; (void) nand_drive;
u8 nand_cid[16] = { 0 }; u8 nand_cid[16] = { 0 };
u8 sd_cid[16] = { 0 }; u8 sd_cid[16] = { 0 };
strncpy(info->nand_cid, "<unknown>", countof("<unknown>")); strncpy(info->nand_cid, "<unknown>", countof("<unknown>"));
strncpy(info->sd_cid, "<unknown>", countof("<unknown>")); strncpy(info->sd_cid, "<unknown>", countof("<unknown>"));
strncpy(info->nand_id1, "<unknown>", countof("<unknown>")); strncpy(info->nand_id1, "<unknown>", countof("<unknown>"));
sdmmc_get_cid(1, (u32*) (void*) nand_cid); sdmmc_get_cid(1, (u32*) (void*) nand_cid);
snprintf(info->nand_cid, 32 + 1, "%016llX%016llX", getbe64(nand_cid), getbe64(nand_cid+8)); snprintf(info->nand_cid, 32 + 1, "%016llX%016llX", getbe64(nand_cid), getbe64(nand_cid+8));
sdmmc_get_cid(0, (u32*) (void*) sd_cid); sdmmc_get_cid(0, (u32*) (void*) sd_cid);
snprintf(info->sd_cid, 32 + 1, "%016llX%016llX", getbe64(sd_cid), getbe64(sd_cid+8)); snprintf(info->sd_cid, 32 + 1, "%016llX%016llX", getbe64(sd_cid), getbe64(sd_cid+8));
snprintf(info->nand_id1, 32 + 1, "%08lX%08lX%08lX%08lX", snprintf(info->nand_id1, 32 + 1, "%08lX%08lX%08lX%08lX",

View File

@ -90,9 +90,9 @@ u64 InitVBDRIDrive(void) { // prerequisite: .db file mounted as virtual diff ima
u64 mount_state = CheckVDisaDiffDrive(); u64 mount_state = CheckVDisaDiffDrive();
if (!(mount_state & SYS_DIFF)) return 0; if (!(mount_state & SYS_DIFF)) return 0;
is_tickdb = (mount_state & SYS_TICKDB); is_tickdb = (mount_state & SYS_TICKDB);
DeinitVBDRIDrive(); DeinitVBDRIDrive();
num_entries = min((is_tickdb ? GetNumTickets(PART_PATH) : GetNumTitleInfoEntries(PART_PATH)) + 1, VBDRI_MAX_ENTRIES); num_entries = min((is_tickdb ? GetNumTickets(PART_PATH) : GetNumTitleInfoEntries(PART_PATH)) + 1, VBDRI_MAX_ENTRIES);
title_ids = (u8*) malloc(num_entries * 8); title_ids = (u8*) malloc(num_entries * 8);
if (!title_ids || if (!title_ids ||
@ -100,12 +100,12 @@ u64 InitVBDRIDrive(void) { // prerequisite: .db file mounted as virtual diff ima
DeinitVBDRIDrive(); DeinitVBDRIDrive();
return 0; return 0;
} }
if (!is_tickdb && ((cached_entry = malloc(sizeof(TitleInfoEntry))) == NULL)) { if (!is_tickdb && ((cached_entry = malloc(sizeof(TitleInfoEntry))) == NULL)) {
DeinitVBDRIDrive(); DeinitVBDRIDrive();
return 0; return 0;
} }
return mount_state; return mount_state;
} }
@ -122,25 +122,25 @@ bool ReadVBDRIDir(VirtualFile* vfile, VirtualDir* vdir) {
if (vdir->flags & VFLAG_TICKDIR) { // ticket dir if (vdir->flags & VFLAG_TICKDIR) { // ticket dir
if (!is_tickdb || (!tick_info && !SortVBDRITickets())) if (!is_tickdb || (!tick_info && !SortVBDRITickets()))
return false; return false;
while (++vdir->index < (int) num_entries) { while (++vdir->index < (int) num_entries) {
u32 type = tick_info[vdir->index].type; u32 type = tick_info[vdir->index].type;
u64 tid = getbe64(title_ids + (vdir->index * 8)); u64 tid = getbe64(title_ids + (vdir->index * 8));
if ((tid == 0) || !( if ((tid == 0) || !(
((vdir->flags & VFLAG_ESHOP) && (type == 0)) || ((vdir->flags & VFLAG_ESHOP) && (type == 0)) ||
((vdir->flags & VFLAG_HOMEBREW) && (type == 1)) || ((vdir->flags & VFLAG_HOMEBREW) && (type == 1)) ||
((vdir->flags & VFLAG_SYSTEM) && (type == 2)) || ((vdir->flags & VFLAG_SYSTEM) && (type == 2)) ||
((vdir->flags & VFLAG_UNKNOWN) && (type == 3)))) ((vdir->flags & VFLAG_UNKNOWN) && (type == 3))))
continue; continue;
memset(vfile, 0, sizeof(VirtualFile)); memset(vfile, 0, sizeof(VirtualFile));
snprintf(vfile->name, 32, NAME_TIK, tid, getle32(tick_info[vdir->index].console_id)); snprintf(vfile->name, 32, NAME_TIK, tid, getle32(tick_info[vdir->index].console_id));
vfile->offset = vdir->index; // "offset" is the internal buffer index vfile->offset = vdir->index; // "offset" is the internal buffer index
vfile->size = tick_info[vdir->index].size; vfile->size = tick_info[vdir->index].size;
vfile->keyslot = 0xFF; vfile->keyslot = 0xFF;
vfile->flags = (vdir->flags | VFLAG_DELETABLE) & ~VFLAG_DIR; vfile->flags = (vdir->flags | VFLAG_DELETABLE) & ~VFLAG_DIR;
return true; // found return true; // found
} }
} else { // root dir } else { // root dir
@ -163,17 +163,17 @@ bool ReadVBDRIDir(VirtualFile* vfile, VirtualDir* vdir) {
vfile->size = sizeof(TitleInfoEntry); vfile->size = sizeof(TitleInfoEntry);
vfile->keyslot = 0xFF; vfile->keyslot = 0xFF;
vfile->flags = (vdir->flags | VFLAG_DELETABLE) & ~VFLAG_DIR; vfile->flags = (vdir->flags | VFLAG_DELETABLE) & ~VFLAG_DIR;
return true; return true;
} }
} }
} }
return false; return false;
} }
bool GetNewVBDRIFile(VirtualFile* vfile, VirtualDir* vdir, const char* path) { bool GetNewVBDRIFile(VirtualFile* vfile, VirtualDir* vdir, const char* path) {
size_t path_len = strlen(path), buf_len = (is_tickdb ? NAME_TIK_LEN : NAME_TIE_LEN) + 2; size_t path_len = strlen(path), buf_len = (is_tickdb ? NAME_TIK_LEN : NAME_TIE_LEN) + 2;
u64 tid; u64 tid;
u32 console_id; u32 console_id;
@ -181,8 +181,8 @@ bool GetNewVBDRIFile(VirtualFile* vfile, VirtualDir* vdir, const char* path) {
char buf[buf_len]; char buf[buf_len];
strcpy(buf, path + path_len - buf_len + 1); strcpy(buf, path + path_len - buf_len + 1);
if (( is_tickdb && (sscanf(buf, "/" NAME_TIK "%c", &tid, &console_id, &c) != 2)) || if (( is_tickdb && (sscanf(buf, "/" NAME_TIK "%c", &tid, &console_id, &c) != 2)) ||
(!is_tickdb && (sscanf(buf, "/" NAME_TIE "%c", &tid, &c) != 1)) || (!is_tickdb && (sscanf(buf, "/" NAME_TIE "%c", &tid, &c) != 1)) ||
(tid == 0)) (tid == 0))
@ -193,15 +193,15 @@ bool GetNewVBDRIFile(VirtualFile* vfile, VirtualDir* vdir, const char* path) {
for (u32 i = 0; i < num_entries; i++) { for (u32 i = 0; i < num_entries; i++) {
if ((entry_index == -1) && (*((u64*)(void*)(title_ids + 8 * i)) == 0)) if ((entry_index == -1) && (*((u64*)(void*)(title_ids + 8 * i)) == 0))
entry_index = i; entry_index = i;
if (memcmp(&tid, title_ids + 8 * i, 8) == 0) if (memcmp(&tid, title_ids + 8 * i, 8) == 0)
return false; return false;
} }
if (entry_index == -1) { if (entry_index == -1) {
if (num_entries == VBDRI_MAX_ENTRIES) if (num_entries == VBDRI_MAX_ENTRIES)
return false; return false;
u32 new_num_entries = min(num_entries + 128, VBDRI_MAX_ENTRIES); u32 new_num_entries = min(num_entries + 128, VBDRI_MAX_ENTRIES);
u8* new_title_ids = realloc(title_ids, new_num_entries * 8); u8* new_title_ids = realloc(title_ids, new_num_entries * 8);
if (!new_title_ids) if (!new_title_ids)
@ -212,37 +212,37 @@ bool GetNewVBDRIFile(VirtualFile* vfile, VirtualDir* vdir, const char* path) {
return false; return false;
tick_info = new_tick_info; tick_info = new_tick_info;
} }
entry_index = num_entries; entry_index = num_entries;
num_entries = new_num_entries; num_entries = new_num_entries;
title_ids = new_title_ids; title_ids = new_title_ids;
memset(title_ids + entry_index * 8, 0, (num_entries - entry_index) * 8); memset(title_ids + entry_index * 8, 0, (num_entries - entry_index) * 8);
} }
u32 size = is_tickdb ? TICKET_COMMON_SIZE : sizeof(TitleInfoEntry); u32 size = is_tickdb ? TICKET_COMMON_SIZE : sizeof(TitleInfoEntry);
u8 entry[size]; u8 entry[size];
if (is_tickdb) if (is_tickdb)
*((u32*)(void*)(entry + 0x2A8)) = 0xAC000000; *((u32*)(void*)(entry + 0x2A8)) = 0xAC000000;
if ((is_tickdb ? AddTicketToDB(PART_PATH, (u8*)&tid, (Ticket*)(void*)entry, false) : if ((is_tickdb ? AddTicketToDB(PART_PATH, (u8*)&tid, (Ticket*)(void*)entry, false) :
AddTitleInfoEntryToDB(PART_PATH, (u8*)&tid, (TitleInfoEntry*)(void*)entry, false)) != 0) AddTitleInfoEntryToDB(PART_PATH, (u8*)&tid, (TitleInfoEntry*)(void*)entry, false)) != 0)
return false; return false;
memcpy(title_ids + entry_index * 8, &tid, 8); memcpy(title_ids + entry_index * 8, &tid, 8);
if (tick_info) { if (tick_info) {
tick_info[entry_index].type = 3; tick_info[entry_index].type = 3;
tick_info[entry_index].size = TICKET_COMMON_SIZE; tick_info[entry_index].size = TICKET_COMMON_SIZE;
memset(tick_info[entry_index].console_id, 0, 4); memset(tick_info[entry_index].console_id, 0, 4);
} }
memset(vfile, 0, sizeof(VirtualFile)); memset(vfile, 0, sizeof(VirtualFile));
strcpy(vfile->name, buf); strcpy(vfile->name, buf);
vfile->offset = entry_index; // "offset" is the internal buffer index vfile->offset = entry_index; // "offset" is the internal buffer index
vfile->size = size; vfile->size = size;
vfile->keyslot = 0xFF; vfile->keyslot = 0xFF;
vfile->flags = (vdir->flags | VFLAG_DELETABLE) & ~VFLAG_DIR; vfile->flags = (vdir->flags | VFLAG_DELETABLE) & ~VFLAG_DIR;
return true; return true;
} }
@ -251,14 +251,14 @@ int ReadVBDRIFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count)
memcpy(buffer, cached_entry + offset, count); memcpy(buffer, cached_entry + offset, count);
return 0; return 0;
} }
if (is_tickdb && (cache_index != -1)) if (is_tickdb && (cache_index != -1))
free(cached_entry); free(cached_entry);
if ((is_tickdb ? ReadTicketFromDB(PART_PATH, title_ids + vfile->offset * 8, (Ticket**) &cached_entry) : if ((is_tickdb ? ReadTicketFromDB(PART_PATH, title_ids + vfile->offset * 8, (Ticket**) &cached_entry) :
ReadTitleInfoEntryFromDB(PART_PATH, title_ids + vfile->offset * 8, (TitleInfoEntry*) cached_entry)) != 0) ReadTitleInfoEntryFromDB(PART_PATH, title_ids + vfile->offset * 8, (TitleInfoEntry*) cached_entry)) != 0)
return 1; return 1;
cache_index = (int) vfile->offset; cache_index = (int) vfile->offset;
memcpy(buffer, cached_entry + offset, count); memcpy(buffer, cached_entry + offset, count);
return 0; return 0;
} }
@ -281,25 +281,25 @@ int WriteVBDRIFile(VirtualFile* vfile, const void* buffer, u64 offset, u64 count
return 1; return 1;
} }
cache_index = (int) vfile->offset; cache_index = (int) vfile->offset;
if (resize) { if (resize) {
u8* new_cached_entry = realloc(cached_entry, vfile->size); u8* new_cached_entry = realloc(cached_entry, vfile->size);
if (!new_cached_entry) { if (!new_cached_entry) {
vfile->size = tick_info[vfile->offset].size; vfile->size = tick_info[vfile->offset].size;
return 1; return 1;
} }
cached_entry = new_cached_entry; cached_entry = new_cached_entry;
if (RemoveTicketFromDB(PART_PATH, title_ids + vfile->offset * 8) != 0) { if (RemoveTicketFromDB(PART_PATH, title_ids + vfile->offset * 8) != 0) {
vfile->size = tick_info[vfile->offset].size; vfile->size = tick_info[vfile->offset].size;
return 1; return 1;
} }
} }
memcpy(cached_entry + offset, buffer, count); memcpy(cached_entry + offset, buffer, count);
if ((is_tickdb ? AddTicketToDB(PART_PATH, title_ids + vfile->offset * 8, (Ticket*)(void*)cached_entry, true) : if ((is_tickdb ? AddTicketToDB(PART_PATH, title_ids + vfile->offset * 8, (Ticket*)(void*)cached_entry, true) :
AddTitleInfoEntryToDB(PART_PATH, title_ids + vfile->offset * 8, (TitleInfoEntry*)(void*)cached_entry, true)) != 0) { AddTitleInfoEntryToDB(PART_PATH, title_ids + vfile->offset * 8, (TitleInfoEntry*)(void*)cached_entry, true)) != 0) {
if (resize) vfile->size = tick_info[vfile->offset].size; if (resize) vfile->size = tick_info[vfile->offset].size;
if (is_tickdb) { if (is_tickdb) {
@ -309,13 +309,13 @@ int WriteVBDRIFile(VirtualFile* vfile, const void* buffer, u64 offset, u64 count
cache_index = -1; cache_index = -1;
return 1; return 1;
} }
if (resize) tick_info[vfile->offset].size = vfile->size; if (resize) tick_info[vfile->offset].size = vfile->size;
if (tick_info && ((offset <= 0x1F1 && offset + count > 0x1F1) || (cached_entry[0x1F1] == 0 && offset <= 0x104 && offset + count > 4))) if (tick_info && ((offset <= 0x1F1 && offset + count > 0x1F1) || (cached_entry[0x1F1] == 0 && offset <= 0x104 && offset + count > 4)))
tick_info[vfile->offset].type = (cached_entry[0x1F1] > 1) ? 3 : tick_info[vfile->offset].type = (cached_entry[0x1F1] > 1) ? 3 :
((ValidateTicketSignature((Ticket*)(void*)cached_entry) != 0) ? 1 : ((cached_entry[0x1F1] == 1) ? 2 : 0)); ((ValidateTicketSignature((Ticket*)(void*)cached_entry) != 0) ? 1 : ((cached_entry[0x1F1] == 1) ? 2 : 0));
return 0; return 0;
} }
@ -333,7 +333,7 @@ int DeleteVBDRIFile(const VirtualFile* vfile) {
memset(title_ids + vfile->offset * 8, 0, 8); memset(title_ids + vfile->offset * 8, 0, 8);
} }
return ret; return ret;
} }

View File

@ -25,14 +25,14 @@ bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir) {
if ((vdir->index < 0) && !cart_init) if ((vdir->index < 0) && !cart_init)
InitVCartDrive(); InitVCartDrive();
if (!cart_init) return false; if (!cart_init) return false;
const char* ext = (cdata->cart_type & CART_CTR) ? "3ds" : "nds"; const char* ext = (cdata->cart_type & CART_CTR) ? "3ds" : "nds";
char name[24]; char name[24];
GetCartName(name, cdata); GetCartName(name, cdata);
memset(vfile, 0, sizeof(VirtualFile)); memset(vfile, 0, sizeof(VirtualFile));
vfile->keyslot = 0xFF; // unused vfile->keyslot = 0xFF; // unused
vfile->flags = VFLAG_READONLY; vfile->flags = VFLAG_READONLY;
while (++vdir->index <= 7) { while (++vdir->index <= 7) {
if ((vdir->index == 0) && (cdata->data_size < FAT_LIMIT)) { // standard full rom if ((vdir->index == 0) && (cdata->data_size < FAT_LIMIT)) { // standard full rom
snprintf(vfile->name, 32, "%s.%s", name, ext); snprintf(vfile->name, 32, "%s.%s", name, ext);
@ -69,7 +69,7 @@ bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir) {
return true; return true;
} }
} }
return false; return false;
} }

View File

@ -31,39 +31,39 @@ static void AlignDisaDiffIvfcRange(DisaDiffIvfcRange* range, u32 log_block_size)
static u32 MergeDisaDiffIvfcRange(DisaDiffIvfcRange new_range, DisaDiffIvfcRange* ranges, u32* n_ranges) { static u32 MergeDisaDiffIvfcRange(DisaDiffIvfcRange new_range, DisaDiffIvfcRange* ranges, u32* n_ranges) {
const u32 new_end_offset = new_range.offset + new_range.size; const u32 new_end_offset = new_range.offset + new_range.size;
bool add_range = true; bool add_range = true;
if (*n_ranges > MAX_IVFC_RANGES) if (*n_ranges > MAX_IVFC_RANGES)
return 1; return 1;
for (u32 i = 0; i < *n_ranges; i++) { for (u32 i = 0; i < *n_ranges; i++) {
u32 end_offset = ranges[i].offset + ranges[i].size; u32 end_offset = ranges[i].offset + ranges[i].size;
if (new_range.offset > ranges[i].offset) { if (new_range.offset > ranges[i].offset) {
if (end_offset >= new_range.offset) { if (end_offset >= new_range.offset) {
add_range = false; add_range = false;
if (end_offset < new_end_offset) { if (end_offset < new_end_offset) {
ranges[i].size += new_end_offset - end_offset; ranges[i].size += new_end_offset - end_offset;
} }
break; break;
} }
continue; continue;
} else if (ranges[i].offset > new_range.offset) { } else if (ranges[i].offset > new_range.offset) {
if (new_end_offset >= ranges[i].offset) { if (new_end_offset >= ranges[i].offset) {
add_range = false; add_range = false;
if (new_end_offset < end_offset) { if (new_end_offset < end_offset) {
ranges[i].offset = new_range.offset; ranges[i].offset = new_range.offset;
ranges[i].size = new_range.size + end_offset - new_end_offset; ranges[i].size = new_range.size + end_offset - new_end_offset;
} else { } else {
ranges[i] = new_range; ranges[i] = new_range;
} }
break; break;
} }
continue; continue;
} else { } else {
add_range = false; add_range = false;
@ -71,46 +71,46 @@ static u32 MergeDisaDiffIvfcRange(DisaDiffIvfcRange new_range, DisaDiffIvfcRange
break; break;
} }
} }
if (add_range) { if (add_range) {
if (*n_ranges == MAX_IVFC_RANGES - 1) if (*n_ranges == MAX_IVFC_RANGES - 1)
return 1; return 1;
ranges[(*n_ranges)++] = new_range; ranges[(*n_ranges)++] = new_range;
} }
return 0; return 0;
} }
static u32 FixVDisaDiffIvfcHashChain(bool partitionB) { static u32 FixVDisaDiffIvfcHashChain(bool partitionB) {
VDisaDiffPartitionInfo* info = partitionB ? partitionB_info : partitionA_info; VDisaDiffPartitionInfo* info = partitionB ? partitionB_info : partitionA_info;
if (!info) return 1; if (!info) return 1;
if (info->n_ivfc_ranges == 0) if (info->n_ivfc_ranges == 0)
return 0; return 0;
DisaDiffIvfcRange* ivfc_range_buffer = malloc(sizeof(DisaDiffIvfcRange) * MAX_IVFC_RANGES); DisaDiffIvfcRange* ivfc_range_buffer = malloc(sizeof(DisaDiffIvfcRange) * MAX_IVFC_RANGES);
if (!ivfc_range_buffer) if (!ivfc_range_buffer)
return 1; return 1;
u32 n_ivfc_ranges = 0; u32 n_ivfc_ranges = 0;
for (u32 i = 0; i < info->n_ivfc_ranges; i++) { for (u32 i = 0; i < info->n_ivfc_ranges; i++) {
if (MergeDisaDiffIvfcRange(info->ivfc_lvl4_ranges[i], ivfc_range_buffer, &n_ivfc_ranges) != 0) if (MergeDisaDiffIvfcRange(info->ivfc_lvl4_ranges[i], ivfc_range_buffer, &n_ivfc_ranges) != 0)
return 1; return 1;
} }
for (u32 level = 4; level > 0; level--) { for (u32 level = 4; level > 0; level--) {
u32 next_n_ivfc_ranges = 0; u32 next_n_ivfc_ranges = 0;
DisaDiffIvfcRange* ivfc_ranges = (level % 2) ? info->ivfc_lvl4_ranges : ivfc_range_buffer; DisaDiffIvfcRange* ivfc_ranges = (level % 2) ? info->ivfc_lvl4_ranges : ivfc_range_buffer;
DisaDiffIvfcRange* next_ivfc_ranges = (level % 2) ? ivfc_range_buffer : info->ivfc_lvl4_ranges; DisaDiffIvfcRange* next_ivfc_ranges = (level % 2) ? ivfc_range_buffer : info->ivfc_lvl4_ranges;
for (u32 j = 0; j < n_ivfc_ranges; j++) { for (u32 j = 0; j < n_ivfc_ranges; j++) {
DisaDiffIvfcRange next_range; DisaDiffIvfcRange next_range;
if (FixDisaDiffIvfcLevel(&(info->rw_info), level, ivfc_ranges[j].offset, ivfc_ranges[j].size, &(next_range.offset), &(next_range.size)) != 0) if (FixDisaDiffIvfcLevel(&(info->rw_info), level, ivfc_ranges[j].offset, ivfc_ranges[j].size, &(next_range.offset), &(next_range.size)) != 0)
return 1; return 1;
if (next_ivfc_ranges) { if (next_ivfc_ranges) {
AlignDisaDiffIvfcRange(&next_range, (&(info->rw_info.log_ivfc_lvl1))[level - 2]); AlignDisaDiffIvfcRange(&next_range, (&(info->rw_info.log_ivfc_lvl1))[level - 2]);
if (MergeDisaDiffIvfcRange(next_range, next_ivfc_ranges, &next_n_ivfc_ranges) != 0) if (MergeDisaDiffIvfcRange(next_range, next_ivfc_ranges, &next_n_ivfc_ranges) != 0)
@ -119,12 +119,12 @@ static u32 FixVDisaDiffIvfcHashChain(bool partitionB) {
} }
n_ivfc_ranges = next_n_ivfc_ranges; n_ivfc_ranges = next_n_ivfc_ranges;
} }
free(ivfc_range_buffer); free(ivfc_range_buffer);
if (FixDisaDiffIvfcLevel(&(info->rw_info), 0, 0, 0, NULL, NULL) != 0) if (FixDisaDiffIvfcLevel(&(info->rw_info), 0, 0, 0, NULL, NULL) != 0)
return 1; return 1;
info->n_ivfc_ranges = 0; info->n_ivfc_ranges = 0;
return 0; return 0;
} }
@ -137,7 +137,7 @@ void DeinitVDisaDiffDrive(void) {
free(partitionA_info); free(partitionA_info);
partitionA_info = NULL; partitionA_info = NULL;
} }
if (partitionB_info) { if (partitionB_info) {
FixVDisaDiffIvfcHashChain(true); FixVDisaDiffIvfcHashChain(true);
if (partitionB_info->rw_info.dpfs_lvl2_cache) if (partitionB_info->rw_info.dpfs_lvl2_cache)
@ -150,28 +150,28 @@ void DeinitVDisaDiffDrive(void) {
u64 InitVDisaDiffDrive(void) { u64 InitVDisaDiffDrive(void) {
DisaDiffRWInfo info; DisaDiffRWInfo info;
u64 type; u64 type;
DeinitVDisaDiffDrive(); DeinitVDisaDiffDrive();
if (!(type = (GetMountState() & (SYS_DISA | SYS_DIFF)))) if (!(type = (GetMountState() & (SYS_DISA | SYS_DIFF))))
return 0; return 0;
if ((GetDisaDiffRWInfo(NULL, &info, false) != 0) || if ((GetDisaDiffRWInfo(NULL, &info, false) != 0) ||
(!(info.dpfs_lvl2_cache = (u8*) malloc(info.size_dpfs_lvl2)) || (!(info.dpfs_lvl2_cache = (u8*) malloc(info.size_dpfs_lvl2)) ||
(BuildDisaDiffDpfsLvl2Cache(NULL, &info, info.dpfs_lvl2_cache, info.size_dpfs_lvl2) != 0))) { (BuildDisaDiffDpfsLvl2Cache(NULL, &info, info.dpfs_lvl2_cache, info.size_dpfs_lvl2) != 0))) {
free(info.dpfs_lvl2_cache); free(info.dpfs_lvl2_cache);
return 0; return 0;
} }
if (!(partitionA_info = malloc(sizeof(VDisaDiffPartitionInfo)))) { if (!(partitionA_info = malloc(sizeof(VDisaDiffPartitionInfo)))) {
free(info.dpfs_lvl2_cache); free(info.dpfs_lvl2_cache);
partitionA_info = NULL; partitionA_info = NULL;
return 0; return 0;
} }
memset(partitionA_info, 0, sizeof(VDisaDiffPartitionInfo)); memset(partitionA_info, 0, sizeof(VDisaDiffPartitionInfo));
partitionA_info->rw_info = info; partitionA_info->rw_info = info;
if ((type & SYS_DISA) && (GetDisaDiffRWInfo(NULL, &info, true) == 0)) { if ((type & SYS_DISA) && (GetDisaDiffRWInfo(NULL, &info, true) == 0)) {
if (!(info.dpfs_lvl2_cache = (u8*) malloc(info.size_dpfs_lvl2)) || if (!(info.dpfs_lvl2_cache = (u8*) malloc(info.size_dpfs_lvl2)) ||
(BuildDisaDiffDpfsLvl2Cache(NULL, &info, info.dpfs_lvl2_cache, info.size_dpfs_lvl2) != 0)) { (BuildDisaDiffDpfsLvl2Cache(NULL, &info, info.dpfs_lvl2_cache, info.size_dpfs_lvl2) != 0)) {
@ -180,7 +180,7 @@ u64 InitVDisaDiffDrive(void) {
partitionA_info = NULL; partitionA_info = NULL;
return 0; return 0;
} }
if (!(partitionB_info = malloc(sizeof(VDisaDiffPartitionInfo)))) { if (!(partitionB_info = malloc(sizeof(VDisaDiffPartitionInfo)))) {
if (info.dpfs_lvl2_cache) free(info.dpfs_lvl2_cache); if (info.dpfs_lvl2_cache) free(info.dpfs_lvl2_cache);
if (partitionA_info->rw_info.dpfs_lvl2_cache) free(partitionA_info->rw_info.dpfs_lvl2_cache); if (partitionA_info->rw_info.dpfs_lvl2_cache) free(partitionA_info->rw_info.dpfs_lvl2_cache);
@ -189,19 +189,19 @@ u64 InitVDisaDiffDrive(void) {
partitionB_info = NULL; partitionB_info = NULL;
return 0; return 0;
} }
memset(partitionB_info, 0, sizeof(VDisaDiffPartitionInfo)); memset(partitionB_info, 0, sizeof(VDisaDiffPartitionInfo));
partitionB_info->rw_info = info; partitionB_info->rw_info = info;
} }
InitVBDRIDrive(); InitVBDRIDrive();
return type; return type;
} }
u64 CheckVDisaDiffDrive(void) { u64 CheckVDisaDiffDrive(void) {
u64 type = GetMountState(); u64 type = GetMountState();
return ((type & (SYS_DISA | SYS_DIFF)) && partitionA_info) ? type : 0; return ((type & (SYS_DISA | SYS_DIFF)) && partitionA_info) ? type : 0;
} }
@ -209,17 +209,17 @@ u64 CheckVDisaDiffDrive(void) {
bool ReadVDisaDiffDir(VirtualFile* vfile, VirtualDir* vdir) { bool ReadVDisaDiffDir(VirtualFile* vfile, VirtualDir* vdir) {
if (++(vdir->index) > 1) if (++(vdir->index) > 1)
return false; return false;
VDisaDiffPartitionInfo* info = vdir->index ? partitionB_info : partitionA_info; VDisaDiffPartitionInfo* info = vdir->index ? partitionB_info : partitionA_info;
if (!info) if (!info)
return false; return false;
memset(vfile, 0, sizeof(VirtualFile)); memset(vfile, 0, sizeof(VirtualFile));
snprintf(vfile->name, 32, "partition%c.bin", vdir->index ? 'B' : 'A'); snprintf(vfile->name, 32, "partition%c.bin", vdir->index ? 'B' : 'A');
vfile->size = info->rw_info.size_ivfc_lvl4; vfile->size = info->rw_info.size_ivfc_lvl4;
if (vdir->index) vfile->flags |= VFLAG_PARTITION_B; if (vdir->index) vfile->flags |= VFLAG_PARTITION_B;
return true; return true;
} }
@ -234,9 +234,9 @@ int ReadVDisaDiffFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 co
int WriteVDisaDiffFile(const VirtualFile* vfile, const void* buffer, u64 offset, u64 count) { int WriteVDisaDiffFile(const VirtualFile* vfile, const void* buffer, u64 offset, u64 count) {
VDisaDiffPartitionInfo* info = (vfile->flags & VFLAG_PARTITION_B) ? partitionB_info : partitionA_info; VDisaDiffPartitionInfo* info = (vfile->flags & VFLAG_PARTITION_B) ? partitionB_info : partitionA_info;
if (!info) return 1; if (!info) return 1;
if (WriteDisaDiffIvfcLvl4(NULL, &(info->rw_info), offset, count, buffer) != count) return 1; if (WriteDisaDiffIvfcLvl4(NULL, &(info->rw_info), offset, count, buffer) != count) return 1;
DisaDiffIvfcRange range; DisaDiffIvfcRange range;
range.offset = offset; range.offset = offset;
range.size = count; range.size = count;
@ -245,6 +245,6 @@ int WriteVDisaDiffFile(const VirtualFile* vfile, const void* buffer, u64 offset,
((FixVDisaDiffIvfcHashChain(vfile->flags & VFLAG_PARTITION_B) != 0) || ((FixVDisaDiffIvfcHashChain(vfile->flags & VFLAG_PARTITION_B) != 0) ||
(MergeDisaDiffIvfcRange(range, info->ivfc_lvl4_ranges, &(info->n_ivfc_ranges)) != 0))) (MergeDisaDiffIvfcRange(range, info->ivfc_lvl4_ranges, &(info->n_ivfc_ranges)) != 0)))
return 1; return 1;
return 0; return 0;
} }

View File

@ -126,7 +126,7 @@ int ReadCbcImageBlocks(void* buffer, u64 block, u64 count, u8* iv0, u64 block0)
if (block == block0) memcpy(ctr, iv0, AES_BLOCK_SIZE); if (block == block0) memcpy(ctr, iv0, AES_BLOCK_SIZE);
else if ((ret = ReadImageBytes(ctr, (block-1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE)) != 0) else if ((ret = ReadImageBytes(ctr, (block-1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE)) != 0)
return ret; return ret;
u32 mode = AES_CNT_TITLEKEY_DECRYPT_MODE; u32 mode = AES_CNT_TITLEKEY_DECRYPT_MODE;
cbc_decrypt(buffer, buffer, count, mode, ctr); cbc_decrypt(buffer, buffer, count, mode, ctr);
} }
@ -139,7 +139,7 @@ int ReadCbcImageBytes(void* buffer, u64 offset, u64 count, u8* iv0, u64 offset0)
u8 __attribute__((aligned(32))) temp[AES_BLOCK_SIZE]; u8 __attribute__((aligned(32))) temp[AES_BLOCK_SIZE];
u8* buffer8 = (u8*) buffer; u8* buffer8 = (u8*) buffer;
int ret = 0; int ret = 0;
if (off_fix) { // misaligned offset (at beginning) if (off_fix) { // misaligned offset (at beginning)
u32 fix_byte = ((off_fix + count) >= AES_BLOCK_SIZE) ? AES_BLOCK_SIZE - off_fix : count; u32 fix_byte = ((off_fix + count) >= AES_BLOCK_SIZE) ? AES_BLOCK_SIZE - off_fix : count;
if ((ret = ReadCbcImageBlocks(temp, offset / AES_BLOCK_SIZE, 1, iv0, block0)) != 0) if ((ret = ReadCbcImageBlocks(temp, offset / AES_BLOCK_SIZE, 1, iv0, block0)) != 0)
@ -149,7 +149,7 @@ int ReadCbcImageBytes(void* buffer, u64 offset, u64 count, u8* iv0, u64 offset0)
offset += fix_byte; offset += fix_byte;
count -= fix_byte; count -= fix_byte;
} }
if (count >= AES_BLOCK_SIZE) { if (count >= AES_BLOCK_SIZE) {
u64 blocks = count / AES_BLOCK_SIZE; u64 blocks = count / AES_BLOCK_SIZE;
if ((ret = ReadCbcImageBlocks(buffer8, offset / AES_BLOCK_SIZE, blocks, iv0, block0)) != 0) if ((ret = ReadCbcImageBlocks(buffer8, offset / AES_BLOCK_SIZE, blocks, iv0, block0)) != 0)
@ -158,14 +158,14 @@ int ReadCbcImageBytes(void* buffer, u64 offset, u64 count, u8* iv0, u64 offset0)
offset += AES_BLOCK_SIZE * blocks; offset += AES_BLOCK_SIZE * blocks;
count -= AES_BLOCK_SIZE * blocks; count -= AES_BLOCK_SIZE * blocks;
} }
if (count) { // misaligned offset (at end) if (count) { // misaligned offset (at end)
if ((ret = ReadCbcImageBlocks(temp, offset / AES_BLOCK_SIZE, 1, iv0, block0)) != 0) if ((ret = ReadCbcImageBlocks(temp, offset / AES_BLOCK_SIZE, 1, iv0, block0)) != 0)
return ret; return ret;
memcpy(buffer8, temp, count); memcpy(buffer8, temp, count);
count = 0; count = 0;
} }
return ret; return ret;
} }
@ -175,12 +175,12 @@ int ReadCiaContentImageBytes(void* buffer, u64 offset, u64 count, u32 cia_cnt_id
memcpy(tik, cia_titlekey, 16); memcpy(tik, cia_titlekey, 16);
setup_aeskey(0x11, tik); setup_aeskey(0x11, tik);
use_aeskey(0x11); use_aeskey(0x11);
// setup IV0 // setup IV0
u8 iv0[AES_BLOCK_SIZE] = { 0 }; u8 iv0[AES_BLOCK_SIZE] = { 0 };
iv0[0] = (cia_cnt_idx >> 8) & 0xFF; iv0[0] = (cia_cnt_idx >> 8) & 0xFF;
iv0[1] = (cia_cnt_idx >> 0) & 0xFF; iv0[1] = (cia_cnt_idx >> 0) & 0xFF;
// continue in next function // continue in next function
u8* iv0_ptr = (cia_cnt_idx <= 0xFFFF) ? iv0 : NULL; u8* iv0_ptr = (cia_cnt_idx <= 0xFFFF) ? iv0 : NULL;
return ReadCbcImageBytes(buffer, offset, count, iv0_ptr, offset0); return ReadCbcImageBytes(buffer, offset, count, iv0_ptr, offset0);
@ -205,7 +205,7 @@ int ReadNcchImageBytes(void* buffer, u64 offset, u64 count) {
bool BuildVGameExeFsDir(void) { bool BuildVGameExeFsDir(void) {
VirtualFile* templates = templates_exefs; VirtualFile* templates = templates_exefs;
u32 n = 0; u32 n = 0;
for (u32 i = 0; i < 10; i++) { for (u32 i = 0; i < 10; i++) {
ExeFsFileHeader* file = exefs->files + i; ExeFsFileHeader* file = exefs->files + i;
if (file->size == 0) continue; if (file->size == 0) continue;
@ -217,7 +217,7 @@ bool BuildVGameExeFsDir(void) {
templates[n].flags = VFLAG_EXEFS_FILE; templates[n].flags = VFLAG_EXEFS_FILE;
n++; n++;
} }
n_templates_exefs = n; n_templates_exefs = n;
return true; return true;
} }
@ -225,10 +225,10 @@ bool BuildVGameExeFsDir(void) {
bool BuildVGameNcchDir(void) { bool BuildVGameNcchDir(void) {
VirtualFile* templates = templates_ncch; VirtualFile* templates = templates_ncch;
u32 n = 0; u32 n = 0;
// NCCH crypto // NCCH crypto
bool ncch_crypto = (NCCH_ENCRYPTED(ncch)) && (SetupNcchCrypto(ncch, NCCH_NOCRYPTO) == 0); bool ncch_crypto = (NCCH_ENCRYPTED(ncch)) && (SetupNcchCrypto(ncch, NCCH_NOCRYPTO) == 0);
// header // header
strncpy(templates[n].name, NAME_NCCH_HEADER, 32); strncpy(templates[n].name, NAME_NCCH_HEADER, 32);
templates[n].offset = offset_ncch + 0; templates[n].offset = offset_ncch + 0;
@ -236,7 +236,7 @@ bool BuildVGameNcchDir(void) {
templates[n].keyslot = 0xFF; templates[n].keyslot = 0xFF;
templates[n].flags = VFLAG_NCCH; templates[n].flags = VFLAG_NCCH;
n++; n++;
// extended header // extended header
if (ncch->size_exthdr) { if (ncch->size_exthdr) {
strncpy(templates[n].name, NAME_NCCH_EXTHEADER, 32); strncpy(templates[n].name, NAME_NCCH_EXTHEADER, 32);
@ -246,7 +246,7 @@ bool BuildVGameNcchDir(void) {
templates[n].flags = VFLAG_EXTHDR; templates[n].flags = VFLAG_EXTHDR;
n++; n++;
} }
// plain region // plain region
if (ncch->size_plain) { if (ncch->size_plain) {
strncpy(templates[n].name, NAME_NCCH_PLAIN, 32); strncpy(templates[n].name, NAME_NCCH_PLAIN, 32);
@ -256,7 +256,7 @@ bool BuildVGameNcchDir(void) {
templates[n].flags = VFLAG_NCCH; templates[n].flags = VFLAG_NCCH;
n++; n++;
} }
// logo region // logo region
if (ncch->size_logo) { if (ncch->size_logo) {
strncpy(templates[n].name, NAME_NCCH_LOGO, 32); strncpy(templates[n].name, NAME_NCCH_LOGO, 32);
@ -266,7 +266,7 @@ bool BuildVGameNcchDir(void) {
templates[n].flags = VFLAG_NCCH; templates[n].flags = VFLAG_NCCH;
n++; n++;
} }
// exefs // exefs
if (ncch->size_exefs) { if (ncch->size_exefs) {
strncpy(templates[n].name, NAME_NCCH_EXEFS, 32); strncpy(templates[n].name, NAME_NCCH_EXEFS, 32);
@ -282,7 +282,7 @@ bool BuildVGameNcchDir(void) {
n++; n++;
} }
} }
// romfs // romfs
if (ncch->size_romfs) { if (ncch->size_romfs) {
strncpy(templates[n].name, NAME_NCCH_ROMFS, 32); strncpy(templates[n].name, NAME_NCCH_ROMFS, 32);
@ -298,16 +298,16 @@ bool BuildVGameNcchDir(void) {
n++; n++;
} }
} }
n_templates_ncch = n; n_templates_ncch = n;
return true; return true;
} }
bool BuildVGameNcsdDir(void) { bool BuildVGameNcsdDir(void) {
const char* name_type[] = { NAME_NCSD_TYPES }; const char* name_type[] = { NAME_NCSD_TYPES };
VirtualFile* templates = templates_ncsd; VirtualFile* templates = templates_ncsd;
u32 n = 0; u32 n = 0;
// header // header
strncpy(templates[n].name, NAME_NCSD_HEADER, 32); strncpy(templates[n].name, NAME_NCSD_HEADER, 32);
templates[n].offset = 0; templates[n].offset = 0;
@ -315,7 +315,7 @@ bool BuildVGameNcsdDir(void) {
templates[n].keyslot = 0xFF; templates[n].keyslot = 0xFF;
templates[n].flags = 0; templates[n].flags = 0;
n++; n++;
// card info header // card info header
if (ncsd->partitions[0].offset * NCSD_MEDIA_UNIT >= NCSD_CINFO_OFFSET + NCSD_CINFO_SIZE) { if (ncsd->partitions[0].offset * NCSD_MEDIA_UNIT >= NCSD_CINFO_OFFSET + NCSD_CINFO_SIZE) {
strncpy(templates[n].name, NAME_NCSD_CARDINFO, 32); strncpy(templates[n].name, NAME_NCSD_CARDINFO, 32);
@ -325,7 +325,7 @@ bool BuildVGameNcsdDir(void) {
templates[n].flags = 0; templates[n].flags = 0;
n++; n++;
} }
// dev info header // dev info header
if (ncsd->partitions[0].offset * NCSD_MEDIA_UNIT >= NCSD_DINFO_OFFSET + NCSD_DINFO_SIZE) { if (ncsd->partitions[0].offset * NCSD_MEDIA_UNIT >= NCSD_DINFO_OFFSET + NCSD_DINFO_SIZE) {
strncpy(templates[n].name, NAME_NCSD_DEVINFO, 32); strncpy(templates[n].name, NAME_NCSD_DEVINFO, 32);
@ -335,7 +335,7 @@ bool BuildVGameNcsdDir(void) {
templates[n].flags = 0; templates[n].flags = 0;
n++; n++;
} }
// contents // contents
for (u32 i = 0; i < 8; i++) { for (u32 i = 0; i < 8; i++) {
NcchPartition* partition = ncsd->partitions + i; NcchPartition* partition = ncsd->partitions + i;
@ -352,7 +352,7 @@ bool BuildVGameNcsdDir(void) {
templates[n].flags |= (VFLAG_NCCH | VFLAG_DIR); templates[n].flags |= (VFLAG_NCCH | VFLAG_DIR);
n++; n++;
} }
n_templates_ncsd = n; n_templates_ncsd = n;
return true; return true;
} }
@ -361,10 +361,10 @@ bool BuildVGameCiaDir(void) {
CiaInfo info; CiaInfo info;
VirtualFile* templates = templates_cia; VirtualFile* templates = templates_cia;
u32 n = 0; u32 n = 0;
if (GetCiaInfo(&info, &(cia->header)) != 0) if (GetCiaInfo(&info, &(cia->header)) != 0)
return false; return false;
// header // header
strncpy(templates[n].name, NAME_CIA_HEADER, 32); strncpy(templates[n].name, NAME_CIA_HEADER, 32);
templates[n].offset = 0; templates[n].offset = 0;
@ -372,7 +372,7 @@ bool BuildVGameCiaDir(void) {
templates[n].keyslot = 0xFF; templates[n].keyslot = 0xFF;
templates[n].flags = VFLAG_NO_CRYPTO; templates[n].flags = VFLAG_NO_CRYPTO;
n++; n++;
// certificates // certificates
if (info.size_cert) { if (info.size_cert) {
strncpy(templates[n].name, NAME_CIA_CERT, 32); strncpy(templates[n].name, NAME_CIA_CERT, 32);
@ -382,7 +382,7 @@ bool BuildVGameCiaDir(void) {
templates[n].flags = VFLAG_NO_CRYPTO; templates[n].flags = VFLAG_NO_CRYPTO;
n++; n++;
} }
// ticket // ticket
if (info.size_ticket) { if (info.size_ticket) {
strncpy(templates[n].name, NAME_CIA_TICKET, 32); strncpy(templates[n].name, NAME_CIA_TICKET, 32);
@ -392,7 +392,7 @@ bool BuildVGameCiaDir(void) {
templates[n].flags = VFLAG_NO_CRYPTO; templates[n].flags = VFLAG_NO_CRYPTO;
n++; n++;
} }
// TMD (the full thing) // TMD (the full thing)
if (info.size_tmd) { if (info.size_tmd) {
strncpy(templates[n].name, NAME_CIA_TMD, 32); strncpy(templates[n].name, NAME_CIA_TMD, 32);
@ -402,7 +402,7 @@ bool BuildVGameCiaDir(void) {
templates[n].flags = VFLAG_NO_CRYPTO; templates[n].flags = VFLAG_NO_CRYPTO;
n++; n++;
} }
// TMD content chunks // TMD content chunks
if (info.size_content_list) { if (info.size_content_list) {
strncpy(templates[n].name, NAME_CIA_TMDCHUNK, 32); strncpy(templates[n].name, NAME_CIA_TMDCHUNK, 32);
@ -412,7 +412,7 @@ bool BuildVGameCiaDir(void) {
templates[n].flags = VFLAG_NO_CRYPTO; templates[n].flags = VFLAG_NO_CRYPTO;
n++; n++;
} }
// meta // meta
if (info.size_meta) { if (info.size_meta) {
strncpy(templates[n].name, NAME_CIA_META, 32); strncpy(templates[n].name, NAME_CIA_META, 32);
@ -428,7 +428,7 @@ bool BuildVGameCiaDir(void) {
templates[n].flags = VFLAG_NO_CRYPTO; templates[n].flags = VFLAG_NO_CRYPTO;
n++; n++;
} }
// contents // contents
if (info.size_content) { if (info.size_content) {
TmdContentChunk* content_list = cia->content_list; TmdContentChunk* content_list = cia->content_list;
@ -443,7 +443,7 @@ bool BuildVGameCiaDir(void) {
if (!(cnt_index[index/8] & (1 << (7-(index%8))))) if (!(cnt_index[index/8] & (1 << (7-(index%8)))))
continue; // skip missing contents continue; // skip missing contents
u32 cnt_type = 0; u32 cnt_type = 0;
if (size >= 0x200) { if (size >= 0x200) {
u8 header[0x200]; u8 header[0x200];
@ -451,25 +451,25 @@ bool BuildVGameCiaDir(void) {
cnt_type = (ValidateNcchHeader((NcchHeader*)(void*)header) == 0) ? VFLAG_NCCH : cnt_type = (ValidateNcchHeader((NcchHeader*)(void*)header) == 0) ? VFLAG_NCCH :
(ValidateTwlHeader((TwlHeader*)(void*)header) == 0) ? VFLAG_NDS : 0; (ValidateTwlHeader((TwlHeader*)(void*)header) == 0) ? VFLAG_NDS : 0;
} }
snprintf(templates[n].name, 32, NAME_CIA_CONTENT, index, id, ".app"); snprintf(templates[n].name, 32, NAME_CIA_CONTENT, index, id, ".app");
templates[n].offset = next_offset; templates[n].offset = next_offset;
templates[n].size = size; templates[n].size = size;
templates[n].keyslot = keyslot; // keyslot is used for CIA content index here templates[n].keyslot = keyslot; // keyslot is used for CIA content index here
templates[n].flags = VFLAG_CIA_CONTENT; templates[n].flags = VFLAG_CIA_CONTENT;
n++; n++;
if (cnt_type) { if (cnt_type) {
memcpy(templates + n, templates + n - 1, sizeof(VirtualFile)); memcpy(templates + n, templates + n - 1, sizeof(VirtualFile));
snprintf(templates[n].name, 32, NAME_CIA_CONTENT, index, id, ""); snprintf(templates[n].name, 32, NAME_CIA_CONTENT, index, id, "");
templates[n].flags |= (cnt_type | VFLAG_DIR); templates[n].flags |= (cnt_type | VFLAG_DIR);
n++; n++;
} }
next_offset += size; next_offset += size;
} }
} }
n_templates_cia = n; n_templates_cia = n;
return true; return true;
} }
@ -477,7 +477,7 @@ bool BuildVGameCiaDir(void) {
bool BuildVGameNdsDir(void) { bool BuildVGameNdsDir(void) {
VirtualFile* templates = templates_nds; VirtualFile* templates = templates_nds;
u32 n = 0; u32 n = 0;
// header // header
strncpy(templates[n].name, NAME_NDS_HEADER, 32); strncpy(templates[n].name, NAME_NDS_HEADER, 32);
templates[n].offset = offset_nds + 0; templates[n].offset = offset_nds + 0;
@ -485,7 +485,7 @@ bool BuildVGameNdsDir(void) {
templates[n].keyslot = 0xFF; templates[n].keyslot = 0xFF;
templates[n].flags = 0; templates[n].flags = 0;
n++; n++;
// banner // banner
if (twl->icon_offset) { if (twl->icon_offset) {
u16 v = 0; u16 v = 0;
@ -497,7 +497,7 @@ bool BuildVGameNdsDir(void) {
templates[n].flags = 0; templates[n].flags = 0;
n++; n++;
} }
// ARM9 section (+ ARM9 section footer) // ARM9 section (+ ARM9 section footer)
if (twl->arm9_size) { if (twl->arm9_size) {
u32 f = 0; u32 f = 0;
@ -510,7 +510,7 @@ bool BuildVGameNdsDir(void) {
templates[n].flags = 0; templates[n].flags = 0;
n++; n++;
} }
// ARM9 overlay section // ARM9 overlay section
if (twl->arm9_overlay_size) { if (twl->arm9_overlay_size) {
strncpy(templates[n].name, NAME_NDS_ARM9OVL, 32); strncpy(templates[n].name, NAME_NDS_ARM9OVL, 32);
@ -520,7 +520,7 @@ bool BuildVGameNdsDir(void) {
templates[n].flags = 0; templates[n].flags = 0;
n++; n++;
} }
// ARM9i section // ARM9i section
if ((twl->unit_code != TWL_UNITCODE_NTR) && (twl->arm9i_size)) { if ((twl->unit_code != TWL_UNITCODE_NTR) && (twl->arm9i_size)) {
strncpy(templates[n].name, NAME_NDS_ARM9I, 32); strncpy(templates[n].name, NAME_NDS_ARM9I, 32);
@ -530,7 +530,7 @@ bool BuildVGameNdsDir(void) {
templates[n].flags = 0; templates[n].flags = 0;
n++; n++;
} }
// ARM7 section // ARM7 section
if (twl->arm7_size) { if (twl->arm7_size) {
strncpy(templates[n].name, NAME_NDS_ARM7, 32); strncpy(templates[n].name, NAME_NDS_ARM7, 32);
@ -540,7 +540,7 @@ bool BuildVGameNdsDir(void) {
templates[n].flags = 0; templates[n].flags = 0;
n++; n++;
} }
// ARM7 overlay section // ARM7 overlay section
if (twl->arm7_overlay_size) { if (twl->arm7_overlay_size) {
strncpy(templates[n].name, NAME_NDS_ARM7OVL, 32); strncpy(templates[n].name, NAME_NDS_ARM7OVL, 32);
@ -550,7 +550,7 @@ bool BuildVGameNdsDir(void) {
templates[n].flags = 0; templates[n].flags = 0;
n++; n++;
} }
// ARM7i section // ARM7i section
if ((twl->unit_code != TWL_UNITCODE_NTR) && (twl->arm7i_size)) { if ((twl->unit_code != TWL_UNITCODE_NTR) && (twl->arm7i_size)) {
strncpy(templates[n].name, NAME_NDS_ARM7I, 32); strncpy(templates[n].name, NAME_NDS_ARM7I, 32);
@ -560,7 +560,7 @@ bool BuildVGameNdsDir(void) {
templates[n].flags = 0; templates[n].flags = 0;
n++; n++;
} }
// data // data
if (twl->fnt_size && twl->fat_size && (twl->fnt_offset < twl->fat_offset)) { if (twl->fnt_size && twl->fat_size && (twl->fnt_offset < twl->fat_offset)) {
strncpy(templates[n].name, NAME_NDS_DATADIR, 32); strncpy(templates[n].name, NAME_NDS_DATADIR, 32);
@ -570,7 +570,7 @@ bool BuildVGameNdsDir(void) {
templates[n].flags = VFLAG_NITRO_DIR | VFLAG_DIR; templates[n].flags = VFLAG_NITRO_DIR | VFLAG_DIR;
n++; n++;
} }
n_templates_nds = n; n_templates_nds = n;
return true; return true;
} }
@ -578,7 +578,7 @@ bool BuildVGameNdsDir(void) {
bool BuildVGameFirmDir(void) { bool BuildVGameFirmDir(void) {
VirtualFile* templates = templates_firm; VirtualFile* templates = templates_firm;
u32 n = 0; u32 n = 0;
// header // header
strncpy(templates[n].name, NAME_FIRM_HEADER, 32); strncpy(templates[n].name, NAME_FIRM_HEADER, 32);
templates[n].offset = 0; templates[n].offset = 0;
@ -586,7 +586,7 @@ bool BuildVGameFirmDir(void) {
templates[n].keyslot = 0xFF; templates[n].keyslot = 0xFF;
templates[n].flags = 0; templates[n].flags = 0;
n++; n++;
// arm9 binary (only for encrypted) // arm9 binary (only for encrypted)
if (offset_a9bin != (u64) -1) { if (offset_a9bin != (u64) -1) {
strncpy(templates[n].name, NAME_FIRM_ARM9BIN, 32); strncpy(templates[n].name, NAME_FIRM_ARM9BIN, 32);
@ -596,7 +596,7 @@ bool BuildVGameFirmDir(void) {
templates[n].flags = 0; templates[n].flags = 0;
n++; n++;
} }
// section binaries & NCCHs // section binaries & NCCHs
for (u32 i = 0; i < 4; i++) { for (u32 i = 0; i < 4; i++) {
FirmSectionHeader* section = firm->sections + i; FirmSectionHeader* section = firm->sections + i;
@ -614,7 +614,7 @@ bool BuildVGameFirmDir(void) {
NcchHeader p9_ncch; NcchHeader p9_ncch;
char name[8]; char name[8];
u32 offset_p9 = 0; u32 offset_p9 = 0;
u8* buffer = (u8*) malloc(section->size); u8* buffer = (u8*) malloc(section->size);
if (buffer) { if (buffer) {
if (ReadGameImageBytes(buffer, section->offset, section->size) != 0) break; if (ReadGameImageBytes(buffer, section->offset, section->size) != 0) break;
@ -627,7 +627,7 @@ bool BuildVGameFirmDir(void) {
} }
free(buffer); 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;
@ -651,7 +651,7 @@ bool BuildVGameFirmDir(void) {
(ReadImageBytes((u8*) name, section->offset + p + 0x200, 0x8) != 0) || (ReadImageBytes((u8*) name, section->offset + p + 0x200, 0x8) != 0) ||
(ValidateNcchHeader(&firm_ncch) != 0)) (ValidateNcchHeader(&firm_ncch) != 0))
break; break;
snprintf(templates[n].name, 32, NAME_FIRM_NCCH, firm_ncch.programId, name, ".app"); snprintf(templates[n].name, 32, NAME_FIRM_NCCH, firm_ncch.programId, name, ".app");
templates[n].offset = section->offset + p; templates[n].offset = section->offset + p;
templates[n].size = firm_ncch.size * NCCH_MEDIA_UNIT; templates[n].size = firm_ncch.size * NCCH_MEDIA_UNIT;
@ -666,7 +666,7 @@ bool BuildVGameFirmDir(void) {
} }
} }
} }
n_templates_firm = n; n_templates_firm = n;
return true; return true;
} }
@ -676,7 +676,7 @@ bool BuildVGameTadDir(void) {
VirtualFile* templates = templates_tad; VirtualFile* templates = templates_tad;
u32 content_offset = 0; u32 content_offset = 0;
u32 n = 0; u32 n = 0;
// read header, setup table // read header, setup table
u8 ALIGN(32) hdr_data[TAD_HEADER_LEN]; u8 ALIGN(32) hdr_data[TAD_HEADER_LEN];
TadHeader* hdr = (void*)hdr_data; TadHeader* hdr = (void*)hdr_data;
@ -686,7 +686,7 @@ bool BuildVGameTadDir(void) {
n_templates_tad = 0; n_templates_tad = 0;
return false; return false;
} }
// banner // banner
strncpy(templates[n].name, NAME_TAD_BANNER, 32); strncpy(templates[n].name, NAME_TAD_BANNER, 32);
templates[n].offset = content_offset; templates[n].offset = content_offset;
@ -695,7 +695,7 @@ bool BuildVGameTadDir(void) {
templates[n].flags = 0; templates[n].flags = 0;
content_offset = tbl.banner_end; content_offset = tbl.banner_end;
n++; n++;
// header // header
strncpy(templates[n].name, NAME_TAD_HEADER, 32); strncpy(templates[n].name, NAME_TAD_HEADER, 32);
templates[n].offset = content_offset; templates[n].offset = content_offset;
@ -704,7 +704,7 @@ bool BuildVGameTadDir(void) {
templates[n].flags = 0; templates[n].flags = 0;
content_offset = tbl.header_end; content_offset = tbl.header_end;
n++; n++;
// footer // footer
strncpy(templates[n].name, NAME_TAD_FOOTER, 32); strncpy(templates[n].name, NAME_TAD_FOOTER, 32);
templates[n].offset = content_offset; templates[n].offset = content_offset;
@ -713,7 +713,7 @@ bool BuildVGameTadDir(void) {
templates[n].flags = 0; templates[n].flags = 0;
content_offset = tbl.footer_end; content_offset = tbl.footer_end;
n++; n++;
// contents // contents
for (u32 i = 0; i < TAD_NUM_CONTENT; content_offset = tbl.content_end[i++]) { for (u32 i = 0; i < TAD_NUM_CONTENT; content_offset = tbl.content_end[i++]) {
if (!hdr->content_size[i]) continue; // nothing in section if (!hdr->content_size[i]) continue; // nothing in section
@ -731,7 +731,7 @@ bool BuildVGameTadDir(void) {
n++; n++;
} }
} }
n_templates_tad = n; n_templates_tad = n;
return true; return true;
} }
@ -745,10 +745,10 @@ void DeinitVGameDrive(void) {
u64 InitVGameDrive(void) { // prerequisite: game file mounted as image u64 InitVGameDrive(void) { // prerequisite: game file mounted as image
u64 type = GetMountState(); u64 type = GetMountState();
vgame_type = 0; vgame_type = 0;
DeinitVGameDrive(); DeinitVGameDrive();
offset_firm = (u64) -1; offset_firm = (u64) -1;
offset_a9bin = (u64) -1; offset_a9bin = (u64) -1;
offset_cia = (u64) -1; offset_cia = (u64) -1;
@ -761,7 +761,7 @@ u64 InitVGameDrive(void) { // prerequisite: game file mounted as image
offset_nds = (u64) -1; offset_nds = (u64) -1;
offset_nitro = (u64) -1; offset_nitro = (u64) -1;
offset_tad = (u64) -1; offset_tad = (u64) -1;
base_vdir = base_vdir =
(type & SYS_FIRM ) ? VFLAG_FIRM : (type & SYS_FIRM ) ? VFLAG_FIRM :
(type & GAME_CIA ) ? VFLAG_CIA : (type & GAME_CIA ) ? VFLAG_CIA :
@ -792,7 +792,7 @@ u64 InitVGameDrive(void) { // prerequisite: game file mounted as image
ncch = (NcchHeader*) (void*) (((u8*) vgame_buffer) + 0x3FC00); // 512 byte reserved ncch = (NcchHeader*) (void*) (((u8*) vgame_buffer) + 0x3FC00); // 512 byte reserved
exefs = (ExeFsHeader*) (void*) (((u8*) vgame_buffer) + 0x3FE00); // 512 byte reserved exefs = (ExeFsHeader*) (void*) (((u8*) vgame_buffer) + 0x3FE00); // 512 byte reserved
// filesystem stuff (RomFS / NitroFS) will be allocated on demand // filesystem stuff (RomFS / NitroFS) will be allocated on demand
vgame_type = type; vgame_type = type;
return type; return type;
} }
@ -816,7 +816,7 @@ bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) {
vdir->size = ventry->size; vdir->size = ventry->size;
vdir->flags = ventry->flags; vdir->flags = ventry->flags;
} }
// CIA content special handling // CIA content special handling
if (vdir->flags & VFLAG_CIA) { // disable content crypto if (vdir->flags & VFLAG_CIA) { // disable content crypto
offset_ccnt = (u64) -1; offset_ccnt = (u64) -1;
@ -825,7 +825,7 @@ bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) {
offset_ccnt = vdir->offset; offset_ccnt = vdir->offset;
index_ccnt = ventry->keyslot; index_ccnt = ventry->keyslot;
} }
// build directories where required // build directories where required
if ((vdir->flags & VFLAG_FIRM) && (offset_firm != vdir->offset)) { if ((vdir->flags & VFLAG_FIRM) && (offset_firm != vdir->offset)) {
if ((ReadImageBytes((u8*) firm, 0, sizeof(FirmHeader)) != 0) || if ((ReadImageBytes((u8*) firm, 0, sizeof(FirmHeader)) != 0) ||
@ -921,8 +921,8 @@ bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) {
return false; return false;
offset_nitro = offset_nds; offset_nitro = offset_nds;
} }
// for romfs/nitro dir: switch to lv3/nitro object // for romfs/nitro dir: switch to lv3/nitro object
if (vdir->flags & (VFLAG_ROMFS|VFLAG_NITRO_DIR)) { if (vdir->flags & (VFLAG_ROMFS|VFLAG_NITRO_DIR)) {
vdir->index = -1; vdir->index = -1;
vdir->offset = 0; vdir->offset = 0;
@ -931,19 +931,19 @@ bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) {
if (vdir->flags & VFLAG_NITRO_DIR) vdir->flags |= VFLAG_NITRO; if (vdir->flags & VFLAG_NITRO_DIR) vdir->flags |= VFLAG_NITRO;
vdir->flags &= ~(VFLAG_ROMFS|VFLAG_NITRO_DIR); vdir->flags &= ~(VFLAG_ROMFS|VFLAG_NITRO_DIR);
} }
return true; return true;
} }
bool ReadVGameDirLv3(VirtualFile* vfile, VirtualDir* vdir) { bool ReadVGameDirLv3(VirtualFile* vfile, VirtualDir* vdir) {
vfile->name[0] = '\0'; vfile->name[0] = '\0';
vfile->flags = VFLAG_LV3 | VFLAG_READONLY; vfile->flags = VFLAG_LV3 | VFLAG_READONLY;
vfile->keyslot = ((offset_ncch != (u64) -1) && NCCH_ENCRYPTED(ncch)) ? vfile->keyslot = ((offset_ncch != (u64) -1) && NCCH_ENCRYPTED(ncch)) ?
0x2C : 0xFF; // actual keyslot may be different 0x2C : 0xFF; // actual keyslot may be different
// start from parent dir object // start from parent dir object
if (vdir->index == -1) vdir->index = 0; if (vdir->index == -1) vdir->index = 0;
// first child file object, skip if not available // first child file object, skip if not available
if (vdir->index == 0) { if (vdir->index == 0) {
RomFsLv3DirMeta* parent = LV3_GET_DIR(vdir->offset, &lv3idx); RomFsLv3DirMeta* parent = LV3_GET_DIR(vdir->offset, &lv3idx);
@ -958,7 +958,7 @@ bool ReadVGameDirLv3(VirtualFile* vfile, VirtualDir* vdir) {
return true; return true;
} else vdir->index = 2; } else vdir->index = 2;
} }
// parse sibling files // parse sibling files
if (vdir->index == 1) { if (vdir->index == 1) {
RomFsLv3FileMeta* current = LV3_GET_FILE(vdir->offset, &lv3idx); RomFsLv3FileMeta* current = LV3_GET_FILE(vdir->offset, &lv3idx);
@ -975,7 +975,7 @@ bool ReadVGameDirLv3(VirtualFile* vfile, VirtualDir* vdir) {
vdir->index = 2; vdir->index = 2;
} else return false; } else return false;
} }
// first child dir object, skip if not available // first child dir object, skip if not available
if (vdir->index == 2) { if (vdir->index == 2) {
RomFsLv3DirMeta* parent = LV3_GET_DIR(vdir->offset, &lv3idx); RomFsLv3DirMeta* parent = LV3_GET_DIR(vdir->offset, &lv3idx);
@ -988,7 +988,7 @@ bool ReadVGameDirLv3(VirtualFile* vfile, VirtualDir* vdir) {
return true; return true;
} else vdir->index = 4; } else vdir->index = 4;
} }
// parse sibling dirs // parse sibling dirs
if (vdir->index == 3) { if (vdir->index == 3) {
RomFsLv3DirMeta* current = LV3_GET_DIR(vdir->offset, &lv3idx); RomFsLv3DirMeta* current = LV3_GET_DIR(vdir->offset, &lv3idx);
@ -1003,18 +1003,18 @@ bool ReadVGameDirLv3(VirtualFile* vfile, VirtualDir* vdir) {
vdir->index = 4; vdir->index = 4;
} else return false; } else return false;
} }
return false; return false;
} }
bool ReadVGameDirNitro(VirtualFile* vfile, VirtualDir* vdir) { bool ReadVGameDirNitro(VirtualFile* vfile, VirtualDir* vdir) {
u8* fnt = vgame_fs_buffer; u8* fnt = vgame_fs_buffer;
u8* fat = vgame_fs_buffer + twl->fat_offset - twl->fnt_offset; u8* fat = vgame_fs_buffer + twl->fat_offset - twl->fnt_offset;
vfile->name[0] = '\0'; vfile->name[0] = '\0';
vfile->flags = VFLAG_NITRO | VFLAG_READONLY; vfile->flags = VFLAG_NITRO | VFLAG_READONLY;
vfile->keyslot = 0; vfile->keyslot = 0;
// start from parent dir object // start from parent dir object
if (vdir->index == -1) { if (vdir->index == -1) {
u8* fnt_entry = NULL; u8* fnt_entry = NULL;
@ -1025,7 +1025,7 @@ bool ReadVGameDirNitro(VirtualFile* vfile, VirtualDir* vdir) {
vdir->offset = (vdir->offset&0xFFFFFFFF) | (((u64)(fnt_entry - fnt)) << 32); // store offsets in offset vdir->offset = (vdir->offset&0xFFFFFFFF) | (((u64)(fnt_entry - fnt)) << 32); // store offsets in offset
} else vdir->index = -3; // error } else vdir->index = -3; // error
} }
// read directory entries until done // read directory entries until done
if (vdir->index >= 0) { if (vdir->index >= 0) {
u8* fnt_entry = fnt + (vdir->offset >> 32); u8* fnt_entry = fnt + (vdir->offset >> 32);
@ -1041,14 +1041,14 @@ bool ReadVGameDirNitro(VirtualFile* vfile, VirtualDir* vdir) {
vdir->offset = (vdir->offset&0xFFFFFFFF) | (((u64)(fnt_entry - fnt)) << 32); vdir->offset = (vdir->offset&0xFFFFFFFF) | (((u64)(fnt_entry - fnt)) << 32);
} else vdir->index = -2; // end of dir } else vdir->index = -2; // end of dir
} }
return (vdir->index >= 0); return (vdir->index >= 0);
} }
bool ReadVGameDir(VirtualFile* vfile, VirtualDir* vdir) { bool ReadVGameDir(VirtualFile* vfile, VirtualDir* vdir) {
VirtualFile* templates = NULL; VirtualFile* templates = NULL;
int n = 0; int n = 0;
if (vdir->flags & VFLAG_FIRM) { if (vdir->flags & VFLAG_FIRM) {
templates = templates_firm; templates = templates_firm;
n = n_templates_firm; n = n_templates_firm;
@ -1075,14 +1075,14 @@ bool ReadVGameDir(VirtualFile* vfile, VirtualDir* vdir) {
} else if (vdir->flags & VFLAG_NITRO) { } else if (vdir->flags & VFLAG_NITRO) {
return ReadVGameDirNitro(vfile, vdir); return ReadVGameDirNitro(vfile, vdir);
} }
if (++vdir->index < n) { if (++vdir->index < n) {
// copy current template to vfile and set readonly flag // copy current template to vfile and set readonly flag
memcpy(vfile, templates + vdir->index, sizeof(VirtualFile)); memcpy(vfile, templates + vdir->index, sizeof(VirtualFile));
vfile->flags |= VFLAG_READONLY; vfile->flags |= VFLAG_READONLY;
return true; return true;
} }
return false; return false;
} }
@ -1110,7 +1110,7 @@ bool FindVirtualFileInLv3Dir(VirtualFile* vfile, const VirtualDir* vdir, const c
vfile->flags = vdir->flags & ~VFLAG_DIR; vfile->flags = vdir->flags & ~VFLAG_DIR;
vfile->keyslot = ((offset_ncch != (u64) -1) && NCCH_ENCRYPTED(ncch)) ? vfile->keyslot = ((offset_ncch != (u64) -1) && NCCH_ENCRYPTED(ncch)) ?
0x2C : 0xFF; // actual keyslot may be different 0x2C : 0xFF; // actual keyslot may be different
RomFsLv3DirMeta* lv3dir = GetLv3DirMeta(name, vdir->offset, &lv3idx); RomFsLv3DirMeta* lv3dir = GetLv3DirMeta(name, vdir->offset, &lv3idx);
if (lv3dir) { if (lv3dir) {
vfile->offset = ((u8*) lv3dir) - ((u8*) lv3idx.dirmeta); vfile->offset = ((u8*) lv3dir) - ((u8*) lv3idx.dirmeta);
@ -1118,24 +1118,24 @@ bool FindVirtualFileInLv3Dir(VirtualFile* vfile, const VirtualDir* vdir, const c
vfile->flags |= VFLAG_DIR; vfile->flags |= VFLAG_DIR;
return true; return true;
} }
RomFsLv3FileMeta* lv3file = GetLv3FileMeta(name, vdir->offset, &lv3idx); RomFsLv3FileMeta* lv3file = GetLv3FileMeta(name, vdir->offset, &lv3idx);
if (lv3file) { if (lv3file) {
vfile->offset = ((u8*) lv3file) - ((u8*) lv3idx.filemeta); vfile->offset = ((u8*) lv3file) - ((u8*) lv3idx.filemeta);
vfile->size = lv3file->size_data; vfile->size = lv3file->size_data;
return true; return true;
} }
return false; return false;
} }
bool GetVGameLv3Filename(char* name, const VirtualFile* vfile, u32 n_chars) { bool GetVGameLv3Filename(char* name, const VirtualFile* vfile, u32 n_chars) {
if (!(vfile->flags & VFLAG_LV3)) if (!(vfile->flags & VFLAG_LV3))
return false; return false;
u16* wname = NULL; u16* wname = NULL;
u32 name_len = 0; u32 name_len = 0;
if (vfile->flags & VFLAG_DIR) { if (vfile->flags & VFLAG_DIR) {
RomFsLv3DirMeta* dirmeta = LV3_GET_DIR(vfile->offset, &lv3idx); RomFsLv3DirMeta* dirmeta = LV3_GET_DIR(vfile->offset, &lv3idx);
if (!dirmeta) return false; if (!dirmeta) return false;
@ -1149,14 +1149,14 @@ bool GetVGameLv3Filename(char* name, const VirtualFile* vfile, u32 n_chars) {
} }
memset(name, 0, n_chars); memset(name, 0, n_chars);
utf16_to_utf8((u8*) name, wname, n_chars-1, name_len); utf16_to_utf8((u8*) name, wname, n_chars-1, name_len);
return true; return true;
} }
bool GetVGameNitroFilename(char* name, const VirtualFile* vfile, u32 n_chars) { bool GetVGameNitroFilename(char* name, const VirtualFile* vfile, u32 n_chars) {
if (!(vfile->flags & VFLAG_NITRO)) if (!(vfile->flags & VFLAG_NITRO))
return false; return false;
u8* fnt_entry = vgame_fs_buffer + (vfile->offset >> 32); u8* fnt_entry = vgame_fs_buffer + (vfile->offset >> 32);
u32 name_len = (*fnt_entry) & ~0x80; u32 name_len = (*fnt_entry) & ~0x80;
if (name_len >= n_chars) return false; if (name_len >= n_chars) return false;
@ -1164,7 +1164,7 @@ bool GetVGameNitroFilename(char* name, const VirtualFile* vfile, u32 n_chars) {
memcpy(name, fnt_entry + 1, name_len); memcpy(name, fnt_entry + 1, name_len);
for (u32 i = 0; i < name_len; i++) for (u32 i = 0; i < name_len; i++)
if (name[i] == '%') name[i] = '_'; if (name[i] == '%') name[i] = '_';
// Shift-JIS workaround // Shift-JIS workaround
for (u32 i = 0; i < name_len; i++) { for (u32 i = 0; i < name_len; i++) {
if (name[i] >= 0x80) { // this is a Shift-JIS filename if (name[i] >= 0x80) { // this is a Shift-JIS filename
@ -1173,7 +1173,7 @@ bool GetVGameNitroFilename(char* name, const VirtualFile* vfile, u32 n_chars) {
break; break;
} }
} }
return true; return true;
} }

View File

@ -112,17 +112,17 @@ bool GetVirtualFile(VirtualFile* vfile, const char* path, u8 mode) {
char lpath[256]; char lpath[256];
strncpy(lpath, path, 256); strncpy(lpath, path, 256);
lpath[255] = '\0'; lpath[255] = '\0';
// get virtual source / root dir object // get virtual source / root dir object
u32 virtual_src = 0; u32 virtual_src = 0;
virtual_src = GetVirtualSource(path); virtual_src = GetVirtualSource(path);
if (!virtual_src) return false; if (!virtual_src) return false;
// set vfile as root object // set vfile as root object
memset(vfile, 0, sizeof(VirtualDir)); memset(vfile, 0, sizeof(VirtualDir));
vfile->flags = VFLAG_ROOT|virtual_src; vfile->flags = VFLAG_ROOT|virtual_src;
if (strnlen(lpath, 256) <= 3) return true; if (strnlen(lpath, 256) <= 3) return true;
// tokenize / parse path // tokenize / parse path
char* name; char* name;
VirtualDir vdir; VirtualDir vdir;
@ -130,7 +130,7 @@ bool GetVirtualFile(VirtualFile* vfile, const char* path, u8 mode) {
for (name = strtok(lpath + 3, "/"); name && vdir.flags; name = strtok(NULL, "/")) { for (name = strtok(lpath + 3, "/"); name && vdir.flags; name = strtok(NULL, "/")) {
if (!(vdir.flags & VFLAG_LV3)) { // standard method if (!(vdir.flags & VFLAG_LV3)) { // standard method
while (true) { while (true) {
if (!ReadVirtualDir(vfile, &vdir)) if (!ReadVirtualDir(vfile, &vdir))
return ((mode & FA_WRITE) && (vdir.flags & VRT_BDRI) && GetNewVBDRIFile(vfile, &vdir, path)); return ((mode & FA_WRITE) && (vdir.flags & VRT_BDRI) && GetNewVBDRIFile(vfile, &vdir, path));
if ((!(vfile->flags & (VRT_GAME|VRT_VRAM)) && (strncasecmp(name, vfile->name, 32) == 0)) || if ((!(vfile->flags & (VRT_GAME|VRT_VRAM)) && (strncasecmp(name, vfile->name, 32) == 0)) ||
((vfile->flags & VRT_GAME) && MatchVGameFilename(name, vfile, 256)) || ((vfile->flags & VRT_GAME) && MatchVGameFilename(name, vfile, 256)) ||
@ -144,7 +144,7 @@ bool GetVirtualFile(VirtualFile* vfile, const char* path, u8 mode) {
if (!OpenVirtualDir(&vdir, vfile)) if (!OpenVirtualDir(&vdir, vfile))
vdir.flags = 0; vdir.flags = 0;
} }
return (name == NULL); // if name is NULL, this succeeded return (name == NULL); // if name is NULL, this succeeded
} }
@ -156,7 +156,7 @@ bool GetVirtualDir(VirtualDir* vdir, const char* path) {
bool GetVirtualFilename(char* name, const VirtualFile* vfile, u32 n_chars) { bool GetVirtualFilename(char* name, const VirtualFile* vfile, u32 n_chars) {
if (vfile->flags & VRT_GAME) return GetVGameFilename(name, vfile, n_chars); if (vfile->flags & VRT_GAME) return GetVGameFilename(name, vfile, n_chars);
else if (vfile->flags & VRT_VRAM) return GetVVramFilename(name, vfile); else if (vfile->flags & VRT_VRAM) return GetVVramFilename(name, vfile);
strncpy(name, vfile->name, n_chars); strncpy(name, vfile->name, n_chars);
return true; return true;
} }
@ -168,7 +168,7 @@ int ReadVirtualFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 coun
else if ((offset + count) > vfile->size) else if ((offset + count) > vfile->size)
count = vfile->size - offset; count = vfile->size - offset;
if (bytes_read) *bytes_read = count; if (bytes_read) *bytes_read = count;
if (vfile->flags & (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND|VRT_XORPAD)) { if (vfile->flags & (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND|VRT_XORPAD)) {
return ReadVNandFile(vfile, buffer, offset, count); return ReadVNandFile(vfile, buffer, offset, count);
} else if (vfile->flags & VRT_MEMORY) { } else if (vfile->flags & VRT_MEMORY) {
@ -186,7 +186,7 @@ int ReadVirtualFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 coun
} else if (vfile->flags & VRT_DISADIFF) { } else if (vfile->flags & VRT_DISADIFF) {
return ReadVDisaDiffFile(vfile, buffer, offset, count); return ReadVDisaDiffFile(vfile, buffer, offset, count);
} }
return -1; return -1;
} }
@ -197,7 +197,7 @@ int WriteVirtualFile(VirtualFile* vfile, const void* buffer, u64 offset, u64 cou
else if (!(vfile->flags & VRT_BDRI) && ((offset + count) > vfile->size)) else if (!(vfile->flags & VRT_BDRI) && ((offset + count) > vfile->size))
count = vfile->size - offset; count = vfile->size - offset;
if (bytes_written) *bytes_written = count; if (bytes_written) *bytes_written = count;
if (vfile->flags & VFLAG_READONLY) { if (vfile->flags & VFLAG_READONLY) {
return -1; return -1;
} else if (vfile->flags & (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND)) { } else if (vfile->flags & (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND)) {
@ -211,30 +211,30 @@ int WriteVirtualFile(VirtualFile* vfile, const void* buffer, u64 offset, u64 cou
} else if (vfile->flags & VRT_BDRI) { } else if (vfile->flags & VRT_BDRI) {
return WriteVBDRIFile(vfile, buffer, offset, count); return WriteVBDRIFile(vfile, buffer, offset, count);
} // no write support for virtual game / keydb / vram files } // no write support for virtual game / keydb / vram files
return -1; return -1;
} }
int DeleteVirtualFile(const VirtualFile* vfile) { int DeleteVirtualFile(const VirtualFile* vfile) {
if (!(vfile->flags & VFLAG_DELETABLE)) return -1; if (!(vfile->flags & VFLAG_DELETABLE)) return -1;
// Special handling for deleting BDRI entries // Special handling for deleting BDRI entries
if (vfile->flags & VRT_BDRI) if (vfile->flags & VRT_BDRI)
return DeleteVBDRIFile(vfile); return DeleteVBDRIFile(vfile);
// For anything else, "deleting" is just filling with 0s // For anything else, "deleting" is just filling with 0s
u32 zeroes_size = STD_BUFFER_SIZE; u32 zeroes_size = STD_BUFFER_SIZE;
u8* zeroes = (u8*) malloc(zeroes_size); u8* zeroes = (u8*) malloc(zeroes_size);
if (!zeroes) return -1; if (!zeroes) return -1;
memset(zeroes, 0x00, zeroes_size); memset(zeroes, 0x00, zeroes_size);
int result = 0; 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);
result = WriteVirtualFile((VirtualFile*)vfile, zeroes, pos, wipe_bytes, NULL); result = WriteVirtualFile((VirtualFile*)vfile, zeroes, pos, wipe_bytes, NULL);
if (result != 0) break; if (result != 0) break;
} }
free(zeroes); free(zeroes);
return result; return result;
} }

View File

@ -17,28 +17,28 @@ void DeinitVKeyDbDrive(void) {
u64 InitVKeyDbDrive(void) { // prerequisite: aeskeydb.bin mounted as image u64 InitVKeyDbDrive(void) { // prerequisite: aeskeydb.bin mounted as image
if (!(GetMountState() & BIN_KEYDB)) return 0; if (!(GetMountState() & BIN_KEYDB)) return 0;
n_keys = 0; n_keys = 0;
// sanity check // sanity check
u64 fsize = GetMountSize(); u64 fsize = GetMountSize();
if (!fsize || (fsize % sizeof(AesKeyInfo)) || (fsize > VKEYDB_BUFFER_SIZE)) { if (!fsize || (fsize % sizeof(AesKeyInfo)) || (fsize > VKEYDB_BUFFER_SIZE)) {
n_keys = 0; n_keys = 0;
return 0; return 0;
} }
// setup vkeydb buffer // setup vkeydb buffer
DeinitVKeyDbDrive(); // dangerous shit DeinitVKeyDbDrive(); // dangerous shit
key_info = (AesKeyInfo*) malloc(VKEYDB_BUFFER_SIZE); key_info = (AesKeyInfo*) malloc(VKEYDB_BUFFER_SIZE);
if (!key_info) return 0; if (!key_info) return 0;
// load the full database into memory // load the full database into memory
n_keys = fsize / sizeof(AesKeyInfo); n_keys = fsize / sizeof(AesKeyInfo);
if (ReadImageBytes((u8*) key_info, 0, fsize) != 0) n_keys = 0; if (ReadImageBytes((u8*) key_info, 0, fsize) != 0) n_keys = 0;
// decrypt keys if required // decrypt keys if required
for (u32 i = 0; i < n_keys; i++) { for (u32 i = 0; i < n_keys; i++) {
if (key_info[i].isEncrypted) CryptAesKeyInfo(&(key_info[i])); if (key_info[i].isEncrypted) CryptAesKeyInfo(&(key_info[i]));
} }
if (!n_keys) DeinitVKeyDbDrive(); if (!n_keys) DeinitVKeyDbDrive();
return (n_keys) ? BIN_KEYDB : 0; return (n_keys) ? BIN_KEYDB : 0;
} }
@ -59,17 +59,17 @@ bool ReadVKeyDbDir(VirtualFile* vfile, VirtualDir* vdir) {
(key_entry->keyUnitType == KEYS_RETAIL) ? ".ret" : ""; (key_entry->keyUnitType == KEYS_RETAIL) ? ".ret" : "";
snprintf(typestr, 12 + 1, "%s%.10s", (key_entry->type == 'I') ? "IV" : snprintf(typestr, 12 + 1, "%s%.10s", (key_entry->type == 'I') ? "IV" :
(key_entry->type == 'X') ? "X" : (key_entry->type == 'Y') ? "Y" : "", key_entry->id); (key_entry->type == 'X') ? "X" : (key_entry->type == 'Y') ? "Y" : "", key_entry->id);
memset(vfile, 0, sizeof(VirtualFile)); memset(vfile, 0, sizeof(VirtualFile));
snprintf(vfile->name, 32, NAME_LEGKEY, keyslot, typestr, unitext); snprintf(vfile->name, 32, NAME_LEGKEY, keyslot, typestr, unitext);
vfile->offset = vdir->index * sizeof(AesKeyInfo); vfile->offset = vdir->index * sizeof(AesKeyInfo);
vfile->size = 16; // standard size of a key vfile->size = 16; // standard size of a key
vfile->keyslot = 0xFF; vfile->keyslot = 0xFF;
vfile->flags = VFLAG_READONLY; vfile->flags = VFLAG_READONLY;
return true; // found return true; // found
} }
return false; return false;
} }

View File

@ -88,23 +88,23 @@ static const VirtualFile vMemFileTemplates[] = {
bool ReadVMemDir(VirtualFile* vfile, VirtualDir* vdir) { // uses a generic vdir object generated in virtual.c bool ReadVMemDir(VirtualFile* vfile, VirtualDir* vdir) { // uses a generic vdir object generated in virtual.c
int n_templates = sizeof(vMemFileTemplates) / sizeof(VirtualFile); int n_templates = sizeof(vMemFileTemplates) / sizeof(VirtualFile);
const VirtualFile* templates = vMemFileTemplates; const VirtualFile* templates = vMemFileTemplates;
while (++vdir->index < n_templates) { while (++vdir->index < n_templates) {
// copy current template to vfile // copy current template to vfile
memcpy(vfile, templates + vdir->index, sizeof(VirtualFile)); memcpy(vfile, templates + vdir->index, sizeof(VirtualFile));
// process special flags // process special flags
if (((vfile->flags & VFLAG_N3DS_EXT) && (IS_O3DS)) || // this is not on O3DS consoles and locked by sighax if (((vfile->flags & VFLAG_N3DS_EXT) && (IS_O3DS)) || // this is not on O3DS consoles and locked by sighax
((vfile->flags & VFLAG_OTP) && !(IS_UNLOCKED)) || // OTP still locked ((vfile->flags & VFLAG_OTP) && !(IS_UNLOCKED)) || // OTP still locked
((vfile->flags & VFLAG_BOOT9) && !(HAS_BOOT9)) || // boot9 not found ((vfile->flags & VFLAG_BOOT9) && !(HAS_BOOT9)) || // boot9 not found
((vfile->flags & VFLAG_BOOT11) && !(HAS_BOOT11)) || // boot11 not found ((vfile->flags & VFLAG_BOOT11) && !(HAS_BOOT11)) || // boot11 not found
((vfile->flags & VFLAG_OTP_KEY) && !(HAS_OTP_KEY))) // OTP key not found ((vfile->flags & VFLAG_OTP_KEY) && !(HAS_OTP_KEY))) // OTP key not found
continue; continue;
// found if arriving here // found if arriving here
return true; return true;
} }
return false; return false;
} }
@ -116,7 +116,7 @@ int ReadVMemOTPDecrypted(const VirtualFile* vfile, void* buffer, u64 offset, u64
alignas(32) u8 otp_iv[0x10]; alignas(32) u8 otp_iv[0x10];
alignas(32) u8 otp_key[0x10]; alignas(32) u8 otp_key[0x10];
u8* otp_mem = (u8*) __OTP_ADDR; u8* otp_mem = (u8*) __OTP_ADDR;
if (HAS_BOOT9) { // easy setup when boot9 available if (HAS_BOOT9) { // easy setup when boot9 available
memcpy(otp_iv, OTP_IV, 0x10); memcpy(otp_iv, OTP_IV, 0x10);
memcpy(otp_key, OTP_KEY, 0x10); memcpy(otp_key, OTP_KEY, 0x10);
@ -125,7 +125,7 @@ int ReadVMemOTPDecrypted(const VirtualFile* vfile, void* buffer, u64 offset, u64
(LoadKeyFromFile(otp_iv , 0x11, 'I', "OTP") != 0)) (LoadKeyFromFile(otp_iv , 0x11, 'I', "OTP") != 0))
return 1; // crypto not available return 1; // crypto not available
} }
setup_aeskey(0x11, otp_key); setup_aeskey(0x11, otp_key);
use_aeskey(0x11); use_aeskey(0x11);
cbc_decrypt(otp_mem, otp_local, __OTP_LEN / 0x10, AES_CNT_TITLEKEY_DECRYPT_MODE, otp_iv); cbc_decrypt(otp_mem, otp_local, __OTP_LEN / 0x10, AES_CNT_TITLEKEY_DECRYPT_MODE, otp_iv);
@ -149,7 +149,7 @@ int ReadVMemMCURegisters(const VirtualFile* vfile, void* buffer, u64 offset, u64
int ReadVMemFlashCID(const VirtualFile* vfile, void* buffer, u64 offset, u64 count) { int ReadVMemFlashCID(const VirtualFile* vfile, void* buffer, u64 offset, u64 count) {
// NAND CID if keyslot field != 0. // NAND CID if keyslot field != 0.
bool is_nand = (bool)vfile->keyslot; bool is_nand = (bool)vfile->keyslot;
u32 cid[4]; // CID is 16 byte in size u32 cid[4]; // CID is 16 byte in size
sdmmc_get_cid(is_nand, (u32*) cid); sdmmc_get_cid(is_nand, (u32*) cid);
memcpy(buffer, ((u8*) cid) + offset, count); memcpy(buffer, ((u8*) cid) + offset, count);
@ -160,7 +160,7 @@ int ReadVMemFlashCID(const VirtualFile* vfile, void* buffer, u64 offset, u64 cou
int ReadVMemNVRAM(const VirtualFile* vfile, void* buffer, u64 offset, u64 count) { int ReadVMemNVRAM(const VirtualFile* vfile, void* buffer, u64 offset, u64 count) {
static bool wififlash_initialized = false; static bool wififlash_initialized = false;
(void) vfile; (void) vfile;
if (!wififlash_initialized) { if (!wififlash_initialized) {
wififlash_initialized = spiflash_get_status(); wififlash_initialized = spiflash_get_status();
if (!wififlash_initialized) return 1; if (!wififlash_initialized) return 1;

View File

@ -54,11 +54,11 @@ bool ReadVNandDir(VirtualFile* vfile, VirtualDir* vdir) { // uses a generic vdir
int n_templates = sizeof(vNandTemplates) / sizeof(VirtualNandTemplate); int n_templates = sizeof(vNandTemplates) / sizeof(VirtualNandTemplate);
const VirtualNandTemplate* templates = vNandTemplates; const VirtualNandTemplate* templates = vNandTemplates;
u32 nand_src = vdir->flags & VRT_SOURCE; u32 nand_src = vdir->flags & VRT_SOURCE;
while (++vdir->index < n_templates) { while (++vdir->index < n_templates) {
const VirtualNandTemplate* template = templates + vdir->index; const VirtualNandTemplate* template = templates + vdir->index;
NandPartitionInfo prt_info; NandPartitionInfo prt_info;
// set up virtual file // set up virtual file
if (template->flags & VFLAG_NAND_SIZE) { // override for "nand.bin" if (template->flags & VFLAG_NAND_SIZE) { // override for "nand.bin"
prt_info.sector = 0; prt_info.sector = 0;
@ -71,7 +71,7 @@ bool ReadVNandDir(VirtualFile* vfile, VirtualDir* vdir) { // uses a generic vdir
vfile->size = ((u64) prt_info.count) * 0x200; vfile->size = ((u64) prt_info.count) * 0x200;
vfile->keyslot = prt_info.keyslot; vfile->keyslot = prt_info.keyslot;
vfile->flags = template->flags; vfile->flags = template->flags;
// handle special cases // handle special cases
if (!vfile->size) continue; if (!vfile->size) continue;
if ((nand_src == VRT_XORPAD) && ((vfile->keyslot == 0x11) || (vfile->keyslot >= 0x40))) if ((nand_src == VRT_XORPAD) && ((vfile->keyslot == 0x11) || (vfile->keyslot >= 0x40)))
@ -98,12 +98,12 @@ bool ReadVNandDir(VirtualFile* vfile, VirtualDir* vdir) { // uses a generic vdir
if (sha_cmp(perfect_sha, keydb, KEYDB_PERFECT_SIZE, SHA256_MODE) != 0) continue; if (sha_cmp(perfect_sha, keydb, KEYDB_PERFECT_SIZE, SHA256_MODE) != 0) continue;
vfile->size = KEYDB_PERFECT_SIZE; vfile->size = KEYDB_PERFECT_SIZE;
} }
// found if arriving here // found if arriving here
vfile->flags |= nand_src; vfile->flags |= nand_src;
return true; return true;
} }
return false; return false;
} }

View File

@ -5,13 +5,13 @@
bool SplitTarFName(char* tar_fname, char** dir, char** name) { bool SplitTarFName(char* tar_fname, char** dir, char** name) {
u32 len = strnlen(tar_fname, 100 + 1); u32 len = strnlen(tar_fname, 100 + 1);
if (!len || (len == 101)) return false; if (!len || (len == 101)) return false;
// remove trailing slash // remove trailing slash
if (tar_fname[len-1] == '/') tar_fname[--len] = '\0'; if (tar_fname[len-1] == '/') tar_fname[--len] = '\0';
// find last slash // find last slash
char* slash = strrchr(tar_fname, '/'); char* slash = strrchr(tar_fname, '/');
// relative root dir entry // relative root dir entry
if (!slash) { if (!slash) {
*name = tar_fname; *name = tar_fname;
@ -21,7 +21,7 @@ bool SplitTarFName(char* tar_fname, char** dir, char** name) {
*name = slash + 1; *name = slash + 1;
*dir = tar_fname; *dir = tar_fname;
} }
return true; return true;
} }
@ -34,8 +34,8 @@ bool ReadVVramDir(VirtualFile* vfile, VirtualDir* vdir) {
vfile->name[0] = '\0'; vfile->name[0] = '\0';
vfile->flags = VFLAG_READONLY; vfile->flags = VFLAG_READONLY;
vfile->keyslot = 0xFF; vfile->keyslot = 0xFF;
// get current dir name // get current dir name
char curr_dir[100 + 1]; char curr_dir[100 + 1];
if (vdir->offset == (u64) -1) return false; // end of the dir? if (vdir->offset == (u64) -1) return false; // end of the dir?
@ -48,47 +48,47 @@ bool ReadVVramDir(VirtualFile* vfile, VirtualDir* vdir) {
if (len == 101) return false; // path error if (len == 101) return false; // path error
if (curr_dir[len-1] == '/') curr_dir[len-1] = '\0'; if (curr_dir[len-1] == '/') curr_dir[len-1] = '\0';
} }
// using vdir index to signify the position limits us to 1TiB TARs // using vdir index to signify the position limits us to 1TiB TARs
void* tardata = NULL; void* tardata = NULL;
if (vdir->index < 0) tardata = FirstVTarEntry(); if (vdir->index < 0) tardata = FirstVTarEntry();
else tardata = NextVTarEntry(OffsetVTarEntry(vdir->index << 9)); else tardata = NextVTarEntry(OffsetVTarEntry(vdir->index << 9));
if (tardata) do { if (tardata) do {
TarHeader* tar = (TarHeader*) tardata; TarHeader* tar = (TarHeader*) tardata;
char tar_fname[100 + 1]; char tar_fname[100 + 1];
char *name, *dir; char *name, *dir;
strncpy(tar_fname, tar->fname, 100); strncpy(tar_fname, tar->fname, 100);
if (!SplitTarFName(tar_fname, &dir, &name)) return false; if (!SplitTarFName(tar_fname, &dir, &name)) return false;
if ((!dir && !*curr_dir) || (dir && (strncmp(dir, curr_dir, 100) == 0))) break; if ((!dir && !*curr_dir) || (dir && (strncmp(dir, curr_dir, 100) == 0))) break;
} while ((tardata = NextVTarEntry(tardata))); } while ((tardata = NextVTarEntry(tardata)));
// match found? // match found?
if (tardata) { if (tardata) {
u64 fsize; u64 fsize;
bool is_dir; bool is_dir;
void* fdata = GetVTarFileInfo(tardata, NULL, &fsize, &is_dir); void* fdata = GetVTarFileInfo(tardata, NULL, &fsize, &is_dir);
vfile->offset = (u32) fdata - VRAM0_OFFSET; vfile->offset = (u32) fdata - VRAM0_OFFSET;
vfile->size = fsize; vfile->size = fsize;
if (is_dir) vfile->flags |= VFLAG_DIR; if (is_dir) vfile->flags |= VFLAG_DIR;
vdir->index = (vfile->offset >> 9) - 1; vdir->index = (vfile->offset >> 9) - 1;
} else { // not found } else { // not found
vdir->offset = (u64) -1; vdir->offset = (u64) -1;
return false; return false;
} }
return true; return true;
} }
int ReadVVramFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count) { int ReadVVramFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count) {
if (vfile->flags & VFLAG_DIR) return -1; if (vfile->flags & VFLAG_DIR) return -1;
void* fdata = (u8*) VRAM0_OFFSET + vfile->offset; void* fdata = (u8*) VRAM0_OFFSET + vfile->offset;
// range checks in virtual.c // range checks in virtual.c
memcpy(buffer, (u8*) fdata + offset, count); memcpy(buffer, (u8*) fdata + offset, count);
return 0; return 0;
@ -99,11 +99,11 @@ bool GetVVramFilename(char* name, const VirtualFile* vfile) {
TarHeader* tar = (TarHeader*) tardata; TarHeader* tar = (TarHeader*) tardata;
char tar_fname[100 + 1]; char tar_fname[100 + 1];
char *name_tmp, *dir; char *name_tmp, *dir;
strncpy(tar_fname, tar->fname, 100); strncpy(tar_fname, tar->fname, 100);
if (!SplitTarFName(tar_fname, &dir, &name_tmp)) return false; if (!SplitTarFName(tar_fname, &dir, &name_tmp)) return false;
strncpy(name, name_tmp, 100); strncpy(name, name_tmp, 100);
return true; return true;
} }
@ -112,7 +112,7 @@ bool MatchVVramFilename(const char* name, const VirtualFile* vfile) {
TarHeader* tar = (TarHeader*) tardata; TarHeader* tar = (TarHeader*) tardata;
char tar_fname[100 + 1]; char tar_fname[100 + 1];
char *name_tmp, *dir; char *name_tmp, *dir;
strncpy(tar_fname, tar->fname, 100); strncpy(tar_fname, tar->fname, 100);
if (!SplitTarFName(tar_fname, &dir, &name_tmp)) return false; if (!SplitTarFName(tar_fname, &dir, &name_tmp)) return false;
return (strncasecmp(name, name_tmp, 100) == 0); return (strncasecmp(name, name_tmp, 100) == 0);