mirror of https://github.com/bjornbytes/lovr.git
Merge pull request #71 from mcclure/args-and-physfs
"Fix" arg standard and allow mounting inside a zip file
This commit is contained in:
commit
5ec3f7ccc0
|
@ -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
|
||||
|
|
|
@ -574,7 +574,7 @@ elseif(APPLE)
|
|||
elseif(EMSCRIPTEN)
|
||||
target_sources(lovr PRIVATE src/platform/web.c)
|
||||
elseif(ANDROID)
|
||||
target_link_libraries(lovr log EGL)
|
||||
target_link_libraries(lovr log EGL GLESv3)
|
||||
target_sourceS(lovr PRIVATE src/platform/linux.c src/platform/android.c src/platform/print_override.c)
|
||||
elseif(UNIX)
|
||||
target_sourceS(lovr PRIVATE src/platform/linux.c)
|
||||
|
|
|
@ -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* root = luaL_optstring(L, 4, NULL);
|
||||
lua_pushboolean(L, !lovrFilesystemMount(path, mountpoint, append, root));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -361,15 +362,17 @@ int luaopen_lovr_filesystem(lua_State* L) {
|
|||
|
||||
lua_getglobal(L, "arg");
|
||||
if (lua_istable(L, -1)) {
|
||||
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);
|
||||
lua_getfield(L, -1, "exe");
|
||||
const char* argExe = lua_tostring(L, -1);
|
||||
lua_rawgeti(L, -2, 0);
|
||||
const char* argGame = lua_tostring(L, -1);
|
||||
lua_getfield(L, -3, "root");
|
||||
const char* argRoot = luaL_optstring(L, -1, NULL);
|
||||
lovrFilesystemInit(argExe, argGame, argRoot);
|
||||
lua_pop(L, 4);
|
||||
} else {
|
||||
lua_pop(L, 1);
|
||||
lovrFilesystemInit(NULL, NULL);
|
||||
lovrFilesystemInit(NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
luax_registerloader(L, moduleLoader, 2);
|
||||
|
|
|
@ -31,11 +31,11 @@ const char lovrDirSep = '/';
|
|||
|
||||
static FilesystemState state;
|
||||
|
||||
void lovrFilesystemInit(const char* arg0, const char* arg1) {
|
||||
void lovrFilesystemInit(const char* argExe, const char* argGame, const char* argRoot) {
|
||||
if (state.initialized) return;
|
||||
state.initialized = true;
|
||||
|
||||
if (!PHYSFS_init(arg0)) {
|
||||
if (!PHYSFS_init(argExe)) {
|
||||
lovrThrow("Could not initialize filesystem: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
||||
}
|
||||
|
||||
|
@ -49,17 +49,17 @@ void lovrFilesystemInit(const char* arg0, const char* arg1) {
|
|||
|
||||
// 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, argRoot)) { // Attempt to load fused. If that fails...
|
||||
state.isFused = false;
|
||||
|
||||
if (arg1) {
|
||||
strncpy(state.source, arg1, LOVR_PATH_MAX);
|
||||
if (!lovrFilesystemMount(state.source, NULL, 1)) {
|
||||
if (argGame) {
|
||||
strncpy(state.source, argGame, LOVR_PATH_MAX);
|
||||
if (!lovrFilesystemMount(state.source, NULL, 1, argRoot)) { // 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* root) {
|
||||
bool success = PHYSFS_mount(path, mountpoint, append);
|
||||
if (success && root) {
|
||||
success = PHYSFS_setRoot(path, root);
|
||||
}
|
||||
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* arg0, const char* arg1);
|
||||
void lovrFilesystemInit(const char* argExe, const char* argGame, const char* argRoot);
|
||||
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 *root);
|
||||
void* lovrFilesystemRead(const char* path, size_t* bytesRead);
|
||||
int lovrFilesystemRemove(const char* path);
|
||||
int lovrFilesystemSetIdentity(const char* identity);
|
||||
|
|
|
@ -313,63 +313,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, va_list args) {
|
||||
#define MAX_ERROR_LENGTH 1024
|
||||
char lovrErrorMessage[MAX_ERROR_LENGTH];
|
||||
|
@ -407,64 +350,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;
|
||||
|
@ -493,23 +378,28 @@ void bridgeLovrInit(BridgeLovrInitData *initData) {
|
|||
|
||||
glfwSetTime(0);
|
||||
|
||||
// Set "arg" global
|
||||
// Set "arg" global (see main.c)
|
||||
{
|
||||
const char *argv[] = {"lovr", programPath};
|
||||
int argc = 2;
|
||||
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, "lovr");
|
||||
lua_pushvalue(L, -1); // Double at named key
|
||||
lua_setfield(L, -3, "exe");
|
||||
lua_rawseti(L, -2, -3);
|
||||
|
||||
// Mimic the arguments "--root /assets" as parsed by lovrInit
|
||||
lua_pushstring(L, "--root");
|
||||
lua_rawseti(L, -2, -2);
|
||||
lua_pushstring(L, "/assets");
|
||||
lua_pushvalue(L, -1); // Double at named key
|
||||
lua_setfield(L, -3, "root");
|
||||
lua_rawseti(L, -2, -1);
|
||||
for (int i = 0; i < argc; i++) {
|
||||
lua_pushstring(L, argv[i]);
|
||||
lua_rawseti(L, -2, i == 0 ? -2 : i);
|
||||
}
|
||||
|
||||
lua_pushstring(L, initData->apkPath);
|
||||
lua_rawseti(L, -2, 0);
|
||||
|
||||
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);
|
||||
|
@ -539,7 +429,7 @@ void bridgeLovrInit(BridgeLovrInitData *initData) {
|
|||
lua_atpanic(Lcoroutine, luax_custom_atpanic);
|
||||
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);
|
||||
|
|
55
src/main.c
55
src/main.c
|
@ -112,19 +112,68 @@ static void onGlfwError(int code, const char* description) {
|
|||
lovrThrow(description);
|
||||
}
|
||||
|
||||
typedef enum { // What flag is being searched for?
|
||||
ARGFLAG_NONE, // Not processing a flag
|
||||
ARGFLAG_ROOT
|
||||
} ArgFlag;
|
||||
|
||||
lua_State* lovrInit(lua_State* L, int argc, char** argv) {
|
||||
glfwSetErrorCallback(onGlfwError);
|
||||
lovrAssert(glfwInit(), "Error initializing GLFW");
|
||||
glfwSetTime(0);
|
||||
|
||||
// arg
|
||||
// arg table
|
||||
// Args follow the lua standard https://en.wikibooks.org/wiki/Lua_Programming/command_line_parameter
|
||||
// In this standard, the contents of argv are put in a global table named "arg", but with indices offset
|
||||
// such that the "script" (in lovr, the game path) is at index 0. So all arguments (if any) intended for
|
||||
// the script/game are at successive indices starting with 1, and the exe and its arguments are in normal
|
||||
// order but stored in negative indices.
|
||||
//
|
||||
// Lovr can be run in ways normal lua can't. It can be run with an empty argv, or with no script (if fused).
|
||||
// So in the case of lovr:
|
||||
// * The script path will always be at index 0, but it can be nil.
|
||||
// * For a fused executable, whatever is given in argv as script name is still at 0, but will be ignored.
|
||||
// * The exe (argv[0]) will always be the lowest-integer index. If it's present, it will always be -1 or less.
|
||||
// * Any named arguments parsed by Lovr will be stored in the arg table at their named keys.
|
||||
// * An exe name will always be stored in the arg table at string key "exe", even if none was supplied in argv.
|
||||
lua_newtable(L);
|
||||
// push dummy "lovr" in case argv is empty
|
||||
lua_pushstring(L, "lovr");
|
||||
lua_rawseti(L, -2, -1);
|
||||
lua_setfield(L, -2, "exe");
|
||||
|
||||
bool exeFound = false;
|
||||
ArgFlag currentFlag = ARGFLAG_NONE;
|
||||
int lovrArgs = 0; // Arg count up to but not including the game path
|
||||
// One pass to parse flags
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (lovrArgs > 0) {
|
||||
// This argument is an argument to a -- flag
|
||||
if (currentFlag == ARGFLAG_ROOT) {
|
||||
lua_pushstring(L, argv[i]);
|
||||
lua_setfield(L, -2, "root");
|
||||
currentFlag = ARGFLAG_NONE;
|
||||
|
||||
// This argument is a -- flag
|
||||
} else if (!strcmp(argv[1], "--root") || !strcmp(argv[1], "-r")) {
|
||||
currentFlag = ARGFLAG_ROOT;
|
||||
|
||||
// This is the game path
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else { // Found exe name
|
||||
lua_pushstring(L, argv[i]);
|
||||
lua_setfield(L, -2, "exe");
|
||||
}
|
||||
|
||||
lovrArgs++;
|
||||
}
|
||||
// Now that we know the negative offset to start at, copy all args in the table
|
||||
for (int i = 0; i < argc; i++) {
|
||||
lua_pushstring(L, argv[i]);
|
||||
lua_rawseti(L, -2, i == 0 ? -2 : i);
|
||||
lua_rawseti(L, -2, -lovrArgs + i);
|
||||
}
|
||||
|
||||
lua_setglobal(L, "arg");
|
||||
|
||||
luax_registerloader(L, loadSelf, 1);
|
||||
|
|
Loading…
Reference in New Issue