1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2025-02-16 21:45: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_MAJOR := 1
VERSION_MINOR := 1 VERSION_MINOR := 1
VERSION_MICRO := 1 VERSION_MICRO := 2
APP_TITLE := nxdumptool APP_TITLE := nxdumptool
APP_AUTHOR := MCMrARM, DarkMatterCore 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 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:** **v1.1.1:**
* Project name changed to `nxdumptool`. This is no longer a gamecard-only tool. * 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! * 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; return true;
} }
bool getNcaKeys() bool loadMemoryKeys()
{ {
if (nca_keyset.memory_key_cnt > 0) return true; 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; return 1;
} }
int loadExternalKeys(FILE *f) int readKeysFromFile(FILE *f)
{ {
u32 i; u32 i;
int ret; int ret;
@ -695,6 +695,38 @@ int loadExternalKeys(FILE *f)
return 1; 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) bool testKeyPair(const void *E, const void *D, const void *N)
{ {
if (!E || !D || !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; return false;
} }
// Retrieve external keys // Load external keys
if (!nca_keyset.ext_key_cnt) if (!loadExternalKeys()) return false;
{
// 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;
}
}
// Decrypt eTicket RSA key // Decrypt eTicket RSA key
memcpy(ctr, eticket_data + ETICKET_DEVKEY_CTR_OFFSET, 0x10); 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. */ u8 key_area_keys[0x20][3][0x10]; /* Key area encryption keys. */
} PACKED nca_keyset_t; } PACKED nca_keyset_t;
bool getNcaKeys(); bool loadMemoryKeys();
bool decryptNcaKeyArea(nca_header_t *dec_nca_header, u8 *out); 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 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 generateEncryptedNcaKeyAreaWithTitlekey(nca_header_t *dec_nca_header, u8 *decrypted_nca_keys);
bool readCertsFromApplicationRomFs(); bool readCertsFromApplicationRomFs();

View file

@ -208,7 +208,7 @@ void convertU64ToNcaSize(const u64 size, u8 out[0x6])
bool loadNcaKeyset() bool loadNcaKeyset()
{ {
// Keyset already loaded // Check if the keyset has been already loaded
if (nca_keyset.total_key_cnt > 0) return true; if (nca_keyset.total_key_cnt > 0) return true;
if (!(envIsSyscallHinted(0x60) && // svcDebugActiveProcess if (!(envIsSyscallHinted(0x60) && // svcDebugActiveProcess
@ -221,7 +221,7 @@ bool loadNcaKeyset()
return false; return false;
} }
return getNcaKeys(); return loadMemoryKeys();
} }
size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u32 sector, bool encrypt) 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))) if (R_FAILED(result = ncmContentStorageReadContentIdFile(ncmStorage, ncaId, block_start_offset, tmp_buf, block_size)))
{ {
free(tmp_buf); 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); uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
return false; 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); memset(decrypted_nca_keys, 0, NCA_KEY_AREA_SIZE);
memcpy(decrypted_nca_keys + (NCA_KEY_AREA_KEY_SIZE * 2), rights_info->dec_titlekey, 0x10); memcpy(decrypted_nca_keys + (NCA_KEY_AREA_KEY_SIZE * 2), rights_info->dec_titlekey, 0x10);
rights_info->retrieved_tik = true;
} }
} else { } else {
// Copy what we already have // Copy what we already have
@ -2209,7 +2211,7 @@ bool generateNacpXmlFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId
out: out:
// Manually free these pointers // 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); free(romFsContext.romfs_dir_entries);
romFsContext.romfs_dir_entries = NULL; romFsContext.romfs_dir_entries = NULL;

View file

@ -299,6 +299,7 @@ typedef struct {
u8 dec_titlekey[0x10]; u8 dec_titlekey[0x10];
u8 cert_data[ETICKET_CERT_FILE_SIZE]; u8 cert_data[ETICKET_CERT_FILE_SIZE];
rsa2048_sha256_ticket tik_data; rsa2048_sha256_ticket tik_data;
bool retrieved_tik;
} PACKED title_rights_ctx; } PACKED title_rights_ctx;
typedef struct { 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_G 255
#define HIGHLIGHT_FONT_COLOR_B 197 #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 COMMON_MAX_ELEMENTS 8
#define HFS0_MAX_ELEMENTS 14 #define HFS0_MAX_ELEMENTS 14
#define ROMFS_MAX_ELEMENTS 12 #define ROMFS_MAX_ELEMENTS 12
#define SDCARD_MAX_ELEMENTS 4 #define SDCARD_MAX_ELEMENTS 3
#define ORPHAN_MAX_ELEMENTS 12 #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 TAB_WIDTH 4
#define BROWSER_ICON_DIMENSION 16 #define BROWSER_ICON_DIMENSION 16
// UTF-16
#define NINTENDO_FONT_A "\xE0\xA0" #define NINTENDO_FONT_A "\xE0\xA0"
#define NINTENDO_FONT_B "\xE0\xA1" #define NINTENDO_FONT_B "\xE0\xA1"
#define NINTENDO_FONT_Y "\xE0\xA3" #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); 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(); void uiRefreshDisplay();

View file

@ -97,6 +97,14 @@ u64 *titleAddOnTitleID = NULL;
u32 *titleAddOnVersion = NULL; u32 *titleAddOnVersion = NULL;
FsStorageId *titleAddOnStorageId = 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 **titleName = NULL;
char **fixedTitleName = NULL; char **fixedTitleName = NULL;
char **titleAuthor = NULL; char **titleAuthor = NULL;
@ -361,6 +369,14 @@ void freeTitleInfo()
titleAddOnStorageId = NULL; titleAddOnStorageId = NULL;
} }
sdCardTitleAppCount = 0;
sdCardTitlePatchCount = 0;
sdCardTitleAddOnCount = 0;
nandUserTitleAppCount = 0;
nandUserTitlePatchCount = 0;
nandUserTitleAddOnCount = 0;
if (titleName != NULL) if (titleName != NULL)
{ {
freeStringsPtr(titleName); freeStringsPtr(titleName);
@ -1065,7 +1081,21 @@ void loadTitleInfo()
freeTitleInfo(); 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) if (proceed && titleAppCount > 0)
@ -1479,6 +1509,8 @@ bool readProgramNcaExeFsOrRomFs(u32 appIndex, bool readRomFs)
u32 i = 0; u32 i = 0;
u32 written = 0; u32 written = 0;
u32 total = 0; u32 total = 0;
u32 appCount = 0;
u32 ncmAppIndex = 0;
u32 appNcaCount = 0; u32 appNcaCount = 0;
u32 partition = 0; u32 partition = 0;
@ -1497,7 +1529,7 @@ bool readProgramNcaExeFsOrRomFs(u32 appIndex, bool readRomFs)
NcmApplicationContentMetaKey *appList = NULL; NcmApplicationContentMetaKey *appList = NULL;
NcmContentRecord *appContentRecords = NULL; NcmContentRecord *appContentRecords = NULL;
size_t appListSize = (sizeof(NcmApplicationContentMetaKey) * titleAppCount); size_t appListSize = sizeof(NcmApplicationContentMetaKey);
NcmNcaId ncaId; NcmNcaId ncaId;
char ncaIdStr[33] = {'\0'}; char ncaIdStr[33] = {'\0'};
@ -1508,9 +1540,9 @@ bool readProgramNcaExeFsOrRomFs(u32 appIndex, bool readRomFs)
bool success = false, foundProgram = false; 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; breaks += 2;
return false; return false;
} }
@ -1524,6 +1556,40 @@ bool readProgramNcaExeFsOrRomFs(u32 appIndex, bool readRomFs)
return false; 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 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) if (curStorageId == FsStorageId_GameCard)
{ {
@ -1575,14 +1641,14 @@ bool readProgramNcaExeFsOrRomFs(u32 appIndex, bool readRomFs)
goto out; 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); uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out; 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); 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(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; 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); 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(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))) 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); uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out; goto out;
} }
@ -1896,6 +1962,21 @@ bool getRomFsFileList(u32 dir_offset)
romFsBrowserEntries[i].offset = entryOffset; romFsBrowserEntries[i].offset = entryOffset;
snprintf(curName, entry->nameLen + 1, (char*)entry->name); 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); addStringToFilenameBuffer(curName, &nextFilename);
i++; i++;
@ -1921,6 +2002,21 @@ bool getRomFsFileList(u32 dir_offset)
romFsBrowserEntries[i].offset = entryOffset; romFsBrowserEntries[i].offset = entryOffset;
snprintf(curName, entry->nameLen + 1, (char*)entry->name); 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); addStringToFilenameBuffer(curName, &nextFilename);
i++; i++;
@ -2125,18 +2221,20 @@ char *generateNSPDumpName(nspDumpType selectedNspDumpType, u32 titleIndex)
return fullname; 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'}; char versionStr[128] = {'\0'};
convertTitleVersionToDecimal(version, versionStr, sizeof(versionStr)); convertTitleVersionToDecimal(version, versionStr, sizeof(versionStr));
if (!titleAppCount || !titleAppTitleID || !titleName || !*titleName) if (!titleAppCount || !titleAppTitleID || !titleName || !*titleName || !addAppName)
{ {
if (prefix) 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 { } else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%016lX v%s", titleID, versionStr); snprintf(outBuf, outBufSize, "%016lX v%s", titleID, versionStr);
} }
return; return;
@ -2158,16 +2256,16 @@ void retrieveDescriptionForPatchOrAddOn(u64 titleID, u32 version, bool addOn, co
{ {
if (prefix) 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 { } 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 { } else {
if (prefix) 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 { } 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; 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; 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++) 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; 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() void waitForButtonPress()
{ {
uiDrawString("Press any button to continue", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255); 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) void removeDirectory(const char *path)
{ {
if (!path || !strlen(path)) return; if (!path || !strlen(path)) return;

View file

@ -188,7 +188,7 @@ char *generateDumpFullName();
char *generateNSPDumpName(nspDumpType selectedNspDumpType, u32 titleIndex); 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); bool checkOrphanPatchOrAddOn(bool addOn);
@ -196,10 +196,14 @@ void generateOrphanPatchOrAddOnList();
bool checkIfBaseApplicationHasPatchOrAddOn(u32 appIndex, bool addOn); 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 retrieveFirstPatchOrAddOnIndexFromBaseApplication(u32 appIndex, bool addOn);
u32 retrievePreviousPatchOrAddOnIndexFromBaseApplication(u32 startTitleIndex, u32 appIndex, bool addOn);
u32 retrieveNextPatchOrAddOnIndexFromBaseApplication(u32 startTitleIndex, u32 appIndex, bool addOn);
void waitForButtonPress(); void waitForButtonPress();
void printProgressBar(progress_ctx_t *progressCtx, bool calcData, u64 chunkSize); 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); 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 removeDirectory(const char *path);
void gameCardDumpNSWDBCheck(u32 crc); void gameCardDumpNSWDBCheck(u32 crc);