mirror of https://github.com/bjornbytes/lovr.git
Allow the mountpoint to be set "inside" a zip file, using an --inside parameter.
This is used in the oculus mobile driver, allowing replacement of the (dubious) recursive-copy-from-zip code. In order for this to work, the argument parsing must be beefed up a bit and also PhysFS must be updated to the newest master in order to get a new PHYSFS_setRoot. The github submodule source has been changed to one which updates more frequently to get this.
This commit is contained in:
parent
26af794e3c
commit
2213b3bcfd
|
@ -24,7 +24,7 @@
|
|||
url = https://github.com/ValveSoftware/openvr
|
||||
[submodule "deps/physfs"]
|
||||
path = deps/physfs
|
||||
url = https://github.com/criptych/physfs
|
||||
url = https://github.com/Didstopia/physfs.git
|
||||
[submodule "deps/assimp"]
|
||||
path = deps/assimp
|
||||
url = https://github.com/assimp/assimp
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 68beaa110991cb5002c8dc3a3d61718845046e79
|
||||
Subproject commit 3ae84ee5d0a0af72a6a808a32b63e1ea0076f2be
|
|
@ -263,7 +263,8 @@ 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);
|
||||
lua_pushboolean(L, !lovrFilesystemMount(path, mountpoint, append));
|
||||
const char* mountpointInner = luaL_optstring(L, 4, NULL);
|
||||
lua_pushboolean(L, !lovrFilesystemMount(path, mountpoint, append, mountpointInner));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -362,14 +363,16 @@ int luaopen_lovr_filesystem(lua_State* L) {
|
|||
lua_getglobal(L, "arg");
|
||||
if (lua_istable(L, -1)) {
|
||||
lua_rawgeti(L, -1, -1);
|
||||
const char* argExe = lua_tostring(L, -1);
|
||||
lua_rawgeti(L, -2, 0);
|
||||
const char* argExe = lua_tostring(L, -2);
|
||||
const char* argGame = lua_tostring(L, -1);
|
||||
lovrFilesystemInit(argExe, argGame);
|
||||
lua_pop(L, 3);
|
||||
lua_getfield(L, -3, "inside");
|
||||
const char* argGameInner = luaL_optstring(L, -1, NULL);
|
||||
lovrFilesystemInit(argExe, argGame, argGameInner);
|
||||
lua_pop(L, 4);
|
||||
} else {
|
||||
lua_pop(L, 1);
|
||||
lovrFilesystemInit(NULL, NULL);
|
||||
lovrFilesystemInit(NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
luax_registerloader(L, moduleLoader, 2);
|
||||
|
|
|
@ -31,7 +31,7 @@ const char lovrDirSep = '/';
|
|||
|
||||
static FilesystemState state;
|
||||
|
||||
void lovrFilesystemInit(const char* argExe, const char* argGame) {
|
||||
void lovrFilesystemInit(const char* argExe, const char* argGame, const char* argGameInner) {
|
||||
if (state.initialized) return;
|
||||
state.initialized = true;
|
||||
|
||||
|
@ -49,17 +49,17 @@ void lovrFilesystemInit(const char* argExe, const char* argGame) {
|
|||
|
||||
// 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)) {
|
||||
if (lovrFilesystemMount(state.source, NULL, 1, argGameInner)) { // Attempt to load fused. If that fails...
|
||||
state.isFused = false;
|
||||
|
||||
if (argGame) {
|
||||
strncpy(state.source, argGame, LOVR_PATH_MAX);
|
||||
if (!lovrFilesystemMount(state.source, NULL, 1)) {
|
||||
if (!lovrFilesystemMount(state.source, NULL, 1, argGameInner)) { // Attempt to load from arg. If success, init is done
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
free(state.source);
|
||||
free(state.source); // Couldn't load from argProject, so apparently it isn't the source
|
||||
state.source = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -201,8 +201,13 @@ bool lovrFilesystemIsFused() {
|
|||
return state.isFused;
|
||||
}
|
||||
|
||||
int lovrFilesystemMount(const char* path, const char* mountpoint, bool append) {
|
||||
return !PHYSFS_mount(path, mountpoint, append);
|
||||
// Returns zero on success, nonzero on failure
|
||||
int lovrFilesystemMount(const char* path, const char* mountpoint, bool append, const char* mountpointInner) {
|
||||
bool success = PHYSFS_mount(path, mountpoint, append);
|
||||
if (success && mountpointInner) {
|
||||
success = PHYSFS_setRoot(path, mountpointInner);
|
||||
}
|
||||
return !success;
|
||||
}
|
||||
|
||||
void* lovrFilesystemRead(const char* path, size_t* bytesRead) {
|
||||
|
|
|
@ -22,7 +22,7 @@ typedef struct {
|
|||
vec_str_t requirePattern[2];
|
||||
} FilesystemState;
|
||||
|
||||
void lovrFilesystemInit(const char* argExe, const char* argGame);
|
||||
void lovrFilesystemInit(const char* argExe, const char* argGame, const char* argGameInner);
|
||||
void lovrFilesystemDestroy();
|
||||
int lovrFilesystemCreateDirectory(const char* path);
|
||||
int lovrFilesystemGetAppdataDirectory(char* dest, unsigned int size);
|
||||
|
@ -41,7 +41,7 @@ int lovrFilesystemGetWorkingDirectory(char* dest, unsigned int size);
|
|||
bool lovrFilesystemIsDirectory(const char* path);
|
||||
bool lovrFilesystemIsFile(const char* path);
|
||||
bool lovrFilesystemIsFused();
|
||||
int lovrFilesystemMount(const char* path, const char* mountpoint, bool append);
|
||||
int lovrFilesystemMount(const char* path, const char* mountpoint, bool append, const char *mountpointInner);
|
||||
void* lovrFilesystemRead(const char* path, size_t* bytesRead);
|
||||
int lovrFilesystemRemove(const char* path);
|
||||
int lovrFilesystemSetIdentity(const char* identity);
|
||||
|
|
|
@ -330,63 +330,6 @@ int lovr_luaB_print_override (lua_State *L);
|
|||
|
||||
#define SDS(...) sdscatfmt(sdsempty(), __VA_ARGS__)
|
||||
|
||||
// Recursively copy a subdirectory out of PhysFS onto disk
|
||||
static void physCopyFiles(sds toDir, sds fromDir) {
|
||||
char **filesOrig = PHYSFS_enumerateFiles(fromDir);
|
||||
char **files = filesOrig;
|
||||
|
||||
if (!files) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "LOVR", "COULD NOT READ DIRECTORY SOMEHOW: [%s]", fromDir);
|
||||
return;
|
||||
}
|
||||
|
||||
mkdir(toDir, 0777);
|
||||
|
||||
while (*files) {
|
||||
sds fromPath = SDS("%S/%s", fromDir, *files);
|
||||
sds toPath = SDS("%S/%s", toDir, *files);
|
||||
PHYSFS_Stat stat;
|
||||
PHYSFS_stat(fromPath, &stat);
|
||||
|
||||
if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY) {
|
||||
__android_log_print(ANDROID_LOG_DEBUG, "LOVR", "DIR: [%s] INTO: [%s]", fromPath, toPath);
|
||||
physCopyFiles(toPath, fromPath);
|
||||
} else {
|
||||
__android_log_print(ANDROID_LOG_DEBUG, "LOVR", "FILE: [%s] INTO: [%s]", fromPath, toPath);
|
||||
|
||||
PHYSFS_File *fromFile = PHYSFS_openRead(fromPath);
|
||||
|
||||
if (!fromFile) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "LOVR", "COULD NOT OPEN TO READ: [%s]", fromPath);
|
||||
|
||||
} else {
|
||||
FILE *toFile = fopen(toPath, "w");
|
||||
|
||||
if (!toFile) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "LOVR", "COULD NOT OPEN TO WRITE: [%s]", toPath);
|
||||
|
||||
} else {
|
||||
#define CPBUFSIZE (1024*8)
|
||||
while(1) {
|
||||
char buffer[CPBUFSIZE];
|
||||
int written = PHYSFS_readBytes(fromFile, buffer, CPBUFSIZE);
|
||||
if (written > 0)
|
||||
fwrite(buffer, 1, written, toFile);
|
||||
if (PHYSFS_eof(fromFile))
|
||||
break;
|
||||
}
|
||||
fclose(toFile);
|
||||
}
|
||||
PHYSFS_close(fromFile);
|
||||
}
|
||||
}
|
||||
files++;
|
||||
sdsfree(fromPath);
|
||||
sdsfree(toPath);
|
||||
}
|
||||
PHYSFS_freeList(filesOrig);
|
||||
}
|
||||
|
||||
static void android_vthrow(lua_State* L, const char* format, ...) {
|
||||
#define MAX_ERROR_LENGTH 1024
|
||||
char lovrErrorMessage[MAX_ERROR_LENGTH];
|
||||
|
@ -416,64 +359,6 @@ void bridgeLovrInit(BridgeLovrInitData *initData) {
|
|||
mkdir(lovrOculusMobileWritablePath, 0777);
|
||||
}
|
||||
|
||||
// This is a bit fancy. We want to run files off disk instead of out of the zip file.
|
||||
// This is for two reasons: Because PhysFS won't let us mount "within" a zip;
|
||||
// and because if we run the files out of a temp data directory, we can overwrite them
|
||||
// with "adb push" to debug.
|
||||
// As a TODO, when PHYSFS_mountSubdir lands, this path should change to only run in debug mode.
|
||||
sds programPath = SDS("%s/program", initData->writablePath);
|
||||
{
|
||||
// We will store the last apk change time in this "lastprogram" file.
|
||||
// We will copy all the files out of the zip into the temp dir, but ONLY if they've changed.
|
||||
sds timePath = SDS("%s/lastprogram.dat", initData->writablePath);
|
||||
|
||||
// When did APK last change?
|
||||
struct stat apkstat;
|
||||
int statfail = stat(initData->apkPath, &apkstat);
|
||||
if (statfail) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "LOVR", "CAN'T FIND APK [%s]\n", initData->apkPath);
|
||||
assert(0);
|
||||
}
|
||||
|
||||
// When did we last do a file copy?
|
||||
struct timespec previoussec;
|
||||
FILE *timeFile = fopen(timePath, "r");
|
||||
bool copyFiles = !timeFile; // If no lastprogram.dat, we've never copied
|
||||
if (timeFile) {
|
||||
fread(&previoussec.tv_sec, sizeof(previoussec.tv_sec), 1, timeFile);
|
||||
fread(&previoussec.tv_nsec, sizeof(previoussec.tv_nsec), 1, timeFile);
|
||||
fclose(timeFile);
|
||||
|
||||
copyFiles = apkstat.st_mtim.tv_sec != previoussec.tv_sec || // If timestamp differs, apk changed
|
||||
apkstat.st_mtim.tv_nsec != previoussec.tv_nsec;
|
||||
}
|
||||
|
||||
if (copyFiles) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "LOVR", "APK CHANGED [%s] WILL UNPACK\n", initData->apkPath);
|
||||
|
||||
// PhysFS hasn't been inited, so we can temporarily use it as an unzip utility if we deinit afterward
|
||||
PHYSFS_init("lovr");
|
||||
int success = PHYSFS_mount(initData->apkPath, NULL, 1);
|
||||
if (!success) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "LOVR", "FAILED TO MOUNT APK [%s]\n", initData->apkPath);
|
||||
assert(0);
|
||||
} else {
|
||||
sds rootFromDir = sdsnew("/assets");
|
||||
physCopyFiles(programPath, rootFromDir);
|
||||
sdsfree(rootFromDir);
|
||||
}
|
||||
PHYSFS_deinit();
|
||||
|
||||
// Save timestamp in a new lastprogram.dat file
|
||||
timeFile = fopen(timePath, "w");
|
||||
fwrite(&apkstat.st_mtim.tv_sec, sizeof(apkstat.st_mtim.tv_sec), 1, timeFile);
|
||||
fwrite(&apkstat.st_mtim.tv_nsec, sizeof(apkstat.st_mtim.tv_nsec), 1, timeFile);
|
||||
fclose(timeFile);
|
||||
}
|
||||
|
||||
sdsfree(timePath);
|
||||
}
|
||||
|
||||
// Unpack init data
|
||||
bridgeLovrMobileData.displayDimensions = initData->suggestedEyeTexture;
|
||||
bridgeLovrMobileData.updateData.displayTime = initData->zeroDisplayTime;
|
||||
|
@ -505,13 +390,20 @@ void bridgeLovrInit(BridgeLovrInitData *initData) {
|
|||
lua_newtable(L);
|
||||
lua_pushstring(L, "lovr");
|
||||
lua_rawseti(L, -2, -1);
|
||||
lua_pushstring(L, programPath);
|
||||
lua_pushstring(L, initData->apkPath);
|
||||
lua_rawseti(L, -2, 0);
|
||||
|
||||
// Mimic the arguments "--inside /assets" as parsed by lovrInit
|
||||
lua_pushstring(L, "--inside");
|
||||
lua_rawseti(L, -2, -2);
|
||||
lua_pushstring(L, "/assets");
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -3, "inside");
|
||||
lua_rawseti(L, -2, -3);
|
||||
|
||||
lua_setglobal(L, "arg");
|
||||
}
|
||||
|
||||
sdsfree(programPath);
|
||||
|
||||
// Register loaders for internal packages (since dynamic load does not seem to work on Android)
|
||||
luax_preloadmodule(L, "lovr", luaopen_lovr);
|
||||
luax_preloadmodule(L, "lovr.audio", luaopen_lovr_audio);
|
||||
|
@ -540,7 +432,7 @@ void bridgeLovrInit(BridgeLovrInitData *initData) {
|
|||
Lcoroutine = lua_newthread(L); // Leave L clear to be used by the draw function
|
||||
coroutineRef = luaL_ref(L, LUA_REGISTRYINDEX); // Hold on to the Lua-side coroutine object so it isn't GC'd
|
||||
|
||||
__android_log_print(ANDROID_LOG_DEBUG, "LOVR", "\n BRIDGE INIT COMPLETE top %d\n", (int)lua_gettop(L));
|
||||
__android_log_print(ANDROID_LOG_DEBUG, "LOVR", "\n BRIDGE INIT COMPLETE\n");
|
||||
}
|
||||
|
||||
void bridgeLovrUpdate(BridgeLovrUpdateData *updateData) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
typedef void GLFWwindow;
|
||||
#define GLFWAPI
|
||||
#define GLFWAPI_IS_FAKE
|
||||
GLFWAPI int glfwInit();
|
||||
GLFWAPI void glfwPollEvents();
|
||||
GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos);
|
||||
GLFWAPI GLFWwindow* glfwGetCurrentContext(void);
|
||||
|
|
31
src/main.c
31
src/main.c
|
@ -101,11 +101,17 @@ static void onGlfwError(int code, const char* description) {
|
|||
lovrThrow(description);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
typedef enum { // What is the argument parser doing?
|
||||
argparse_exe, // Interpreter
|
||||
argparse_flags,
|
||||
argparse_game // Game path and arguments
|
||||
} ArgParseState;
|
||||
|
||||
typedef enum { // In state argparse_flag, what flag is being searched for?
|
||||
argflag_none, // Not processing a flag
|
||||
argflag_inside
|
||||
} ArgFlag;
|
||||
|
||||
lua_State* lovrInit(lua_State* L, int argc, char** argv) {
|
||||
glfwSetErrorCallback(onGlfwError);
|
||||
lovrAssert(glfwInit(), "Error initializing GLFW");
|
||||
|
@ -117,24 +123,45 @@ lua_State* lovrInit(lua_State* L, int argc, char** argv) {
|
|||
// The interpreter will always be at arg[-1] and always non-nil.
|
||||
// * The "script" (in the case of Lovr, the "game") is at arg[0]. This may be nil (for a fused exe)
|
||||
// * Arguments intended for the script (the "game") are at positive indexes starting with 1.
|
||||
// Named arguments recognized by lovr will also be stored in the table at their names. This is not standard.
|
||||
lua_newtable(L);
|
||||
// push dummy "lovr" in case argv is empty
|
||||
lua_pushstring(L, "lovr");
|
||||
lua_rawseti(L, -2, -1);
|
||||
|
||||
ArgParseState parseState = argparse_exe;
|
||||
ArgFlag currentFlag = argflag_none;
|
||||
int lovrArgs = 0, userArgs = 0;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
// Handle flags first, so we can immediately reinterpret this as the game path if it isn't a flag
|
||||
if (parseState == argparse_flags) {
|
||||
// This argument is an argument to a -- flag
|
||||
if (currentFlag == argflag_inside) {
|
||||
lua_pushstring(L, argv[i]);
|
||||
lua_setfield(L, -2, "inside");
|
||||
currentFlag = argflag_none;
|
||||
|
||||
// This argument is a -- flag
|
||||
} else if (!strcmp(argv[1], "--inside") || !strcmp(argv[1], "-i")) {
|
||||
currentFlag = argflag_inside;
|
||||
|
||||
// No flags, this is the game path
|
||||
} else {
|
||||
parseState = argparse_game;
|
||||
}
|
||||
}
|
||||
|
||||
lua_pushstring(L, argv[i]);
|
||||
lua_rawseti(L, -2, parseState == argparse_game ? (0 + userArgs) : (-1 - lovrArgs));
|
||||
|
||||
// Count flags
|
||||
if (parseState == argparse_game) {
|
||||
userArgs++;
|
||||
} else {
|
||||
lovrArgs++;
|
||||
|
||||
if (parseState == argparse_exe) {
|
||||
parseState = argparse_game;
|
||||
parseState = argparse_flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue