diff --git a/nx/include/switch.h b/nx/include/switch.h index b687f14c..9bb1ad23 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -12,6 +12,8 @@ extern "C" { #include "switch/types.h" #include "switch/result.h" +#include "switch/nro.h" + #include "switch/kernel/svc.h" #include "switch/kernel/tmem.h" #include "switch/kernel/shmem.h" @@ -59,6 +61,7 @@ extern "C" { #include "switch/runtime/devices/console.h" #include "switch/runtime/devices/usb_comms.h" #include "switch/runtime/devices/fs_dev.h" +#include "switch/runtime/devices/romfs_dev.h" #ifdef __cplusplus } diff --git a/nx/include/switch/nro.h b/nx/include/switch/nro.h new file mode 100644 index 00000000..75876f0e --- /dev/null +++ b/nx/include/switch/nro.h @@ -0,0 +1,54 @@ +/** + * @file nro.h + * @brief NRO headers. + * @copyright libnx Authors + */ + +#pragma once + +#define NROHEADER_MAGICNUM 0x304f524e + +#define ASSETHEADER_MAGICNUM 0x54455341 +#define ASSETHEADER_VERSION 0 + +/// Entry for each segment in the codebin. +typedef struct { + u32 FileOff; + u32 Size; +} NsoSegment; + +/// Offset 0x0 in the NRO. +typedef struct { + u32 unused; + u32 modOffset; + u8 Padding[8]; +} NroStart; + +/// This follows NroStart, the actual nro-header. +typedef struct { + u32 Magic; + u32 Unk1; + u32 size; + u32 Unk2; + NsoSegment Segments[3]; + u32 bssSize; + u32 Unk3; + u8 BuildId[0x20]; + u8 Padding[0x20]; +} NroHeader; + +/// Custom asset section. +typedef struct { + u64 offset; + u64 size; +} AssetSection; + +/// Custom asset header. +typedef struct { + u32 magic; + u32 version; + AssetSection icon; + AssetSection nacp; + AssetSection romfs; +} AssetHeader; + diff --git a/nx/include/switch/runtime/devices/fs_dev.h b/nx/include/switch/runtime/devices/fs_dev.h index 76164d40..de3e05c3 100644 --- a/nx/include/switch/runtime/devices/fs_dev.h +++ b/nx/include/switch/runtime/devices/fs_dev.h @@ -38,3 +38,6 @@ int fsdevUnmountDevice(const char *name); /// Uses fsFsCommit() with the specified device. This must be used after any savedata-write operations(not just file-write). /// This is not used automatically at device unmount. Result fsdevCommitDevice(const char *name); + +/// Returns the FsFileSystem for the default device (SD card), if mounted. Used internally by romfs_dev. +FsFileSystem* fsdevGetDefaultFileSystem(void); diff --git a/nx/include/switch/runtime/devices/romfs_dev.h b/nx/include/switch/runtime/devices/romfs_dev.h index d6dee80c..f23ab5ab 100644 --- a/nx/include/switch/runtime/devices/romfs_dev.h +++ b/nx/include/switch/runtime/devices/romfs_dev.h @@ -66,7 +66,7 @@ static inline Result romfsInit(void) /** * @brief Mounts RomFS from an open file. - * @param file Handle of the RomFS file. + * @param file FsFile of the RomFS image. * @param offset Offset of the RomFS within the file. * @param mount Output mount handle */ @@ -76,6 +76,18 @@ static inline Result romfsInitFromFile(FsFile file, u64 offset) return romfsMountFromFile(file, offset, NULL); } +/** + * @brief Mounts RomFS from an open storage. + * @param storage FsStorage of the RomFS image. + * @param offset Offset of the RomFS within the storage. + * @param mount Output mount handle + */ +Result romfsMountFromStorage(FsStorage storage, u64 offset, struct romfs_mount **mount); +static inline Result romfsInitFromStorage(FsStorage storage, u64 offset) +{ + return romfsMountFromStorage(storage, offset, NULL); +} + /// Bind the RomFS mount Result romfsBind(struct romfs_mount *mount); diff --git a/nx/include/switch/services/fs.h b/nx/include/switch/services/fs.h index 2cd41499..14546685 100644 --- a/nx/include/switch/services/fs.h +++ b/nx/include/switch/services/fs.h @@ -93,6 +93,7 @@ Service* fsGetServiceSession(void); Result fsMountSdcard(FsFileSystem* out); Result fsMountSaveData(FsFileSystem* out, u8 inval, FsSave *save); +Result fsOpenDataStorageByCurrentProcess(FsStorage* out); // todo: Rest of commands here /// FsFileSystem can be mounted with fs_dev for use with stdio, see fs_dev.h. @@ -130,4 +131,6 @@ Result fsDirRead(FsDir* d, u64 inval, size_t* total_entries, size_t max_entries, Result fsDirGetEntryCount(FsDir* d, u64* count); void fsDirClose(FsDir* d); -// todo: IStorage +// IStorage +Result fsStorageRead(FsStorage* s, u64 off, void* buf, size_t len); +void fsStorageClose(FsStorage* s); diff --git a/nx/source/runtime/devices/fs_dev.c b/nx/source/runtime/devices/fs_dev.c index f5e99590..e4d787ef 100644 --- a/nx/source/runtime/devices/fs_dev.c +++ b/nx/source/runtime/devices/fs_dev.c @@ -469,6 +469,13 @@ Result fsdevExit(void) return 0; } +FsFileSystem* fsdevGetDefaultFileSystem(void) +{ + if(fsdev_fsdevice_default==-1) return NULL; + + return &fsdev_fsdevices[fsdev_fsdevice_default].fs; +} + /*! Open a file * * @param[in,out] r newlib reentrancy struct diff --git a/nx/source/runtime/devices/romfs_dev.c b/nx/source/runtime/devices/romfs_dev.c index fd2a415d..5bfd3497 100644 --- a/nx/source/runtime/devices/romfs_dev.c +++ b/nx/source/runtime/devices/romfs_dev.c @@ -9,14 +9,17 @@ #include #include "runtime/devices/romfs_dev.h" +#include "runtime/devices/fs_dev.h" #include "runtime/util/utf.h" #include "services/fs.h" - -/// WARNING: This is not ready to be used. +#include "runtime/env.h" +#include "nro.h" typedef struct romfs_mount { + bool fd_type; FsFile fd; + FsStorage fd_storage; time_t mtime; u64 offset; romfs_header header; @@ -30,7 +33,6 @@ extern int __system_argc; extern char** __system_argv; static char __component[PATH_MAX+1]; -//static uint16_t __utf16path[PATH_MAX+1]; #define romFS_root(m) ((romfs_dir*)(m)->dirTable) #define romFS_dir(m,x) ((romfs_dir*) ((u8*)(m)->dirTable + (x))) @@ -39,16 +41,25 @@ static char __component[PATH_MAX+1]; #define romFS_dir_mode (S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH) #define romFS_file_mode (S_IFREG | S_IRUSR | S_IRGRP | S_IROTH) -static ssize_t _romfs_read(romfs_mount *mount, u64 offset, void* buffer, u32 size) +static ssize_t _romfs_read(romfs_mount *mount, u64 offset, void* buffer, u64 size) { u64 pos = mount->offset + offset; size_t read = 0; - Result rc = fsFileRead(&mount->fd, pos, buffer, size, &read); + Result rc = 0; + if(!mount->fd_type) + { + rc = fsFileRead(&mount->fd, pos, buffer, size, &read); + } + else + { + rc = fsStorageRead(&mount->fd_storage, pos, buffer, size); + read = size; + } if (R_FAILED(rc)) return -1; return read; } -static bool _romfs_read_chk(romfs_mount *mount, u64 offset, void* buffer, u32 size) +static bool _romfs_read_chk(romfs_mount *mount, u64 offset, void* buffer, u64 size) { return _romfs_read(mount, offset, buffer, size) == size; } @@ -104,26 +115,8 @@ static devoptab_t romFS_devoptab = //----------------------------------------------------------------------------- -// File header -/*#define _3DSX_MAGIC 0x58534433 // '3DSX' -typedef struct -{ - u32 magic; - u16 headerSize, relocHdrSize; - u32 formatVer; - u32 flags; - - // Sizes of the code, rodata and data segments + - // size of the BSS section (uninitialized latter half of the data segment) - u32 codeSegSize, rodataSegSize, dataSegSize, bssSize; - // offset and size of smdh - u32 smdhOffset, smdhSize; - // offset to filesystem - u32 fsOffset; -} _3DSX_Header;*/ - static Result romfsMountCommon(romfs_mount *mount); -//static void romfsInitMtime(romfs_mount *mount, FS_ArchiveID archId, FS_Path archPath, FS_Path filePath); +static void romfsInitMtime(romfs_mount *mount); __attribute__((weak)) const char* __romfs_path = NULL; @@ -173,9 +166,19 @@ Result romfsMount(struct romfs_mount **p) if(mount == NULL) return 99; - if (/*envIsHomebrew()*/1)//TODO: How to handle? + if (!envIsNso()) { - // RomFS appended to a 3DSX file + // RomFS embedded in a NRO + + mount->fd_type = 0; + + FsFileSystem *sdfs = fsdevGetDefaultFileSystem(); + if(sdfs==NULL) + { + romfs_free(mount); + return 1; + } + const char* filename = __romfs_path; if (__system_argc > 0 && __system_argv[0]) filename = __system_argv[0]; @@ -187,69 +190,57 @@ Result romfsMount(struct romfs_mount **p) if (strncmp(filename, "sdmc:/", 6) == 0) filename += 5; - /*else if (strncmp(filename, "3dslink:/", 9) == 0) + else if (strncmp(filename, "nxlink:/", 8) == 0) { - strncpy(__component, "/3ds", PATH_MAX); - strncat(__component, filename+8, PATH_MAX); + strncpy(__component, "/switch", PATH_MAX); + strncat(__component, filename+7, PATH_MAX); __component[PATH_MAX] = 0; filename = __component; - }*/ + } else { romfs_free(mount); return 2; } - //TODO - /*ssize_t units = utf8_to_utf16(__utf16path, (const uint8_t*)filename, PATH_MAX); - if (units < 0) - { - romfs_free(mount); - return 3; - } - if (units >= PATH_MAX) - { - romfs_free(mount); - return 4; - } - __utf16path[units] = 0; - - FS_Path archPath = { PATH_EMPTY, 1, (u8*)"" }; - FS_Path filePath = { PATH_UTF16, (units+1)*2, (u8*)__utf16path }; - - Result rc = FSUSER_OpenFileDirectly(&mount->fd, ARCHIVE_SDMC, archPath, filePath, FS_OPEN_READ, 0); + Result rc = fsFsOpenFile(sdfs, filename, FS_OPEN_READ, &mount->fd); if (R_FAILED(rc)) { romfs_free(mount); return rc; } - //romfsInitMtime(mount, ARCHIVE_SDMC, archPath, filePath); + romfsInitMtime(mount); - _3DSX_Header hdr; - if (!_romfs_read_chk(mount, 0, &hdr, sizeof(hdr))) goto _fail0; - if (hdr.magic != _3DSX_MAGIC) goto _fail0; - if (hdr.headerSize < sizeof(hdr)) goto _fail0; - mount->offset = hdr.fsOffset; - if (!mount->offset) goto _fail0;*/ + NroHeader hdr; + AssetHeader asset_header; + + if (!_romfs_read_chk(mount, sizeof(NroStart), &hdr, sizeof(hdr))) goto _fail0; + if (hdr.Magic != NROHEADER_MAGICNUM) goto _fail0; + if (!_romfs_read_chk(mount, hdr.size, &asset_header, sizeof(asset_header))) goto _fail0; + + if (asset_header.magic != ASSETHEADER_MAGICNUM + || asset_header.version > ASSETHEADER_VERSION + || asset_header.romfs.offset == 0 + || asset_header.romfs.size == 0) + goto _fail0; + + mount->offset = hdr.size + asset_header.romfs.offset; } - else//TODO + else { // Regular RomFS - /*u8 zeros[0xC]; - memset(zeros, 0, sizeof(zeros)); - FS_Path archPath = { PATH_EMPTY, 1, (u8*)"" }; - FS_Path filePath = { PATH_BINARY, sizeof(zeros), zeros }; + mount->fd_type = 1; - Result rc = FSUSER_OpenFileDirectly(&mount->fd, ARCHIVE_ROMFS, archPath, filePath, FS_OPEN_READ, 0); + Result rc = fsOpenDataStorageByCurrentProcess(&mount->fd_storage); if (R_FAILED(rc)) { romfs_free(mount); return rc; } - //romfsInitMtime(mount, ARCHIVE_ROMFS, archPath, filePath);*/ + romfsInitMtime(mount); } Result ret = romfsMountCommon(mount); @@ -258,8 +249,9 @@ Result romfsMount(struct romfs_mount **p) return ret; -//_fail0: - fsFileClose(&mount->fd); +_fail0: + if(!mount->fd_type)fsFileClose(&mount->fd); + if(mount->fd_type)fsStorageClose(&mount->fd_storage); romfs_free(mount); return 10; } @@ -270,6 +262,7 @@ Result romfsMountFromFile(FsFile file, u64 offset, struct romfs_mount **p) if(mount == NULL) return 99; + mount->fd_type = 0; mount->fd = file; mount->offset = offset; @@ -280,6 +273,23 @@ Result romfsMountFromFile(FsFile file, u64 offset, struct romfs_mount **p) return ret; } +Result romfsMountFromStorage(FsStorage storage, u64 offset, struct romfs_mount **p) +{ + romfs_mount *mount = romfs_alloc(); + if(mount == NULL) + return 99; + + mount->fd_type = 1; + mount->fd_storage = storage; + mount->offset = offset; + + Result ret = romfsMountCommon(mount); + if(R_SUCCEEDED(ret) && p) + *p = mount; + + return ret; +} + Result romfsMountCommon(romfs_mount *mount) { if (_romfs_read(mount, 0, &mount->header, sizeof(mount->header)) != sizeof(mount->header)) @@ -318,35 +328,16 @@ Result romfsMountCommon(romfs_mount *mount) return 0; fail: - fsFileClose(&mount->fd); + if(!mount->fd_type)fsFileClose(&mount->fd); + if(mount->fd_type)fsStorageClose(&mount->fd_storage); romfs_free(mount); return 10; } -/*static void romfsInitMtime(romfs_mount *mount, FS_ArchiveID archId, FS_Path archPath, FS_Path filePath) +static void romfsInitMtime(romfs_mount *mount) { - u64 mtime; - FS_Archive arch; - Result rc; - mount->mtime = time(NULL); - rc = FSUSER_OpenArchive(&arch, archId, archPath); - if (R_FAILED(rc)) - return; - - rc = FSUSER_ControlArchive(arch, ARCHIVE_ACTION_GET_TIMESTAMP, - (void*)filePath.data, filePath.size, - &mtime, sizeof(mtime)); - FSUSER_CloseArchive(arch); - if (R_FAILED(rc)) - return;*/ - - /* convert from milliseconds to seconds */ - //mtime /= 1000; - /* convert from 2000-based timestamp to UNIX timestamp */ - /*mtime += 946684800; - mount->mtime = mtime; -}*/ +} Result romfsBind(struct romfs_mount *mount) { @@ -368,7 +359,8 @@ Result romfsUnmount(struct romfs_mount *mount) if(mount) { // unmount specific - fsFileClose(&mount->fd); + if(!mount->fd_type)fsFileClose(&mount->fd); + if(mount->fd_type)fsStorageClose(&mount->fd_storage); romfs_free(mount); } else @@ -376,7 +368,8 @@ Result romfsUnmount(struct romfs_mount *mount) // unmount everything while(romfs_mount_list) { - fsFileClose(&romfs_mount_list->fd); + if(!romfs_mount_list->fd_type)fsFileClose(&romfs_mount_list->fd); + if(romfs_mount_list->fd_type)fsStorageClose(&romfs_mount_list->fd_storage); romfs_free(romfs_mount_list); } } @@ -483,7 +476,7 @@ static int navigateToDir(romfs_mount *mount, romfs_dir** ppDir, const char** pPa } } - *ppDir = searchForDir(mount, *ppDir, (uint8_t*)component, strlen(component)+1); + *ppDir = searchForDir(mount, *ppDir, (uint8_t*)component, strlen(component)); if (!*ppDir) return EEXIST; } @@ -551,7 +544,7 @@ int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, if (r->_errno != 0) return -1; - romfs_file* file = searchForFile(fileobj->mount, curDir, (uint8_t*)path, strlen(path)+1); + romfs_file* file = searchForFile(fileobj->mount, curDir, (uint8_t*)path, strlen(path)); if (!file) { if(flags & O_CREAT) @@ -567,7 +560,7 @@ int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, } fileobj->file = file; - fileobj->offset = (u64)fileobj->mount->header.fileDataOff + file->dataOff; + fileobj->offset = fileobj->mount->header.fileDataOff + file->dataOff; fileobj->pos = 0; return 0; @@ -669,7 +662,7 @@ int romfs_stat(struct _reent *r, const char *path, struct stat *st) if(r->_errno != 0) return -1; - romfs_dir* dir = searchForDir(mount, curDir, (uint8_t*)path, strlen(path)+1); + romfs_dir* dir = searchForDir(mount, curDir, (uint8_t*)path, strlen(path)); if(dir) { memset(st, 0, sizeof(*st)); @@ -684,7 +677,7 @@ int romfs_stat(struct _reent *r, const char *path, struct stat *st) return 0; } - romfs_file* file = searchForFile(mount, curDir, (uint8_t*)path, strlen(path)+1); + romfs_file* file = searchForFile(mount, curDir, (uint8_t*)path, strlen(path)); if(file) { memset(st, 0, sizeof(*st)); diff --git a/nx/source/services/fs.c b/nx/source/services/fs.c index f23ec654..013a5779 100644 --- a/nx/source/services/fs.c +++ b/nx/source/services/fs.c @@ -131,6 +131,41 @@ Result fsMountSaveData(FsFileSystem* out, u8 inval, FsSave *save) { return rc; } +Result fsOpenDataStorageByCurrentProcess(FsStorage* out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 200; + + Result rc = serviceIpcDispatch(&g_fsSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + out->h = r.Handles[0]; + } + } + + return rc; +} + // Wrapper(s) for fsMountSaveData. Result fsMount_SaveData(FsFileSystem* out, u64 titleID, u128 userID) { FsSave save; @@ -595,7 +630,7 @@ Result fsFsGetTotalSpace(FsFileSystem* fs, const char* path, u64* out) { } void fsFsClose(FsFileSystem* fs) { - svcCloseHandle(fs->h); + if(fs->h != INVALID_HANDLE) svcCloseHandle(fs->h); } // IFile implementation @@ -778,12 +813,12 @@ Result fsFileGetSize(FsFile* f, u64* out) { } void fsFileClose(FsFile* f) { - svcCloseHandle(f->h); + if(f->h != INVALID_HANDLE) svcCloseHandle(f->h); } // IDirectory implementation void fsDirClose(FsDir* d) { - svcCloseHandle(d->h); + if(d->h != INVALID_HANDLE) svcCloseHandle(d->h); } Result fsDirRead(FsDir* d, u64 inval, size_t* total_entries, size_t max_entries, FsDirectoryEntry *buf) { @@ -858,3 +893,44 @@ Result fsDirGetEntryCount(FsDir* d, u64* count) { return rc; } +// IStorage implementation +Result fsStorageRead(FsStorage* s, u64 off, void* buf, size_t len) { + IpcCommand c; + ipcInitialize(&c); + ipcAddRecvBuffer(&c, buf, len, 1); + + struct { + u64 magic; + u64 cmd_id; + u64 offset; + u64 read_size; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + raw->offset = off; + raw->read_size = len; + + Result rc = ipcDispatch(s->h); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +void fsStorageClose(FsStorage* s) { + if(s->h != INVALID_HANDLE) svcCloseHandle(s->h); +} +