From 33341de7bc3b3b40a8864b501868387e6677f407 Mon Sep 17 00:00:00 2001 From: Bart van Strien Date: Fri, 26 Feb 2016 15:24:01 +0100 Subject: [PATCH] Rewrite the c module loader, adding a search path (fixes #1050) Note: also removes the restriction that only fused games can load libraries from their save folders. --HG-- branch : minor --- src/modules/filesystem/Filesystem.h | 1 + src/modules/filesystem/physfs/Filesystem.cpp | 6 ++ src/modules/filesystem/physfs/Filesystem.h | 2 + src/modules/filesystem/wrap_Filesystem.cpp | 81 ++++++++++++++------ 4 files changed, 68 insertions(+), 22 deletions(-) diff --git a/src/modules/filesystem/Filesystem.h b/src/modules/filesystem/Filesystem.h index 993f35156..dbc12b58a 100644 --- a/src/modules/filesystem/Filesystem.h +++ b/src/modules/filesystem/Filesystem.h @@ -261,6 +261,7 @@ public: // Require path accessors // Not const because it's R/W virtual std::vector &getRequirePath() = 0; + virtual std::vector &getCRequirePath() = 0; /** * Allows a full (OS-dependent) path to be used with Filesystem::mount. diff --git a/src/modules/filesystem/physfs/Filesystem.cpp b/src/modules/filesystem/physfs/Filesystem.cpp index 85dfc274d..c466a6d6b 100644 --- a/src/modules/filesystem/physfs/Filesystem.cpp +++ b/src/modules/filesystem/physfs/Filesystem.cpp @@ -107,6 +107,7 @@ Filesystem::Filesystem() , fusedSet(false) { requirePath = {"?.lua", "?/init.lua"}; + cRequirePath = {"??"}; } Filesystem::~Filesystem() @@ -771,6 +772,11 @@ std::vector &Filesystem::getRequirePath() return requirePath; } +std::vector &Filesystem::getCRequirePath() +{ + return cRequirePath; +} + void Filesystem::allowMountingForPath(const std::string &path) { if (std::find(allowedMountPaths.begin(), allowedMountPaths.end(), path) == allowedMountPaths.end()) diff --git a/src/modules/filesystem/physfs/Filesystem.h b/src/modules/filesystem/physfs/Filesystem.h index 5d279da49..dd42b1fd8 100644 --- a/src/modules/filesystem/physfs/Filesystem.h +++ b/src/modules/filesystem/physfs/Filesystem.h @@ -97,6 +97,7 @@ public: bool areSymlinksEnabled() const; std::vector &getRequirePath(); + std::vector &getCRequirePath(); void allowMountingForPath(const std::string &path); @@ -127,6 +128,7 @@ private: // Search path for require std::vector requirePath; + std::vector cRequirePath; std::vector allowedMountPaths; diff --git a/src/modules/filesystem/wrap_Filesystem.cpp b/src/modules/filesystem/wrap_Filesystem.cpp index e01b3840e..9edfa5a24 100644 --- a/src/modules/filesystem/wrap_Filesystem.cpp +++ b/src/modules/filesystem/wrap_Filesystem.cpp @@ -590,6 +590,24 @@ int w_getRequirePath(lua_State *L) return 1; } +int w_getCRequirePath(lua_State *L) +{ + std::stringstream path; + bool seperator = false; + for (auto &element : instance()->getCRequirePath()) + { + if (seperator) + path << ";"; + else + seperator = true; + + path << element; + } + + luax_pushstring(L, path.str()); + return 1; +} + int w_setRequirePath(lua_State *L) { std::string element = luax_checkstring(L, 1); @@ -605,6 +623,21 @@ int w_setRequirePath(lua_State *L) return 0; } +int w_setCRequirePath(lua_State *L) +{ + std::string element = luax_checkstring(L, 1); + auto &requirePath = instance()->getCRequirePath(); + + requirePath.clear(); + std::stringstream path; + path << element; + + while(std::getline(path, element, ';')) + requirePath.push_back(element); + + return 0; +} + int loader(lua_State *L) { std::string modulename = luax_tostring(L, 1); @@ -651,6 +684,9 @@ int extloader(lua_State *L) std::string tokenized_name(filename); std::string tokenized_function(filename); + // We need both the tokenized filename (dots replaced with slashes) + // and the tokenized function name (dots replaced with underscores) + // NOTE: Lua's loader queries more names than this one. for (unsigned int i = 0; i < tokenized_name.size(); i++) { if (tokenized_name[i] == '.') @@ -660,32 +696,29 @@ int extloader(lua_State *L) } } - tokenized_name += library_extension(); - void *handle = nullptr; - - // If the game is fused, try looking for the DLL in the game's read paths. - if (instance()->isFused()) + auto *inst = instance(); + for (std::string element : inst->getCRequirePath()) { - try - { - std::string dir = instance()->getRealDirectory(tokenized_name.c_str()); + // Replace ?? with the filename and extension + size_t pos = element.find("??"); + if (pos != std::string::npos) + element.replace(pos, 2, tokenized_name + library_extension()); + // Or ? with just the filename + pos = element.find('?'); + if (pos != std::string::npos) + element.replace(pos, 1, tokenized_name); - // We don't want to look in the game's source, because it can be a - // zip sometimes and a folder other times. - if (dir.find(instance()->getSource()) == std::string::npos) - handle = SDL_LoadObject((dir + LOVE_PATH_SEPARATOR + tokenized_name).c_str()); - } - catch (love::Exception &) - { - // Nothing... - } - } + if (!inst->isFile(element.c_str())) + continue; - if (!handle) - { - std::string path = std::string(instance()->getAppdataDirectory()) + LOVE_PATH_SEPARATOR LOVE_APPDATA_FOLDER LOVE_PATH_SEPARATOR + tokenized_name; - handle = SDL_LoadObject(path.c_str()); + // Now resolve the full path, as we're bypassing physfs for the next part. + element = inst->getRealDirectory(element.c_str()) + LOVE_PATH_SEPARATOR + element; + + handle = SDL_LoadObject(element.c_str()); + // Can fail, for instance if it turned out the source was a zip + if (handle) + break; } if (!handle) @@ -694,6 +727,8 @@ int extloader(lua_State *L) return 1; } + // We look for both loveopen_ and luaopen_, so libraries with specific love support + // can tell when they've been loaded by love. void *func = SDL_LoadFunction(handle, ("loveopen_" + tokenized_function).c_str()); if (!func) func = SDL_LoadFunction(handle, ("luaopen_" + tokenized_function).c_str()); @@ -749,6 +784,8 @@ static const luaL_Reg functions[] = { "newFileData", w_newFileData }, { "getRequirePath", w_getRequirePath }, { "setRequirePath", w_setRequirePath }, + { "getCRequirePath", w_getCRequirePath }, + { "setCRequirePath", w_setCRequirePath }, { 0, 0 } };