Update to v1.1.8.

This commit is contained in:
Pablo Curiel 2019-12-11 04:56:58 -04:00
parent 3a16147c59
commit 5445d360d1
21 changed files with 5907 additions and 5801 deletions

View File

@ -33,7 +33,7 @@ include $(DEVKITPRO)/libnx/switch_rules
VERSION_MAJOR := 1
VERSION_MINOR := 1
VERSION_MICRO := 7
VERSION_MICRO := 8
APP_TITLE := nxdumptool
APP_AUTHOR := MCMrARM, DarkMatterCore
@ -52,7 +52,7 @@ ROMFS := romfs
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
CFLAGS := -g -Wall -Wno-address-of-packed-member -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__ -D__LINUX_ERRNO_EXTENSIONS__ -DAPP_VERSION=\"${APP_VERSION}\"

View File

@ -16,8 +16,12 @@ Main features
* Manual ticket dump from installed SD/eMMC titles + optional removal of console specific data.
* 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.
* XCI/NSP dump verification through CRC32 checksum lookup.
* Using offline XML database from NSWDB.COM (NSWreleases.xml) (XCI only).
* Performing an online lookup against the No-Intro database.
* Bundled-in update capabilities via libcurl.
* Update to the latest version by downloading it right from GitHub.
* Update the NSWDB.COM XML database.
* 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 & Data NCA RomFS section file data dumping + browser with manual file dump support.
@ -52,6 +56,8 @@ Thanks to
* The [LZ4 project](http://www.lz4.org), for the LZ4 C-code implementation (licensed under [BSD 2-Clause](https://github.com/lz4/lz4/blob/master/lib/LICENSE)).
* [AnalogMan](https://github.com/AnalogMan151) and [0Liam](https://github.com/0Liam), for their constant support and ideas.
* [RattletraPM](https://github.com/RattletraPM), for the awesome icon used in the application.
* [FennecTECH](https://github.com/fennectech), for testing / breaking stuff inside the application on a regular basis.
* The folks from NSWDB.COM and No-Intro.org, for being kind enough to put up a HTTP(S) endpoint(s) to perform CRC32 checksum lookups.
* The GNOME project, from which the [high contrast icons](https://commons.wikimedia.org/wiki/GNOME_High_contrast_icons) were retrieved.
* The folks from ReSwitched, for working towards the creation of a good homebrew ecosystem.
* The Comfy Boyes, for being both awesome and supportive. You know who you are.
@ -67,6 +73,60 @@ If you like my work and you'd like to support me in any way, it's not necessary,
Changelog
--------------
**v1.1.8:**
* Added compatibility with latest devkitA64 and libnx releases. Thanks to [HookedBehemoth](https://github.com/HookedBehemoth) for porting the extra IPC calls used by the application to the new IPC system!
* Now using global title contexts instead of global variables for each different title property (ID, version, source storage, etc.). Simplifies metadata retrieval functions.
* Refactored the HFS0/IStorage parsing code, optimizing all gamecard reads performed by the application.
* Increased dump buffer sizes to 4 MiB.
* NCA content size is now calculated and displayed for all titles.
* Content size for updates and DLCs is displayed in the title selector from the NSP menus.
* Additionally, the application now displays the size for each title in the batch dump summary screen. Plus, an *approximate* total dump size is calculated according to the selected titles.
* Please bear in mind that the displayed information does not reflect output NSP dump sizes.
* Changes to the HFS0, ExeFS and RomFS browsers:
* File sizes are now displayed for all file entries.
* Dumping a file/directory won't reset the cursor position anymore.
* Displayed lists are now lexicographically sorted.
* It is now possible to perform CRC32 checksum lookups using the No-Intro database. Big thanks to the folks from No-Intro.org!
* This new method requires a working Internet connection at runtime.
* For XCI dumps, this is merely offered as an alternative to the NSWDB.COM XML database method, without replacing it. A new option has been added to the XCI dump menu, which lets the user select the verification method they wish to use.
* For NSP dumps, on the other hand, this offers a way to actually validate dumps:
* The "CRC32 checksum calculation" feature, which was a bit pointless, has been entirely removed. The new "Verify dump using No-Intro database" option has taken its place.
* NSP dump verification is achieved by just calculating the CRC32 checksum from the output CNMT NCA and performing a lookup using the No-Intro database. This works because of the way CNMT data is handled by the application:
* The SHA-256 checksum for each NCA is always recalculated during the dump process, and the CNMT NCA is always patched afterwards. However, if no NCA modifications are performed, the CNMT NCA ends up being identical to its original counterpart, because the content records won't have changed at all.
* This lets the application verify the NSP dump by performing a CRC32 checksum lookup using the CNMT NCA data, as long as no NCA modifications take place.
* As such, this method only works with SD card / eMMC titles, as long as the "Generate ticket-less dump" option is disabled.
* This option doesn't appear in gamecard-related menus, and it's not compatible with batch dumps.
* By popular demand, an option has been added in XCI, NSP and batch dump menus to change the naming scheme used with output files to the following:
* XCI dumps:
* Single game: `TitleName [TitleID][TitleVersion]`.
* Multigame: `TitleName1 [TitleID1][TitleVersion1] + TitleName2 [TitleID2][TitleVersion2] + ... + TitleNameN [TitleIDN][TitleVersionN]`.
* NSP/Batch dumps: `TitleName [TitleID][TitleVersion][TitleType]`.
* The "Remember dumped titles" feature available in batch mode isn't affected by this new setting - batch overrides will keep using the regular naming scheme.
* Added an option to include delta fragment NCAs in output NSP dumps from installed SD/eMMC updates. It is disabled by default.
* Added a small settings menu to the ExeFS/RomFS sections with the following options:
* `Split files bigger than 4 GiB (FAT32 support)`: unlike previous versions, it is now possible to control if file splitting will take place for ExeFS/RomFS file dumps, instead of always splitting them. If this option is enabled, files bigger than 4 GiB will now be split and stored in a subdirectory with the archive bit set (like NSPs).
* `Save data to CFW directory (LayeredFS)`: enabling this option will save output data to the directory from the CFW you're running, using the LayeredFS layout.
* Added a new option to the batch mode menu to control if the batch dump process should halt on any errors. If disabled, it'll make the batch dump process wait for 5 seconds on any errors, then it will keep going.
* Free SD card space is now always displayed on every UI state. It is also displayed and updated during batch mode operations.
* ExeFS submenu is now available for updates in the orphan content list (Y button menu).
* It is now possible to exit the application from the batch dump summary screen.
* A warning is now displayed in the main menu if the application is launched using applet mode. NSP dumps from base applications and updates can fail if there's not enough heap available to hold the uncompressed `main` NSO while generating the `programinfo.xml`.
* Improved XPath query used when looking for checksum matches in the NSWDB.COM XML database. Fixes CRC32 checksum lookup for multigame cartridges.
* Empty RomFS directories are now properly handled by the RomFS browser.
* Removed a BKTR RomFS section offset check that was causing trouble while trying to perform RomFS-related operations with some updates (e.g. Luigi's Mansion 3).
* Physical IStorage reads are now performed to retrieve NCAs from gamecards, instead of using `ncmContentStorageReadContentIdFile()`. Fixes gamecard NSP/ExeFS/RomFS operations under FW versions < 4.0.0.
* Fixed unaligned IStorage reads in manual file dumps files from HFS0 partitions in gamecards. Unaligned files dumped this way should no longer contain garbage data.
* Fixed a memory leak in the XML database verification code.
* Fixed an indexing bug in the RomFS browser that could potentially cause problems when performing any action from the root directory.
* Fixed gamecard hotswapping in gamecard-related submenus.
* Fixed a free SD card space check in sequential XCI/NSP dump procedures.
* Fixed a bug where the output dump name wouldn't be generated for orphan content when no base applications are installed, preventing the NSP dump procedure from starting. Thanks to [snes878](https://github.com/snes878) for reporting this!
* Fixed a bug that prevented to retrieve the ticket for a bundled-in gamecard update from the Secure HFS0 partition during a NSP dump procedure. Thanks to [snes878](https://github.com/snes878) for reporting this!
* Fixed a bug where a NSP dump process would stop if no personalized ticket certificate is found in the ES system savefile (e.g. when no titles with personalized titlekey crypto have been downloaded from the eShop). Thanks to [satel](https://gbatemp.net/members/satel.27798/) for reporting this!
* Fixed a bug where an empty orphan content list would have been generated if no base applications are installed. Thanks to `Newb_3DS#6287` for reporting this issue!
Thanks to [FennecTECH](https://github.com/fennectech) and MUXI from PSXTools forums for providing with testing!
**v1.1.7:**
* Tickets and RSA certificates are now properly parsed from their respective system savedata files, thanks to the efforts of [shchmue](https://github.com/shchmue)!
* Speeds up ticket / titlekey retrieval for NSP/ExeFS/RomFS operations.

File diff suppressed because it is too large Load Diff

View File

@ -6,8 +6,6 @@
#include <switch.h>
#include "util.h"
#define ISTORAGE_PARTITION_CNT 2
#define FAT32_FILESIZE_LIMIT (u64)0xFFFFFFFF // 4 GiB - 1 (4294967295 bytes)
#define SPLIT_FILE_XCI_PART_SIZE (u64)0xFFFF8000 // 4 GiB - 0x8000 (4294934528 bytes) (based on XCI-Cutter)
@ -18,12 +16,6 @@
#define CERT_OFFSET 0x7000
#define CERT_SIZE 0x200
#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
#define DUMP_NSP_CRC_WAIT 5 // The user must wait for at least DUMP_NSP_CRC_WAIT seconds before the CRC32 checksum calculation process starts after the NSP dump process is finished
typedef struct {
bool keepCert; // Original value for the "Keep certificate" option. Overrides the selected setting in the current session
bool trimDump; // Original value for the "Trim output dump" option. Overrides the selected setting in the current session
@ -36,7 +28,7 @@ typedef struct {
} PACKED sequentialXciCtx;
typedef struct {
FsStorageId storageId; // Source storage from which the data is dumped
NcmStorageId storageId; // Source storage from which the data is dumped
bool removeConsoleData; // Original value for the "Remove console specific data" option. Overrides the selected setting in the current session
bool tiklessDump; // Original value for the "Generate ticket-less dump" option. Overrides the selected setting in the current session. Ignored if removeConsoleData == false
bool npdmAcidRsaPatch; // Original value for the "Change NPDM RSA key/sig in Program NCA" option. Overrides the selected setting in the current session
@ -54,22 +46,23 @@ typedef struct {
bool enabled;
nspDumpType titleType;
u32 titleIndex;
u64 contentSize;
char *contentSizeStr;
char nspFilename[NAME_BUF_LEN];
char truncatedNspFilename[NAME_BUF_LEN];
} batchEntry;
void workaroundPartitionZeroAccess();
bool dumpCartridgeImage(xciOptions *xciDumpCfg);
bool dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleIndex, nspOptions *nspDumpCfg, bool batch);
bool dumpNintendoSubmissionPackageBatch(batchOptions *batchDumpCfg);
bool dumpNXCardImage(xciOptions *xciDumpCfg);
int dumpNintendoSubmissionPackage(nspDumpType selectedNspDumpType, u32 titleIndex, nspOptions *nspDumpCfg, bool batch);
int dumpNintendoSubmissionPackageBatch(batchOptions *batchDumpCfg);
bool dumpRawHfs0Partition(u32 partition, bool doSplitting);
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, selectedRomFsType curRomFsType, bool doSplitting);
bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType curRomFsType, bool doSplitting);
bool dumpCurrentDirFromRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType, bool doSplitting);
bool dumpFileFromHfs0Partition(u32 partition, u32 fileIndex, char *filename, bool doSplitting);
bool dumpExeFsSectionData(u32 titleIndex, bool usePatch, ncaFsOptions *exeFsDumpCfg);
bool dumpFileFromExeFsSection(u32 titleIndex, u32 fileIndex, bool usePatch, ncaFsOptions *exeFsDumpCfg);
bool dumpRomFsSectionData(u32 titleIndex, selectedRomFsType curRomFsType, ncaFsOptions *romFsDumpCfg);
bool dumpFileFromRomFsSection(u32 titleIndex, u32 file_offset, selectedRomFsType curRomFsType, ncaFsOptions *romFsDumpCfg);
bool dumpCurrentDirFromRomFsSection(u32 titleIndex, selectedRomFsType curRomFsType, ncaFsOptions *romFsDumpCfg);
bool dumpGameCardCertificate();
bool dumpTicketFromTitle(u32 titleIndex, selectedTicketType curTikType, ticketOptions *tikDumpCfg);

View File

@ -5,171 +5,72 @@
#include <string.h>
#include "es.h"
#include "service_guard.h"
static Service g_esSrv;
static u64 g_esRefCnt;
Result esInitialize()
{
atomicIncrement64(&g_esRefCnt);
if (serviceIsActive(&g_esSrv)) return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized);
NX_GENERATE_SERVICE_GUARD(es);
Result _esInitialize() {
return smGetService(&g_esSrv, "es");
}
void esExit()
{
if (atomicDecrement64(&g_esRefCnt) == 0) serviceClose(&g_esSrv);
void _esCleanup() {
serviceClose(&g_esSrv);
}
Result esCountCommonTicket(u32 *num_tickets)
{
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
} *raw;
u32 num_tickets;
} out;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 9;
Result rc = serviceIpcDispatch(&g_esSrv);
if (R_SUCCEEDED(rc))
{
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
u32 num_tickets;
} *resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) *num_tickets = resp->num_tickets;
}
Result rc = serviceDispatchOut(&g_esSrv, 9, out);
if (R_SUCCEEDED(rc) && num_tickets) *num_tickets = out.num_tickets;
return rc;
}
Result esCountPersonalizedTicket(u32 *num_tickets)
{
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
} *raw;
u32 num_tickets;
} out;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 10;
Result rc = serviceIpcDispatch(&g_esSrv);
if (R_SUCCEEDED(rc))
{
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
u32 num_tickets;
} *resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) *num_tickets = resp->num_tickets;
}
Result rc = serviceDispatchOut(&g_esSrv, 10, out);
if (R_SUCCEEDED(rc) && num_tickets) *num_tickets = out.num_tickets;
return rc;
}
Result esListCommonTicket(u32 *numRightsIdsWritten, FsRightsId *outBuf, size_t bufSize)
{
IpcCommand c;
ipcInitialize(&c);
ipcAddRecvBuffer(&c, outBuf, bufSize, BufferType_Normal);
struct {
u64 magic;
u64 cmd_id;
} *raw;
u32 num_rights_ids_written;
} out;
raw = ipcPrepareHeader(&c, sizeof(*raw));
Result rc = serviceDispatchInOut(&g_esSrv, 11, *numRightsIdsWritten, out,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { outBuf, bufSize } },
);
raw->magic = SFCI_MAGIC;
raw->cmd_id = 11;
Result rc = serviceIpcDispatch(&g_esSrv);
if (R_SUCCEEDED(rc))
{
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
u32 num_rights_ids_written;
} *resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc))
{
if (numRightsIdsWritten) *numRightsIdsWritten = resp->num_rights_ids_written;
}
}
if (R_SUCCEEDED(rc) && numRightsIdsWritten) *numRightsIdsWritten = out.num_rights_ids_written;
return rc;
}
Result esListPersonalizedTicket(u32 *numRightsIdsWritten, FsRightsId *outBuf, size_t bufSize)
{
IpcCommand c;
ipcInitialize(&c);
ipcAddRecvBuffer(&c, outBuf, bufSize, BufferType_Normal);
struct {
u64 magic;
u64 cmd_id;
} *raw;
u32 num_rights_ids_written;
} out;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 12;
Result rc = serviceDispatchInOut(&g_esSrv, 12, *numRightsIdsWritten, out,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { outBuf, bufSize } },
);
Result rc = serviceIpcDispatch(&g_esSrv);
if (R_SUCCEEDED(rc))
{
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
u32 num_rights_ids_written;
} *resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc))
{
if (numRightsIdsWritten) *numRightsIdsWritten = resp->num_rights_ids_written;
}
}
if (R_SUCCEEDED(rc) && numRightsIdsWritten) *numRightsIdsWritten = out.num_rights_ids_written;
return rc;
}

View File

@ -1,4 +1,5 @@
#include <switch/kernel/ipc.h>
#include <switch/services/fs.h>
#include <stdlib.h>
#include <string.h>
@ -7,124 +8,41 @@
// IFileSystemProxy
Result fsOpenGameCardStorage(FsStorage* out, const FsGameCardHandle* handle, u32 partition)
{
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u32 handle;
u32 partition;
} *raw;
} in = { handle->value, partition };
raw = serviceIpcPrepareHeader(fsGetServiceSession(), &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 30;
raw->handle = handle->value;
raw->partition = partition;
Result rc = serviceIpcDispatch(fsGetServiceSession());
if (R_SUCCEEDED(rc))
{
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(fsGetServiceSession(), &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) serviceCreateSubservice(&out->s, fsGetServiceSession(), &r, 0);
}
return rc;
return serviceDispatchIn(fsGetServiceSession(), 30, in,
.out_num_objects = 1,
.out_objects = &out->s
);
}
Result fsOpenGameCardDetectionEventNotifier(FsEventNotifier* out)
{
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
} *raw;
raw = serviceIpcPrepareHeader(fsGetServiceSession(), &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 501;
Result rc = serviceIpcDispatch(fsGetServiceSession());
if (R_SUCCEEDED(rc))
{
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(fsGetServiceSession(), &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) serviceCreateSubservice(&out->s, fsGetServiceSession(), &r, 0);
}
return rc;
return serviceDispatch(fsGetServiceSession(), 501,
.out_num_objects = 1,
.out_objects = &out->s
);
}
// IDeviceOperator
Result fsDeviceOperatorUpdatePartitionInfo(FsDeviceOperator* d, const FsGameCardHandle* handle, u32* out_title_version, u64* out_title_id)
{
IpcCommand c;
ipcInitialize(&c);
struct {
u32 handle;
} in = { handle->value };
struct {
u64 magic;
u64 cmd_id;
u32 handle;
} *raw;
u32 title_ver;
u64 title_id;
} out;
raw = serviceIpcPrepareHeader(&d->s, &c, sizeof(*raw));
Result rc = serviceDispatchInOut(&d->s, 203, in, out);
raw->magic = SFCI_MAGIC;
raw->cmd_id = 203;
raw->handle = handle->value;
Result rc = serviceIpcDispatch(&d->s);
if (R_SUCCEEDED(rc))
{
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
u32 title_ver;
u64 title_id;
} *resp;
serviceIpcParse(&d->s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc))
{
if (out_title_version != NULL) *out_title_version = resp->title_ver;
if (out_title_id != NULL) *out_title_id = resp->title_id;
}
}
if (R_SUCCEEDED(rc) && out_title_version) *out_title_version = out.title_ver;
if (R_SUCCEEDED(rc) && out_title_id) *out_title_id = out.title_id;
return rc;
}

View File

