diff options
| -rw-r--r-- | src/common/file_util.cpp | 16 | ||||
| -rw-r--r-- | src/common/file_util.h | 8 | ||||
| -rw-r--r-- | src/core/core.cpp | 8 | ||||
| -rw-r--r-- | src/core/core.h | 12 | ||||
| -rw-r--r-- | src/core/file_sys/savedata_factory.h | 1 | ||||
| -rw-r--r-- | src/core/file_sys/vfs.cpp | 148 | ||||
| -rw-r--r-- | src/core/file_sys/vfs.h | 66 | ||||
| -rw-r--r-- | src/core/file_sys/vfs_real.cpp | 341 | ||||
| -rw-r--r-- | src/core/file_sys/vfs_real.h | 56 | ||||
| -rw-r--r-- | src/core/hle/service/filesystem/filesystem.cpp | 16 | ||||
| -rw-r--r-- | src/core/hle/service/filesystem/filesystem.h | 2 | ||||
| -rw-r--r-- | src/core/hle/service/service.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/service/service.h | 7 | ||||
| -rw-r--r-- | src/core/loader/loader.cpp | 4 | ||||
| -rw-r--r-- | src/core/loader/loader.h | 8 | ||||
| -rw-r--r-- | src/video_core/engines/maxwell_3d.cpp | 13 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | 35 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer_cache.h | 64 | ||||
| -rw-r--r-- | src/yuzu/game_list.cpp | 14 | ||||
| -rw-r--r-- | src/yuzu/game_list.h | 3 | ||||
| -rw-r--r-- | src/yuzu/game_list_p.h | 5 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 8 | ||||
| -rw-r--r-- | src/yuzu/main.h | 3 | ||||
| -rw-r--r-- | src/yuzu_cmd/yuzu.cpp | 1 | 
24 files changed, 670 insertions, 173 deletions
| diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 7aeda737f..3ce590062 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -884,11 +884,21 @@ std::string_view RemoveTrailingSlash(std::string_view path) {      return path;  } -std::string SanitizePath(std::string_view path_) { +std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {      std::string path(path_); -    std::replace(path.begin(), path.end(), '\\', '/'); +    char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\'; +    char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/'; + +    if (directory_separator == DirectorySeparator::PlatformDefault) { +#ifdef _WIN32 +        type1 = '/'; +        type2 = '\\'; +#endif +    } + +    std::replace(path.begin(), path.end(), type1, type2);      path.erase(std::unique(path.begin(), path.end(), -                           [](char c1, char c2) { return c1 == '/' && c2 == '/'; }), +                           [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),                 path.end());      return std::string(RemoveTrailingSlash(path));  } diff --git a/src/common/file_util.h b/src/common/file_util.h index d0987fb57..2711872ae 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -182,8 +182,12 @@ std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t la      return std::vector<T>(vector.begin() + first, vector.begin() + first + last);  } -// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. -std::string SanitizePath(std::string_view path); +enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault }; + +// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\' +// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows +std::string SanitizePath(std::string_view path, +                         DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);  // simple wrapper for cstdlib file functions to  // hopefully will make error checking easier diff --git a/src/core/core.cpp b/src/core/core.cpp index 085ba68d0..69c45c026 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -89,7 +89,7 @@ System::ResultStatus System::SingleStep() {  }  System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) { -    app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath)); +    app_loader = Loader::GetLoader(virtual_filesystem->OpenFile(filepath, FileSys::Mode::Read));      if (!app_loader) {          LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); @@ -174,6 +174,10 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {      CoreTiming::Init(); +    // Create a default fs if one doesn't already exist. +    if (virtual_filesystem == nullptr) +        virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); +      current_process = Kernel::Process::Create("main");      cpu_barrier = std::make_shared<CpuBarrier>(); @@ -186,7 +190,7 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {      service_manager = std::make_shared<Service::SM::ServiceManager>();      Kernel::Init(); -    Service::Init(service_manager); +    Service::Init(service_manager, virtual_filesystem);      GDBStub::Init();      renderer = VideoCore::CreateRenderer(emu_window); diff --git a/src/core/core.h b/src/core/core.h index c8ca4b247..7cf7ea4e1 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -17,6 +17,8 @@  #include "core/memory.h"  #include "core/perf_stats.h"  #include "core/telemetry_session.h" +#include "file_sys/vfs_real.h" +#include "hle/service/filesystem/filesystem.h"  #include "video_core/debug_utils/debug_utils.h"  #include "video_core/gpu.h" @@ -211,6 +213,14 @@ public:          return debug_context;      } +    void SetFilesystem(FileSys::VirtualFilesystem vfs) { +        virtual_filesystem = std::move(vfs); +    } + +    FileSys::VirtualFilesystem GetFilesystem() const { +        return virtual_filesystem; +    } +  private:      System(); @@ -225,6 +235,8 @@ private:       */      ResultStatus Init(EmuWindow& emu_window); +    /// RealVfsFilesystem instance +    FileSys::VirtualFilesystem virtual_filesystem;      /// AppLoader used to load the current executing application      std::unique_ptr<Loader::AppLoader> app_loader;      std::unique_ptr<VideoCore::RendererBase> renderer; diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index e3a578c0f..f3cf50d5a 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -7,6 +7,7 @@  #include <memory>  #include <string>  #include "common/common_types.h" +#include "common/swap.h"  #include "core/hle/result.h"  namespace FileSys { diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index dae1c16ef..24e158962 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -4,12 +4,160 @@  #include <algorithm>  #include <numeric> +#include <string> +#include "common/common_paths.h"  #include "common/file_util.h"  #include "common/logging/backend.h"  #include "core/file_sys/vfs.h"  namespace FileSys { +VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {} + +VfsFilesystem::~VfsFilesystem() = default; + +std::string VfsFilesystem::GetName() const { +    return root->GetName(); +} + +bool VfsFilesystem::IsReadable() const { +    return root->IsReadable(); +} + +bool VfsFilesystem::IsWritable() const { +    return root->IsWritable(); +} + +VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { +    const auto path = FileUtil::SanitizePath(path_); +    if (root->GetFileRelative(path) != nullptr) +        return VfsEntryType::File; +    if (root->GetDirectoryRelative(path) != nullptr) +        return VfsEntryType::Directory; + +    return VfsEntryType::None; +} + +VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { +    const auto path = FileUtil::SanitizePath(path_); +    return root->GetFileRelative(path); +} + +VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { +    const auto path = FileUtil::SanitizePath(path_); +    return root->CreateFileRelative(path); +} + +VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { +    const auto old_path = FileUtil::SanitizePath(old_path_); +    const auto new_path = FileUtil::SanitizePath(new_path_); + +    // VfsDirectory impls are only required to implement copy across the current directory. +    if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) { +        if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path))) +            return nullptr; +        return OpenFile(new_path, Mode::ReadWrite); +    } + +    // Do it using RawCopy. Non-default impls are encouraged to optimize this. +    const auto old_file = OpenFile(old_path, Mode::Read); +    if (old_file == nullptr) +        return nullptr; +    auto new_file = OpenFile(new_path, Mode::Read); +    if (new_file != nullptr) +        return nullptr; +    new_file = CreateFile(new_path, Mode::Write); +    if (new_file == nullptr) +        return nullptr; +    if (!VfsRawCopy(old_file, new_file)) +        return nullptr; +    return new_file; +} + +VirtualFile VfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { +    const auto old_path = FileUtil::SanitizePath(old_path_); +    const auto new_path = FileUtil::SanitizePath(new_path_); + +    // Again, non-default impls are highly encouraged to provide a more optimized version of this. +    auto out = CopyFile(old_path_, new_path_); +    if (out == nullptr) +        return nullptr; +    if (DeleteFile(old_path)) +        return out; +    return nullptr; +} + +bool VfsFilesystem::DeleteFile(std::string_view path_) { +    const auto path = FileUtil::SanitizePath(path_); +    auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); +    if (parent == nullptr) +        return false; +    return parent->DeleteFile(FileUtil::GetFilename(path)); +} + +VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { +    const auto path = FileUtil::SanitizePath(path_); +    return root->GetDirectoryRelative(path); +} + +VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { +    const auto path = FileUtil::SanitizePath(path_); +    return root->CreateDirectoryRelative(path); +} + +VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) { +    const auto old_path = FileUtil::SanitizePath(old_path_); +    const auto new_path = FileUtil::SanitizePath(new_path_); + +    // Non-default impls are highly encouraged to provide a more optimized version of this. +    auto old_dir = OpenDirectory(old_path, Mode::Read); +    if (old_dir == nullptr) +        return nullptr; +    auto new_dir = OpenDirectory(new_path, Mode::Read); +    if (new_dir != nullptr) +        return nullptr; +    new_dir = CreateDirectory(new_path, Mode::Write); +    if (new_dir == nullptr) +        return nullptr; + +    for (const auto& file : old_dir->GetFiles()) { +        const auto x = +            CopyFile(old_path + DIR_SEP + file->GetName(), new_path + DIR_SEP + file->GetName()); +        if (x == nullptr) +            return nullptr; +    } + +    for (const auto& dir : old_dir->GetSubdirectories()) { +        const auto x = +            CopyDirectory(old_path + DIR_SEP + dir->GetName(), new_path + DIR_SEP + dir->GetName()); +        if (x == nullptr) +            return nullptr; +    } + +    return new_dir; +} + +VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path_, std::string_view new_path_) { +    const auto old_path = FileUtil::SanitizePath(old_path_); +    const auto new_path = FileUtil::SanitizePath(new_path_); + +    // Non-default impls are highly encouraged to provide a more optimized version of this. +    auto out = CopyDirectory(old_path_, new_path_); +    if (out == nullptr) +        return nullptr; +    if (DeleteDirectory(old_path)) +        return out; +    return nullptr; +} + +bool VfsFilesystem::DeleteDirectory(std::string_view path_) { +    const auto path = FileUtil::SanitizePath(path_); +    auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); +    if (parent == nullptr) +        return false; +    return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path)); +} +  VfsFile::~VfsFile() = default;  std::string VfsFile::GetExtension() const { diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index fab9e2b45..141a053ce 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h @@ -11,14 +11,74 @@  #include <vector>  #include "boost/optional.hpp"  #include "common/common_types.h" +#include "core/file_sys/mode.h"  namespace FileSys { + +struct VfsFilesystem;  struct VfsFile;  struct VfsDirectory; -// Convenience typedefs to use VfsDirectory and VfsFile -using VirtualDir = std::shared_ptr<FileSys::VfsDirectory>; -using VirtualFile = std::shared_ptr<FileSys::VfsFile>; +// Convenience typedefs to use Vfs* interfaces +using VirtualFilesystem = std::shared_ptr<VfsFilesystem>; +using VirtualDir = std::shared_ptr<VfsDirectory>; +using VirtualFile = std::shared_ptr<VfsFile>; + +// An enumeration representing what can be at the end of a path in a VfsFilesystem +enum class VfsEntryType { +    None, +    File, +    Directory, +}; + +// A class representing an abstract filesystem. A default implementation given the root VirtualDir +// is provided for convenience, but if the Vfs implementation has any additional state or +// functionality, they will need to override. +struct VfsFilesystem : NonCopyable { +    VfsFilesystem(VirtualDir root); +    virtual ~VfsFilesystem(); + +    // Gets the friendly name for the filesystem. +    virtual std::string GetName() const; + +    // Return whether or not the user has read permissions on this filesystem. +    virtual bool IsReadable() const; +    // Return whether or not the user has write permission on this filesystem. +    virtual bool IsWritable() const; + +    // Determine if the entry at path is non-existant, a file, or a directory. +    virtual VfsEntryType GetEntryType(std::string_view path) const; + +    // Opens the file with path relative to root. If it doesn't exist, returns nullptr. +    virtual VirtualFile OpenFile(std::string_view path, Mode perms); +    // Creates a new, empty file at path +    virtual VirtualFile CreateFile(std::string_view path, Mode perms); +    // Copies the file from old_path to new_path, returning the new file on success and nullptr on +    // failure. +    virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path); +    // Moves the file from old_path to new_path, returning the moved file on success and nullptr on +    // failure. +    virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path); +    // Deletes the file with path relative to root, returing true on success. +    virtual bool DeleteFile(std::string_view path); + +    // Opens the directory with path relative to root. If it doesn't exist, returns nullptr. +    virtual VirtualDir OpenDirectory(std::string_view path, Mode perms); +    // Creates a new, empty directory at path +    virtual VirtualDir CreateDirectory(std::string_view path, Mode perms); +    // Copies the directory from old_path to new_path, returning the new directory on success and +    // nullptr on failure. +    virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path); +    // Moves the directory from old_path to new_path, returning the moved directory on success and +    // nullptr on failure. +    virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path); +    // Deletes the directory with path relative to root, returing true on success. +    virtual bool DeleteDirectory(std::string_view path); + +protected: +    // Root directory in default implementation. +    VirtualDir root; +};  // A class representing a file in an abstract filesystem.  struct VfsFile : NonCopyable { diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 82d54da4a..1b5919737 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -6,7 +6,7 @@  #include <cstddef>  #include <iterator>  #include <utility> - +#include "common/assert.h"  #include "common/common_paths.h"  #include "common/logging/log.h"  #include "core/file_sys/vfs_real.h" @@ -29,6 +29,8 @@ static std::string ModeFlagsToString(Mode mode) {              mode_str = "a";          else if (mode & Mode::Write)              mode_str = "w"; +        else +            UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode));      }      mode_str += "b"; @@ -36,8 +38,174 @@ static std::string ModeFlagsToString(Mode mode) {      return mode_str;  } -RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_) -    : backing(path_, ModeFlagsToString(perms_).c_str()), path(path_), +RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {} + +std::string RealVfsFilesystem::GetName() const { +    return "Real"; +} + +bool RealVfsFilesystem::IsReadable() const { +    return true; +} + +bool RealVfsFilesystem::IsWritable() const { +    return true; +} + +VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { +    const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); +    if (!FileUtil::Exists(path)) +        return VfsEntryType::None; +    if (FileUtil::IsDirectory(path)) +        return VfsEntryType::Directory; + +    return VfsEntryType::File; +} + +VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { +    const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); +    if (cache.find(path) != cache.end()) { +        auto weak = cache[path]; +        if (!weak.expired()) { +            return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms)); +        } +    } + +    if (!FileUtil::Exists(path) && (perms & Mode::WriteAppend) != 0) +        FileUtil::CreateEmptyFile(path); + +    auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str()); +    cache[path] = backing; + +    // Cannot use make_shared as RealVfsFile constructor is private +    return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms)); +} + +VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { +    const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); +    if (!FileUtil::Exists(path) && !FileUtil::CreateEmptyFile(path)) +        return nullptr; +    return OpenFile(path, perms); +} + +VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { +    const auto old_path = +        FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); +    const auto new_path = +        FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); + +    if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || +        FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path)) +        return nullptr; +    return OpenFile(new_path, Mode::ReadWrite); +} + +VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { +    const auto old_path = +        FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); +    const auto new_path = +        FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); + +    if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || +        FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) +        return nullptr; + +    if (cache.find(old_path) != cache.end()) { +        auto cached = cache[old_path]; +        if (!cached.expired()) { +            auto file = cached.lock(); +            file->Open(new_path, "r+b"); +            cache.erase(old_path); +            cache[new_path] = file; +        } +    } +    return OpenFile(new_path, Mode::ReadWrite); +} + +bool RealVfsFilesystem::DeleteFile(std::string_view path_) { +    const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); +    if (cache.find(path) != cache.end()) { +        if (!cache[path].expired()) +            cache[path].lock()->Close(); +        cache.erase(path); +    } +    return FileUtil::Delete(path); +} + +VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { +    const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); +    // Cannot use make_shared as RealVfsDirectory constructor is private +    return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); +} + +VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { +    const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); +    if (!FileUtil::Exists(path) && !FileUtil::CreateDir(path)) +        return nullptr; +    // Cannot use make_shared as RealVfsDirectory constructor is private +    return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); +} + +VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, +                                            std::string_view new_path_) { +    const auto old_path = +        FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); +    const auto new_path = +        FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); +    if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || +        !FileUtil::IsDirectory(old_path)) +        return nullptr; +    FileUtil::CopyDir(old_path, new_path); +    return OpenDirectory(new_path, Mode::ReadWrite); +} + +VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, +                                            std::string_view new_path_) { +    const auto old_path = +        FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); +    const auto new_path = +        FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); +    if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || +        FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) +        return nullptr; + +    for (auto& kv : cache) { +        // Path in cache starts with old_path +        if (kv.first.rfind(old_path, 0) == 0) { +            const auto file_old_path = +                FileUtil::SanitizePath(kv.first, FileUtil::DirectorySeparator::PlatformDefault); +            const auto file_new_path = +                FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), +                                       FileUtil::DirectorySeparator::PlatformDefault); +            auto cached = cache[file_old_path]; +            if (!cached.expired()) { +                auto file = cached.lock(); +                file->Open(file_new_path, "r+b"); +                cache.erase(file_old_path); +                cache[file_new_path] = file; +            } +        } +    } + +    return OpenDirectory(new_path, Mode::ReadWrite); +} + +bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { +    const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); +    for (auto& kv : cache) { +        // Path in cache starts with old_path +        if (kv.first.rfind(path, 0) == 0) { +            if (!cache[kv.first].expired()) +                cache[kv.first].lock()->Close(); +            cache.erase(kv.first); +        } +    } +    return FileUtil::DeleteDirRecursively(path); +} + +RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOFile> backing_, +                         const std::string& path_, Mode perms_) +    : base(base_), backing(std::move(backing_)), path(path_),        parent_path(FileUtil::GetParentPath(path_)),        path_components(FileUtil::SplitPathComponents(path_)),        parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), @@ -48,15 +216,15 @@ std::string RealVfsFile::GetName() const {  }  size_t RealVfsFile::GetSize() const { -    return backing.GetSize(); +    return backing->GetSize();  }  bool RealVfsFile::Resize(size_t new_size) { -    return backing.Resize(new_size); +    return backing->Resize(new_size);  }  std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const { -    return std::make_shared<RealVfsDirectory>(parent_path, perms); +    return base.OpenDirectory(parent_path, perms);  }  bool RealVfsFile::IsWritable() const { @@ -68,62 +236,118 @@ bool RealVfsFile::IsReadable() const {  }  size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const { -    if (!backing.Seek(offset, SEEK_SET)) +    if (!backing->Seek(offset, SEEK_SET))          return 0; -    return backing.ReadBytes(data, length); +    return backing->ReadBytes(data, length);  }  size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) { -    if (!backing.Seek(offset, SEEK_SET)) +    if (!backing->Seek(offset, SEEK_SET))          return 0; -    return backing.WriteBytes(data, length); +    return backing->WriteBytes(data, length);  }  bool RealVfsFile::Rename(std::string_view name) { -    std::string name_str(name.begin(), name.end()); -    const auto out = FileUtil::Rename(GetName(), name_str); +    return base.MoveFile(path, parent_path + DIR_SEP + std::string(name)) != nullptr; +} + +bool RealVfsFile::Close() { +    return backing->Close(); +} -    path = (parent_path + DIR_SEP).append(name); -    path_components = parent_components; -    path_components.push_back(std::move(name_str)); -    backing = FileUtil::IOFile(path, ModeFlagsToString(perms).c_str()); +// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if +// constexpr' because there is a compile error in the branch not used. + +template <> +std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const { +    if (perms == Mode::Append) +        return {}; + +    std::vector<VirtualFile> out; +    FileUtil::ForeachDirectoryEntry( +        nullptr, path, +        [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { +            const std::string full_path = directory + DIR_SEP + filename; +            if (!FileUtil::IsDirectory(full_path)) +                out.emplace_back(base.OpenFile(full_path, perms)); +            return true; +        });      return out;  } -bool RealVfsFile::Close() { -    return backing.Close(); +template <> +std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const { +    if (perms == Mode::Append) +        return {}; + +    std::vector<VirtualDir> out; +    FileUtil::ForeachDirectoryEntry( +        nullptr, path, +        [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { +            const std::string full_path = directory + DIR_SEP + filename; +            if (FileUtil::IsDirectory(full_path)) +                out.emplace_back(base.OpenDirectory(full_path, perms)); +            return true; +        }); + +    return out;  } -RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_) -    : path(FileUtil::RemoveTrailingSlash(path_)), parent_path(FileUtil::GetParentPath(path)), +RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) +    : base(base_), path(FileUtil::RemoveTrailingSlash(path_)), +      parent_path(FileUtil::GetParentPath(path)),        path_components(FileUtil::SplitPathComponents(path)),        parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),        perms(perms_) {      if (!FileUtil::Exists(path) && perms & Mode::WriteAppend)          FileUtil::CreateDir(path); +} -    if (perms == Mode::Append) -        return; +std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const { +    const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); +    if (!FileUtil::Exists(full_path)) +        return nullptr; +    return base.OpenFile(full_path, perms); +} -    FileUtil::ForeachDirectoryEntry( -        nullptr, path, -        [this](u64* entries_out, const std::string& directory, const std::string& filename) { -            std::string full_path = directory + DIR_SEP + filename; -            if (FileUtil::IsDirectory(full_path)) -                subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(full_path, perms)); -            else -                files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms)); -            return true; -        }); +std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const { +    const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); +    if (!FileUtil::Exists(full_path)) +        return nullptr; +    return base.OpenDirectory(full_path, perms); +} + +std::shared_ptr<VfsFile> RealVfsDirectory::GetFile(std::string_view name) const { +    return GetFileRelative(name); +} + +std::shared_ptr<VfsDirectory> RealVfsDirectory::GetSubdirectory(std::string_view name) const { +    return GetDirectoryRelative(name); +} + +std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) { +    const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); +    return base.CreateFile(full_path, perms); +} + +std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) { +    const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); +    auto parent = std::string(FileUtil::GetParentPath(full_path)); +    return base.CreateDirectory(full_path, perms); +} + +bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { +    auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(name)); +    return base.DeleteDirectory(full_path);  }  std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const { -    return files; +    return IterateEntries<RealVfsFile, VfsFile>();  }  std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const { -    return subdirectories; +    return IterateEntries<RealVfsDirectory, VfsDirectory>();  }  bool RealVfsDirectory::IsWritable() const { @@ -142,57 +366,32 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {      if (path_components.size() <= 1)          return nullptr; -    return std::make_shared<RealVfsDirectory>(parent_path, perms); +    return base.OpenDirectory(parent_path, perms);  }  std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(std::string_view name) {      const std::string subdir_path = (path + DIR_SEP).append(name); - -    if (!FileUtil::CreateDir(subdir_path)) { -        return nullptr; -    } - -    subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(subdir_path, perms)); -    return subdirectories.back(); +    return base.CreateDirectory(subdir_path, perms);  }  std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(std::string_view name) {      const std::string file_path = (path + DIR_SEP).append(name); - -    if (!FileUtil::CreateEmptyFile(file_path)) { -        return nullptr; -    } - -    files.emplace_back(std::make_shared<RealVfsFile>(file_path, perms)); -    return files.back(); +    return base.CreateFile(file_path, perms);  }  bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {      const std::string subdir_path = (path + DIR_SEP).append(name); - -    return FileUtil::DeleteDirRecursively(subdir_path); +    return base.DeleteDirectory(subdir_path);  }  bool RealVfsDirectory::DeleteFile(std::string_view name) { -    const auto file = GetFile(name); - -    if (file == nullptr) { -        return false; -    } - -    files.erase(std::find(files.begin(), files.end(), file)); - -    auto real_file = std::static_pointer_cast<RealVfsFile>(file); -    real_file->Close(); -      const std::string file_path = (path + DIR_SEP).append(name); -    return FileUtil::Delete(file_path); +    return base.DeleteFile(file_path);  }  bool RealVfsDirectory::Rename(std::string_view name) {      const std::string new_name = (parent_path + DIR_SEP).append(name); - -    return FileUtil::Rename(path, new_name); +    return base.MoveFile(path, new_name) != nullptr;  }  std::string RealVfsDirectory::GetFullPath() const { @@ -202,16 +401,6 @@ std::string RealVfsDirectory::GetFullPath() const {  }  bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { -    const auto iter = std::find(files.begin(), files.end(), file); -    if (iter == files.end()) -        return false; - -    const std::ptrdiff_t offset = std::distance(files.begin(), iter); -    files[offset] = files.back(); -    files.pop_back(); - -    subdirectories.emplace_back(std::move(dir)); - -    return true; +    return false;  }  } // namespace FileSys diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h index 243d58576..8a1e79ef6 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs_real.h @@ -6,18 +6,45 @@  #include <string_view> +#include <boost/container/flat_map.hpp>  #include "common/file_util.h"  #include "core/file_sys/mode.h"  #include "core/file_sys/vfs.h"  namespace FileSys { +class RealVfsFilesystem : public VfsFilesystem { +public: +    RealVfsFilesystem(); + +    std::string GetName() const override; +    bool IsReadable() const override; +    bool IsWritable() const override; +    VfsEntryType GetEntryType(std::string_view path) const override; +    VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override; +    VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override; +    VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override; +    VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override; +    bool DeleteFile(std::string_view path) override; +    VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override; +    VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override; +    VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override; +    VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override; +    bool DeleteDirectory(std::string_view path) override; + +private: +    boost::container::flat_map<std::string, std::weak_ptr<FileUtil::IOFile>> cache; +}; +  // An implmentation of VfsFile that represents a file on the user's computer. -struct RealVfsFile : public VfsFile { -    friend struct RealVfsDirectory; +class RealVfsFile : public VfsFile { +    friend class RealVfsDirectory; +    friend class RealVfsFilesystem; -    RealVfsFile(const std::string& name, Mode perms = Mode::Read); +    RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing, +                const std::string& path, Mode perms = Mode::Read); +public:      std::string GetName() const override;      size_t GetSize() const override;      bool Resize(size_t new_size) override; @@ -31,7 +58,8 @@ struct RealVfsFile : public VfsFile {  private:      bool Close(); -    FileUtil::IOFile backing; +    RealVfsFilesystem& base; +    std::shared_ptr<FileUtil::IOFile> backing;      std::string path;      std::string parent_path;      std::vector<std::string> path_components; @@ -40,9 +68,19 @@ private:  };  // An implementation of VfsDirectory that represents a directory on the user's computer. -struct RealVfsDirectory : public VfsDirectory { -    RealVfsDirectory(const std::string& path, Mode perms = Mode::Read); +class RealVfsDirectory : public VfsDirectory { +    friend class RealVfsFilesystem; +    RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); + +public: +    std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override; +    std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override; +    std::shared_ptr<VfsFile> GetFile(std::string_view name) const override; +    std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const override; +    std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path) override; +    std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path) override; +    bool DeleteSubdirectoryRecursive(std::string_view name) override;      std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;      std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;      bool IsWritable() const override; @@ -60,13 +98,15 @@ protected:      bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;  private: +    template <typename T, typename R> +    std::vector<std::shared_ptr<R>> IterateEntries() const; + +    RealVfsFilesystem& base;      std::string path;      std::string parent_path;      std::vector<std::string> path_components;      std::vector<std::string> parent_components;      Mode perms; -    std::vector<std::shared_ptr<VfsFile>> files; -    std::vector<std::shared_ptr<VfsDirectory>> subdirectories;  };  } // namespace FileSys diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index e17d637e4..5e416cde2 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -59,7 +59,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64  ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {      std::string path(FileUtil::SanitizePath(path_));      auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); -    if (path == "/" || path == "\\") { +    if (path.empty()) {          // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...          return RESULT_SUCCESS;      } @@ -281,15 +281,15 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {      return sdmc_factory->Open();  } -void RegisterFileSystems() { +void RegisterFileSystems(const FileSys::VirtualFilesystem& vfs) {      romfs_factory = nullptr;      save_data_factory = nullptr;      sdmc_factory = nullptr; -    auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>( -        FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::ReadWrite); -    auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>( -        FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::ReadWrite); +    auto nand_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), +                                             FileSys::Mode::ReadWrite); +    auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), +                                           FileSys::Mode::ReadWrite);      auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));      save_data_factory = std::move(savedata); @@ -298,8 +298,8 @@ void RegisterFileSystems() {      sdmc_factory = std::move(sdcard);  } -void InstallInterfaces(SM::ServiceManager& service_manager) { -    RegisterFileSystems(); +void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs) { +    RegisterFileSystems(vfs);      std::make_shared<FSP_LDR>()->InstallAsService(service_manager);      std::make_shared<FSP_PR>()->InstallAsService(service_manager);      std::make_shared<FSP_SRV>()->InstallAsService(service_manager); diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index d4483daa5..462c13f20 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -36,7 +36,7 @@ ResultVal<FileSys::VirtualDir> OpenSDMC();  // ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS();  /// Registers all Filesystem services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs);  // A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of  // pointers and booleans. This makes using a VfsDirectory with switch services much easier and diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 6f286ea74..11951adaf 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -198,7 +198,7 @@ void AddNamedPort(std::string name, SharedPtr<ClientPort> port) {  }  /// Initialize ServiceManager -void Init(std::shared_ptr<SM::ServiceManager>& sm) { +void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) {      // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it      // here and pass it into the respective InstallInterfaces functions.      auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); @@ -221,7 +221,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {      EUPLD::InstallInterfaces(*sm);      Fatal::InstallInterfaces(*sm);      FGM::InstallInterfaces(*sm); -    FileSystem::InstallInterfaces(*sm); +    FileSystem::InstallInterfaces(*sm, rfs);      Friend::InstallInterfaces(*sm);      GRC::InstallInterfaces(*sm);      HID::InstallInterfaces(*sm); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 046c5e18d..8a294c0f2 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -22,6 +22,10 @@ class ServerSession;  class HLERequestContext;  } // namespace Kernel +namespace FileSys { +struct VfsFilesystem; +} +  namespace Service {  namespace SM { @@ -177,7 +181,8 @@ private:  };  /// Initialize ServiceManager -void Init(std::shared_ptr<SM::ServiceManager>& sm); +void Init(std::shared_ptr<SM::ServiceManager>& sm, +          const std::shared_ptr<FileSys::VfsFilesystem>& vfs);  /// Shutdown ServiceManager  void Shutdown(); diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 0781fb8c1..a288654df 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -43,10 +43,6 @@ FileType IdentifyFile(FileSys::VirtualFile file) {      return FileType::Unknown;  } -FileType IdentifyFile(const std::string& file_name) { -    return IdentifyFile(std::make_shared<FileSys::RealVfsFile>(file_name)); -} -  FileType GuessFromFilename(const std::string& name) {      if (name == "main")          return FileType::DeconstructedRomDirectory; diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 7bd0adedb..6a9e5a68b 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -43,14 +43,6 @@ enum class FileType {  FileType IdentifyFile(FileSys::VirtualFile file);  /** - * Identifies the type of a bootable file based on the magic value in its header. - * @param file_name path to file - * @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine - * a filetype, and will never return FileType::Error. - */ -FileType IdentifyFile(const std::string& file_name); - -/**   * Guess the type of a bootable file from its name   * @param name String name of bootable file   * @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index ed22a2090..a46ed4bd7 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -23,12 +23,17 @@ Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager&      : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {}  void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { -    auto macro_code = uploaded_macros.find(method); +    // Reset the current macro. +    executing_macro = 0; +      // The requested macro must have been uploaded already. -    ASSERT_MSG(macro_code != uploaded_macros.end(), "Macro %08X was not uploaded", method); +    auto macro_code = uploaded_macros.find(method); +    if (macro_code == uploaded_macros.end()) { +        LOG_ERROR(HW_GPU, "Macro {:04X} was not uploaded", method); +        return; +    } -    // Reset the current macro and execute it. -    executing_macro = 0; +    // Execute the current macro.      macro_interpreter.Execute(macro_code->second, std::move(parameters));  } diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 8b6d1b89d..f6efce818 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -109,7 +109,9 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form      {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,       true},                                                                                 // DXT45      {GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXN1 -    {GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true},   // DXN2 +    {GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, +     true},                                                                     // DXN2UNORM +    {GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, ComponentType::SNorm, true}, // DXN2SNORM      {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,       true},                                                                    // BC7U      {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false},        // ASTC_2D_4X4 @@ -219,17 +221,18 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),          MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::RGBA32UI>,          MortonCopy<true, PixelFormat::DXT1>,         MortonCopy<true, PixelFormat::DXT23>,          MortonCopy<true, PixelFormat::DXT45>,        MortonCopy<true, PixelFormat::DXN1>, -        MortonCopy<true, PixelFormat::DXN2>,         MortonCopy<true, PixelFormat::BC7U>, -        MortonCopy<true, PixelFormat::ASTC_2D_4X4>,  MortonCopy<true, PixelFormat::G8R8>, -        MortonCopy<true, PixelFormat::BGRA8>,        MortonCopy<true, PixelFormat::RGBA32F>, -        MortonCopy<true, PixelFormat::RG32F>,        MortonCopy<true, PixelFormat::R32F>, -        MortonCopy<true, PixelFormat::R16F>,         MortonCopy<true, PixelFormat::R16UNORM>, -        MortonCopy<true, PixelFormat::RG16>,         MortonCopy<true, PixelFormat::RG16F>, -        MortonCopy<true, PixelFormat::RG16UI>,       MortonCopy<true, PixelFormat::RG16I>, -        MortonCopy<true, PixelFormat::RG16S>,        MortonCopy<true, PixelFormat::RGB32F>, -        MortonCopy<true, PixelFormat::SRGBA8>,       MortonCopy<true, PixelFormat::Z24S8>, -        MortonCopy<true, PixelFormat::S8Z24>,        MortonCopy<true, PixelFormat::Z32F>, -        MortonCopy<true, PixelFormat::Z16>,          MortonCopy<true, PixelFormat::Z32FS8>, +        MortonCopy<true, PixelFormat::DXN2UNORM>,    MortonCopy<true, PixelFormat::DXN2SNORM>, +        MortonCopy<true, PixelFormat::BC7U>,         MortonCopy<true, PixelFormat::ASTC_2D_4X4>, +        MortonCopy<true, PixelFormat::G8R8>,         MortonCopy<true, PixelFormat::BGRA8>, +        MortonCopy<true, PixelFormat::RGBA32F>,      MortonCopy<true, PixelFormat::RG32F>, +        MortonCopy<true, PixelFormat::R32F>,         MortonCopy<true, PixelFormat::R16F>, +        MortonCopy<true, PixelFormat::R16UNORM>,     MortonCopy<true, PixelFormat::RG16>, +        MortonCopy<true, PixelFormat::RG16F>,        MortonCopy<true, PixelFormat::RG16UI>, +        MortonCopy<true, PixelFormat::RG16I>,        MortonCopy<true, PixelFormat::RG16S>, +        MortonCopy<true, PixelFormat::RGB32F>,       MortonCopy<true, PixelFormat::SRGBA8>, +        MortonCopy<true, PixelFormat::Z24S8>,        MortonCopy<true, PixelFormat::S8Z24>, +        MortonCopy<true, PixelFormat::Z32F>,         MortonCopy<true, PixelFormat::Z16>, +        MortonCopy<true, PixelFormat::Z32FS8>,  };  static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), @@ -251,6 +254,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),          nullptr,          nullptr,          nullptr, +        nullptr, +        nullptr,          MortonCopy<false, PixelFormat::G8R8>,          MortonCopy<false, PixelFormat::BGRA8>,          MortonCopy<false, PixelFormat::RGBA32F>, @@ -761,10 +766,12 @@ void RasterizerCacheOpenGL::FlushRegion(Tegra::GPUVAddr /*addr*/, size_t /*size*  }  void RasterizerCacheOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, size_t size) { -    for (const auto& pair : surface_cache) { -        const auto& surface{pair.second}; +    for (auto iter = surface_cache.cbegin(); iter != surface_cache.cend();) { +        const auto& surface{iter->second};          const auto& params{surface->GetSurfaceParams()}; +        ++iter; +          if (params.IsOverlappingRegion(addr, size)) {              UnregisterSurface(surface);          } diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 6f01b2bf0..26e2ee203 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -35,32 +35,33 @@ struct SurfaceParams {          DXT23 = 9,          DXT45 = 10,          DXN1 = 11, // This is also known as BC4 -        DXN2 = 12, // This is also known as BC5 -        BC7U = 13, -        ASTC_2D_4X4 = 14, -        G8R8 = 15, -        BGRA8 = 16, -        RGBA32F = 17, -        RG32F = 18, -        R32F = 19, -        R16F = 20, -        R16UNORM = 21, -        RG16 = 22, -        RG16F = 23, -        RG16UI = 24, -        RG16I = 25, -        RG16S = 26, -        RGB32F = 27, -        SRGBA8 = 28, +        DXN2UNORM = 12, +        DXN2SNORM = 13, +        BC7U = 14, +        ASTC_2D_4X4 = 15, +        G8R8 = 16, +        BGRA8 = 17, +        RGBA32F = 18, +        RG32F = 19, +        R32F = 20, +        R16F = 21, +        R16UNORM = 22, +        RG16 = 23, +        RG16F = 24, +        RG16UI = 25, +        RG16I = 26, +        RG16S = 27, +        RGB32F = 28, +        SRGBA8 = 29,          MaxColorFormat,          // DepthStencil formats -        Z24S8 = 29, -        S8Z24 = 30, -        Z32F = 31, -        Z16 = 32, -        Z32FS8 = 33, +        Z24S8 = 30, +        S8Z24 = 31, +        Z32F = 32, +        Z16 = 33, +        Z32FS8 = 34,          MaxDepthStencilFormat, @@ -110,7 +111,8 @@ struct SurfaceParams {              4, // DXT23              4, // DXT45              4, // DXN1 -            4, // DXN2 +            4, // DXN2UNORM +            4, // DXN2SNORM              4, // BC7U              4, // ASTC_2D_4X4              1, // G8R8 @@ -155,7 +157,8 @@ struct SurfaceParams {              128, // DXT23              128, // DXT45              64,  // DXN1 -            128, // DXN2 +            128, // DXN2UNORM +            128, // DXN2SNORM              128, // BC7U              32,  // ASTC_2D_4X4              16,  // G8R8 @@ -309,7 +312,15 @@ struct SurfaceParams {          case Tegra::Texture::TextureFormat::DXN1:              return PixelFormat::DXN1;          case Tegra::Texture::TextureFormat::DXN2: -            return PixelFormat::DXN2; +            switch (component_type) { +            case Tegra::Texture::ComponentType::UNORM: +                return PixelFormat::DXN2UNORM; +            case Tegra::Texture::ComponentType::SNORM: +                return PixelFormat::DXN2SNORM; +            } +            LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", +                         static_cast<u32>(component_type)); +            UNREACHABLE();          case Tegra::Texture::TextureFormat::BC7U:              return PixelFormat::BC7U;          case Tegra::Texture::TextureFormat::ASTC_2D_4X4: @@ -367,7 +378,8 @@ struct SurfaceParams {              return Tegra::Texture::TextureFormat::DXT45;          case PixelFormat::DXN1:              return Tegra::Texture::TextureFormat::DXN1; -        case PixelFormat::DXN2: +        case PixelFormat::DXN2UNORM: +        case PixelFormat::DXN2SNORM:              return Tegra::Texture::TextureFormat::DXN2;          case PixelFormat::BC7U:              return Tegra::Texture::TextureFormat::BC7U; diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 5f47f5a2b..1c738d2a4 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -15,6 +15,7 @@  #include "common/string_util.h"  #include "core/file_sys/content_archive.h"  #include "core/file_sys/control_metadata.h" +#include "core/file_sys/romfs.h"  #include "core/file_sys/vfs_real.h"  #include "core/loader/loader.h"  #include "game_list.h" @@ -197,7 +198,8 @@ void GameList::onFilterCloseClicked() {      main_window->filterBarSetChecked(false);  } -GameList::GameList(GMainWindow* parent) : QWidget{parent} { +GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) +    : QWidget{parent}, vfs(std::move(vfs)) {      watcher = new QFileSystemWatcher(this);      connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); @@ -341,7 +343,7 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {      emit ShouldCancelWorker(); -    GameListWorker* worker = new GameListWorker(dir_path, deep_scan); +    GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan);      connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);      connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, @@ -414,8 +416,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign          bool is_dir = FileUtil::IsDirectory(physical_name);          QFileInfo file_info(physical_name.c_str());          if (!is_dir && file_info.suffix().toStdString() == "nca") { -            auto nca = std::make_shared<FileSys::NCA>( -                std::make_shared<FileSys::RealVfsFile>(physical_name)); +            auto nca = +                std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));              if (nca->GetType() == FileSys::NCAContentType::Control)                  nca_control_map.insert_or_assign(nca->GetTitleId(), nca);          } @@ -436,7 +438,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign          if (!is_dir &&              (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {              std::unique_ptr<Loader::AppLoader> loader = -                Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(physical_name)); +                Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read));              if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown ||                               loader->GetFileType() == Loader::FileType::Error) &&                              !UISettings::values.show_unknown)) @@ -459,7 +461,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign                  // Use from metadata pool.                  if (nca_control_map.find(program_id) != nca_control_map.end()) {                      const auto nca = nca_control_map[program_id]; -                    const auto control_dir = nca->GetSubdirectories()[0]; +                    const auto control_dir = FileSys::ExtractRomFS(nca->GetRomFS());                      const auto nacp_file = control_dir->GetFile("control.nacp");                      FileSys::NACP nacp(nacp_file); diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 3bc14f07f..afe624b32 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -59,7 +59,7 @@ public:          QToolButton* button_filter_close = nullptr;      }; -    explicit GameList(GMainWindow* parent = nullptr); +    explicit GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent = nullptr);      ~GameList() override;      void clearFilter(); @@ -90,6 +90,7 @@ private:      void PopupContextMenu(const QPoint& menu_location);      void RefreshGameDirectory(); +    FileSys::VirtualFilesystem vfs;      SearchField* search_field;      GMainWindow* main_window = nullptr;      QVBoxLayout* layout = nullptr; diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index a22025e67..114a0fc7f 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -139,8 +139,8 @@ class GameListWorker : public QObject, public QRunnable {      Q_OBJECT  public: -    GameListWorker(QString dir_path, bool deep_scan) -        : dir_path(std::move(dir_path)), deep_scan(deep_scan) {} +    GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan) +        : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan) {}  public slots:      /// Starts the processing of directory tree information. @@ -163,6 +163,7 @@ signals:      void Finished(QStringList watch_list);  private: +    FileSys::VirtualFilesystem vfs;      QStringList watch_list;      QString dir_path;      bool deep_scan; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index a6241e63e..67e3c6549 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -24,6 +24,7 @@  #include "common/string_util.h"  #include "core/core.h"  #include "core/crypto/key_manager.h" +#include "core/file_sys/vfs_real.h"  #include "core/gdbstub/gdbstub.h"  #include "core/loader/loader.h"  #include "core/settings.h" @@ -83,7 +84,9 @@ void GMainWindow::ShowCallouts() {}  const int GMainWindow::max_recent_files_item; -GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { +GMainWindow::GMainWindow() +    : config(new Config()), emu_thread(nullptr), +      vfs(std::make_shared<FileSys::RealVfsFilesystem>()) {      debug_context = Tegra::DebugContext::Construct(); @@ -132,7 +135,7 @@ void GMainWindow::InitializeWidgets() {      render_window = new GRenderWindow(this, emu_thread.get());      render_window->hide(); -    game_list = new GameList(this); +    game_list = new GameList(vfs, this);      ui.horizontalLayout->addWidget(game_list);      // Create status bar @@ -406,6 +409,7 @@ bool GMainWindow::LoadROM(const QString& filename) {      }      Core::System& system{Core::System::GetInstance()}; +    system.SetFilesystem(vfs);      system.SetGPUDebugContext(debug_context); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 6e335b8f8..74487c58c 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -161,6 +161,9 @@ private:      bool emulation_running = false;      std::unique_ptr<EmuThread> emu_thread; +    // FS +    FileSys::VirtualFilesystem vfs; +      // Debugger panes      ProfilerWidget* profilerWidget;      MicroProfileDialog* microProfileDialog; diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index d637dbd0c..0605c92e3 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -161,6 +161,7 @@ int main(int argc, char** argv) {      }      Core::System& system{Core::System::GetInstance()}; +    system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());      SCOPE_EXIT({ system.Shutdown(); }); | 
