]> Chaos Git - corbenik/ctrulib.git/commitdiff
Add work in progress RomFS driver/devoptab
authorfincs <fincs.alt1@gmail.com>
Thu, 27 Aug 2015 18:53:08 +0000 (20:53 +0200)
committerfincs <fincs.alt1@gmail.com>
Thu, 27 Aug 2015 18:53:08 +0000 (20:53 +0200)
libctru/include/3ds.h
libctru/include/3ds/romfs.h [new file with mode: 0644]
libctru/source/romfs_dev.c [new file with mode: 0644]

index 9b54fd1eddfb2419d3f1f9d22b99534592bb9e31..301d782c39cf26d78b6ddb821e118b81b7239425 100644 (file)
@@ -45,6 +45,7 @@ extern "C" {
 #include <3ds/gpu/shaderProgram.h>
 
 #include <3ds/sdmc.h>
+#include <3ds/romfs.h>
 
 #ifdef __cplusplus
 }
diff --git a/libctru/include/3ds/romfs.h b/libctru/include/3ds/romfs.h
new file mode 100644 (file)
index 0000000..6a160a1
--- /dev/null
@@ -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);
diff --git a/libctru/source/romfs_dev.c b/libctru/source/romfs_dev.c
new file mode 100644 (file)
index 0000000..9faf5df
--- /dev/null
@@ -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;
+}