Browse Source

Add love.filesystem.mountFullPath.

Alex Szpakowski 4 years ago
parent
commit
0706b6243c

+ 8 - 0
platform/xcode/liblove.xcodeproj/project.pbxproj

@@ -960,6 +960,8 @@
 		FAAA3FDA1F64B3AD00F89E99 /* lstrlib.h in Headers */ = {isa = PBXBuildFile; fileRef = FAAA3FD51F64B3AD00F89E99 /* lstrlib.h */; };
 		FAAA3FDB1F64B3AD00F89E99 /* lutf8lib.c in Sources */ = {isa = PBXBuildFile; fileRef = FAAA3FD61F64B3AD00F89E99 /* lutf8lib.c */; };
 		FAAA3FDC1F64B3AD00F89E99 /* lutf8lib.h in Headers */ = {isa = PBXBuildFile; fileRef = FAAA3FD71F64B3AD00F89E99 /* lutf8lib.h */; };
+		FAAC2F79251A9D2200BCB81B /* apple.mm in Sources */ = {isa = PBXBuildFile; fileRef = FAAC2F78251A9D2200BCB81B /* apple.mm */; };
+		FAAC2F7A251A9D2200BCB81B /* apple.mm in Sources */ = {isa = PBXBuildFile; fileRef = FAAC2F78251A9D2200BCB81B /* apple.mm */; };
 		FAAFF04416CB11C700CCDE45 /* OpenAL-Soft.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAAFF04316CB11C700CCDE45 /* OpenAL-Soft.framework */; };
 		FAB17BE61ABFAA9000F9BA27 /* lz4.c in Sources */ = {isa = PBXBuildFile; fileRef = FAB17BE41ABFAA9000F9BA27 /* lz4.c */; };
 		FAB17BE71ABFAA9000F9BA27 /* lz4.c in Sources */ = {isa = PBXBuildFile; fileRef = FAB17BE41ABFAA9000F9BA27 /* lz4.c */; };
@@ -1916,6 +1918,8 @@
 		FAAA3FD51F64B3AD00F89E99 /* lstrlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lstrlib.h; sourceTree = "<group>"; };
 		FAAA3FD61F64B3AD00F89E99 /* lutf8lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lutf8lib.c; sourceTree = "<group>"; };
 		FAAA3FD71F64B3AD00F89E99 /* lutf8lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lutf8lib.h; sourceTree = "<group>"; };
+		FAAC2F78251A9D2200BCB81B /* apple.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = apple.mm; sourceTree = "<group>"; };
+		FAAC2F7F251A9D3E00BCB81B /* apple.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = apple.h; sourceTree = "<group>"; };
 		FAAFF04316CB11C700CCDE45 /* OpenAL-Soft.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "OpenAL-Soft.framework"; path = "/Library/Frameworks/OpenAL-Soft.framework"; sourceTree = "<absolute>"; };
 		FAB17BE41ABFAA9000F9BA27 /* lz4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lz4.c; sourceTree = "<group>"; };
 		FAB17BE51ABFAA9000F9BA27 /* lz4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lz4.h; sourceTree = "<group>"; };
@@ -2189,6 +2193,8 @@
 			children = (
 				FAA3A9AC1B7D465A00CED060 /* android.cpp */,
 				FAA3A9AD1B7D465A00CED060 /* android.h */,
+				FAAC2F7F251A9D3E00BCB81B /* apple.h */,
+				FAAC2F78251A9D2200BCB81B /* apple.mm */,
 				FA0B78F71A958E3B000E1D17 /* b64.cpp */,
 				FA0B78F81A958E3B000E1D17 /* b64.h */,
 				FA6BDE5B1F31725300786805 /* Color.h */,
@@ -4649,6 +4655,7 @@
 				FA0B7E1F1A95902C000E1D17 /* Physics.cpp in Sources */,
 				FA6A2B7B1F60B8250074C308 /* wrap_ByteData.cpp in Sources */,
 				FA0B7E821A95902C000E1D17 /* Shape.cpp in Sources */,
+				FAAC2F7A251A9D2200BCB81B /* apple.mm in Sources */,
 				FA0B7ACE1A958EA3000E1D17 /* packet.c in Sources */,
 				FAF140891E20934C00F898D2 /* PoolAlloc.cpp in Sources */,
 				FA27B3B41B498151008A9DCE /* wrap_Video.cpp in Sources */,
@@ -5045,6 +5052,7 @@
 				FAF140881E20934C00F898D2 /* PoolAlloc.cpp in Sources */,
 				FA0B7AAD1A958EA3000E1D17 /* b2WheelJoint.cpp in Sources */,
 				FA0B7DEE1A95902C000E1D17 /* Mouse.cpp in Sources */,
+				FAAC2F79251A9D2200BCB81B /* apple.mm in Sources */,
 				FAA54ACC1F91660400A8FA7B /* TheoraVideoStream.cpp in Sources */,
 				FA1E887E1DF363CD00E808AA /* Filter.cpp in Sources */,
 				FA0B7D281A95902C000E1D17 /* wrap_GlyphData.cpp in Sources */,

+ 15 - 0
src/common/StringMap.h

@@ -197,6 +197,21 @@ bool getConstant(const char *in, type &out) { return name##s.find(in, out); } \
 bool getConstant(type in, const char *&out) { return name##s.find(in, out); } \
 std::vector<std::string> getConstants(type) { return name##s.getNames(); }
 
+#define STRINGMAP_CLASS_DECLARE(type) \
+static bool getConstant(const char *in, type &out); \
+static bool getConstant(type in, const char *&out); \
+static std::vector<std::string> getConstants(type); \
+
+#define STRINGMAP_CLASS_BEGIN(classname, type, count, name) \
+static StringMap<type, count>::Entry name##Entries[] =
+
+#define STRINGMAP_CLASS_END(classname, type, count, name) \
+; \
+static StringMap<type, count> name##s(name##Entries, sizeof(name##Entries)); \
+bool classname::getConstant(const char *in, type &out) { return name##s.find(in, out); } \
+bool classname::getConstant(type in, const char *&out) { return name##s.find(in, out); } \
+std::vector<std::string> classname::getConstants(type) { return name##s.getNames(); }
+
 } // love
 
 #endif // LOVE_STRING_MAP_H

+ 50 - 0
src/common/apple.h

@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2006-2020 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#pragma once
+
+#include "config.h"
+
+#if defined(LOVE_IOS) || defined(LOVE_MACOS)
+
+#include <string>
+
+namespace love
+{
+namespace apple
+{
+
+enum UserDirectory
+{
+	USER_DIRECTORY_HOME,
+	USER_DIRECTORY_APPSUPPORT,
+	USER_DIRECTORY_DOCUMENTS,
+	USER_DIRECTORY_DESKTOP,
+	USER_DIRECTORY_CACHES,
+};
+
+std::string getUserDirectory(UserDirectory dir);
+
+std::string getExecutablePath();
+
+} // apple
+} // love
+
+#endif // defined(LOVE_IOS) || defined(LOVE_MACOS)

+ 76 - 0
src/common/apple.mm

@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2006-2020 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#include "apple.h"
+
+#if defined(LOVE_IOS) || defined(LOVE_MACOS)
+
+#import <Foundation/Foundation.h>
+
+namespace love
+{
+namespace apple
+{
+
+std::string getUserDirectory(UserDirectory dir)
+{
+	std::string path;
+	NSSearchPathDirectory nsdir = NSTrashDirectory;
+
+	@autoreleasepool
+	{
+		switch (dir)
+		{
+		case USER_DIRECTORY_HOME:
+			return NSHomeDirectory().UTF8String;
+		case USER_DIRECTORY_APPSUPPORT:
+			nsdir = NSApplicationSupportDirectory;
+			break;
+		case USER_DIRECTORY_DOCUMENTS:
+			nsdir = NSDocumentDirectory;
+			break;
+		case USER_DIRECTORY_DESKTOP:
+			nsdir = NSDesktopDirectory;
+			break;
+		case USER_DIRECTORY_CACHES:
+			nsdir = NSCachesDirectory;
+			break;
+		}
+
+		NSArray<NSURL *> *dirs = [[NSFileManager defaultManager] URLsForDirectory:nsdir inDomains:NSUserDomainMask];
+		if (dirs.count > 0)
+			path = [dirs[0].path UTF8String];
+	}
+
+	return path;
+}
+
+std::string getExecutablePath()
+{
+	@autoreleasepool
+	{
+		return std::string([NSBundle mainBundle].executablePath.UTF8String);
+	}
+}
+
+} // apple
+} // love
+
+#endif // defined(LOVE_IOS) || defined(LOVE_MACOS)

+ 0 - 15
src/common/ios.h

@@ -42,27 +42,12 @@ namespace ios
  **/
 std::string getLoveInResources(bool &fused);
 
-/**
- * Gets the directory path where files should be stored.
- **/
-std::string getAppdataDirectory();
-
-/**
- * Get the home directory (on iOS, this really means the app's sandbox dir.)
- **/
-std::string getHomeDirectory();
-
 /**
  * Opens the specified URL with the default program associated with the URL's
  * scheme.
  **/
 bool openURL(const std::string &url);
 
-/**
- * Returns the full path to the executable.
- **/
-std::string getExecutablePath();
-
 /**
  * Causes devices with vibration support to vibrate for about 0.5 seconds.
  **/

+ 1 - 46
src/common/ios.mm

@@ -19,6 +19,7 @@
  **/
 
 #include "ios.h"
+#include "apple.h"
 
 #ifdef LOVE_IOS
 
@@ -125,12 +126,6 @@ static bool deleteFileInDocuments(NSString *filename);
 
 @end
 
-static NSString *getDocumentsDirectory()
-{
-	NSArray *docdirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
-	return docdirs[0];
-}
-
 static NSArray *getLovesInDocuments()
 {
 	NSMutableArray *paths = [NSMutableArray new];
@@ -287,38 +282,6 @@ std::string getLoveInResources(bool &fused)
 	return path;
 }
 
-static std::string getUserDirectory(NSSearchPathDirectory dir)
-{
-	std::string path;
-
-	@autoreleasepool
-	{
-		NSArray<NSURL *> *dirs = [[NSFileManager defaultManager] URLsForDirectory:dir inDomains:NSUserDomainMask];
-
-		if (dirs.count > 0)
-			path = [dirs[0].path UTF8String];
-	}
-
-	return path;
-}
-
-std::string getAppdataDirectory()
-{
-	return getUserDirectory(NSApplicationSupportDirectory);
-}
-
-std::string getHomeDirectory()
-{
-	std::string path;
-
-	@autoreleasepool
-	{
-		path = [NSHomeDirectory() UTF8String];
-	}
-
-	return path;
-}
-
 bool openURL(const std::string &url)
 {
 	bool success = false;
@@ -335,14 +298,6 @@ bool openURL(const std::string &url)
 	return success;
 }
 
-std::string getExecutablePath()
-{
-	@autoreleasepool
-	{
-		return std::string([NSBundle mainBundle].executablePath.UTF8String);
-	}
-}
-
 void vibrate()
 {
 	@autoreleasepool

+ 0 - 7
src/common/macos.h

@@ -31,8 +31,6 @@ namespace love
 namespace macos
 {
 
-std::string getAppdataDirectory();
-
 /**
  * Returns the filepath of the first detected love file in the Resources folder
  * in the main bundle (love.app.)
@@ -46,11 +44,6 @@ std::string getLoveInResources();
  **/
 std::string checkDropEvents();
 
-/**
- * Returns the full path to the executable.
- **/
-std::string getExecutablePath();
-
 /**
  * Bounce the dock icon, if the app isn't in the foreground.
  **/

+ 0 - 28
src/common/macos.mm

@@ -36,26 +36,6 @@ namespace love
 namespace macos
 {
 
-static std::string getUserDirectory(NSSearchPathDirectory dir)
-{
-	std::string path;
-
-	@autoreleasepool
-	{
-		NSArray<NSURL *> *dirs = [[NSFileManager defaultManager] URLsForDirectory:dir inDomains:NSUserDomainMask];
-
-		if (dirs.count > 0)
-			path = [dirs[0].path UTF8String];
-	}
-
-	return path;
-}
-
-std::string getAppdataDirectory()
-{
-	return getUserDirectory(NSApplicationSupportDirectory);
-}
-
 std::string getLoveInResources()
 {
 	std::string path;
@@ -94,14 +74,6 @@ std::string checkDropEvents()
 	return dropstr;
 }
 
-std::string getExecutablePath()
-{
-	@autoreleasepool
-	{
-		return std::string([NSBundle mainBundle].executablePath.UTF8String);
-	}
-}
-
 void requestAttention(bool continuous)
 {
 	@autoreleasepool

+ 23 - 24
src/modules/filesystem/Filesystem.cpp

@@ -26,10 +26,8 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 
-#if defined(LOVE_MACOS)
-#include "common/macos.h"
-#elif defined(LOVE_IOS)
-#include "common/ios.h"
+#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+#include "common/apple.h"
 #elif defined(LOVE_WINDOWS)
 #include <windows.h>
 #include "common/utf8.h"
@@ -92,10 +90,8 @@ bool Filesystem::isRealDirectory(const std::string &path) const
 
 std::string Filesystem::getExecutablePath() const
 {
-#if defined(LOVE_MACOS)
-	return love::macos::getExecutablePath();
-#elif defined(LOVE_IOS)
-	return love::ios::getExecutablePath();
+#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+	return love::apple::getExecutablePath();
 #elif defined(LOVE_WINDOWS)
 
 	wchar_t buffer[MAX_PATH + 1] = {0};
@@ -120,30 +116,33 @@ std::string Filesystem::getExecutablePath() const
 #endif
 }
 
-bool Filesystem::getConstant(const char *in, FileType &out)
+STRINGMAP_CLASS_BEGIN(Filesystem, Filesystem::FileType, Filesystem::FILETYPE_MAX_ENUM, fileType)
 {
-	return fileTypes.find(in, out);
+	{ "file",      Filesystem::FILETYPE_FILE      },
+	{ "directory", Filesystem::FILETYPE_DIRECTORY },
+	{ "symlink",   Filesystem::FILETYPE_SYMLINK   },
+	{ "other",     Filesystem::FILETYPE_OTHER     },
 }
+STRINGMAP_CLASS_END(Filesystem, Filesystem::FileType, Filesystem::FILETYPE_MAX_ENUM, fileType)
 
-bool Filesystem::getConstant(FileType in, const char *&out)
+STRINGMAP_CLASS_BEGIN(Filesystem, Filesystem::CommonPath, Filesystem::COMMONPATH_MAX_ENUM, commonPath)
 {
-	return fileTypes.find(in, out);
+	{ "appidentity",   Filesystem::COMMONPATH_APP_IDENTITY   },
+	{ "appdocuments",  Filesystem::COMMONPATH_APP_DOCUMENTS  },
+	{ "apptemp",       Filesystem::COMMONPATH_APP_TEMP       },
+	{ "userhome",      Filesystem::COMMONPATH_USER_HOME      },
+	{ "userappdata",   Filesystem::COMMONPATH_USER_APPDATA   },
+	{ "userdesktop",   Filesystem::COMMONPATH_USER_DESKTOP   },
+	{ "userdocuments", Filesystem::COMMONPATH_USER_DOCUMENTS },
 }
+STRINGMAP_CLASS_END(Filesystem, Filesystem::CommonPath, Filesystem::COMMONPATH_MAX_ENUM, commonPath)
 
-std::vector<std::string> Filesystem::getConstants(FileType)
+STRINGMAP_CLASS_BEGIN(Filesystem, Filesystem::MountPermissions, Filesystem::MOUNT_PERMISSIONS_MAX_ENUM, mountPermissions)
 {
-	return fileTypes.getNames();
+	{ "read",      Filesystem::MOUNT_PERMISSIONS_READ      },
+	{ "readwrite", Filesystem::MOUNT_PERMISSIONS_READWRITE },
 }
-
-StringMap<Filesystem::FileType, Filesystem::FILETYPE_MAX_ENUM>::Entry Filesystem::fileTypeEntries[] =
-{
-	{ "file",      FILETYPE_FILE      },
-	{ "directory", FILETYPE_DIRECTORY },
-	{ "symlink",   FILETYPE_SYMLINK   },
-	{ "other",     FILETYPE_OTHER     },
-};
-
-StringMap<Filesystem::FileType, Filesystem::FILETYPE_MAX_ENUM> Filesystem::fileTypes(Filesystem::fileTypeEntries, sizeof(Filesystem::fileTypeEntries));
+STRINGMAP_CLASS_END(Filesystem, Filesystem::MountPermissions, Filesystem::MOUNT_PERMISSIONS_MAX_ENUM, mountPermissions)
 
 } // filesystem
 } // love

+ 29 - 6
src/modules/filesystem/Filesystem.h

@@ -71,6 +71,25 @@ public:
 		FILETYPE_MAX_ENUM
 	};
 
+	enum CommonPath
+	{
+		COMMONPATH_APP_IDENTITY,
+		COMMONPATH_APP_DOCUMENTS,
+		COMMONPATH_APP_TEMP,
+		COMMONPATH_USER_HOME,
+		COMMONPATH_USER_APPDATA,
+		COMMONPATH_USER_DESKTOP,
+		COMMONPATH_USER_DOCUMENTS,
+		COMMONPATH_MAX_ENUM
+	};
+
+	enum MountPermissions
+	{
+		MOUNT_PERMISSIONS_READ,
+		MOUNT_PERMISSIONS_READWRITE,
+		MOUNT_PERMISSIONS_MAX_ENUM
+	};
+
 	struct Info
 	{
 		// Numbers will be -1 if they cannot be determined.
@@ -136,8 +155,13 @@ public:
 
 	virtual bool mount(const char *archive, const char *mountpoint, bool appendToPath = false) = 0;
 	virtual bool mount(Data *data, const char *archivename, const char *mountpoint, bool appendToPath = false) = 0;
+
+	virtual bool mountFullPath(const char *archive, const char *mountpoint, MountPermissions permissions, bool appendToPath = false) = 0;
+	virtual bool mountCommonPath(CommonPath path, const char *mountpoint, MountPermissions permissions, bool appendToPath = false) = 0;
+
 	virtual bool unmount(const char *archive) = 0;
 	virtual bool unmount(Data *data) = 0;
+	virtual bool unmount(CommonPath path) = 0;
 
 	/**
 	 * Creates a new file.
@@ -152,6 +176,8 @@ public:
 	 **/
 	virtual FileData *newFileData(const void *data, size_t size, const char *filename) const;
 
+	virtual std::string getFullCommonPath(CommonPath path) = 0;
+
 	/**
 	 * Gets the current working directory.
 	 **/
@@ -263,18 +289,15 @@ public:
 	 **/
 	virtual std::string getExecutablePath() const;
 
-	static bool getConstant(const char *in, FileType &out);
-	static bool getConstant(FileType in, const char *&out);
-	static std::vector<std::string> getConstants(FileType);
+	STRINGMAP_CLASS_DECLARE(FileType);
+	STRINGMAP_CLASS_DECLARE(CommonPath);
+	STRINGMAP_CLASS_DECLARE(MountPermissions);
 
 private:
 
 	// Should we save external or internal for Android
 	bool useExternal;
 
-	static StringMap<FileType, FILETYPE_MAX_ENUM>::Entry fileTypeEntries[];
-	static StringMap<FileType, FILETYPE_MAX_ENUM> fileTypes;
-
 }; // Filesystem
 
 } // filesystem

+ 212 - 77
src/modules/filesystem/physfs/Filesystem.cpp

@@ -45,6 +45,10 @@
 #	include <unistd.h>
 #endif
 
+#if defined(LOVE_IOS) || defined(LOVE_MACOS)
+#	include "common/apple.h"
+#endif
+
 #ifdef LOVE_IOS
 #	include "common/ios.h"
 #endif
@@ -60,50 +64,59 @@
 #include "common/android.h"
 #endif
 
-namespace
+namespace love
+{
+namespace filesystem
+{
+namespace physfs
 {
-	size_t getDriveDelim(const std::string &input)
-	{
-		for (size_t i = 0; i < input.size(); ++i)
-			if (input[i] == '/' || input[i] == '\\')
-				return i;
-		// Something's horribly wrong
-		return 0;
-	}
 
-	std::string getDriveRoot(const std::string &input)
-	{
-		return input.substr(0, getDriveDelim(input)+1);
-	}
+static size_t getDriveDelim(const std::string &input)
+{
+	for (size_t i = 0; i < input.size(); ++i)
+		if (input[i] == '/' || input[i] == '\\')
+			return i;
+	// Something's horribly wrong
+	return 0;
+}
 
-	std::string skipDriveRoot(const std::string &input)
-	{
-		return input.substr(getDriveDelim(input)+1);
-	}
+static std::string getDriveRoot(const std::string &input)
+{
+	return input.substr(0, getDriveDelim(input)+1);
+}
 
-	std::string normalize(const std::string &input)
-	{
-		std::stringstream out;
-		bool seenSep = false, isSep = false;
-		for (size_t i = 0; i < input.size(); ++i)
-		{
-			isSep = (input[i] == LOVE_PATH_SEPARATOR[0]);
-			if (!isSep || !seenSep)
-				out << input[i];
-			seenSep = isSep;
-		}
+static std::string skipDriveRoot(const std::string &input)
+{
+	return input.substr(getDriveDelim(input)+1);
+}
 
-		return out.str();
+static std::string normalize(const std::string &input)
+{
+	std::stringstream out;
+	bool seenSep = false, isSep = false;
+	for (size_t i = 0; i < input.size(); ++i)
+	{
+		isSep = (input[i] == LOVE_PATH_SEPARATOR[0]);
+		if (!isSep || !seenSep)
+			out << input[i];
+		seenSep = isSep;
 	}
 
+	return out.str();
 }
 
-namespace love
-{
-namespace filesystem
-{
-namespace physfs
+static bool isAppCommonPath(Filesystem::CommonPath path)
 {
+	switch (path)
+	{
+	case Filesystem::COMMONPATH_APP_IDENTITY:
+	case Filesystem::COMMONPATH_APP_DOCUMENTS:
+	case Filesystem::COMMONPATH_APP_TEMP:
+		return true;
+	default:
+		return false;
+	}
+}
 
 Filesystem::Filesystem()
 	: fused(false)
@@ -159,15 +172,13 @@ bool Filesystem::setIdentity(const char *ident, bool appendToPath)
 	save_identity = std::string(ident);
 
 	// Generate the relative path to the game save folder.
-	save_path_relative = std::string(LOVE_APPDATA_PREFIX LOVE_APPDATA_FOLDER LOVE_PATH_SEPARATOR) + save_identity;
-
-	// Generate the full path to the game save folder.
-	save_path_full = std::string(getAppdataDirectory()) + std::string(LOVE_PATH_SEPARATOR);
 	if (fused)
-		save_path_full += std::string(LOVE_APPDATA_PREFIX) + save_identity;
+		save_path_relative = std::string(LOVE_APPDATA_PREFIX) + save_identity;
 	else
-		save_path_full += save_path_relative;
+		save_path_relative = std::string(LOVE_APPDATA_PREFIX LOVE_APPDATA_FOLDER LOVE_PATH_SEPARATOR) + save_identity;
 
+	// Generate the full path to the game save folder.
+	save_path_full = std::string(getAppdataDirectory()) + std::string(LOVE_PATH_SEPARATOR) + save_path_relative;
 	save_path_full = normalize(save_path_full);	
 	
 #ifdef LOVE_ANDROID
@@ -370,10 +381,35 @@ bool Filesystem::mount(const char *archive, const char *mountpoint, bool appendT
 		realPath += archive;
 	}
 
-	if (realPath.length() == 0)
+	return mountFullPath(realPath.c_str(), mountpoint, MOUNT_PERMISSIONS_READ, appendToPath);
+}
+
+bool Filesystem::mountFullPath(const char *archive, const char *mountpoint, MountPermissions permissions, bool appendToPath)
+{
+	if (!PHYSFS_isInit() || !archive || !mountpoint)
+		return false;
+
+	if (permissions == MOUNT_PERMISSIONS_READWRITE && strlen(mountpoint) == 0)
 		return false;
 
-	return PHYSFS_mount(realPath.c_str(), mountpoint, appendToPath) != 0;
+	// TODO: readwrite mount
+	return PHYSFS_mount(archive, mountpoint, appendToPath) != 0;
+}
+
+bool Filesystem::mountCommonPath(CommonPath path, const char *mountpoint, MountPermissions permissions, bool appendToPath)
+{
+	std::string fullpath = getFullCommonPath(path);
+	if (fullpath.empty())
+		return false;
+
+	bool success = mountFullPath(fullpath.c_str(), mountpoint, permissions, appendToPath);
+
+	if (!success && isAppCommonPath(path))
+	{
+		
+	}
+
+	return success;
 }
 
 bool Filesystem::mount(Data *data, const char *archivename, const char *mountpoint, bool appendToPath)
@@ -403,42 +439,35 @@ bool Filesystem::unmount(const char *archive)
 		return true;
 	}
 
-	std::string realPath;
-	std::string sourceBase = getSourceBaseDirectory();
-
-	// Check whether the given archive path is in the list of allowed full paths.
-	auto it = std::find(allowedMountPaths.begin(), allowedMountPaths.end(), archive);
+	if (PHYSFS_getRealDir(archive) != nullptr)
+		return PHYSFS_unmount(archive) != 0;
 
-	if (it != allowedMountPaths.end())
-		realPath = *it;
-	else 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;
+	if (strlen(archive) == 0 || strstr(archive, "..") || strcmp(archive, "/") == 0)
+		return false;
 
-		const char *realDir = PHYSFS_getRealDir(archive);
-		if (!realDir)
-			return false;
+	const char *realDir = PHYSFS_getRealDir(archive);
+	if (!realDir)
+		return false;
 
-		realPath = realDir;
-		realPath += LOVE_PATH_SEPARATOR;
-		realPath += archive;
-	}
+	std::string realPath = realDir;
+	realPath += LOVE_PATH_SEPARATOR;
+	realPath += archive;
 
-	const char *mountPoint = PHYSFS_getMountPoint(realPath.c_str());
-	if (!mountPoint)
+	if (PHYSFS_getMountPoint(realPath.c_str()) == nullptr)
 		return false;
 
 	return PHYSFS_unmount(realPath.c_str()) != 0;
 }
 
+bool Filesystem::unmount(CommonPath path)
+{
+	std::string fullpath = getFullCommonPath(path);
+	if (fullpath.empty())
+		return false;
+
+	return unmount(fullpath.c_str());
+}
+
 bool Filesystem::unmount(Data *data)
 {
 	for (const auto &datapair : mountedData)
@@ -458,6 +487,115 @@ love::filesystem::File *Filesystem::newFile(const char *filename) const
 	return new File(filename);
 }
 
+std::string Filesystem::getFullCommonPath(CommonPath path)
+{
+	if (!fullCommonPaths[path].empty())
+		return fullCommonPaths[path];
+
+	if (path == COMMONPATH_APP_IDENTITY || path == COMMONPATH_APP_DOCUMENTS || path == COMMONPATH_APP_TEMP)
+	{
+		
+	}
+
+#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+
+	switch (path)
+	{
+	case COMMONPATH_APP_IDENTITY:
+	case COMMONPATH_APP_DOCUMENTS:
+	case COMMONPATH_APP_TEMP:
+		// Handled above.
+		break;
+	case COMMONPATH_USER_HOME:
+		fullCommonPaths[path] = apple::getUserDirectory(apple::USER_DIRECTORY_HOME);
+		break;
+	case COMMONPATH_USER_APPDATA:
+		fullCommonPaths[path] = apple::getUserDirectory(apple::USER_DIRECTORY_APPSUPPORT);
+		break;
+	case COMMONPATH_USER_DESKTOP:
+		fullCommonPaths[path] = apple::getUserDirectory(apple::USER_DIRECTORY_DESKTOP);
+		break;
+	case COMMONPATH_USER_DOCUMENTS:
+		fullCommonPaths[path] = apple::getUserDirectory(apple::USER_DIRECTORY_DOCUMENTS);
+		break;
+	case COMMONPATH_MAX_ENUM:
+		break;
+	}
+
+#elif defined(LOVE_WINDOWS)
+
+	PWSTR winpath = nullptr;
+	HRESULT hr = E_FAIL;
+
+	switch (path)
+	{
+	case COMMONPATH_APP_IDENTITY:
+	case COMMONPATH_APP_DOCUMENTS:
+	case COMMONPATH_APP_TEMP:
+		// Handled above.
+		break;
+	case COMMONPATH_USER_HOME:
+		hr = SHGetKnownFolderPath(FOLDERID_Profile, 0, nullptr, &winpath);
+		break;
+	case COMMONPATH_USER_APPDATA:
+		hr = SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &winpath);
+		break;
+	case COMMONPATH_USER_DESKTOP:
+		hr = SHGetKnownFolderPath(FOLDERID_Desktop, 0, nullptr, &winpath);
+		break;
+	case COMMONPATH_USER_DOCUMENTS:
+		hr = SHGetKnownFolderPath(FOLDERID_Documents, 0, nullptr, &winpath);
+		break;
+	case COMMONPATH_MAX_ENUM:
+		break;
+	}
+
+	if (SUCCEEDED(hr))
+	{
+		fullCommonPaths[path] = to_utf8(winpath);
+		CoTaskMemFree(winpath);
+	}
+	else
+	{
+
+	}
+
+#elif defined(LOVE_ANDROID)
+
+#elif defined(LOVE_LINUX)
+
+	const char *xdgdir = nullptr;
+
+	switch (path)
+	{
+	case COMMONPATH_APP_IDENTITY:
+	case COMMONPATH_APP_DOCUMENTS:
+	case COMMONPATH_APP_TEMP:
+		// Handled above.
+		break;
+	case COMMONPATH_USER_HOME:
+		fullCommonPaths[path] = normalize(PHYSFS_getUserDir());
+		break;
+	case COMMONPATH_USER_APPDATA:
+		xdgdir = getenv("XDG_DATA_HOME");
+		if (!xdgdir)
+			fullCommonPaths[path] = normalize(std::string(getUserDirectory()) + "/.local/share/");
+		else
+			fullCommonPaths[path] = xdgdir;
+		break;
+	case COMMONPATH_USER_DESKTOP:
+		break;
+	case COMMONPATH_USER_DOCUMENTS:
+		break;
+	case COMMONPATH_MAX_ENUM:
+		break;
+	}
+
+#endif
+
+	return fullCommonPaths[path];
+}
+
 const char *Filesystem::getWorkingDirectory()
 {
 	if (cwd.empty())
@@ -474,7 +612,7 @@ const char *Filesystem::getWorkingDirectory()
 		if (getcwd(cwd_char, LOVE_MAX_PATH))
 			cwd = cwd_char; // if getcwd fails, cwd_char (and thus cwd) will still be empty
 
-		delete [] cwd_char;
+		delete[] cwd_char;
 #endif
 	}
 
@@ -483,9 +621,9 @@ const char *Filesystem::getWorkingDirectory()
 
 std::string Filesystem::getUserDirectory()
 {
-#ifdef LOVE_IOS
+#if defined(LOVE_IOS) || defined(LOVE_MACOS)
 	// PHYSFS_getUserDir doesn't give exactly the path we want on iOS.
-	static std::string userDir = normalize(love::ios::getHomeDirectory());
+	static std::string userDir = normalize(apple::getUserDirectory(apple::USER_DIRECTORY_HOME));
 #else
 	static std::string userDir = normalize(PHYSFS_getUserDir());
 #endif
@@ -512,10 +650,8 @@ std::string Filesystem::getAppdataDirectory()
 			appdata = to_utf8(w_appdata);
 		}
 		replace_char(appdata, '\\', '/');
-#elif defined(LOVE_MACOS)
-		appdata = normalize(love::macos::getAppdataDirectory());
-#elif defined(LOVE_IOS)
-		appdata = normalize(love::ios::getAppdataDirectory());
+#elif defined(LOVE_MACOS) || defined(LOVE_IOS)
+		appdata = normalize(apple::getUserDirectory(apple::USER_DIRECTORY_APPSUPPORT));
 #elif defined(LOVE_LINUX)
 		char *xdgdatahome = getenv("XDG_DATA_HOME");
 		if (!xdgdatahome)
@@ -529,7 +665,6 @@ std::string Filesystem::getAppdataDirectory()
 	return appdata;
 }
 
-
 const char *Filesystem::getSaveDirectory()
 {
 	return save_path_full.c_str();

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

@@ -63,11 +63,16 @@ public:
 	bool mount(const char *archive, const char *mountpoint, bool appendToPath = false) override;
 	bool mount(Data *data, const char *archivename, const char *mountpoint, bool appendToPath = false) override;
 
+	bool mountFullPath(const char *archive, const char *mountpoint, MountPermissions permissions, bool appendToPath = false) override;
+	bool mountCommonPath(CommonPath path, const char *mountpoint, MountPermissions permissions, bool appendToPath = false) override;
+
 	bool unmount(const char *archive) override;
 	bool unmount(Data *data) override;
+	bool unmount(CommonPath path) override;
 
 	love::filesystem::File *newFile(const char *filename) const override;
 
+	std::string getFullCommonPath(CommonPath path) override;
 	const char *getWorkingDirectory() override;
 	std::string getUserDirectory() override;
 	std::string getAppdataDirectory() override;
@@ -129,6 +134,8 @@ private:
 
 	std::map<std::string, StrongRef<Data>> mountedData;
 
+	std::string fullCommonPaths[COMMONPATH_MAX_ENUM];
+
 }; // Filesystem
 
 } // physfs

+ 68 - 0
src/modules/filesystem/wrap_Filesystem.cpp

@@ -148,6 +148,48 @@ int w_mount(lua_State *L)
 	return 1;
 }
 
+int w_mountFullPath(lua_State *L)
+{
+	const char *fullpath = luaL_checkstring(L, 1);
+	const char *mountpoint = luaL_checkstring(L, 2);
+
+	auto permissions = Filesystem::MOUNT_PERMISSIONS_READ;
+	if (!lua_isnoneornil(L, 3))
+	{
+		const char *permissionstr = luaL_checkstring(L, 3);
+		if (!Filesystem::getConstant(permissionstr, permissions))
+			return luax_enumerror(L, "mount permissions", Filesystem::getConstants(permissions), permissionstr);
+	}
+
+	bool append = luax_optboolean(L, 4, false);
+
+	luax_pushboolean(L, instance()->mountFullPath(fullpath, mountpoint, permissions, append));
+	return 1;
+}
+
+int w_mountCommonPath(lua_State *L)
+{
+	const char *commonpathstr = luaL_checkstring(L, 1);
+	Filesystem::CommonPath commonpath;
+	if (!Filesystem::getConstant(commonpathstr, commonpath))
+		return luax_enumerror(L, "common path", Filesystem::getConstants(commonpath), commonpathstr);
+
+	const char *mountpoint = luaL_checkstring(L, 2);
+
+	auto permissions = Filesystem::MOUNT_PERMISSIONS_READ;
+	if (!lua_isnoneornil(L, 3))
+	{
+		const char *permissionstr = luaL_checkstring(L, 3);
+		if (!Filesystem::getConstant(permissionstr, permissions))
+			return luax_enumerror(L, "mount permissions", Filesystem::getConstants(permissions), permissionstr);
+	}
+
+	bool append = luax_optboolean(L, 4, false);
+
+	luax_pushboolean(L, instance()->mountCommonPath(commonpath, mountpoint, permissions, append));
+	return 1;
+}
+
 int w_unmount(lua_State *L)
 {
 	if (luax_istype(L, 1, Data::type))
@@ -163,6 +205,17 @@ int w_unmount(lua_State *L)
 	return 1;
 }
 
+int w_unmountCommonPath(lua_State *L)
+{
+	const char *commonpathstr = luaL_checkstring(L, 1);
+	Filesystem::CommonPath commonpath;
+	if (!Filesystem::getConstant(commonpathstr, commonpath))
+		return luax_enumerror(L, "common path", Filesystem::getConstants(commonpath), commonpathstr);
+
+	luax_pushboolean(L, instance()->unmount(commonpath));
+	return 1;
+}
+
 int w_newFile(lua_State *L)
 {
 	const char *filename = luaL_checkstring(L, 1);
@@ -331,6 +384,17 @@ int w_newFileData(lua_State *L)
 	return 1;
 }
 
+int w_getFullCommonPath(lua_State *L)
+{
+	const char *commonpathstr = luaL_checkstring(L, 1);
+	Filesystem::CommonPath commonpath;
+	if (!Filesystem::getConstant(commonpathstr, commonpath))
+		return luax_enumerror(L, "common path", Filesystem::getConstants(commonpath), commonpathstr);
+
+	luax_pushstring(L, instance()->getFullCommonPath(commonpath));
+	return 1;
+}
+
 int w_getWorkingDirectory(lua_State *L)
 {
 	lua_pushstring(L, instance()->getWorkingDirectory());
@@ -839,8 +903,12 @@ static const luaL_Reg functions[] =
 	{ "setSource", w_setSource },
 	{ "getSource", w_getSource },
 	{ "mount", w_mount },
+	{ "mountFullPath", w_mountFullPath },
+//	{ "mountCommonPath", w_mountCommonPath },
 	{ "unmount", w_unmount },
+//	{ "unmountCommonPath", w_unmountCommonPath },
 	{ "newFile", w_newFile },
+//	{ "getFullCommonPath", w_getFullCommonPath },
 	{ "getWorkingDirectory", w_getWorkingDirectory },
 	{ "getUserDirectory", w_getUserDirectory },
 	{ "getAppdataDirectory", w_getAppdataDirectory },