@ -95,7 +95,7 @@ bool retrieveProcessMemory(keyLocation *location)
{
if (!location || !location->titleID || !location->mask)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to retrieve process memory.");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid parameters to retrieve process memory.", __func__);
return false;
}
@ -110,24 +110,24 @@ bool retrieveProcessMemory(keyLocation *location)
// If not a kernel process, get PID from pm:dmnt
u64 pid;
result = pmdmntGetTitlePid(&pid, location->titleID);
result = pmdmntGetProcessId(&pid, location->titleID);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: pmdmntGetTitlePid failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: pmdmntGetProcessId failed! (0x%08X)", __func__, result);
return false;
}
result = svcDebugActiveProcess(&debug_handle, pid);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: svcDebugActiveProcess failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: svcDebugActiveProcess failed! (0x%08X)", __func__, result);
return false;
}
result = svcGetDebugEvent((u8*)&d, debug_handle);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: svcGetDebugEvent failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: svcGetDebugEvent failed! (0x%08X)", __func__, result);
return false;
}
} else {
@ -138,7 +138,7 @@ bool retrieveProcessMemory(keyLocation *location)
result = svcGetProcessList(&num_processes, pids, 300);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: svcGetProcessList failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: svcGetProcessList failed! (0x%08X)", __func__, result);
return false;
}
@ -147,13 +147,12 @@ bool retrieveProcessMemory(keyLocation *location)
for(i = 0; i < (num_processes - 1); i++)
{
if (R_SUCCEEDED(svcDebugActiveProcess(&debug_handle, pids[i])) && R_SUCCEEDED(svcGetDebugEvent((u8*)&d, debug_handle)) && (d[2] == location->titleID)) break;
if (debug_handle) svcCloseHandle(debug_handle);
}
if (i == (num_processes - 1))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to retrieve debug handle for process with Title ID %016lX!", location->titleID);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: unable to retrieve debug handle for process with Title ID %016lX!", __func__, location->titleID);
if (debug_handle) svcCloseHandle(debug_handle);
return false;
}
@ -176,7 +175,7 @@ bool retrieveProcessMemory(keyLocation *location)
result = svcQueryDebugProcessMemory(&mem_info, &page_info, debug_handle, addr);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: svcQueryDebugProcessMemory failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: svcQueryDebugProcessMemory failed! (0x%08X)", __func__, result);
success = false;
break;
}
@ -200,7 +199,7 @@ bool retrieveProcessMemory(keyLocation *location)
result = svcQueryDebugProcessMemory(&mem_info, &page_info, debug_handle, addr);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: svcQueryDebugProcessMemory failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: svcQueryDebugProcessMemory failed! (0x%08X)", __func__, result);
success = false;
break;
}
@ -212,7 +211,7 @@ bool retrieveProcessMemory(keyLocation *location)
dataTmp = realloc(location->data, location->dataSize + mem_info.size);
if (!dataTmp)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to resize key location data buffer to %lu bytes.", location->dataSize + mem_info.size);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: failed to resize key location data buffer to %lu bytes.", __func__, location->dataSize + mem_info.size);
success = false;
break;
}
@ -225,7 +224,7 @@ bool retrieveProcessMemory(keyLocation *location)
result = svcReadDebugProcessMemory(location->data + location->dataSize, debug_handle, mem_info.addr, mem_info.size);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: svcReadDebugProcessMemory failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: svcReadDebugProcessMemory failed! (0x%08X)", __func__, result);
success = false;
break;
}
@ -251,7 +250,7 @@ bool findKeyInProcessMemory(const keyLocation *location, const keyInfo *findKey,
{
if (!location || !location->data || !location->dataSize || !findKey || !strlen(findKey->name) || !findKey->size)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to locate key in process memory.");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid parameters to locate key in process memory.", __func__);
return false;
}
@ -275,7 +274,7 @@ bool findKeyInProcessMemory(const keyLocation *location, const keyInfo *findKey,
}
}
if (!found) uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to locate key \"%s\" in process memory!", findKey->name);
if (!found) uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: unable to locate key \"%s\" in process memory!", __func__, findKey->name);
return found;
}
@ -284,7 +283,7 @@ bool findFSRodataKeys(keyLocation *location)
{
if (!location || location->titleID != FS_TID || location->mask != SEG_RODATA || !location->data || !location->dataSize)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to locate keys in FS RODATA segment.");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid parameters to locate keys in FS .rodata segment.", __func__);
return false;
}
@ -325,14 +324,14 @@ bool loadMemoryKeys()
result = splCryptoInitialize();
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to initialize the spl:crypto service! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: failed to initialize the spl:crypto service! (0x%08X)", __func__, result);
return false;
}
result = splCryptoGenerateAesKek(nca_keyset.header_kek_source, 0, 0, nca_keyset.header_kek);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splCryptoGenerateAesKek(header_kek_source) failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: splCryptoGenerateAesKek(header_kek_source) failed! (0x%08X)", __func__, result);
splCryptoExit();
return false;
}
@ -342,7 +341,7 @@ bool loadMemoryKeys()
result = splCryptoGenerateAesKey(nca_keyset.header_kek, nca_keyset.header_key_source + 0x00, nca_keyset.header_key + 0x00);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splCryptoGenerateAesKey(header_key_source + 0x00) failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: splCryptoGenerateAesKey(header_key_source + 0x00) failed! (0x%08X)", __func__, result);
splCryptoExit();
return false;
}
@ -350,7 +349,7 @@ bool loadMemoryKeys()
result = splCryptoGenerateAesKey(nca_keyset.header_kek, nca_keyset.header_key_source + 0x10, nca_keyset.header_key + 0x10);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splCryptoGenerateAesKey(header_key_source + 0x10) failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: splCryptoGenerateAesKey(header_key_source + 0x10) failed! (0x%08X)", __func__, result);
splCryptoExit();
return false;
}
@ -368,7 +367,7 @@ bool decryptNcaKeyArea(nca_header_t *dec_nca_header, u8 *out)
{
if (!dec_nca_header || dec_nca_header->kaek_ind > 2 || !out)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to decrypt NCA key area.");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid parameters to decrypt NCA key area.", __func__);
return false;
}
@ -380,7 +379,7 @@ bool decryptNcaKeyArea(nca_header_t *dec_nca_header, u8 *out)
u8 crypto_type = (dec_nca_header->crypto_type2 > dec_nca_header->crypto_type ? dec_nca_header->crypto_type2 : dec_nca_header->crypto_type);
if (crypto_type > 0x20)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NCA keyblob index.");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid NCA keyblob index!", __func__);
return false;
}
@ -389,14 +388,14 @@ bool decryptNcaKeyArea(nca_header_t *dec_nca_header, u8 *out)
result = splCryptoInitialize();
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to initialize the spl:crypto service! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: failed to initialize the spl:crypto service! (0x%08X)", __func__, result);
return false;
}
result = splCryptoGenerateAesKek(kek_source, crypto_type, 0, tmp_kek);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splCryptoGenerateAesKek(kek_source) failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: splCryptoGenerateAesKek(kek_source) failed! (0x%08X)", __func__, result);
splCryptoExit();
return false;
}
@ -409,7 +408,7 @@ bool decryptNcaKeyArea(nca_header_t *dec_nca_header, u8 *out)
result = splCryptoGenerateAesKey(tmp_kek, dec_nca_header->nca_keys[i], decrypted_nca_keys[i]);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splCryptoGenerateAesKey(nca_kaek_%02u) failed! (0x%08X)", i, result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: splCryptoGenerateAesKey(nca_kaek_%02u) failed! (0x%08X)", __func__, i, result);
success = false;
break;
}
@ -574,7 +573,7 @@ int parse_hex_key(unsigned char *key, const char *hex, unsigned int len)
{
if (strlen(hex) != (2 * len))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: key (%s) must be %u hex digits!", hex, 2 * len);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: key (%s) must be %u hex digits!", __func__, hex, 2 * len);
return 0;
}
@ -583,7 +582,7 @@ int parse_hex_key(unsigned char *key, const char *hex, unsigned int len)
{
if (!ishex(hex[i]))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: key (%s) must be %u hex digits!", hex, 2 * len);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: key (%s) must be %u hex digits!", __func__, hex, 2 * len);
return 0;
}
}
@ -679,7 +678,7 @@ bool loadExternalKeys()
FILE *keysFile = fopen(KEYS_FILE_PATH, "rb");
if (!keysFile)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to open \"%s\" to retrieve \"eticket_rsa_kek\", titlekeks and KAEKs!", KEYS_FILE_PATH);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: unable to open \"%s\" to retrieve \"eticket_rsa_kek\", titlekeks and KAEKs!", __func__, KEYS_FILE_PATH);
return false;
}
@ -689,7 +688,7 @@ bool loadExternalKeys()
if (ret < 1)
{
if (ret == -1) uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to parse necessary keys from \"%s\"! (keys file empty?)", KEYS_FILE_PATH);
if (ret == -1) uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: unable to parse necessary keys from \"%s\"! (keys file empty?)", __func__, KEYS_FILE_PATH);
return false;
}
@ -700,7 +699,7 @@ bool testKeyPair(const void *E, const void *D, const void *N)
{
if (!E || !D || !N)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to test RSA key pair.");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid parameters to test RSA key pair.", __func__);
return false;
}
@ -717,14 +716,14 @@ bool testKeyPair(const void *E, const void *D, const void *N)
result = splUserExpMod(X, N, D, 0x100, Y);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splUserExpMod failed! (testKeyPair #1) (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: splUserExpMod failed! (testKeyPair #1) (0x%08X)", __func__, result);
return false;
}
result = splUserExpMod(Y, N, E, 4, Z);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splUserExpMod failed! (testKeyPair #2) (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: splUserExpMod failed! (testKeyPair #2) (0x%08X)", __func__, result);
return false;
}
@ -732,7 +731,7 @@ bool testKeyPair(const void *E, const void *D, const void *N)
{
if (X[i] != Z[i])
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid RSA key pair!");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid RSA key pair!", __func__);
return false;
}
}
@ -775,7 +774,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
if (!dec_nca_header || dec_nca_header->kaek_ind > 2 || (!out_tik && !out_dec_key && !out_enc_key))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to retrieve NCA ticket and/or titlekey.");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid parameters to retrieve NCA ticket and/or titlekey.", __func__);
return ret;
}
@ -793,7 +792,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
if (!has_rights_id)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: NCA doesn't use titlekey crypto.");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: NCA doesn't use titlekey crypto.", __func__);
return ret;
}
@ -802,7 +801,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
if (crypto_type >= 0x20)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NCA keyblob index.");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid NCA keyblob index.", __func__);
return ret;
}
@ -840,14 +839,14 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
result = esInitialize();
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to initialize the ES service! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: failed to initialize the ES service! (0x%08X)", __func__, result);
return ret;
}
result = esCountCommonTicket(&common_count);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: esCountCommonTicket failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: esCountCommonTicket failed! (0x%08X)", __func__, result);
esExit();
return ret;
}
@ -855,14 +854,14 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
result = esCountPersonalizedTicket(&personalized_count);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: esCountPersonalizedTicket failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: esCountPersonalizedTicket failed! (0x%08X)", __func__, result);
esExit();
return ret;
}
if (!common_count && !personalized_count)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: no tickets available!");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: no tickets available!", __func__);
esExit();
return ret;
}
@ -872,7 +871,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
common_rights_ids = calloc(common_count, sizeof(FsRightsId));
if (!common_rights_ids)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to allocate memory for common tickets' rights IDs!");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: failed to allocate memory for common tickets' rights IDs!", __func__);
esExit();
return ret;
}
@ -880,7 +879,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
result = esListCommonTicket(&ids_written, common_rights_ids, common_count * sizeof(FsRightsId));
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: esListCommonTicket failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: esListCommonTicket failed! (0x%08X)", __func__, result);
free(common_rights_ids);
esExit();
return ret;
@ -904,7 +903,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
personalized_rights_ids = calloc(personalized_count, sizeof(FsRightsId));
if (!personalized_rights_ids)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to allocate memory for personalized tickets' rights IDs!");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: failed to allocate memory for personalized tickets' rights IDs!", __func__);
esExit();
return ret;
}
@ -912,7 +911,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
result = esListPersonalizedTicket(&ids_written, personalized_rights_ids, personalized_count * sizeof(FsRightsId));
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: esListPersonalizedTicket failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: esListPersonalizedTicket failed! (0x%08X)", __func__, result);
free(personalized_rights_ids);
esExit();
return ret;
@ -935,7 +934,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
if (!foundRightsId || (rightsIdType != 1 && rightsIdType != 2))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: NCA rights ID unavailable in this console!");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: NCA rights ID unavailable in this console!", __func__);
ret = -2;
return ret;
}
@ -951,7 +950,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
result = setcalInitialize();
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to initialize the set:cal service! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: failed to initialize the set:cal service! (0x%08X)", __func__, result);
return ret;
}
@ -961,7 +960,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: setcalGetEticketDeviceKey failed! (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: setcalGetEticketDeviceKey failed! (0x%08X)", __func__, result);
return ret;
}
@ -974,35 +973,33 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
// The value is stored use big endian byte order
if (bswap_32(*((u32*)(&(eticket_data[ETICKET_DEVKEY_RSA_OFFSET + 0x200])))) != SIGTYPE_RSA2048_SHA1)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid public RSA exponent for eTicket data! Wrong keys?\nTry running Lockpick_RCM to generate the keys file from scratch.");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid public RSA exponent for eTicket data! Wrong keys?\nTry running Lockpick_RCM to generate the keys file from scratch.", __func__);
return ret;
}
D = &(eticket_data[ETICKET_DEVKEY_RSA_OFFSET]);
N = &(eticket_data[ETICKET_DEVKEY_RSA_OFFSET + 0x100]);
E = &(eticket_data[ETICKET_DEVKEY_RSA_OFFSET + 0x200]);
}
D = &(eticket_data[ETICKET_DEVKEY_RSA_OFFSET]);
N = &(eticket_data[ETICKET_DEVKEY_RSA_OFFSET + 0x100]);
E = &(eticket_data[ETICKET_DEVKEY_RSA_OFFSET + 0x200]);
if (!setcal_eticket_retrieved)
{
if (!testKeyPair(E, D, N)) return ret;
setcal_eticket_retrieved = true;
} else {
D = &(eticket_data[ETICKET_DEVKEY_RSA_OFFSET]);
N = &(eticket_data[ETICKET_DEVKEY_RSA_OFFSET + 0x100]);
E = &(eticket_data[ETICKET_DEVKEY_RSA_OFFSET + 0x200]);
}
// FatFs is used to mount the BIS System partition and read the ES savedata files to avoid 0xE02 (file already in use) errors
fr = f_open(&eTicketSave, (rightsIdType == 1 ? BIS_COMMON_TIK_SAVE_NAME : BIS_PERSONALIZED_TIK_SAVE_NAME), FA_READ | FA_OPEN_EXISTING);
if (fr)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to open ES %s eTicket save! (%u)", (rightsIdType == 1 ? "common" : "personalized"), fr);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: failed to open ES %s eTicket save! (%u)", __func__, (rightsIdType == 1 ? "common" : "personalized"), fr);
return ret;
}
save_ctx = calloc(1, sizeof(save_ctx_t));
if (!save_ctx)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: failed to allocate memory for ticket savefile context!");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: failed to allocate memory for ticket savefile context!");
f_close(&eTicketSave);
return ret;
}
@ -1013,7 +1010,8 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
if (!save_process(save_ctx))
{
strcat(strbuf, "\nError: failed to process ticket savefile!");
snprintf(tmp, MAX_CHARACTERS(tmp), "\n%s: failed to process ticket savefile!", __func__);
strcat(strbuf, tmp);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, strbuf);
free(save_ctx);
f_close(&eTicketSave);
@ -1022,7 +1020,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
if (!save_hierarchical_file_table_get_file_entry_by_path(&save_ctx->save_filesystem_core.file_table, ticket_bin_path, &entry))
{
snprintf(tmp, MAX_ELEMENTS(tmp), "\nError: failed to get file entry for \"%s\" in ticket savefile!", ticket_bin_path);
snprintf(tmp, MAX_CHARACTERS(tmp), "\n%s: failed to get file entry for \"%s\" in ticket savefile!", __func__, ticket_bin_path);
strcat(strbuf, tmp);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, strbuf);
save_free_contexts(save_ctx);
@ -1033,7 +1031,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
if (!save_open_fat_storage(&save_ctx->save_filesystem_core, &fat_storage, entry.value.save_file_info.start_block))
{
snprintf(tmp, MAX_ELEMENTS(tmp), "\nError: failed to open FAT storage at block 0x%X for \"%s\" in ticket savefile!", entry.value.save_file_info.start_block, ticket_bin_path);
snprintf(tmp, MAX_CHARACTERS(tmp), "\n%s: failed to open FAT storage at block 0x%X for \"%s\" in ticket savefile!", __func__, entry.value.save_file_info.start_block, ticket_bin_path);
strcat(strbuf, tmp);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, strbuf);
save_free_contexts(save_ctx);
@ -1047,7 +1045,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
br = save_allocation_table_storage_read(&fat_storage, dumpBuf, total_br, buf_size);
if (br != buf_size)
{
snprintf(tmp, MAX_ELEMENTS(tmp), "\nError: failed to read %u bytes chunk at offset 0x%lX from \"%s\" in ticket savefile!", buf_size, total_br, ticket_bin_path);
snprintf(tmp, MAX_CHARACTERS(tmp), "\n%s: failed to read %u bytes chunk at offset 0x%lX from \"%s\" in ticket savefile!", __func__, buf_size, total_br, ticket_bin_path);
strcat(strbuf, tmp);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, strbuf);
proceed = false;
@ -1079,7 +1077,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
result = splUserExpMod(titleKeyBlock, N, D, 0x100, M);
if (R_FAILED(result))
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: splUserExpMod failed! (titleKeyBlock) (0x%08X)", result);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: splUserExpMod failed! (titleKeyBlock) (0x%08X)", __func__, result);
proceed = false;
break;
}
@ -1094,7 +1092,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
// Verify if it starts with a null string hash
if (memcmp(db, null_hash, 0x20) != 0)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: titlekey decryption failed! Wrong keys?\nTry running Lockpick_RCM to generate the keys file from scratch.");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: titlekey decryption failed! Wrong keys?\nTry running Lockpick_RCM to generate the keys file from scratch.", __func__);
proceed = false;
break;
}
@ -1116,7 +1114,7 @@ int retrieveNcaTikTitleKey(nca_header_t *dec_nca_header, u8 *out_tik, u8 *out_en
if (!foundEticket)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to find a matching eTicket entry for NCA rights ID!");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: unable to find a matching eTicket entry for NCA rights ID!", __func__);
ret = -2;
return ret;
}
@ -1145,7 +1143,7 @@ bool generateEncryptedNcaKeyAreaWithTitlekey(nca_header_t *dec_nca_header, u8 *d
{
if (!dec_nca_header || dec_nca_header->kaek_ind > 2 || !decrypted_nca_keys || !nca_keyset.ext_key_cnt)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to generate encrypted NCA key area using titlekey!");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid parameters to generate encrypted NCA key area using titlekey!", __func__);
return false;
}
@ -1157,7 +1155,7 @@ bool generateEncryptedNcaKeyAreaWithTitlekey(nca_header_t *dec_nca_header, u8 *d
if (crypto_type >= 0x20)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid NCA keyblob index.");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid NCA keyblob index.", __func__);
return false;
}

