mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-12-23 00:52:10 +00:00
Various changes.
* Cancel USB file transfer if something goes wrong during a NSP transfer via USB. * Added SD card RomFS dumper. * Further optimizations to the *WriteNcaPatch() functions. * Change struct naming in nso.c/h (thanks @0Liam !). * Replace fsp-usb with libusbhsfs. ( ͡° ͜ʖ ͡°)
This commit is contained in:
parent
5f0f281e2d
commit
e8956c0e4b
15 changed files with 753 additions and 211 deletions
4
Makefile
4
Makefile
|
@ -69,13 +69,13 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
|||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lxml2 -lz -lnx -ljson-c -lm `freetype-config --libs` -lturbojpeg
|
||||
LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lxml2 -lz -lusbhsfsd -lnx -ljson-c -lm `freetype-config --libs` -lturbojpeg
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/../libusbhsfs
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
|
|
|
@ -94,6 +94,7 @@ static void nspDump(TitleInfo *title_info)
|
|||
bool patch_sua = options[4].val;
|
||||
bool patch_screenshot = options[5].val;
|
||||
bool patch_video_capture = options[6].val;
|
||||
bool success = false, usb_conn = false;
|
||||
|
||||
u8 *buf = NULL;
|
||||
char *dump_name = NULL, *path = NULL;
|
||||
|
@ -458,7 +459,6 @@ static void nspDump(TitleInfo *title_info)
|
|||
consolePrint("waiting for usb connection... ");
|
||||
|
||||
time_t start = time(NULL);
|
||||
bool usb_conn = false;
|
||||
|
||||
while(true)
|
||||
{
|
||||
|
@ -727,7 +727,11 @@ static void nspDump(TitleInfo *title_info)
|
|||
start = (time(NULL) - start);
|
||||
consolePrint("process successfully completed in %lu seconds!\n", start);
|
||||
|
||||
success = true;
|
||||
|
||||
end:
|
||||
if (usb_conn && !success) usbCancelFileTransfer();
|
||||
|
||||
pfsFreeFileContext(&pfs_file_ctx);
|
||||
|
||||
if (raw_cert_chain) free(raw_cert_chain);
|
||||
|
|
657
code_templates/sd_romfs_dumper.c
Normal file
657
code_templates/sd_romfs_dumper.c
Normal file
|
@ -0,0 +1,657 @@
|
|||
/*
|
||||
* main.c
|
||||
*
|
||||
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
|
||||
*
|
||||
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
|
||||
*
|
||||
* nxdumptool is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
#include "bktr.h"
|
||||
#include "gamecard.h"
|
||||
#include "title.h"
|
||||
|
||||
#define BLOCK_SIZE 0x800000
|
||||
|
||||
static Mutex g_fileMutex = 0;
|
||||
static CondVar g_readCondvar = 0, g_writeCondvar = 0;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FILE *fd;
|
||||
RomFileSystemContext *romfs_ctx;
|
||||
BktrContext *bktr_ctx;
|
||||
void *data;
|
||||
size_t data_size;
|
||||
size_t data_written;
|
||||
size_t total_size;
|
||||
bool read_error;
|
||||
bool write_error;
|
||||
bool transfer_cancelled;
|
||||
} ThreadSharedData;
|
||||
|
||||
static void consolePrint(const char *text, ...)
|
||||
{
|
||||
va_list v;
|
||||
va_start(v, text);
|
||||
vfprintf(stdout, text, v);
|
||||
va_end(v);
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
||||
static void read_thread_func(void *arg)
|
||||
{
|
||||
ThreadSharedData *shared_data = (ThreadSharedData*)arg;
|
||||
if (!shared_data || shared_data->fd || !shared_data->data || !shared_data->total_size || (!shared_data->romfs_ctx && !shared_data->bktr_ctx))
|
||||
{
|
||||
shared_data->read_error = true;
|
||||
goto end;
|
||||
}
|
||||
|
||||
u8 *buf = malloc(BLOCK_SIZE);
|
||||
if (!buf)
|
||||
{
|
||||
shared_data->read_error = true;
|
||||
goto end;
|
||||
}
|
||||
|
||||
u64 file_table_offset = 0;
|
||||
u64 file_table_size = (shared_data->bktr_ctx ? shared_data->bktr_ctx->patch_romfs_ctx.file_table_size : shared_data->romfs_ctx->file_table_size);
|
||||
RomFileSystemFileEntry *file_entry = NULL;
|
||||
|
||||
char path[FS_MAX_PATH] = {0};
|
||||
sprintf(path, "sdmc:/romfs");
|
||||
|
||||
while(file_table_offset < file_table_size)
|
||||
{
|
||||
/* Check if the transfer has been cancelled by the user. */
|
||||
if (shared_data->transfer_cancelled)
|
||||
{
|
||||
condvarWakeAll(&g_writeCondvar);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Wait until the previous file data chunk has been written. */
|
||||
mutexLock(&g_fileMutex);
|
||||
if (shared_data->data_size && !shared_data->write_error) condvarWait(&g_readCondvar, &g_fileMutex);
|
||||
mutexUnlock(&g_fileMutex);
|
||||
if (shared_data->write_error) break;
|
||||
|
||||
/* Close file. */
|
||||
if (shared_data->fd)
|
||||
{
|
||||
fclose(shared_data->fd);
|
||||
shared_data->fd = NULL;
|
||||
}
|
||||
|
||||
/* Retrieve RomFS file entry information. */
|
||||
if (shared_data->bktr_ctx)
|
||||
{
|
||||
shared_data->read_error = (!(file_entry = bktrGetFileEntryByOffset(shared_data->bktr_ctx, file_table_offset)) || \
|
||||
!bktrGeneratePathFromFileEntry(shared_data->bktr_ctx, file_entry, path + 11, FS_MAX_PATH - 11, RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly));
|
||||
} else {
|
||||
shared_data->read_error = (!(file_entry = romfsGetFileEntryByOffset(shared_data->romfs_ctx, file_table_offset)) || \
|
||||
!romfsGeneratePathFromFileEntry(shared_data->romfs_ctx, file_entry, path + 11, FS_MAX_PATH - 11, RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly));
|
||||
}
|
||||
|
||||
if (shared_data->read_error)
|
||||
{
|
||||
condvarWakeAll(&g_writeCondvar);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Create directory tree. */
|
||||
utilsCreateDirectoryTree(path, false);
|
||||
|
||||
/* Create file. */
|
||||
shared_data->read_error = ((shared_data->fd = fopen(path, "wb")) == NULL);
|
||||
if (shared_data->read_error)
|
||||
{
|
||||
condvarWakeAll(&g_writeCondvar);
|
||||
break;
|
||||
}
|
||||
|
||||
for(u64 offset = 0, blksize = BLOCK_SIZE; offset < file_entry->size; offset += blksize)
|
||||
{
|
||||
if (blksize > (file_entry->size - offset)) blksize = (file_entry->size - offset);
|
||||
|
||||
/* Check if the transfer has been cancelled by the user. */
|
||||
if (shared_data->transfer_cancelled)
|
||||
{
|
||||
condvarWakeAll(&g_writeCondvar);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read current file data chunk. */
|
||||
shared_data->read_error = (shared_data->bktr_ctx ? !bktrReadFileEntryData(shared_data->bktr_ctx, file_entry, buf, blksize, offset) : \
|
||||
!romfsReadFileEntryData(shared_data->romfs_ctx, file_entry, buf, blksize, offset));
|
||||
if (shared_data->read_error)
|
||||
{
|
||||
condvarWakeAll(&g_writeCondvar);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Wait until the previous file data chunk has been written. */
|
||||
mutexLock(&g_fileMutex);
|
||||
|
||||
if (shared_data->data_size && !shared_data->write_error) condvarWait(&g_readCondvar, &g_fileMutex);
|
||||
|
||||
if (shared_data->write_error)
|
||||
{
|
||||
mutexUnlock(&g_fileMutex);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy current file data chunk to the shared buffer. */
|
||||
memcpy(shared_data->data, buf, blksize);
|
||||
shared_data->data_size = blksize;
|
||||
|
||||
/* Wake up the write thread to continue writing data. */
|
||||
mutexUnlock(&g_fileMutex);
|
||||
condvarWakeAll(&g_writeCondvar);
|
||||
}
|
||||
|
||||
if (shared_data->read_error || shared_data->write_error || shared_data->transfer_cancelled) break;
|
||||
|
||||
file_table_offset += ALIGN_UP(sizeof(RomFileSystemFileEntry) + file_entry->name_length, 4);
|
||||
}
|
||||
|
||||
/* Wait until the previous file data chunk has been written. */
|
||||
mutexLock(&g_fileMutex);
|
||||
if (shared_data->data_size && !shared_data->write_error) condvarWait(&g_readCondvar, &g_fileMutex);
|
||||
mutexUnlock(&g_fileMutex);
|
||||
|
||||
if (shared_data->fd)
|
||||
{
|
||||
fclose(shared_data->fd);
|
||||
shared_data->fd = NULL;
|
||||
}
|
||||
|
||||
if ((shared_data->read_error || shared_data->write_error || shared_data->transfer_cancelled) && *path) remove(path);
|
||||
|
||||
free(buf);
|
||||
|
||||
end:
|
||||
threadExit();
|
||||
}
|
||||
|
||||
static void write_thread_func(void *arg)
|
||||
{
|
||||
ThreadSharedData *shared_data = (ThreadSharedData*)arg;
|
||||
if (!shared_data || !shared_data->data)
|
||||
{
|
||||
shared_data->write_error = true;
|
||||
goto end;
|
||||
}
|
||||
|
||||
while(shared_data->data_written < shared_data->total_size)
|
||||
{
|
||||
/* Wait until the current file data chunk has been read */
|
||||
mutexLock(&g_fileMutex);
|
||||
|
||||
if (!shared_data->data_size && !shared_data->read_error) condvarWait(&g_writeCondvar, &g_fileMutex);
|
||||
|
||||
if (shared_data->read_error || shared_data->transfer_cancelled || !shared_data->fd)
|
||||
{
|
||||
mutexUnlock(&g_fileMutex);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write current file data chunk */
|
||||
shared_data->write_error = (fwrite(shared_data->data, 1, shared_data->data_size, shared_data->fd) != shared_data->data_size);
|
||||
if (!shared_data->write_error)
|
||||
{
|
||||
shared_data->data_written += shared_data->data_size;
|
||||
shared_data->data_size = 0;
|
||||
}
|
||||
|
||||
/* Wake up the read thread to continue reading data */
|
||||
mutexUnlock(&g_fileMutex);
|
||||
condvarWakeAll(&g_readCondvar);
|
||||
|
||||
if (shared_data->write_error) break;
|
||||
}
|
||||
|
||||
end:
|
||||
threadExit();
|
||||
}
|
||||
|
||||
u8 get_program_id_offset(TitleInfo *info, u32 program_count)
|
||||
{
|
||||
if (program_count <= 1) return 0;
|
||||
|
||||
u8 id_offset = 0;
|
||||
u32 selected_idx = 0, page_size = 30, scroll = 0;
|
||||
char nca_id_str[0x21] = {0};
|
||||
|
||||
NcmContentInfo **content_infos = calloc(program_count, sizeof(NcmContentInfo*));
|
||||
if (!content_infos) return 0;
|
||||
|
||||
for(u32 i = 0, j = 0; i < info->content_count && j < program_count; i++)
|
||||
{
|
||||
if (info->content_infos[i].content_type != NcmContentType_Program) continue;
|
||||
content_infos[j++] = &(info->content_infos[i]);
|
||||
}
|
||||
|
||||
while(true)
|
||||
{
|
||||
consoleClear();
|
||||
printf("select a program nca to dump the romfs from.\n\n");
|
||||
|
||||
for(u32 i = scroll; i < program_count; i++)
|
||||
{
|
||||
if (i >= (scroll + page_size)) break;
|
||||
utilsGenerateHexStringFromData(nca_id_str, sizeof(nca_id_str), content_infos[i]->content_id.c, SHA256_HASH_SIZE / 2);
|
||||
printf("%s%s.nca (ID offset #%u)\n", i == selected_idx ? " -> " : " ", nca_id_str, content_infos[i]->id_offset);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
consoleUpdate(NULL);
|
||||
|
||||
u64 btn_down = 0, btn_held = 0;
|
||||
while(true)
|
||||
{
|
||||
hidScanInput();
|
||||
btn_down = utilsHidKeysAllDown();
|
||||
btn_held = utilsHidKeysAllHeld();
|
||||
if (btn_down || btn_held) break;
|
||||
}
|
||||
|
||||
if (btn_down & KEY_A)
|
||||
{
|
||||
id_offset = content_infos[selected_idx]->id_offset;
|
||||
break;
|
||||
} else
|
||||
if ((btn_down & KEY_DDOWN) || (btn_held & (KEY_LSTICK_DOWN | KEY_RSTICK_DOWN)))
|
||||
{
|
||||
selected_idx++;
|
||||
|
||||
if (selected_idx >= program_count)
|
||||
{
|
||||
if (btn_down & KEY_DDOWN)
|
||||
{
|
||||
selected_idx = scroll = 0;
|
||||
} else {
|
||||
selected_idx = (program_count - 1);
|
||||
}
|
||||
} else
|
||||
if (selected_idx >= (scroll + (page_size / 2)) && program_count > (scroll + page_size))
|
||||
{
|
||||
scroll++;
|
||||
}
|
||||
} else
|
||||
if ((btn_down & KEY_DUP) || (btn_held & (KEY_LSTICK_UP | KEY_RSTICK_UP)))
|
||||
{
|
||||
selected_idx--;
|
||||
|
||||
if (selected_idx == UINT32_MAX)
|
||||
{
|
||||
if (btn_down & KEY_DUP)
|
||||
{
|
||||
selected_idx = (program_count - 1);
|
||||
scroll = (program_count >= page_size ? (program_count - page_size) : 0);
|
||||
} else {
|
||||
selected_idx = 0;
|
||||
}
|
||||
} else
|
||||
if (selected_idx < (scroll + (page_size / 2)) && scroll > 0)
|
||||
{
|
||||
scroll--;
|
||||
}
|
||||
}
|
||||
|
||||
if (btn_held & (KEY_LSTICK_DOWN | KEY_RSTICK_DOWN | KEY_LSTICK_UP | KEY_RSTICK_UP)) svcSleepThread(50000000); // 50 ms
|
||||
}
|
||||
|
||||
free(content_infos);
|
||||
|
||||
return id_offset;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
int ret = 0;
|
||||
|
||||
consoleInit(NULL);
|
||||
|
||||
consolePrint("initializing...\n");
|
||||
|
||||
if (!utilsInitializeResources())
|
||||
{
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
u32 app_count = 0;
|
||||
TitleApplicationMetadata **app_metadata = NULL;
|
||||
TitleUserApplicationData user_app_data = {0};
|
||||
|
||||
u32 selected_idx = 0, page_size = 30, scroll = 0;
|
||||
bool exit_prompt = true;
|
||||
|
||||
u8 *buf = NULL;
|
||||
|
||||
NcaContext *base_nca_ctx = NULL, *update_nca_ctx = NULL;
|
||||
|
||||
RomFileSystemContext romfs_ctx = {0};
|
||||
BktrContext bktr_ctx = {0};
|
||||
|
||||
ThreadSharedData shared_data = {0};
|
||||
Thread read_thread = {0}, write_thread = {0};
|
||||
|
||||
app_metadata = titleGetApplicationMetadataEntries(false, &app_count);
|
||||
if (!app_metadata || !app_count)
|
||||
{
|
||||
consolePrint("app metadata failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("app metadata succeeded\n");
|
||||
|
||||
buf = malloc(BLOCK_SIZE);
|
||||
if (!buf)
|
||||
{
|
||||
consolePrint("buf failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("buf succeeded\n");
|
||||
|
||||
base_nca_ctx = calloc(1, sizeof(NcaContext));
|
||||
if (!base_nca_ctx)
|
||||
{
|
||||
consolePrint("base nca ctx buf failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("base nca ctx buf succeeded\n");
|
||||
|
||||
update_nca_ctx = calloc(1, sizeof(NcaContext));
|
||||
if (!update_nca_ctx)
|
||||
{
|
||||
consolePrint("update nca ctx buf failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("update nca ctx buf succeeded\n");
|
||||
|
||||
utilsSleep(1);
|
||||
|
||||
while(true)
|
||||
{
|
||||
consoleClear();
|
||||
printf("select a user application to dump its romfs.\nif an update is available, patch romfs data will be dumped instead.\ndata will be saved to \"sdmc:/romfs\".\npress b to exit.\n\n");
|
||||
printf("title: %u / %u\n", selected_idx + 1, app_count);
|
||||
printf("selected title: %016lX - %s\n\n", app_metadata[selected_idx]->title_id, app_metadata[selected_idx]->lang_entry.name);
|
||||
|
||||
for(u32 i = scroll; i < app_count; i++)
|
||||
{
|
||||
if (i >= (scroll + page_size)) break;
|
||||
printf("%s%016lX - %s\n", i == selected_idx ? " -> " : " ", app_metadata[i]->title_id, app_metadata[i]->lang_entry.name);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
consoleUpdate(NULL);
|
||||
|
||||
u64 btn_down = 0, btn_held = 0;
|
||||
while(true)
|
||||
{
|
||||
hidScanInput();
|
||||
btn_down = utilsHidKeysAllDown();
|
||||
btn_held = utilsHidKeysAllHeld();
|
||||
if (btn_down || btn_held) break;
|
||||
|
||||
if (titleIsGameCardInfoUpdated())
|
||||
{
|
||||
free(app_metadata);
|
||||
|
||||
app_metadata = titleGetApplicationMetadataEntries(false, &app_count);
|
||||
if (!app_metadata)
|
||||
{
|
||||
consolePrint("\napp metadata failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
selected_idx = scroll = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (btn_down & KEY_A)
|
||||
{
|
||||
if (!titleGetUserApplicationData(app_metadata[selected_idx]->title_id, &user_app_data) || !user_app_data.app_info)
|
||||
{
|
||||
consolePrint("\nthe selected title doesn't have available base content.\n");
|
||||
utilsSleep(3);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
} else
|
||||
if ((btn_down & KEY_DDOWN) || (btn_held & (KEY_LSTICK_DOWN | KEY_RSTICK_DOWN)))
|
||||
{
|
||||
selected_idx++;
|
||||
|
||||
if (selected_idx >= app_count)
|
||||
{
|
||||
if (btn_down & KEY_DDOWN)
|
||||
{
|
||||
selected_idx = scroll = 0;
|
||||
} else {
|
||||
selected_idx = (app_count - 1);
|
||||
}
|
||||
} else
|
||||
if (selected_idx >= (scroll + (page_size / 2)) && app_count > (scroll + page_size))
|
||||
{
|
||||
scroll++;
|
||||
}
|
||||
} else
|
||||
if ((btn_down & KEY_DUP) || (btn_held & (KEY_LSTICK_UP | KEY_RSTICK_UP)))
|
||||
{
|
||||
selected_idx--;
|
||||
|
||||
if (selected_idx == UINT32_MAX)
|
||||
{
|
||||
if (btn_down & KEY_DUP)
|
||||
{
|
||||
selected_idx = (app_count - 1);
|
||||
scroll = (app_count >= page_size ? (app_count - page_size) : 0);
|
||||
} else {
|
||||
selected_idx = 0;
|
||||
}
|
||||
} else
|
||||
if (selected_idx < (scroll + (page_size / 2)) && scroll > 0)
|
||||
{
|
||||
scroll--;
|
||||
}
|
||||
} else
|
||||
if (btn_down & KEY_B)
|
||||
{
|
||||
exit_prompt = false;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (btn_held & (KEY_LSTICK_DOWN | KEY_RSTICK_DOWN | KEY_LSTICK_UP | KEY_RSTICK_UP)) svcSleepThread(50000000); // 50 ms
|
||||
}
|
||||
|
||||
u32 program_count = titleGetContentCountByType(user_app_data.app_info, NcmContentType_Program);
|
||||
if (!program_count)
|
||||
{
|
||||
consolePrint("base app has no program ncas!\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
u8 program_id_offset = get_program_id_offset(user_app_data.app_info, program_count);
|
||||
|
||||
consoleClear();
|
||||
consolePrint("selected title:\n%s (%016lX)\n\n", app_metadata[selected_idx]->lang_entry.name, app_metadata[selected_idx]->title_id + program_id_offset);
|
||||
|
||||
if (!ncaInitializeContext(base_nca_ctx, user_app_data.app_info->storage_id, (user_app_data.app_info->storage_id == NcmStorageId_GameCard ? GameCardHashFileSystemPartitionType_Secure : 0), \
|
||||
titleGetContentInfoByTypeAndIdOffset(user_app_data.app_info, NcmContentType_Program, program_id_offset), NULL))
|
||||
{
|
||||
consolePrint("nca initialize base ctx failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (user_app_data.patch_info)
|
||||
{
|
||||
if (!ncaInitializeContext(update_nca_ctx, user_app_data.patch_info->storage_id, (user_app_data.patch_info->storage_id == NcmStorageId_GameCard ? GameCardHashFileSystemPartitionType_Secure : 0), \
|
||||
titleGetContentInfoByTypeAndIdOffset(user_app_data.patch_info, NcmContentType_Program, program_id_offset), NULL))
|
||||
{
|
||||
consolePrint("nca initialize update ctx failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (!bktrInitializeContext(&bktr_ctx, &(base_nca_ctx->fs_ctx[1]), &(update_nca_ctx->fs_ctx[1])))
|
||||
{
|
||||
consolePrint("bktr initialize ctx failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
shared_data.bktr_ctx = &bktr_ctx;
|
||||
bktrGetTotalDataSize(&bktr_ctx, &(shared_data.total_size));
|
||||
|
||||
consolePrint("bktr initialize ctx succeeded\n");
|
||||
} else {
|
||||
if (!romfsInitializeContext(&romfs_ctx, &(base_nca_ctx->fs_ctx[1])))
|
||||
{
|
||||
consolePrint("romfs initialize ctx failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
shared_data.romfs_ctx = &romfs_ctx;
|
||||
romfsGetTotalDataSize(&romfs_ctx, &(shared_data.total_size));
|
||||
|
||||
consolePrint("romfs initialize ctx succeeded\n");
|
||||
}
|
||||
|
||||
shared_data.fd = NULL;
|
||||
shared_data.data = buf;
|
||||
shared_data.data_size = 0;
|
||||
shared_data.data_written = 0;
|
||||
|
||||
consolePrint("creating threads\n");
|
||||
utilsCreateThread(&read_thread, read_thread_func, &shared_data, 2);
|
||||
utilsCreateThread(&write_thread, write_thread_func, &shared_data, 2);
|
||||
|
||||
u8 prev_time = 0;
|
||||
u64 prev_size = 0;
|
||||
u8 percent = 0;
|
||||
|
||||
time_t btn_cancel_start_tmr = 0, btn_cancel_end_tmr = 0;
|
||||
bool btn_cancel_cur_state = false, btn_cancel_prev_state = false;
|
||||
|
||||
utilsChangeHomeButtonBlockStatus(true);
|
||||
|
||||
consolePrint("hold b to cancel\n\n");
|
||||
|
||||
time_t start = time(NULL);
|
||||
|
||||
while(shared_data.data_written < shared_data.total_size)
|
||||
{
|
||||
if (shared_data.read_error || shared_data.write_error) break;
|
||||
|
||||
time_t now = time(NULL);
|
||||
struct tm *ts = localtime(&now);
|
||||
size_t size = shared_data.data_written;
|
||||
|
||||
hidScanInput();
|
||||
btn_cancel_cur_state = (utilsHidKeysAllHeld() & KEY_B);
|
||||
|
||||
if (btn_cancel_cur_state && btn_cancel_cur_state != btn_cancel_prev_state)
|
||||
{
|
||||
btn_cancel_start_tmr = now;
|
||||
} else
|
||||
if (btn_cancel_cur_state && btn_cancel_cur_state == btn_cancel_prev_state)
|
||||
{
|
||||
btn_cancel_end_tmr = now;
|
||||
if ((btn_cancel_end_tmr - btn_cancel_start_tmr) >= 3)
|
||||
{
|
||||
mutexLock(&g_fileMutex);
|
||||
shared_data.transfer_cancelled = true;
|
||||
mutexUnlock(&g_fileMutex);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
btn_cancel_start_tmr = btn_cancel_end_tmr = 0;
|
||||
}
|
||||
|
||||
btn_cancel_prev_state = btn_cancel_cur_state;
|
||||
|
||||
if (prev_time == ts->tm_sec || prev_size == size) continue;
|
||||
|
||||
percent = (u8)((size * 100) / shared_data.total_size);
|
||||
|
||||
prev_time = ts->tm_sec;
|
||||
prev_size = size;
|
||||
|
||||
printf("%lu / %lu (%u%%) | Time elapsed: %lu\n", size, shared_data.total_size, percent, (now - start));
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
||||
start = (time(NULL) - start);
|
||||
|
||||
consolePrint("\nwaiting for threads to join\n");
|
||||
utilsJoinThread(&read_thread);
|
||||
consolePrint("read_thread done: %lu\n", time(NULL));
|
||||
utilsJoinThread(&write_thread);
|
||||
consolePrint("write_thread done: %lu\n", time(NULL));
|
||||
|
||||
utilsChangeHomeButtonBlockStatus(false);
|
||||
|
||||
if (shared_data.read_error || shared_data.write_error)
|
||||
{
|
||||
consolePrint("i/o error\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (shared_data.transfer_cancelled)
|
||||
{
|
||||
consolePrint("process cancelled\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("process completed in %lu seconds\n", start);
|
||||
|
||||
out2:
|
||||
if (exit_prompt)
|
||||
{
|
||||
consolePrint("press any button to exit\n");
|
||||
utilsWaitForButtonPress(KEY_ANY);
|
||||
}
|
||||
|
||||
romfsFreeContext(&romfs_ctx);
|
||||
bktrFreeContext(&bktr_ctx);
|
||||
|
||||
if (update_nca_ctx) free(update_nca_ctx);
|
||||
|
||||
if (base_nca_ctx) free(base_nca_ctx);
|
||||
|
||||
if (buf) free(buf);
|
||||
|
||||
if (app_metadata) free(app_metadata);
|
||||
|
||||
out:
|
||||
utilsCloseResources();
|
||||
|
||||
consoleExit(NULL);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -43,7 +43,7 @@ bool cnmtInitializeContext(ContentMetaContext *out, NcaContext *nca_ctx)
|
|||
{
|
||||
if (!out || !nca_ctx || !*(nca_ctx->content_id_str) || nca_ctx->content_type != NcmContentType_Meta || nca_ctx->content_size < NCA_FULL_HEADER_LENGTH || \
|
||||
(nca_ctx->storage_id != NcmStorageId_GameCard && !nca_ctx->ncm_storage) || (nca_ctx->storage_id == NcmStorageId_GameCard && !nca_ctx->gamecard_offset) || \
|
||||
nca_ctx->header.content_type != NcaContentType_Meta || !out)
|
||||
nca_ctx->header.content_type != NcaContentType_Meta || nca_ctx->content_type_ctx || !out)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
|
@ -317,17 +317,20 @@ bool cnmtGenerateNcaPatch(ContentMetaContext *cnmt_ctx)
|
|||
|
||||
void cnmtWriteNcaPatch(ContentMetaContext *cnmt_ctx, void *buf, u64 buf_size, u64 buf_offset)
|
||||
{
|
||||
NcaContext *nca_ctx = NULL;
|
||||
NcaHierarchicalSha256Patch *nca_patch = (cnmt_ctx ? &(cnmt_ctx->nca_patch) : NULL);
|
||||
|
||||
/* Using cnmtIsValidContext() here would probably take up precious CPU cycles. */
|
||||
if (!cnmt_ctx || !cnmt_ctx->nca_ctx || cnmt_ctx->nca_ctx->content_type != NcmContentType_Meta || !cnmt_ctx->nca_ctx->content_type_ctx_patch || cnmt_ctx->nca_patch.written) return;
|
||||
if (!nca_patch || nca_patch->written || !(nca_ctx = cnmt_ctx->nca_ctx) || nca_ctx->content_type != NcmContentType_Meta || !nca_ctx->content_type_ctx_patch) return;
|
||||
|
||||
/* Attempt to write Partition FS entry patch. */
|
||||
pfsWriteEntryPatchToMemoryBuffer(&(cnmt_ctx->pfs_ctx), &(cnmt_ctx->nca_patch), buf, buf_size, buf_offset);
|
||||
pfsWriteEntryPatchToMemoryBuffer(&(cnmt_ctx->pfs_ctx), nca_patch, buf, buf_size, buf_offset);
|
||||
|
||||
/* Check if we need to update the NCA content type context patch status. */
|
||||
if (cnmt_ctx->nca_patch.written)
|
||||
if (nca_patch->written)
|
||||
{
|
||||
cnmt_ctx->nca_ctx->content_type_ctx_patch = false;
|
||||
LOGFILE("CNMT Partition FS entry patch successfully written to NCA \"%s\"!", cnmt_ctx->nca_ctx->content_id_str);
|
||||
nca_ctx->content_type_ctx_patch = false;
|
||||
LOGFILE("CNMT Partition FS entry patch successfully written to NCA \"%s\"!", nca_ctx->content_id_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* fspusb.c
|
||||
*
|
||||
* Copyright (c) 2019-2020, XorTroll.
|
||||
* Copyright (c) 2019-2020, DarkMatterCore <pabloacurielz@gmail.com>.
|
||||
*
|
||||
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
|
||||
*
|
||||
* nxdumptool is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define NX_SERVICE_ASSUME_NON_DOMAIN
|
||||
|
||||
#include "utils.h"
|
||||
#include "fspusb.h"
|
||||
#include "service_guard.h"
|
||||
|
||||
static Service g_fspusbSrv;
|
||||
|
||||
NX_GENERATE_SERVICE_GUARD(fspusb);
|
||||
|
||||
Result _fspusbInitialize(void) {
|
||||
return smGetService(&g_fspusbSrv, "fsp-usb");
|
||||
}
|
||||
|
||||
void _fspusbCleanup(void) {
|
||||
serviceClose(&g_fspusbSrv);
|
||||
}
|
||||
|
||||
Service* fspusbGetServiceSession(void) {
|
||||
return &g_fspusbSrv;
|
||||
}
|
||||
|
||||
Result fspusbListMountedDrives(s32 *drives_buf, size_t drive_count, s32 *out_total) {
|
||||
return serviceDispatchOut(&g_fspusbSrv, 0, *out_total,
|
||||
.buffer_attrs = { SfBufferAttr_Out | SfBufferAttr_HipcMapAlias },
|
||||
.buffers = { { drives_buf, drive_count * sizeof(s32) } },
|
||||
);
|
||||
}
|
||||
|
||||
Result fspusbGetDriveFileSystemType(s32 interface_id, FspUsbFileSystemType *out_type) {
|
||||
return serviceDispatchInOut(&g_fspusbSrv, 1, interface_id, *out_type);
|
||||
}
|
||||
|
||||
Result fspusbGetDriveLabel(s32 interface_id, char *out_label, size_t out_label_size) {
|
||||
return serviceDispatchIn(&g_fspusbSrv, 2, interface_id,
|
||||
.buffer_attrs = { SfBufferAttr_Out | SfBufferAttr_HipcMapAlias },
|
||||
.buffers = { { out_label, out_label_size } },
|
||||
);
|
||||
}
|
||||
|
||||
Result fspusbSetDriveLabel(s32 interface_id, const char *label) {
|
||||
char inputlbl[11 + 1] = {0}; /* Actual limit is 11 characters. */
|
||||
strncpy(inputlbl, label, 11);
|
||||
return serviceDispatchIn(&g_fspusbSrv, 3, interface_id,
|
||||
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias },
|
||||
.buffers = { { inputlbl, 11 + 1 } },
|
||||
);
|
||||
}
|
||||
|
||||
Result fspusbOpenDriveFileSystem(s32 interface_id, FsFileSystem *out_fs) {
|
||||
return serviceDispatchIn(&g_fspusbSrv, 4, interface_id,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out_fs->s,
|
||||
);
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* fspusb.h
|
||||
*
|
||||
* Copyright (c) 2019-2020, XorTroll.
|
||||
* Copyright (c) 2019-2020, DarkMatterCore <pabloacurielz@gmail.com>.
|
||||
*
|
||||
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
|
||||
*
|
||||
* nxdumptool is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* nxdumptool is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __FSPUSB_H__
|
||||
#define __FSPUSB_H__
|
||||
|
||||
/// This is basically FatFs' file system types.
|
||||
typedef enum {
|
||||
FspUsbFileSystemType_FAT12 = 1,
|
||||
FspUsbFileSystemType_FAT16 = 2,
|
||||
FspUsbFileSystemType_FAT32 = 3,
|
||||
FspUsbFileSystemType_exFAT = 4
|
||||
} FspUsbFileSystemType;
|
||||
|
||||
/// Initialize fsp-usb.
|
||||
Result fspusbInitialize(void);
|
||||
|
||||
/// Exit fsp-usb.
|
||||
void fspusbExit(void);
|
||||
|
||||
/// Gets the Service object for the actual fsp-usb service session.
|
||||
Service* fspusbGetServiceSession(void);
|
||||
|
||||
Result fspusbListMountedDrives(s32 *drives_buf, size_t drive_count, s32 *out_total);
|
||||
Result fspusbGetDriveFileSystemType(s32 interface_id, FspUsbFileSystemType *out_type);
|
||||
Result fspusbGetDriveLabel(s32 interface_id, char *out_label, size_t out_label_size);
|
||||
Result fspusbSetDriveLabel(s32 interface_id, const char *label);
|
||||
Result fspusbOpenDriveFileSystem(s32 interface_id, FsFileSystem *out_fs);
|
||||
|
||||
#endif /* __FSPUSB_H__ */
|
|
@ -26,7 +26,7 @@ bool legalInfoInitializeContext(LegalInfoContext *out, NcaContext *nca_ctx)
|
|||
{
|
||||
if (!out || !nca_ctx || !*(nca_ctx->content_id_str) || nca_ctx->content_type != NcmContentType_LegalInformation || nca_ctx->content_size < NCA_FULL_HEADER_LENGTH || \
|
||||
(nca_ctx->storage_id != NcmStorageId_GameCard && !nca_ctx->ncm_storage) || (nca_ctx->storage_id == NcmStorageId_GameCard && !nca_ctx->gamecard_offset) || \
|
||||
nca_ctx->header.content_type != NcaContentType_Manual || !out)
|
||||
nca_ctx->header.content_type != NcaContentType_Manual || nca_ctx->content_type_ctx || !out)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
|
|
|
@ -199,7 +199,7 @@ bool nacpInitializeContext(NacpContext *out, NcaContext *nca_ctx)
|
|||
{
|
||||
if (!out || !nca_ctx || !*(nca_ctx->content_id_str) || nca_ctx->content_type != NcmContentType_Control || nca_ctx->content_size < NCA_FULL_HEADER_LENGTH || \
|
||||
(nca_ctx->storage_id != NcmStorageId_GameCard && !nca_ctx->ncm_storage) || (nca_ctx->storage_id == NcmStorageId_GameCard && !nca_ctx->gamecard_offset) || \
|
||||
nca_ctx->header.content_type != NcaContentType_Control || !out)
|
||||
nca_ctx->header.content_type != NcaContentType_Control || nca_ctx->content_type_ctx || !out)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
|
@ -380,17 +380,20 @@ bool nacpGenerateNcaPatch(NacpContext *nacp_ctx, bool patch_sua, bool patch_scre
|
|||
|
||||
void nacpWriteNcaPatch(NacpContext *nacp_ctx, void *buf, u64 buf_size, u64 buf_offset)
|
||||
{
|
||||
NcaContext *nca_ctx = NULL;
|
||||
RomFileSystemFileEntryPatch *nca_patch = (nacp_ctx ? &(nacp_ctx->nca_patch) : NULL);
|
||||
|
||||
/* Using nacpIsValidContext() here would probably take up precious CPU cycles. */
|
||||
if (!nacp_ctx || !nacp_ctx->nca_ctx || nacp_ctx->nca_ctx->content_type != NcmContentType_Control || !nacp_ctx->nca_ctx->content_type_ctx_patch || nacp_ctx->nca_patch.written) return;
|
||||
if (!nca_patch || nca_patch->written || !(nca_ctx = nacp_ctx->nca_ctx) || nca_ctx->content_type != NcmContentType_Control || !nca_ctx->content_type_ctx_patch) return;
|
||||
|
||||
/* Attempt to write RomFS file entry patch. */
|
||||
romfsWriteFileEntryPatchToMemoryBuffer(&(nacp_ctx->romfs_ctx), &(nacp_ctx->nca_patch), buf, buf_size, buf_offset);
|
||||
romfsWriteFileEntryPatchToMemoryBuffer(&(nacp_ctx->romfs_ctx), nca_patch, buf, buf_size, buf_offset);
|
||||
|
||||
/* Check if we need to update the NCA content type context patch status. */
|
||||
if (nacp_ctx->nca_patch.written)
|
||||
if (nca_patch->written)
|
||||
{
|
||||
nacp_ctx->nca_ctx->content_type_ctx_patch = false;
|
||||
LOGFILE("NACP RomFS file entry patch successfully written to NCA \"%s\"!", nacp_ctx->nca_ctx->content_id_str);
|
||||
nca_ctx->content_type_ctx_patch = false;
|
||||
LOGFILE("NACP RomFS file entry patch successfully written to NCA \"%s\"!", nca_ctx->content_id_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ bool npdmInitializeContext(NpdmContext *out, PartitionFileSystemContext *pfs_ctx
|
|||
npdmFreeContext(out);
|
||||
|
||||
/* Get 'main.npdm' file entry. */
|
||||
out->nca_ctx = nca_ctx;
|
||||
out->pfs_ctx = pfs_ctx;
|
||||
if (!(out->pfs_entry = pfsGetEntryByName(out->pfs_ctx, "main.npdm")))
|
||||
{
|
||||
|
@ -260,14 +261,14 @@ end:
|
|||
|
||||
bool npdmGenerateNcaPatch(NpdmContext *npdm_ctx)
|
||||
{
|
||||
NcaContext *nca_ctx = NULL;
|
||||
|
||||
if (!npdmIsValidContext(npdm_ctx) || !(nca_ctx = (NcaContext*)npdm_ctx->pfs_ctx->nca_fs_ctx->nca_ctx) || nca_ctx->content_type != NcmContentType_Program)
|
||||
if (!npdmIsValidContext(npdm_ctx) || npdm_ctx->nca_ctx->content_type != NcmContentType_Program)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
NcaContext *nca_ctx = npdm_ctx->nca_ctx;
|
||||
|
||||
/* Check if we really need to generate this patch. */
|
||||
if (!ncaIsHeaderDirty(nca_ctx))
|
||||
{
|
||||
|
@ -301,16 +302,16 @@ bool npdmGenerateNcaPatch(NpdmContext *npdm_ctx)
|
|||
void npdmWriteNcaPatch(NpdmContext *npdm_ctx, void *buf, u64 buf_size, u64 buf_offset)
|
||||
{
|
||||
NcaContext *nca_ctx = NULL;
|
||||
NcaHierarchicalSha256Patch *nca_patch = (npdm_ctx ? &(npdm_ctx->nca_patch) : NULL);
|
||||
|
||||
/* Using npdmIsValidContext() here would probably take up precious CPU cycles. */
|
||||
if (!npdm_ctx || !npdm_ctx->pfs_ctx || !npdm_ctx->pfs_ctx->nca_fs_ctx || !(nca_ctx = (NcaContext*)npdm_ctx->pfs_ctx->nca_fs_ctx->nca_ctx) || nca_ctx->content_type != NcmContentType_Program || \
|
||||
!nca_ctx->content_type_ctx_patch || npdm_ctx->nca_patch.written) return;
|
||||
if (!nca_patch || nca_patch->written || !(nca_ctx = npdm_ctx->nca_ctx) || nca_ctx->content_type != NcmContentType_Program || !nca_ctx->content_type_ctx_patch) return;
|
||||
|
||||
/* Attempt to write Partition FS entry patch. */
|
||||
pfsWriteEntryPatchToMemoryBuffer(npdm_ctx->pfs_ctx, &(npdm_ctx->nca_patch), buf, buf_size, buf_offset);
|
||||
pfsWriteEntryPatchToMemoryBuffer(npdm_ctx->pfs_ctx, nca_patch, buf, buf_size, buf_offset);
|
||||
|
||||
/* Check if we need to update the NCA content type context patch status. */
|
||||
if (npdm_ctx->nca_patch.written)
|
||||
if (nca_patch->written)
|
||||
{
|
||||
nca_ctx->content_type_ctx_patch = false;
|
||||
LOGFILE("NPDM Partition FS entry patch successfully written to NCA \"%s\"!", nca_ctx->content_id_str);
|
||||
|
|
|
@ -534,6 +534,7 @@ typedef struct {
|
|||
} NpdmKernelCapabilityDescriptorEntry;
|
||||
|
||||
typedef struct {
|
||||
NcaContext *nca_ctx; ///< Pointer to the NCA context for the Program NCA from which NPDM data is retrieved.
|
||||
PartitionFileSystemContext *pfs_ctx; ///< PartitionFileSystemContext for the Program NCA FS section #0, which is where the NPDM is stored.
|
||||
PartitionFileSystemEntry *pfs_entry; ///< PartitionFileSystemEntry for the NPDM in the Program NCA FS section #0. Used to generate a NcaHierarchicalSha256Patch if needed.
|
||||
NcaHierarchicalSha256Patch nca_patch; ///< NcaHierarchicalSha256Patch generated if NPDM modifications are needed. Used to seamlessly replace Program NCA data while writing it.
|
||||
|
@ -572,7 +573,8 @@ NX_INLINE void npdmFreeContext(NpdmContext *npdm_ctx)
|
|||
|
||||
NX_INLINE bool npdmIsValidContext(NpdmContext *npdm_ctx)
|
||||
{
|
||||
return (npdm_ctx && npdm_ctx->pfs_ctx && npdm_ctx->pfs_entry && npdm_ctx->raw_data && npdm_ctx->raw_data_size && npdm_ctx->meta_header && npdm_ctx->acid_header && npdm_ctx->acid_fac_descriptor && \
|
||||
return (npdm_ctx && npdm_ctx->nca_ctx && npdm_ctx->pfs_ctx && npdm_ctx->pfs_entry && npdm_ctx->raw_data && npdm_ctx->raw_data_size && npdm_ctx->meta_header && npdm_ctx->acid_header && \
|
||||
npdm_ctx->acid_fac_descriptor && \
|
||||
((npdm_ctx->acid_header->srv_access_control_size && npdm_ctx->acid_sac_descriptor) || (!npdm_ctx->acid_header->srv_access_control_size && !npdm_ctx->acid_sac_descriptor)) && \
|
||||
((npdm_ctx->acid_header->kernel_capability_size && npdm_ctx->acid_kc_descriptor) || (!npdm_ctx->acid_header->kernel_capability_size && !npdm_ctx->acid_kc_descriptor)) && \
|
||||
npdm_ctx->aci_header && npdm_ctx->aci_fac_data && \
|
||||
|
|
72
source/nso.c
72
source/nso.c
|
@ -72,33 +72,33 @@ bool nsoInitializeContext(NsoContext *out, PartitionFileSystemContext *pfs_ctx,
|
|||
goto end;
|
||||
}
|
||||
|
||||
if (out->nso_header.text_segment_header.file_offset < sizeof(NsoHeader) || !out->nso_header.text_segment_header.size || \
|
||||
((out->nso_header.flags & NsoFlags_TextCompress) && (!out->nso_header.text_file_size || out->nso_header.text_file_size > out->nso_header.text_segment_header.size)) || \
|
||||
(!(out->nso_header.flags & NsoFlags_TextCompress) && out->nso_header.text_file_size != out->nso_header.text_segment_header.size) || \
|
||||
(out->nso_header.text_segment_header.file_offset + out->nso_header.text_file_size) > pfs_entry->size)
|
||||
if (out->nso_header.text_segment_info.file_offset < sizeof(NsoHeader) || !out->nso_header.text_segment_info.size || \
|
||||
((out->nso_header.flags & NsoFlags_TextCompress) && (!out->nso_header.text_file_size || out->nso_header.text_file_size > out->nso_header.text_segment_info.size)) || \
|
||||
(!(out->nso_header.flags & NsoFlags_TextCompress) && out->nso_header.text_file_size != out->nso_header.text_segment_info.size) || \
|
||||
(out->nso_header.text_segment_info.file_offset + out->nso_header.text_file_size) > pfs_entry->size)
|
||||
{
|
||||
LOGFILE("Invalid .text segment offset/size for NSO \"%s\"! (0x%08X, 0x%08X, 0x%08X).", out->nso_filename, out->nso_header.text_segment_header.file_offset, out->nso_header.text_file_size, \
|
||||
out->nso_header.text_segment_header.size);
|
||||
LOGFILE("Invalid .text segment offset/size for NSO \"%s\"! (0x%08X, 0x%08X, 0x%08X).", out->nso_filename, out->nso_header.text_segment_info.file_offset, out->nso_header.text_file_size, \
|
||||
out->nso_header.text_segment_info.size);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (out->nso_header.rodata_segment_header.file_offset < sizeof(NsoHeader) || !out->nso_header.rodata_segment_header.size || \
|
||||
((out->nso_header.flags & NsoFlags_RoCompress) && (!out->nso_header.rodata_file_size || out->nso_header.rodata_file_size > out->nso_header.rodata_segment_header.size)) || \
|
||||
(!(out->nso_header.flags & NsoFlags_RoCompress) && out->nso_header.rodata_file_size != out->nso_header.rodata_segment_header.size) || \
|
||||
(out->nso_header.rodata_segment_header.file_offset + out->nso_header.rodata_file_size) > pfs_entry->size)
|
||||
if (out->nso_header.rodata_segment_info.file_offset < sizeof(NsoHeader) || !out->nso_header.rodata_segment_info.size || \
|
||||
((out->nso_header.flags & NsoFlags_RoCompress) && (!out->nso_header.rodata_file_size || out->nso_header.rodata_file_size > out->nso_header.rodata_segment_info.size)) || \
|
||||
(!(out->nso_header.flags & NsoFlags_RoCompress) && out->nso_header.rodata_file_size != out->nso_header.rodata_segment_info.size) || \
|
||||
(out->nso_header.rodata_segment_info.file_offset + out->nso_header.rodata_file_size) > pfs_entry->size)
|
||||
{
|
||||
LOGFILE("Invalid .rodata segment offset/size for NSO \"%s\"! (0x%08X, 0x%08X, 0x%08X).", out->nso_filename, out->nso_header.rodata_segment_header.file_offset, out->nso_header.rodata_file_size, \
|
||||
out->nso_header.rodata_segment_header.size);
|
||||
LOGFILE("Invalid .rodata segment offset/size for NSO \"%s\"! (0x%08X, 0x%08X, 0x%08X).", out->nso_filename, out->nso_header.rodata_segment_info.file_offset, out->nso_header.rodata_file_size, \
|
||||
out->nso_header.rodata_segment_info.size);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (out->nso_header.data_segment_header.file_offset < sizeof(NsoHeader) || !out->nso_header.data_segment_header.size || \
|
||||
((out->nso_header.flags & NsoFlags_DataCompress) && (!out->nso_header.data_file_size || out->nso_header.data_file_size > out->nso_header.data_segment_header.size)) || \
|
||||
(!(out->nso_header.flags & NsoFlags_DataCompress) && out->nso_header.data_file_size != out->nso_header.data_segment_header.size) || \
|
||||
(out->nso_header.data_segment_header.file_offset + out->nso_header.data_file_size) > pfs_entry->size)
|
||||
if (out->nso_header.data_segment_info.file_offset < sizeof(NsoHeader) || !out->nso_header.data_segment_info.size || \
|
||||
((out->nso_header.flags & NsoFlags_DataCompress) && (!out->nso_header.data_file_size || out->nso_header.data_file_size > out->nso_header.data_segment_info.size)) || \
|
||||
(!(out->nso_header.flags & NsoFlags_DataCompress) && out->nso_header.data_file_size != out->nso_header.data_segment_info.size) || \
|
||||
(out->nso_header.data_segment_info.file_offset + out->nso_header.data_file_size) > pfs_entry->size)
|
||||
{
|
||||
LOGFILE("Invalid .data segment offset/size for NSO \"%s\"! (0x%08X, 0x%08X, 0x%08X).", out->nso_filename, out->nso_header.data_segment_header.file_offset, out->nso_header.data_file_size, \
|
||||
out->nso_header.data_segment_header.size);
|
||||
LOGFILE("Invalid .data segment offset/size for NSO \"%s\"! (0x%08X, 0x%08X, 0x%08X).", out->nso_filename, out->nso_header.data_segment_info.file_offset, out->nso_header.data_file_size, \
|
||||
out->nso_header.data_segment_info.size);
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
@ -107,19 +107,19 @@ bool nsoInitializeContext(NsoContext *out, PartitionFileSystemContext *pfs_ctx,
|
|||
LOGFILE("Invalid module name offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.module_name_offset, out->nso_header.module_name_size);
|
||||
}
|
||||
|
||||
if (out->nso_header.api_info_section_header.size && (out->nso_header.api_info_section_header.offset + out->nso_header.api_info_section_header.size) > out->nso_header.rodata_segment_header.size)
|
||||
if (out->nso_header.api_info_section_info.size && (out->nso_header.api_info_section_info.offset + out->nso_header.api_info_section_info.size) > out->nso_header.rodata_segment_info.size)
|
||||
{
|
||||
LOGFILE("Invalid .api_info section offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.api_info_section_header.offset, out->nso_header.api_info_section_header.size);
|
||||
LOGFILE("Invalid .api_info section offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.api_info_section_info.offset, out->nso_header.api_info_section_info.size);
|
||||
}
|
||||
|
||||
if (!out->nso_header.dynstr_section_header.size || (out->nso_header.dynstr_section_header.offset + out->nso_header.dynstr_section_header.size) > out->nso_header.rodata_segment_header.size)
|
||||
if (!out->nso_header.dynstr_section_info.size || (out->nso_header.dynstr_section_info.offset + out->nso_header.dynstr_section_info.size) > out->nso_header.rodata_segment_info.size)
|
||||
{
|
||||
LOGFILE("Invalid .dynstr section offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.dynstr_section_header.offset, out->nso_header.dynstr_section_header.size);
|
||||
LOGFILE("Invalid .dynstr section offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.dynstr_section_info.offset, out->nso_header.dynstr_section_info.size);
|
||||
}
|
||||
|
||||
if (!out->nso_header.dynsym_section_header.size || (out->nso_header.dynsym_section_header.offset + out->nso_header.dynsym_section_header.size) > out->nso_header.rodata_segment_header.size)
|
||||
if (!out->nso_header.dynsym_section_info.size || (out->nso_header.dynsym_section_info.offset + out->nso_header.dynsym_section_info.size) > out->nso_header.rodata_segment_info.size)
|
||||
{
|
||||
LOGFILE("Invalid .dynsym section offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.dynsym_section_header.offset, out->nso_header.dynsym_section_header.size);
|
||||
LOGFILE("Invalid .dynsym section offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.dynsym_section_info.offset, out->nso_header.dynsym_section_info.size);
|
||||
}
|
||||
|
||||
/* Get module name. */
|
||||
|
@ -132,16 +132,16 @@ bool nsoInitializeContext(NsoContext *out, PartitionFileSystemContext *pfs_ctx,
|
|||
if (!nsoGetModuleInfoName(out, rodata_buf)) goto end;
|
||||
|
||||
/* Get .api_info section data. */
|
||||
if (!nsoGetSectionFromRodataSegment(out, rodata_buf, (u8**)&(out->rodata_api_info_section), out->nso_header.api_info_section_header.offset, out->nso_header.api_info_section_header.size)) goto end;
|
||||
out->rodata_api_info_section_size = out->nso_header.api_info_section_header.size;
|
||||
if (!nsoGetSectionFromRodataSegment(out, rodata_buf, (u8**)&(out->rodata_api_info_section), out->nso_header.api_info_section_info.offset, out->nso_header.api_info_section_info.size)) goto end;
|
||||
out->rodata_api_info_section_size = out->nso_header.api_info_section_info.size;
|
||||
|
||||
/* Get .dynstr section data. */
|
||||
if (!nsoGetSectionFromRodataSegment(out, rodata_buf, (u8**)&(out->rodata_dynstr_section), out->nso_header.dynstr_section_header.offset, out->nso_header.dynstr_section_header.size)) goto end;
|
||||
out->rodata_dynstr_section_size = out->nso_header.dynstr_section_header.size;
|
||||
if (!nsoGetSectionFromRodataSegment(out, rodata_buf, (u8**)&(out->rodata_dynstr_section), out->nso_header.dynstr_section_info.offset, out->nso_header.dynstr_section_info.size)) goto end;
|
||||
out->rodata_dynstr_section_size = out->nso_header.dynstr_section_info.size;
|
||||
|
||||
/* Get .dynsym section data. */
|
||||
if (!nsoGetSectionFromRodataSegment(out, rodata_buf, &(out->rodata_dynsym_section), out->nso_header.dynsym_section_header.offset, out->nso_header.dynsym_section_header.size)) goto end;
|
||||
out->rodata_dynsym_section_size = out->nso_header.dynsym_section_header.size;
|
||||
if (!nsoGetSectionFromRodataSegment(out, rodata_buf, &(out->rodata_dynsym_section), out->nso_header.dynsym_section_info.offset, out->nso_header.dynsym_section_info.size)) goto end;
|
||||
out->rodata_dynsym_section_size = out->nso_header.dynsym_section_info.size;
|
||||
|
||||
success = true;
|
||||
|
||||
|
@ -197,10 +197,10 @@ static u8 *nsoGetRodataSegment(NsoContext *nso_ctx)
|
|||
bool compressed = (nso_ctx->nso_header.flags & NsoFlags_RoCompress), verify = (nso_ctx->nso_header.flags & NsoFlags_RoHash);
|
||||
|
||||
u8 *rodata_buf = NULL;
|
||||
u64 rodata_buf_size = (compressed ? LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(nso_ctx->nso_header.rodata_segment_header.size) : nso_ctx->nso_header.rodata_segment_header.size);
|
||||
u64 rodata_buf_size = (compressed ? LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(nso_ctx->nso_header.rodata_segment_info.size) : nso_ctx->nso_header.rodata_segment_info.size);
|
||||
|
||||
u8 *rodata_read_ptr = NULL;
|
||||
u64 rodata_read_size = (compressed ? nso_ctx->nso_header.rodata_file_size : nso_ctx->nso_header.rodata_segment_header.size);
|
||||
u64 rodata_read_size = (compressed ? nso_ctx->nso_header.rodata_file_size : nso_ctx->nso_header.rodata_segment_info.size);
|
||||
|
||||
u8 rodata_hash[SHA256_HASH_SIZE] = {0};
|
||||
|
||||
|
@ -216,7 +216,7 @@ static u8 *nsoGetRodataSegment(NsoContext *nso_ctx)
|
|||
rodata_read_ptr = (compressed ? (rodata_buf + (rodata_buf_size - nso_ctx->nso_header.rodata_file_size)) : rodata_buf);
|
||||
|
||||
/* Read .rodata segment data. */
|
||||
if (!pfsReadEntryData(nso_ctx->pfs_ctx, nso_ctx->pfs_entry, rodata_read_ptr, rodata_read_size, nso_ctx->nso_header.rodata_segment_header.file_offset))
|
||||
if (!pfsReadEntryData(nso_ctx->pfs_ctx, nso_ctx->pfs_entry, rodata_read_ptr, rodata_read_size, nso_ctx->nso_header.rodata_segment_info.file_offset))
|
||||
{
|
||||
LOGFILE("Failed to read %s .rodata segment in NRO \"%s\"!", nso_ctx->nso_filename);
|
||||
goto end;
|
||||
|
@ -226,7 +226,7 @@ static u8 *nsoGetRodataSegment(NsoContext *nso_ctx)
|
|||
{
|
||||
/* Decompress .rodata segment in-place. */
|
||||
if ((lz4_res = LZ4_decompress_safe((char*)rodata_read_ptr, (char*)rodata_buf, (int)nso_ctx->nso_header.rodata_file_size, (int)rodata_buf_size)) != \
|
||||
(int)nso_ctx->nso_header.rodata_segment_header.size)
|
||||
(int)nso_ctx->nso_header.rodata_segment_info.size)
|
||||
{
|
||||
LOGFILE("LZ4 decompression failed for NRO \"%s\"! (0x%08X).", nso_ctx->nso_filename, (u32)lz4_res);
|
||||
goto end;
|
||||
|
@ -236,7 +236,7 @@ static u8 *nsoGetRodataSegment(NsoContext *nso_ctx)
|
|||
if (verify)
|
||||
{
|
||||
/* Verify .rodata segment hash. */
|
||||
sha256CalculateHash(rodata_hash, rodata_buf, nso_ctx->nso_header.rodata_segment_header.size);
|
||||
sha256CalculateHash(rodata_hash, rodata_buf, nso_ctx->nso_header.rodata_segment_info.size);
|
||||
if (memcmp(rodata_hash, nso_ctx->nso_header.rodata_segment_hash, SHA256_HASH_SIZE) != 0)
|
||||
{
|
||||
LOGFILE(".rodata segment checksum mismatch for NRO \"%s\"!", nso_ctx->nso_filename);
|
||||
|
@ -277,7 +277,7 @@ static bool nsoGetModuleInfoName(NsoContext *nso_ctx, u8 *rodata_buf)
|
|||
|
||||
static bool nsoGetSectionFromRodataSegment(NsoContext *nso_ctx, u8 *rodata_buf, u8 **section_ptr, u64 section_offset, u64 section_size)
|
||||
{
|
||||
if (!section_size || (section_offset + section_size) > nso_ctx->nso_header.rodata_segment_header.size) return true;
|
||||
if (!section_size || (section_offset + section_size) > nso_ctx->nso_header.rodata_segment_info.size) return true;
|
||||
|
||||
/* Allocate memory for the desired .rodata section. */
|
||||
if (!(*section_ptr = malloc(section_size)))
|
||||
|
|
16
source/nso.h
16
source/nso.h
|
@ -41,12 +41,12 @@ typedef struct {
|
|||
u32 file_offset; ///< NSO segment offset.
|
||||
u32 memory_offset; ///< Memory segment offset.
|
||||
u32 size; ///< Decompressed segment size.
|
||||
} NsoSegmentHeader;
|
||||
} NsoSegmentInfo;
|
||||
|
||||
typedef struct {
|
||||
u32 offset; ///< Relative to the .rodata segment start.
|
||||
u32 size;
|
||||
} NsoSectionHeader;
|
||||
} NsoSectionInfo;
|
||||
|
||||
/// This is the start of every NSO.
|
||||
/// This is always followed by a NsoModuleName block.
|
||||
|
@ -55,20 +55,20 @@ typedef struct {
|
|||
u32 version; ///< Always set to 0.
|
||||
u8 reserved_1[0x4];
|
||||
u32 flags; ///< NsoFlags.
|
||||
NsoSegmentHeader text_segment_header;
|
||||
NsoSegmentInfo text_segment_info;
|
||||
u32 module_name_offset; ///< NsoModuleName block offset.
|
||||
NsoSegmentHeader rodata_segment_header;
|
||||
NsoSegmentInfo rodata_segment_info;
|
||||
u32 module_name_size; ///< NsoModuleName block size.
|
||||
NsoSegmentHeader data_segment_header;
|
||||
NsoSegmentInfo data_segment_info;
|
||||
u32 bss_size;
|
||||
u8 module_id[0x20]; ///< Also known as build ID.
|
||||
u32 text_file_size; ///< .text segment compressed size (if NsoFlags_TextCompress is enabled).
|
||||
u32 rodata_file_size; ///< .rodata segment compressed size (if NsoFlags_RoCompress is enabled).
|
||||
u32 data_file_size; ///< .data segment compressed size (if NsoFlags_DataCompress is enabled).
|
||||
u8 reserved_2[0x1C];
|
||||
NsoSectionHeader api_info_section_header;
|
||||
NsoSectionHeader dynstr_section_header;
|
||||
NsoSectionHeader dynsym_section_header;
|
||||
NsoSectionInfo api_info_section_info;
|
||||
NsoSectionInfo dynstr_section_info;
|
||||
NsoSectionInfo dynsym_section_info;
|
||||
u8 text_segment_hash[0x20]; ///< Decompressed .text segment SHA-256 checksum.
|
||||
u8 rodata_segment_hash[0x20]; ///< Decompressed .rodata segment SHA-256 checksum.
|
||||
u8 data_segment_hash[0x20]; ///< Decompressed .data segment SHA-256 checksum.
|
||||
|
|
|
@ -56,7 +56,7 @@ bool programInfoInitializeContext(ProgramInfoContext *out, NcaContext *nca_ctx)
|
|||
{
|
||||
if (!out || !nca_ctx || !*(nca_ctx->content_id_str) || nca_ctx->content_type != NcmContentType_Program || nca_ctx->content_size < NCA_FULL_HEADER_LENGTH || \
|
||||
(nca_ctx->storage_id != NcmStorageId_GameCard && !nca_ctx->ncm_storage) || (nca_ctx->storage_id == NcmStorageId_GameCard && !nca_ctx->gamecard_offset) || \
|
||||
nca_ctx->header.content_type != NcaContentType_Program || !out)
|
||||
nca_ctx->header.content_type != NcaContentType_Program || nca_ctx->content_type_ctx || !out)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "utils.h"
|
||||
#include "services.h"
|
||||
#include "es.h"
|
||||
#include "fspusb.h"
|
||||
|
||||
/* Type definitions. */
|
||||
|
||||
|
@ -44,7 +43,6 @@ static Result smHasService(bool *out_has_service, SmServiceName name);
|
|||
static Result servicesNifmUserInitialize(void);
|
||||
static bool servicesClkGetServiceType(void *arg);
|
||||
static bool servicesSplCryptoCheckAvailability(void *arg);
|
||||
static bool servicesFspUsbCheckAvailability(void *arg);
|
||||
|
||||
/* Global variables. */
|
||||
|
||||
|
@ -58,7 +56,6 @@ static ServiceInfo g_serviceInfo[] = {
|
|||
{ false, "psm", NULL, &psmInitialize, &psmExit },
|
||||
{ false, "nifm:u", NULL, &servicesNifmUserInitialize, &nifmExit },
|
||||
{ false, "clk", &servicesClkGetServiceType, NULL, NULL }, /* Placeholder for pcv / clkrst. */
|
||||
{ false, "fsp-usb", &servicesFspUsbCheckAvailability, &fspusbInitialize, &fspusbExit }, /* Checks if fsp-usb really is available. */
|
||||
{ false, "es", NULL, &esInitialize, &esExit },
|
||||
{ false, "set:cal", NULL, &setcalInitialize, &setcalExit }
|
||||
};
|
||||
|
@ -290,16 +287,3 @@ static bool servicesSplCryptoCheckAvailability(void *arg)
|
|||
/* Check if spl:mig is available (sysver equal to or greater than 4.0.0). */
|
||||
return !hosversionBefore(4, 0, 0);
|
||||
}
|
||||
|
||||
static bool servicesFspUsbCheckAvailability(void *arg)
|
||||
{
|
||||
if (!arg) return false;
|
||||
|
||||
ServiceInfo *info = (ServiceInfo*)arg;
|
||||
if (strlen(info->name) != 7 || strcmp(info->name, "fsp-usb") != 0 || info->init_func == NULL || info->close_func == NULL) return false;
|
||||
|
||||
/* Check if fsp-usb is actually running in the background. */
|
||||
bool has_service = false;
|
||||
return (utilsGetCustomFirmwareType() == UtilsCustomFirmwareType_Atmosphere ? (R_SUCCEEDED(servicesAtmosphereHasService(&has_service, info->name)) && has_service) : \
|
||||
servicesCheckRunningServiceByName(info->name));
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <usbhsfs.h>
|
||||
|
||||
#include "utils.h"
|
||||
//#include "freetype_helper.h"
|
||||
//#include "lvgl_helper.h"
|
||||
|
@ -75,6 +77,7 @@ bool utilsInitializeResources(void)
|
|||
{
|
||||
mutexLock(&g_resourcesMutex);
|
||||
|
||||
Result rc = 0;
|
||||
bool ret = g_resourcesInitialized;
|
||||
if (ret) goto end;
|
||||
|
||||
|
@ -107,6 +110,14 @@ bool utilsInitializeResources(void)
|
|||
goto end;
|
||||
}
|
||||
|
||||
/* Initialize USB host FS interface. */
|
||||
rc = usbHsFsInitialize();
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOGFILE("Failed to initialize USB host FS interface! (0x%08X).", rc);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Load NCA keyset. */
|
||||
if (!keysLoadNcaKeyset())
|
||||
{
|
||||
|
@ -216,6 +227,9 @@ void utilsCloseResources(void)
|
|||
/* Free NCA crypto buffer. */
|
||||
ncaFreeCryptoBuffer();
|
||||
|
||||
/* Close USB host FS interface. */
|
||||
usbHsFsExit();
|
||||
|
||||
/* Close USB interface. */
|
||||
usbExit();
|
||||
|
||||
|
|
Loading…
Reference in a new issue