Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support "going up" from the working directory in the game browser #3203

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 1 addition & 1 deletion src/filefinder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ FilesystemView FileFinder::Root() {
root_fs = std::make_unique<RootFilesystem>();
}

return root_fs->Subtree("");
return *root_fs;
}

std::string FileFinder::MakePath(StringView dir, StringView name) {
Expand Down
46 changes: 43 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 @@ -382,6 +404,24 @@ FilesystemView FilesystemView::Subtree(StringView sub_path) const {
return FilesystemView(fs, MakePath(sub_path));
}

bool FilesystemView::CanGoUp() const {
return static_cast<bool>(GoUp());
}

FilesystemView FilesystemView::GoUp() const {
if (GetSubPath().empty() || GetSubPath() == "/") {
return fs->GetParent();
}

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

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

return FilesystemView(fs, path);
}

std::string FilesystemView::Describe() const {
assert(fs);
if (GetSubPath().empty()) {
Expand Down
29 changes: 29 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 All @@ -475,6 +491,19 @@ class FilesystemView {
*/
FilesystemView Subtree(StringView sub_path) const;

/**
* @return Whether it is possible to go up from the current view
*/
bool CanGoUp() const;

/**
* From the current view goes up by one.
* Returns an invalid view when going up is not possible (no parent).
*
* @return View that is rooted at the parent.
*/
FilesystemView GoUp() const;

/** @return human readable representation of this filesystem for debug purposes */
std::string Describe() const;

Expand Down
110 changes: 110 additions & 0 deletions src/filesystem_drive.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* 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

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
Loading