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:
parent
655c856eaf
commit
6d435a2532
11 changed files with 1670 additions and 721 deletions
2
Makefile
2
Makefile
|
@ -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
|
||||
|
|
20
README.md
20
README.md
|
@ -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!
|
||||
|
|
1259
source/dumper.c
1259
source/dumper.c
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
10
source/nca.c
10
source/nca.c
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
661
source/ui.c
661
source/ui.c
File diff suppressed because it is too large
Load diff
13
source/ui.h
13
source/ui.h
|
@ -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();
|
||||
|
||||
|
|
343
source/util.c
343
source/util.c
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue