Skip to content

Commit

Permalink
Update to v1.1.15.
Browse files Browse the repository at this point in the history
  • Loading branch information
DarkMatterCore committed Feb 26, 2022
1 parent aebd6d8 commit 90647a9
Show file tree
Hide file tree
Showing 11 changed files with 42 additions and 5,106 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ include $(DEVKITPRO)/libnx/switch_rules

VERSION_MAJOR := 1
VERSION_MINOR := 1
VERSION_MICRO := 14
VERSION_MICRO := 15

APP_TITLE := nxdumptool
APP_AUTHOR := DarkMatterCore
Expand Down
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Main features

* Generates NX Card Image (XCI) dumps from the inserted gamecard, with optional certificate removal and/or trimming.
* Generates installable Nintendo Submission Packages (NSP) from base applications, updates and DLCs stored in the inserted gamecard, SD card and eMMC storage devices.
* The generated dumps follow the `AuditingTool` format from Scene releases.
* The generated dumps follow the `AuthoringTool` format from Scene releases.
* Capable of generating dumps without console specific information (common ticket).
* Capable of generating ticket-less (standard crypto) dumps.
* Capable of generating dumps from installed updates/DLCs with missing base applications (orphan titles).
Expand Down Expand Up @@ -72,6 +72,15 @@ Thanks to
Changelog
--------------

**v1.1.15:**

* Built with latest devkitA64 and libnx.
* Removed AuthoringTool XML generation code to make it possible to dump NSPs from games that use NCAs with sparse / compressed FS sections. Bear in mind ExeFS / RomFS dumping won't still be possible -- using PC tools such as `hactoolnet` to retrieve data from these dumps is encouraged in the meantime.
* The NPDM ACID signature/private key patch option is now disabled by default.
*

This is only a bugfix release. I don't expect to release any new versions until the rewrite is finished - the only exception being fixing some kind of feature-breaking bug. Please understand.

**v1.1.14:**

* Fixed support for HOS 12.0.x / AMS 0.19.x. If needed, TIPC serialization is now used to dispatch the `AtmosphereHasService` IPC request.
Expand Down Expand Up @@ -305,7 +314,7 @@ Thanks to [FennecTECH](https://github.com/fennectech) for providing with testing
6. NACP XML (if available).
7. legalinfo.xml (if available).
8. Ticket + Certificate chain (if available).
* These changes essentially make the NSP dumps generated by the application comparable to Scene releases that follow the `AuditingTool` format (like those from groups like BigBlueBox or JRP), as long as the "Remove console specific data" option is enabled and the "Generate ticket-less dump" option is disabled. Happy dumping!
* These changes essentially make the NSP dumps generated by the application comparable to Scene releases that follow the `AuthoringTool` format (like those from groups like BigBlueBox or JRP), as long as the "Remove console specific data" option is enabled and the "Generate ticket-less dump" option is disabled. Happy dumping!
* Because of this, dumping update NSPs from gamecards will require the keys file at "sdmc:/switch/prod.keys" from now on (but only if the bundled update uses titlekey crypto). Base applications and DLCs can still be dumped from gamecards without needing a keys file.
* Added ExeFS/RomFS browsing/dumping from game updates.
* Upon entering ExeFS/RomFS menus, it is now possible to select which update is going to be used for ExeFS/RomFS procedures.
Expand Down
216 changes: 14 additions & 202 deletions source/dumper.c
Original file line number Diff line number Diff line change
Expand Up @@ -780,10 +780,6 @@ int dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInde
u32 cnmtNcaIndex = 0;
u8 *cnmtNcaBuf = NULL;
bool cnmtFound = false;
char *cnmtXml = NULL;

u32 xml_rec_cnt = 0;
xml_record_info *xml_records = NULL, *tmp_xml_rec = NULL;

pfs0_header nspPfs0Header;
memset(&nspPfs0Header, 0, sizeof(pfs0_header));
Expand Down Expand Up @@ -1182,67 +1178,6 @@ int dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInde
}
}

if ((!has_rights_id || (has_rights_id && rights_info.retrieved_tik)) && (xml_content_info[i].type == NcmContentType_Program || xml_content_info[i].type == NcmContentType_Control || xml_content_info[i].type == NcmContentType_LegalInformation))
{
// Reallocate XML records
tmp_xml_rec = realloc(xml_records, (xml_rec_cnt + 1) * sizeof(xml_record_info));
if (!tmp_xml_rec)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: error reallocating XML records buffer!", __func__);
proceed = false;
break;
}

xml_records = tmp_xml_rec;
tmp_xml_rec = NULL;

memset(&(xml_records[xml_rec_cnt]), 0, sizeof(xml_record_info));
xml_records[xml_rec_cnt].nca_index = i;

xml_rec_cnt++;

// Generate programinfo.xml
if (xml_content_info[i].type == NcmContentType_Program)
{
bool use_acid_pubkey = false;

for(j = 0; j < ncaProgramModCnt; j++)
{
if (ncaProgramMod[j].nca_index == i)
{
use_acid_pubkey = true;
break;
}
}

if (!generateProgramInfoXml(&ncmStorage, &ncaId, &dec_nca_header, xml_content_info[i].decrypted_nca_keys, use_acid_pubkey, &(xml_records[xml_rec_cnt - 1].xml_data), &(xml_records[xml_rec_cnt - 1].xml_size)))
{
proceed = false;
break;
}
}

// Retrieve NACP data (XML and icons)
if (xml_content_info[i].type == NcmContentType_Control)
{
if (!retrieveNacpDataFromNca(&ncmStorage, &ncaId, &dec_nca_header, xml_content_info[i].decrypted_nca_keys, &(xml_records[xml_rec_cnt - 1].xml_data), &(xml_records[xml_rec_cnt - 1].xml_size), &(xml_records[xml_rec_cnt - 1].nacp_icons), &(xml_records[xml_rec_cnt - 1].nacp_icon_cnt)))
{
proceed = false;
break;
}
}

// Retrieve legalinfo.xml
if (xml_content_info[i].type == NcmContentType_LegalInformation)
{
if (!retrieveLegalInfoXmlFromNca(&ncmStorage, &ncaId, &dec_nca_header, xml_content_info[i].decrypted_nca_keys, &(xml_records[xml_rec_cnt - 1].xml_data), &(xml_records[xml_rec_cnt - 1].xml_size)))
{
proceed = false;
break;
}
}
}

// Reencrypt header
if (!encryptNcaHeader(&dec_nca_header, xml_content_info[i].encrypted_header_mod, NCA_FULL_HEADER_LENGTH))
{
Expand Down Expand Up @@ -1292,18 +1227,6 @@ int dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInde
// Retrieve CNMT NCA data
if (!retrieveCnmtNcaData(curStorageId, cnmtNcaBuf, &xml_program_info, xml_content_info, cnmtNcaIndex, &ncaCnmtMod, &rights_info)) goto out;

// Generate a placeholder CNMT XML. It's length will be used to calculate the final output dump size

// Make sure that the output buffer for our CNMT XML is big enough
cnmtXml = calloc(NSP_XML_BUFFER_SIZE, sizeof(char));
if (!cnmtXml)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: unable to allocate memory for the CNMT XML!", __func__);
goto out;
}

generateCnmtXml(&xml_program_info, xml_content_info, cnmtXml);

bool includeTikAndCert = (rights_info.retrieved_tik && !tiklessDump);

if (includeTikAndCert)
Expand All @@ -1319,42 +1242,17 @@ int dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInde
goto out;
}

// File count = NCA count + CNMT XML + tik + cert
nspPfs0Header.file_cnt = (titleContentInfoCnt + 3);
// File count = NCA count + tik + cert
nspPfs0Header.file_cnt = (titleContentInfoCnt + 2);

// Calculate PFS0 String Table size
nspPfs0StrTableSize = (((nspPfs0Header.file_cnt - 4) * NSP_NCA_FILENAME_LENGTH) + (NSP_CNMT_FILENAME_LENGTH * 2) + NSP_TIK_FILENAME_LENGTH + NSP_CERT_FILENAME_LENGTH);
nspPfs0StrTableSize = (((titleContentInfoCnt - 1) * NSP_NCA_FILENAME_LENGTH) + NSP_CNMT_FILENAME_LENGTH + NSP_TIK_FILENAME_LENGTH + NSP_CERT_FILENAME_LENGTH);
} else {
// File count = NCA count + CNMT XML
nspPfs0Header.file_cnt = (titleContentInfoCnt + 1);
// File count = NCA count
nspPfs0Header.file_cnt = titleContentInfoCnt;

// Calculate PFS0 String Table size
nspPfs0StrTableSize = (((nspPfs0Header.file_cnt - 2) * NSP_NCA_FILENAME_LENGTH) + (NSP_CNMT_FILENAME_LENGTH * 2));
}

// Add our XML records
if (xml_rec_cnt)
{
for(i = 0; i < xml_rec_cnt; i++)
{
if (!xml_records[i].xml_data || !xml_records[i].xml_size) continue;

nspPfs0Header.file_cnt++;
u8 type = xml_content_info[xml_records[i].nca_index].type;
nspPfs0StrTableSize += (type == NcmContentType_Program ? NSP_PROGRAM_XML_FILENAME_LENGTH : (type == NcmContentType_Control ? NSP_NACP_XML_FILENAME_LENGTH : NSP_LEGAL_XML_FILENAME_LENGTH));
progressCtx.totalSize += xml_records[i].xml_size;

// Add icons if we retrieved them
if (type == NcmContentType_Control && xml_records[i].nacp_icons && xml_records[i].nacp_icon_cnt)
{
for(j = 0; j < xml_records[i].nacp_icon_cnt; j++)
{
nspPfs0Header.file_cnt++;
nspPfs0StrTableSize += (u32)(strlen(xml_records[i].nacp_icons[j].filename) + 1);
progressCtx.totalSize += xml_records[i].nacp_icons[j].icon_size;
}
}
}
nspPfs0StrTableSize = (((titleContentInfoCnt - 1) * NSP_NCA_FILENAME_LENGTH) + NSP_CNMT_FILENAME_LENGTH);
}

// Start NSP creation
Expand Down Expand Up @@ -1401,21 +1299,13 @@ int dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInde

u32 entryIdx = 0, ptrIdx = 0;

for(i = 0; i <= titleContentInfoCnt; i++, entryIdx++)
for(i = 0; i < titleContentInfoCnt; i++, entryIdx++)
{
if (i < titleContentInfoCnt)
{
// Always reserve the first titleContentInfoCnt entries for our NCAs
// Only save the CNMT NCA buffer pointer to the PFS0 file data pointer array. We don't have any other pointers to raw NCA data, so we leave the rest untouched
entrySize = xml_content_info[i].size;
entryFilenameSize = (i == cnmtNcaIndex ? NSP_CNMT_FILENAME_LENGTH : NSP_NCA_FILENAME_LENGTH);
if (i == cnmtNcaIndex) nspPfs0FilePtrs[ptrIdx++] = cnmtNcaBuf;
} else {
// Reserve the entry right after our NCAs for the CNMT XML
entrySize = strlen(cnmtXml);
entryFilenameSize = NSP_CNMT_FILENAME_LENGTH;
nspPfs0FilePtrs[ptrIdx++] = (u8*)cnmtXml;
}
// Always reserve the first titleContentInfoCnt entries for our NCAs
// Only save the CNMT NCA buffer pointer to the PFS0 file data pointer array. We don't have any other pointers to raw NCA data, so we leave the rest untouched
entrySize = xml_content_info[i].size;
entryFilenameSize = (i == cnmtNcaIndex ? NSP_CNMT_FILENAME_LENGTH : NSP_NCA_FILENAME_LENGTH);
if (i == cnmtNcaIndex) nspPfs0FilePtrs[ptrIdx++] = cnmtNcaBuf;

nspPfs0EntryTable[i].file_size = entrySize;
nspPfs0EntryTable[i].file_offset = curFileOffset;
Expand All @@ -1425,41 +1315,6 @@ int dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInde
curFilenameOffset += entryFilenameSize;
}

for(i = 0; i < xml_rec_cnt; i++, entryIdx++)
{
u8 type = xml_content_info[xml_records[i].nca_index].type;

if (type == NcmContentType_Control && xml_records[i].nacp_icons && xml_records[i].nacp_icon_cnt)
{
// Process all icons at once
for(j = 0; j < xml_records[i].nacp_icon_cnt; j++, entryIdx++)
{
entrySize = xml_records[i].nacp_icons[j].icon_size;
entryFilenameSize = (u32)(strlen(xml_records[i].nacp_icons[j].filename) + 1); // This is the only entry type with variable filename length
nspPfs0FilePtrs[ptrIdx++] = xml_records[i].nacp_icons[j].icon_data;

nspPfs0EntryTable[entryIdx].file_size = entrySize;
nspPfs0EntryTable[entryIdx].file_offset = curFileOffset;
nspPfs0EntryTable[entryIdx].filename_offset = curFilenameOffset;

curFileOffset += entrySize;
curFilenameOffset += entryFilenameSize;
}
}

// XML entry
entrySize = xml_records[i].xml_size;
entryFilenameSize = (type == NcmContentType_Program ? NSP_PROGRAM_XML_FILENAME_LENGTH : (type == NcmContentType_Control ? NSP_NACP_XML_FILENAME_LENGTH : NSP_LEGAL_XML_FILENAME_LENGTH));
nspPfs0FilePtrs[ptrIdx++] = (u8*)xml_records[i].xml_data;

nspPfs0EntryTable[entryIdx].file_size = entrySize;
nspPfs0EntryTable[entryIdx].file_offset = curFileOffset;
nspPfs0EntryTable[entryIdx].filename_offset = curFilenameOffset;

curFileOffset += entrySize;
curFilenameOffset += entryFilenameSize;
}