View File

@ -56,8 +56,8 @@ typedef struct {
// Needed to reencrypt the NCA key area for tik-less NSP dumps. Retrieved from the Lockpick_RCM keys file.
u8 key_area_keys[0x20][3][0x10]; /* Key area encryption keys. */
// Needed to decrypt saves from system and application titles
u8 save_mac_key[0x10];
// Console specific. Needed to calculate the AES CMAC for savefiles. Retrieved from the Lockpick_RCM keys file.
u8 save_mac_key[0x10]; /* Savefile CMAC key */
} PACKED nca_keyset_t;
bool loadMemoryKeys();

View File

@ -2,203 +2,19 @@
#include <stdlib.h>
#include <string.h>
#include <switch.h>
#include <memory.h>
#include "dumper.h"
#include "ui.h"
#include "util.h"
#include "fs_ext.h"
#include "keys.h"
/* Extern variables */
extern bool keysFileAvailable;
extern FsDeviceOperator fsOperatorInstance;
extern FsEventNotifier fsGameCardEventNotifier;
extern Handle fsGameCardEventHandle;
extern Event fsGameCardKernelEvent;
extern UEvent exitEvent;
extern bool gameCardInserted;
extern char appLaunchPath[NAME_BUF_LEN];
extern nca_keyset_t nca_keyset;
int main(int argc, char *argv[])
{
/* Copy launch path */
if (!envIsNso() && argc > 0)
{
int i;
for(i = 0; i < argc; i++)
{
if (strlen(argv[i]) > 10 && !strncasecmp(argv[i], "sdmc:/", 6) && !strncasecmp(argv[i] + strlen(argv[i]) - 4, ".nro", 4))
{
snprintf(appLaunchPath, MAX_ELEMENTS(appLaunchPath), argv[i]);
break;
}
}
}
/* Initialize UI */
if (!uiInit()) return -1;
/* Zero out NCA keyset */
memset(&nca_keyset, 0, sizeof(nca_keyset_t));
/* Init ExeFS context */
initExeFsContext();
/* Init RomFS context */
initRomFsContext();
/* Init BKTR context */
initBktrContext();
/* Make sure output directories exist */
createOutputDirectories();
/* Load settings from configuration file */
loadConfig();
/* Check if the Lockpick_RCM keys file is available */
keysFileAvailable = checkIfFileExists(KEYS_FILE_PATH);
/* Enable CPU boost mode */
appletSetCpuBoostMode(ApmCpuBoostMode_Type1);
Result result;
Thread thread;
int ret = 0;
bool exitMainLoop = false;
bool initNcm = false, initNs = false, initCsrng = false, initSpl = false, initPmdmnt = false;
bool openFsDevOp = false, openGcEvtNotifier = false, loadGcKernEvt = false, startGcThread = false;
/* Initialize the ncm service */
result = ncmInitialize();
if (R_FAILED(result))
/* Initialize application resources */
if (!initApplicationResources(argc, argv))
{
uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_ERROR_RGB, "Failed to initialize the ncm service! (0x%08X)", result);
ret = -2;
goto out;
}
initNcm = true;
/* Initialize the ns service */
result = nsInitialize();
if (R_FAILED(result))
{
uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_ERROR_RGB, "Failed to initialize the ns service! (0x%08X)", result);
ret = -3;
goto out;
}
initNs = true;
/* Initialize the csrng service */
result = csrngInitialize();
if (R_FAILED(result))
{
uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_ERROR_RGB, "Failed to initialize the csrng service! (0x%08X)", result);
ret = -4;
goto out;
}
initCsrng = true;
/* Initialize the spl service */
result = splInitialize();
if (R_FAILED(result))
{
uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_ERROR_RGB, "Failed to initialize the spl service! (0x%08X)", result);
ret = -5;
goto out;
}
initSpl = true;
/* Initialize the pm:dmnt service */
result = pmdmntInitialize();
if (R_FAILED(result))
{
uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_ERROR_RGB, "Failed to initialize the pm:dmnt service! (0x%08X)", result);
ret = -6;
goto out;
}
initPmdmnt = true;
/* Open device operator */
result = fsOpenDeviceOperator(&fsOperatorInstance);
if (R_FAILED(result))
{
uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_ERROR_RGB, "Failed to open device operator! (0x%08X)", result);
ret = -7;
goto out;
}
openFsDevOp = true;
/* Open gamecard detection event notifier */
result = fsOpenGameCardDetectionEventNotifier(&fsGameCardEventNotifier);
if (R_FAILED(result))
{
uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_ERROR_RGB, "Failed to open gamecard detection event notifier! (0x%08X)", result);
ret = -8;
goto out;
}
openGcEvtNotifier = true;
/* Retrieve gamecard detection event handle */
result = fsEventNotifierGetEventHandle(&fsGameCardEventNotifier, &fsGameCardEventHandle);
if (R_FAILED(result))
{
uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_ERROR_RGB, "Failed to retrieve gamecard detection event handle! (0x%08X)", result);
ret = -9;
goto out;
}
/* Retrieve initial gamecard status */
gameCardInserted = isGameCardInserted();
/* Load gamecard detection kernel event */
eventLoadRemote(&fsGameCardKernelEvent, fsGameCardEventHandle, false);
loadGcKernEvt = true;
/* Create usermode exit event */
ueventCreate(&exitEvent, false);
/* Create gamecard detection thread */
result = threadCreate(&thread, fsGameCardDetectionThreadFunc, NULL, 0x10000, 0x2C, -2);
if (R_FAILED(result))
{
uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_ERROR_RGB, "Failed to create gamecard detection thread! (0x%08X)", result);
ret = -10;
goto out;
}
/* Start gamecard detection thread */
result = threadStart(&thread);
if (R_FAILED(result))
{
uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_ERROR_RGB, "Failed to start gamecard detection thread! (0x%08X)", result);
ret = -11;
goto out;
}
startGcThread = true;
/* Mount BIS System partition from the eMMC */
if (!mountSysEmmcPartition())
{
ret = -12;
ret = -1;
goto out;
}
@ -353,57 +169,9 @@ int main(int argc, char *argv[])
if (exitMainLoop) break;
}
/* Signal the exit event to terminate the gamecard detection thread */
ueventSignal(&exitEvent);
/* Wait for the gamecard detection thread to exit */
threadWaitForExit(&thread);
out:
if (ret < 0)
{
uiRefreshDisplay();
delay(5);
}
/* Unmount BIS System partition from the eMMC */
unmountSysEmmcPartition();
/* Close gamecard detection thread */
if (startGcThread) threadClose(&thread);
/* Close gamecard detection kernel event */
if (loadGcKernEvt) eventClose(&fsGameCardKernelEvent);
/* Close gamecard detection event notifier */
if (openGcEvtNotifier) fsEventNotifierClose(&fsGameCardEventNotifier);
/* Close device operator */
if (openFsDevOp) fsDeviceOperatorClose(&fsOperatorInstance);
/* Denitialize the pm:dmnt service */
if (initPmdmnt) pmdmntExit();
/* Denitialize the spl service */
if (initSpl) splExit();
/* Denitialize the csrng service */
if (initCsrng) csrngExit();
/* Denitialize the ns service */
if (initNs) nsExit();
/* Denitialize the ncm service */
if (initNcm) ncmExit();
/* Disable CPU boost mode */
appletSetCpuBoostMode(ApmCpuBoostMode_Disabled);
/* Free global resources */
freeGlobalData();
/* Deinitialize UI */
uiDeinit();
/* Deinitialize application resources */
deinitApplicationResources();
return ret;
}

