1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-22 10:16:39 +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:
Pablo Curiel 2020-11-08 15:08:30 -04:00
parent 5f0f281e2d
commit e8956c0e4b
15 changed files with 753 additions and 211 deletions

View file

@ -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
#---------------------------------------------------------------------------------

View file

@ -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);

View 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;
}

View file

@ -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);
}
}

View file

@ -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,
);
}

View file

@ -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__ */

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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 && \

View file

@ -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)))

View file

@ -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.

View file

@ -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;

View file

@ -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));
}

View file

@ -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();