if (includeTikAndCert)
{
for(i = 0; i < 2; i++, entryIdx++)
Expand All @@ -1480,7 +1335,6 @@ int dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInde
// Calculate total dump size
progressCtx.totalSize += fullPfs0HeaderSize;
for(i = 0; i < titleContentInfoCnt; i++) progressCtx.totalSize += xml_content_info[i].size;
progressCtx.totalSize += strlen(cnmtXml);
if (includeTikAndCert) progressCtx.totalSize += (ETICKET_TIK_FILE_SIZE + ETICKET_CERT_FILE_SIZE);

convertSize(progressCtx.totalSize, progressCtx.totalSizeStr, MAX_CHARACTERS(progressCtx.totalSizeStr));
Expand Down Expand Up @@ -1862,43 +1716,14 @@ int dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInde

breaks = (progressCtx.line_offset - 4);

// Generate proper CNMT XML
generateCnmtXml(&xml_program_info, xml_content_info, cnmtXml);

// Fill PFS0 string table
// This is done here because we'll need to display filenames for the rest of the PFS0 entries starting with the next loop iteration
entryIdx = 0;

for(j = 0; j <= titleContentInfoCnt; j++, entryIdx++)
{
char *curFilename = (nspPfs0StrTable + nspPfs0EntryTable[entryIdx].filename_offset);

if (j < titleContentInfoCnt)
{
sprintf(curFilename, "%s.%s", xml_content_info[j].nca_id_str, (j == cnmtNcaIndex ? "cnmt.nca" : "nca"));
} else
if (j == titleContentInfoCnt)
{
sprintf(curFilename, "%s.cnmt.xml", xml_content_info[cnmtNcaIndex].nca_id_str);
}
}

for(j = 0; j < xml_rec_cnt; j++, entryIdx++)
for(j = 0; j < titleContentInfoCnt; j++, entryIdx++)
{
u8 type = xml_content_info[xml_records[j].nca_index].type;

if (type == NcmContentType_Control && xml_records[j].nacp_icons && xml_records[j].nacp_icon_cnt)
{
// Process all icons at once
for(u32 k = 0; k < xml_records[j].nacp_icon_cnt; k++, entryIdx++)
{
char *curFilename = (nspPfs0StrTable + nspPfs0EntryTable[entryIdx].filename_offset);
sprintf(curFilename, "%s%s", xml_content_info[xml_records[j].nca_index].nca_id_str, strchr(xml_records[j].nacp_icons[k].filename, '.'));
}
}

char *curFilename = (nspPfs0StrTable + nspPfs0EntryTable[entryIdx].filename_offset);
sprintf(curFilename, "%s.%s.xml", xml_content_info[xml_records[j].nca_index].nca_id_str, (type == NcmContentType_Program ? "programinfo" : (type == NcmContentType_Control ? "nacp" : "legalinfo")));
sprintf(curFilename, "%s.%s", xml_content_info[j].nca_id_str, (j == cnmtNcaIndex ? "cnmt.nca" : "nca"));
}

if (includeTikAndCert)
Expand Down Expand Up @@ -2404,8 +2229,6 @@ int dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInde

if (nspPfs0EntryTable) free(nspPfs0EntryTable);

if (cnmtXml) free(cnmtXml);

if (cnmtNcaBuf) free(cnmtNcaBuf);

if (ncaProgramMod)
Expand All @@ -2420,17 +2243,6 @@ int dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleInde
free(ncaProgramMod);
}

if (xml_records)
{
for(i = 0; i < xml_rec_cnt; i++)
{
if (xml_records[i].xml_data) free(xml_records[i].xml_data);
if (xml_records[i].nacp_icons) free(xml_records[i].nacp_icons);
}

free(xml_records);
}

if (xml_content_info) free(xml_content_info);

ncmContentStorageClose(&ncmStorage);
Expand Down
Loading

0 comments on commit 90647a9

Please sign in to comment.