This commit is contained in:
bjorn 2019-12-08 01:46:54 -08:00
parent 8795ebb31d
commit 403ed8d3b0
13 changed files with 201 additions and 118 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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 <math.h>
@ -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;

View File

@ -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);
}

View File

@ -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");

View File

@ -1,10 +1,8 @@
#include "zip.h"
#include <string.h>
#include <time.h>
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 <stdio.h>
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;
}

View File

@ -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);

View File

@ -4,10 +4,10 @@
#include "core/ref.h"
#include <stdlib.h>
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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -7,6 +7,7 @@
#include "lib/stb/stb_image.h"
#include <string.h>
#include <stdlib.h>
#include <time.h>
#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
};