diff --git a/README.md b/README.md index 88d15e6..5a00200 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ Currently planned changes for this branch include: * Control.nacp patching while dumping NSPs (lets you patch screenshot, video, user account and HDCP restrictions). :white_check_mark: * Partition FS / Hash FS / RomFS browser using custom devoptab wrappers. :white_check_mark: * Full system update dumps with checksum and signature verification. :white_check_mark: +* `FsStorage` + `FatFs` based eMMC browser using a custom devoptab wrapper (allows copying files protected by the FS sysmodule at runtime). :white_check_mark: * Batch NSP dumps. :x: -* `FsFileSystem` + `FatFs` based eMMC browser using a custom devoptab wrapper (allows copying files protected by the FS sysmodule at runtime). :x: * New UI using a [customized borealis fork](https://github.com/DarkMatterCore/borealis/tree/nxdumptool-legacy). :warning: Legend: diff --git a/code_templates/nxdt_rw_poc.c b/code_templates/nxdt_rw_poc.c index c420e8d..351099a 100644 --- a/code_templates/nxdt_rw_poc.c +++ b/code_templates/nxdt_rw_poc.c @@ -30,6 +30,7 @@ #include #include #include +#include #define BLOCK_SIZE USB_TRANSFER_BUFFER_SIZE #define WAIT_TIME_LIMIT 30 @@ -85,7 +86,8 @@ typedef enum { MenuId_NcaFsSectionsSubMenu = 14, MenuId_SystemTitles = 15, MenuId_SystemUpdate = 16, - MenuId_Count = 17 + MenuId_BrowseEmmc = 17, + MenuId_Count = 18 } MenuId; typedef struct @@ -233,8 +235,11 @@ static bool browseNintendoContentArchiveFsSection(void *userdata); static bool saveSystemUpdateDump(void *userdata); +static bool browseEmmcPartition(void *userdata); + static bool fsBrowser(const char *mount_name, const char *base_out_path); static bool fsBrowserGetDirEntries(const char *dir_path, FsBrowserEntry **out_entries, u32 *out_entry_count); +static int fsBrowserDirEntrySortFunction(const void *a, const void *b); static bool fsBrowserDumpFile(const char *dir_path, const FsBrowserEntry *entry, const char *base_out_path); static bool fsBrowserDumpHighlightedEntries(const char *dir_path, const FsBrowserEntry *entries, u32 entries_count, const char *base_out_path); @@ -989,12 +994,42 @@ static MenuElement *g_dumpSystemUpdateMenuElements[] = { NULL }; -static Menu g_dumpSystemUpdateMenu = { - .id = MenuId_SystemUpdate, - .parent = NULL, - .selected = 0, - .scroll = 0, - .elements = g_dumpSystemUpdateMenuElements +static u8 g_emmcProdinfofPartition = FsBisPartitionId_CalibrationFile; +static u8 g_emmcSafePartition = FsBisPartitionId_SafeMode; +static u8 g_emmcUserPartition = FsBisPartitionId_User; +static u8 g_emmcSystemPartition = FsBisPartitionId_System; + +static MenuElement *g_emmcBrowseMenuElements[] = { + &(MenuElement){ + .str = "browse prodinfof emmc partition", + .child_menu = NULL, + .task_func = &browseEmmcPartition, + .element_options = NULL, + .userdata = &g_emmcProdinfofPartition + }, + &(MenuElement){ + .str = "browse safe emmc partition", + .child_menu = NULL, + .task_func = &browseEmmcPartition, + .element_options = NULL, + .userdata = &g_emmcSafePartition + }, + &(MenuElement){ + .str = "browse user emmc partition", + .child_menu = NULL, + .task_func = &browseEmmcPartition, + .element_options = NULL, + .userdata = &g_emmcUserPartition + }, + &(MenuElement){ + .str = "browse system mmc partition", + .child_menu = NULL, + .task_func = &browseEmmcPartition, + .element_options = NULL, + .userdata = &g_emmcSystemPartition + }, + &g_storageMenuElement, + NULL }; static MenuElement *g_rootMenuElements[] = { @@ -1027,7 +1062,26 @@ static MenuElement *g_rootMenuElements[] = { }, &(MenuElement){ .str = "dump system update", - .child_menu = &g_dumpSystemUpdateMenu, + .child_menu = &(Menu){ + .id = MenuId_SystemUpdate, + .parent = NULL, + .selected = 0, + .scroll = 0, + .elements = g_dumpSystemUpdateMenuElements + }, + .task_func = NULL, + .element_options = NULL, + .userdata = NULL + }, + &(MenuElement){ + .str = "browse emmc partitions", + .child_menu = &(Menu){ + .id = MenuId_BrowseEmmc, + .parent = NULL, + .selected = 0, + .scroll = 0, + .elements = g_emmcBrowseMenuElements + }, .task_func = NULL, .element_options = NULL, .userdata = NULL @@ -1382,7 +1436,7 @@ int main(int argc, char *argv[]) break; } - if ((cur_menu->id == MenuId_NcaFsSectionsSubMenu && cur_menu->selected == 1) || cur_menu->id == MenuId_BrowseHFS) + if ((cur_menu->id == MenuId_NcaFsSectionsSubMenu && cur_menu->selected == 1) || cur_menu->id == MenuId_BrowseHFS || cur_menu->id == MenuId_BrowseEmmc) { show_button_prompt = false; @@ -3668,6 +3722,49 @@ end: return success; } +static bool browseEmmcPartition(void *userdata) +{ + u8 bis_partition_id = (userdata ? *((u8*)userdata) : 0); + const char *mount_name = NULL; + char *base_out_path = NULL; + + bool success = false; + + if (bis_partition_id < FsBisPartitionId_CalibrationFile || bis_partition_id > FsBisPartitionId_System) + { + consolePrint("invalid bis partition id! (%u)\n", bis_partition_id); + goto end; + } + + /* Mount BIS partition. */ + if (!bisStorageMountPartition(bis_partition_id, &mount_name)) + { + consolePrint("failed to mount bis partition %u!\n", bis_partition_id); + goto end; + } + + /* Generate output base path. */ + base_out_path = generateOutputGameCardFileName(utilsGetAtmosphereEmummcStatus() ? "emuMMC" : "sysMMC", mount_name, false); + if (!base_out_path) goto end; + + /* Display file browser. */ + success = fsBrowser(mount_name, base_out_path); + +end: + /* Free data. */ + if (base_out_path) free(base_out_path); + + if (mount_name) bisStorageUnmountPartition(bis_partition_id); + + if (!success && g_appletStatus) + { + consolePrint("press any button to continue\n"); + utilsWaitForButtonPress(0); + } + + return success; +} + static bool fsBrowser(const char *mount_name, const char *base_out_path) { char dir_path[FS_MAX_PATH] = {0}; @@ -3701,8 +3798,13 @@ static bool fsBrowser(const char *mount_name, const char *base_out_path) consolePrint("press + to exit\n"); consolePrint("______________________________\n\n"); - consolePrint("entry: %u / %u\n", selected + 1, entries_count); - consolePrint("highlighted: %u / %u\n", highlighted, entries_count); + if (entries_count) + { + consolePrint("entry: %u / %u\n", selected + 1, entries_count); + consolePrint("highlighted: %u / %u\n", highlighted, entries_count); + consolePrint("selected: %s\n", entries[selected].dt.d_name); + } + consolePrint("current path: %s\n", dir_path); consolePrint("______________________________\n\n"); @@ -3957,6 +4059,9 @@ static bool fsBrowserGetDirEntries(const char *dir_path, FsBrowserEntry **out_en goto end; } + /* Sort entries. */ + if (count > 1) qsort(entries, count, sizeof(FsBrowserEntry), &fsBrowserDirEntrySortFunction); + /* Update output pointers. */ *out_entries = entries; *out_entry_count = count; @@ -3972,6 +4077,23 @@ end: return success; } +static int fsBrowserDirEntrySortFunction(const void *a, const void *b) +{ + const FsBrowserEntry *entry_1 = (const FsBrowserEntry*)a; + const FsBrowserEntry *entry_2 = (const FsBrowserEntry*)b; + + if (entry_1->dt.d_type < entry_2->dt.d_type) + { + return -1; + } else + if (entry_1->dt.d_type > entry_2->dt.d_type) + { + return 1; + } + + return strcasecmp(entry_1->dt.d_name, entry_2->dt.d_name); +} + static bool fsBrowserDumpFile(const char *dir_path, const FsBrowserEntry *entry, const char *base_out_path) { u64 free_space = 0; diff --git a/source/core/bis_storage.c b/source/core/bis_storage.c index 049bca1..a8ed9bc 100644 --- a/source/core/bis_storage.c +++ b/source/core/bis_storage.c @@ -43,10 +43,10 @@ static BisStorageFatFsContext *g_bisStorageContexts[FF_VOLUMES] = {0}; /// Required by FatFs. const char *VolumeStr[FF_VOLUMES] = { - "prodinfof", - "safe", - "user", - "system", + "PRODINFOF", + "SAFE", + "USER", + "SYSTEM", }; /* Function prototypes. */ diff --git a/source/core/save.c b/source/core/save.c index 6e136c8..b3e46d0 100644 --- a/source/core/save.c +++ b/source/core/save.c @@ -1738,40 +1738,6 @@ save_ctx_t *save_open_savefile(const char *path, u32 action) goto end; } - /* Code to dump the requested file in its entirety. Useful to retrieve protected system savefiles without exiting HOS. */ - /*char sd_path[FS_MAX_PATH] = {0}; - snprintf(sd_path, MAX_ELEMENTS(sd_path), DEVOPTAB_SDMC_DEVICE "/%s", strrchr(path, '/') + 1); - - utilsCreateDirectoryTree(sd_path, false); - - u64 blksize = 0x100000; - u8 *buf = malloc(blksize); - FILE *sd_fp = fopen(sd_path, "wb"); - - if (buf && sd_fp) - { - fseek(save_fp, 0, SEEK_END); - u64 size = ftell(save_fp); - rewind(save_fp); - - for(u64 offset = 0; offset < size; offset += blksize) - { - if ((size - offset) < blksize) blksize = (size - offset); - if (fread(buf, 1, blksize, save_fp) != blksize) break; - fwrite(buf, 1, blksize, sd_fp); - } - - rewind(save_fp); - } - - if (sd_fp) - { - fclose(sd_fp); - utilsCommitSdCardFileSystemChanges(); - } - - if (buf) free(buf);*/ - save_ctx = calloc(1, sizeof(save_ctx_t)); if (!save_ctx) {