Add love.filesystem.mountFullPath.

This commit is contained in:
Alex Szpakowski
2020-10-03 19:28:57 -03:00
parent dcf0968f05
commit 0706b6243c
13 changed files with 494 additions and 208 deletions
+23 -24
View File
@@ -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
View File
@@ -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
+217 -82
View File
@@ -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,44 +64,6 @@
#include "common/android.h"
#endif
namespace
{
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);
}
std::string skipDriveRoot(const std::string &input)
{
return input.substr(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;
}
return out.str();
}
}
namespace love
{
namespace filesystem
@@ -105,6 +71,53 @@ namespace filesystem
namespace physfs
{
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;
}
static std::string getDriveRoot(const std::string &input)
{
return input.substr(0, getDriveDelim(input)+1);
}
static std::string skipDriveRoot(const std::string &input)
{
return input.substr(getDriveDelim(input)+1);
}
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();
}
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)
, fusedSet(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;
if (fused)
save_path_relative = std::string(LOVE_APPDATA_PREFIX) + save_identity;
else
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;
else
save_path_full += save_path_relative;
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;
return PHYSFS_mount(realPath.c_str(), mountpoint, appendToPath) != 0;
if (permissions == MOUNT_PERMISSIONS_READWRITE && strlen(mountpoint) == 0)
return false;
// 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();
if (PHYSFS_getRealDir(archive) != nullptr)
return PHYSFS_unmount(archive) != 0;
// Check whether the given archive path is in the list of allowed full paths.
auto it = std::find(allowedMountPaths.begin(), allowedMountPaths.end(), archive);
if (strlen(archive) == 0 || strstr(archive, "..") || strcmp(archive, "/") == 0)
return false;
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;
const char *realDir = PHYSFS_getRealDir(archive);
if (!realDir)
return false;
const char *realDir = PHYSFS_getRealDir(archive);
if (!realDir)
return false;
std::string realPath = realDir;
realPath += LOVE_PATH_SEPARATOR;
realPath += archive;
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();
@@ -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
@@ -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 },