Add work in progress RomFS driver/devoptab

This commit is contained in:
fincs 2015-08-27 20:53:08 +02:00
parent 24fd71c4ad
commit 570ea2ebd2
3 changed files with 500 additions and 0 deletions

View File

@ -45,6 +45,7 @@ extern "C" {
#include <3ds/gpu/shaderProgram.h>
#include <3ds/sdmc.h>
#include <3ds/romfs.h>
#ifdef __cplusplus
}

View 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
View 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;
}