romfs_dev: Backport proper multimount system from libnx with some tweaks
This commit is contained in:
parent
1c7914e2f4
commit
da529aed21
@ -45,36 +45,40 @@ typedef struct
|
||||
u16 name[]; ///< Name. (UTF-16)
|
||||
} romfs_file;
|
||||
|
||||
struct romfs_mount;
|
||||
|
||||
/**
|
||||
* @brief Mounts the Application's RomFS.
|
||||
* @param mount Output mount handle
|
||||
* @param name Device mount name.
|
||||
* @remark This function is intended to be used to access one's own RomFS.
|
||||
* If the application is running as 3DSX, it mounts the embedded RomFS section inside the 3DSX.
|
||||
* If on the other hand it's an NCCH, it behaves identically to \ref romfsMountFromCurrentProcess.
|
||||
*/
|
||||
Result romfsMount(struct romfs_mount **mount);
|
||||
static inline Result romfsInit(void)
|
||||
{
|
||||
return romfsMount(NULL);
|
||||
}
|
||||
Result romfsMountSelf(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Mounts RomFS from an open file.
|
||||
* @param file Handle of the RomFS file.
|
||||
* @param fd FSFILE handle of the RomFS image.
|
||||
* @param offset Offset of the RomFS within the file.
|
||||
* @param mount Output mount handle
|
||||
* @param name Device mount name.
|
||||
*/
|
||||
Result romfsMountFromFile(Handle file, u32 offset, struct romfs_mount **mount);
|
||||
static inline Result romfsInitFromFile(Handle file, u32 offset)
|
||||
{
|
||||
return romfsMountFromFile(file, offset, NULL);
|
||||
}
|
||||
Result romfsMountFromFile(Handle fd, u32 offset, const char *name);
|
||||
|
||||
/// Bind the RomFS mount
|
||||
Result romfsBind(struct romfs_mount *mount);
|
||||
/**
|
||||
* @brief Mounts RomFS using the current process host program RomFS.
|
||||
* @param name Device mount name.
|
||||
*/
|
||||
Result romfsMountFromCurrentProcess(const char *name);
|
||||
|
||||
/// Unmounts the RomFS device.
|
||||
Result romfsUnmount(struct romfs_mount *mount);
|
||||
Result romfsUnmount(const char *name);
|
||||
|
||||
/// Wrapper for \ref romfsMountSelf with the default "romfs" device name.
|
||||
static inline Result romfsInit(void)
|
||||
{
|
||||
return romfsMountSelf("romfs");
|
||||
}
|
||||
|
||||
/// Wrapper for \ref romfsUnmount with the default "romfs" device name.
|
||||
static inline Result romfsExit(void)
|
||||
{
|
||||
return romfsUnmount(NULL);
|
||||
return romfsUnmount("romfs");
|
||||
}
|
||||
|
@ -41,15 +41,14 @@ static Result __read_versionbin(FS_ArchiveID archiveId, FS_Path archivePath, FS_
|
||||
Result ret = 0;
|
||||
Handle filehandle = 0;
|
||||
FILE *f = NULL;
|
||||
struct romfs_mount *mount;
|
||||
|
||||
ret = FSUSER_OpenFileDirectly(&filehandle, archiveId, archivePath, fileLowPath, FS_OPEN_READ, 0x0);
|
||||
if(R_FAILED(ret))return ret;
|
||||
|
||||
ret = romfsMountFromFile(filehandle, 0x0, &mount);
|
||||
ret = romfsMountFromFile(filehandle, 0x0, "ver");
|
||||
if(R_FAILED(ret))return ret;
|
||||
|
||||
f = fopen("romfs:/version.bin", "r");
|
||||
f = fopen("ver:/version.bin", "r");
|
||||
if(f==NULL)
|
||||
{
|
||||
ret = errno;
|
||||
@ -60,7 +59,7 @@ static Result __read_versionbin(FS_ArchiveID archiveId, FS_Path archivePath, FS_
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
romfsUnmount(mount);
|
||||
romfsUnmount("ver");
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -121,7 +120,7 @@ Result osGetSystemVersionDataString(OS_VersionBin *nver_versionbin, OS_VersionBi
|
||||
ret = osGetSystemVersionData(nver_versionbin, cver_versionbin);
|
||||
if(R_FAILED(ret))return ret;
|
||||
|
||||
snprintf(sysverstr, sysverstr_maxsize, "%u.%u.%u-%u%c\n", cver_versionbin->mainver, cver_versionbin->minor, cver_versionbin->build, nver_versionbin->mainver, nver_versionbin->region);
|
||||
snprintf(sysverstr, sysverstr_maxsize, "%u.%u.%u-%u%c", cver_versionbin->mainver, cver_versionbin->minor, cver_versionbin->build, nver_versionbin->mainver, nver_versionbin->region);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -20,6 +20,8 @@
|
||||
|
||||
typedef struct romfs_mount
|
||||
{
|
||||
devoptab_t device;
|
||||
char name[32];
|
||||
Handle fd;
|
||||
time_t mtime;
|
||||
u32 offset;
|
||||
@ -84,9 +86,8 @@ typedef struct
|
||||
u32 childFile;
|
||||
} romfs_diriter;
|
||||
|
||||
static devoptab_t romFS_devoptab =
|
||||
static const devoptab_t romFS_devoptab =
|
||||
{
|
||||
.name = "romfs",
|
||||
.structSize = sizeof(romfs_fileobj),
|
||||
.open_r = romfs_open,
|
||||
.close_r = romfs_close,
|
||||
@ -101,7 +102,6 @@ static devoptab_t romFS_devoptab =
|
||||
.dirreset_r = romfs_dirreset,
|
||||
.dirnext_r = romfs_dirnext,
|
||||
.dirclose_r = romfs_dirclose,
|
||||
.deviceData = 0,
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -124,9 +124,6 @@ typedef struct
|
||||
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);
|
||||
|
||||
__attribute__((weak)) const char* __romfs_path = NULL;
|
||||
|
||||
static romfs_mount *romfs_mount_list = NULL;
|
||||
@ -151,16 +148,23 @@ static void romfs_remove(romfs_mount *mount)
|
||||
|
||||
static romfs_mount* romfs_alloc(void)
|
||||
{
|
||||
romfs_mount *mount = (romfs_mount*)calloc(1, sizeof(romfs_mount));
|
||||
romfs_mount *mount = (romfs_mount*)malloc(sizeof(romfs_mount));
|
||||
|
||||
if(mount)
|
||||
if (mount)
|
||||
{
|
||||
memset(mount, 0, sizeof(*mount));
|
||||
memcpy(&mount->device, &romFS_devoptab, sizeof(romFS_devoptab));
|
||||
mount->device.name = mount->name;
|
||||
mount->device.deviceData = mount;
|
||||
romfs_insert(mount);
|
||||
}
|
||||
|
||||
return mount;
|
||||
}
|
||||
|
||||
static void romfs_free(romfs_mount *mount)
|
||||
{
|
||||
FSFILE_Close(mount->fd);
|
||||
romfs_remove(mount);
|
||||
free(mount->fileTable);
|
||||
free(mount->fileHashTable);
|
||||
@ -169,221 +173,165 @@ static void romfs_free(romfs_mount *mount)
|
||||
free(mount);
|
||||
}
|
||||
|
||||
Result romfsMount(struct romfs_mount **p)
|
||||
Result romfsMountFromFile(Handle fd, u32 offset, const char *name)
|
||||
{
|
||||
romfs_mount *mount = romfs_alloc();
|
||||
if(mount == NULL)
|
||||
return 99;
|
||||
|
||||
if (envIsHomebrew())
|
||||
if (mount == NULL)
|
||||
{
|
||||
// RomFS appended to a 3DSX file
|
||||
const char* filename = __romfs_path;
|
||||
if (__system_argc > 0 && __system_argv[0])
|
||||
filename = __system_argv[0];
|
||||
if (!filename)
|
||||
{
|
||||
romfs_free(mount);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strncmp(filename, "sdmc:/", 6) == 0)
|
||||
filename += 5;
|
||||
else if (strncmp(filename, "3dslink:/", 9) == 0)
|
||||
{
|
||||
strncpy(__ctru_dev_path_buf, "/3ds", PATH_MAX);
|
||||
strncat(__ctru_dev_path_buf, filename+8, PATH_MAX);
|
||||
__ctru_dev_path_buf[PATH_MAX] = 0;
|
||||
filename = __ctru_dev_path_buf;
|
||||
}
|
||||
else
|
||||
{
|
||||
romfs_free(mount);
|
||||
return 2;
|
||||
}
|
||||
|
||||
ssize_t units = utf8_to_utf16(__ctru_dev_utf16_buf, (const uint8_t*)filename, PATH_MAX);
|
||||
if (units < 0)
|
||||
{
|
||||
romfs_free(mount);
|
||||
return 3;
|
||||
}
|
||||
if (units > PATH_MAX)
|
||||
{
|
||||
romfs_free(mount);
|
||||
return 4;
|
||||
}
|
||||
__ctru_dev_utf16_buf[units] = 0;
|
||||
|
||||
FS_Path archPath = { PATH_EMPTY, 1, "" };
|
||||
FS_Path filePath = { PATH_UTF16, (units+1)*2, __ctru_dev_utf16_buf };
|
||||
|
||||
Result rc = FSUSER_OpenFileDirectly(&mount->fd, ARCHIVE_SDMC, archPath, filePath, FS_OPEN_READ, 0);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
romfs_free(mount);
|
||||
return rc;
|
||||
}
|
||||
|
||||
romfsInitMtime(mount, ARCHIVE_SDMC, archPath, filePath);
|
||||
|
||||
_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;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Regular RomFS
|
||||
u8 zeros[0xC] = {0};
|
||||
|
||||
FS_Path archPath = { PATH_EMPTY, 1, "" };
|
||||
FS_Path filePath = { PATH_BINARY, sizeof(zeros), zeros };
|
||||
|
||||
Result rc = FSUSER_OpenFileDirectly(&mount->fd, ARCHIVE_ROMFS, archPath, filePath, FS_OPEN_READ, 0);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
romfs_free(mount);
|
||||
return rc;
|
||||
}
|
||||
|
||||
romfsInitMtime(mount, ARCHIVE_ROMFS, archPath, filePath);
|
||||
FSFILE_Close(fd);
|
||||
return MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_ROMFS, RD_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
Result ret = romfsMountCommon(mount);
|
||||
if(R_SUCCEEDED(ret) && p)
|
||||
*p = mount;
|
||||
|
||||
return ret;
|
||||
|
||||
_fail0:
|
||||
FSFILE_Close(mount->fd);
|
||||
romfs_free(mount);
|
||||
return 10;
|
||||
}
|
||||
|
||||
Result romfsMountFromFile(Handle file, u32 offset, struct romfs_mount **p)
|
||||
{
|
||||
romfs_mount *mount = romfs_alloc();
|
||||
if(mount == NULL)
|
||||
return 99;
|
||||
|
||||
mount->fd = file;
|
||||
mount->fd = fd;
|
||||
mount->offset = offset;
|
||||
mount->mtime = time(NULL);
|
||||
strncpy(mount->name, name, sizeof(mount->name)-1);
|
||||
|
||||
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))
|
||||
goto fail;
|
||||
goto fail_io;
|
||||
|
||||
mount->dirHashTable = (u32*)malloc(mount->header.dirHashTableSize);
|
||||
if (!mount->dirHashTable)
|
||||
goto fail;
|
||||
goto fail_oom;
|
||||
if (!_romfs_read_chk(mount, mount->header.dirHashTableOff, mount->dirHashTable, mount->header.dirHashTableSize))
|
||||
goto fail;
|
||||
goto fail_io;
|
||||
|
||||
mount->dirTable = malloc(mount->header.dirTableSize);
|
||||
if (!mount->dirTable)
|
||||
goto fail;
|
||||
goto fail_oom;
|
||||
if (!_romfs_read_chk(mount, mount->header.dirTableOff, mount->dirTable, mount->header.dirTableSize))
|
||||
goto fail;
|
||||
goto fail_io;
|
||||
|
||||
mount->fileHashTable = (u32*)malloc(mount->header.fileHashTableSize);
|
||||
if (!mount->fileHashTable)
|
||||
goto fail;
|
||||
goto fail_oom;
|
||||
if (!_romfs_read_chk(mount, mount->header.fileHashTableOff, mount->fileHashTable, mount->header.fileHashTableSize))
|
||||
goto fail;
|
||||
goto fail_io;
|
||||
|
||||
mount->fileTable = malloc(mount->header.fileTableSize);
|
||||
if (!mount->fileTable)
|
||||
goto fail;
|
||||
goto fail_oom;
|
||||
if (!_romfs_read_chk(mount, mount->header.fileTableOff, mount->fileTable, mount->header.fileTableSize))
|
||||
goto fail;
|
||||
goto fail_io;
|
||||
|
||||
mount->cwd = romFS_root(mount);
|
||||
|
||||
// add device if this is the first one
|
||||
if(mount->next == NULL && AddDevice(&romFS_devoptab) < 0)
|
||||
goto fail;
|
||||
if (AddDevice(&mount->device) < 0)
|
||||
goto fail_oom;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
FSFILE_Close(mount->fd);
|
||||
fail_oom:
|
||||
romfs_free(mount);
|
||||
return 10;
|
||||
return MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_ROMFS, RD_OUT_OF_MEMORY);
|
||||
|
||||
fail_io:
|
||||
romfs_free(mount);
|
||||
return MAKERESULT(RL_FATAL, RS_INVALIDSTATE, RM_ROMFS, RD_NOT_FOUND);
|
||||
}
|
||||
|
||||
static void romfsInitMtime(romfs_mount *mount, FS_ArchiveID archId, FS_Path archPath, FS_Path filePath)
|
||||
Result romfsMountSelf(const char* name)
|
||||
{
|
||||
u64 mtime;
|
||||
FS_Archive arch;
|
||||
Result rc;
|
||||
// If we are not 3DSX, we need to mount this process' real RomFS
|
||||
if (!envIsHomebrew())
|
||||
return romfsMountFromCurrentProcess(name);
|
||||
|
||||
mount->mtime = time(NULL);
|
||||
rc = FSUSER_OpenArchive(&arch, archId, archPath);
|
||||
if (R_FAILED(rc))
|
||||
return;
|
||||
// Otherwise, we use the embedded RomFS inside the 3DSX file
|
||||
// Retrieve the filename of our 3DSX file
|
||||
const char* filename = __romfs_path;
|
||||
if (__system_argc > 0 && __system_argv[0])
|
||||
filename = __system_argv[0];
|
||||
if (!filename)
|
||||
return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_ROMFS, RD_NO_DATA);
|
||||
|
||||
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)
|
||||
{
|
||||
for(romfs_mount **it = &romfs_mount_list; *it; it = &(*it)->next)
|
||||
// Adjust the path name
|
||||
if (strncmp(filename, "sdmc:/", 6) == 0)
|
||||
filename += 5;
|
||||
else if (strncmp(filename, "3dslink:/", 9) == 0)
|
||||
{
|
||||
if(*it == mount)
|
||||
{
|
||||
*it = mount->next;
|
||||
romfs_insert(mount);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 99;
|
||||
}
|
||||
|
||||
Result romfsUnmount(struct romfs_mount *mount)
|
||||
{
|
||||
if(mount)
|
||||
{
|
||||
// unmount specific
|
||||
FSFILE_Close(mount->fd);
|
||||
romfs_free(mount);
|
||||
strncpy(__ctru_dev_path_buf, "/3ds", PATH_MAX);
|
||||
strncat(__ctru_dev_path_buf, filename+8, PATH_MAX);
|
||||
__ctru_dev_path_buf[PATH_MAX] = 0;
|
||||
filename = __ctru_dev_path_buf;
|
||||
}
|
||||
else
|
||||
return MAKERESULT(RL_USAGE, RS_NOTSUPPORTED, RM_ROMFS, RD_NOT_IMPLEMENTED);
|
||||
|
||||
// Convert the path to UTF-16
|
||||
ssize_t units = utf8_to_utf16(__ctru_dev_utf16_buf, (const uint8_t*)filename, PATH_MAX);
|
||||
if (units < 0 || units > PATH_MAX)
|
||||
return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_ROMFS, RD_OUT_OF_RANGE);
|
||||
__ctru_dev_utf16_buf[units] = 0;
|
||||
|
||||
// Open the file directly
|
||||
Handle fd = 0;
|
||||
FS_Path archPath = { PATH_EMPTY, 1, "" };
|
||||
FS_Path filePath = { PATH_UTF16, (units+1)*2, __ctru_dev_utf16_buf };
|
||||
Result rc = FSUSER_OpenFileDirectly(&fd, ARCHIVE_SDMC, archPath, filePath, FS_OPEN_READ, 0);
|
||||
if (R_FAILED(rc))
|
||||
return rc;
|
||||
|
||||
// Read and parse the header
|
||||
_3DSX_Header hdr;
|
||||
u32 bytesRead = 0;
|
||||
rc = FSFILE_Read(fd, &bytesRead, 0, &hdr, sizeof(hdr));
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
// unmount everything
|
||||
while(romfs_mount_list)
|
||||
{
|
||||
FSFILE_Close(romfs_mount_list->fd);
|
||||
romfs_free(romfs_mount_list);
|
||||
}
|
||||
FSFILE_Close(fd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// if no more mounts, remove device
|
||||
if(romfs_mount_list == NULL)
|
||||
RemoveDevice("romfs:");
|
||||
// Validate the 3DSX header
|
||||
if (bytesRead != sizeof(hdr) || hdr.magic != _3DSX_MAGIC || hdr.headerSize < sizeof(hdr))
|
||||
{
|
||||
FSFILE_Close(fd);
|
||||
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_ROMFS, RD_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Mount the file
|
||||
return romfsMountFromFile(fd, hdr.fsOffset, name);
|
||||
}
|
||||
|
||||
Result romfsMountFromCurrentProcess(const char *name)
|
||||
{
|
||||
// Set up FS_Path structures
|
||||
u8 zeros[0xC] = {0};
|
||||
FS_Path archPath = { PATH_EMPTY, 1, "" };
|
||||
FS_Path filePath = { PATH_BINARY, sizeof(zeros), zeros };
|
||||
|
||||
// Open the RomFS file and mount it
|
||||
Handle fd = 0;
|
||||
Result rc = FSUSER_OpenFileDirectly(&fd, ARCHIVE_ROMFS, archPath, filePath, FS_OPEN_READ, 0);
|
||||
if (R_SUCCEEDED(rc))
|
||||
rc = romfsMountFromFile(fd, 0, name);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result romfsUnmount(const char* name)
|
||||
{
|
||||
// Find the mount
|
||||
romfs_mount* mount = romfs_mount_list;
|
||||
while (mount)
|
||||
{
|
||||
if (strncmp(mount->name, name, sizeof(mount->name)) == 0)
|
||||
break;
|
||||
mount = mount->next;
|
||||
}
|
||||
|
||||
if (mount == NULL)
|
||||
return MAKERESULT(RL_STATUS, RS_NOTFOUND, RM_ROMFS, RD_NOT_FOUND);
|
||||
|
||||
// Remove device
|
||||
char tmpname[34];
|
||||
unsigned len = strlen(name);
|
||||
memcpy(tmpname, mount->name, len);
|
||||
tmpname[len] = ':';
|
||||
tmpname[len+1] = 0;
|
||||
RemoveDevice(tmpname);
|
||||
|
||||
// Free the mount
|
||||
romfs_free(mount);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -567,7 +515,7 @@ int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags,
|
||||
{
|
||||
romfs_fileobj* fileobj = (romfs_fileobj*)fileStruct;
|
||||
|
||||
fileobj->mount = romfs_mount_list;
|
||||
fileobj->mount = (romfs_mount*)r->deviceData;
|
||||
|
||||
if ((flags & O_ACCMODE) != O_RDONLY)
|
||||
{
|
||||
@ -696,7 +644,7 @@ int romfs_fstat(struct _reent *r, void *fd, struct stat *st)
|
||||
|
||||
int romfs_stat(struct _reent *r, const char *path, struct stat *st)
|
||||
{
|
||||
romfs_mount* mount = romfs_mount_list;
|
||||
romfs_mount* mount = (romfs_mount*)r->deviceData;
|
||||
romfs_dir* curDir = NULL;
|
||||
r->_errno = navigateToDir(mount, &curDir, &path, false);
|
||||
if(r->_errno != 0)
|
||||
@ -740,7 +688,7 @@ int romfs_stat(struct _reent *r, const char *path, struct stat *st)
|
||||
|
||||
int romfs_chdir(struct _reent *r, const char *path)
|
||||
{
|
||||
romfs_mount* mount = romfs_mount_list;
|
||||
romfs_mount* mount = (romfs_mount*)r->deviceData;
|
||||
romfs_dir* curDir = NULL;
|
||||
r->_errno = navigateToDir(mount, &curDir, &path, true);
|
||||
if (r->_errno != 0)
|
||||
@ -754,7 +702,7 @@ DIR_ITER* romfs_diropen(struct _reent *r, DIR_ITER *dirState, const char *path)
|
||||
{
|
||||
romfs_diriter* iter = (romfs_diriter*)(dirState->dirStruct);
|
||||
romfs_dir* curDir = NULL;
|
||||
iter->mount = romfs_mount_list;
|
||||
iter->mount = (romfs_mount*)r->deviceData;
|
||||
|
||||
r->_errno = navigateToDir(iter->mount, &curDir, &path, true);
|
||||
if(r->_errno != 0)
|
||||
|
Loading…
Reference in New Issue
Block a user