File diff suppressed because it is too large Load Diff

View File

@ -13,8 +13,6 @@
#define NCA_SECTION_HEADER_CNT 4
#define NCA_FULL_HEADER_LENGTH (NCA_HEADER_LENGTH + (NCA_SECTION_HEADER_LENGTH * NCA_SECTION_HEADER_CNT))
#define NCA_CONTENT_TYPE_DELTA 0x06
#define NCA_AES_XTS_SECTOR_SIZE 0x200
#define NCA_KEY_AREA_KEY_CNT 4
@ -94,7 +92,7 @@ typedef struct {
u64 file_size;
u32 filename_offset;
u32 reserved;
} PACKED pfs0_entry_table;
} PACKED pfs0_file_entry;
typedef struct {
u32 media_start_offset;
@ -238,7 +236,7 @@ typedef struct {
} PACKED cnmt_header;
typedef struct {
u64 patch_tid; /* Patch TID / Original TID / Application TID */
u64 patch_tid; /* Patch TID / Application TID */
u32 min_sysver; /* Minimum system/application version */
} PACKED cnmt_extended_header;
@ -332,22 +330,24 @@ typedef struct {
} PACKED title_rights_ctx;
typedef struct {
NcmStorageId storageId;
NcmContentStorage ncmStorage;
NcmNcaId ncaId;
NcmContentId ncaId;
Aes128CtrContext aes_ctx;
u64 exefs_offset; // Relative to NCA start
u64 exefs_size;
pfs0_header exefs_header;
u64 exefs_entries_offset; // Relative to NCA start
pfs0_entry_table *exefs_entries;
pfs0_file_entry *exefs_entries;
u64 exefs_str_table_offset; // Relative to NCA start
char *exefs_str_table;
u64 exefs_data_offset; // Relative to NCA start
} PACKED exefs_ctx_t;
typedef struct {
NcmStorageId storageId;
NcmContentStorage ncmStorage;
NcmNcaId ncaId;
NcmContentId ncaId;
Aes128CtrContext aes_ctx;
u64 section_offset; // Relative to NCA start
u64 section_size;
@ -406,8 +406,9 @@ typedef struct {
} PACKED bktr_subsection_block_t;
typedef struct {
NcmStorageId storageId;
NcmContentStorage ncmStorage;
NcmNcaId ncaId;
NcmContentId ncaId;
Aes128CtrContext aes_ctx;
u64 section_offset; // Relative to NCA start
u64 section_size;
@ -428,9 +429,16 @@ typedef struct {
u64 romfs_filedata_offset; // Relative to section start
} PACKED bktr_ctx_t;
// Used in HFS0 / ExeFS / RomFS browsers
typedef struct {
u64 size;
char sizeStr[32];
} PACKED browser_entry_size_info;
typedef struct {
u8 type; // 1 = Dir, 2 = File
u64 offset; // Relative to directory/file table, depending on type
browser_entry_size_info sizeInfo; // Only used if type == 2
} PACKED romfs_browser_entry;
typedef struct {
@ -514,7 +522,9 @@ void convertNcaSizeToU64(const u8 size[0x6], u64 *out);
void convertU64ToNcaSize(const u64 size, u8 out[0x6]);
bool processNcaCtrSectionBlock(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes128CtrContext *ctx, u64 offset, void *outBuf, size_t bufSize, bool encrypt);
bool readNcaDataByContentId(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, u64 offset, void *outBuf, size_t bufSize);
bool processNcaCtrSectionBlock(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, Aes128CtrContext *ctx, u64 offset, void *outBuf, size_t bufSize, bool encrypt);
bool readBktrSectionBlock(u64 offset, void *outBuf, size_t bufSize);
@ -522,22 +532,22 @@ bool encryptNcaHeader(nca_header_t *input, u8 *outBuf, u64 outBufSize);
bool decryptNcaHeader(const u8 *ncaBuf, u64 ncaBufSize, nca_header_t *out, title_rights_ctx *rights_info, u8 *decrypted_nca_keys, bool retrieveTitleKeyData);
bool processProgramNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca_header_t *dec_nca_header, cnmt_xml_content_info *xml_content_info, nca_program_mod_data *output);
bool processProgramNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, cnmt_xml_content_info *xml_content_info, nca_program_mod_data *output);
bool retrieveCnmtNcaData(FsStorageId curStorageId, nspDumpType selectedNspDumpType, u8 *ncaBuf, cnmt_xml_program_info *xml_program_info, cnmt_xml_content_info *xml_content_info, u32 cnmtNcaIndex, nca_cnmt_mod_data *output, title_rights_ctx *rights_info, bool replaceKeyArea);
bool retrieveCnmtNcaData(NcmStorageId curStorageId, nspDumpType selectedNspDumpType, u8 *ncaBuf, cnmt_xml_program_info *xml_program_info, cnmt_xml_content_info *xml_content_info, u32 cnmtNcaIndex, nca_cnmt_mod_data *output, title_rights_ctx *rights_info);
bool patchCnmtNca(u8 *ncaBuf, u64 ncaBufSize, cnmt_xml_program_info *xml_program_info, cnmt_xml_content_info *xml_content_info, nca_cnmt_mod_data *cnmt_mod);
bool readExeFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys);
bool parseExeFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys);
bool readRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys);
bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys);
bool readBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys);
bool parseBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys);
bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys, nca_program_mod_data *program_mod_data, char **outBuf, u64 *outBufSize);
bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys, nca_program_mod_data *program_mod_data, char **outBuf, u64 *outBufSize);
bool retrieveNacpDataFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys, char **out_nacp_xml, u64 *out_nacp_xml_size, nacp_icons_ctx **out_nacp_icons_ctx, u8 *out_nacp_icons_ctx_cnt);
bool retrieveNacpDataFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys, char **out_nacp_xml, u64 *out_nacp_xml_size, nacp_icons_ctx **out_nacp_icons_ctx, u8 *out_nacp_icons_ctx_cnt);
bool retrieveLegalInfoXmlFromNca(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys, char **outBuf, u64 *outBufSize);
bool retrieveLegalInfoXmlFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys, char **outBuf, u64 *outBufSize);
#endif

View File

@ -47,11 +47,11 @@ void freeNsoBinaryData()
nsoBinaryDataSectionSize = 0;
}
bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes128CtrContext *aes_ctx, u64 nso_base_offset, nso_header_t *nsoHeader)
bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, Aes128CtrContext *aes_ctx, u64 nso_base_offset, nso_header_t *nsoHeader)
{
if (!ncmStorage || !ncaId || !aes_ctx || !nso_base_offset || !nsoHeader)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to load .text, .rodata and .data sections from NSO in Program NCA!");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid parameters to load .text, .rodata and .data sections from NSO in Program NCA!", __func__);
return false;
}
@ -92,7 +92,7 @@ bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes
curCompressedSection = malloc(curCompressedSectionSize);
if (!curCompressedSection)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the compressed %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data")));
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: unable to allocate memory for the compressed %s section from NSO in Program NCA!", __func__, (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data")));
success = false;
break;
}
@ -100,7 +100,7 @@ bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes
if (!processNcaCtrSectionBlock(ncmStorage, ncaId, aes_ctx, curCompressedSectionOffset, curCompressedSection, curCompressedSectionSize, false))
{
breaks++;
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Failed to read 0x%016lX bytes %s section from NSO in Program NCA!", curCompressedSectionSize, (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data")));
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: failed to read 0x%016lX bytes %s section from NSO in Program NCA!", __func__, curCompressedSectionSize, (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data")));
free(curCompressedSection);
success = false;
break;
@ -110,7 +110,7 @@ bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes
{
if (curDecompressedSectionSize <= curCompressedSectionSize)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid decompressed size for %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data")));
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid decompressed size for %s section from NSO in Program NCA!", __func__, (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data")));
free(curCompressedSection);
success = false;
break;
@ -120,7 +120,7 @@ bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes
curDecompressedSection = malloc(curDecompressedSectionSize);
if (!curDecompressedSection)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate memory for the decompressed %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data")));
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: unable to allocate memory for the decompressed %s section from NSO in Program NCA!", __func__, (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data")));
free(curCompressedSection);
success = false;
break;
@ -128,7 +128,7 @@ bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes
if (LZ4_decompress_safe((const char*)curCompressedSection, (char*)curDecompressedSection, (int)curCompressedSectionSize, (int)curDecompressedSectionSize) != (int)curDecompressedSectionSize)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to decompress %s section from NSO in Program NCA!", (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data")));
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: unable to decompress %s section from NSO in Program NCA!", __func__, (i == 0 ? ".text" : (i == 1 ? ".rodata" : ".data")));
free(curDecompressedSection);
free(curCompressedSection);
success = false;
@ -225,7 +225,7 @@ bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes
nsoBinaryDataSectionOffset = (u64)nsoHeader->data_segment_header.memory_offset;
nsoBinaryDataSectionSize = nsoDataSectionSize;
} else {
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to allocate %lu bytes for full decompressed NSO in Program NCA!", nsoBinaryDataSize);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: unable to allocate %lu bytes for full decompressed NSO in Program NCA!", __func__, nsoBinaryDataSize);
nsoBinaryDataSize = 0;
success = false;
}
@ -238,11 +238,11 @@ bool loadNsoBinaryData(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes
return success;
}
bool retrieveMiddlewareListFromNso(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes128CtrContext *aes_ctx, const char *nso_filename, u64 nso_base_offset, nso_header_t *nsoHeader, char *programInfoXml)
bool retrieveMiddlewareListFromNso(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, Aes128CtrContext *aes_ctx, const char *nso_filename, u64 nso_base_offset, nso_header_t *nsoHeader, char *programInfoXml)
{
if (!ncmStorage || !ncaId || !aes_ctx || !nso_filename || !strlen(nso_filename) || !nso_base_offset || !nsoHeader || !programInfoXml)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to retrieve middleware list from NSO in Program NCA!");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid parameters to retrieve middleware list from NSO in Program NCA!", __func__);
return false;
}
@ -289,11 +289,11 @@ bool retrieveMiddlewareListFromNso(NcmContentStorage *ncmStorage, const NcmNcaId
return true;
}
bool retrieveSymbolsListFromNso(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes128CtrContext *aes_ctx, const char *nso_filename, u64 nso_base_offset, nso_header_t *nsoHeader, char *programInfoXml)
bool retrieveSymbolsListFromNso(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, Aes128CtrContext *aes_ctx, const char *nso_filename, u64 nso_base_offset, nso_header_t *nsoHeader, char *programInfoXml)
{
if (!ncmStorage || !ncaId || !aes_ctx || !nso_filename || !strlen(nso_filename) || !nso_base_offset || !nsoHeader || !programInfoXml)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid parameters to retrieve symbols list from NSO in Program NCA!");
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid parameters to retrieve symbols list from NSO in Program NCA!", __func__);
return false;
}
@ -333,7 +333,7 @@ bool retrieveSymbolsListFromNso(NcmContentStorage *ncmStorage, const NcmNcaId *n
if (bswap_32(mod_magic) != MOD_MAGIC)
{
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: invalid MOD0 magic word in decompressed NSO from Program NCA! (0x%08X)", bswap_32(mod_magic));
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: invalid MOD0 magic word in decompressed NSO from Program NCA! (0x%08X)", __func__, bswap_32(mod_magic));
goto out;
}

View File

@ -50,9 +50,9 @@ typedef struct {
} PACKED nso_header_t;
// Retrieves the middleware list from a NSO stored in a partition from a NCA file
bool retrieveMiddlewareListFromNso(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes128CtrContext *aes_ctx, const char *nso_filename, u64 nso_base_offset, nso_header_t *nsoHeader, char *programInfoXml);
bool retrieveMiddlewareListFromNso(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, Aes128CtrContext *aes_ctx, const char *nso_filename, u64 nso_base_offset, nso_header_t *nsoHeader, char *programInfoXml);
// Retrieves the symbols list from a NSO stored in a partition from a NCA file
bool retrieveSymbolsListFromNso(NcmContentStorage *ncmStorage, const NcmNcaId *ncaId, Aes128CtrContext *aes_ctx, const char *nso_filename, u64 nso_base_offset, nso_header_t *nsoHeader, char *programInfoXml);
bool retrieveSymbolsListFromNso(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, Aes128CtrContext *aes_ctx, const char *nso_filename, u64 nso_base_offset, nso_header_t *nsoHeader, char *programInfoXml);
#endif

View File

@ -56,13 +56,13 @@ bool rsa_sign(void* input, size_t input_size, unsigned char* output, size_t outp
memcpy(output, buf, output_size);
success = true;
} else {
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "rsa_sign: mbedtls_pk_sign failed! (%d)", ret);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: mbedtls_pk_sign failed! (%d)", __func__, ret);
}
} else {
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "rsa_sign: mbedtls_pk_parse_key failed! (%d)", ret);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: mbedtls_pk_parse_key failed! (%d)", __func__, ret);
}
} else {
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "rsa_sign: mbedtls_ctr_drbg_seed failed! (%d)", ret);
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "%s: mbedtls_ctr_drbg_seed failed! (%d)", __func__, ret);
}
mbedtls_ctr_drbg_free(&ctr_drbg);

File diff suppressed because it is too large Load Diff

52
source/service_guard.h Normal file
View File

