Skip to content

Commit

Permalink
Add Drive Filesystem
Browse files Browse the repository at this point in the history
Is a virtual filesystem that lists drive letters on e.g. Windows.
The filesystem is deactivated on all other platforms right now.

Invented a new filetype "Filesystem" for delegation of the drive FS to the native FS.
This is kinda a hack but I couldn't think of a better way to add this...
  • Loading branch information
Ghabry committed Apr 1, 2024
1 parent 5f0c5d1 commit a924541
Show file tree
Hide file tree
Showing 11 changed files with 259 additions and 23 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ add_library(${PROJECT_NAME} OBJECT
src/fileext_guesser.h
src/filesystem.cpp
src/filesystem.h
src/filesystem_drive.cpp
src/filesystem_drive.h
src/filesystem_lzh.cpp
src/filesystem_lzh.h
src/filesystem_native.cpp
Expand Down
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ libeasyrpg_player_a_SOURCES = \
src/fileext_guesser.h \
src/filesystem.cpp \
src/filesystem.h \
src/filesystem_drive.cpp \
src/filesystem_drive.h \
src/filesystem_lzh.cpp \
src/filesystem_lzh.h \
src/filesystem_native.cpp \
Expand Down
9 changes: 9 additions & 0 deletions src/directory_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class DirectoryTree {
Regular,
/** Directory */
Directory,
/** A virtual directory that will access a different virtual filesystem */
Filesystem,
/** Anything of no interest such as block devices */
Other
};
Expand All @@ -53,8 +55,15 @@ class DirectoryTree {
std::string name;
/** File type */
FileType type;
/** Human readable name shown in the Game Browser (if different to the filename) */
std::string human_name;

Entry(std::string name, FileType type) : name(std::move(name)), type(type) {}
Entry(std::string name, FileType type, std::string human_name) : name(std::move(name)), type(type), human_name(std::move(human_name)) {}

StringView GetReadableName() const {
return human_name.empty() ? name : human_name;
}
};

/** Argument struct for more complex find operations */
Expand Down
32 changes: 29 additions & 3 deletions src/filesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,13 @@ void Filesystem::ClearCache(StringView path) const {
FilesystemView Filesystem::Create(StringView path) const {
// Determine the proper file system to use

// When the path doesn't exist check if the path contains a file that can
// be handled by another filesystem
if (!IsDirectory(path, true)) {
if (IsFilesystemNode(path)) {
// The support for "mounted" virtual filesystems is very limited and the only
// use right now is to delegate from DriveFilesystem to NativeFilesystem.
return CreateFromNode(path);
} else if (!IsDirectory(path, true)) {
// When the path doesn't exist check if the path contains a file that can
// be handled by another filesystem
std::string dir_of_file;
std::string path_prefix;
std::vector<std::string> components = FileFinder::SplitPath(path);
Expand Down Expand Up @@ -175,10 +179,18 @@ FilesystemView Filesystem::Subtree(std::string sub_path) const {
return FilesystemView(shared_from_this(), sub_path);
}

bool Filesystem::IsFilesystemNode(StringView) const {
return false;
}

bool Filesystem::MakeDirectory(StringView, bool) const {
return false;
}

FilesystemView Filesystem::CreateFromNode(StringView) const {
return FilesystemView();
}

bool Filesystem::IsValid() const {
// FIXME: better way to do this?
return Exists("");
Expand Down Expand Up @@ -307,6 +319,11 @@ bool FilesystemView::IsDirectory(StringView path, bool follow_symlinks) const {
return fs->IsDirectory(MakePath(path), follow_symlinks);
}

bool FilesystemView::IsFilesystemNode(StringView path) const {
assert(fs);
return fs->IsFilesystemNode(MakePath(path));
}

bool FilesystemView::Exists(StringView path) const {
assert(fs);
return fs->Exists(MakePath(path));
Expand Down Expand Up @@ -372,6 +389,11 @@ bool FilesystemView::MakeDirectory(StringView dir, bool follow_symlinks) const {
return fs->MakeDirectory(MakePath(dir), follow_symlinks);
}

FilesystemView FilesystemView::CreateFromNode(StringView path) const {
assert(fs);
return fs->CreateFromNode(MakePath(path));
}

bool FilesystemView::IsFeatureSupported(Filesystem::Feature f) const {
assert(fs);
return fs->IsFeatureSupported(f);
Expand All @@ -393,6 +415,10 @@ FilesystemView FilesystemView::GoUp() const {

auto [path, file] = FileFinder::GetPathAndFilename(GetSubPath());

if (path == GetSubPath()) {
return fs->GetParent();
}

return FilesystemView(fs, path);
}

Expand Down
16 changes: 16 additions & 0 deletions src/filesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,11 @@ class Filesystem : public std::enable_shared_from_this<Filesystem> {
/** @{ */
virtual bool IsFile(StringView path) const = 0;
virtual bool IsDirectory(StringView path, bool follow_symlinks) const = 0;
virtual bool IsFilesystemNode(StringView path) const;
virtual bool Exists(StringView path) const = 0;
virtual int64_t GetFilesize(StringView path) const = 0;
virtual bool MakeDirectory(StringView dir, bool follow_symlinks) const;
virtual FilesystemView CreateFromNode(StringView path) const;
virtual bool IsFeatureSupported(Feature f) const;
virtual std::string Describe() const = 0;
/** @} */
Expand Down Expand Up @@ -369,6 +371,12 @@ class FilesystemView {
*/
bool IsDirectory(StringView path, bool follow_symlinks) const;

/**
* @param path Path to check
* @return True when path is pointing to a virtual filesystem
*/
bool IsFilesystemNode(StringView path) const;

/**
* @param path Path to check
* @return True when a file exists at the path
Expand Down Expand Up @@ -461,6 +469,14 @@ class FilesystemView {
*/
bool MakeDirectory(StringView dir, bool follow_symlinks) const;

/**
* Create a filesystem view from the passed in path.
* The path must point to a virtual filesystem entry (type Filesystem).
* @param path Path to create filesystem from
* @return view pointing at the new fs
*/
FilesystemView CreateFromNode(StringView path) const;

/**
* @param f Filesystem feature to check
* @return true when the feature is supported.
Expand Down
118 changes: 118 additions & 0 deletions src/filesystem_drive.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* This file is part of EasyRPG Player.
*
* EasyRPG Player is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EasyRPG Player is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
*/

#include "filesystem_drive.h"
#include "filefinder.h"
#include "output.h"

#include <algorithm>

#ifdef _WIN32
# include <windows.h>
# include <fileapi.h>
#endif

namespace {
#ifdef _WIN32
StringView drive_sep = "\\";
#else
StringView drive_sep = "/";
#endif
}

DriveFilesystem::DriveFilesystem() : Filesystem("", FilesystemView()) {
#ifdef _WIN32
std::wstring volume = L"A:\\";

DWORD logical_drives = GetLogicalDrives();
for (int i = 0; i < 26; i++) {
if ((logical_drives & (1 << i)) > 0) {
DirectoryTree::Entry entry = { Utils::FromWideString(volume), DirectoryTree::FileType::Filesystem };

wchar_t volume_name[MAX_PATH];
if (GetVolumeInformation(volume.c_str(), volume_name, MAX_PATH, nullptr, nullptr, nullptr, nullptr, 0) != 0) {
entry.human_name = fmt::format("{} ({})", Utils::FromWideString(volume), Utils::FromWideString(volume_name));
}

drives.push_back(entry);
}
volume[0]++; // Increment drive letter
}
#endif
}

bool DriveFilesystem::HasDrives() const {
return !drives.empty();
}

bool DriveFilesystem::IsFile(StringView path) const {
(void)path;
return false;
}

bool DriveFilesystem::IsDirectory(StringView path, bool) const {
return path.empty();
}

bool DriveFilesystem::IsFilesystemNode(StringView path) const {
for (const auto& drive: drives) {
if (drive.name == path) {
return true;
}
#ifdef _WIN32
if (drive.name == Utils::ReplaceAll(ToString(path), "/", "\\")) {
return true;
}
#endif
}

return false;
}

bool DriveFilesystem::Exists(StringView path) const {
return IsDirectory(path, false) || IsFilesystemNode(path);
}

int64_t DriveFilesystem::GetFilesize(StringView path) const {
(void)path;
return 0;
}

FilesystemView DriveFilesystem::CreateFromNode(StringView path) const {
if (!IsFilesystemNode(path)) {
return {};
}

return FileFinder::Root().Create(path);
}

std::streambuf* DriveFilesystem::CreateInputStreambuffer(StringView path, std::ios_base::openmode mode) const {
return nullptr;
}

bool DriveFilesystem::GetDirectoryContent(StringView path, std::vector<DirectoryTree::Entry>& tree) const {
if (!path.empty()) {
return false;
}

tree = drives;
return true;
}

std::string DriveFilesystem::Describe() const {
return "[Drive]";
}
56 changes: 56 additions & 0 deletions src/filesystem_drive.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* This file is part of EasyRPG Player.
*
* EasyRPG Player is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EasyRPG Player is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef EP_FILESYSTEM_DRIVE_H
#define EP_FILESYSTEM_DRIVE_H

#include "filesystem.h"

/**
* A virtual filesystem that lists e.g. drive letters on Windows
*/
class DriveFilesystem : public Filesystem {
public:
/**
* Initializes a OS Filesystem on the given os path
*/
explicit DriveFilesystem();

/** @return Whether the current target platform has drive letters to list */
bool HasDrives() const;

protected:
/**
* Implementation of abstract methods
*/
/** @{ */
bool IsFile(StringView path) const override;
bool IsDirectory(StringView path, bool follow_symlinks) const override;
bool IsFilesystemNode(StringView path) const override;
bool Exists(StringView path) const override;
int64_t GetFilesize(StringView path) const override;
FilesystemView CreateFromNode(StringView path) const override;
std::streambuf* CreateInputStreambuffer(StringView path, std::ios_base::openmode mode) const override;
bool GetDirectoryContent(StringView path, std::vector<DirectoryTree::Entry>& entries) const override;
std::string Describe() const override;
/** @} */

private:
std::vector<DirectoryTree::Entry> drives;
};

#endif
15 changes: 12 additions & 3 deletions src/filesystem_root.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

#include "filesystem_root.h"
#include "filesystem_drive.h"
#include "output.h"

#if defined(__ANDROID__) && !defined(USE_LIBRETRO)
Expand All @@ -28,12 +29,20 @@ constexpr const StringView root_ns = "root://";
RootFilesystem::RootFilesystem() : Filesystem("", FilesystemView()) {
// Add platform specific namespaces here
#if defined(__ANDROID__) && !defined(USE_LIBRETRO)
fs_list.push_back(std::make_pair("apk", std::make_unique<ApkFilesystem>()));
fs_list.push_back(std::make_pair("content", std::make_unique<SafFilesystem>("", FilesystemView())));
fs_list.push_back(std::make_pair("apk", std::make_shared<ApkFilesystem>()));
fs_list.push_back(std::make_pair("content", std::make_shared<SafFilesystem>("", FilesystemView())));
#endif

// Support for drive letters on e.g. Windows (and similiar concepts on other platforms)
auto drive_fs = std::make_shared<DriveFilesystem>();
FilesystemView drive_view;
if (drive_fs->HasDrives()) {
drive_view = *drive_fs;
fs_list.push_back(std::make_pair("drive", drive_fs));
}

// IMPORTANT: This must be the last filesystem in the list, do not push anything to fs_list afterwards!
fs_list.push_back(std::make_pair("file", std::make_unique<NativeFilesystem>("", FilesystemView())));
fs_list.push_back(std::make_pair("file", std::make_shared<NativeFilesystem>("", drive_view)));

assert(fs_list.back().first == "file" && "File namespace must be last!");
}
Expand Down
4 changes: 1 addition & 3 deletions src/scene_gamebrowser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,7 @@ void Scene_GameBrowser::BootGame() {
return;
}

FilesystemView fs;
std::string entry;
std::tie(fs, entry) = gamelist_window->GetGameFilesystem();
FilesystemView fs = gamelist_window->GetGameFilesystem();

if (!fs) {
Output::Warning("The selected file or directory cannot be opened");
Expand Down
Loading

0 comments on commit a924541

Please sign in to comment.