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:
mcc 2018-11-13 15:20:04 -05:00
parent 26af794e3c
commit 2213b3bcfd
8 changed files with 64 additions and 136 deletions

2
.gitmodules vendored
View File

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

2
deps/physfs vendored

@ -1 +1 @@
Subproject commit 68beaa110991cb5002c8dc3a3d61718845046e79
Subproject commit 3ae84ee5d0a0af72a6a808a32b63e1ea0076f2be

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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