@ -0,0 +1,52 @@
#pragma once
#include <switch.h>
typedef struct ServiceGuard {
Mutex mutex;
u32 refCount;
} ServiceGuard;
NX_INLINE bool serviceGuardBeginInit(ServiceGuard* g)
{
mutexLock(&g->mutex);
return (g->refCount++) == 0;
}
NX_INLINE Result serviceGuardEndInit(ServiceGuard* g, Result rc, void (*cleanupFunc)(void))
{
if (R_FAILED(rc)) {
cleanupFunc();
--g->refCount;
}
mutexUnlock(&g->mutex);
return rc;
}
NX_INLINE void serviceGuardExit(ServiceGuard* g, void (*cleanupFunc)(void))
{
mutexLock(&g->mutex);
if (g->refCount && (--g->refCount) == 0)
cleanupFunc();
mutexUnlock(&g->mutex);
}
#define NX_GENERATE_SERVICE_GUARD_PARAMS(name, _paramdecl, _parampass) \
\
static ServiceGuard g_##name##Guard; \
NX_INLINE Result _##name##Initialize _paramdecl; \
static void _##name##Cleanup(void); \
\
Result name##Initialize _paramdecl \
{ \
Result rc = 0; \
if (serviceGuardBeginInit(&g_##name##Guard)) \
rc = _##name##Initialize _parampass; \
return serviceGuardEndInit(&g_##name##Guard, rc, _##name##Cleanup); \
} \
\
void name##Exit(void) \
{ \
serviceGuardExit(&g_##name##Guard, _##name##Cleanup); \
}
#define NX_GENERATE_SERVICE_GUARD(name) NX_GENERATE_SERVICE_GUARD_PARAMS(name, (void), ())

View File

@ -5,54 +5,24 @@
#include <string.h>
#include "set_ext.h"
#include "service_guard.h"
static Service g_setcalSrv;
static u64 g_refCntCal;
Result setcalInitialize(void)
{
atomicIncrement64(&g_refCntCal);
if (serviceIsActive(&g_setcalSrv)) return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized);
NX_GENERATE_SERVICE_GUARD(setcal);
Result _setcalInitialize() {
return smGetService(&g_setcalSrv, "set:cal");
}
void setcalExit(void)
{
if (atomicDecrement64(&g_refCntCal) == 0) serviceClose(&g_setcalSrv);
void _setcalCleanup() {
serviceClose(&g_setcalSrv);
}
Result setcalGetEticketDeviceKey(void *key)
{
IpcCommand c;
ipcInitialize(&c);
ipcAddRecvBuffer(&c, key, 0x244, 0);
struct {
u64 magic;
u64 cmd_id;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 21;
Result rc = serviceIpcDispatch(&g_setcalSrv);
if (R_SUCCEEDED(rc))
{
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
} *resp = r.Raw;
rc = resp->result;
}
return rc;
return serviceDispatch(&g_setcalSrv, 21,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { key, 0x244 } },
);
}

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,7 @@
#define ROMFS_MAX_ELEMENTS 12
#define SDCARD_MAX_ELEMENTS 3
#define ORPHAN_MAX_ELEMENTS 12
#define BATCH_MAX_ELEMENTS 12
#define BATCH_MAX_ELEMENTS 14
#define OPTIONS_X_START_POS (35 * CHAR_PT_SIZE)
#define OPTIONS_X_END_POS (OPTIONS_X_START_POS + (6 * CHAR_PT_SIZE))
@ -185,17 +185,17 @@ void uiStatusMsg(const char *fmt, ...);
void uiUpdateStatusMsg();
void uiPleaseWait(u8 wait);
void uiClearStatusMsg();
void uiUpdateFreeSpace();
void uiPleaseWait(u8 wait);
void uiClearScreen();
void uiPrintHeadline();
void uiDeinit();
bool uiInit();
int uiInit();
void uiDeinit();
void uiSetState(UIState state);

File diff suppressed because it is too large Load Diff

View File

@ -19,24 +19,24 @@
#define KEYS_FILE_PATH APP_BASE_PATH "prod.keys"
#define CFW_PATH_ATMOSPHERE "sdmc:/atmosphere/contents/"
#define CFW_PATH_SXOS "sdmc:/sxos/titles/"
#define CFW_PATH_REINX "sdmc:/ReiNX/titles/"
#define KiB (1024.0)
#define MiB (1024.0 * KiB)
#define GiB (1024.0 * MiB)
#define NAME_BUF_LEN 4096
#define SOCK_BUFFERSIZE 65536
#define DUMP_BUFFER_SIZE (u64)0x400000 // 4 MiB (4194304 bytes)
#define DUMP_BUFFER_SIZE (u64)0x100000 // 1 MiB (1048576 bytes)
#define GAMECARD_READ_BUFFER_SIZE DUMP_BUFFER_SIZE // 4 MiB (4194304 bytes)
#define NCA_CTR_BUFFER_SIZE DUMP_BUFFER_SIZE // 1 MiB (1048576 bytes)
#define NCA_CTR_BUFFER_SIZE DUMP_BUFFER_SIZE // 4 MiB (4194304 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
#define APPLICATION_PATCH_BITMASK (u64)0x800
#define APPLICATION_ADDON_BITMASK (u64)0xFFFFFFFFFFFF0000
@ -48,32 +48,13 @@
#define NACP_AUTHOR_LEN 0x100
#define VERSION_STR_LEN 0x40
#define GAMECARD_WAIT_TIME 3 // 3 seconds
#define GAMECARD_HEADER_SIZE 0x200
#define GAMECARD_SIZE_ADDR 0x10D
#define GAMECARD_DATAEND_ADDR 0x118
#define GAMECARD_ECC_BLOCK_SIZE (u64)0x200 // 512 bytes
#define GAMECARD_ECC_DATA_SIZE (u64)0x24 // 36 bytes
#define HFS0_OFFSET_ADDR 0x130
#define HFS0_SIZE_ADDR 0x138
#define HFS0_MAGIC (u32)0x48465330 // "HFS0"
#define HFS0_FILE_COUNT_ADDR 0x04
#define HFS0_STR_TABLE_SIZE_ADDR 0x08
#define HFS0_ENTRY_TABLE_ADDR 0x10
#define MEDIA_UNIT_SIZE 0x200
#define GAMECARD_TYPE1_PARTITION_CNT 3 // "update" (0), "normal" (1), "update" (2)
#define GAMECARD_TYPE2_PARTITION_CNT 4 // "update" (0), "logo" (1), "normal" (2), "update" (3)
#define GAMECARD_TYPE(x) ((x) == GAMECARD_TYPE1_PARTITION_CNT ? "Type 0x01" : ((x) == GAMECARD_TYPE2_PARTITION_CNT ? "Type 0x02" : "Unknown"))
#define GAMECARD_TYPE1_PART_NAMES(x) ((x) == 0 ? "Update" : ((x) == 1 ? "Normal" : ((x) == 2 ? "Secure" : "Unknown")))
#define GAMECARD_TYPE2_PART_NAMES(x) ((x) == 0 ? "Update" : ((x) == 1 ? "Logo" : ((x) == 2 ? "Normal" : ((x) == 3 ? "Secure" : "Unknown"))))
#define GAMECARD_PARTITION_NAME(x, y) ((x) == GAMECARD_TYPE1_PARTITION_CNT ? GAMECARD_TYPE1_PART_NAMES(y) : ((x) == GAMECARD_TYPE2_PARTITION_CNT ? GAMECARD_TYPE2_PART_NAMES(y) : "Unknown"))
#define ISTORAGE_PARTITION_CNT 2
#define HFS0_TO_ISTORAGE_IDX(x, y) ((x) == GAMECARD_TYPE1_PARTITION_CNT ? ((y) < 2 ? 0 : 1) : ((y) < 3 ? 0 : 1))
#define GAMECARD_WAIT_TIME 3 // 3 seconds
#define GAMECARD_HEADER_MAGIC (u32)0x48454144 // "HEAD"
#define GAMECARD_SIZE_1GiB (u64)0x40000000
#define GAMECARD_SIZE_2GiB (u64)0x80000000
@ -82,9 +63,22 @@
#define GAMECARD_SIZE_16GiB (u64)0x400000000
#define GAMECARD_SIZE_32GiB (u64)0x800000000
/* Reference: https://switchbrew.org/wiki/Title_list */
#define GAMECARD_UPDATE_TITLEID (u64)0x0100000000000816
#define GAMECARD_ECC_BLOCK_SIZE (u64)0x200 // 512 bytes
#define GAMECARD_ECC_DATA_SIZE (u64)0x24 // 36 bytes
#define GAMECARD_TYPE1_PARTITION_CNT 3 // "update" (0), "normal" (1), "secure" (2)
#define GAMECARD_TYPE2_PARTITION_CNT 4 // "update" (0), "logo" (1), "normal" (2), "secure" (3)
#define GAMECARD_TYPE(x) ((x) == GAMECARD_TYPE1_PARTITION_CNT ? "Type 0x01" : ((x) == GAMECARD_TYPE2_PARTITION_CNT ? "Type 0x02" : "Unknown"))
#define GAMECARD_TYPE1_PART_NAMES(x) ((x) == 0 ? "Update" : ((x) == 1 ? "Normal" : ((x) == 2 ? "Secure" : "Unknown")))
#define GAMECARD_TYPE2_PART_NAMES(x) ((x) == 0 ? "Update" : ((x) == 1 ? "Logo" : ((x) == 2 ? "Normal" : ((x) == 3 ? "Secure" : "Unknown"))))
#define GAMECARD_PARTITION_NAME(x, y) ((x) == GAMECARD_TYPE1_PARTITION_CNT ? GAMECARD_TYPE1_PART_NAMES(y) : ((x) == GAMECARD_TYPE2_PARTITION_CNT ? GAMECARD_TYPE2_PART_NAMES(y) : "Unknown"))
#define HFS0_MAGIC (u32)0x48465330 // "HFS0"
#define HFS0_TO_ISTORAGE_IDX(x, y) ((x) == GAMECARD_TYPE1_PARTITION_CNT ? ((y) < (GAMECARD_TYPE1_PARTITION_CNT - 1) ? 0 : 1) : ((y) < (GAMECARD_TYPE2_PARTITION_CNT - 1) ? 0 : 1))
#define NACP_ICON_SQUARE_DIMENSION 256
#define NACP_ICON_DOWNSCALED 96
@ -95,12 +89,117 @@
#define ORPHAN_ENTRY_TYPE_ADDON 2
#define MAX_ELEMENTS(x) ((sizeof((x))) / (sizeof((x)[0]))) // Returns the max number of elements that can be stored in an array
#define MAX_CHARACTERS(x) (MAX_ELEMENTS((x)) - 1) // Returns the max number of characters that can be stored in char array while also leaving space for a NULL terminator
#define BIS_MOUNT_NAME "sys:"
#define BIS_CERT_SAVE_NAME BIS_MOUNT_NAME "/save/80000000000000e0"
#define BIS_COMMON_TIK_SAVE_NAME BIS_MOUNT_NAME "/save/80000000000000e1"
#define BIS_PERSONALIZED_TIK_SAVE_NAME BIS_MOUNT_NAME "/save/80000000000000e2"
#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
typedef struct {
u8 signature[0x100];
u32 magic;
u32 secureAreaStartAddr;
u32 backupAreaStartAddr;
u8 titleKeyIndex;
u8 size;
u8 headerVersion;
u8 flags;
u64 packageId;
u64 validDataEndAddr;
u8 iv[0x10];
u64 rootHfs0HeaderOffset;
u64 rootHfs0HeaderSize;
u8 rootHfs0HeaderHash[SHA256_HASH_SIZE];
u8 initialDataHash[SHA256_HASH_SIZE];
u32 securityMode;
u32 t1KeyIndex;
u32 keyIndex;
u32 normalAreaEndAddr;
u8 encryptedInfoBlock[0x70];
} PACKED gamecard_header_t;
typedef struct {
u64 offset;
u64 size;
u8 *header;
u64 header_size;
u32 file_cnt;
u32 str_table_size;
} PACKED hfs0_partition_info;
typedef enum {
ISTORAGE_PARTITION_NONE = 0,
ISTORAGE_PARTITION_NORMAL,
ISTORAGE_PARTITION_SECURE,
ISTORAGE_PARTITION_INVALID
} openIStoragePartition;
typedef struct {
FsDeviceOperator fsOperatorInstance;
FsEventNotifier fsGameCardEventNotifier;
Event fsGameCardKernelEvent;
FsGameCardHandle fsGameCardHandle;
FsStorage fsGameCardStorage;
openIStoragePartition curIStorageIndex;
volatile bool isInserted;
gamecard_header_t header;
u8 *rootHfs0Header;
u32 hfs0PartitionCnt;
hfs0_partition_info *hfs0Partitions;
u64 size;
char sizeStr[32];
u64 trimmedSize;
char trimmedSizeStr[32];
u64 IStoragePartitionSizes[ISTORAGE_PARTITION_CNT];
u64 updateTitleId;
u32 updateVersion;
char updateVersionStr[64];
} PACKED gamecard_ctx_t;
typedef struct {
u64 titleId;
u32 version;
u32 ncmIndex;
NcmStorageId storageId;
char name[NACP_APPNAME_LEN];
char fixedName[NACP_APPNAME_LEN];
char author[NACP_AUTHOR_LEN];
char versionStr[VERSION_STR_LEN];
u8 *icon;
u64 contentSize;
char contentSizeStr[32];
} PACKED base_app_ctx_t;
typedef struct {
u64 titleId;
u32 version;
u32 ncmIndex;
NcmStorageId storageId;
char versionStr[VERSION_STR_LEN];
u64 contentSize;
char contentSizeStr[32];
} PACKED patch_addon_ctx_t;
typedef struct {
u32 index;
u8 type; // 1 = Patch, 2 = AddOn
char name[NACP_APPNAME_LEN];
char fixedName[NACP_APPNAME_LEN];
char orphanListStr[NACP_APPNAME_LEN * 2];
} PACKED orphan_patch_addon_entry;
typedef struct {
u32 magic;
u32 file_cnt;
u32 str_table_size;
u32 reserved;
} PACKED hfs0_header;
typedef struct {
u64 file_offset;
u64 file_size;
@ -108,7 +207,7 @@ typedef struct {
u32 hashed_region_size;
u64 reserved;
u8 hashed_region_sha256[0x20];
} PACKED hfs0_entry_table;
} PACKED hfs0_file_entry;
typedef struct {
int line_offset;
@ -142,27 +241,24 @@ typedef enum {
TICKET_TYPE_ADDON
} selectedTicketType;
typedef struct {
u32 index;
u8 type; // 1 = Patch, 2 = AddOn
char name[NACP_APPNAME_LEN + 1];
char fixedName[NACP_APPNAME_LEN + 1];
} PACKED orphan_patch_addon_entry;
typedef struct {
bool isFat32;
bool setXciArchiveBit;
bool keepCert;
bool trimDump;
bool calcCrc;
bool useNoIntroLookup;
bool useBrackets;
} PACKED xciOptions;
typedef struct {
bool isFat32;
bool calcCrc;
bool useNoIntroLookup;
bool removeConsoleData;
bool tiklessDump;
bool npdmAcidRsaPatch;
bool dumpDeltaFragments;
bool useBrackets;
} PACKED nspOptions;
typedef enum {
@ -180,8 +276,11 @@ typedef struct {
bool removeConsoleData;
bool tiklessDump;
bool npdmAcidRsaPatch;
bool dumpDeltaFragments;
bool skipDumpedTitles;
bool rememberDumpedTitles;
bool haltOnErrors;
bool useBrackets;
batchModeSourceStorage batchModeSrc;
} PACKED batchOptions;
@ -189,68 +288,68 @@ typedef struct {
bool removeConsoleData;
} PACKED ticketOptions;
typedef struct {
bool isFat32;
bool useLayeredFSDir;
} PACKED ncaFsOptions;
typedef struct {
xciOptions xciDumpCfg;
nspOptions nspDumpCfg;
batchOptions batchDumpCfg;
ticketOptions tikDumpCfg;
ncaFsOptions exeFsDumpCfg;
ncaFsOptions romFsDumpCfg;
} PACKED dumpOptions;
void loadConfig();
void saveConfig();
bool isGameCardInserted();
void fsGameCardDetectionThreadFunc(void *arg);
bool mountSysEmmcPartition();
void unmountSysEmmcPartition();
bool isServiceRunning(const char *serviceName);
bool checkSxOsServices();
void closeGameCardStoragePartition();
Result openGameCardStoragePartition(openIStoragePartition partitionIndex);
Result readGameCardStoragePartition(u64 off, void *buf, size_t len);
void delay(u8 seconds);
void formatETAString(u64 curTime, char *output, u32 outSize);
void convertSize(u64 size, char *out, size_t outSize);
void updateFreeSpace();
void initExeFsContext();
void freeExeFsContext();
void initRomFsContext();
void freeRomFsContext();
void initBktrContext();
void freeBktrContext();
void freeGlobalData();
void freeRomFsBrowserEntries();
void freeHfs0ExeFsEntriesSizes();
bool loadTitlesFromSdCardAndEmmc(u8 titleType);
void consoleErrorScreen(const char *fmt, ...);
bool initApplicationResources(int argc, char **argv);
void deinitApplicationResources();
void freeTitlesFromSdCardAndEmmc(u8 titleType);
void formatETAString(u64 curTime, char *out, size_t outSize);
void convertTitleVersionToDecimal(u32 version, char *versionBuf, size_t versionBufSize);
void addStringToFilenameBuffer(const char *string);
void generateSdCardEmmcTitleList();
bool loadTitlesFromSdCardAndEmmc(NcmContentMetaType metaType);
void freeTitlesFromSdCardAndEmmc(NcmContentMetaType metaType);
void removeIllegalCharacters(char *name);
void createOutputDirectories();
void strtrim(char *str);
void loadTitleInfo();
bool getHfs0EntryDetails(u8 *hfs0Header, u64 hfs0HeaderOffset, u64 hfs0HeaderSize, u32 num_entries, u32 entry_idx, bool isRoot, u32 partitionIndex, u64 *out_offset, u64 *out_size);
bool getPartitionHfs0Header(u32 partition);
void truncateBrowserEntryName(char *str);
bool getHfs0FileList(u32 partition);
bool getFileFromHfs0PartitionByName(FsStorage *gameCardStorage, const char *filename, u8 *outBuf, u64 outBufSize);
bool readFileFromSecureHfs0PartitionByName(const char *filename, u64 offset, void *outBuf, size_t bufSize);
bool calculateExeFsExtractedDataSize(u64 *out);
@ -258,7 +357,7 @@ bool calculateRomFsFullExtractedSize(bool usePatch, u64 *out);
bool calculateRomFsExtractedDirSize(u32 dir_offset, bool usePatch, u64 *out);
bool retrieveNcaContentRecords(FsStorageId curStorageId, u8 filter, u32 titleCount, u32 titleIndex, NcmContentRecord **outContentRecords, u32 *outContentRecordsCnt);
bool retrieveContentInfosFromTitle(NcmStorageId storageId, NcmContentMetaType metaType, u32 titleCount, u32 titleIndex, NcmContentInfo **outContentInfos, u32 *outContentInfoCnt);
void removeConsoleDataFromTicket(title_rights_ctx *rights_info);
@ -270,21 +369,13 @@ bool getExeFsFileList();
bool getRomFsFileList(u32 dir_offset, bool usePatch);
int getSdCardFreeSpace(u64 *out);
char *generateGameCardDumpName(bool useBrackets);
void convertSize(u64 size, char *out, int bufsize);
char *generateNSPDumpName(nspDumpType selectedNspDumpType, u32 titleIndex, bool useBrackets);
void addStringToFilenameBuffer(const char *string, char **nextFilename);
void retrieveDescriptionForPatchOrAddOn(u32 titleIndex, bool addOn, bool addAppName, const char *prefix, char *outBuf, size_t outBufSize);
char *generateFullDumpName();
char *generateNSPDumpName(nspDumpType selectedNspDumpType, u32 titleIndex);
void retrieveDescriptionForPatchOrAddOn(u64 titleID, u32 version, bool addOn, bool addAppName, const char *prefix, char *outBuf, size_t outBufSize);
bool checkOrphanPatchOrAddOn(bool addOn);
void freeOrphanPatchOrAddOnList();
u32 calculateOrphanPatchOrAddOnCount(bool addOn);
void generateOrphanPatchOrAddOnList();
@ -322,6 +413,8 @@ void removeDirectoryWithVerbose(const char *path, const char *msg);
void gameCardDumpNSWDBCheck(u32 crc);
void noIntroDumpCheck(bool isDigital, u32 crc);
void updateNSWDBXml();
bool updateApplication();