1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-12-23 00:52:10 +00:00

Update to v1.1.6.

This commit is contained in:
Pablo Curiel 2019-10-17 16:15:35 -04:00
parent ba6b483cb6
commit 4d44e315be
19 changed files with 3184 additions and 2453 deletions

View file

@ -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

View file

@ -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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

File diff suppressed because it is too large Load diff

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

File diff suppressed because it is too large Load diff

View file

@ -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 {

View file

@ -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;
}

View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -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();

File diff suppressed because it is too large Load diff

View file

@ -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);