summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/common/color.h50
-rw-r--r--src/common/file_util.cpp16
-rw-r--r--src/common/file_util.h8
-rw-r--r--src/common/vector_math.h6
-rw-r--r--src/core/core.cpp8
-rw-r--r--src/core/core.h12
-rw-r--r--src/core/file_sys/directory.h12
-rw-r--r--src/core/file_sys/savedata_factory.h1
-rw-r--r--src/core/file_sys/vfs.cpp148
-rw-r--r--src/core/file_sys/vfs.h66
-rw-r--r--src/core/file_sys/vfs_real.cpp341
-rw-r--r--src/core/file_sys/vfs_real.h56
-rw-r--r--src/core/hle/service/acc/acc.cpp23
-rw-r--r--src/core/hle/service/am/am.cpp9
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp16
-rw-r--r--src/core/hle/service/filesystem/filesystem.h2
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp9
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp2
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/hle/service/service.h7
-rw-r--r--src/core/loader/loader.cpp4
-rw-r--r--src/core/loader/loader.h8
-rw-r--r--src/video_core/engines/maxwell_3d.cpp15
-rw-r--r--src/video_core/engines/maxwell_3d.h2
-rw-r--r--src/video_core/engines/shader_bytecode.h15
-rw-r--r--src/video_core/gpu.cpp1
-rw-r--r--src/video_core/gpu.h1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp12
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp31
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h64
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp22
-rw-r--r--src/video_core/renderer_opengl/gl_state.h6
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h4
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp2
-rw-r--r--src/video_core/textures/decoders.cpp3
-rw-r--r--src/yuzu/game_list.cpp14
-rw-r--r--src/yuzu/game_list.h3
-rw-r--r--src/yuzu/game_list_p.h5
-rw-r--r--src/yuzu/main.cpp8
-rw-r--r--src/yuzu/main.h3
-rw-r--r--src/yuzu_cmd/yuzu.cpp1
42 files changed, 803 insertions, 218 deletions
diff --git a/src/common/color.h b/src/common/color.h
index 24a445dac..0379040be 100644
--- a/src/common/color.h
+++ b/src/common/color.h
@@ -4,6 +4,8 @@
#pragma once
+#include <cstring>
+
#include "common/common_types.h"
#include "common/swap.h"
#include "common/vector_math.h"
@@ -55,7 +57,7 @@ constexpr u8 Convert8To6(u8 value) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
-inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
+inline Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
return {bytes[3], bytes[2], bytes[1], bytes[0]};
}
@@ -64,7 +66,7 @@ inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
-inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
+inline Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
return {bytes[2], bytes[1], bytes[0], 255};
}
@@ -73,7 +75,7 @@ inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
-inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
+inline Math::Vec4<u8> DecodeRG8(const u8* bytes) {
return {bytes[1], bytes[0], 0, 255};
}
@@ -82,8 +84,9 @@ inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
-inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
- const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
+inline Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
+ u16_le pixel;
+ std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
Convert5To8(pixel & 0x1F), 255};
}
@@ -93,8 +96,9 @@ inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
-inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
- const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
+inline Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
+ u16_le pixel;
+ std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)};
}
@@ -104,8 +108,9 @@ inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
-inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
- const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
+inline Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
+ u16_le pixel;
+ std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)};
}
@@ -116,7 +121,9 @@ inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
* @return Depth value as an u32
*/
inline u32 DecodeD16(const u8* bytes) {
- return *reinterpret_cast<const u16_le*>(bytes);
+ u16_le data;
+ std::memcpy(&data, bytes, sizeof(data));
+ return data;
}
/**
@@ -133,7 +140,7 @@ inline u32 DecodeD24(const u8* bytes) {
* @param bytes Pointer to encoded source values
* @return Resulting values stored as a Math::Vec2
*/
-inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
+inline Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
}
@@ -175,8 +182,10 @@ inline void EncodeRG8(const Math::Vec4<u8>& color, u8* bytes) {
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
- *reinterpret_cast<u16_le*>(bytes) =
+ const u16_le data =
(Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
+
+ std::memcpy(bytes, &data, sizeof(data));
}
/**
@@ -185,9 +194,10 @@ inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
- *reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) |
- (Convert8To5(color.g()) << 6) |
- (Convert8To5(color.b()) << 1) | Convert8To1(color.a());
+ const u16_le data = (Convert8To5(color.r()) << 11) | (Convert8To5(color.g()) << 6) |
+ (Convert8To5(color.b()) << 1) | Convert8To1(color.a());
+
+ std::memcpy(bytes, &data, sizeof(data));
}
/**
@@ -196,9 +206,10 @@ inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
- *reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) |
- (Convert8To4(color.g()) << 8) |
- (Convert8To4(color.b()) << 4) | Convert8To4(color.a());
+ const u16 data = (Convert8To4(color.r()) << 12) | (Convert8To4(color.g()) << 8) |
+ (Convert8To4(color.b()) << 4) | Convert8To4(color.a());
+
+ std::memcpy(bytes, &data, sizeof(data));
}
/**
@@ -207,7 +218,8 @@ inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
* @param bytes Pointer where to store the encoded value
*/
inline void EncodeD16(u32 value, u8* bytes) {
- *reinterpret_cast<u16_le*>(bytes) = value & 0xFFFF;
+ const u16_le data = static_cast<u16>(value);
+ std::memcpy(bytes, &data, sizeof(data));
}
/**
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/common/vector_math.h b/src/common/vector_math.h
index 5c94fcda3..8feb49941 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -78,7 +78,7 @@ public:
}
template <typename U = T>
- constexpr Vec2<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
+ constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y};
}
constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
@@ -227,7 +227,7 @@ public:
}
template <typename U = T>
- constexpr Vec3<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
+ constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y, -z};
}
@@ -436,7 +436,7 @@ public:
}
template <typename U = T>
- constexpr Vec4<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
+ constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y, -z, -w};
}
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/directory.h b/src/core/file_sys/directory.h
index 213ce1826..3759e743a 100644
--- a/src/core/file_sys/directory.h
+++ b/src/core/file_sys/directory.h
@@ -4,8 +4,9 @@
#pragma once
-#include <array>
#include <cstddef>
+#include <iterator>
+#include <string_view>
#include "common/common_funcs.h"
#include "common/common_types.h"
@@ -21,9 +22,14 @@ enum EntryType : u8 {
// Structure of a directory entry, from
// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
-const size_t FILENAME_LENGTH = 0x300;
struct Entry {
- char filename[FILENAME_LENGTH];
+ Entry(std::string_view view, EntryType entry_type, u64 entry_size)
+ : type{entry_type}, file_size{entry_size} {
+ const size_t copy_size = view.copy(filename, std::size(filename) - 1);
+ filename[copy_size] = '\0';
+ }
+
+ char filename[0x300];
INSERT_PADDING_BYTES(4);
EntryType type;
INSERT_PADDING_BYTES(3);
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/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index e952b0518..f3c5b1b9c 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -42,7 +42,7 @@ public:
{0, &IProfile::Get, "Get"},
{1, &IProfile::GetBase, "GetBase"},
{10, nullptr, "GetImageSize"},
- {11, nullptr, "LoadImage"},
+ {11, &IProfile::LoadImage, "LoadImage"},
};
RegisterHandlers(functions);
}
@@ -83,6 +83,27 @@ private:
rb.PushRaw(profile_base);
}
+ void LoadImage(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_ACC, "(STUBBED) called");
+ // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
+ // TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000
+ const u32 jpeg_size = 107;
+ static const std::array<u8, jpeg_size> jpeg{
+ 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03,
+ 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a,
+ 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10,
+ 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00,
+ 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01,
+ 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
+ };
+ ctx.WriteBuffer(jpeg.data(), jpeg_size);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(jpeg_size);
+ }
+
u128 user_id; ///< The user id this profile refers to.
};
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 9404d6b8c..762763463 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -136,7 +136,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
{16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"},
{17, nullptr, "SetControllerFirmwareUpdateSection"},
{18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"},
- {19, nullptr, "SetScreenShotImageOrientation"},
+ {19, &ISelfController::SetScreenShotImageOrientation, "SetScreenShotImageOrientation"},
{20, nullptr, "SetDesirableKeyboardLayout"},
{40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
{41, nullptr, "IsSystemBufferSharingEnabled"},
@@ -254,6 +254,13 @@ void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext&
LOG_WARNING(Service_AM, "(STUBBED) called");
}
+void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+}
+
void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
// TODO(Subv): Find out how AM determines the display to use, for now just create the layer
// in the Default display.
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 8f4f98346..862f338ac 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -83,6 +83,7 @@ private:
void LockExit(Kernel::HLERequestContext& ctx);
void UnlockExit(Kernel::HLERequestContext& ctx);
void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx);
+ void SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx);
void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
void SetScreenShotPermission(Kernel::HLERequestContext& ctx);
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
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/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index e7ffb6bd1..1470f9017 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -193,13 +193,10 @@ private:
template <typename T>
static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data,
FileSys::EntryType type) {
+ entries.reserve(entries.size() + new_data.size());
+
for (const auto& new_entry : new_data) {
- FileSys::Entry entry;
- entry.filename[0] = '\0';
- std::strncat(entry.filename, new_entry->GetName().c_str(), FileSys::FILENAME_LENGTH - 1);
- entry.type = type;
- entry.file_size = new_entry->GetSize();
- entries.emplace_back(std::move(entry));
+ entries.emplace_back(new_entry->GetName(), type, new_entry->GetSize());
}
}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 116dabedb..4cdf7f613 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -147,7 +147,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
}
params.fence_out.id = 0;
params.fence_out.value = 0;
- std::memcpy(output.data(), &params, output.size());
+ std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
return 0;
}
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 5c0ae8009..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));
}
@@ -238,6 +243,8 @@ void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) {
auto& buffer = shader.const_buffers[bind_data.index];
+ ASSERT(bind_data.index < Regs::MaxConstBuffers);
+
buffer.enabled = bind_data.valid.Value() != 0;
buffer.index = bind_data.index;
buffer.address = regs.const_buffer.BufferAddress();
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 4d0ff96a5..0506ac8fe 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -44,7 +44,7 @@ public:
static constexpr size_t MaxShaderProgram = 6;
static constexpr size_t MaxShaderStage = 5;
// Maximum number of const buffers per shader stage.
- static constexpr size_t MaxConstBuffers = 16;
+ static constexpr size_t MaxConstBuffers = 18;
enum class QueryMode : u32 {
Write = 0,
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index c7e3fb4b1..3d4557b7e 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -78,6 +78,8 @@ union Attribute {
// shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
// shader.
TessCoordInstanceIDVertexID = 47,
+ // TODO(bunnei): Figure out what this is used for. Super Mario Odyssey uses this.
+ Unknown_63 = 63,
};
union {
@@ -254,20 +256,15 @@ union Instruction {
BitField<56, 1, u64> invert_b;
} lop32i;
- float GetImm20_19() const {
- float result{};
+ u32 GetImm20_19() const {
u32 imm{static_cast<u32>(imm20_19)};
imm <<= 12;
imm |= negate_imm ? 0x80000000 : 0;
- std::memcpy(&result, &imm, sizeof(imm));
- return result;
+ return imm;
}
- float GetImm20_32() const {
- float result{};
- s32 imm{static_cast<s32>(imm20_32)};
- std::memcpy(&result, &imm, sizeof(imm));
- return result;
+ u32 GetImm20_32() const {
+ return static_cast<u32>(imm20_32);
}
s32 GetSignedImm20_20() const {
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index b2a83ce0b..4ff4d71c5 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -42,6 +42,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
case RenderTargetFormat::RGB10_A2_UNORM:
case RenderTargetFormat::BGRA8_UNORM:
case RenderTargetFormat::R32_FLOAT:
+ case RenderTargetFormat::R11G11B10_FLOAT:
return 4;
default:
UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format));
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 440505c9d..874eddd78 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -34,6 +34,7 @@ enum class RenderTargetFormat : u32 {
RG16_FLOAT = 0xDE,
R11G11B10_FLOAT = 0xE0,
R32_FLOAT = 0xE5,
+ B5G6R5_UNORM = 0xE8,
R16_FLOAT = 0xF2,
R8_UNORM = 0xF3,
};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index c2a931469..546e86532 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -161,7 +161,7 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
// assume every shader uses them all.
for (unsigned index = 0; index < 16; ++index) {
auto& attrib = regs.vertex_attrib_format[index];
- LOG_DEBUG(HW_GPU, "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}",
+ LOG_TRACE(HW_GPU, "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}",
index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(),
attrib.offset.Value(), attrib.IsNormalized());
@@ -324,6 +324,11 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_c
bool using_depth_fb) {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
+ if (regs.rt[0].format == Tegra::RenderTargetFormat::NONE) {
+ LOG_ERROR(HW_GPU, "RenderTargetFormat is not configured");
+ using_color_fb = false;
+ }
+
// TODO(bunnei): Implement this
const bool has_stencil = false;
@@ -659,7 +664,10 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
auto& buffer_draw_state =
state.draw.const_buffers[static_cast<size_t>(stage)][used_buffer.GetIndex()];
- ASSERT_MSG(buffer.enabled, "Attempted to upload disabled constbuffer");
+ if (!buffer.enabled) {
+ continue;
+ }
+
buffer_draw_state.enabled = true;
buffer_draw_state.bindpoint = current_bindpoint + bindpoint;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 257aa9571..f6efce818 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -109,6 +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}, // 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
@@ -218,6 +221,7 @@ 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::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>,
@@ -242,7 +246,10 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
MortonCopy<false, PixelFormat::RGBA16F>,
MortonCopy<false, PixelFormat::R11FG11FB10F>,
MortonCopy<false, PixelFormat::RGBA32UI>,
- // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/BC7U/ASTC_2D_4X4 formats is not supported
+ // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/ASTC_2D_4X4 formats is not
+ // supported
+ nullptr,
+ nullptr,
nullptr,
nullptr,
nullptr,
@@ -447,22 +454,24 @@ MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64
void CachedSurface::LoadGLBuffer() {
ASSERT(params.type != SurfaceType::Fill);
- u8* const texture_src_data = Memory::GetPointer(params.GetCpuAddr());
+ const u8* const texture_src_data = Memory::GetPointer(params.GetCpuAddr());
ASSERT(texture_src_data);
- gl_buffer.resize(params.width * params.height * GetGLBytesPerPixel(params.pixel_format));
+ const u32 bytes_per_pixel = GetGLBytesPerPixel(params.pixel_format);
+ const u32 copy_size = params.width * params.height * bytes_per_pixel;
MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
- if (!params.is_tiled) {
- const u32 bytes_per_pixel{params.GetFormatBpp() >> 3};
+ if (params.is_tiled) {
+ gl_buffer.resize(copy_size);
- std::memcpy(gl_buffer.data(), texture_src_data,
- bytes_per_pixel * params.width * params.height);
- } else {
morton_to_gl_fns[static_cast<size_t>(params.pixel_format)](
params.width, params.block_height, params.height, gl_buffer.data(), params.addr);
+ } else {
+ const u8* const texture_src_data_end = texture_src_data + copy_size;
+
+ gl_buffer.assign(texture_src_data, texture_src_data_end);
}
ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer, params.pixel_format, params.width, params.height);
@@ -757,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 0c6652c7a..26e2ee203 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -35,31 +35,33 @@ struct SurfaceParams {
DXT23 = 9,
DXT45 = 10,
DXN1 = 11, // This is also known as BC4
- BC7U = 12,
- ASTC_2D_4X4 = 13,
- G8R8 = 14,
- BGRA8 = 15,
- RGBA32F = 16,
- RG32F = 17,
- R32F = 18,
- R16F = 19,
- R16UNORM = 20,
- RG16 = 21,
- RG16F = 22,
- RG16UI = 23,
- RG16I = 24,
- RG16S = 25,
- RGB32F = 26,
- SRGBA8 = 27,
+ 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 = 28,
- S8Z24 = 29,
- Z32F = 30,
- Z16 = 31,
- Z32FS8 = 32,
+ Z24S8 = 30,
+ S8Z24 = 31,
+ Z32F = 32,
+ Z16 = 33,
+ Z32FS8 = 34,
MaxDepthStencilFormat,
@@ -109,6 +111,8 @@ struct SurfaceParams {
4, // DXT23
4, // DXT45
4, // DXN1
+ 4, // DXN2UNORM
+ 4, // DXN2SNORM
4, // BC7U
4, // ASTC_2D_4X4
1, // G8R8
@@ -153,6 +157,8 @@ struct SurfaceParams {
128, // DXT23
128, // DXT45
64, // DXN1
+ 128, // DXN2UNORM
+ 128, // DXN2SNORM
128, // BC7U
32, // ASTC_2D_4X4
16, // G8R8
@@ -221,6 +227,8 @@ struct SurfaceParams {
return PixelFormat::RG32F;
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
return PixelFormat::R11FG11FB10F;
+ case Tegra::RenderTargetFormat::B5G6R5_UNORM:
+ return PixelFormat::B5G6R5;
case Tegra::RenderTargetFormat::RGBA32_UINT:
return PixelFormat::RGBA32UI;
case Tegra::RenderTargetFormat::R8_UNORM:
@@ -303,6 +311,16 @@ struct SurfaceParams {
return PixelFormat::DXT45;
case Tegra::Texture::TextureFormat::DXN1:
return PixelFormat::DXN1;
+ case Tegra::Texture::TextureFormat::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:
@@ -360,6 +378,9 @@ struct SurfaceParams {
return Tegra::Texture::TextureFormat::DXT45;
case PixelFormat::DXN1:
return Tegra::Texture::TextureFormat::DXN1;
+ case PixelFormat::DXN2UNORM:
+ case PixelFormat::DXN2SNORM:
+ return Tegra::Texture::TextureFormat::DXN2;
case PixelFormat::BC7U:
return Tegra::Texture::TextureFormat::BC7U;
case PixelFormat::ASTC_2D_4X4:
@@ -441,6 +462,7 @@ struct SurfaceParams {
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
case Tegra::RenderTargetFormat::R8_UNORM:
case Tegra::RenderTargetFormat::RG16_UNORM:
+ case Tegra::RenderTargetFormat::B5G6R5_UNORM:
return ComponentType::UNorm;
case Tegra::RenderTargetFormat::RG16_SNORM:
return ComponentType::SNorm;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index e3217db81..32f06f409 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -507,6 +507,8 @@ private:
/// Build the GLSL register list.
void BuildRegisterList() {
+ regs.reserve(Register::NumRegisters);
+
for (size_t index = 0; index < Register::NumRegisters; ++index) {
regs.emplace_back(index, suffix);
}
@@ -523,6 +525,11 @@ private:
// shader.
ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex);
return "vec4(0, 0, uintBitsToFloat(gl_InstanceID), uintBitsToFloat(gl_VertexID))";
+ case Attribute::Index::Unknown_63:
+ // TODO(bunnei): Figure out what this is used for. Super Mario Odyssey uses this.
+ LOG_CRITICAL(HW_GPU, "Unhandled input attribute Unknown_63");
+ UNREACHABLE();
+ break;
default:
const u32 index{static_cast<u32>(attribute) -
static_cast<u32>(Attribute::Index::Attribute_0)};
@@ -534,6 +541,8 @@ private:
LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", index);
UNREACHABLE();
}
+
+ return "vec4(0, 0, 0, 0)";
}
/// Generates code representing an output attribute register.
@@ -602,12 +611,12 @@ private:
/// Generates code representing a 19-bit immediate value
static std::string GetImmediate19(const Instruction& instr) {
- return std::to_string(instr.alu.GetImm20_19());
+ return fmt::format("uintBitsToFloat({})", instr.alu.GetImm20_19());
}
/// Generates code representing a 32-bit immediate value
static std::string GetImmediate32(const Instruction& instr) {
- return std::to_string(instr.alu.GetImm20_32());
+ return fmt::format("uintBitsToFloat({})", instr.alu.GetImm20_32());
}
/// Generates code representing a texture sampler.
@@ -650,16 +659,17 @@ private:
* @param instr Instruction to generate the if condition for.
* @returns string containing the predicate condition.
*/
- std::string GetPredicateCondition(u64 index, bool negate) const {
+ std::string GetPredicateCondition(u64 index, bool negate) {
using Tegra::Shader::Pred;
std::string variable;
// Index 7 is used as an 'Always True' condition.
- if (index == static_cast<u64>(Pred::UnusedIndex))
+ if (index == static_cast<u64>(Pred::UnusedIndex)) {
variable = "true";
- else
+ } else {
variable = 'p' + std::to_string(index) + '_' + suffix;
-
+ declr_predicates.insert(variable);
+ }
if (negate) {
return "!(" + variable + ')';
}
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 24b1d956b..5c7b636e4 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -7,6 +7,10 @@
#include <array>
#include <glad/glad.h>
+#include "video_core/engines/maxwell_3d.h"
+
+using Regs = Tegra::Engines::Maxwell3D::Regs;
+
namespace TextureUnits {
struct TextureUnit {
@@ -120,7 +124,7 @@ public:
GLuint bindpoint;
GLuint ssbo;
};
- std::array<std::array<ConstBufferConfig, 16>, 5> const_buffers{};
+ std::array<std::array<ConstBufferConfig, Regs::MaxConstBuffers>, 5> const_buffers;
} draw;
struct {
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 16b1bd606..43be69dd1 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -27,9 +27,11 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
case Maxwell::VertexAttribute::Type::UnsignedNorm: {
switch (attrib.size) {
+ case Maxwell::VertexAttribute::Size::Size_8_8:
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
return GL_UNSIGNED_BYTE;
case Maxwell::VertexAttribute::Size::Size_16_16:
+ case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
return GL_UNSIGNED_SHORT;
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
return GL_UNSIGNED_INT_2_10_10_10_REV;
@@ -84,6 +86,8 @@ inline GLenum IndexFormat(Maxwell::IndexFormat index_format) {
inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
switch (topology) {
+ case Maxwell::PrimitiveTopology::Points:
+ return GL_POINTS;
case Maxwell::PrimitiveTopology::Triangles:
return GL_TRIANGLES;
case Maxwell::PrimitiveTopology::TriangleStrip:
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index bf9131193..899865e3b 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -430,7 +430,7 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum
break;
case GL_DEBUG_SEVERITY_NOTIFICATION:
case GL_DEBUG_SEVERITY_LOW:
- LOG_DEBUG(Render_OpenGL, format, str_source, str_type, id, message);
+ LOG_TRACE(Render_OpenGL, format, str_source, str_type, id, message);
break;
}
}
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 65db84ad3..7ea66584c 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -54,6 +54,7 @@ u32 BytesPerPixel(TextureFormat format) {
return 8;
case TextureFormat::DXT23:
case TextureFormat::DXT45:
+ case TextureFormat::DXN2:
case TextureFormat::BC7U:
// In this case a 'pixel' actually refers to a 4x4 tile.
return 16;
@@ -113,6 +114,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width,
case TextureFormat::DXT23:
case TextureFormat::DXT45:
case TextureFormat::DXN1:
+ case TextureFormat::DXN2:
case TextureFormat::BC7U:
// In the DXT and DXN formats, each 4x4 tile is swizzled instead of just individual pixel
// values.
@@ -179,6 +181,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
case TextureFormat::DXT23:
case TextureFormat::DXT45:
case TextureFormat::DXN1:
+ case TextureFormat::DXN2:
case TextureFormat::BC7U:
case TextureFormat::ASTC_2D_4X4:
case TextureFormat::A8R8G8B8:
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(); });