From 4df40699153af1cde7c3bfbf71991193fab86c54 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sat, 11 Mar 2017 01:37:00 -0800 Subject: [PATCH] Add more filesystem functionality; --- src/filesystem/filesystem.c | 169 +++++++++++++++++++++--------------- src/filesystem/filesystem.h | 23 +++-- src/graphics/skybox.c | 2 +- src/graphics/skybox.h | 2 +- src/lovr.c | 4 - src/lovr/audio.c | 2 +- src/lovr/filesystem.c | 107 +++++++++++++++++++---- src/lovr/filesystem.h | 10 ++- src/lovr/graphics.c | 10 +-- 9 files changed, 222 insertions(+), 107 deletions(-) diff --git a/src/filesystem/filesystem.c b/src/filesystem/filesystem.c index 7643d451..3c75276f 100644 --- a/src/filesystem/filesystem.c +++ b/src/filesystem/filesystem.c @@ -5,53 +5,84 @@ #include #ifdef __APPLE__ #include -#elif _WIN32 +#endif +#if _WIN32 #include #include #include #include #include +#else +#include +#include #endif static FilesystemState state; -void lovrFilesystemInit(const char* arg0) { +void lovrFilesystemInit(const char* arg0, const char* arg1) { if (!PHYSFS_init(arg0)) { error("Could not initialize filesystem: %s", PHYSFS_getLastError()); } - state.gameSource = NULL; + state.source = malloc(LOVR_PATH_MAX * sizeof(char)); state.identity = NULL; + state.isFused = 1; + + // Try to mount either an archive fused to the executable or an archive from the command line + lovrFilesystemGetExecutablePath(state.source, LOVR_PATH_MAX); + if (lovrFilesystemMount(state.source, NULL, 1)) { + state.isFused = 0; + strncpy(state.source, arg1, LOVR_PATH_MAX); + if (!state.source || lovrFilesystemMount(state.source, NULL, 1)) { + free(state.source); + state.source = NULL; + } + } + atexit(lovrFilesystemDestroy); } void lovrFilesystemDestroy() { + free(state.source); free(state.savePathFull); free(state.savePathRelative); PHYSFS_deinit(); } -int lovrFilesystemAppend(const char* path, const char* content, int size) { - if (!PHYSFS_isInit() || !state.identity) { - error("Can not write files until lovr.filesystem.setIdentity is called"); - } - - // Open file - PHYSFS_file* handle = PHYSFS_openAppend(path); - if (!handle) { - return 0; - } - - // Perform write - int bytesWritten = PHYSFS_write(handle, content, 1, size); - PHYSFS_close(handle); - return bytesWritten; +int lovrFilesystemCreateDirectory(const char* path) { + return !PHYSFS_mkdir(path); } int lovrFilesystemExists(const char* path) { return PHYSFS_exists(path); } +int lovrFilesystemGetAppdataDirectory(char* dest, unsigned int size) { +#ifdef __APPLE__ + const char* home; + if ((home = getenv("HOME")) == NULL) { + home = getpwuid(getuid())->pw_dir; + } + + snprintf(dest, size, "%s/Library/Application Support", home); + return 0; +#elif _WIN32 + PWSTR appData = NULL; + SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, NULL, &appData); + wcstombs(dest, appData, size); + CoTaskMemFree(appData); + return 0; +#else +#error "This platform is missing an implementation for lovrFilesystemGetAppdataDirectory" +#endif + + return 1; +} + +void lovrFilesystemGetDirectoryItems(const char* path, getDirectoryItemsCallback callback, void* userdata) { + PHYSFS_enumerateFilesCallback(path, callback, userdata); +} + int lovrFilesystemGetExecutablePath(char* dest, unsigned int size) { #ifdef __APPLE__ if (_NSGetExecutablePath(dest, &size) == 0) { @@ -70,23 +101,34 @@ const char* lovrFilesystemGetIdentity() { return state.identity; } -const char* lovrFilesystemGetRealDirectory(const char* path) { - if (!PHYSFS_isInit()) { - return NULL; - } +long lovrFilesystemGetLastModified(const char* path) { + return PHYSFS_getLastModTime(path); +} +const char* lovrFilesystemGetRealDirectory(const char* path) { return PHYSFS_getRealDir(path); } +const char* lovrFilesystemGetSaveDirectory() { + return state.savePathFull; +} + +int lovrFilesystemGetSize(const char* path) { + PHYSFS_file* handle = PHYSFS_openRead(path); + if (!handle) { + return 1; + } + + int length = PHYSFS_fileLength(handle); + PHYSFS_close(handle); + return length; +} + const char* lovrFilesystemGetSource() { - return state.gameSource; + return state.source; } const char* lovrFilesystemGetUserDirectory() { - if (!PHYSFS_isInit()) { - return NULL; - } - return PHYSFS_getUserDir(); } @@ -98,10 +140,15 @@ int lovrFilesystemIsFile(const char* path) { return lovrFilesystemExists(path) && !lovrFilesystemIsDirectory(path); } -void* lovrFilesystemRead(const char* path, int* bytesRead) { - if (!PHYSFS_isInit()) { - return NULL; - } +int lovrFilesystemIsFused() { + return state.isFused; +} + +int lovrFilesystemMount(const char* path, const char* mountpoint, int append) { + return !PHYSFS_mount(path, mountpoint, append); +} + +void* lovrFilesystemRead(const char* path, size_t* bytesRead) { // Open file PHYSFS_file* handle = PHYSFS_openRead(path); @@ -126,7 +173,7 @@ void* lovrFilesystemRead(const char* path, int* bytesRead) { PHYSFS_close(handle); // Make sure we got everything - if (*bytesRead != size) { + if (*bytesRead != (size_t) size) { free(data); return NULL; } @@ -134,6 +181,10 @@ void* lovrFilesystemRead(const char* path, int* bytesRead) { return data; } +int lovrFilesystemRemove(const char* path) { + return !PHYSFS_delete(path); +} + int lovrFilesystemSetIdentity(const char* identity) { state.identity = identity; @@ -143,58 +194,32 @@ int lovrFilesystemSetIdentity(const char* identity) { } else { state.savePathRelative = malloc(LOVR_PATH_MAX); state.savePathFull = malloc(LOVR_PATH_MAX); + if (!state.savePathRelative || !state.savePathFull) { + return 1; + } } - // Set new write directory -#ifdef __APPLE__ - const char* userDir = PHYSFS_getUserDir(); - PHYSFS_setWriteDir(userDir); - - snprintf(state.savePathRelative, LOVR_PATH_MAX, "Library/Application Support/LOVR/%s", identity); - PHYSFS_mkdir(state.savePathRelative); - - snprintf(state.savePathFull, LOVR_PATH_MAX, "%s%s", userDir, state.savePathRelative); - if (PHYSFS_setWriteDir(state.savePathFull)) { - PHYSFS_mount(state.savePathFull, NULL, 0); - return 0; - } -#elif _WIN32 - PWSTR appData = NULL; - SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, NULL, &appData); + lovrFilesystemGetAppdataDirectory(state.savePathFull, LOVR_PATH_MAX); + PHYSFS_setWriteDir(state.savePathFull); snprintf(state.savePathRelative, LOVR_PATH_MAX, "LOVR/%s", identity); - CoTaskMemFree(appData); -#else -#error "This platform is missing an implementation of lovrFilesystemSetIdentity" -#endif + snprintf(state.savePathFull, LOVR_PATH_MAX, "%s/%s", state.savePathFull, state.savePathRelative); + PHYSFS_mkdir(state.savePathRelative); + PHYSFS_setWriteDir(state.savePathRelative); + PHYSFS_mount(state.savePathFull, NULL, 0); - return 1; + return 0; } -int lovrFilesystemSetSource(const char* source) { - if (state.gameSource) { - return 1; - } - - if (PHYSFS_mount(source, NULL, 0)) { - state.gameSource = source; - return 0; - } - - return 1; +int lovrFilesystemUnmount(const char* path) { + return !PHYSFS_removeFromSearchPath(path); } -int lovrFilesystemWrite(const char* path, const char* content, int size) { - if (!PHYSFS_isInit() || !state.identity) { - error("Can not write files until lovr.filesystem.setIdentity is called"); - } - - // Open file - PHYSFS_file* handle = PHYSFS_openWrite(path); +int lovrFilesystemWrite(const char* path, const char* content, size_t size, int append) { + PHYSFS_file* handle = append ? PHYSFS_openAppend(path) : PHYSFS_openWrite(path); if (!handle) { return 0; } - // Perform write int bytesWritten = PHYSFS_write(handle, content, 1, size); PHYSFS_close(handle); return bytesWritten; diff --git a/src/filesystem/filesystem.h b/src/filesystem/filesystem.h index 8d38518b..75a437de 100644 --- a/src/filesystem/filesystem.h +++ b/src/filesystem/filesystem.h @@ -1,26 +1,39 @@ +#include + #pragma once #define LOVR_PATH_MAX 1024 +typedef void getDirectoryItemsCallback(void* userdata, const char* dir, const char* file); + typedef struct { - const char* gameSource; + char* source; const char* identity; char* savePathRelative; char* savePathFull; + int isFused; } FilesystemState; -void lovrFilesystemInit(const char* arg0); +void lovrFilesystemInit(const char* arg0, const char* arg1); void lovrFilesystemDestroy(); -int lovrFilesystemAppend(const char* path, const char* content, int size); int lovrFilesystemExists(const char* path); +int lovrFilesystemGetAppdataDirectory(char* dest, unsigned int size); +void lovrFilesystemGetDirectoryItems(const char* path, getDirectoryItemsCallback callback, void* userdata); int lovrFilesystemGetExecutablePath(char* dest, unsigned int size); const char* lovrFilesystemGetIdentity(); +long lovrFilesystemGetLastModified(const char* path); const char* lovrFilesystemGetRealDirectory(const char* path); +const char* lovrFilesystemGetSaveDirectory(); +int lovrFilesystemGetSize(const char* path); const char* lovrFilesystemGetSource(); const char* lovrFilesystemGetUserDirectory(); int lovrFilesystemIsDirectory(const char* path); int lovrFilesystemIsFile(const char* path); -void* lovrFilesystemRead(const char* path, int* bytesRead); +int lovrFilesystemIsFused(); +int lovrFilesystemMount(const char* path, const char* mountpoint, int append); +void* lovrFilesystemRead(const char* path, size_t* bytesRead); +int lovrFilesystemRemove(const char* path); int lovrFilesystemSetIdentity(const char* identity); int lovrFilesystemSetSource(const char* source); -int lovrFilesystemWrite(const char* path, const char* content, int size); +int lovrFilesystemUnmount(const char* path); +int lovrFilesystemWrite(const char* path, const char* content, size_t size, int append); diff --git a/src/graphics/skybox.c b/src/graphics/skybox.c index e324c0af..e69df669 100644 --- a/src/graphics/skybox.c +++ b/src/graphics/skybox.c @@ -2,7 +2,7 @@ #include "vendor/stb/stb_image.h" #include -Skybox* lovrSkyboxCreate(void** data, int* size) { +Skybox* lovrSkyboxCreate(void** data, size_t* size) { Skybox* skybox = lovrAlloc(sizeof(Skybox), lovrSkyboxDestroy); if (!skybox) return NULL; diff --git a/src/graphics/skybox.h b/src/graphics/skybox.h index 7977e12a..4255a464 100644 --- a/src/graphics/skybox.h +++ b/src/graphics/skybox.h @@ -8,5 +8,5 @@ typedef struct { GLuint texture; } Skybox; -Skybox* lovrSkyboxCreate(void** data, int* size); +Skybox* lovrSkyboxCreate(void** data, size_t* size); void lovrSkyboxDestroy(const Ref* ref); diff --git a/src/lovr.c b/src/lovr.c index 0dccf3f0..076a9a65 100644 --- a/src/lovr.c +++ b/src/lovr.c @@ -88,10 +88,6 @@ void lovrInit(lua_State* L, int argc, char** argv) { "} " "lovr.filesystem = require('lovr.filesystem') " - "lovr.filesystem.init(arg[-2]) " - "if not lovr.filesystem.setSource(lovr.filesystem.getExecutablePath()) and arg[1] then " - " lovr.filesystem.setSource(arg[1]) " - "end " "local success, err = pcall(require, 'conf') " "if lovr.conf then " diff --git a/src/lovr/audio.c b/src/lovr/audio.c index 03940d8f..9d51dca9 100644 --- a/src/lovr/audio.c +++ b/src/lovr/audio.c @@ -96,7 +96,7 @@ int l_lovrAudioNewSource(lua_State* L) { return luaL_error(L, "Only .ogg files are supported"); } - int size; + size_t size; void* data = lovrFilesystemRead(filename, &size); if (!data) { return luaL_error(L, "Could not load source from file '%s'", filename); diff --git a/src/lovr/filesystem.c b/src/lovr/filesystem.c index e779de14..7712df57 100644 --- a/src/lovr/filesystem.c +++ b/src/lovr/filesystem.c @@ -3,6 +3,13 @@ #include #include +static void pushDirectoryItem(void* userdata, const char* path, const char* filename) { + lua_State* L = userdata; + int n = lua_objlen(L, -1); + lua_pushstring(L, filename); + lua_rawseti(L, -2, n + 1); +} + // Loader to help Lua's require understand PhysFS. static int filesystemLoader(lua_State* L) { const char* module = luaL_checkstring(L, -1); @@ -30,7 +37,7 @@ static int filesystemLoader(lua_State* L) { strncat(filename, requirePath[i] + index + 1, strlen(requirePath[i]) - index); if (lovrFilesystemIsFile(filename)) { - int size; + size_t size; void* data = lovrFilesystemRead(filename, &size); if (data) { @@ -48,20 +55,24 @@ static int filesystemLoader(lua_State* L) { } const luaL_Reg lovrFilesystem[] = { - { "init", l_lovrFilesystemInitialize }, { "append", l_lovrFilesystemAppend }, { "exists", l_lovrFilesystemExists }, + { "getAppdataDirectory", l_lovrFilesystemGetAppdataDirectory }, + { "getDirectoryItems", l_lovrFilesystemGetDirectoryItems }, { "getExecutablePath", l_lovrFilesystemGetExecutablePath }, { "getIdentity", l_lovrFilesystemGetIdentity }, { "getRealDirectory", l_lovrFilesystemGetRealDirectory }, + { "getSaveDirectory", l_lovrFilesystemGetSaveDirectory }, { "getSource", l_lovrFilesystemGetSource }, { "getUserDirectory", l_lovrFilesystemGetUserDirectory }, { "isDirectory", l_lovrFilesystemIsDirectory }, { "isFile", l_lovrFilesystemIsFile }, + { "isFused", l_lovrFilesystemIsFused }, { "load", l_lovrFilesystemLoad }, + { "mount", l_lovrFilesystemMount }, { "read", l_lovrFilesystemRead }, + { "remove", l_lovrFilesystemRemove }, { "setIdentity", l_lovrFilesystemSetIdentity }, - { "setSource", l_lovrFilesystemSetSource }, { "write", l_lovrFilesystemWrite }, { NULL, NULL } }; @@ -70,6 +81,14 @@ int l_lovrFilesystemInit(lua_State* L) { lua_newtable(L); luaL_register(L, NULL, lovrFilesystem); + lua_getglobal(L, "arg"); + lua_rawgeti(L, -1, -2); + lua_rawgeti(L, -2, 1); + const char* arg0 = lua_tostring(L, -2); + const char* arg1 = lua_tostring(L, -1); + lovrFilesystemInit(arg0, arg1); + lua_pop(L, 3); + // Add custom package loader lua_getglobal(L, "table"); lua_getfield(L, -1, "insert"); @@ -87,17 +106,11 @@ int l_lovrFilesystemInit(lua_State* L) { return 1; } -int l_lovrFilesystemInitialize(lua_State* L) { - const char* arg0 = luaL_checkstring(L, 1); - lovrFilesystemInit(arg0); - return 0; -} - int l_lovrFilesystemAppend(lua_State* L) { size_t size; const char* path = luaL_checkstring(L, 1); const char* content = luaL_checklstring(L, 2, &size); - lua_pushnumber(L, lovrFilesystemAppend(path, content, size)); + lua_pushnumber(L, lovrFilesystemWrite(path, content, size, 1)); return 1; } @@ -107,6 +120,25 @@ int l_lovrFilesystemExists(lua_State* L) { return 1; } +int l_lovrFilesystemGetAppdataDirectory(lua_State* L) { + char buffer[1024]; + + if (lovrFilesystemGetExecutablePath(buffer, sizeof(buffer))) { + lua_pushnil(L); + } else { + lua_pushstring(L, buffer); + } + + return 1; +} + +int l_lovrFilesystemGetDirectoryItems(lua_State* L) { + const char* path = luaL_checkstring(L, 1); + lua_newtable(L); + lovrFilesystemGetDirectoryItems(path, pushDirectoryItem, L); + return 1; +} + int l_lovrFilesystemGetExecutablePath(lua_State* L) { char buffer[1024]; @@ -124,12 +156,36 @@ int l_lovrFilesystemGetIdentity(lua_State* L) { return 1; } +int l_lovrFilesystemGetLastModified(lua_State* L) { + const char* path = luaL_checkstring(L, 1); + int lastModified = lovrFilesystemGetLastModified(path); + + if (lastModified < 0) { + lua_pushnil(L); + } else { + lua_pushinteger(L, lastModified); + } + + return 1; +} + int l_lovrFilesystemGetRealDirectory(lua_State* L) { const char* path = luaL_checkstring(L, 1); lua_pushstring(L, lovrFilesystemGetRealDirectory(path)); return 1; } +int l_lovrFilesystemGetSaveDirectory(lua_State* L) { + lua_pushstring(L, lovrFilesystemGetSaveDirectory()); + return 1; +} + +int l_lovrFilesystemGetSize(lua_State* L) { + const char* path = luaL_checkstring(L, 1); + lua_pushinteger(L, lovrFilesystemGetSize(path)); + return 1; +} + int l_lovrFilesystemGetSource(lua_State* L) { lua_pushstring(L, lovrFilesystemGetSource()); return 1; @@ -152,9 +208,14 @@ int l_lovrFilesystemIsFile(lua_State* L) { return 1; } +int l_lovrFilesystemIsFused(lua_State* L) { + lua_pushboolean(L, lovrFilesystemIsFused()); + return 1; +} + int l_lovrFilesystemLoad(lua_State* L) { const char* path = luaL_checkstring(L, 1); - int size; + size_t size; char* content = lovrFilesystemRead(path, &size); int status = luaL_loadbuffer(L, content, size, path); @@ -165,9 +226,17 @@ int l_lovrFilesystemLoad(lua_State* L) { } } +int l_lovrFilesystemMount(lua_State* L) { + const char* path = luaL_checkstring(L, 1); + const char* mountpoint = luaL_optstring(L, 2, NULL); + int append = lua_isnoneornil(L, 3) ? 0 : lua_toboolean(L, 3); + lua_pushboolean(L, !lovrFilesystemMount(path, mountpoint, append)); + return 1; +} + int l_lovrFilesystemRead(lua_State* L) { const char* path = luaL_checkstring(L, 1); - int size; + size_t size; char* content = lovrFilesystemRead(path, &size); if (!content) { return luaL_error(L, "Could not read file '%s'", path); @@ -178,6 +247,12 @@ int l_lovrFilesystemRead(lua_State* L) { return 1; } +int l_lovrFilesystemRemove(lua_State* L) { + const char* path = luaL_checkstring(L, 1); + lua_pushboolean(L, !lovrFilesystemRemove(path)); + return 1; +} + int l_lovrFilesystemSetIdentity(lua_State* L) { const char* identity = luaL_checkstring(L, 1); @@ -188,9 +263,9 @@ int l_lovrFilesystemSetIdentity(lua_State* L) { return 0; } -int l_lovrFilesystemSetSource(lua_State* L) { - const char* source = luaL_checkstring(L, 1); - lua_pushboolean(L, !lovrFilesystemSetSource(source)); +int l_lovrFilesystemUnmount(lua_State* L) { + const char* path = luaL_checkstring(L, 1); + lua_pushboolean(L, !lovrFilesystemUnmount(path)); return 1; } @@ -198,6 +273,6 @@ int l_lovrFilesystemWrite(lua_State* L) { size_t size; const char* path = luaL_checkstring(L, 1); const char* content = luaL_checklstring(L, 2, &size); - lua_pushnumber(L, lovrFilesystemWrite(path, content, size)); + lua_pushnumber(L, lovrFilesystemWrite(path, content, size, 0)); return 1; } diff --git a/src/lovr/filesystem.h b/src/lovr/filesystem.h index b0f418d9..d06aa9f4 100644 --- a/src/lovr/filesystem.h +++ b/src/lovr/filesystem.h @@ -2,18 +2,24 @@ extern const luaL_Reg lovrFilesystem[]; int l_lovrFilesystemInit(lua_State* L); -int l_lovrFilesystemInitialize(lua_State* L); int l_lovrFilesystemAppend(lua_State* L); int l_lovrFilesystemExists(lua_State* L); +int l_lovrFilesystemGetAppdataDirectory(lua_State* L); +int l_lovrFilesystemGetDirectoryItems(lua_State* L); int l_lovrFilesystemGetExecutablePath(lua_State* L); int l_lovrFilesystemGetIdentity(lua_State* L); +int l_lovrFilesystemGetLastModified(lua_State* L); int l_lovrFilesystemGetRealDirectory(lua_State* L); +int l_lovrFilesystemGetSaveDirectory(lua_State* L); int l_lovrFilesystemGetSource(lua_State* L); int l_lovrFilesystemGetUserDirectory(lua_State* L); int l_lovrFilesystemIsDirectory(lua_State* L); int l_lovrFilesystemIsFile(lua_State* L); +int l_lovrFilesystemIsFused(lua_State* L); int l_lovrFilesystemLoad(lua_State* L); +int l_lovrFilesystemMount(lua_State* L); int l_lovrFilesystemRead(lua_State* L); +int l_lovrFilesystemRemove(lua_State* L); int l_lovrFilesystemSetIdentity(lua_State* L); -int l_lovrFilesystemSetSource(lua_State* L); +int l_lovrFilesystemUnmount(lua_State* L); int l_lovrFilesystemWrite(lua_State* L); diff --git a/src/lovr/graphics.c b/src/lovr/graphics.c index 033e58fe..3cf7d3de 100644 --- a/src/lovr/graphics.c +++ b/src/lovr/graphics.c @@ -45,7 +45,7 @@ static void luax_readvertices(lua_State* L, int index, vec_float_t* points) { static Texture* luax_readtexture(lua_State* L, int index) { const char* path = luaL_checkstring(L, index); - int size; + size_t size; void* data = lovrFilesystemRead(path, &size); if (!data) { luaL_error(L, "Could not load texture file '%s'", path); @@ -636,7 +636,7 @@ int l_lovrGraphicsNewBuffer(lua_State* L) { int l_lovrGraphicsNewFont(lua_State* L) { void* data = NULL; - int size = 0; + size_t size = 0; float fontSize; if (lua_type(L, 1) == LUA_TNUMBER || lua_isnoneornil(L, 1)) { @@ -662,7 +662,7 @@ int l_lovrGraphicsNewFont(lua_State* L) { int l_lovrGraphicsNewModel(lua_State* L) { const char* path = lua_tostring(L, 1); - int size; + size_t size; void* data = lovrFilesystemRead(path, &size); if (!data) { return luaL_error(L, "Could not load model file '%s'", path); @@ -688,7 +688,7 @@ int l_lovrGraphicsNewShader(lua_State* L) { if (lua_isnoneornil(L, i + 1)) continue; const char* source = luaL_checkstring(L, i + 1); if (!lovrFilesystemIsFile(source)) continue; - int bytesRead; + size_t bytesRead; char* contents = lovrFilesystemRead(source, &bytesRead); if (bytesRead <= 0) { return luaL_error(L, "Could not read shader from file '%s'", source); @@ -708,7 +708,7 @@ int l_lovrGraphicsNewShader(lua_State* L) { int l_lovrGraphicsNewSkybox(lua_State* L) { void* data[6]; - int size[6]; + size_t size[6]; if (lua_istable(L, 1)) { if (lua_objlen(L, 1) != 6) {