Compare commits
49 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef41983c84 | ||
|
|
6828c25498 | ||
|
|
5191c20b71 | ||
|
|
a91bb7080d | ||
|
|
69cd213fac | ||
|
|
05c33d89a1 | ||
|
|
96ef22d3d0 | ||
|
|
e8c52d4c89 | ||
|
|
37e1ed3744 | ||
|
|
75e12a33ae | ||
|
|
e58f7ff843 | ||
|
|
6ef027b958 | ||
|
|
59ea37daa7 | ||
|
|
0bfe974281 | ||
|
|
88b18b9ba4 | ||
|
|
b125137493 | ||
|
|
f765a6b902 | ||
|
|
5cb6eceecf | ||
|
|
b46a5c42ff | ||
|
|
ff5024ee2a | ||
|
|
5c908c0373 | ||
|
|
c333bfc193 | ||
|
|
e831b80d69 | ||
|
|
434f352eb3 | ||
|
|
5b1b758326 | ||
|
|
dfc3eed0cb | ||
|
|
06d0b96ca9 | ||
|
|
efe6b473c5 | ||
|
|
557c466994 | ||
|
|
668458525e | ||
|
|
94cf327e77 | ||
|
|
2b6128fe0b | ||
|
|
dad2ae1ee0 | ||
|
|
656e97df16 | ||
|
|
52a2e42cb9 | ||
|
|
4b471f0554 | ||
|
|
aaa8fdea52 | ||
|
|
2de2ec25d6 | ||
|
|
3f82dad1e4 | ||
|
|
3bf488ce52 | ||
|
|
b36dee364e | ||
|
|
ddec200290 | ||
|
|
7353cfc781 | ||
|
|
4afb05d0cc | ||
|
|
6e90f0bf6a | ||
|
|
5a9c00ea04 | ||
|
|
76197a4be9 | ||
|
|
cc9d7bbf01 | ||
|
|
aaf8d9ac2f |
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 4f96c63025...0118ee04f9
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -171,15 +171,21 @@ void FileBackend::Write(const Entry& entry) {
|
||||
SUB(Service, ARP) \
|
||||
SUB(Service, BCAT) \
|
||||
SUB(Service, BPC) \
|
||||
SUB(Service, BTDRV) \
|
||||
SUB(Service, BTM) \
|
||||
SUB(Service, Capture) \
|
||||
SUB(Service, ERPT) \
|
||||
SUB(Service, ETicket) \
|
||||
SUB(Service, EUPLD) \
|
||||
SUB(Service, Fatal) \
|
||||
SUB(Service, FGM) \
|
||||
SUB(Service, Friend) \
|
||||
SUB(Service, FS) \
|
||||
SUB(Service, GRC) \
|
||||
SUB(Service, HID) \
|
||||
SUB(Service, LBL) \
|
||||
SUB(Service, LDN) \
|
||||
SUB(Service, LDR) \
|
||||
SUB(Service, LM) \
|
||||
SUB(Service, Migration) \
|
||||
SUB(Service, Mii) \
|
||||
@@ -188,11 +194,13 @@ void FileBackend::Write(const Entry& entry) {
|
||||
SUB(Service, NFC) \
|
||||
SUB(Service, NFP) \
|
||||
SUB(Service, NIFM) \
|
||||
SUB(Service, NIM) \
|
||||
SUB(Service, NS) \
|
||||
SUB(Service, NVDRV) \
|
||||
SUB(Service, PCIE) \
|
||||
SUB(Service, PCTL) \
|
||||
SUB(Service, PCV) \
|
||||
SUB(Service, PM) \
|
||||
SUB(Service, PREPO) \
|
||||
SUB(Service, PSC) \
|
||||
SUB(Service, SET) \
|
||||
|
||||
@@ -58,15 +58,21 @@ enum class Class : ClassType {
|
||||
Service_Audio, ///< The Audio (Audio control) service
|
||||
Service_BCAT, ///< The BCAT service
|
||||
Service_BPC, ///< The BPC service
|
||||
Service_BTDRV, ///< The Bluetooth driver service
|
||||
Service_BTM, ///< The BTM service
|
||||
Service_Capture, ///< The capture service
|
||||
Service_ERPT, ///< The error reporting service
|
||||
Service_ETicket, ///< The ETicket service
|
||||
Service_EUPLD, ///< The error upload service
|
||||
Service_Fatal, ///< The Fatal service
|
||||
Service_FGM, ///< The FGM service
|
||||
Service_Friend, ///< The friend service
|
||||
Service_FS, ///< The FS (Filesystem) service
|
||||
Service_GRC, ///< The game recording service
|
||||
Service_HID, ///< The HID (Human interface device) service
|
||||
Service_LBL, ///< The LBL (LCD backlight) service
|
||||
Service_LDN, ///< The LDN (Local domain network) service
|
||||
Service_LDR, ///< The loader service
|
||||
Service_LM, ///< The LM (Logger) service
|
||||
Service_Migration, ///< The migration service
|
||||
Service_Mii, ///< The Mii service
|
||||
@@ -75,11 +81,13 @@ enum class Class : ClassType {
|
||||
Service_NFC, ///< The NFC (Near-field communication) service
|
||||
Service_NFP, ///< The NFP service
|
||||
Service_NIFM, ///< The NIFM (Network interface) service
|
||||
Service_NIM, ///< The NIM service
|
||||
Service_NS, ///< The NS services
|
||||
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
|
||||
Service_PCIE, ///< The PCIe service
|
||||
Service_PCTL, ///< The PCTL (Parental control) service
|
||||
Service_PCV, ///< The PCV service
|
||||
Service_PM, ///< The PM service
|
||||
Service_PREPO, ///< The PREPO (Play report) service
|
||||
Service_PSC, ///< The PSC service
|
||||
Service_SET, ///< The SET (Settings) service
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
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());
|
||||
bool RealVfsFile::Close() {
|
||||
return backing->Close();
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -201,7 +201,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) {
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
|
||||
Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
|
||||
HandleTable& src_table);
|
||||
/// Writes data from this context back to the requesting process/thread.
|
||||
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
|
||||
ResultCode WriteToOutgoingCommandBuffer(const Thread& thread);
|
||||
|
||||
u32_le GetCommand() const {
|
||||
return command;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
|
||||
Kernel::Event::Create(Kernel::ResetType::Sticky, "BufferQueue NativeHandle");
|
||||
}
|
||||
|
||||
void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
|
||||
void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
|
||||
Buffer buffer{};
|
||||
buffer.slot = slot;
|
||||
buffer.igbp_buffer = igbp_buffer;
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
MathUtil::Rectangle<int> crop_rect;
|
||||
};
|
||||
|
||||
void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
|
||||
void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
|
||||
boost::optional<u32> DequeueBuffer(u32 width, u32 height);
|
||||
const IGBPBuffer& RequestBuffer(u32 slot) const;
|
||||
void QueueBuffer(u32 slot, BufferTransformFlags transform,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -42,14 +42,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
|
||||
|
||||
@@ -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);
|
||||
// The requested macro must have been uploaded already.
|
||||
ASSERT_MSG(macro_code != uploaded_macros.end(), "Macro %08X was not uploaded", method);
|
||||
|
||||
// Reset the current macro and execute it.
|
||||
// Reset the current macro.
|
||||
executing_macro = 0;
|
||||
|
||||
// The requested macro must have been uploaded already.
|
||||
auto macro_code = uploaded_macros.find(method);
|
||||
if (macro_code == uploaded_macros.end()) {
|
||||
LOG_ERROR(HW_GPU, "Macro {:04X} was not uploaded", method);
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute the current macro.
|
||||
macro_interpreter.Execute(macro_code->second, std::move(parameters));
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -181,35 +184,37 @@ MathUtil::Rectangle<u32> SurfaceParams::GetRect() const {
|
||||
}
|
||||
|
||||
template <bool morton_to_gl, PixelFormat format>
|
||||
void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, Tegra::GPUVAddr addr) {
|
||||
void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_buffer,
|
||||
Tegra::GPUVAddr addr) {
|
||||
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
|
||||
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
|
||||
const auto& gpu = Core::System::GetInstance().GPU();
|
||||
|
||||
if (morton_to_gl) {
|
||||
std::vector<u8> data;
|
||||
if (SurfaceParams::GetFormatType(format) == SurfaceType::ColorTexture) {
|
||||
auto data = Tegra::Texture::UnswizzleTexture(
|
||||
data = Tegra::Texture::UnswizzleTexture(
|
||||
*gpu.memory_manager->GpuToCpuAddress(addr),
|
||||
SurfaceParams::TextureFormatFromPixelFormat(format), stride, height, block_height);
|
||||
std::memcpy(gl_buffer, data.data(), data.size());
|
||||
} else {
|
||||
auto data = Tegra::Texture::UnswizzleDepthTexture(
|
||||
data = Tegra::Texture::UnswizzleDepthTexture(
|
||||
*gpu.memory_manager->GpuToCpuAddress(addr),
|
||||
SurfaceParams::DepthFormatFromPixelFormat(format), stride, height, block_height);
|
||||
std::memcpy(gl_buffer, data.data(), data.size());
|
||||
}
|
||||
const size_t size_to_copy{std::min(gl_buffer.size(), data.size())};
|
||||
gl_buffer.assign(data.begin(), data.begin() + size_to_copy);
|
||||
} else {
|
||||
// TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should
|
||||
// check the configuration for this and perform more generic un/swizzle
|
||||
LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
|
||||
VideoCore::MortonCopyPixels128(
|
||||
stride, height, bytes_per_pixel, gl_bytes_per_pixel,
|
||||
Memory::GetPointer(*gpu.memory_manager->GpuToCpuAddress(addr)), gl_buffer,
|
||||
Memory::GetPointer(*gpu.memory_manager->GpuToCpuAddress(addr)), gl_buffer.data(),
|
||||
morton_to_gl);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
|
||||
static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr),
|
||||
SurfaceParams::MaxPixelFormat>
|
||||
morton_to_gl_fns = {
|
||||
MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>,
|
||||
@@ -218,6 +223,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>,
|
||||
@@ -231,7 +237,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
|
||||
MortonCopy<true, PixelFormat::Z32FS8>,
|
||||
};
|
||||
|
||||
static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
|
||||
static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr),
|
||||
SurfaceParams::MaxPixelFormat>
|
||||
gl_to_morton_fns = {
|
||||
MortonCopy<false, PixelFormat::ABGR8>,
|
||||
@@ -242,7 +248,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 +456,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);
|
||||
params.width, params.block_height, params.height, gl_buffer, 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);
|
||||
@@ -485,7 +496,7 @@ void CachedSurface::FlushGLBuffer() {
|
||||
std::memcpy(dst_buffer, gl_buffer.data(), params.size_in_bytes);
|
||||
} else {
|
||||
gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
|
||||
params.width, params.block_height, params.height, gl_buffer.data(), params.addr);
|
||||
params.width, params.block_height, params.height, gl_buffer, params.addr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -757,10 +768,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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
|
||||
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;
|
||||
@@ -85,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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(); });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user