1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-12-03 15:32:05 +00:00

Merging attempt; do not run

This commit is contained in:
bilditup1 2023-12-04 18:57:10 -05:00
commit 62fd26b622
15 changed files with 329 additions and 155 deletions

7
.gitignore vendored
View file

@ -1,9 +1,10 @@
.vscode .vscode
build build
host/__pycache__ host/__pycache__
host/installer host/standalone
host/build host/nxdt_host.build
host/dist host/nxdt_host.dist
host/nxdt_host.onefile-build
host/nxdumptool host/nxdumptool
*.elf *.elf
*.nacp *.nacp

View file

@ -43,6 +43,7 @@ typedef bool (*MenuElementFunction)(void *userdata);
typedef struct { typedef struct {
u32 selected; ///< Used to keep track of the selected option. u32 selected; ///< Used to keep track of the selected option.
bool retrieved; ///< Used to determine if the value for this option has already been retrieved from configuration.
MenuElementOptionGetterFunction getter_func; ///< Pointer to a function to be called the first time an option value is loaded. Should be set to NULL if not used. MenuElementOptionGetterFunction getter_func; ///< Pointer to a function to be called the first time an option value is loaded. Should be set to NULL if not used.
MenuElementOptionSetterFunction setter_func; ///< Pointer to a function to be called each time a new option value is selected. Should be set to NULL if not used. MenuElementOptionSetterFunction setter_func; ///< Pointer to a function to be called each time a new option value is selected. Should be set to NULL if not used.
char **options; ///< Pointer to multiple char pointers with strings representing options. Last element must be set to NULL. char **options; ///< Pointer to multiple char pointers with strings representing options. Last element must be set to NULL.
@ -147,6 +148,7 @@ static void consolePrintReversedColors(const char *text, ...);
static void consoleRefresh(void); static void consoleRefresh(void);
static u32 menuGetElementCount(const Menu *menu); static u32 menuGetElementCount(const Menu *menu);
static void menuResetAttributes(Menu *cur_menu, u32 element_count);
void freeStorageList(void); void freeStorageList(void);
void updateStorageList(void); void updateStorageList(void);
@ -177,6 +179,8 @@ static char *generateOutputLayeredFsFileName(u64 title_id, const char *subdir, c
static bool dumpGameCardSecurityInformation(GameCardSecurityInformation *out); static bool dumpGameCardSecurityInformation(GameCardSecurityInformation *out);
static bool resetSettings(void *userdata);
static bool saveGameCardImage(void *userdata); static bool saveGameCardImage(void *userdata);
static bool saveGameCardHeader(void *userdata); static bool saveGameCardHeader(void *userdata);
static bool saveGameCardCardInfo(void *userdata); static bool saveGameCardCardInfo(void *userdata);
@ -289,6 +293,7 @@ static char **g_storageOptions = NULL;
static MenuElementOption g_storageMenuElementOption = { static MenuElementOption g_storageMenuElementOption = {
.selected = 0, .selected = 0,
.retrieved = false,
.getter_func = &getOutputStorageOption, .getter_func = &getOutputStorageOption,
.setter_func = &setOutputStorageOption, .setter_func = &setOutputStorageOption,
.options = NULL // Dynamically set .options = NULL // Dynamically set
@ -316,6 +321,7 @@ static MenuElement *g_xciMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 0, .selected = 0,
.retrieved = false,
.getter_func = &getGameCardPrependKeyAreaOption, .getter_func = &getGameCardPrependKeyAreaOption,
.setter_func = &setGameCardPrependKeyAreaOption, .setter_func = &setGameCardPrependKeyAreaOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -328,6 +334,7 @@ static MenuElement *g_xciMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 0, .selected = 0,
.retrieved = false,
.getter_func = &getGameCardKeepCertificateOption, .getter_func = &getGameCardKeepCertificateOption,
.setter_func = &setGameCardKeepCertificateOption, .setter_func = &setGameCardKeepCertificateOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -340,6 +347,7 @@ static MenuElement *g_xciMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 0, .selected = 0,
.retrieved = false,
.getter_func = &getGameCardTrimDumpOption, .getter_func = &getGameCardTrimDumpOption,
.setter_func = &setGameCardTrimDumpOption, .setter_func = &setGameCardTrimDumpOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -352,6 +360,7 @@ static MenuElement *g_xciMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 1, .selected = 1,
.retrieved = false,
.getter_func = &getGameCardCalculateChecksumOption, .getter_func = &getGameCardCalculateChecksumOption,
.setter_func = &setGameCardCalculateChecksumOption, .setter_func = &setGameCardCalculateChecksumOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -410,6 +419,7 @@ static MenuElement *g_gameCardHfsMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 0, .selected = 0,
.retrieved = false,
.getter_func = &getGameCardWriteRawHfsPartitionOption, .getter_func = &getGameCardWriteRawHfsPartitionOption,
.setter_func = &setGameCardWriteRawHfsPartitionOption, .setter_func = &setGameCardWriteRawHfsPartitionOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -514,6 +524,7 @@ static MenuElement *g_nspMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 0, .selected = 0,
.retrieved = false,
.getter_func = &getNspSetDownloadDistributionOption, .getter_func = &getNspSetDownloadDistributionOption,
.setter_func = &setNspSetDownloadDistributionOption, .setter_func = &setNspSetDownloadDistributionOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -526,6 +537,7 @@ static MenuElement *g_nspMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 0, .selected = 0,
.retrieved = false,
.getter_func = &getNspRemoveConsoleDataOption, .getter_func = &getNspRemoveConsoleDataOption,
.setter_func = &setNspRemoveConsoleDataOption, .setter_func = &setNspRemoveConsoleDataOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -538,6 +550,7 @@ static MenuElement *g_nspMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 0, .selected = 0,
.retrieved = false,
.getter_func = &getNspRemoveTitlekeyCryptoOption, .getter_func = &getNspRemoveTitlekeyCryptoOption,
.setter_func = &setNspRemoveTitlekeyCryptoOption, .setter_func = &setNspRemoveTitlekeyCryptoOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -550,6 +563,7 @@ static MenuElement *g_nspMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 1, .selected = 1,
.retrieved = false,
.getter_func = &getNspDisableLinkedAccountRequirementOption, .getter_func = &getNspDisableLinkedAccountRequirementOption,
.setter_func = &setNspDisableLinkedAccountRequirementOption, .setter_func = &setNspDisableLinkedAccountRequirementOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -562,6 +576,7 @@ static MenuElement *g_nspMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 1, .selected = 1,
.retrieved = false,
.getter_func = &getNspEnableScreenshotsOption, .getter_func = &getNspEnableScreenshotsOption,
.setter_func = &setNspEnableScreenshotsOption, .setter_func = &setNspEnableScreenshotsOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -574,6 +589,7 @@ static MenuElement *g_nspMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 1, .selected = 1,
.retrieved = false,
.getter_func = &getNspEnableVideoCaptureOption, .getter_func = &getNspEnableVideoCaptureOption,
.setter_func = &setNspEnableVideoCaptureOption, .setter_func = &setNspEnableVideoCaptureOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -586,6 +602,7 @@ static MenuElement *g_nspMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 1, .selected = 1,
.retrieved = false,
.getter_func = &getNspDisableHdcpOption, .getter_func = &getNspDisableHdcpOption,
.setter_func = &setNspDisableHdcpOption, .setter_func = &setNspDisableHdcpOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -598,6 +615,7 @@ static MenuElement *g_nspMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 1, .selected = 1,
.retrieved = false,
.getter_func = &getNspGenerateAuthoringToolDataOption, .getter_func = &getNspGenerateAuthoringToolDataOption,
.setter_func = &setNspGenerateAuthoringToolDataOption, .setter_func = &setNspGenerateAuthoringToolDataOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -630,6 +648,7 @@ static MenuElement *g_ticketMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 0, .selected = 0,
.retrieved = false,
.getter_func = &getTicketRemoveConsoleDataOption, .getter_func = &getTicketRemoveConsoleDataOption,
.setter_func = &setTicketRemoveConsoleDataOption, .setter_func = &setTicketRemoveConsoleDataOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -653,6 +672,7 @@ static char **g_ncaBasePatchOptions = NULL;
static MenuElementOption g_ncaFsSectionsSubMenuBasePatchElementOption = { static MenuElementOption g_ncaFsSectionsSubMenuBasePatchElementOption = {
.selected = 0, .selected = 0,
.retrieved = false,
.getter_func = NULL, .getter_func = NULL,
.setter_func = NULL, .setter_func = NULL,
.options = NULL // Dynamically set .options = NULL // Dynamically set
@ -679,6 +699,7 @@ static MenuElement *g_ncaFsSectionsSubMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 0, .selected = 0,
.retrieved = false,
.getter_func = &getNcaFsWriteRawSectionOption, .getter_func = &getNcaFsWriteRawSectionOption,
.setter_func = &setNcaFsWriteRawSectionOption, .setter_func = &setNcaFsWriteRawSectionOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -691,6 +712,7 @@ static MenuElement *g_ncaFsSectionsSubMenuElements[] = {
.task_func = NULL, .task_func = NULL,
.element_options = &(MenuElementOption){ .element_options = &(MenuElementOption){
.selected = 0, .selected = 0,
.retrieved = false,
.getter_func = &getNcaFsUseLayeredFsDirOption, .getter_func = &getNcaFsUseLayeredFsDirOption,
.setter_func = &setNcaFsUseLayeredFsDirOption, .setter_func = &setNcaFsUseLayeredFsDirOption,
.options = g_noYesStrings .options = g_noYesStrings
@ -869,6 +891,13 @@ static MenuElement *g_rootMenuElements[] = {
.element_options = NULL, .element_options = NULL,
.userdata = NULL .userdata = NULL
}, },
&(MenuElement){
.str = "reset settings",
.child_menu = NULL,
.task_func = &resetSettings,
.element_options = NULL,
.userdata = NULL
},
NULL NULL
}; };
@ -941,6 +970,7 @@ int main(int argc, char *argv[])
consolePrint("______________________________\n\n"); consolePrint("______________________________\n\n");
if (cur_menu->parent) consolePrint("press b to go back\n"); if (cur_menu->parent) consolePrint("press b to go back\n");
if (g_umsDeviceCount) consolePrint("press x to safely remove all ums devices\n"); if (g_umsDeviceCount) consolePrint("press x to safely remove all ums devices\n");
consolePrint("use the sticks to scroll faster\n");
consolePrint("press + to exit\n"); consolePrint("press + to exit\n");
consolePrint("______________________________\n\n"); consolePrint("______________________________\n\n");
@ -1032,10 +1062,10 @@ int main(int argc, char *argv[])
if (cur_options) if (cur_options)
{ {
if (cur_options->getter_func) if (cur_options->getter_func && !cur_options->retrieved)
{ {
cur_options->selected = cur_options->getter_func(); cur_options->selected = cur_options->getter_func();
cur_options->getter_func = NULL; cur_options->retrieved = true;
} }
consolePrint(": "); consolePrint(": ");
@ -1179,7 +1209,6 @@ int main(int argc, char *argv[])
if (!error) if (!error)
{ {
child_menu->parent = cur_menu; child_menu->parent = cur_menu;
child_menu->selected = child_menu->scroll = 0;
cur_menu = child_menu; cur_menu = child_menu;
element_count = menuGetElementCount(cur_menu); element_count = menuGetElementCount(cur_menu);
@ -1200,23 +1229,29 @@ int main(int argc, char *argv[])
break; break;
} }
/* Wait for USB session (if needed). */ if (cur_menu->id > MenuId_Root)
if (useUsbHost() && !waitForUsb())
{ {
if (g_appletStatus) continue; /* Wait for USB session (if needed). */
break; if (useUsbHost() && !waitForUsb())
{
if (g_appletStatus) continue;
break;
}
/* Run task. */
utilsSetLongRunningProcessState(true);
if (selected_element->task_func(selected_element->userdata))
{
if (!useUsbHost()) updateStorageList(); // update free space
}
utilsSetLongRunningProcessState(false);
} else {
/* Ignore result. */
selected_element->task_func(selected_element->userdata);
} }
/* Run task. */
utilsSetLongRunningProcessState(true);
if (selected_element->task_func(selected_element->userdata))
{
if (!useUsbHost()) updateStorageList(); // update free space
}
utilsSetLongRunningProcessState(false);
/* Display prompt. */ /* Display prompt. */
consolePrint("press any button to continue"); consolePrint("press any button to continue");
utilsWaitForButtonPress(0); utilsWaitForButtonPress(0);
@ -1302,6 +1337,8 @@ int main(int argc, char *argv[])
} else } else
if ((btn_down & HidNpadButton_B) && cur_menu->parent) if ((btn_down & HidNpadButton_B) && cur_menu->parent)
{ {
menuResetAttributes(cur_menu, element_count);
if (cur_menu->id == MenuId_UserTitles || cur_menu->id == MenuId_SystemTitles) if (cur_menu->id == MenuId_UserTitles || cur_menu->id == MenuId_SystemTitles)
{ {
app_metadata = NULL; app_metadata = NULL;
@ -1344,9 +1381,6 @@ int main(int argc, char *argv[])
freeNcaBasePatchList(); freeNcaBasePatchList();
} }
cur_menu->selected = 0;
cur_menu->scroll = 0;
cur_menu = cur_menu->parent; cur_menu = cur_menu->parent;
element_count = menuGetElementCount(cur_menu); element_count = menuGetElementCount(cur_menu);
} else } else
@ -1483,6 +1517,21 @@ static u32 menuGetElementCount(const Menu *menu)
return cnt; return cnt;
} }
static void menuResetAttributes(Menu *cur_menu, u32 element_count)
{
if (!cur_menu) return;
cur_menu->selected = 0;
cur_menu->scroll = 0;
for(u32 i = 0; i < element_count; i++)
{
MenuElement *cur_element = cur_menu->elements[i];
MenuElementOption *cur_options = cur_element->element_options;
if (cur_options && cur_options != &g_storageMenuElementOption) cur_options->retrieved = false;
}
}
void freeStorageList(void) void freeStorageList(void)
{ {
u32 elem_count = (2 + g_umsDeviceCount); // sd card, usb host, ums devices u32 elem_count = (2 + g_umsDeviceCount); // sd card, usb host, ums devices
@ -1638,7 +1687,12 @@ end:
static TitleInfo *getLatestTitleInfo(TitleInfo *title_info, u32 *out_idx, u32 *out_count) static TitleInfo *getLatestTitleInfo(TitleInfo *title_info, u32 *out_idx, u32 *out_count)
{ {
if (!title_info || !out_idx || !out_count || (title_info->meta_key.type != NcmContentMetaType_Patch && title_info->meta_key.type != NcmContentMetaType_DataPatch)) return title_info; if (!title_info || !out_idx || !out_count || (title_info->meta_key.type != NcmContentMetaType_Patch && title_info->meta_key.type != NcmContentMetaType_DataPatch))
{
if (out_idx) *out_idx = 0;
if (out_count) *out_count = titleGetCountFromInfoBlock(title_info);
return title_info;
}
u32 idx = 0, count = 1; u32 idx = 0, count = 1;
TitleInfo *cur_info = title_info->previous, *out = title_info; TitleInfo *cur_info = title_info->previous, *out = title_info;
@ -2241,6 +2295,21 @@ static bool dumpGameCardSecurityInformation(GameCardSecurityInformation *out)
return true; return true;
} }
static bool resetSettings(void *userdata)
{
consolePrint("are you sure you want to reset all settings to their default values?\n");
consolePrint("press a to proceed, or b to cancel\n\n");
u64 btn_down = utilsWaitForButtonPress(HidNpadButton_A | HidNpadButton_B);
if (btn_down & HidNpadButton_A)
{
configResetSettings();
consolePrint("settings successfully reset\n");
}
return false;
}
static bool saveGameCardImage(void *userdata) static bool saveGameCardImage(void *userdata)
{ {
(void)userdata; (void)userdata;
@ -4964,8 +5033,19 @@ static void nspThreadFunc(void *arg)
consolePrintReversedColors("\nif you proceed, nca modifications will be disabled, and content decryption"); consolePrintReversedColors("\nif you proceed, nca modifications will be disabled, and content decryption");
consolePrintReversedColors("\nwill not be possible for external tools (e.g. emulators, etc.)\n"); consolePrintReversedColors("\nwill not be possible for external tools (e.g. emulators, etc.)\n");
consolePrintReversedColors("\nif this is a shared game and you wish to fix this, exit the application and"); consolePrintReversedColors("\nthis may occur because of different reasons:\n");
consolePrintReversedColors("\ntry running it at least once, then come back and try again\n");
consolePrintReversedColors("\n1. you haven't launched this game/dlc at least once since you downloaded it");
consolePrintReversedColors("\n2. this is a shared game/dlc across different switch consoles using the");
consolePrintReversedColors("\n same nintendo account and you're using the secondary console");
consolePrintReversedColors("\n3. you downloaded this game/dlc onto your sd card using your sysmmc, then");
consolePrintReversedColors("\n copied the 'nintendo' folder data into the 'emummc' folder (or viceversa)\n");
consolePrintReversedColors("\ncases 1 and 2 can be fixed by exiting nxdumptool, launching the game");
consolePrintReversedColors("\nand then running nxdumptool once again\n");
consolePrintReversedColors("\ncase 3 can be fixed by running nxdumptool directly under the emmc that was");
consolePrintReversedColors("\nused to download the game/dlc\n");
consolePrintReversedColors("\npress a to proceed anyway, or b to cancel\n\n"); consolePrintReversedColors("\npress a to proceed anyway, or b to cancel\n\n");

View file

@ -7,8 +7,11 @@
from subprocess import run from subprocess import run
from os.path import dirname, join from os.path import dirname, join
from sys import executable from sys import executable
from platform import system
root_dir = dirname(__file__) root_dir = dirname(__file__)
run([executable, '-m', 'pip', 'install', '-r', join(root_dir, 'requirements-win32.txt')]) requirements_file = ('requirements-win32.txt' if system() == 'Windows' else 'requirements.txt')
run([executable, '-m', 'pip', 'install', '-r', join(root_dir, requirements_file)])
input('Press enter to close') input('Press enter to close')

View file

@ -58,7 +58,7 @@ from tqdm import tqdm
from argparse import ArgumentParser, RawTextHelpFormatter, ArgumentDefaultsHelpFormatter from argparse import ArgumentParser, RawTextHelpFormatter, ArgumentDefaultsHelpFormatter
from io import BufferedWriter from io import BufferedWriter
from typing import List, Tuple, Any, Callable, Optional from typing import Generator, Any, Callable
from datetime import datetime from datetime import datetime
@ -149,8 +149,8 @@ SERVER_START_MSG = f'Please connect a Nintendo Switch console running {USB_DEV_P
SERVER_STOP_MSG = f'Exit {USB_DEV_PRODUCT} on your console or disconnect it at any time to stop the server.' SERVER_STOP_MSG = f'Exit {USB_DEV_PRODUCT} on your console or disconnect it at any time to stop the server.'
# Default directory paths. # Default directory paths.
INITIAL_DIR = os.path.abspath(os.path.dirname(sys.executable if getattr(sys, 'frozen', False) else __file__)) INITIAL_DIR = os.path.dirname(os.path.abspath(os.path.expanduser(os.path.expandvars(sys.argv[0]))))
DEFAULT_DIR = (INITIAL_DIR + os.path.sep + USB_DEV_PRODUCT) DEFAULT_DIR = os.path.join(INITIAL_DIR, USB_DEV_PRODUCT)
# Application icon (PNG). # Application icon (PNG).
# Embedded to load it as the icon for all windows using PhotoImage (which doesn't support ICO files) + wm_iconphoto. # Embedded to load it as the icon for all windows using PhotoImage (which doesn't support ICO files) + wm_iconphoto.
@ -237,7 +237,7 @@ APP_ICON = b'iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAAR
b'43EDnoiNHI8a8FRs5HjMgCdjI8cj7+rp2MhR/Z3p7b5gyzRyjN0ei80cwP+bQrjkWSh1LgAAAABJRU5ErkJggg==' b'43EDnoiNHI8a8FRs5HjMgCdjI8cj7+rp2MhR/Z3p7b5gyzRyjN0ei80cwP+bQrjkWSh1LgAAAABJRU5ErkJggg=='
# Taskbar Type Library (TLB). Used under Windows 7 or greater. # Taskbar Type Library (TLB). Used under Windows 7 or greater.
TASKBAR_LIB_PATH = (INITIAL_DIR + os.path.sep + 'TaskbarLib.tlb') TASKBAR_LIB_PATH = os.path.join(INITIAL_DIR, 'TaskbarLib.tlb')
TASKBAR_LIB = b'TVNGVAIAAQAAAAAACQQAAAAAAABBAAAAAQAAAAAAAAAOAAAA/////wAAAAAAAAAATgAAADMDAAAAAAAA/////xgAAAAgAAAAgAAAAP////8AAAAAAAAAAGQAAADIAAAA' + \ TASKBAR_LIB = b'TVNGVAIAAQAAAAAACQQAAAAAAABBAAAAAQAAAAAAAAAOAAAA/////wAAAAAAAAAATgAAADMDAAAAAAAA/////xgAAAAgAAAAgAAAAP////8AAAAAAAAAAGQAAADIAAAA' + \
b'LAEAAJABAAD0AQAAWAIAALwCAAAgAwAAhAMAAOgDAABMBAAAsAQAABQFAAB8AQAAeAUAAP////8PAAAA/////wAAAAD/////DwAAAP////8AAAAA/////w8AAABMCAAA' + \ b'LAEAAJABAAD0AQAAWAIAALwCAAAgAwAAhAMAAOgDAABMBAAAsAQAABQFAAB8AQAAeAUAAP////8PAAAA/////wAAAAD/////DwAAAP////8AAAAA/////w8AAABMCAAA' + \
@ -318,11 +318,11 @@ g_logToFile: bool = False
g_logVerbose: bool = False g_logVerbose: bool = False
g_terminalColors: bool = False g_terminalColors: bool = False
g_outputDir: str = '' g_outputDir: str = ''
g_logLevelIntVar: tk.IntVar | None = None
g_logToFileBoolVar: tk.BooleanVar | None = None
g_logPath: str = '' g_logPath: str = ''
g_pathSep: str = '' g_pathSep: str = ''
g_logLevelIntVar: tk.IntVar | None = None
g_logToFileBoolVar: tk.BooleanVar | None = None
g_osType: str = '' g_osType: str = ''
g_osVersion: str = '' g_osVersion: str = ''
@ -331,18 +331,18 @@ g_isWindowsVista: bool = False
g_isWindows7: bool = False g_isWindows7: bool = False
g_isWindows10: bool = False g_isWindows10: bool = False
g_tkRoot: Optional[tk.Tk] = None g_tkRoot: tk.Tk | None = None
g_tkCanvas: Optional[tk.Canvas] = None g_tkCanvas: tk.Canvas | None = None
g_tkDirText: Optional[tk.Text] = None g_tkDirText: tk.Text | None = None
g_tkChooseDirButton: Optional[tk.Button] = None g_tkChooseDirButton: tk.Button | None = None
g_tkServerButton: Optional[tk.Button] = None g_tkServerButton: tk.Button | None = None
g_tkTipMessage: Any = None g_tkTipMessage: int = 0
g_tkScrolledTextLog: Optional[scrolledtext.ScrolledText] = None g_tkScrolledTextLog: scrolledtext.ScrolledText | None = None
g_tkVerboseCheckbox: Optional[tk.Checkbutton] = None g_tkVerboseCheckbox: tk.Checkbutton | None = None
g_logger: Optional[logging.Logger] = None g_logger: logging.Logger | None = None
g_stopEvent: Optional[threading.Event] = None g_stopEvent: threading.Event | None = None
g_tlb: Any = None g_tlb: Any = None
g_taskbar: Any = None g_taskbar: Any = None
@ -363,7 +363,7 @@ g_nspTransferMode: bool = False
g_nspSize: int = 0 g_nspSize: int = 0
g_nspHeaderSize: int = 0 g_nspHeaderSize: int = 0
g_nspRemainingSize: int = 0 g_nspRemainingSize: int = 0
g_nspFile: Optional[BufferedWriter] = None g_nspFile: BufferedWriter | None = None
g_nspFilePath: str = '' g_nspFilePath: str = ''
g_extractedFsDumpMode: bool = False g_extractedFsDumpMode: bool = False
@ -378,7 +378,7 @@ g_extractedFsAbsRoot: str = ""
# Reference: https://beenje.github.io/blog/posts/logging-to-a-tkinter-scrolledtext-widget. # Reference: https://beenje.github.io/blog/posts/logging-to-a-tkinter-scrolledtext-widget.
class LogQueueHandler(logging.Handler): class LogQueueHandler(logging.Handler):
def __init__(self, log_queue: queue.Queue): def __init__(self, log_queue: queue.Queue) -> None:
super().__init__() super().__init__()
self.log_queue = log_queue self.log_queue = log_queue
@ -481,7 +481,7 @@ class LogConsole:
class ProgressBarWindow: class ProgressBarWindow:
global g_tlb, g_taskbar global g_tlb, g_taskbar
def __init__(self, bar_format: str = '', tk_parent: Any = None, window_title: str = '', window_resize: bool = False, window_protocol: Optional[Callable] = None): def __init__(self, bar_format: str = '', tk_parent: Any = None, window_title: str = '', window_resize: bool = False, window_protocol: Callable | None = None) -> None:
self.n: int = 0 self.n: int = 0
self.total: int = 0 self.total: int = 0
self.divider: float = 1.0 self.divider: float = 1.0
@ -496,11 +496,11 @@ class ProgressBarWindow:
self.tk_parent = tk_parent self.tk_parent = tk_parent
self.tk_window = (tk.Toplevel(self.tk_parent) if self.tk_parent else None) self.tk_window = (tk.Toplevel(self.tk_parent) if self.tk_parent else None)
self.withdrawn = False self.withdrawn = False
self.tk_text_var: Optional[tk.StringVar] = None self.tk_text_var: tk.StringVar | None = None
self.tk_n_var: Optional[tk.DoubleVar] = None self.tk_n_var: tk.DoubleVar | None = None
self.tk_pbar: Optional[ttk.Progressbar] = None self.tk_pbar: ttk.Progressbar | None = None
self.pbar: Optional[tqdm] = None self.pbar: tqdm | None = None
if self.tk_window: if self.tk_window:
self.tk_window.withdraw() self.tk_window.withdraw()
@ -526,8 +526,8 @@ class ProgressBarWindow:
self.tk_pbar.configure(maximum=100, mode='indeterminate') self.tk_pbar.configure(maximum=100, mode='indeterminate')
self.tk_pbar.pack() self.tk_pbar.pack()
def __del__(self): def __del__(self) -> None:
if self.tk_parent: if self.tk_window:
self.tk_parent.after(0, self.tk_window.destroy) self.tk_parent.after(0, self.tk_window.destroy)
def start(self, total: int, n: int = 0, divider: int = 1, prefix: str = '', unit: str = 'B') -> None: def start(self, total: int, n: int = 0, divider: int = 1, prefix: str = '', unit: str = 'B') -> None:
@ -619,7 +619,7 @@ class ProgressBarWindow:
def set_prefix(self, prefix) -> None: def set_prefix(self, prefix) -> None:
self.prefix = prefix self.prefix = prefix
g_progressBarWindow: Optional[ProgressBarWindow] = None g_progressBarWindow: ProgressBarWindow | None = None
def eprint(*args, **kwargs) -> None: def eprint(*args, **kwargs) -> None:
print(*args, file=sys.stderr, **kwargs) print(*args, file=sys.stderr, **kwargs)
@ -747,7 +747,7 @@ def utilsResetNspInfo(delete: bool = False) -> None:
g_nspFile = None g_nspFile = None
g_nspFilePath = '' g_nspFilePath = ''
def utilsGetSizeUnitAndDivisor(size: int) -> Tuple[str, int]: def utilsGetSizeUnitAndDivisor(size: int) -> tuple[str, int]:
size_suffixes = [ 'B', 'KiB', 'MiB', 'GiB' ] size_suffixes = [ 'B', 'KiB', 'MiB', 'GiB' ]
size_suffixes_count = len(size_suffixes) size_suffixes_count = len(size_suffixes)
@ -773,9 +773,10 @@ def usbGetDeviceEndpoints() -> bool:
global g_usbEpIn, g_usbEpOut, g_usbEpMaxPacketSize, g_usbVer global g_usbEpIn, g_usbEpOut, g_usbEpMaxPacketSize, g_usbVer
assert g_logger is not None assert g_logger is not None
# assert g_stopEvent is not None assert g_stopEvent is not None
prev_dev = cur_dev = None cur_dev: Generator[usb.core.Device, Any, None] | None = None
prev_dev: usb.core.Device | None = None
usb_ep_in_lambda = lambda ep: usb.util.endpoint_direction(ep.bEndpointAddress) == usb.util.ENDPOINT_IN usb_ep_in_lambda = lambda ep: usb.util.endpoint_direction(ep.bEndpointAddress) == usb.util.ENDPOINT_IN
usb_ep_out_lambda = lambda ep: usb.util.endpoint_direction(ep.bEndpointAddress) == usb.util.ENDPOINT_OUT usb_ep_out_lambda = lambda ep: usb.util.endpoint_direction(ep.bEndpointAddress) == usb.util.ENDPOINT_OUT
usb_version = 0 usb_version = 0
@ -791,8 +792,20 @@ def usbGetDeviceEndpoints() -> bool:
# Find a connected USB device with a matching VID/PID pair. # Find a connected USB device with a matching VID/PID pair.
# Using == here to compare both device instances would also compare the backend, so we'll just compare certain elements manually. # Using == here to compare both device instances would also compare the backend, so we'll just compare certain elements manually.
cur_dev = usb.core.find(idVendor=USB_DEV_VID, idProduct=USB_DEV_PID) try:
if (cur_dev is None) or ((prev_dev is not None) and (cur_dev.bus == prev_dev.bus) and (cur_dev.address == prev_dev.address)): cur_dev = usb.core.find(find_all=False, idVendor=USB_DEV_VID, idProduct=USB_DEV_PID)
except:
if not g_cliMode:
utilsLogException(traceback.format_exc())
g_logger.error('\nFatal error ocurred while enumerating USB devices.')
if g_isWindows:
g_logger.error('\nTry reinstalling the libusbK driver using Zadig.')
return False
if (not isinstance(cur_dev, usb.core.Device)) or (isinstance(prev_dev, usb.core.Device) and (cur_dev.bus == prev_dev.bus) and (cur_dev.address == prev_dev.address)):
time.sleep(0.1) time.sleep(0.1)
continue continue
@ -846,6 +859,7 @@ def usbRead(size: int, timeout: int = -1) -> bytes:
except usb.core.USBError: except usb.core.USBError:
if not g_cliMode: if not g_cliMode:
utilsLogException(traceback.format_exc()) utilsLogException(traceback.format_exc())
if g_logger is not None: if g_logger is not None:
g_logger.error('\nUSB timeout triggered or console disconnected.') g_logger.error('\nUSB timeout triggered or console disconnected.')
@ -859,13 +873,14 @@ def usbWrite(data: bytes, timeout: int = -1) -> int:
except usb.core.USBError: except usb.core.USBError:
if not g_cliMode: if not g_cliMode:
utilsLogException(traceback.format_exc()) utilsLogException(traceback.format_exc())
if g_logger is not None: if g_logger is not None:
g_logger.error('\nUSB timeout triggered or console disconnected.') g_logger.error('\nUSB timeout triggered or console disconnected.')
return wr return wr
def usbSendStatus(code: int) -> bool: def usbSendStatus(code: int) -> bool:
status = struct.pack('<4sIH6p', USB_MAGIC_WORD, code, g_usbEpMaxPacketSize, b'') status = struct.pack('<4sIH6x', USB_MAGIC_WORD, code, g_usbEpMaxPacketSize)
return bool(usbWrite(status, USB_TRANSFER_TIMEOUT) == len(status)) return bool(usbWrite(status, USB_TRANSFER_TIMEOUT) == len(status))
def usbHandleStartSession(cmd_block: bytes) -> int: def usbHandleStartSession(cmd_block: bytes) -> int:
@ -1020,6 +1035,7 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None:
if (not g_nspTransferMode) or (g_nspFile is None): if (not g_nspTransferMode) or (g_nspFile is None):
# Generate full, absolute path to the destination file. # Generate full, absolute path to the destination file.
fullpath = os.path.abspath(g_outputDir + os.path.sep + filename) fullpath = os.path.abspath(g_outputDir + os.path.sep + filename)
printable_fullpath = (fullpath[4:] if g_isWindows else fullpath)
printable_fullpath = (fullpath[4:] if g_isWindows else fullpath) printable_fullpath = (fullpath[4:] if g_isWindows else fullpath)
@ -1036,11 +1052,11 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None:
# Make sure the output filepath doesn't point to an existing directory. # Make sure the output filepath doesn't point to an existing directory.
if os.path.exists(fullpath) and (not os.path.isfile(fullpath)): if os.path.exists(fullpath) and (not os.path.isfile(fullpath)):
utilsResetNspInfo() utilsResetNspInfo()
g_logger.error(f'\nOutput filepath points to an existing directory! ("{printable_fullpath}").\n') g_logger.error(f'Output filepath points to an existing directory! ("{printable_fullpath}").\n')
return USB_STATUS_HOST_IO_ERROR return USB_STATUS_HOST_IO_ERROR
# Make sure we have enough free space. # Make sure we have enough free space.
(total_space, used_space, free_space) = shutil.disk_usage(dirpath) (_, _, free_space) = shutil.disk_usage(dirpath)
if free_space <= file_size: if free_space <= file_size:
utilsResetNspInfo() utilsResetNspInfo()
g_logger.error('\nNot enough free space available in output volume!\n') g_logger.error('\nNot enough free space available in output volume!\n')
@ -1090,10 +1106,7 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None:
# We're not using dynamic tqdm prefixes under CLI mode. # We're not using dynamic tqdm prefixes under CLI mode.
prefix = '' prefix = ''
else: else:
idx = filename.rfind(os.path.sep) prefix = f'Current {file_type_str}: "{os.path.basename(filename)}".\n'
prefix_filename = (filename[idx+1:] if (idx >= 0) else filename)
prefix = f'Current {file_type_str}: "{prefix_filename}".\n'
prefix += 'Use your console to cancel the file transfer if you wish to do so.' prefix += 'Use your console to cancel the file transfer if you wish to do so.'
if (not g_nspTransferMode) or g_nspRemainingSize == (g_nspSize - g_nspHeaderSize): if (not g_nspTransferMode) or g_nspRemainingSize == (g_nspSize - g_nspHeaderSize):
@ -1117,8 +1130,13 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None:
def cancelTransfer(): def cancelTransfer():
# Cancel file transfer. # Cancel file transfer.
utilsResetNspInfo(True) if g_nspTransferMode:
if use_pbar: utilsResetNspInfo(True)
else:
file.close()
os.remove(fullpath)
if use_pbar and (g_progressBarWindow is not None):
g_progressBarWindow.end() g_progressBarWindow.end()
# Start transfer process. # Start transfer process.
@ -1152,7 +1170,7 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None:
# Check if we're dealing with a CancelFileTransfer command. # Check if we're dealing with a CancelFileTransfer command.
if chunk_size == USB_CMD_HEADER_SIZE: if chunk_size == USB_CMD_HEADER_SIZE:
(magic, cmd_id, cmd_block_size) = struct.unpack_from('<4sII', chunk, 0) (magic, cmd_id, _) = struct.unpack_from('<4sII', chunk, 0)
if (magic == USB_MAGIC_WORD) and (cmd_id == USB_CMD_CANCEL_FILE_TRANSFER): if (magic == USB_MAGIC_WORD) and (cmd_id == USB_CMD_CANCEL_FILE_TRANSFER):
# Cancel file transfer. # Cancel file transfer.
cancelTransfer() cancelTransfer()
@ -1259,8 +1277,7 @@ def usbHandleEndSession(cmd_block: bytes) -> int:
def usbHandleStartExtractedFsDump(cmd_block: bytes) -> int: def usbHandleStartExtractedFsDump(cmd_block: bytes) -> int:
assert g_logger is not None assert g_logger is not None
global g_isWindows, g_outputDir, g_extractedFsDumpMode, g_extractedFsAbsRoot, g_formattedFileSize, g_formattedFileUnit, g_fileSizeMiB, g_startTime
g_logger.debug(f'Received StartExtractedFsDump ({USB_CMD_START_EXTRACTED_FS_DUMP:02X}) command.') g_logger.debug(f'Received StartExtractedFsDump ({USB_CMD_START_EXTRACTED_FS_DUMP:02X}) command.')
if g_nspTransferMode: if g_nspTransferMode:
@ -1310,7 +1327,6 @@ def usbHandleStartExtractedFsDump(cmd_block: bytes) -> int:
return USB_STATUS_SUCCESS return USB_STATUS_SUCCESS
def usbHandleEndExtractedFsDump(cmd_block: bytes) -> int: def usbHandleEndExtractedFsDump(cmd_block: bytes) -> int:
global g_extractedFsDumpMode, g_extractedFsAbsRoot
assert g_logger is not None assert g_logger is not None
g_logger.debug(f'Received EndExtractedFsDump ({USB_CMD_END_EXTRACTED_FS_DUMP:02X}) command.') g_logger.debug(f'Received EndExtractedFsDump ({USB_CMD_END_EXTRACTED_FS_DUMP:02X}) command.')
g_logger.info(f'Finished extracted FS dump.') g_logger.info(f'Finished extracted FS dump.')
@ -1422,10 +1438,25 @@ def uiStopServer() -> None:
def uiStartServer() -> None: def uiStartServer() -> None:
uiUpdateOutputDir() assert g_tkDirText is not None
# Set new log path for this session if logging to file is turned on.
if g_logToFile: g_outputDir = g_tkDirText.get('1.0', tk.END).strip()
utilsUpdateLogPath() if not g_outputDir:
# We should never reach this, honestly.
messagebox.showerror('Error', 'You must provide an output directory!', parent=g_tkRoot)
return
# Unconditionally enable 32-bit paths on Windows.
if g_isWindows:
g_outputDir = '\\\\?\\' + g_outputDir
# Make sure the full directory tree exists.
try:
os.makedirs(g_outputDir, exist_ok=True)
except:
utilsLogException(traceback.format_exc())
messagebox.showerror('Error', 'Unable to create full output directory tree!', parent=g_tkRoot)
return
# Update UI. # Update UI.
uiToggleElements(False) uiToggleElements(False)
@ -1442,7 +1473,6 @@ def uiToggleElements(flag: bool) -> None:
assert g_tkChooseDirButton is not None assert g_tkChooseDirButton is not None
assert g_tkServerButton is not None assert g_tkServerButton is not None
assert g_tkCanvas is not None assert g_tkCanvas is not None
assert g_tkLogToFileCheckbox is not None
assert g_tkVerboseCheckbox is not None assert g_tkVerboseCheckbox is not None
if flag: if flag:
@ -1529,11 +1559,7 @@ def uiHandleLogToFileCheckbox() -> None:
def uiHandleVerboseCheckbox() -> None: def uiHandleVerboseCheckbox() -> None:
assert g_logger is not None assert g_logger is not None
assert g_logLevelIntVar is not None assert g_logLevelIntVar is not None
global g_logVerbose g_logger.setLevel(g_logLevelIntVar.get())
logLevel=g_logLevelIntVar.get()
g_logger.setLevel(logLevel)
g_logVerbose = True if(logLevel == logging.DEBUG) else False
return
def uiInitialize() -> None: def uiInitialize() -> None:
global SCALE, g_logLevelIntVar, g_logToFileBoolVar, g_logToFile, g_logVerbose global SCALE, g_logLevelIntVar, g_logToFileBoolVar, g_logToFile, g_logVerbose
@ -1678,6 +1704,8 @@ def uiInitialize() -> None:
def cliInitialize() -> None: def cliInitialize() -> None:
global g_progressBarWindow, g_outputDir, g_logToFile global g_progressBarWindow, g_outputDir, g_logToFile
assert g_logger is not None
# Unconditionally enable long paths on Windows. # Unconditionally enable long paths on Windows.
g_outputDir = utilsGetWinFullPath(g_outputDir) #if g_isWindows else g_outputDir g_outputDir = utilsGetWinFullPath(g_outputDir) #if g_isWindows else g_outputDir
@ -1688,16 +1716,20 @@ def cliInitialize() -> None:
# NB, g_outputDir should be adjusted for Windows prior. # NB, g_outputDir should be adjusted for Windows prior.
if g_logToFile: if g_logToFile:
utilsUpdateLogPath() utilsUpdateLogPath()
# Initialize console logger. # Initialize console logger.
console = LogConsole() console = LogConsole()
# Initialize progress bar window object. # Initialize progress bar window object.
bar_format = '{percentage:.2f}% |{bar}| {n:.2f}/{total:.2f} [{elapsed}<{remaining}, {rate_fmt}]' bar_format = '{percentage:.2f}% |{bar}| {n:.2f}/{total:.2f} [{elapsed}<{remaining}, {rate_fmt}]'
g_progressBarWindow = ProgressBarWindow(bar_format) g_progressBarWindow = ProgressBarWindow(bar_format)
# Log basic info about the script and settings. # Print info.
utilsLogBasicScriptInfo() g_logger.info(f'\n{SCRIPT_TITLE}. {COPYRIGHT_TEXT}.')
g_logger.info(f'Output directory: "{g_outputDir}".\n')
# Unconditionally enable 32-bit paths on Windows.
if g_isWindows:
g_outputDir = '\\\\?\\' + g_outputDir
# Start USB command handler directly. # Start USB command handler directly.
usbCommandHandler() usbCommandHandler()
@ -1709,12 +1741,11 @@ def main() -> int:
warnings.filterwarnings("ignore") warnings.filterwarnings("ignore")
# Parse command line arguments. # Parse command line arguments.
parser = ArgumentParser(formatter_class=RawTextHelpFormatter, description=SCRIPT_TITLE + '. ' + COPYRIGHT_TEXT + '.') parser = ArgumentParser(description=SCRIPT_TITLE + '. ' + COPYRIGHT_TEXT + '.')
parser.add_argument('-c', '--cli', required=False, action='store_true', default=False, help='Start the script in CLI mode.') parser.add_argument('-c', '--cli', required=False, action='store_true', default=False, help='Start the script in CLI mode.')
parser.add_argument('-o', '--outdir', required=False, type=str, metavar='DIR', help='Path to output directory; will attempt to create if non-existent.'+\ parser.add_argument('-o', '--outdir', required=False, type=str, metavar='DIR', help=f'Path to output directory. Defaults to "{DEFAULT_DIR}".')
'\nDefaults to "' + DEFAULT_DIR + '".')
parser.add_argument('-l', '--log', required=False, action='store_true', default=False, help='Enables logging to file in output directory in CLI mode.')
parser.add_argument('-v', '--verbose', required=False, action='store_true', default=False, help='Enable verbose output.') parser.add_argument('-v', '--verbose', required=False, action='store_true', default=False, help='Enable verbose output.')
args = parser.parse_args() args = parser.parse_args()
# Update global flags. # Update global flags.
@ -1766,9 +1797,7 @@ if __name__ == "__main__":
ret = main() ret = main()
except KeyboardInterrupt: except KeyboardInterrupt:
time.sleep(0.2) time.sleep(0.2)
g_logger.info("Host script exited!") print('\nScript interrupted.')
if g_isWindows10: print(COLOR_RESET)
except: except:
utilsLogException(traceback.format_exc()) utilsLogException(traceback.format_exc())

View file

@ -3,26 +3,22 @@
set scriptdir=%~dp0 set scriptdir=%~dp0
set scriptdir=%scriptdir:~0,-1% set scriptdir=%scriptdir:~0,-1%
set venvname=installer set venvname=standalone
set venvscripts=%scriptdir%\%venvname%\Scripts set venvscripts=%scriptdir%\%venvname%\Scripts
set venvpython=%venvscripts%\python.exe set venvpython=%venvscripts%\python.exe
set venvpyinstaller=%venvscripts%\pyinstaller.exe
cd /D "%scriptdir%" cd /D "%scriptdir%"
python -m venv "%venvname%" python -m venv "%venvname%"
"%venvpython%" -m pip install --upgrade pyinstaller -r requirements-win32.txt "%venvpython%" -m pip install --upgrade nuitka -r requirements-win32.txt
"%venvpyinstaller%" -y --clean --log-level WARN -F --add-binary "C:\Windows\System32\libusb0.dll;." -w -i nxdt.ico nxdt_host.py "%venvpython%" -m nuitka --standalone --onefile --deployment --disable-console --windows-icon-from-ico=nxdt.ico --enable-plugin=tk-inter nxdt_host.py
move dist\nxdt_host.exe nxdt_host.exe rmdir /s /q nxdt_host.build
rmdir /s /q nxdt_host.dist
rmdir /s /q __pycache__ rmdir /s /q nxdt_host.onefile-build
rmdir /s /q build rmdir /s /q standalone
rmdir /s /q dist
rmdir /s /q installer
del nxdt_host.spec
pause pause

View file

@ -49,6 +49,9 @@ bool configInitialize(void);
/// Closes the configuration interface. /// Closes the configuration interface.
void configExit(void); void configExit(void);
/// Resets settings to their default values.
void configResetSettings(void);
/// Getters and setters for various data types. /// Getters and setters for various data types.
/// Path elements must be separated using forward slashes. /// Path elements must be separated using forward slashes.

View file

@ -33,6 +33,8 @@ extern "C" {
#define SIGNED_TIK_MIN_SIZE sizeof(TikSigHmac160) /* Assuming no ESV1/ESV2 records are available. */ #define SIGNED_TIK_MIN_SIZE sizeof(TikSigHmac160) /* Assuming no ESV1/ESV2 records are available. */
#define SIGNED_TIK_MAX_SIZE 0x400 /* Max ticket entry size in the ES ticket system savedata file. */ #define SIGNED_TIK_MAX_SIZE 0x400 /* Max ticket entry size in the ES ticket system savedata file. */
#define TIK_FORMAT_VERSION 2
#define GENERATE_TIK_STRUCT(sigtype, tiksize) \ #define GENERATE_TIK_STRUCT(sigtype, tiksize) \
typedef struct { \ typedef struct { \
SignatureBlock##sigtype sig_block; \ SignatureBlock##sigtype sig_block; \
@ -72,7 +74,7 @@ typedef enum {
typedef struct { typedef struct {
char issuer[0x40]; char issuer[0x40];
u8 titlekey_block[0x100]; u8 titlekey_block[0x100];
u8 format_version; u8 format_version; ///< Always matches TIK_FORMAT_VERSION.
u8 titlekey_type; ///< TikTitleKeyType. u8 titlekey_type; ///< TikTitleKeyType.
u16 ticket_version; u16 ticket_version;
u8 license_type; ///< TikLicenseType. u8 license_type; ///< TikLicenseType.

View file

@ -133,6 +133,4 @@
#define DISCORD_SERVER_URL "https://discord.gg/SCbbcQx" #define DISCORD_SERVER_URL "https://discord.gg/SCbbcQx"
#define LOCKPICK_RCM_URL "https://github.com/shchmue/Lockpick_RCM"
#endif /* __DEFINES_H__ */ #endif /* __DEFINES_H__ */

View file

@ -66,6 +66,7 @@ static struct json_object *g_configJson = NULL;
/* Function prototypes. */ /* Function prototypes. */
static bool configParseConfigJson(void); static bool configParseConfigJson(void);
static bool configResetConfigJson(void);
static void configWriteConfigJson(void); static void configWriteConfigJson(void);
static void configFreeConfigJson(void); static void configFreeConfigJson(void);
@ -111,6 +112,11 @@ void configExit(void)
} }
} }
void configResetSettings(void)
{
configResetConfigJson();
}
CONFIG_GETTER(Boolean, bool); CONFIG_GETTER(Boolean, bool);
CONFIG_SETTER(Boolean, bool); CONFIG_SETTER(Boolean, bool);
@ -151,29 +157,36 @@ static bool configParseConfigJson(void)
jsonLogLastError(); jsonLogLastError();
} }
if (use_default_config) /* Try to load the default settings. */
{ if (use_default_config) ret = configResetConfigJson();
LOG_MSG_INFO("Loading default configuration.");
/* Free config JSON. */
configFreeConfigJson();
/* Read default config JSON. */
g_configJson = json_object_from_file(DEFAULT_CONFIG_PATH);
if (g_configJson)
{
configWriteConfigJson();
ret = true;
} else {
jsonLogLastError();
}
}
if (!ret) LOG_MSG_ERROR("Failed to parse both current and default JSON configuration files!"); if (!ret) LOG_MSG_ERROR("Failed to parse both current and default JSON configuration files!");
return ret; return ret;
} }
static bool configResetConfigJson(void)
{
bool ret = false;
LOG_MSG_INFO("Loading default configuration.");
/* Free config JSON. */
configFreeConfigJson();
/* Read default config JSON. */
g_configJson = json_object_from_file(DEFAULT_CONFIG_PATH);
if (g_configJson)
{
configWriteConfigJson();
ret = true;
} else {
jsonLogLastError();
}
return ret;
}
static void configWriteConfigJson(void) static void configWriteConfigJson(void)
{ {
if (!g_configJson) return; if (!g_configJson) return;

View file

@ -28,7 +28,7 @@
#define GAMECARD_READ_BUFFER_SIZE 0x800000 /* 8 MiB. */ #define GAMECARD_READ_BUFFER_SIZE 0x800000 /* 8 MiB. */
#define GAMECARD_ACCESS_DELAY 3 /* Seconds. */ #define GAMECARD_ACCESS_DELAY 3 /* Seconds. */
#define GAMECARD_UNUSED_AREA_BLOCK_SIZE 0x24 #define GAMECARD_UNUSED_AREA_BLOCK_SIZE 0x24
#define GAMECARD_UNUSED_AREA_SIZE(x) (((x) / GAMECARD_PAGE_SIZE) * GAMECARD_UNUSED_AREA_BLOCK_SIZE) #define GAMECARD_UNUSED_AREA_SIZE(x) (((x) / GAMECARD_PAGE_SIZE) * GAMECARD_UNUSED_AREA_BLOCK_SIZE)
@ -134,7 +134,6 @@ static bool _gamecardGetDecryptedCardInfoArea(void);
static bool gamecardReadSecurityInformation(GameCardSecurityInformation *out); static bool gamecardReadSecurityInformation(GameCardSecurityInformation *out);
static bool gamecardGetHandleAndStorage(u32 partition); static bool gamecardGetHandleAndStorage(u32 partition);
NX_INLINE void gamecardCloseHandle(void);
static bool gamecardOpenStorageArea(u8 area); static bool gamecardOpenStorageArea(u8 area);
static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset); static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset);
@ -259,6 +258,12 @@ void gamecardExit(void)
g_gameCardReadBuf = NULL; g_gameCardReadBuf = NULL;
} }
/* Make sure NS can access the gamecard. */
/* Fixes gamecard launch errors after exiting the application. */
/* TODO: find out why this doesn't work. */
//Result rc = nsEnsureGameCardAccess();
//if (R_FAILED(rc)) LOG_MSG_ERROR("nsEnsureGameCardAccess failed! (0x%X).", rc);
g_gameCardInterfaceInit = false; g_gameCardInterfaceInit = false;
} }
} }
@ -866,7 +871,7 @@ static bool gamecardReadHeader(void)
} }
/* Read gamecard header. */ /* Read gamecard header. */
/* This step doesn't rely on gamecardReadStorageArea() because of its dependence on storage area sizes (which we haven't retrieved). */ /* We don't use gamecardReadStorageArea() here because of its dependence on storage area sizes (which we haven't yet retrieved). */
Result rc = fsStorageRead(&g_gameCardStorage, 0, &g_gameCardHeader, sizeof(GameCardHeader)); Result rc = fsStorageRead(&g_gameCardStorage, 0, &g_gameCardHeader, sizeof(GameCardHeader));
if (R_FAILED(rc)) if (R_FAILED(rc))
{ {
@ -1000,7 +1005,6 @@ static bool gamecardGetHandleAndStorage(u32 partition)
rc = fsOpenGameCardStorage(&g_gameCardStorage, &g_gameCardHandle, partition); rc = fsOpenGameCardStorage(&g_gameCardStorage, &g_gameCardHandle, partition);
if (R_FAILED(rc)) if (R_FAILED(rc))
{ {
gamecardCloseHandle(); /* Close invalid gamecard handle. */
LOG_MSG_DEBUG("fsOpenGameCardStorage failed to open %s storage area on try #%u! (0x%X).", GAMECARD_STORAGE_AREA_NAME(partition + 1), i + 1, rc); LOG_MSG_DEBUG("fsOpenGameCardStorage failed to open %s storage area on try #%u! (0x%X).", GAMECARD_STORAGE_AREA_NAME(partition + 1), i + 1, rc);
continue; continue;
} }
@ -1018,11 +1022,6 @@ static bool gamecardGetHandleAndStorage(u32 partition)
return R_SUCCEEDED(rc); return R_SUCCEEDED(rc);
} }
NX_INLINE void gamecardCloseHandle(void)
{
g_gameCardHandle.value = 0;
}
static bool gamecardOpenStorageArea(u8 area) static bool gamecardOpenStorageArea(u8 area)
{ {
if (g_gameCardStatus < GameCardStatus_InsertedAndInfoNotLoaded || (area != GameCardStorageArea_Normal && area != GameCardStorageArea_Secure)) if (g_gameCardStatus < GameCardStatus_InsertedAndInfoNotLoaded || (area != GameCardStorageArea_Normal && area != GameCardStorageArea_Secure))
@ -1034,7 +1033,7 @@ static bool gamecardOpenStorageArea(u8 area)
/* Return right away if a valid handle has already been retrieved and the desired gamecard storage area is currently open. */ /* Return right away if a valid handle has already been retrieved and the desired gamecard storage area is currently open. */
if (g_gameCardHandle.value && serviceIsActive(&(g_gameCardStorage.s)) && g_gameCardCurrentStorageArea == area) return true; if (g_gameCardHandle.value && serviceIsActive(&(g_gameCardStorage.s)) && g_gameCardCurrentStorageArea == area) return true;
/* Close both gamecard handle and open storage area. */ /* Close both the gamecard handle and the open storage area. */
gamecardCloseStorageArea(); gamecardCloseStorageArea();
/* Retrieve both a new gamecard handle and a storage area handle. */ /* Retrieve both a new gamecard handle and a storage area handle. */
@ -1137,7 +1136,7 @@ static void gamecardCloseStorageArea(void)
memset(&g_gameCardStorage, 0, sizeof(FsStorage)); memset(&g_gameCardStorage, 0, sizeof(FsStorage));
} }
gamecardCloseHandle(); g_gameCardHandle.value = 0;
g_gameCardCurrentStorageArea = GameCardStorageArea_None; g_gameCardCurrentStorageArea = GameCardStorageArea_None;
} }

