1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-22 18:26:39 +00:00

Update to v1.1.2.

This commit is contained in:
Pablo Curiel 2019-06-19 23:56:14 -04:00
parent 655c856eaf
commit 6d435a2532
11 changed files with 1670 additions and 721 deletions

View file

@ -33,7 +33,7 @@ include $(DEVKITPRO)/libnx/switch_rules
VERSION_MAJOR := 1
VERSION_MINOR := 1
VERSION_MICRO := 1
VERSION_MICRO := 2
APP_TITLE := nxdumptool
APP_AUTHOR := MCMrARM, DarkMatterCore

View file

@ -54,6 +54,26 @@ If you like my work and you'd like to support me in any way, it's not necessary,
Changelog
--------------
**v1.1.2:**
* Delta fragment NCAs are now included in update NSPs dumped from SD/eMMC if the "Generate ticket-less dump" option is disabled.
* It is now possible to generate ticket-less NSP dumps from bundled updates in gamecards. Please bear in mind that this option requires the external "sdmc:/switch/prod.keys" file.
* UI tweaks:
* The application now keeps track of the selected title in SD/eMMC and "orphan" content modes when entering a menu and then going back to the list.
* After selecting a title in the SD/eMMC menu, information about content already dumped related to the selected title will now be displayed (BASE / UPD / DLC).
* Likewise, after selecting a title in the "orphan" title list (Y button), an additional line will now display if the selected title has been dumped or not.
* This also informs the user if the dumps contain console-specific data.
* Three additional entries will now be displayed in the "orphan" title list.
* Upwards and downwards arrows will now be displayed for lists that exceed the max element count.
* Because of this change, max element count for the SD/eMMC title list had to be reduced from 4 to 3.
* Leftwards and rightwards arrowheads are now displayed in menus with options.
* A "hint" message is now displayed in the "orphan" content mode to let the user know they'll be able to find gamecard updates in that section.
* If a file has been already dumped, the application will display a prompt asking the user if they want to proceed anyway or not. This doesn't apply to full HFS0/ExeFS/RomFS data dumps.
* It is now possible to jump from the first list element to the last one and viceversa using the D-Pad Up/Down and Left Stick Up/Down. The Right Stick is still used exclusively for fast scrolling and won't be affected by this change.
* Fixed a bug where NSP/ExeFS/RomFS dumping would fail if the written entry count returned by `ncmContentMetaDatabaseListApplication()` didn't match the total entry count for the selected NSP dump type.
* Fixed a bug where NSP/ExeFS/RomFS dumping would fail if an invalid title index was used with `ncmContentMetaDatabaseGet()`.
Thanks to [Maschell](https://github.com/Maschell), [DuIslingr](https://github.com/DuIslingr) and MUXI from PSXTools forums for reporting these bugs and providing with testing!
**v1.1.1:**
* Project name changed to `nxdumptool`. This is no longer a gamecard-only tool.
* Added ExeFS dumping/browsing support. This feature, along with the already available RomFS options, makes the application an excellent tool for modders!

File diff suppressed because it is too large Load diff

View file

@ -331,7 +331,7 @@ bool findFSRodataKeys(keyLocation *location)
return true;
}
bool getNcaKeys()
bool loadMemoryKeys()
{
if (nca_keyset.memory_key_cnt > 0) return true;
@ -630,7 +630,7 @@ int parse_hex_key(unsigned char *key, const char *hex, unsigned int len)
return 1;
}
int loadExternalKeys(FILE *f)
int readKeysFromFile(FILE *f)
{
u32 i;
int ret;
@ -695,6 +695,38 @@ int loadExternalKeys(FILE *f)
return 1;
}
bool loadExternalKeys()
{
// Check if the keyset has been already loaded
if (nca_keyset.ext_key_cnt > 0) return true;
// Open keys file
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);
return false;
}
// Load keys
int ret = readKeysFromFile(keysFile);
fclose(keysFile);
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);
}
return false;
}
return true;
}
bool testKeyPair(const void *E, const void *D, const void *N)
{
if (!E || !D || !N)
@ -951,33 +983,8 @@ bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_e
return false;
}
// Retrieve external keys
if (!nca_keyset.ext_key_cnt)
{
// Open keys file
FILE *keysFile = fopen(keysFilePath, "rb");
if (!keysFile)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to open \"%s\" to retrieve \"eticket_rsa_kek\" and titlekeks!", keysFilePath);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
return false;
}
// Load keys
int ret = loadExternalKeys(keysFile);
fclose(keysFile);
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);
}
return false;
}
}
// Load external keys
if (!loadExternalKeys()) return false;
// Decrypt eTicket RSA key
memcpy(ctr, eticket_data + ETICKET_DEVKEY_CTR_OFFSET, 0x10);

View file

@ -64,8 +64,9 @@ typedef struct {
u8 key_area_keys[0x20][3][0x10]; /* Key area encryption keys. */
} PACKED nca_keyset_t;
bool getNcaKeys();
bool loadMemoryKeys();
bool decryptNcaKeyArea(nca_header_t *dec_nca_header, u8 *out);
bool loadExternalKeys();
bool retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_enc_key, u8 *out_dec_key);
bool generateEncryptedNcaKeyAreaWithTitlekey(nca_header_t *dec_nca_header, u8 *decrypted_nca_keys);
bool readCertsFromApplicationRomFs();

View file

@ -208,7 +208,7 @@ void convertU64ToNcaSize(const u64 size, u8 out[0x6])
bool loadNcaKeyset()
{
// Keyset already loaded
// Check if the keyset has been already loaded
if (nca_keyset.total_key_cnt > 0) return true;
if (!(envIsSyscallHinted(0x60) && // svcDebugActiveProcess
@ -221,7 +221,7 @@ bool loadNcaKeyset()
return false;
}
return getNcaKeys();
return loadMemoryKeys();
}
size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u32 sector, bool encrypt)
@ -297,7 +297,7 @@ bool processNcaCtrSectionBlock(NcmContentStorage *ncmStorage, const NcmNcaId *nc
if (R_FAILED(result = ncmContentStorageReadContentIdFile(ncmStorage, ncaId, block_start_offset, tmp_buf, block_size)))
{
free(tmp_buf);
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmContentStorageReadContentIdFile failed for %lu bytes block at offset 0x%016lX from NCA \"%s\"! (0x%08X)", block_size, block_start_offset, nca_id, result);
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, block_start_offset, nca_id, result);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
return false;
}
@ -481,6 +481,8 @@ bool decryptNcaHeader(const u8 *ncaBuf, u64 ncaBufSize, nca_header_t *out, title
memset(decrypted_nca_keys, 0, NCA_KEY_AREA_SIZE);
memcpy(decrypted_nca_keys + (NCA_KEY_AREA_KEY_SIZE * 2), rights_info->dec_titlekey, 0x10);
rights_info->retrieved_tik = true;
}
} else {
// Copy what we already have
@ -2209,7 +2211,7 @@ bool generateNacpXmlFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId
out:
// Manually free these pointers
// Calling freeRomFsContext() would also close the ncmStorage handle
// Calling freeRomFsContext() would also close the ncmStorage handle and the gamecard IStorage instance (if available)
free(romFsContext.romfs_dir_entries);
romFsContext.romfs_dir_entries = NULL;

View file

@ -299,6 +299,7 @@ typedef struct {
u8 dec_titlekey[0x10];
u8 cert_data[ETICKET_CERT_FILE_SIZE];
rsa2048_sha256_ticket tik_data;
bool retrieved_tik;
} PACKED title_rights_ctx;
typedef struct {

File diff suppressed because it is too large Load diff

View file

@ -19,18 +19,25 @@
#define HIGHLIGHT_FONT_COLOR_G 255
#define HIGHLIGHT_FONT_COLOR_B 197
// UTF-8
#define UPWARDS_ARROW "\xE2\x86\x91"
#define DOWNWARDS_ARROW "\xE2\x86\x93"
#define COMMON_MAX_ELEMENTS 8
#define HFS0_MAX_ELEMENTS 14
#define ROMFS_MAX_ELEMENTS 12
#define SDCARD_MAX_ELEMENTS 4
#define SDCARD_MAX_ELEMENTS 3
#define ORPHAN_MAX_ELEMENTS 12
#define OPTIONS_X_POS (35 * CHAR_PT_SIZE)
#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-16
#define NINTENDO_FONT_A "\xE0\xA0"
#define NINTENDO_FONT_B "\xE0\xA1"
#define NINTENDO_FONT_Y "\xE0\xA3"
@ -148,7 +155,7 @@ bool uiLoadJpgFromFile(const char *filename, int expectedWidth, int expectedHeig
void uiDrawString(const char *string, int x, int y, u8 r, u8 g, u8 b);
u32 uiGetStrWidth(char *string);
u32 uiGetStrWidth(const char *string);
void uiRefreshDisplay();

View file

@ -97,6 +97,14 @@ u64 *titleAddOnTitleID = NULL;
u32 *titleAddOnVersion = NULL;
FsStorageId *titleAddOnStorageId = NULL;
u32 sdCardTitleAppCount = 0;
u32 sdCardTitlePatchCount = 0;
u32 sdCardTitleAddOnCount = 0;
u32 nandUserTitleAppCount = 0;
u32 nandUserTitlePatchCount = 0;
u32 nandUserTitleAddOnCount = 0;
char **titleName = NULL;
char **fixedTitleName = NULL;
char **titleAuthor = NULL;
@ -361,6 +369,14 @@ void freeTitleInfo()
titleAddOnStorageId = NULL;
}
sdCardTitleAppCount = 0;
sdCardTitlePatchCount = 0;
sdCardTitleAddOnCount = 0;
nandUserTitleAppCount = 0;
nandUserTitlePatchCount = 0;
nandUserTitleAddOnCount = 0;
if (titleName != NULL)
{
freeStringsPtr(titleName);
@ -1065,7 +1081,21 @@ void loadTitleInfo()
freeTitleInfo();
proceed = (getTitleIDAndVersionList(FsStorageId_SdCard) && getTitleIDAndVersionList(FsStorageId_NandUser));
if (getTitleIDAndVersionList(FsStorageId_SdCard))
{
sdCardTitleAppCount = titleAppCount;
sdCardTitlePatchCount = titlePatchCount;
sdCardTitleAddOnCount = titleAddOnCount;
if (getTitleIDAndVersionList(FsStorageId_NandUser))
{
nandUserTitleAppCount = (titleAppCount - sdCardTitleAppCount);
nandUserTitlePatchCount = (titlePatchCount - sdCardTitlePatchCount);
nandUserTitleAddOnCount = (titleAddOnCount - sdCardTitleAddOnCount);
proceed = true;
}
}
}
if (proceed && titleAppCount > 0)
@ -1479,6 +1509,8 @@ bool readProgramNcaExeFsOrRomFs(u32 appIndex, bool readRomFs)
u32 i = 0;
u32 written = 0;
u32 total = 0;
u32 appCount = 0;
u32 ncmAppIndex = 0;
u32 appNcaCount = 0;
u32 partition = 0;
@ -1497,7 +1529,7 @@ bool readProgramNcaExeFsOrRomFs(u32 appIndex, bool readRomFs)
NcmApplicationContentMetaKey *appList = NULL;
NcmContentRecord *appContentRecords = NULL;
size_t appListSize = (sizeof(NcmApplicationContentMetaKey) * titleAppCount);
size_t appListSize = sizeof(NcmApplicationContentMetaKey);
NcmNcaId ncaId;
char ncaIdStr[33] = {'\0'};
@ -1508,9 +1540,9 @@ bool readProgramNcaExeFsOrRomFs(u32 appIndex, bool readRomFs)
bool success = false, foundProgram = false;
if (!titleAppCount || appIndex > (titleAppCount - 1))
if (!titleAppStorageId)
{
uiDrawString("Error: invalid application index!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
uiDrawString("Error: title storage ID unavailable!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
breaks += 2;
return false;
}
@ -1524,6 +1556,40 @@ bool readProgramNcaExeFsOrRomFs(u32 appIndex, bool readRomFs)
return false;
}
switch(curStorageId)
{
case FsStorageId_GameCard:
appCount = titleAppCount;
ncmAppIndex = appIndex;
break;
case FsStorageId_SdCard:
appCount = sdCardTitleAppCount;
ncmAppIndex = appIndex;
break;
case FsStorageId_NandUser:
appCount = nandUserTitleAppCount;
ncmAppIndex = (appIndex - sdCardTitleAppCount);
break;
default:
break;
}
if (!appCount)
{
uiDrawString("Error: invalid base application count!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
breaks += 2;
return false;
}
if (ncmAppIndex > (appCount - 1))
{
uiDrawString("Error: invalid base application index!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
breaks += 2;
return false;
}
appListSize *= appCount;
// If we're dealing with a gamecard, call workaroundPartitionZeroAccess() and read the secure partition header. Otherwise, ncmContentStorageReadContentIdFile() will fail with error 0x00171002
if (curStorageId == FsStorageId_GameCard)
{
@ -1575,14 +1641,14 @@ bool readProgramNcaExeFsOrRomFs(u32 appIndex, bool readRomFs)
goto out;
}
if (written != total || written != titleAppCount)
if (written != total)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: title count mismatch in ncmContentMetaDatabaseListApplication (%u != %u)", written, titleAppCount);
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);
goto out;
}
if (R_FAILED(result = ncmContentMetaDatabaseGet(&ncmDb, &(appList[appIndex].metaRecord), sizeof(NcmContentMetaRecordsHeader), &contentRecordsHeader, &contentRecordsHeaderReadSize)))
if (R_FAILED(result = ncmContentMetaDatabaseGet(&ncmDb, &(appList[ncmAppIndex].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);
@ -1598,7 +1664,7 @@ bool readProgramNcaExeFsOrRomFs(u32 appIndex, bool readRomFs)
goto out;
}
if (R_FAILED(result = ncmContentMetaDatabaseListContentInfo(&ncmDb, &(appList[appIndex].metaRecord), 0, appContentRecords, appNcaCount * sizeof(NcmContentRecord), &written)))
if (R_FAILED(result = ncmContentMetaDatabaseListContentInfo(&ncmDb, &(appList[ncmAppIndex].metaRecord), 0, appContentRecords, appNcaCount * 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);
@ -1641,7 +1707,7 @@ bool readProgramNcaExeFsOrRomFs(u32 appIndex, bool readRomFs)
if (R_FAILED(result = ncmContentStorageReadContentIdFile(&ncmStorage, &ncaId, 0, ncaHeader, NCA_FULL_HEADER_LENGTH)))
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmContentStorageReadContentIdFile failed! (0x%08X)", result);
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);
goto out;
}
@ -1896,6 +1962,21 @@ bool getRomFsFileList(u32 dir_offset)
romFsBrowserEntries[i].offset = entryOffset;
snprintf(curName, entry->nameLen + 1, (char*)entry->name);
// Fix entry name length
u32 strWidth = uiGetStrWidth(curName);
if ((BROWSER_ICON_DIMENSION + 16 + strWidth) >= (FB_WIDTH - (font_height * 5)))
{
while((BROWSER_ICON_DIMENSION + 16 + strWidth) >= (FB_WIDTH - (font_height * 5)))
{
curName[strlen(curName) - 1] = '\0';
strWidth = uiGetStrWidth(curName);
}
strcat(curName, "...");
}
addStringToFilenameBuffer(curName, &nextFilename);
i++;
@ -1921,6 +2002,21 @@ bool getRomFsFileList(u32 dir_offset)
romFsBrowserEntries[i].offset = entryOffset;
snprintf(curName, entry->nameLen + 1, (char*)entry->name);
// Fix entry name length
u32 strWidth = uiGetStrWidth(curName);
if ((BROWSER_ICON_DIMENSION + 16 + strWidth) >= (FB_WIDTH - (font_height * 5)))
{
while((BROWSER_ICON_DIMENSION + 16 + strWidth) >= (FB_WIDTH - (font_height * 5)))
{
curName[strlen(curName) - 1] = '\0';
strWidth = uiGetStrWidth(curName);
}
strcat(curName, "...");
}
addStringToFilenameBuffer(curName, &nextFilename);
i++;
@ -2125,18 +2221,20 @@ char *generateNSPDumpName(nspDumpType selectedNspDumpType, u32 titleIndex)
return fullname;
}
void retrieveDescriptionForPatchOrAddOn(u64 titleID, u32 version, bool addOn, const char *prefix)
void retrieveDescriptionForPatchOrAddOn(u64 titleID, u32 version, bool addOn, bool addAppName, const char *prefix, char *outBuf, size_t outBufSize)
{
if (!outBuf || !outBufSize) return;
char versionStr[128] = {'\0'};
convertTitleVersionToDecimal(version, versionStr, sizeof(versionStr));
if (!titleAppCount || !titleAppTitleID || !titleName || !*titleName)
if (!titleAppCount || !titleAppTitleID || !titleName || !*titleName || !addAppName)
{
if (prefix)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%016lX v%s", prefix, titleID, versionStr);
snprintf(outBuf, outBufSize, "%s%016lX v%s", prefix, titleID, versionStr);
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%016lX v%s", titleID, versionStr);
snprintf(outBuf, outBufSize, "%016lX v%s", titleID, versionStr);
}
return;
@ -2158,16 +2256,16 @@ void retrieveDescriptionForPatchOrAddOn(u64 titleID, u32 version, bool addOn, co
{
if (prefix)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s | %016lX v%s", prefix, titleName[app], titleID, versionStr);
snprintf(outBuf, outBufSize, "%s%s | %016lX v%s", prefix, titleName[app], titleID, versionStr);
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s | %016lX v%s", titleName[app], titleID, versionStr);
snprintf(outBuf, outBufSize, "%s | %016lX v%s", titleName[app], titleID, versionStr);
}
} else {
if (prefix)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%016lX v%s", prefix, titleID, versionStr);
snprintf(outBuf, outBufSize, "%s%016lX v%s", prefix, titleID, versionStr);
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%016lX v%s", titleID, versionStr);
snprintf(outBuf, outBufSize, "%016lX v%s", titleID, versionStr);
}
}
}
@ -2336,7 +2434,7 @@ bool checkIfBaseApplicationHasPatchOrAddOn(u32 appIndex, bool addOn)
return false;
}
bool checkIfPatchOrAddOnBelongToBaseApplication(u32 titleIndex, u32 appIndex, bool addOn)
bool checkIfPatchOrAddOnBelongsToBaseApplication(u32 titleIndex, u32 appIndex, bool addOn)
{
if (!titleAppCount || appIndex > (titleAppCount - 1) || !titleAppTitleID || (!addOn && (!titlePatchCount || titleIndex > (titlePatchCount - 1) || !titlePatchTitleID)) || (addOn && (!titleAddOnCount || titleIndex > (titleAddOnCount - 1) || !titleAddOnTitleID))) return false;
@ -2354,12 +2452,52 @@ u32 retrieveFirstPatchOrAddOnIndexFromBaseApplication(u32 appIndex, bool addOn)
for(titleIndex = 0; titleIndex < count; titleIndex++)
{
if ((!addOn && (titleAppTitleID[appIndex] | APPLICATION_PATCH_BITMASK) == titlePatchTitleID[titleIndex]) || (addOn && (titleAppTitleID[appIndex] & APPLICATION_ADDON_BITMASK) == (titleAddOnTitleID[titleIndex] & APPLICATION_ADDON_BITMASK))) return titleIndex;
if (checkIfPatchOrAddOnBelongsToBaseApplication(titleIndex, appIndex, addOn)) return titleIndex;
}
return 0;
}
u32 retrievePreviousPatchOrAddOnIndexFromBaseApplication(u32 startTitleIndex, u32 appIndex, bool addOn)
{
u32 count = (!addOn ? titlePatchCount : titleAddOnCount);
u32 retTitleIndex = startTitleIndex;
u32 curTitleIndex = 0;
if (!titleAppCount || appIndex > (titleAppCount - 1) || !titleAppTitleID || !startTitleIndex || startTitleIndex >= count || (!addOn && (!titlePatchCount || !titlePatchTitleID)) || (addOn && (!titleAddOnCount || !titleAddOnTitleID))) return retTitleIndex;
for(curTitleIndex = startTitleIndex; curTitleIndex > 0; curTitleIndex--)
{
if (checkIfPatchOrAddOnBelongsToBaseApplication((curTitleIndex - 1), appIndex, addOn))
{
retTitleIndex = (curTitleIndex - 1);
break;
}
}
return retTitleIndex;
}
u32 retrieveNextPatchOrAddOnIndexFromBaseApplication(u32 startTitleIndex, u32 appIndex, bool addOn)
{
u32 count = (!addOn ? titlePatchCount : titleAddOnCount);
u32 retTitleIndex = startTitleIndex;
u32 curTitleIndex = 0;
if (!titleAppCount || appIndex > (titleAppCount - 1) || !titleAppTitleID || startTitleIndex >= count || (!addOn && (!titlePatchCount || !titlePatchTitleID)) || (addOn && (!titleAddOnCount || !titleAddOnTitleID))) return retTitleIndex;
for(curTitleIndex = (startTitleIndex + 1); curTitleIndex < count; curTitleIndex++)
{
if (checkIfPatchOrAddOnBelongsToBaseApplication(curTitleIndex, appIndex, addOn))
{
retTitleIndex = curTitleIndex;
break;
}
}
return retTitleIndex;
}
void waitForButtonPress()
{
uiDrawString("Press any button to continue", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255);
@ -2435,6 +2573,173 @@ void convertDataToHexString(const u8 *data, const u32 dataSize, char *outBuf, co
}
}
bool checkIfFileExists(const char *path)
{
if (!path || !strlen(path)) return false;
FILE *chkfile = fopen(path, "rb");
if (chkfile)
{
fclose(chkfile);
return true;
}
return false;
}
bool yesNoPrompt(const char *message)
{
if (message && strlen(message))
{
uiDrawString(message, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255);
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);
breaks += 2;
uiRefreshDisplay();
bool ret = false;
while(true)
{
hidScanInput();
u32 keysDown = hidKeysDown(CONTROLLER_P1_AUTO);
if (keysDown & KEY_A)
{
ret = true;
break;
} else
if (keysDown & KEY_B)
{
ret = false;
break;
}
}
return ret;
}
bool checkIfDumpedNspContainsConsoleData(const char *nspPath)
{
if (!nspPath || !strlen(nspPath)) return false;
FILE *nspFile = NULL;
u64 nspSize = 0;
size_t read_bytes;
pfs0_header nspHeader;
pfs0_entry_table *nspEntries = NULL;
char *nspStrTable = NULL;
u32 i;
bool foundTik = false;
u64 tikOffset = 0, tikSize = 0;
rsa2048_sha256_ticket tikData;
const u8 titlekey_block_0x190_empty_hash[0x20] = {
0x2D, 0xFB, 0xA6, 0x33, 0x81, 0x70, 0x46, 0xC7, 0xF5, 0x59, 0xED, 0x4B, 0x93, 0x07, 0x60, 0x48,
0x43, 0x5F, 0x7E, 0x1A, 0x90, 0xF1, 0x4E, 0xB8, 0x03, 0x5C, 0x04, 0xB9, 0xEB, 0xAE, 0x25, 0x37
};
u8 titlekey_block_0x190_hash[0x20];
nspFile = fopen(nspPath, "rb");
if (!nspFile) return false;
fseek(nspFile, 0, SEEK_END);
nspSize = ftell(nspFile);
rewind(nspFile);
if (nspSize < sizeof(pfs0_header))
{
fclose(nspFile);
return false;
}
read_bytes = fread(&nspHeader, 1, sizeof(pfs0_header), nspFile);
if (read_bytes != sizeof(pfs0_header) || bswap_32(nspHeader.magic) != PFS0_MAGIC || nspSize < (sizeof(pfs0_header) + (sizeof(pfs0_entry_table) * (u64)nspHeader.file_cnt) + (u64)nspHeader.str_table_size))
{
fclose(nspFile);
return false;
}
nspEntries = calloc((u64)nspHeader.file_cnt, sizeof(pfs0_entry_table));
if (!nspEntries)
{
fclose(nspFile);
return false;
}
read_bytes = fread(nspEntries, 1, sizeof(pfs0_entry_table) * (u64)nspHeader.file_cnt, nspFile);
if (read_bytes != (sizeof(pfs0_entry_table) * (u64)nspHeader.file_cnt))
{
free(nspEntries);
fclose(nspFile);
return false;
}
nspStrTable = calloc((u64)nspHeader.str_table_size, sizeof(char));
if (!nspStrTable)
{
free(nspEntries);
fclose(nspFile);
return false;
}
read_bytes = fread(nspStrTable, 1, (u64)nspHeader.str_table_size, nspFile);
if (read_bytes != (u64)nspHeader.str_table_size)
{
free(nspStrTable);
free(nspEntries);
fclose(nspFile);
return false;
}
for(i = 0; i < nspHeader.file_cnt; i++)
{
char *curFilename = (nspStrTable + nspEntries[i].filename_offset);
if (!strncasecmp(curFilename + strlen(curFilename) - 4, ".tik", 4))
{
tikOffset = (sizeof(pfs0_header) + (sizeof(pfs0_entry_table) * (u64)nspHeader.file_cnt) + (u64)nspHeader.str_table_size + nspEntries[i].file_offset);
tikSize = nspEntries[i].file_size;
foundTik = true;
break;
}
}
free(nspStrTable);
free(nspEntries);
if (!foundTik || tikSize != ETICKET_TIK_FILE_SIZE || nspSize < (tikOffset + tikSize))
{
fclose(nspFile);
return false;
}
fseek(nspFile, tikOffset, SEEK_SET);
read_bytes = fread(&tikData, 1, ETICKET_TIK_FILE_SIZE, nspFile);
fclose(nspFile);
if (read_bytes != ETICKET_TIK_FILE_SIZE) return false;
sha256CalculateHash(titlekey_block_0x190_hash, tikData.titlekey_block + 0x10, 0xF0);
if (!strncmp(tikData.sig_issuer, "Root-CA00000003-XS00000021", 26) || memcmp(titlekey_block_0x190_hash, titlekey_block_0x190_empty_hash, 0x20) != 0 || tikData.titlekey_type != ETICKET_TITLEKEY_COMMON || tikData.ticket_id != 0 || tikData.device_id != 0 || tikData.account_id != 0) return true;
return false;
}
void removeDirectory(const char *path)
{
if (!path || !strlen(path)) return;

View file

@ -188,7 +188,7 @@ char *generateDumpFullName();
char *generateNSPDumpName(nspDumpType selectedNspDumpType, u32 titleIndex);
void retrieveDescriptionForPatchOrAddOn(u64 titleID, u32 version, bool addOn, const char *prefix);
void retrieveDescriptionForPatchOrAddOn(u64 titleID, u32 version, bool addOn, bool addAppName, const char *prefix, char *outBuf, size_t outBufSize);
bool checkOrphanPatchOrAddOn(bool addOn);
@ -196,10 +196,14 @@ void generateOrphanPatchOrAddOnList();
bool checkIfBaseApplicationHasPatchOrAddOn(u32 appIndex, bool addOn);
bool checkIfPatchOrAddOnBelongToBaseApplication(u32 titleIndex, u32 appIndex, bool addOn);
bool checkIfPatchOrAddOnBelongsToBaseApplication(u32 titleIndex, u32 appIndex, bool addOn);
u32 retrieveFirstPatchOrAddOnIndexFromBaseApplication(u32 appIndex, bool addOn);
u32 retrievePreviousPatchOrAddOnIndexFromBaseApplication(u32 startTitleIndex, u32 appIndex, bool addOn);
u32 retrieveNextPatchOrAddOnIndexFromBaseApplication(u32 startTitleIndex, u32 appIndex, bool addOn);
void waitForButtonPress();
void printProgressBar(progress_ctx_t *progressCtx, bool calcData, u64 chunkSize);
@ -208,6 +212,12 @@ void setProgressBarError(progress_ctx_t *progressCtx);
void convertDataToHexString(const u8 *data, const u32 dataSize, char *outBuf, const u32 outBufSize);
bool checkIfFileExists(const char *path);
bool yesNoPrompt(const char *message);
bool checkIfDumpedNspContainsConsoleData(const char *nspPath);
void removeDirectory(const char *path);
void gameCardDumpNSWDBCheck(u32 crc);