Browse Source

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
Bart van Strien 9 years ago
parent
commit
33341de7bc

+ 1 - 0
src/modules/filesystem/Filesystem.h

@@ -261,6 +261,7 @@ public:
 	// Require path accessors
 	// Not const because it's R/W
 	virtual std::vector<std::string> &getRequirePath() = 0;
+	virtual std::vector<std::string> &getCRequirePath() = 0;
 
 	/**
 	 * Allows a full (OS-dependent) path to be used with Filesystem::mount.

+ 6 - 0
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<std::string> &Filesystem::getRequirePath()
 	return requirePath;
 }
 
+std::vector<std::string> &Filesystem::getCRequirePath()
+{
+	return cRequirePath;
+}
+
 void Filesystem::allowMountingForPath(const std::string &path)
 {
 	if (std::find(allowedMountPaths.begin(), allowedMountPaths.end(), path) == allowedMountPaths.end())

+ 2 - 0
src/modules/filesystem/physfs/Filesystem.h

@@ -97,6 +97,7 @@ public:
 	bool areSymlinksEnabled() const;
 
 	std::vector<std::string> &getRequirePath();
+	std::vector<std::string> &getCRequirePath();
 
 	void allowMountingForPath(const std::string &path);
 
@@ -127,6 +128,7 @@ private:
 
 	// Search path for require
 	std::vector<std::string> requirePath;
+	std::vector<std::string> cRequirePath;
 
 	std::vector<std::string> allowedMountPaths;
 

+ 59 - 22
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 }
 };