diff --git a/source/fs.c b/source/fs.c index 215724b..96a8f4e 100644 --- a/source/fs.c +++ b/source/fs.c @@ -2,6 +2,7 @@ #include "fs.h" #include "virtual.h" #include "image.h" +#include "sha.h" #include "ff.h" #define MAIN_BUFFER ((u8*)0x21200000) @@ -204,6 +205,53 @@ size_t FileGetSize(const char* path) { return 0; } +bool FileGetSha256(const char* path, u8* sha256) { + bool ret = true; + + sha_init(SHA256_MODE); + ShowProgress(0, 0, path); + if (GetVirtualSource(path)) { // for virtual files + VirtualFile vfile; + u32 fsize; + + if (!FindVirtualFile(&vfile, path, 0)) + return false; + fsize = vfile.size; + + for (size_t pos = 0; (pos < fsize) && ret; pos += MAIN_BUFFER_SIZE) { + UINT read_bytes = min(MAIN_BUFFER_SIZE, fsize - pos); + if (ReadVirtualFile(&vfile, MAIN_BUFFER, pos, read_bytes, NULL) != 0) + ret = false; + if (!ShowProgress(pos + read_bytes, fsize, path)) + ret = false; + sha_update(MAIN_BUFFER, read_bytes); + } + } else { // for regular FAT files + FIL file; + size_t fsize; + + if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) + return false; + fsize = f_size(&file); + f_lseek(&file, 0); + f_sync(&file); + + for (size_t pos = 0; (pos < fsize) && ret; pos += MAIN_BUFFER_SIZE) { + UINT bytes_read = 0; + if (f_read(&file, MAIN_BUFFER, MAIN_BUFFER_SIZE, &bytes_read) != FR_OK) + ret = false; + if (!ShowProgress(pos + bytes_read, fsize, path)) + ret = false; + sha_update(MAIN_BUFFER, bytes_read); + } + f_close(&file); + } + ShowProgress(1, 1, path); + sha_get(sha256); + + return ret; +} + bool PathCopyVirtual(const char* destdir, const char* orig) { char dest[256]; // maximum path name length in FAT char* oname = strrchr(orig, '/'); diff --git a/source/fs.h b/source/fs.h index 7d3a2f4..0c9f398 100644 --- a/source/fs.h +++ b/source/fs.h @@ -47,6 +47,9 @@ size_t FileGetData(const char* path, u8* data, size_t size, size_t foffset); /** Get size of file **/ size_t FileGetSize(const char* path); +/** Get SHA-256 of file **/ +bool FileGetSha256(const char* path, u8* sha256); + /** Recursively copy a file or directory **/ bool PathCopy(const char* destdir, const char* orig); diff --git a/source/godmode.c b/source/godmode.c index 12f1ceb..e1421f6 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -335,9 +335,46 @@ u32 GodMode() { scroll = 0; } else cursor = 0; } else if ((pad_state & BUTTON_A) && (curr_entry->type == T_FILE)) { // process a file - u32 file_type = IdentifyImage(curr_entry->path); - if (file_type && (PathToNumFS(curr_entry->path) == 0) && // try to mount image / only on SD - ShowPrompt(true, "This looks like a %s image\nTry to mount it?", (file_type == IMG_NAND) ? "NAND" : "FAT")) { + u32 file_type = IdentifyImage(curr_entry->path); + char pathstr[32 + 1]; + const char* options[4]; + u32 n_opt = 2; + + TruncateString(pathstr, curr_entry->path, 32, 8); + options[0] = "Show in Hexviewer"; + options[1] = "Calculate SHA-256"; + if (file_type && (PathToNumFS(curr_entry->path) == 0)) { + options[2] = (file_type == IMG_NAND) ? "Mount as NAND image" : "Mount as FAT image"; + n_opt = 3; + } + + u32 user_select = ShowSelectPrompt(n_opt, options, pathstr); + if (user_select == 1) { // -> show in hex viewer + static bool show_instr = true; + if (show_instr) { + ShowPrompt(false, "HexViewer Controls:\n \n\x18\x19\x1A\x1B(+R) - Scroll\nR+Y - Switch view\nB - Exit\n"); + show_instr = false; + } + HexViewer(curr_entry->path); + } else if (user_select == 2) { // -> calculate SHA-256 + static char pathstr_prev[32 + 1] = { 0 }; + static u8 sha256_prev[32] = { 0 }; + u8 sha256[32]; + if (!FileGetSha256(curr_entry->path, sha256)) { + ShowPrompt(false, "Calculating SHA-256: failed!"); + } else { + ShowPrompt(false, "%s\n%08X%08X%08X%08X\n%08X%08X%08X%08X%s%s", + pathstr, + getbe32(sha256 + 0), getbe32(sha256 + 4), + getbe32(sha256 + 8), getbe32(sha256 + 12), + getbe32(sha256 + 16), getbe32(sha256 + 20), + getbe32(sha256 + 24), getbe32(sha256 + 28), + (memcmp(sha256, sha256_prev, 32) == 0) ? "\n \nIdentical with previous file:\n" : "", + (memcmp(sha256, sha256_prev, 32) == 0) ? pathstr_prev : ""); + strncpy(pathstr_prev, pathstr, 32 + 1); + memcpy(sha256_prev, sha256, 32); + } + } else if (user_select == 3) { // -> mount as image DeinitExtFS(); u32 mount_state = MountImage(curr_entry->path); InitExtFS(); @@ -353,9 +390,6 @@ u32 GodMode() { } if (clipboard->n_entries && (strcspn(clipboard->entry[0].path, IMG_DRV) == 0)) clipboard->n_entries = 0; // remove invalid clipboard stuff - } else if (FileGetSize(curr_entry->path) && - ShowPrompt(true, "Show HexViewer?\n \nControls:\n\x18\x19\x1A\x1B(+R) - Scroll\nR+Y - Switch view\nB - Exit\n")) { - HexViewer(curr_entry->path); } } else if (*current_path && ((pad_state & BUTTON_B) || // one level down ((pad_state & BUTTON_A) && (curr_entry->type == T_DOTDOT)))) { diff --git a/source/nand/virtual.c b/source/nand/virtual.c index 2773884..6c14c5e 100644 --- a/source/nand/virtual.c +++ b/source/nand/virtual.c @@ -25,7 +25,7 @@ VirtualFile virtualFileTemplates[] = { { "ctrnand_full.bin" , 0x0B930000, 0x41ED0000, 0x05, VFLAG_ON_N3DS }, { "ctrnand_full.bin" , 0x0B930000, 0x41ED0000, 0x04, VFLAG_ON_NO3DS }, { "nand.bin" , 0x00000000, 0x00000000, 0xFF, VFLAG_ON_NAND | VFLAG_NAND_SIZE | VFLAG_A9LH_AREA }, - { "nand_minsize.bin" , 0x00000000, 0x3AF00000, 0xFF, VFLAG_ON_O3DS | VFLAG_A9LH_AREA}, + { "nand_minsize.bin" , 0x00000000, 0x3AF00000, 0xFF, VFLAG_ON_O3DS | VFLAG_A9LH_AREA }, { "nand_minsize.bin" , 0x00000000, 0x4D800000, 0xFF, VFLAG_ON_N3DS | VFLAG_ON_NO3DS | VFLAG_A9LH_AREA }, { "sector0x96.bin" , 0x00012C00, 0x00000200, 0xFF, VFLAG_ON_NAND | VFLAG_A9LH_AREA }, { "nand_hdr.bin" , 0x00000000, 0x00000200, 0xFF, VFLAG_ON_NAND | VFLAG_A9LH_AREA }, diff --git a/source/ui.c b/source/ui.c index 2cef878..3b17327 100644 --- a/source/ui.c +++ b/source/ui.c @@ -203,7 +203,7 @@ bool ShowPrompt(bool ask, const char *format, ...) while (true) { u32 pad_state = InputWait(); if (pad_state & BUTTON_A) break; - else if (ask && (pad_state & BUTTON_B)) { + else if (pad_state & BUTTON_B) { ret = false; break; } @@ -274,6 +274,51 @@ bool ShowUnlockSequence(u32 seqlvl, const char *format, ...) { return (lvl >= len); } +u32 ShowSelectPrompt(u32 n, const char** options, const char *format, ...) { + u32 str_width, str_height; + u32 x, y, yopt; + u32 sel = 0; + + char str[512] = {}; // 512 should be more than enough + va_list va; + + va_start(va, format); + vsnprintf(str, 512, format, va); + va_end(va); + + if (n == 0) return 0; // check for low number of options + else if (n == 1) return ShowPrompt(true, "%s\n%s?", str, options[0]) ? 1 : 0; + + str_width = GetDrawStringWidth(str); + str_height = GetDrawStringHeight(str) + (n * 12) + (3 * 10); + if (str_width < 18*8) str_width = 18 * 8; + x = (str_width >= SCREEN_WIDTH_TOP) ? 0 : (SCREEN_WIDTH_TOP - str_width) / 2; + y = (str_height >= SCREEN_HEIGHT) ? 0 : (SCREEN_HEIGHT - str_height) / 2; + yopt = y + GetDrawStringHeight(str) + 8; + + ClearScreenF(true, false, COLOR_STD_BG); + DrawStringF(true, x, y, COLOR_STD_FONT, COLOR_STD_BG, str); + DrawStringF(true, x, yopt + (n*12) + 10, COLOR_STD_FONT, COLOR_STD_BG, "( select, cancel)"); + while (true) { + for (u32 i = 0; i < n; i++) { + DrawStringF(true, x, yopt + (12*i), (sel == i) ? COLOR_STD_FONT : COLOR_LIGHTGREY, COLOR_STD_BG, "%2.2s %s", + (sel == i) ? "->" : "", options[i]); + } + u32 pad_state = InputWait(); + if (pad_state & BUTTON_DOWN) sel = (sel+1) % n; + else if (pad_state & BUTTON_UP) sel = (sel+n-1) % n; + else if (pad_state & BUTTON_A) break; + else if (pad_state & BUTTON_B) { + sel = n; + break; + } + } + + ClearScreenF(true, false, COLOR_STD_BG); + + return (sel >= n) ? 0 : sel + 1; +} + bool ShowInputPrompt(char* inputstr, u32 max_size, const char *format, ...) { const char* alphabet = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz(){}[]'`^,~!@#$%&0123456789=+-_."; const u32 alphabet_size = strnlen(alphabet, 256); diff --git a/source/ui.h b/source/ui.h index 7e2876c..f0e79ab 100644 --- a/source/ui.h +++ b/source/ui.h @@ -30,6 +30,7 @@ #define COLOR_TINTEDBLUE RGB(0x60, 0x60, 0x80) #define COLOR_TINTEDYELLOW RGB(0xD0, 0xD0, 0x60) #define COLOR_TINTEDGREEN RGB(0x70, 0x80, 0x70) +#define COLOR_LIGHTGREY RGB(0xB0, 0xB0, 0xB0) #define COLOR_DARKGREY RGB(0x50, 0x50, 0x50) #define COLOR_DARKESTGREY RGB(0x20, 0x20, 0x20) @@ -72,5 +73,6 @@ void FormatBytes(char* str, u64 bytes); bool ShowPrompt(bool ask, const char *format, ...); bool ShowUnlockSequence(u32 seqlvl, const char *format, ...); +u32 ShowSelectPrompt(u32 n, const char** options, const char *format, ...); bool ShowInputPrompt(char* inputstr, u32 max_size, const char *format, ...); bool ShowProgress(u64 current, u64 total, const char* opstr);