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:
commit
62fd26b622
15 changed files with 329 additions and 155 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
@ -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')
|
|
@ -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())
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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__ */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue