Commits

Alex Szpakowski  committed 798c38d

Added love.filesystem.getSourceBaseDirectory and changed love.filesystem.mount to allow mounting the path returned by love.filesystem.getSourceBaseDirectory in fused mode.

  • Participants
  • Parent commits bb2e19f

Comments (0)

Files changed (4)

File src/modules/filesystem/physfs/Filesystem.cpp

 	return true;
 }
 
+const char *Filesystem::getSource() const
+{
+	return game_source.c_str();
+}
+
 bool Filesystem::setupWriteDirectory()
 {
 	if (!initialized)
 
 	if (!success)
 	{
-		PHYSFS_setWriteDir(0); // Clear the write directory in case of error.
+		// Clear the write directory in case of error.
+		PHYSFS_setWriteDir(0);
 		return false;
 	}
 
 	if (!initialized || !archive)
 		return false;
 
-	// Not allowed for safety reasons.
-	if (strlen(archive) == 0 || strstr(archive, "..") || strcmp(archive, "/") == 0)
+	std::string realPath;
+	std::string sourceBase = getSourceBaseDirectory();
+
+	if (isFused() && sourceBase.compare(archive) == 0)
+	{
+		// Special case: if the game is fused and the archive is the source's
+		// base directory, mount it even though it's outside of the save dir.
+		realPath = sourceBase;
+	}
+	else
+	{
+		// Not allowed for safety reasons.
+		if (strlen(archive) == 0 || strstr(archive, "..") || strcmp(archive, "/") == 0)
+			return false;
+
+		const char *realDir = PHYSFS_getRealDir(archive);
+		if (!realDir)
+			return false;
+
+		realPath = realDir;
+
+		// Always disallow mounting of files inside the game source, since it
+		// won't work anyway if the game source is a zipped .love file.
+		if (realPath.find(game_source) == 0)
+			return false;
+
+		realPath += LOVE_PATH_SEPARATOR;
+		realPath += archive;
+	}
+
+	if (realPath.length() == 0)
 		return false;
 
-	const char *realDir = PHYSFS_getRealDir(archive);
-	if (!realDir)
-		return false;
-
-	std::string realPath(realDir);
-
-	// Always disallow mounting of files inside the game source, since it won't
-	// work anyway if the game source is a zipped .love file.
-	if (realPath.find(game_source) == 0)
-		return false;
-
-	realPath += LOVE_PATH_SEPARATOR;
-	realPath += archive;
-
 	bool append = (searchorder == SEARCH_ORDER_LAST);
 
 	return PHYSFS_mount(realPath.c_str(), mountpoint, append);
 	if (!initialized || !archive)
 		return false;
 
-	// Not allowed for safety reasons.
-	if (strlen(archive) == 0 || strstr(archive, "..") || strcmp(archive, "/") == 0)
-		return false;
+	std::string realPath;
+	std::string sourceBase = getSourceBaseDirectory();
 
-	const char *realDir = PHYSFS_getRealDir(archive);
-	if (!realDir)
-		return false;
+	if (isFused() && sourceBase.compare(archive) == 0)
+	{
+		// Special case: if the game is fused and the archive is the source's
+		// base directory, unmount it even though it's outside of the save dir.
+		realPath = sourceBase;
+	}
+	else
+	{
+		// Not allowed for safety reasons.
+		if (strlen(archive) == 0 || strstr(archive, "..") || strcmp(archive, "/") == 0)
+			return false;
 
-	std::string realPath(realDir);
-	realPath += LOVE_PATH_SEPARATOR;
-	realPath += archive;
+		const char *realDir = PHYSFS_getRealDir(archive);
+		if (!realDir)
+			return false;
+
+		realPath = realDir;
+		realPath += LOVE_PATH_SEPARATOR;
+		realPath += archive;
+	}
 
 	const char *mountPoint = PHYSFS_getMountPoint(realPath.c_str());
 	if (!mountPoint)
 	return save_path_full.c_str();
 }
 
+std::string Filesystem::getSourceBaseDirectory() const
+{
+	size_t source_len = game_source.length();
+
+	if (source_len == 0)
+		return "";
+
+	// FIXME: This doesn't take into account parent and current directory
+	// symbols (i.e. '..' and '.')
+#ifdef LOVE_WINDOWS
+	// In windows, delimiters can be either '/' or '\'.
+	size_t base_end_pos = game_source.find_last_of("/\\", source_len - 2);
+#else
+	size_t base_end_pos = game_source.find_last_of('/', source_len - 2);
+#endif
+
+	if (base_end_pos == std::string::npos)
+		return "";
+
+	// If the source is in the unix root (aka '/'), we want to keep the '/'.
+	if (base_end_pos == 0)
+		base_end_pos = 1;
+
+	return game_source.substr(0, base_end_pos);
+}
+
 bool Filesystem::exists(const char *file) const
 {
-	if (PHYSFS_exists(file))
-		return true;
-	return false;
+	return PHYSFS_exists(file);
 }
 
 bool Filesystem::isDirectory(const char *file) const
 {
-	if (PHYSFS_isDirectory(file))
-		return true;
-	return false;
+	return PHYSFS_isDirectory(file);
 }
 
 bool Filesystem::isFile(const char *file) const

File src/modules/filesystem/physfs/Filesystem.h

 	 **/
 	bool setSource(const char *source);
 
+	/**
+	 * Gets the path to the game source.
+	 * Returns a 0-length string if the source has not been set.
+	 **/
+	const char *getSource() const;
+
 	bool mount(const char *archive, const char *mountpoint, SearchOrder searchorder = SEARCH_ORDER_FIRST);
 	bool unmount(const char *archive);
 
 	const char *getSaveDirectory();
 
 	/**
+	 * Gets the full path to the directory containing the game source.
+	 * For example if the game source is C:\Games\mygame.love, this will return
+	 * C:\Games.
+	 **/
+	std::string getSourceBaseDirectory() const;
+
+	/**
 	 * Checks whether a file exists in the current search path
 	 * or not.
 	 * @param file The filename to check.

File src/modules/filesystem/physfs/wrap_Filesystem.cpp

 	return 0;
 }
 
+int w_getSource(lua_State *L)
+{
+	lua_pushstring(L, instance->getSource());
+	return 1;
+}
+
 int w_mount(lua_State *L)
 {
 	const char *archive = luaL_checkstring(L, 1);
 	return 1;
 }
 
+int w_getSourceBaseDirectory(lua_State *L)
+{
+	luax_pushstring(L, instance->getSourceBaseDirectory());
+	return 1;
+}
+
 int w_exists(lua_State *L)
 {
 	const char *arg = luaL_checkstring(L, 1);
 	{ "setIdentity",  w_setIdentity },
 	{ "getIdentity", w_getIdentity },
 	{ "setSource",  w_setSource },
+	{ "getSource", w_getSource },
 	{ "mount", w_mount },
 	{ "unmount", w_unmount },
 	{ "newFile",  w_newFile },
 	{ "getUserDirectory",  w_getUserDirectory },
 	{ "getAppdataDirectory",  w_getAppdataDirectory },
 	{ "getSaveDirectory",  w_getSaveDirectory },
+	{ "getSourceBaseDirectory", w_getSourceBaseDirectory },
 	{ "exists",  w_exists },
 	{ "isDirectory",  w_isDirectory },
 	{ "isFile",  w_isFile },

File src/modules/filesystem/physfs/wrap_Filesystem.h

 int w_setIdentity(lua_State *L);
 int w_getIdentity(lua_State *L);
 int w_setSource(lua_State *L);
+int w_getSource(lua_State *L);
 int w_mount(lua_State *L);
 int w_unmount(lua_State *L);
 int w_newFile(lua_State *L);
 int w_getUserDirectory(lua_State *L);
 int w_getAppdataDirectory(lua_State *L);
 int w_getSaveDirectory(lua_State *L);
+int w_getSourceBaseDirectory(lua_State *L);
 int w_exists(lua_State *L);
 int w_isDirectory(lua_State *L);
 int w_isFile(lua_State *L);