View file

@ -202,7 +202,7 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
/* Load keyset. */ /* Load keyset. */
if (!keysLoadKeyset()) if (!keysLoadKeyset())
{ {
LOG_MSG_ERROR("Failed to load keyset!\nUpdate your keys file with Lockpick_RCM:\n" LOCKPICK_RCM_URL); LOG_MSG_ERROR("Failed to load keyset!\nPlease update your keys file with Lockpick_RCM.\nYou can get an updated build at:" DISCORD_SERVER_URL);
break; break;
} }

View file

@ -386,8 +386,9 @@ static bool tikFixTamperedCommonTicket(Ticket *tik)
TikCommonBlock *tik_common_block = NULL; TikCommonBlock *tik_common_block = NULL;
u32 sig_type = 0; u32 sig_type = 0;
u8 *signature = NULL; bool dev_cert = false;
u64 signature_size = 0, hash_area_size = 0; TikSigRsa2048 *tik_data = NULL;
u64 hash_area_size = 0;
bool success = false; bool success = false;
@ -397,15 +398,23 @@ static bool tikFixTamperedCommonTicket(Ticket *tik)
return false; return false;
} }
/* Get ticket signature and its properties, as well as the ticket hash area size. */ /* Get ticket signature type. Also determine if it's a development ticket. */
sig_type = signatureGetTypeFromSignedBlob(tik->data, false); sig_type = signatureGetTypeFromSignedBlob(tik->data, false);
signature = signatureGetSigFromSignedBlob(tik->data); dev_cert = (strstr(tik_common_block->issuer, TIK_DEV_CERT_ISSUER) != NULL);
signature_size = signatureGetSigSizeByType(sig_type);
/* Return right away if we're not dealing with a common ticket or if the signature type doesn't match RSA-2048 + SHA-256. */
if (tik_common_block->titlekey_type != TikTitleKeyType_Common || sig_type != SignatureType_Rsa2048Sha256)
{
success = true;
goto end;
}
/* Make sure we're dealing with a tampered ticket by verifying its signature. */
tik_data = (TikSigRsa2048*)tik->data;
tik_common_block = &(tik_data->tik_common_block);
hash_area_size = tikGetSignedTicketBlobHashAreaSize(tik->data); hash_area_size = tikGetSignedTicketBlobHashAreaSize(tik->data);
/* Return right away if we're not dealing with a common ticket, if the signature type doesn't match RSA-2048 + SHA-256, or if the signature is valid. */ if (tikVerifyRsa2048Sha256Signature(tik_common_block, hash_area_size, tik_data->sig_block.signature))
if (tik_common_block->titlekey_type != TikTitleKeyType_Common || sig_type != SignatureType_Rsa2048Sha256 || \
tikVerifyRsa2048Sha256Signature(tik_common_block, hash_area_size, signature))
{ {
success = true; success = true;
goto end; goto end;
@ -416,22 +425,35 @@ static bool tikFixTamperedCommonTicket(Ticket *tik)
/* Nintendo didn't start putting the key generation value into the rights ID until HOS 3.0.1. */ /* Nintendo didn't start putting the key generation value into the rights ID until HOS 3.0.1. */
/* Old custom tools used to wipe the key generation field and/or save its value into a different offset. */ /* Old custom tools used to wipe the key generation field and/or save its value into a different offset. */
/* We're gonna take care of that by setting the correct values where they need to go. */ /* We're gonna take care of that by setting the correct values where they need to go. */
memset(signature, 0xFF, signature_size); memset(tik_data->sig_block.signature, 0xFF, sizeof(tik_data->sig_block.signature));
memset(tik_data->sig_block.padding, 0, sizeof(tik_data->sig_block.padding));
memset(tik_common_block->issuer, 0, sizeof(tik_common_block->issuer));
sprintf(tik_common_block->issuer, "Root-CA%08X-%s", dev_cert ? 4 : 3, TIK_COMMON_CERT_NAME);
memset(tik_common_block->titlekey_block + 0x10, 0, sizeof(tik_common_block->titlekey_block) - 0x10);
tik_common_block->format_version = TIK_FORMAT_VERSION;
tik_common_block->titlekey_type = TikTitleKeyType_Common; tik_common_block->titlekey_type = TikTitleKeyType_Common;
tik_common_block->ticket_version = 0;
tik_common_block->license_type = TikLicenseType_Permanent; tik_common_block->license_type = TikLicenseType_Permanent;
tik_common_block->key_generation = tik->key_generation; tik_common_block->key_generation = tik->key_generation;
tik_common_block->property_mask = TikPropertyMask_None; tik_common_block->property_mask = TikPropertyMask_None;
memset(tik_common_block->reserved, 0, sizeof(tik_common_block->reserved));
tik_common_block->ticket_id = 0; tik_common_block->ticket_id = 0;
tik_common_block->device_id = 0; tik_common_block->device_id = 0;
tik_common_block->account_id = 0; tik_common_block->account_id = 0;
tik_common_block->sect_total_size = 0; tik_common_block->sect_total_size = 0;
tik_common_block->sect_hdr_offset = (u32)tik->size; tik_common_block->sect_hdr_offset = (u32)sizeof(TikSigRsa2048);
tik_common_block->sect_hdr_count = 0; tik_common_block->sect_hdr_count = 0;
tik_common_block->sect_hdr_entry_size = 0; tik_common_block->sect_hdr_entry_size = 0;
/* Update ticket size. */
tik->size = sizeof(TikSigRsa2048);
/* Update return value. */ /* Update return value. */
success = true; success = true;
@ -447,8 +469,8 @@ static bool tikVerifyRsa2048Sha256Signature(const TikCommonBlock *tik_common_blo
return false; return false;
} }
const char *cert_name = (strrchr(tik_common_block->issuer, '-') + 1);
Certificate cert = {0}; Certificate cert = {0};
const char *cert_name = (strrchr(tik_common_block->issuer, '-') + 1);
const u8 *modulus = NULL, *public_exponent = NULL; const u8 *modulus = NULL, *public_exponent = NULL;
/* Get certificate for the ticket signature issuer. */ /* Get certificate for the ticket signature issuer. */

View file

@ -552,7 +552,7 @@ static bool titleRetrieveUserApplicationMetadataByTitleId(u64 title_id, TitleApp
NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id, bool is_system, u32 extra_app_count); NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id, bool is_system, u32 extra_app_count);
NX_INLINE u64 titleGetApplicationIdByMetaKey(const NcmContentMetaKey *meta_key); NX_INLINE u64 titleGetApplicationIdByContentMetaKey(const NcmContentMetaKey *meta_key);
static bool titleGenerateTitleInfoEntriesForTitleStorage(TitleStorage *title_storage); static bool titleGenerateTitleInfoEntriesForTitleStorage(TitleStorage *title_storage);
static bool titleGetMetaKeysFromContentDatabase(NcmContentMetaDatabase *ncm_db, NcmContentMetaKey **out_meta_keys, u32 *out_meta_key_count); static bool titleGetMetaKeysFromContentDatabase(NcmContentMetaDatabase *ncm_db, NcmContentMetaKey **out_meta_keys, u32 *out_meta_key_count);
@ -1026,6 +1026,7 @@ TitleInfo **titleGetOrphanTitles(u32 *out_count)
} }
/* Allocate orphan title info pointer array. */ /* Allocate orphan title info pointer array. */
/* titleFreeOrphanTitles() depends on the last NULL element. */
orphan_info = calloc(g_orphanTitleInfoCount + 1, sizeof(TitleInfo*)); orphan_info = calloc(g_orphanTitleInfoCount + 1, sizeof(TitleInfo*));
if (!orphan_info) if (!orphan_info)
{ {
@ -1373,6 +1374,33 @@ NX_INLINE bool titleInitializePersistentTitleStorages(void)
} }
} }
#if LOG_LEVEL <= LOG_LEVEL_INFO
#define ORPHAN_INFO_LOG(fmt, ...) utilsAppendFormattedStringToBuffer(&orphan_info_buf, &orphan_info_buf_size, fmt, ##__VA_ARGS__)
if (g_orphanTitleInfo && g_orphanTitleInfoCount)
{
char *orphan_info_buf = NULL;
size_t orphan_info_buf_size = 0;
ORPHAN_INFO_LOG("Identified %u orphan title(s) across all initialized title storages.\r\n", g_orphanTitleInfoCount);
for(u32 i = 0; i < g_orphanTitleInfoCount; i++)
{
TitleInfo *orphan_info = g_orphanTitleInfo[i];
ORPHAN_INFO_LOG("- %016lX v%u (%s, %s).%s", orphan_info->meta_key.id, orphan_info->version.value, titleGetNcmContentMetaTypeName(orphan_info->meta_key.type), \
titleGetNcmStorageIdName(orphan_info->storage_id), (i + 1) < g_orphanTitleInfoCount ? "\r\n" : "");
}
if (orphan_info_buf)
{
LOG_MSG_INFO("%s", orphan_info_buf);
free(orphan_info_buf);
}
}
#undef ORPHAN_INFO_LOG
#endif /* LOG_LEVEL <= LOG_LEVEL_INFO */
return true; return true;
} }
@ -1861,7 +1889,7 @@ NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 ti
return NULL; return NULL;
} }
NX_INLINE u64 titleGetApplicationIdByMetaKey(const NcmContentMetaKey *meta_key) NX_INLINE u64 titleGetApplicationIdByContentMetaKey(const NcmContentMetaKey *meta_key)
{ {
if (!meta_key) return 0; if (!meta_key) return 0;
@ -1961,7 +1989,7 @@ static bool titleGenerateTitleInfoEntriesForTitleStorage(TitleStorage *title_sto
utilsGenerateFormattedSizeString((double)cur_title_info->size, cur_title_info->size_str, sizeof(cur_title_info->size_str)); utilsGenerateFormattedSizeString((double)cur_title_info->size, cur_title_info->size_str, sizeof(cur_title_info->size_str));
/* Retrieve application metadata. */ /* Retrieve application metadata. */
u64 app_id = titleGetApplicationIdByMetaKey(&(cur_title_info->meta_key)); u64 app_id = titleGetApplicationIdByContentMetaKey(&(cur_title_info->meta_key));
cur_title_info->app_metadata = titleFindApplicationMetadataByTitleId(app_id, storage_id == NcmStorageId_BuiltInSystem, 0); cur_title_info->app_metadata = titleFindApplicationMetadataByTitleId(app_id, storage_id == NcmStorageId_BuiltInSystem, 0);
if (!cur_title_info->app_metadata && storage_id == NcmStorageId_BuiltInSystem) if (!cur_title_info->app_metadata && storage_id == NcmStorageId_BuiltInSystem)
{ {
@ -2203,7 +2231,7 @@ static void titleUpdateTitleInfoLinkedLists(void)
{ {
/* We're dealing with a patch, an add-on content or an add-on content patch. */ /* We're dealing with a patch, an add-on content or an add-on content patch. */
/* We'll just retrieve a pointer to the first matching user application entry and use it to set a pointer to an application metadata entry. */ /* We'll just retrieve a pointer to the first matching user application entry and use it to set a pointer to an application metadata entry. */
u64 app_id = titleGetApplicationIdByMetaKey(&(child_info->meta_key)); u64 app_id = titleGetApplicationIdByContentMetaKey(&(child_info->meta_key));
TitleInfo *parent = _titleGetInfoFromStorageByTitleId(NcmStorageId_Any, app_id); TitleInfo *parent = _titleGetInfoFromStorageByTitleId(NcmStorageId_Any, app_id);
if (parent) if (parent)
{ {
@ -2367,7 +2395,7 @@ static bool titleRefreshGameCardTitleInfo(void)
if (!cur_title_info) continue; if (!cur_title_info) continue;
/* Do not proceed if application metadata has already been retrieved, or if we can successfully retrieve it. */ /* Do not proceed if application metadata has already been retrieved, or if we can successfully retrieve it. */
u64 app_id = titleGetApplicationIdByMetaKey(&(cur_title_info->meta_key)); u64 app_id = titleGetApplicationIdByContentMetaKey(&(cur_title_info->meta_key));
if (cur_title_info->app_metadata != NULL || (cur_title_info->app_metadata = titleFindApplicationMetadataByTitleId(app_id, false, extra_app_count)) != NULL) continue; if (cur_title_info->app_metadata != NULL || (cur_title_info->app_metadata = titleFindApplicationMetadataByTitleId(app_id, false, extra_app_count)) != NULL) continue;
/* Retrieve application metadata pointer. */ /* Retrieve application metadata pointer. */

View file

@ -160,7 +160,7 @@ enum usb_supported_speed {
}; };
/// Imported from libusb, with some adjustments. /// Imported from libusb, with some adjustments.
struct PACKED usb_bos_descriptor { struct NX_PACKED usb_bos_descriptor {
u8 bLength; u8 bLength;
u8 bDescriptorType; ///< Must match USB_DT_BOS. u8 bDescriptorType; ///< Must match USB_DT_BOS.
u16 wTotalLength; ///< Length of this descriptor and all of its sub descriptors. u16 wTotalLength; ///< Length of this descriptor and all of its sub descriptors.
@ -170,7 +170,7 @@ struct PACKED usb_bos_descriptor {
NXDT_ASSERT(struct usb_bos_descriptor, 0x5); NXDT_ASSERT(struct usb_bos_descriptor, 0x5);
/// Imported from libusb, with some adjustments. /// Imported from libusb, with some adjustments.
struct PACKED usb_2_0_extension_descriptor { struct NX_PACKED usb_2_0_extension_descriptor {
u8 bLength; u8 bLength;
u8 bDescriptorType; ///< Must match USB_DT_DEVICE_CAPABILITY. u8 bDescriptorType; ///< Must match USB_DT_DEVICE_CAPABILITY.
u8 bDevCapabilityType; ///< Must match USB_BT_USB_2_0_EXTENSION. u8 bDevCapabilityType; ///< Must match USB_BT_USB_2_0_EXTENSION.
@ -180,7 +180,7 @@ struct PACKED usb_2_0_extension_descriptor {
NXDT_ASSERT(struct usb_2_0_extension_descriptor, 0x7); NXDT_ASSERT(struct usb_2_0_extension_descriptor, 0x7);
/// Imported from libusb, with some adjustments. /// Imported from libusb, with some adjustments.
struct PACKED usb_ss_usb_device_capability_descriptor { struct NX_PACKED usb_ss_usb_device_capability_descriptor {
u8 bLength; u8 bLength;
u8 bDescriptorType; ///< Must match USB_DT_DEVICE_CAPABILITY. u8 bDescriptorType; ///< Must match USB_DT_DEVICE_CAPABILITY.
u8 bDevCapabilityType; ///< Must match USB_BT_SS_USB_DEVICE_CAPABILITY. u8 bDevCapabilityType; ///< Must match USB_BT_SS_USB_DEVICE_CAPABILITY.

View file

@ -92,7 +92,7 @@ namespace nxdt::utils {
} }
#endif /* LOG_LEVEL < LOG_LEVEL_NONE */ #endif /* LOG_LEVEL < LOG_LEVEL_NONE */
static void NORETURN AbortProgramExecution(std::string str) static void NX_NORETURN AbortProgramExecution(std::string str)
{ {
if (g_borealisInitialized) if (g_borealisInitialized)
{ {