lovr/src/modules/filesystem/filesystem.c

337 lines
8.8 KiB
C
Raw Normal View History

2016-11-19 09:28:01 +00:00
#include "filesystem/filesystem.h"
2017-10-22 00:35:50 +00:00
#include "filesystem/file.h"
2018-10-29 19:01:20 +00:00
#include "platform.h"
2016-11-19 09:28:01 +00:00
#include "util.h"
2016-11-01 00:14:31 +00:00
#include <physfs.h>
2017-01-22 02:18:12 +00:00
#include <stdlib.h>
2018-07-04 20:51:35 +00:00
#include <string.h>
2019-04-05 11:34:21 +00:00
#include <stdio.h>
2016-11-07 02:39:16 +00:00
#ifdef __APPLE__
2016-11-01 00:14:31 +00:00
#include <mach-o/dyld.h>
2017-03-11 09:37:00 +00:00
#endif
#if _WIN32
2016-11-12 09:19:47 +00:00
#include <windows.h>
#include <initguid.h>
#include <KnownFolders.h>
#include <ShlObj.h>
#include <wchar.h>
2017-03-11 09:37:00 +00:00
#else
#include <unistd.h>
#include <pwd.h>
2016-11-01 00:14:31 +00:00
#endif
#if LOVR_USE_OCULUS_MOBILE
#include "headset/oculus_mobile.h"
#endif
2016-11-01 00:14:31 +00:00
2018-07-19 00:09:47 +00:00
#ifdef _WIN32
const char lovrDirSep = '\\';
#else
const char lovrDirSep = '/';
#endif
2016-11-07 22:30:32 +00:00
2019-05-20 10:51:22 +00:00
static struct {
bool initialized;
char* source;
const char* identity;
char* savePathRelative;
char* savePathFull;
bool isFused;
char* requirePath[2];
vec_str_t requirePattern[2];
} state;
bool lovrFilesystemInit(const char* argExe, const char* argGame, const char* argRoot) {
if (state.initialized) return false;
state.initialized = true;
if (!PHYSFS_init(argExe)) {
lovrThrow("Could not initialize filesystem: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
2016-11-01 00:14:31 +00:00
}
2016-11-07 22:30:32 +00:00
PHYSFS_permitSymbolicLinks(1);
2017-03-11 09:37:00 +00:00
state.source = malloc(LOVR_PATH_MAX * sizeof(char));
lovrAssert(state.source, "Out of memory");
2016-11-07 22:30:32 +00:00
state.identity = NULL;
2017-10-31 08:14:09 +00:00
state.isFused = true;
2018-07-07 04:21:07 +00:00
vec_init(&state.requirePattern[0]);
vec_init(&state.requirePattern[1]);
2018-10-05 07:40:50 +00:00
lovrFilesystemSetRequirePath("?.lua;?/init.lua;lua_modules/?.lua;lua_modules/?/init.lua;deps/?.lua;deps/?/init.lua");
lovrFilesystemSetCRequirePath("??;lua_modules/??;deps/??");
2017-03-11 09:37:00 +00:00
// Try to mount either an archive fused to the executable or an archive from the command line
lovrFilesystemGetExecutablePath(state.source, LOVR_PATH_MAX);
2019-05-20 10:51:22 +00:00
if (!lovrFilesystemMount(state.source, NULL, 1, argRoot)) { // Attempt to load fused. If that fails...
2017-10-31 08:14:09 +00:00
state.isFused = false;
2017-04-02 12:55:21 +00:00
if (argGame) {
strncpy(state.source, argGame, LOVR_PATH_MAX);
2019-05-20 10:51:22 +00:00
if (lovrFilesystemMount(state.source, NULL, 1, argRoot)) { // Attempt to load from arg. If success, init is done
return true;
2017-04-02 12:55:21 +00:00
}
2017-03-11 09:37:00 +00:00
}
2017-04-02 12:55:21 +00:00
free(state.source); // Couldn't load from argProject, so apparently it isn't the source
2017-04-02 12:55:21 +00:00
state.source = NULL;
2017-03-11 09:37:00 +00:00
}
return true;
2016-11-01 00:14:31 +00:00
}
void lovrFilesystemDestroy() {
if (!state.initialized) return;
2017-03-11 09:37:00 +00:00
free(state.source);
2016-11-08 06:23:13 +00:00
free(state.savePathFull);
free(state.savePathRelative);
2018-07-07 04:21:07 +00:00
for (int i = 0; i < 2; i++) {
free(state.requirePath[i]);
vec_deinit(&state.requirePattern[i]);
}
2016-11-01 00:14:31 +00:00
PHYSFS_deinit();
2019-05-20 10:51:22 +00:00
memset(&state, 0, sizeof(state));
2016-11-01 00:14:31 +00:00
}
2019-05-20 10:51:22 +00:00
bool lovrFilesystemCreateDirectory(const char* path) {
return PHYSFS_mkdir(path);
2017-03-11 09:37:00 +00:00
}
2016-11-07 22:31:11 +00:00
2019-05-20 10:51:22 +00:00
bool lovrFilesystemGetAppdataDirectory(char* dest, unsigned int size) {
2017-03-11 09:37:00 +00:00
#ifdef __APPLE__
const char* home;
if ((home = getenv("HOME")) == NULL) {
home = getpwuid(getuid())->pw_dir;
2016-11-07 22:31:11 +00:00
}
snprintf(dest, size, "%s/Library/Application Support", home);
2019-05-20 10:51:22 +00:00
return true;
2017-03-11 09:37:00 +00:00
#elif _WIN32
PWSTR appData = NULL;
SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, NULL, &appData);
PHYSFS_utf8FromUtf16(appData, dest, size);
2017-03-11 09:37:00 +00:00
CoTaskMemFree(appData);
2019-05-20 10:51:22 +00:00
return true;
#elif EMSCRIPTEN
2017-04-23 00:02:41 +00:00
strncpy(dest, "/home/web_user", size);
2019-05-20 10:51:22 +00:00
return true;
#elif LOVR_USE_OCULUS_MOBILE
strncpy(dest, lovrOculusMobileWritablePath, size);
2019-05-20 10:51:22 +00:00
return true;
2017-08-01 19:23:33 +00:00
#elif __linux__
const char* home;
if ((home = getenv("HOME")) == NULL) {
home = getpwuid(getuid())->pw_dir;
}
snprintf(dest, size, "%s/.config", home);
2019-05-20 10:51:22 +00:00
return true;
2017-03-11 09:37:00 +00:00
#else
#error "This platform is missing an implementation for lovrFilesystemGetAppdataDirectory"
#endif
2019-05-20 10:51:22 +00:00
return false;
2016-11-07 22:31:11 +00:00
}
2017-03-11 09:37:00 +00:00
void lovrFilesystemGetDirectoryItems(const char* path, getDirectoryItemsCallback callback, void* userdata) {
2019-03-17 07:58:01 +00:00
PHYSFS_enumerate(path, (PHYSFS_EnumerateCallback) callback, userdata);
2016-11-01 00:14:31 +00:00
}
2018-10-29 19:01:20 +00:00
int lovrFilesystemGetExecutablePath(char* path, uint32_t size) {
2019-05-20 11:02:25 +00:00
return lovrPlatformGetExecutablePath(path, size);
2016-11-01 00:14:31 +00:00
}
2016-11-07 22:30:32 +00:00
const char* lovrFilesystemGetIdentity() {
return state.identity;
}
2017-03-11 09:37:00 +00:00
long lovrFilesystemGetLastModified(const char* path) {
PHYSFS_Stat stat;
return PHYSFS_stat(path, &stat) ? stat.modtime : -1;
2017-03-11 09:37:00 +00:00
}
2016-11-07 22:30:32 +00:00
const char* lovrFilesystemGetRealDirectory(const char* path) {
2017-03-11 09:37:00 +00:00
return PHYSFS_getRealDir(path);
}
vec_str_t* lovrFilesystemGetRequirePath() {
2018-07-07 04:21:07 +00:00
return &state.requirePattern[0];
}
vec_str_t* lovrFilesystemGetCRequirePath() {
return &state.requirePattern[1];
}
2017-03-11 09:37:00 +00:00
const char* lovrFilesystemGetSaveDirectory() {
return state.savePathFull;
}
2017-10-22 00:35:50 +00:00
size_t lovrFilesystemGetSize(const char* path) {
PHYSFS_Stat stat;
return PHYSFS_stat(path, &stat) ? stat.filesize : -1;
2016-11-07 22:30:32 +00:00
}
const char* lovrFilesystemGetSource() {
2017-03-11 09:37:00 +00:00
return state.source;
2016-11-07 22:30:32 +00:00
}
const char* lovrFilesystemGetUserDirectory() {
#if defined(__APPLE__) || defined(__linux__)
const char* home;
if ((home = getenv("HOME")) == NULL) {
home = getpwuid(getuid())->pw_dir;
}
return home;
#elif _WIN32
return getenv("USERPROFILE");
#elif EMSCRIPTEN
return "/home/web_user";
#else
#error "This platform is missing an implementation for lovrFilesystemGetUserDirectory"
#endif
2016-11-01 00:14:31 +00:00
}
2019-05-20 10:51:22 +00:00
bool lovrFilesystemGetWorkingDirectory(char* dest, unsigned int size) {
#ifdef _WIN32
WCHAR w_cwd[LOVR_PATH_MAX];
_wgetcwd(w_cwd, LOVR_PATH_MAX);
PHYSFS_utf8FromUtf16(w_cwd, dest, size);
2019-05-20 10:51:22 +00:00
return true;
#else
if (getcwd(dest, size)) {
2019-05-20 10:51:22 +00:00
return true;
}
#endif
2019-05-20 10:51:22 +00:00
return false;
}
2017-10-31 08:14:09 +00:00
bool lovrFilesystemIsDirectory(const char* path) {
PHYSFS_Stat stat;
2017-10-31 08:14:09 +00:00
return PHYSFS_stat(path, &stat) ? stat.filetype == PHYSFS_FILETYPE_DIRECTORY : false;
2016-11-01 01:35:00 +00:00
}
2017-10-31 08:14:09 +00:00
bool lovrFilesystemIsFile(const char* path) {
PHYSFS_Stat stat;
2017-10-31 08:14:09 +00:00
return PHYSFS_stat(path, &stat) ? stat.filetype == PHYSFS_FILETYPE_REGULAR : false;
2016-11-01 01:35:00 +00:00
}
2017-10-31 08:14:09 +00:00
bool lovrFilesystemIsFused() {
2017-03-11 09:37:00 +00:00
return state.isFused;
}
// Returns zero on success, nonzero on failure
2019-05-20 10:51:22 +00:00
bool lovrFilesystemMount(const char* path, const char* mountpoint, bool append, const char* root) {
bool success = PHYSFS_mount(path, mountpoint, append);
if (success && root) {
success = PHYSFS_setRoot(path, root);
}
2019-05-20 10:51:22 +00:00
return success;
2017-03-11 09:37:00 +00:00
}
void* lovrFilesystemRead(const char* path, size_t bytes, size_t* bytesRead) {
File file;
lovrFileInit(memset(&file, 0, sizeof(File)), path);
2016-11-02 03:27:15 +00:00
2019-05-20 10:51:22 +00:00
if (!lovrFileOpen(&file, OPEN_READ)) {
2017-10-22 03:23:29 +00:00
return NULL;
}
2017-10-22 00:35:50 +00:00
// Get file size if no size was specified
if (bytes == (size_t) -1) {
bytes = lovrFileGetSize(&file);
if (bytes == (size_t) -1) {
lovrFileDestroy(&file);
return NULL;
}
2016-11-05 22:55:01 +00:00
}
// Allocate buffer
void* data = malloc(bytes);
2016-11-05 22:55:01 +00:00
if (!data) {
lovrFileDestroy(&file);
2016-11-05 22:55:01 +00:00
return NULL;
}
// Perform read
*bytesRead = lovrFileRead(&file, data, bytes);
lovrFileDestroy(&file);
2016-11-02 03:27:15 +00:00
return data;
}
2019-05-20 10:51:22 +00:00
bool lovrFilesystemRemove(const char* path) {
return PHYSFS_delete(path);
2017-03-11 09:37:00 +00:00
}
2019-05-20 10:51:22 +00:00
bool lovrFilesystemSetIdentity(const char* identity) {
2016-11-07 22:30:32 +00:00
state.identity = identity;
// Unmount old write directory
if (state.savePathFull && state.savePathRelative) {
PHYSFS_unmount(state.savePathRelative);
2016-11-07 22:30:32 +00:00
} else {
state.savePathRelative = malloc(LOVR_PATH_MAX);
state.savePathFull = malloc(LOVR_PATH_MAX);
lovrAssert(state.savePathRelative && state.savePathFull, "Out of memory");
2017-03-11 09:37:00 +00:00
if (!state.savePathRelative || !state.savePathFull) {
2019-05-20 10:51:22 +00:00
return false;
2017-03-11 09:37:00 +00:00
}
2016-11-07 22:30:32 +00:00
}
2017-03-11 09:37:00 +00:00
lovrFilesystemGetAppdataDirectory(state.savePathFull, LOVR_PATH_MAX);
if (!PHYSFS_setWriteDir(state.savePathFull)) {
const char* error = PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode());
lovrThrow("Could not set write directory: %s (%s)", error, state.savePathFull);
}
snprintf(state.savePathRelative, LOVR_PATH_MAX, "LOVR/%s", identity ? identity : "default");
2017-08-01 19:23:33 +00:00
char fullPathBuffer[LOVR_PATH_MAX];
snprintf(fullPathBuffer, LOVR_PATH_MAX, "%s/%s", state.savePathFull, state.savePathRelative);
2017-08-01 19:23:33 +00:00
strncpy(state.savePathFull, fullPathBuffer, LOVR_PATH_MAX);
2017-03-11 09:37:00 +00:00
PHYSFS_mkdir(state.savePathRelative);
if (!PHYSFS_setWriteDir(state.savePathFull)) {
const char* error = PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode());
lovrThrow("Could not set write directory: %s (%s)", error, state.savePathRelative);
}
2019-05-20 10:51:22 +00:00
return PHYSFS_mount(state.savePathFull, NULL, 0);
2016-11-07 22:30:32 +00:00
}
2018-07-07 04:21:07 +00:00
static void setRequirePath(int i, const char* requirePath) {
if (state.requirePath[i]) {
free(state.requirePath[i]);
vec_clear(&state.requirePattern[i]);
2018-03-22 17:39:31 +00:00
}
2018-07-07 04:21:07 +00:00
char* p = state.requirePath[i] = strdup(requirePath);
2018-03-22 17:39:31 +00:00
while (1) {
2018-07-07 04:21:07 +00:00
vec_push(&state.requirePattern[i], p);
2018-03-22 17:39:31 +00:00
if ((p = strchr(p, ';')) != NULL) {
*p++ = '\0';
} else {
break;
}
}
}
2018-07-07 04:21:07 +00:00
void lovrFilesystemSetRequirePath(const char* requirePath) {
setRequirePath(0, requirePath);
}
void lovrFilesystemSetCRequirePath(const char* requirePath) {
setRequirePath(1, requirePath);
}
2019-05-20 10:51:22 +00:00
bool lovrFilesystemUnmount(const char* path) {
return PHYSFS_unmount(path);
2016-11-01 00:14:31 +00:00
}
2016-11-07 22:31:02 +00:00
2017-10-31 08:14:09 +00:00
size_t lovrFilesystemWrite(const char* path, const char* content, size_t size, bool append) {
File file;
lovrFileInit(memset(&file, 0, sizeof(File)), path);
2016-11-07 22:31:02 +00:00
2019-05-20 10:51:22 +00:00
if (!lovrFileOpen(&file, append ? OPEN_APPEND : OPEN_WRITE)) {
return 0;
}
size_t bytesWritten = lovrFileWrite(&file, (void*) content, size);
return lovrFileDestroy(&file), bytesWritten;
2016-11-07 22:31:02 +00:00
}