mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-26 04:02:11 +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)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
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
|
# list of directories containing libraries, this must be the top level containing
|
||||||
# include and lib
|
# 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_sua = options[4].val;
|
||||||
bool patch_screenshot = options[5].val;
|
bool patch_screenshot = options[5].val;
|
||||||
bool patch_video_capture = options[6].val;
|
bool patch_video_capture = options[6].val;
|
||||||
|
bool success = false, usb_conn = false;
|
||||||
|
|
||||||
u8 *buf = NULL;
|
u8 *buf = NULL;
|
||||||
char *dump_name = NULL, *path = NULL;
|
char *dump_name = NULL, *path = NULL;
|
||||||
|
@ -458,7 +459,6 @@ static void nspDump(TitleInfo *title_info)
|
||||||
consolePrint("waiting for usb connection... ");
|
consolePrint("waiting for usb connection... ");
|
||||||
|
|
||||||
time_t start = time(NULL);
|
time_t start = time(NULL);
|
||||||
bool usb_conn = false;
|
|
||||||
|
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
|
@ -727,7 +727,11 @@ static void nspDump(TitleInfo *title_info)
|
||||||
start = (time(NULL) - start);
|
start = (time(NULL) - start);
|
||||||
consolePrint("process successfully completed in %lu seconds!\n", start);
|
consolePrint("process successfully completed in %lu seconds!\n", start);
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
|
||||||
end:
|
end:
|
||||||
|
if (usb_conn && !success) usbCancelFileTransfer();
|
||||||
|
|
||||||
pfsFreeFileContext(&pfs_file_ctx);
|
pfsFreeFileContext(&pfs_file_ctx);
|
||||||
|
|
||||||
if (raw_cert_chain) free(raw_cert_chain);
|
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 || \
|
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->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!");
|
LOGFILE("Invalid parameters!");
|
||||||
return false;
|
return false;
|
||||||
|
@ -317,17 +317,20 @@ bool cnmtGenerateNcaPatch(ContentMetaContext *cnmt_ctx)
|
||||||
|
|
||||||
void cnmtWriteNcaPatch(ContentMetaContext *cnmt_ctx, void *buf, u64 buf_size, u64 buf_offset)
|
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. */
|
/* 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. */
|
/* 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. */
|
/* 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;
|
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);
|
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 || \
|
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->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!");
|
LOGFILE("Invalid parameters!");
|
||||||
return false;
|
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 || \
|
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->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!");
|
LOGFILE("Invalid parameters!");
|
||||||
return false;
|
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)
|
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. */
|
/* 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. */
|
/* 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. */
|
/* 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;
|
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);
|
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);
|
npdmFreeContext(out);
|
||||||
|
|
||||||
/* Get 'main.npdm' file entry. */
|
/* Get 'main.npdm' file entry. */
|
||||||
|
out->nca_ctx = nca_ctx;
|
||||||
out->pfs_ctx = pfs_ctx;
|
out->pfs_ctx = pfs_ctx;
|
||||||
if (!(out->pfs_entry = pfsGetEntryByName(out->pfs_ctx, "main.npdm")))
|
if (!(out->pfs_entry = pfsGetEntryByName(out->pfs_ctx, "main.npdm")))
|
||||||
{
|
{
|
||||||
|
@ -260,14 +261,14 @@ end:
|
||||||
|
|
||||||
bool npdmGenerateNcaPatch(NpdmContext *npdm_ctx)
|
bool npdmGenerateNcaPatch(NpdmContext *npdm_ctx)
|
||||||
{
|
{
|
||||||
NcaContext *nca_ctx = NULL;
|
if (!npdmIsValidContext(npdm_ctx) || npdm_ctx->nca_ctx->content_type != NcmContentType_Program)
|
||||||
|
|
||||||
if (!npdmIsValidContext(npdm_ctx) || !(nca_ctx = (NcaContext*)npdm_ctx->pfs_ctx->nca_fs_ctx->nca_ctx) || nca_ctx->content_type != NcmContentType_Program)
|
|
||||||
{
|
{
|
||||||
LOGFILE("Invalid parameters!");
|
LOGFILE("Invalid parameters!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NcaContext *nca_ctx = npdm_ctx->nca_ctx;
|
||||||
|
|
||||||
/* Check if we really need to generate this patch. */
|
/* Check if we really need to generate this patch. */
|
||||||
if (!ncaIsHeaderDirty(nca_ctx))
|
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)
|
void npdmWriteNcaPatch(NpdmContext *npdm_ctx, void *buf, u64 buf_size, u64 buf_offset)
|
||||||
{
|
{
|
||||||
NcaContext *nca_ctx = NULL;
|
NcaContext *nca_ctx = NULL;
|
||||||
|
NcaHierarchicalSha256Patch *nca_patch = (npdm_ctx ? &(npdm_ctx->nca_patch) : NULL);
|
||||||
|
|
||||||
/* Using npdmIsValidContext() here would probably take up precious CPU cycles. */
|
/* 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 || \
|
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;
|
||||||
!nca_ctx->content_type_ctx_patch || npdm_ctx->nca_patch.written) return;
|
|
||||||
|
|
||||||
/* Attempt to write Partition FS entry patch. */
|
/* 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. */
|
/* 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;
|
nca_ctx->content_type_ctx_patch = false;
|
||||||
LOGFILE("NPDM Partition FS entry patch successfully written to NCA \"%s\"!", nca_ctx->content_id_str);
|
LOGFILE("NPDM Partition FS entry patch successfully written to NCA \"%s\"!", nca_ctx->content_id_str);
|
||||||
|
|
|
@ -534,6 +534,7 @@ typedef struct {
|
||||||
} NpdmKernelCapabilityDescriptorEntry;
|
} NpdmKernelCapabilityDescriptorEntry;
|
||||||
|
|
||||||
typedef struct {
|
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.
|
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.
|
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.
|
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)
|
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->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->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 && \
|
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;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out->nso_header.text_segment_header.file_offset < sizeof(NsoHeader) || !out->nso_header.text_segment_header.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_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_info.size)) || \
|
||||||
(!(out->nso_header.flags & NsoFlags_TextCompress) && 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_info.size) || \
|
||||||
(out->nso_header.text_segment_header.file_offset + out->nso_header.text_file_size) > pfs_entry->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, \
|
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_header.size);
|
out->nso_header.text_segment_info.size);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out->nso_header.rodata_segment_header.file_offset < sizeof(NsoHeader) || !out->nso_header.rodata_segment_header.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_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_info.size)) || \
|
||||||
(!(out->nso_header.flags & NsoFlags_RoCompress) && 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_info.size) || \
|
||||||
(out->nso_header.rodata_segment_header.file_offset + out->nso_header.rodata_file_size) > pfs_entry->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, \
|
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_header.size);
|
out->nso_header.rodata_segment_info.size);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out->nso_header.data_segment_header.file_offset < sizeof(NsoHeader) || !out->nso_header.data_segment_header.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_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_info.size)) || \
|
||||||
(!(out->nso_header.flags & NsoFlags_DataCompress) && 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_info.size) || \
|
||||||
(out->nso_header.data_segment_header.file_offset + out->nso_header.data_file_size) > pfs_entry->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, \
|
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_header.size);
|
out->nso_header.data_segment_info.size);
|
||||||
goto end;
|
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);
|
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. */
|
/* Get module name. */
|
||||||
|
@ -132,16 +132,16 @@ bool nsoInitializeContext(NsoContext *out, PartitionFileSystemContext *pfs_ctx,
|
||||||
if (!nsoGetModuleInfoName(out, rodata_buf)) goto end;
|
if (!nsoGetModuleInfoName(out, rodata_buf)) goto end;
|
||||||
|
|
||||||
/* Get .api_info section data. */
|
/* 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;
|
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_header.size;
|
out->rodata_api_info_section_size = out->nso_header.api_info_section_info.size;
|
||||||
|
|
||||||
/* Get .dynstr section data. */
|
/* 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;
|
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_header.size;
|
out->rodata_dynstr_section_size = out->nso_header.dynstr_section_info.size;
|
||||||
|
|
||||||
/* Get .dynsym section data. */
|
/* 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;
|
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_header.size;
|
out->rodata_dynsym_section_size = out->nso_header.dynsym_section_info.size;
|
||||||
|
|
||||||
success = true;
|
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);
|
bool compressed = (nso_ctx->nso_header.flags & NsoFlags_RoCompress), verify = (nso_ctx->nso_header.flags & NsoFlags_RoHash);
|
||||||
|
|
||||||
u8 *rodata_buf = NULL;
|
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;
|
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};
|
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);
|
rodata_read_ptr = (compressed ? (rodata_buf + (rodata_buf_size - nso_ctx->nso_header.rodata_file_size)) : rodata_buf);
|
||||||
|
|
||||||
/* Read .rodata segment data. */
|
/* 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);
|
LOGFILE("Failed to read %s .rodata segment in NRO \"%s\"!", nso_ctx->nso_filename);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -226,7 +226,7 @@ static u8 *nsoGetRodataSegment(NsoContext *nso_ctx)
|
||||||
{
|
{
|
||||||
/* Decompress .rodata segment in-place. */
|
/* 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)) != \
|
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);
|
LOGFILE("LZ4 decompression failed for NRO \"%s\"! (0x%08X).", nso_ctx->nso_filename, (u32)lz4_res);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -236,7 +236,7 @@ static u8 *nsoGetRodataSegment(NsoContext *nso_ctx)
|
||||||
if (verify)
|
if (verify)
|
||||||
{
|
{
|
||||||
/* Verify .rodata segment hash. */
|
/* 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)
|
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);
|
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)
|
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. */
|
/* Allocate memory for the desired .rodata section. */
|
||||||
if (!(*section_ptr = malloc(section_size)))
|
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 file_offset; ///< NSO segment offset.
|
||||||
u32 memory_offset; ///< Memory segment offset.
|
u32 memory_offset; ///< Memory segment offset.
|
||||||
u32 size; ///< Decompressed segment size.
|
u32 size; ///< Decompressed segment size.
|
||||||
} NsoSegmentHeader;
|
} NsoSegmentInfo;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u32 offset; ///< Relative to the .rodata segment start.
|
u32 offset; ///< Relative to the .rodata segment start.
|
||||||
u32 size;
|
u32 size;
|
||||||
} NsoSectionHeader;
|
} NsoSectionInfo;
|
||||||
|
|
||||||
/// This is the start of every NSO.
|
/// This is the start of every NSO.
|
||||||
/// This is always followed by a NsoModuleName block.
|
/// This is always followed by a NsoModuleName block.
|
||||||
|
@ -55,20 +55,20 @@ typedef struct {
|
||||||
u32 version; ///< Always set to 0.
|
u32 version; ///< Always set to 0.
|
||||||
u8 reserved_1[0x4];
|
u8 reserved_1[0x4];
|
||||||
u32 flags; ///< NsoFlags.
|
u32 flags; ///< NsoFlags.
|
||||||
NsoSegmentHeader text_segment_header;
|
NsoSegmentInfo text_segment_info;
|
||||||
u32 module_name_offset; ///< NsoModuleName block offset.
|
u32 module_name_offset; ///< NsoModuleName block offset.
|
||||||
NsoSegmentHeader rodata_segment_header;
|
NsoSegmentInfo rodata_segment_info;
|
||||||
u32 module_name_size; ///< NsoModuleName block size.
|
u32 module_name_size; ///< NsoModuleName block size.
|
||||||
NsoSegmentHeader data_segment_header;
|
NsoSegmentInfo data_segment_info;
|
||||||
u32 bss_size;
|
u32 bss_size;
|
||||||
u8 module_id[0x20]; ///< Also known as build ID.
|
u8 module_id[0x20]; ///< Also known as build ID.
|
||||||
u32 text_file_size; ///< .text segment compressed size (if NsoFlags_TextCompress is enabled).
|
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 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).
|
u32 data_file_size; ///< .data segment compressed size (if NsoFlags_DataCompress is enabled).
|
||||||
u8 reserved_2[0x1C];
|
u8 reserved_2[0x1C];
|
||||||
NsoSectionHeader api_info_section_header;
|
NsoSectionInfo api_info_section_info;
|
||||||
NsoSectionHeader dynstr_section_header;
|
NsoSectionInfo dynstr_section_info;
|
||||||
NsoSectionHeader dynsym_section_header;
|
NsoSectionInfo dynsym_section_info;
|
||||||
u8 text_segment_hash[0x20]; ///< Decompressed .text segment SHA-256 checksum.
|
u8 text_segment_hash[0x20]; ///< Decompressed .text segment SHA-256 checksum.
|
||||||
u8 rodata_segment_hash[0x20]; ///< Decompressed .rodata 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.
|
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 || \
|
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->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!");
|
LOGFILE("Invalid parameters!");
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "services.h"
|
#include "services.h"
|
||||||
#include "es.h"
|
#include "es.h"
|
||||||
#include "fspusb.h"
|
|
||||||
|
|
||||||
/* Type definitions. */
|
/* Type definitions. */
|
||||||
|
|
||||||
|
@ -44,7 +43,6 @@ static Result smHasService(bool *out_has_service, SmServiceName name);
|
||||||
static Result servicesNifmUserInitialize(void);
|
static Result servicesNifmUserInitialize(void);
|
||||||
static bool servicesClkGetServiceType(void *arg);
|
static bool servicesClkGetServiceType(void *arg);
|
||||||
static bool servicesSplCryptoCheckAvailability(void *arg);
|
static bool servicesSplCryptoCheckAvailability(void *arg);
|
||||||
static bool servicesFspUsbCheckAvailability(void *arg);
|
|
||||||
|
|
||||||
/* Global variables. */
|
/* Global variables. */
|
||||||
|
|
||||||
|
@ -58,7 +56,6 @@ static ServiceInfo g_serviceInfo[] = {
|
||||||
{ false, "psm", NULL, &psmInitialize, &psmExit },
|
{ false, "psm", NULL, &psmInitialize, &psmExit },
|
||||||
{ false, "nifm:u", NULL, &servicesNifmUserInitialize, &nifmExit },
|
{ false, "nifm:u", NULL, &servicesNifmUserInitialize, &nifmExit },
|
||||||
{ false, "clk", &servicesClkGetServiceType, NULL, NULL }, /* Placeholder for pcv / clkrst. */
|
{ 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, "es", NULL, &esInitialize, &esExit },
|
||||||
{ false, "set:cal", NULL, &setcalInitialize, &setcalExit }
|
{ 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). */
|
/* Check if spl:mig is available (sysver equal to or greater than 4.0.0). */
|
||||||
return !hosversionBefore(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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <usbhsfs.h>
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
//#include "freetype_helper.h"
|
//#include "freetype_helper.h"
|
||||||
//#include "lvgl_helper.h"
|
//#include "lvgl_helper.h"
|
||||||
|
@ -75,6 +77,7 @@ bool utilsInitializeResources(void)
|
||||||
{
|
{
|
||||||
mutexLock(&g_resourcesMutex);
|
mutexLock(&g_resourcesMutex);
|
||||||
|
|
||||||
|
Result rc = 0;
|
||||||
bool ret = g_resourcesInitialized;
|
bool ret = g_resourcesInitialized;
|
||||||
if (ret) goto end;
|
if (ret) goto end;
|
||||||
|
|
||||||
|
@ -107,6 +110,14 @@ bool utilsInitializeResources(void)
|
||||||
goto end;
|
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. */
|
/* Load NCA keyset. */
|
||||||
if (!keysLoadNcaKeyset())
|
if (!keysLoadNcaKeyset())
|
||||||
{
|
{
|
||||||
|
@ -216,6 +227,9 @@ void utilsCloseResources(void)
|
||||||
/* Free NCA crypto buffer. */
|
/* Free NCA crypto buffer. */
|
||||||
ncaFreeCryptoBuffer();
|
ncaFreeCryptoBuffer();
|
||||||
|
|
||||||
|
/* Close USB host FS interface. */
|
||||||
|
usbHsFsExit();
|
||||||
|
|
||||||
/* Close USB interface. */
|
/* Close USB interface. */
|
||||||
usbExit();
|
usbExit();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue