Add work in progress RomFS driver/devoptab
This commit is contained in:
parent
24fd71c4ad
commit
570ea2ebd2
@ -45,6 +45,7 @@ extern "C" {
|
||||
#include <3ds/gpu/shaderProgram.h>
|
||||
|
||||
#include <3ds/sdmc.h>
|
||||
#include <3ds/romfs.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
34
libctru/include/3ds/romfs.h
Normal file
34
libctru/include/3ds/romfs.h
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 headerSize;
|
||||
u32 dirHashTableOff, dirHashTableSize;
|
||||
u32 dirTableOff, dirTableSize;
|
||||
u32 fileHashTableOff, fileHashTableSize;
|
||||
u32 fileTableOff, fileTableSize;
|
||||
u32 fileDataOff;
|
||||
} romfs_header;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 parent, sibling;
|
||||
u32 childDir, childFile;
|
||||
u32 nextHash;
|
||||
u32 nameLen;
|
||||
u16 name[];
|
||||
} romfs_dir;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 parent, sibling;
|
||||
u64 dataOff, dataSize;
|
||||
u32 nextHash;
|
||||
u32 nameLen;
|
||||
u16 name[];
|
||||
} romfs_file;
|
||||
|
||||
Result romfsInit(void);
|
||||
Result romfsExit(void);
|
465
libctru/source/romfs_dev.c
Normal file
465
libctru/source/romfs_dev.c
Normal file
@ -0,0 +1,465 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/iosupport.h>
|
||||
#include <sys/param.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <3ds/types.h>
|
||||
#include <3ds/svc.h>
|
||||
#include <3ds/romfs.h>
|
||||
#include <3ds/services/fs.h>
|
||||
#include <3ds/util/utf.h>
|
||||
|
||||
static bool romFS_active;
|
||||
static Handle romFS_file;
|
||||
static u32 romFS_offset;
|
||||
static romfs_header romFS_header;
|
||||
static romfs_dir* romFS_cwd;
|
||||
|
||||
static u32 *dirHashTable, *fileHashTable;
|
||||
static void *dirTable, *fileTable;
|
||||
|
||||
extern u32 __service_ptr;
|
||||
extern int __system_argc;
|
||||
extern char** __system_argv;
|
||||
|
||||
static char __component[PATH_MAX+1];
|
||||
static uint16_t __utf16path[PATH_MAX+1];
|
||||
|
||||
#define romFS_root ((romfs_dir*)dirTable)
|
||||
#define romFS_dir(x) ((romfs_dir*) ((u8*)dirTable + (x)))
|
||||
#define romFS_file(x) ((romfs_file*)((u8*)fileTable + (x)))
|
||||
#define romFS_none ((u32)~0)
|
||||
|
||||
static ssize_t _romfs_read(u64 offset, void* buffer, u32 size)
|
||||
{
|
||||
u64 pos = (u64)romFS_offset + offset;
|
||||
u32 read = 0;
|
||||
Result rc = FSFILE_Read(romFS_file, &read, pos, buffer, size);
|
||||
if (rc) return -1;
|
||||
return read;
|
||||
}
|
||||
|
||||
static bool _romfs_read_chk(u64 offset, void* buffer, u32 size)
|
||||
{
|
||||
return _romfs_read(offset, buffer, size) == size;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
|
||||
static int romfs_close(struct _reent *r, int fd);
|
||||
static ssize_t romfs_read(struct _reent *r, int fd, char *ptr, size_t len);
|
||||
static off_t romfs_seek(struct _reent *r, int fd, off_t pos, int dir);
|
||||
static int romfs_fstat(struct _reent *r, int fd, struct stat *st);
|
||||
static int romfs_stat(struct _reent *r, const char *file, struct stat *st);
|
||||
static int romfs_chdir(struct _reent *r, const char *name);
|
||||
static DIR_ITER* romfs_diropen(struct _reent *r, DIR_ITER *dirState, const char *path);
|
||||
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_dirclose(struct _reent *r, DIR_ITER *dirState);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u64 offset, size, pos;
|
||||
} romfs_fileobj;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
} romfs_diriter;
|
||||
|
||||
static devoptab_t romFS_devoptab =
|
||||
{
|
||||
.name = "romfs",
|
||||
.structSize = sizeof(romfs_fileobj),
|
||||
.open_r = romfs_open,
|
||||
.close_r = romfs_close,
|
||||
.read_r = romfs_read,
|
||||
.seek_r = romfs_seek,
|
||||
.fstat_r = romfs_fstat,
|
||||
.stat_r = romfs_stat,
|
||||
.chdir_r = romfs_chdir,
|
||||
.dirStateSize = sizeof(romfs_diriter),
|
||||
.diropen_r = romfs_diropen,
|
||||
.dirreset_r = romfs_dirreset,
|
||||
.dirnext_r = romfs_dirnext,
|
||||
.dirclose_r = romfs_dirclose,
|
||||
.deviceData = NULL,
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// 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;
|
||||
|
||||
Result romfsInit(void)
|
||||
{
|
||||
if (romFS_active) return 0;
|
||||
if (__service_ptr)
|
||||
{
|
||||
// RomFS appended to a 3DSX file
|
||||
if (__system_argc == 0 || !__system_argv[0]) return 1;
|
||||
const char* filename = __system_argv[0];
|
||||
if (strncmp(filename, "sdmc:/", 6) == 0)
|
||||
filename += 5;
|
||||
else if (strncmp(filename, "3dslink:/", 9) == 0)
|
||||
{
|
||||
strncpy(__component, "/3ds", PATH_MAX);
|
||||
strncat(__component, filename+8, PATH_MAX);
|
||||
__component[PATH_MAX] = 0;
|
||||
filename = __component;
|
||||
} else
|
||||
return 2;
|
||||
|
||||
size_t units = utf8_to_utf16(__utf16path, (const uint8_t*)filename, PATH_MAX+1);
|
||||
if (units == (size_t)-1) return 3;
|
||||
__utf16path[units] = 0;
|
||||
|
||||
FS_archive arch = { ARCH_SDMC, { PATH_EMPTY, 1, (u8*)"" }, 0, 0 };
|
||||
FS_path path = { PATH_WCHAR, units+1, (u8*)__utf16path };
|
||||
|
||||
Result rc = FSUSER_OpenFileDirectly(NULL, &romFS_file, arch, path, FS_OPEN_READ, FS_ATTRIBUTE_NONE);
|
||||
if (rc) return rc;
|
||||
|
||||
_3DSX_Header hdr;
|
||||
if (!_romfs_read_chk(0, &hdr, sizeof(hdr))) goto _fail0;
|
||||
if (hdr.magic != _3DSX_MAGIC) goto _fail0;
|
||||
if (hdr.headerSize < sizeof(hdr)) goto _fail0;
|
||||
romFS_offset = hdr.fsOffset;
|
||||
if (!romFS_offset) goto _fail0;
|
||||
} else
|
||||
{
|
||||
// Regular RomFS
|
||||
u8 zeros[0xC];
|
||||
memset(zeros, 0, sizeof(zeros));
|
||||
|
||||
FS_archive arch = { ARCH_ROMFS, { PATH_EMPTY, 1, (u8*)"" }, 0, 0 };
|
||||
FS_path path = { PATH_BINARY, sizeof(zeros), zeros };
|
||||
|
||||
Result rc = FSUSER_OpenFileDirectly(NULL, &romFS_file, arch, path, FS_OPEN_READ, FS_ATTRIBUTE_NONE);
|
||||
if (rc) return rc;
|
||||
}
|
||||
|
||||
if (_romfs_read(0, &romFS_header, sizeof(romFS_header)) != sizeof(romFS_header))
|
||||
goto _fail0;
|
||||
|
||||
dirHashTable = (u32*)malloc(romFS_header.dirHashTableSize);
|
||||
if (!dirHashTable) goto _fail0;
|
||||
if (!_romfs_read_chk(romFS_header.dirHashTableOff, dirHashTable, romFS_header.dirHashTableSize)) goto _fail1;
|
||||
|
||||
dirTable = malloc(romFS_header.dirTableSize);
|
||||
if (!dirTable) goto _fail1;
|
||||
if (!_romfs_read_chk(romFS_header.dirTableOff, dirTable, romFS_header.dirTableSize)) goto _fail2;
|
||||
|
||||
fileHashTable = (u32*)malloc(romFS_header.fileHashTableSize);
|
||||
if (!fileHashTable) goto _fail2;
|
||||
if (!_romfs_read_chk(romFS_header.fileHashTableOff, fileHashTable, romFS_header.fileHashTableSize)) goto _fail3;
|
||||
|
||||
fileTable = malloc(romFS_header.fileTableSize);
|
||||
if (!fileTable) goto _fail3;
|
||||
if (!_romfs_read_chk(romFS_header.fileTableOff, fileTable, romFS_header.fileTableSize)) goto _fail4;
|
||||
|
||||
romFS_cwd = romFS_root;
|
||||
romFS_active = true;
|
||||
|
||||
AddDevice(&romFS_devoptab);
|
||||
chdir("romfs:/");
|
||||
|
||||
return 0;
|
||||
|
||||
_fail4:
|
||||
free(fileTable);
|
||||
_fail3:
|
||||
free(fileHashTable);
|
||||
_fail2:
|
||||
free(dirTable);
|
||||
_fail1:
|
||||
free(dirHashTable);
|
||||
_fail0:
|
||||
svcCloseHandle(romFS_file);
|
||||
return 10;
|
||||
}
|
||||
|
||||
Result romfsExit(void)
|
||||
{
|
||||
if (!romFS_active) return 0;
|
||||
romFS_active = false;
|
||||
|
||||
RemoveDevice("romfs");
|
||||
svcCloseHandle(romFS_file);
|
||||
free(dirHashTable);
|
||||
free(fileHashTable);
|
||||
free(dirTable);
|
||||
free(fileTable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static u32 calcHash(u32 parent, u16* name, u32 namelen, u32 total)
|
||||
{
|
||||
u32 hash = parent ^ 123456789;
|
||||
u32 i;
|
||||
for (i = 0; i < namelen; i ++)
|
||||
{
|
||||
hash = (hash >> 5) | (hash << 27);
|
||||
hash ^= name[i];
|
||||
}
|
||||
return hash % total;
|
||||
}
|
||||
|
||||
static romfs_dir* searchForDir(romfs_dir* parent, u16* name, u32 namelen)
|
||||
{
|
||||
u32 parentOff = (u32)parent - (u32)dirTable;
|
||||
u32 hash = calcHash(parentOff, name, namelen, romFS_header.dirHashTableSize/4);
|
||||
romfs_dir* curDir = NULL;
|
||||
u32 curOff;
|
||||
for (curOff = dirHashTable[hash]; curOff != romFS_none; curOff = curDir->nextHash)
|
||||
{
|
||||
curDir = romFS_dir(curOff);
|
||||
if (curDir->parent != parentOff) continue;
|
||||
if (curDir->nameLen != namelen*2) continue;
|
||||
if (memcmp(curDir->name, name, namelen*2) != 0) continue;
|
||||
return curDir;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static romfs_file* searchForFile(romfs_dir* parent, u16* name, u32 namelen)
|
||||
{
|
||||
u32 parentOff = (u32)parent - (u32)dirTable;
|
||||
u32 hash = calcHash(parentOff, name, namelen, romFS_header.fileHashTableSize/4);
|
||||
romfs_file* curFile = NULL;
|
||||
u32 curOff;
|
||||
for (curOff = fileHashTable[hash]; curOff != romFS_none; curOff = curFile->nextHash)
|
||||
{
|
||||
curFile = romFS_file(curOff);
|
||||
if (curFile->parent != parentOff) continue;
|
||||
if (curFile->nameLen != namelen*2) continue;
|
||||
if (memcmp(curFile->name, name, namelen*2) != 0) continue;
|
||||
return curFile;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int navigateToDir(romfs_dir** ppDir, const char** pPath, bool isDir)
|
||||
{
|
||||
size_t units;
|
||||
|
||||
char* colonPos = strchr(*pPath, ':');
|
||||
if (colonPos) *pPath = colonPos+1;
|
||||
if (!**pPath)
|
||||
return EILSEQ;
|
||||
|
||||
*ppDir = romFS_cwd;
|
||||
if (**pPath == '/')
|
||||
{
|
||||
*ppDir = romFS_root;
|
||||
(*pPath)++;
|
||||
}
|
||||
|
||||
while (**pPath)
|
||||
{
|
||||
char* slashPos = strchr(*pPath, '/');
|
||||
char* component = __component;
|
||||
|
||||
if (slashPos)
|
||||
{
|
||||
u32 len = slashPos - *pPath;
|
||||
if (!len)
|
||||
return EILSEQ;
|
||||
if (len > PATH_MAX)
|
||||
return ENAMETOOLONG;
|
||||
|
||||
memcpy(component, *pPath, len);
|
||||
component[len] = 0;
|
||||
*pPath = slashPos+1;
|
||||
} else if (isDir)
|
||||
{
|
||||
component = (char*)*pPath;
|
||||
*pPath += strlen(component);
|
||||
} else
|
||||
return 0;
|
||||
|
||||
if (component[0]=='.')
|
||||
{
|
||||
if (!component[1]) continue;
|
||||
if (component[1]=='.' && !component[2])
|
||||
{
|
||||
*ppDir = romFS_dir((*ppDir)->parent);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
units = utf8_to_utf16(__utf16path, (const uint8_t*)component, PATH_MAX+1);
|
||||
if (units == (size_t)-1)
|
||||
return EILSEQ;
|
||||
|
||||
*ppDir = searchForDir(*ppDir, __utf16path, units);
|
||||
if (!*ppDir)
|
||||
return EEXIST;
|
||||
}
|
||||
|
||||
if (!isDir && !**pPath)
|
||||
return EILSEQ;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode)
|
||||
{
|
||||
romfs_fileobj* fileobj = (romfs_fileobj*)fileStruct;
|
||||
|
||||
if ((flags & O_ACCMODE) != O_RDONLY)
|
||||
{
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
romfs_dir* curDir = NULL;
|
||||
r->_errno = navigateToDir(&curDir, &path, false);
|
||||
if (r->_errno != 0)
|
||||
return -1;
|
||||
|
||||
size_t units = utf8_to_utf16(__utf16path, (const uint8_t*)path, PATH_MAX+1);
|
||||
if (!units || units == (size_t)-1)
|
||||
{
|
||||
r->_errno = EILSEQ;
|
||||
return -1;
|
||||
}
|
||||
|
||||
romfs_file* file = searchForFile(curDir, __utf16path, units);
|
||||
if (!file)
|
||||
{
|
||||
r->_errno = EEXIST;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fileobj->offset = (u64)romFS_header.fileDataOff + file->dataOff;
|
||||
fileobj->size = file->dataSize;
|
||||
fileobj->pos = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int romfs_close(struct _reent *r, int fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t romfs_read(struct _reent *r, int fd, char *ptr, size_t len)
|
||||
{
|
||||
romfs_fileobj* file = (romfs_fileobj*)fd;
|
||||
u64 endPos = file->pos + len;
|
||||
if (endPos > file->size)
|
||||
endPos = file->size;
|
||||
len = endPos - file->pos;
|
||||
|
||||
ssize_t adv = _romfs_read(file->offset + file->pos, ptr, len);
|
||||
if (adv >= 0)
|
||||
{
|
||||
file->pos += adv;
|
||||
return adv;
|
||||
}
|
||||
|
||||
r->_errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
off_t romfs_seek(struct _reent *r, int fd, off_t pos, int dir)
|
||||
{
|
||||
romfs_fileobj* file = (romfs_fileobj*)fd;
|
||||
switch (dir)
|
||||
{
|
||||
case SEEK_SET:
|
||||
file->pos = pos;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
file->pos += pos;
|
||||
break;
|
||||
case SEEK_END:
|
||||
file->pos = file->size + pos;
|
||||
break;
|
||||
default:
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (file->pos > file->size)
|
||||
file->pos = file->size;
|
||||
return file->pos;
|
||||
}
|
||||
|
||||
int romfs_fstat(struct _reent *r, int fd, struct stat *st)
|
||||
{
|
||||
romfs_fileobj* file = (romfs_fileobj*)fd;
|
||||
memset(st, 0, sizeof(struct stat));
|
||||
st->st_size = (off_t)file->size;
|
||||
st->st_nlink = 1;
|
||||
st->st_mode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int romfs_stat(struct _reent *r, const char *file, struct stat *st)
|
||||
{
|
||||
r->_errno = ENOTSUP;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int romfs_chdir(struct _reent *r, const char *name)
|
||||
{
|
||||
romfs_dir* curDir = NULL;
|
||||
r->_errno = navigateToDir(&curDir, &name, true);
|
||||
if (r->_errno != 0)
|
||||
return -1;
|
||||
|
||||
romFS_cwd = curDir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DIR_ITER* romfs_diropen(struct _reent *r, DIR_ITER *dirState, const char *path)
|
||||
{
|
||||
//romfs_diriter* dir = (romfs_diriter*)(dirState->dirStruct);
|
||||
r->_errno = ENOTSUP;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int romfs_dirreset(struct _reent *r, DIR_ITER *dirState)
|
||||
{
|
||||
//romfs_diriter* dir = (romfs_diriter*)(dirState->dirStruct);
|
||||
r->_errno = ENOTSUP;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int romfs_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat)
|
||||
{
|
||||
//romfs_diriter* dir = (romfs_diriter*)(dirState->dirStruct);
|
||||
r->_errno = ENOTSUP;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int romfs_dirclose(struct _reent *r, DIR_ITER *dirState)
|
||||
{
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user