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

Update to v1.1.5.

This commit is contained in:
Pablo Curiel 2019-09-14 22:45:27 -04:00
parent 0dbe126426
commit ba6b483cb6
10 changed files with 1987 additions and 1166 deletions

View file

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

View file

@ -9,14 +9,15 @@ Main features
* The generated dumps follow the `AuditingTool` format from Scene releases.
* Capable of generating ticket-less (standard crypto) dumps.
* Capable of generating dumps from installed updates/DLCs with missing base applications (orphan titles).
* Batch mode available.
* Compatible with multigame carts.
* CRC32 checksum calculation for XCI/NSP dumps.
* Full XCI dump verification using XML database from NSWDB.COM (NSWreleases.xml).
* XML database and in-app update capabilities via libcurl.
* Precise HFS0 raw partition dumping, using the root HFS0 header from the gamecard.
* HFS0 partition file data dumping + browser with manual file dump support.
* Program NCA ExeFS/RomFS section file data dumping + browser with manual file dump support.
* Compatible with both base applications and updates (if available).
* Program NCA ExeFS/RomFS section & Data NCA RomFS section file data dumping + browser with manual file dump support.
* Compatible with base applications, updates and DLCs (if available).
* Supports manual RomFS directory dumping.
* Manual gamecard certificate dump.
* Free SD card space checks in place.
@ -59,6 +60,18 @@ If you like my work and you'd like to support me in any way, it's not necessary,
Changelog
--------------
**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.
* Added browsing/dumping support for RomFS sections in Data NCAs from DLCs.
* Compatible with orphan DLCs (Y button) as well.
* Output directories for ExeFS/RomFS operations are now properly tagged as "(BASE)", "(UPD)" or "(DLC)" (RomFS only), depending on the title type being processed.
* Some measures have been taken to help speed up dumping operations:
* CPU boost mode type 1 is now used with `appletSetCpuBoostMode` - only effective under HOS 7.0.0+!
* Removed the need for dynamic memory allocations in NCA AES-CTR block decryption/encryption steps.
* Although these changes get me some extra ~4 MiB/s in most operations, keep in mind this doesn't do much to help with RomFS dumps from titles with lots of (small) file entries. Even so, although the calculated ETA can sometimes be discouraging, the dump most likely *won't* take that much time - just let the process advance until it hits bigger files. Sequential write speeds for the inserted SD card still play a huge role in these cases.
* Moved base output directory from "sdmc:/nxdumptool/" to "sdmc:/switch/nxdumptool/". Both the NSWreleases.xml file and the NRO binary are also expected to be inside this directory.
**v1.1.4:**
* Fixed building with latest libnx release.
* Optimized RomFS recursive file dump function to not rely on code recursion as much as before, avoiding stack memory exhaustion problems. Fixes crashes while dumping RomFS data from games with lots of file entries.

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,6 @@
#include <switch.h>
#include "util.h"
#define DUMP_BUFFER_SIZE (u64)0x100000 // 1 MiB (1048576 bytes)
#define ISTORAGE_PARTITION_CNT 2
#define FAT32_FILESIZE_LIMIT (u64)0xFFFFFFFF // 4 GiB - 1 (4294967295 bytes)
@ -18,7 +17,7 @@
#define CERT_OFFSET 0x7000
#define CERT_SIZE 0x200
#define SMOOTHING_FACTOR (double)0.05
#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
@ -29,7 +28,7 @@ typedef enum {
} batchModeSourceStorage;
void workaroundPartitionZeroAccess();
bool dumpCartridgeImage(bool isFat32, bool setXciArchiveBit, bool dumpCert, bool trimDump, bool calcCrc);
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 dumpRawHfs0Partition(u32 partition, bool doSplitting);
@ -37,9 +36,9 @@ bool dumpHfs0PartitionData(u32 partition, bool doSplitting);
bool dumpFileFromHfs0Partition(u32 partition, u32 file, char *filename, bool doSplitting);
bool dumpExeFsSectionData(u32 titleIndex, bool usePatch, bool doSplitting);
bool dumpFileFromExeFsSection(u32 titleIndex, u32 fileIndex, bool usePatch, bool doSplitting);
bool dumpRomFsSectionData(u32 titleIndex, bool usePatch, bool doSplitting);
bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, bool usePatch, bool doSplitting);
bool dumpCurrentDirFromRomFsSection(u32 titleIndex, bool usePatch, bool doSplitting);
bool dumpRomFsSectionData(u32 titleIndex, selectedRomFsType curRomFsType, bool doSplitting);
bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType curRomFsType, bool doSplitting);
bool dumpCurrentDirFromRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType, bool doSplitting);
bool dumpGameCardCertificate();
#endif

View file

@ -46,6 +46,9 @@ int main(int argc, char *argv[])
/* Initialize UI */
if (!uiInit()) return -1;
/* Enable CPU boost mode */
appletSetCpuBoostMode(ApmCpuBoostMode_Type1);
int ret = 0;
Result result;
@ -363,6 +366,9 @@ int main(int argc, char *argv[])
ret = -2;
}
/* Disable CPU boost mode */
appletSetCpuBoostMode(ApmCpuBoostMode_Disabled);
/* Free global resources */
freeGlobalData();

View file

@ -22,6 +22,8 @@ extern char strbuf[NAME_BUF_LEN * 4];
extern nca_keyset_t nca_keyset;
extern u8 *ncaCtrBuf;
char *getTitleType(u8 type)
{
char *out = NULL;
@ -301,7 +303,6 @@ bool processNcaCtrSectionBlock(NcmContentStorage *ncmStorage, const NcmNcaId *nc
Result result;
unsigned char ctr[0x10];
u8 *tmp_buf = NULL;
char nca_id[33] = {'\0'};
convertDataToHexString(ncaId->c, 16, nca_id, 33);
@ -310,40 +311,39 @@ bool processNcaCtrSectionBlock(NcmContentStorage *ncmStorage, const NcmNcaId *nc
u64 block_end_offset = (u64)round_up(offset + bufSize, 0x10);
u64 block_size = (block_end_offset - block_start_offset);
tmp_buf = malloc(block_size);
if (!tmp_buf)
{
uiDrawString("Error: unable to allocate memory for the temporary NCA section block read buffer!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
return false;
}
u64 block_size_used = (block_size > NCA_CTR_BUFFER_SIZE ? NCA_CTR_BUFFER_SIZE : block_size);
u64 output_block_size = (block_size > NCA_CTR_BUFFER_SIZE ? (NCA_CTR_BUFFER_SIZE - (offset - block_start_offset)) : bufSize);
if (R_FAILED(result = ncmContentStorageReadContentIdFile(ncmStorage, ncaId, block_start_offset, tmp_buf, block_size)))
if (R_FAILED(result = ncmContentStorageReadContentIdFile(ncmStorage, ncaId, block_start_offset, ncaCtrBuf, block_size_used)))
{
free(tmp_buf);
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);
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read encrypted %lu bytes block at offset 0x%016lX from NCA \"%s\"! (0x%08X)", block_size_used, block_start_offset, nca_id, result);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
return false;
}
// Update CTR
memcpy(ctr, ctx->ctr, 0x10);
nca_update_ctr(ctr, block_start_offset);
// Decrypt
aes128CtrContextResetCtr(ctx, ctr);
aes128CtrCrypt(ctx, tmp_buf, tmp_buf, block_size);
// Decrypt CTR block
aes128CtrCrypt(ctx, ncaCtrBuf, ncaCtrBuf, block_size_used);
if (encrypt)
{
memcpy(tmp_buf + (offset - block_start_offset), outBuf, bufSize);
// Copy data to be encrypted
memcpy(ncaCtrBuf + (offset - block_start_offset), outBuf, output_block_size);
// Encrypt
// Reset CTR
aes128CtrContextResetCtr(ctx, ctr);
aes128CtrCrypt(ctx, tmp_buf, tmp_buf, block_size);
// Encrypt CTR block
aes128CtrCrypt(ctx, ncaCtrBuf, ncaCtrBuf, block_size_used);
}
memcpy(outBuf, tmp_buf + (offset - block_start_offset), bufSize);
memcpy(outBuf, ncaCtrBuf + (offset - block_start_offset), output_block_size);
free(tmp_buf);
if (block_size > NCA_CTR_BUFFER_SIZE) return processNcaCtrSectionBlock(ncmStorage, ncaId, ctx, offset + output_block_size, outBuf + output_block_size, bufSize - output_block_size, encrypt);
return true;
}
@ -491,7 +491,6 @@ bool bktrSectionPhysicalRead(void *outBuf, size_t bufSize)
Result result;
unsigned char ctr[0x10];
u8 *tmp_buf = NULL;
bktr_subsection_entry_t *subsec = bktr_get_subsection(bktrContext.subsection_block, bktrContext.bktr_seek);
if (!subsec) return false;
@ -500,10 +499,6 @@ bool bktrSectionPhysicalRead(void *outBuf, size_t bufSize)
u64 base_offset = (bktrContext.section_offset + bktrContext.bktr_seek);
memcpy(ctr, bktrContext.aes_ctx.ctr, 0x10);
nca_update_bktr_ctr(ctr, subsec->ctr_val, base_offset);
aes128CtrContextResetCtr(&(bktrContext.aes_ctx), ctr);
u64 virt_seek = bktrContext.virtual_seek;
if ((bktrContext.bktr_seek + bufSize) <= next_subsec->offset)
@ -513,27 +508,40 @@ bool bktrSectionPhysicalRead(void *outBuf, size_t bufSize)
u64 block_end_offset = (u64)round_up(base_offset + bufSize, 0x10);
u64 block_size = (block_end_offset - block_start_offset);
tmp_buf = malloc(block_size);
if (!tmp_buf)
u64 output_offset = 0;
u64 ctr_buf_offset = (base_offset - block_start_offset);
u64 output_block_size = (block_size > NCA_CTR_BUFFER_SIZE ? (NCA_CTR_BUFFER_SIZE - (base_offset - block_start_offset)) : bufSize);
while(block_size > 0)
{
uiDrawString("Error: unable to allocate memory for the temporary NCA BKTR section block read buffer!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
return false;
u64 block_size_used = (block_size > NCA_CTR_BUFFER_SIZE ? NCA_CTR_BUFFER_SIZE : block_size);
if (R_FAILED(result = ncmContentStorageReadContentIdFile(&(bktrContext.ncmStorage), &(bktrContext.ncaId), block_start_offset, ncaCtrBuf, block_size_used)))
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "BKTR: failed to read encrypted %lu bytes block at offset 0x%016lX! (0x%08X)", block_size_used, block_start_offset, result);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
return false;
}
// Update BKTR CTR
memcpy(ctr, bktrContext.aes_ctx.ctr, 0x10);
nca_update_bktr_ctr(ctr, subsec->ctr_val, block_start_offset);
aes128CtrContextResetCtr(&(bktrContext.aes_ctx), ctr);
// Decrypt CTR block
aes128CtrCrypt(&(bktrContext.aes_ctx), ncaCtrBuf, ncaCtrBuf, block_size_used);
memcpy(outBuf + output_offset, ncaCtrBuf + ctr_buf_offset, output_block_size);
block_start_offset += block_size_used;
block_size -= block_size_used;
if (block_size)
{
output_offset += output_block_size;
ctr_buf_offset = 0;
output_block_size = (block_size > NCA_CTR_BUFFER_SIZE ? NCA_CTR_BUFFER_SIZE : ((base_offset + bufSize) - block_start_offset));
}
}
if (R_FAILED(result = ncmContentStorageReadContentIdFile(&(bktrContext.ncmStorage), &(bktrContext.ncaId), block_start_offset, tmp_buf, block_size)))
{
free(tmp_buf);
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "BKTR: failed to read encrypted %lu bytes block at offset 0x%016lX! (0x%08X)", block_size, block_start_offset, result);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
return false;
}
// Decrypt
aes128CtrCrypt(&(bktrContext.aes_ctx), tmp_buf, tmp_buf, block_size);
memcpy(outBuf, tmp_buf + (base_offset - block_start_offset), bufSize);
free(tmp_buf);
} else {
// Sad path
u64 within_subsection = (next_subsec->offset - bktrContext.bktr_seek);
@ -2146,7 +2154,7 @@ bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId
nca_pfs0_data_offset = (nca_pfs0_str_table_offset + (u64)nca_pfs0_header.str_table_size);
// Allocate memory for the programinfo.xml contents, making sure there's enough space
programInfoXml = calloc(0xA00000, sizeof(char));
programInfoXml = calloc(NSP_XML_BUFFER_SIZE, sizeof(char));
if (!programInfoXml)
{
uiDrawString("Error: unable to allocate memory for the \"programinfo.xml\" contents!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
@ -2918,7 +2926,7 @@ bool retrieveNacpDataFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaI
}
// Make sure that the output buffer for our NACP XML is big enough
nacpXml = calloc(NAME_BUF_LEN * 4, sizeof(char));
nacpXml = calloc(NSP_XML_BUFFER_SIZE, sizeof(char));
if (!nacpXml)
{
uiDrawString("Error: unable to allocate memory for the NACP XML!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);

File diff suppressed because it is too large Load diff

View file

@ -19,10 +19,6 @@
#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 9
#define HFS0_MAX_ELEMENTS 14
#define ROMFS_MAX_ELEMENTS 12
@ -37,20 +33,24 @@
#define BROWSER_ICON_DIMENSION 16
// UTF-16
#define NINTENDO_FONT_A "\xE0\xA0"
#define NINTENDO_FONT_B "\xE0\xA1"
#define NINTENDO_FONT_X "\xE0\xA2"
#define NINTENDO_FONT_Y "\xE0\xA3"
#define NINTENDO_FONT_L "\xE0\xA4"
#define NINTENDO_FONT_R "\xE0\xA5"
#define NINTENDO_FONT_ZL "\xE0\xA6"
#define NINTENDO_FONT_ZR "\xE0\xA7"
#define NINTENDO_FONT_DPAD "\xE0\xAA"
#define NINTENDO_FONT_PLUS "\xE0\xB5"
#define NINTENDO_FONT_HOME "\xE0\xB9"
#define NINTENDO_FONT_LSTICK "\xE0\xC1"
#define NINTENDO_FONT_RSTICK "\xE0\xC2"
// UTF-8 sequences
#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"
typedef enum {
resultNone,

View file

@ -34,8 +34,8 @@ extern curMenuType menuType;
/* Constants */
const char *nswReleasesXmlUrl = "http://nswdb.com/xml.php";
const char *nswReleasesXmlTmpPath = OUTPUT_DUMP_BASE_PATH "NSWreleases.xml.tmp";
const char *nswReleasesXmlPath = OUTPUT_DUMP_BASE_PATH "NSWreleases.xml";
const char *nswReleasesXmlTmpPath = NXDUMPTOOL_BASE_PATH "NSWreleases.xml.tmp";
const char *nswReleasesXmlPath = NXDUMPTOOL_BASE_PATH "NSWreleases.xml";
const char *nswReleasesRootElement = "releases";
const char *nswReleasesChildren = "release";
const char *nswReleasesChildrenImageSize = "imagesize";
@ -44,7 +44,7 @@ const char *nswReleasesChildrenImgCrc = "imgcrc";
const char *nswReleasesChildrenReleaseName = "releasename";
const char *githubReleasesApiUrl = "https://api.github.com/repos/DarkMatterCore/nxdumptool/releases/latest";
const char *nxDumpToolPath = "sdmc:/switch/nxdumptool.nro";
const char *nxDumpToolPath = "sdmc:/switch/nxdumptool/nxdumptool.nro";
const char *userAgent = "nxdumptool/" APP_VERSION " (Nintendo Switch)";
/* Statically allocated variables */
@ -109,7 +109,7 @@ u32 nandUserTitleAddOnCount = 0;
static bool sdCardAndEmmcTitleInfoLoaded = false;
u32 gameCardSdCardEmmcPatchCount = 0;
u32 gameCardSdCardEmmcPatchCount = 0, gameCardSdCardEmmcAddOnCount = 0;
char **titleName = NULL;
char **fixedTitleName = NULL;
@ -125,6 +125,9 @@ char curRomFsPath[NAME_BUF_LEN] = {'\0'};
u32 curRomFsDirOffset = 0;
romfs_browser_entry *romFsBrowserEntries = NULL;
u8 *dumpBuf = NULL;
u8 *ncaCtrBuf = NULL;
orphan_patch_addon_entry *orphanEntries = NULL;
char strbuf[NAME_BUF_LEN * 4] = {'\0'};
@ -770,9 +773,11 @@ bool getTitleIDAndVersionList(FsStorageId curStorageId)
return success;
}
bool loadPatchesFromSdCardAndEmmc()
bool loadTitlesFromSdCardAndEmmc(u8 titleType)
{
if (menuType != MENUTYPE_GAMECARD || !titleAppCount || !titleAppTitleID) return false;
if (menuType != MENUTYPE_GAMECARD || !titleAppCount || !titleAppTitleID || (titleType != META_DB_PATCH && titleType != META_DB_ADDON)) return false;
if ((titleType == META_DB_PATCH && gameCardSdCardEmmcPatchCount) || (titleType == META_DB_ADDON && gameCardSdCardEmmcAddOnCount)) return true;
u32 i, j;
@ -815,7 +820,7 @@ bool loadPatchesFromSdCardAndEmmc()
titleList = calloc(1, titleListSize);
if (titleList)
{
if (R_SUCCEEDED(result = ncmContentMetaDatabaseListApplication(&ncmDb, META_DB_PATCH, titleList, titleListSize, &written, &total)) && written && total)
if (R_SUCCEEDED(result = ncmContentMetaDatabaseListApplication(&ncmDb, titleType, titleList, titleListSize, &written, &total)) && written && total)
{
if (total > written)
{
@ -826,7 +831,7 @@ bool loadPatchesFromSdCardAndEmmc()
titleList = titleListTmp;
memset(titleList, 0, titleListSize);
if (R_SUCCEEDED(result = ncmContentMetaDatabaseListApplication(&ncmDb, META_DB_PATCH, titleList, titleListSize, &written, &total)))
if (R_SUCCEEDED(result = ncmContentMetaDatabaseListApplication(&ncmDb, titleType, titleList, titleListSize, &written, &total)))
{
if (written != total) proceed = false;
} else {
@ -850,38 +855,75 @@ bool loadPatchesFromSdCardAndEmmc()
versions[j] = titleList[j].metaRecord.version;
}
// If ptr == NULL, realloc will essentially act as a malloc
tmpTIDs = realloc(titlePatchTitleID, (titlePatchCount + total) * sizeof(u64));
tmpVersions = realloc(titlePatchVersion, (titlePatchCount + total) * sizeof(u32));
tmpStorages = realloc(titlePatchStorageId, (titlePatchCount + total) * sizeof(FsStorageId));
if (tmpTIDs != NULL && tmpVersions != NULL && tmpStorages != NULL)
if (titleType == META_DB_PATCH)
{
titlePatchTitleID = tmpTIDs;
memcpy(titlePatchTitleID + titlePatchCount, titleIDs, total * sizeof(u64));
// If ptr == NULL, realloc will essentially act as a malloc
tmpTIDs = realloc(titlePatchTitleID, (titlePatchCount + total) * sizeof(u64));
tmpVersions = realloc(titlePatchVersion, (titlePatchCount + total) * sizeof(u32));
tmpStorages = realloc(titlePatchStorageId, (titlePatchCount + total) * sizeof(FsStorageId));
titlePatchVersion = tmpVersions;
memcpy(titlePatchVersion + titlePatchCount, versions, total * sizeof(u32));
titlePatchStorageId = tmpStorages;
for(j = titlePatchCount; j < (titlePatchCount + total); j++) titlePatchStorageId[j] = curStorageId;
titlePatchCount += total;
gameCardSdCardEmmcPatchCount += total;
if (curStorageId == FsStorageId_SdCard)
if (tmpTIDs != NULL && tmpVersions != NULL && tmpStorages != NULL)
{
sdCardTitlePatchCount = total;
titlePatchTitleID = tmpTIDs;
memcpy(titlePatchTitleID + titlePatchCount, titleIDs, total * sizeof(u64));
titlePatchVersion = tmpVersions;
memcpy(titlePatchVersion + titlePatchCount, versions, total * sizeof(u32));
titlePatchStorageId = tmpStorages;
for(j = titlePatchCount; j < (titlePatchCount + total); j++) titlePatchStorageId[j] = curStorageId;
titlePatchCount += total;
gameCardSdCardEmmcPatchCount += total;
if (curStorageId == FsStorageId_SdCard)
{
sdCardTitlePatchCount = total;
} else {
nandUserTitlePatchCount = total;
}
} else {
nandUserTitlePatchCount = total;
if (tmpTIDs != NULL) titlePatchTitleID = tmpTIDs;
if (tmpVersions != NULL) titlePatchVersion = tmpVersions;
if (tmpStorages != NULL) titlePatchStorageId = tmpStorages;
}
} else {
if (tmpTIDs != NULL) titlePatchTitleID = tmpTIDs;
// If ptr == NULL, realloc will essentially act as a malloc
tmpTIDs = realloc(titleAddOnTitleID, (titleAddOnCount + total) * sizeof(u64));
tmpVersions = realloc(titleAddOnVersion, (titleAddOnCount + total) * sizeof(u32));
tmpStorages = realloc(titleAddOnStorageId, (titleAddOnCount + total) * sizeof(FsStorageId));
if (tmpVersions != NULL) titlePatchVersion = tmpVersions;
if (tmpStorages != NULL) titlePatchStorageId = tmpStorages;
if (tmpTIDs != NULL && tmpVersions != NULL && tmpStorages != NULL)
{
titleAddOnTitleID = tmpTIDs;
memcpy(titleAddOnTitleID + titleAddOnCount, titleIDs, total * sizeof(u64));
titleAddOnVersion = tmpVersions;
memcpy(titleAddOnVersion + titleAddOnCount, versions, total * sizeof(u32));
titleAddOnStorageId = tmpStorages;
for(j = titleAddOnCount; j < (titleAddOnCount + total); j++) titleAddOnStorageId[j] = curStorageId;
titleAddOnCount += total;
gameCardSdCardEmmcAddOnCount += total;
if (curStorageId == FsStorageId_SdCard)
{
sdCardTitleAddOnCount = total;
} else {
nandUserTitleAddOnCount = total;
}
} else {
if (tmpTIDs != NULL) titleAddOnTitleID = tmpTIDs;
if (tmpVersions != NULL) titleAddOnVersion = tmpVersions;
if (tmpStorages != NULL) titleAddOnStorageId = tmpStorages;
}
}
}
@ -898,67 +940,109 @@ bool loadPatchesFromSdCardAndEmmc()
}
}
if (gameCardSdCardEmmcPatchCount) return true;
if ((titleType == META_DB_PATCH && gameCardSdCardEmmcPatchCount) || (titleType == META_DB_ADDON && gameCardSdCardEmmcAddOnCount)) return true;
return false;
}
void freePatchesFromSdCardAndEmmc()
void freeTitlesFromSdCardAndEmmc(u8 titleType)
{
if (menuType != MENUTYPE_GAMECARD || !titleAppCount || !titleAppTitleID || !titlePatchCount || !titlePatchTitleID || !titlePatchVersion || !titlePatchStorageId || !gameCardSdCardEmmcPatchCount) return;
if (menuType != MENUTYPE_GAMECARD || !titleAppCount || !titleAppTitleID || (titleType != META_DB_PATCH && titleType != META_DB_ADDON) || (titleType == META_DB_PATCH && (!titlePatchCount || !titlePatchTitleID || !titlePatchVersion || !titlePatchStorageId || !gameCardSdCardEmmcPatchCount)) || (titleType == META_DB_ADDON && (!titleAddOnCount || !titleAddOnTitleID || !titleAddOnVersion || !titleAddOnStorageId || !gameCardSdCardEmmcAddOnCount))) return;
u64 *tmpTIDs = NULL;
u32 *tmpVersions = NULL;
FsStorageId *tmpStorages = NULL;
if ((titlePatchCount - gameCardSdCardEmmcPatchCount) > 0)
if (titleType == META_DB_PATCH)
{
tmpTIDs = realloc(titlePatchTitleID, (titlePatchCount - gameCardSdCardEmmcPatchCount) * sizeof(u64));
tmpVersions = realloc(titlePatchVersion, (titlePatchCount - gameCardSdCardEmmcPatchCount) * sizeof(u32));
tmpStorages = realloc(titlePatchStorageId, (titlePatchCount - gameCardSdCardEmmcPatchCount) * sizeof(FsStorageId));
if (tmpTIDs != NULL && tmpVersions != NULL && tmpStorages != NULL)
if ((titlePatchCount - gameCardSdCardEmmcPatchCount) > 0)
{
titlePatchTitleID = tmpTIDs;
tmpTIDs = realloc(titlePatchTitleID, (titlePatchCount - gameCardSdCardEmmcPatchCount) * sizeof(u64));
tmpVersions = realloc(titlePatchVersion, (titlePatchCount - gameCardSdCardEmmcPatchCount) * sizeof(u32));
tmpStorages = realloc(titlePatchStorageId, (titlePatchCount - gameCardSdCardEmmcPatchCount) * sizeof(FsStorageId));
titlePatchVersion = tmpVersions;
titlePatchStorageId = tmpStorages;
if (tmpTIDs != NULL && tmpVersions != NULL && tmpStorages != NULL)
{
titlePatchTitleID = tmpTIDs;
titlePatchVersion = tmpVersions;
titlePatchStorageId = tmpStorages;
} else {
if (tmpTIDs != NULL) titlePatchTitleID = tmpTIDs;
if (tmpVersions != NULL) titlePatchVersion = tmpVersions;
if (tmpStorages != NULL) titlePatchStorageId = tmpStorages;
}
} else {
if (tmpTIDs != NULL) titlePatchTitleID = tmpTIDs;
free(titlePatchTitleID);
titlePatchTitleID = NULL;
if (tmpVersions != NULL) titlePatchVersion = tmpVersions;
free(titlePatchVersion);
titlePatchVersion = NULL;
if (tmpStorages != NULL) titlePatchStorageId = tmpStorages;
free(titlePatchStorageId);
titlePatchStorageId = NULL;
}
titlePatchCount -= gameCardSdCardEmmcPatchCount;
gameCardSdCardEmmcPatchCount = 0;
sdCardTitlePatchCount = 0;
nandUserTitlePatchCount = 0;
} else {
free(titlePatchTitleID);
titlePatchTitleID = NULL;
if ((titleAddOnCount - gameCardSdCardEmmcAddOnCount) > 0)
{
tmpTIDs = realloc(titleAddOnTitleID, (titleAddOnCount - gameCardSdCardEmmcAddOnCount) * sizeof(u64));
tmpVersions = realloc(titleAddOnVersion, (titleAddOnCount - gameCardSdCardEmmcAddOnCount) * sizeof(u32));
tmpStorages = realloc(titleAddOnStorageId, (titleAddOnCount - gameCardSdCardEmmcAddOnCount) * sizeof(FsStorageId));
if (tmpTIDs != NULL && tmpVersions != NULL && tmpStorages != NULL)
{
titleAddOnTitleID = tmpTIDs;
titleAddOnVersion = tmpVersions;
titleAddOnStorageId = tmpStorages;
} else {
if (tmpTIDs != NULL) titleAddOnTitleID = tmpTIDs;
if (tmpVersions != NULL) titleAddOnVersion = tmpVersions;
if (tmpStorages != NULL) titleAddOnStorageId = tmpStorages;
}
} else {
free(titleAddOnTitleID);
titleAddOnTitleID = NULL;
free(titleAddOnVersion);
titleAddOnVersion = NULL;
free(titleAddOnStorageId);
titleAddOnStorageId = NULL;
}
free(titlePatchVersion);
titlePatchVersion = NULL;
titleAddOnCount -= gameCardSdCardEmmcAddOnCount;
free(titlePatchStorageId);
titlePatchStorageId = NULL;
gameCardSdCardEmmcAddOnCount = 0;
sdCardTitleAddOnCount = 0;
nandUserTitleAddOnCount = 0;
}
titlePatchCount -= gameCardSdCardEmmcPatchCount;
gameCardSdCardEmmcPatchCount = 0;
sdCardTitlePatchCount = 0;
nandUserTitlePatchCount = 0;
}
void convertTitleVersionToDecimal(u32 version, char *versionBuf, size_t versionBufSize)
{
u8 major = (u8)((version >> 26) & 0x3F);
u8 middle = (u8)((version >> 20) & 0x3F);
u8 minor = (u8)((version >> 16) & 0xF);
u16 build = (u16)version;
u8 minor = (u8)((version >> 20) & 0x3F);
u8 micro = (u8)((version >> 16) & 0xF);
u16 bugfix = (u16)version;
snprintf(versionBuf, versionBufSize, "%u (%u.%u.%u.%u)", version, major, middle, minor, build);
snprintf(versionBuf, versionBufSize, "%u (%u.%u.%u.%u)", version, major, minor, micro, bugfix);
}
bool getTitleControlNacp(u64 titleID, char *nameBuf, int nameBufSize, char *authorBuf, int authorBufSize, u8 **iconBuf)
@ -1021,7 +1105,8 @@ void removeIllegalCharacters(char *name)
void createOutputDirectories()
{
mkdir(OUTPUT_DUMP_BASE_PATH, 0744);
mkdir(APP_BASE_PATH, 0744);
mkdir(NXDUMPTOOL_BASE_PATH, 0744);
mkdir(XCI_DUMP_PATH, 0744);
mkdir(NSP_DUMP_PATH, 0744);
mkdir(HFS0_DUMP_PATH, 0744);
@ -1204,81 +1289,12 @@ void getGameCardUpdateInfo()
{
if (gameCardUpdateTitleID == GAMECARD_UPDATE_TITLEID)
{
char decimalVersion[64] = {'\0'};
convertTitleVersionToDecimal(gameCardUpdateVersion, decimalVersion, sizeof(decimalVersion));
u8 major = (u8)((gameCardUpdateVersion >> 26) & 0x3F);
u8 minor = (u8)((gameCardUpdateVersion >> 20) & 0x3F);
u8 micro = (u8)((gameCardUpdateVersion >> 16) & 0xF);
u16 bugfix = (u16)gameCardUpdateVersion;
switch(gameCardUpdateVersion)
{
case SYSUPDATE_100:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "1.0.0 - v%s", decimalVersion);
break;
case SYSUPDATE_200:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "2.0.0 - v%s", decimalVersion);
break;
case SYSUPDATE_210:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "2.1.0 - v%s", decimalVersion);
break;
case SYSUPDATE_220:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "2.2.0 - v%s", decimalVersion);
break;
case SYSUPDATE_230:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "2.3.0 - v%s", decimalVersion);
break;
case SYSUPDATE_300:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "3.0.0 - v%s", decimalVersion);
break;
case SYSUPDATE_301:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "3.0.1 - v%s", decimalVersion);
break;
case SYSUPDATE_302:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "3.0.2 - v%s", decimalVersion);
break;
case SYSUPDATE_400:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "4.0.0 - v%s", decimalVersion);
break;
case SYSUPDATE_401:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "4.0.1 - v%s", decimalVersion);
break;
case SYSUPDATE_410:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "4.1.0 - v%s", decimalVersion);
break;
case SYSUPDATE_500:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "5.0.0 - v%s", decimalVersion);
break;
case SYSUPDATE_501:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "5.0.1 - v%s", decimalVersion);
break;
case SYSUPDATE_502:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "5.0.2 - v%s", decimalVersion);
break;
case SYSUPDATE_510:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "5.1.0 - v%s", decimalVersion);
break;
case SYSUPDATE_600:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "6.0.0 - v%s", decimalVersion);
break;
case SYSUPDATE_601:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "6.0.1 - v%s", decimalVersion);
break;
case SYSUPDATE_610:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "6.1.0 - v%s", decimalVersion);
break;
case SYSUPDATE_620:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "6.2.0 - v%s", decimalVersion);
break;
case SYSUPDATE_700:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "7.0.0 - v%s", decimalVersion);
break;
case SYSUPDATE_701:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "7.0.1 - v%s", decimalVersion);
break;
case SYSUPDATE_800:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "8.0.0 - v%s", decimalVersion);
break;
default:
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "UNKNOWN - v%s", decimalVersion);
break;
}
snprintf(gameCardUpdateVersionStr, sizeof(gameCardUpdateVersionStr) / sizeof(gameCardUpdateVersionStr[0]), "%u.%u.%u (bugfix %u) - v%u", major, minor, micro, bugfix, gameCardUpdateVersion);
} else {
uiStatusMsg("getGameCardUpdateInfo: update Title ID mismatch! %016lX != %016lX", gameCardUpdateTitleID, GAMECARD_UPDATE_TITLEID);
}
@ -1816,7 +1832,7 @@ bool calculateRomFsExtractedDirSize(u32 dir_offset, bool usePatch, u64 *out)
return true;
}
bool readProgramNcaExeFsOrRomFs(u32 titleIndex, bool usePatch, bool readRomFs)
bool readNcaExeFsSection(u32 titleIndex, bool usePatch)
{
Result result;
u32 i = 0;
@ -1882,7 +1898,7 @@ bool readProgramNcaExeFsOrRomFs(u32 titleIndex, bool usePatch, bool readRomFs)
{
ncmTitleIndex = titleIndex;
} else {
// Patches loaded using loadPatchesFromSdCardAndEmmc()
// Patches loaded using loadTitlesFromSdCardAndEmmc()
ncmTitleIndex = (titleIndex - (titlePatchCount - gameCardSdCardEmmcPatchCount)); // Substract gamecard patch count
}
@ -1894,7 +1910,7 @@ bool readProgramNcaExeFsOrRomFs(u32 titleIndex, bool usePatch, bool readRomFs)
{
ncmTitleIndex = (titleIndex - (!usePatch ? sdCardTitleAppCount : sdCardTitlePatchCount)); // Substract SD card patch count
} else {
// Patches loaded using loadPatchesFromSdCardAndEmmc()
// Patches loaded using loadTitlesFromSdCardAndEmmc()
ncmTitleIndex = (titleIndex - ((titlePatchCount - gameCardSdCardEmmcPatchCount) + sdCardTitlePatchCount)); // Substract gamecard + SD card patch count
}
@ -2032,8 +2048,7 @@ bool readProgramNcaExeFsOrRomFs(u32 titleIndex, bool usePatch, bool readRomFs)
uiRefreshDisplay();
breaks += 2;
/*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Retrieving %s...", (!readRomFs ? "ExeFS entries" : "RomFS entry tables"));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255);
/*uiDrawString("Retrieving ExeFS entries...", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255);
uiRefreshDisplay();
breaks++;*/
@ -2067,40 +2082,339 @@ bool readProgramNcaExeFsOrRomFs(u32 titleIndex, bool usePatch, bool readRomFs)
}
}
if (!readRomFs)
// Read file entries from the ExeFS section
success = readExeFsEntryFromNca(&ncmStorage, &ncaId, &dec_nca_header, decrypted_nca_keys);
out:
if (titleContentRecords) free(titleContentRecords);
if (!success) serviceClose(&(ncmStorage.s));
serviceClose(&(ncmDb.s));
if (titleList) free(titleList);
if (curStorageId == FsStorageId_GameCard)
{
// Read file entries from the ExeFS section
if (!readExeFsEntryFromNca(&ncmStorage, &ncaId, &dec_nca_header, decrypted_nca_keys)) goto out;
} else {
if (!usePatch)
if (partitionHfs0Header)
{
// Read directory and file tables from the RomFS section
if (!readRomFsEntryFromNca(&ncmStorage, &ncaId, &dec_nca_header, decrypted_nca_keys)) goto out;
} else {
// Look for the base application title index
u32 appIndex;
free(partitionHfs0Header);
partitionHfs0Header = NULL;
partitionHfs0HeaderOffset = 0;
partitionHfs0HeaderSize = 0;
partitionHfs0FileCount = 0;
partitionHfs0StrTableSize = 0;
}
}
return success;
}
bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType)
{
Result result;
u32 i = 0;
u32 written = 0;
u32 total = 0;
u32 titleCount = 0;
u32 ncmTitleIndex = 0;
u32 titleNcaCount = 0;
u32 partition = 0;
FsStorageId curStorageId;
NcmContentMetaDatabase ncmDb;
memset(&ncmDb, 0, sizeof(NcmContentMetaDatabase));
NcmContentMetaRecordsHeader contentRecordsHeader;
memset(&contentRecordsHeader, 0, sizeof(NcmContentMetaRecordsHeader));
u64 contentRecordsHeaderReadSize = 0;
NcmContentStorage ncmStorage;
memset(&ncmStorage, 0, sizeof(NcmContentStorage));
NcmApplicationContentMetaKey *titleList = NULL;
NcmContentRecord *titleContentRecords = NULL;
size_t titleListSize = sizeof(NcmApplicationContentMetaKey);
NcmNcaId ncaId;
char ncaIdStr[33] = {'\0'};
u8 ncaHeader[NCA_FULL_HEADER_LENGTH] = {0};
nca_header_t dec_nca_header;
u8 decrypted_nca_keys[NCA_KEY_AREA_SIZE];
bool success = false, foundNca = false;
if ((curRomFsType == ROMFS_TYPE_APP && !titleAppStorageId) || (curRomFsType == ROMFS_TYPE_PATCH && !titlePatchStorageId) || (curRomFsType == ROMFS_TYPE_ADDON && !titlePatchStorageId))
{
uiDrawString("Error: title storage ID unavailable!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
breaks += 2;
return false;
}
curStorageId = (curRomFsType == ROMFS_TYPE_APP ? titleAppStorageId[titleIndex] : (curRomFsType == ROMFS_TYPE_PATCH ? titlePatchStorageId[titleIndex] : titleAddOnStorageId[titleIndex]));
if (curStorageId != FsStorageId_GameCard && curStorageId != FsStorageId_SdCard && curStorageId != FsStorageId_NandUser)
{
uiDrawString("Error: invalid title storage ID!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
breaks += 2;
return false;
}
switch(curStorageId)
{
case FsStorageId_GameCard:
titleCount = (curRomFsType == ROMFS_TYPE_APP ? titleAppCount : (curRomFsType == ROMFS_TYPE_PATCH ? titlePatchCount : titleAddOnCount));
ncmTitleIndex = titleIndex;
break;
case FsStorageId_SdCard:
titleCount = (curRomFsType == ROMFS_TYPE_APP ? sdCardTitleAppCount : (curRomFsType == ROMFS_TYPE_PATCH ? sdCardTitlePatchCount : sdCardTitleAddOnCount));
for(i = 0; i < titleAppCount; i++)
if (menuType == MENUTYPE_SDCARD_EMMC)
{
if (checkIfPatchOrAddOnBelongsToBaseApplication(titleIndex, i, false))
ncmTitleIndex = titleIndex;
} else {
// Titles loaded using loadTitlesFromSdCardAndEmmc()
if (curRomFsType == ROMFS_TYPE_PATCH)
{
appIndex = i;
break;
ncmTitleIndex = (titleIndex - (titlePatchCount - gameCardSdCardEmmcPatchCount)); // Substract gamecard patch count
} else
if (curRomFsType == ROMFS_TYPE_ADDON)
{
ncmTitleIndex = (titleIndex - (titleAddOnCount - gameCardSdCardEmmcAddOnCount)); // Substract gamecard add-on count
}
}
if (i == titleAppCount)
break;
case FsStorageId_NandUser:
titleCount = (curRomFsType == ROMFS_TYPE_APP ? nandUserTitleAppCount : (curRomFsType == ROMFS_TYPE_PATCH ? nandUserTitlePatchCount : nandUserTitleAddOnCount));
if (menuType == MENUTYPE_SDCARD_EMMC)
{
uiDrawString("Error: unable to find base application title index for the selected update!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
if (curRomFsType == ROMFS_TYPE_APP)
{
ncmTitleIndex = (titleIndex - sdCardTitleAppCount); // Substract SD card app count
} else
if (curRomFsType == ROMFS_TYPE_PATCH)
{
ncmTitleIndex = (titleIndex - sdCardTitlePatchCount); // Substract SD card patch count
} else
if (curRomFsType == ROMFS_TYPE_ADDON)
{
ncmTitleIndex = (titleIndex - sdCardTitleAddOnCount); // Substract SD card add-on count
}
} else {
// Titles loaded using loadTitlesFromSdCardAndEmmc()
if (curRomFsType == ROMFS_TYPE_PATCH)
{
ncmTitleIndex = (titleIndex - ((titlePatchCount - gameCardSdCardEmmcPatchCount) + sdCardTitlePatchCount)); // Substract gamecard + SD card patch count
} else
if (curRomFsType == ROMFS_TYPE_ADDON)
{
ncmTitleIndex = (titleIndex - ((titleAddOnCount - gameCardSdCardEmmcAddOnCount) + sdCardTitleAddOnCount)); // Substract gamecard + SD card add-on count
}
}
// Read directory and file tables from the RomFS section in the Program NCA from the base application
if (!readProgramNcaExeFsOrRomFs(appIndex, false, true)) goto out;
// Read BKTR entry data in the Program NCA from the update
if (!readBktrEntryFromNca(&ncmStorage, &ncaId, &dec_nca_header, decrypted_nca_keys)) goto out;
break;
default:
break;
}
if (!titleCount)
{
uiDrawString("Error: invalid title type count!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
breaks += 2;
return false;
}
if (ncmTitleIndex > (titleCount - 1))
{
uiDrawString("Error: invalid title index!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
breaks += 2;
return false;
}
titleListSize *= titleCount;
// 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 && !partitionHfs0Header)
{
partition = (hfs0_partition_cnt - 1); // Select the secure partition
workaroundPartitionZeroAccess();
if (!getPartitionHfs0Header(partition))
{
breaks += 2;
return false;
}
if (!partitionHfs0FileCount)
{
uiDrawString("The Secure HFS0 partition is empty!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
}
}
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Looking for the %s NCA (%s)...", (curRomFsType == ROMFS_TYPE_ADDON ? "Data" : "Program"), (curRomFsType == ROMFS_TYPE_APP ? "base application" : (curRomFsType == ROMFS_TYPE_PATCH ? "update" : "DLC")));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255);
uiRefreshDisplay();
breaks++;
titleList = calloc(1, titleListSize);
if (!titleList)
{
uiDrawString("Error: unable to allocate memory for the ApplicationContentMetaKey struct!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
}
if (R_FAILED(result = ncmOpenContentMetaDatabase(curStorageId, &ncmDb)))
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmOpenContentMetaDatabase failed! (0x%08X)", result);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
}
u8 filter = (curRomFsType == ROMFS_TYPE_APP ? META_DB_REGULAR_APPLICATION : (curRomFsType == ROMFS_TYPE_PATCH ? META_DB_PATCH : META_DB_ADDON));
if (R_FAILED(result = ncmContentMetaDatabaseListApplication(&ncmDb, filter, titleList, titleListSize, &written, &total)))
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmContentMetaDatabaseListApplication failed! (0x%08X)", result);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
}
if (!written || !total)
{
uiDrawString("Error: ncmContentMetaDatabaseListApplication wrote no entries to output buffer!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
}
if (written != total)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: title count mismatch in ncmContentMetaDatabaseListApplication (%u != %u)", written, total);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
}
if (R_FAILED(result = ncmContentMetaDatabaseGet(&ncmDb, &(titleList[ncmTitleIndex].metaRecord), sizeof(NcmContentMetaRecordsHeader), &contentRecordsHeader, &contentRecordsHeaderReadSize)))
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmContentMetaDatabaseGet failed! (0x%08X)", result);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
}
titleNcaCount = (u32)(contentRecordsHeader.numContentRecords);
titleContentRecords = calloc(titleNcaCount, sizeof(NcmContentRecord));
if (!titleContentRecords)
{
uiDrawString("Error: unable to allocate memory for the ContentRecord struct!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
}
if (R_FAILED(result = ncmContentMetaDatabaseListContentInfo(&ncmDb, &(titleList[ncmTitleIndex].metaRecord), 0, titleContentRecords, titleNcaCount * sizeof(NcmContentRecord), &written)))
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmContentMetaDatabaseListContentInfo failed! (0x%08X)", result);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
}
if (R_FAILED(result = ncmOpenContentStorage(curStorageId, &ncmStorage)))
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: ncmOpenContentStorage failed! (0x%08X)", result);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
}
for(i = 0; i < titleNcaCount; i++)
{
if (((curRomFsType == ROMFS_TYPE_APP || curRomFsType == ROMFS_TYPE_PATCH) && titleContentRecords[i].type == NcmContentType_Program) || (curRomFsType == ROMFS_TYPE_ADDON && titleContentRecords[i].type == NcmContentType_Data))
{
memcpy(&ncaId, &(titleContentRecords[i].ncaId), sizeof(NcmNcaId));
convertDataToHexString(titleContentRecords[i].ncaId.c, 16, ncaIdStr, 33);
foundNca = true;
break;
}
}
if (!foundNca)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to find %s NCA!", (curRomFsType == ROMFS_TYPE_ADDON ? "Data" : "Program"));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
}
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Found %s NCA: \"%s.nca\".", (curRomFsType == ROMFS_TYPE_ADDON ? "Data" : "Program"), ncaIdStr);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255);
uiRefreshDisplay();
breaks += 2;
/*uiDrawString("Retrieving RomFS entry tables...", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255);
uiRefreshDisplay();
breaks++;*/
if (R_FAILED(result = ncmContentStorageReadContentIdFile(&ncmStorage, &ncaId, 0, ncaHeader, NCA_FULL_HEADER_LENGTH)))
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read header from %s NCA! (0x%08X)", (curRomFsType == ROMFS_TYPE_ADDON ? "Data" : "Program"), result);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
}
// Decrypt the NCA header
if (!decryptNcaHeader(ncaHeader, NCA_FULL_HEADER_LENGTH, &dec_nca_header, NULL, decrypted_nca_keys, (curStorageId != FsStorageId_GameCard || (curStorageId == FsStorageId_GameCard && curRomFsType == ROMFS_TYPE_PATCH)))) goto out;
if (curStorageId == FsStorageId_GameCard && curRomFsType != ROMFS_TYPE_PATCH)
{
bool has_rights_id = false;
for(i = 0; i < 0x10; i++)
{
if (dec_nca_header.rights_id[i] != 0)
{
has_rights_id = true;
break;
}
}
if (has_rights_id)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: Rights ID field in %s NCA header not empty!", (curRomFsType == ROMFS_TYPE_ADDON ? "Data" : "Program"));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
}
}
if (curRomFsType != ROMFS_TYPE_PATCH)
{
// Read directory and file tables from the RomFS section
if (!readRomFsEntryFromNca(&ncmStorage, &ncaId, &dec_nca_header, decrypted_nca_keys)) goto out;
} else {
// Look for the base application title index
u32 appIndex;
for(i = 0; i < titleAppCount; i++)
{
if (checkIfPatchOrAddOnBelongsToBaseApplication(titleIndex, i, false))
{
appIndex = i;
break;
}
}
if (i == titleAppCount)
{
uiDrawString("Error: unable to find base application title index for the selected update!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
goto out;
}
// Read directory and file tables from the RomFS section in the Program NCA from the base application
if (!readNcaRomFsSection(appIndex, ROMFS_TYPE_APP)) goto out;
// Read BKTR entry data in the Program NCA from the update
if (!readBktrEntryFromNca(&ncmStorage, &ncaId, &dec_nca_header, decrypted_nca_keys)) goto out;
}
success = true;
@ -2866,6 +3180,21 @@ u32 retrieveNextPatchOrAddOnIndexFromBaseApplication(u32 startTitleIndex, u32 ap
return retTitleIndex;
}
u32 retrieveLastPatchOrAddOnIndexFromBaseApplication(u32 appIndex, bool addOn)
{
if (!titleAppCount || appIndex > (titleAppCount - 1) || !titleAppTitleID || (!addOn && (!titlePatchCount || !titlePatchTitleID)) || (addOn && (!titleAddOnCount || !titleAddOnTitleID))) return 0;
u32 titleIndex;
u32 count = (!addOn ? titlePatchCount : titleAddOnCount);
for(titleIndex = count; titleIndex > 0; titleIndex--)
{
if (checkIfPatchOrAddOnBelongsToBaseApplication(titleIndex - 1, appIndex, addOn)) return (titleIndex - 1);
}
return 0;
}
void waitForButtonPress()
{
uiDrawString("Press any button to continue", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255);

View file

@ -6,13 +6,14 @@
#include <switch.h>
#include "nca.h"
#define OUTPUT_DUMP_BASE_PATH "sdmc:/nxdumptool/"
#define XCI_DUMP_PATH (OUTPUT_DUMP_BASE_PATH "XCI/")
#define NSP_DUMP_PATH (OUTPUT_DUMP_BASE_PATH "NSP/")
#define HFS0_DUMP_PATH (OUTPUT_DUMP_BASE_PATH "HFS0/")
#define EXEFS_DUMP_PATH (OUTPUT_DUMP_BASE_PATH "ExeFS/")
#define ROMFS_DUMP_PATH (OUTPUT_DUMP_BASE_PATH "RomFS/")
#define CERT_DUMP_PATH (OUTPUT_DUMP_BASE_PATH "Certificate/")
#define APP_BASE_PATH "sdmc:/switch/"
#define NXDUMPTOOL_BASE_PATH APP_BASE_PATH "nxdumptool/"
#define XCI_DUMP_PATH NXDUMPTOOL_BASE_PATH "XCI/"
#define NSP_DUMP_PATH NXDUMPTOOL_BASE_PATH "NSP/"
#define HFS0_DUMP_PATH NXDUMPTOOL_BASE_PATH "HFS0/"
#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 KiB (1024.0)
#define MiB (1024.0 * KiB)
@ -22,6 +23,12 @@
#define SOCK_BUFFERSIZE 65536
#define DUMP_BUFFER_SIZE (u64)0x100000 // 1 MiB (1048576 bytes)
#define NCA_CTR_BUFFER_SIZE DUMP_BUFFER_SIZE // 1 MiB (1048576 bytes)
#define NSP_XML_BUFFER_SIZE (u64)0xA00000 // 10 MiB (10485760 bytes)
#define META_DB_REGULAR_APPLICATION 0x80
#define META_DB_PATCH 0x81
#define META_DB_ADDON 0x82
@ -74,29 +81,6 @@
/* Reference: https://switchbrew.org/wiki/Title_list */
#define GAMECARD_UPDATE_TITLEID (u64)0x0100000000000816
#define SYSUPDATE_100 (u32)450
#define SYSUPDATE_200 (u32)65796
#define SYSUPDATE_210 (u32)131162
#define SYSUPDATE_220 (u32)196628
#define SYSUPDATE_230 (u32)262164
#define SYSUPDATE_300 (u32)201327002
#define SYSUPDATE_301 (u32)201392178
#define SYSUPDATE_302 (u32)201457684
#define SYSUPDATE_400 (u32)268435656
#define SYSUPDATE_401 (u32)268501002
#define SYSUPDATE_410 (u32)269484082
#define SYSUPDATE_500 (u32)335544750
#define SYSUPDATE_501 (u32)335609886
#define SYSUPDATE_502 (u32)335675432
#define SYSUPDATE_510 (u32)336592976
#define SYSUPDATE_600 (u32)402653544
#define SYSUPDATE_601 (u32)402718730
#define SYSUPDATE_610 (u32)403701850
#define SYSUPDATE_620 (u32)404750376
#define SYSUPDATE_700 (u32)469762248
#define SYSUPDATE_701 (u32)469827614
#define SYSUPDATE_800 (u32)536871442
#define NACP_ICON_SQUARE_DIMENSION 256
#define NACP_ICON_DOWNSCALED 96
@ -134,6 +118,12 @@ typedef struct {
u64 cancelEndTmr;
} PACKED progress_ctx_t;
typedef enum {
ROMFS_TYPE_APP = 0,
ROMFS_TYPE_PATCH,
ROMFS_TYPE_ADDON
} selectedRomFsType;
typedef struct {
u32 index;
u8 type; // 1 = Patch, 2 = AddOn
@ -165,9 +155,9 @@ void freeBktrContext();
void freeGlobalData();
bool loadPatchesFromSdCardAndEmmc();
bool loadTitlesFromSdCardAndEmmc(u8 titleType);
void freePatchesFromSdCardAndEmmc();
void freeTitlesFromSdCardAndEmmc(u8 titleType);
void convertTitleVersionToDecimal(u32 version, char *versionBuf, size_t versionBufSize);
@ -193,7 +183,9 @@ bool calculateRomFsFullExtractedSize(bool usePatch, u64 *out);
bool calculateRomFsExtractedDirSize(u32 dir_offset, bool usePatch, u64 *out);
bool readProgramNcaExeFsOrRomFs(u32 titleIndex, bool usePatch, bool readRomFs);
bool readNcaExeFsSection(u32 titleIndex, bool usePatch);
bool readNcaRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType);
bool getExeFsFileList();
@ -225,6 +217,8 @@ u32 retrievePreviousPatchOrAddOnIndexFromBaseApplication(u32 startTitleIndex, u3
u32 retrieveNextPatchOrAddOnIndexFromBaseApplication(u32 startTitleIndex, u32 appIndex, bool addOn);
u32 retrieveLastPatchOrAddOnIndexFromBaseApplication(u32 appIndex, bool addOn);
void waitForButtonPress();
void printProgressBar(progress_ctx_t *progressCtx, bool calcData, u64 chunkSize);