mirror of
https://github.com/switchbrew/libnx.git
synced 2025-08-12 10:39:23 +02:00
Added native RomFS file reading/directory listing
Find RomFS mounts by name Find RomFS files by path or with a mount Iterate RomFS directory entries
This commit is contained in:
parent
c769852631
commit
ff3e698d96
@ -120,3 +120,105 @@ static inline Result romfsExit(void)
|
|||||||
{
|
{
|
||||||
return romfsUnmount("romfs");
|
return romfsUnmount("romfs");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opaque handle to a RomFS mount
|
||||||
|
typedef struct romfs_mount romfs_mount;
|
||||||
|
|
||||||
|
/// Object for interacting with a romfs_file
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
romfs_mount *mount; ///< The RomFS mount the file is associated with.
|
||||||
|
romfs_file *file; ///< Detail about the actual RomFS file.
|
||||||
|
|
||||||
|
u64 offset; ///< The starting offset in RomFS for file data.
|
||||||
|
u64 pos; ///< Current read position into the file.
|
||||||
|
} romfs_fileobj;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Finds a RomFS mount by the given name, the default mount name is "romfs"
|
||||||
|
* @param name The name of the mount to search for
|
||||||
|
* @param mount A pointer to a romfs_mount pointer to fill out with the mount information
|
||||||
|
*/
|
||||||
|
Result romfsFindMount(const char *name, romfs_mount **mount);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Finds a file in RomFS automatically determining the mount
|
||||||
|
* @param path The path to the file
|
||||||
|
* @param file A pointer to a romfs_fileobj structure to fill out
|
||||||
|
* @remark The path structure should follow <mount name>:<path> (i.e. romfs:/data/file.txt)
|
||||||
|
* If no mount name is provided the default of "romfs" will be used.
|
||||||
|
*/
|
||||||
|
Result romfsFindFile(const char *path, romfs_fileobj *file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Finds a file in a specific RomFS mount
|
||||||
|
* @param mount The mount to search in
|
||||||
|
* @param path The path to the file
|
||||||
|
* @param file A pointer to a romfs_fileobj structure to fill out
|
||||||
|
* @remark The mount name prefix is not required and is ignored if provided
|
||||||
|
*/
|
||||||
|
Result romfsFindFileInMount(romfs_mount *mount, const char *path, romfs_fileobj *file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads data from the specified RomFS file.
|
||||||
|
* @param file The RomFS file to read from.
|
||||||
|
* @param buffer The buffer to read data into.
|
||||||
|
* @param size The number of bytes to read.
|
||||||
|
* @param offset The offset in bytes from the beginning of the file to start reading from.
|
||||||
|
* @param nread A pointer in which the number of total bytes read will be written to.
|
||||||
|
* @remark The file's position pointer is not updated by this function.
|
||||||
|
*/
|
||||||
|
Result romfsReadFile(romfs_fileobj *file, void *buffer, u64 size, u64 offset, u64 *nread);
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
romfs_mount *mount; ///< The RomFS mount associated with the directory.
|
||||||
|
romfs_dir *dir; ///< Information about the directory being searched.
|
||||||
|
u32 state; ///< Current iteration count.
|
||||||
|
u32 childDir; ///< Next child directory of the directory.
|
||||||
|
u32 childFile; ///< Next child file of the directory.
|
||||||
|
} romfs_diriter;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
RomfsDirEntryType_File = 0,
|
||||||
|
RomfsDirEntryType_Dir
|
||||||
|
} RomfsDirEntryType;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
RomfsDirEntryType type; ///< Type of this entry.
|
||||||
|
union
|
||||||
|
{
|
||||||
|
const romfs_file *file; ///< Entry information if type is RomfsDirEntryType_File.
|
||||||
|
const romfs_dir *dir; ///< Entry information if type is RomfsDirEntryType_Dir.
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *name; ///< Basename of the entry, not null-terminated, UTF-8 coded.
|
||||||
|
u32 name_len; ///< Length in bytes of the basename.
|
||||||
|
} romfs_direntry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialises the directory iterator for seaching at the given path, automatically determines mount.
|
||||||
|
* @param path The directory path to search.
|
||||||
|
* @param iter The directory iterator to fill out with found information.
|
||||||
|
* @remark The path structure should follow <mount name>:<path> (i.e. romfs:/data/folder).
|
||||||
|
* If no mount name is provided the default of "romfs" will be used.
|
||||||
|
*/
|
||||||
|
Result romfsDirOpen(const char *path, romfs_diriter *iter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialises the directory iterator for seaching at the given path in the supplied mount.
|
||||||
|
* @param mount The RomFS mount to search in.
|
||||||
|
* @param path The directory path to search.
|
||||||
|
* @param iter The directory iterator to fill out with found information.
|
||||||
|
* @remark The mount does not need to be specified in the path and is ignored if provided.
|
||||||
|
*/
|
||||||
|
Result romfsDirOpenWithMount(romfs_mount *mount, const char *path, romfs_diriter *iter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the next entry in the directory, returns false on error or when no more entries are found.
|
||||||
|
* @param iter The directory iterator.
|
||||||
|
* @param entry The entry to fill out with information.
|
||||||
|
*/
|
||||||
|
bool romfsDirNext(romfs_diriter *iter, romfs_direntry *entry);
|
||||||
|
@ -22,6 +22,8 @@ typedef enum {
|
|||||||
RomfsSource_FsStorage,
|
RomfsSource_FsStorage,
|
||||||
} RomfsSource;
|
} RomfsSource;
|
||||||
|
|
||||||
|
#define ROMFS_MOUNT_NAME_LIMIT 32
|
||||||
|
|
||||||
typedef struct romfs_mount
|
typedef struct romfs_mount
|
||||||
{
|
{
|
||||||
devoptab_t device;
|
devoptab_t device;
|
||||||
@ -36,7 +38,7 @@ typedef struct romfs_mount
|
|||||||
romfs_dir *cwd;
|
romfs_dir *cwd;
|
||||||
u32 *dirHashTable, *fileHashTable;
|
u32 *dirHashTable, *fileHashTable;
|
||||||
void *dirTable, *fileTable;
|
void *dirTable, *fileTable;
|
||||||
char name[32];
|
char name[ROMFS_MOUNT_NAME_LIMIT];
|
||||||
} romfs_mount;
|
} romfs_mount;
|
||||||
|
|
||||||
extern int __system_argc;
|
extern int __system_argc;
|
||||||
@ -138,22 +140,6 @@ static int romfs_dirreset(struct _reent *r, DIR_ITER *dirState);
|
|||||||
static int romfs_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
|
static int romfs_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
|
||||||
static int romfs_dirclose(struct _reent *r, DIR_ITER *dirState);
|
static int romfs_dirclose(struct _reent *r, DIR_ITER *dirState);
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
romfs_mount *mount;
|
|
||||||
romfs_file *file;
|
|
||||||
u64 offset, pos;
|
|
||||||
} romfs_fileobj;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
romfs_mount *mount;
|
|
||||||
romfs_dir* dir;
|
|
||||||
u32 state;
|
|
||||||
u32 childDir;
|
|
||||||
u32 childFile;
|
|
||||||
} romfs_diriter;
|
|
||||||
|
|
||||||
static const devoptab_t romFS_devoptab =
|
static const devoptab_t romFS_devoptab =
|
||||||
{
|
{
|
||||||
.structSize = sizeof(romfs_fileobj),
|
.structSize = sizeof(romfs_fileobj),
|
||||||
@ -205,7 +191,7 @@ static void _romfsInit(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static romfs_mount *romfsFindMount(const char *name)
|
static romfs_mount *_romfsFindMount(const char *name)
|
||||||
{
|
{
|
||||||
u32 i;
|
u32 i;
|
||||||
u32 total = sizeof(romfs_mounts) / sizeof(romfs_mount);
|
u32 total = sizeof(romfs_mounts) / sizeof(romfs_mount);
|
||||||
@ -236,7 +222,7 @@ __attribute__((weak)) const char* __romfs_path = NULL;
|
|||||||
|
|
||||||
static romfs_mount* romfs_alloc(void)
|
static romfs_mount* romfs_alloc(void)
|
||||||
{
|
{
|
||||||
return romfsFindMount(NULL);
|
return _romfsFindMount(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void romfs_free(romfs_mount *mount)
|
static void romfs_free(romfs_mount *mount)
|
||||||
@ -445,9 +431,9 @@ static void romfsInitMtime(romfs_mount *mount)
|
|||||||
Result romfsUnmount(const char *name)
|
Result romfsUnmount(const char *name)
|
||||||
{
|
{
|
||||||
romfs_mount *mount;
|
romfs_mount *mount;
|
||||||
char tmpname[34];
|
char tmpname[ROMFS_MOUNT_NAME_LIMIT + 2]; // +2 for : and \0
|
||||||
|
|
||||||
mount = romfsFindMount(name);
|
mount = _romfsFindMount(name);
|
||||||
if (mount == NULL)
|
if (mount == NULL)
|
||||||
return MAKERESULT(Module_Libnx, LibnxError_NotFound);
|
return MAKERESULT(Module_Libnx, LibnxError_NotFound);
|
||||||
|
|
||||||
@ -616,6 +602,218 @@ static ino_t file_inode(romfs_mount *mount, romfs_file *file)
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Result romfsFindMount(const char *name, romfs_mount **mount)
|
||||||
|
{
|
||||||
|
if (name == NULL || mount == NULL)
|
||||||
|
{
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
*mount = _romfsFindMount(name);
|
||||||
|
if (*mount == NULL)
|
||||||
|
{
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_NotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result romfsFindFile(const char *path, romfs_fileobj *file)
|
||||||
|
{
|
||||||
|
// Get the mount name from the path if there is one, otherwise fallback on "romfs"
|
||||||
|
char mount_name[ROMFS_MOUNT_NAME_LIMIT] = "romfs";
|
||||||
|
|
||||||
|
char *colon_pos = strchr(path, ':');
|
||||||
|
if (colon_pos)
|
||||||
|
{
|
||||||
|
size_t len = (size_t) (colon_pos - path);
|
||||||
|
len = (len > ROMFS_MOUNT_NAME_LIMIT) ? ROMFS_MOUNT_NAME_LIMIT : len;
|
||||||
|
|
||||||
|
memset(mount_name, 0, sizeof(mount_name));
|
||||||
|
memcpy(mount_name, path, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for the mount by name
|
||||||
|
romfs_mount *mount = _romfsFindMount(mount_name);
|
||||||
|
if (mount == NULL)
|
||||||
|
{
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_NotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
return romfsFindFileInMount(mount, path, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result romfsFindFileInMount(romfs_mount *mount, const char *path, romfs_fileobj *file)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
// In the event the user has retrieved a handle to a RomFS mount but then unmounted
|
||||||
|
// it, the mount will no longer be setup so we return with a fail
|
||||||
|
if (!mount->setup)
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||||
|
|
||||||
|
// Get the directory containing the file, navigateToDir already strips the mount name for us
|
||||||
|
romfs_dir *dir = 0;
|
||||||
|
ret = navigateToDir(mount, &dir, &path, false);
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
if (ret == ENOENT)
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_NotFound);
|
||||||
|
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the file information from the directory
|
||||||
|
romfs_file *info = 0;
|
||||||
|
ret = searchForFile(mount, dir, (uint8_t *) path, strlen(path), &info);
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
if (ret == ENOENT)
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_NotFound);
|
||||||
|
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill out the fileobj information
|
||||||
|
file->mount = mount;
|
||||||
|
file->file = info;
|
||||||
|
file->offset = mount->header.fileDataOff + info->dataOff;
|
||||||
|
file->pos = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result romfsReadFile(romfs_fileobj *file, void *buffer, u64 size, u64 offset, u64 *nread)
|
||||||
|
{
|
||||||
|
if (nread == NULL || file == NULL || buffer == NULL)
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||||
|
|
||||||
|
if (offset >= file->file->dataSize)
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||||
|
|
||||||
|
// Truncate read if past end of file
|
||||||
|
u64 end = offset + size;
|
||||||
|
if (end > file->file->dataSize) end = file->file->dataSize;
|
||||||
|
|
||||||
|
size = end - offset;
|
||||||
|
|
||||||
|
ssize_t bytes_read = _romfs_read(file->mount, file->offset + offset, buffer, size);
|
||||||
|
if (bytes_read < 0)
|
||||||
|
{
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_IoError);
|
||||||
|
}
|
||||||
|
|
||||||
|
*nread = bytes_read;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result romfsDirOpen(const char *path, romfs_diriter *iter)
|
||||||
|
{
|
||||||
|
char mount_name[ROMFS_MOUNT_NAME_LIMIT] = "romfs";
|
||||||
|
char *colon_pos = strchr(path, ':');
|
||||||
|
if (colon_pos)
|
||||||
|
{
|
||||||
|
size_t len = (size_t) (colon_pos - path);
|
||||||
|
len = (len > ROMFS_MOUNT_NAME_LIMIT) ? ROMFS_MOUNT_NAME_LIMIT : len;
|
||||||
|
|
||||||
|
memset(mount_name, 0, sizeof(mount_name));
|
||||||
|
memcpy(mount_name, path, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
romfs_mount *mount = _romfsFindMount(mount_name);
|
||||||
|
if (mount == NULL)
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_NotFound);
|
||||||
|
|
||||||
|
return romfsDirOpenWithMount(mount, path, iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result romfsDirOpenWithMount(romfs_mount *mount, const char *path, romfs_diriter *iter)
|
||||||
|
{
|
||||||
|
romfs_dir *dir = 0;
|
||||||
|
int ret = navigateToDir(mount, &dir, &path, true);
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
if (ret == ENOENT)
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_NotFound);
|
||||||
|
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
iter->mount = mount;
|
||||||
|
iter->dir = dir;
|
||||||
|
iter->state = 0;
|
||||||
|
iter->childDir = dir->childDir;
|
||||||
|
iter->childFile = dir->childFile;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool romfsDirNext(romfs_diriter *iter, romfs_direntry *entry)
|
||||||
|
{
|
||||||
|
bool error = true;
|
||||||
|
|
||||||
|
iter->state += 1;
|
||||||
|
|
||||||
|
if (iter->state == 1)
|
||||||
|
{
|
||||||
|
// relative '.'
|
||||||
|
entry->type = RomfsDirEntryType_Dir;
|
||||||
|
entry->dir = iter->dir;
|
||||||
|
entry->name = ".";
|
||||||
|
entry->name_len = 1;
|
||||||
|
|
||||||
|
error = false;
|
||||||
|
}
|
||||||
|
else if (iter->state == 2)
|
||||||
|
{
|
||||||
|
// relative '..'
|
||||||
|
romfs_dir *dir = romFS_dir(iter->mount, iter->dir->parent);
|
||||||
|
if (dir != NULL)
|
||||||
|
{
|
||||||
|
entry->type = RomfsDirEntryType_Dir;
|
||||||
|
entry->dir = dir;
|
||||||
|
entry->name = "..";
|
||||||
|
entry->name_len = 2;
|
||||||
|
|
||||||
|
error = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (iter->childDir != romFS_none)
|
||||||
|
{
|
||||||
|
romfs_dir *dir = romFS_dir(iter->mount, iter->childDir);
|
||||||
|
if (dir != NULL)
|
||||||
|
{
|
||||||
|
entry->type = RomfsDirEntryType_Dir;
|
||||||
|
entry->dir = dir;
|
||||||
|
entry->name = (const char *) dir->name;
|
||||||
|
entry->name_len = dir->nameLen;
|
||||||
|
|
||||||
|
iter->childDir = dir->sibling;
|
||||||
|
|
||||||
|
error = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (iter->childFile != romFS_none)
|
||||||
|
{
|
||||||
|
romfs_file *file = romFS_file(iter->mount, iter->childFile);
|
||||||
|
if (file != NULL)
|
||||||
|
{
|
||||||
|
entry->type = RomfsDirEntryType_File;
|
||||||
|
entry->file = file;
|
||||||
|
entry->name = (const char *) file->name;
|
||||||
|
entry->name_len = file->nameLen;
|
||||||
|
|
||||||
|
iter->childFile = file->sibling;
|
||||||
|
|
||||||
|
error = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !error;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode)
|
int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode)
|
||||||
{
|
{
|
||||||
romfs_fileobj* fileobj = (romfs_fileobj*)fileStruct;
|
romfs_fileobj* fileobj = (romfs_fileobj*)fileStruct;
|
||||||
|
Loading…
Reference in New Issue
Block a user