From 403ed8d3b03f7c8bcf8b539bbb212ecb3f57c818 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 8 Dec 2019 01:46:54 -0800 Subject: [PATCH] Windows; --- src/api/api.h | 4 + src/api/l_data.c | 2 +- src/api/l_filesystem.c | 12 ++- src/api/l_graphics.c | 38 ++++----- src/api/l_thread_module.c | 3 +- src/core/fs.c | 124 +++++++++++++++++++++++----- src/core/zip.c | 42 +--------- src/core/zip.h | 3 +- src/modules/data/modelData.c | 6 +- src/modules/data/modelData.h | 8 +- src/modules/data/modelData_gltf.c | 7 +- src/modules/data/modelData_obj.c | 11 ++- src/modules/filesystem/filesystem.c | 59 ++++++++++--- 13 files changed, 201 insertions(+), 118 deletions(-) diff --git a/src/api/api.h b/src/api/api.h index 1a691686..d2891fb7 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -145,6 +145,10 @@ void luax_checkvariant(lua_State* L, int index, struct Variant* variant); int luax_pushvariant(lua_State* L, struct Variant* variant); #endif +#ifdef LOVR_ENABLE_FILESYSTEM +void* luax_readfile(const char* filename, size_t* bytesRead); +#endif + #ifdef LOVR_ENABLE_GRAPHICS struct Attachment; struct Texture; diff --git a/src/api/l_data.c b/src/api/l_data.c index eda8fa2e..1f2638a0 100644 --- a/src/api/l_data.c +++ b/src/api/l_data.c @@ -50,7 +50,7 @@ static int l_lovrDataNewAudioStream(lua_State* L) { static int l_lovrDataNewModelData(lua_State* L) { Blob* blob = luax_readblob(L, 1, "Model"); - ModelData* modelData = lovrModelDataCreate(blob); + ModelData* modelData = lovrModelDataCreate(blob, luax_readfile); luax_pushtype(L, ModelData, modelData); lovrRelease(Blob, blob); lovrRelease(ModelData, modelData); diff --git a/src/api/l_filesystem.c b/src/api/l_filesystem.c index ba8f8c0c..2f60136b 100644 --- a/src/api/l_filesystem.c +++ b/src/api/l_filesystem.c @@ -13,6 +13,10 @@ const char lovrDirSep = '\\'; const char lovrDirSep = '/'; #endif +void* luax_readfile(const char* filename, size_t* bytesRead) { + return lovrFilesystemRead(filename, -1, bytesRead); +} + // Returns a Blob, leaving stack unchanged. The Blob must be released when finished. Blob* luax_readblob(lua_State* L, int index, const char* debug) { if (lua_type(L, index) == LUA_TUSERDATA) { @@ -23,7 +27,7 @@ Blob* luax_readblob(lua_State* L, int index, const char* debug) { const char* path = luaL_checkstring(L, index); size_t size; - void* data = lovrFilesystemRead(path, -1, &size); + void* data = luax_readfile(path, &size); if (!data) { luaL_error(L, "Could not read %s from '%s'", debug, path); } @@ -60,7 +64,7 @@ static void pushDirectoryItem(void* context, const char* path) { static int luax_loadfile(lua_State* L, const char* path, const char* debug) { size_t size; - void* buffer = lovrFilesystemRead(path, -1, &size); + void* buffer = luax_readfile(path, &size); int status = luaL_loadbuffer(L, buffer, size, debug); free(buffer); switch (status) { @@ -259,7 +263,7 @@ static int l_lovrFilesystemLoad(lua_State* L) { static int l_lovrFilesystemMount(lua_State* L) { const char* path = luaL_checkstring(L, 1); const char* mountpoint = luaL_optstring(L, 2, NULL); - bool append = lua_isnoneornil(L, 3) ? 0 : lua_toboolean(L, 3); + bool append = lua_toboolean(L, 3); const char* root = luaL_optstring(L, 4, NULL); lua_pushboolean(L, lovrFilesystemMount(path, mountpoint, append, root)); return 1; @@ -268,7 +272,7 @@ static int l_lovrFilesystemMount(lua_State* L) { static int l_lovrFilesystemNewBlob(lua_State* L) { size_t size; const char* path = luaL_checkstring(L, 1); - uint8_t* data = lovrFilesystemRead(path, -1, &size); + uint8_t* data = luax_readfile(path, &size); lovrAssert(data, "Could not load file '%s'", path); Blob* blob = lovrBlobCreate(data, size, path); luax_pushtype(L, Blob, blob); diff --git a/src/api/l_graphics.c b/src/api/l_graphics.c index 0a991266..f787a927 100644 --- a/src/api/l_graphics.c +++ b/src/api/l_graphics.c @@ -10,7 +10,6 @@ #include "data/modelData.h" #include "data/rasterizer.h" #include "data/textureData.h" -#include "filesystem/filesystem.h" #include "core/arr.h" #include "core/ref.h" #include @@ -1365,7 +1364,7 @@ static int l_lovrGraphicsNewModel(lua_State* L) { if (!modelData) { Blob* blob = luax_readblob(L, 1, "Model"); - modelData = lovrModelDataCreate(blob); + modelData = lovrModelDataCreate(blob, luax_readfile); lovrRelease(Blob, blob); } @@ -1376,29 +1375,25 @@ static int l_lovrGraphicsNewModel(lua_State* L) { return 1; } -static void luax_readshadersource(lua_State* L, int index) { +static const char* luax_checkshadersource(lua_State* L, int index) { if (lua_isnoneornil(L, index)) { - return; + return NULL; } Blob* blob = luax_totype(L, index, Blob); if (blob) { - lua_pushlstring(L, blob->data, blob->size); - lua_replace(L, index); - return; + return blob->data; } - const char* source = luaL_checkstring(L, index); - if (!lovrFilesystemIsFile(source)) { - return; + size_t length; + const char* source = luaL_checklstring(L, index, &length); + if (memchr(source, '\n', MIN(1024, length))) { + return source; + } else { + void* contents = luax_readfile(source, &length); + lovrAssert(contents, "Could not read shader from file '%s'", source); + return contents; } - - size_t bytesRead; - char* contents = lovrFilesystemRead(source, -1, &bytesRead); - lovrAssert(bytesRead > 0, "Could not read shader from file '%s'", source); - lua_pushlstring(L, contents, bytesRead); - lua_replace(L, index); - free(contents); } #define MAX_SHADER_FLAGS 32 @@ -1468,10 +1463,8 @@ static int l_lovrGraphicsNewShader(lua_State* L) { lovrShaderSetFloats(shader, "lovrLightColor", (float[4]) { 1.f, 1.f, 1.f, 1.f }, 0, 4); } } else { - luax_readshadersource(L, 1); - luax_readshadersource(L, 2); - const char* vertexSource = lua_tostring(L, 1); - const char* fragmentSource = lua_tostring(L, 2); + const char* vertexSource = luax_checkshadersource(L, 1); + const char* fragmentSource = luax_checkshadersource(L, 2); if (lua_istable(L, 3)) { lua_getfield(L, 3, "flags"); @@ -1492,8 +1485,7 @@ static int l_lovrGraphicsNewShader(lua_State* L) { } static int l_lovrGraphicsNewComputeShader(lua_State* L) { - luax_readshadersource(L, 1); - const char* source = lua_tostring(L, 1); + const char* source = luax_checkshadersource(L, 1); ShaderFlag flags[MAX_SHADER_FLAGS]; uint32_t flagCount = 0; diff --git a/src/api/l_thread_module.c b/src/api/l_thread_module.c index f7f2e849..2ec7b3a1 100644 --- a/src/api/l_thread_module.c +++ b/src/api/l_thread_module.c @@ -1,6 +1,5 @@ #include "api.h" #include "event/event.h" -#include "filesystem/filesystem.h" #include "thread/thread.h" #include "thread/channel.h" #include "core/ref.h" @@ -62,7 +61,7 @@ static int l_lovrThreadNewThread(lua_State* L) { memcpy(data, str, length + 1); blob = lovrBlobCreate(data, length, "thread code"); } else { - void* code = lovrFilesystemRead(str, -1, &length); + void* code = luax_readfile(str, &length); lovrAssert(code, "Could not read thread code from file '%s'", str); blob = lovrBlobCreate(code, length, str); } diff --git a/src/core/fs.c b/src/core/fs.c index 56140951..d3b83455 100644 --- a/src/core/fs.c +++ b/src/core/fs.c @@ -21,10 +21,19 @@ bool fs_open(const char* path, OpenMode mode, fs_handle* file) { case OPEN_APPEND: access = GENERIC_WRITE; creation = OPEN_ALWAYS; break; default: return false; } + DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE; file->handle = CreateFileW(wpath, access, share, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); - // TODO seek to end of file if appending - return file->handle != INVALID_HANDLE_VALUE; + if (file->handle == INVALID_HANDLE_VALUE) { + return false; + } + + if (mode == OPEN_APPEND && SetFilePointer(file->handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) { + CloseHandle(file->handle); + return false; + } + + return true; } bool fs_close(fs_handle file) { @@ -32,19 +41,62 @@ bool fs_close(fs_handle file) { } bool fs_read(fs_handle file, void* buffer, size_t* bytes) { - return ReadFile(file.handle, buffer, *bytes, bytes, NULL); + uint32_t bytes32 = *bytes > UINT32_MAX ? UINT32_MAX : (uint32_t) *bytes; + bool success = ReadFile(file.handle, buffer, bytes32, &bytes32, NULL); + *bytes = bytes32; + return success; } bool fs_write(fs_handle file, const void* buffer, size_t* bytes) { - return WriteFile(file.handle, buffer, *bytes, bytes, NULL); + uint32_t bytes32 = *bytes > UINT32_MAX ? UINT32_MAX : (uint32_t) *bytes; + bool success = WriteFile(file.handle, buffer, bytes32, &bytes32, NULL); + *bytes = bytes32; + return success; } void* fs_map(const char* path, size_t* size) { - return NULL; + WCHAR wpath[FS_PATH_MAX]; + if (!MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, FS_PATH_MAX)) { + return false; + } + + fs_handle file; + file.handle = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (file.handle == INVALID_HANDLE_VALUE) { + return NULL; + } + + DWORD hi; + DWORD lo = GetFileSize(file.handle, &hi); + if (lo == INVALID_FILE_SIZE) { + CloseHandle(file.handle); + return NULL; + } + + if (SIZE_MAX > UINT32_MAX) { + *size = ((size_t) hi << 32) | lo; + } else if (hi > 0) { + CloseHandle(file.handle); + return NULL; + } else { + *size = lo; + } + + HANDLE mapping = CreateFileMappingA(file.handle, NULL, PAGE_READONLY, hi, lo, NULL); + if (mapping == NULL) { + CloseHandle(file.handle); + return NULL; + } + + void* data = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, *size); + + CloseHandle(mapping); + CloseHandle(file.handle); + return data; } bool fs_unmap(void* data, size_t size) { - return false; + return UnmapViewOfFile(data); } bool fs_stat(const char* path, FileInfo* info) { @@ -59,8 +111,10 @@ bool fs_stat(const char* path, FileInfo* info) { } FILETIME lastModified = attributes.ftLastWriteTime; - info->type = attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + info->type = (attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FILE_DIRECTORY : FILE_REGULAR; info->lastModified = ((uint64_t) lastModified.dwHighDateTime << 32) | lastModified.dwLowDateTime; + info->lastModified /= 10000000ULL; // Convert windows 100ns ticks to seconds + info->lastModified -= 11644473600ULL; // Convert windows epoch (1601) to POSIX epoch (1970) info->size = ((uint64_t) attributes.nFileSizeHigh << 32) | attributes.nFileSizeLow; return true; } @@ -82,13 +136,35 @@ bool fs_mkdir(const char* path) { } bool fs_list(const char* path, fs_list_cb* callback, void* context) { - return false; + WCHAR wpath[FS_PATH_MAX]; + if (!MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, FS_PATH_MAX)) { + return false; + } + + WIN32_FIND_DATAW findData; + HANDLE handle = FindFirstFileW(wpath, &findData); + if (handle == INVALID_HANDLE_VALUE) { + return false; + } + + char filename[FS_PATH_MAX]; + do { + if (!WideCharToMultiByte(CP_UTF8, 0, findData.cFileName, -1, filename, FS_PATH_MAX, NULL, NULL)) { + FindClose(handle); + return false; + } + + callback(context, filename); + } while (FindNextFileW(handle, &findData)); + + FindClose(handle); + return true; } size_t fs_getHomeDir(char* buffer, size_t size) { PWSTR wpath = NULL; if (SHGetKnownFolderPath(&FOLDERID_Profile, 0, NULL, &wpath) == S_OK) { - size_t bytes = WideCharToMultiByte(CP_UTF8, 0, wpath, -1, buffer, size, NULL, NULL); + size_t bytes = WideCharToMultiByte(CP_UTF8, 0, wpath, -1, buffer, (int) size, NULL, NULL) - 1; CoTaskMemFree(wpath); return bytes; } @@ -98,7 +174,7 @@ size_t fs_getHomeDir(char* buffer, size_t size) { size_t fs_getDataDir(char* buffer, size_t size) { PWSTR wpath = NULL; if (SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, NULL, &wpath) == S_OK) { - size_t bytes = WideCharToMultiByte(CP_UTF8, 0, wpath, -1, buffer, size, NULL, NULL); + size_t bytes = WideCharToMultiByte(CP_UTF8, 0, wpath, -1, buffer, (int) size, NULL, NULL) - 1; CoTaskMemFree(wpath); return bytes; } @@ -107,14 +183,19 @@ size_t fs_getDataDir(char* buffer, size_t size) { size_t fs_getWorkDir(char* buffer, size_t size) { WCHAR wpath[FS_PATH_MAX]; - int length = GetCurrentDirectoryW(size, wpath); + int length = GetCurrentDirectoryW((int) size, wpath); if (length) { - return WideCharToMultiByte(CP_UTF8, 0, wpath, length, buffer, size, NULL, NULL); + return WideCharToMultiByte(CP_UTF8, 0, wpath, length + 1, buffer, (int) size, NULL, NULL) - 1; } return 0; } size_t fs_getExecutablePath(char* buffer, size_t size) { + WCHAR wpath[FS_PATH_MAX]; + DWORD length = GetModuleFileNameW(NULL, wpath, FS_PATH_MAX); + if (length < FS_PATH_MAX) { + return WideCharToMultiByte(CP_UTF8, 0, wpath, length + 1, buffer, (int) size, NULL, NULL) - 1; + } return 0; } @@ -151,27 +232,27 @@ bool fs_open(const char* path, OpenMode mode, fs_handle* file) { } bool fs_close(fs_handle file) { - return close(file.handle) == 0; + return close(file.fd) == 0; } bool fs_read(fs_handle file, void* buffer, size_t* bytes) { - ssize_t result = read(file.handle, buffer, *bytes); - if (result < 0) { + ssize_t result = read(file.fd, buffer, *bytes); + if (result < 0 || result > UINT32_MAX) { *bytes = 0; return false; } else { - *bytes = (size_t) result; + *bytes = (uint32_t) result; return true; } } bool fs_write(fs_handle file, const void* buffer, size_t* bytes) { - ssize_t result = write(file.handle, buffer, *bytes); - if (result < 0) { + ssize_t result = write(file.fd, buffer, *bytes); + if (result < 0 || result > UINT32_MAX) { *bytes = 0; return false; } else { - *bytes = (size_t) result; + *bytes = (uint32_t) result; return true; } } @@ -183,7 +264,7 @@ void* fs_map(const char* path, size_t* size) { return NULL; } *size = info.size; - void* data = mmap(NULL, *size, PROT_READ, MAP_PRIVATE, file.handle, 0); + void* data = mmap(NULL, *size, PROT_READ, MAP_PRIVATE, file.fd, 0); fs_close(file); return data; } @@ -251,6 +332,7 @@ size_t fs_getHomeDir(char* buffer, size_t size) { return copy(buffer, size, home, strlen(home)); } +extern const char* lovrOculusMobileWritablePath; // TODO size_t fs_getDataDir(char* buffer, size_t size) { #if __APPLE__ size_t cursor = fs_getHomeDir(buffer, size); @@ -264,6 +346,8 @@ size_t fs_getDataDir(char* buffer, size_t size) { #elif EMSCRIPTEN const char* path = "/home/web_user"; return copy(buffer, size, path, strlen(path)); +#elif __ANDROID__ + return copy(buffer, size, lovrOculusMobileWritablePath, strlen(lovrOculusMobileWritablePath)); #else const char* xdg = getenv("XDG_DATA_HOME"); diff --git a/src/core/zip.c b/src/core/zip.c index 36c52889..93e03aaa 100644 --- a/src/core/zip.c +++ b/src/core/zip.c @@ -1,10 +1,8 @@ #include "zip.h" #include -#include static uint16_t readu16(const uint8_t* p) { uint16_t x; memcpy(&x, p, sizeof(x)); return x; } static uint32_t readu32(const uint8_t* p) { uint32_t x; memcpy(&x, p, sizeof(x)); return x; } -//static uint64_t readu64(const uint8_t* p) { uint64_t x; memcpy(&x, p, sizeof(x)); return x; } bool zip_open(zip_state* zip) { const uint8_t* p = zip->data + zip->size - 22; @@ -47,7 +45,6 @@ bool zip_open(zip_state* zip) { return true; } -#include bool zip_next(zip_state* zip, zip_file* file) { const uint8_t* p = zip->data + zip->cursor; @@ -55,19 +52,8 @@ bool zip_next(zip_state* zip, zip_file* file) { return false; } - uint16_t mtime = readu16(p + 12); - uint16_t mdate = readu16(p + 14); - struct tm t; - memset(&t, 0, sizeof(t)); - t.tm_isdst = -1; - t.tm_year = ((mdate >> 9) & 127) + 80; - t.tm_mon = ((mdate >> 5) & 15) - 1; - t.tm_mday = mdate & 31; - t.tm_hour = (mtime >> 11) & 31; - t.tm_min = (mtime >> 5) & 63; - t.tm_sec = (mtime << 1) & 62; - file->modtime = mktime(&t); - + file->mtime = readu16(p + 12); + file->mdate = readu16(p + 14); file->size = readu32(p + 24); file->length = readu16(p + 28); file->offset = readu32(p + 42) + zip->base; @@ -96,27 +82,3 @@ void* zip_load(zip_state* zip, size_t offset, size_t* csize, bool* compressed) { uint32_t skip = readu16(p + 26) + readu16(p + 28); return offset + 30 + skip + *csize > zip->size ? NULL : (p + 30 + skip); } - -int LLVMFuzzerTestOneInput(const char *data, long long size) { - zip_state zip = { - .data = (uint8_t*) data, - .size = size - }; - - if (!zip_open(&zip)) { - return 0; - } - - zip_file file; - for (uint64_t i = 0; i < zip.count; i++) { - if (!zip_next(&zip, &file)) { - return 0; - } - - size_t csize; - bool compressed; - zip_load(&zip, file.offset, &csize, &compressed); - } - - return 0; -} diff --git a/src/core/zip.h b/src/core/zip.h index 71a4a3bb..8c57d677 100644 --- a/src/core/zip.h +++ b/src/core/zip.h @@ -24,9 +24,10 @@ typedef struct { typedef struct { uint64_t offset; uint64_t size; - uint64_t modtime; const char* name; uint16_t length; + uint16_t mdate; + uint16_t mtime; } zip_file; bool zip_open(zip_state* zip); diff --git a/src/modules/data/modelData.c b/src/modules/data/modelData.c index 6b716a91..cc5a31c8 100644 --- a/src/modules/data/modelData.c +++ b/src/modules/data/modelData.c @@ -4,10 +4,10 @@ #include "core/ref.h" #include -ModelData* lovrModelDataInit(ModelData* model, Blob* source) { - if (lovrModelDataInitGltf(model, source)) { +ModelData* lovrModelDataInit(ModelData* model, Blob* source, ModelDataIO* io) { + if (lovrModelDataInitGltf(model, source, io)) { return model; - } else if (lovrModelDataInitObj(model, source)) { + } else if (lovrModelDataInitObj(model, source, io)) { return model; } diff --git a/src/modules/data/modelData.h b/src/modules/data/modelData.h index c510efbc..ba6be249 100644 --- a/src/modules/data/modelData.h +++ b/src/modules/data/modelData.h @@ -212,9 +212,11 @@ typedef struct ModelData { map_t nodeMap; } ModelData; -ModelData* lovrModelDataInit(ModelData* model, struct Blob* blob); +typedef void* ModelDataIO(const char* filename, size_t* bytesRead); + +ModelData* lovrModelDataInit(ModelData* model, struct Blob* blob, ModelDataIO* io); #define lovrModelDataCreate(...) lovrModelDataInit(lovrAlloc(ModelData), __VA_ARGS__) -ModelData* lovrModelDataInitGltf(ModelData* model, struct Blob* blob); -ModelData* lovrModelDataInitObj(ModelData* model, struct Blob* blob); +ModelData* lovrModelDataInitGltf(ModelData* model, struct Blob* blob, ModelDataIO* io); +ModelData* lovrModelDataInitObj(ModelData* model, struct Blob* blob, ModelDataIO* io); void lovrModelDataDestroy(void* ref); void lovrModelDataAllocate(ModelData* model); diff --git a/src/modules/data/modelData_gltf.c b/src/modules/data/modelData_gltf.c index 92e8441e..a5d24f83 100644 --- a/src/modules/data/modelData_gltf.c +++ b/src/modules/data/modelData_gltf.c @@ -1,7 +1,6 @@ #include "data/modelData.h" #include "data/blob.h" #include "data/textureData.h" -#include "filesystem/filesystem.h" #include "core/hash.h" #include "core/maf.h" #include "core/ref.h" @@ -148,7 +147,7 @@ static jsmntok_t* resolveTexture(const char* json, jsmntok_t* token, ModelMateri return token; } -ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) { +ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io) { uint8_t* data = source->data; gltfHeader* header = (gltfHeader*) data; bool glb = header->magic == MAGIC_glTF; @@ -490,7 +489,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) { } else { lovrAssert(uri.length < maxPathLength, "Buffer filename is too long"); strncat(filename, uri.data, uri.length); - *blob = lovrBlobCreate(lovrFilesystemRead(filename, -1, &bytesRead), size, NULL); + *blob = lovrBlobCreate(io(filename, &bytesRead), size, NULL); lovrAssert((*blob)->data && bytesRead == size, "Unable to read %s", filename); *root = '\0'; } @@ -671,7 +670,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) { lovrAssert(uri.length < 5 || strncmp("data:", uri.data, 5), "Base64 images aren't supported yet"); lovrAssert(uri.length < maxPathLength, "Image filename is too long"); strncat(filename, uri.data, uri.length); - void* data = lovrFilesystemRead(filename, -1, &size); + void* data = io(filename, &size); lovrAssert(data && size > 0, "Unable to read texture from '%s'", filename); Blob* blob = lovrBlobCreate(data, size, NULL); *texture = lovrTextureDataCreateFromBlob(blob, false); diff --git a/src/modules/data/modelData_obj.c b/src/modules/data/modelData_obj.c index 6d801fa6..e4fc6f8d 100644 --- a/src/modules/data/modelData_obj.c +++ b/src/modules/data/modelData_obj.c @@ -1,7 +1,6 @@ #include "data/modelData.h" #include "data/blob.h" #include "data/textureData.h" -#include "filesystem/filesystem.h" #include "core/arr.h" #include "core/hash.h" #include "core/maf.h" @@ -25,9 +24,9 @@ typedef arr_t(objGroup) arr_group_t; #define STARTS_WITH(a, b) !strncmp(a, b, strlen(b)) -static void parseMtl(char* path, arr_texturedata_t* textures, arr_material_t* materials, map_t* names, char* base) { +static void parseMtl(char* path, ModelDataIO* io, arr_texturedata_t* textures, arr_material_t* materials, map_t* names, char* base) { size_t length = 0; - char* data = lovrFilesystemRead(path, -1, &length); + char* data = io(path, &length); lovrAssert(data && length > 0, "Unable to read mtl from '%s'", path); char* s = data; @@ -61,7 +60,7 @@ static void parseMtl(char* path, arr_texturedata_t* textures, arr_material_t* ma char path[1024]; snprintf(path, sizeof(path), "%s%s", base, filename); size_t size = 0; - void* data = lovrFilesystemRead(path, -1, &size); + void* data = io(path, &size); lovrAssert(data && size > 0, "Unable to read texture from %s", path); Blob* blob = lovrBlobCreate(data, size, NULL); @@ -87,7 +86,7 @@ static void parseMtl(char* path, arr_texturedata_t* textures, arr_material_t* ma free(data); } -ModelData* lovrModelDataInitObj(ModelData* model, Blob* source) { +ModelData* lovrModelDataInitObj(ModelData* model, Blob* source, ModelDataIO* io) { char* data = (char*) source->data; size_t length = source->size; @@ -200,7 +199,7 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source) { lovrAssert(hasName, "Bad OBJ: Expected filename after mtllib"); char path[1024]; snprintf(path, sizeof(path), "%s%s", base, filename); - parseMtl(path, &textures, &materials, &materialMap, base); + parseMtl(path, io, &textures, &materials, &materialMap, base); } else if (STARTS_WITH(data, "usemtl ")) { char name[128]; uint64_t length = sscanf(data + 7, "%s\n%n", name, &lineLength); diff --git a/src/modules/filesystem/filesystem.c b/src/modules/filesystem/filesystem.c index 0bf1e249..33331ad7 100644 --- a/src/modules/filesystem/filesystem.c +++ b/src/modules/filesystem/filesystem.c @@ -7,6 +7,7 @@ #include "lib/stb/stb_image.h" #include #include +#include #define FOREACH_ARCHIVE(a) for (Archive* a = state.archives.data; a != state.archives.data + state.archives.length; a++) @@ -30,12 +31,14 @@ typedef struct { uint32_t nextSibling; size_t filename; uint64_t offset; + uint16_t mdate; + uint16_t mtime; FileInfo info; } zip_node; typedef struct Archive { bool (*stat)(struct Archive* archive, const char* path, FileInfo* info); - void (*list)(struct Archive* archive, const char* path, void (*callback)(void* context, const char* path), void* context); + void (*list)(struct Archive* archive, const char* path, fs_list_cb callback, void* context); bool (*read)(struct Archive* archive, const char* path, size_t bytes, size_t* bytesRead, void** data); bool (*close)(struct Archive* archive); zip_state zip; @@ -60,16 +63,25 @@ static struct { } state; static bool valid(const char* path) { - char c; + if (path[0] == '.' && (path[1] == '\0' || path[1] == '.')) { + return false; + } + do { - c = *path++; - if (c == ':' || c == '\\' || (c == '.' && *path == '.')) { + if ( + *path == ':' || + *path == '\\' || + (*path == '/' && path[1] == '.' && + (path[2] == '.' ? (path[3] == '/' || path[3] == '\0') : (path[2] == '/' || path[2] == '\0'))) + ) { return false; } - } while (c != '\0'); + } while (*path++ != '\0'); + return true; } +// Does not work with empty strings static bool concat(char* buffer, const char* p1, size_t length1, const char* p2, size_t length2) { if (length1 + 1 + length2 >= LOVR_PATH_MAX) return false; memcpy(buffer + length1 + 1, p2, length2); @@ -142,6 +154,12 @@ static bool dir_init(Archive* archive, const char* path, const char* mountpoint, static bool zip_init(Archive* archive, const char* path, const char* mountpoint, const char* root); bool lovrFilesystemMount(const char* path, const char* mountpoint, bool append, const char* root) { + FOREACH_ARCHIVE(archive) { + if (!strcmp(strpool_resolve(&archive->strings, archive->path), path)) { + return false; + } + } + Archive archive; arr_init(&archive.strings); @@ -472,8 +490,25 @@ static zip_node* zip_lookup(Archive* archive, const char* path) { } static bool zip_stat(Archive* archive, const char* path, FileInfo* info) { - const zip_node* node = zip_lookup(archive, path); + zip_node* node = zip_lookup(archive, path); if (!node) return false; + + // zip stores timestamps in dos time, conversion is slow so we do it only on request + if (node->info.lastModified == ~0ull) { + uint16_t mdate = node->mdate; + uint16_t mtime = node->mtime; + struct tm t; + memset(&t, 0, sizeof(t)); + t.tm_isdst = -1; + t.tm_year = ((mdate >> 9) & 127) + 80; + t.tm_mon = ((mdate >> 5) & 15) - 1; + t.tm_mday = mdate & 31; + t.tm_hour = (mtime >> 11) & 31; + t.tm_min = (mtime >> 5) & 63; + t.tm_sec = (mtime << 1) & 62; + node->info.lastModified = mktime(&t); + } + *info = node->info; return true; } @@ -513,15 +548,15 @@ static bool zip_read(Archive* archive, const char* path, size_t bytes, size_t* b return true; } - *bytesRead = (bytes == (size_t) -1 || bytes > dstSize) ? dstSize : bytes; + *bytesRead = (bytes == (size_t) -1 || bytes > dstSize) ? (uint32_t) dstSize : bytes; if (compressed) { - if (stbi_zlib_decode_noheader_buffer(*dst, dstSize, src, srcSize) < 0) { + if (stbi_zlib_decode_noheader_buffer(*dst, (int) dstSize, src, (int) srcSize) < 0) { free(*dst); *dst = NULL; } } else { - memcpy(*dst, src, bytes); + memcpy(*dst, src, *bytesRead); } return true; @@ -536,6 +571,7 @@ static bool zip_close(Archive* archive) { static bool zip_init(Archive* archive, const char* filename, const char* mountpoint, const char* root) { char path[LOVR_PATH_MAX]; memset(&archive->lookup, 0, sizeof(archive->lookup)); + arr_init(&archive->nodes); // mmap the zip file, try to parse it, and figure out how many files there are archive->zip.data = fs_map(filename, &archive->zip.size); @@ -564,7 +600,6 @@ static bool zip_init(Archive* archive, const char* filename, const char* mountpo while (root && root[rootLength - 1] == '/') rootLength--; // Allocate - arr_init(&archive->nodes); map_init(&archive->lookup, archive->zip.count); arr_reserve(&archive->nodes, archive->zip.count); @@ -581,8 +616,10 @@ static bool zip_init(Archive* archive, const char* filename, const char* mountpo .nextSibling = ~0u, .filename = (size_t) -1, .offset = info.offset, + .mdate = info.mdate, + .mtime = info.mtime, .info.size = info.size, - .info.lastModified = info.modtime, + .info.lastModified = ~0ull, .info.type = FILE_REGULAR };