diff --git a/Makefile b/Makefile index 9fadbda..e1dd3dc 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ include $(DEVKITPRO)/libnx/switch_rules VERSION_MAJOR := 1 VERSION_MINOR := 1 -VERSION_MICRO := 5 +VERSION_MICRO := 6 APP_TITLE := nxdumptool APP_AUTHOR := MCMrARM, DarkMatterCore diff --git a/README.md b/README.md index 59dddff..394c88c 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,40 @@ If you like my work and you'd like to support me in any way, it's not necessary, Changelog -------------- +**v1.1.6:** +* Added sequential dump support: it is now possible to start a XCI/NSP dump procedure even if there's not enough space available in the SD card! + * No setting has to be modified in order to enable this feature - the application will automatically ask the user if they want to use this mode if there's not enough space for the full dump. + * At least 1 GiB (2^30 bytes) of free space must be available in order to trigger this feature. + * A file-based checkpoint system is used to keep track of the already dumped parts (à la Hekate). + * The part(s) generated in each run must be transferred to a PC before continuing the process - except for the `.xci.seq`/`.nsp.seq` files used to keep track of the current dump status. + * NSPs generated using this method will also include a `.nsp.hdr` file, which holds the PFS0 header data. The information from this header is filled after writing all the NCAs, thus it is saved as an additional file. This *must* be used as the first file (placed before `.nsp.00`) when merging all the parts into a full NSP. + * The following options are ignored when this feature is triggered: + * `Split output dump (FAT32 support)` (XCI/NSP). File splitting *will* take place, regardless of the filesystem used by the SD card. Additionally, the creation of a directory with the archive bit set isn't performed with NSP dumps. + * `Create directory with archive bit set` (XCI only). + * `CRC32 checksum calculation` (NSP only). CRC32 checksum calculation is still available for XCI dumps. + * This feature is *not* compatible with batch dump operations. +* General changes to batch dump operations: + * Entries from the summary list displayed in the batch dump menu can now be manually excluded from the dump operation before starting it. + * It is possible to disable all entries, enable all entries and/or handpick specific titles from the summary list, thus letting the user further customize the batch dump process. + * A new option has been added to keep track of previous successful dumps created using batch mode: "Remember dumped titles". + * If enabled, a 0-byte file will be created for each successful dump in a separate subdirectory. + * These files act as an override: they will make the application skip the titles they represent in later batch mode operations even if the "Skip already dumped titles" option is disabled. + * This is specially useful if someone wants to skip titles that have already been successfully dumped using batch mode - even more so if their NSPs have already been moved or deleted from the SD card. + * To restore the original behaviour, simply delete the contents from the "BatchOverrides" subdirectory inside "NSP". + * Free storage space is now properly recalculated after each successful dump during a batch mode operation. +* UI code cleanup: + * `uiDrawString()`, `uiGetStrWidth()` and `uiPrintOption()` are now compatible with variable argument lists, removing the need to format a string beforehand and pass its variable to any of those functions. + * Preprocessor definitions are now used to specify RGB colors and for calculating vertical line coordinates, greatly simplifying calls to UI functions. + * Menu code now properly waits for any user input before drawing changes to the screen. + * Other minor coordinate fixes. +* The application is now capable of automatically reading/saving dump settings from/to a configuration file. +* The "Split output dump" option is, once again, enabled by default. FAT32 is the recommended filesystem for Switch SD cards if someone wants to use homebrew applications, so it's only logical to do this. +* Filenames for NACP icons in NSPs now properly reflect the NCA ID from its respective content file if it was modified. +* Fixed a bug that prevented to dump a specific file in the RomFS section from any update. +* Fixed a bug in the RomFS block collision check code that prevented to generate NSP dumps from certain titles with a RomFS section in Control/Manual NCAs that falls under an edge case that wasn't being handled properly. Thanks to [Zet-sensei](https://github.com/Zet-sensei) for reporting this problem! + +Thanks to [FennecTECH](https://github.com/fennectech) for providing with testing! + **v1.1.5:** * Built with latest libnx release, in order to fix HID problems under HOS 9.0.0+. * Added support for Korean and Chinese character sets. diff --git a/romfs/browser/disabled_highlight.jpg b/romfs/browser/disabled_highlight.jpg new file mode 100644 index 0000000..df687be Binary files /dev/null and b/romfs/browser/disabled_highlight.jpg differ diff --git a/romfs/browser/disabled_normal.jpg b/romfs/browser/disabled_normal.jpg new file mode 100644 index 0000000..edf54d8 Binary files /dev/null and b/romfs/browser/disabled_normal.jpg differ diff --git a/romfs/browser/enabled_highlight.jpg b/romfs/browser/enabled_highlight.jpg new file mode 100644 index 0000000..b05be78 Binary files /dev/null and b/romfs/browser/enabled_highlight.jpg differ diff --git a/romfs/browser/enabled_normal.jpg b/romfs/browser/enabled_normal.jpg new file mode 100644 index 0000000..9d25aa1 Binary files /dev/null and b/romfs/browser/enabled_normal.jpg differ diff --git a/source/dumper.c b/source/dumper.c index 80a44ff..d980554 100644 --- a/source/dumper.c +++ b/source/dumper.c @@ -25,6 +25,7 @@ extern nca_keyset_t nca_keyset; extern u64 freeSpace; +extern bool highlight; extern int breaks; extern int font_height; @@ -65,13 +66,14 @@ extern bktr_ctx_t bktrContext; extern char curRomFsPath[NAME_BUF_LEN]; extern u32 curRomFsDirOffset; -extern char strbuf[NAME_BUF_LEN * 4]; - extern char *filenameBuffer; extern char *filenames[FILENAME_MAX_CNT]; extern int filenamesCount; -extern u8 *fileNormalIconBuf; +extern u8 *enabledNormalIconBuf; +extern u8 *enabledHighlightIconBuf; +extern u8 *disabledNormalIconBuf; +extern u8 *disabledHighlightIconBuf; extern u8 *dumpBuf; @@ -86,8 +88,21 @@ void workaroundPartitionZeroAccess() fsStorageClose(&gameCardStorage); } -bool dumpCartridgeImage(bool isFat32, bool setXciArchiveBit, bool keepCert, bool trimDump, bool calcCrc) +bool dumpCartridgeImage(xciOptions *xciDumpCfg) { + if (!xciDumpCfg) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid XCI configuration struct!"); + breaks += 2; + return false; + } + + bool isFat32 = xciDumpCfg->isFat32; + bool setXciArchiveBit = xciDumpCfg->setXciArchiveBit; + bool keepCert = xciDumpCfg->keepCert; + bool trimDump = xciDumpCfg->trimDump; + bool calcCrc = xciDumpCfg->calcCrc; + u64 partitionOffset = 0, xciDataSize = 0, n; u64 partitionSizes[ISTORAGE_PARTITION_CNT]; char partitionSizesStr[ISTORAGE_PARTITION_CNT][32] = {'\0'}, xciDataSizeStr[32] = {'\0'}, filename[NAME_BUF_LEN * 2] = {'\0'}; @@ -98,18 +113,24 @@ bool dumpCartridgeImage(bool isFat32, bool setXciArchiveBit, bool keepCert, bool bool proceed = true, success = false, fat32_error = false; FILE *outFile = NULL; u8 splitIndex = 0; - u32 crc1 = 0, crc2 = 0; + u32 certCrc = 0, certlessCrc = 0; memset(dumpBuf, 0, DUMP_BUFFER_SIZE); progress_ctx_t progressCtx; memset(&progressCtx, 0, sizeof(progress_ctx_t)); + bool seqDumpMode = false, seqDumpFileRemove = false, seqDumpFinish = false; + char seqDumpFilename[NAME_BUF_LEN * 2] = {'\0'}; + FILE *seqDumpFile = NULL; + u64 seqDumpFileSize = 0, seqDumpSessionOffset = 0; + + sequentialXciCtx seqXciCtx; + memset(&seqXciCtx, 0, sizeof(sequentialXciCtx)); + char tmp_idx[5]; - size_t write_res; - - u64 part_size = (!setXciArchiveBit ? SPLIT_FILE_XCI_PART_SIZE : SPLIT_FILE_NSP_PART_SIZE); + size_t read_res, write_res; char *dumpName = generateFullDumpName(); if (!dumpName) @@ -118,7 +139,7 @@ bool dumpCartridgeImage(bool isFat32, bool setXciArchiveBit, bool keepCert, bool dumpName = calloc(16, sizeof(char)); if (!dumpName) { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to generate output dump name!"); breaks += 2; return false; } @@ -126,10 +147,70 @@ bool dumpCartridgeImage(bool isFat32, bool setXciArchiveBit, bool keepCert, bool sprintf(dumpName, "gamecard"); } + // Check if we're dealing with a sequential dump + snprintf(seqDumpFilename, MAX_ELEMENTS(seqDumpFilename), "%s%s.xci.seq", XCI_DUMP_PATH, dumpName); + seqDumpMode = checkIfFileExists(seqDumpFilename); + if (seqDumpMode) + { + // Open sequence file + seqDumpFile = fopen(seqDumpFilename, "rb+"); + if (seqDumpFile) + { + // Retrieve sequence file size + fseek(seqDumpFile, 0, SEEK_END); + seqDumpFileSize = ftell(seqDumpFile); + rewind(seqDumpFile); + + // Check file size + if (seqDumpFileSize == sizeof(sequentialXciCtx)) + { + // Read file contents + read_res = fread(&seqXciCtx, 1, seqDumpFileSize, seqDumpFile); + rewind(seqDumpFile); + + if (read_res == seqDumpFileSize) + { + // Check if the IStorage partition index is valid + if (seqXciCtx.partitionIndex <= (ISTORAGE_PARTITION_CNT - 1)) + { + // Restore parameters from the sequence file + isFat32 = true; + setXciArchiveBit = false; + keepCert = seqXciCtx.keepCert; + trimDump = seqXciCtx.trimDump; + calcCrc = seqXciCtx.calcCrc; + splitIndex = seqXciCtx.partNumber; + certCrc = seqXciCtx.certCrc; + certlessCrc = seqXciCtx.certlessCrc; + progressCtx.curOffset = ((u64)seqXciCtx.partNumber * SPLIT_FILE_SEQUENTIAL_SIZE); + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid IStorage partition index in sequential dump reference file!"); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to read %lu bytes long sequential dump reference file! (read %lu bytes)", seqDumpFileSize, read_res); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: sequential dump reference file size mismatch! (%lu != %lu)", seqDumpFileSize, sizeof(sequentialXciCtx)); + proceed = false; + seqDumpFileRemove = true; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to open existing sequential dump reference file for reading! (\"%s\")", seqDumpFilename); + proceed = false; + } + + uiRefreshDisplay(); + } + + if (!proceed) goto out; + + u64 part_size = (seqDumpMode ? SPLIT_FILE_SEQUENTIAL_SIZE : (!setXciArchiveBit ? SPLIT_FILE_XCI_PART_SIZE : SPLIT_FILE_NSP_PART_SIZE)); + for(partition = 0; partition < ISTORAGE_PARTITION_CNT; partition++) { - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Getting partition #%u size...", partition); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Getting partition #%u size...", partition); breaks++;*/ if (partition == (ISTORAGE_PARTITION_CNT - 1) && runningSxOs) @@ -143,47 +224,40 @@ bool dumpCartridgeImage(bool isFat32, bool setXciArchiveBit, bool keepCert, bool partitionSizes[partition] = ((gameCardSize - ((gameCardSize / GAMECARD_ECC_BLOCK_SIZE) * GAMECARD_ECC_DATA_SIZE)) - partitionSizesSum); xciDataSize += partitionSizes[partition]; - convertSize(partitionSizes[partition], partitionSizesStr[partition], sizeof(partitionSizesStr[partition]) / sizeof(partitionSizesStr[partition][0])); - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Partition #%u size: %s (%lu bytes).", partition, partitionSizesStr[partition], partitionSizes[partition]); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + convertSize(partitionSizes[partition], partitionSizesStr[partition], MAX_ELEMENTS(partitionSizesStr[partition])); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Partition #%u size: %s (%lu bytes).", partition, partitionSizesStr[partition], partitionSizes[partition]); breaks += 2;*/ } else { workaroundPartitionZeroAccess(); if (R_SUCCEEDED(result = fsDeviceOperatorGetGameCardHandle(&fsOperatorInstance, &handle))) { - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle succeeded: 0x%08X", handle.value); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "GetGameCardHandle succeeded: 0x%08X", handle.value); breaks++;*/ if (R_SUCCEEDED(result = fsOpenGameCardStorage(&gameCardStorage, &handle, partition))) { - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage succeeded: 0x%08X", handle.value); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "OpenGameCardStorage succeeded: 0x%08X", handle.value); breaks++;*/ if (R_SUCCEEDED(result = fsStorageGetSize(&gameCardStorage, &(partitionSizes[partition])))) { xciDataSize += partitionSizes[partition]; - convertSize(partitionSizes[partition], partitionSizesStr[partition], sizeof(partitionSizesStr[partition]) / sizeof(partitionSizesStr[partition][0])); - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Partition #%u size: %s (%lu bytes).", partition, partitionSizesStr[partition], partitionSizes[partition]); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + convertSize(partitionSizes[partition], partitionSizesStr[partition], MAX_ELEMENTS(partitionSizesStr[partition])); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Partition #%u size: %s (%lu bytes).", partition, partitionSizesStr[partition], partitionSizes[partition]); breaks += 2;*/ } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "StorageGetSize failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "StorageGetSize failed! (0x%08X)", result); proceed = false; } fsStorageClose(&gameCardStorage); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "OpenGameCardStorage failed! (0x%08X)", result); proceed = false; } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "GetGameCardHandle failed! (0x%08X)", result); proceed = false; } } @@ -191,386 +265,556 @@ bool dumpCartridgeImage(bool isFat32, bool setXciArchiveBit, bool keepCert, bool uiRefreshDisplay(); } - if (proceed) + if (!proceed) goto out; + + convertSize(xciDataSize, xciDataSizeStr, MAX_ELEMENTS(xciDataSizeStr)); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "XCI data size: %s (%lu bytes).", xciDataSizeStr, xciDataSize); + breaks += 2;*/ + + if (trimDump) { - convertSize(xciDataSize, xciDataSizeStr, sizeof(xciDataSizeStr) / sizeof(xciDataSizeStr[0])); - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "XCI data size: %s (%lu bytes).", xciDataSizeStr, xciDataSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - breaks += 2;*/ + progressCtx.totalSize = trimmedCardSize; + snprintf(progressCtx.totalSizeStr, MAX_ELEMENTS(progressCtx.totalSizeStr), "%s", trimmedCardSizeStr); - if (trimDump) + // Change dump size for the last IStorage partition + u64 partitionSizesSum = 0; + for(int i = 0; i < (ISTORAGE_PARTITION_CNT - 1); i++) partitionSizesSum += partitionSizes[i]; + + partitionSizes[ISTORAGE_PARTITION_CNT - 1] = (trimmedCardSize - partitionSizesSum); + } else { + progressCtx.totalSize = xciDataSize; + snprintf(progressCtx.totalSizeStr, MAX_ELEMENTS(progressCtx.totalSizeStr), "%s", xciDataSizeStr); + } + + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Output dump size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); + breaks++; + + if (seqDumpMode) + { + // Check if the current offset doesn't exceed the total XCI size + if (progressCtx.curOffset < progressCtx.totalSize) { - progressCtx.totalSize = trimmedCardSize; - snprintf(progressCtx.totalSizeStr, sizeof(progressCtx.totalSizeStr) / sizeof(progressCtx.totalSizeStr[0]), "%s", trimmedCardSizeStr); - - // Change dump size for the last IStorage partition - u64 partitionSizesSum = 0; - for(int i = 0; i < (ISTORAGE_PARTITION_CNT - 1); i++) partitionSizesSum += partitionSizes[i]; - - partitionSizes[ISTORAGE_PARTITION_CNT - 1] = (trimmedCardSize - partitionSizesSum); + // Check if the current partition offset doesn't exceed the partition size + if (seqXciCtx.partitionOffset < partitionSizes[seqXciCtx.partitionIndex]) + { + // Check if we have at least SPLIT_FILE_SEQUENTIAL_SIZE of free space + if (progressCtx.totalSize <= freeSpace || (progressCtx.totalSize > freeSpace && freeSpace >= SPLIT_FILE_SEQUENTIAL_SIZE)) + { + // Inform that we are resuming an already started sequential dump operation + breaks++; + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Resuming previous sequential dump operation. Configuration parameters overrided."); + breaks++; + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Keep certificate: %s | Trim output dump: %s | CRC32 checksum calculation + dump verification: %s.", (keepCert ? "Yes" : "No"), (trimDump ? "Yes" : "No"), (calcCrc ? "Yes" : "No")); + breaks++; + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: not enough free space available in the SD card."); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid IStorage partition offset in the sequential dump reference file!"); + proceed = false; + } } else { - progressCtx.totalSize = xciDataSize; - snprintf(progressCtx.totalSizeStr, sizeof(progressCtx.totalSizeStr) / sizeof(progressCtx.totalSizeStr[0]), "%s", xciDataSizeStr); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid XCI offset in the sequential dump reference file!"); + proceed = false; + } + } else { + if (progressCtx.totalSize > freeSpace) + { + // Check if we have at least (SPLIT_FILE_SEQUENTIAL_SIZE + sizeof(sequentialXciCtx)) of free space + if (freeSpace >= (SPLIT_FILE_SEQUENTIAL_SIZE + sizeof(sequentialXciCtx))) + { + // Ask the user if they want to use the sequential dump mode + int cur_breaks = breaks; + breaks++; + + if (yesNoPrompt("There's not enough space available to generate a whole dump in this session. Do you want to use sequential dumping?\nIn this mode, the selected content will be dumped in more than one session.\nYou'll have to transfer the generated part files to a PC before continuing the process in the next session.")) + { + // Remove the prompt from the screen + breaks = cur_breaks; + uiFill(0, 8 + STRING_Y_POS(breaks), FB_WIDTH, FB_HEIGHT - (8 + STRING_Y_POS(breaks)), BG_COLOR_RGB); + uiRefreshDisplay(); + + // Modify config parameters + isFat32 = true; + setXciArchiveBit = false; + + part_size = SPLIT_FILE_SEQUENTIAL_SIZE; + + seqDumpMode = true; + seqDumpFileSize = sizeof(sequentialXciCtx); + + // Fill information in our sequential context + seqXciCtx.keepCert = keepCert; + seqXciCtx.trimDump = trimDump; + seqXciCtx.calcCrc = calcCrc; + + // Create sequential reference file and keep the handle to it opened + seqDumpFile = fopen(seqDumpFilename, "wb+"); + if (seqDumpFile) + { + write_res = fwrite(&seqXciCtx, 1, seqDumpFileSize, seqDumpFile); + rewind(seqDumpFile); + + if (write_res == seqDumpFileSize) + { + // Update free space + freeSpace -= seqDumpFileSize; + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to write %lu bytes chunk to the sequential dump reference file! (wrote %lu bytes)", seqDumpFileSize, write_res); + proceed = false; + seqDumpFileRemove = true; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to create sequential dump reference file! (\"%s\")", seqDumpFilename); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Process canceled."); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: not enough free space available in the SD card."); + proceed = false; + } + } + } + + if (!proceed) goto out; + + breaks++; + + if (seqDumpMode) + { + snprintf(filename, MAX_ELEMENTS(filename), "%s%s.xci.%02u", XCI_DUMP_PATH, dumpName, splitIndex); + } else { + if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32) + { + if (setXciArchiveBit) + { + // Temporary, we'll use this to check if the dump already exists (it should have the archive bit set if so) + snprintf(filename, MAX_ELEMENTS(filename), "%s%s.xci", XCI_DUMP_PATH, dumpName); + } else { + snprintf(filename, MAX_ELEMENTS(filename), "%s%s.xc%u", XCI_DUMP_PATH, dumpName, splitIndex); + } + } else { + snprintf(filename, MAX_ELEMENTS(filename), "%s%s.xci", XCI_DUMP_PATH, dumpName); } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output dump size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - breaks++; + // Check if the dump already exists + if (checkIfFileExists(filename)) + { + // Ask the user if they want to proceed anyway + int cur_breaks = breaks; + breaks++; + + proceed = yesNoPrompt("You have already dumped this content. Do you wish to proceed anyway?"); + if (!proceed) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Process canceled."); + goto out; + } else { + // Remove the prompt from the screen + breaks = cur_breaks; + uiFill(0, 8 + STRING_Y_POS(breaks), FB_WIDTH, FB_HEIGHT - (8 + STRING_Y_POS(breaks)), BG_COLOR_RGB); + } + } - if (progressCtx.totalSize <= freeSpace) + if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32 && setXciArchiveBit) + { + // Since we may actually be dealing with an existing directory with the archive bit set or unset, let's try both + // Better safe than sorry + unlink(filename); + fsdevDeleteDirectoryRecursively(filename); + + mkdir(filename, 0744); + + sprintf(tmp_idx, "/%02u", splitIndex); + strcat(filename, tmp_idx); + } + } + + outFile = fopen(filename, "wb"); + if (!outFile) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to open output file \"%s\"!", filename); + goto out; + } + + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Dump procedure started. Hold %s to cancel.", NINTENDO_FONT_B); + breaks += 2; + + if (programAppletType != AppletType_Application && programAppletType != AppletType_SystemApplication) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem."); + breaks += 2; + } + + progressCtx.line_offset = (breaks + 4); + timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx.start)); + + u32 startPartitionIndex = (seqDumpMode ? seqXciCtx.partitionIndex : 0); + u64 startPartitionOffset; + + for(partition = startPartitionIndex; partition < ISTORAGE_PARTITION_CNT; partition++) + { + n = DUMP_BUFFER_SIZE; + + startPartitionOffset = ((seqDumpMode && partition == startPartitionIndex) ? seqXciCtx.partitionOffset : 0); + + workaroundPartitionZeroAccess(); + + if (R_FAILED(result = fsDeviceOperatorGetGameCardHandle(&fsOperatorInstance, &handle))) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "GetGameCardHandle failed for partition #%u! (0x%08X)", partition, result); + proceed = false; + break; + } + + if (R_FAILED(result = fsOpenGameCardStorage(&gameCardStorage, &handle, partition))) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "OpenGameCardStorage failed for partition #%u! (0x%08X)", partition, result); + proceed = false; + break; + } + + for(partitionOffset = startPartitionOffset; partitionOffset < partitionSizes[partition]; partitionOffset += n, progressCtx.curOffset += n, seqDumpSessionOffset += n) + { + if (seqDumpMode && seqDumpFinish) break; + + uiFill(0, ((progressCtx.line_offset - 4) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 4, BG_COLOR_RGB); + + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 4), FONT_COLOR_RGB, "Output file: \"%s\".", strrchr(filename, '/' ) + 1); + + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "Dumping IStorage partition #%u...", partition); + + if (n > (partitionSizes[partition] - partitionOffset)) n = (partitionSizes[partition] - partitionOffset); + + // Check if the next read chunk will exceed the size of the current part file + if (seqDumpMode && (seqDumpSessionOffset + n) >= (((splitIndex - seqXciCtx.partNumber) + 1) * part_size)) + { + u64 new_file_chunk_size = ((seqDumpSessionOffset + n) - (((splitIndex - seqXciCtx.partNumber) + 1) * part_size)); + u64 old_file_chunk_size = (n - new_file_chunk_size); + + u64 remainderDumpSize = (progressCtx.totalSize - (progressCtx.curOffset + old_file_chunk_size)); + u64 remainderFreeSize = (freeSpace - (seqDumpSessionOffset + old_file_chunk_size)); + + // Check if we have enough space for the next part + // If so, set the chunk size to old_file_chunk_size + if ((remainderDumpSize <= part_size && remainderDumpSize > remainderFreeSize) || (remainderDumpSize > part_size && part_size > remainderFreeSize)) + { + n = old_file_chunk_size; + seqDumpFinish = true; + } + } + + if (R_FAILED(result = fsStorageRead(&gameCardStorage, partitionOffset, dumpBuf, n))) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "StorageRead failed (0x%08X) at offset 0x%016lX for partition #%u", result, partitionOffset, partition); + proceed = false; + break; + } + + // Remove gamecard certificate + if (progressCtx.curOffset == 0 && !keepCert) memset(dumpBuf + CERT_OFFSET, 0xFF, CERT_SIZE); + + if (calcCrc) + { + if (!trimDump) + { + if (keepCert) + { + if (progressCtx.curOffset == 0) + { + // Update CRC32 (with gamecard certificate) + crc32(dumpBuf, n, &certCrc); + + // Backup gamecard certificate to an array + char tmpCert[CERT_SIZE] = {'\0'}; + memcpy(tmpCert, dumpBuf + CERT_OFFSET, CERT_SIZE); + + // Remove gamecard certificate from buffer + memset(dumpBuf + CERT_OFFSET, 0xFF, CERT_SIZE); + + // Update CRC32 (without gamecard certificate) + crc32(dumpBuf, n, &certlessCrc); + + // Restore gamecard certificate to buffer + memcpy(dumpBuf + CERT_OFFSET, tmpCert, CERT_SIZE); + } else { + // Update CRC32 (with gamecard certificate) + crc32(dumpBuf, n, &certCrc); + + // Update CRC32 (without gamecard certificate) + crc32(dumpBuf, n, &certlessCrc); + } + } else { + // Update CRC32 + crc32(dumpBuf, n, &certlessCrc); + } + } else { + // Update CRC32 + crc32(dumpBuf, n, &certCrc); + } + } + + if ((seqDumpMode || (!seqDumpMode && progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32)) && (progressCtx.curOffset + n) >= ((splitIndex + 1) * part_size)) + { + u64 new_file_chunk_size = ((progressCtx.curOffset + n) - ((splitIndex + 1) * part_size)); + u64 old_file_chunk_size = (n - new_file_chunk_size); + + if (old_file_chunk_size > 0) + { + write_res = fwrite(dumpBuf, 1, old_file_chunk_size, outFile); + if (write_res != old_file_chunk_size) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, progressCtx.curOffset, splitIndex, write_res); + proceed = false; + break; + } + } + + fclose(outFile); + outFile = NULL; + + if (((seqDumpMode && !seqDumpFinish) || !seqDumpMode) && (new_file_chunk_size > 0 || (progressCtx.curOffset + n) < progressCtx.totalSize)) + { + splitIndex++; + + if (seqDumpMode) + { + snprintf(filename, MAX_ELEMENTS(filename), "%s%s.xci.%02u", XCI_DUMP_PATH, dumpName, splitIndex); + } else { + if (setXciArchiveBit) + { + snprintf(filename, MAX_ELEMENTS(filename), "%s%s.xci/%02u", XCI_DUMP_PATH, dumpName, splitIndex); + } else { + snprintf(filename, MAX_ELEMENTS(filename), "%s%s.xc%u", XCI_DUMP_PATH, dumpName, splitIndex); + } + } + + outFile = fopen(filename, "wb"); + if (!outFile) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to open output file for part #%u!", splitIndex); + proceed = false; + break; + } + + if (new_file_chunk_size > 0) + { + write_res = fwrite(dumpBuf + old_file_chunk_size, 1, new_file_chunk_size, outFile); + if (write_res != new_file_chunk_size) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, progressCtx.curOffset + old_file_chunk_size, splitIndex, write_res); + proceed = false; + break; + } + } + } + } else { + write_res = fwrite(dumpBuf, 1, n, outFile); + if (write_res != n) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, progressCtx.curOffset, write_res); + + if (!seqDumpMode && (progressCtx.curOffset + n) > FAT32_FILESIZE_LIMIT) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 4), FONT_COLOR_RGB, "You're probably using a FAT32 partition. Make sure to enable the \"Split output dump\" option."); + fat32_error = true; + } + + proceed = false; + break; + } + } + + if (seqDumpMode) progressCtx.seqDumpCurOffset = seqDumpSessionOffset; + printProgressBar(&progressCtx, true, n); + + if ((progressCtx.curOffset + n) < progressCtx.totalSize) + { + if (cancelProcessCheck(&progressCtx)) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Process canceled."); + proceed = false; + if (seqDumpMode) seqDumpFileRemove = true; + break; + } + } + } + + fsStorageClose(&gameCardStorage); + + if (!proceed) break; + + // Support empty files + if (!partitionSizes[partition]) + { + uiFill(0, ((progressCtx.line_offset - 4) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 4, BG_COLOR_RGB); + + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 4), FONT_COLOR_RGB, "Output file: \"%s\".", strrchr(filename, '/' ) + 1); + + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "Dumping IStorage partition #%u...", partition); + + printProgressBar(&progressCtx, false, 0); + } + + if (progressCtx.curOffset >= progressCtx.totalSize || (seqDumpMode && seqDumpFinish)) success = true; + + if (seqDumpMode && seqDumpFinish) break; + } + + if (!proceed) setProgressBarError(&progressCtx); + + breaks = (progressCtx.line_offset + 2); + if (fat32_error) breaks += 2; + + if (outFile) fclose(outFile); + + if (success) + { + if (seqDumpMode) + { + if (seqDumpFinish) + { + // Update the sequence reference file in the SD card + seqXciCtx.partNumber = (splitIndex + 1); + seqXciCtx.partitionIndex = partition; + seqXciCtx.partitionOffset = partitionOffset; + + if (calcCrc) + { + seqXciCtx.certCrc = certCrc; + seqXciCtx.certlessCrc = certlessCrc; + } + + write_res = fwrite(&seqXciCtx, 1, seqDumpFileSize, seqDumpFile); + if (write_res != seqDumpFileSize) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to write %lu bytes chunk to the sequential dump reference file! (wrote %lu bytes)", seqDumpFileSize, write_res); + success = false; + seqDumpFileRemove = true; + goto out; + } + } else { + // Mark the file for deletion + seqDumpFileRemove = true; + + // Finally disable sequential dump mode flag + seqDumpMode = false; + } + } + + timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx.now)); + progressCtx.now -= progressCtx.start; + + formatETAString(progressCtx.now, progressCtx.etaInfo, MAX_ELEMENTS(progressCtx.etaInfo)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Process successfully completed after %s!", progressCtx.etaInfo); + + if (seqDumpMode && seqDumpFinish) + { + breaks += 2; + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Please remember to exit the application and transfer the generated part file(s) to a PC before continuing in the next session!"); + breaks++; + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Do NOT move the \"%s\" file!", strrchr(seqDumpFilename, '/' ) + 1); + } + + if (!seqDumpMode && calcCrc) { breaks++; + if (!trimDump) + { + if (keepCert) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "XCI dump CRC32 checksum (with certificate): %08X", certCrc); + breaks++; + + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "XCI dump CRC32 checksum (without certificate): %08X", certlessCrc); + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "XCI dump CRC32 checksum: %08X", certlessCrc); + } + + breaks += 2; + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Starting verification process using XML database from NSWDB.COM..."); + breaks++; + + uiRefreshDisplay(); + + gameCardDumpNSWDBCheck(certlessCrc); + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "XCI dump CRC32 checksum: %08X", certCrc); + breaks++; + + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Dump verification disabled (not compatible with trimmed dumps)."); + } + } + + // Set archive bit (only for FAT32 and if the required option is enabled) + if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32 && setXciArchiveBit) + { + snprintf(filename, MAX_ELEMENTS(filename), "%s%s.xci", XCI_DUMP_PATH, dumpName); + if (R_FAILED(result = fsdevSetArchiveBit(filename))) + { + breaks += 2; + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Warning: failed to set archive bit on output directory! (0x%08X)", result); + } + } + } else { + if (seqDumpMode) + { + for(u8 i = 0; i <= splitIndex; i++) + { + snprintf(filename, MAX_ELEMENTS(filename), "%s%s.xci.%02u", XCI_DUMP_PATH, dumpName, i); + unlink(filename); + } + } else { if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32) { if (setXciArchiveBit) { - // Temporary, we'll use this to check if the dump already exists (it should have the archive bit set if so) - snprintf(filename, sizeof(filename) / sizeof(filename[0]), "%s%s.xci", XCI_DUMP_PATH, dumpName); + snprintf(filename, MAX_ELEMENTS(filename), "%s%s.xci", XCI_DUMP_PATH, dumpName); + fsdevDeleteDirectoryRecursively(filename); } else { - snprintf(filename, sizeof(filename) / sizeof(filename[0]), "%s%s.xc%u", XCI_DUMP_PATH, dumpName, splitIndex); + for(u8 i = 0; i <= splitIndex; i++) + { + snprintf(filename, MAX_ELEMENTS(filename), "%s%s.xc%u", XCI_DUMP_PATH, dumpName, i); + unlink(filename); + } } } else { - snprintf(filename, sizeof(filename) / sizeof(filename[0]), "%s%s.xci", XCI_DUMP_PATH, dumpName); + unlink(filename); } - - // Check if the dump already exists - if (checkIfFileExists(filename)) - { - // Ask the user if they want to proceed anyway - int cur_breaks = breaks; - breaks++; - - proceed = yesNoPrompt("You have already dumped this content. Do you wish to proceed anyway?"); - if (!proceed) - { - uiDrawString("Process canceled.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - } else { - // Remove the prompt from the screen - breaks = cur_breaks; - uiFill(0, 8 + (breaks * (font_height + (font_height / 4))) + (font_height / 8), FB_WIDTH, FB_HEIGHT - (8 + (breaks * (font_height + (font_height / 4))) + (font_height / 8)), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); - } - } - - if (proceed) - { - if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32 && setXciArchiveBit) - { - // Since we may actually be dealing with an existing directory with the archive bit set or unset, let's try both - // Better safe than sorry - unlink(filename); - fsdevDeleteDirectoryRecursively(filename); - - mkdir(filename, 0744); - - sprintf(tmp_idx, "/%02u", splitIndex); - strcat(filename, tmp_idx); - } - - outFile = fopen(filename, "wb"); - if (outFile) - { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dump procedure started. Hold %s to cancel.", NINTENDO_FONT_B); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - breaks += 2; - - if (programAppletType != AppletType_Application && programAppletType != AppletType_SystemApplication) - { - uiDrawString("Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - breaks += 2; - } - - progressCtx.line_offset = (breaks + 4); - timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx.start)); - - for(partition = 0; partition < ISTORAGE_PARTITION_CNT; partition++) - { - n = DUMP_BUFFER_SIZE; - - workaroundPartitionZeroAccess(); - - if (R_SUCCEEDED(result = fsDeviceOperatorGetGameCardHandle(&fsOperatorInstance, &handle))) - { - if (R_SUCCEEDED(result = fsOpenGameCardStorage(&gameCardStorage, &handle, partition))) - { - for(partitionOffset = 0; partitionOffset < partitionSizes[partition]; partitionOffset += n, progressCtx.curOffset += n) - { - uiFill(0, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 4, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); - - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", strrchr(filename, '/' ) + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dumping IStorage partition #%u...", partition); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - - if (DUMP_BUFFER_SIZE > (partitionSizes[partition] - partitionOffset)) n = (partitionSizes[partition] - partitionOffset); - - if (R_FAILED(result = fsStorageRead(&gameCardStorage, partitionOffset, dumpBuf, n))) - { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "StorageRead failed (0x%08X) at offset 0x%016lX for partition #%u", result, partitionOffset, partition); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - proceed = false; - break; - } - - // Remove gamecard certificate - if (progressCtx.curOffset == 0 && !keepCert) memset(dumpBuf + CERT_OFFSET, 0xFF, CERT_SIZE); - - if (calcCrc) - { - if (!trimDump) - { - if (keepCert) - { - if (progressCtx.curOffset == 0) - { - // Update CRC32 (with gamecard certificate) - crc32(dumpBuf, n, &crc1); - - // Backup gamecard certificate to an array - char tmpCert[CERT_SIZE] = {'\0'}; - memcpy(tmpCert, dumpBuf + CERT_OFFSET, CERT_SIZE); - - // Remove gamecard certificate from buffer - memset(dumpBuf + CERT_OFFSET, 0xFF, CERT_SIZE); - - // Update CRC32 (without gamecard certificate) - crc32(dumpBuf, n, &crc2); - - // Restore gamecard certificate to buffer - memcpy(dumpBuf + CERT_OFFSET, tmpCert, CERT_SIZE); - } else { - // Update CRC32 (with gamecard certificate) - crc32(dumpBuf, n, &crc1); - - // Update CRC32 (without gamecard certificate) - crc32(dumpBuf, n, &crc2); - } - } else { - // Update CRC32 - crc32(dumpBuf, n, &crc2); - } - } else { - // Update CRC32 - crc32(dumpBuf, n, &crc1); - } - } - - if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32 && (progressCtx.curOffset + n) >= ((splitIndex + 1) * part_size)) - { - u64 new_file_chunk_size = ((progressCtx.curOffset + n) - ((splitIndex + 1) * part_size)); - u64 old_file_chunk_size = (n - new_file_chunk_size); - - if (old_file_chunk_size > 0) - { - write_res = fwrite(dumpBuf, 1, old_file_chunk_size, outFile); - if (write_res != old_file_chunk_size) - { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, progressCtx.curOffset, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - proceed = false; - break; - } - } - - fclose(outFile); - outFile = NULL; - - if (new_file_chunk_size > 0 || (progressCtx.curOffset + n) < progressCtx.totalSize) - { - splitIndex++; - - if (setXciArchiveBit) - { - snprintf(filename, sizeof(filename) / sizeof(filename[0]), "%s%s.xci/%02u", XCI_DUMP_PATH, dumpName, splitIndex); - } else { - snprintf(filename, sizeof(filename) / sizeof(filename[0]), "%s%s.xc%u", XCI_DUMP_PATH, dumpName, splitIndex); - } - - outFile = fopen(filename, "wb"); - if (!outFile) - { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open output file for part #%u!", splitIndex); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - proceed = false; - break; - } - - if (new_file_chunk_size > 0) - { - write_res = fwrite(dumpBuf + old_file_chunk_size, 1, new_file_chunk_size, outFile); - if (write_res != new_file_chunk_size) - { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, progressCtx.curOffset + old_file_chunk_size, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - proceed = false; - break; - } - } - } - } else { - write_res = fwrite(dumpBuf, 1, n, outFile); - if (write_res != n) - { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, progressCtx.curOffset, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - - if ((progressCtx.curOffset + n) > FAT32_FILESIZE_LIMIT) - { - uiDrawString("You're probably using a FAT32 partition. Make sure to enable the \"Split output dump\" option.", 8, ((progressCtx.line_offset + 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - fat32_error = true; - } - - proceed = false; - break; - } - } - - printProgressBar(&progressCtx, true, n); - - if ((progressCtx.curOffset + n) < progressCtx.totalSize) - { - if (cancelProcessCheck(&progressCtx)) - { - uiDrawString("Process canceled.", 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - proceed = false; - break; - } - } - } - - if (progressCtx.curOffset >= progressCtx.totalSize) success = true; - - // Support empty files - if (!partitionSizes[partition]) - { - uiFill(0, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 4, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); - - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", strrchr(filename, '/' ) + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dumping IStorage partition #%u...", partition); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - - printProgressBar(&progressCtx, false, 0); - } - - fsStorageClose(&gameCardStorage); - } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage failed for partition #%u! (0x%08X)", partition, result); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - proceed = false; - } - } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle failed for partition #%u! (0x%08X)", partition, result); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - proceed = false; - } - - if (!proceed) - { - setProgressBarError(&progressCtx); - break; - } - } - - breaks = (progressCtx.line_offset + 2); - if (fat32_error) breaks += 2; - - if (outFile) fclose(outFile); - - if (success) - { - timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx.now)); - progressCtx.now -= progressCtx.start; - - formatETAString(progressCtx.now, progressCtx.etaInfo, sizeof(progressCtx.etaInfo) / sizeof(progressCtx.etaInfo[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Process successfully completed after %s!", progressCtx.etaInfo); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); - - if (calcCrc) - { - breaks++; - - if (!trimDump) - { - if (keepCert) - { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "XCI dump CRC32 checksum (with certificate): %08X", crc1); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); - breaks++; - - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "XCI dump CRC32 checksum (without certificate): %08X", crc2); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); - } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "XCI dump CRC32 checksum: %08X", crc2); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); - } - - breaks += 2; - uiDrawString("Starting verification process using XML database from NSWDB.COM...", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - breaks++; - - uiRefreshDisplay(); - - gameCardDumpNSWDBCheck(crc2); - } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "XCI dump CRC32 checksum: %08X", crc1); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); - breaks++; - - uiDrawString("Dump verification disabled (not compatible with trimmed dumps).", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - } - } - - // Set archive bit (only for FAT32 and if the required option is enabled) - if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32 && setXciArchiveBit) - { - snprintf(filename, sizeof(filename) / sizeof(filename[0]), "%s%s.xci", XCI_DUMP_PATH, dumpName); - if (R_FAILED(result = fsdevSetArchiveBit(filename))) - { - breaks += 2; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Warning: failed to set archive bit on output directory! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - } - } - } else { - if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32) - { - if (setXciArchiveBit) - { - snprintf(filename, sizeof(filename) / sizeof(filename[0]), "%s%s.xci", XCI_DUMP_PATH, dumpName); - fsdevDeleteDirectoryRecursively(filename); - } else { - for(u8 i = 0; i <= splitIndex; i++) - { - snprintf(filename, sizeof(filename) / sizeof(filename[0]), "%s%s.xc%u", XCI_DUMP_PATH, dumpName, i); - unlink(filename); - } - } - } else { - unlink(filename); - } - } - } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open output file \"%s\"!", filename); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - } - } - } else { - uiDrawString("Error: not enough free space available in the SD card.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); } } +out: breaks += 2; - free(dumpName); + if (dumpName) free(dumpName); + + if (seqDumpFile) fclose(seqDumpFile); + + if (seqDumpFileRemove) unlink(seqDumpFilename); return success; } -bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleIndex, bool isFat32, bool calcCrc, bool removeConsoleData, bool tiklessDump, bool batch) +bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleIndex, nspOptions *nspDumpCfg, bool batch) { + if (!nspDumpCfg) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NSP configuration struct!"); + breaks += 2; + return false; + } + + bool isFat32 = nspDumpCfg->isFat32; + bool calcCrc = nspDumpCfg->calcCrc; + bool removeConsoleData = nspDumpCfg->removeConsoleData; + bool tiklessDump = nspDumpCfg->tiklessDump; + Result result; u32 i = 0, j = 0; u32 written = 0; @@ -649,14 +893,8 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd u64 nspPfs0StrTableSize = 0; u64 full_nsp_header_size = 0; - u64 hash_table_dump_buffer_start = 0; - u64 hash_table_dump_buffer_end = 0; - u64 block0_dump_buffer_start = 0; - u64 block0_dump_buffer_end = 0; - u64 block1_dump_buffer_start = 0; - u64 block1_dump_buffer_end = 0; - Sha256Context nca_hash_ctx; + sha256ContextCreate(&nca_hash_ctx); char dumpPath[NAME_BUF_LEN * 2] = {'\0'}; @@ -671,15 +909,27 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd progress_ctx_t progressCtx; memset(&progressCtx, 0, sizeof(progress_ctx_t)); + bool seqDumpMode = false, seqDumpFileRemove = false, seqDumpFinish = false; + char seqDumpFilename[NAME_BUF_LEN * 2] = {'\0'}; + FILE *seqDumpFile = NULL; + u64 seqDumpFileSize = 0, seqDumpSessionOffset = 0; + u8 *seqDumpNcaHashes = NULL; + + sequentialNspCtx seqNspCtx; + memset(&seqNspCtx, 0, sizeof(sequentialNspCtx)); + + char pfs0HeaderFilename[NAME_BUF_LEN * 2] = {'\0'}; + FILE *pfs0HeaderFile = NULL; + char tmp_idx[5]; - size_t write_res; + size_t read_res, write_res; int initial_breaks = breaks; if ((selectedNspDumpType == DUMP_APP_NSP && !titleAppStorageId) || (selectedNspDumpType == DUMP_PATCH_NSP && !titlePatchStorageId) || (selectedNspDumpType == DUMP_ADDON_NSP && !titleAddOnStorageId)) { - uiDrawString("Error: title storage ID unavailable!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: title storage ID unavailable!"); breaks += 2; return false; } @@ -688,7 +938,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd if (curStorageId != FsStorageId_GameCard && curStorageId != FsStorageId_SdCard && curStorageId != FsStorageId_NandUser) { - uiDrawString("Error: invalid title storage ID!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid title storage ID!"); breaks += 2; return false; } @@ -713,14 +963,14 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd if (!titleCount) { - uiDrawString("Error: invalid title type count!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid title type count!"); breaks += 2; return false; } if (ncmTitleIndex > (titleCount - 1)) { - uiDrawString("Error: invalid title index!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid title index!"); breaks += 2; return false; } @@ -730,11 +980,97 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd char *dumpName = generateNSPDumpName(selectedNspDumpType, titleIndex); if (!dumpName) { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to generate output dump name!"); breaks += 2; return false; } + if (!batch) + { + snprintf(seqDumpFilename, MAX_ELEMENTS(seqDumpFilename), "%s%s.nsp.seq", NSP_DUMP_PATH, dumpName); + snprintf(pfs0HeaderFilename, MAX_ELEMENTS(pfs0HeaderFilename), "%s%s.nsp.hdr", NSP_DUMP_PATH, dumpName); + + // Check if we're dealing with a sequential dump + seqDumpMode = checkIfFileExists(seqDumpFilename); + if (seqDumpMode) + { + // Open sequence file + seqDumpFile = fopen(seqDumpFilename, "rb+"); + if (seqDumpFile) + { + // Retrieve sequence file size + fseek(seqDumpFile, 0, SEEK_END); + seqDumpFileSize = ftell(seqDumpFile); + rewind(seqDumpFile); + + // Check file size + if (seqDumpFileSize > sizeof(sequentialNspCtx) && ((seqDumpFileSize - sizeof(sequentialNspCtx)) % SHA256_HASH_SIZE) == 0) + { + // Read sequentialNspCtx struct info + read_res = fread(&seqNspCtx, 1, sizeof(sequentialNspCtx), seqDumpFile); + if (read_res == sizeof(sequentialNspCtx)) + { + // Check if the storage ID is right + if (seqNspCtx.storageId == curStorageId) + { + // Check if we have the right amount of NCA hashes + if ((seqNspCtx.ncaCount * SHA256_HASH_SIZE) == (seqDumpFileSize - sizeof(sequentialNspCtx))) + { + // Allocate memory for the NCA hashes + seqDumpNcaHashes = calloc(1, seqDumpFileSize - sizeof(sequentialNspCtx)); + if (seqDumpNcaHashes) + { + // Read NCA hashes + read_res = fread(seqDumpNcaHashes, 1, seqDumpFileSize - sizeof(sequentialNspCtx), seqDumpFile); + rewind(seqDumpFile); + + if (read_res == (seqDumpFileSize - sizeof(sequentialNspCtx))) + { + // Restore parameters from the sequence file + isFat32 = true; + calcCrc = false; + removeConsoleData = seqNspCtx.removeConsoleData; + tiklessDump = seqNspCtx.tiklessDump; + splitIndex = seqNspCtx.partNumber; + progressCtx.curOffset = ((u64)seqNspCtx.partNumber * SPLIT_FILE_SEQUENTIAL_SIZE); + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to read %lu bytes chunk from the sequential dump reference file! (read %lu bytes)", seqNspCtx.ncaCount * SHA256_HASH_SIZE, read_res); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for NCA hashes from the sequential dump reference file!"); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NCA count and/or NCA hash count in sequential dump reference file!"); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid source storage ID in sequential dump reference file!"); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to read %lu bytes chunk from the sequential dump reference file! (read %lu bytes)", seqDumpFileSize, read_res); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid sequential dump reference file size!"); + proceed = false; + seqDumpFileRemove = true; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to open existing sequential dump reference file for reading! (\"%s\")", seqDumpFilename); + proceed = false; + } + + uiRefreshDisplay(); + } + + if (!proceed) goto out; + } + + u64 part_size = (seqDumpMode ? SPLIT_FILE_SEQUENTIAL_SIZE : SPLIT_FILE_NSP_PART_SIZE); + // If we're dealing with a gamecard, call workaroundPartitionZeroAccess() and read the secure partition header. Otherwise, ncmContentStorageReadContentIdFile() will fail with error 0x00171002 // Also open an IStorage instance for the HFS0 Secure partition, since we may also need it if we're dealing with a Patch with titlekey crypto, in order to retrieve the tik file if (curStorageId == FsStorageId_GameCard) @@ -747,28 +1083,26 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd if (!partitionHfs0FileCount) { - uiDrawString("The Secure HFS0 partition is empty!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "The Secure HFS0 partition is empty!"); goto out; } if (R_FAILED(result = fsDeviceOperatorGetGameCardHandle(&fsOperatorInstance, &handle))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "GetGameCardHandle failed! (0x%08X)", result); goto out; } if (R_FAILED(result = fsOpenGameCardStorage(&gameCardStorage, &handle, HFS0_TO_ISTORAGE_IDX(hfs0_partition_cnt, partition)))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "OpenGameCardStorage failed! (0x%08X)", result); goto out; } } if (!batch) { - uiDrawString("Retrieving information from encrypted NCA content files...", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Retrieving information from encrypted NCA content files..."); uiRefreshDisplay(); breaks++; } @@ -776,14 +1110,13 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd titleList = calloc(1, titleListSize); if (!titleList) { - uiDrawString("Error: unable to allocate memory for the ApplicationContentMetaKey struct!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the ApplicationContentMetaKey struct!"); goto out; } if (R_FAILED(result = ncmOpenContentMetaDatabase(curStorageId, &ncmDb))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmOpenContentMetaDatabase failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmOpenContentMetaDatabase failed! (0x%08X)", result); goto out; } @@ -791,28 +1124,25 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd if (R_FAILED(result = ncmContentMetaDatabaseListApplication(&ncmDb, filter, titleList, titleListSize, &written, &total))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmContentMetaDatabaseListApplication failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmContentMetaDatabaseListApplication failed! (0x%08X)", result); goto out; } if (!written || !total) { - uiDrawString("Error: ncmContentMetaDatabaseListApplication wrote no entries to output buffer!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmContentMetaDatabaseListApplication wrote no entries to output buffer!"); goto out; } if (written != total) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: title count mismatch in ncmContentMetaDatabaseListApplication (%u != %u)", written, total); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: title count mismatch in ncmContentMetaDatabaseListApplication (%u != %u)", written, total); goto out; } if (R_FAILED(result = ncmContentMetaDatabaseGet(&ncmDb, &(titleList[ncmTitleIndex].metaRecord), sizeof(NcmContentMetaRecordsHeader), &contentRecordsHeader, &contentRecordsHeaderReadSize))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmContentMetaDatabaseGet failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmContentMetaDatabaseGet failed! (0x%08X)", result); goto out; } @@ -821,14 +1151,13 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd titleContentRecords = calloc(titleNcaCount, sizeof(NcmContentRecord)); if (!titleContentRecords) { - uiDrawString("Error: unable to allocate memory for the ContentRecord struct!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the ContentRecord struct!"); goto out; } if (R_FAILED(result = ncmContentMetaDatabaseListContentInfo(&ncmDb, &(titleList[ncmTitleIndex].metaRecord), 0, titleContentRecords, titleNcaCount * sizeof(NcmContentRecord), &written))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmContentMetaDatabaseListContentInfo failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmContentMetaDatabaseListContentInfo failed! (0x%08X)", result); goto out; } @@ -842,14 +1171,13 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd xml_content_info = calloc(titleNcaCount, sizeof(cnmt_xml_content_info)); if (!xml_content_info) { - uiDrawString("Error: unable to allocate memory for the CNMT XML content info struct!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the CNMT XML content info struct!"); goto out; } if (R_FAILED(result = ncmOpenContentStorage(curStorageId, &ncmStorage))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmOpenContentStorage failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmOpenContentStorage failed! (0x%08X)", result); goto out; } @@ -875,17 +1203,16 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd // Fill information for our CNMT XML xml_content_info[i].type = titleContentRecords[titleRecordIndex].type; - memcpy(xml_content_info[i].nca_id, titleContentRecords[titleRecordIndex].ncaId.c, 16); // Temporary - convertDataToHexString(titleContentRecords[titleRecordIndex].ncaId.c, 16, xml_content_info[i].nca_id_str, 33); // Temporary + memcpy(xml_content_info[i].nca_id, titleContentRecords[titleRecordIndex].ncaId.c, SHA256_HASH_SIZE / 2); // Temporary + convertDataToHexString(titleContentRecords[titleRecordIndex].ncaId.c, SHA256_HASH_SIZE / 2, xml_content_info[i].nca_id_str, SHA256_HASH_SIZE + 1); // Temporary convertNcaSizeToU64(titleContentRecords[titleRecordIndex].size, &(xml_content_info[i].size)); - convertDataToHexString(xml_content_info[i].hash, 32, xml_content_info[i].hash_str, 65); // Temporary + convertDataToHexString(xml_content_info[i].hash, SHA256_HASH_SIZE, xml_content_info[i].hash_str, (SHA256_HASH_SIZE * 2) + 1); // Temporary memcpy(&ncaId, &(titleContentRecords[titleRecordIndex].ncaId), sizeof(NcmNcaId)); if (R_FAILED(result = ncmContentStorageReadContentIdFile(&ncmStorage, &ncaId, 0, ncaHeader, NCA_FULL_HEADER_LENGTH))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read header from NCA \"%s\"! (0x%08X)", xml_content_info[i].nca_id_str, result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read header from NCA \"%s\"! (0x%08X)", xml_content_info[i].nca_id_str, result); proceed = false; break; } @@ -918,7 +1245,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd { if (has_rights_id) { - uiDrawString("Error: Rights ID field in NCA header not empty!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: Rights ID field in NCA header not empty!"); proceed = false; break; } @@ -1076,7 +1403,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd if (proceed && !cnmtFound) { - uiDrawString("Error: unable to find CNMT NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to find CNMT NCA!"); goto out; } @@ -1085,10 +1412,10 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd // Fill information for our CNMT XML xml_content_info[titleNcaCount - 1].type = titleContentRecords[cnmtNcaIndex].type; - memcpy(xml_content_info[titleNcaCount - 1].nca_id, titleContentRecords[cnmtNcaIndex].ncaId.c, 16); // Temporary - convertDataToHexString(titleContentRecords[cnmtNcaIndex].ncaId.c, 16, xml_content_info[titleNcaCount - 1].nca_id_str, 33); // Temporary + memcpy(xml_content_info[titleNcaCount - 1].nca_id, titleContentRecords[cnmtNcaIndex].ncaId.c, SHA256_HASH_SIZE / 2); // Temporary + convertDataToHexString(titleContentRecords[cnmtNcaIndex].ncaId.c, SHA256_HASH_SIZE / 2, xml_content_info[titleNcaCount - 1].nca_id_str, SHA256_HASH_SIZE + 1); // Temporary convertNcaSizeToU64(titleContentRecords[cnmtNcaIndex].size, &(xml_content_info[titleNcaCount - 1].size)); - convertDataToHexString(xml_content_info[titleNcaCount - 1].hash, 32, xml_content_info[titleNcaCount - 1].hash_str, 65); // Temporary + convertDataToHexString(xml_content_info[titleNcaCount - 1].hash, SHA256_HASH_SIZE, xml_content_info[titleNcaCount - 1].hash_str, (SHA256_HASH_SIZE * 2) + 1); // Temporary memcpy(&ncaId, &(titleContentRecords[cnmtNcaIndex].ncaId), sizeof(NcmNcaId)); @@ -1098,14 +1425,13 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd cnmtNcaBuf = malloc(xml_content_info[cnmtNcaIndex].size); if (!cnmtNcaBuf) { - uiDrawString("Error: unable to allocate memory for CNMT NCA data!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for CNMT NCA data!"); goto out; } if (R_FAILED(result = ncmContentStorageReadContentIdFile(&ncmStorage, &ncaId, 0, cnmtNcaBuf, xml_content_info[cnmtNcaIndex].size))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read CNMT NCA \"%s\"! (0x%08X)", xml_content_info[cnmtNcaIndex].nca_id_str, result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read CNMT NCA \"%s\"! (0x%08X)", xml_content_info[cnmtNcaIndex].nca_id_str, result); goto out; } @@ -1114,7 +1440,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd // Generate a placeholder CNMT XML. It's length will be used to calculate the final output dump size /*breaks++; - uiDrawString("Generating placeholder CNMT XML...", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Generating placeholder CNMT XML..."); uiRefreshDisplay(); breaks++;*/ @@ -1122,7 +1448,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd cnmtXml = calloc(NSP_XML_BUFFER_SIZE, sizeof(char)); if (!cnmtXml) { - uiDrawString("Error: unable to allocate memory for the CNMT XML!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the CNMT XML!"); goto out; } @@ -1218,7 +1544,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd // Start NSP creation /*breaks++; - uiDrawString("Generating placeholder PFS0 header...", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Generating placeholder PFS0 header..."); uiRefreshDisplay(); breaks++;*/ @@ -1229,7 +1555,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd nspPfs0EntryTable = calloc(nspFileCount, sizeof(pfs0_entry_table)); if (!nspPfs0EntryTable) { - uiDrawString("Unable to allocate memory for the PFS0 file entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Unable to allocate memory for the PFS0 file entries!"); goto out; } @@ -1237,7 +1563,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd nspPfs0StrTable = calloc(nspPfs0StrTableSize * 2, sizeof(char)); if (!nspPfs0StrTable) { - uiDrawString("Unable to allocate memory for the PFS0 string table!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Unable to allocate memory for the PFS0 string table!"); goto out; } @@ -1274,107 +1600,351 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd if (includeTikAndCert) progressCtx.totalSize += (ETICKET_TIK_FILE_SIZE + ETICKET_CERT_FILE_SIZE); - convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, sizeof(progressCtx.totalSizeStr) / sizeof(progressCtx.totalSizeStr[0])); + convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, MAX_ELEMENTS(progressCtx.totalSizeStr)); if (!batch) { breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Total NSP dump size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Total NSP dump size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); uiRefreshDisplay(); breaks++; - } - - if (progressCtx.totalSize > freeSpace) - { - uiDrawString("Error: not enough free space available in the SD card.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - goto out; - } - - // Temporary, we'll use this to check if the dump already exists (it should have the archive bit set if so) - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp", NSP_DUMP_PATH, dumpName); - - // Check if the dump already exists - if (!batch && checkIfFileExists(dumpPath)) - { - // Ask the user if they want to proceed anyway - int cur_breaks = breaks; - breaks++; - proceed = yesNoPrompt("You have already dumped this content. Do you wish to proceed anyway?"); - if (!proceed) + if (seqDumpMode) { - uiDrawString("Process canceled.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - removeFile = false; - goto out; + // Check if the current offset doesn't exceed the total NSP size + if (progressCtx.curOffset < progressCtx.totalSize) + { + // Check if we have at least SPLIT_FILE_SEQUENTIAL_SIZE of free space + if (progressCtx.totalSize <= freeSpace || (progressCtx.totalSize > freeSpace && freeSpace >= SPLIT_FILE_SEQUENTIAL_SIZE)) + { + // Check if the NCA count is valid + // The CNMT NCA is excluded from the hash list + if (seqNspCtx.ncaCount == (titleNcaCount - 1)) + { + // Check if the PFS0 file count is valid + if (seqNspCtx.nspFileCount == nspFileCount) + { + // Check if the current PFS0 file index is valid + if (seqNspCtx.fileIndex < nspFileCount) + { + for(i = 0; i < nspFileCount; i++) + { + if (i < seqNspCtx.fileIndex) + { + // Exclude the CNMT NCA + if (i < (titleNcaCount - 1)) + { + // Fill information for our CNMT XML + memcpy(xml_content_info[i].nca_id, seqDumpNcaHashes + (i * SHA256_HASH_SIZE), SHA256_HASH_SIZE / 2); + convertDataToHexString(xml_content_info[i].nca_id, SHA256_HASH_SIZE / 2, xml_content_info[i].nca_id_str, SHA256_HASH_SIZE + 1); + memcpy(xml_content_info[i].hash, seqDumpNcaHashes + (i * SHA256_HASH_SIZE), SHA256_HASH_SIZE); + convertDataToHexString(xml_content_info[i].hash, SHA256_HASH_SIZE, xml_content_info[i].hash_str, (SHA256_HASH_SIZE * 2) + 1); + } + } else { + if (i < titleNcaCount) + { + // Check if the offset for the current NCA is valid + if (seqNspCtx.fileOffset < xml_content_info[i].size) + { + // Copy the SHA-256 context data, but only if we're not dealing with the CNMT NCA + // NCA ID/hash for the CNMT NCA is handled in patchCnmtNca() + if (i != cnmtNcaIndex) memcpy(&nca_hash_ctx, &(seqNspCtx.hashCtx), sizeof(Sha256Context)); + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NCA offset in the sequential dump reference file!"); + proceed = false; + } + } else + if (i == titleNcaCount) + { + // Check if the offset for the CNMT XML is valid + if (seqNspCtx.fileOffset >= strlen(cnmtXml)) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid CNMT XML offset in the sequential dump reference file!"); + proceed = false; + } + } else { + if (programInfoXml && i == (titleNcaCount + 1)) + { + // Check if the offset for the programinfo.xml is valid + if (seqNspCtx.fileOffset >= programInfoXmlSize) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid programinfo.xml offset in the sequential dump reference file!"); + proceed = false; + } + } else + if (nacpIcons && nacpIconCnt && ((!programInfoXml && i <= (titleNcaCount + nacpIconCnt)) || (programInfoXml && i <= (titleNcaCount + 1 + nacpIconCnt)))) + { + // Check if the offset for the NACP icon is valid + u32 icon_idx = (!programInfoXml ? (i - (titleNcaCount + 1)) : (i - (titleNcaCount + 2))); + if (seqNspCtx.fileOffset >= nacpIcons[icon_idx].icon_size) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NACP icon offset in the sequential dump reference file!"); + proceed = false; + } + } else + if (nacpXml && ((!programInfoXml && i == (titleNcaCount + nacpIconCnt + 1)) || (programInfoXml && i == (titleNcaCount + 1 + nacpIconCnt + 1)))) + { + // Check if the offset for the NACP XML is valid + if (seqNspCtx.fileOffset >= nacpXmlSize) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NACP XML offset in the sequential dump reference file!"); + proceed = false; + } + } else + if (legalInfoXml && ((!includeTikAndCert && i == (nspFileCount - 1)) || (includeTikAndCert && i == (nspFileCount - 3)))) + { + // Check if the offset for the legalinfo.xml is valid + if (seqNspCtx.fileOffset >= legalInfoXmlSize) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid legalinfo.xml offset in the sequential dump reference file!"); + proceed = false; + } + } else { + if (i == (nspFileCount - 2)) + { + // Check if the offset for the ticket is valid + if (seqNspCtx.fileOffset >= ETICKET_TIK_FILE_SIZE) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid ticket offset in the sequential dump reference file!"); + proceed = false; + } + } else { + // Check if the offset for the certificate chain is valid + if (seqNspCtx.fileOffset >= ETICKET_CERT_FILE_SIZE) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid certificate chain offset in the sequential dump reference file!"); + proceed = false; + } + } + } + } + + break; + } + } + + if (proceed) + { + // Restore the modified Program NCA header + // The NPDM signature from the NCA header is generated using cryptographically secure random numbers, so the modified header is stored during the first sequential dump session + // If needed, it must be restored in later sessions + if (ncaProgramMod.block_mod_cnt) memcpy(xml_content_info[programNcaIndex].encrypted_header_mod, &(seqNspCtx.programNcaHeaderMod), NCA_FULL_HEADER_LENGTH); + + // Inform that we are resuming an already started sequential dump operation + breaks++; + + if (curStorageId == FsStorageId_GameCard) + { + if (selectedNspDumpType == DUMP_APP_NSP || selectedNspDumpType == DUMP_ADDON_NSP) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Resuming previous sequential dump operation."); + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Resuming previous sequential dump operation. Configuration parameters overrided."); + breaks++; + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Generate ticket-less dump: %s.", (tiklessDump ? "Yes" : "No")); + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Resuming previous sequential dump operation. Configuration parameters overrided."); + breaks++; + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Remove console specific data: %s | Generate ticket-less dump: %s.", (removeConsoleData ? "Yes" : "No"), (tiklessDump ? "Yes" : "No")); + } + + breaks++; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid PFS0 file index in the sequential dump reference file!"); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: PFS0 file count mismatch in the sequential dump reference file! (%u != %u)", seqNspCtx.nspFileCount, nspFileCount); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: NCA count mismatch in the sequential dump reference file! (%u != %u)", seqNspCtx.ncaCount, titleNcaCount - 1); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: not enough free space available in the SD card."); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NSP offset in the sequential dump reference file!"); + proceed = false; + } } else { - // Remove the prompt from the screen - breaks = cur_breaks; - uiFill(0, 8 + (breaks * (font_height + (font_height / 4))) + (font_height / 8), FB_WIDTH, FB_HEIGHT - (8 + (breaks * (font_height + (font_height / 4))) + (font_height / 8)), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + if (progressCtx.totalSize > freeSpace) + { + // Check if we have at least (SPLIT_FILE_SEQUENTIAL_SIZE + (sizeof(sequentialNspCtx) + ((titleNcaCount - 1) * SHA256_HASH_SIZE))) of free space + // The CNMT NCA is excluded from the hash list + if (freeSpace >= (SPLIT_FILE_SEQUENTIAL_SIZE + (sizeof(sequentialNspCtx) + ((titleNcaCount - 1) * SHA256_HASH_SIZE)))) + { + // Ask the user if they want to use the sequential dump mode + int cur_breaks = breaks; + breaks++; + + if (yesNoPrompt("There's not enough space available to generate a whole dump in this session. Do you want to use sequential dumping?\nIn this mode, the selected content will be dumped in more than one session.\nYou'll have to transfer the generated part files to a PC before continuing the process in the next session.")) + { + // Remove the prompt from the screen + breaks = cur_breaks; + uiFill(0, 8 + STRING_Y_POS(breaks), FB_WIDTH, FB_HEIGHT - (8 + STRING_Y_POS(breaks)), BG_COLOR_RGB); + uiRefreshDisplay(); + + // Modify config parameters + isFat32 = true; + calcCrc = false; + + part_size = SPLIT_FILE_SEQUENTIAL_SIZE; + + seqDumpMode = true; + seqDumpFileSize = (sizeof(sequentialNspCtx) + ((titleNcaCount - 1) * SHA256_HASH_SIZE)); + + // Fill information in our sequential context + seqNspCtx.storageId = curStorageId; + seqNspCtx.removeConsoleData = removeConsoleData; + seqNspCtx.tiklessDump = tiklessDump; + seqNspCtx.nspFileCount = nspFileCount; + seqNspCtx.ncaCount = (titleNcaCount - 1); // Exclude the CNMT NCA from the hash list + + // Store the modified Program NCA header + // The NPDM signature from the NCA header is generated using cryptographically secure random numbers, so we must store the modified header during the first sequential dump session + if (ncaProgramMod.block_mod_cnt) memcpy(&(seqNspCtx.programNcaHeaderMod), xml_content_info[programNcaIndex].encrypted_header_mod, NCA_FULL_HEADER_LENGTH); + + // Allocate memory for the NCA hashes + seqDumpNcaHashes = calloc(1, (titleNcaCount - 1) * SHA256_HASH_SIZE); + if (seqDumpNcaHashes) + { + // Create sequential reference file and keep the handle to it opened + seqDumpFile = fopen(seqDumpFilename, "wb+"); + if (seqDumpFile) + { + // Write the sequential dump struct + write_res = fwrite(&seqNspCtx, 1, sizeof(sequentialNspCtx), seqDumpFile); + if (write_res == sizeof(sequentialNspCtx)) + { + // Write the NCA hashes block + write_res = fwrite(seqDumpNcaHashes, 1, seqDumpFileSize - sizeof(sequentialNspCtx), seqDumpFile); + rewind(seqDumpFile); + + if (write_res == (seqDumpFileSize - sizeof(sequentialNspCtx))) + { + // Update free space + freeSpace -= seqDumpFileSize; + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to write %lu bytes chunk to the sequential dump reference file! (wrote %lu bytes)", titleNcaCount * SHA256_HASH_SIZE, write_res); + proceed = false; + seqDumpFileRemove = true; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to write %lu bytes chunk to the sequential dump reference file! (wrote %lu bytes)", sizeof(sequentialNspCtx), write_res); + proceed = false; + seqDumpFileRemove = true; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to create sequential dump reference file! (\"%s\")", seqDumpFilename); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for NCA hashes from the sequential dump reference file!"); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Process canceled."); + proceed = false; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: not enough free space available in the SD card."); + proceed = false; + } + } + } + } else { + if (progressCtx.totalSize > freeSpace) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: not enough free space available in the SD card."); + proceed = false; } } - // Since we may actually be dealing with an existing directory with the archive bit set or unset, let's try both - // Better safe than sorry - unlink(dumpPath); - fsdevDeleteDirectoryRecursively(dumpPath); + if (!proceed) goto out; - if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32) + if (seqDumpMode) { - mkdir(dumpPath, 0744); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp.%02u", NSP_DUMP_PATH, dumpName, splitIndex); + } else { + // Temporary, we'll use this to check if the dump already exists (it should have the archive bit set if so) + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp", NSP_DUMP_PATH, dumpName); - sprintf(tmp_idx, "/%02u", splitIndex); - strcat(dumpPath, tmp_idx); + // Check if the dump already exists + if (!batch && checkIfFileExists(dumpPath)) + { + // Ask the user if they want to proceed anyway + int cur_breaks = breaks; + breaks++; + + proceed = yesNoPrompt("You have already dumped this content. Do you wish to proceed anyway?"); + if (!proceed) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Process canceled."); + removeFile = false; + goto out; + } else { + // Remove the prompt from the screen + breaks = cur_breaks; + uiFill(0, 8 + STRING_Y_POS(breaks), FB_WIDTH, FB_HEIGHT - (8 + STRING_Y_POS(breaks)), BG_COLOR_RGB); + } + } + + // Since we may actually be dealing with an existing directory with the archive bit set or unset, let's try both + // Better safe than sorry + unlink(dumpPath); + fsdevDeleteDirectoryRecursively(dumpPath); + + if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32) + { + mkdir(dumpPath, 0744); + + sprintf(tmp_idx, "/%02u", splitIndex); + strcat(dumpPath, tmp_idx); + } } outFile = fopen(dumpPath, "wb"); if (!outFile) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open output file \"%s\"!", dumpPath); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to open output file \"%s\"!", dumpPath); goto out; } if (!batch) { breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dump procedure started. Hold %s to cancel.", NINTENDO_FONT_B); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Dump procedure started. Hold %s to cancel.", NINTENDO_FONT_B); uiRefreshDisplay(); breaks += 2; } if (programAppletType != AppletType_Application && programAppletType != AppletType_SystemApplication) { - uiDrawString("Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem."); breaks += 2; } - // Write placeholder zeroes - write_res = fwrite(dumpBuf, 1, full_nsp_header_size, outFile); - if (write_res != full_nsp_header_size) + if (seqDumpMode) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes placeholder data to file offset 0x%016lX! (wrote %lu bytes)", full_nsp_header_size, (u64)0, write_res); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - goto out; - } - - progressCtx.curOffset = full_nsp_header_size; - - // Calculate DUMP_BUFFER_SIZE block numbers for the modified Program NCA data blocks - if (ncaProgramMod.block_mod_cnt > 0) - { - hash_table_dump_buffer_start = ((ncaProgramMod.hash_table_offset / DUMP_BUFFER_SIZE) * DUMP_BUFFER_SIZE); - hash_table_dump_buffer_end = (((ncaProgramMod.hash_table_offset + ncaProgramMod.hash_table_size) / DUMP_BUFFER_SIZE) * DUMP_BUFFER_SIZE); - - block0_dump_buffer_start = ((ncaProgramMod.block_offset[0] / DUMP_BUFFER_SIZE) * DUMP_BUFFER_SIZE); - block0_dump_buffer_end = (((ncaProgramMod.block_offset[0] + ncaProgramMod.block_size[0]) / DUMP_BUFFER_SIZE) * DUMP_BUFFER_SIZE); - - if (ncaProgramMod.block_mod_cnt == 2) + // Skip the PFS0 header in the first part file + // It will be saved to an additional ".nsp.hdr" file + if (!seqNspCtx.partNumber) progressCtx.curOffset = seqDumpSessionOffset = full_nsp_header_size; + } else { + // Write placeholder zeroes + write_res = fwrite(dumpBuf, 1, full_nsp_header_size, outFile); + if (write_res != full_nsp_header_size) { - block1_dump_buffer_start = ((ncaProgramMod.block_offset[1] / DUMP_BUFFER_SIZE) * DUMP_BUFFER_SIZE); - block1_dump_buffer_end = (((ncaProgramMod.block_offset[1] + ncaProgramMod.block_size[1]) / DUMP_BUFFER_SIZE) * DUMP_BUFFER_SIZE); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes placeholder data to file offset 0x%016lX! (wrote %lu bytes)", full_nsp_header_size, (u64)0, write_res); + goto out; } + + // Advance our current offset + progressCtx.curOffset = full_nsp_header_size; } progressCtx.line_offset = (breaks + 4); @@ -1382,105 +1952,115 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd dumping = true; + u32 startFileIndex = (seqDumpMode ? seqNspCtx.fileIndex : 0); + u64 startFileOffset; + // Dump all NCAs excluding the CNMT NCA - for(i = 0; i < (titleNcaCount - 1); i++) + for(i = startFileIndex; i < (titleNcaCount - 1); i++, startFileIndex++) { n = DUMP_BUFFER_SIZE; - memcpy(ncaId.c, xml_content_info[i].nca_id, 16); + startFileOffset = ((seqDumpMode && i == seqNspCtx.fileIndex) ? seqNspCtx.fileOffset : 0); - sha256ContextCreate(&nca_hash_ctx); + memcpy(ncaId.c, xml_content_info[i].nca_id, SHA256_HASH_SIZE / 2); - for(nca_offset = 0; nca_offset < xml_content_info[i].size; nca_offset += n, progressCtx.curOffset += n) + if (!seqDumpMode || (seqDumpMode && i != seqNspCtx.fileIndex)) sha256ContextCreate(&nca_hash_ctx); + + for(nca_offset = startFileOffset; nca_offset < xml_content_info[i].size; nca_offset += n, progressCtx.curOffset += n, seqDumpSessionOffset += n) { - uiFill(0, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 4, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + if (seqDumpMode && seqDumpFinish) break; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", strrchr(dumpPath, '/' ) + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiFill(0, ((progressCtx.line_offset - 4) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 4, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dumping NCA content \"%s\"...", xml_content_info[i].nca_id_str); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 4), FONT_COLOR_RGB, "Output file: \"%s\".", strrchr(dumpPath, '/' ) + 1); - if (DUMP_BUFFER_SIZE > (xml_content_info[i].size - nca_offset)) n = (xml_content_info[i].size - nca_offset); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "Dumping NCA content \"%s\"...", xml_content_info[i].nca_id_str); + + if (n > (xml_content_info[i].size - nca_offset)) n = (xml_content_info[i].size - nca_offset); + + // Check if the next read chunk will exceed the size of the current part file + if (seqDumpMode && (seqDumpSessionOffset + n) >= (((splitIndex - seqNspCtx.partNumber) + 1) * part_size)) + { + u64 new_file_chunk_size = ((seqDumpSessionOffset + n) - (((splitIndex - seqNspCtx.partNumber) + 1) * part_size)); + u64 old_file_chunk_size = (n - new_file_chunk_size); + + u64 remainderDumpSize = (progressCtx.totalSize - (progressCtx.curOffset + old_file_chunk_size)); + u64 remainderFreeSize = (freeSpace - (seqDumpSessionOffset + old_file_chunk_size)); + + // Check if we have enough space for the next part + // If so, set the chunk size to old_file_chunk_size + if ((remainderDumpSize <= part_size && remainderDumpSize > remainderFreeSize) || (remainderDumpSize > part_size && part_size > remainderFreeSize)) + { + n = old_file_chunk_size; + seqDumpFinish = true; + } + } if (R_FAILED(result = ncmContentStorageReadContentIdFile(&ncmStorage, &ncaId, nca_offset, dumpBuf, n))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read %lu bytes chunk at offset 0x%016lX from NCA \"%s\"! (0x%08X)", n, nca_offset, xml_content_info[i].nca_id_str, result); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to read %lu bytes chunk at offset 0x%016lX from NCA \"%s\"! (0x%08X)", n, nca_offset, xml_content_info[i].nca_id_str, result); proceed = false; break; } // Replace NCA header with our modified one - if (nca_offset == 0) memcpy(dumpBuf, xml_content_info[i].encrypted_header_mod, NCA_FULL_HEADER_LENGTH); + if (nca_offset < NCA_FULL_HEADER_LENGTH) + { + u64 write_size = (NCA_FULL_HEADER_LENGTH - nca_offset); + if (write_size > n) write_size = n; + + memcpy(dumpBuf, xml_content_info[i].encrypted_header_mod + nca_offset, write_size); + } // Replace modified Program NCA data blocks if (ncaProgramMod.block_mod_cnt > 0 && xml_content_info[i].type == NcmContentType_Program) { - u64 program_nca_prev_write; - u64 program_nca_next_write; + u64 internal_block_offset; + u64 internal_block_chunk_size; - if (nca_offset == hash_table_dump_buffer_start || nca_offset == hash_table_dump_buffer_end) + u64 buffer_offset; + u64 buffer_chunk_size; + + if ((nca_offset + n) > ncaProgramMod.hash_table_offset && (ncaProgramMod.hash_table_offset + ncaProgramMod.hash_table_size) > nca_offset) { - if (hash_table_dump_buffer_start == hash_table_dump_buffer_end) - { - memcpy(dumpBuf + (ncaProgramMod.hash_table_offset - hash_table_dump_buffer_start), ncaProgramMod.hash_table, ncaProgramMod.hash_table_size); - } else { - program_nca_prev_write = (DUMP_BUFFER_SIZE - (ncaProgramMod.hash_table_offset - hash_table_dump_buffer_start)); - program_nca_next_write = (ncaProgramMod.hash_table_size - program_nca_prev_write); - - if (nca_offset == hash_table_dump_buffer_start) - { - memcpy(dumpBuf + (ncaProgramMod.hash_table_offset - hash_table_dump_buffer_start), ncaProgramMod.hash_table, program_nca_prev_write); - } else { - memcpy(dumpBuf, ncaProgramMod.hash_table + program_nca_prev_write, program_nca_next_write); - } - } + internal_block_offset = (nca_offset > ncaProgramMod.hash_table_offset ? (nca_offset - ncaProgramMod.hash_table_offset) : 0); + internal_block_chunk_size = (ncaProgramMod.hash_table_size - internal_block_offset); + + buffer_offset = (nca_offset > ncaProgramMod.hash_table_offset ? 0 : (ncaProgramMod.hash_table_offset - nca_offset)); + buffer_chunk_size = ((n - buffer_offset) > internal_block_chunk_size ? internal_block_chunk_size : (n - buffer_offset)); + + memcpy(dumpBuf + buffer_offset, ncaProgramMod.hash_table + internal_block_offset, buffer_chunk_size); } - if (nca_offset == block0_dump_buffer_start || nca_offset == block0_dump_buffer_end) + if ((nca_offset + n) > ncaProgramMod.block_offset[0] && (ncaProgramMod.block_offset[0] + ncaProgramMod.block_size[0]) > nca_offset) { - if (block0_dump_buffer_start == block0_dump_buffer_end) - { - memcpy(dumpBuf + (ncaProgramMod.block_offset[0] - block0_dump_buffer_start), ncaProgramMod.block_data[0], ncaProgramMod.block_size[0]); - } else { - program_nca_prev_write = (DUMP_BUFFER_SIZE - (ncaProgramMod.block_offset[0] - block0_dump_buffer_start)); - program_nca_next_write = (ncaProgramMod.block_size[0] - program_nca_prev_write); - - if (nca_offset == block0_dump_buffer_start) - { - memcpy(dumpBuf + (ncaProgramMod.block_offset[0] - block0_dump_buffer_start), ncaProgramMod.block_data[0], program_nca_prev_write); - } else { - memcpy(dumpBuf, ncaProgramMod.block_data[0] + program_nca_prev_write, program_nca_next_write); - } - } + internal_block_offset = (nca_offset > ncaProgramMod.block_offset[0] ? (nca_offset - ncaProgramMod.block_offset[0]) : 0); + internal_block_chunk_size = (ncaProgramMod.block_size[0] - internal_block_offset); + + buffer_offset = (nca_offset > ncaProgramMod.block_offset[0] ? 0 : (ncaProgramMod.block_offset[0] - nca_offset)); + buffer_chunk_size = ((n - buffer_offset) > internal_block_chunk_size ? internal_block_chunk_size : (n - buffer_offset)); + + memcpy(dumpBuf + buffer_offset, ncaProgramMod.block_data[0] + internal_block_offset, buffer_chunk_size); } - if (ncaProgramMod.block_mod_cnt == 2 && (nca_offset == block1_dump_buffer_start || nca_offset == block1_dump_buffer_end)) + if (ncaProgramMod.block_mod_cnt == 2 && (nca_offset + n) > ncaProgramMod.block_offset[1] && (ncaProgramMod.block_offset[1] + ncaProgramMod.block_size[1]) > nca_offset) { - if (block1_dump_buffer_start == block1_dump_buffer_end) - { - memcpy(dumpBuf + (ncaProgramMod.block_offset[1] - block1_dump_buffer_start), ncaProgramMod.block_data[1], ncaProgramMod.block_size[1]); - } else { - program_nca_prev_write = (DUMP_BUFFER_SIZE - (ncaProgramMod.block_offset[1] - block1_dump_buffer_start)); - program_nca_next_write = (ncaProgramMod.block_size[1] - program_nca_prev_write); - - if (nca_offset == block1_dump_buffer_start) - { - memcpy(dumpBuf + (ncaProgramMod.block_offset[1] - block1_dump_buffer_start), ncaProgramMod.block_data[1], program_nca_prev_write); - } else { - memcpy(dumpBuf, ncaProgramMod.block_data[1] + program_nca_prev_write, program_nca_next_write); - } - } + internal_block_offset = (nca_offset > ncaProgramMod.block_offset[1] ? (nca_offset - ncaProgramMod.block_offset[1]) : 0); + internal_block_chunk_size = (ncaProgramMod.block_size[1] - internal_block_offset); + + buffer_offset = (nca_offset > ncaProgramMod.block_offset[1] ? 0 : (ncaProgramMod.block_offset[1] - nca_offset)); + buffer_chunk_size = ((n - buffer_offset) > internal_block_chunk_size ? internal_block_chunk_size : (n - buffer_offset)); + + memcpy(dumpBuf + buffer_offset, ncaProgramMod.block_data[1] + internal_block_offset, buffer_chunk_size); } } // Update SHA-256 calculation sha256ContextUpdate(&nca_hash_ctx, dumpBuf, n); - if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32 && (progressCtx.curOffset + n) >= ((splitIndex + 1) * SPLIT_FILE_NSP_PART_SIZE)) + if ((seqDumpMode || (!seqDumpMode && progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32)) && (progressCtx.curOffset + n) >= ((splitIndex + 1) * part_size)) { - u64 new_file_chunk_size = ((progressCtx.curOffset + n) - ((splitIndex + 1) * SPLIT_FILE_NSP_PART_SIZE)); + u64 new_file_chunk_size = ((progressCtx.curOffset + n) - ((splitIndex + 1) * part_size)); u64 old_file_chunk_size = (n - new_file_chunk_size); if (old_file_chunk_size > 0) @@ -1488,8 +2068,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd write_res = fwrite(dumpBuf, 1, old_file_chunk_size, outFile); if (write_res != old_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, progressCtx.curOffset, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, progressCtx.curOffset, splitIndex, write_res); proceed = false; break; } @@ -1498,16 +2077,15 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd fclose(outFile); outFile = NULL; - if (new_file_chunk_size > 0 || (progressCtx.curOffset + n) < progressCtx.totalSize) + if (((seqDumpMode && !seqDumpFinish) || !seqDumpMode) && (new_file_chunk_size > 0 || (progressCtx.curOffset + n) < progressCtx.totalSize)) { splitIndex++; - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp/%02u", NSP_DUMP_PATH, dumpName, splitIndex); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp%c%02u", NSP_DUMP_PATH, dumpName, (seqDumpMode ? '.' : '/'), splitIndex); outFile = fopen(dumpPath, "wb"); if (!outFile) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open output file for part #%u!", splitIndex); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to open output file for part #%u!", splitIndex); proceed = false; break; } @@ -1517,8 +2095,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd write_res = fwrite(dumpBuf + old_file_chunk_size, 1, new_file_chunk_size, outFile); if (write_res != new_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, progressCtx.curOffset + old_file_chunk_size, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, progressCtx.curOffset + old_file_chunk_size, splitIndex, write_res); proceed = false; break; } @@ -1528,12 +2105,11 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd write_res = fwrite(dumpBuf, 1, n, outFile); if (write_res != n) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, progressCtx.curOffset, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, progressCtx.curOffset, write_res); - if ((progressCtx.curOffset + n) > FAT32_FILESIZE_LIMIT) + if (!seqDumpMode && (progressCtx.curOffset + n) > FAT32_FILESIZE_LIMIT) { - uiDrawString("You're probably using a FAT32 partition. Make sure to enable the \"Split output dump\" option.", 8, ((progressCtx.line_offset + 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 4), FONT_COLOR_RGB, "You're probably using a FAT32 partition. Make sure to enable the \"Split output dump\" option."); fat32_error = true; } @@ -1542,14 +2118,16 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd } } + if (seqDumpMode) progressCtx.seqDumpCurOffset = seqDumpSessionOffset; printProgressBar(&progressCtx, true, n); if ((progressCtx.curOffset + n) < progressCtx.totalSize) { if (cancelProcessCheck(&progressCtx)) { - uiDrawString("Process canceled.", 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Process canceled."); proceed = false; + if (seqDumpMode) seqDumpFileRemove = true; break; } } @@ -1559,37 +2137,43 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd { setProgressBarError(&progressCtx); break; + } else { + if (seqDumpMode && seqDumpFinish) + { + success = true; + break; + } } // Support empty files if (!xml_content_info[i].size) { - uiFill(0, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 4, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx.line_offset - 4) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 4, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), strrchr(dumpPath, '/' ) + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 4), FONT_COLOR_RGB, strrchr(dumpPath, '/' ) + 1); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dumping NCA content \"%s\"...", xml_content_info[i].nca_id_str); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "Dumping NCA content \"%s\"...", xml_content_info[i].nca_id_str); printProgressBar(&progressCtx, false, 0); } // Update content info sha256ContextGetHash(&nca_hash_ctx, xml_content_info[i].hash); - convertDataToHexString(xml_content_info[i].hash, 32, xml_content_info[i].hash_str, 65); - memcpy(xml_content_info[i].nca_id, xml_content_info[i].hash, 16); - convertDataToHexString(xml_content_info[i].nca_id, 16, xml_content_info[i].nca_id_str, 33); + convertDataToHexString(xml_content_info[i].hash, SHA256_HASH_SIZE, xml_content_info[i].hash_str, (SHA256_HASH_SIZE * 2) + 1); + memcpy(xml_content_info[i].nca_id, xml_content_info[i].hash, SHA256_HASH_SIZE / 2); + convertDataToHexString(xml_content_info[i].nca_id, SHA256_HASH_SIZE / 2, xml_content_info[i].nca_id_str, SHA256_HASH_SIZE + 1); + + // If we're doing a sequential dump, copy the hash from the NCA we just finished dumping + if (seqDumpMode) memcpy(seqDumpNcaHashes + (i * SHA256_HASH_SIZE), xml_content_info[i].hash, SHA256_HASH_SIZE); } - if (!proceed) goto out; + if (!proceed || success) goto out; - uiFill(0, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 4, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx.line_offset - 4) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 4, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", strrchr(dumpPath, '/' ) + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 4), FONT_COLOR_RGB, "Output file: \"%s\".", strrchr(dumpPath, '/' ) + 1); - uiDrawString("Writing PFS0 header...", 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "Writing PFS0 header..."); uiRefreshDisplay(); @@ -1640,8 +2224,9 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd if (nacpIcons && nacpIconCnt && ((!programInfoXml && i <= (titleNcaCount + nacpIconCnt)) || (programInfoXml && i <= (titleNcaCount + 1 + nacpIconCnt)))) { // NACP icon entry + // Replace the NCA ID from its filename, since it could have changed u32 icon_idx = (!programInfoXml ? (i - (titleNcaCount + 1)) : (i - (titleNcaCount + 2))); - sprintf(ncaFileName, nacpIcons[icon_idx].filename); + sprintf(ncaFileName, "%s%s", xml_content_info[nacpNcaIndex].nca_id_str, strchr(nacpIcons[icon_idx].filename, '.')); cur_file_size = nacpIcons[icon_idx].icon_size; } else if (nacpXml && ((!programInfoXml && i == (titleNcaCount + nacpIconCnt + 1)) || (programInfoXml && i == (titleNcaCount + 1 + nacpIconCnt + 1)))) @@ -1679,63 +2264,106 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd memcpy(dumpBuf + sizeof(pfs0_header), nspPfs0EntryTable, (u64)nspFileCount * sizeof(pfs0_entry_table)); memcpy(dumpBuf + sizeof(pfs0_header) + ((u64)nspFileCount * sizeof(pfs0_entry_table)), nspPfs0StrTable, nspPfs0Header.str_table_size); - if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32) + if (seqDumpMode) { - if (outFile) + // Check if the PFS0 header file already exists + if (!checkIfFileExists(pfs0HeaderFilename)) { - fclose(outFile); - outFile = NULL; - } - - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp/%02u", NSP_DUMP_PATH, dumpName, 0); - - outFile = fopen(dumpPath, "rb+"); - if (!outFile) - { - setProgressBarError(&progressCtx); - uiDrawString("Failed to re-open output file for part #0!", 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - goto out; + // Check if we have enough space for the header file + u64 curFreeSpace = (freeSpace - seqDumpSessionOffset); + if (!seqNspCtx.partNumber) curFreeSpace += full_nsp_header_size; // The PFS0 header size is skipped during the first sequential dump session + + if (curFreeSpace >= full_nsp_header_size) + { + pfs0HeaderFile = fopen(pfs0HeaderFilename, "wb"); + if (pfs0HeaderFile) + { + write_res = fwrite(dumpBuf, 1, full_nsp_header_size, pfs0HeaderFile); + fclose(pfs0HeaderFile); + + if (write_res == full_nsp_header_size) + { + // Update free space + freeSpace -= full_nsp_header_size; + } else { + setProgressBarError(&progressCtx); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Error: failed to write %lu bytes PFS0 header file! (wrote %lu bytes)", full_nsp_header_size, write_res); + unlink(pfs0HeaderFilename); + goto out; + } + } else { + setProgressBarError(&progressCtx); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Error: failed to create PFS0 header file!"); + goto out; + } + } else { + // Finish current sequential dump session + seqDumpFinish = true; + success = true; + goto out; + } } } else { - rewind(outFile); - } - - write_res = fwrite(dumpBuf, 1, full_nsp_header_size, outFile); - if (write_res != full_nsp_header_size) - { - setProgressBarError(&progressCtx); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes PFS0 header to file offset 0x%016lX! (wrote %lu bytes)", full_nsp_header_size, (u64)0, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - goto out; - } - - if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32) - { - if (outFile) + if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32) { - fclose(outFile); - outFile = NULL; + if (outFile) + { + fclose(outFile); + outFile = NULL; + } + + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp/%02u", NSP_DUMP_PATH, dumpName, 0); + + outFile = fopen(dumpPath, "rb+"); + if (!outFile) + { + setProgressBarError(&progressCtx); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to re-open output file for part #0!"); + goto out; + } + } else { + rewind(outFile); } - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp/%02u", NSP_DUMP_PATH, dumpName, splitIndex); - - outFile = fopen(dumpPath, "rb+"); - if (!outFile) + write_res = fwrite(dumpBuf, 1, full_nsp_header_size, outFile); + if (write_res != full_nsp_header_size) { setProgressBarError(&progressCtx); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to re-open output file for part #%u!", splitIndex); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes PFS0 header to file offset 0x%016lX! (wrote %lu bytes)", full_nsp_header_size, (u64)0, write_res); goto out; } + + if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32) + { + if (outFile) + { + fclose(outFile); + outFile = NULL; + } + + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp/%02u", NSP_DUMP_PATH, dumpName, splitIndex); + + outFile = fopen(dumpPath, "rb+"); + if (!outFile) + { + setProgressBarError(&progressCtx); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to re-open output file for part #%u!", splitIndex); + goto out; + } + } + + fseek(outFile, 0, SEEK_END); } - fseek(outFile, 0, SEEK_END); + startFileIndex = ((seqDumpMode && seqNspCtx.fileIndex > (titleNcaCount - 1)) ? seqNspCtx.fileIndex : (titleNcaCount - 1)); // Now let's write the rest of the data, including our modified CNMT NCA - for(i = (titleNcaCount - 1); i < nspFileCount; i++) + for(i = startFileIndex; i < nspFileCount; i++, startFileIndex++) { n = DUMP_BUFFER_SIZE; + startFileOffset = ((seqDumpMode && i == seqNspCtx.fileIndex) ? seqNspCtx.fileOffset : 0); + char ncaFileName[100] = {'\0'}; u64 cur_file_size = 0; @@ -1782,19 +2410,37 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd } } - for(nca_offset = 0; nca_offset < cur_file_size; nca_offset += n, progressCtx.curOffset += n) + for(nca_offset = startFileOffset; nca_offset < cur_file_size; nca_offset += n, progressCtx.curOffset += n, seqDumpSessionOffset += n) { - uiFill(0, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 4, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + if (seqDumpMode && seqDumpFinish) break; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", strrchr(dumpPath, '/' ) + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiFill(0, ((progressCtx.line_offset - 4) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 4, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Writing \"%s\"...", ncaFileName); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 4), FONT_COLOR_RGB, "Output file: \"%s\".", strrchr(dumpPath, '/' ) + 1); + + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "Writing \"%s\"...", ncaFileName); uiRefreshDisplay(); - if (DUMP_BUFFER_SIZE > (cur_file_size - nca_offset)) n = (cur_file_size - nca_offset); + if (n > (cur_file_size - nca_offset)) n = (cur_file_size - nca_offset); + + // Check if the next read chunk will exceed the size of the current part file + if (seqDumpMode && (seqDumpSessionOffset + n) >= (((splitIndex - seqNspCtx.partNumber) + 1) * part_size)) + { + u64 new_file_chunk_size = ((seqDumpSessionOffset + n) - (((splitIndex - seqNspCtx.partNumber) + 1) * part_size)); + u64 old_file_chunk_size = (n - new_file_chunk_size); + + u64 remainderDumpSize = (progressCtx.totalSize - (progressCtx.curOffset + old_file_chunk_size)); + u64 remainderFreeSize = (freeSpace - (seqDumpSessionOffset + old_file_chunk_size)); + + // Check if we have enough space for the next part + // If so, set the chunk size to old_file_chunk_size + if ((remainderDumpSize <= part_size && remainderDumpSize > remainderFreeSize) || (remainderDumpSize > part_size && part_size > remainderFreeSize)) + { + n = old_file_chunk_size; + seqDumpFinish = true; + } + } // Retrieve data from its respective source if (i == (titleNcaCount - 1)) @@ -1838,9 +2484,9 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd } } - if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32 && (progressCtx.curOffset + n) >= ((splitIndex + 1) * SPLIT_FILE_NSP_PART_SIZE)) + if ((seqDumpMode || (!seqDumpMode && progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32)) && (progressCtx.curOffset + n) >= ((splitIndex + 1) * part_size)) { - u64 new_file_chunk_size = ((progressCtx.curOffset + n) - ((splitIndex + 1) * SPLIT_FILE_NSP_PART_SIZE)); + u64 new_file_chunk_size = ((progressCtx.curOffset + n) - ((splitIndex + 1) * part_size)); u64 old_file_chunk_size = (n - new_file_chunk_size); if (old_file_chunk_size > 0) @@ -1848,8 +2494,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd write_res = fwrite(dumpBuf, 1, old_file_chunk_size, outFile); if (write_res != old_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, progressCtx.curOffset, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, progressCtx.curOffset, splitIndex, write_res); proceed = false; break; } @@ -1858,16 +2503,15 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd fclose(outFile); outFile = NULL; - if (new_file_chunk_size > 0 || (progressCtx.curOffset + n) < progressCtx.totalSize) + if (((seqDumpMode && !seqDumpFinish) || !seqDumpMode) && (new_file_chunk_size > 0 || (progressCtx.curOffset + n) < progressCtx.totalSize)) { splitIndex++; - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp/%02u", NSP_DUMP_PATH, dumpName, splitIndex); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp%c%02u", NSP_DUMP_PATH, dumpName, (seqDumpMode ? '.' : '/'), splitIndex); outFile = fopen(dumpPath, "wb"); if (!outFile) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open output file for part #%u!", splitIndex); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to open output file for part #%u!", splitIndex); proceed = false; break; } @@ -1877,8 +2521,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd write_res = fwrite(dumpBuf + old_file_chunk_size, 1, new_file_chunk_size, outFile); if (write_res != new_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, progressCtx.curOffset + old_file_chunk_size, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, progressCtx.curOffset + old_file_chunk_size, splitIndex, write_res); proceed = false; break; } @@ -1888,12 +2531,11 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd write_res = fwrite(dumpBuf, 1, n, outFile); if (write_res != n) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, progressCtx.curOffset, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, progressCtx.curOffset, write_res); - if ((progressCtx.curOffset + n) > FAT32_FILESIZE_LIMIT) + if (!seqDumpMode && (progressCtx.curOffset + n) > FAT32_FILESIZE_LIMIT) { - uiDrawString("You're probably using a FAT32 partition. Make sure to enable the \"Split output dump\" option.", 8, ((progressCtx.line_offset + 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 4), FONT_COLOR_RGB, "You're probably using a FAT32 partition. Make sure to enable the \"Split output dump\" option."); fat32_error = true; } @@ -1902,14 +2544,16 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd } } + if (seqDumpMode) progressCtx.seqDumpCurOffset = seqDumpSessionOffset; printProgressBar(&progressCtx, true, n); if ((progressCtx.curOffset + n) < progressCtx.totalSize) { if (cancelProcessCheck(&progressCtx)) { - uiDrawString("Process canceled.", 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Process canceled."); proceed = false; + if (seqDumpMode) seqDumpFileRemove = true; break; } } @@ -1919,18 +2563,18 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd { setProgressBarError(&progressCtx); break; + } else { + if (seqDumpMode && seqDumpFinish) break; } // Support empty files if (!cur_file_size) { - uiFill(0, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 4, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx.line_offset - 4) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 4, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), strrchr(dumpPath, '/' ) + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 4), FONT_COLOR_RGB, strrchr(dumpPath, '/' ) + 1); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Writing \"%s\"...", ncaFileName); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "Writing \"%s\"...", ncaFileName); printProgressBar(&progressCtx, false, 0); } @@ -1942,19 +2586,20 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd breaks = (progressCtx.line_offset + 2); - if (progressCtx.curOffset < progressCtx.totalSize) + if (progressCtx.curOffset >= progressCtx.totalSize || (seqDumpMode && seqDumpFinish)) success = true; + + if (!success) { setProgressBarError(&progressCtx); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Unexpected underdump error! Wrote %lu bytes, expected %lu bytes.", progressCtx.curOffset, progressCtx.totalSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Unexpected underdump error! Wrote %lu bytes, expected %lu bytes.", progressCtx.curOffset, progressCtx.totalSize); + if (seqDumpMode) seqDumpFileRemove = true; goto out; } - success = true; - - // Finalize dump - if (!batch) + // Calculate CRC32 checksum + if (!batch && calcCrc) { + // Finalize dump timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx.now)); progressCtx.now -= progressCtx.start; @@ -1963,26 +2608,21 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd printProgressBar(&progressCtx, false, 0); - formatETAString(progressCtx.now, progressCtx.etaInfo, sizeof(progressCtx.etaInfo) / sizeof(progressCtx.etaInfo[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Process successfully completed after %s!", progressCtx.etaInfo); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + formatETAString(progressCtx.now, progressCtx.etaInfo, MAX_ELEMENTS(progressCtx.etaInfo)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Process successfully completed after %s!", progressCtx.etaInfo); uiRefreshDisplay(); - } - - if (!batch && calcCrc) - { + breaks += 2; - uiDrawString("CRC32 checksum calculation will begin in 5 seconds...", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "CRC32 checksum calculation will begin in %u seconds...", DUMP_NSP_CRC_WAIT); uiRefreshDisplay(); - delay(5); + delay(DUMP_NSP_CRC_WAIT); breaks = initial_breaks; - uiFill(0, (breaks * (font_height + (font_height / 4))) + 8, FB_WIDTH, FB_HEIGHT - (breaks * (font_height + (font_height / 4))), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, (breaks * LINE_HEIGHT) + 8, FB_WIDTH, FB_HEIGHT - (breaks * LINE_HEIGHT), BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Calculating CRC32 checksum. Hold %s to cancel.", NINTENDO_FONT_B); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Calculating CRC32 checksum. Hold %s to cancel.", NINTENDO_FONT_B); breaks += 2; if (outFile) @@ -1991,7 +2631,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd outFile = NULL; } - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp", NSP_DUMP_PATH, dumpName); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp", NSP_DUMP_PATH, dumpName); if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32) { @@ -2014,16 +2654,15 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd for(progressCtx.curOffset = 0; progressCtx.curOffset < progressCtx.totalSize; progressCtx.curOffset += n) { - uiFill(0, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx.line_offset - 2) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 2, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "File: \"%s\".", strrchr(dumpPath, '/' ) + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "File: \"%s\".", strrchr(dumpPath, '/' ) + 1); - if (DUMP_BUFFER_SIZE > (progressCtx.totalSize - progressCtx.curOffset)) n = (progressCtx.totalSize - progressCtx.curOffset); + if (n > (progressCtx.totalSize - progressCtx.curOffset)) n = (progressCtx.totalSize - progressCtx.curOffset); - if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32 && (progressCtx.curOffset + n) >= ((splitIndex + 1) * SPLIT_FILE_NSP_PART_SIZE)) + if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32 && (progressCtx.curOffset + n) >= ((splitIndex + 1) * part_size)) { - u64 new_file_chunk_size = ((progressCtx.curOffset + n) - ((splitIndex + 1) * SPLIT_FILE_NSP_PART_SIZE)); + u64 new_file_chunk_size = ((progressCtx.curOffset + n) - ((splitIndex + 1) * part_size)); u64 old_file_chunk_size = (n - new_file_chunk_size); if (old_file_chunk_size > 0) @@ -2031,8 +2670,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd read_res = fread(dumpBuf, 1, old_file_chunk_size, outFile); if (read_res != old_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read %lu bytes chunk from offset 0x%016lX from part #%02u! (read %lu bytes)", old_file_chunk_size, progressCtx.curOffset, splitIndex, read_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to read %lu bytes chunk from offset 0x%016lX from part #%02u! (read %lu bytes)", old_file_chunk_size, progressCtx.curOffset, splitIndex, read_res); proceed = false; break; } @@ -2044,13 +2682,12 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd if (new_file_chunk_size > 0 || (progressCtx.curOffset + n) < progressCtx.totalSize) { splitIndex++; - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp/%02u", NSP_DUMP_PATH, dumpName, splitIndex); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp/%02u", NSP_DUMP_PATH, dumpName, splitIndex); outFile = fopen(dumpPath, "rb"); if (!outFile) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to re-open output file for part #%u!", splitIndex); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to re-open output file for part #%u!", splitIndex); proceed = false; break; } @@ -2060,8 +2697,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd read_res = fread(dumpBuf + old_file_chunk_size, 1, new_file_chunk_size, outFile); if (read_res != new_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read %lu bytes chunk from offset 0x%016lX from part #%02u! (read %lu bytes)", new_file_chunk_size, progressCtx.curOffset + old_file_chunk_size, splitIndex, read_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to read %lu bytes chunk from offset 0x%016lX from part #%02u! (read %lu bytes)", new_file_chunk_size, progressCtx.curOffset + old_file_chunk_size, splitIndex, read_res); proceed = false; break; } @@ -2071,8 +2707,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd read_res = fread(dumpBuf, 1, n, outFile); if (read_res != n) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read %lu bytes chunk from offset 0x%016lX! (read %lu bytes)", n, progressCtx.curOffset, read_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to read %lu bytes chunk from offset 0x%016lX! (read %lu bytes)", n, progressCtx.curOffset, read_res); proceed = false; break; } @@ -2087,7 +2722,7 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd { if (cancelProcessCheck(&progressCtx)) { - uiDrawString("Process canceled.", 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Process canceled."); proceed = false; break; } @@ -2101,53 +2736,146 @@ bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInd timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx.now)); progressCtx.now -= progressCtx.start; - formatETAString(progressCtx.now, progressCtx.etaInfo, sizeof(progressCtx.etaInfo) / sizeof(progressCtx.etaInfo[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Process successfully completed after %s!", progressCtx.etaInfo); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + formatETAString(progressCtx.now, progressCtx.etaInfo, MAX_ELEMENTS(progressCtx.etaInfo)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Process successfully completed after %s!", progressCtx.etaInfo); breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "NSP dump CRC32 checksum: %08X", crc); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "NSP dump CRC32 checksum: %08X", crc); + breaks += 2; } else { setProgressBarError(&progressCtx); } } else { - uiDrawString("Failed to re-open output file in read mode!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to re-open output file in read mode!"); + breaks += 2; } } // Set archive bit (only for FAT32) - if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32) + if (!seqDumpMode && progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32) { - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp", NSP_DUMP_PATH, dumpName); - if (R_FAILED(result = fsdevSetArchiveBit(dumpPath))) + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp", NSP_DUMP_PATH, dumpName); + if (R_FAILED(result = fsdevSetArchiveBit(dumpPath))) { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Warning: failed to set archive bit on output directory! (0x%08X)", result); breaks += 2; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Warning: failed to set archive bit on output directory! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); } } out: if (outFile) fclose(outFile); - if (!success) + if (success) { + if (seqDumpMode) + { + if (seqDumpFinish) + { + // Update line count + breaks = (progressCtx.line_offset + 2); + + // Update the sequence reference file in the SD card + seqNspCtx.partNumber = (splitIndex + 1); + seqNspCtx.fileIndex = startFileIndex; + seqNspCtx.fileOffset = nca_offset; + + // Copy the SHA-256 context data, but only if we're not dealing with the CNMT NCA + // NCA ID/hash for the CNMT NCA is handled in patchCnmtNca() + if (seqNspCtx.fileIndex < titleNcaCount && seqNspCtx.fileIndex != cnmtNcaIndex) + { + memcpy(&(seqNspCtx.hashCtx), &nca_hash_ctx, sizeof(Sha256Context)); + } else { + memset(&(seqNspCtx.hashCtx), 0, sizeof(Sha256Context)); + } + + // Write the struct data + write_res = fwrite(&seqNspCtx, 1, sizeof(sequentialNspCtx), seqDumpFile); + if (write_res == sizeof(sequentialNspCtx)) + { + // Write the NCA hashes + write_res = fwrite(seqDumpNcaHashes, 1, seqDumpFileSize - sizeof(sequentialNspCtx), seqDumpFile); + if (write_res != (seqDumpFileSize - sizeof(sequentialNspCtx))) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to write %lu bytes chunk to the sequential dump reference file! (wrote %lu bytes)", seqDumpFileSize - sizeof(sequentialNspCtx), write_res); + success = false; + seqDumpFileRemove = true; + } + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to write %lu bytes chunk to the sequential dump reference file! (wrote %lu bytes)", sizeof(sequentialNspCtx), write_res); + success = false; + seqDumpFileRemove = true; + } + } else { + // Mark the file for deletion + seqDumpFileRemove = true; + } + } + + if (success && !batch && !calcCrc) + { + timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx.now)); + progressCtx.now -= progressCtx.start; + + if (!seqDumpMode || (seqDumpMode && !seqDumpFinish)) + { + progressCtx.progress = 100; + progressCtx.remainingTime = 0; + } + + printProgressBar(&progressCtx, false, 0); + + formatETAString(progressCtx.now, progressCtx.etaInfo, MAX_ELEMENTS(progressCtx.etaInfo)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Process successfully completed after %s!", progressCtx.etaInfo); + + if (seqDumpMode) + { + breaks += 2; + + if (seqDumpFinish) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Please remember to exit the application and transfer the generated part file(s) to a PC before continuing in the next session!"); + breaks++; + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Do NOT move the \"%s\" file!", strrchr(seqDumpFilename, '/' ) + 1); + } + + if (checkIfFileExists(pfs0HeaderFilename)) + { + if (seqDumpFinish) breaks++; + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "The \"%s\" file contains the PFS0 header.\nUse it as the first file when concatenating all parts!", strrchr(pfs0HeaderFilename, '/' ) + 1); + } + } + + breaks += 2; + + uiRefreshDisplay(); + } + } else { if (dumping) { breaks += 6; if (fat32_error) breaks += 2; } + breaks += 2; + if (removeFile) { - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp", NSP_DUMP_PATH, dumpName); - - if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32) + if (seqDumpMode) { - fsdevDeleteDirectoryRecursively(dumpPath); + for(u8 i = 0; i <= splitIndex; i++) + { + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp.%02u", NSP_DUMP_PATH, dumpName, i); + unlink(dumpPath); + } } else { - unlink(dumpPath); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp", NSP_DUMP_PATH, dumpName); + + if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && isFat32) + { + fsdevDeleteDirectoryRecursively(dumpPath); + } else { + unlink(dumpPath); + } } } } @@ -2199,18 +2927,39 @@ out: } } - free(dumpName); + if (seqDumpNcaHashes) free(seqDumpNcaHashes); - if (!batch) breaks += 2; + if (seqDumpFile) fclose(seqDumpFile); + + if (seqDumpFileRemove) unlink(seqDumpFilename); + + if (dumpName) free(dumpName); return success; } -bool dumpNintendoSubmissionPackageBatch(bool dumpAppTitles, bool dumpPatchTitles, bool dumpAddOnTitles, bool isFat32, bool removeConsoleData, bool tiklessDump, bool skipDumpedTitles, batchModeSourceStorage batchModeSrc) +bool dumpNintendoSubmissionPackageBatch(batchOptions *batchDumpCfg) { - if ((!dumpAppTitles && !dumpPatchTitles && !dumpAddOnTitles) || (batchModeSrc == BATCH_SOURCE_ALL && ((dumpAppTitles && !titleAppCount) || (dumpPatchTitles && !titlePatchCount) || (dumpAddOnTitles && !titleAddOnCount))) || (batchModeSrc == BATCH_SOURCE_SDCARD && ((dumpAppTitles && !sdCardTitleAppCount) || (dumpPatchTitles && !sdCardTitlePatchCount) || (dumpAddOnTitles && !sdCardTitleAddOnCount))) || (batchModeSrc == BATCH_SOURCE_EMMC && ((dumpAppTitles && !nandUserTitleAppCount) || (dumpPatchTitles && !nandUserTitlePatchCount) || (dumpAddOnTitles && !nandUserTitleAddOnCount)))) + if (!batchDumpCfg) { - uiDrawString("Error: invalid parameters to perform batch NSP dump!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid batch dump configuration struct!"); + breaks += 2; + return false; + } + + bool dumpAppTitles = batchDumpCfg->dumpAppTitles; + bool dumpPatchTitles = batchDumpCfg->dumpPatchTitles; + bool dumpAddOnTitles = batchDumpCfg->dumpAddOnTitles; + bool isFat32 = batchDumpCfg->isFat32; + bool removeConsoleData = batchDumpCfg->removeConsoleData; + bool tiklessDump = batchDumpCfg->tiklessDump; + bool skipDumpedTitles = batchDumpCfg->skipDumpedTitles; + batchModeSourceStorage batchModeSrc = batchDumpCfg->batchModeSrc; + bool rememberDumpedTitles = batchDumpCfg->rememberDumpedTitles; + + if ((!dumpAppTitles && !dumpPatchTitles && !dumpAddOnTitles) || (batchModeSrc == BATCH_SOURCE_ALL && ((dumpAppTitles && !titleAppCount) || (dumpPatchTitles && !titlePatchCount) || (dumpAddOnTitles && !titleAddOnCount))) || (batchModeSrc == BATCH_SOURCE_SDCARD && ((dumpAppTitles && !sdCardTitleAppCount) || (dumpPatchTitles && !sdCardTitlePatchCount) || (dumpAddOnTitles && !sdCardTitleAddOnCount))) || (batchModeSrc == BATCH_SOURCE_EMMC && ((dumpAppTitles && !nandUserTitleAppCount) || (dumpPatchTitles && !nandUserTitlePatchCount) || (dumpAddOnTitles && !nandUserTitleAddOnCount))) || batchModeSrc >= BATCH_SOURCE_CNT) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to perform batch NSP dump!"); breaks += 2; return false; } @@ -2222,20 +2971,41 @@ bool dumpNintendoSubmissionPackageBatch(bool dumpAppTitles, bool dumpPatchTitles u32 titleCount, titleIndex; char *dumpName = NULL; - char dumpPath[NAME_BUF_LEN * 2] = {'\0'}; - char curName[NAME_BUF_LEN * 2] = {'\0'}; + char dumpPath[NAME_BUF_LEN] = {'\0'}; + char summary_str[256] = {'\0'}; int initial_breaks = breaks, cur_breaks; - const u32 maxSummaryFileCount = 6; - u32 summaryPage = 0; + const u32 maxSummaryFileCount = 8; + u32 summaryPage = 0, selectedSummaryEntry = 0; + u32 xpos = 0, ypos = 0; + u32 keysDown = 0, keysHeld = 0; - memset(filenameBuffer, 0, FILENAME_BUFFER_SIZE); - filenamesCount = 0; + u32 maxEntryCount = 0, batchEntryIndex = 0, disabledEntryCount = 0; + batchEntry *batchEntries = NULL; - char *nextFilename = filenameBuffer; + bool proceed = true, success = false; - bool proceed = true; + // Generate NSP configuration struct + nspOptions nspDumpCfg; + + nspDumpCfg.isFat32 = isFat32; + nspDumpCfg.calcCrc = false; + nspDumpCfg.removeConsoleData = removeConsoleData; + nspDumpCfg.tiklessDump = tiklessDump; + + // Allocate memory for the batch entries + if (dumpAppTitles) maxEntryCount += (batchModeSrc == BATCH_SOURCE_ALL ? titleAppCount : (batchModeSrc == BATCH_SOURCE_SDCARD ? sdCardTitleAppCount : nandUserTitleAppCount)); + if (dumpPatchTitles) maxEntryCount += (batchModeSrc == BATCH_SOURCE_ALL ? titlePatchCount : (batchModeSrc == BATCH_SOURCE_SDCARD ? sdCardTitlePatchCount : nandUserTitlePatchCount)); + if (dumpAppTitles) maxEntryCount += (batchModeSrc == BATCH_SOURCE_ALL ? titleAddOnCount : (batchModeSrc == BATCH_SOURCE_SDCARD ? sdCardTitleAddOnCount : nandUserTitleAddOnCount)); + + batchEntries = calloc(maxEntryCount, sizeof(batchEntry)); + if (!batchEntries) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for batch entries!"); + breaks += 2; + return false; + } if (dumpAppTitles) { @@ -2248,40 +3018,59 @@ bool dumpNintendoSubmissionPackageBatch(bool dumpAppTitles, bool dumpPatchTitles dumpName = generateNSPDumpName(DUMP_APP_NSP, titleIndex); if (!dumpName) { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to generate output dump name!"); breaks += 2; - return false; + goto out; } - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp", NSP_DUMP_PATH, dumpName); + // Check if an override file already exists for this dump + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp", BATCH_OVERRIDES_PATH, dumpName); + + if (checkIfFileExists(dumpPath)) + { + free(dumpName); + dumpName = NULL; + continue; + } + + // Check if this title has already been dumped + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp", NSP_DUMP_PATH, dumpName); free(dumpName); dumpName = NULL; - // Check if this title has already been dumped if (skipDumpedTitles && checkIfFileExists(dumpPath)) continue; - snprintf(curName, sizeof(curName) / sizeof(curName[0]), strrchr(dumpPath, '/') + 1); + // Save title properties + batchEntries[batchEntryIndex].enabled = true; + batchEntries[batchEntryIndex].titleType = DUMP_APP_NSP; + batchEntries[batchEntryIndex].titleIndex = titleIndex; + snprintf(batchEntries[batchEntryIndex].nspFilename, MAX_ELEMENTS(batchEntries[batchEntryIndex].nspFilename), strrchr(dumpPath, '/') + 1); // Fix entry name length - u32 strWidth = uiGetStrWidth(curName); + snprintf(batchEntries[batchEntryIndex].truncatedNspFilename, MAX_ELEMENTS(batchEntries[batchEntryIndex].truncatedNspFilename), batchEntries[batchEntryIndex].nspFilename); + + u32 strWidth = uiGetStrWidth(batchEntries[batchEntryIndex].truncatedNspFilename); if ((8 + strWidth) >= (FB_WIDTH - (font_height * 5))) { while((8 + strWidth) >= (FB_WIDTH - (font_height * 5))) { - curName[strlen(curName) - 1] = '\0'; - strWidth = uiGetStrWidth(curName); + batchEntries[batchEntryIndex].truncatedNspFilename[strlen(batchEntries[batchEntryIndex].truncatedNspFilename) - 1] = '\0'; + strWidth = uiGetStrWidth(batchEntries[batchEntryIndex].truncatedNspFilename); } - strcat(curName, "..."); + strcat(batchEntries[batchEntryIndex].truncatedNspFilename, "..."); } - addStringToFilenameBuffer(curName, &nextFilename); + // Increase batch entry index + batchEntryIndex++; + // Increase total base application count totalAppCount++; } + // Increase total title count totalTitleCount += totalAppCount; } @@ -2296,40 +3085,59 @@ bool dumpNintendoSubmissionPackageBatch(bool dumpAppTitles, bool dumpPatchTitles dumpName = generateNSPDumpName(DUMP_PATCH_NSP, titleIndex); if (!dumpName) { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to generate output dump name!"); breaks += 2; - return false; + goto out; } - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp", NSP_DUMP_PATH, dumpName); + // Check if an override file already exists for this dump + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp", BATCH_OVERRIDES_PATH, dumpName); + + if (checkIfFileExists(dumpPath)) + { + free(dumpName); + dumpName = NULL; + continue; + } + + // Check if this title has already been dumped + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp", NSP_DUMP_PATH, dumpName); free(dumpName); dumpName = NULL; - // Check if this title has already been dumped if (skipDumpedTitles && checkIfFileExists(dumpPath)) continue; - snprintf(curName, sizeof(curName) / sizeof(curName[0]), strrchr(dumpPath, '/') + 1); + // Save title properties + batchEntries[batchEntryIndex].enabled = true; + batchEntries[batchEntryIndex].titleType = DUMP_PATCH_NSP; + batchEntries[batchEntryIndex].titleIndex = titleIndex; + snprintf(batchEntries[batchEntryIndex].nspFilename, MAX_ELEMENTS(batchEntries[batchEntryIndex].nspFilename), strrchr(dumpPath, '/') + 1); // Fix entry name length - u32 strWidth = uiGetStrWidth(curName); + snprintf(batchEntries[batchEntryIndex].truncatedNspFilename, MAX_ELEMENTS(batchEntries[batchEntryIndex].truncatedNspFilename), batchEntries[batchEntryIndex].nspFilename); + + u32 strWidth = uiGetStrWidth(batchEntries[batchEntryIndex].truncatedNspFilename); if ((8 + strWidth) >= (FB_WIDTH - (font_height * 5))) { while((8 + strWidth) >= (FB_WIDTH - (font_height * 5))) { - curName[strlen(curName) - 1] = '\0'; - strWidth = uiGetStrWidth(curName); + batchEntries[batchEntryIndex].truncatedNspFilename[strlen(batchEntries[batchEntryIndex].truncatedNspFilename) - 1] = '\0'; + strWidth = uiGetStrWidth(batchEntries[batchEntryIndex].truncatedNspFilename); } - strcat(curName, "..."); + strcat(batchEntries[batchEntryIndex].truncatedNspFilename, "..."); } - addStringToFilenameBuffer(curName, &nextFilename); + // Increase batch entry index + batchEntryIndex++; + // Increase total patch count totalPatchCount++; } + // Increase total title count totalTitleCount += totalPatchCount; } @@ -2344,282 +3152,326 @@ bool dumpNintendoSubmissionPackageBatch(bool dumpAppTitles, bool dumpPatchTitles dumpName = generateNSPDumpName(DUMP_ADDON_NSP, titleIndex); if (!dumpName) { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to generate output dump name!"); breaks += 2; - return false; + goto out; } - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp", NSP_DUMP_PATH, dumpName); + // Check if an override file already exists for this dump + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp", BATCH_OVERRIDES_PATH, dumpName); + + if (checkIfFileExists(dumpPath)) + { + free(dumpName); + dumpName = NULL; + continue; + } + + // Check if this title has already been dumped + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp", NSP_DUMP_PATH, dumpName); free(dumpName); dumpName = NULL; - // Check if this title has already been dumped if (skipDumpedTitles && checkIfFileExists(dumpPath)) continue; - snprintf(curName, sizeof(curName) / sizeof(curName[0]), strrchr(dumpPath, '/') + 1); + // Save title properties + batchEntries[batchEntryIndex].enabled = true; + batchEntries[batchEntryIndex].titleType = DUMP_ADDON_NSP; + batchEntries[batchEntryIndex].titleIndex = titleIndex; + snprintf(batchEntries[batchEntryIndex].nspFilename, MAX_ELEMENTS(batchEntries[batchEntryIndex].nspFilename), strrchr(dumpPath, '/') + 1); // Fix entry name length - u32 strWidth = uiGetStrWidth(curName); + snprintf(batchEntries[batchEntryIndex].truncatedNspFilename, MAX_ELEMENTS(batchEntries[batchEntryIndex].truncatedNspFilename), batchEntries[batchEntryIndex].nspFilename); + + u32 strWidth = uiGetStrWidth(batchEntries[batchEntryIndex].truncatedNspFilename); if ((8 + strWidth) >= (FB_WIDTH - (font_height * 5))) { while((8 + strWidth) >= (FB_WIDTH - (font_height * 5))) { - curName[strlen(curName) - 1] = '\0'; - strWidth = uiGetStrWidth(curName); + batchEntries[batchEntryIndex].truncatedNspFilename[strlen(batchEntries[batchEntryIndex].truncatedNspFilename) - 1] = '\0'; + strWidth = uiGetStrWidth(batchEntries[batchEntryIndex].truncatedNspFilename); } - strcat(curName, "..."); + strcat(batchEntries[batchEntryIndex].truncatedNspFilename, "..."); } - addStringToFilenameBuffer(curName, &nextFilename); + // Increase batch entry index + batchEntryIndex++; + // Increase total addon count totalAddOnCount++; } + // Increase total title count totalTitleCount += totalAddOnCount; } if (!totalTitleCount) { - uiDrawString("You have already dumped all titles matching the selected settings!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "You have already dumped all titles matching the selected settings!"); breaks += 2; return false; } - // Display summary - uiDrawString("Summary:", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + // Display summary controls + if (totalTitleCount > maxSummaryFileCount) + { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "[ " NINTENDO_FONT_DPAD " / " NINTENDO_FONT_LSTICK " / " NINTENDO_FONT_RSTICK " ] Move | [ " NINTENDO_FONT_ZL " / " NINTENDO_FONT_ZR " ] Change page | [ " NINTENDO_FONT_A " ] Proceed | [ " NINTENDO_FONT_B " ] Cancel | [ " NINTENDO_FONT_Y " ] Toggle selected entry | [ " NINTENDO_FONT_L " ] Disable all entries | [ " NINTENDO_FONT_R " ] Enable all entries"); + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "[ " NINTENDO_FONT_DPAD " / " NINTENDO_FONT_LSTICK " / " NINTENDO_FONT_RSTICK " ] Move | [ " NINTENDO_FONT_A " ] Proceed | [ " NINTENDO_FONT_B " ] Cancel | [ " NINTENDO_FONT_Y " ] Toggle selected entry | [ " NINTENDO_FONT_L " ] Disable all entries | [ " NINTENDO_FONT_R " ] Enable all entries"); + } + breaks += 2; - strbuf[0] = '\0'; + // Display summary + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Summary:"); + breaks += 2; if (totalAppCount) { - snprintf(curName, sizeof(curName) / sizeof(curName[0]), "BASE: %u", totalAppCount); - strcat(strbuf, curName); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "BASE: %u", totalAppCount); + strcat(summary_str, dumpPath); } if (totalPatchCount) { - if (totalAppCount) strcat(strbuf, " | "); - snprintf(curName, sizeof(curName) / sizeof(curName[0]), "UPD: %u", totalPatchCount); - strcat(strbuf, curName); + if (totalAppCount) strcat(summary_str, " | "); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "UPD: %u", totalPatchCount); + strcat(summary_str, dumpPath); } if (totalAddOnCount) { - if (totalAppCount || totalPatchCount) strcat(strbuf, " | "); - snprintf(curName, sizeof(curName) / sizeof(curName[0]), "DLC: %u", totalAddOnCount); - strcat(strbuf, curName); + if (totalAppCount || totalPatchCount) strcat(summary_str, " | "); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "DLC: %u", totalAddOnCount); + strcat(summary_str, dumpPath); } - strcat(strbuf, " | "); - snprintf(curName, sizeof(curName) / sizeof(curName[0]), "Total: %u", totalTitleCount); - strcat(strbuf, curName); + strcat(summary_str, " | "); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "Total: %u", totalTitleCount); + strcat(summary_str, dumpPath); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, summary_str); breaks++; while(true) { cur_breaks = breaks; - uiFill(0, 8 + (cur_breaks * (font_height + (font_height / 4))), FB_WIDTH, FB_HEIGHT - (8 + (cur_breaks * (font_height + (font_height / 4)))), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, 8 + (cur_breaks * LINE_HEIGHT), FB_WIDTH, FB_HEIGHT - (8 + (cur_breaks * LINE_HEIGHT)) - ((3 * LINE_HEIGHT) + 8), BG_COLOR_RGB); + + // Calculate the number of selected titles + j = 0; + for(i = 0; i < totalTitleCount; i++) + { + if (batchEntries[i].enabled) j++; + } if (totalTitleCount > maxSummaryFileCount) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Current page: %u", summaryPage + 1); - uiDrawString(strbuf, 8, (cur_breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - cur_breaks++; + uiDrawString(STRING_X_POS, STRING_Y_POS(cur_breaks), FONT_COLOR_RGB, "Current page: %u | Selected titles: %u", summaryPage + 1, j); + } else { + uiDrawString(STRING_X_POS, STRING_Y_POS(cur_breaks), FONT_COLOR_RGB, "Selected titles: %u", j); } - cur_breaks++; + cur_breaks += 2; - for(i = (summaryPage * maxSummaryFileCount); i < ((summaryPage * maxSummaryFileCount) + maxSummaryFileCount); i++) + j = 0; + highlight = false; + + for(i = (summaryPage * maxSummaryFileCount); i < ((summaryPage + 1) * maxSummaryFileCount); i++, j++) { if (i >= totalTitleCount) break; - uiDrawIcon(fileNormalIconBuf, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, 8, 8 + (cur_breaks * (font_height + (font_height / 4))) + (font_height / 8)); - uiDrawString(filenames[i], BROWSER_ICON_DIMENSION + 8, (cur_breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - cur_breaks++; + + xpos = STRING_X_POS; + ypos = ((cur_breaks * LINE_HEIGHT) + (j * (font_height + 12)) + 6); + + if (i == selectedSummaryEntry) + { + highlight = true; + uiFill(0, (ypos + 8) - 6, FB_WIDTH, font_height + 12, HIGHLIGHT_BG_COLOR_RGB); + } + + uiDrawIcon((highlight ? (batchEntries[i].enabled ? enabledHighlightIconBuf : disabledHighlightIconBuf) : (batchEntries[i].enabled ? enabledNormalIconBuf : disabledNormalIconBuf)), BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, xpos, ypos + 8); + xpos += BROWSER_ICON_DIMENSION; + + if (highlight) + { + uiDrawString(xpos, ypos, HIGHLIGHT_FONT_COLOR_RGB, batchEntries[i].truncatedNspFilename); + } else { + uiDrawString(xpos, ypos, FONT_COLOR_RGB, batchEntries[i].truncatedNspFilename); + } + + if (i == selectedSummaryEntry) highlight = false; } - cur_breaks++; - - if (totalTitleCount > maxSummaryFileCount) + while(true) { - uiDrawString("[ " NINTENDO_FONT_L " / " NINTENDO_FONT_R " / " NINTENDO_FONT_ZL " / " NINTENDO_FONT_ZR " ] Change page | [ " NINTENDO_FONT_A " ] Proceed | [ " NINTENDO_FONT_B " ] Cancel", 8, (cur_breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - } else { - uiDrawString("[ " NINTENDO_FONT_A " ] Proceed | [ " NINTENDO_FONT_B " ] Cancel", 8, (cur_breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiUpdateStatusMsg(); + uiRefreshDisplay(); + + hidScanInput(); + + keysDown = hidKeysDown(CONTROLLER_P1_AUTO); + keysHeld = hidKeysHeld(CONTROLLER_P1_AUTO); + + if ((keysDown && !(keysDown & KEY_TOUCH)) || (keysHeld && !(keysHeld & KEY_TOUCH))) break; } - uiRefreshDisplay(); - - hidScanInput(); - - u32 keysDown = hidKeysDown(CONTROLLER_P1_AUTO); - + // Start batch dump process if (keysDown & KEY_A) { - proceed = true; - break; - } else + // Check if we have at least a single enabled entry + for(i = 0; i < totalTitleCount; i++) + { + if (batchEntries[i].enabled) break; + } + + if (i < totalTitleCount) + { + proceed = true; + break; + } else { + uiStatusMsg("Please enable at least one entry from the list."); + } + } + + // Cancel batch dump process if (keysDown & KEY_B) { proceed = false; break; - } else - if (((keysDown & KEY_L) || (keysDown & KEY_ZL)) && totalTitleCount > maxSummaryFileCount) + } + + // Toggle selected entry + if (keysDown & KEY_Y) batchEntries[selectedSummaryEntry].enabled ^= 0x01; + + // Disable all entries + if (keysDown & KEY_L) { - if (summaryPage > 0) summaryPage--; - } else - if (((keysDown & KEY_R) || (keysDown & KEY_ZR)) && totalTitleCount > maxSummaryFileCount) + for(i = 0; i < totalTitleCount; i++) batchEntries[i].enabled = false; + } + + // Enable all entries + if (keysDown & KEY_R) { - if (((summaryPage * maxSummaryFileCount) + maxSummaryFileCount) < totalTitleCount) summaryPage++; + for(i = 0; i < totalTitleCount; i++) batchEntries[i].enabled = true; + } + + // Change page (left) + if ((keysDown & KEY_ZL) && totalTitleCount > maxSummaryFileCount) + { + if (summaryPage > 0) + { + summaryPage--; + selectedSummaryEntry = (summaryPage * maxSummaryFileCount); + } + } + + // Change page (right) + if ((keysDown & KEY_ZR) && totalTitleCount > maxSummaryFileCount) + { + if (((summaryPage + 1) * maxSummaryFileCount) < totalTitleCount) + { + summaryPage++; + selectedSummaryEntry = (summaryPage * maxSummaryFileCount); + } + } + + // Go up + if ((keysDown & KEY_DUP) || (keysDown & KEY_LSTICK_UP) || (keysHeld & KEY_RSTICK_UP)) + { + if (selectedSummaryEntry > (summaryPage * maxSummaryFileCount)) + { + selectedSummaryEntry--; + } else { + if ((keysDown & KEY_DUP) || (keysDown & KEY_LSTICK_UP)) + { + if (((summaryPage + 1) * maxSummaryFileCount) < totalTitleCount) + { + selectedSummaryEntry = (((summaryPage + 1) * maxSummaryFileCount) - 1); + } else { + selectedSummaryEntry = (totalTitleCount - 1); + } + } + } + } + + // Go down + if ((keysDown & KEY_DDOWN) || (keysDown & KEY_LSTICK_DOWN) || (keysHeld & KEY_RSTICK_DOWN)) + { + if (((((summaryPage + 1) * maxSummaryFileCount) < totalTitleCount) && selectedSummaryEntry < (((summaryPage + 1) * maxSummaryFileCount) - 1)) || ((((summaryPage + 1) * maxSummaryFileCount) >= totalTitleCount) && selectedSummaryEntry < (totalTitleCount - 1))) + { + selectedSummaryEntry++; + } else { + if ((keysDown & KEY_DDOWN) || (keysDown & KEY_LSTICK_DOWN)) + { + selectedSummaryEntry = (summaryPage * maxSummaryFileCount); + } + } } } + breaks = initial_breaks; + uiFill(0, 8 + (breaks * LINE_HEIGHT), FB_WIDTH, FB_HEIGHT - (8 + (breaks * LINE_HEIGHT)), BG_COLOR_RGB); + if (!proceed) { - breaks = (cur_breaks + 2); - uiDrawString("Process canceled", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Process canceled"); breaks += 2; - return false; + goto out; } - breaks = initial_breaks; - uiFill(0, 8 + (breaks * (font_height + (font_height / 4))), FB_WIDTH, FB_HEIGHT - (8 + (breaks * (font_height + (font_height / 4)))), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + // Substract the disabled entries from the total title count + for(i = 0; i < totalTitleCount; i++) + { + if (!batchEntries[i].enabled) disabledEntryCount++; + } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dump procedure started. Hold %s to cancel.", NINTENDO_FONT_B); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Dump procedure started. Hold %s to cancel.", NINTENDO_FONT_B); breaks += 2; initial_breaks = breaks; j = 0; - if (totalAppCount) + for(i = 0; i < totalTitleCount; i++) { - titleCount = (batchModeSrc == BATCH_SOURCE_ALL ? titleAppCount : (batchModeSrc == BATCH_SOURCE_SDCARD ? sdCardTitleAppCount : nandUserTitleAppCount)); + if (!batchEntries[i].enabled) continue; - for(i = 0; i < titleCount; i++) + breaks = initial_breaks; + + uiFill(0, 8 + (breaks * LINE_HEIGHT), FB_WIDTH, FB_HEIGHT - (8 + (breaks * LINE_HEIGHT)), BG_COLOR_RGB); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Title: %u / %u.", j + 1, totalTitleCount - disabledEntryCount); + uiRefreshDisplay(); + + breaks += 2; + + // Dump title + if (!dumpNintendoSubmissionPackage(batchEntries[i].titleType, batchEntries[i].titleIndex, &nspDumpCfg, true)) goto out; + + // Create override file if necessary + if (rememberDumpedTitles) { - breaks = initial_breaks; - uiFill(0, 8 + (breaks * (font_height + (font_height / 4))), FB_WIDTH, FB_HEIGHT - (8 + (breaks * (font_height + (font_height / 4)))), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); - - titleIndex = ((batchModeSrc == BATCH_SOURCE_ALL || batchModeSrc == BATCH_SOURCE_SDCARD) ? i : (i + sdCardTitleAppCount)); - - dumpName = generateNSPDumpName(DUMP_APP_NSP, titleIndex); - if (!dumpName) - { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - breaks += 2; - return false; - } - - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp", NSP_DUMP_PATH, dumpName); - - free(dumpName); - dumpName = NULL; - - // Check if this title has already been dumped - if (skipDumpedTitles && checkIfFileExists(dumpPath)) continue; - - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Title: %u / %u.", j + 1, totalTitleCount); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - uiRefreshDisplay(); - breaks += 2; - - // Dump title - if (!dumpNintendoSubmissionPackage(DUMP_APP_NSP, titleIndex, isFat32, false, removeConsoleData, tiklessDump, true)) return false; - - j++; + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s", BATCH_OVERRIDES_PATH, batchEntries[i].nspFilename); + FILE *overrideFile = fopen(dumpPath, "wb"); + if (overrideFile) fclose(overrideFile); } + + // Update free space + uiUpdateFreeSpace(); + + j++; } - if (totalPatchCount) - { - titleCount = (batchModeSrc == BATCH_SOURCE_ALL ? titlePatchCount : (batchModeSrc == BATCH_SOURCE_SDCARD ? sdCardTitlePatchCount : nandUserTitlePatchCount)); - - for(i = 0; i < titleCount; i++) - { - breaks = initial_breaks; - uiFill(0, 8 + (breaks * (font_height + (font_height / 4))), FB_WIDTH, FB_HEIGHT - (8 + (breaks * (font_height + (font_height / 4)))), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); - - titleIndex = ((batchModeSrc == BATCH_SOURCE_ALL || batchModeSrc == BATCH_SOURCE_SDCARD) ? i : (i + sdCardTitlePatchCount)); - - dumpName = generateNSPDumpName(DUMP_PATCH_NSP, titleIndex); - if (!dumpName) - { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - breaks += 2; - return false; - } - - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp", NSP_DUMP_PATH, dumpName); - - free(dumpName); - dumpName = NULL; - - // Check if this title has already been dumped - if (skipDumpedTitles && checkIfFileExists(dumpPath)) continue; - - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Title: %u / %u.", j + 1, totalTitleCount); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - uiRefreshDisplay(); - breaks += 2; - - // Dump title - if (!dumpNintendoSubmissionPackage(DUMP_PATCH_NSP, titleIndex, isFat32, false, removeConsoleData, tiklessDump, true)) return false; - - j++; - } - } - - if (totalAddOnCount) - { - titleCount = (batchModeSrc == BATCH_SOURCE_ALL ? titleAddOnCount : (batchModeSrc == BATCH_SOURCE_SDCARD ? sdCardTitleAddOnCount : nandUserTitleAddOnCount)); - - for(i = 0; i < titleCount; i++) - { - breaks = initial_breaks; - uiFill(0, 8 + (breaks * (font_height + (font_height / 4))), FB_WIDTH, FB_HEIGHT - (8 + (breaks * (font_height + (font_height / 4)))), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); - - titleIndex = ((batchModeSrc == BATCH_SOURCE_ALL || batchModeSrc == BATCH_SOURCE_SDCARD) ? i : (i + sdCardTitleAddOnCount)); - - dumpName = generateNSPDumpName(DUMP_ADDON_NSP, titleIndex); - if (!dumpName) - { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - breaks += 2; - return false; - } - - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp", NSP_DUMP_PATH, dumpName); - - free(dumpName); - dumpName = NULL; - - // Check if this title has already been dumped - if (skipDumpedTitles && checkIfFileExists(dumpPath)) continue; - - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Title: %u / %u.", j + 1, totalTitleCount); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - uiRefreshDisplay(); - breaks += 2; - - // Dump title - if (!dumpNintendoSubmissionPackage(DUMP_ADDON_NSP, titleIndex, isFat32, false, removeConsoleData, tiklessDump, true)) return false; - - j++; - } - } - - uiDrawString("Process successfully completed!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); - + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Process successfully completed!"); breaks += 2; - return true; +out: + free(batchEntries); + return success; } bool dumpRawHfs0Partition(u32 partition, bool doSplitting) @@ -2644,7 +3496,7 @@ bool dumpRawHfs0Partition(u32 partition, bool doSplitting) char *dumpName = generateFullDumpName(); if (!dumpName) { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to generate output dump name!"); breaks += 2; return false; } @@ -2653,8 +3505,7 @@ bool dumpRawHfs0Partition(u32 partition, bool doSplitting) if (R_SUCCEEDED(result = fsDeviceOperatorGetGameCardHandle(&fsOperatorInstance, &handle))) { - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle succeeded: 0x%08X", handle.value); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "GetGameCardHandle succeeded: 0x%08X", handle.value); breaks++;*/ // Ugly hack @@ -2668,28 +3519,25 @@ bool dumpRawHfs0Partition(u32 partition, bool doSplitting) if (R_SUCCEEDED(result = fsOpenGameCardStorage(&gameCardStorage, &handle, HFS0_TO_ISTORAGE_IDX(hfs0_partition_cnt, partition)))) { - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage succeeded: 0x%08X", handle.value); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "OpenGameCardStorage succeeded: 0x%08X", handle.value); breaks++;*/ if (getHfs0EntryDetails(hfs0_header, hfs0_offset, hfs0_size, hfs0_partition_cnt, partition, true, 0, &partitionOffset, &(progressCtx.totalSize))) { - convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, sizeof(progressCtx.totalSizeStr) / sizeof(progressCtx.totalSizeStr[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "HFS0 partition size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, MAX_ELEMENTS(progressCtx.totalSizeStr)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "HFS0 partition size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); breaks += 2; - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "HFS0 partition offset (relative to IStorage instance): 0x%016lX", partitionOffset); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "HFS0 partition offset (relative to IStorage instance): 0x%016lX", partitionOffset); breaks += 2;*/ if (progressCtx.totalSize <= freeSpace) { if (progressCtx.totalSize > FAT32_FILESIZE_LIMIT && doSplitting) { - snprintf(filename, sizeof(filename) / sizeof(filename[0]), "%s%s - Partition %u (%s).hfs0.%02u", HFS0_DUMP_PATH, dumpName, partition, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, partition), splitIndex); + snprintf(filename, MAX_ELEMENTS(filename), "%s%s - Partition %u (%s).hfs0.%02u", HFS0_DUMP_PATH, dumpName, partition, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, partition), splitIndex); } else { - snprintf(filename, sizeof(filename) / sizeof(filename[0]), "%s%s - Partition %u (%s).hfs0", HFS0_DUMP_PATH, dumpName, partition, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, partition)); + snprintf(filename, MAX_ELEMENTS(filename), "%s%s - Partition %u (%s).hfs0", HFS0_DUMP_PATH, dumpName, partition, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, partition)); } // Check if the dump already exists @@ -2701,11 +3549,11 @@ bool dumpRawHfs0Partition(u32 partition, bool doSplitting) proceed = yesNoPrompt("You have already dumped this content. Do you wish to proceed anyway?"); if (!proceed) { - uiDrawString("Process canceled.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Process canceled."); } else { // Remove the prompt from the screen breaks = cur_breaks; - uiFill(0, 8 + (breaks * (font_height + (font_height / 4))) + (font_height / 8), FB_WIDTH, FB_HEIGHT - (8 + (breaks * (font_height + (font_height / 4))) + (font_height / 8)), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, 8 + STRING_Y_POS(breaks), FB_WIDTH, FB_HEIGHT - (8 + STRING_Y_POS(breaks)), BG_COLOR_RGB); } } @@ -2714,13 +3562,12 @@ bool dumpRawHfs0Partition(u32 partition, bool doSplitting) outFile = fopen(filename, "wb"); if (outFile) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dumping raw HFS0 partition #%u. Hold %s to cancel.", partition, NINTENDO_FONT_B); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Dumping raw HFS0 partition #%u. Hold %s to cancel.", partition, NINTENDO_FONT_B); breaks += 2; if (programAppletType != AppletType_Application && programAppletType != AppletType_SystemApplication) { - uiDrawString("Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem."); breaks += 2; } @@ -2731,17 +3578,15 @@ bool dumpRawHfs0Partition(u32 partition, bool doSplitting) for (progressCtx.curOffset = 0; progressCtx.curOffset < progressCtx.totalSize; progressCtx.curOffset += n) { - uiFill(0, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx.line_offset - 2) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 2, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", strrchr(filename, '/' ) + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "Output file: \"%s\".", strrchr(filename, '/' ) + 1); - if (DUMP_BUFFER_SIZE > (progressCtx.totalSize - progressCtx.curOffset)) n = (progressCtx.totalSize - progressCtx.curOffset); + if (n > (progressCtx.totalSize - progressCtx.curOffset)) n = (progressCtx.totalSize - progressCtx.curOffset); if (R_FAILED(result = fsStorageRead(&gameCardStorage, partitionOffset + progressCtx.curOffset, dumpBuf, n))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "StorageRead failed (0x%08X) at offset 0x%016lX", result, partitionOffset + progressCtx.curOffset); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "StorageRead failed (0x%08X) at offset 0x%016lX", result, partitionOffset + progressCtx.curOffset); break; } @@ -2755,8 +3600,7 @@ bool dumpRawHfs0Partition(u32 partition, bool doSplitting) write_res = fwrite(dumpBuf, 1, old_file_chunk_size, outFile); if (write_res != old_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, progressCtx.curOffset, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, progressCtx.curOffset, splitIndex, write_res); break; } } @@ -2767,13 +3611,12 @@ bool dumpRawHfs0Partition(u32 partition, bool doSplitting) if (new_file_chunk_size > 0 || (progressCtx.curOffset + n) < progressCtx.totalSize) { splitIndex++; - snprintf(filename, sizeof(filename) / sizeof(filename[0]), "%s%s - Partition %u (%s).hfs0.%02u", HFS0_DUMP_PATH, dumpName, partition, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, partition), splitIndex); + snprintf(filename, MAX_ELEMENTS(filename), "%s%s - Partition %u (%s).hfs0.%02u", HFS0_DUMP_PATH, dumpName, partition, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, partition), splitIndex); outFile = fopen(filename, "wb"); if (!outFile) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open output file for part #%u!", splitIndex); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to open output file for part #%u!", splitIndex); break; } @@ -2782,8 +3625,7 @@ bool dumpRawHfs0Partition(u32 partition, bool doSplitting) write_res = fwrite(dumpBuf + old_file_chunk_size, 1, new_file_chunk_size, outFile); if (write_res != new_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, progressCtx.curOffset + old_file_chunk_size, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, progressCtx.curOffset + old_file_chunk_size, splitIndex, write_res); break; } } @@ -2792,12 +3634,11 @@ bool dumpRawHfs0Partition(u32 partition, bool doSplitting) write_res = fwrite(dumpBuf, 1, n, outFile); if (write_res != n) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, progressCtx.curOffset, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, progressCtx.curOffset, write_res); if ((progressCtx.curOffset + n) > FAT32_FILESIZE_LIMIT) { - uiDrawString("You're probably using a FAT32 partition. Make sure to enable file splitting.", 8, ((progressCtx.line_offset + 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 4), FONT_COLOR_RGB, "You're probably using a FAT32 partition. Make sure to enable file splitting."); fat32_error = true; } @@ -2811,7 +3652,7 @@ bool dumpRawHfs0Partition(u32 partition, bool doSplitting) { if (cancelProcessCheck(&progressCtx)) { - uiDrawString("Process canceled.", 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Process canceled."); break; } } @@ -2822,10 +3663,9 @@ bool dumpRawHfs0Partition(u32 partition, bool doSplitting) // Support empty files if (!progressCtx.totalSize) { - uiFill(0, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx.line_offset - 2) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 2, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", strrchr(filename, '/' ) + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "Output file: \"%s\".", strrchr(filename, '/' ) + 1); progressCtx.progress = 100; @@ -2839,9 +3679,8 @@ bool dumpRawHfs0Partition(u32 partition, bool doSplitting) timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx.now)); progressCtx.now -= progressCtx.start; - formatETAString(progressCtx.now, progressCtx.etaInfo, sizeof(progressCtx.etaInfo) / sizeof(progressCtx.etaInfo[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Process successfully completed after %s!", progressCtx.etaInfo); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + formatETAString(progressCtx.now, progressCtx.etaInfo, MAX_ELEMENTS(progressCtx.etaInfo)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Process successfully completed after %s!", progressCtx.etaInfo); } else { setProgressBarError(&progressCtx); if (fat32_error) breaks += 2; @@ -2855,7 +3694,7 @@ bool dumpRawHfs0Partition(u32 partition, bool doSplitting) { for(u8 i = 0; i <= splitIndex; i++) { - snprintf(filename, sizeof(filename) / sizeof(filename[0]), "%s%s - Partition %u (%s).hfs0.%02u", HFS0_DUMP_PATH, dumpName, partition, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, partition), i); + snprintf(filename, MAX_ELEMENTS(filename), "%s%s - Partition %u (%s).hfs0.%02u", HFS0_DUMP_PATH, dumpName, partition, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, partition), i); unlink(filename); } } else { @@ -2863,25 +3702,22 @@ bool dumpRawHfs0Partition(u32 partition, bool doSplitting) } } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open output file \"%s\"!", filename); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to open output file \"%s\"!", filename); } } } else { - uiDrawString("Error: not enough free space available in the SD card.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: not enough free space available in the SD card."); } } else { - uiDrawString("Error: unable to get partition details from the root HFS0 header!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to get partition details from the root HFS0 header!"); } fsStorageClose(&gameCardStorage); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "OpenGameCardStorage failed! (0x%08X)", result); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "GetGameCardHandle failed! (0x%08X)", result); } breaks += 2; @@ -2908,46 +3744,41 @@ bool copyFileFromHfs0(u32 partition, const char* source, const char* dest, const memset(dumpBuf, 0, DUMP_BUFFER_SIZE); - uiFill(0, ((progressCtx->line_offset - 4) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 4, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx->line_offset - 4) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 4, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Copying \"%s\"...", source); - uiDrawString(strbuf, 8, ((progressCtx->line_offset - 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset - 4), FONT_COLOR_RGB, "Copying \"%s\"...", source); if ((destLen + 1) < NAME_BUF_LEN) { if (R_SUCCEEDED(result = fsDeviceOperatorGetGameCardHandle(&fsOperatorInstance, &handle))) { - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle succeeded: 0x%08X", handle.value); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "GetGameCardHandle succeeded: 0x%08X", handle.value); breaks++;*/ // Same ugly hack from dumpRawHfs0Partition() if (R_SUCCEEDED(result = fsOpenGameCardStorage(&gameCardStorage, &handle, HFS0_TO_ISTORAGE_IDX(hfs0_partition_cnt, partition)))) { - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage succeeded: 0x%08X", handle.value); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "OpenGameCardStorage succeeded: 0x%08X", handle.value); breaks++;*/ - if (size > FAT32_FILESIZE_LIMIT && doSplitting) snprintf(splitFilename, sizeof(splitFilename) / sizeof(splitFilename[0]), "%s.%02u", dest, splitIndex); + if (size > FAT32_FILESIZE_LIMIT && doSplitting) snprintf(splitFilename, MAX_ELEMENTS(splitFilename), "%s.%02u", dest, splitIndex); outFile = fopen(((size > FAT32_FILESIZE_LIMIT && doSplitting) ? splitFilename : dest), "wb"); if (outFile) { for (off = 0; off < size; off += n, progressCtx->curOffset += n) { - uiFill(0, ((progressCtx->line_offset - 2) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx->line_offset - 2) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 2, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", ((size > FAT32_FILESIZE_LIMIT && doSplitting) ? (strrchr(splitFilename, '/') + 1) : (strrchr(dest, '/') + 1))); - uiDrawString(strbuf, 8, ((progressCtx->line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset - 2), FONT_COLOR_RGB, "Output file: \"%s\".", ((size > FAT32_FILESIZE_LIMIT && doSplitting) ? (strrchr(splitFilename, '/') + 1) : (strrchr(dest, '/') + 1))); uiRefreshDisplay(); - if (DUMP_BUFFER_SIZE > (size - off)) n = (size - off); + if (n > (size - off)) n = (size - off); if (R_FAILED(result = fsStorageRead(&gameCardStorage, file_offset + off, dumpBuf, n))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "StorageRead failed (0x%08X) at offset 0x%016lX", result, file_offset + off); - uiDrawString(strbuf, 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "StorageRead failed (0x%08X) at offset 0x%016lX", result, file_offset + off); break; } @@ -2961,8 +3792,7 @@ bool copyFileFromHfs0(u32 partition, const char* source, const char* dest, const write_res = fwrite(dumpBuf, 1, old_file_chunk_size, outFile); if (write_res != old_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, off, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, off, splitIndex, write_res); break; } } @@ -2973,13 +3803,12 @@ bool copyFileFromHfs0(u32 partition, const char* source, const char* dest, const if (new_file_chunk_size > 0 || (off + n) < size) { splitIndex++; - snprintf(splitFilename, sizeof(splitFilename) / sizeof(splitFilename[0]), "%s.%02u", dest, splitIndex); + snprintf(splitFilename, MAX_ELEMENTS(splitFilename), "%s.%02u", dest, splitIndex); outFile = fopen(splitFilename, "wb"); if (!outFile) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open output file for part #%u!", splitIndex); - uiDrawString(strbuf, 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to open output file for part #%u!", splitIndex); break; } @@ -2988,8 +3817,7 @@ bool copyFileFromHfs0(u32 partition, const char* source, const char* dest, const write_res = fwrite(dumpBuf + old_file_chunk_size, 1, new_file_chunk_size, outFile); if (write_res != new_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, off + old_file_chunk_size, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, off + old_file_chunk_size, splitIndex, write_res); break; } } @@ -2998,12 +3826,11 @@ bool copyFileFromHfs0(u32 partition, const char* source, const char* dest, const write_res = fwrite(dumpBuf, 1, n, outFile); if (write_res != n) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, off, write_res); - uiDrawString(strbuf, 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, off, write_res); if ((off + n) > FAT32_FILESIZE_LIMIT) { - uiDrawString("You're probably using a FAT32 partition. Make sure to enable file splitting.", 8, ((progressCtx->line_offset + 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 4), FONT_COLOR_RGB, "You're probably using a FAT32 partition. Make sure to enable file splitting."); fat32_error = true; } @@ -3017,7 +3844,7 @@ bool copyFileFromHfs0(u32 partition, const char* source, const char* dest, const { if (cancelProcessCheck(progressCtx)) { - uiDrawString("Process canceled.", 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Process canceled."); break; } } @@ -3028,10 +3855,9 @@ bool copyFileFromHfs0(u32 partition, const char* source, const char* dest, const // Support empty files if (!size) { - uiFill(0, ((progressCtx->line_offset - 2) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx->line_offset - 2) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 2, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", ((size > FAT32_FILESIZE_LIMIT && doSplitting) ? (strrchr(splitFilename, '/') + 1) : (strrchr(dest, '/') + 1))); - uiDrawString(strbuf, 8, ((progressCtx->line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset - 2), FONT_COLOR_RGB, "Output file: \"%s\".", ((size > FAT32_FILESIZE_LIMIT && doSplitting) ? (strrchr(splitFilename, '/') + 1) : (strrchr(dest, '/') + 1))); if (progressCtx->totalSize == size) progressCtx->progress = 100; @@ -3053,7 +3879,7 @@ bool copyFileFromHfs0(u32 partition, const char* source, const char* dest, const { for(u8 i = 0; i <= splitIndex; i++) { - snprintf(splitFilename, sizeof(splitFilename) / sizeof(splitFilename[0]), "%s.%02u", dest, i); + snprintf(splitFilename, MAX_ELEMENTS(splitFilename), "%s.%02u", dest, i); unlink(splitFilename); } } else { @@ -3061,21 +3887,18 @@ bool copyFileFromHfs0(u32 partition, const char* source, const char* dest, const } } } else { - uiDrawString("Failed to open output file!", 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_RGB, "Failed to open output file!"); } fsStorageClose(&gameCardStorage); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage failed! (0x%08X)", result); - uiDrawString(strbuf, 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "OpenGameCardStorage failed! (0x%08X)", result); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle failed! (0x%08X)", result); - uiDrawString(strbuf, 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "GetGameCardHandle failed! (0x%08X)", result); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Destination path is too long! (%lu bytes)", destLen); - uiDrawString(strbuf, 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Destination path is too long! (%lu bytes)", destLen); } return success; @@ -3085,19 +3908,19 @@ bool copyHfs0Contents(u32 partition, hfs0_entry_table *partitionEntryTable, prog { if (!dest || !*dest) { - uiDrawString("Error: destination directory is empty.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: destination directory is empty."); return false; } if (!partitionHfs0Header || !partitionEntryTable) { - uiDrawString("HFS0 partition header information unavailable!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "HFS0 partition header information unavailable!"); return false; } if (!progressCtx) { - uiDrawString("Error: invalid progress context.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid progress context."); return false; } @@ -3106,8 +3929,7 @@ bool copyHfs0Contents(u32 partition, hfs0_entry_table *partitionEntryTable, prog if ((dest_len + 1) >= NAME_BUF_LEN) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Destination directory name is too long! (%lu bytes)", dest_len); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Destination directory name is too long! (%lu bytes)", dest_len); return false; } @@ -3153,7 +3975,7 @@ bool dumpHfs0PartitionData(u32 partition, bool doSplitting) char *dumpName = generateFullDumpName(); if (!dumpName) { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to generate output dump name!"); breaks += 2; return false; } @@ -3172,22 +3994,20 @@ bool dumpHfs0PartitionData(u32 partition, bool doSplitting) // Calculate total size for(i = 0; i < partitionHfs0FileCount; i++) progressCtx.totalSize += entryTable[i].file_size; - convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, sizeof(progressCtx.totalSizeStr) / sizeof(progressCtx.totalSizeStr[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Total partition data size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, MAX_ELEMENTS(progressCtx.totalSizeStr)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Total partition data size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); breaks += 2; if (progressCtx.totalSize <= freeSpace) { - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s - Partition %u (%s)", HFS0_DUMP_PATH, dumpName, partition, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, partition)); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s - Partition %u (%s)", HFS0_DUMP_PATH, dumpName, partition, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, partition)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Copying partition #%u data to \"%s/\". Hold %s to cancel.", partition, dumpPath, NINTENDO_FONT_B); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Copying partition #%u data to \"%s/\". Hold %s to cancel.", partition, dumpPath, NINTENDO_FONT_B); breaks += 2; if (programAppletType != AppletType_Application && programAppletType != AppletType_SystemApplication) { - uiDrawString("Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem."); breaks += 2; } @@ -3204,22 +4024,21 @@ bool dumpHfs0PartitionData(u32 partition, bool doSplitting) timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx.now)); progressCtx.now -= progressCtx.start; - formatETAString(progressCtx.now, progressCtx.etaInfo, sizeof(progressCtx.etaInfo) / sizeof(progressCtx.etaInfo[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Process successfully completed after %s!", progressCtx.etaInfo); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + formatETAString(progressCtx.now, progressCtx.etaInfo, MAX_ELEMENTS(progressCtx.etaInfo)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Process successfully completed after %s!", progressCtx.etaInfo); } else { removeDirectoryWithVerbose(dumpPath, "Deleting output directory. Please wait..."); } } else { - uiDrawString("Error: not enough free space available in the SD card.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: not enough free space available in the SD card."); } free(entryTable); } else { - uiDrawString("Unable to allocate memory for the HFS0 file entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Unable to allocate memory for the HFS0 file entries!"); } } else { - uiDrawString("The selected partition is empty!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "The selected partition is empty!"); } free(partitionHfs0Header); @@ -3241,14 +4060,14 @@ bool dumpFileFromHfs0Partition(u32 partition, u32 file, char *filename, bool doS { if (!partitionHfs0Header) { - uiDrawString("HFS0 partition header information unavailable!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "HFS0 partition header information unavailable!"); breaks += 2; return false; } if (!filename || !*filename) { - uiDrawString("Filename unavailable!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Filename unavailable!"); breaks += 2; return false; } @@ -3259,7 +4078,7 @@ bool dumpFileFromHfs0Partition(u32 partition, u32 file, char *filename, bool doS char *dumpName = generateFullDumpName(); if (!dumpName) { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to generate output dump name!"); breaks += 2; return false; } @@ -3271,10 +4090,9 @@ bool dumpFileFromHfs0Partition(u32 partition, u32 file, char *filename, bool doS if (getHfs0EntryDetails(partitionHfs0Header, partitionHfs0HeaderOffset, partitionHfs0HeaderSize, partitionHfs0FileCount, file, false, partition, &file_offset, &file_size)) { progressCtx.totalSize = file_size; - convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, sizeof(progressCtx.totalSizeStr) / sizeof(progressCtx.totalSizeStr[0])); + convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, MAX_ELEMENTS(progressCtx.totalSizeStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "File size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "File size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); breaks++; if (file_size <= freeSpace) @@ -3285,7 +4103,7 @@ bool dumpFileFromHfs0Partition(u32 partition, u32 file, char *filename, bool doS sprintf(fixedFilename, filename); removeIllegalCharacters(fixedFilename); - snprintf(destCopyPath, sizeof(destCopyPath) / sizeof(destCopyPath[0]), "%s%s - Partition %u (%s)", HFS0_DUMP_PATH, dumpName, partition, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, partition)); + snprintf(destCopyPath, MAX_ELEMENTS(destCopyPath), "%s%s - Partition %u (%s)", HFS0_DUMP_PATH, dumpName, partition, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, partition)); if ((strlen(destCopyPath) + 1 + strlen(filename)) < NAME_BUF_LEN) { @@ -3305,23 +4123,22 @@ bool dumpFileFromHfs0Partition(u32 partition, u32 file, char *filename, bool doS proceed = yesNoPrompt("You have already dumped this content. Do you wish to proceed anyway?"); if (!proceed) { - uiDrawString("Process canceled.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Process canceled."); } else { // Remove the prompt from the screen breaks = cur_breaks; - uiFill(0, 8 + (breaks * (font_height + (font_height / 4))) + (font_height / 8), FB_WIDTH, FB_HEIGHT - (8 + (breaks * (font_height + (font_height / 4))) + (font_height / 8)), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, 8 + STRING_Y_POS(breaks), FB_WIDTH, FB_HEIGHT - (8 + STRING_Y_POS(breaks)), BG_COLOR_RGB); } } if (proceed) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Hold %s to cancel.", NINTENDO_FONT_B); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Hold %s to cancel.", NINTENDO_FONT_B); breaks += 2; if (programAppletType != AppletType_Application && programAppletType != AppletType_SystemApplication) { - uiDrawString("Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem."); breaks += 2; } @@ -3339,21 +4156,19 @@ bool dumpFileFromHfs0Partition(u32 partition, u32 file, char *filename, bool doS timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx.now)); progressCtx.now -= progressCtx.start; - formatETAString(progressCtx.now, progressCtx.etaInfo, sizeof(progressCtx.etaInfo) / sizeof(progressCtx.etaInfo[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Process successfully completed after %s!", progressCtx.etaInfo); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + formatETAString(progressCtx.now, progressCtx.etaInfo, MAX_ELEMENTS(progressCtx.etaInfo)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Process successfully completed after %s!", progressCtx.etaInfo); } } } else { breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Destination path is too long! (%lu bytes)", strlen(destCopyPath) + 1 + strlen(filename)); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Destination path is too long! (%lu bytes)", strlen(destCopyPath) + 1 + strlen(filename)); } } else { - uiDrawString("Error: not enough free space available in the SD card.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: not enough free space available in the SD card."); } } else { - uiDrawString("Error: unable to get file details from the partition HFS0 header!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to get file details from the partition HFS0 header!"); } free(dumpName); @@ -3385,14 +4200,14 @@ bool dumpExeFsSectionData(u32 titleIndex, bool usePatch, bool doSplitting) if ((!usePatch && !titleAppCount) || (usePatch && !titlePatchCount)) { - uiDrawString("Error: invalid title count!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid title count!"); breaks += 2; return false; } if ((!usePatch && titleIndex > (titleAppCount - 1)) || (usePatch && titleIndex > (titlePatchCount - 1))) { - uiDrawString("Error: invalid title index!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid title index!"); breaks += 2; return false; } @@ -3400,7 +4215,7 @@ bool dumpExeFsSectionData(u32 titleIndex, bool usePatch, bool doSplitting) char *dumpName = generateNSPDumpName((!usePatch ? DUMP_APP_NSP : DUMP_PATCH_NSP), titleIndex); if (!dumpName) { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to generate output dump name!"); breaks += 2; return false; } @@ -3416,32 +4231,30 @@ bool dumpExeFsSectionData(u32 titleIndex, bool usePatch, bool doSplitting) // Calculate total dump size if (!calculateExeFsExtractedDataSize(&(progressCtx.totalSize))) goto out; - convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, sizeof(progressCtx.totalSizeStr) / sizeof(progressCtx.totalSizeStr[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Extracted ExeFS dump size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, MAX_ELEMENTS(progressCtx.totalSizeStr)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Extracted ExeFS dump size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); uiRefreshDisplay(); breaks++; if (progressCtx.totalSize > freeSpace) { - uiDrawString("Error: not enough free space available in the SD card.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: not enough free space available in the SD card."); goto out; } // Prepare output dump path - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s", EXEFS_DUMP_PATH, dumpName); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s", EXEFS_DUMP_PATH, dumpName); mkdir(dumpPath, 0744); // Start dump process breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dump procedure started. Hold %s to cancel.", NINTENDO_FONT_B); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Dump procedure started. Hold %s to cancel.", NINTENDO_FONT_B); uiRefreshDisplay(); breaks += 2; if (programAppletType != AppletType_Application && programAppletType != AppletType_SystemApplication) { - uiDrawString("Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem."); breaks += 2; } @@ -3459,11 +4272,11 @@ bool dumpExeFsSectionData(u32 titleIndex, bool usePatch, bool doSplitting) // Check if we're dealing with a nameless file if (!strlen(exeFsFilename)) { - uiDrawString("Error: file entry without name in ExeFS section!", 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Error: file entry without name in ExeFS section!"); break; } - snprintf(curDumpPath, sizeof(curDumpPath) / sizeof(curDumpPath[0]), "%s/%s", dumpPath, exeFsFilename); + snprintf(curDumpPath, MAX_ELEMENTS(curDumpPath), "%s/%s", dumpPath, exeFsFilename); removeIllegalCharacters(curDumpPath + strlen(dumpPath) + 1); if (exeFsContext.exefs_entries[i].file_size > FAT32_FILESIZE_LIMIT && doSplitting) @@ -3475,25 +4288,23 @@ bool dumpExeFsSectionData(u32 titleIndex, bool usePatch, bool doSplitting) outFile = fopen(curDumpPath, "wb"); if (!outFile) { - uiDrawString("Failed to open output file!", 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to open output file!"); break; } - uiFill(0, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx.line_offset - 4) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 2, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Copying \"%s\"...", exeFsFilename); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 4), FONT_COLOR_RGB, "Copying \"%s\"...", exeFsFilename); for(offset = 0; offset < exeFsContext.exefs_entries[i].file_size; offset += n, progressCtx.curOffset += n) { - uiFill(0, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx.line_offset - 2) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 2, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", strrchr(curDumpPath, '/') + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "Output file: \"%s\".", strrchr(curDumpPath, '/') + 1); uiRefreshDisplay(); - if (DUMP_BUFFER_SIZE > (exeFsContext.exefs_entries[i].file_size - offset)) n = (exeFsContext.exefs_entries[i].file_size - offset); + if (n > (exeFsContext.exefs_entries[i].file_size - offset)) n = (exeFsContext.exefs_entries[i].file_size - offset); breaks = (progressCtx.line_offset + 2); proceed = processNcaCtrSectionBlock(&(exeFsContext.ncmStorage), &(exeFsContext.ncaId), &(exeFsContext.aes_ctx), exeFsContext.exefs_data_offset + exeFsContext.exefs_entries[i].file_offset + offset, dumpBuf, n, false); @@ -3511,8 +4322,7 @@ bool dumpExeFsSectionData(u32 titleIndex, bool usePatch, bool doSplitting) write_res = fwrite(dumpBuf, 1, old_file_chunk_size, outFile); if (write_res != old_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, offset, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, offset, splitIndex, write_res); proceed = false; break; } @@ -3533,8 +4343,7 @@ bool dumpExeFsSectionData(u32 titleIndex, bool usePatch, bool doSplitting) outFile = fopen(curDumpPath, "wb"); if (!outFile) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open output file for part #%u!", splitIndex); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to open output file for part #%u!", splitIndex); proceed = false; break; } @@ -3544,8 +4353,7 @@ bool dumpExeFsSectionData(u32 titleIndex, bool usePatch, bool doSplitting) write_res = fwrite(dumpBuf + old_file_chunk_size, 1, new_file_chunk_size, outFile); if (write_res != new_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, offset + old_file_chunk_size, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, offset + old_file_chunk_size, splitIndex, write_res); proceed = false; break; } @@ -3555,12 +4363,11 @@ bool dumpExeFsSectionData(u32 titleIndex, bool usePatch, bool doSplitting) write_res = fwrite(dumpBuf, 1, n, outFile); if (write_res != n) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, offset, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, offset, write_res); if ((offset + n) > FAT32_FILESIZE_LIMIT) { - uiDrawString("You're probably using a FAT32 partition. Make sure to enable file splitting.", 8, ((progressCtx.line_offset + 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 4), FONT_COLOR_RGB, "You're probably using a FAT32 partition. Make sure to enable file splitting."); fat32_error = true; } @@ -3575,7 +4382,7 @@ bool dumpExeFsSectionData(u32 titleIndex, bool usePatch, bool doSplitting) { if (cancelProcessCheck(&progressCtx)) { - uiDrawString("Process canceled.", 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Process canceled."); proceed = false; break; } @@ -3589,16 +4396,13 @@ bool dumpExeFsSectionData(u32 titleIndex, bool usePatch, bool doSplitting) // Support empty files if (!exeFsContext.exefs_entries[i].file_size) { - uiFill(0, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx.line_offset - 2) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 2, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", strrchr(curDumpPath, '/') + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - - uiRefreshDisplay(); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "Output file: \"%s\".", strrchr(curDumpPath, '/') + 1); if (progressCtx.totalSize == exeFsContext.exefs_entries[i].file_size) progressCtx.progress = 100; - printProgressBar(&progressCtx, true, 0); + printProgressBar(&progressCtx, false, 0); } } @@ -3608,8 +4412,7 @@ bool dumpExeFsSectionData(u32 titleIndex, bool usePatch, bool doSplitting) { success = true; } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Unexpected underdump error! Wrote %lu bytes, expected %lu bytes.", progressCtx.curOffset, progressCtx.totalSize); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Unexpected underdump error! Wrote %lu bytes, expected %lu bytes.", progressCtx.curOffset, progressCtx.totalSize); } } @@ -3620,9 +4423,8 @@ bool dumpExeFsSectionData(u32 titleIndex, bool usePatch, bool doSplitting) timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx.now)); progressCtx.now -= progressCtx.start; - formatETAString(progressCtx.now, progressCtx.etaInfo, sizeof(progressCtx.etaInfo) / sizeof(progressCtx.etaInfo[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Process successfully completed after %s!", progressCtx.etaInfo); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + formatETAString(progressCtx.now, progressCtx.etaInfo, MAX_ELEMENTS(progressCtx.etaInfo)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Process successfully completed after %s!", progressCtx.etaInfo); } else { setProgressBarError(&progressCtx); if (fat32_error) breaks += 2; @@ -3643,7 +4445,7 @@ bool dumpFileFromExeFsSection(u32 titleIndex, u32 fileIndex, bool usePatch, bool { if (!exeFsContext.exefs_header.file_cnt || fileIndex > (exeFsContext.exefs_header.file_cnt - 1) || !exeFsContext.exefs_entries || !exeFsContext.exefs_str_table || exeFsContext.exefs_data_offset <= exeFsContext.exefs_offset || (!usePatch && titleIndex > (titleAppCount - 1)) || (usePatch && titleIndex > (titlePatchCount - 1))) { - uiDrawString("Error: invalid parameters to parse file entry from ExeFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to parse file entry from ExeFS section!"); breaks += 2; return false; } @@ -3668,7 +4470,7 @@ bool dumpFileFromExeFsSection(u32 titleIndex, u32 fileIndex, bool usePatch, bool // Check if we're dealing with a nameless file if (!strlen(exeFsFilename)) { - uiDrawString("Error: file entry without name in ExeFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: file entry without name in ExeFS section!"); breaks += 2; return false; } @@ -3676,13 +4478,13 @@ bool dumpFileFromExeFsSection(u32 titleIndex, u32 fileIndex, bool usePatch, bool char *dumpName = generateNSPDumpName((!usePatch ? DUMP_APP_NSP : DUMP_PATCH_NSP), titleIndex); if (!dumpName) { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to generate output dump name!"); breaks += 2; return false; } // Generate output path - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s", EXEFS_DUMP_PATH, dumpName); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s", EXEFS_DUMP_PATH, dumpName); mkdir(dumpPath, 0744); strcat(dumpPath, "/"); @@ -3697,15 +4499,14 @@ bool dumpFileFromExeFsSection(u32 titleIndex, u32 fileIndex, bool usePatch, bool } progressCtx.totalSize = exeFsContext.exefs_entries[fileIndex].file_size; - convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, sizeof(progressCtx.totalSizeStr) / sizeof(progressCtx.totalSizeStr[0])); + convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, MAX_ELEMENTS(progressCtx.totalSizeStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "File size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "File size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); breaks++; if (progressCtx.totalSize > freeSpace) { - uiDrawString("Error: not enough free space available in the SD card.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: not enough free space available in the SD card."); goto out; } @@ -3720,35 +4521,33 @@ bool dumpFileFromExeFsSection(u32 titleIndex, u32 fileIndex, bool usePatch, bool proceed = yesNoPrompt("You have already dumped this content. Do you wish to proceed anyway?"); if (!proceed) { - uiDrawString("Process canceled.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Process canceled."); removeFile = false; goto out; } else { // Remove the prompt from the screen breaks = cur_breaks; - uiFill(0, 8 + (breaks * (font_height + (font_height / 4))) + (font_height / 8), FB_WIDTH, FB_HEIGHT - (8 + (breaks * (font_height + (font_height / 4))) + (font_height / 8)), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, 8 + STRING_Y_POS(breaks), FB_WIDTH, FB_HEIGHT - (8 + STRING_Y_POS(breaks)), BG_COLOR_RGB); } } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Hold %s to cancel.", NINTENDO_FONT_B); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Hold %s to cancel.", NINTENDO_FONT_B); breaks += 2; if (programAppletType != AppletType_Application && programAppletType != AppletType_SystemApplication) { - uiDrawString("Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem."); breaks += 2; } // Start dump process - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Copying \"%s\"...", exeFsFilename); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Copying \"%s\"...", exeFsFilename); breaks += 2; outFile = fopen(dumpPath, "wb"); if (!outFile) { - uiDrawString("Failed to open output file!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to open output file!"); goto out; } @@ -3757,14 +4556,13 @@ bool dumpFileFromExeFsSection(u32 titleIndex, u32 fileIndex, bool usePatch, bool for(progressCtx.curOffset = 0; progressCtx.curOffset < progressCtx.totalSize; progressCtx.curOffset += n) { - uiFill(0, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx.line_offset - 2) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 2, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", strrchr(dumpPath, '/') + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "Output file: \"%s\".", strrchr(dumpPath, '/') + 1); uiRefreshDisplay(); - if (DUMP_BUFFER_SIZE > (progressCtx.totalSize - progressCtx.curOffset)) n = (progressCtx.totalSize - progressCtx.curOffset); + if (n > (progressCtx.totalSize - progressCtx.curOffset)) n = (progressCtx.totalSize - progressCtx.curOffset); breaks = (progressCtx.line_offset + 2); proceed = processNcaCtrSectionBlock(&(exeFsContext.ncmStorage), &(exeFsContext.ncaId), &(exeFsContext.aes_ctx), exeFsContext.exefs_data_offset + exeFsContext.exefs_entries[fileIndex].file_offset + progressCtx.curOffset, dumpBuf, n, false); @@ -3782,8 +4580,7 @@ bool dumpFileFromExeFsSection(u32 titleIndex, u32 fileIndex, bool usePatch, bool write_res = fwrite(dumpBuf, 1, old_file_chunk_size, outFile); if (write_res != old_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, progressCtx.curOffset, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, progressCtx.curOffset, splitIndex, write_res); break; } } @@ -3803,8 +4600,7 @@ bool dumpFileFromExeFsSection(u32 titleIndex, u32 fileIndex, bool usePatch, bool outFile = fopen(dumpPath, "wb"); if (!outFile) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open output file for part #%u!", splitIndex); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to open output file for part #%u!", splitIndex); break; } @@ -3813,8 +4609,7 @@ bool dumpFileFromExeFsSection(u32 titleIndex, u32 fileIndex, bool usePatch, bool write_res = fwrite(dumpBuf + old_file_chunk_size, 1, new_file_chunk_size, outFile); if (write_res != new_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, progressCtx.curOffset + old_file_chunk_size, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, progressCtx.curOffset + old_file_chunk_size, splitIndex, write_res); break; } } @@ -3823,12 +4618,11 @@ bool dumpFileFromExeFsSection(u32 titleIndex, u32 fileIndex, bool usePatch, bool write_res = fwrite(dumpBuf, 1, n, outFile); if (write_res != n) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, progressCtx.curOffset, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, progressCtx.curOffset, write_res); if ((progressCtx.curOffset + n) > FAT32_FILESIZE_LIMIT) { - uiDrawString("You're probably using a FAT32 partition. Make sure to enable file splitting.", 8, ((progressCtx.line_offset + 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 4), FONT_COLOR_RGB, "You're probably using a FAT32 partition. Make sure to enable file splitting."); fat32_error = true; } @@ -3842,7 +4636,7 @@ bool dumpFileFromExeFsSection(u32 titleIndex, u32 fileIndex, bool usePatch, bool { if (cancelProcessCheck(&progressCtx)) { - uiDrawString("Process canceled.", 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Process canceled."); break; } } @@ -3853,10 +4647,9 @@ bool dumpFileFromExeFsSection(u32 titleIndex, u32 fileIndex, bool usePatch, bool // Support empty files if (!progressCtx.totalSize) { - uiFill(0, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx.line_offset - 2) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 2, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", strrchr(dumpPath, '/') + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "Output file: \"%s\".", strrchr(dumpPath, '/') + 1); progressCtx.progress = 100; @@ -3870,9 +4663,8 @@ bool dumpFileFromExeFsSection(u32 titleIndex, u32 fileIndex, bool usePatch, bool timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx.now)); progressCtx.now -= progressCtx.start; - formatETAString(progressCtx.now, progressCtx.etaInfo, sizeof(progressCtx.etaInfo) / sizeof(progressCtx.etaInfo[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Process successfully completed after %s!", progressCtx.etaInfo); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + formatETAString(progressCtx.now, progressCtx.etaInfo, MAX_ELEMENTS(progressCtx.etaInfo)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Process successfully completed after %s!", progressCtx.etaInfo); } else { setProgressBarError(&progressCtx); } @@ -3914,7 +4706,7 @@ bool recursiveDumpRomFsFile(u32 file_offset, char *romfs_path, char *output_path { if ((!usePatch && (!romFsContext.romfs_filetable_size || file_offset > romFsContext.romfs_filetable_size || !romFsContext.romfs_file_entries)) || (usePatch && (!bktrContext.romfs_filetable_size || file_offset > bktrContext.romfs_filetable_size || !bktrContext.romfs_file_entries)) || !romfs_path || !output_path || !progressCtx) { - uiDrawString("Error: invalid parameters to parse file entry from RomFS section!", 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to parse file entry from RomFS section!"); return false; } @@ -3947,13 +4739,13 @@ bool recursiveDumpRomFsFile(u32 file_offset, char *romfs_path, char *output_path // Check if we're dealing with a nameless file if (!entry->nameLen) { - uiDrawString("Error: file entry without name in RomFS section!", 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Error: file entry without name in RomFS section!"); break; } if ((orig_romfs_path_len + 1 + entry->nameLen) >= (NAME_BUF_LEN * 2) || (orig_output_path_len + 1 + entry->nameLen) >= (NAME_BUF_LEN * 2)) { - uiDrawString("Error: RomFS section file path is too long!", 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Error: RomFS section file path is too long!"); break; } @@ -3966,10 +4758,9 @@ bool recursiveDumpRomFsFile(u32 file_offset, char *romfs_path, char *output_path removeIllegalCharacters(output_path + orig_output_path_len + 1); // Start dump process - uiFill(0, ((progressCtx->line_offset - 4) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 4, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx->line_offset - 4) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 4, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Copying \"romfs:%s\"...", romfs_path); - uiDrawString(strbuf, 8, ((progressCtx->line_offset - 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset - 4), FONT_COLOR_RGB, "Copying \"romfs:%s\"...", romfs_path); if (entry->dataSize > FAT32_FILESIZE_LIMIT && doSplitting) { @@ -3980,7 +4771,7 @@ bool recursiveDumpRomFsFile(u32 file_offset, char *romfs_path, char *output_path outFile = fopen(output_path, "wb"); if (!outFile) { - uiDrawString("Failed to open output file!", 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_RGB, "Failed to open output file!"); break; } @@ -3989,14 +4780,13 @@ bool recursiveDumpRomFsFile(u32 file_offset, char *romfs_path, char *output_path for(off = 0; off < entry->dataSize; off += n, progressCtx->curOffset += n) { - uiFill(0, ((progressCtx->line_offset - 2) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx->line_offset - 2) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 2, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", strrchr(output_path, '/') + 1); - uiDrawString(strbuf, 8, ((progressCtx->line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset - 2), FONT_COLOR_RGB, "Output file: \"%s\".", strrchr(output_path, '/') + 1); uiRefreshDisplay(); - if (DUMP_BUFFER_SIZE > (entry->dataSize - off)) n = (entry->dataSize - off); + if (n > (entry->dataSize - off)) n = (entry->dataSize - off); breaks = (progressCtx->line_offset + 2); @@ -4021,8 +4811,7 @@ bool recursiveDumpRomFsFile(u32 file_offset, char *romfs_path, char *output_path write_res = fwrite(dumpBuf, 1, old_file_chunk_size, outFile); if (write_res != old_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, off, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, off, splitIndex, write_res); proceed = false; break; } @@ -4043,8 +4832,7 @@ bool recursiveDumpRomFsFile(u32 file_offset, char *romfs_path, char *output_path outFile = fopen(output_path, "wb"); if (!outFile) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open output file for part #%u!", splitIndex); - uiDrawString(strbuf, 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to open output file for part #%u!", splitIndex); proceed = false; break; } @@ -4054,8 +4842,7 @@ bool recursiveDumpRomFsFile(u32 file_offset, char *romfs_path, char *output_path write_res = fwrite(dumpBuf + old_file_chunk_size, 1, new_file_chunk_size, outFile); if (write_res != new_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, off + old_file_chunk_size, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, off + old_file_chunk_size, splitIndex, write_res); proceed = false; break; } @@ -4065,12 +4852,11 @@ bool recursiveDumpRomFsFile(u32 file_offset, char *romfs_path, char *output_path write_res = fwrite(dumpBuf, 1, n, outFile); if (write_res != n) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, off, write_res); - uiDrawString(strbuf, 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, off, write_res); if ((off + n) > FAT32_FILESIZE_LIMIT) { - uiDrawString("You're probably using a FAT32 partition. Make sure to enable file splitting.", 8, ((progressCtx->line_offset + 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 4), FONT_COLOR_RGB, "You're probably using a FAT32 partition. Make sure to enable file splitting."); fat32_error = true; } @@ -4085,7 +4871,7 @@ bool recursiveDumpRomFsFile(u32 file_offset, char *romfs_path, char *output_path { if (cancelProcessCheck(progressCtx)) { - uiDrawString("Process canceled.", 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Process canceled."); proceed = false; break; } @@ -4103,10 +4889,9 @@ bool recursiveDumpRomFsFile(u32 file_offset, char *romfs_path, char *output_path // Support empty files if (!entry->dataSize) { - uiFill(0, ((progressCtx->line_offset - 2) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx->line_offset - 2) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 2, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", strrchr(output_path, '/') + 1); - uiDrawString(strbuf, 8, ((progressCtx->line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset - 2), FONT_COLOR_RGB, "Output file: \"%s\".", strrchr(output_path, '/') + 1); if (progressCtx->totalSize == entry->dataSize) progressCtx->progress = 100; @@ -4133,7 +4918,7 @@ bool recursiveDumpRomFsDir(u32 dir_offset, char *romfs_path, char *output_path, { if ((!usePatch && (!romFsContext.romfs_dirtable_size || dir_offset > romFsContext.romfs_dirtable_size || !romFsContext.romfs_dir_entries || !romFsContext.romfs_filetable_size || !romFsContext.romfs_file_entries)) || (usePatch && (!bktrContext.romfs_dirtable_size || dir_offset > bktrContext.romfs_dirtable_size || !bktrContext.romfs_dir_entries || !bktrContext.romfs_filetable_size || !bktrContext.romfs_file_entries)) || !romfs_path || !output_path || !progressCtx) { - uiDrawString("Error: invalid parameters to parse directory entry from RomFS section!", 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to parse directory entry from RomFS section!"); return false; } @@ -4145,13 +4930,13 @@ bool recursiveDumpRomFsDir(u32 dir_offset, char *romfs_path, char *output_path, // Check if we're dealing with a nameless directory that's not the root directory if (!entry->nameLen && dir_offset > 0) { - uiDrawString("Error: directory entry without name in RomFS section!", 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Error: directory entry without name in RomFS section!"); return false; } if ((orig_romfs_path_len + 1 + entry->nameLen) >= (NAME_BUF_LEN * 2) || (orig_output_path_len + 1 + entry->nameLen) >= (NAME_BUF_LEN * 2)) { - uiDrawString("Error: RomFS section directory path is too long!", 8, ((progressCtx->line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx->line_offset + 2), FONT_COLOR_ERROR_RGB, "Error: RomFS section directory path is too long!"); return false; } @@ -4209,14 +4994,14 @@ bool dumpRomFsSectionData(u32 titleIndex, selectedRomFsType curRomFsType, bool d if ((curRomFsType == ROMFS_TYPE_APP && !titleAppCount) || (curRomFsType == ROMFS_TYPE_PATCH && !titlePatchCount) || (curRomFsType == ROMFS_TYPE_ADDON && !titleAddOnCount)) { - uiDrawString("Error: invalid title count!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid title count!"); breaks += 2; return false; } if ((curRomFsType == ROMFS_TYPE_APP && titleIndex > (titleAppCount - 1)) || (curRomFsType == ROMFS_TYPE_PATCH && titleIndex > (titlePatchCount - 1)) || (curRomFsType == ROMFS_TYPE_ADDON && titleIndex > (titleAddOnCount - 1))) { - uiDrawString("Error: invalid title index!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid title index!"); breaks += 2; return false; } @@ -4224,7 +5009,7 @@ bool dumpRomFsSectionData(u32 titleIndex, selectedRomFsType curRomFsType, bool d char *dumpName = generateNSPDumpName((curRomFsType == ROMFS_TYPE_APP ? DUMP_APP_NSP : (curRomFsType == ROMFS_TYPE_PATCH ? DUMP_PATCH_NSP : DUMP_ADDON_NSP)), titleIndex); if (!dumpName) { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to generate output dump name!"); breaks += 2; return false; } @@ -4240,32 +5025,30 @@ bool dumpRomFsSectionData(u32 titleIndex, selectedRomFsType curRomFsType, bool d // Calculate total dump size if (!calculateRomFsFullExtractedSize((curRomFsType == ROMFS_TYPE_PATCH), &(progressCtx.totalSize))) goto out; - convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, sizeof(progressCtx.totalSizeStr) / sizeof(progressCtx.totalSizeStr[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Extracted RomFS dump size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, MAX_ELEMENTS(progressCtx.totalSizeStr)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Extracted RomFS dump size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); uiRefreshDisplay(); breaks++; if (progressCtx.totalSize > freeSpace) { - uiDrawString("Error: not enough free space available in the SD card.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: not enough free space available in the SD card."); goto out; } // Prepare output dump path - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s", ROMFS_DUMP_PATH, dumpName); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s", ROMFS_DUMP_PATH, dumpName); mkdir(dumpPath, 0744); // Start dump process breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dump procedure started. Hold %s to cancel.", NINTENDO_FONT_B); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Dump procedure started. Hold %s to cancel.", NINTENDO_FONT_B); uiRefreshDisplay(); breaks += 2; if (programAppletType != AppletType_Application && programAppletType != AppletType_SystemApplication) { - uiDrawString("Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem."); breaks += 2; } @@ -4281,9 +5064,8 @@ bool dumpRomFsSectionData(u32 titleIndex, selectedRomFsType curRomFsType, bool d timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx.now)); progressCtx.now -= progressCtx.start; - formatETAString(progressCtx.now, progressCtx.etaInfo, sizeof(progressCtx.etaInfo) / sizeof(progressCtx.etaInfo[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Process successfully completed after %s!", progressCtx.etaInfo); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + formatETAString(progressCtx.now, progressCtx.etaInfo, MAX_ELEMENTS(progressCtx.etaInfo)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Process successfully completed after %s!", progressCtx.etaInfo); } else { setProgressBarError(&progressCtx); removeDirectoryWithVerbose(dumpPath, "Deleting output directory. Please wait..."); @@ -4303,9 +5085,9 @@ out: bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType curRomFsType, bool doSplitting) { - if (!romFsContext.romfs_filetable_size || file_offset > romFsContext.romfs_filetable_size || !romFsContext.romfs_file_entries || (curRomFsType == ROMFS_TYPE_APP && titleIndex > (titleAppCount - 1)) || (curRomFsType == ROMFS_TYPE_PATCH && titleIndex > (titlePatchCount - 1)) || (curRomFsType == ROMFS_TYPE_ADDON && titleIndex > (titleAddOnCount - 1))) + if ((curRomFsType != ROMFS_TYPE_PATCH && (!romFsContext.romfs_filetable_size || file_offset > romFsContext.romfs_filetable_size || !romFsContext.romfs_file_entries)) || (curRomFsType == ROMFS_TYPE_PATCH && (!bktrContext.romfs_filetable_size || file_offset > bktrContext.romfs_filetable_size || !bktrContext.romfs_file_entries)) || (curRomFsType == ROMFS_TYPE_APP && titleIndex > (titleAppCount - 1)) || (curRomFsType == ROMFS_TYPE_PATCH && titleIndex > (titlePatchCount - 1)) || (curRomFsType == ROMFS_TYPE_ADDON && titleIndex > (titleAddOnCount - 1))) { - uiDrawString("Error: invalid parameters to parse file entry from RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to parse file entry from RomFS section!"); breaks += 2; return false; } @@ -4330,7 +5112,7 @@ bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType // Check if we're dealing with a nameless file if (!entry->nameLen) { - uiDrawString("Error: file entry without name in RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: file entry without name in RomFS section!"); breaks += 2; return false; } @@ -4338,13 +5120,13 @@ bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType char *dumpName = generateNSPDumpName((curRomFsType == ROMFS_TYPE_APP ? DUMP_APP_NSP : (curRomFsType == ROMFS_TYPE_PATCH ? DUMP_PATCH_NSP : DUMP_ADDON_NSP)), titleIndex); if (!dumpName) { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to generate output dump name!"); breaks += 2; return false; } // Generate output path - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s", ROMFS_DUMP_PATH, dumpName); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s", ROMFS_DUMP_PATH, dumpName); mkdir(dumpPath, 0744); // Create subdirectories @@ -4387,15 +5169,14 @@ bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType removeIllegalCharacters(dumpPath + cur_len); progressCtx.totalSize = entry->dataSize; - convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, sizeof(progressCtx.totalSizeStr) / sizeof(progressCtx.totalSizeStr[0])); + convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, MAX_ELEMENTS(progressCtx.totalSizeStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "File size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "File size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); breaks++; if (progressCtx.totalSize > freeSpace) { - uiDrawString("Error: not enough free space available in the SD card.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: not enough free space available in the SD card."); goto out; } @@ -4416,40 +5197,39 @@ bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType proceed = yesNoPrompt("You have already dumped this content. Do you wish to proceed anyway?"); if (!proceed) { - uiDrawString("Process canceled.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Process canceled."); removeFile = false; goto out; } else { // Remove the prompt from the screen breaks = cur_breaks; - uiFill(0, 8 + (breaks * (font_height + (font_height / 4))) + (font_height / 8), FB_WIDTH, FB_HEIGHT - (8 + (breaks * (font_height + (font_height / 4))) + (font_height / 8)), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, 8 + STRING_Y_POS(breaks), FB_WIDTH, FB_HEIGHT - (8 + STRING_Y_POS(breaks)), BG_COLOR_RGB); } } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Hold %s to cancel.", NINTENDO_FONT_B); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Hold %s to cancel.", NINTENDO_FONT_B); breaks += 2; if (programAppletType != AppletType_Application && programAppletType != AppletType_SystemApplication) { - uiDrawString("Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem."); breaks += 2; } // Start dump process if (strlen(curRomFsPath) > 1) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Copying \"romfs:%s/%.*s\"...", curRomFsPath, entry->nameLen, entry->name); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Copying \"romfs:%s/%.*s\"...", curRomFsPath, entry->nameLen, entry->name); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Copying \"romfs:/%.*s\"...", entry->nameLen, entry->name); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Copying \"romfs:/%.*s\"...", entry->nameLen, entry->name); } - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + breaks += 2; outFile = fopen(dumpPath, "wb"); if (!outFile) { - uiDrawString("Failed to open output file!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to open output file!"); goto out; } @@ -4458,14 +5238,13 @@ bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType for(progressCtx.curOffset = 0; progressCtx.curOffset < progressCtx.totalSize; progressCtx.curOffset += n) { - uiFill(0, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx.line_offset - 2) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 2, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", strrchr(dumpPath, '/') + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "Output file: \"%s\".", strrchr(dumpPath, '/') + 1); uiRefreshDisplay(); - if (DUMP_BUFFER_SIZE > (progressCtx.totalSize - progressCtx.curOffset)) n = (progressCtx.totalSize - progressCtx.curOffset); + if (n > (progressCtx.totalSize - progressCtx.curOffset)) n = (progressCtx.totalSize - progressCtx.curOffset); breaks = (progressCtx.line_offset + 2); @@ -4490,8 +5269,7 @@ bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType write_res = fwrite(dumpBuf, 1, old_file_chunk_size, outFile); if (write_res != old_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, progressCtx.curOffset, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", old_file_chunk_size, progressCtx.curOffset, splitIndex, write_res); break; } } @@ -4511,8 +5289,7 @@ bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType outFile = fopen(dumpPath, "wb"); if (!outFile) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open output file for part #%u!", splitIndex); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to open output file for part #%u!", splitIndex); break; } @@ -4521,8 +5298,7 @@ bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType write_res = fwrite(dumpBuf + old_file_chunk_size, 1, new_file_chunk_size, outFile); if (write_res != new_file_chunk_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, progressCtx.curOffset + old_file_chunk_size, splitIndex, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX to part #%02u! (wrote %lu bytes)", new_file_chunk_size, progressCtx.curOffset + old_file_chunk_size, splitIndex, write_res); break; } } @@ -4531,12 +5307,11 @@ bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType write_res = fwrite(dumpBuf, 1, n, outFile); if (write_res != n) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, progressCtx.curOffset, write_res); - uiDrawString(strbuf, 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Failed to write %lu bytes chunk from offset 0x%016lX! (wrote %lu bytes)", n, progressCtx.curOffset, write_res); if ((progressCtx.curOffset + n) > FAT32_FILESIZE_LIMIT) { - uiDrawString("You're probably using a FAT32 partition. Make sure to enable file splitting.", 8, ((progressCtx.line_offset + 4) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 4), FONT_COLOR_RGB, "You're probably using a FAT32 partition. Make sure to enable file splitting."); fat32_error = true; } @@ -4550,7 +5325,7 @@ bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType { if (cancelProcessCheck(&progressCtx)) { - uiDrawString("Process canceled.", 8, ((progressCtx.line_offset + 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset + 2), FONT_COLOR_ERROR_RGB, "Process canceled."); break; } } @@ -4561,10 +5336,9 @@ bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType // Support empty files if (!progressCtx.totalSize) { - uiFill(0, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 4)) * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, ((progressCtx.line_offset - 2) * LINE_HEIGHT) + 8, FB_WIDTH, LINE_HEIGHT * 2, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output file: \"%s\".", strrchr(dumpPath, '/') + 1); - uiDrawString(strbuf, 8, ((progressCtx.line_offset - 2) * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(progressCtx.line_offset - 2), FONT_COLOR_RGB, "Output file: \"%s\".", strrchr(dumpPath, '/') + 1); progressCtx.progress = 100; @@ -4578,9 +5352,8 @@ bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx.now)); progressCtx.now -= progressCtx.start; - formatETAString(progressCtx.now, progressCtx.etaInfo, sizeof(progressCtx.etaInfo) / sizeof(progressCtx.etaInfo[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Process successfully completed after %s!", progressCtx.etaInfo); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + formatETAString(progressCtx.now, progressCtx.etaInfo, MAX_ELEMENTS(progressCtx.etaInfo)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Process successfully completed after %s!", progressCtx.etaInfo); } else { setProgressBarError(&progressCtx); } @@ -4629,14 +5402,14 @@ bool dumpCurrentDirFromRomFsSection(u32 titleIndex, selectedRomFsType curRomFsTy if ((curRomFsType == ROMFS_TYPE_APP && !titleAppCount) || (curRomFsType == ROMFS_TYPE_PATCH && !titlePatchCount) || (curRomFsType == ROMFS_TYPE_ADDON && !titleAddOnCount)) { - uiDrawString("Error: invalid title count!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid title count!"); breaks += 2; return false; } if ((curRomFsType == ROMFS_TYPE_APP && titleIndex > (titleAppCount - 1)) || (curRomFsType == ROMFS_TYPE_PATCH && titleIndex > (titlePatchCount - 1)) || (curRomFsType == ROMFS_TYPE_ADDON && titleIndex > (titleAddOnCount - 1))) { - uiDrawString("Error: invalid title index!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid title index!"); breaks += 2; return false; } @@ -4644,7 +5417,7 @@ bool dumpCurrentDirFromRomFsSection(u32 titleIndex, selectedRomFsType curRomFsTy char *dumpName = generateNSPDumpName((curRomFsType == ROMFS_TYPE_APP ? DUMP_APP_NSP : (curRomFsType == ROMFS_TYPE_PATCH ? DUMP_PATCH_NSP : DUMP_ADDON_NSP)), titleIndex); if (!dumpName) { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to generate output dump name!"); breaks += 2; return false; } @@ -4652,15 +5425,14 @@ bool dumpCurrentDirFromRomFsSection(u32 titleIndex, selectedRomFsType curRomFsTy // Calculate total dump size if (!calculateRomFsExtractedDirSize(curRomFsDirOffset, (curRomFsType == ROMFS_TYPE_PATCH), &(progressCtx.totalSize))) goto out; - convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, sizeof(progressCtx.totalSizeStr) / sizeof(progressCtx.totalSizeStr[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Extracted RomFS directory size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, MAX_ELEMENTS(progressCtx.totalSizeStr)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Extracted RomFS directory size: %s (%lu bytes).", progressCtx.totalSizeStr, progressCtx.totalSize); uiRefreshDisplay(); breaks++; if (progressCtx.totalSize > freeSpace) { - uiDrawString("Error: not enough free space available in the SD card.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: not enough free space available in the SD card."); goto out; } @@ -4668,13 +5440,13 @@ bool dumpCurrentDirFromRomFsSection(u32 titleIndex, selectedRomFsType curRomFsTy { // Copy the whole current path and remove the last element (current directory) from it // It will be re-added later - snprintf(romFsPath, sizeof(romFsPath) / sizeof(romFsPath[0]), curRomFsPath); + snprintf(romFsPath, MAX_ELEMENTS(romFsPath), curRomFsPath); char *slash = strrchr(romFsPath, '/'); if (slash) *slash = '\0'; } // Prepare output dump path - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s", ROMFS_DUMP_PATH, dumpName); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s", ROMFS_DUMP_PATH, dumpName); mkdir(dumpPath, 0744); // Create subdirectories @@ -4712,14 +5484,13 @@ bool dumpCurrentDirFromRomFsSection(u32 titleIndex, selectedRomFsType curRomFsTy // Start dump process breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dump procedure started. Hold %s to cancel.", NINTENDO_FONT_B); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Dump procedure started. Hold %s to cancel.", NINTENDO_FONT_B); uiRefreshDisplay(); breaks += 2; if (programAppletType != AppletType_Application && programAppletType != AppletType_SystemApplication) { - uiDrawString("Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem."); breaks += 2; } @@ -4735,9 +5506,8 @@ bool dumpCurrentDirFromRomFsSection(u32 titleIndex, selectedRomFsType curRomFsTy timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx.now)); progressCtx.now -= progressCtx.start; - formatETAString(progressCtx.now, progressCtx.etaInfo, sizeof(progressCtx.etaInfo) / sizeof(progressCtx.etaInfo[0])); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Process successfully completed after %s!", progressCtx.etaInfo); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + formatETAString(progressCtx.now, progressCtx.etaInfo, MAX_ELEMENTS(progressCtx.etaInfo)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Process successfully completed after %s!", progressCtx.etaInfo); } else { setProgressBarError(&progressCtx); removeDirectoryWithVerbose(dumpPath, "Deleting output directory. Please wait..."); @@ -4767,7 +5537,7 @@ bool dumpGameCardCertificate() char *dumpName = generateFullDumpName(); if (!dumpName) { - uiDrawString("Error: unable to generate output dump name!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to generate output dump name!"); breaks += 2; return false; } @@ -4776,14 +5546,12 @@ bool dumpGameCardCertificate() if (R_SUCCEEDED(result = fsDeviceOperatorGetGameCardHandle(&fsOperatorInstance, &handle))) { - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle succeeded: 0x%08X", handle.value); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "GetGameCardHandle succeeded: 0x%08X", handle.value); breaks++;*/ if (R_SUCCEEDED(result = fsOpenGameCardStorage(&gameCardStorage, &handle, 0))) { - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage succeeded: 0x%08X", handle.value); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "OpenGameCardStorage succeeded: 0x%08X", handle.value); breaks++;*/ if (CERT_SIZE <= freeSpace) @@ -4793,7 +5561,7 @@ bool dumpGameCardCertificate() // Calculate CRC32 crc32(dumpBuf, CERT_SIZE, &crc); - snprintf(filename, sizeof(filename) / sizeof(filename[0]), "%s%s - Certificate (%08X).bin", CERT_DUMP_PATH, dumpName, crc); + snprintf(filename, MAX_ELEMENTS(filename), "%s%s - Certificate (%08X).bin", CERT_DUMP_PATH, dumpName, crc); // Check if the dump already exists if (checkIfFileExists(filename)) @@ -4804,23 +5572,22 @@ bool dumpGameCardCertificate() proceed = yesNoPrompt("You have already dumped this content. Do you wish to proceed anyway?"); if (!proceed) { - uiDrawString("Process canceled.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Process canceled."); } else { // Remove the prompt from the screen breaks = cur_breaks; - uiFill(0, 8 + (breaks * (font_height + (font_height / 4))) + (font_height / 8), FB_WIDTH, FB_HEIGHT - (8 + (breaks * (font_height + (font_height / 4))) + (font_height / 8)), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, 8 + STRING_Y_POS(breaks), FB_WIDTH, FB_HEIGHT - (8 + STRING_Y_POS(breaks)), BG_COLOR_RGB); } } if (proceed) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dumping gamecard certificate to \"%s\"...", strrchr(filename, '/' ) + 1); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Dumping gamecard certificate to \"%s\"...", strrchr(filename, '/' ) + 1); breaks += 2; if (programAppletType != AppletType_Application && programAppletType != AppletType_SystemApplication) { - uiDrawString("Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem."); breaks += 2; } @@ -4833,35 +5600,30 @@ bool dumpGameCardCertificate() if (write_res == CERT_SIZE) { success = true; - uiDrawString("Process successfully completed!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Process successfully completed!"); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to write %u bytes certificate data! (wrote %lu bytes)", CERT_SIZE, write_res); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to write %u bytes certificate data! (wrote %lu bytes)", CERT_SIZE, write_res); } fclose(outFile); if (!success) unlink(filename); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open output file \"%s\"!", filename); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to open output file \"%s\"!", filename); } } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "StorageRead failed (0x%08X) at offset 0x%08X", result, CERT_OFFSET); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "StorageRead failed (0x%08X) at offset 0x%08X", result, CERT_OFFSET); } } else { - uiDrawString("Error: not enough free space available in the SD card.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: not enough free space available in the SD card."); } fsStorageClose(&gameCardStorage); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "OpenGameCardStorage failed! (0x%08X)", result); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "GetGameCardHandle failed! (0x%08X)", result); } breaks += 2; diff --git a/source/dumper.h b/source/dumper.h index 8b6c67f..c9fed5b 100644 --- a/source/dumper.h +++ b/source/dumper.h @@ -8,29 +8,58 @@ #define ISTORAGE_PARTITION_CNT 2 -#define FAT32_FILESIZE_LIMIT (u64)0xFFFFFFFF // 4 GiB - 1 (4294967295 bytes) +#define FAT32_FILESIZE_LIMIT (u64)0xFFFFFFFF // 4 GiB - 1 (4294967295 bytes) -#define SPLIT_FILE_XCI_PART_SIZE (u64)0xFFFF8000 // 4 GiB - 0x8000 (4294934528 bytes) (based on XCI-Cutter) -#define SPLIT_FILE_NSP_PART_SIZE (u64)0xFFFF0000 // 4 GiB - 0x10000 (4294901760 bytes) (based on splitNSP.py) +#define SPLIT_FILE_XCI_PART_SIZE (u64)0xFFFF8000 // 4 GiB - 0x8000 (4294934528 bytes) (based on XCI-Cutter) +#define SPLIT_FILE_NSP_PART_SIZE (u64)0xFFFF0000 // 4 GiB - 0x10000 (4294901760 bytes) (based on splitNSP.py) #define SPLIT_FILE_GENERIC_PART_SIZE SPLIT_FILE_NSP_PART_SIZE +#define SPLIT_FILE_SEQUENTIAL_SIZE (u64)0x40000000 // 1 GiB (used for sequential dumps when there's not enough storage space available) #define CERT_OFFSET 0x7000 #define CERT_SIZE 0x200 #define SMOOTHING_FACTOR (double)0.1 -#define CANCEL_BTN_SEC_HOLD 2 // The cancel button must be held for at least CANCEL_BTN_SEC_HOLD seconds to cancel an ongoing operation +#define CANCEL_BTN_SEC_HOLD 2 // The cancel button must be held for at least CANCEL_BTN_SEC_HOLD seconds to cancel an ongoing operation -typedef enum { - BATCH_SOURCE_ALL = 0, - BATCH_SOURCE_SDCARD, - BATCH_SOURCE_EMMC -} batchModeSourceStorage; +#define DUMP_NSP_CRC_WAIT 5 // The user must wait for at least DUMP_NSP_CRC_WAIT seconds before the CRC32 checksum calculation process starts after the NSP dump process is finished + +typedef struct { + bool keepCert; // Original value for the "Keep certificate" option. Overrides the selected setting in the current session + bool trimDump; // Original value for the "Trim output dump" option. Overrides the selected setting in the current session + bool calcCrc; // "CRC32 checksum calculation + dump verification" option + u8 partNumber; // Next part number + u32 partitionIndex; // IStorage partition index + u64 partitionOffset; // IStorage partition offset + u32 certCrc; // CRC32 checksum accumulator (XCI with cert). Only used if calcCrc == true and keepCert == true + u32 certlessCrc; // CRC32 checksum accumulator (certless XCI). Only used if calcCrc == true +} PACKED sequentialXciCtx; + +typedef struct { + FsStorageId storageId; // Source storage from which the data is dumped + bool removeConsoleData; // Original value for the "Remove console specific data" option. Overrides the selected setting in the current session + bool tiklessDump; // Original value for the "Generate ticket-less dump" option. Overrides the selected setting in the current session. Ignored if removeConsoleData == false + u8 partNumber; // Next part number + u32 nspFileCount; // PFS0 file count + u32 ncaCount; // NCA count + u32 fileIndex; // Current PFS0 file entry index + u64 fileOffset; // Current PFS0 file entry offset + Sha256Context hashCtx; // Current NCA SHA-256 checksum context. Only used when dealing with the same NCA between different parts + u8 programNcaHeaderMod[NCA_FULL_HEADER_LENGTH]; // Modified + reencrypted Program NCA header. Only needed if the NPDM signature in the Program NCA header is replaced (it uses cryptographically secure random numbers). The RSA public key used in the ACID section from the main.npdm file is constant, so we don't need to keep track of that +} PACKED sequentialNspCtx; + +typedef struct { + bool enabled; + nspDumpType titleType; + u32 titleIndex; + char nspFilename[NAME_BUF_LEN]; + char truncatedNspFilename[NAME_BUF_LEN]; +} batchEntry; void workaroundPartitionZeroAccess(); -bool dumpCartridgeImage(bool isFat32, bool setXciArchiveBit, bool keepCert, bool trimDump, bool calcCrc); -bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleIndex, bool isFat32, bool calcCrc, bool removeConsoleData, bool tiklessDump, bool batch); -bool dumpNintendoSubmissionPackageBatch(bool dumpAppTitles, bool dumpPatchTitles, bool dumpAddOnTitles, bool isFat32, bool removeConsoleData, bool tiklessDump, bool skipDumpedTitles, batchModeSourceStorage batchModeSrc); +bool dumpCartridgeImage(xciOptions *xciDumpCfg); +bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleIndex, nspOptions *nspDumpCfg, bool batch); +bool dumpNintendoSubmissionPackageBatch(batchOptions *batchDumpCfg); bool dumpRawHfs0Partition(u32 partition, bool doSplitting); bool dumpHfs0PartitionData(u32 partition, bool doSplitting); bool dumpFileFromHfs0Partition(u32 partition, u32 file, char *filename, bool doSplitting); diff --git a/source/keys.c b/source/keys.c index f60dcfa..0f1324c 100644 --- a/source/keys.c +++ b/source/keys.c @@ -15,7 +15,7 @@ extern int breaks; extern int font_height; -extern char strbuf[NAME_BUF_LEN * 4]; +extern char strbuf[NAME_BUF_LEN]; /* Statically allocated variables */ @@ -117,7 +117,7 @@ bool retrieveProcessMemory(keyLocation *location) { if (!location || !location->titleID || !location->mask) { - uiDrawString("Error: invalid parameters to retrieve process memory.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to retrieve process memory."); return false; } @@ -134,22 +134,19 @@ bool retrieveProcessMemory(keyLocation *location) if (R_FAILED(result = pmdmntGetTitlePid(&pid, location->titleID))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: pmdmntGetTitlePid failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: pmdmntGetTitlePid failed! (0x%08X)", result); return false; } if (R_FAILED(result = svcDebugActiveProcess(&debug_handle, pid))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: svcDebugActiveProcess failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: svcDebugActiveProcess failed! (0x%08X)", result); return false; } if (R_FAILED(result = svcGetDebugEvent((u8*)&d, debug_handle))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: svcGetDebugEvent failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: svcGetDebugEvent failed! (0x%08X)", result); return false; } } else { @@ -159,8 +156,7 @@ bool retrieveProcessMemory(keyLocation *location) if (R_FAILED(result = svcGetProcessList(&num_processes, pids, 300))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: svcGetProcessList failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: svcGetProcessList failed! (0x%08X)", result); return false; } @@ -175,8 +171,7 @@ bool retrieveProcessMemory(keyLocation *location) if (i == (num_processes - 1)) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to retrieve debug handle for process with Title ID %016lX!", location->titleID); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to retrieve debug handle for process with Title ID %016lX!", location->titleID); if (debug_handle) svcCloseHandle(debug_handle); return false; } @@ -198,8 +193,7 @@ bool retrieveProcessMemory(keyLocation *location) { if (R_FAILED(result = svcQueryDebugProcessMemory(&mem_info, &page_info, debug_handle, addr))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: svcQueryDebugProcessMemory failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: svcQueryDebugProcessMemory failed! (0x%08X)", result); success = false; break; } @@ -222,8 +216,7 @@ bool retrieveProcessMemory(keyLocation *location) { if (R_FAILED(result = svcQueryDebugProcessMemory(&mem_info, &page_info, debug_handle, addr))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: svcQueryDebugProcessMemory failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: svcQueryDebugProcessMemory failed! (0x%08X)", result); success = false; break; } @@ -235,8 +228,7 @@ bool retrieveProcessMemory(keyLocation *location) dataTmp = realloc(location->data, location->dataSize + mem_info.size); if (!dataTmp) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to resize key location data buffer to %lu bytes.", location->dataSize + mem_info.size); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to resize key location data buffer to %lu bytes.", location->dataSize + mem_info.size); success = false; break; } @@ -248,8 +240,7 @@ bool retrieveProcessMemory(keyLocation *location) if (R_FAILED(result = svcReadDebugProcessMemory(location->data + location->dataSize, debug_handle, mem_info.addr, mem_info.size))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: svcReadDebugProcessMemory failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: svcReadDebugProcessMemory failed! (0x%08X)", result); success = false; break; } @@ -275,12 +266,12 @@ bool findKeyInProcessMemory(const keyLocation *location, const keyInfo *findKey, { if (!location || !location->data || !location->dataSize || !findKey || !strlen(findKey->name) || !findKey->size) { - uiDrawString("Error: invalid parameters to locate key in process memory.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to locate key in process memory."); return false; } u64 i; - u8 temp_hash[SHA256_HASH_LENGTH]; + u8 temp_hash[SHA256_HASH_SIZE]; bool found = false; // Hash every key-length-sized byte chunk in data until it matches a key hash @@ -290,7 +281,7 @@ bool findKeyInProcessMemory(const keyLocation *location, const keyInfo *findKey, sha256CalculateHash(temp_hash, location->data + i, findKey->size); - if (!memcmp(temp_hash, findKey->hash, SHA256_HASH_LENGTH)) + if (!memcmp(temp_hash, findKey->hash, SHA256_HASH_SIZE)) { // Jackpot memcpy(out, location->data + i, findKey->size); @@ -299,11 +290,7 @@ bool findKeyInProcessMemory(const keyLocation *location, const keyInfo *findKey, } } - if (!found) - { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to locate key \"%s\" in process memory!", findKey->name); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - } + if (!found) uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to locate key \"%s\" in process memory!", findKey->name); return found; } @@ -312,7 +299,7 @@ bool findFSRodataKeys(keyLocation *location) { if (!location || location->titleID != FS_TID || location->mask != SEG_RODATA || !location->data || !location->dataSize) { - uiDrawString("Error: invalid parameters to locate keys in FS RODATA segment.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to locate keys in FS RODATA segment."); return false; } @@ -352,15 +339,13 @@ bool loadMemoryKeys() // Derive NCA header key if (R_FAILED(result = splCryptoInitialize())) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to initialize the spl:crypto service! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to initialize the spl:crypto service! (0x%08X)", result); return false; } if (R_FAILED(result = splCryptoGenerateAesKek(nca_keyset.header_kek_source, 0, 0, nca_keyset.header_kek))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: splCryptoGenerateAesKek(header_kek_source) failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splCryptoGenerateAesKek(header_kek_source) failed! (0x%08X)", result); splCryptoExit(); return false; } @@ -369,16 +354,14 @@ bool loadMemoryKeys() if (R_FAILED(result = splCryptoGenerateAesKey(nca_keyset.header_kek, nca_keyset.header_key_source + 0x00, nca_keyset.header_key + 0x00))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: splCryptoGenerateAesKey(header_key_source + 0x00) failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splCryptoGenerateAesKey(header_key_source + 0x00) failed! (0x%08X)", result); splCryptoExit(); return false; } if (R_FAILED(result = splCryptoGenerateAesKey(nca_keyset.header_kek, nca_keyset.header_key_source + 0x10, nca_keyset.header_key + 0x10))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: splCryptoGenerateAesKey(header_key_source + 0x10) failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splCryptoGenerateAesKey(header_key_source + 0x10) failed! (0x%08X)", result); splCryptoExit(); return false; } @@ -396,7 +379,7 @@ bool decryptNcaKeyArea(nca_header_t *dec_nca_header, u8 *out) { if (!dec_nca_header || dec_nca_header->kaek_ind > 2 || !out) { - uiDrawString("Error: invalid parameters to decrypt NCA key area.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to decrypt NCA key area."); return false; } @@ -408,7 +391,7 @@ bool decryptNcaKeyArea(nca_header_t *dec_nca_header, u8 *out) u8 crypto_type = (dec_nca_header->crypto_type2 > dec_nca_header->crypto_type ? dec_nca_header->crypto_type2 : dec_nca_header->crypto_type); if (crypto_type > 0x20) { - uiDrawString("Error: invalid NCA keyblob index.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NCA keyblob index."); return false; } @@ -416,15 +399,13 @@ bool decryptNcaKeyArea(nca_header_t *dec_nca_header, u8 *out) if (R_FAILED(result = splCryptoInitialize())) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to initialize the spl:crypto service! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to initialize the spl:crypto service! (0x%08X)", result); return false; } if (R_FAILED(result = splCryptoGenerateAesKek(kek_source, crypto_type, 0, tmp_kek))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: splCryptoGenerateAesKek(kek_source) failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splCryptoGenerateAesKek(kek_source) failed! (0x%08X)", result); splCryptoExit(); return false; } @@ -436,8 +417,7 @@ bool decryptNcaKeyArea(nca_header_t *dec_nca_header, u8 *out) { if (R_FAILED(result = splCryptoGenerateAesKey(tmp_kek, dec_nca_header->nca_keys[i], decrypted_nca_keys[i]))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: splCryptoGenerateAesKey(nca_kaek_%02u) failed! (0x%08X)", i, result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splCryptoGenerateAesKey(nca_kaek_%02u) failed! (0x%08X)", i, result); success = false; break; } @@ -602,8 +582,7 @@ int parse_hex_key(unsigned char *key, const char *hex, unsigned int len) { if (strlen(hex) != (2 * len)) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: key (%s) must be %u hex digits!", hex, 2 * len); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: key (%s) must be %u hex digits!", hex, 2 * len); return 0; } @@ -612,8 +591,7 @@ int parse_hex_key(unsigned char *key, const char *hex, unsigned int len) { if (!ishex(hex[i])) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: key (%s) must be %u hex digits!", hex, 2 * len); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: key (%s) must be %u hex digits!", hex, 2 * len); return 0; } } @@ -704,8 +682,7 @@ bool loadExternalKeys() FILE *keysFile = fopen(keysFilePath, "rb"); if (!keysFile) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to open \"%s\" to retrieve \"eticket_rsa_kek\", titlekeks and KAEKs!", keysFilePath); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to open \"%s\" to retrieve \"eticket_rsa_kek\", titlekeks and KAEKs!", keysFilePath); return false; } @@ -715,12 +692,7 @@ bool loadExternalKeys() if (ret < 1) { - if (ret == -1) - { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to parse necessary keys from \"%s\"! (keys file empty?)", keysFilePath); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - } - + if (ret == -1) uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to parse necessary keys from \"%s\"! (keys file empty?)", keysFilePath); return false; } @@ -731,7 +703,7 @@ bool testKeyPair(const void *E, const void *D, const void *N) { if (!E || !D || !N) { - uiDrawString("Error: invalid parameters to test RSA key pair.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to test RSA key pair."); return false; } @@ -747,15 +719,13 @@ bool testKeyPair(const void *E, const void *D, const void *N) if (R_FAILED(result = splUserExpMod(X, N, D, 0x100, Y))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: splUserExpMod failed! (testKeyPair #1) (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splUserExpMod failed! (testKeyPair #1) (0x%08X)", result); return false; } if (R_FAILED(result = splUserExpMod(Y, N, E, 4, Z))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: splUserExpMod failed! (testKeyPair #2) (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splUserExpMod failed! (testKeyPair #2) (0x%08X)", result); return false; } @@ -763,7 +733,7 @@ bool testKeyPair(const void *E, const void *D, const void *N) { if (X[i] != Z[i]) { - uiDrawString("Error: invalid RSA key pair!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid RSA key pair!"); return false; } } @@ -804,7 +774,7 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e { if (!dec_nca_header || dec_nca_header->kaek_ind > 2 || (!out_tik && !out_dec_key && !out_enc_key)) { - uiDrawString("Error: invalid parameters to retrieve NCA ticket and/or titlekey.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to retrieve NCA ticket and/or titlekey."); return false; } @@ -822,7 +792,7 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e if (!has_rights_id) { - uiDrawString("Error: NCA doesn't use titlekey crypto.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: NCA doesn't use titlekey crypto."); return false; } @@ -831,7 +801,7 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e if (crypto_type >= 0x20) { - uiDrawString("Error: invalid NCA keyblob index.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NCA keyblob index."); return false; } @@ -864,30 +834,27 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e if (R_FAILED(result = esInitialize())) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to initialize the ES service! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to initialize the ES service! (0x%08X)", result); return false; } if (R_FAILED(result = esCountCommonTicket(&common_count))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: esCountCommonTicket failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: esCountCommonTicket failed! (0x%08X)", result); esExit(); return false; } if (R_FAILED(result = esCountPersonalizedTicket(&personalized_count))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: esCountPersonalizedTicket failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: esCountPersonalizedTicket failed! (0x%08X)", result); esExit(); return false; } if (!common_count && !personalized_count) { - uiDrawString("Error: no tickets available!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: no tickets available!"); esExit(); return false; } @@ -897,15 +864,14 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e common_rights_ids = calloc(common_count, sizeof(FsRightsId)); if (!common_rights_ids) { - uiDrawString("Error: failed to allocate memory for common tickets' rights IDs!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to allocate memory for common tickets' rights IDs!"); esExit(); return false; } if (R_FAILED(result = esListCommonTicket(&ids_written, common_rights_ids, common_count * sizeof(FsRightsId)))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: esListCommonTicket failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: esListCommonTicket failed! (0x%08X)", result); free(common_rights_ids); esExit(); return false; @@ -929,15 +895,14 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e personalized_rights_ids = calloc(personalized_count, sizeof(FsRightsId)); if (!personalized_rights_ids) { - uiDrawString("Error: failed to allocate memory for personalized tickets' rights IDs!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to allocate memory for personalized tickets' rights IDs!"); esExit(); return false; } if (R_FAILED(result = esListPersonalizedTicket(&ids_written, personalized_rights_ids, personalized_count * sizeof(FsRightsId)))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: esListPersonalizedTicket failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: esListPersonalizedTicket failed! (0x%08X)", result); free(personalized_rights_ids); esExit(); return false; @@ -960,15 +925,14 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e if (!foundRightsId || (rightsIdType != 1 && rightsIdType != 2)) { - uiDrawString("Error: NCA rights ID unavailable in this console!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: NCA rights ID unavailable in this console!"); return false; } // Get extended eTicket RSA key from PRODINFO if (R_FAILED(result = setcalInitialize())) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to initialize the set:cal service! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to initialize the set:cal service! (0x%08X)", result); return false; } @@ -978,8 +942,7 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e if (R_FAILED(result)) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: setcalGetEticketDeviceKey failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: setcalGetEticketDeviceKey failed! (0x%08X)", result); return false; } @@ -995,7 +958,7 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e // The value is stored use big endian byte order if (bswap_32(*((u32*)(&(eticket_data[ETICKET_DEVKEY_RSA_OFFSET + 0x200])))) != SIGTYPE_RSA2048_SHA1) { - uiDrawString("Error: invalid public RSA exponent for eTicket data! Wrong keys?", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid public RSA exponent for eTicket data! Wrong keys?"); return false; } @@ -1007,8 +970,7 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e if (R_FAILED(result = fsOpenBisStorage(&fatFsStorage, FsBisStorageId_System))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to open BIS System partition! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to open BIS System partition! (0x%08X)", result); return false; } @@ -1016,8 +978,7 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e fr = f_mount(&fs, "sys", 1); if (fr != FR_OK) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to mount BIS System partition! (%u)", fr); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to mount BIS System partition! (%u)", fr); fsStorageClose(&fatFsStorage); return false; } @@ -1025,8 +986,7 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e fr = f_chdir("/save"); if (fr != FR_OK) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to change BIS System partition directory! (%u)", fr); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to change BIS System partition directory! (%u)", fr); f_unmount("sys"); fsStorageClose(&fatFsStorage); return false; @@ -1035,8 +995,7 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e fr = f_open(&eTicketSave, (rightsIdType == 1 ? COMMON_ETICKET_FILENAME : PERSONALIZED_ETICKET_FILENAME), FA_READ | FA_OPEN_EXISTING); if (fr != FR_OK) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to open ES %s eTicket save! (%u)", (rightsIdType == 1 ? "common" : "personalized"), fr); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to open ES %s eTicket save! (%u)", (rightsIdType == 1 ? "common" : "personalized"), fr); f_unmount("sys"); fsStorageClose(&fatFsStorage); return false; @@ -1067,8 +1026,7 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e if (R_FAILED(result = splUserExpMod(titleKeyBlock, N, D, 0x100, M))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: splUserExpMod failed! (titleKeyBlock) (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splUserExpMod failed! (titleKeyBlock) (0x%08X)", result); proceed = false; break; } @@ -1083,7 +1041,7 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e // Verify if it starts with a null string hash if (memcmp(db, null_hash, 0x20) != 0) { - uiDrawString("Error: titlekey decryption failed! Wrong keys?", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: titlekey decryption failed! Wrong keys?"); proceed = false; break; } @@ -1106,7 +1064,7 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e if (!foundEticket) { - uiDrawString("Error: unable to find a matching eTicket entry for NCA rights ID!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to find a matching eTicket entry for NCA rights ID!"); return false; } @@ -1132,7 +1090,7 @@ bool generateEncryptedNcaKeyAreaWithTitlekey(nca_header_t *dec_nca_header, u8 *d { if (!dec_nca_header || dec_nca_header->kaek_ind > 2 || !decrypted_nca_keys || !nca_keyset.ext_key_cnt) { - uiDrawString("Error: invalid parameters to generate encrypted NCA key area using titlekey!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to generate encrypted NCA key area using titlekey!"); return false; } @@ -1144,7 +1102,7 @@ bool generateEncryptedNcaKeyAreaWithTitlekey(nca_header_t *dec_nca_header, u8 *d if (crypto_type >= 0x20) { - uiDrawString("Error: invalid NCA keyblob index.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NCA keyblob index."); return false; } @@ -1177,7 +1135,7 @@ bool readCertsFromApplicationRomFs() certFile = fopen(path, "rb"); if (!certFile) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "readCertsFromApplicationRomFs: failed to open file \"%s\".\n", path); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "readCertsFromApplicationRomFs: failed to open file \"%s\".\n", path); return false; } @@ -1187,7 +1145,7 @@ bool readCertsFromApplicationRomFs() if (certSize != expected_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "readCertsFromApplicationRomFs: invalid file size for \"%s\".\n", path); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "readCertsFromApplicationRomFs: invalid file size for \"%s\".\n", path); return false; } @@ -1197,7 +1155,7 @@ bool readCertsFromApplicationRomFs() if (read_bytes != expected_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "readCertsFromApplicationRomFs: error reading file \"%s\".\n", path); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "readCertsFromApplicationRomFs: error reading file \"%s\".\n", path); return false; } @@ -1205,7 +1163,7 @@ bool readCertsFromApplicationRomFs() if (memcmp(tmp_hash, hash_ptr, 0x20) != 0) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "readCertsFromApplicationRomFs: invalid hash for file \"%s\".\n", path); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "readCertsFromApplicationRomFs: invalid hash for file \"%s\".\n", path); return false; } } @@ -1217,8 +1175,7 @@ bool retrieveCertData(u8 *out_cert, bool personalized) { if (!out_cert) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid parameters to retrieve %s ticket certificate chain.", (!personalized ? "common" : "personalized")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to retrieve %s ticket certificate chain.", (!personalized ? "common" : "personalized")); return false; } diff --git a/source/keys.h b/source/keys.h index 6dd2bbb..1ca1b84 100644 --- a/source/keys.h +++ b/source/keys.h @@ -12,8 +12,6 @@ #define SEG_RODATA BIT(1) #define SEG_DATA BIT(2) -#define SHA256_HASH_LENGTH 0x20 - #define ETICKET_DEVKEY_DATA_SIZE 0x244 #define ETICKET_DEVKEY_CTR_OFFSET 0x4 #define ETICKET_DEVKEY_RSA_OFFSET 0x14 @@ -34,7 +32,7 @@ typedef struct { typedef struct { char name[128]; - u8 hash[SHA256_HASH_LENGTH]; + u8 hash[SHA256_HASH_SIZE]; u64 size; } PACKED keyInfo; diff --git a/source/main.c b/source/main.c index 3bc4e69..37efe48 100644 --- a/source/main.c +++ b/source/main.c @@ -21,8 +21,6 @@ extern UEvent exitEvent; extern bool gameCardInserted; -extern char strbuf[NAME_BUF_LEN * 4]; - extern char appLaunchPath[NAME_BUF_LEN]; extern nca_keyset_t nca_keyset; @@ -37,7 +35,7 @@ int main(int argc, char *argv[]) { if (strlen(argv[i]) > 10 && !strncasecmp(argv[i], "sdmc:/", 6) && !strncasecmp(argv[i] + strlen(argv[i]) - 4, ".nro", 4)) { - snprintf(appLaunchPath, sizeof(appLaunchPath) / sizeof(appLaunchPath[0]), argv[i]); + snprintf(appLaunchPath, MAX_ELEMENTS(appLaunchPath), argv[i]); break; } } @@ -117,6 +115,9 @@ int main(int argc, char *argv[]) /* Make sure output directories exist */ createOutputDirectories(); + /* Load settings from configuration file */ + loadConfig(); + /* Main application loop */ bool exitLoop = false; while(appletMainLoop()) @@ -269,8 +270,7 @@ int main(int argc, char *argv[]) /* Wait for the gamecard detection thread to exit */ threadWaitForExit(&thread); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to start gamecard detection thread! (0x%08X)", result); - uiDrawString(strbuf, 8, 8, 255, 255, 255); + uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_RGB, "Failed to start gamecard detection thread! (0x%08X)", result); uiRefreshDisplay(); delay(5); ret = -11; @@ -279,8 +279,7 @@ int main(int argc, char *argv[]) /* Close gamecard detection thread */ threadClose(&thread); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to create gamecard detection thread! (0x%08X)", result); - uiDrawString(strbuf, 8, 8, 255, 255, 255); + uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_RGB, "Failed to create gamecard detection thread! (0x%08X)", result); uiRefreshDisplay(); delay(5); ret = -10; @@ -289,8 +288,7 @@ int main(int argc, char *argv[]) /* Close gamecard detection kernel event */ eventClose(&fsGameCardKernelEvent); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to retrieve gamecard detection event handle! (0x%08X)", result); - uiDrawString(strbuf, 8, 8, 255, 255, 255); + uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_RGB, "Failed to retrieve gamecard detection event handle! (0x%08X)", result); uiRefreshDisplay(); delay(5); ret = -9; @@ -299,8 +297,7 @@ int main(int argc, char *argv[]) /* Close gamecard detection event notifier */ fsEventNotifierClose(&fsGameCardEventNotifier); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open gamecard detection event notifier! (0x%08X)", result); - uiDrawString(strbuf, 8, 8, 255, 255, 255); + uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_RGB, "Failed to open gamecard detection event notifier! (0x%08X)", result); uiRefreshDisplay(); delay(5); ret = -8; @@ -309,8 +306,7 @@ int main(int argc, char *argv[]) /* Close device operator */ fsDeviceOperatorClose(&fsOperatorInstance); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open device operator! (0x%08X)", result); - uiDrawString(strbuf, 8, 8, 255, 255, 255); + uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_RGB, "Failed to open device operator! (0x%08X)", result); uiRefreshDisplay(); delay(5); ret = -7; @@ -319,8 +315,7 @@ int main(int argc, char *argv[]) /* Denitialize the pm:dmnt service */ pmdmntExit(); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to initialize the pm:dmnt service! (0x%08X)", result); - uiDrawString(strbuf, 8, 8, 255, 255, 255); + uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_RGB, "Failed to initialize the pm:dmnt service! (0x%08X)", result); uiRefreshDisplay(); delay(5); ret = -6; @@ -329,8 +324,7 @@ int main(int argc, char *argv[]) /* Denitialize the spl service */ splExit(); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to initialize the spl service! (0x%08X)", result); - uiDrawString(strbuf, 8, 8, 255, 255, 255); + uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_RGB, "Failed to initialize the spl service! (0x%08X)", result); uiRefreshDisplay(); delay(5); ret = -5; @@ -339,8 +333,7 @@ int main(int argc, char *argv[]) /* Denitialize the csrng service */ csrngExit(); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to initialize the csrng service! (0x%08X)", result); - uiDrawString(strbuf, 8, 8, 255, 255, 255); + uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_RGB, "Failed to initialize the csrng service! (0x%08X)", result); uiRefreshDisplay(); delay(5); ret = -4; @@ -349,8 +342,7 @@ int main(int argc, char *argv[]) /* Denitialize the ns service */ nsExit(); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to initialize the ns service! (0x%08X)", result); - uiDrawString(strbuf, 8, 8, 255, 255, 255); + uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_RGB, "Failed to initialize the ns service! (0x%08X)", result); uiRefreshDisplay(); delay(5); ret = -3; @@ -359,8 +351,7 @@ int main(int argc, char *argv[]) /* Denitialize the ncm service */ ncmExit(); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to initialize the ncm service! (0x%08X)", result); - uiDrawString(strbuf, 8, 8, 255, 255, 255); + uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_RGB, "Failed to initialize the ncm service! (0x%08X)", result); uiRefreshDisplay(); delay(5); ret = -2; diff --git a/source/nca.c b/source/nca.c index 08f4994..6e8cf7c 100644 --- a/source/nca.c +++ b/source/nca.c @@ -18,8 +18,6 @@ extern exefs_ctx_t exeFsContext; extern romfs_ctx_t romFsContext; extern bktr_ctx_t bktrContext; -extern char strbuf[NAME_BUF_LEN * 4]; - extern nca_keyset_t nca_keyset; extern u8 *ncaCtrBuf; @@ -223,7 +221,7 @@ bool loadNcaKeyset() envIsSyscallHinted(0x69) && // svcQueryDebugProcessMemory envIsSyscallHinted(0x6a))) // svcReadDebugProcessMemory { - uiDrawString("Error: please run the application with debug svc permissions!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: please run the application with debug svc permissions!"); return false; } @@ -234,8 +232,7 @@ size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, { if (!ctx || !dst || !src || !size || (size % NCA_AES_XTS_SECTOR_SIZE) != 0) return 0; - u32 i; - size_t crypt_res = 0, out = 0; + size_t i, crypt_res = 0, out = 0; u32 cur_sector = sector; for(i = 0; i < size; i += NCA_AES_XTS_SECTOR_SIZE, cur_sector++) @@ -294,8 +291,7 @@ bool processNcaCtrSectionBlock(NcmContentStorage *ncmStorage, const NcmNcaId *nc { if (!ncmStorage || !ncaId || !outBuf || !bufSize || !ctx) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid parameters to process %s NCA section block!", encrypt ? "decrypted" : "encrypted"); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to process %s NCA section block!", encrypt ? "decrypted" : "encrypted"); return false; } @@ -316,8 +312,7 @@ bool processNcaCtrSectionBlock(NcmContentStorage *ncmStorage, const NcmNcaId *nc if (R_FAILED(result = ncmContentStorageReadContentIdFile(ncmStorage, ncaId, block_start_offset, ncaCtrBuf, block_size_used))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read encrypted %lu bytes block at offset 0x%016lX from NCA \"%s\"! (0x%08X)", block_size_used, block_start_offset, nca_id, result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read encrypted %lu bytes block at offset 0x%016lX from NCA \"%s\"! (0x%08X)", block_size_used, block_start_offset, nca_id, result); return false; } @@ -359,7 +354,7 @@ bktr_relocation_entry_t *bktr_get_relocation(bktr_relocation_block_t *block, u64 // Weak check for invalid offset if (offset > block->total_size) { - uiDrawString("Error: too big offset looked up in BKTR relocation table!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: too big offset looked up in BKTR relocation table!"); return NULL; } @@ -396,8 +391,7 @@ bktr_relocation_entry_t *bktr_get_relocation(bktr_relocation_block_t *block, u64 } } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to find offset 0x%016lX in BKTR relocation table!", offset); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to find offset 0x%016lX in BKTR relocation table!", offset); return NULL; } @@ -446,8 +440,7 @@ bktr_subsection_entry_t *bktr_get_subsection(bktr_subsection_block_t *block, u64 } } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to find offset 0x%016lX in BKTR subsection table!", offset); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to find offset 0x%016lX in BKTR subsection table!", offset); return NULL; } @@ -455,7 +448,7 @@ bool bktrSectionSeek(u64 offset) { if (!bktrContext.section_offset || !bktrContext.section_size || !bktrContext.relocation_block || !bktrContext.subsection_block) { - uiDrawString("Error: invalid parameters to seek within NCA BKTR section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to seek within NCA BKTR section!"); return false; } @@ -485,7 +478,7 @@ bool bktrSectionPhysicalRead(void *outBuf, size_t bufSize) { if (!bktrContext.section_offset || !bktrContext.section_size || !bktrContext.relocation_block || !bktrContext.subsection_block || !outBuf || !bufSize) { - uiDrawString("Error: invalid parameters to perform physical block read from NCA BKTR section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to perform physical block read from NCA BKTR section!"); return false; } @@ -518,8 +511,7 @@ bool bktrSectionPhysicalRead(void *outBuf, size_t bufSize) if (R_FAILED(result = ncmContentStorageReadContentIdFile(&(bktrContext.ncmStorage), &(bktrContext.ncaId), block_start_offset, ncaCtrBuf, block_size_used))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "BKTR: failed to read encrypted %lu bytes block at offset 0x%016lX! (0x%08X)", block_size_used, block_start_offset, result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "BKTR: failed to read encrypted %lu bytes block at offset 0x%016lX! (0x%08X)", block_size_used, block_start_offset, result); return false; } @@ -558,7 +550,7 @@ bool readBktrSectionBlock(u64 offset, void *outBuf, size_t bufSize) { if (!bktrContext.section_offset || !bktrContext.section_size || !bktrContext.relocation_block || !bktrContext.subsection_block || !romFsContext.section_offset || !romFsContext.section_size || !outBuf || !bufSize) { - uiDrawString("Error: invalid parameters to read block from NCA BKTR section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to read block from NCA BKTR section!"); return false; } @@ -600,7 +592,7 @@ bool encryptNcaHeader(nca_header_t *input, u8 *outBuf, u64 outBufSize) { if (!input || !outBuf || !outBufSize || outBufSize < NCA_FULL_HEADER_LENGTH || (bswap_32(input->magic) != NCA3_MAGIC && bswap_32(input->magic) != NCA2_MAGIC)) { - uiDrawString("Error: invalid NCA header encryption parameters.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NCA header encryption parameters."); return false; } @@ -623,8 +615,7 @@ bool encryptNcaHeader(nca_header_t *input, u8 *outBuf, u64 outBufSize) crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, outBuf, input, NCA_FULL_HEADER_LENGTH, 0, true); if (crypt_res != NCA_FULL_HEADER_LENGTH) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid output length for encrypted NCA header! (%u != %lu)", NCA_FULL_HEADER_LENGTH, crypt_res); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid output length for encrypted NCA header! (%u != %lu)", NCA_FULL_HEADER_LENGTH, crypt_res); return false; } } else @@ -633,8 +624,7 @@ bool encryptNcaHeader(nca_header_t *input, u8 *outBuf, u64 outBufSize) crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, outBuf, input, NCA_HEADER_LENGTH, 0, true); if (crypt_res != NCA_HEADER_LENGTH) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid output length for encrypted NCA header! (%u != %lu)", NCA_HEADER_LENGTH, crypt_res); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid output length for encrypted NCA header! (%u != %lu)", NCA_HEADER_LENGTH, crypt_res); return false; } @@ -643,14 +633,12 @@ bool encryptNcaHeader(nca_header_t *input, u8 *outBuf, u64 outBufSize) crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, outBuf + NCA_HEADER_LENGTH + (i * NCA_SECTION_HEADER_LENGTH), &(input->fs_headers[i]), NCA_SECTION_HEADER_LENGTH, 0, true); if (crypt_res != NCA_SECTION_HEADER_LENGTH) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid output length for encrypted NCA header section #%u! (%u != %lu)", i, NCA_SECTION_HEADER_LENGTH, crypt_res); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid output length for encrypted NCA header section #%u! (%u != %lu)", i, NCA_SECTION_HEADER_LENGTH, crypt_res); return false; } } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid decrypted NCA magic word! (0x%08X)", bswap_32(input->magic)); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid decrypted NCA magic word! (0x%08X)", bswap_32(input->magic)); return false; } @@ -661,7 +649,7 @@ bool decryptNcaHeader(const u8 *ncaBuf, u64 ncaBufSize, nca_header_t *out, title { if (!ncaBuf || !ncaBufSize || ncaBufSize < NCA_FULL_HEADER_LENGTH || !out || !decrypted_nca_keys) { - uiDrawString("Error: invalid NCA header decryption parameters.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NCA header decryption parameters."); return false; } @@ -684,8 +672,7 @@ bool decryptNcaHeader(const u8 *ncaBuf, u64 ncaBufSize, nca_header_t *out, title crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, out, ncaBuf, NCA_HEADER_LENGTH, 0, false); if (crypt_res != NCA_HEADER_LENGTH) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid output length for decrypted NCA header! (%u != %lu)", NCA_HEADER_LENGTH, crypt_res); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid output length for decrypted NCA header! (%u != %lu)", NCA_HEADER_LENGTH, crypt_res); return false; } @@ -694,8 +681,7 @@ bool decryptNcaHeader(const u8 *ncaBuf, u64 ncaBufSize, nca_header_t *out, title crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, out, ncaBuf, NCA_FULL_HEADER_LENGTH, 0, false); if (crypt_res != NCA_FULL_HEADER_LENGTH) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid output length for decrypted NCA header! (%u != %lu)", NCA_FULL_HEADER_LENGTH, crypt_res); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid output length for decrypted NCA header! (%u != %lu)", NCA_FULL_HEADER_LENGTH, crypt_res); return false; } } else @@ -708,8 +694,7 @@ bool decryptNcaHeader(const u8 *ncaBuf, u64 ncaBufSize, nca_header_t *out, title crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(out->fs_headers[i]), ncaBuf + NCA_HEADER_LENGTH + (i * NCA_SECTION_HEADER_LENGTH), NCA_SECTION_HEADER_LENGTH, 0, false); if (crypt_res != NCA_SECTION_HEADER_LENGTH) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid output length for decrypted NCA header section #%u! (%u != %lu)", i, NCA_SECTION_HEADER_LENGTH, crypt_res); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid output length for decrypted NCA header section #%u! (%u != %lu)", i, NCA_SECTION_HEADER_LENGTH, crypt_res); return false; } } else { @@ -717,8 +702,7 @@ bool decryptNcaHeader(const u8 *ncaBuf, u64 ncaBufSize, nca_header_t *out, title } } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid NCA magic word! Wrong header key? (0x%08X)", bswap_32(out->magic)); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NCA magic word! Wrong header key? (0x%08X)", bswap_32(out->magic)); return false; } @@ -786,26 +770,25 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca { if (!ncmStorage || !ncaId || !dec_nca_header || !xml_content_info || !output) { - uiDrawString("Error: invalid parameters to process Program NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to process Program NCA!"); return false; } if (dec_nca_header->fs_headers[0].partition_type != NCA_FS_HEADER_PARTITION_PFS0 || dec_nca_header->fs_headers[0].fs_type != NCA_FS_HEADER_FSTYPE_PFS0) { - uiDrawString("Error: Program NCA section #0 doesn't hold a PFS0 partition!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: Program NCA section #0 doesn't hold a PFS0 partition!"); return false; } if (!dec_nca_header->fs_headers[0].pfs0_superblock.pfs0_size) { - uiDrawString("Error: invalid size for PFS0 partition in Program NCA section #0!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid size for PFS0 partition in Program NCA section #0!"); return false; } if (dec_nca_header->fs_headers[0].crypt_type != NCA_FS_HEADER_CRYPT_CTR) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid AES crypt type for Program NCA section #0! (0x%02X)", dec_nca_header->fs_headers[0].crypt_type); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid AES crypt type for Program NCA section #0! (0x%02X)", dec_nca_header->fs_headers[0].crypt_type); return false; } @@ -828,7 +811,7 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca u64 block_hash_table_end_offset; u64 block_start_offset[2] = { 0, 0 }; u64 block_size[2] = { 0, 0 }; - u8 block_hash[2][SHA256_HASH_LENGTH]; + u8 block_hash[2][SHA256_HASH_SIZE]; u8 *block_data[2] = { NULL, NULL }; u64 sig_write_size[2] = { 0, 0 }; @@ -843,7 +826,7 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca if (!section_offset || section_offset < NCA_FULL_HEADER_LENGTH || !hash_table_offset || hash_table_offset < section_offset || !nca_pfs0_offset || nca_pfs0_offset <= hash_table_offset) { - uiDrawString("Error: invalid offsets for Program NCA section #0!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid offsets for Program NCA section #0!"); return false; } @@ -865,34 +848,33 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, nca_pfs0_offset, &nca_pfs0_header, sizeof(pfs0_header), false)) { breaks++; - uiDrawString("Failed to read Program NCA section #0 PFS0 partition header!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read Program NCA section #0 PFS0 partition header!"); return false; } if (bswap_32(nca_pfs0_header.magic) != PFS0_MAGIC) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid magic word for Program NCA section #0 PFS0 partition! Wrong KAEK? (0x%08X)", bswap_32(nca_pfs0_header.magic)); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid magic word for Program NCA section #0 PFS0 partition! Wrong KAEK? (0x%08X)", bswap_32(nca_pfs0_header.magic)); return false; } if (!nca_pfs0_header.file_cnt || !nca_pfs0_header.str_table_size) { - uiDrawString("Error: Program NCA section #0 PFS0 partition is empty! Wrong KAEK?", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: Program NCA section #0 PFS0 partition is empty! Wrong KAEK?"); return false; } nca_pfs0_entries = calloc(nca_pfs0_header.file_cnt, sizeof(pfs0_entry_table)); if (!nca_pfs0_entries) { - uiDrawString("Error: unable to allocate memory for Program NCA section #0 PFS0 partition entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for Program NCA section #0 PFS0 partition entries!"); return false; } if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, nca_pfs0_offset + sizeof(pfs0_header), nca_pfs0_entries, (u64)nca_pfs0_header.file_cnt * sizeof(pfs0_entry_table), false)) { breaks++; - uiDrawString("Failed to read Program NCA section #0 PFS0 partition entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read Program NCA section #0 PFS0 partition entries!"); free(nca_pfs0_entries); return false; } @@ -908,8 +890,7 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, nca_pfs0_cur_file_offset, &npdm_header, sizeof(npdm_t), false)) { breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read Program NCA section #0 PFS0 entry #%u!", i); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read Program NCA section #0 PFS0 entry #%u!", i); free(nca_pfs0_entries); return false; } @@ -927,13 +908,13 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca if (!found_meta) { - uiDrawString("Error: unable to find NPDM entry in Program NCA section #0 PFS0 partition!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to find NPDM entry in Program NCA section #0 PFS0 partition!"); return false; } // Calculate block offsets - block_hash_table_offset = (hash_table_offset + (((acid_pubkey_offset - nca_pfs0_offset) / (u64)dec_nca_header->fs_headers[0].pfs0_superblock.block_size)) * (u64)SHA256_HASH_LENGTH); - block_hash_table_end_offset = (hash_table_offset + (((acid_pubkey_offset + (u64)NPDM_SIGNATURE_SIZE - nca_pfs0_offset) / (u64)dec_nca_header->fs_headers[0].pfs0_superblock.block_size) * (u64)SHA256_HASH_LENGTH)); + block_hash_table_offset = (hash_table_offset + (((acid_pubkey_offset - nca_pfs0_offset) / (u64)dec_nca_header->fs_headers[0].pfs0_superblock.block_size)) * (u64)SHA256_HASH_SIZE); + block_hash_table_end_offset = (hash_table_offset + (((acid_pubkey_offset + (u64)NPDM_SIGNATURE_SIZE - nca_pfs0_offset) / (u64)dec_nca_header->fs_headers[0].pfs0_superblock.block_size) * (u64)SHA256_HASH_SIZE)); block_start_offset[0] = (nca_pfs0_offset + (((acid_pubkey_offset - nca_pfs0_offset) / (u64)dec_nca_header->fs_headers[0].pfs0_superblock.block_size) * (u64)dec_nca_header->fs_headers[0].pfs0_superblock.block_size)); // Make sure our block doesn't pass PFS0 end offset @@ -947,7 +928,7 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca block_data[0] = malloc(block_size[0]); if (!block_data[0]) { - uiDrawString("Error: unable to allocate memory for Program NCA section #0 PFS0 NPDM block 0!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for Program NCA section #0 PFS0 NPDM block 0!"); return false; } @@ -955,7 +936,7 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, block_start_offset[0], block_data[0], block_size[0], false)) { breaks++; - uiDrawString("Failed to read Program NCA section #0 PFS0 NPDM block 0!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read Program NCA section #0 PFS0 NPDM block 0!"); free(block_data[0]); return false; } @@ -969,7 +950,7 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca sig_write_size[0] = (u64)NPDM_SIGNATURE_SIZE; } - // Patch acid pubkey changing it to a self-generated pubkey + // Patch ACID public key changing it to a self-generated pubkey memcpy(block_data[0] + (acid_pubkey_offset - block_start_offset[0]), rsa_get_public_key(), sig_write_size[0]); // Calculate new block hash @@ -989,7 +970,7 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca block_data[1] = malloc(block_size[1]); if (!block_data[1]) { - uiDrawString("Error: unable to allocate memory for Program NCA section #0 PFS0 NPDM block 1!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for Program NCA section #0 PFS0 NPDM block 1!"); free(block_data[0]); return false; } @@ -997,7 +978,7 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, block_start_offset[1], block_data[1], block_size[1], false)) { breaks++; - uiDrawString("Failed to read Program NCA section #0 PFS0 NPDM block 1!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read Program NCA section #0 PFS0 NPDM block 1!"); free(block_data[0]); free(block_data[1]); return false; @@ -1011,7 +992,7 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca hash_table = malloc(dec_nca_header->fs_headers[0].pfs0_superblock.hash_table_size); if (!hash_table) { - uiDrawString("Error: unable to allocate memory for Program NCA section #0 PFS0 hash table!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for Program NCA section #0 PFS0 hash table!"); free(block_data[0]); if (block_data[1]) free(block_data[1]); return false; @@ -1020,7 +1001,7 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, hash_table_offset, hash_table, dec_nca_header->fs_headers[0].pfs0_superblock.hash_table_size, false)) { breaks++; - uiDrawString("Failed to read Program NCA section #0 PFS0 hash table!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read Program NCA section #0 PFS0 hash table!"); free(block_data[0]); if (block_data[1]) free(block_data[1]); free(hash_table); @@ -1028,8 +1009,8 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca } // Update block hashes - memcpy(hash_table + (block_hash_table_offset - hash_table_offset), block_hash[0], SHA256_HASH_LENGTH); - if (block_hash_table_offset != block_hash_table_end_offset) memcpy(hash_table + (block_hash_table_end_offset - hash_table_offset), block_hash[1], SHA256_HASH_LENGTH); + memcpy(hash_table + (block_hash_table_offset - hash_table_offset), block_hash[0], SHA256_HASH_SIZE); + if (block_hash_table_offset != block_hash_table_end_offset) memcpy(hash_table + (block_hash_table_end_offset - hash_table_offset), block_hash[1], SHA256_HASH_SIZE); // Calculate PFS0 superblock master hash sha256CalculateHash(dec_nca_header->fs_headers[0].pfs0_superblock.master_hash, hash_table, dec_nca_header->fs_headers[0].pfs0_superblock.hash_table_size); @@ -1041,7 +1022,7 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca if (!rsa_sign(&(dec_nca_header->magic), NPDM_SIGNATURE_AREA_SIZE, dec_nca_header->npdm_key_sig, NPDM_SIGNATURE_SIZE)) { breaks++; - uiDrawString("Failed to recreate Program NCA NPDM signature!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to recreate Program NCA NPDM signature!"); free(block_data[0]); if (block_data[1]) free(block_data[1]); free(hash_table); @@ -1052,7 +1033,7 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, block_start_offset[0], block_data[0], block_size[0], true)) { breaks++; - uiDrawString("Failed to encrypt Program NCA section #0 PFS0 NPDM block 0!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to encrypt Program NCA section #0 PFS0 NPDM block 0!"); free(block_data[0]); if (block_data[1]) free(block_data[1]); free(hash_table); @@ -1064,7 +1045,7 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, block_start_offset[1], block_data[1], block_size[1], true)) { breaks++; - uiDrawString("Failed to encrypt Program NCA section #0 PFS0 NPDM block 1!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to encrypt Program NCA section #0 PFS0 NPDM block 1!"); free(block_data[0]); free(block_data[1]); free(hash_table); @@ -1075,7 +1056,7 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, hash_table_offset, hash_table, dec_nca_header->fs_headers[0].pfs0_superblock.hash_table_size, true)) { breaks++; - uiDrawString("Failed to encrypt Program NCA section #0 PFS0 hash table!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to encrypt Program NCA section #0 PFS0 hash table!"); free(block_data[0]); if (block_data[1]) free(block_data[1]); free(hash_table); @@ -1101,8 +1082,6 @@ bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca output->block_size[1] = block_size[1]; } - output->acid_pubkey_offset = (acid_pubkey_offset - block_start_offset[0]); - return true; } @@ -1110,7 +1089,7 @@ bool retrieveCnmtNcaData(FsStorageId curStorageId, nspDumpType selectedNspDumpTy { if (!ncaBuf || !xml_program_info || !xml_content_info || !output || !rights_info) { - uiDrawString("Error: invalid parameters to retrieve CNMT NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to retrieve CNMT NCA!"); return false; } @@ -1142,7 +1121,7 @@ bool retrieveCnmtNcaData(FsStorageId curStorageId, nspDumpType selectedNspDumpTy // Generate filename for our required CNMT file char cnmtFileName[50] = {'\0'}; - snprintf(cnmtFileName, sizeof(cnmtFileName) / sizeof(cnmtFileName[0]), "%s_%016lx.cnmt", getTitleType(xml_program_info->type), xml_program_info->title_id); + snprintf(cnmtFileName, MAX_ELEMENTS(cnmtFileName), "%s_%016lx.cnmt", getTitleType(xml_program_info->type), xml_program_info->title_id); // Decrypt the NCA header // Don't retrieve the ticket and/or titlekey if we're dealing with a Patch with titlekey crypto bundled with the inserted gamecard @@ -1150,20 +1129,19 @@ bool retrieveCnmtNcaData(FsStorageId curStorageId, nspDumpType selectedNspDumpTy if (dec_header.fs_headers[0].partition_type != NCA_FS_HEADER_PARTITION_PFS0 || dec_header.fs_headers[0].fs_type != NCA_FS_HEADER_FSTYPE_PFS0) { - uiDrawString("Error: CNMT NCA section #0 doesn't hold a PFS0 partition!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: CNMT NCA section #0 doesn't hold a PFS0 partition!"); return false; } if (!dec_header.fs_headers[0].pfs0_superblock.pfs0_size) { - uiDrawString("Error: invalid size for PFS0 partition in CNMT NCA section #0!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid size for PFS0 partition in CNMT NCA section #0!"); return false; } if (dec_header.fs_headers[0].crypt_type != NCA_FS_HEADER_CRYPT_CTR) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid AES crypt type for CNMT NCA section #0! (0x%02X)", dec_header.fs_headers[0].crypt_type); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid AES crypt type for CNMT NCA section #0! (0x%02X)", dec_header.fs_headers[0].crypt_type); return false; } @@ -1182,7 +1160,7 @@ bool retrieveCnmtNcaData(FsStorageId curStorageId, nspDumpType selectedNspDumpTy { if (has_rights_id) { - uiDrawString("Error: Rights ID field in NCA header not empty!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: Rights ID field in NCA header not empty!"); return false; } @@ -1206,7 +1184,7 @@ bool retrieveCnmtNcaData(FsStorageId curStorageId, nspDumpType selectedNspDumpTy if (!section_offset || section_offset < NCA_FULL_HEADER_LENGTH || !section_size) { - uiDrawString("Error: invalid offset/size for CNMT NCA section #0!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid offset/size for CNMT NCA section #0!"); return false; } @@ -1228,7 +1206,7 @@ bool retrieveCnmtNcaData(FsStorageId curStorageId, nspDumpType selectedNspDumpTy section_data = malloc(section_size); if (!section_data) { - uiDrawString("Error: unable to allocate memory for the decrypted CNMT NCA section #0!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the decrypted CNMT NCA section #0!"); return false; } @@ -1239,15 +1217,14 @@ bool retrieveCnmtNcaData(FsStorageId curStorageId, nspDumpType selectedNspDumpTy if (bswap_32(nca_pfs0_header.magic) != PFS0_MAGIC) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid magic word for CNMT NCA section #0 PFS0 partition! Wrong KAEK? (0x%08X)", bswap_32(nca_pfs0_header.magic)); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid magic word for CNMT NCA section #0 PFS0 partition! Wrong KAEK? (0x%08X)", bswap_32(nca_pfs0_header.magic)); free(section_data); return false; } if (!nca_pfs0_header.file_cnt || !nca_pfs0_header.str_table_size) { - uiDrawString("Error: CNMT NCA section #0 PFS0 partition is empty! Wrong KAEK?", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: CNMT NCA section #0 PFS0 partition is empty! Wrong KAEK?"); free(section_data); return false; } @@ -1255,7 +1232,7 @@ bool retrieveCnmtNcaData(FsStorageId curStorageId, nspDumpType selectedNspDumpTy nca_pfs0_entries = calloc(nca_pfs0_header.file_cnt, sizeof(pfs0_entry_table)); if (!nca_pfs0_entries) { - uiDrawString("Error: unable to allocate memory for CNMT NCA section #0 PFS0 partition entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for CNMT NCA section #0 PFS0 partition entries!"); free(section_data); return false; } @@ -1282,8 +1259,7 @@ bool retrieveCnmtNcaData(FsStorageId curStorageId, nspDumpType selectedNspDumpTy if (!found_cnmt) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to find file \"%s\" in PFS0 partition from CNMT NCA section #0!", cnmtFileName); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to find file \"%s\" in PFS0 partition from CNMT NCA section #0!", cnmtFileName); free(section_data); return false; } @@ -1292,8 +1268,8 @@ bool retrieveCnmtNcaData(FsStorageId curStorageId, nspDumpType selectedNspDumpTy memcpy(&title_cnmt_extended_header, section_data + title_cnmt_offset + sizeof(cnmt_header), sizeof(cnmt_extended_header)); // Fill information for our CNMT XML - digest_offset = (title_cnmt_offset + title_cnmt_size - (u64)SHA256_HASH_LENGTH); - memcpy(xml_program_info->digest, section_data + digest_offset, SHA256_HASH_LENGTH); + digest_offset = (title_cnmt_offset + title_cnmt_size - (u64)SHA256_HASH_SIZE); + memcpy(xml_program_info->digest, section_data + digest_offset, SHA256_HASH_SIZE); convertDataToHexString(xml_program_info->digest, 32, xml_program_info->digest_str, 65); xml_content_info->keyblob = (dec_header.crypto_type2 > dec_header.crypto_type ? dec_header.crypto_type2 : dec_header.crypto_type); xml_program_info->min_keyblob = (rights_info->has_rights_id ? rights_info->rights_id[15] : xml_content_info->keyblob); @@ -1301,7 +1277,7 @@ bool retrieveCnmtNcaData(FsStorageId curStorageId, nspDumpType selectedNspDumpTy xml_program_info->patch_tid = title_cnmt_extended_header.patch_tid; // Empty CNMT content records - memset(section_data + title_cnmt_offset + sizeof(cnmt_header) + (u64)title_cnmt_header.table_offset, 0, title_cnmt_size - sizeof(cnmt_header) - (u64)title_cnmt_header.table_offset - (u64)SHA256_HASH_LENGTH); + memset(section_data + title_cnmt_offset + sizeof(cnmt_header) + (u64)title_cnmt_header.table_offset, 0, title_cnmt_size - sizeof(cnmt_header) - (u64)title_cnmt_header.table_offset - (u64)SHA256_HASH_SIZE); // Replace input buffer data in-place memcpy(ncaBuf, &dec_header, NCA_FULL_HEADER_LENGTH); @@ -1329,7 +1305,7 @@ bool patchCnmtNca(u8 *ncaBuf, u64 ncaBufSize, cnmt_xml_program_info *xml_program { if (!ncaBuf || !ncaBufSize || !xml_program_info || xml_program_info->nca_cnt <= 1 || !xml_content_info || !cnmt_mod) { - uiDrawString("Error: invalid parameters to patch CNMT NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to patch CNMT NCA!"); return false; } @@ -1340,7 +1316,7 @@ bool patchCnmtNca(u8 *ncaBuf, u64 ncaBufSize, cnmt_xml_program_info *xml_program cnmt_content_record *title_cnmt_content_records = NULL; u64 title_cnmt_content_records_offset; - u8 pfs0_block_hash[SHA256_HASH_LENGTH]; + u8 pfs0_block_hash[SHA256_HASH_SIZE]; nca_header_t dec_header; @@ -1356,7 +1332,7 @@ bool patchCnmtNca(u8 *ncaBuf, u64 ncaBufSize, cnmt_xml_program_info *xml_program title_cnmt_content_records = calloc(nca_cnt, sizeof(cnmt_content_record)); if (!title_cnmt_content_records) { - uiDrawString("Error: unable to allocate memory for CNMT NCA content records!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for CNMT NCA content records!"); return false; } @@ -1379,7 +1355,7 @@ bool patchCnmtNca(u8 *ncaBuf, u64 ncaBufSize, cnmt_xml_program_info *xml_program // Calculate block hash sha256CalculateHash(pfs0_block_hash, ncaBuf + cnmt_mod->pfs0_offset, cnmt_mod->pfs0_size); - memcpy(ncaBuf + cnmt_mod->hash_table_offset, pfs0_block_hash, SHA256_HASH_LENGTH); + memcpy(ncaBuf + cnmt_mod->hash_table_offset, pfs0_block_hash, SHA256_HASH_SIZE); // Copy header to struct memcpy(&dec_header, ncaBuf, sizeof(nca_header_t)); @@ -1409,7 +1385,7 @@ bool patchCnmtNca(u8 *ncaBuf, u64 ncaBufSize, cnmt_xml_program_info *xml_program if (!encryptNcaHeader(&dec_header, ncaBuf, ncaBufSize)) { breaks++; - uiDrawString("Failed to encrypt modified CNMT NCA header!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to encrypt modified CNMT NCA header!"); return false; } @@ -1419,7 +1395,7 @@ bool patchCnmtNca(u8 *ncaBuf, u64 ncaBufSize, cnmt_xml_program_info *xml_program sha256CalculateHash(xml_content_info[xml_program_info->nca_cnt - 1].hash, ncaBuf, ncaBufSize); convertDataToHexString(xml_content_info[xml_program_info->nca_cnt - 1].hash, 32, xml_content_info[xml_program_info->nca_cnt - 1].hash_str, 65); memcpy(xml_content_info[xml_program_info->nca_cnt - 1].nca_id, xml_content_info[xml_program_info->nca_cnt - 1].hash, 16); - convertDataToHexString(xml_content_info[xml_program_info->nca_cnt - 1].nca_id, 16, xml_content_info[xml_program_info->nca_cnt - 1].nca_id_str, 33); + convertDataToHexString(xml_content_info[xml_program_info->nca_cnt - 1].nca_id, SHA256_HASH_SIZE / 2, xml_content_info[xml_program_info->nca_cnt - 1].nca_id_str, 33); return true; } @@ -1428,7 +1404,7 @@ bool readExeFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, { if (!ncmStorage || !ncaId || !dec_nca_header || !decrypted_nca_keys) { - uiDrawString("Error: invalid parameters to read RomFS section from NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to read RomFS section from NCA!"); return false; } @@ -1541,7 +1517,7 @@ bool readExeFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, if (!found_exefs) { - uiDrawString("Error: NCA doesn't hold an ExeFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: NCA doesn't hold an ExeFS section!"); return false; } @@ -1568,7 +1544,7 @@ bool readRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, { if (!ncmStorage || !ncaId || !dec_nca_header || !decrypted_nca_keys) { - uiDrawString("Error: invalid parameters to read RomFS section from NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to read RomFS section from NCA!"); return false; } @@ -1610,7 +1586,7 @@ bool readRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, if (!found_romfs) { - uiDrawString("Error: NCA doesn't hold a RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: NCA doesn't hold a RomFS section!"); return false; } @@ -1619,8 +1595,7 @@ bool readRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, if (!section_offset || section_offset < NCA_FULL_HEADER_LENGTH || !section_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid offset/size for NCA RomFS section! (#%u)", romfs_index); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid offset/size for NCA RomFS section! (#%u)", romfs_index); return false; } @@ -1641,15 +1616,13 @@ bool readRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, if (bswap_32(dec_nca_header->fs_headers[romfs_index].romfs_superblock.ivfc_header.magic) != IVFC_MAGIC) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid IVFC magic word for NCA RomFS section! Wrong KAEK? (0x%08X)", bswap_32(dec_nca_header->fs_headers[romfs_index].romfs_superblock.ivfc_header.magic)); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid IVFC magic word for NCA RomFS section! Wrong KAEK? (0x%08X)", bswap_32(dec_nca_header->fs_headers[romfs_index].romfs_superblock.ivfc_header.magic)); return false; } if (dec_nca_header->fs_headers[romfs_index].crypt_type != NCA_FS_HEADER_CRYPT_CTR) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid AES crypt type for NCA RomFS section! (0x%02X)", dec_nca_header->fs_headers[romfs_index].crypt_type); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid AES crypt type for NCA RomFS section! (0x%02X)", dec_nca_header->fs_headers[romfs_index].crypt_type); return false; } @@ -1658,7 +1631,7 @@ bool readRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, if (romfs_offset < section_offset || romfs_offset >= (section_offset + section_size) || romfs_size < ROMFS_HEADER_SIZE || (romfs_offset + romfs_size) > (section_offset + section_size)) { - uiDrawString("Error: invalid offset/size for NCA RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid offset/size for NCA RomFS section!"); return false; } @@ -1666,14 +1639,13 @@ bool readRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, romfs_offset, &romFsHeader, sizeof(romfs_header), false)) { breaks++; - uiDrawString("Failed to read NCA RomFS section header!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read NCA RomFS section header!"); return false; } if (romFsHeader.headerSize != ROMFS_HEADER_SIZE) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid header size for NCA RomFS section! (0x%016lX at 0x%016lX)", romFsHeader.headerSize, romfs_offset); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid header size for NCA RomFS section! (0x%016lX at 0x%016lX)", romFsHeader.headerSize, romfs_offset); return false; } @@ -1683,9 +1655,9 @@ bool readRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, romfs_filetable_offset = (romfs_offset + romFsHeader.fileTableOff); romfs_filetable_size = romFsHeader.fileTableSize; - if (romfs_dirtable_offset >= (section_offset + section_size) || !romfs_dirtable_size || (romfs_dirtable_offset + romfs_dirtable_size) >= (section_offset + section_size) || romfs_filetable_offset >= (section_offset + section_size) || !romfs_filetable_size || (romfs_filetable_offset + romfs_filetable_size) >= (section_offset + section_size)) + if (romfs_dirtable_offset >= (section_offset + section_size) || !romfs_dirtable_size || (romfs_dirtable_offset + romfs_dirtable_size) > (section_offset + section_size) || romfs_filetable_offset >= (section_offset + section_size) || !romfs_filetable_size || (romfs_filetable_offset + romfs_filetable_size) > (section_offset + section_size)) { - uiDrawString("Error: invalid directory/file table for NCA RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid directory/file table for NCA RomFS section!"); return false; } @@ -1693,21 +1665,21 @@ bool readRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, if (romfs_filedata_offset >= (section_offset + section_size)) { - uiDrawString("Error: invalid file data block offset for NCA RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid file data block offset for NCA RomFS section!"); return false; } romfs_dir_entries = calloc(1, romfs_dirtable_size); if (!romfs_dir_entries) { - uiDrawString("Error: unable to allocate memory for NCA RomFS section directory entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for NCA RomFS section directory entries!"); return false; } if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, romfs_dirtable_offset, romfs_dir_entries, romfs_dirtable_size, false)) { breaks++; - uiDrawString("Failed to read NCA RomFS section directory entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read NCA RomFS section directory entries!"); free(romfs_dir_entries); return false; } @@ -1715,7 +1687,7 @@ bool readRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, romfs_file_entries = calloc(1, romfs_filetable_size); if (!romfs_file_entries) { - uiDrawString("Error: unable to allocate memory for NCA RomFS section file entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for NCA RomFS section file entries!"); free(romfs_dir_entries); return false; } @@ -1723,7 +1695,7 @@ bool readRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, romfs_filetable_offset, romfs_file_entries, romfs_filetable_size, false)) { breaks++; - uiDrawString("Failed to read NCA RomFS section file entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read NCA RomFS section file entries!"); free(romfs_file_entries); free(romfs_dir_entries); return false; @@ -1753,7 +1725,7 @@ bool readBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, { if (!ncmStorage || !ncaId || !dec_nca_header || !decrypted_nca_keys || !romFsContext.section_offset || !romFsContext.section_size || !romFsContext.romfs_dir_entries || !romFsContext.romfs_file_entries) { - uiDrawString("Error: invalid parameters to read BKTR section from NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to read BKTR section from NCA!"); return false; } @@ -1780,7 +1752,7 @@ bool readBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, if (!found_bktr) { - uiDrawString("Error: NCA doesn't hold a BKTR section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: NCA doesn't hold a BKTR section!"); return false; } @@ -1789,8 +1761,7 @@ bool readBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, if (!bktrContext.section_offset || bktrContext.section_offset < NCA_FULL_HEADER_LENGTH || !bktrContext.section_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid offset/size for NCA BKTR section! (#%u)", bktr_index); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid offset/size for NCA BKTR section! (#%u)", bktr_index); return false; } @@ -1813,28 +1784,25 @@ bool readBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, if (bswap_32(bktrContext.superblock.ivfc_header.magic) != IVFC_MAGIC) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid IVFC magic word for NCA BKTR section! Wrong KAEK? (0x%08X)", bswap_32(bktrContext.superblock.ivfc_header.magic)); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid IVFC magic word for NCA BKTR section! Wrong KAEK? (0x%08X)", bswap_32(bktrContext.superblock.ivfc_header.magic)); return false; } if (dec_nca_header->fs_headers[bktr_index].crypt_type != NCA_FS_HEADER_CRYPT_BKTR) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid AES crypt type for NCA BKTR section! (0x%02X)", dec_nca_header->fs_headers[bktr_index].crypt_type); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid AES crypt type for NCA BKTR section! (0x%02X)", dec_nca_header->fs_headers[bktr_index].crypt_type); return false; } if (bswap_32(bktrContext.superblock.relocation_header.magic) != BKTR_MAGIC || bswap_32(bktrContext.superblock.subsection_header.magic) != BKTR_MAGIC) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid BKTR magic word for NCA BKTR relocation/subsection header! (0x%02X | 0x%02X)", bswap_32(bktrContext.superblock.relocation_header.magic), bswap_32(bktrContext.superblock.subsection_header.magic)); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid BKTR magic word for NCA BKTR relocation/subsection header! (0x%02X | 0x%02X)", bswap_32(bktrContext.superblock.relocation_header.magic), bswap_32(bktrContext.superblock.subsection_header.magic)); return false; } if ((bktrContext.superblock.relocation_header.offset + bktrContext.superblock.relocation_header.size) != bktrContext.superblock.subsection_header.offset || (bktrContext.superblock.subsection_header.offset + bktrContext.superblock.subsection_header.size) != bktrContext.section_size) { - uiDrawString("Error: invalid layout for NCA BKTR section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid layout for NCA BKTR section!"); return false; } @@ -1842,7 +1810,7 @@ bool readBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, bktrContext.relocation_block = calloc(1, bktrContext.superblock.relocation_header.size + ((0x3FF0 / sizeof(u64)) * sizeof(bktr_relocation_entry_t))); if (!bktrContext.relocation_block) { - uiDrawString("Error: unable to allocate memory for NCA BKTR relocation header!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for NCA BKTR relocation header!"); return false; } @@ -1850,7 +1818,7 @@ bool readBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, bktrContext.subsection_block = calloc(1, bktrContext.superblock.subsection_header.size + ((0x3FF0 / sizeof(u64)) * sizeof(bktr_subsection_entry_t)) + sizeof(bktr_subsection_entry_t)); if (!bktrContext.subsection_block) { - uiDrawString("Error: unable to allocate memory for NCA BKTR subsection header!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for NCA BKTR subsection header!"); goto out; } @@ -1858,7 +1826,7 @@ bool readBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &(bktrContext.aes_ctx), bktrContext.section_offset + bktrContext.superblock.relocation_header.offset, bktrContext.relocation_block, bktrContext.superblock.relocation_header.size, false)) { breaks++; - uiDrawString("Failed to read NCA BKTR relocation header!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read NCA BKTR relocation header!"); goto out; } @@ -1866,13 +1834,13 @@ bool readBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &(bktrContext.aes_ctx), bktrContext.section_offset + bktrContext.superblock.subsection_header.offset, bktrContext.subsection_block, bktrContext.superblock.subsection_header.size, false)) { breaks++; - uiDrawString("Failed to read NCA BKTR subsection header!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read NCA BKTR subsection header!"); goto out; } if (bktrContext.subsection_block->total_size != bktrContext.superblock.subsection_header.offset) { - uiDrawString("Error: invalid NCA BKTR subsection header size!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NCA BKTR subsection header size!"); goto out; } @@ -1920,21 +1888,20 @@ bool readBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, // Do not check the RomFS size, because it reflects the full patched RomFS image if (!bktrContext.romfs_offset || bktrContext.romfs_size < ROMFS_HEADER_SIZE || (bktrContext.section_offset + bktrContext.romfs_offset) > (bktrContext.section_offset + bktrContext.section_size)) { - uiDrawString("Error: invalid offset/size for NCA BKTR RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid offset/size for NCA BKTR RomFS section!"); goto out; } if (!readBktrSectionBlock(bktrContext.romfs_offset, &romFsHeader, sizeof(romfs_header))) { breaks++; - uiDrawString("Failed to read NCA BKTR RomFS section header!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read NCA BKTR RomFS section header!"); goto out; } if (romFsHeader.headerSize != ROMFS_HEADER_SIZE) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid header size for NCA BKTR RomFS section! (0x%016lX at 0x%016lX)", romFsHeader.headerSize, bktrContext.section_offset + bktrContext.romfs_offset); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid header size for NCA BKTR RomFS section! (0x%016lX at 0x%016lX)", romFsHeader.headerSize, bktrContext.section_offset + bktrContext.romfs_offset); goto out; } @@ -1947,7 +1914,7 @@ bool readBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, // Then again, do not check these offsets/sizes, because they reflect the patched RomFS image if (!bktrContext.romfs_dirtable_offset || !bktrContext.romfs_dirtable_size || !bktrContext.romfs_filetable_offset || !bktrContext.romfs_filetable_size) { - uiDrawString("Error: invalid directory/file table for NCA BKTR RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid directory/file table for NCA BKTR RomFS section!"); goto out; } @@ -1955,35 +1922,35 @@ bool readBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, if (!bktrContext.romfs_filedata_offset) { - uiDrawString("Error: invalid file data block offset for NCA BKTR RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid file data block offset for NCA BKTR RomFS section!"); goto out; } bktrContext.romfs_dir_entries = calloc(1, bktrContext.romfs_dirtable_size); if (!bktrContext.romfs_dir_entries) { - uiDrawString("Error: unable to allocate memory for NCA BKTR RomFS section directory entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for NCA BKTR RomFS section directory entries!"); goto out; } if (!readBktrSectionBlock(bktrContext.romfs_dirtable_offset, bktrContext.romfs_dir_entries, bktrContext.romfs_dirtable_size)) { breaks++; - uiDrawString("Failed to read NCA BKTR RomFS section directory entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read NCA BKTR RomFS section directory entries!"); goto out; } bktrContext.romfs_file_entries = calloc(1, bktrContext.romfs_filetable_size); if (!bktrContext.romfs_file_entries) { - uiDrawString("Error: unable to allocate memory for NCA BKTR RomFS section file entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for NCA BKTR RomFS section file entries!"); goto out; } if (!readBktrSectionBlock(bktrContext.romfs_filetable_offset, bktrContext.romfs_file_entries, bktrContext.romfs_filetable_size)) { breaks++; - uiDrawString("Failed to read NCA RomFS section file entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read NCA RomFS section file entries!"); goto out; } @@ -2026,26 +1993,25 @@ bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId { if (!ncmStorage || !ncaId || !dec_nca_header || !decrypted_nca_keys || !program_mod_data || !outBuf || !outBufSize) { - uiDrawString("Error: invalid parameters to generate \"programinfo.xml\"!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to generate \"programinfo.xml\"!"); return false; } if (dec_nca_header->fs_headers[0].partition_type != NCA_FS_HEADER_PARTITION_PFS0 || dec_nca_header->fs_headers[0].fs_type != NCA_FS_HEADER_FSTYPE_PFS0) { - uiDrawString("Error: Program NCA section #0 doesn't hold a PFS0 partition!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: Program NCA section #0 doesn't hold a PFS0 partition!"); return false; } if (!dec_nca_header->fs_headers[0].pfs0_superblock.pfs0_size) { - uiDrawString("Error: invalid size for PFS0 partition in Program NCA section #0!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid size for PFS0 partition in Program NCA section #0!"); return false; } if (dec_nca_header->fs_headers[0].crypt_type != NCA_FS_HEADER_CRYPT_CTR) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid AES crypt type for Program NCA section #0! (0x%02X)", dec_nca_header->fs_headers[0].crypt_type); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid AES crypt type for Program NCA section #0! (0x%02X)", dec_nca_header->fs_headers[0].crypt_type); return false; } @@ -2082,7 +2048,7 @@ bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId if (!section_offset || section_offset < NCA_FULL_HEADER_LENGTH || !nca_pfs0_offset) { - uiDrawString("Error: invalid offsets for Program NCA section #0!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid offsets for Program NCA section #0!"); return false; } @@ -2104,34 +2070,33 @@ bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, nca_pfs0_offset, &nca_pfs0_header, sizeof(pfs0_header), false)) { breaks++; - uiDrawString("Failed to read Program NCA section #0 PFS0 partition header!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read Program NCA section #0 PFS0 partition header!"); return false; } if (bswap_32(nca_pfs0_header.magic) != PFS0_MAGIC) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid magic word for Program NCA section #0 PFS0 partition! Wrong KAEK? (0x%08X)", bswap_32(nca_pfs0_header.magic)); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid magic word for Program NCA section #0 PFS0 partition! Wrong KAEK? (0x%08X)", bswap_32(nca_pfs0_header.magic)); return false; } if (!nca_pfs0_header.file_cnt || !nca_pfs0_header.str_table_size) { - uiDrawString("Error: Program NCA section #0 PFS0 partition is empty! Wrong KAEK?", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: Program NCA section #0 PFS0 partition is empty! Wrong KAEK?"); return false; } nca_pfs0_entries = calloc(nca_pfs0_header.file_cnt, sizeof(pfs0_entry_table)); if (!nca_pfs0_entries) { - uiDrawString("Error: unable to allocate memory for Program NCA section #0 PFS0 partition entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for Program NCA section #0 PFS0 partition entries!"); return false; } if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, nca_pfs0_offset + sizeof(pfs0_header), nca_pfs0_entries, (u64)nca_pfs0_header.file_cnt * sizeof(pfs0_entry_table), false)) { breaks++; - uiDrawString("Failed to read Program NCA section #0 PFS0 partition entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read Program NCA section #0 PFS0 partition entries!"); goto out; } @@ -2140,14 +2105,14 @@ bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId nca_pfs0_str_table = calloc((u64)nca_pfs0_header.str_table_size, sizeof(char)); if (!nca_pfs0_str_table) { - uiDrawString("Error: unable to allocate memory for Program NCA section #0 PFS0 string table!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for Program NCA section #0 PFS0 string table!"); goto out; } if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, nca_pfs0_str_table_offset, nca_pfs0_str_table, (u64)nca_pfs0_header.str_table_size, false)) { breaks++; - uiDrawString("Failed to read Program NCA section #0 PFS0 string table!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read Program NCA section #0 PFS0 string table!"); goto out; } @@ -2157,7 +2122,7 @@ bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId programInfoXml = calloc(NSP_XML_BUFFER_SIZE, sizeof(char)); if (!programInfoXml) { - uiDrawString("Error: unable to allocate memory for the \"programinfo.xml\" contents!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the \"programinfo.xml\" contents!"); goto out; } @@ -2181,7 +2146,7 @@ bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId if (!found_npdm) { - uiDrawString("Error: unable to allocate memory for the \"programinfo.xml\" contents!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the \"programinfo.xml\" contents!"); goto out; } @@ -2189,14 +2154,13 @@ bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, nca_pfs0_data_offset + nca_pfs0_entries[npdmEntry].file_offset, &npdm_header, sizeof(npdm_t), false)) { breaks++; - uiDrawString("Failed to read NPDM entry header from Program NCA section #0 PFS0!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read NPDM entry header from Program NCA section #0 PFS0!"); goto out; } if (bswap_32(npdm_header.magic) != META_MAGIC) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid NPDM META magic word! (0x%08X)", bswap_32(npdm_header.magic)); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NPDM META magic word! (0x%08X)", bswap_32(npdm_header.magic)); goto out; } @@ -2204,14 +2168,14 @@ bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId npdm_acid_section = malloc(npdm_header.acid_size); if (!npdm_acid_section) { - uiDrawString("Error: unable to allocate memory for the Program NCA NPDM ACID section contents!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the Program NCA NPDM ACID section contents!"); goto out; } if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, nca_pfs0_data_offset + nca_pfs0_entries[npdmEntry].file_offset + (u64)npdm_header.acid_offset, npdm_acid_section, (u64)npdm_header.acid_size, false)) { breaks++; - uiDrawString("Failed to read ACID section from Program NCA NPDM!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read ACID section from Program NCA NPDM!"); goto out; } @@ -2228,21 +2192,21 @@ bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId mbedtls_base64_encode(NULL, 0, &npdm_acid_section_b64_size, npdm_acid_section, (u64)npdm_header.acid_size); if (npdm_acid_section_b64_size <= (u64)npdm_header.acid_size) { - uiDrawString("Error: invalid Base64 conversion length for the ACID section from Program NCA NPDM!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid Base64 conversion length for the ACID section from Program NCA NPDM!"); goto out; } npdm_acid_section_b64 = calloc(npdm_acid_section_b64_size + 1, sizeof(char)); if (!npdm_acid_section_b64) { - uiDrawString("Error: unable to allocate memory for the Base64 converted ACID section from Program NCA NPDM!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the Base64 converted ACID section from Program NCA NPDM!"); goto out; } // Perform the Base64 conversion if (mbedtls_base64_encode((unsigned char*)npdm_acid_section_b64, npdm_acid_section_b64_size + 1, &npdm_acid_section_b64_size, npdm_acid_section, (u64)npdm_header.acid_size) != 0) { - uiDrawString("Error: Base64 conversion failed for the ACID section from Program NCA NPDM!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: Base64 conversion failed for the ACID section from Program NCA NPDM!"); goto out; } @@ -2276,8 +2240,7 @@ bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, curFileOffset, &nsoHeader, sizeof(nso_header_t), false)) { breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read 0x%016lX bytes from \"%s\" in Program NCA section #0 PFS0 partition!", sizeof(nso_header_t), curFilename); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read 0x%016lX bytes from \"%s\" in Program NCA section #0 PFS0 partition!", sizeof(nso_header_t), curFilename); proceed = false; break; } @@ -2313,8 +2276,7 @@ bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &aes_ctx, curFileOffset, &nsoHeader, sizeof(nso_header_t), false)) { breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read 0x%016lX bytes from \"%s\" in Program NCA section #0 PFS0 partition!", sizeof(nso_header_t), curFilename); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read 0x%016lX bytes from \"%s\" in Program NCA section #0 PFS0 partition!", sizeof(nso_header_t), curFilename); proceed = false; break; } @@ -2865,7 +2827,7 @@ bool retrieveNacpDataFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaI { if (!ncmStorage || !ncaId || !dec_nca_header || !decrypted_nca_keys || !out_nacp_xml || !out_nacp_xml_size || !out_nacp_icons_ctx || !out_nacp_icons_ctx_cnt) { - uiDrawString("Error: invalid parameters to generate NACP XML!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to generate NACP XML!"); return false; } @@ -2914,14 +2876,14 @@ bool retrieveNacpDataFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaI if (!found_nacp) { - uiDrawString("Error: unable to find \"control.nacp\" file in Control NCA RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to find \"control.nacp\" file in Control NCA RomFS section!"); goto out; } if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &(romFsContext.aes_ctx), romFsContext.romfs_filedata_offset + entry->dataOff, &controlNacp, sizeof(nacp_t), false)) { breaks++; - uiDrawString("Failed to read \"control.nacp\" from RomFS section in Control NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read \"control.nacp\" from RomFS section in Control NCA!"); goto out; } @@ -2929,7 +2891,7 @@ bool retrieveNacpDataFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaI nacpXml = calloc(NSP_XML_BUFFER_SIZE, sizeof(char)); if (!nacpXml) { - uiDrawString("Error: unable to allocate memory for the NACP XML!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the NACP XML!"); goto out; } @@ -3063,7 +3025,7 @@ bool retrieveNacpDataFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaI nacpIcons = calloc(nacpIconCnt, sizeof(nacp_icons_ctx)); if (!nacpIcons) { - uiDrawString("Error: unable to allocate memory for the NACP icons!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the NACP icons!"); goto out; } @@ -3106,14 +3068,13 @@ bool retrieveNacpDataFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaI strcat(nacpXml, tmp); // Fill details for our NACP icon context - sprintf(nacpIcons[j].filename, "%s.nx.%s.jpg", ncaIdStr, getNacpLangName(i)); + sprintf(nacpIcons[j].filename, "%s.nx.%s.jpg", ncaIdStr, getNacpLangName(i)); // Temporary, the NCA ID is subject to change nacpIcons[j].icon_size = entry->dataSize; if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &(romFsContext.aes_ctx), romFsContext.romfs_filedata_offset + entry->dataOff, nacpIcons[j].icon_data, nacpIcons[j].icon_size, false)) { breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read \"%s\" from RomFS section in Control NCA!", tmp); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read \"%s\" from RomFS section in Control NCA!", tmp); goto out; } @@ -3302,7 +3263,7 @@ bool retrieveLegalInfoXmlFromNca(NcmContentStorage *ncmStorage, const NcmNcaId * { if (!ncmStorage || !ncaId || !dec_nca_header || !decrypted_nca_keys || !outBuf) { - uiDrawString("Error: invalid parameters to retrieve \"legalinfo.xml\"!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to retrieve \"legalinfo.xml\"!"); return false; } @@ -3331,7 +3292,7 @@ bool retrieveLegalInfoXmlFromNca(NcmContentStorage *ncmStorage, const NcmNcaId * if (!found_legalinfo) { - uiDrawString("Error: unable to find \"legalinfo.xml\" file in Manual NCA RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to find \"legalinfo.xml\" file in Manual NCA RomFS section!"); goto out; } @@ -3340,14 +3301,14 @@ bool retrieveLegalInfoXmlFromNca(NcmContentStorage *ncmStorage, const NcmNcaId * legalInfoXml = calloc(legalInfoXmlSize, sizeof(char)); if (!legalInfoXml) { - uiDrawString("Error: unable to allocate memory for the \"legalinfo.xml\" contents!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the \"legalinfo.xml\" contents!"); goto out; } if (!processNcaCtrSectionBlock(ncmStorage, ncaId, &(romFsContext.aes_ctx), romFsContext.romfs_filedata_offset + entry->dataOff, legalInfoXml, legalInfoXmlSize, false)) { breaks++; - uiDrawString("Failed to read \"legalinfo.xml\" from RomFS section in Manual NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read \"legalinfo.xml\" from RomFS section in Manual NCA!"); goto out; } diff --git a/source/nca.h b/source/nca.h index ef08b5e..2c77fef 100644 --- a/source/nca.h +++ b/source/nca.h @@ -280,7 +280,6 @@ typedef struct { u8 *block_data[2]; u64 block_offset[2]; // Relative to NCA start u64 block_size[2]; - u64 acid_pubkey_offset; // Relative to block_data[0] start } PACKED nca_program_mod_data; typedef struct { diff --git a/source/nso.c b/source/nso.c index 77d7bc4..8f6eb6e 100644 --- a/source/nso.c +++ b/source/nso.c @@ -13,8 +13,6 @@ extern int breaks; extern int font_height; -extern char strbuf[NAME_BUF_LEN * 4]; - /* Statically allocated variables */ static u8 *nsoBinaryData = NULL; @@ -53,7 +51,7 @@ bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes { if (!ncmStorage || !ncaId || !aes_ctx || !nso_base_offset || !nsoHeader) { - uiDrawString("Error: invalid parameters to load .text, .rodata and .data sections from NSO in Program NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to load .text, .rodata and .data sections from NSO in Program NCA!"); return false; } @@ -94,8 +92,7 @@ bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes curCompressedSection = malloc(curCompressedSectionSize); if (!curCompressedSection) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to allocate memory for the compressed %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data"))); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the compressed %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data"))); success = false; break; } @@ -103,8 +100,7 @@ bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes if (!processNcaCtrSectionBlock(ncmStorage, ncaId, aes_ctx, curCompressedSectionOffset, curCompressedSection, curCompressedSectionSize, false)) { breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read 0x%016lX bytes %s section from NSO in Program NCA!", curCompressedSectionSize, (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data"))); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read 0x%016lX bytes %s section from NSO in Program NCA!", curCompressedSectionSize, (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data"))); free(curCompressedSection); success = false; break; @@ -114,8 +110,7 @@ bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes { if (curDecompressedSectionSize <= curCompressedSectionSize) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid decompressed size for %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data"))); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid decompressed size for %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data"))); free(curCompressedSection); success = false; break; @@ -125,8 +120,7 @@ bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes curDecompressedSection = malloc(curDecompressedSectionSize); if (!curDecompressedSection) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to allocate memory for the decompressed %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data"))); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the decompressed %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data"))); free(curCompressedSection); success = false; break; @@ -134,8 +128,7 @@ bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes if (LZ4_decompress_safe((const char*)curCompressedSection, (char*)curDecompressedSection, (int)curCompressedSectionSize, (int)curDecompressedSectionSize) != (int)curDecompressedSectionSize) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to allocate memory for the decompressed %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data"))); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to decompress %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data"))); free(curDecompressedSection); free(curCompressedSection); success = false; @@ -190,7 +183,7 @@ bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes u64 finalTextSectionSize = nsoTextSectionSize; u64 finalRodataSectionSize = nsoRodataSectionSize; - nsoBinaryDataSize = finalTextSectionSize = nsoTextSectionSize; + nsoBinaryDataSize = nsoTextSectionSize; if ((u64)nsoHeader->rodata_segment_header.memory_offset > nsoBinaryDataSize) { @@ -232,7 +225,7 @@ bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes nsoBinaryDataSectionOffset = (u64)nsoHeader->data_segment_header.memory_offset; nsoBinaryDataSectionSize = nsoDataSectionSize; } else { - uiDrawString("Error: unable to allocate memory for full decompressed NSO in Program NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate %lu bytes for full decompressed NSO in Program NCA!", nsoBinaryDataSize); nsoBinaryDataSize = 0; success = false; } @@ -249,7 +242,7 @@ bool retrieveMiddlewareListFromNso(NcmContentStorage *ncmStorage, const NcmNcaId { if (!ncmStorage || !ncaId || !aes_ctx || !nso_filename || !strlen(nso_filename) || !nso_base_offset || !nsoHeader || !programInfoXml) { - uiDrawString("Error: invalid parameters to retrieve middleware list from NSO in Program NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to retrieve middleware list from NSO in Program NCA!"); return false; } @@ -300,7 +293,7 @@ bool retrieveSymbolsListFromNso(NcmContentStorage *ncmStorage, const NcmNcaId *n { if (!ncmStorage || !ncaId || !aes_ctx || !nso_filename || !strlen(nso_filename) || !nso_base_offset || !nsoHeader || !programInfoXml) { - uiDrawString("Error: invalid parameters to retrieve symbols list from NSO in Program NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to retrieve symbols list from NSO in Program NCA!"); return false; } @@ -340,8 +333,7 @@ bool retrieveSymbolsListFromNso(NcmContentStorage *ncmStorage, const NcmNcaId *n if (bswap_32(mod_magic) != MOD_MAGIC) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: invalid MOD0 magic word in decompressed NSO from Program NCA! (0x%08X)", bswap_32(mod_magic)); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid MOD0 magic word in decompressed NSO from Program NCA! (0x%08X)", bswap_32(mod_magic)); goto out; } diff --git a/source/rsa.c b/source/rsa.c index 01074fb..963f28b 100644 --- a/source/rsa.c +++ b/source/rsa.c @@ -16,8 +16,6 @@ extern int breaks; extern int font_height; -extern char strbuf[NAME_BUF_LEN * 4]; - bool rsa_sign(void* input, size_t input_size, unsigned char* output, size_t output_size) { unsigned char hash[32]; @@ -58,16 +56,13 @@ bool rsa_sign(void* input, size_t input_size, unsigned char* output, size_t outp memcpy(output, buf, output_size); success = true; } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "rsa_sign: mbedtls_pk_sign failed! (%d)", ret); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "rsa_sign: mbedtls_pk_sign failed! (%d)", ret); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "rsa_sign: mbedtls_pk_parse_key failed! (%d)", ret); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "rsa_sign: mbedtls_pk_parse_key failed! (%d)", ret); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "rsa_sign: mbedtls_ctr_drbg_seed failed! (%d)", ret); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "rsa_sign: mbedtls_ctr_drbg_seed failed! (%d)", ret); } mbedtls_ctr_drbg_free(&ctr_drbg); diff --git a/source/ui.c b/source/ui.c index 970b834..ef9f173 100644 --- a/source/ui.c +++ b/source/ui.c @@ -21,6 +21,8 @@ /* Extern variables */ +extern dumpOptions dumpCfg; + extern AppletType programAppletType; extern bool runningSxOs; @@ -79,7 +81,7 @@ extern u8 *ncaCtrBuf; extern orphan_patch_addon_entry *orphanEntries; -extern char strbuf[NAME_BUF_LEN * 4]; +extern char strbuf[NAME_BUF_LEN]; /* Statically allocated variables */ @@ -91,6 +93,9 @@ static Framebuffer fb; static u32 *framebuf = NULL; static u32 framebuf_width = 0; +static const u8 bgColors[3] = { BG_COLOR_RGB }; +static const u8 hlBgColors[3] = { HIGHLIGHT_BG_COLOR_RGB }; + int cursor = 0; int scroll = 0; int breaks = 0; @@ -117,16 +122,11 @@ static u32 selectedPartitionIndex; static u32 selectedFileIndex; static nspDumpType selectedNspDumpType; -static batchModeSourceStorage batchModeSrc; static bool exeFsUpdateFlag = false; static selectedRomFsType curRomFsType = ROMFS_TYPE_APP; -static bool highlight = false; - -static bool isFat32 = false, keepCert = false, trimDump = false, calcCrc = true, setXciArchiveBit = false, removeConsoleData = false, tiklessDump = false; - -static bool dumpAppTitles = false, dumpPatchTitles = false, dumpAddOnTitles = false, skipDumpedTitles = false; +bool highlight = false; static char statusMessage[2048] = {'\0'}; static int statusMessageFadeout = 0; @@ -143,11 +143,23 @@ static const char *dirHighlightIconPath = "romfs:/browser/dir_highlight.jpg"; static u8 *dirHighlightIconBuf = NULL; static const char *fileNormalIconPath = "romfs:/browser/file_normal.jpg"; -u8 *fileNormalIconBuf = NULL; +static u8 *fileNormalIconBuf = NULL; static const char *fileHighlightIconPath = "romfs:/browser/file_highlight.jpg"; static u8 *fileHighlightIconBuf = NULL; +static const char *enabledNormalIconPath = "romfs:/browser/enabled_normal.jpg"; +u8 *enabledNormalIconBuf = NULL; + +static const char *enabledHighlightIconPath = "romfs:/browser/enabled_highlight.jpg"; +u8 *enabledHighlightIconBuf = NULL; + +static const char *disabledNormalIconPath = "romfs:/browser/disabled_normal.jpg"; +u8 *disabledNormalIconBuf = NULL; + +static const char *disabledHighlightIconPath = "romfs:/browser/disabled_highlight.jpg"; +u8 *disabledHighlightIconBuf = NULL; + static const char *appHeadline = "NXDumpTool v" APP_VERSION ".\nOriginal codebase by MCMrARM.\nUpdated and maintained by DarkMatterCore.\n\n"; static const char *appControlsCommon = "[ " NINTENDO_FONT_DPAD " / " NINTENDO_FONT_LSTICK " / " NINTENDO_FONT_RSTICK " ] Move | [ " NINTENDO_FONT_A " ] Select | [ " NINTENDO_FONT_B " ] Back | [ " NINTENDO_FONT_PLUS " ] Exit"; static const char *appControlsGameCardMultiApp = "[ " NINTENDO_FONT_DPAD " / " NINTENDO_FONT_LSTICK " / " NINTENDO_FONT_RSTICK " ] Move | [ " NINTENDO_FONT_A " ] Select | [ " NINTENDO_FONT_B " ] Back | [ " NINTENDO_FONT_L " / " NINTENDO_FONT_R " / " NINTENDO_FONT_ZL " / " NINTENDO_FONT_ZR " ] Change displayed base application info | [ " NINTENDO_FONT_PLUS " ] Exit"; @@ -176,13 +188,13 @@ static const char *romFsMenuItems[] = { "RomFS section data dump", "Browse RomFS static const char *romFsSectionDumpMenuItems[] = { "Start RomFS data dump process", "Base application to dump: ", "Use update/DLC: " }; static const char *romFsSectionBrowserMenuItems[] = { "Browse RomFS section", "Base application to browse: ", "Use update/DLC: " }; static const char *sdCardEmmcMenuItems[] = { "Nintendo Submission Package (NSP) dump", "ExeFS options", "RomFS options" }; -static const char *batchModeMenuItems[] = { "Start batch dump process", "Dump base applications: ", "Dump updates: ", "Dump DLCs: ", "Split output dumps (FAT32 support): ", "Remove console specific data: ", "Generate ticket-less dumps: ", "Skip already dumped titles: ", "Source storage: " }; +static const char *batchModeMenuItems[] = { "Start batch dump process", "Dump base applications: ", "Dump updates: ", "Dump DLCs: ", "Split output dumps (FAT32 support): ", "Remove console specific data: ", "Generate ticket-less dumps: ", "Skip already dumped titles: ", "Source storage: ", "Remember dumped titles: " }; static const char *updateMenuItems[] = { "Update NSWDB.COM XML database", "Update application" }; void uiFill(int x, int y, int width, int height, u8 r, u8 g, u8 b) { /* Perform validity checks */ - if ((x + width) < 0 || (y + height) < 0 || x >= FB_WIDTH || y >= FB_HEIGHT) return; + if (width <= 0 || height <= 0 || (x + width) < 0 || (y + height) < 0 || x >= FB_WIDTH || y >= FB_HEIGHT) return; if (x < 0) { @@ -274,7 +286,7 @@ bool uiLoadJpgFromMem(u8 *rawJpg, size_t rawJpgSize, int expectedWidth, int expe { if (!rawJpg || !rawJpgSize || !expectedWidth || !expectedHeight || !desiredWidth || !desiredHeight || !outBuf) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: invalid parameters to process JPG image buffer."); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "uiLoadJpgFromMem: invalid parameters to process JPG image buffer."); return false; } @@ -322,27 +334,27 @@ bool uiLoadJpgFromMem(u8 *rawJpg, size_t rawJpgSize, int expectedWidth, int expe success = true; } else { free(jpgScaledBuf); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: tjDecompress2 failed (%d).", ret); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "uiLoadJpgFromMem: tjDecompress2 failed (%d).", ret); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: unable to allocated memory for the scaled RGB image output."); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "uiLoadJpgFromMem: unable to allocated memory for the scaled RGB image output."); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: unable to find a valid scaling factor."); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "uiLoadJpgFromMem: unable to find a valid scaling factor."); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: error retrieving scaling factors."); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "uiLoadJpgFromMem: error retrieving scaling factors."); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: invalid image width/height."); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "uiLoadJpgFromMem: invalid image width/height."); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: tjDecompressHeader2 failed (%d).", ret); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "uiLoadJpgFromMem: tjDecompressHeader2 failed (%d).", ret); } tjDestroy(_jpegDecompressor); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: tjInitDecompress failed."); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "uiLoadJpgFromMem: tjInitDecompress failed."); } return success; @@ -352,7 +364,7 @@ bool uiLoadJpgFromFile(const char *filename, int expectedWidth, int expectedHeig { if (!filename || !desiredWidth || !desiredHeight || !outBuf) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromFile: invalid parameters to process JPG image file.\n"); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "uiLoadJpgFromFile: invalid parameters to process JPG image file.\n"); return false; } @@ -363,7 +375,7 @@ bool uiLoadJpgFromFile(const char *filename, int expectedWidth, int expectedHeig fp = fopen(filename, "rb"); if (!fp) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromFile: failed to open file \"%s\".\n", filename); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "uiLoadJpgFromFile: failed to open file \"%s\".\n", filename); return false; } @@ -373,7 +385,7 @@ bool uiLoadJpgFromFile(const char *filename, int expectedWidth, int expectedHeig if (!filesize) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromFile: file \"%s\" is empty.\n", filename); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "uiLoadJpgFromFile: file \"%s\" is empty.\n", filename); fclose(fp); return false; } @@ -381,7 +393,7 @@ bool uiLoadJpgFromFile(const char *filename, int expectedWidth, int expectedHeig buf = malloc(filesize); if (!buf) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromFile: error allocating memory for image \"%s\".\n", filename); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "uiLoadJpgFromFile: error allocating memory for image \"%s\".\n", filename); fclose(fp); return false; } @@ -392,7 +404,7 @@ bool uiLoadJpgFromFile(const char *filename, int expectedWidth, int expectedHeig if (read != filesize) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromFile: error reading image \"%s\".\n", filename); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "uiLoadJpgFromFile: error reading image \"%s\".\n", filename); free(buf); return false; } @@ -410,16 +422,14 @@ void uiDrawChar(FT_Bitmap *bitmap, int x, int y, u8 r, u8 g, u8 b) { if (framebuf == NULL) return; - u32 framex, framey; + u32 framex, framey, framebuf_offset; u32 tmpx, tmpy; u8 *imageptr = bitmap->buffer; u8 src_val; float opacity; - u8 fontR; - u8 fontG; - u8 fontB; + u8 fontR, fontG, fontB; if (bitmap->pixel_mode != FT_PIXEL_MODE_GRAY) return; @@ -432,32 +442,22 @@ void uiDrawChar(FT_Bitmap *bitmap, int x, int y, u8 r, u8 g, u8 b) if (framex >= FB_WIDTH || framey >= FB_HEIGHT) continue; + framebuf_offset = ((framey * framebuf_width) + framex); + src_val = imageptr[tmpx]; if (!src_val) { /* Render background color */ - if (highlight) - { - framebuf[(framey * framebuf_width) + framex] = RGBA8_MAXALPHA(HIGHLIGHT_BG_COLOR_R, HIGHLIGHT_BG_COLOR_G, HIGHLIGHT_BG_COLOR_B); - } else { - framebuf[(framey * framebuf_width) + framex] = RGBA8_MAXALPHA(BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); - } + framebuf[framebuf_offset] = (highlight ? RGBA8_MAXALPHA(hlBgColors[0], hlBgColors[1], hlBgColors[2]) : RGBA8_MAXALPHA(bgColors[0], bgColors[1], bgColors[2])); } else { /* Calculate alpha (opacity) */ opacity = (src_val / 255.0); - if (highlight) - { - fontR = (r * opacity + (1 - opacity) * HIGHLIGHT_BG_COLOR_R); - fontG = (g * opacity + (1 - opacity) * HIGHLIGHT_BG_COLOR_G); - fontB = (b * opacity + (1 - opacity) * HIGHLIGHT_BG_COLOR_B); - } else { - fontR = (r * opacity + (1 - opacity) * BG_COLOR_RGB); - fontG = (g * opacity + (1 - opacity) * BG_COLOR_RGB); - fontB = (b * opacity + (1 - opacity) * BG_COLOR_RGB); - } + fontR = (r * opacity + (1 - opacity) * (highlight ? hlBgColors[0] : bgColors[0])); + fontG = (g * opacity + (1 - opacity) * (highlight ? hlBgColors[1] : bgColors[1])); + fontB = (b * opacity + (1 - opacity) * (highlight ? hlBgColors[2] : bgColors[2])); - framebuf[(framey * framebuf_width) + framex] = RGBA8_MAXALPHA(fontR, fontG, fontB); + framebuf[framebuf_offset] = RGBA8_MAXALPHA(fontR, fontG, fontB); } } @@ -465,33 +465,17 @@ void uiDrawChar(FT_Bitmap *bitmap, int x, int y, u8 r, u8 g, u8 b) } } -void uiScroll() +void uiDrawString(int x, int y, u8 r, u8 g, u8 b, const char *fmt, ...) { - if (framebuf == NULL) - { - /* Begin new frame */ - u32 stride; - framebuf = (u32*)framebufferBegin(&fb, &stride); - framebuf_width = (stride / sizeof(u32)); - } + if (!fmt || !*fmt) return; - u32 lx, ly; + char string[NAME_BUF_LEN] = {'\0'}; - for (ly = 0; ly < (FB_HEIGHT - font_height - 8); ly++) - { - for (lx = 0; lx < FB_WIDTH; lx++) - { - framebuf[(ly * framebuf_width) + lx] = framebuf[((ly + font_height) * framebuf_width) + lx]; - } - } + va_list args; + va_start(args, fmt); + vsnprintf(string, MAX_ELEMENTS(string), fmt, args); + va_end(args); - uiFill(0, FB_HEIGHT - (8 + (font_height + (font_height / 4))), FB_WIDTH, (8 + (font_height + (font_height / 4))), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); - - breaks = (FB_HEIGHT - (8 + (font_height + (font_height / 4))) + (font_height / 8)); -} - -void uiDrawString(const char *string, int x, int y, u8 r, u8 g, u8 b) -{ u32 tmpx = (x <= 8 ? 8 : (x + 8)); u32 tmpy = (font_height + (y <= 8 ? 8 : (y + 8))); @@ -511,12 +495,6 @@ void uiDrawString(const char *string, int x, int y, u8 r, u8 g, u8 b) framebuf_width = (stride / sizeof(u32)); } - if (tmpy >= FB_HEIGHT) - { - tmpy = (FB_HEIGHT - (8 + (font_height + (font_height / 4))) + (font_height / 8)); - uiScroll(); - } - for(i = 0; i < str_size;) { unitcount = decode_utf8(&tmpchar, (const u8*)&string[i]); @@ -526,7 +504,7 @@ void uiDrawString(const char *string, int x, int y, u8 r, u8 g, u8 b) if (tmpchar == '\n') { tmpx = 8; - tmpy += ((font_height + (font_height / 4)) + (font_height / 8)); + tmpy += LINE_HEIGHT; breaks++; continue; } else @@ -556,7 +534,7 @@ void uiDrawString(const char *string, int x, int y, u8 r, u8 g, u8 b) if ((tmpx + (sharedFontsFaces[j]->glyph->advance.x >> 6)) >= (FB_WIDTH - 8)) { tmpx = 8; - tmpy += ((font_height + (font_height / 4)) + (font_height / 8)); + tmpy += LINE_HEIGHT; breaks++; } @@ -567,9 +545,16 @@ void uiDrawString(const char *string, int x, int y, u8 r, u8 g, u8 b) } } -u32 uiGetStrWidth(const char *string) +u32 uiGetStrWidth(const char *fmt, ...) { - if (!string || !strlen(string)) return 0; + if (!fmt || !*fmt) return 0; + + char string[NAME_BUF_LEN] = {'\0'}; + + va_list args; + va_start(args, fmt); + vsnprintf(string, MAX_ELEMENTS(string), fmt, args); + va_end(args); FT_Error ret = 0; FT_UInt glyph_index = 0; @@ -627,23 +612,24 @@ void uiRefreshDisplay() void uiStatusMsg(const char *fmt, ...) { - statusMessageFadeout = 1000; - - va_list args; - va_start(args, fmt); - vsnprintf(statusMessage, sizeof(statusMessage) / sizeof(statusMessage[0]), fmt, args); - va_end(args); + statusMessageFadeout = 1000; + + va_list args; + va_start(args, fmt); + vsnprintf(statusMessage, MAX_ELEMENTS(statusMessage), fmt, args); + va_end(args); } void uiUpdateStatusMsg() { if (!strlen(statusMessage) || !statusMessageFadeout) return; - if ((statusMessageFadeout - 4) > BG_COLOR_RGB) + uiFill(0, FB_HEIGHT - (font_height + STRING_Y_POS(1)), FB_WIDTH, font_height + STRING_Y_POS(1), BG_COLOR_RGB); + + if ((statusMessageFadeout - 4) > bgColors[0]) { int fadeout = (statusMessageFadeout > 255 ? 255 : statusMessageFadeout); - uiFill(0, FB_HEIGHT - (8 + (font_height + (font_height / 4))), FB_WIDTH, (8 + (font_height + (font_height / 4))), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); - uiDrawString(statusMessage, 8, (FB_HEIGHT - (16 + (font_height + (font_height / 4))) + (font_height / 8)), fadeout, fadeout, fadeout); + uiDrawString(STRING_X_POS, FB_HEIGHT - (font_height + STRING_Y_POS(1)), fadeout, fadeout, fadeout, statusMessage); statusMessageFadeout -= 4; } else { statusMessageFadeout = 0; @@ -652,7 +638,7 @@ void uiUpdateStatusMsg() void uiPleaseWait(u8 wait) { - uiDrawString("Please wait...", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Please wait..."); uiRefreshDisplay(); if (wait) delay(wait); } @@ -662,44 +648,50 @@ void uiUpdateFreeSpace() getSdCardFreeSpace(&freeSpace); char tmp[32] = {'\0'}; - convertSize(freeSpace, tmp, sizeof(tmp) / sizeof(tmp[0])); + convertSize(freeSpace, tmp, MAX_ELEMENTS(tmp)); - snprintf(freeSpaceStr, sizeof(freeSpaceStr) / sizeof(freeSpaceStr[0]), "Free SD card space: %s.", tmp); + snprintf(freeSpaceStr, MAX_ELEMENTS(freeSpaceStr), "Free SD card space: %s.", tmp); } void uiClearScreen() { - uiFill(0, 0, FB_WIDTH, FB_HEIGHT, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, 0, FB_WIDTH, FB_HEIGHT, BG_COLOR_RGB); } void uiPrintHeadline() { breaks = 0; uiClearScreen(); - uiDrawString(appHeadline, 8, 8, 255, 255, 255); + uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_RGB, appHeadline); } -void uiPrintOption(int x, int y, int endPosition, bool leftArrow, bool rightArrow, int r, int g, int b) +void uiPrintOption(int x, int y, int endPosition, bool leftArrow, bool rightArrow, int r, int g, int b, const char *fmt, ...) { - if (!strlen(strbuf) || x < 8 || x >= OPTIONS_X_END_POS || y < 8 || y >= (FB_HEIGHT - 8 - font_height) || endPosition < OPTIONS_X_END_POS || endPosition >= (FB_WIDTH - 8)) return; + if (x < 8 || x >= OPTIONS_X_END_POS || y < 8 || y >= (FB_HEIGHT - 8 - font_height) || endPosition < OPTIONS_X_END_POS || endPosition >= (FB_WIDTH - 8) || !fmt || !*fmt) return; int xpos = x; - char *option = strbuf; + char option[NAME_BUF_LEN] = {'\0'}; + + va_list args; + va_start(args, fmt); + vsnprintf(option, MAX_ELEMENTS(option), fmt, args); + va_end(args); + u32 optionStrWidth = uiGetStrWidth(option); - if (leftArrow) uiDrawString("<", xpos, y, 255, 255, 255); + if (leftArrow) uiDrawString(xpos, y, FONT_COLOR_RGB, "<"); xpos += uiGetStrWidth("<"); xpos += (((endPosition - xpos) / 2) - (optionStrWidth / 2)); - uiDrawString(option, xpos, y, r, g, b); + uiDrawString(xpos, y, r, g, b, option); if (rightArrow) { xpos = endPosition; - uiDrawString(">", xpos, y, 255, 255, 255); + uiDrawString(xpos, y, FONT_COLOR_RGB, ">"); } } @@ -907,6 +899,34 @@ int uiInit() goto out; } + if (!uiLoadJpgFromFile(enabledNormalIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &enabledNormalIconBuf)) + { + strcat(strbuf, "Failed to load enabled icon (normal).\n"); + error_screen(strbuf); + goto out; + } + + if (!uiLoadJpgFromFile(enabledHighlightIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &enabledHighlightIconBuf)) + { + strcat(strbuf, "Failed to load enabled icon (highlighted).\n"); + error_screen(strbuf); + goto out; + } + + if (!uiLoadJpgFromFile(disabledNormalIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &disabledNormalIconBuf)) + { + strcat(strbuf, "Failed to load disabled icon (normal).\n"); + error_screen(strbuf); + goto out; + } + + if (!uiLoadJpgFromFile(disabledHighlightIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &disabledHighlightIconBuf)) + { + strcat(strbuf, "Failed to load disabled icon (highlighted).\n"); + error_screen(strbuf); + goto out; + } + /* Unmount Application's RomFS */ romfsExit(); romfs_init = false; @@ -959,6 +979,10 @@ int uiInit() out: if (!status) { + if (disabledHighlightIconBuf) free(disabledHighlightIconBuf); + if (disabledNormalIconBuf) free(disabledNormalIconBuf); + if (enabledHighlightIconBuf) free(enabledHighlightIconBuf); + if (enabledNormalIconBuf) free(enabledNormalIconBuf); if (fileHighlightIconBuf) free(fileHighlightIconBuf); if (fileNormalIconBuf) free(fileNormalIconBuf); if (dirHighlightIconBuf) free(dirHighlightIconBuf); @@ -998,6 +1022,12 @@ void uiDeinit() /* Free framebuffer object */ framebufferClose(&fb); + /* Free enabled/disabled icons (batch mode summary list) */ + free(disabledHighlightIconBuf); + free(disabledNormalIconBuf); + free(enabledHighlightIconBuf); + free(enabledNormalIconBuf); + /* Free directory/file icons */ free(fileHighlightIconBuf); free(fileNormalIconBuf); @@ -1066,6 +1096,9 @@ void uiSetState(UIState state) } else { cursor = 0; scroll = 0; + + // Avoid placing the cursor on the parent directory entry ("..") right after entering the RomFS browser + if (state == stateRomFsSectionBrowser && strlen(curRomFsPath) <= 1) cursor = 1; } titleSelectorStr[0] = '\0'; @@ -1086,8 +1119,7 @@ UIResult uiProcess() const char **menu = NULL; int menuItemsCount = 0; - u32 keysDown; - u32 keysHeld; + u32 keysDown = 0, keysHeld = 0; int scrollAmount = 0; bool scrollWithKeysDown = false; @@ -1096,7 +1128,7 @@ UIResult uiProcess() char versionStr[128] = {'\0'}; - int maxElements = (uiState == stateSdCardEmmcMenu ? SDCARD_MAX_ELEMENTS : (uiState == stateSdCardEmmcOrphanPatchAddOnMenu ? ORPHAN_MAX_ELEMENTS : (uiState == stateHfs0Browser ? HFS0_MAX_ELEMENTS : ((uiState == stateExeFsSectionBrowser || uiState == stateRomFsSectionBrowser) ? ROMFS_MAX_ELEMENTS : COMMON_MAX_ELEMENTS)))); + int maxElements = (uiState == stateSdCardEmmcMenu ? SDCARD_MAX_ELEMENTS : (uiState == stateSdCardEmmcOrphanPatchAddOnMenu ? ORPHAN_MAX_ELEMENTS : (uiState == stateHfs0Browser ? HFS0_MAX_ELEMENTS : ((uiState == stateExeFsSectionBrowser || uiState == stateRomFsSectionBrowser) ? ROMFS_MAX_ELEMENTS : (uiState == stateSdCardEmmcBatchModeMenu ? BATCH_MAX_ELEMENTS : COMMON_MAX_ELEMENTS))))); const char *upwardsArrow = UPWARDS_ARROW; const char *downwardsArrow = DOWNWARDS_ARROW; @@ -1111,20 +1143,20 @@ UIResult uiProcess() switch(menuType) { case MENUTYPE_MAIN: - uiDrawString(appControlsCommon, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsCommon); break; case MENUTYPE_GAMECARD: if (uiState == stateRomFsSectionBrowser && strlen(curRomFsPath) > 1) { - uiDrawString(appControlsRomFs, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsRomFs); } else { - uiDrawString((!gameCardInserted ? appControlsNoContent : (titleAppCount > 1 ? appControlsGameCardMultiApp : appControlsCommon)), 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, (!gameCardInserted ? appControlsNoContent : (titleAppCount > 1 ? appControlsGameCardMultiApp : appControlsCommon))); } break; case MENUTYPE_SDCARD_EMMC: if (uiState == stateSdCardEmmcBatchModeMenu) { - uiDrawString(appControlsCommon, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsCommon); } else { if (!orphanMode) { @@ -1132,28 +1164,28 @@ UIResult uiProcess() { if (uiState == stateSdCardEmmcMenu && ((titlePatchCount && checkOrphanPatchOrAddOn(false)) || (titleAddOnCount && checkOrphanPatchOrAddOn(true)))) { - uiDrawString(appControlsSdCardEmmcFull, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsSdCardEmmcFull); } else if (uiState == stateRomFsSectionBrowser && strlen(curRomFsPath) > 1) { - uiDrawString(appControlsRomFs, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsRomFs); } else { - uiDrawString(appControlsCommon, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsCommon); } } else { if (titlePatchCount || titleAddOnCount) { - uiDrawString(appControlsSdCardEmmcNoApp, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsSdCardEmmcNoApp); } else { - uiDrawString(appControlsNoContent, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsNoContent); } } } else { if (uiState == stateRomFsSectionBrowser && strlen(curRomFsPath) > 1) { - uiDrawString(appControlsRomFs, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsRomFs); } else { - uiDrawString(appControlsCommon, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsCommon); } } } @@ -1163,7 +1195,7 @@ UIResult uiProcess() } breaks += 2; - uiDrawString(freeSpaceStr, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, freeSpaceStr); breaks += 2; } @@ -1181,35 +1213,33 @@ UIResult uiProcess() if (titleAppCount > 0) { - uiDrawString("Error: unable to retrieve the gamecard Title ID!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to retrieve the gamecard Title ID!"); if (strlen(gameCardUpdateVersionStr)) { breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Bundled FW Update: %s", gameCardUpdateVersionStr); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Bundled FW Update: %s", gameCardUpdateVersionStr); breaks++; - uiDrawString("In order to be able to dump data from this cartridge, make sure your console is at least on this FW version.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "In order to be able to dump data from this cartridge, make sure your console is at least on this FW version."); } } else { - uiDrawString("Error: gamecard application count is zero!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: gamecard application count is zero!"); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unknown root HFS0 header partition count! (%u)", hfs0_partition_cnt); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unknown root HFS0 header partition count! (%u)", hfs0_partition_cnt); } } else { - uiDrawString("Error: unable to get root HFS0 header data!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to get root HFS0 header data!"); } } else { - uiDrawString("Gamecard is not inserted!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Gamecard is not inserted!"); } if (forcedXciDump) { breaks += 2; - uiDrawString("Press " NINTENDO_FONT_Y " to dump the cartridge image to \"gamecard.xci\".", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Press " NINTENDO_FONT_Y " to dump the cartridge image to \"gamecard.xci\"."); } uiUpdateStatusMsg(); @@ -1235,19 +1265,21 @@ UIResult uiProcess() { uiPrintHeadline(); - uiDrawString(gameCardMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[0]); breaks++; uiRefreshDisplay(); // Set default options - isFat32 = true; - setXciArchiveBit = false; - keepCert = true; - trimDump = false; - calcCrc = false; + xciOptions xciDumpCfg; - dumpCartridgeImage(isFat32, setXciArchiveBit, keepCert, trimDump, calcCrc); + xciDumpCfg.isFat32 = true; + xciDumpCfg.setXciArchiveBit = false; + xciDumpCfg.keepCert = true; + xciDumpCfg.trimDump = false; + xciDumpCfg.calcCrc = false; + + dumpCartridgeImage(&xciDumpCfg); waitForButtonPress(); @@ -1263,12 +1295,12 @@ UIResult uiProcess() { if (titlePatchCount || titleAddOnCount) { - uiDrawString("No base applications available in the SD card / eMMC storage!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "No base applications available in the SD card / eMMC storage!"); breaks++; - uiDrawString("Use the Y button to dump installed content with missing base applications!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Use the Y button to dump installed content with missing base applications!"); } else { - uiDrawString("No titles available in the SD card / eMMC storage!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "No titles available in the SD card / eMMC storage!"); } uiUpdateStatusMsg(); @@ -1306,21 +1338,19 @@ UIResult uiProcess() { if (menuType == MENUTYPE_GAMECARD) { - uiDrawString("Gamecard is inserted!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Gamecard is inserted!"); breaks += 2; - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Root HFS0 header offset: 0x%016lX", hfs0_offset); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Root HFS0 header offset: 0x%016lX", hfs0_offset); breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Root HFS0 header size: 0x%016lX", hfs0_size); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Root HFS0 header size: 0x%016lX", hfs0_size); breaks++;*/ } /* Print application info */ - xpos = 8; - ypos = ((breaks * (font_height + (font_height / 4))) + (font_height / 8)); + xpos = STRING_X_POS; + ypos = STRING_Y_POS(breaks); startYPos = ypos; /* Draw icon */ @@ -1333,26 +1363,23 @@ UIResult uiProcess() if (titleName != NULL && titleName[selectedAppInfoIndex] != NULL && strlen(titleName[selectedAppInfoIndex])) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Name: %s", titleName[selectedAppInfoIndex]); - uiDrawString(strbuf, xpos, ypos, 0, 255, 0); - ypos += (font_height + (font_height / 4)); + uiDrawString(xpos, ypos, FONT_COLOR_SUCCESS_RGB, "Name: %s", titleName[selectedAppInfoIndex]); + ypos += LINE_HEIGHT; } if (titleAuthor != NULL && titleAuthor[selectedAppInfoIndex] != NULL && strlen(titleAuthor[selectedAppInfoIndex])) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Publisher: %s", titleAuthor[selectedAppInfoIndex]); - uiDrawString(strbuf, xpos, ypos, 0, 255, 0); - ypos += (font_height + (font_height / 4)); + uiDrawString(xpos, ypos, FONT_COLOR_SUCCESS_RGB, "Publisher: %s", titleAuthor[selectedAppInfoIndex]); + ypos += LINE_HEIGHT; } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Title ID: %016lX", titleAppTitleID[selectedAppInfoIndex]); - uiDrawString(strbuf, xpos, ypos, 0, 255, 0); + uiDrawString(xpos, ypos, FONT_COLOR_SUCCESS_RGB, "Title ID: %016lX", titleAppTitleID[selectedAppInfoIndex]); if (titlePatchCount > 0) { u32 patchCnt = 0; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s update(s): v", (menuType == MENUTYPE_GAMECARD ? "Bundled" : "Installed")); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "%s update(s): v", (menuType == MENUTYPE_GAMECARD ? "Bundled" : "Installed")); for(patch = 0; patch < titlePatchCount; patch++) { @@ -1360,23 +1387,22 @@ UIResult uiProcess() { if (patchCnt > 0) strcat(strbuf, ", v"); - convertTitleVersionToDecimal(titlePatchVersion[patch], versionStr, sizeof(versionStr)); + convertTitleVersionToDecimal(titlePatchVersion[patch], versionStr, MAX_ELEMENTS(versionStr)); strcat(strbuf, versionStr); patchCnt++; } } - if (patchCnt > 0) uiDrawString(strbuf, (FB_WIDTH / 2) - (FB_WIDTH / 8), ypos, 0, 255, 0); + if (patchCnt > 0) uiDrawString((FB_WIDTH / 2) - (FB_WIDTH / 8), ypos, FONT_COLOR_SUCCESS_RGB, strbuf); } - ypos += (font_height + (font_height / 4)); + ypos += LINE_HEIGHT; if (titleAppVersionStr != NULL && titleAppVersionStr[selectedAppInfoIndex] != NULL && strlen(titleAppVersionStr[selectedAppInfoIndex])) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Version: %s", titleAppVersionStr[selectedAppInfoIndex]); - uiDrawString(strbuf, xpos, ypos, 0, 255, 0); - if (!titleAddOnCount) ypos += (font_height + (font_height / 4)); + uiDrawString(xpos, ypos, FONT_COLOR_SUCCESS_RGB, "Version: %s", titleAppVersionStr[selectedAppInfoIndex]); + if (!titleAddOnCount) ypos += LINE_HEIGHT; } if (titleAddOnCount > 0) @@ -1390,55 +1416,36 @@ UIResult uiProcess() if (addOnCnt > 0) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s DLC(s): %u", (menuType == MENUTYPE_GAMECARD ? "Bundled" : "Installed"), addOnCnt); - uiDrawString(strbuf, (FB_WIDTH / 2) - (FB_WIDTH / 8), ypos, 0, 255, 0); - ypos += (font_height + (font_height / 4)); + uiDrawString((FB_WIDTH / 2) - (FB_WIDTH / 8), ypos, FONT_COLOR_SUCCESS_RGB, "%s DLC(s): %u", (menuType == MENUTYPE_GAMECARD ? "Bundled" : "Installed"), addOnCnt); + ypos += LINE_HEIGHT; } } ypos += 8; if (xpos > 8 && (ypos - NACP_ICON_DOWNSCALED) < startYPos) ypos += (NACP_ICON_DOWNSCALED - (ypos - startYPos)); - ypos += (font_height + (font_height / 4)); + ypos += LINE_HEIGHT; - breaks += (int)round((double)(ypos - startYPos) / (double)(font_height + (font_height / 4))); + breaks += (int)round((double)(ypos - startYPos) / (double)LINE_HEIGHT); if (menuType == MENUTYPE_GAMECARD) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Size: %s | Used space: %s", gameCardSizeStr, trimmedCardSizeStr); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Size: %s | Used space: %s", gameCardSizeStr, trimmedCardSizeStr); - if (titleAppCount > 1) - { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Base application count: %u | Base application currently displayed: %u", titleAppCount, selectedAppInfoIndex + 1); - uiDrawString(strbuf, (FB_WIDTH / 2) - (FB_WIDTH / 8), (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); - } + if (titleAppCount > 1) uiDrawString((FB_WIDTH / 2) - (FB_WIDTH / 8), STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Base application count: %u | Base application currently displayed: %u", titleAppCount, selectedAppInfoIndex + 1); breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Partition count: %u (%s)", hfs0_partition_cnt, GAMECARD_TYPE(hfs0_partition_cnt)); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Partition count: %u (%s)", hfs0_partition_cnt, GAMECARD_TYPE(hfs0_partition_cnt)); - if (strlen(gameCardUpdateVersionStr)) - { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Bundled FW update: %s", gameCardUpdateVersionStr); - uiDrawString(strbuf, (FB_WIDTH / 2) - (FB_WIDTH / 8), (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); - } + if (strlen(gameCardUpdateVersionStr)) uiDrawString((FB_WIDTH / 2) - (FB_WIDTH / 8), STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Bundled FW update: %s", gameCardUpdateVersionStr); breaks++; if (titleAppCount > 1 && (titlePatchCount > 0 || titleAddOnCount > 0)) { - if (titlePatchCount > 0) - { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Total bundled update(s): %u", titlePatchCount); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); - } + if (titlePatchCount > 0) uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Total bundled update(s): %u", titlePatchCount); - if (titleAddOnCount > 0) - { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Total bundled DLC(s): %u", titleAddOnCount); - uiDrawString(strbuf, (titlePatchCount > 0 ? ((FB_WIDTH / 2) - (FB_WIDTH / 8)) : 8), (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); - } + if (titleAddOnCount > 0) uiDrawString((titlePatchCount > 0 ? ((FB_WIDTH / 2) - (FB_WIDTH / 8)) : 8), STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Total bundled DLC(s): %u", titleAddOnCount); breaks++; } @@ -1454,7 +1461,7 @@ UIResult uiProcess() u32 patchCnt = 0, addOnCnt = 0; u32 patchCntConsoleData = 0, addOnCntConsoleData = 0; - snprintf(dumpedContentInfoStr, sizeof(dumpedContentInfoStr) / sizeof(dumpedContentInfoStr[0]), "Content already dumped: "); + snprintf(dumpedContentInfoStr, MAX_ELEMENTS(dumpedContentInfoStr), "Content already dumped: "); if (menuType == MENUTYPE_GAMECARD) { @@ -1462,11 +1469,11 @@ UIResult uiProcess() if (xciName) { // First check if an unsplitted XCI dump is available - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.xci", XCI_DUMP_PATH, xciName); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.xci", XCI_DUMP_PATH, xciName); if (!(dumpedXci = checkIfFileExists(dumpPath))) { // Check if a splitted XCI dump is available - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.xc0", XCI_DUMP_PATH, xciName); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.xc0", XCI_DUMP_PATH, xciName); dumpedXci = checkIfFileExists(dumpPath); } @@ -1483,7 +1490,7 @@ UIResult uiProcess() dumpName = generateNSPDumpName(DUMP_APP_NSP, selectedAppInfoIndex); if (dumpName) { - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp", NSP_DUMP_PATH, dumpName); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp", NSP_DUMP_PATH, dumpName); free(dumpName); dumpName = NULL; @@ -1505,7 +1512,7 @@ UIResult uiProcess() dumpName = generateNSPDumpName(DUMP_PATCH_NSP, patch); if (dumpName) { - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp", NSP_DUMP_PATH, dumpName); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp", NSP_DUMP_PATH, dumpName); free(dumpName); dumpName = NULL; @@ -1530,7 +1537,7 @@ UIResult uiProcess() dumpName = generateNSPDumpName(DUMP_ADDON_NSP, addon); if (dumpName) { - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp", NSP_DUMP_PATH, dumpName); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp", NSP_DUMP_PATH, dumpName); free(dumpName); dumpName = NULL; @@ -1583,19 +1590,19 @@ UIResult uiProcess() { if (patchCnt > 1) { - snprintf(tmpStr, sizeof(tmpStr) / sizeof(tmpStr[0]), "%u UPD (all with console data)", patchCnt); + snprintf(tmpStr, MAX_ELEMENTS(tmpStr), "%u UPD (all with console data)", patchCnt); } else { - snprintf(tmpStr, sizeof(tmpStr) / sizeof(tmpStr[0]), "UPD (with console data)"); + snprintf(tmpStr, MAX_ELEMENTS(tmpStr), "UPD (with console data)"); } } else { - snprintf(tmpStr, sizeof(tmpStr) / sizeof(tmpStr[0]), "%u UPD (%u with console data)", patchCnt, patchCntConsoleData); + snprintf(tmpStr, MAX_ELEMENTS(tmpStr), "%u UPD (%u with console data)", patchCnt, patchCntConsoleData); } } else { if (patchCnt > 1) { - snprintf(tmpStr, sizeof(tmpStr) / sizeof(tmpStr[0]), "%u UPD (all without console data)", patchCnt); + snprintf(tmpStr, MAX_ELEMENTS(tmpStr), "%u UPD (all without console data)", patchCnt); } else { - snprintf(tmpStr, sizeof(tmpStr) / sizeof(tmpStr[0]), "UPD (without console data)"); + snprintf(tmpStr, MAX_ELEMENTS(tmpStr), "UPD (without console data)"); } } @@ -1610,12 +1617,12 @@ UIResult uiProcess() { if (addOnCntConsoleData == addOnCnt) { - snprintf(tmpStr, sizeof(tmpStr) / sizeof(tmpStr[0]), "%u DLC (%s console data)", addOnCnt, (addOnCnt > 1 ? "all with" : "with")); + snprintf(tmpStr, MAX_ELEMENTS(tmpStr), "%u DLC (%s console data)", addOnCnt, (addOnCnt > 1 ? "all with" : "with")); } else { - snprintf(tmpStr, sizeof(tmpStr) / sizeof(tmpStr[0]), "%u DLC (%u with console data)", addOnCnt, addOnCntConsoleData); + snprintf(tmpStr, MAX_ELEMENTS(tmpStr), "%u DLC (%u with console data)", addOnCnt, addOnCntConsoleData); } } else { - snprintf(tmpStr, sizeof(tmpStr) / sizeof(tmpStr[0]), "%u DLC (%s console data)", addOnCnt, (addOnCnt > 1 ? "all without" : "without")); + snprintf(tmpStr, MAX_ELEMENTS(tmpStr), "%u DLC (%s console data)", addOnCnt, (addOnCnt > 1 ? "all without" : "without")); } if (dumpedBase || patchCnt) strcat(dumpedContentInfoStr, ", "); @@ -1625,19 +1632,17 @@ UIResult uiProcess() } } - uiDrawString(dumpedContentInfoStr, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, dumpedContentInfoStr); breaks += 2; } else if (menuType == MENUTYPE_SDCARD_EMMC && orphanMode && (uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu || uiState == stateSdCardEmmcTitleMenu || uiState == stateRomFsMenu)) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Title ID: %016lX", (uiState == stateNspPatchDumpMenu ? titlePatchTitleID[selectedPatchIndex] : titleAddOnTitleID[selectedAddOnIndex])); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Title ID: %016lX", (uiState == stateNspPatchDumpMenu ? titlePatchTitleID[selectedPatchIndex] : titleAddOnTitleID[selectedAddOnIndex])); breaks++; - convertTitleVersionToDecimal((uiState == stateNspPatchDumpMenu ? titlePatchVersion[selectedPatchIndex] : titleAddOnVersion[selectedAddOnIndex]), versionStr, sizeof(versionStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Version: %s", versionStr); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + convertTitleVersionToDecimal((uiState == stateNspPatchDumpMenu ? titlePatchVersion[selectedPatchIndex] : titleAddOnVersion[selectedAddOnIndex]), versionStr, MAX_ELEMENTS(versionStr)); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Version: %s", versionStr); breaks++; if (!strlen(dumpedContentInfoStr)) @@ -1646,7 +1651,7 @@ UIResult uiProcess() char *dumpName = NULL; char dumpPath[NAME_BUF_LEN] = {'\0'}; - snprintf(dumpedContentInfoStr, sizeof(dumpedContentInfoStr) / sizeof(dumpedContentInfoStr[0]), "Title already dumped: "); + snprintf(dumpedContentInfoStr, MAX_ELEMENTS(dumpedContentInfoStr), "Title already dumped: "); if (uiState == stateNspPatchDumpMenu) { @@ -1659,7 +1664,7 @@ UIResult uiProcess() if (dumpName) { - snprintf(dumpPath, sizeof(dumpPath) / sizeof(dumpPath[0]), "%s%s.nsp", NSP_DUMP_PATH, dumpName); + snprintf(dumpPath, MAX_ELEMENTS(dumpPath), "%s%s.nsp", NSP_DUMP_PATH, dumpName); free(dumpName); dumpName = NULL; @@ -1682,7 +1687,7 @@ UIResult uiProcess() } } - uiDrawString(dumpedContentInfoStr, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, dumpedContentInfoStr); breaks += 2; } else { dumpedContentInfoStr[0] = '\0'; @@ -1692,209 +1697,205 @@ UIResult uiProcess() { case stateMainMenu: menu = mainMenuItems; - menuItemsCount = (sizeof(mainMenuItems) / sizeof(mainMenuItems[0])); + menuItemsCount = MAX_ELEMENTS(mainMenuItems); - uiDrawString("Main menu", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Main menu"); break; case stateGameCardMenu: menu = gameCardMenuItems; - menuItemsCount = (sizeof(gameCardMenuItems) / sizeof(gameCardMenuItems[0])); + menuItemsCount = MAX_ELEMENTS(gameCardMenuItems); - uiDrawString(mainMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, mainMenuItems[0]); break; case stateXciDumpMenu: menu = xciDumpMenuItems; - menuItemsCount = (sizeof(xciDumpMenuItems) / sizeof(xciDumpMenuItems[0])); + menuItemsCount = MAX_ELEMENTS(xciDumpMenuItems); - uiDrawString(gameCardMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[0]); break; case stateNspDumpMenu: if (menuType == MENUTYPE_GAMECARD) { menu = nspDumpGameCardMenuItems; - menuItemsCount = (sizeof(nspDumpGameCardMenuItems) / sizeof(nspDumpGameCardMenuItems[0])); + menuItemsCount = MAX_ELEMENTS(nspDumpGameCardMenuItems); } else if (menuType == MENUTYPE_SDCARD_EMMC) { menu = nspDumpSdCardEmmcMenuItems; - menuItemsCount = (sizeof(nspDumpSdCardEmmcMenuItems) / sizeof(nspDumpSdCardEmmcMenuItems[0])); + menuItemsCount = MAX_ELEMENTS(nspDumpSdCardEmmcMenuItems); } - uiDrawString(gameCardMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[1]); break; case stateNspAppDumpMenu: menu = nspAppDumpMenuItems; - menuItemsCount = (sizeof(nspAppDumpMenuItems) / sizeof(nspAppDumpMenuItems[0])); + menuItemsCount = MAX_ELEMENTS(nspAppDumpMenuItems); - uiDrawString(nspDumpGameCardMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, nspDumpGameCardMenuItems[0]); break; case stateNspPatchDumpMenu: menu = nspPatchDumpMenuItems; - menuItemsCount = (sizeof(nspPatchDumpMenuItems) / sizeof(nspPatchDumpMenuItems[0])); + menuItemsCount = MAX_ELEMENTS(nspPatchDumpMenuItems); - uiDrawString((menuType == MENUTYPE_GAMECARD ? nspDumpGameCardMenuItems[1] : nspDumpSdCardEmmcMenuItems[1]), 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, (menuType == MENUTYPE_GAMECARD ? nspDumpGameCardMenuItems[1] : nspDumpSdCardEmmcMenuItems[1])); break; case stateNspAddOnDumpMenu: menu = nspAddOnDumpMenuItems; - menuItemsCount = sizeof(nspAddOnDumpMenuItems) / sizeof(nspAddOnDumpMenuItems[0]); + menuItemsCount = MAX_ELEMENTS(nspAddOnDumpMenuItems); - uiDrawString((menuType == MENUTYPE_GAMECARD ? nspDumpGameCardMenuItems[2] : nspDumpSdCardEmmcMenuItems[2]), 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, (menuType == MENUTYPE_GAMECARD ? nspDumpGameCardMenuItems[2] : nspDumpSdCardEmmcMenuItems[2])); break; case stateHfs0Menu: menu = hfs0MenuItems; - menuItemsCount = (sizeof(hfs0MenuItems) / sizeof(hfs0MenuItems[0])); + menuItemsCount = MAX_ELEMENTS(hfs0MenuItems); - uiDrawString(gameCardMenuItems[2], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[2]); break; case stateRawHfs0PartitionDumpMenu: case stateHfs0PartitionDataDumpMenu: menu = (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0PartitionDumpType1MenuItems : hfs0PartitionDumpType2MenuItems); - menuItemsCount = (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? (sizeof(hfs0PartitionDumpType1MenuItems) / sizeof(hfs0PartitionDumpType1MenuItems[0])) : (sizeof(hfs0PartitionDumpType2MenuItems) / sizeof(hfs0PartitionDumpType2MenuItems[0]))); + menuItemsCount = (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? MAX_ELEMENTS(hfs0PartitionDumpType1MenuItems) : MAX_ELEMENTS(hfs0PartitionDumpType2MenuItems)); - uiDrawString((uiState == stateRawHfs0PartitionDumpMenu ? hfs0MenuItems[0] : hfs0MenuItems[1]), 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, (uiState == stateRawHfs0PartitionDumpMenu ? hfs0MenuItems[0] : hfs0MenuItems[1])); break; case stateHfs0BrowserMenu: menu = (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0BrowserType1MenuItems : hfs0BrowserType2MenuItems); - menuItemsCount = (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? (sizeof(hfs0BrowserType1MenuItems) / sizeof(hfs0BrowserType1MenuItems[0])) : (sizeof(hfs0BrowserType2MenuItems) / sizeof(hfs0BrowserType2MenuItems[0]))); + menuItemsCount = (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? MAX_ELEMENTS(hfs0BrowserType1MenuItems) : MAX_ELEMENTS(hfs0BrowserType2MenuItems)); - uiDrawString(hfs0MenuItems[2], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, hfs0MenuItems[2]); break; case stateHfs0Browser: menu = (const char**)filenames; menuItemsCount = filenamesCount; - uiDrawString((hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0BrowserType1MenuItems[selectedPartitionIndex] : hfs0BrowserType2MenuItems[selectedPartitionIndex]), 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0BrowserType1MenuItems[selectedPartitionIndex] : hfs0BrowserType2MenuItems[selectedPartitionIndex])); breaks += 2; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Entry count: %d | Current entry: %d", menuItemsCount, cursor + 1); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Entry count: %d | Current entry: %d", menuItemsCount, cursor + 1); break; case stateExeFsMenu: menu = exeFsMenuItems; - menuItemsCount = (sizeof(exeFsMenuItems) / sizeof(exeFsMenuItems[0])); + menuItemsCount = MAX_ELEMENTS(exeFsMenuItems); - uiDrawString(gameCardMenuItems[3], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[3]); break; case stateExeFsSectionDataDumpMenu: menu = exeFsSectionDumpMenuItems; - menuItemsCount = (sizeof(exeFsSectionDumpMenuItems) / sizeof(exeFsSectionDumpMenuItems[0])); + menuItemsCount = MAX_ELEMENTS(exeFsSectionDumpMenuItems); - uiDrawString(exeFsMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, exeFsMenuItems[0]); break; case stateExeFsSectionBrowserMenu: menu = exeFsSectionBrowserMenuItems; - menuItemsCount = (sizeof(exeFsSectionBrowserMenuItems) / sizeof(exeFsSectionBrowserMenuItems[0])); + menuItemsCount = MAX_ELEMENTS(exeFsSectionBrowserMenuItems); - uiDrawString(exeFsMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, exeFsMenuItems[1]); break; case stateExeFsSectionBrowser: menu = (const char**)filenames; menuItemsCount = filenamesCount; - uiDrawString(exeFsMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, exeFsMenuItems[1]); breaks++; if (!exeFsUpdateFlag) { - convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, sizeof(versionStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s v%s", exeFsSectionBrowserMenuItems[1], titleName[selectedAppIndex], versionStr); + convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "%s%s v%s", exeFsSectionBrowserMenuItems[1], titleName[selectedAppIndex], versionStr); } else { - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update to browse: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update to browse: ", strbuf, MAX_ELEMENTS(strbuf)); } - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf); breaks += 2; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Entry count: %d | Current entry: %d", menuItemsCount, cursor + 1); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Entry count: %d | Current entry: %d", menuItemsCount, cursor + 1); break; case stateRomFsMenu: menu = romFsMenuItems; - menuItemsCount = (sizeof(romFsMenuItems) / sizeof(romFsMenuItems[0])); + menuItemsCount = MAX_ELEMENTS(romFsMenuItems); - uiDrawString(gameCardMenuItems[4], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[4]); break; case stateRomFsSectionDataDumpMenu: menu = romFsSectionDumpMenuItems; - menuItemsCount = (sizeof(romFsSectionDumpMenuItems) / sizeof(romFsSectionDumpMenuItems[0])); + menuItemsCount = MAX_ELEMENTS(romFsSectionDumpMenuItems); - uiDrawString(romFsMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, romFsMenuItems[0]); break; case stateRomFsSectionBrowserMenu: menu = romFsSectionBrowserMenuItems; - menuItemsCount = (sizeof(romFsSectionBrowserMenuItems) / sizeof(romFsSectionBrowserMenuItems[0])); + menuItemsCount = MAX_ELEMENTS(romFsSectionBrowserMenuItems); - uiDrawString(romFsMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, romFsMenuItems[1]); break; case stateRomFsSectionBrowser: menu = (const char**)filenames; menuItemsCount = filenamesCount; - uiDrawString(romFsMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, romFsMenuItems[1]); breaks++; switch(curRomFsType) { case ROMFS_TYPE_APP: - convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, sizeof(versionStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s v%s", romFsSectionBrowserMenuItems[1], titleName[selectedAppIndex], versionStr); + convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "%s%s v%s", romFsSectionBrowserMenuItems[1], titleName[selectedAppIndex], versionStr); break; case ROMFS_TYPE_PATCH: - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update to browse: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update to browse: ", strbuf, MAX_ELEMENTS(strbuf)); break; case ROMFS_TYPE_ADDON: - retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, true, "DLC to browse: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, true, "DLC to browse: ", strbuf, MAX_ELEMENTS(strbuf)); break; default: break; } - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf); breaks += 2; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Path: romfs:%s", curRomFsPath); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Path: romfs:%s", curRomFsPath); breaks += 2; if (cursor > 0) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Entry count: %d | Current entry: %d", menuItemsCount - 1, cursor); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "Entry count: %d | Current entry: %d", menuItemsCount - 1, cursor); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Entry count: %d", menuItemsCount - 1); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "Entry count: %d", menuItemsCount - 1); } - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, strbuf); break; case stateSdCardEmmcMenu: menu = (const char**)titleName; menuItemsCount = (int)titleAppCount; - uiDrawString(mainMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, mainMenuItems[1]); if (menuItemsCount) { breaks += 2; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Title count: %d | Current title: %d", menuItemsCount, cursor + 1); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Title count: %d | Current title: %d", menuItemsCount, cursor + 1); } breaks++; @@ -1902,9 +1903,9 @@ UIResult uiProcess() break; case stateSdCardEmmcTitleMenu: menu = sdCardEmmcMenuItems; - menuItemsCount = (sizeof(sdCardEmmcMenuItems) / sizeof(sdCardEmmcMenuItems[0])); + menuItemsCount = MAX_ELEMENTS(sdCardEmmcMenuItems); - uiDrawString((!orphanMode ? mainMenuItems[1] : "Dump orphan DLC content"), 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, (!orphanMode ? mainMenuItems[1] : "Dump orphan DLC content")); break; case stateSdCardEmmcOrphanPatchAddOnMenu: @@ -1913,31 +1914,30 @@ UIResult uiProcess() menu = (const char**)filenames; menuItemsCount = filenamesCount; - uiDrawString("Dump installed content with missing base application", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Dump installed content with missing base application"); breaks += 2; - uiDrawString("Hint: installed updates/DLCs for gamecard titles can be found in this section.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Hint: installed updates/DLCs for gamecard titles can be found in this section."); if (menuItemsCount) { breaks += 2; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Title count: %d | Current title: %d", menuItemsCount, cursor + 1); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Title count: %d | Current title: %d", menuItemsCount, cursor + 1); } break; case stateSdCardEmmcBatchModeMenu: menu = batchModeMenuItems; - menuItemsCount = (sizeof(batchModeMenuItems) / sizeof(batchModeMenuItems[0])); + menuItemsCount = MAX_ELEMENTS(batchModeMenuItems); - uiDrawString("Batch mode", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Batch mode"); break; case stateUpdateMenu: menu = updateMenuItems; - menuItemsCount = sizeof(updateMenuItems) / sizeof(updateMenuItems[0]); + menuItemsCount = MAX_ELEMENTS(updateMenuItems); - uiDrawString(mainMenuItems[2], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, mainMenuItems[2]); break; default: @@ -1952,7 +1952,7 @@ UIResult uiProcess() { u32 arrowWidth = uiGetStrWidth(upwardsArrow); - uiDrawString(upwardsArrow, (FB_WIDTH / 2) - (arrowWidth / 2), (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString((FB_WIDTH / 2) - (arrowWidth / 2), STRING_Y_POS(breaks), FONT_COLOR_RGB, upwardsArrow); } breaks++; @@ -1965,7 +1965,7 @@ UIResult uiProcess() if (j >= maxElements) break; // Avoid printing the "Create directory with archive bit set" option if "Split output dump" is disabled - if (uiState == stateXciDumpMenu && i == 2 && !isFat32) + if (uiState == stateXciDumpMenu && i == 2 && !dumpCfg.xciDumpCfg.isFat32) { j--; continue; @@ -1996,21 +1996,21 @@ UIResult uiProcess() } // Avoid printing the "Generate ticket-less dump" option in the NSP dump menus if we're dealing with a SD/eMMC title and the "Remove console specific data" option is disabled - if (menuType == MENUTYPE_SDCARD_EMMC && (uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu) && i == 4 && !removeConsoleData) + if (menuType == MENUTYPE_SDCARD_EMMC && (uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu) && i == 4 && !dumpCfg.nspDumpCfg.removeConsoleData) { j--; continue; } // Avoid printing the "Dump base applications", "Dump updates" and/or "Dump DLCs" options in the batch mode menu if we're dealing with a storage source that doesn't hold any title belonging to the current category - if (uiState == stateSdCardEmmcBatchModeMenu && ((batchModeSrc == BATCH_SOURCE_ALL && ((!titleAppCount && i == 1) || (!titlePatchCount && i == 2) || (!titleAddOnCount && i == 3))) || (batchModeSrc == BATCH_SOURCE_SDCARD && ((!sdCardTitleAppCount && i == 1) || (!sdCardTitlePatchCount && i == 2) || (!sdCardTitleAddOnCount && i == 3))) || (batchModeSrc == BATCH_SOURCE_EMMC && ((!nandUserTitleAppCount && i == 1) || (!nandUserTitlePatchCount && i == 2) || (!nandUserTitleAddOnCount && i == 3))))) + if (uiState == stateSdCardEmmcBatchModeMenu && ((dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL && ((!titleAppCount && i == 1) || (!titlePatchCount && i == 2) || (!titleAddOnCount && i == 3))) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD && ((!sdCardTitleAppCount && i == 1) || (!sdCardTitlePatchCount && i == 2) || (!sdCardTitleAddOnCount && i == 3))) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC && ((!nandUserTitleAppCount && i == 1) || (!nandUserTitlePatchCount && i == 2) || (!nandUserTitleAddOnCount && i == 3))))) { j--; continue; } // Avoid printing the "Generate ticket-less dumps" option in the batch mode menu if the "Remove console specific data" option is disabled - if (uiState == stateSdCardEmmcBatchModeMenu && i == 6 && !removeConsoleData) + if (uiState == stateSdCardEmmcBatchModeMenu && i == 6 && !dumpCfg.batchDumpCfg.removeConsoleData) { j--; continue; @@ -2067,20 +2067,13 @@ UIResult uiProcess() continue; } - xpos = 8; - ypos = ((breaks * (font_height + (font_height / 4))) + (uiState == stateSdCardEmmcMenu ? (j * (NACP_ICON_DOWNSCALED + 12)) : (j * (font_height + 12))) + 6); - - int font_r = 255, font_g = 255, font_b = 255; + xpos = STRING_X_POS; + ypos = ((breaks * LINE_HEIGHT) + (uiState == stateSdCardEmmcMenu ? (j * (NACP_ICON_DOWNSCALED + 12)) : (j * (font_height + 12))) + 6); if (i == cursor) { highlight = true; - - uiFill(0, (ypos + 8) - 6, FB_WIDTH, (uiState == stateSdCardEmmcMenu ? (NACP_ICON_DOWNSCALED + 12) : (font_height + 12)), HIGHLIGHT_BG_COLOR_R, HIGHLIGHT_BG_COLOR_G, HIGHLIGHT_BG_COLOR_B); - - font_r = HIGHLIGHT_FONT_COLOR_R; - font_g = HIGHLIGHT_FONT_COLOR_G; - font_b = HIGHLIGHT_FONT_COLOR_B; + uiFill(0, (ypos + 8) - 6, FB_WIDTH, (uiState == stateSdCardEmmcMenu ? (NACP_ICON_DOWNSCALED + 12) : (font_height + 12)), HIGHLIGHT_BG_COLOR_RGB); } if (uiState == stateSdCardEmmcMenu) @@ -2103,7 +2096,12 @@ UIResult uiProcess() xpos += (BROWSER_ICON_DIMENSION + 8); } - uiDrawString(menu[i], xpos, ypos, font_r, font_g, font_b); + if (highlight) + { + uiDrawString(xpos, ypos, HIGHLIGHT_FONT_COLOR_RGB, menu[i]); + } else { + uiDrawString(xpos, ypos, FONT_COLOR_RGB, menu[i]); + } xpos = OPTIONS_X_START_POS; @@ -2116,24 +2114,19 @@ UIResult uiProcess() switch(i) { case 1: // Split output dump (FAT32 support) - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (isFat32 ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, isFat32, !isFat32, (isFat32 ? 0 : 255), (isFat32 ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.xciDumpCfg.isFat32, !dumpCfg.xciDumpCfg.isFat32, (dumpCfg.xciDumpCfg.isFat32 ? 0 : 255), (dumpCfg.xciDumpCfg.isFat32 ? 255 : 0), 0, (dumpCfg.xciDumpCfg.isFat32 ? "Yes" : "No")); break; case 2: // Create directory with archive bit set - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (setXciArchiveBit ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, setXciArchiveBit, !setXciArchiveBit, (setXciArchiveBit ? 0 : 255), (setXciArchiveBit ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.xciDumpCfg.setXciArchiveBit, !dumpCfg.xciDumpCfg.setXciArchiveBit, (dumpCfg.xciDumpCfg.setXciArchiveBit ? 0 : 255), (dumpCfg.xciDumpCfg.setXciArchiveBit ? 255 : 0), 0, (dumpCfg.xciDumpCfg.setXciArchiveBit ? "Yes" : "No")); break; case 3: // Keep certificate - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (keepCert ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, keepCert, !keepCert, (keepCert ? 0 : 255), (keepCert ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.xciDumpCfg.keepCert, !dumpCfg.xciDumpCfg.keepCert, (dumpCfg.xciDumpCfg.keepCert ? 0 : 255), (dumpCfg.xciDumpCfg.keepCert ? 255 : 0), 0, (dumpCfg.xciDumpCfg.keepCert ? "Yes" : "No")); break; case 4: // Trim output dump - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (trimDump ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, trimDump, !trimDump, (trimDump ? 0 : 255), (trimDump ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.xciDumpCfg.trimDump, !dumpCfg.xciDumpCfg.trimDump, (dumpCfg.xciDumpCfg.trimDump ? 0 : 255), (dumpCfg.xciDumpCfg.trimDump ? 255 : 0), 0, (dumpCfg.xciDumpCfg.trimDump ? "Yes" : "No")); break; case 5: // CRC32 checksum calculation + dump verification - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (calcCrc ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, calcCrc, !calcCrc, (calcCrc ? 0 : 255), (calcCrc ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.xciDumpCfg.calcCrc, !dumpCfg.xciDumpCfg.calcCrc, (dumpCfg.xciDumpCfg.calcCrc ? 0 : 255), (dumpCfg.xciDumpCfg.calcCrc ? 255 : 0), 0, (dumpCfg.xciDumpCfg.calcCrc ? "Yes" : "No")); break; default: break; @@ -2146,20 +2139,16 @@ UIResult uiProcess() switch(i) { case 1: // Split output dump (FAT32 support) - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (isFat32 ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, isFat32, !isFat32, (isFat32 ? 0 : 255), (isFat32 ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.nspDumpCfg.isFat32, !dumpCfg.nspDumpCfg.isFat32, (dumpCfg.nspDumpCfg.isFat32 ? 0 : 255), (dumpCfg.nspDumpCfg.isFat32 ? 255 : 0), 0, (dumpCfg.nspDumpCfg.isFat32 ? "Yes" : "No")); break; case 2: // CRC32 checksum calculation - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (calcCrc ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, calcCrc, !calcCrc, (calcCrc ? 0 : 255), (calcCrc ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.nspDumpCfg.calcCrc, !dumpCfg.nspDumpCfg.calcCrc, (dumpCfg.nspDumpCfg.calcCrc ? 0 : 255), (dumpCfg.nspDumpCfg.calcCrc ? 255 : 0), 0, (dumpCfg.nspDumpCfg.calcCrc ? "Yes" : "No")); break; case 3: // Remove console specific data - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (removeConsoleData ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, removeConsoleData, !removeConsoleData, (removeConsoleData ? 0 : 255), (removeConsoleData ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.nspDumpCfg.removeConsoleData, !dumpCfg.nspDumpCfg.removeConsoleData, (dumpCfg.nspDumpCfg.removeConsoleData ? 0 : 255), (dumpCfg.nspDumpCfg.removeConsoleData ? 255 : 0), 0, (dumpCfg.nspDumpCfg.removeConsoleData ? "Yes" : "No")); break; case 4: // Generate ticket-less dump - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (tiklessDump ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, tiklessDump, !tiklessDump, (tiklessDump ? 0 : 255), (tiklessDump ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.nspDumpCfg.tiklessDump, !dumpCfg.nspDumpCfg.tiklessDump, (dumpCfg.nspDumpCfg.tiklessDump ? 0 : 255), (dumpCfg.nspDumpCfg.tiklessDump ? 255 : 0), 0, (dumpCfg.nspDumpCfg.tiklessDump ? "Yes" : "No")); break; case 5: // Bundled application/update/DLC to dump if (uiState == stateNspAppDumpMenu) @@ -2167,8 +2156,8 @@ UIResult uiProcess() if (!strlen(titleSelectorStr)) { // Print application name - convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, sizeof(versionStr)); - snprintf(titleSelectorStr, sizeof(titleSelectorStr) / sizeof(titleSelectorStr[0]), "%s v%s", titleName[selectedAppIndex], versionStr); + convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(titleSelectorStr, MAX_ELEMENTS(titleSelectorStr), "%s v%s", titleName[selectedAppIndex], versionStr); uiTruncateOptionStr(titleSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP); } @@ -2181,7 +2170,7 @@ UIResult uiProcess() { // Find a matching application to print its name // Otherwise, just print the Title ID - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, (menuType == MENUTYPE_GAMECARD), NULL, titleSelectorStr, sizeof(titleSelectorStr) / sizeof(titleSelectorStr[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, (menuType == MENUTYPE_GAMECARD), NULL, titleSelectorStr, MAX_ELEMENTS(titleSelectorStr)); uiTruncateOptionStr(titleSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP); } @@ -2194,7 +2183,7 @@ UIResult uiProcess() { // Find a matching application to print its name and Title ID // Otherwise, just print the Title ID - retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, (menuType == MENUTYPE_GAMECARD), NULL, titleSelectorStr, sizeof(titleSelectorStr) / sizeof(titleSelectorStr[0])); + retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, (menuType == MENUTYPE_GAMECARD), NULL, titleSelectorStr, MAX_ELEMENTS(titleSelectorStr)); uiTruncateOptionStr(titleSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP); } @@ -2202,9 +2191,7 @@ UIResult uiProcess() rightArrowCondition = ((menuType == MENUTYPE_GAMECARD && titleAddOnCount > 0 && selectedAddOnIndex < (titleAddOnCount - 1)) || (menuType == MENUTYPE_SDCARD_EMMC && !orphanMode && retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedAddOnIndex, selectedAppInfoIndex, true) != selectedAddOnIndex)); } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), titleSelectorStr); - - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, 255, 255, 255); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, titleSelectorStr); break; default: break; @@ -2212,11 +2199,16 @@ UIResult uiProcess() if (i == 2) { - if (calcCrc) + if (dumpCfg.nspDumpCfg.calcCrc) { - uiDrawString("This takes extra time after the NSP dump has been completed!", FB_WIDTH / 2, ypos, 255, 255, 255); + uiDrawString(FB_WIDTH / 2, ypos, FONT_COLOR_RGB, "This takes extra time after the NSP dump has been completed!"); } else { - uiFill(FB_WIDTH / 2, (ypos + 8) - 6, FB_WIDTH / 2, font_height + 12, (highlight ? HIGHLIGHT_BG_COLOR_R : BG_COLOR_RGB), (highlight ? HIGHLIGHT_BG_COLOR_G : BG_COLOR_RGB), (highlight ? HIGHLIGHT_BG_COLOR_B : BG_COLOR_RGB)); + if (highlight) + { + uiFill(FB_WIDTH / 2, (ypos + 8) - 6, FB_WIDTH / 2, font_height + 12, HIGHLIGHT_BG_COLOR_RGB); + } else { + uiFill(FB_WIDTH / 2, (ypos + 8) - 6, FB_WIDTH / 2, font_height + 12, BG_COLOR_RGB); + } } } } @@ -2227,41 +2219,35 @@ UIResult uiProcess() switch(i) { case 1: // Dump base applications - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (dumpAppTitles ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpAppTitles, !dumpAppTitles, (dumpAppTitles ? 0 : 255), (dumpAppTitles ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.dumpAppTitles, !dumpCfg.batchDumpCfg.dumpAppTitles, (dumpCfg.batchDumpCfg.dumpAppTitles ? 0 : 255), (dumpCfg.batchDumpCfg.dumpAppTitles ? 255 : 0), 0, (dumpCfg.batchDumpCfg.dumpAppTitles ? "Yes" : "No")); break; case 2: // Dump updates - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (dumpPatchTitles ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpPatchTitles, !dumpPatchTitles, (dumpPatchTitles ? 0 : 255), (dumpPatchTitles ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.dumpPatchTitles, !dumpCfg.batchDumpCfg.dumpPatchTitles, (dumpCfg.batchDumpCfg.dumpPatchTitles ? 0 : 255), (dumpCfg.batchDumpCfg.dumpPatchTitles ? 255 : 0), 0, (dumpCfg.batchDumpCfg.dumpPatchTitles ? "Yes" : "No")); break; case 3: // Dump DLCs - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (dumpAddOnTitles ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpAddOnTitles, !dumpAddOnTitles, (dumpAddOnTitles ? 0 : 255), (dumpAddOnTitles ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.dumpAddOnTitles, !dumpCfg.batchDumpCfg.dumpAddOnTitles, (dumpCfg.batchDumpCfg.dumpAddOnTitles ? 0 : 255), (dumpCfg.batchDumpCfg.dumpAddOnTitles ? 255 : 0), 0, (dumpCfg.batchDumpCfg.dumpAddOnTitles ? "Yes" : "No")); break; case 4: // Split output dumps (FAT32 support) - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (isFat32 ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, isFat32, !isFat32, (isFat32 ? 0 : 255), (isFat32 ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.isFat32, !dumpCfg.batchDumpCfg.isFat32, (dumpCfg.batchDumpCfg.isFat32 ? 0 : 255), (dumpCfg.batchDumpCfg.isFat32 ? 255 : 0), 0, (dumpCfg.batchDumpCfg.isFat32 ? "Yes" : "No")); break; case 5: // Remove console specific data - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (removeConsoleData ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, removeConsoleData, !removeConsoleData, (removeConsoleData ? 0 : 255), (removeConsoleData ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.removeConsoleData, !dumpCfg.batchDumpCfg.removeConsoleData, (dumpCfg.batchDumpCfg.removeConsoleData ? 0 : 255), (dumpCfg.batchDumpCfg.removeConsoleData ? 255 : 0), 0, (dumpCfg.batchDumpCfg.removeConsoleData ? "Yes" : "No")); break; case 6: // Generate ticket-less dumps - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (tiklessDump ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, tiklessDump, !tiklessDump, (tiklessDump ? 0 : 255), (tiklessDump ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.tiklessDump, !dumpCfg.batchDumpCfg.tiklessDump, (dumpCfg.batchDumpCfg.tiklessDump ? 0 : 255), (dumpCfg.batchDumpCfg.tiklessDump ? 255 : 0), 0, (dumpCfg.batchDumpCfg.tiklessDump ? "Yes" : "No")); break; case 7: // Skip dumped titles - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (skipDumpedTitles ? "Yes" : "No")); - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, skipDumpedTitles, !skipDumpedTitles, (skipDumpedTitles ? 0 : 255), (skipDumpedTitles ? 255 : 0), 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.skipDumpedTitles, !dumpCfg.batchDumpCfg.skipDumpedTitles, (dumpCfg.batchDumpCfg.skipDumpedTitles ? 0 : 255), (dumpCfg.batchDumpCfg.skipDumpedTitles ? 255 : 0), 0, (dumpCfg.batchDumpCfg.skipDumpedTitles ? "Yes" : "No")); break; case 8: // Source storage - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s", (batchModeSrc == BATCH_SOURCE_ALL ? "All (SD card + eMMC)" : (batchModeSrc == BATCH_SOURCE_SDCARD ? "SD card" : "eMMC"))); + leftArrowCondition = (dumpCfg.batchDumpCfg.batchModeSrc != BATCH_SOURCE_ALL); + rightArrowCondition = (dumpCfg.batchDumpCfg.batchModeSrc != BATCH_SOURCE_EMMC); - leftArrowCondition = (batchModeSrc != BATCH_SOURCE_ALL); - rightArrowCondition = (batchModeSrc != BATCH_SOURCE_EMMC); - - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, 255, 255, 255); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL ? "All (SD card + eMMC)" : (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD ? "SD card" : "eMMC"))); + break; + case 9: // Remember dumped titles + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.rememberDumpedTitles, !dumpCfg.batchDumpCfg.rememberDumpedTitles, (dumpCfg.batchDumpCfg.rememberDumpedTitles ? 0 : 255), (dumpCfg.batchDumpCfg.rememberDumpedTitles ? 255 : 0), 0, (dumpCfg.batchDumpCfg.rememberDumpedTitles ? "Yes" : "No")); break; default: break; @@ -2282,7 +2268,7 @@ UIResult uiProcess() { // Find a matching application to print its name // Otherwise, just print the Title ID - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, (menuType == MENUTYPE_GAMECARD && titleAppCount > 1), NULL, exeFsAndRomFsSelectorStr, sizeof(exeFsAndRomFsSelectorStr) / sizeof(exeFsAndRomFsSelectorStr[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, (menuType == MENUTYPE_GAMECARD && titleAppCount > 1), NULL, exeFsAndRomFsSelectorStr, MAX_ELEMENTS(exeFsAndRomFsSelectorStr)); // Concatenate patch source storage strcat(exeFsAndRomFsSelectorStr, (titlePatchStorageId[selectedPatchIndex] == FsStorageId_GameCard ? " (gamecard)" : (titlePatchStorageId[selectedPatchIndex] == FsStorageId_SdCard ? " (SD card)" : "(eMMC)"))); @@ -2293,16 +2279,12 @@ UIResult uiProcess() leftArrowCondition = true; rightArrowCondition = (((menuType == MENUTYPE_GAMECARD && titleAppCount == 1) || menuType == MENUTYPE_SDCARD_EMMC) && retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, appIndex, false) != selectedPatchIndex); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), exeFsAndRomFsSelectorStr); - - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, 255, 255, 255); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, exeFsAndRomFsSelectorStr); } else { leftArrowCondition = false; rightArrowCondition = (((menuType == MENUTYPE_GAMECARD && titleAppCount == 1) || menuType == MENUTYPE_SDCARD_EMMC) && checkIfBaseApplicationHasPatchOrAddOn(appIndex, false)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "No"); - - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, 255, 0, 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_ERROR_RGB, "No"); } break; @@ -2320,17 +2302,15 @@ UIResult uiProcess() if (!strlen(titleSelectorStr)) { // Print application name - convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, sizeof(versionStr)); - snprintf(titleSelectorStr, sizeof(titleSelectorStr) / sizeof(titleSelectorStr[0]), "%s v%s", titleName[selectedAppIndex], versionStr); + convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(titleSelectorStr, MAX_ELEMENTS(titleSelectorStr), "%s v%s", titleName[selectedAppIndex], versionStr); uiTruncateOptionStr(titleSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP); } leftArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && selectedAppIndex > 0); rightArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && selectedAppIndex < (titleAppCount - 1)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), titleSelectorStr); - - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, 255, 255, 255); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, titleSelectorStr); break; case 2: // Use update @@ -2340,7 +2320,7 @@ UIResult uiProcess() { // Find a matching application to print its name // Otherwise, just print the Title ID - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, (menuType == MENUTYPE_GAMECARD), NULL, exeFsAndRomFsSelectorStr, sizeof(exeFsAndRomFsSelectorStr) / sizeof(exeFsAndRomFsSelectorStr[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, (menuType == MENUTYPE_GAMECARD), NULL, exeFsAndRomFsSelectorStr, MAX_ELEMENTS(exeFsAndRomFsSelectorStr)); // Concatenate patch source storage strcat(exeFsAndRomFsSelectorStr, (titlePatchStorageId[selectedPatchIndex] == FsStorageId_GameCard ? " (gamecard)" : (titlePatchStorageId[selectedPatchIndex] == FsStorageId_SdCard ? " (SD card)" : "(eMMC)"))); @@ -2351,16 +2331,12 @@ UIResult uiProcess() leftArrowCondition = true; rightArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, selectedAppIndex, false) != selectedPatchIndex); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), exeFsAndRomFsSelectorStr); - - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, 255, 255, 255); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, exeFsAndRomFsSelectorStr); } else { leftArrowCondition = false; rightArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, false)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "No"); - - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, 255, 0, 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_ERROR_RGB, "No"); } break; @@ -2388,12 +2364,12 @@ UIResult uiProcess() switch(curRomFsType) { case ROMFS_TYPE_PATCH: - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, (menuType == MENUTYPE_GAMECARD && titleAppCount > 1), NULL, exeFsAndRomFsSelectorStr, sizeof(exeFsAndRomFsSelectorStr) / sizeof(exeFsAndRomFsSelectorStr[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, (menuType == MENUTYPE_GAMECARD && titleAppCount > 1), NULL, exeFsAndRomFsSelectorStr, MAX_ELEMENTS(exeFsAndRomFsSelectorStr)); strcat(exeFsAndRomFsSelectorStr, " (UPD)"); strcat(exeFsAndRomFsSelectorStr, (titlePatchStorageId[selectedPatchIndex] == FsStorageId_GameCard ? " (gamecard)" : (titlePatchStorageId[selectedPatchIndex] == FsStorageId_SdCard ? " (SD card)" : "(eMMC)"))); break; case ROMFS_TYPE_ADDON: - retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, (menuType == MENUTYPE_GAMECARD && titleAppCount > 1), NULL, exeFsAndRomFsSelectorStr, sizeof(exeFsAndRomFsSelectorStr) / sizeof(exeFsAndRomFsSelectorStr[0])); + retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, (menuType == MENUTYPE_GAMECARD && titleAppCount > 1), NULL, exeFsAndRomFsSelectorStr, MAX_ELEMENTS(exeFsAndRomFsSelectorStr)); strcat(exeFsAndRomFsSelectorStr, " (DLC)"); strcat(exeFsAndRomFsSelectorStr, (titleAddOnStorageId[selectedAddOnIndex] == FsStorageId_GameCard ? " (gamecard)" : (titleAddOnStorageId[selectedAddOnIndex] == FsStorageId_SdCard ? " (SD card)" : "(eMMC)"))); break; @@ -2407,16 +2383,12 @@ UIResult uiProcess() leftArrowCondition = true; rightArrowCondition = (((menuType == MENUTYPE_GAMECARD && titleAppCount == 1) || menuType == MENUTYPE_SDCARD_EMMC) && ((curRomFsType == ROMFS_TYPE_PATCH && (retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, appIndex, false) != selectedPatchIndex || checkIfBaseApplicationHasPatchOrAddOn(appIndex, true))) || (curRomFsType == ROMFS_TYPE_ADDON && retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedAddOnIndex, appIndex, true) != selectedAddOnIndex))); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), exeFsAndRomFsSelectorStr); - - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, 255, 255, 255); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, exeFsAndRomFsSelectorStr); } else { leftArrowCondition = false; rightArrowCondition = (((menuType == MENUTYPE_GAMECARD && titleAppCount == 1) || menuType == MENUTYPE_SDCARD_EMMC) && (checkIfBaseApplicationHasPatchOrAddOn(appIndex, false) || checkIfBaseApplicationHasPatchOrAddOn(appIndex, true))); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "No"); - - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, 255, 0, 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_ERROR_RGB, "No"); } break; @@ -2434,17 +2406,15 @@ UIResult uiProcess() if (!strlen(titleSelectorStr)) { // Print application name - convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, sizeof(versionStr)); - snprintf(titleSelectorStr, sizeof(titleSelectorStr) / sizeof(titleSelectorStr[0]), "%s v%s", titleName[selectedAppIndex], versionStr); + convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(titleSelectorStr, MAX_ELEMENTS(titleSelectorStr), "%s v%s", titleName[selectedAppIndex], versionStr); uiTruncateOptionStr(titleSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP); } leftArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && selectedAppIndex > 0); rightArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && selectedAppIndex < (titleAppCount - 1)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), titleSelectorStr); - - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, 255, 255, 255); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, titleSelectorStr); break; case 2: // Use update @@ -2459,12 +2429,12 @@ UIResult uiProcess() switch(curRomFsType) { case ROMFS_TYPE_PATCH: - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, (menuType == MENUTYPE_GAMECARD), NULL, exeFsAndRomFsSelectorStr, sizeof(exeFsAndRomFsSelectorStr) / sizeof(exeFsAndRomFsSelectorStr[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, (menuType == MENUTYPE_GAMECARD), NULL, exeFsAndRomFsSelectorStr, MAX_ELEMENTS(exeFsAndRomFsSelectorStr)); strcat(exeFsAndRomFsSelectorStr, " (UPD)"); strcat(exeFsAndRomFsSelectorStr, (titlePatchStorageId[selectedPatchIndex] == FsStorageId_GameCard ? " (gamecard)" : (titlePatchStorageId[selectedPatchIndex] == FsStorageId_SdCard ? " (SD card)" : "(eMMC)"))); break; case ROMFS_TYPE_ADDON: - retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, (menuType == MENUTYPE_GAMECARD), NULL, exeFsAndRomFsSelectorStr, sizeof(exeFsAndRomFsSelectorStr) / sizeof(exeFsAndRomFsSelectorStr[0])); + retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, (menuType == MENUTYPE_GAMECARD), NULL, exeFsAndRomFsSelectorStr, MAX_ELEMENTS(exeFsAndRomFsSelectorStr)); strcat(exeFsAndRomFsSelectorStr, " (DLC)"); strcat(exeFsAndRomFsSelectorStr, (titleAddOnStorageId[selectedAddOnIndex] == FsStorageId_GameCard ? " (gamecard)" : (titleAddOnStorageId[selectedAddOnIndex] == FsStorageId_SdCard ? " (SD card)" : "(eMMC)"))); break; @@ -2478,16 +2448,12 @@ UIResult uiProcess() leftArrowCondition = true; rightArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && ((curRomFsType == ROMFS_TYPE_PATCH && (retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, selectedAppIndex, false) != selectedPatchIndex || checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, true))) || (curRomFsType == ROMFS_TYPE_ADDON && retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedAddOnIndex, selectedAppIndex, true) != selectedAddOnIndex))); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), exeFsAndRomFsSelectorStr); - - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, 255, 255, 255); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, exeFsAndRomFsSelectorStr); } else { leftArrowCondition = false; rightArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && (checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, false) || checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, true))); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "No"); - - uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, 255, 0, 0); + uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_ERROR_RGB, "No"); } break; @@ -2501,11 +2467,11 @@ UIResult uiProcess() if ((scroll + maxElements) < menuItemsCount) { - ypos = ((breaks * (font_height + (font_height / 4))) + (uiState == stateSdCardEmmcMenu ? (j * (NACP_ICON_DOWNSCALED + 12)) : (j * (font_height + 12)))); + ypos = ((breaks * LINE_HEIGHT) + (uiState == stateSdCardEmmcMenu ? (j * (NACP_ICON_DOWNSCALED + 12)) : (j * (font_height + 12)))); u32 arrowWidth = uiGetStrWidth(downwardsArrow); - uiDrawString(downwardsArrow, (FB_WIDTH / 2) - (arrowWidth / 2), ypos, 255, 255, 255); + uiDrawString((FB_WIDTH / 2) - (arrowWidth / 2), ypos, FONT_COLOR_RGB, downwardsArrow); } // Print hint about dumping RomFS content from DLCs @@ -2514,25 +2480,30 @@ UIResult uiProcess() j++; if ((scroll + maxElements) < menuItemsCount) j++; - ypos = ((breaks * (font_height + (font_height / 4))) + (j * (font_height + 12))); + ypos = ((breaks * LINE_HEIGHT) + (j * (font_height + 12))); - uiDrawString("Hint: choosing a DLC will only access RomFS data from it, unlike updates.", 8, ypos, 255, 255, 255); + uiDrawString(STRING_X_POS, ypos, FONT_COLOR_RGB, "Hint: choosing a DLC will only access RomFS data from it, unlike updates."); } } else { if (uiState == stateSdCardEmmcOrphanPatchAddOnMenu) { breaks += 2; - uiDrawString("No orphan updates/DLCs available in the SD card / eMMC storage!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "No orphan updates/DLCs available in the SD card / eMMC storage!"); } } - uiUpdateStatusMsg(); - uiRefreshDisplay(); - - hidScanInput(); - - keysDown = hidKeysDown(CONTROLLER_P1_AUTO); - keysHeld = hidKeysHeld(CONTROLLER_P1_AUTO); + while(true) + { + uiUpdateStatusMsg(); + uiRefreshDisplay(); + + hidScanInput(); + + keysDown = hidKeysDown(CONTROLLER_P1_AUTO); + keysHeld = hidKeysHeld(CONTROLLER_P1_AUTO); + + if ((keysDown && !(keysDown & KEY_TOUCH)) || (keysHeld && !(keysHeld & KEY_TOUCH))) break; + } // Exit if (keysDown & KEY_PLUS) res = resultExit; @@ -2576,23 +2547,26 @@ UIResult uiProcess() switch(cursor) { case 1: // Split output dump (FAT32 support) - isFat32 = setXciArchiveBit = false; + dumpCfg.xciDumpCfg.isFat32 = dumpCfg.xciDumpCfg.setXciArchiveBit = false; break; case 2: // Create directory with archive bit set - setXciArchiveBit = false; + dumpCfg.xciDumpCfg.setXciArchiveBit = false; break; case 3: // Keep certificate - keepCert = false; + dumpCfg.xciDumpCfg.keepCert = false; break; case 4: // Trim output dump - trimDump = false; + dumpCfg.xciDumpCfg.trimDump = false; break; case 5: // CRC32 checksum calculation + dump verification - calcCrc = false; + dumpCfg.xciDumpCfg.calcCrc = false; break; default: break; } + + // Save settings to configuration file + saveConfig(); } // Change option to true @@ -2601,23 +2575,26 @@ UIResult uiProcess() switch(cursor) { case 1: // Split output dump (FAT32 support) - isFat32 = true; + dumpCfg.xciDumpCfg.isFat32 = true; break; case 2: // Create directory with archive bit set - setXciArchiveBit = true; + dumpCfg.xciDumpCfg.setXciArchiveBit = true; break; case 3: // Keep certificate - keepCert = true; + dumpCfg.xciDumpCfg.keepCert = true; break; case 4: // Trim output dump - trimDump = true; + dumpCfg.xciDumpCfg.trimDump = true; break; case 5: // CRC32 checksum calculation + dump verification - calcCrc = true; + dumpCfg.xciDumpCfg.calcCrc = true; break; default: break; } + + // Save settings to configuration file + saveConfig(); } // Go up @@ -2677,16 +2654,16 @@ UIResult uiProcess() switch(cursor) { case 1: // Split output dump (FAT32 support) - isFat32 = false; + dumpCfg.nspDumpCfg.isFat32 = false; break; case 2: // CRC32 checksum calculation - calcCrc = false; + dumpCfg.nspDumpCfg.calcCrc = false; break; case 3: // Remove console specific data - removeConsoleData = tiklessDump = false; + dumpCfg.nspDumpCfg.removeConsoleData = dumpCfg.nspDumpCfg.tiklessDump = false; break; case 4: // Generate ticket-less dump - tiklessDump = false; + dumpCfg.nspDumpCfg.tiklessDump = false; break; case 5: // Bundled application/update/DLC to dump if (uiState == stateNspAppDumpMenu) @@ -2750,6 +2727,9 @@ UIResult uiProcess() default: break; } + + // Save settings to configuration file + saveConfig(); } // Change option to true @@ -2758,16 +2738,16 @@ UIResult uiProcess() switch(cursor) { case 1: // Split output dump (FAT32 support) - isFat32 = true; + dumpCfg.nspDumpCfg.isFat32 = true; break; case 2: // CRC32 checksum calculation - calcCrc = true; + dumpCfg.nspDumpCfg.calcCrc = true; break; case 3: // Remove console specific data - removeConsoleData = true; + dumpCfg.nspDumpCfg.removeConsoleData = true; break; case 4: // Generate ticket-less dump - tiklessDump = true; + dumpCfg.nspDumpCfg.tiklessDump = true; break; case 5: // Bundled application/update/DLC to dump if (uiState == stateNspAppDumpMenu) @@ -2831,6 +2811,9 @@ UIResult uiProcess() default: break; } + + // Save settings to configuration file + saveConfig(); } // Go up @@ -2850,7 +2833,7 @@ UIResult uiProcess() if (uiState == stateSdCardEmmcBatchModeMenu) { // Select - if ((keysDown & KEY_A) && cursor == 0 && (dumpAppTitles || dumpPatchTitles || dumpAddOnTitles)) res = resultSdCardEmmcBatchDump; + if ((keysDown & KEY_A) && cursor == 0 && (dumpCfg.batchDumpCfg.dumpAppTitles || dumpCfg.batchDumpCfg.dumpPatchTitles || dumpCfg.batchDumpCfg.dumpAddOnTitles)) res = resultSdCardEmmcBatchDump; // Back if (keysDown & KEY_B) res = resultShowSdCardEmmcMenu; @@ -2861,54 +2844,60 @@ UIResult uiProcess() switch(cursor) { case 1: // Dump base applications - dumpAppTitles = false; + dumpCfg.batchDumpCfg.dumpAppTitles = false; break; case 2: // Dump updates - dumpPatchTitles = false; + dumpCfg.batchDumpCfg.dumpPatchTitles = false; break; case 3: // Dump DLCs - dumpAddOnTitles = false; + dumpCfg.batchDumpCfg.dumpAddOnTitles = false; break; case 4: // Split output dumps (FAT32 support) - isFat32 = false; + dumpCfg.batchDumpCfg.isFat32 = false; break; case 5: // Remove console specific data - removeConsoleData = tiklessDump = false; + dumpCfg.batchDumpCfg.removeConsoleData = dumpCfg.batchDumpCfg.tiklessDump = false; break; case 6: // Generate ticket-less dumps - tiklessDump = false; + dumpCfg.batchDumpCfg.tiklessDump = false; break; case 7: // Skip already dumped titles - skipDumpedTitles = false; + dumpCfg.batchDumpCfg.skipDumpedTitles = false; break; case 8: // Source storage - if (batchModeSrc != BATCH_SOURCE_ALL) + if (dumpCfg.batchDumpCfg.batchModeSrc != BATCH_SOURCE_ALL) { - batchModeSrc--; + dumpCfg.batchDumpCfg.batchModeSrc--; - if (batchModeSrc == BATCH_SOURCE_ALL) + if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL) { - dumpAppTitles = (titleAppCount > 0); - dumpPatchTitles = (titlePatchCount > 0); - dumpAddOnTitles = (titleAddOnCount > 0); + dumpCfg.batchDumpCfg.dumpAppTitles = (titleAppCount > 0); + dumpCfg.batchDumpCfg.dumpPatchTitles = (titlePatchCount > 0); + dumpCfg.batchDumpCfg.dumpAddOnTitles = (titleAddOnCount > 0); } else - if (batchModeSrc == BATCH_SOURCE_SDCARD) + if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD) { - dumpAppTitles = (sdCardTitleAppCount > 0); - dumpPatchTitles = (sdCardTitlePatchCount > 0); - dumpAddOnTitles = (sdCardTitleAddOnCount > 0); + dumpCfg.batchDumpCfg.dumpAppTitles = (sdCardTitleAppCount > 0); + dumpCfg.batchDumpCfg.dumpPatchTitles = (sdCardTitlePatchCount > 0); + dumpCfg.batchDumpCfg.dumpAddOnTitles = (sdCardTitleAddOnCount > 0); } else - if (batchModeSrc == BATCH_SOURCE_EMMC) + if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC) { - dumpAppTitles = (nandUserTitleAppCount > 0); - dumpPatchTitles = (nandUserTitlePatchCount > 0); - dumpAddOnTitles = (nandUserTitleAddOnCount > 0); + dumpCfg.batchDumpCfg.dumpAppTitles = (nandUserTitleAppCount > 0); + dumpCfg.batchDumpCfg.dumpPatchTitles = (nandUserTitlePatchCount > 0); + dumpCfg.batchDumpCfg.dumpAddOnTitles = (nandUserTitleAddOnCount > 0); } } break; + case 9: // Remember dumped titles + dumpCfg.batchDumpCfg.rememberDumpedTitles = false; + break; default: break; } + + // Save settings to configuration file + saveConfig(); } // Change option to true @@ -2917,54 +2906,60 @@ UIResult uiProcess() switch(cursor) { case 1: // Dump base applications - dumpAppTitles = true; + dumpCfg.batchDumpCfg.dumpAppTitles = true; break; case 2: // Dump updates - dumpPatchTitles = true; + dumpCfg.batchDumpCfg.dumpPatchTitles = true; break; case 3: // Dump DLCs - dumpAddOnTitles = true; + dumpCfg.batchDumpCfg.dumpAddOnTitles = true; break; case 4: // Split output dumps (FAT32 support) - isFat32 = true; + dumpCfg.batchDumpCfg.isFat32 = true; break; case 5: // Remove console specific data - removeConsoleData = true; + dumpCfg.batchDumpCfg.removeConsoleData = true; break; case 6: // Generate ticket-less dumps - tiklessDump = true; + dumpCfg.batchDumpCfg.tiklessDump = true; break; case 7: // Skip already dumped titles - skipDumpedTitles = true; + dumpCfg.batchDumpCfg.skipDumpedTitles = true; break; case 8: // Source storage - if (batchModeSrc != BATCH_SOURCE_EMMC) + if (dumpCfg.batchDumpCfg.batchModeSrc != BATCH_SOURCE_EMMC) { - batchModeSrc++; + dumpCfg.batchDumpCfg.batchModeSrc++; - if (batchModeSrc == BATCH_SOURCE_ALL) + if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL) { - dumpAppTitles = (titleAppCount > 0); - dumpPatchTitles = (titlePatchCount > 0); - dumpAddOnTitles = (titleAddOnCount > 0); + dumpCfg.batchDumpCfg.dumpAppTitles = (titleAppCount > 0); + dumpCfg.batchDumpCfg.dumpPatchTitles = (titlePatchCount > 0); + dumpCfg.batchDumpCfg.dumpAddOnTitles = (titleAddOnCount > 0); } else - if (batchModeSrc == BATCH_SOURCE_SDCARD) + if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD) { - dumpAppTitles = (sdCardTitleAppCount > 0); - dumpPatchTitles = (sdCardTitlePatchCount > 0); - dumpAddOnTitles = (sdCardTitleAddOnCount > 0); + dumpCfg.batchDumpCfg.dumpAppTitles = (sdCardTitleAppCount > 0); + dumpCfg.batchDumpCfg.dumpPatchTitles = (sdCardTitlePatchCount > 0); + dumpCfg.batchDumpCfg.dumpAddOnTitles = (sdCardTitleAddOnCount > 0); } else - if (batchModeSrc == BATCH_SOURCE_EMMC) + if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC) { - dumpAppTitles = (nandUserTitleAppCount > 0); - dumpPatchTitles = (nandUserTitlePatchCount > 0); - dumpAddOnTitles = (nandUserTitleAddOnCount > 0); + dumpCfg.batchDumpCfg.dumpAppTitles = (nandUserTitleAppCount > 0); + dumpCfg.batchDumpCfg.dumpPatchTitles = (nandUserTitlePatchCount > 0); + dumpCfg.batchDumpCfg.dumpAddOnTitles = (nandUserTitleAddOnCount > 0); } } break; + case 9: // Remember dumped titles + dumpCfg.batchDumpCfg.rememberDumpedTitles = true; + break; default: break; } + + // Save settings to configuration file + saveConfig(); } // Go up @@ -3507,24 +3502,13 @@ UIResult uiProcess() { case 0: res = resultShowXciDumpMenu; - - // Reset options to their default values - isFat32 = false; - keepCert = false; - trimDump = false; - calcCrc = true; - setXciArchiveBit = false; break; case 1: if (!titlePatchCount && !titleAddOnCount) { res = resultShowNspAppDumpMenu; - // Reset options to their default values - isFat32 = false; - calcCrc = false; - removeConsoleData = false; - tiklessDump = false; + // Reset option to its default value selectedAppIndex = 0; } else { res = resultShowNspDumpMenu; @@ -3564,11 +3548,6 @@ UIResult uiProcess() if (uiState == stateNspDumpMenu) { // Reset options to their default values - isFat32 = false; - calcCrc = false; - removeConsoleData = false; - tiklessDump = false; - selectedAppIndex = 0; selectedPatchIndex = 0; selectedAddOnIndex = 0; @@ -3674,12 +3653,6 @@ UIResult uiProcess() switch(cursor) { case 0: - // Reset options to their default values - isFat32 = false; - calcCrc = false; - removeConsoleData = false; - tiklessDump = false; - if (!orphanMode) { if ((!titlePatchCount || !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false)) && (!titleAddOnCount || !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true))) @@ -3724,12 +3697,6 @@ UIResult uiProcess() { if (menu && menuItemsCount) { - // Reset options to their default values - isFat32 = false; - calcCrc = false; - removeConsoleData = false; - tiklessDump = false; - if (orphanEntries[cursor].type == ORPHAN_ENTRY_TYPE_PATCH) { selectedPatchIndex = orphanEntries[cursor].index; @@ -3863,16 +3830,13 @@ UIResult uiProcess() // Batch mode res = resultShowSdCardEmmcBatchModeMenu; - // Reset options to their default values - dumpAppTitles = (titleAppCount > 0); - dumpPatchTitles = (titlePatchCount > 0); - dumpAddOnTitles = (titleAddOnCount > 0); - isFat32 = false; - calcCrc = false; - removeConsoleData = false; - tiklessDump = false; - skipDumpedTitles = true; - batchModeSrc = BATCH_SOURCE_ALL; + // Check if we're using the default configuration + if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL && !dumpCfg.batchDumpCfg.dumpAppTitles && !dumpCfg.batchDumpCfg.dumpPatchTitles && !dumpCfg.batchDumpCfg.dumpAddOnTitles) + { + dumpCfg.batchDumpCfg.dumpAppTitles = (titleAppCount > 0); + dumpCfg.batchDumpCfg.dumpPatchTitles = (titlePatchCount > 0); + dumpCfg.batchDumpCfg.dumpAddOnTitles = (titleAddOnCount > 0); + } } } @@ -3938,7 +3902,7 @@ UIResult uiProcess() } // Avoid placing the cursor on the "Create directory with archive bit set" option in the XCI dump menu if "Split output dump" is disabled - if (uiState == stateXciDumpMenu && cursor == 2 && !isFat32) + if (uiState == stateXciDumpMenu && cursor == 2 && !dumpCfg.xciDumpCfg.isFat32) { if (scrollAmount > 0) { @@ -3995,7 +3959,7 @@ UIResult uiProcess() } // Avoid placing the cursor on the "Generate ticket-less dump" option in the NSP dump menus if we're dealing with a SD/eMMC title and the "Remove console specific data" option is disabled - if (menuType == MENUTYPE_SDCARD_EMMC && (uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu) && cursor == 4 && !removeConsoleData) + if (menuType == MENUTYPE_SDCARD_EMMC && (uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu) && cursor == 4 && !dumpCfg.nspDumpCfg.removeConsoleData) { if (scrollAmount > 0) { @@ -4008,7 +3972,7 @@ UIResult uiProcess() } // Avoid placing the cursor on the "Dump base applications", "Dump updates" and/or "Dump DLCs" options in the batch mode menu if we're dealing with a storage source that doesn't hold any title belonging to the current category - if (uiState == stateSdCardEmmcBatchModeMenu && ((batchModeSrc == BATCH_SOURCE_ALL && ((!titleAppCount && cursor == 1) || (!titlePatchCount && cursor == 2) || (!titleAddOnCount && cursor == 3))) || (batchModeSrc == BATCH_SOURCE_SDCARD && ((!sdCardTitleAppCount && cursor == 1) || (!sdCardTitlePatchCount && cursor == 2) || (!sdCardTitleAddOnCount && cursor == 3))) || (batchModeSrc == BATCH_SOURCE_EMMC && ((!nandUserTitleAppCount && cursor == 1) || (!nandUserTitlePatchCount && cursor == 2) || (!nandUserTitleAddOnCount && cursor == 3))))) + if (uiState == stateSdCardEmmcBatchModeMenu && ((dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL && ((!titleAppCount && cursor == 1) || (!titlePatchCount && cursor == 2) || (!titleAddOnCount && cursor == 3))) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD && ((!sdCardTitleAppCount && cursor == 1) || (!sdCardTitlePatchCount && cursor == 2) || (!sdCardTitleAddOnCount && cursor == 3))) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC && ((!nandUserTitleAppCount && cursor == 1) || (!nandUserTitlePatchCount && cursor == 2) || (!nandUserTitleAddOnCount && cursor == 3))))) { if (scrollAmount > 0) { @@ -4021,7 +3985,7 @@ UIResult uiProcess() } // Avoid placing the cursor on the "Generate ticket-less dumps" option in the batch mode menu if the "Remove console specific data" option is disabled - if (uiState == stateSdCardEmmcBatchModeMenu && cursor == 6 && !removeConsoleData) + if (uiState == stateSdCardEmmcBatchModeMenu && cursor == 6 && !dumpCfg.batchDumpCfg.removeConsoleData) { if (scrollAmount > 0) { @@ -4038,7 +4002,7 @@ UIResult uiProcess() { if (scrollAmount > 0) { - cursor = (scrollWithKeysDown ? 0 : 7); + cursor++; } else if (scrollAmount < 0) { @@ -4129,35 +4093,30 @@ UIResult uiProcess() } else if (uiState == stateDumpXci) { - uiDrawString(gameCardMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[0]); breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", xciDumpMenuItems[1], (isFat32 ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", xciDumpMenuItems[1], (dumpCfg.xciDumpCfg.isFat32 ? "Yes" : "No")); breaks++; - if (isFat32) + if (dumpCfg.xciDumpCfg.isFat32) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", xciDumpMenuItems[2], (setXciArchiveBit ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", xciDumpMenuItems[2], (dumpCfg.xciDumpCfg.setXciArchiveBit ? "Yes" : "No")); breaks++; } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", xciDumpMenuItems[3], (keepCert ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", xciDumpMenuItems[3], (dumpCfg.xciDumpCfg.keepCert ? "Yes" : "No")); breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", xciDumpMenuItems[4], (trimDump ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", xciDumpMenuItems[4], (dumpCfg.xciDumpCfg.trimDump ? "Yes" : "No")); breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", xciDumpMenuItems[5], (calcCrc ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", xciDumpMenuItems[5], (dumpCfg.xciDumpCfg.calcCrc ? "Yes" : "No")); breaks += 2; uiRefreshDisplay(); - dumpCartridgeImage(isFat32, setXciArchiveBit, keepCert, trimDump, calcCrc); + dumpCartridgeImage(&(dumpCfg.xciDumpCfg)); waitForButtonPress(); @@ -4168,59 +4127,54 @@ UIResult uiProcess() } else if (uiState == stateDumpNsp) { - uiDrawString((menuType == MENUTYPE_GAMECARD ? nspDumpGameCardMenuItems[selectedNspDumpType] : nspDumpSdCardEmmcMenuItems[selectedNspDumpType]), 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, (menuType == MENUTYPE_GAMECARD ? nspDumpGameCardMenuItems[selectedNspDumpType] : nspDumpSdCardEmmcMenuItems[selectedNspDumpType])); breaks++; menu = (selectedNspDumpType == DUMP_APP_NSP ? nspAppDumpMenuItems : (selectedNspDumpType == DUMP_PATCH_NSP ? nspPatchDumpMenuItems : nspAddOnDumpMenuItems)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", menu[1], (isFat32 ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[1], (dumpCfg.nspDumpCfg.isFat32 ? "Yes" : "No")); breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", menu[2], (calcCrc ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[2], (dumpCfg.nspDumpCfg.calcCrc ? "Yes" : "No")); breaks++; if (menuType == MENUTYPE_GAMECARD && selectedNspDumpType == DUMP_PATCH_NSP) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", menu[4], (tiklessDump ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[4], (dumpCfg.nspDumpCfg.tiklessDump ? "Yes" : "No")); breaks++; } else if (menuType == MENUTYPE_SDCARD_EMMC) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", menu[3], (removeConsoleData ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[3], (dumpCfg.nspDumpCfg.removeConsoleData ? "Yes" : "No")); breaks++; - if (removeConsoleData) + if (dumpCfg.nspDumpCfg.removeConsoleData) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", menu[4], (tiklessDump ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[4], (dumpCfg.nspDumpCfg.tiklessDump ? "Yes" : "No")); breaks++; } } if (selectedNspDumpType == DUMP_APP_NSP) { - convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, sizeof(versionStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s v%s", menu[5], titleName[selectedAppIndex], versionStr); + convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "%s%s v%s", menu[5], titleName[selectedAppIndex], versionStr); } else if (selectedNspDumpType == DUMP_PATCH_NSP) { - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, menu[5], strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, menu[5], strbuf, MAX_ELEMENTS(strbuf)); } else if (selectedNspDumpType == DUMP_ADDON_NSP) { - retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, true, menu[5], strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, true, menu[5], strbuf, MAX_ELEMENTS(strbuf)); } - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf); breaks += 2; uiRefreshDisplay(); - dumpNintendoSubmissionPackage(selectedNspDumpType, (selectedNspDumpType == DUMP_APP_NSP ? selectedAppIndex : (selectedNspDumpType == DUMP_PATCH_NSP ? selectedPatchIndex : selectedAddOnIndex)), isFat32, calcCrc, removeConsoleData, tiklessDump, false); + dumpNintendoSubmissionPackage(selectedNspDumpType, (selectedNspDumpType == DUMP_APP_NSP ? selectedAppIndex : (selectedNspDumpType == DUMP_PATCH_NSP ? selectedPatchIndex : selectedAddOnIndex)), &(dumpCfg.nspDumpCfg), false); waitForButtonPress(); @@ -4232,61 +4186,56 @@ UIResult uiProcess() } else if (uiState == stateSdCardEmmcBatchDump) { - uiDrawString("Batch dump", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Batch dump"); breaks++; menu = batchModeMenuItems; - if ((batchModeSrc == BATCH_SOURCE_ALL && titleAppCount) || (batchModeSrc == BATCH_SOURCE_SDCARD && sdCardTitleAppCount) || (batchModeSrc == BATCH_SOURCE_EMMC && nandUserTitleAppCount)) + if ((dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL && titleAppCount) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD && sdCardTitleAppCount) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC && nandUserTitleAppCount)) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", menu[1], (dumpAppTitles ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[1], (dumpCfg.batchDumpCfg.dumpAppTitles ? "Yes" : "No")); breaks++; } - if ((batchModeSrc == BATCH_SOURCE_ALL && titlePatchCount) || (batchModeSrc == BATCH_SOURCE_SDCARD && sdCardTitlePatchCount) || (batchModeSrc == BATCH_SOURCE_EMMC && nandUserTitlePatchCount)) + if ((dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL && titlePatchCount) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD && sdCardTitlePatchCount) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC && nandUserTitlePatchCount)) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", menu[2], (dumpPatchTitles ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[2], (dumpCfg.batchDumpCfg.dumpPatchTitles ? "Yes" : "No")); breaks++; } - if ((batchModeSrc == BATCH_SOURCE_ALL && titleAddOnCount) || (batchModeSrc == BATCH_SOURCE_SDCARD && sdCardTitleAddOnCount) || (batchModeSrc == BATCH_SOURCE_EMMC && nandUserTitleAddOnCount)) + if ((dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL && titleAddOnCount) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD && sdCardTitleAddOnCount) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC && nandUserTitleAddOnCount)) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", menu[3], (dumpAddOnTitles ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[3], (dumpCfg.batchDumpCfg.dumpAddOnTitles ? "Yes" : "No")); breaks++; } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", menu[4], (isFat32 ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[4], (dumpCfg.batchDumpCfg.isFat32 ? "Yes" : "No")); breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", menu[5], (removeConsoleData ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[5], (dumpCfg.batchDumpCfg.removeConsoleData ? "Yes" : "No")); breaks++; - if (removeConsoleData) + if (dumpCfg.batchDumpCfg.removeConsoleData) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", menu[6], (tiklessDump ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[6], (dumpCfg.batchDumpCfg.tiklessDump ? "Yes" : "No")); breaks++; } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", menu[7], (skipDumpedTitles ? "Yes" : "No")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[7], (dumpCfg.batchDumpCfg.skipDumpedTitles ? "Yes" : "No")); + breaks++; if ((sdCardTitleAppCount || sdCardTitlePatchCount || sdCardTitleAddOnCount) && (nandUserTitleAppCount || nandUserTitlePatchCount || nandUserTitleAddOnCount)) { + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[8], (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL ? "All (SD card + eMMC)" : (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD ? "SD card" : "eMMC"))); breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", menu[8], (batchModeSrc == BATCH_SOURCE_ALL ? "All (SD card + eMMC)" : (batchModeSrc == BATCH_SOURCE_SDCARD ? "SD card" : "eMMC"))); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); } + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[9], (dumpCfg.batchDumpCfg.rememberDumpedTitles ? "Yes" : "No")); + breaks += 2; uiRefreshDisplay(); - dumpNintendoSubmissionPackageBatch(dumpAppTitles, dumpPatchTitles, dumpAddOnTitles, isFat32, removeConsoleData, tiklessDump, skipDumpedTitles, batchModeSrc); + dumpNintendoSubmissionPackageBatch(&(dumpCfg.batchDumpCfg)); waitForButtonPress(); @@ -4296,8 +4245,7 @@ UIResult uiProcess() } else if (uiState == stateDumpRawHfs0Partition) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Raw %s", (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0PartitionDumpType1MenuItems[selectedPartitionIndex] : hfs0PartitionDumpType2MenuItems[selectedPartitionIndex])); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Raw %s", (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0PartitionDumpType1MenuItems[selectedPartitionIndex] : hfs0PartitionDumpType2MenuItems[selectedPartitionIndex])); breaks += 2; uiRefreshDisplay(); @@ -4311,8 +4259,7 @@ UIResult uiProcess() } else if (uiState == stateDumpHfs0PartitionData) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Data %s", (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0PartitionDumpType1MenuItems[selectedPartitionIndex] : hfs0PartitionDumpType2MenuItems[selectedPartitionIndex])); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Data %s", (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0PartitionDumpType1MenuItems[selectedPartitionIndex] : hfs0PartitionDumpType2MenuItems[selectedPartitionIndex])); breaks += 2; uiRefreshDisplay(); @@ -4326,7 +4273,7 @@ UIResult uiProcess() } else if (uiState == stateHfs0BrowserGetList) { - uiDrawString((hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0BrowserType1MenuItems[selectedPartitionIndex] : hfs0BrowserType2MenuItems[selectedPartitionIndex]), 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0BrowserType1MenuItems[selectedPartitionIndex] : hfs0BrowserType2MenuItems[selectedPartitionIndex])); breaks += 2; uiPleaseWait(0); @@ -4342,8 +4289,7 @@ UIResult uiProcess() } else if (uiState == stateHfs0BrowserCopyFile) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Manual File Dump: %s (HFS0 partition %u [%s])", filenames[selectedFileIndex], selectedPartitionIndex, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, selectedPartitionIndex)); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Manual File Dump: %s (HFS0 partition %u [%s])", filenames[selectedFileIndex], selectedPartitionIndex, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, selectedPartitionIndex)); breaks += 2; uiRefreshDisplay(); @@ -4357,18 +4303,18 @@ UIResult uiProcess() } else if (uiState == stateDumpExeFsSectionData) { - uiDrawString(exeFsMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, exeFsMenuItems[0]); breaks++; if (!exeFsUpdateFlag) { - convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, sizeof(versionStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s v%s", exeFsSectionDumpMenuItems[1], titleName[selectedAppIndex], versionStr); + convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "%s%s v%s", exeFsSectionDumpMenuItems[1], titleName[selectedAppIndex], versionStr); } else { - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update to dump: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update to dump: ", strbuf, MAX_ELEMENTS(strbuf)); } - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf); breaks += 2; uiRefreshDisplay(); @@ -4383,18 +4329,18 @@ UIResult uiProcess() } else if (uiState == stateExeFsSectionBrowserGetList) { - uiDrawString(exeFsMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, exeFsMenuItems[1]); breaks++; if (!exeFsUpdateFlag) { - convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, sizeof(versionStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s v%s", exeFsSectionBrowserMenuItems[1], titleName[selectedAppIndex], versionStr); + convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "%s%s v%s", exeFsSectionBrowserMenuItems[1], titleName[selectedAppIndex], versionStr); } else { - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update to browse: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update to browse: ", strbuf, MAX_ELEMENTS(strbuf)); } - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf); breaks += 2; uiPleaseWait(0); @@ -4424,19 +4370,18 @@ UIResult uiProcess() } else if (uiState == stateExeFsSectionBrowserCopyFile) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Manual File Dump: %s (ExeFS)", filenames[selectedFileIndex]); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Manual File Dump: %s (ExeFS)", filenames[selectedFileIndex]); breaks++; if (!exeFsUpdateFlag) { - convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, sizeof(versionStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Base application: %s v%s", titleName[selectedAppIndex], versionStr); + convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "Base application: %s v%s", titleName[selectedAppIndex], versionStr); } else { - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update: ", strbuf, MAX_ELEMENTS(strbuf)); } - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf); breaks += 2; uiRefreshDisplay(); @@ -4452,29 +4397,29 @@ UIResult uiProcess() { u32 curIndex = 0; - uiDrawString(romFsMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, romFsMenuItems[0]); breaks++; switch(curRomFsType) { case ROMFS_TYPE_APP: - convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, sizeof(versionStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s v%s", romFsSectionDumpMenuItems[1], titleName[selectedAppIndex], versionStr); + convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "%s%s v%s", romFsSectionDumpMenuItems[1], titleName[selectedAppIndex], versionStr); curIndex = selectedAppIndex; break; case ROMFS_TYPE_PATCH: - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update to dump: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update to dump: ", strbuf, MAX_ELEMENTS(strbuf)); curIndex = selectedPatchIndex; break; case ROMFS_TYPE_ADDON: - retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, true, "DLC to dump: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, true, "DLC to dump: ", strbuf, MAX_ELEMENTS(strbuf)); curIndex = selectedAddOnIndex; break; default: break; } - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf); breaks += 2; uiRefreshDisplay(); @@ -4491,29 +4436,29 @@ UIResult uiProcess() { u32 curIndex = 0; - uiDrawString(romFsMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, romFsMenuItems[1]); breaks++; switch(curRomFsType) { case ROMFS_TYPE_APP: - convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, sizeof(versionStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s v%s", romFsSectionBrowserMenuItems[1], titleName[selectedAppIndex], versionStr); + convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "%s%s v%s", romFsSectionBrowserMenuItems[1], titleName[selectedAppIndex], versionStr); curIndex = selectedAppIndex; break; case ROMFS_TYPE_PATCH: - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update to browse: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update to browse: ", strbuf, MAX_ELEMENTS(strbuf)); curIndex = selectedPatchIndex; break; case ROMFS_TYPE_ADDON: - retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, true, "DLC to browse: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, true, "DLC to browse: ", strbuf, MAX_ELEMENTS(strbuf)); curIndex = selectedAddOnIndex; break; default: break; } - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf); breaks += 2; uiPleaseWait(0); @@ -4544,26 +4489,26 @@ UIResult uiProcess() } else if (uiState == stateRomFsSectionBrowserChangeDir) { - uiDrawString(romFsMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, romFsMenuItems[1]); breaks++; switch(curRomFsType) { case ROMFS_TYPE_APP: - convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, sizeof(versionStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s v%s", romFsSectionBrowserMenuItems[1], titleName[selectedAppIndex], versionStr); + convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "%s%s v%s", romFsSectionBrowserMenuItems[1], titleName[selectedAppIndex], versionStr); break; case ROMFS_TYPE_PATCH: - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update to browse: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update to browse: ", strbuf, MAX_ELEMENTS(strbuf)); break; case ROMFS_TYPE_ADDON: - retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, true, "DLC to browse: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, true, "DLC to browse: ", strbuf, MAX_ELEMENTS(strbuf)); break; default: break; } - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf); breaks += 2; bool romfs_fail = false; @@ -4578,7 +4523,7 @@ UIResult uiProcess() } } else { // Unexpected condition - uiDrawString("Error: the selected entry is not a directory!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Error: the selected entry is not a directory!"); romfs_fail = true; } @@ -4603,30 +4548,29 @@ UIResult uiProcess() { u32 curIndex = 0; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Manual File Dump: %s (RomFS)", filenames[selectedFileIndex]); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Manual File Dump: %s (RomFS)", filenames[selectedFileIndex]); breaks++; switch(curRomFsType) { case ROMFS_TYPE_APP: - convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, sizeof(versionStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Base application: %s v%s", titleName[selectedAppIndex], versionStr); + convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "Base application: %s v%s", titleName[selectedAppIndex], versionStr); curIndex = selectedAppIndex; break; case ROMFS_TYPE_PATCH: - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update: ", strbuf, MAX_ELEMENTS(strbuf)); curIndex = selectedPatchIndex; break; case ROMFS_TYPE_ADDON: - retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, true, "DLC: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, true, "DLC: ", strbuf, MAX_ELEMENTS(strbuf)); curIndex = selectedAddOnIndex; break; default: break; } - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf); breaks += 2; uiRefreshDisplay(); @@ -4636,7 +4580,7 @@ UIResult uiProcess() dumpFileFromRomFsSection(curIndex, romFsBrowserEntries[selectedFileIndex].offset, curRomFsType, true); } else { // Unexpected condition - uiDrawString("Error: the selected entry is not a file!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Error: the selected entry is not a file!"); } waitForButtonPress(); @@ -4648,30 +4592,29 @@ UIResult uiProcess() { u32 curIndex = 0; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Manual Directory Dump: romfs:%s (RomFS)", curRomFsPath); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Manual Directory Dump: romfs:%s (RomFS)", curRomFsPath); breaks++; switch(curRomFsType) { case ROMFS_TYPE_APP: - convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, sizeof(versionStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Base application: %s v%s", titleName[selectedAppIndex], versionStr); + convertTitleVersionToDecimal(titleAppVersion[selectedAppIndex], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "Base application: %s v%s", titleName[selectedAppIndex], versionStr); curIndex = selectedAppIndex; break; case ROMFS_TYPE_PATCH: - retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titlePatchTitleID[selectedPatchIndex], titlePatchVersion[selectedPatchIndex], false, true, "Update: ", strbuf, MAX_ELEMENTS(strbuf)); curIndex = selectedPatchIndex; break; case ROMFS_TYPE_ADDON: - retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, true, "DLC: ", strbuf, sizeof(strbuf) / sizeof(strbuf[0])); + retrieveDescriptionForPatchOrAddOn(titleAddOnTitleID[selectedAddOnIndex], titleAddOnVersion[selectedAddOnIndex], true, true, "DLC: ", strbuf, MAX_ELEMENTS(strbuf)); curIndex = selectedAddOnIndex; break; default: break; } - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf); breaks += 2; uiRefreshDisplay(); @@ -4685,7 +4628,7 @@ UIResult uiProcess() } else if (uiState == stateDumpGameCardCertificate) { - uiDrawString(gameCardMenuItems[4], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[4]); breaks += 2; dumpGameCardCertificate(); @@ -4697,7 +4640,7 @@ UIResult uiProcess() } else if (uiState == stateUpdateNSWDBXml) { - uiDrawString(updateMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, updateMenuItems[0]); breaks += 2; updateNSWDBXml(); @@ -4709,7 +4652,7 @@ UIResult uiProcess() } else if (uiState == stateUpdateApplication) { - uiDrawString(updateMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, updateMenuItems[1]); breaks += 2; updateApplication(); diff --git a/source/ui.h b/source/ui.h index 04459b2..852f57c 100644 --- a/source/ui.h +++ b/source/ui.h @@ -3,54 +3,65 @@ #ifndef __UI_H__ #define __UI_H__ -#define FB_WIDTH 1280 -#define FB_HEIGHT 720 +#define FB_WIDTH 1280 +#define FB_HEIGHT 720 -#define CHAR_PT_SIZE 12 -#define SCREEN_DPI_CNT 96 +#define CHAR_PT_SIZE 12 +#define SCREEN_DPI_CNT 96 -#define BG_COLOR_RGB 50 +#define LINE_HEIGHT (font_height + (font_height / 4)) +#define LINE_STRING_OFFSET (font_height / 8) -#define HIGHLIGHT_BG_COLOR_R 33 -#define HIGHLIGHT_BG_COLOR_G 34 -#define HIGHLIGHT_BG_COLOR_B 39 +#define STRING_DEFAULT_POS 8, 8 -#define HIGHLIGHT_FONT_COLOR_R 0 -#define HIGHLIGHT_FONT_COLOR_G 255 -#define HIGHLIGHT_FONT_COLOR_B 197 +#define STRING_X_POS 8 +#define STRING_Y_POS(x) (((x) * LINE_HEIGHT) + LINE_STRING_OFFSET) -#define COMMON_MAX_ELEMENTS 9 -#define HFS0_MAX_ELEMENTS 14 -#define ROMFS_MAX_ELEMENTS 12 -#define SDCARD_MAX_ELEMENTS 3 -#define ORPHAN_MAX_ELEMENTS 12 +#define BG_COLOR_RGB 50, 50, 50 +#define FONT_COLOR_RGB 255, 255, 255 -#define OPTIONS_X_START_POS (35 * CHAR_PT_SIZE) -#define OPTIONS_X_END_POS (OPTIONS_X_START_POS + (6 * CHAR_PT_SIZE)) -#define OPTIONS_X_END_POS_NSP (FB_WIDTH - (4 * CHAR_PT_SIZE)) +#define HIGHLIGHT_BG_COLOR_RGB 33, 34, 39 +#define HIGHLIGHT_FONT_COLOR_RGB 0, 255, 197 -#define TAB_WIDTH 4 +#define FONT_COLOR_SUCCESS_RGB 0, 255, 0 +#define FONT_COLOR_ERROR_RGB 255, 0, 0 +#define FONT_COLOR_TITLE_RGB 115, 115, 255 -#define BROWSER_ICON_DIMENSION 16 +#define EMPTY_BAR_COLOR_RGB 0, 0, 0 + +#define COMMON_MAX_ELEMENTS 9 +#define HFS0_MAX_ELEMENTS 14 +#define ROMFS_MAX_ELEMENTS 12 +#define SDCARD_MAX_ELEMENTS 3 +#define ORPHAN_MAX_ELEMENTS 12 +#define BATCH_MAX_ELEMENTS 12 + +#define OPTIONS_X_START_POS (35 * CHAR_PT_SIZE) +#define OPTIONS_X_END_POS (OPTIONS_X_START_POS + (6 * CHAR_PT_SIZE)) +#define OPTIONS_X_END_POS_NSP (FB_WIDTH - (4 * CHAR_PT_SIZE)) + +#define TAB_WIDTH 4 + +#define BROWSER_ICON_DIMENSION 16 // UTF-8 sequences -#define UPWARDS_ARROW "\xE2\x86\x91" -#define DOWNWARDS_ARROW "\xE2\x86\x93" +#define UPWARDS_ARROW "\xE2\x86\x91" +#define DOWNWARDS_ARROW "\xE2\x86\x93" -#define NINTENDO_FONT_A "\xEE\x82\xA0" -#define NINTENDO_FONT_B "\xEE\x82\xA1" -#define NINTENDO_FONT_X "\xEE\x82\xA2" -#define NINTENDO_FONT_Y "\xEE\x82\xA3" -#define NINTENDO_FONT_L "\xEE\x82\xA4" -#define NINTENDO_FONT_R "\xEE\x82\xA5" -#define NINTENDO_FONT_ZL "\xEE\x82\xA6" -#define NINTENDO_FONT_ZR "\xEE\x82\xA7" -#define NINTENDO_FONT_DPAD "\xEE\x82\xAA" -#define NINTENDO_FONT_PLUS "\xEE\x82\xB5" -#define NINTENDO_FONT_HOME "\xEE\x82\xB9" -#define NINTENDO_FONT_LSTICK "\xEE\x83\x81" -#define NINTENDO_FONT_RSTICK "\xEE\x83\x82" +#define NINTENDO_FONT_A "\xEE\x82\xA0" +#define NINTENDO_FONT_B "\xEE\x82\xA1" +#define NINTENDO_FONT_X "\xEE\x82\xA2" +#define NINTENDO_FONT_Y "\xEE\x82\xA3" +#define NINTENDO_FONT_L "\xEE\x82\xA4" +#define NINTENDO_FONT_R "\xEE\x82\xA5" +#define NINTENDO_FONT_ZL "\xEE\x82\xA6" +#define NINTENDO_FONT_ZR "\xEE\x82\xA7" +#define NINTENDO_FONT_DPAD "\xEE\x82\xAA" +#define NINTENDO_FONT_PLUS "\xEE\x82\xB5" +#define NINTENDO_FONT_HOME "\xEE\x82\xB9" +#define NINTENDO_FONT_LSTICK "\xEE\x83\x81" +#define NINTENDO_FONT_RSTICK "\xEE\x83\x82" typedef enum { resultNone, @@ -160,9 +171,9 @@ bool uiLoadJpgFromMem(u8 *rawJpg, size_t rawJpgSize, int expectedWidth, int expe bool uiLoadJpgFromFile(const char *filename, int expectedWidth, int expectedHeight, int desiredWidth, int desiredHeight, u8 **outBuf); -void uiDrawString(const char *string, int x, int y, u8 r, u8 g, u8 b); +void uiDrawString(int x, int y, u8 r, u8 g, u8 b, const char *fmt, ...); -u32 uiGetStrWidth(const char *string); +u32 uiGetStrWidth(const char *fmt, ...); void uiRefreshDisplay(); diff --git a/source/util.c b/source/util.c index d1769fc..cdee781 100644 --- a/source/util.c +++ b/source/util.c @@ -33,6 +33,8 @@ extern curMenuType menuType; /* Constants */ +const char *configPath = NXDUMPTOOL_BASE_PATH "config.bin"; + const char *nswReleasesXmlUrl = "http://nswdb.com/xml.php"; const char *nswReleasesXmlTmpPath = NXDUMPTOOL_BASE_PATH "NSWreleases.xml.tmp"; const char *nswReleasesXmlPath = NXDUMPTOOL_BASE_PATH "NSWreleases.xml"; @@ -44,11 +46,13 @@ const char *nswReleasesChildrenImgCrc = "imgcrc"; const char *nswReleasesChildrenReleaseName = "releasename"; const char *githubReleasesApiUrl = "https://api.github.com/repos/DarkMatterCore/nxdumptool/releases/latest"; -const char *nxDumpToolPath = "sdmc:/switch/nxdumptool/nxdumptool.nro"; +const char *nxDumpToolPath = NXDUMPTOOL_BASE_PATH "nxdumptool.nro"; const char *userAgent = "nxdumptool/" APP_VERSION " (Nintendo Switch)"; /* Statically allocated variables */ +dumpOptions dumpCfg; + static char *result_buf = NULL; static size_t result_sz = 0; static size_t result_written = 0; @@ -130,10 +134,71 @@ u8 *ncaCtrBuf = NULL; orphan_patch_addon_entry *orphanEntries = NULL; -char strbuf[NAME_BUF_LEN * 4] = {'\0'}; +char strbuf[NAME_BUF_LEN] = {'\0'}; char appLaunchPath[NAME_BUF_LEN] = {'\0'}; +void loadConfig() +{ + // Set default configuration values + memset(&dumpCfg, 0x00, sizeof(dumpOptions)); + + dumpCfg.xciDumpCfg.isFat32 = true; + dumpCfg.xciDumpCfg.calcCrc = true; + + dumpCfg.nspDumpCfg.isFat32 = true; + + dumpCfg.batchDumpCfg.isFat32 = true; + dumpCfg.batchDumpCfg.skipDumpedTitles = true; + dumpCfg.batchDumpCfg.batchModeSrc = BATCH_SOURCE_ALL; + + FILE *configFile = fopen(configPath, "rb"); + if (!configFile) return; + + fseek(configFile, 0, SEEK_END); + size_t configFileSize = ftell(configFile); + rewind(configFile); + + if (configFileSize != sizeof(dumpOptions)) + { + fclose(configFile); + unlink(configPath); + return; + } + + dumpOptions tmpCfg; + size_t read_res = fread(&tmpCfg, 1, sizeof(dumpOptions), configFile); + fclose(configFile); + + if (read_res != sizeof(dumpOptions)) + { + unlink(configPath); + return; + } + + memcpy(&dumpCfg, &tmpCfg, sizeof(dumpOptions)); + + // Check if the configuration is correct + if (dumpCfg.xciDumpCfg.setXciArchiveBit && !dumpCfg.xciDumpCfg.isFat32) dumpCfg.xciDumpCfg.setXciArchiveBit = false; + + if (dumpCfg.nspDumpCfg.tiklessDump && !dumpCfg.nspDumpCfg.removeConsoleData) dumpCfg.nspDumpCfg.tiklessDump = false; + + if (dumpCfg.batchDumpCfg.tiklessDump && !dumpCfg.batchDumpCfg.removeConsoleData) dumpCfg.batchDumpCfg.tiklessDump = false; + + if (dumpCfg.batchDumpCfg.batchModeSrc >= BATCH_SOURCE_CNT) dumpCfg.batchDumpCfg.batchModeSrc = BATCH_SOURCE_ALL; +} + +void saveConfig() +{ + FILE *configFile = fopen(configPath, "wb"); + if (!configFile) return; + + size_t write_res = fwrite(&dumpCfg, 1, sizeof(dumpOptions), configFile); + fclose(configFile); + + if (write_res != sizeof(dumpOptions)) unlink(configPath); +} + bool isGameCardInserted() { bool inserted; @@ -1113,6 +1178,7 @@ void createOutputDirectories() mkdir(EXEFS_DUMP_PATH, 0744); mkdir(ROMFS_DUMP_PATH, 0744); mkdir(CERT_DUMP_PATH, 0744); + mkdir(BATCH_OVERRIDES_PATH, 0744); } void strtrim(char *str) @@ -1193,11 +1259,11 @@ bool getRootHfs0Header() return false; } - convertSize(gameCardSize, gameCardSizeStr, sizeof(gameCardSizeStr) / sizeof(gameCardSizeStr[0])); + convertSize(gameCardSize, gameCardSizeStr, MAX_ELEMENTS(gameCardSizeStr)); memcpy(&trimmedCardSize, gamecard_header + GAMECARD_DATAEND_ADDR, sizeof(u64)); trimmedCardSize = (GAMECARD_HEADER_SIZE + (trimmedCardSize * MEDIA_UNIT_SIZE)); - convertSize(trimmedCardSize, trimmedCardSizeStr, sizeof(trimmedCardSizeStr) / sizeof(trimmedCardSizeStr[0])); + convertSize(trimmedCardSize, trimmedCardSizeStr, MAX_ELEMENTS(trimmedCardSizeStr)); memcpy(&hfs0_offset, gamecard_header + HFS0_OFFSET_ADDR, sizeof(u64)); memcpy(&hfs0_size, gamecard_header + HFS0_SIZE_ADDR, sizeof(u64)); @@ -1294,7 +1360,7 @@ void getGameCardUpdateInfo() u8 micro = (u8)((gameCardUpdateVersion >> 16) & 0xF); u16 bugfix = (u16)gameCardUpdateVersion; - snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "%u.%u.%u (bugfix %u) - v%u", major, minor, micro, bugfix, gameCardUpdateVersion); + snprintf(gameCardUpdateVersionStr, MAX_ELEMENTS(gameCardUpdateVersionStr), "%u.%u.%u (bugfix %u) - v%u", major, minor, micro, bugfix, gameCardUpdateVersion); } else { uiStatusMsg("getGameCardUpdateInfo: update Title ID mismatch! %016lX != %016lX", gameCardUpdateTitleID, GAMECARD_UPDATE_TITLEID); } @@ -1503,15 +1569,13 @@ bool getPartitionHfs0Header(u32 partition) if (R_SUCCEEDED(result = fsDeviceOperatorGetGameCardHandle(&fsOperatorInstance, &handle))) { - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle succeeded: 0x%08X", handle.value); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "GetGameCardHandle succeeded: 0x%08X", handle.value); breaks++;*/ // Same ugly hack from dumpRawHfs0Partition() if (R_SUCCEEDED(result = fsOpenGameCardStorage(&gameCardStorage, &handle, HFS0_TO_ISTORAGE_IDX(hfs0_partition_cnt, partition)))) { - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage succeeded: 0x%08X", handle); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "OpenGameCardStorage succeeded: 0x%08X", handle); breaks++;*/ // First read MEDIA_UNIT_SIZE bytes @@ -1527,20 +1591,16 @@ bool getPartitionHfs0Header(u32 partition) memcpy(&partitionHfs0StrTableSize, buf + HFS0_STR_TABLE_SIZE_ADDR, sizeof(u32)); partitionHfs0HeaderSize = (HFS0_ENTRY_TABLE_ADDR + (sizeof(hfs0_entry_table) * partitionHfs0FileCount) + partitionHfs0StrTableSize); - /*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Partition #%u HFS0 header offset (relative to IStorage instance): 0x%016lX", partition, partitionHfs0HeaderOffset); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Partition #%u HFS0 header offset (relative to IStorage instance): 0x%016lX", partition, partitionHfs0HeaderOffset); breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Partition #%u HFS0 header size: %lu bytes", partition, partitionHfs0HeaderSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Partition #%u HFS0 header size: %lu bytes", partition, partitionHfs0HeaderSize); breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Partition #%u file count: %u", partition, partitionHfs0FileCount); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Partition #%u file count: %u", partition, partitionHfs0FileCount); breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Partition #%u string table size: %u bytes", partition, partitionHfs0StrTableSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Partition #%u string table size: %u bytes", partition, partitionHfs0StrTableSize); breaks++; uiRefreshDisplay();*/ @@ -1570,45 +1630,35 @@ bool getPartitionHfs0Header(u32 partition) partitionHfs0FileCount = 0; partitionHfs0StrTableSize = 0; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "StorageRead failed (0x%08X) at offset 0x%016lX", result, partitionHfs0HeaderOffset); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "StorageRead failed (0x%08X) at offset 0x%016lX", result, partitionHfs0HeaderOffset); } } - /*if (success) - { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Partition #%u HFS0 header successfully retrieved!", partition); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - }*/ + //if (success) uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Partition #%u HFS0 header successfully retrieved!", partition); } else { partitionHfs0HeaderOffset = 0; partitionHfs0HeaderSize = 0; partitionHfs0FileCount = 0; partitionHfs0StrTableSize = 0; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to allocate memory for the HFS0 header from partition #%u!", partition); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to allocate memory for the HFS0 header from partition #%u!", partition); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Magic word mismatch! 0x%08X != 0x%08X", magic, HFS0_MAGIC); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Magic word mismatch! 0x%08X != 0x%08X", magic, HFS0_MAGIC); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "StorageRead failed (0x%08X) at offset 0x%016lX", result, partitionHfs0HeaderOffset); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "StorageRead failed (0x%08X) at offset 0x%016lX", result, partitionHfs0HeaderOffset); } fsStorageClose(&gameCardStorage); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "OpenGameCardStorage failed! (0x%08X)", result); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "GetGameCardHandle failed! (0x%08X)", result); } } else { - uiDrawString("Error: unable to get partition details from the root HFS0 header!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to get partition details from the root HFS0 header!"); } return success; @@ -1624,22 +1674,21 @@ bool getHfs0FileList(u32 partition) if (!partitionHfs0Header) { - uiDrawString("HFS0 partition header information unavailable!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "HFS0 partition header information unavailable!"); breaks += 2; return false; } if (!partitionHfs0FileCount) { - uiDrawString("The selected partition is empty!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "The selected partition is empty!"); breaks += 2; return false; } if (partitionHfs0FileCount > FILENAME_MAX_CNT) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "HFS0 partition contains more than %u files! (%u entries)", FILENAME_MAX_CNT, partitionHfs0FileCount); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "HFS0 partition contains more than %u files! (%u entries)", FILENAME_MAX_CNT, partitionHfs0FileCount); breaks += 2; return false; } @@ -1647,7 +1696,7 @@ bool getHfs0FileList(u32 partition) hfs0_entry_table *entryTable = calloc(partitionHfs0FileCount, sizeof(hfs0_entry_table)); if (!entryTable) { - uiDrawString("Unable to allocate memory for the HFS0 file entries!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Unable to allocate memory for the HFS0 file entries!"); breaks += 2; return false; } @@ -1680,7 +1729,7 @@ bool getPartitionHfs0FileByName(FsStorage *gameCardStorage, const char *filename { if (!partitionHfs0Header || !partitionHfs0FileCount || !partitionHfs0HeaderSize || !gameCardStorage || !filename || !outBuf || !outBufSize) { - uiDrawString("Error: invalid parameters to retrieve file from HFS0 partition!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to retrieve file from HFS0 partition!"); return NULL; } @@ -1700,16 +1749,14 @@ bool getPartitionHfs0FileByName(FsStorage *gameCardStorage, const char *filename if (outBufSize > tmp_hfs0_entry.file_size) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: file \"%s\" is smaller than expected! (0x%016lX < 0x%016lX)", filename, tmp_hfs0_entry.file_size, outBufSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: file \"%s\" is smaller than expected! (0x%016lX < 0x%016lX)", filename, tmp_hfs0_entry.file_size, outBufSize); proceed = false; break; } if (R_FAILED(result = fsStorageRead(gameCardStorage, partitionHfs0HeaderSize + tmp_hfs0_entry.file_offset, outBuf, outBufSize))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to read file \"%s\" from the HFS0 partition!", filename); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to read file \"%s\" from the HFS0 partition!", filename); proceed = false; break; } @@ -1720,11 +1767,7 @@ bool getPartitionHfs0FileByName(FsStorage *gameCardStorage, const char *filename } } - if (proceed && !found) - { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to find file \"%s\" in the HFS0 partition!", filename); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); - } + if (proceed && !found) uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to find file \"%s\" in the HFS0 partition!", filename); return success; } @@ -1733,7 +1776,7 @@ bool calculateExeFsExtractedDataSize(u64 *out) { if (!exeFsContext.exefs_header.file_cnt || !exeFsContext.exefs_entries || !out) { - uiDrawString("Error: invalid parameters to calculate extracted data size for the ExeFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to calculate extracted data size for the ExeFS section!"); return false; } @@ -1751,7 +1794,7 @@ bool calculateRomFsFullExtractedSize(bool usePatch, u64 *out) { if ((!usePatch && (!romFsContext.romfs_filetable_size || !romFsContext.romfs_file_entries)) || (usePatch && (!bktrContext.romfs_filetable_size || !bktrContext.romfs_file_entries)) || !out) { - uiDrawString("Error: invalid parameters to calculate extracted data size for the RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to calculate extracted data size for the RomFS section!"); return false; } @@ -1779,7 +1822,7 @@ bool calculateRomFsExtractedDirSize(u32 dir_offset, bool usePatch, u64 *out) { if ((!usePatch && (!romFsContext.romfs_dirtable_size || !romFsContext.romfs_dir_entries || !romFsContext.romfs_filetable_size || !romFsContext.romfs_file_entries || dir_offset > romFsContext.romfs_dirtable_size)) || (usePatch && (!bktrContext.romfs_dirtable_size || !bktrContext.romfs_dir_entries || !bktrContext.romfs_filetable_size || !bktrContext.romfs_file_entries || dir_offset > bktrContext.romfs_dirtable_size)) || !out) { - uiDrawString("Error: invalid parameters to calculate extracted size for the current RomFS directory!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to calculate extracted size for the current RomFS directory!"); return false; } @@ -1791,7 +1834,7 @@ bool calculateRomFsExtractedDirSize(u32 dir_offset, bool usePatch, u64 *out) // Check if we're dealing with a nameless directory that's not the root directory if (!dirEntry->nameLen && dir_offset > 0) { - uiDrawString("Error: directory entry without name in RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: directory entry without name in RomFS section!"); return false; } @@ -1871,7 +1914,7 @@ bool readNcaExeFsSection(u32 titleIndex, bool usePatch) if ((!usePatch && !titleAppStorageId) || (usePatch && !titlePatchStorageId)) { - uiDrawString("Error: title storage ID unavailable!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: title storage ID unavailable!"); breaks += 2; return false; } @@ -1880,7 +1923,7 @@ bool readNcaExeFsSection(u32 titleIndex, bool usePatch) if (curStorageId != FsStorageId_GameCard && curStorageId != FsStorageId_SdCard && curStorageId != FsStorageId_NandUser) { - uiDrawString("Error: invalid title storage ID!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid title storage ID!"); breaks += 2; return false; } @@ -1921,14 +1964,14 @@ bool readNcaExeFsSection(u32 titleIndex, bool usePatch) if (!titleCount) { - uiDrawString("Error: invalid title type count!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid title type count!"); breaks += 2; return false; } if (ncmTitleIndex > (titleCount - 1)) { - uiDrawString("Error: invalid title index!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid title index!"); breaks += 2; return false; } @@ -1950,27 +1993,25 @@ bool readNcaExeFsSection(u32 titleIndex, bool usePatch) if (!partitionHfs0FileCount) { - uiDrawString("The Secure HFS0 partition is empty!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "The Secure HFS0 partition is empty!"); goto out; } } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Looking for the Program NCA (%s)...", (!usePatch ? "base application" : "update")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Looking for the Program NCA (%s)...", (!usePatch ? "base application" : "update")); uiRefreshDisplay(); breaks++; titleList = calloc(1, titleListSize); if (!titleList) { - uiDrawString("Error: unable to allocate memory for the ApplicationContentMetaKey struct!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the ApplicationContentMetaKey struct!"); goto out; } if (R_FAILED(result = ncmOpenContentMetaDatabase(curStorageId, &ncmDb))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmOpenContentMetaDatabase failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmOpenContentMetaDatabase failed! (0x%08X)", result); goto out; } @@ -1978,28 +2019,25 @@ bool readNcaExeFsSection(u32 titleIndex, bool usePatch) if (R_FAILED(result = ncmContentMetaDatabaseListApplication(&ncmDb, filter, titleList, titleListSize, &written, &total))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmContentMetaDatabaseListApplication failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmContentMetaDatabaseListApplication failed! (0x%08X)", result); goto out; } if (!written || !total) { - uiDrawString("Error: ncmContentMetaDatabaseListApplication wrote no entries to output buffer!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmContentMetaDatabaseListApplication wrote no entries to output buffer!"); goto out; } if (written != total) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: title count mismatch in ncmContentMetaDatabaseListApplication (%u != %u)", written, total); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: title count mismatch in ncmContentMetaDatabaseListApplication (%u != %u)", written, total); goto out; } if (R_FAILED(result = ncmContentMetaDatabaseGet(&ncmDb, &(titleList[ncmTitleIndex].metaRecord), sizeof(NcmContentMetaRecordsHeader), &contentRecordsHeader, &contentRecordsHeaderReadSize))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmContentMetaDatabaseGet failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmContentMetaDatabaseGet failed! (0x%08X)", result); goto out; } @@ -2008,21 +2046,19 @@ bool readNcaExeFsSection(u32 titleIndex, bool usePatch) titleContentRecords = calloc(titleNcaCount, sizeof(NcmContentRecord)); if (!titleContentRecords) { - uiDrawString("Error: unable to allocate memory for the ContentRecord struct!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the ContentRecord struct!"); goto out; } if (R_FAILED(result = ncmContentMetaDatabaseListContentInfo(&ncmDb, &(titleList[ncmTitleIndex].metaRecord), 0, titleContentRecords, titleNcaCount * sizeof(NcmContentRecord), &written))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmContentMetaDatabaseListContentInfo failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmContentMetaDatabaseListContentInfo failed! (0x%08X)", result); goto out; } if (R_FAILED(result = ncmOpenContentStorage(curStorageId, &ncmStorage))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmOpenContentStorage failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmOpenContentStorage failed! (0x%08X)", result); goto out; } @@ -2031,7 +2067,7 @@ bool readNcaExeFsSection(u32 titleIndex, bool usePatch) if (titleContentRecords[i].type == NcmContentType_Program) { memcpy(&ncaId, &(titleContentRecords[i].ncaId), sizeof(NcmNcaId)); - convertDataToHexString(titleContentRecords[i].ncaId.c, 16, ncaIdStr, 33); + convertDataToHexString(titleContentRecords[i].ncaId.c, SHA256_HASH_SIZE / 2, ncaIdStr, 33); foundProgram = true; break; } @@ -2039,23 +2075,21 @@ bool readNcaExeFsSection(u32 titleIndex, bool usePatch) if (!foundProgram) { - uiDrawString("Error: unable to find Program NCA!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to find Program NCA!"); goto out; } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Found Program NCA: \"%s.nca\".", ncaIdStr); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Found Program NCA: \"%s.nca\".", ncaIdStr); uiRefreshDisplay(); breaks += 2; - /*uiDrawString("Retrieving ExeFS entries...", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Retrieving ExeFS entries..."); uiRefreshDisplay(); breaks++;*/ if (R_FAILED(result = ncmContentStorageReadContentIdFile(&ncmStorage, &ncaId, 0, ncaHeader, NCA_FULL_HEADER_LENGTH))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read header from Program NCA! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read header from Program NCA! (0x%08X)", result); goto out; } @@ -2077,7 +2111,7 @@ bool readNcaExeFsSection(u32 titleIndex, bool usePatch) if (has_rights_id) { - uiDrawString("Error: Rights ID field in Program NCA header not empty!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: Rights ID field in Program NCA header not empty!"); goto out; } } @@ -2149,7 +2183,7 @@ bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType) if ((curRomFsType == ROMFS_TYPE_APP && !titleAppStorageId) || (curRomFsType == ROMFS_TYPE_PATCH && !titlePatchStorageId) || (curRomFsType == ROMFS_TYPE_ADDON && !titlePatchStorageId)) { - uiDrawString("Error: title storage ID unavailable!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: title storage ID unavailable!"); breaks += 2; return false; } @@ -2158,7 +2192,7 @@ bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType) if (curStorageId != FsStorageId_GameCard && curStorageId != FsStorageId_SdCard && curStorageId != FsStorageId_NandUser) { - uiDrawString("Error: invalid title storage ID!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid title storage ID!"); breaks += 2; return false; } @@ -2224,14 +2258,14 @@ bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType) if (!titleCount) { - uiDrawString("Error: invalid title type count!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid title type count!"); breaks += 2; return false; } if (ncmTitleIndex > (titleCount - 1)) { - uiDrawString("Error: invalid title index!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid title index!"); breaks += 2; return false; } @@ -2253,27 +2287,25 @@ bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType) if (!partitionHfs0FileCount) { - uiDrawString("The Secure HFS0 partition is empty!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "The Secure HFS0 partition is empty!"); goto out; } } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Looking for the %s NCA (%s)...", (curRomFsType == ROMFS_TYPE_ADDON ? "Data" : "Program"), (curRomFsType == ROMFS_TYPE_APP ? "base application" : (curRomFsType == ROMFS_TYPE_PATCH ? "update" : "DLC"))); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Looking for the %s NCA (%s)...", (curRomFsType == ROMFS_TYPE_ADDON ? "Data" : "Program"), (curRomFsType == ROMFS_TYPE_APP ? "base application" : (curRomFsType == ROMFS_TYPE_PATCH ? "update" : "DLC"))); uiRefreshDisplay(); breaks++; titleList = calloc(1, titleListSize); if (!titleList) { - uiDrawString("Error: unable to allocate memory for the ApplicationContentMetaKey struct!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the ApplicationContentMetaKey struct!"); goto out; } if (R_FAILED(result = ncmOpenContentMetaDatabase(curStorageId, &ncmDb))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmOpenContentMetaDatabase failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmOpenContentMetaDatabase failed! (0x%08X)", result); goto out; } @@ -2281,28 +2313,25 @@ bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType) if (R_FAILED(result = ncmContentMetaDatabaseListApplication(&ncmDb, filter, titleList, titleListSize, &written, &total))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmContentMetaDatabaseListApplication failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmContentMetaDatabaseListApplication failed! (0x%08X)", result); goto out; } if (!written || !total) { - uiDrawString("Error: ncmContentMetaDatabaseListApplication wrote no entries to output buffer!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmContentMetaDatabaseListApplication wrote no entries to output buffer!"); goto out; } if (written != total) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: title count mismatch in ncmContentMetaDatabaseListApplication (%u != %u)", written, total); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: title count mismatch in ncmContentMetaDatabaseListApplication (%u != %u)", written, total); goto out; } if (R_FAILED(result = ncmContentMetaDatabaseGet(&ncmDb, &(titleList[ncmTitleIndex].metaRecord), sizeof(NcmContentMetaRecordsHeader), &contentRecordsHeader, &contentRecordsHeaderReadSize))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmContentMetaDatabaseGet failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmContentMetaDatabaseGet failed! (0x%08X)", result); goto out; } @@ -2311,21 +2340,19 @@ bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType) titleContentRecords = calloc(titleNcaCount, sizeof(NcmContentRecord)); if (!titleContentRecords) { - uiDrawString("Error: unable to allocate memory for the ContentRecord struct!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the ContentRecord struct!"); goto out; } if (R_FAILED(result = ncmContentMetaDatabaseListContentInfo(&ncmDb, &(titleList[ncmTitleIndex].metaRecord), 0, titleContentRecords, titleNcaCount * sizeof(NcmContentRecord), &written))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmContentMetaDatabaseListContentInfo failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmContentMetaDatabaseListContentInfo failed! (0x%08X)", result); goto out; } if (R_FAILED(result = ncmOpenContentStorage(curStorageId, &ncmStorage))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmOpenContentStorage failed! (0x%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: ncmOpenContentStorage failed! (0x%08X)", result); goto out; } @@ -2334,7 +2361,7 @@ bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType) if (((curRomFsType == ROMFS_TYPE_APP || curRomFsType == ROMFS_TYPE_PATCH) && titleContentRecords[i].type == NcmContentType_Program) || (curRomFsType == ROMFS_TYPE_ADDON && titleContentRecords[i].type == NcmContentType_Data)) { memcpy(&ncaId, &(titleContentRecords[i].ncaId), sizeof(NcmNcaId)); - convertDataToHexString(titleContentRecords[i].ncaId.c, 16, ncaIdStr, 33); + convertDataToHexString(titleContentRecords[i].ncaId.c, SHA256_HASH_SIZE / 2, ncaIdStr, 33); foundNca = true; break; } @@ -2342,24 +2369,21 @@ bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType) if (!foundNca) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to find %s NCA!", (curRomFsType == ROMFS_TYPE_ADDON ? "Data" : "Program")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to find %s NCA!", (curRomFsType == ROMFS_TYPE_ADDON ? "Data" : "Program")); goto out; } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Found %s NCA: \"%s.nca\".", (curRomFsType == ROMFS_TYPE_ADDON ? "Data" : "Program"), ncaIdStr); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Found %s NCA: \"%s.nca\".", (curRomFsType == ROMFS_TYPE_ADDON ? "Data" : "Program"), ncaIdStr); uiRefreshDisplay(); breaks += 2; - /*uiDrawString("Retrieving RomFS entry tables...", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + /*uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Retrieving RomFS entry tables..."); uiRefreshDisplay(); breaks++;*/ if (R_FAILED(result = ncmContentStorageReadContentIdFile(&ncmStorage, &ncaId, 0, ncaHeader, NCA_FULL_HEADER_LENGTH))) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read header from %s NCA! (0x%08X)", (curRomFsType == ROMFS_TYPE_ADDON ? "Data" : "Program"), result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read header from %s NCA! (0x%08X)", (curRomFsType == ROMFS_TYPE_ADDON ? "Data" : "Program"), result); goto out; } @@ -2381,8 +2405,7 @@ bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType) if (has_rights_id) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: Rights ID field in %s NCA header not empty!", (curRomFsType == ROMFS_TYPE_ADDON ? "Data" : "Program")); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: Rights ID field in %s NCA header not empty!", (curRomFsType == ROMFS_TYPE_ADDON ? "Data" : "Program")); goto out; } } @@ -2406,7 +2429,7 @@ bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType) if (i == titleAppCount) { - uiDrawString("Error: unable to find base application title index for the selected update!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to find base application title index for the selected update!"); goto out; } @@ -2448,14 +2471,13 @@ bool getExeFsFileList() { if (!exeFsContext.exefs_header.file_cnt || !exeFsContext.exefs_entries || !exeFsContext.exefs_str_table) { - uiDrawString("Error: invalid parameters to retrieve ExeFS section filelist!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to retrieve ExeFS section filelist!"); return false; } if (exeFsContext.exefs_header.file_cnt > FILENAME_MAX_CNT) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "ExeFS section contains more than %u entries! (%u entries)", FILENAME_MAX_CNT, exeFsContext.exefs_header.file_cnt); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "ExeFS section contains more than %u entries! (%u entries)", FILENAME_MAX_CNT, exeFsContext.exefs_header.file_cnt); return false; } @@ -2479,7 +2501,7 @@ bool getRomFsParentDir(u32 dir_offset, bool usePatch, u32 *out) { if ((!usePatch && (!romFsContext.romfs_dirtable_size || dir_offset > romFsContext.romfs_dirtable_size || !romFsContext.romfs_dir_entries)) || (usePatch && (!bktrContext.romfs_dirtable_size || dir_offset > bktrContext.romfs_dirtable_size || !bktrContext.romfs_dir_entries))) { - uiDrawString("Error: invalid parameters to retrieve parent RomFS section directory!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to retrieve parent RomFS section directory!"); return false; } @@ -2494,7 +2516,7 @@ bool generateCurrentRomFsPath(u32 dir_offset, bool usePatch) { if ((!usePatch && (!romFsContext.romfs_dirtable_size || dir_offset > romFsContext.romfs_dirtable_size || !romFsContext.romfs_dir_entries)) || (usePatch && (!bktrContext.romfs_dirtable_size || dir_offset > bktrContext.romfs_dirtable_size || !bktrContext.romfs_dir_entries))) { - uiDrawString("Error: invalid parameters to generate current RomFS section path!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to generate current RomFS section path!"); return false; } @@ -2505,7 +2527,7 @@ bool generateCurrentRomFsPath(u32 dir_offset, bool usePatch) if (!entry->nameLen) { - uiDrawString("Error: directory entry without name in RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: directory entry without name in RomFS section!"); return false; } @@ -2547,7 +2569,7 @@ bool getRomFsFileList(u32 dir_offset, bool usePatch) if ((!usePatch && (!romFsContext.romfs_dirtable_size || dir_offset > romFsContext.romfs_dirtable_size || !romFsContext.romfs_dir_entries || !romFsContext.romfs_filetable_size || !romFsContext.romfs_file_entries)) || (usePatch && (!bktrContext.romfs_dirtable_size || dir_offset > bktrContext.romfs_dirtable_size || !bktrContext.romfs_dir_entries || !bktrContext.romfs_filetable_size || !bktrContext.romfs_file_entries))) { - uiDrawString("Error: invalid parameters to retrieve RomFS section filelist!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to retrieve RomFS section filelist!"); return false; } @@ -2567,7 +2589,7 @@ bool getRomFsFileList(u32 dir_offset, bool usePatch) if (!entry->nameLen) { - uiDrawString("Error: directory entry without name in RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: directory entry without name in RomFS section!"); return false; } @@ -2586,7 +2608,7 @@ bool getRomFsFileList(u32 dir_offset, bool usePatch) if (!entry->nameLen) { - uiDrawString("Error: file entry without name in RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: file entry without name in RomFS section!"); return false; } @@ -2600,8 +2622,7 @@ bool getRomFsFileList(u32 dir_offset, bool usePatch) if (totalEntryCnt > FILENAME_MAX_CNT) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Current RomFS dir contains more than %u entries! (%u entries)", FILENAME_MAX_CNT, totalEntryCnt); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Current RomFS dir contains more than %u entries! (%u entries)", FILENAME_MAX_CNT, totalEntryCnt); return false; } @@ -2609,7 +2630,7 @@ bool getRomFsFileList(u32 dir_offset, bool usePatch) romFsBrowserEntries = calloc(totalEntryCnt, sizeof(romfs_browser_entry)); if (!romFsBrowserEntries) { - uiDrawString("Error: unable to allocate memory for file/dir attributes in RomFS section!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for file/dir attributes in RomFS section!"); return false; } @@ -2817,7 +2838,7 @@ char *generateFullDumpName() } } - snprintf(tmp, sizeof(tmp) / sizeof(tmp[0]), "%s v%u (%016lX)", fixedTitleName[i], highestVersion, titleAppTitleID[i]); + snprintf(tmp, MAX_ELEMENTS(tmp), "%s v%u (%016lX)", fixedTitleName[i], highestVersion, titleAppTitleID[i]); if ((strlen(fullname) + strlen(tmp) + 4) > strsize) { @@ -2908,7 +2929,7 @@ void retrieveDescriptionForPatchOrAddOn(u64 titleID, u32 version, bool addOn, bo if (!outBuf || !outBufSize) return; char versionStr[128] = {'\0'}; - convertTitleVersionToDecimal(version, versionStr, sizeof(versionStr)); + convertTitleVersionToDecimal(version, versionStr, MAX_ELEMENTS(versionStr)); if (!titleAppCount || !titleAppTitleID || !titleName || !*titleName || !addAppName) { @@ -3020,8 +3041,8 @@ void generateOrphanPatchOrAddOnList() if (!foundMatch) { - convertTitleVersionToDecimal(titlePatchVersion[i], versionStr, sizeof(versionStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%016lX v%s (Update)", titlePatchTitleID[i], versionStr); + convertTitleVersionToDecimal(titlePatchVersion[i], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "%016lX v%s (Update)", titlePatchTitleID[i], versionStr); addStringToFilenameBuffer(strbuf, &nextFilename); orphanPatchEntries[orphanEntryIndex].index = i; @@ -3062,8 +3083,8 @@ void generateOrphanPatchOrAddOnList() if (!foundMatch) { - convertTitleVersionToDecimal(titleAddOnVersion[i], versionStr, sizeof(versionStr)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%016lX v%s (DLC)", titleAddOnTitleID[i], versionStr); + convertTitleVersionToDecimal(titleAddOnVersion[i], versionStr, MAX_ELEMENTS(versionStr)); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "%016lX v%s (DLC)", titleAddOnTitleID[i], versionStr); addStringToFilenameBuffer(strbuf, &nextFilename); orphanAddOnEntries[orphanEntryIndex].index = i; @@ -3197,11 +3218,13 @@ u32 retrieveLastPatchOrAddOnIndexFromBaseApplication(u32 appIndex, bool addOn) void waitForButtonPress() { - uiDrawString("Press any button to continue", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); - uiRefreshDisplay(); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Press any button to continue"); while(true) { + uiUpdateStatusMsg(); + uiRefreshDisplay(); + hidScanInput(); u32 keysDown = hidKeysDown(CONTROLLER_P1_AUTO); @@ -3219,7 +3242,10 @@ void printProgressBar(progress_ctx_t *progressCtx, bool calcData, u64 chunkSize) { timeGetCurrentTime(TimeType_LocalSystemClock, &(progressCtx->now)); - progressCtx->lastSpeed = (((double)(progressCtx->curOffset + chunkSize) / (double)DUMP_BUFFER_SIZE) / (double)(progressCtx->now - progressCtx->start)); + // Workaround to properly calculate speed for sequential dumps + u64 speedCurOffset = (progressCtx->seqDumpCurOffset ? progressCtx->seqDumpCurOffset : progressCtx->curOffset); + + progressCtx->lastSpeed = (((double)(speedCurOffset + chunkSize) / (double)DUMP_BUFFER_SIZE) / (double)(progressCtx->now - progressCtx->start)); progressCtx->averageSpeed = ((SMOOTHING_FACTOR * progressCtx->lastSpeed) + ((1 - SMOOTHING_FACTOR) * progressCtx->averageSpeed)); if (!isnormal(progressCtx->averageSpeed)) progressCtx->averageSpeed = SMOOTHING_FACTOR; // Very low values @@ -3228,30 +3254,39 @@ void printProgressBar(progress_ctx_t *progressCtx, bool calcData, u64 chunkSize) progressCtx->progress = (u8)(((progressCtx->curOffset + chunkSize) * 100) / progressCtx->totalSize); } - formatETAString(progressCtx->remainingTime, progressCtx->etaInfo, sizeof(progressCtx->etaInfo) / sizeof(progressCtx->etaInfo[0])); + formatETAString(progressCtx->remainingTime, progressCtx->etaInfo, MAX_ELEMENTS(progressCtx->etaInfo)); - convertSize(progressCtx->curOffset + chunkSize, progressCtx->curOffsetStr, sizeof(progressCtx->curOffsetStr) / sizeof(progressCtx->curOffsetStr[0])); + convertSize(progressCtx->curOffset + chunkSize, progressCtx->curOffsetStr, MAX_ELEMENTS(progressCtx->curOffsetStr)); - uiFill(0, (progressCtx->line_offset * (font_height + (font_height / 4))) + 8, FB_WIDTH / 4, (font_height + (font_height / 4)), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%.2lf MiB/s [ETA: %s]", progressCtx->averageSpeed, progressCtx->etaInfo); - uiDrawString(strbuf, font_height * 2, (progressCtx->line_offset * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiFill(0, (progressCtx->line_offset * LINE_HEIGHT) + 8, FB_WIDTH / 4, LINE_HEIGHT * 2, BG_COLOR_RGB); + uiDrawString(font_height * 2, STRING_Y_POS(progressCtx->line_offset), FONT_COLOR_RGB, "%.2lf MiB/s [ETA: %s]", progressCtx->averageSpeed, progressCtx->etaInfo); - uiFill(FB_WIDTH / 4, (progressCtx->line_offset * (font_height + (font_height / 4))) + 10, FB_WIDTH / 2, (font_height + (font_height / 4)), 0, 0, 0); - uiFill(FB_WIDTH / 4, (progressCtx->line_offset * (font_height + (font_height / 4))) + 10, (((progressCtx->curOffset + chunkSize) * (u64)(FB_WIDTH / 2)) / progressCtx->totalSize), (font_height + (font_height / 4)), 0, 255, 0); + if (progressCtx->totalSize && (progressCtx->curOffset + chunkSize) < progressCtx->totalSize) + { + uiFill(FB_WIDTH / 4, (progressCtx->line_offset * LINE_HEIGHT) + 10, FB_WIDTH / 2, LINE_HEIGHT, EMPTY_BAR_COLOR_RGB); + uiFill(FB_WIDTH / 4, (progressCtx->line_offset * LINE_HEIGHT) + 10, (((progressCtx->curOffset + chunkSize) * (u64)(FB_WIDTH / 2)) / progressCtx->totalSize), LINE_HEIGHT, FONT_COLOR_SUCCESS_RGB); + } else { + uiFill(FB_WIDTH / 4, (progressCtx->line_offset * LINE_HEIGHT) + 10, FB_WIDTH / 2, LINE_HEIGHT, FONT_COLOR_SUCCESS_RGB); + } - uiFill(FB_WIDTH - (FB_WIDTH / 4), (progressCtx->line_offset * (font_height + (font_height / 4))) + 8, FB_WIDTH / 4, (font_height + (font_height / 4)), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%u%% [%s / %s]", progressCtx->progress, progressCtx->curOffsetStr, progressCtx->totalSizeStr); - uiDrawString(strbuf, FB_WIDTH - (FB_WIDTH / 4) + (font_height * 2), (progressCtx->line_offset * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiFill(FB_WIDTH - (FB_WIDTH / 4), (progressCtx->line_offset * LINE_HEIGHT) + 8, FB_WIDTH / 4, LINE_HEIGHT * 2, BG_COLOR_RGB); + uiDrawString(FB_WIDTH - (FB_WIDTH / 4) + (font_height * 2), STRING_Y_POS(progressCtx->line_offset), FONT_COLOR_RGB, "%u%% [%s / %s]", progressCtx->progress, progressCtx->curOffsetStr, progressCtx->totalSizeStr); uiRefreshDisplay(); + uiUpdateStatusMsg(); } void setProgressBarError(progress_ctx_t *progressCtx) { if (!progressCtx) return; - uiFill(FB_WIDTH / 4, (progressCtx->line_offset * (font_height + (font_height / 4))) + 10, FB_WIDTH / 2, (font_height + (font_height / 4)), 0, 0, 0); - uiFill(FB_WIDTH / 4, (progressCtx->line_offset * (font_height + (font_height / 4))) + 10, (((u32)progressCtx->progress * ((u32)FB_WIDTH / 2)) / 100), (font_height + (font_height / 4)), 255, 0, 0); + if (progressCtx->totalSize && progressCtx->curOffset < progressCtx->totalSize) + { + uiFill(FB_WIDTH / 4, (progressCtx->line_offset * LINE_HEIGHT) + 10, FB_WIDTH / 2, LINE_HEIGHT, EMPTY_BAR_COLOR_RGB); + uiFill(FB_WIDTH / 4, (progressCtx->line_offset * LINE_HEIGHT) + 10, ((progressCtx->curOffset * (u64)(FB_WIDTH / 2)) / progressCtx->totalSize), LINE_HEIGHT, FONT_COLOR_ERROR_RGB); + } else { + uiFill(FB_WIDTH / 4, (progressCtx->line_offset * LINE_HEIGHT) + 10, FB_WIDTH / 2, LINE_HEIGHT, FONT_COLOR_ERROR_RGB); + } } bool cancelProcessCheck(progress_ctx_t *progressCtx) @@ -3317,20 +3352,20 @@ bool yesNoPrompt(const char *message) { if (message && strlen(message)) { - uiDrawString(message, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, message); breaks++; } - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "[ %s ] Yes | [ %s ] No", NINTENDO_FONT_A, NINTENDO_FONT_B); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "[ %s ] Yes | [ %s ] No", NINTENDO_FONT_A, NINTENDO_FONT_B); breaks += 2; - uiRefreshDisplay(); - bool ret = false; while(true) { + uiUpdateStatusMsg(); + uiRefreshDisplay(); + hidScanInput(); u32 keysDown = hidKeysDown(CONTROLLER_P1_AUTO); @@ -3512,12 +3547,12 @@ void removeDirectoryWithVerbose(const char *path, const char *msg) breaks += 2; - uiDrawString(msg, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, msg); uiRefreshDisplay(); fsdevDeleteDirectoryRecursively(path); - uiFill(0, (breaks * (font_height + (font_height / 4))) + 8, FB_WIDTH, (font_height + (font_height / 2)), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB); + uiFill(0, (breaks * LINE_HEIGHT) + 8, FB_WIDTH, (font_height + (font_height / 2)), BG_COLOR_RGB); uiRefreshDisplay(); breaks -= 2; @@ -3573,7 +3608,7 @@ bool parseNSWDBRelease(xmlDocPtr doc, xmlNodePtr cur, u64 gc_tid, u32 crc) key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if (key) { - snprintf(xmlReleaseName, sizeof(xmlReleaseName) / sizeof(xmlReleaseName[0]), "%s", (char*)key); + snprintf(xmlReleaseName, MAX_ELEMENTS(xmlReleaseName), "%s", (char*)key); xmlFree(key); } } @@ -3583,31 +3618,25 @@ bool parseNSWDBRelease(xmlDocPtr doc, xmlNodePtr cur, u64 gc_tid, u32 crc) /*if (xmlImageSize && xmlTitleID && strlen(xmlReleaseName)) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "XML Image Size: %u.", xmlImageSize); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "XML Image Size: %u.", xmlImageSize); breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "XML Title ID: %016lX.", xmlTitleID); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "XML Title ID: %016lX.", xmlTitleID); breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "XML Image CRC32: %08X.", xmlCrc); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "XML Image CRC32: %08X.", xmlCrc); breaks++; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "XML Release Name: %s.", xmlReleaseName); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "XML Release Name: %s.", xmlReleaseName); breaks += 2; }*/ if (xmlImageSize == imageSize && xmlTitleID == gc_tid && xmlCrc == crc) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Found matching Scene release: \"%s\" (CRC32: %08X). This is a good dump!", xmlReleaseName, xmlCrc); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Found matching Scene release: \"%s\" (CRC32: %08X). This is a good dump!", xmlReleaseName, xmlCrc); found = true; } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dump doesn't match Scene release: \"%s\"! (CRC32: %08X)", xmlReleaseName, xmlCrc); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Dump doesn't match Scene release: \"%s\"! (CRC32: %08X)", xmlReleaseName, xmlCrc); } breaks++; @@ -3645,12 +3674,11 @@ void gameCardDumpNSWDBCheck(u32 crc) u32 i; for(i = 0; i < titleAppCount; i++) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "//%s/%s[.//%s='%016lX']", nswReleasesRootElement, nswReleasesChildren, nswReleasesChildrenTitleID, titleAppTitleID[i]); + snprintf(strbuf, MAX_ELEMENTS(strbuf), "//%s/%s[.//%s='%016lX']", nswReleasesRootElement, nswReleasesChildren, nswReleasesChildrenTitleID, titleAppTitleID[i]); xmlXPathObjectPtr nodeSet = getNodeSet(doc, (xmlChar*)strbuf); if (nodeSet) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Found %d %s with Title ID \"%016lX\".", nodeSet->nodesetval->nodeNr, (nodeSet->nodesetval->nodeNr > 1 ? "releases" : "release"), titleAppTitleID[i]); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Found %d %s with Title ID \"%016lX\".", nodeSet->nodesetval->nodeNr, (nodeSet->nodesetval->nodeNr > 1 ? "releases" : "release"), titleAppTitleID[i]); breaks++; uiRefreshDisplay(); @@ -3668,16 +3696,14 @@ void gameCardDumpNSWDBCheck(u32 crc) if (!found) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "No checksum matches found in XML document for Title ID \"%016lX\"!", titleAppTitleID[i]); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "No checksum matches found in XML document for Title ID \"%016lX\"!", titleAppTitleID[i]); if ((i + 1) < titleAppCount) breaks += 2; } else { breaks--; break; } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "No records with Title ID \"%016lX\" found within the XML document!", titleAppTitleID[i]); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "No records with Title ID \"%016lX\" found within the XML document!", titleAppTitleID[i]); if ((i + 1) < titleAppCount) breaks += 2; } } @@ -3687,11 +3713,10 @@ void gameCardDumpNSWDBCheck(u32 crc) if (!found) { breaks++; - uiDrawString("This could either be a bad dump or an undumped cartridge.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "This could either be a bad dump or an undumped cartridge."); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to open and/or parse \"%s\"!", nswReleasesXmlPath); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to open and/or parse \"%s\"!", nswReleasesXmlPath); } } @@ -3764,13 +3789,12 @@ void updateNSWDBXml() FILE *nswdbXml = fopen(nswReleasesXmlTmpPath, "wb"); if (nswdbXml) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Downloading XML database from \"%s\", please wait...", nswReleasesXmlUrl); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Downloading XML database from \"%s\", please wait...", nswReleasesXmlUrl); breaks += 2; if (programAppletType != AppletType_Application && programAppletType != AppletType_SystemApplication) { - uiDrawString("Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem."); breaks += 2; } @@ -3796,12 +3820,10 @@ void updateNSWDBXml() if (res == CURLE_OK && http_code >= 200 && http_code <= 299 && size > 0) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Successfully downloaded %.0lf bytes!", size); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Successfully downloaded %.0lf bytes!", size); success = true; } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to request XML database! HTTP status code: %ld", http_code); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to request XML database! HTTP status code: %ld", http_code); } fclose(nswdbXml); @@ -3814,19 +3836,17 @@ void updateNSWDBXml() unlink(nswReleasesXmlTmpPath); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to open \"%s\" in write mode!", nswReleasesXmlTmpPath); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to open \"%s\" in write mode!", nswReleasesXmlTmpPath); } curl_easy_cleanup(curl); } else { - uiDrawString("Error: failed to initialize CURL context!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to initialize CURL context!"); } networkDeinit(); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to initialize socket! (%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to initialize socket! (%08X)", result); } breaks += 2; @@ -3946,7 +3966,7 @@ void updateApplication() { if (envIsNso()) { - uiDrawString("Error: unable to update application. It is not running as a NRO.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to update application. It is not running as a NRO."); breaks += 2; return; } @@ -3967,8 +3987,7 @@ void updateApplication() curl = curl_easy_init(); if (curl) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Requesting latest release information from \"%s\"...", githubReleasesApiUrl); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Requesting latest release information from \"%s\"...", githubReleasesApiUrl); breaks++; uiRefreshDisplay(); @@ -3993,8 +4012,7 @@ void updateApplication() if (res == CURLE_OK && http_code >= 200 && http_code <= 299 && size > 0) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Parsing response JSON data from \"%s\"...", githubReleasesApiUrl); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Parsing response JSON data from \"%s\"...", githubReleasesApiUrl); breaks++; uiRefreshDisplay(); @@ -4004,10 +4022,9 @@ void updateApplication() { if (json_object_object_get_ex(jobj, "name", &name) && json_object_get_type(name) == json_type_string) { - snprintf(releaseTag, sizeof(releaseTag) / sizeof(releaseTag[0]), json_object_get_string(name)); + snprintf(releaseTag, MAX_ELEMENTS(releaseTag), json_object_get_string(name)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Latest release: %s.", releaseTag); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Latest release: %s.", releaseTag); breaks++; uiRefreshDisplay(); @@ -4029,24 +4046,23 @@ void updateApplication() { if (json_object_object_get_ex(assets, "browser_download_url", &assets) && json_object_get_type(assets) == json_type_string) { - snprintf(downloadUrl, sizeof(downloadUrl) / sizeof(downloadUrl[0]), json_object_get_string(assets)); + snprintf(downloadUrl, MAX_ELEMENTS(downloadUrl), json_object_get_string(assets)); - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Download URL: \"%s\".", downloadUrl); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Download URL: \"%s\".", downloadUrl); breaks++; - uiDrawString("Please wait...", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Please wait..."); breaks += 2; if (programAppletType != AppletType_Application && programAppletType != AppletType_SystemApplication) { - uiDrawString("Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Do not press the " NINTENDO_FONT_HOME " button. Doing so could corrupt the SD card filesystem."); breaks += 2; } uiRefreshDisplay(); - snprintf(nroPath, sizeof(nroPath) / sizeof(nroPath[0]), "%s.tmp", (strlen(appLaunchPath) ? appLaunchPath : nxDumpToolPath)); + snprintf(nroPath, MAX_ELEMENTS(nroPath), "%s.tmp", (strlen(appLaunchPath) ? appLaunchPath : nxDumpToolPath)); nxDumpToolNro = fopen(nroPath, "wb"); if (nxDumpToolNro) @@ -4076,21 +4092,19 @@ void updateApplication() { success = true; - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Successfully downloaded %.0lf bytes!", size); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Successfully downloaded %.0lf bytes!", size); breaks++; - uiDrawString("Please restart the application to reflect the changes.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Please restart the application to reflect the changes."); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to request latest update binary! HTTP status code: %ld", http_code); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to request latest update binary! HTTP status code: %ld", http_code); } fclose(nxDumpToolNro); if (success) { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), nroPath); + snprintf(strbuf, MAX_ELEMENTS(strbuf), nroPath); nroPath[strlen(nroPath) - 4] = '\0'; unlink(nroPath); @@ -4099,45 +4113,42 @@ void updateApplication() unlink(nroPath); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to open \"%s\" in write mode!", nroPath); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to open \"%s\" in write mode!", nroPath); } } else { - uiDrawString("Error: unable to parse download URL from JSON response!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to parse download URL from JSON response!"); } } else { - uiDrawString("Error: unable to parse object at index 0 from \"assets\" array in JSON response!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to parse object at index 0 from \"assets\" array in JSON response!"); } } else { - uiDrawString("Error: unable to parse \"assets\" array from JSON response!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to parse \"assets\" array from JSON response!"); } } else { - uiDrawString("You already have the latest version!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "You already have the latest version!"); } } else { - uiDrawString("Error: unable to parse version tag from JSON response!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to parse version tag from JSON response!"); } json_object_put(jobj); } else { - uiDrawString("Error: unable to parse JSON response!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to parse JSON response!"); } } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to request latest release information! HTTP status code: %ld", http_code); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to request latest release information! HTTP status code: %ld", http_code); } if (result_buf) free(result_buf); curl_easy_cleanup(curl); } else { - uiDrawString("Error: failed to initialize CURL context!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to initialize CURL context!"); } networkDeinit(); } else { - snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to initialize socket! (%08X)", result); - uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0); + uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to initialize socket! (%08X)", result); } breaks += 2; diff --git a/source/util.h b/source/util.h index 5cdd45a..eae7a42 100644 --- a/source/util.h +++ b/source/util.h @@ -14,6 +14,7 @@ #define EXEFS_DUMP_PATH NXDUMPTOOL_BASE_PATH "ExeFS/" #define ROMFS_DUMP_PATH NXDUMPTOOL_BASE_PATH "RomFS/" #define CERT_DUMP_PATH NXDUMPTOOL_BASE_PATH "Certificate/" +#define BATCH_OVERRIDES_PATH NSP_DUMP_PATH "BatchOverrides/" #define KiB (1024.0) #define MiB (1024.0 * KiB) @@ -90,6 +91,8 @@ #define ORPHAN_ENTRY_TYPE_PATCH 1 #define ORPHAN_ENTRY_TYPE_ADDON 2 +#define MAX_ELEMENTS(x) ((sizeof((x))) / (sizeof((x)[0]))) // Returns the max number of elements that can be stored in an array + typedef struct { u64 file_offset; u64 file_size; @@ -105,6 +108,7 @@ typedef struct { char totalSizeStr[32]; u64 curOffset; char curOffsetStr[32]; + u64 seqDumpCurOffset; u8 progress; u64 start; u64 now; @@ -129,6 +133,50 @@ typedef struct { u8 type; // 1 = Patch, 2 = AddOn } PACKED orphan_patch_addon_entry; +typedef enum { + BATCH_SOURCE_ALL = 0, + BATCH_SOURCE_SDCARD, + BATCH_SOURCE_EMMC, + BATCH_SOURCE_CNT +} batchModeSourceStorage; + +typedef struct { + bool isFat32; + bool keepCert; + bool trimDump; + bool calcCrc; + bool setXciArchiveBit; +} PACKED xciOptions; + +typedef struct { + bool isFat32; + bool calcCrc; + bool removeConsoleData; + bool tiklessDump; +} PACKED nspOptions; + +typedef struct { + bool dumpAppTitles; + bool dumpPatchTitles; + bool dumpAddOnTitles; + bool isFat32; + bool removeConsoleData; + bool tiklessDump; + bool skipDumpedTitles; + batchModeSourceStorage batchModeSrc; + bool rememberDumpedTitles; +} PACKED batchOptions; + +typedef struct { + xciOptions xciDumpCfg; + nspOptions nspDumpCfg; + batchOptions batchDumpCfg; +} PACKED dumpOptions; + +void loadConfig(); + +void saveConfig(); + bool isGameCardInserted(); void fsGameCardDetectionThreadFunc(void *arg);