summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--src/common/assert.h2
-rw-r--r--src/common/file_util.cpp93
-rw-r--r--src/common/file_util.h63
-rw-r--r--src/core/CMakeLists.txt18
-rw-r--r--src/core/core.cpp3
-rw-r--r--src/core/file_sys/content_archive.cpp164
-rw-r--r--src/core/file_sys/content_archive.h89
-rw-r--r--src/core/file_sys/disk_filesystem.cpp237
-rw-r--r--src/core/file_sys/disk_filesystem.h84
-rw-r--r--src/core/file_sys/filesystem.h132
-rw-r--r--src/core/file_sys/partition_filesystem.cpp136
-rw-r--r--src/core/file_sys/partition_filesystem.h29
-rw-r--r--src/core/file_sys/program_metadata.cpp43
-rw-r--r--src/core/file_sys/program_metadata.h6
-rw-r--r--src/core/file_sys/romfs_factory.cpp38
-rw-r--r--src/core/file_sys/romfs_factory.h35
-rw-r--r--src/core/file_sys/romfs_filesystem.cpp110
-rw-r--r--src/core/file_sys/romfs_filesystem.h85
-rw-r--r--src/core/file_sys/savedata_factory.cpp63
-rw-r--r--src/core/file_sys/savedata_factory.h33
-rw-r--r--src/core/file_sys/sdmc_factory.cpp39
-rw-r--r--src/core/file_sys/sdmc_factory.h31
-rw-r--r--src/core/file_sys/vfs.cpp187
-rw-r--r--src/core/file_sys/vfs.h220
-rw-r--r--src/core/file_sys/vfs_offset.cpp92
-rw-r--r--src/core/file_sys/vfs_offset.h46
-rw-r--r--src/core/file_sys/vfs_real.cpp168
-rw-r--r--src/core/file_sys/vfs_real.h65
-rw-r--r--src/core/hle/service/am/am.cpp2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp225
-rw-r--r--src/core/hle/service/filesystem/filesystem.h133
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp114
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp2
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp113
-rw-r--r--src/core/loader/deconstructed_rom_directory.h14
-rw-r--r--src/core/loader/elf.cpp24
-rw-r--r--src/core/loader/elf.h12
-rw-r--r--src/core/loader/loader.cpp63
-rw-r--r--src/core/loader/loader.h30
-rw-r--r--src/core/loader/nca.cpp252
-rw-r--r--src/core/loader/nca.h19
-rw-r--r--src/core/loader/nro.cpp32
-rw-r--r--src/core/loader/nro.h13
-rw-r--r--src/core/loader/nso.cpp91
-rw-r--r--src/core/loader/nso.h17
-rw-r--r--src/video_core/engines/shader_bytecode.h20
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h15
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp14
-rw-r--r--src/video_core/textures/decoders.cpp3
-rw-r--r--src/yuzu/bootmanager.cpp13
-rw-r--r--src/yuzu/game_list.cpp4
56 files changed, 1752 insertions, 1804 deletions
diff --git a/.gitignore b/.gitignore
index 7999a40e1..5ec0d110b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@ src/common/scm_rev.cpp
.idea/
.vs/
.vscode/
+CMakeLists.txt.user
# *nix related
# Common convention for backup or temporary files
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ce0608db2..008dc5b50 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,6 +1,6 @@
# Reporting Issues
-**The issue tracker is not a support forum.** Unless you can provide precise *technical information* regarding an issue, you *should not post in it*. If you need support, first read the [FAQ](https://github.com/yuzu-emu/yuzu/wiki/FAQ) and then either visit our Discord server, [our forum](https://community.citra-emu.org) or ask in a general emulation forum such as [/r/emulation](https://www.reddit.com/r/emulation/). If you post support questions, generic messages to the developers or vague reports without technical details, they will be closed and locked.
+**The issue tracker is not a support forum.** Unless you can provide precise *technical information* regarding an issue, you *should not post in it*. If you need support, first read the [FAQ](https://github.com/yuzu-emu/yuzu/wiki/FAQ) and then either visit our [Discord server](https://discordapp.com/invite/u77vRWY), [our forum](https://community.citra-emu.org) or ask in a general emulation forum such as [/r/emulation](https://www.reddit.com/r/emulation/). If you post support questions, generic messages to the developers or vague reports without technical details, they will be closed and locked.
If you believe you have a valid issue report, please post text or a screenshot from the log (the console window that opens alongside yuzu) and build version (hex string visible in the titlebar and zip filename), as well as your hardware and software information if applicable.
diff --git a/src/common/assert.h b/src/common/assert.h
index 655446f34..0d4eddc19 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -52,5 +52,5 @@ __declspec(noinline, noreturn)
#define DEBUG_ASSERT_MSG(_a_, _desc_, ...)
#endif
-#define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!")
+#define UNIMPLEMENTED() LOG_CRITICAL(Debug, "Unimplemented code!")
#define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 493a81e01..7213abe18 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <sstream>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_paths.h"
@@ -387,7 +386,7 @@ u64 GetSize(FILE* f) {
bool CreateEmptyFile(const std::string& filename) {
LOG_TRACE(Common_Filesystem, "{}", filename);
- if (!FileUtil::IOFile(filename, "wb").IsOpen()) {
+ if (!FileUtil::IOFile(filename, "wb")) {
LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg());
return false;
}
@@ -751,7 +750,7 @@ size_t WriteStringToFile(bool text_file, const std::string& str, const char* fil
size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
IOFile file(filename, text_file ? "r" : "rb");
- if (!file.IsOpen())
+ if (!file)
return false;
str.resize(static_cast<u32>(file.GetSize()));
@@ -800,57 +799,6 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
}
}
-std::vector<std::string> SplitPathComponents(const std::string& filename) {
- auto copy(filename);
- std::replace(copy.begin(), copy.end(), '\\', '/');
- std::vector<std::string> out;
-
- std::stringstream stream(filename);
- std::string item;
- while (std::getline(stream, item, '/'))
- out.push_back(std::move(item));
-
- return out;
-}
-
-std::string GetParentPath(const std::string& path) {
- auto out = path;
- const auto name_bck_index = out.find_last_of('\\');
- const auto name_fwd_index = out.find_last_of('/');
- size_t name_index;
- if (name_bck_index == std::string::npos || name_fwd_index == std::string::npos)
- name_index = std::min<size_t>(name_bck_index, name_fwd_index);
- else
- name_index = std::max<size_t>(name_bck_index, name_fwd_index);
-
- return out.erase(name_index);
-}
-
-std::string GetFilename(std::string path) {
- std::replace(path.begin(), path.end(), '\\', '/');
- auto name_index = path.find_last_of('/');
- if (name_index == std::string::npos)
- return "";
- return path.substr(name_index + 1);
-}
-
-std::string GetExtensionFromFilename(const std::string& name) {
- size_t index = name.find_last_of('.');
- if (index == std::string::npos)
- return "";
-
- return name.substr(index + 1);
-}
-
-std::string RemoveTrailingSlash(const std::string& path) {
- if (path.empty())
- return path;
- if (path.back() == '\\' || path.back() == '/')
- return path.substr(0, path.size() - 1);
-
- return path;
-}
-
IOFile::IOFile() {}
IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
@@ -872,6 +820,7 @@ IOFile& IOFile::operator=(IOFile&& other) noexcept {
void IOFile::Swap(IOFile& other) noexcept {
std::swap(m_file, other.m_file);
+ std::swap(m_good, other.m_good);
}
bool IOFile::Open(const std::string& filename, const char openmode[], int flags) {
@@ -888,15 +837,16 @@ bool IOFile::Open(const std::string& filename, const char openmode[], int flags)
m_file = fopen(filename.c_str(), openmode);
#endif
- return IsOpen();
+ m_good = IsOpen();
+ return m_good;
}
bool IOFile::Close() {
if (!IsOpen() || 0 != std::fclose(m_file))
- return false;
+ m_good = false;
m_file = nullptr;
- return true;
+ return m_good;
}
u64 IOFile::GetSize() const {
@@ -906,8 +856,11 @@ u64 IOFile::GetSize() const {
return 0;
}
-bool IOFile::Seek(s64 off, int origin) const {
- return IsOpen() && 0 == fseeko(m_file, off, origin);
+bool IOFile::Seek(s64 off, int origin) {
+ if (!IsOpen() || 0 != fseeko(m_file, off, origin))
+ m_good = false;
+
+ return m_good;
}
u64 IOFile::Tell() const {
@@ -918,20 +871,26 @@ u64 IOFile::Tell() const {
}
bool IOFile::Flush() {
- return IsOpen() && 0 == std::fflush(m_file);
+ if (!IsOpen() || 0 != std::fflush(m_file))
+ m_good = false;
+
+ return m_good;
}
bool IOFile::Resize(u64 size) {
- return IsOpen() && 0 ==
+ if (!IsOpen() || 0 !=
#ifdef _WIN32
- // ector: _chsize sucks, not 64-bit safe
- // F|RES: changed to _chsize_s. i think it is 64-bit safe
- _chsize_s(_fileno(m_file), size)
+ // ector: _chsize sucks, not 64-bit safe
+ // F|RES: changed to _chsize_s. i think it is 64-bit safe
+ _chsize_s(_fileno(m_file), size)
#else
- // TODO: handle 64bit and growing
- ftruncate(fileno(m_file), size)
+ // TODO: handle 64bit and growing
+ ftruncate(fileno(m_file), size)
#endif
- ;
+ )
+ m_good = false;
+
+ return m_good;
}
} // namespace FileUtil
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 9bb3c4109..5bc7fbf7c 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -150,31 +150,6 @@ size_t ReadFileToString(bool text_file, const char* filename, std::string& str);
void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
std::array<char, 4>& extension);
-// Splits the path on '/' or '\' and put the components into a vector
-// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
-std::vector<std::string> SplitPathComponents(const std::string& filename);
-
-// Gets all of the text prior to the last '/' or '\' in the path.
-std::string GetParentPath(const std::string& path);
-
-// Gets the filename of the path
-std::string GetFilename(std::string path);
-
-// Gets the extension of the filename
-std::string GetExtensionFromFilename(const std::string& name);
-
-// Removes the final '/' or '\' if one exists
-std::string RemoveTrailingSlash(const std::string& path);
-
-// Creates a new vector containing indices [first, last) from the original.
-template <typename T>
-std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t last) {
- if (first >= last)
- return {};
- last = std::min<size_t>(last, vector.size());
- return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
-}
-
// simple wrapper for cstdlib file functions to
// hopefully will make error checking easier
// and make forgetting an fclose() harder
@@ -197,27 +172,41 @@ public:
bool Close();
template <typename T>
- size_t ReadArray(T* data, size_t length) const {
+ size_t ReadArray(T* data, size_t length) {
static_assert(std::is_trivially_copyable<T>(),
"Given array does not consist of trivially copyable objects");
- if (!IsOpen())
+ if (!IsOpen()) {
+ m_good = false;
return -1;
+ }
- return std::fread(data, sizeof(T), length, m_file);
+ size_t items_read = std::fread(data, sizeof(T), length, m_file);
+ if (items_read != length)
+ m_good = false;
+
+ return items_read;
}
template <typename T>
size_t WriteArray(const T* data, size_t length) {
static_assert(std::is_trivially_copyable<T>(),
"Given array does not consist of trivially copyable objects");
- if (!IsOpen())
+
+ if (!IsOpen()) {
+ m_good = false;
return -1;
- return std::fwrite(data, sizeof(T), length, m_file);
+ }
+
+ size_t items_written = std::fwrite(data, sizeof(T), length, m_file);
+ if (items_written != length)
+ m_good = false;
+
+ return items_written;
}
template <typename T>
- size_t ReadBytes(T* data, size_t length) const {
+ size_t ReadBytes(T* data, size_t length) {
static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable");
return ReadArray(reinterpret_cast<char*>(data), length);
}
@@ -242,7 +231,15 @@ public:
return nullptr != m_file;
}
- bool Seek(s64 off, int origin) const;
+ // m_good is set to false when a read, write or other function fails
+ bool IsGood() const {
+ return m_good;
+ }
+ explicit operator bool() const {
+ return IsGood();
+ }
+
+ bool Seek(s64 off, int origin);
u64 Tell() const;
u64 GetSize() const;
bool Resize(u64 size);
@@ -250,11 +247,13 @@ public:
// clear error state
void Clear() {
+ m_good = true;
std::clearerr(m_file);
}
private:
std::FILE* m_file = nullptr;
+ bool m_good = true;
};
} // namespace FileUtil
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 582294a57..3dff068df 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -8,9 +8,9 @@ add_library(core STATIC
core_cpu.h
core_timing.cpp
core_timing.h
- file_sys/content_archive.cpp
- file_sys/content_archive.h
file_sys/directory.h
+ file_sys/disk_filesystem.cpp
+ file_sys/disk_filesystem.h
file_sys/errors.h
file_sys/filesystem.cpp
file_sys/filesystem.h
@@ -20,13 +20,15 @@ add_library(core STATIC
file_sys/path_parser.h
file_sys/program_metadata.cpp
file_sys/program_metadata.h
+ file_sys/romfs_factory.cpp
+ file_sys/romfs_factory.h
+ file_sys/romfs_filesystem.cpp
+ file_sys/romfs_filesystem.h
+ file_sys/savedata_factory.cpp
+ file_sys/savedata_factory.h
+ file_sys/sdmc_factory.cpp
+ file_sys/sdmc_factory.h
file_sys/storage.h
- file_sys/vfs.cpp
- file_sys/vfs.h
- file_sys/vfs_offset.cpp
- file_sys/vfs_offset.h
- file_sys/vfs_real.cpp
- file_sys/vfs_real.h
frontend/emu_window.cpp
frontend/emu_window.h
frontend/framebuffer_layout.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 82db5cccf..8335d502e 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -19,7 +19,6 @@
#include "core/loader/loader.h"
#include "core/memory_setup.h"
#include "core/settings.h"
-#include "file_sys/vfs_real.h"
#include "video_core/video_core.h"
namespace Core {
@@ -85,7 +84,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(filepath);
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
deleted file mode 100644
index b45b83a26..000000000
--- a/src/core/file_sys/content_archive.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/logging/log.h"
-#include "core/file_sys/content_archive.h"
-#include "core/file_sys/vfs_offset.h"
-#include "core/loader/loader.h"
-
-namespace FileSys {
-
-// Media offsets in headers are stored divided by 512. Mult. by this to get real offset.
-constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200;
-
-constexpr u64 SECTION_HEADER_SIZE = 0x200;
-constexpr u64 SECTION_HEADER_OFFSET = 0x400;
-
-constexpr u32 IVFC_MAX_LEVEL = 6;
-
-enum class NCASectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 };
-
-struct NCASectionHeaderBlock {
- INSERT_PADDING_BYTES(3);
- NCASectionFilesystemType filesystem_type;
- u8 crypto_type;
- INSERT_PADDING_BYTES(3);
-};
-static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
-
-struct PFS0Superblock {
- NCASectionHeaderBlock header_block;
- std::array<u8, 0x20> hash;
- u32_le size;
- INSERT_PADDING_BYTES(4);
- u64_le hash_table_offset;
- u64_le hash_table_size;
- u64_le pfs0_header_offset;
- u64_le pfs0_size;
- INSERT_PADDING_BYTES(432);
-};
-static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
-
-struct IVFCLevel {
- u64_le offset;
- u64_le size;
- u32_le block_size;
- u32_le reserved;
-};
-static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
-
-struct RomFSSuperblock {
- NCASectionHeaderBlock header_block;
- u32_le magic;
- u32_le magic_number;
- INSERT_PADDING_BYTES(8);
- std::array<IVFCLevel, 6> levels;
- INSERT_PADDING_BYTES(64);
-};
-static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size.");
-
-NCA::NCA(VirtualFile file_) : file(file_) {
- if (sizeof(NCAHeader) != file->ReadObject(&header))
- LOG_CRITICAL(Loader, "File reader errored out during header read.");
-
- if (!IsValidNCA(header)) {
- status = Loader::ResultStatus::ErrorInvalidFormat;
- return;
- }
-
- std::ptrdiff_t number_sections =
- std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
- [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
-
- for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
- // Seek to beginning of this section.
- NCASectionHeaderBlock block{};
- if (sizeof(NCASectionHeaderBlock) !=
- file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
- LOG_CRITICAL(Loader, "File reader errored out during header read.");
-
- if (block.filesystem_type == NCASectionFilesystemType::ROMFS) {
- RomFSSuperblock sb{};
- if (sizeof(RomFSSuperblock) !=
- file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
- LOG_CRITICAL(Loader, "File reader errored out during header read.");
-
- const size_t romfs_offset =
- header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER +
- sb.levels[IVFC_MAX_LEVEL - 1].offset;
- const size_t romfs_size = sb.levels[IVFC_MAX_LEVEL - 1].size;
- files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset));
- romfs = files.back();
- } else if (block.filesystem_type == NCASectionFilesystemType::PFS0) {
- PFS0Superblock sb{};
- // Seek back to beginning of this section.
- if (sizeof(PFS0Superblock) !=
- file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
- LOG_CRITICAL(Loader, "File reader errored out during header read.");
-
- u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
- MEDIA_OFFSET_MULTIPLIER) +
- sb.pfs0_header_offset;
- u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
- header.section_tables[i].media_offset);
- auto npfs = std::make_shared<PartitionFilesystem>(
- std::make_shared<OffsetVfsFile>(file, size, offset));
-
- if (npfs->GetStatus() == Loader::ResultStatus::Success) {
- dirs.emplace_back(npfs);
- if (IsDirectoryExeFS(dirs.back()))
- exefs = dirs.back();
- }
- }
- }
-
- status = Loader::ResultStatus::Success;
-}
-
-Loader::ResultStatus NCA::GetStatus() const {
- return status;
-}
-
-std::vector<std::shared_ptr<VfsFile>> NCA::GetFiles() const {
- if (status != Loader::ResultStatus::Success)
- return {};
- return files;
-}
-
-std::vector<std::shared_ptr<VfsDirectory>> NCA::GetSubdirectories() const {
- if (status != Loader::ResultStatus::Success)
- return {};
- return dirs;
-}
-
-std::string NCA::GetName() const {
- return file->GetName();
-}
-
-std::shared_ptr<VfsDirectory> NCA::GetParentDirectory() const {
- return file->GetContainingDirectory();
-}
-
-NCAContentType NCA::GetType() const {
- return header.content_type;
-}
-
-u64 NCA::GetTitleId() const {
- if (status != Loader::ResultStatus::Success)
- return {};
- return header.title_id;
-}
-
-VirtualFile NCA::GetRomFS() const {
- return romfs;
-}
-
-VirtualDir NCA::GetExeFS() const {
- return exefs;
-}
-
-bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
- return false;
-}
-} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
deleted file mode 100644
index eb4ca1c18..000000000
--- a/src/core/file_sys/content_archive.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/common_funcs.h"
-#include "common/common_types.h"
-#include "common/swap.h"
-#include "core/file_sys/partition_filesystem.h"
-
-namespace FileSys {
-
-enum class NCAContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 };
-
-struct NCASectionTableEntry {
- u32_le media_offset;
- u32_le media_end_offset;
- INSERT_PADDING_BYTES(0x8);
-};
-static_assert(sizeof(NCASectionTableEntry) == 0x10, "NCASectionTableEntry has incorrect size.");
-
-struct NCAHeader {
- std::array<u8, 0x100> rsa_signature_1;
- std::array<u8, 0x100> rsa_signature_2;
- u32_le magic;
- u8 is_system;
- NCAContentType content_type;
- u8 crypto_type;
- u8 key_index;
- u64_le size;
- u64_le title_id;
- INSERT_PADDING_BYTES(0x4);
- u32_le sdk_version;
- u8 crypto_type_2;
- INSERT_PADDING_BYTES(15);
- std::array<u8, 0x10> rights_id;
- std::array<NCASectionTableEntry, 0x4> section_tables;
- std::array<std::array<u8, 0x20>, 0x4> hash_tables;
- std::array<std::array<u8, 0x10>, 0x4> key_area;
- INSERT_PADDING_BYTES(0xC0);
-};
-static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size.");
-
-static bool IsDirectoryExeFS(std::shared_ptr<FileSys::VfsDirectory> pfs) {
- // According to switchbrew, an exefs must only contain these two files:
- return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
-}
-
-static bool IsValidNCA(const NCAHeader& header) {
- return header.magic == Common::MakeMagic('N', 'C', 'A', '2') ||
- header.magic == Common::MakeMagic('N', 'C', 'A', '3');
-}
-
-// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
-// After construction, use GetStatus to determine if the file is valid and ready to be used.
-class NCA : public ReadOnlyVfsDirectory {
-public:
- explicit NCA(VirtualFile file);
- Loader::ResultStatus GetStatus() const;
-
- std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
- std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
- std::string GetName() const override;
- std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
-
- NCAContentType GetType() const;
- u64 GetTitleId() const;
-
- VirtualFile GetRomFS() const;
- VirtualDir GetExeFS() const;
-
-protected:
- bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
-
-private:
- std::vector<VirtualDir> dirs;
- std::vector<VirtualFile> files;
-
- VirtualFile romfs = nullptr;
- VirtualDir exefs = nullptr;
- VirtualFile file;
-
- NCAHeader header{};
-
- Loader::ResultStatus status{};
-};
-
-} // namespace FileSys
diff --git a/src/core/file_sys/disk_filesystem.cpp b/src/core/file_sys/disk_filesystem.cpp
new file mode 100644
index 000000000..8c6f15bb5
--- /dev/null
+++ b/src/core/file_sys/disk_filesystem.cpp
@@ -0,0 +1,237 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include <memory>
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "core/file_sys/disk_filesystem.h"
+#include "core/file_sys/errors.h"
+
+namespace FileSys {
+
+static std::string ModeFlagsToString(Mode mode) {
+ std::string mode_str;
+ u32 mode_flags = static_cast<u32>(mode);
+
+ // Calculate the correct open mode for the file.
+ if ((mode_flags & static_cast<u32>(Mode::Read)) &&
+ (mode_flags & static_cast<u32>(Mode::Write))) {
+ if (mode_flags & static_cast<u32>(Mode::Append))
+ mode_str = "a+";
+ else
+ mode_str = "r+";
+ } else {
+ if (mode_flags & static_cast<u32>(Mode::Read))
+ mode_str = "r";
+ else if (mode_flags & static_cast<u32>(Mode::Append))
+ mode_str = "a";
+ else if (mode_flags & static_cast<u32>(Mode::Write))
+ mode_str = "w";
+ }
+
+ mode_str += "b";
+
+ return mode_str;
+}
+
+std::string Disk_FileSystem::GetName() const {
+ return "Disk";
+}
+
+ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::string& path,
+ Mode mode) const {
+
+ // Calculate the correct open mode for the file.
+ std::string mode_str = ModeFlagsToString(mode);
+
+ std::string full_path = base_directory + path;
+ auto file = std::make_shared<FileUtil::IOFile>(full_path, mode_str.c_str());
+
+ if (!file->IsOpen()) {
+ return ERROR_PATH_NOT_FOUND;
+ }
+
+ return MakeResult<std::unique_ptr<StorageBackend>>(
+ std::make_unique<Disk_Storage>(std::move(file)));
+}
+
+ResultCode Disk_FileSystem::DeleteFile(const std::string& path) const {
+ if (!FileUtil::Exists(path)) {
+ return ERROR_PATH_NOT_FOUND;
+ }
+
+ FileUtil::Delete(path);
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode Disk_FileSystem::RenameFile(const std::string& src_path,
+ const std::string& dest_path) const {
+ const std::string full_src_path = base_directory + src_path;
+ const std::string full_dest_path = base_directory + dest_path;
+
+ if (!FileUtil::Exists(full_src_path)) {
+ return ERROR_PATH_NOT_FOUND;
+ }
+ // TODO(wwylele): Use correct error code
+ return FileUtil::Rename(full_src_path, full_dest_path) ? RESULT_SUCCESS : ResultCode(-1);
+}
+
+ResultCode Disk_FileSystem::DeleteDirectory(const Path& path) const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultCode Disk_FileSystem::DeleteDirectoryRecursively(const Path& path) const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+
+ std::string full_path = base_directory + path;
+ if (size == 0) {
+ FileUtil::CreateEmptyFile(full_path);
+ return RESULT_SUCCESS;
+ }
+
+ FileUtil::IOFile file(full_path, "wb");
+ // Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
+ // We do this by seeking to the right size, then writing a single null byte.
+ if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_ERROR(Service_FS, "Too large file");
+ // TODO(Subv): Find out the correct error code
+ return ResultCode(-1);
+}
+
+ResultCode Disk_FileSystem::CreateDirectory(const std::string& path) const {
+ // TODO(Subv): Perform path validation to prevent escaping the emulator sandbox.
+ std::string full_path = base_directory + path;
+
+ if (FileUtil::CreateDir(full_path)) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating {}", full_path);
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory(
+ const std::string& path) const {
+
+ std::string full_path = base_directory + path;
+
+ if (!FileUtil::IsDirectory(full_path)) {
+ // TODO(Subv): Find the correct error code for this.
+ return ResultCode(-1);
+ }
+
+ auto directory = std::make_unique<Disk_Directory>(full_path);
+ return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
+}
+
+u64 Disk_FileSystem::GetFreeSpaceSize() const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ return 0;
+}
+
+ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& path) const {
+ std::string full_path = base_directory + path;
+ if (!FileUtil::Exists(full_path)) {
+ return ERROR_PATH_NOT_FOUND;
+ }
+
+ if (FileUtil::IsDirectory(full_path))
+ return MakeResult(EntryType::Directory);
+
+ return MakeResult(EntryType::File);
+}
+
+ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
+ LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length);
+ file->Seek(offset, SEEK_SET);
+ return MakeResult<size_t>(file->ReadBytes(buffer, length));
+}
+
+ResultVal<size_t> Disk_Storage::Write(const u64 offset, const size_t length, const bool flush,
+ const u8* buffer) const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ file->Seek(offset, SEEK_SET);
+ size_t written = file->WriteBytes(buffer, length);
+ if (flush) {
+ file->Flush();
+ }
+ return MakeResult<size_t>(written);
+}
+
+u64 Disk_Storage::GetSize() const {
+ return file->GetSize();
+}
+
+bool Disk_Storage::SetSize(const u64 size) const {
+ file->Resize(size);
+ file->Flush();
+ return true;
+}
+
+Disk_Directory::Disk_Directory(const std::string& path) {
+ unsigned size = FileUtil::ScanDirectoryTree(path, directory);
+ directory.size = size;
+ directory.isDirectory = true;
+ children_iterator = directory.children.begin();
+}
+
+u64 Disk_Directory::Read(const u64 count, Entry* entries) {
+ u64 entries_read = 0;
+
+ while (entries_read < count && children_iterator != directory.children.cend()) {
+ const FileUtil::FSTEntry& file = *children_iterator;
+ const std::string& filename = file.virtualName;
+ Entry& entry = entries[entries_read];
+
+ LOG_TRACE(Service_FS, "File {}: size={} dir={}", filename, file.size, file.isDirectory);
+
+ // TODO(Link Mauve): use a proper conversion to UTF-16.
+ for (size_t j = 0; j < FILENAME_LENGTH; ++j) {
+ entry.filename[j] = filename[j];
+ if (!filename[j])
+ break;
+ }
+
+ if (file.isDirectory) {
+ entry.file_size = 0;
+ entry.type = EntryType::Directory;
+ } else {
+ entry.file_size = file.size;
+ entry.type = EntryType::File;
+ }
+
+ ++entries_read;
+ ++children_iterator;
+ }
+ return entries_read;
+}
+
+u64 Disk_Directory::GetEntryCount() const {
+ // We convert the children iterator into a const_iterator to allow template argument deduction
+ // in std::distance.
+ std::vector<FileUtil::FSTEntry>::const_iterator current = children_iterator;
+ return std::distance(current, directory.children.end());
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/disk_filesystem.h b/src/core/file_sys/disk_filesystem.h
new file mode 100644
index 000000000..591e39fda
--- /dev/null
+++ b/src/core/file_sys/disk_filesystem.h
@@ -0,0 +1,84 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <memory>
+#include <string>
+#include "common/common_types.h"
+#include "common/file_util.h"
+#include "core/file_sys/directory.h"
+#include "core/file_sys/filesystem.h"
+#include "core/file_sys/storage.h"
+#include "core/hle/result.h"
+
+namespace FileSys {
+
+class Disk_FileSystem : public FileSystemBackend {
+public:
+ explicit Disk_FileSystem(std::string base_directory)
+ : base_directory(std::move(base_directory)) {}
+
+ std::string GetName() const override;
+
+ ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
+ Mode mode) const override;
+ ResultCode DeleteFile(const std::string& path) const override;
+ ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const override;
+ ResultCode DeleteDirectory(const Path& path) const override;
+ ResultCode DeleteDirectoryRecursively(const Path& path) const override;
+ ResultCode CreateFile(const std::string& path, u64 size) const override;
+ ResultCode CreateDirectory(const std::string& path) const override;
+ ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
+ ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
+ const std::string& path) const override;
+ u64 GetFreeSpaceSize() const override;
+ ResultVal<EntryType> GetEntryType(const std::string& path) const override;
+
+protected:
+ std::string base_directory;
+};
+
+class Disk_Storage : public StorageBackend {
+public:
+ explicit Disk_Storage(std::shared_ptr<FileUtil::IOFile> file) : file(std::move(file)) {}
+
+ ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
+ ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
+ u64 GetSize() const override;
+ bool SetSize(u64 size) const override;
+ bool Close() const override {
+ return false;
+ }
+ void Flush() const override {}
+
+private:
+ std::shared_ptr<FileUtil::IOFile> file;
+};
+
+class Disk_Directory : public DirectoryBackend {
+public:
+ explicit Disk_Directory(const std::string& path);
+
+ ~Disk_Directory() override {
+ Close();
+ }
+
+ u64 Read(const u64 count, Entry* entries) override;
+ u64 GetEntryCount() const override;
+
+ bool Close() const override {
+ return true;
+ }
+
+protected:
+ FileUtil::FSTEntry directory;
+
+ // We need to remember the last entry we returned, so a subsequent call to Read will continue
+ // from the next one. This iterator will always point to the next unread entry.
+ std::vector<FileUtil::FSTEntry>::iterator children_iterator;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/filesystem.h b/src/core/file_sys/filesystem.h
index 2d925d9e4..295a3133e 100644
--- a/src/core/file_sys/filesystem.h
+++ b/src/core/file_sys/filesystem.h
@@ -66,4 +66,136 @@ private:
std::u16string u16str;
};
+/// Parameters of the archive, as specified in the Create or Format call.
+struct ArchiveFormatInfo {
+ u32_le total_size; ///< The pre-defined size of the archive.
+ u32_le number_directories; ///< The pre-defined number of directories in the archive.
+ u32_le number_files; ///< The pre-defined number of files in the archive.
+ u8 duplicate_data; ///< Whether the archive should duplicate the data.
+};
+static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD");
+
+class FileSystemBackend : NonCopyable {
+public:
+ virtual ~FileSystemBackend() {}
+
+ /**
+ * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
+ */
+ virtual std::string GetName() const = 0;
+
+ /**
+ * Create a file specified by its path
+ * @param path Path relative to the Archive
+ * @param size The size of the new file, filled with zeroes
+ * @return Result of the operation
+ */
+ virtual ResultCode CreateFile(const std::string& path, u64 size) const = 0;
+
+ /**
+ * Delete a file specified by its path
+ * @param path Path relative to the archive
+ * @return Result of the operation
+ */
+ virtual ResultCode DeleteFile(const std::string& path) const = 0;
+
+ /**
+ * Create a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Result of the operation
+ */
+ virtual ResultCode CreateDirectory(const std::string& path) const = 0;
+
+ /**
+ * Delete a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Result of the operation
+ */
+ virtual ResultCode DeleteDirectory(const Path& path) const = 0;
+
+ /**
+ * Delete a directory specified by its path and anything under it
+ * @param path Path relative to the archive
+ * @return Result of the operation
+ */
+ virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0;
+
+ /**
+ * Rename a File specified by its path
+ * @param src_path Source path relative to the archive
+ * @param dest_path Destination path relative to the archive
+ * @return Result of the operation
+ */
+ virtual ResultCode RenameFile(const std::string& src_path,
+ const std::string& dest_path) const = 0;
+
+ /**
+ * Rename a Directory specified by its path
+ * @param src_path Source path relative to the archive
+ * @param dest_path Destination path relative to the archive
+ * @return Result of the operation
+ */
+ virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0;
+
+ /**
+ * Open a file specified by its path, using the specified mode
+ * @param path Path relative to the archive
+ * @param mode Mode to open the file with
+ * @return Opened file, or error code
+ */
+ virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
+ Mode mode) const = 0;
+
+ /**
+ * Open a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Opened directory, or error code
+ */
+ virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
+ const std::string& path) const = 0;
+
+ /**
+ * Get the free space
+ * @return The number of free bytes in the archive
+ */
+ virtual u64 GetFreeSpaceSize() const = 0;
+
+ /**
+ * Get the type of the specified path
+ * @return The type of the specified path or error code
+ */
+ virtual ResultVal<EntryType> GetEntryType(const std::string& path) const = 0;
+};
+
+class FileSystemFactory : NonCopyable {
+public:
+ virtual ~FileSystemFactory() {}
+
+ /**
+ * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
+ */
+ virtual std::string GetName() const = 0;
+
+ /**
+ * Tries to open the archive of this type with the specified path
+ * @param path Path to the archive
+ * @return An ArchiveBackend corresponding operating specified archive path.
+ */
+ virtual ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) = 0;
+
+ /**
+ * Deletes the archive contents and then re-creates the base folder
+ * @param path Path to the archive
+ * @return ResultCode of the operation, 0 on success
+ */
+ virtual ResultCode Format(const Path& path) = 0;
+
+ /**
+ * Retrieves the format info about the archive with the specified path
+ * @param path Path to the archive
+ * @return Format information about the archive or error code
+ */
+ virtual ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const = 0;
+};
+
} // namespace FileSys
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp
index 15b1fb946..46d438aca 100644
--- a/src/core/file_sys/partition_filesystem.cpp
+++ b/src/core/file_sys/partition_filesystem.cpp
@@ -6,30 +6,29 @@
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/partition_filesystem.h"
-#include "core/file_sys/vfs_offset.h"
#include "core/loader/loader.h"
namespace FileSys {
-PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
+Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, size_t offset) {
+ FileUtil::IOFile file(file_path, "rb");
+ if (!file.IsOpen())
+ return Loader::ResultStatus::Error;
+
// At least be as large as the header
- if (file->GetSize() < sizeof(Header)) {
- status = Loader::ResultStatus::Error;
- return;
- }
+ if (file.GetSize() < sizeof(Header))
+ return Loader::ResultStatus::Error;
+ file.Seek(offset, SEEK_SET);
// For cartridges, HFSs can get very large, so we need to calculate the size up to
// the actual content itself instead of just blindly reading in the entire file.
Header pfs_header;
- if (sizeof(Header) != file->ReadObject(&pfs_header)) {
- status = Loader::ResultStatus::Error;
- return;
- }
+ if (!file.ReadBytes(&pfs_header, sizeof(Header)))
+ return Loader::ResultStatus::Error;
if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') &&
pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) {
- status = Loader::ResultStatus::ErrorInvalidFormat;
- return;
+ return Loader::ResultStatus::ErrorInvalidFormat;
}
bool is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
@@ -39,86 +38,99 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size;
// Actually read in now...
- std::vector<u8> file_data = file->ReadBytes(metadata_size);
+ file.Seek(offset, SEEK_SET);
+ std::vector<u8> file_data(metadata_size);
- if (file_data.size() != metadata_size) {
- status = Loader::ResultStatus::Error;
- return;
- }
+ if (!file.ReadBytes(file_data.data(), metadata_size))
+ return Loader::ResultStatus::Error;
- size_t total_size = file_data.size();
- if (total_size < sizeof(Header)) {
- status = Loader::ResultStatus::Error;
- return;
- }
+ Loader::ResultStatus result = Load(file_data);
+ if (result != Loader::ResultStatus::Success)
+ LOG_ERROR(Service_FS, "Failed to load PFS from file {}!", file_path);
+
+ return result;
+}
- memcpy(&pfs_header, file_data.data(), sizeof(Header));
+Loader::ResultStatus PartitionFilesystem::Load(const std::vector<u8>& file_data, size_t offset) {
+ size_t total_size = file_data.size() - offset;
+ if (total_size < sizeof(Header))
+ return Loader::ResultStatus::Error;
+
+ memcpy(&pfs_header, &file_data[offset], sizeof(Header));
if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') &&
pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) {
- status = Loader::ResultStatus::ErrorInvalidFormat;
- return;
+ return Loader::ResultStatus::ErrorInvalidFormat;
}
is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
- size_t entries_offset = sizeof(Header);
+ size_t entries_offset = offset + sizeof(Header);
+ size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry);
size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size);
- content_offset = strtab_offset + pfs_header.strtab_size;
for (u16 i = 0; i < pfs_header.num_entries; i++) {
- FSEntry entry;
-
- memcpy(&entry, &file_data[entries_offset + (i * entry_size)], sizeof(FSEntry));
- std::string name(
- reinterpret_cast<const char*>(&file_data[strtab_offset + entry.strtab_offset]));
+ FileEntry entry;
- pfs_files.emplace_back(
- std::make_shared<OffsetVfsFile>(file, entry.size, content_offset + entry.offset, name));
+ memcpy(&entry.fs_entry, &file_data[entries_offset + (i * entry_size)], sizeof(FSEntry));
+ entry.name = std::string(reinterpret_cast<const char*>(
+ &file_data[strtab_offset + entry.fs_entry.strtab_offset]));
+ pfs_entries.push_back(std::move(entry));
}
- status = Loader::ResultStatus::Success;
-}
+ content_offset = strtab_offset + pfs_header.strtab_size;
-Loader::ResultStatus PartitionFilesystem::GetStatus() const {
- return status;
+ return Loader::ResultStatus::Success;
}
-std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const {
- return pfs_files;
+u32 PartitionFilesystem::GetNumEntries() const {
+ return pfs_header.num_entries;
}
-std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const {
- return {};
+u64 PartitionFilesystem::GetEntryOffset(u32 index) const {
+ if (index > GetNumEntries())
+ return 0;
+
+ return content_offset + pfs_entries[index].fs_entry.offset;
}
-std::string PartitionFilesystem::GetName() const {
- return is_hfs ? "HFS0" : "PFS0";
+u64 PartitionFilesystem::GetEntrySize(u32 index) const {
+ if (index > GetNumEntries())
+ return 0;
+
+ return pfs_entries[index].fs_entry.size;
}
-std::shared_ptr<VfsDirectory> PartitionFilesystem::GetParentDirectory() const {
- // TODO(DarkLordZach): Add support for nested containers.
- return nullptr;
+std::string PartitionFilesystem::GetEntryName(u32 index) const {
+ if (index > GetNumEntries())
+ return "";
+
+ return pfs_entries[index].name;
}
-void PartitionFilesystem::PrintDebugInfo() const {
- LOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic);
- LOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries);
+u64 PartitionFilesystem::GetFileOffset(const std::string& name) const {
for (u32 i = 0; i < pfs_header.num_entries; i++) {
- LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i,
- pfs_files[i]->GetName(), pfs_files[i]->GetSize(),
- dynamic_cast<OffsetVfsFile*>(pfs_files[i].get())->GetOffset());
+ if (pfs_entries[i].name == name)
+ return content_offset + pfs_entries[i].fs_entry.offset;
}
-}
-bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
- auto iter = std::find(pfs_files.begin(), pfs_files.end(), file);
- if (iter == pfs_files.end())
- return false;
+ return 0;
+}
- pfs_files[iter - pfs_files.begin()] = pfs_files.back();
- pfs_files.pop_back();
+u64 PartitionFilesystem::GetFileSize(const std::string& name) const {
+ for (u32 i = 0; i < pfs_header.num_entries; i++) {
+ if (pfs_entries[i].name == name)
+ return pfs_entries[i].fs_entry.size;
+ }
- pfs_dirs.emplace_back(dir);
+ return 0;
+}
- return true;
+void PartitionFilesystem::Print() const {
+ LOG_DEBUG(Service_FS, "Magic: {}", pfs_header.magic);
+ LOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries);
+ for (u32 i = 0; i < pfs_header.num_entries; i++) {
+ LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i,
+ pfs_entries[i].name.c_str(), pfs_entries[i].fs_entry.size,
+ GetFileOffset(pfs_entries[i].name));
+ }
}
} // namespace FileSys
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h
index 9656b40bf..9c5810cf1 100644
--- a/src/core/file_sys/partition_filesystem.h
+++ b/src/core/file_sys/partition_filesystem.h
@@ -10,7 +10,6 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
-#include "core/file_sys/vfs.h"
namespace Loader {
enum class ResultStatus;
@@ -22,19 +21,19 @@ namespace FileSys {
* Helper which implements an interface to parse PFS/HFS filesystems.
* Data can either be loaded from a file path or data with an offset into it.
*/
-class PartitionFilesystem : public ReadOnlyVfsDirectory {
+class PartitionFilesystem {
public:
- explicit PartitionFilesystem(std::shared_ptr<VfsFile> file);
- Loader::ResultStatus GetStatus() const;
+ Loader::ResultStatus Load(const std::string& file_path, size_t offset = 0);
+ Loader::ResultStatus Load(const std::vector<u8>& file_data, size_t offset = 0);
- std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
- std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
- std::string GetName() const override;
- std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
- void PrintDebugInfo() const;
+ u32 GetNumEntries() const;
+ u64 GetEntryOffset(u32 index) const;
+ u64 GetEntrySize(u32 index) const;
+ std::string GetEntryName(u32 index) const;
+ u64 GetFileOffset(const std::string& name) const;
+ u64 GetFileSize(const std::string& name) const;
-protected:
- bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
+ void Print() const;
private:
struct Header {
@@ -73,14 +72,16 @@ private:
#pragma pack(pop)
- Loader::ResultStatus status;
+ struct FileEntry {
+ FSEntry fs_entry;
+ std::string name;
+ };
Header pfs_header;
bool is_hfs;
size_t content_offset;
- std::vector<VirtualFile> pfs_files;
- std::vector<VirtualDir> pfs_dirs;
+ std::vector<FileEntry> pfs_entries;
};
} // namespace FileSys
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 63d4b6e4f..226811115 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -9,30 +9,41 @@
namespace FileSys {
-Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
- size_t total_size = static_cast<size_t>(file->GetSize());
- if (total_size < sizeof(Header))
+Loader::ResultStatus ProgramMetadata::Load(const std::string& file_path) {
+ FileUtil::IOFile file(file_path, "rb");
+ if (!file.IsOpen())
return Loader::ResultStatus::Error;
- // TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable.
- std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header));
- if (sizeof(Header) != npdm_header_data.size())
- return Loader::ResultStatus::Error;
- std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header));
+ std::vector<u8> file_data(file.GetSize());
- std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset);
- if (sizeof(AcidHeader) != acid_header_data.size())
+ if (!file.ReadBytes(file_data.data(), file_data.size()))
return Loader::ResultStatus::Error;
- std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader));
- if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset))
- return Loader::ResultStatus::Error;
+ Loader::ResultStatus result = Load(file_data);
+ if (result != Loader::ResultStatus::Success)
+ LOG_ERROR(Service_FS, "Failed to load NPDM from file {}!", file_path);
- if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset))
- return Loader::ResultStatus::Error;
- if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
+ return result;
+}
+
+Loader::ResultStatus ProgramMetadata::Load(const std::vector<u8> file_data, size_t offset) {
+ size_t total_size = static_cast<size_t>(file_data.size() - offset);
+ if (total_size < sizeof(Header))
return Loader::ResultStatus::Error;
+ size_t header_offset = offset;
+ memcpy(&npdm_header, &file_data[offset], sizeof(Header));
+
+ size_t aci_offset = header_offset + npdm_header.aci_offset;
+ size_t acid_offset = header_offset + npdm_header.acid_offset;
+ memcpy(&aci_header, &file_data[aci_offset], sizeof(AciHeader));
+ memcpy(&acid_header, &file_data[acid_offset], sizeof(AcidHeader));
+
+ size_t fac_offset = acid_offset + acid_header.fac_offset;
+ size_t fah_offset = aci_offset + aci_header.fah_offset;
+ memcpy(&acid_file_access, &file_data[fac_offset], sizeof(FileAccessControl));
+ memcpy(&aci_file_access, &file_data[fah_offset], sizeof(FileAccessHeader));
+
return Loader::ResultStatus::Success;
}
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 06a7315db..b80a08485 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -10,7 +10,6 @@
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
-#include "partition_filesystem.h"
namespace Loader {
enum class ResultStatus;
@@ -38,7 +37,8 @@ enum class ProgramFilePermission : u64 {
*/
class ProgramMetadata {
public:
- Loader::ResultStatus Load(VirtualFile file);
+ Loader::ResultStatus Load(const std::string& file_path);
+ Loader::ResultStatus Load(const std::vector<u8> file_data, size_t offset = 0);
bool Is64BitProgram() const;
ProgramAddressSpaceType GetAddressSpaceType() const;
@@ -51,7 +51,6 @@ public:
void Print() const;
private:
- // TODO(DarkLordZach): BitField is not trivially copyable.
struct Header {
std::array<char, 4> magic;
std::array<u8, 8> reserved;
@@ -78,7 +77,6 @@ private:
static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong");
- // TODO(DarkLordZach): BitField is not trivially copyable.
struct AcidHeader {
std::array<u8, 0x100> signature;
std::array<u8, 0x100> nca_modulus;
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
new file mode 100644
index 000000000..84ae0d99b
--- /dev/null
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -0,0 +1,38 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <memory>
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "core/file_sys/romfs_factory.h"
+#include "core/file_sys/romfs_filesystem.h"
+
+namespace FileSys {
+
+RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) {
+ // Load the RomFS from the app
+ if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) {
+ LOG_ERROR(Service_FS, "Unable to read RomFS!");
+ }
+}
+
+ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& path) {
+ auto archive = std::make_unique<RomFS_FileSystem>(romfs_file, data_offset, data_size);
+ return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
+}
+
+ResultCode RomFS_Factory::Format(const Path& path) {
+ LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName());
+ // TODO(bunnei): Find the right error code for this
+ return ResultCode(-1);
+}
+
+ResultVal<ArchiveFormatInfo> RomFS_Factory::GetFormatInfo(const Path& path) const {
+ LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName());
+ // TODO(bunnei): Find the right error code for this
+ return ResultCode(-1);
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h
new file mode 100644
index 000000000..e0698e642
--- /dev/null
+++ b/src/core/file_sys/romfs_factory.h
@@ -0,0 +1,35 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <vector>
+#include "common/common_types.h"
+#include "core/file_sys/filesystem.h"
+#include "core/hle/result.h"
+#include "core/loader/loader.h"
+
+namespace FileSys {
+
+/// File system interface to the RomFS archive
+class RomFS_Factory final : public FileSystemFactory {
+public:
+ explicit RomFS_Factory(Loader::AppLoader& app_loader);
+
+ std::string GetName() const override {
+ return "ArchiveFactory_RomFS";
+ }
+ ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
+ ResultCode Format(const Path& path) override;
+ ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
+
+private:
+ std::shared_ptr<FileUtil::IOFile> romfs_file;
+ u64 data_offset;
+ u64 data_size;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/romfs_filesystem.cpp b/src/core/file_sys/romfs_filesystem.cpp
new file mode 100644
index 000000000..83162622b
--- /dev/null
+++ b/src/core/file_sys/romfs_filesystem.cpp
@@ -0,0 +1,110 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include <memory>
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "core/file_sys/romfs_filesystem.h"
+
+namespace FileSys {
+
+std::string RomFS_FileSystem::GetName() const {
+ return "RomFS";
+}
+
+ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const std::string& path,
+ Mode mode) const {
+ return MakeResult<std::unique_ptr<StorageBackend>>(
+ std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size));
+}
+
+ResultCode RomFS_FileSystem::DeleteFile(const std::string& path) const {
+ LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive ({}).", GetName());
+ // TODO(bunnei): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultCode RomFS_FileSystem::RenameFile(const std::string& src_path,
+ const std::string& dest_path) const {
+ LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName());
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const {
+ LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).",
+ GetName());
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const {
+ LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).",
+ GetName());
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const {
+ LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive ({}).", GetName());
+ // TODO(bunnei): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultCode RomFS_FileSystem::CreateDirectory(const std::string& path) const {
+ LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive ({}).",
+ GetName());
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const {
+ LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName());
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory(
+ const std::string& path) const {
+ LOG_WARNING(Service_FS, "Opening Directory in a ROMFS archive");
+ return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>());
+}
+
+u64 RomFS_FileSystem::GetFreeSpaceSize() const {
+ LOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive");
+ return 0;
+}
+
+ResultVal<FileSys::EntryType> RomFS_FileSystem::GetEntryType(const std::string& path) const {
+ LOG_CRITICAL(Service_FS, "Called within an ROMFS archive (path {}).", path);
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
+ LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length);
+ romfs_file->Seek(data_offset + offset, SEEK_SET);
+ size_t read_length = (size_t)std::min((u64)length, data_size - offset);
+
+ return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length));
+}
+
+ResultVal<size_t> RomFS_Storage::Write(const u64 offset, const size_t length, const bool flush,
+ const u8* buffer) const {
+ LOG_ERROR(Service_FS, "Attempted to write to ROMFS file");
+ // TODO(Subv): Find error code
+ return MakeResult<size_t>(0);
+}
+
+u64 RomFS_Storage::GetSize() const {
+ return data_size;
+}
+
+bool RomFS_Storage::SetSize(const u64 size) const {
+ LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file");
+ return false;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/romfs_filesystem.h b/src/core/file_sys/romfs_filesystem.h
new file mode 100644
index 000000000..ba9d85823
--- /dev/null
+++ b/src/core/file_sys/romfs_filesystem.h
@@ -0,0 +1,85 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <vector>
+#include "common/common_types.h"
+#include "common/file_util.h"
+#include "core/file_sys/directory.h"
+#include "core/file_sys/filesystem.h"
+#include "core/file_sys/storage.h"
+#include "core/hle/result.h"
+
+namespace FileSys {
+
+/**
+ * Helper which implements an interface to deal with Switch .istorage ROMFS images used in some
+ * archives This should be subclassed by concrete archive types, which will provide the input data
+ * (load the raw ROMFS archive) and override any required methods
+ */
+class RomFS_FileSystem : public FileSystemBackend {
+public:
+ RomFS_FileSystem(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
+ : romfs_file(file), data_offset(offset), data_size(size) {}
+
+ std::string GetName() const override;
+
+ ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
+ Mode mode) const override;
+ ResultCode DeleteFile(const std::string& path) const override;
+ ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const override;
+ ResultCode DeleteDirectory(const Path& path) const override;
+ ResultCode DeleteDirectoryRecursively(const Path& path) const override;
+ ResultCode CreateFile(const std::string& path, u64 size) const override;
+ ResultCode CreateDirectory(const std::string& path) const override;
+ ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
+ ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
+ const std::string& path) const override;
+ u64 GetFreeSpaceSize() const override;
+ ResultVal<EntryType> GetEntryType(const std::string& path) const override;
+
+protected:
+ std::shared_ptr<FileUtil::IOFile> romfs_file;
+ u64 data_offset;
+ u64 data_size;
+};
+
+class RomFS_Storage : public StorageBackend {
+public:
+ RomFS_Storage(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
+ : romfs_file(file), data_offset(offset), data_size(size) {}
+
+ ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
+ ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
+ u64 GetSize() const override;
+ bool SetSize(u64 size) const override;
+ bool Close() const override {
+ return false;
+ }
+ void Flush() const override {}
+
+private:
+ std::shared_ptr<FileUtil::IOFile> romfs_file;
+ u64 data_offset;
+ u64 data_size;
+};
+
+class ROMFSDirectory : public DirectoryBackend {
+public:
+ u64 Read(const u64 count, Entry* entries) override {
+ return 0;
+ }
+ u64 GetEntryCount() const override {
+ return 0;
+ }
+ bool Close() const override {
+ return false;
+ }
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
new file mode 100644
index 000000000..f3aa213af
--- /dev/null
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -0,0 +1,63 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/file_sys/disk_filesystem.h"
+#include "core/file_sys/savedata_factory.h"
+#include "core/hle/kernel/process.h"
+
+namespace FileSys {
+
+SaveData_Factory::SaveData_Factory(std::string nand_directory)
+ : nand_directory(std::move(nand_directory)) {}
+
+ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path& path) {
+ std::string save_directory = GetFullPath();
+
+ if (!FileUtil::Exists(save_directory)) {
+ // TODO(bunnei): This is a work-around to always create a save data directory if it does not
+ // already exist. This is a hack, as we do not understand yet how this works on hardware.
+ // Without a save data directory, many games will assert on boot. This should not have any
+ // bad side-effects.
+ FileUtil::CreateFullPath(save_directory);
+ }
+
+ // Return an error if the save data doesn't actually exist.
+ if (!FileUtil::IsDirectory(save_directory)) {
+ // TODO(Subv): Find out correct error code.
+ return ResultCode(-1);
+ }
+
+ auto archive = std::make_unique<Disk_FileSystem>(save_directory);
+ return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
+}
+
+ResultCode SaveData_Factory::Format(const Path& path) {
+ LOG_WARNING(Service_FS, "Format archive {}", GetName());
+ // Create the save data directory.
+ if (!FileUtil::CreateFullPath(GetFullPath())) {
+ // TODO(Subv): Find the correct error code.
+ return ResultCode(-1);
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultVal<ArchiveFormatInfo> SaveData_Factory::GetFormatInfo(const Path& path) const {
+ LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName());
+ // TODO(bunnei): Find the right error code for this
+ return ResultCode(-1);
+}
+
+std::string SaveData_Factory::GetFullPath() const {
+ u64 title_id = Core::CurrentProcess()->program_id;
+ // TODO(Subv): Somehow obtain this value.
+ u32 user = 0;
+ return fmt::format("{}save/{:016X}/{:08X}/", nand_directory, title_id, user);
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
new file mode 100644
index 000000000..73a42aab6
--- /dev/null
+++ b/src/core/file_sys/savedata_factory.h
@@ -0,0 +1,33 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include "common/common_types.h"
+#include "core/file_sys/filesystem.h"
+#include "core/hle/result.h"
+
+namespace FileSys {
+
+/// File system interface to the SaveData archive
+class SaveData_Factory final : public FileSystemFactory {
+public:
+ explicit SaveData_Factory(std::string nand_directory);
+
+ std::string GetName() const override {
+ return "SaveData_Factory";
+ }
+ ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
+ ResultCode Format(const Path& path) override;
+ ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
+
+private:
+ std::string nand_directory;
+
+ std::string GetFullPath() const;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
new file mode 100644
index 000000000..2e5ffb764
--- /dev/null
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -0,0 +1,39 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/file_sys/disk_filesystem.h"
+#include "core/file_sys/sdmc_factory.h"
+
+namespace FileSys {
+
+SDMC_Factory::SDMC_Factory(std::string sd_directory) : sd_directory(std::move(sd_directory)) {}
+
+ResultVal<std::unique_ptr<FileSystemBackend>> SDMC_Factory::Open(const Path& path) {
+ // Create the SD Card directory if it doesn't already exist.
+ if (!FileUtil::IsDirectory(sd_directory)) {
+ FileUtil::CreateFullPath(sd_directory);
+ }
+
+ auto archive = std::make_unique<Disk_FileSystem>(sd_directory);
+ return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
+}
+
+ResultCode SDMC_Factory::Format(const Path& path) {
+ LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName());
+ // TODO(Subv): Find the right error code for this
+ return ResultCode(-1);
+}
+
+ResultVal<ArchiveFormatInfo> SDMC_Factory::GetFormatInfo(const Path& path) const {
+ LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName());
+ // TODO(bunnei): Find the right error code for this
+ return ResultCode(-1);
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
new file mode 100644
index 000000000..93becda25
--- /dev/null
+++ b/src/core/file_sys/sdmc_factory.h
@@ -0,0 +1,31 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include "common/common_types.h"
+#include "core/file_sys/filesystem.h"
+#include "core/hle/result.h"
+
+namespace FileSys {
+
+/// File system interface to the SDCard archive
+class SDMC_Factory final : public FileSystemFactory {
+public:
+ explicit SDMC_Factory(std::string sd_directory);
+
+ std::string GetName() const override {
+ return "SDMC_Factory";
+ }
+ ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
+ ResultCode Format(const Path& path) override;
+ ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
+
+private:
+ std::string sd_directory;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
deleted file mode 100644
index c99071d6a..000000000
--- a/src/core/file_sys/vfs.cpp
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <numeric>
-#include "common/file_util.h"
-#include "core/file_sys/vfs.h"
-
-namespace FileSys {
-
-VfsFile::~VfsFile() = default;
-
-std::string VfsFile::GetExtension() const {
- return FileUtil::GetExtensionFromFilename(GetName());
-}
-
-VfsDirectory::~VfsDirectory() = default;
-
-boost::optional<u8> VfsFile::ReadByte(size_t offset) const {
- u8 out{};
- size_t size = Read(&out, 1, offset);
- if (size == 1)
- return out;
-
- return boost::none;
-}
-
-std::vector<u8> VfsFile::ReadBytes(size_t size, size_t offset) const {
- std::vector<u8> out(size);
- size_t read_size = Read(out.data(), size, offset);
- out.resize(read_size);
- return out;
-}
-
-std::vector<u8> VfsFile::ReadAllBytes() const {
- return ReadBytes(GetSize());
-}
-
-bool VfsFile::WriteByte(u8 data, size_t offset) {
- return Write(&data, 1, offset) == 1;
-}
-
-size_t VfsFile::WriteBytes(std::vector<u8> data, size_t offset) {
- return Write(data.data(), data.size(), offset);
-}
-
-std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(const std::string& path) const {
- auto vec = FileUtil::SplitPathComponents(path);
- vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
- vec.end());
- if (vec.empty())
- return nullptr;
- if (vec.size() == 1)
- return GetFile(vec[0]);
- auto dir = GetSubdirectory(vec[0]);
- for (size_t i = 1; i < vec.size() - 1; ++i) {
- if (dir == nullptr)
- return nullptr;
- dir = dir->GetSubdirectory(vec[i]);
- }
- if (dir == nullptr)
- return nullptr;
- return dir->GetFile(vec.back());
-}
-
-std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(const std::string& path) const {
- if (IsRoot())
- return GetFileRelative(path);
-
- return GetParentDirectory()->GetFileAbsolute(path);
-}
-
-std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(const std::string& path) const {
- auto vec = FileUtil::SplitPathComponents(path);
- vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
- vec.end());
- if (vec.empty())
- // return std::shared_ptr<VfsDirectory>(this);
- return nullptr;
- auto dir = GetSubdirectory(vec[0]);
- for (size_t i = 1; i < vec.size(); ++i) {
- dir = dir->GetSubdirectory(vec[i]);
- }
- return dir;
-}
-
-std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(const std::string& path) const {
- if (IsRoot())
- return GetDirectoryRelative(path);
-
- return GetParentDirectory()->GetDirectoryAbsolute(path);
-}
-
-std::shared_ptr<VfsFile> VfsDirectory::GetFile(const std::string& name) const {
- const auto& files = GetFiles();
- const auto iter = std::find_if(files.begin(), files.end(),
- [&name](const auto& file1) { return name == file1->GetName(); });
- return iter == files.end() ? nullptr : *iter;
-}
-
-std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(const std::string& name) const {
- const auto& subs = GetSubdirectories();
- const auto iter = std::find_if(subs.begin(), subs.end(),
- [&name](const auto& file1) { return name == file1->GetName(); });
- return iter == subs.end() ? nullptr : *iter;
-}
-
-bool VfsDirectory::IsRoot() const {
- return GetParentDirectory() == nullptr;
-}
-
-size_t VfsDirectory::GetSize() const {
- const auto& files = GetFiles();
- const auto file_total =
- std::accumulate(files.begin(), files.end(), 0ull,
- [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); });
-
- const auto& sub_dir = GetSubdirectories();
- const auto subdir_total =
- std::accumulate(sub_dir.begin(), sub_dir.end(), 0ull,
- [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); });
-
- return file_total + subdir_total;
-}
-
-bool VfsDirectory::DeleteSubdirectoryRecursive(const std::string& name) {
- auto dir = GetSubdirectory(name);
- if (dir == nullptr)
- return false;
-
- bool success = true;
- for (const auto& file : dir->GetFiles()) {
- if (!DeleteFile(file->GetName()))
- success = false;
- }
-
- for (const auto& sdir : dir->GetSubdirectories()) {
- if (!dir->DeleteSubdirectoryRecursive(sdir->GetName()))
- success = false;
- }
-
- return success;
-}
-
-bool VfsDirectory::Copy(const std::string& src, const std::string& dest) {
- const auto f1 = GetFile(src);
- auto f2 = CreateFile(dest);
- if (f1 == nullptr || f2 == nullptr)
- return false;
-
- if (!f2->Resize(f1->GetSize())) {
- DeleteFile(dest);
- return false;
- }
-
- return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
-}
-
-bool ReadOnlyVfsDirectory::IsWritable() const {
- return false;
-}
-
-bool ReadOnlyVfsDirectory::IsReadable() const {
- return true;
-}
-
-std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(const std::string& name) {
- return nullptr;
-}
-
-std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(const std::string& name) {
- return nullptr;
-}
-
-bool ReadOnlyVfsDirectory::DeleteSubdirectory(const std::string& name) {
- return false;
-}
-
-bool ReadOnlyVfsDirectory::DeleteFile(const std::string& name) {
- return false;
-}
-
-bool ReadOnlyVfsDirectory::Rename(const std::string& name) {
- return false;
-}
-} // namespace FileSys
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
deleted file mode 100644
index 0e30991b4..000000000
--- a/src/core/file_sys/vfs.h
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-#include <string>
-#include <type_traits>
-#include <vector>
-#include "boost/optional.hpp"
-#include "common/common_types.h"
-#include "common/file_util.h"
-
-namespace FileSys {
-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>;
-
-// A class representing a file in an abstract filesystem.
-struct VfsFile : NonCopyable {
- virtual ~VfsFile();
-
- // Retrieves the file name.
- virtual std::string GetName() const = 0;
- // Retrieves the extension of the file name.
- virtual std::string GetExtension() const;
- // Retrieves the size of the file.
- virtual size_t GetSize() const = 0;
- // Resizes the file to new_size. Returns whether or not the operation was successful.
- virtual bool Resize(size_t new_size) = 0;
- // Gets a pointer to the directory containing this file, returning nullptr if there is none.
- virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0;
-
- // Returns whether or not the file can be written to.
- virtual bool IsWritable() const = 0;
- // Returns whether or not the file can be read from.
- virtual bool IsReadable() const = 0;
-
- // The primary method of reading from the file. Reads length bytes into data starting at offset
- // into file. Returns number of bytes successfully read.
- virtual size_t Read(u8* data, size_t length, size_t offset = 0) const = 0;
- // The primary method of writing to the file. Writes length bytes from data starting at offset
- // into file. Returns number of bytes successfully written.
- virtual size_t Write(const u8* data, size_t length, size_t offset = 0) = 0;
-
- // Reads exactly one byte at the offset provided, returning boost::none on error.
- virtual boost::optional<u8> ReadByte(size_t offset = 0) const;
- // Reads size bytes starting at offset in file into a vector.
- virtual std::vector<u8> ReadBytes(size_t size, size_t offset = 0) const;
- // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(),
- // 0)'
- virtual std::vector<u8> ReadAllBytes() const;
-
- // Reads an array of type T, size number_elements starting at offset.
- // Returns the number of bytes (sizeof(T)*number_elements) read successfully.
- template <typename T>
- size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const {
- static_assert(std::is_trivially_copyable<T>::value,
- "Data type must be trivially copyable.");
-
- return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset);
- }
-
- // Reads size bytes into the memory starting at data starting at offset into the file.
- // Returns the number of bytes read successfully.
- template <typename T>
- size_t ReadBytes(T* data, size_t size, size_t offset = 0) const {
- static_assert(std::is_trivially_copyable<T>::value,
- "Data type must be trivially copyable.");
- return Read(reinterpret_cast<u8*>(data), size, offset);
- }
-
- // Reads one object of type T starting at offset in file.
- // Returns the number of bytes read successfully (sizeof(T)).
- template <typename T>
- size_t ReadObject(T* data, size_t offset = 0) const {
- static_assert(std::is_trivially_copyable<T>::value,
- "Data type must be trivially copyable.");
- return Read(reinterpret_cast<u8*>(data), sizeof(T), offset);
- }
-
- // Writes exactly one byte to offset in file and retuns whether or not the byte was written
- // successfully.
- virtual bool WriteByte(u8 data, size_t offset = 0);
- // Writes a vector of bytes to offset in file and returns the number of bytes successfully
- // written.
- virtual size_t WriteBytes(std::vector<u8> data, size_t offset = 0);
-
- // Writes an array of type T, size number_elements to offset in file.
- // Returns the number of bytes (sizeof(T)*number_elements) written successfully.
- template <typename T>
- size_t WriteArray(T* data, size_t number_elements, size_t offset = 0) {
- static_assert(std::is_trivially_copyable<T>::value,
- "Data type must be trivially copyable.");
-
- return Write(data, number_elements * sizeof(T), offset);
- }
-
- // Writes size bytes starting at memory location data to offset in file.
- // Returns the number of bytes written successfully.
- template <typename T>
- size_t WriteBytes(T* data, size_t size, size_t offset = 0) {
- static_assert(std::is_trivially_copyable<T>::value,
- "Data type must be trivially copyable.");
- return Write(reinterpret_cast<u8*>(data), size, offset);
- }
-
- // Writes one object of type T to offset in file.
- // Returns the number of bytes written successfully (sizeof(T)).
- template <typename T>
- size_t WriteObject(const T& data, size_t offset = 0) {
- static_assert(std::is_trivially_copyable<T>::value,
- "Data type must be trivially copyable.");
- return Write(&data, sizeof(T), offset);
- }
-
- // Renames the file to name. Returns whether or not the operation was successsful.
- virtual bool Rename(const std::string& name) = 0;
-};
-
-// A class representing a directory in an abstract filesystem.
-struct VfsDirectory : NonCopyable {
- virtual ~VfsDirectory();
-
- // Retrives the file located at path as if the current directory was root. Returns nullptr if
- // not found.
- virtual std::shared_ptr<VfsFile> GetFileRelative(const std::string& path) const;
- // Calls GetFileRelative(path) on the root of the current directory.
- virtual std::shared_ptr<VfsFile> GetFileAbsolute(const std::string& path) const;
-
- // Retrives the directory located at path as if the current directory was root. Returns nullptr
- // if not found.
- virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(const std::string& path) const;
- // Calls GetDirectoryRelative(path) on the root of the current directory.
- virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(const std::string& path) const;
-
- // Returns a vector containing all of the files in this directory.
- virtual std::vector<std::shared_ptr<VfsFile>> GetFiles() const = 0;
- // Returns the file with filename matching name. Returns nullptr if directory dosen't have a
- // file with name.
- virtual std::shared_ptr<VfsFile> GetFile(const std::string& name) const;
-
- // Returns a vector containing all of the subdirectories in this directory.
- virtual std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const = 0;
- // Returns the directory with name matching name. Returns nullptr if directory dosen't have a
- // directory with name.
- virtual std::shared_ptr<VfsDirectory> GetSubdirectory(const std::string& name) const;
-
- // Returns whether or not the directory can be written to.
- virtual bool IsWritable() const = 0;
- // Returns whether of not the directory can be read from.
- virtual bool IsReadable() const = 0;
-
- // Returns whether or not the directory is the root of the current file tree.
- virtual bool IsRoot() const;
-
- // Returns the name of the directory.
- virtual std::string GetName() const = 0;
- // Returns the total size of all files and subdirectories in this directory.
- virtual size_t GetSize() const;
- // Returns the parent directory of this directory. Returns nullptr if this directory is root or
- // has no parent.
- virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0;
-
- // Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr
- // if the operation failed.
- virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) = 0;
- // Creates a new file with name name. Returns a pointer to the new file or nullptr if the
- // operation failed.
- virtual std::shared_ptr<VfsFile> CreateFile(const std::string& name) = 0;
-
- // Deletes the subdirectory with name and returns true on success.
- virtual bool DeleteSubdirectory(const std::string& name) = 0;
- // Deletes all subdirectories and files of subdirectory with name recirsively and then deletes
- // the subdirectory. Returns true on success.
- virtual bool DeleteSubdirectoryRecursive(const std::string& name);
- // Returnes whether or not the file with name name was deleted successfully.
- virtual bool DeleteFile(const std::string& name) = 0;
-
- // Returns whether or not this directory was renamed to name.
- virtual bool Rename(const std::string& name) = 0;
-
- // Returns whether or not the file with name src was successfully copied to a new file with name
- // dest.
- virtual bool Copy(const std::string& src, const std::string& dest);
-
- // Interprets the file with name file instead as a directory of type directory.
- // The directory must have a constructor that takes a single argument of type
- // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
- // subdirectory in one call.
- template <typename Directory>
- bool InterpretAsDirectory(const std::string& file) {
- auto file_p = GetFile(file);
- if (file_p == nullptr)
- return false;
- return ReplaceFileWithSubdirectory(file, std::make_shared<Directory>(file_p));
- }
-
-protected:
- // Backend for InterpretAsDirectory.
- // Removes all references to file and adds a reference to dir in the directory's implementation.
- virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0;
-};
-
-// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work
-// if writable. This is to avoid redundant empty methods everywhere.
-struct ReadOnlyVfsDirectory : public VfsDirectory {
- bool IsWritable() const override;
- bool IsReadable() const override;
- std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override;
- std::shared_ptr<VfsFile> CreateFile(const std::string& name) override;
- bool DeleteSubdirectory(const std::string& name) override;
- bool DeleteFile(const std::string& name) override;
- bool Rename(const std::string& name) override;
-};
-} // namespace FileSys
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
deleted file mode 100644
index 288499cb5..000000000
--- a/src/core/file_sys/vfs_offset.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "core/file_sys/vfs_offset.h"
-
-namespace FileSys {
-
-OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_,
- const std::string& name_)
- : file(file_), offset(offset_), size(size_), name(name_) {}
-
-std::string OffsetVfsFile::GetName() const {
- return name.empty() ? file->GetName() : name;
-}
-
-size_t OffsetVfsFile::GetSize() const {
- return size;
-}
-
-bool OffsetVfsFile::Resize(size_t new_size) {
- if (offset + new_size < file->GetSize()) {
- size = new_size;
- } else {
- auto res = file->Resize(offset + new_size);
- if (!res)
- return false;
- size = new_size;
- }
-
- return true;
-}
-
-std::shared_ptr<VfsDirectory> OffsetVfsFile::GetContainingDirectory() const {
- return file->GetContainingDirectory();
-}
-
-bool OffsetVfsFile::IsWritable() const {
- return file->IsWritable();
-}
-
-bool OffsetVfsFile::IsReadable() const {
- return file->IsReadable();
-}
-
-size_t OffsetVfsFile::Read(u8* data, size_t length, size_t r_offset) const {
- return file->Read(data, TrimToFit(length, r_offset), offset + r_offset);
-}
-
-size_t OffsetVfsFile::Write(const u8* data, size_t length, size_t r_offset) {
- return file->Write(data, TrimToFit(length, r_offset), offset + r_offset);
-}
-
-boost::optional<u8> OffsetVfsFile::ReadByte(size_t r_offset) const {
- if (r_offset < size)
- return file->ReadByte(offset + r_offset);
-
- return boost::none;
-}
-
-std::vector<u8> OffsetVfsFile::ReadBytes(size_t r_size, size_t r_offset) const {
- return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset);
-}
-
-std::vector<u8> OffsetVfsFile::ReadAllBytes() const {
- return file->ReadBytes(size, offset);
-}
-
-bool OffsetVfsFile::WriteByte(u8 data, size_t r_offset) {
- if (r_offset < size)
- return file->WriteByte(data, offset + r_offset);
-
- return false;
-}
-
-size_t OffsetVfsFile::WriteBytes(std::vector<u8> data, size_t r_offset) {
- return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset);
-}
-
-bool OffsetVfsFile::Rename(const std::string& name) {
- return file->Rename(name);
-}
-
-size_t OffsetVfsFile::GetOffset() const {
- return offset;
-}
-
-size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const {
- return std::max<size_t>(std::min<size_t>(size - r_offset, r_size), 0);
-}
-
-} // namespace FileSys
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h
deleted file mode 100644
index adc615b38..000000000
--- a/src/core/file_sys/vfs_offset.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "core/file_sys/vfs.h"
-
-namespace FileSys {
-
-// An implementation of VfsFile that wraps around another VfsFile at a certain offset.
-// Similar to seeking to an offset.
-// If the file is writable, operations that would write past the end of the offset file will expand
-// the size of this wrapper.
-struct OffsetVfsFile : public VfsFile {
- OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0,
- const std::string& new_name = "");
-
- std::string GetName() const override;
- size_t GetSize() const override;
- bool Resize(size_t new_size) override;
- std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
- bool IsWritable() const override;
- bool IsReadable() const override;
- size_t Read(u8* data, size_t length, size_t offset) const override;
- size_t Write(const u8* data, size_t length, size_t offset) override;
- boost::optional<u8> ReadByte(size_t offset) const override;
- std::vector<u8> ReadBytes(size_t size, size_t offset) const override;
- std::vector<u8> ReadAllBytes() const override;
- bool WriteByte(u8 data, size_t offset) override;
- size_t WriteBytes(std::vector<u8> data, size_t offset) override;
-
- bool Rename(const std::string& name) override;
-
- size_t GetOffset() const;
-
-private:
- size_t TrimToFit(size_t r_size, size_t r_offset) const;
-
- std::shared_ptr<VfsFile> file;
- size_t offset;
- size_t size;
- std::string name;
-};
-
-} // namespace FileSys
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
deleted file mode 100644
index 8b95e8c72..000000000
--- a/src/core/file_sys/vfs_real.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/common_paths.h"
-#include "common/logging/log.h"
-#include "core/file_sys/vfs_real.h"
-
-namespace FileSys {
-
-static std::string PermissionsToCharArray(Mode perms) {
- std::string out;
- switch (perms) {
- case Mode::Read:
- out += "r";
- break;
- case Mode::Write:
- out += "r+";
- break;
- case Mode::Append:
- out += "a";
- break;
- }
- return out + "b";
-}
-
-RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_)
- : backing(path_, PermissionsToCharArray(perms_).c_str()), path(path_),
- parent_path(FileUtil::GetParentPath(path_)),
- path_components(FileUtil::SplitPathComponents(path_)),
- parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
- perms(perms_) {}
-
-std::string RealVfsFile::GetName() const {
- return path_components.back();
-}
-
-size_t RealVfsFile::GetSize() const {
- return backing.GetSize();
-}
-
-bool RealVfsFile::Resize(size_t new_size) {
- return backing.Resize(new_size);
-}
-
-std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
- return std::make_shared<RealVfsDirectory>(parent_path, perms);
-}
-
-bool RealVfsFile::IsWritable() const {
- return perms == Mode::Append || perms == Mode::Write;
-}
-
-bool RealVfsFile::IsReadable() const {
- return perms == Mode::Read || perms == Mode::Write;
-}
-
-size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
- if (!backing.Seek(offset, SEEK_SET))
- return 0;
- return backing.ReadBytes(data, length);
-}
-
-size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) {
- if (!backing.Seek(offset, SEEK_SET))
- return 0;
- return backing.WriteBytes(data, length);
-}
-
-bool RealVfsFile::Rename(const std::string& name) {
- const auto out = FileUtil::Rename(GetName(), name);
- path = parent_path + DIR_SEP + name;
- path_components = parent_components;
- path_components.push_back(name);
- backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str());
- return out;
-}
-
-RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
- : 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::Write || perms == Mode::Append))
- FileUtil::CreateDir(path);
- unsigned size;
- if (perms != Mode::Append) {
- FileUtil::ForeachDirectoryEntry(
- &size, path,
- [this](unsigned* 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::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const {
- return std::vector<std::shared_ptr<VfsFile>>(files);
-}
-
-std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const {
- return std::vector<std::shared_ptr<VfsDirectory>>(subdirectories);
-}
-
-bool RealVfsDirectory::IsWritable() const {
- return perms == Mode::Write || perms == Mode::Append;
-}
-
-bool RealVfsDirectory::IsReadable() const {
- return perms == Mode::Read || perms == Mode::Write;
-}
-
-std::string RealVfsDirectory::GetName() const {
- return path_components.back();
-}
-
-std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
- if (path_components.size() <= 1)
- return nullptr;
-
- return std::make_shared<RealVfsDirectory>(parent_path, perms);
-}
-
-std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(const std::string& name) {
- if (!FileUtil::CreateDir(path + DIR_SEP + name))
- return nullptr;
- subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(path + DIR_SEP + name, perms));
- return subdirectories.back();
-}
-
-std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(const std::string& name) {
- if (!FileUtil::CreateEmptyFile(path + DIR_SEP + name))
- return nullptr;
- files.emplace_back(std::make_shared<RealVfsFile>(path + DIR_SEP + name, perms));
- return files.back();
-}
-
-bool RealVfsDirectory::DeleteSubdirectory(const std::string& name) {
- return FileUtil::DeleteDirRecursively(path + DIR_SEP + name);
-}
-
-bool RealVfsDirectory::DeleteFile(const std::string& name) {
- return FileUtil::Delete(path + DIR_SEP + name);
-}
-
-bool RealVfsDirectory::Rename(const std::string& name) {
- return FileUtil::Rename(path, parent_path + DIR_SEP + name);
-}
-
-bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
- auto iter = std::find(files.begin(), files.end(), file);
- if (iter == files.end())
- return false;
-
- files[iter - files.begin()] = files.back();
- files.pop_back();
-
- subdirectories.emplace_back(dir);
-
- return true;
-}
-} // namespace FileSys
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
deleted file mode 100644
index 01717f485..000000000
--- a/src/core/file_sys/vfs_real.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/file_util.h"
-#include "core/file_sys/filesystem.h"
-#include "core/file_sys/vfs.h"
-
-namespace FileSys {
-
-// An implmentation of VfsFile that represents a file on the user's computer.
-struct RealVfsFile : public VfsFile {
- RealVfsFile(const std::string& name, Mode perms = Mode::Read);
-
- std::string GetName() const override;
- size_t GetSize() const override;
- bool Resize(size_t new_size) override;
- std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
- bool IsWritable() const override;
- bool IsReadable() const override;
- size_t Read(u8* data, size_t length, size_t offset) const override;
- size_t Write(const u8* data, size_t length, size_t offset) override;
- bool Rename(const std::string& name) override;
-
-private:
- FileUtil::IOFile backing;
- std::string path;
- std::string parent_path;
- std::vector<std::string> path_components;
- std::vector<std::string> parent_components;
- Mode perms;
-};
-
-// An implementation of VfsDirectory that represents a directory on the user's computer.
-struct RealVfsDirectory : public VfsDirectory {
- RealVfsDirectory(const std::string& path, Mode perms);
-
- std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
- std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
- bool IsWritable() const override;
- bool IsReadable() const override;
- std::string GetName() const override;
- std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
- std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override;
- std::shared_ptr<VfsFile> CreateFile(const std::string& name) override;
- bool DeleteSubdirectory(const std::string& name) override;
- bool DeleteFile(const std::string& name) override;
- bool Rename(const std::string& name) override;
-
-protected:
- bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
-
-private:
- 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/am/am.cpp b/src/core/hle/service/am/am.cpp
index 7bb13c735..a871b3eaa 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -621,7 +621,7 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
FileSys::Path unused;
- auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData);
+ auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData, unused);
if (savedata.Failed()) {
// Create the save data and return an error indicating that the operation was performed.
FileSystem::FormatFileSystem(FileSystem::Type::SaveData);
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 25810c165..f58b518b6 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -2,221 +2,36 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "boost/container/flat_map.hpp"
-#include "common/assert.h"
-#include "common/common_paths.h"
+#include <boost/container/flat_map.hpp>
#include "common/file_util.h"
-#include "core/core.h"
-#include "core/file_sys/errors.h"
#include "core/file_sys/filesystem.h"
-#include "core/file_sys/vfs.h"
-#include "core/file_sys/vfs_offset.h"
-#include "core/file_sys/vfs_real.h"
-#include "core/hle/kernel/process.h"
+#include "core/file_sys/savedata_factory.h"
+#include "core/file_sys/sdmc_factory.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_srv.h"
namespace Service::FileSystem {
-// Size of emulated sd card free space, reported in bytes.
-// Just using 32GB because thats reasonable
-// TODO(DarkLordZach): Eventually make this configurable in settings.
-constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
-
-static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
- const std::string& dir_name) {
- if (dir_name == "." || dir_name == "" || dir_name == "/" || dir_name == "\\")
- return base;
-
- return base->GetDirectoryRelative(dir_name);
-}
-
-VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
- : backing(backing_) {}
-
-std::string VfsDirectoryServiceWrapper::GetName() const {
- return backing->GetName();
-}
-
-ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 size) const {
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
- auto file = dir->CreateFile(FileUtil::GetFilename(path));
- if (file == nullptr)
- return ResultCode(-1);
- if (!file->Resize(size))
- return ResultCode(-1);
- return RESULT_SUCCESS;
-}
-
-ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const {
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
- if (!backing->DeleteFile(FileUtil::GetFilename(path)))
- return ResultCode(-1);
- return RESULT_SUCCESS;
-}
-
-ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const {
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
- if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
- dir = backing;
- auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path));
- if (new_dir == nullptr)
- return ResultCode(-1);
- return RESULT_SUCCESS;
-}
-
-ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const {
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
- if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path)))
- return ResultCode(-1);
- return RESULT_SUCCESS;
-}
-
-ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const {
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
- if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path)))
- return ResultCode(-1);
- return RESULT_SUCCESS;
-}
-
-ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
- const std::string& dest_path) const {
- auto src = backing->GetFileRelative(src_path);
- if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
- // Use more-optimized vfs implementation rename.
- if (src == nullptr)
- return FileSys::ERROR_PATH_NOT_FOUND;
- if (!src->Rename(FileUtil::GetFilename(dest_path)))
- return ResultCode(-1);
- return RESULT_SUCCESS;
- }
-
- // Move by hand -- TODO(DarkLordZach): Optimize
- auto c_res = CreateFile(dest_path, src->GetSize());
- if (c_res != RESULT_SUCCESS)
- return c_res;
-
- auto dest = backing->GetFileRelative(dest_path);
- ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
-
- ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
- "Could not write all of the bytes but everything else has succeded.");
-
- if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path)))
- return ResultCode(-1);
-
- return RESULT_SUCCESS;
-}
-
-ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path,
- const std::string& dest_path) const {
- auto src = GetDirectoryRelativeWrapped(backing, src_path);
- if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
- // Use more-optimized vfs implementation rename.
- if (src == nullptr)
- return FileSys::ERROR_PATH_NOT_FOUND;
- if (!src->Rename(FileUtil::GetFilename(dest_path)))
- return ResultCode(-1);
- return RESULT_SUCCESS;
- }
-
- // TODO(DarkLordZach): Implement renaming across the tree (move).
- ASSERT_MSG(false,
- "Could not rename directory with path \"{}\" to new path \"{}\" because parent dirs "
- "don't match -- UNIMPLEMENTED",
- src_path, dest_path);
-
- return ResultCode(-1);
-}
-
-ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path,
- FileSys::Mode mode) const {
- auto file = backing->GetFileRelative(path);
- if (file == nullptr)
- return FileSys::ERROR_PATH_NOT_FOUND;
-
- if ((static_cast<u32>(mode) & static_cast<u32>(FileSys::Mode::Append)) != 0) {
- return MakeResult<FileSys::VirtualFile>(
- std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize()));
- }
-
- return MakeResult<FileSys::VirtualFile>(file);
-}
-
-ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) {
- auto dir = GetDirectoryRelativeWrapped(backing, path);
- if (dir == nullptr)
- return ResultCode(-1);
- return MakeResult(dir);
-}
-
-u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
- if (backing->IsWritable())
- return EMULATED_SD_REPORTED_SIZE;
-
- return 0;
-}
-
-ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
- const std::string& path) const {
- auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
- if (dir == nullptr)
- return ResultCode(-1);
- auto filename = FileUtil::GetFilename(path);
- if (dir->GetFile(filename) != nullptr)
- return MakeResult(FileSys::EntryType::File);
- if (dir->GetSubdirectory(filename) != nullptr)
- return MakeResult(FileSys::EntryType::Directory);
- return ResultCode(-1);
-}
-
-// A deferred filesystem for nand save data.
-// This must be deferred because the directory is dependent on title id, which is not set at
-// registration time.
-struct SaveDataDeferredFilesystem : DeferredFilesystem {
-protected:
- FileSys::VirtualDir CreateFilesystem() override {
- u64 title_id = Core::CurrentProcess()->program_id;
- // TODO(DarkLordZach): Users
- u32 user_id = 0;
- std::string nand_directory = fmt::format(
- "{}save/{:016X}/{:08X}/", FileUtil::GetUserPath(D_NAND_IDX), title_id, user_id);
-
- auto savedata =
- std::make_shared<FileSys::RealVfsDirectory>(nand_directory, FileSys::Mode::Write);
- return savedata;
- }
-};
-
/**
* Map of registered file systems, identified by type. Once an file system is registered here, it
* is never removed until UnregisterFileSystems is called.
*/
-static boost::container::flat_map<Type, std::unique_ptr<DeferredFilesystem>> filesystem_map;
-static FileSys::VirtualFile filesystem_romfs = nullptr;
+static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map;
-ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& factory, Type type) {
+ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type) {
auto result = filesystem_map.emplace(type, std::move(factory));
bool inserted = result.second;
ASSERT_MSG(inserted, "Tried to register more than one system with same id code");
auto& filesystem = result.first->second;
- LOG_DEBUG(Service_FS, "Registered file system with id code 0x{:08X}", static_cast<u32>(type));
- return RESULT_SUCCESS;
-}
-
-ResultCode RegisterRomFS(FileSys::VirtualFile filesystem) {
- ASSERT_MSG(filesystem_romfs == nullptr,
- "Tried to register more than one system with same id code");
-
- filesystem_romfs = filesystem;
LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(),
- static_cast<u32>(Type::RomFS));
+ static_cast<u32>(type));
return RESULT_SUCCESS;
}
-ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type) {
+ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
+ FileSys::Path& path) {
LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type));
auto itr = filesystem_map.find(type);
@@ -225,13 +40,7 @@ ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type) {
return ResultCode(-1);
}
- return MakeResult(itr->second->Get());
-}
-
-ResultVal<FileSys::VirtualFile> OpenRomFS() {
- if (filesystem_romfs == nullptr)
- return ResultCode(-1);
- return MakeResult(filesystem_romfs);
+ return itr->second->Open(path);
}
ResultCode FormatFileSystem(Type type) {
@@ -243,21 +52,21 @@ ResultCode FormatFileSystem(Type type) {
return ResultCode(-1);
}
- return itr->second->Get()->GetParentDirectory()->DeleteSubdirectory(
- itr->second->Get()->GetName())
- ? RESULT_SUCCESS
- : ResultCode(-1);
+ FileSys::Path unused;
+ return itr->second->Format(unused);
}
void RegisterFileSystems() {
filesystem_map.clear();
- filesystem_romfs = nullptr;
+ std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX);
- auto sdcard = std::make_shared<FileSys::RealVfsDirectory>(sd_directory, FileSys::Mode::Write);
- RegisterFileSystem(std::make_unique<DeferredFilesystem>(sdcard), Type::SDMC);
- RegisterFileSystem(std::make_unique<SaveDataDeferredFilesystem>(), Type::SaveData);
+ auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory));
+ RegisterFileSystem(std::move(savedata), Type::SaveData);
+
+ auto sdcard = std::make_unique<FileSys::SDMC_Factory>(std::move(sd_directory));
+ RegisterFileSystem(std::move(sdcard), Type::SDMC);
}
void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index d3de797f1..56d26146e 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -6,8 +6,6 @@
#include <memory>
#include "common/common_types.h"
-#include "core/file_sys/filesystem.h"
-#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
namespace FileSys {
@@ -31,136 +29,12 @@ enum class Type {
SDMC = 3,
};
-// 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
-// avoids repetitive code.
-class VfsDirectoryServiceWrapper {
-public:
- explicit VfsDirectoryServiceWrapper(FileSys::VirtualDir backing);
-
- /**
- * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
- */
- std::string GetName() const;
-
- /**
- * Create a file specified by its path
- * @param path Path relative to the Archive
- * @param size The size of the new file, filled with zeroes
- * @return Result of the operation
- */
- ResultCode CreateFile(const std::string& path, u64 size) const;
-
- /**
- * Delete a file specified by its path
- * @param path Path relative to the archive
- * @return Result of the operation
- */
- ResultCode DeleteFile(const std::string& path) const;
-
- /**
- * Create a directory specified by its path
- * @param path Path relative to the archive
- * @return Result of the operation
- */
- ResultCode CreateDirectory(const std::string& path) const;
-
- /**
- * Delete a directory specified by its path
- * @param path Path relative to the archive
- * @return Result of the operation
- */
- ResultCode DeleteDirectory(const std::string& path) const;
-
- /**
- * Delete a directory specified by its path and anything under it
- * @param path Path relative to the archive
- * @return Result of the operation
- */
- ResultCode DeleteDirectoryRecursively(const std::string& path) const;
-
- /**
- * Rename a File specified by its path
- * @param src_path Source path relative to the archive
- * @param dest_path Destination path relative to the archive
- * @return Result of the operation
- */
- ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const;
-
- /**
- * Rename a Directory specified by its path
- * @param src_path Source path relative to the archive
- * @param dest_path Destination path relative to the archive
- * @return Result of the operation
- */
- ResultCode RenameDirectory(const std::string& src_path, const std::string& dest_path) const;
-
- /**
- * Open a file specified by its path, using the specified mode
- * @param path Path relative to the archive
- * @param mode Mode to open the file with
- * @return Opened file, or error code
- */
- ResultVal<FileSys::VirtualFile> OpenFile(const std::string& path, FileSys::Mode mode) const;
-
- /**
- * Open a directory specified by its path
- * @param path Path relative to the archive
- * @return Opened directory, or error code
- */
- ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path);
-
- /**
- * Get the free space
- * @return The number of free bytes in the archive
- */
- u64 GetFreeSpaceSize() const;
-
- /**
- * Get the type of the specified path
- * @return The type of the specified path or error code
- */
- ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const;
-
-private:
- FileSys::VirtualDir backing;
-};
-
-// A class that deferres the creation of a filesystem until a later time.
-// This is useful if construction depends on a variable not known when the filesystem is registered.
-// Construct this with a filesystem (VirtualDir) to avoid the deferrence feature or override the
-// CreateFilesystem method which will be called on first use.
-struct DeferredFilesystem {
- DeferredFilesystem() = default;
-
- explicit DeferredFilesystem(FileSys::VirtualDir vfs_directory) : fs(std::move(vfs_directory)) {}
-
- FileSys::VirtualDir Get() {
- if (fs == nullptr)
- fs = CreateFilesystem();
-
- return fs;
- }
-
- virtual ~DeferredFilesystem() = default;
-
-protected:
- virtual FileSys::VirtualDir CreateFilesystem() {
- return fs;
- }
-
-private:
- FileSys::VirtualDir fs;
-};
-
/**
* Registers a FileSystem, instances of which can later be opened using its IdCode.
* @param factory FileSystem backend interface to use
* @param type Type used to access this type of FileSystem
*/
-ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& fs, Type type);
-
-ResultCode RegisterRomFS(FileSys::VirtualFile fs);
+ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type);
/**
* Opens a file system
@@ -168,9 +42,8 @@ ResultCode RegisterRomFS(FileSys::VirtualFile fs);
* @param path Path to the file system, used with Binary paths
* @return FileSys::FileSystemBackend interface to the file system
*/
-ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type);
-
-ResultVal<FileSys::VirtualFile> OpenRomFS();
+ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
+ FileSys::Path& path);
/**
* Formats a file system
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 26e8a8c88..216bfea0a 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -19,8 +19,8 @@ namespace Service::FileSystem {
class IStorage final : public ServiceFramework<IStorage> {
public:
- IStorage(FileSys::VirtualFile backend_)
- : ServiceFramework("IStorage"), backend(std::move(backend_)) {
+ IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend)
+ : ServiceFramework("IStorage"), backend(std::move(backend)) {
static const FunctionInfo functions[] = {
{0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"},
{3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"},
@@ -29,7 +29,7 @@ public:
}
private:
- FileSys::VirtualFile backend;
+ std::unique_ptr<FileSys::StorageBackend> backend;
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -51,8 +51,8 @@ private:
}
// Read the data from the Storage backend
- std::vector<u8> output = backend->ReadBytes(length, offset);
- auto res = MakeResult<size_t>(output.size());
+ std::vector<u8> output(length);
+ ResultVal<size_t> res = backend->Read(offset, length, output.data());
if (res.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
@@ -69,8 +69,8 @@ private:
class IFile final : public ServiceFramework<IFile> {
public:
- explicit IFile(FileSys::VirtualFile backend_)
- : ServiceFramework("IFile"), backend(std::move(backend_)) {
+ explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend)
+ : ServiceFramework("IFile"), backend(std::move(backend)) {
static const FunctionInfo functions[] = {
{0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"},
{2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"},
@@ -80,7 +80,7 @@ public:
}
private:
- FileSys::VirtualFile backend;
+ std::unique_ptr<FileSys::StorageBackend> backend;
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -103,8 +103,8 @@ private:
}
// Read the data from the Storage backend
- std::vector<u8> output = backend->ReadBytes(length, offset);
- auto res = MakeResult<size_t>(output.size());
+ std::vector<u8> output(length);
+ ResultVal<size_t> res = backend->Read(offset, length, output.data());
if (res.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
@@ -139,10 +139,9 @@ private:
return;
}
- std::vector<u8> data = ctx.ReadBuffer();
- data.resize(length);
// Write the data to the Storage backend
- auto res = MakeResult<size_t>(backend->WriteBytes(data, offset));
+ std::vector<u8> data = ctx.ReadBuffer();
+ ResultVal<size_t> res = backend->Write(offset, length, true, data.data());
if (res.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
@@ -155,8 +154,7 @@ private:
void Flush(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
-
- // Exists for SDK compatibiltity -- No need to flush file.
+ backend->Flush();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -165,7 +163,7 @@ private:
void SetSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 size = rp.Pop<u64>();
- backend->Resize(size);
+ backend->SetSize(size);
LOG_DEBUG(Service_FS, "called, size={}", size);
IPC::ResponseBuilder rb{ctx, 2};
@@ -182,38 +180,19 @@ private:
}
};
-template <typename T>
-static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data,
- FileSys::EntryType type) {
- 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));
- }
-}
-
class IDirectory final : public ServiceFramework<IDirectory> {
public:
- explicit IDirectory(FileSys::VirtualDir backend_)
- : ServiceFramework("IDirectory"), backend(std::move(backend_)) {
+ explicit IDirectory(std::unique_ptr<FileSys::DirectoryBackend>&& backend)
+ : ServiceFramework("IDirectory"), backend(std::move(backend)) {
static const FunctionInfo functions[] = {
{0, &IDirectory::Read, "Read"},
{1, &IDirectory::GetEntryCount, "GetEntryCount"},
};
RegisterHandlers(functions);
-
- // Build entry index now to save time later.
- BuildEntryIndex(entries, backend->GetFiles(), FileSys::File);
- BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::Directory);
}
private:
- FileSys::VirtualDir backend;
- std::vector<FileSys::Entry> entries;
- u64 next_entry_index = 0;
+ std::unique_ptr<FileSys::DirectoryBackend> backend;
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -224,31 +203,26 @@ private:
// Calculate how many entries we can fit in the output buffer
u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
- // Cap at total number of entries.
- u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
-
// Read the data from the Directory backend
- std::vector<FileSys::Entry> entry_data(entries.begin() + next_entry_index,
- entries.begin() + next_entry_index + actual_entries);
-
- next_entry_index += actual_entries;
+ std::vector<FileSys::Entry> entries(count_entries);
+ u64 read_entries = backend->Read(count_entries, entries.data());
// Convert the data into a byte array
- std::vector<u8> output(entry_data.size() * sizeof(FileSys::Entry));
- std::memcpy(output.data(), entry_data.data(), output.size());
+ std::vector<u8> output(entries.size() * sizeof(FileSys::Entry));
+ std::memcpy(output.data(), entries.data(), output.size());
// Write the data to memory
ctx.WriteBuffer(output);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push(actual_entries);
+ rb.Push(read_entries);
}
void GetEntryCount(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
- u64 count = entries.size() - next_entry_index;
+ u64 count = backend->GetEntryCount();
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
@@ -258,7 +232,7 @@ private:
class IFileSystem final : public ServiceFramework<IFileSystem> {
public:
- explicit IFileSystem(FileSys::VirtualDir backend)
+ explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend)
: ServiceFramework("IFileSystem"), backend(std::move(backend)) {
static const FunctionInfo functions[] = {
{0, &IFileSystem::CreateFile, "CreateFile"},
@@ -293,7 +267,7 @@ public:
LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(backend.CreateFile(name, size));
+ rb.Push(backend->CreateFile(name, size));
}
void DeleteFile(Kernel::HLERequestContext& ctx) {
@@ -305,7 +279,7 @@ public:
LOG_DEBUG(Service_FS, "called file {}", name);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(backend.DeleteFile(name));
+ rb.Push(backend->DeleteFile(name));
}
void CreateDirectory(Kernel::HLERequestContext& ctx) {
@@ -317,7 +291,7 @@ public:
LOG_DEBUG(Service_FS, "called directory {}", name);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(backend.CreateDirectory(name));
+ rb.Push(backend->CreateDirectory(name));
}
void RenameFile(Kernel::HLERequestContext& ctx) {
@@ -335,7 +309,7 @@ public:
LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(backend.RenameFile(src_name, dst_name));
+ rb.Push(backend->RenameFile(src_name, dst_name));
}
void OpenFile(Kernel::HLERequestContext& ctx) {
@@ -348,14 +322,14 @@ public:
LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode));
- auto result = backend.OpenFile(name, mode);
+ auto result = backend->OpenFile(name, mode);
if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code());
return;
}
- IFile file(result.Unwrap());
+ auto file = std::move(result.Unwrap());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -373,14 +347,14 @@ public:
LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags);
- auto result = backend.OpenDirectory(name);
+ auto result = backend->OpenDirectory(name);
if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code());
return;
}
- IDirectory directory(result.Unwrap());
+ auto directory = std::move(result.Unwrap());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -395,7 +369,7 @@ public:
LOG_DEBUG(Service_FS, "called file {}", name);
- auto result = backend.GetEntryType(name);
+ auto result = backend->GetEntryType(name);
if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code());
@@ -415,7 +389,7 @@ public:
}
private:
- VfsDirectoryServiceWrapper backend;
+ std::unique_ptr<FileSys::FileSystemBackend> backend;
};
FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
@@ -516,7 +490,8 @@ void FSP_SRV::TryLoadRomFS() {
if (romfs) {
return;
}
- auto res = OpenRomFS();
+ FileSys::Path unused;
+ auto res = OpenFileSystem(Type::RomFS, unused);
if (res.Succeeded()) {
romfs = std::move(res.Unwrap());
}
@@ -532,7 +507,8 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
- IFileSystem filesystem(OpenFileSystem(Type::SDMC).Unwrap());
+ FileSys::Path unused;
+ auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -555,7 +531,8 @@ void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
- IFileSystem filesystem(OpenFileSystem(Type::SaveData).Unwrap());
+ FileSys::Path unused;
+ auto filesystem = OpenFileSystem(Type::SaveData, unused).Unwrap();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -582,11 +559,18 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
return;
}
- IStorage storage(romfs);
+ // Attempt to open a StorageBackend interface to the RomFS
+ auto storage = romfs->OpenFile({}, {});
+ if (storage.Failed()) {
+ LOG_CRITICAL(Service_FS, "no storage interface available!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(storage.Code());
+ return;
+ }
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IStorage>(std::move(storage));
+ rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap()));
}
void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index b1ef6397f..acb78fac1 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -29,7 +29,7 @@ private:
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void OpenRomStorage(Kernel::HLERequestContext& ctx);
- FileSys::VirtualFile romfs;
+ std::unique_ptr<FileSys::FileSystemBackend> romfs;
};
} // namespace Service::FileSystem
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 303acdcb3..315f81e90 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -40,7 +40,7 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>&
} else if (!strcmp(params.param_str.data(), "NVRM_GPU_PREVENT_USE")) {
params.config_str[0] = '0';
} else {
- params.config_str[0] = '\0';
+ params.config_str[0] = '0';
}
} else {
UNIMPLEMENTED(); // unknown domain? Only nv has been seen so far on hardware
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 0b3b4cd73..eb7feb617 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -4,10 +4,11 @@
#include <cinttypes>
#include "common/common_funcs.h"
+#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
-#include "core/file_sys/content_archive.h"
+#include "core/file_sys/romfs_factory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -45,11 +46,55 @@ static std::string FindRomFS(const std::string& directory) {
return filepath_romfs;
}
-AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file)
- : AppLoader(std::move(file)) {}
+AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file,
+ std::string filepath)
+ : AppLoader(std::move(file)), filepath(std::move(filepath)) {}
+
+FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& file,
+ const std::string& filepath) {
+ bool is_main_found{};
+ bool is_npdm_found{};
+ bool is_rtld_found{};
+ bool is_sdk_found{};
+
+ const auto callback = [&](unsigned* num_entries_out, const std::string& directory,
+ const std::string& virtual_name) -> bool {
+ // Skip directories
+ std::string physical_name = directory + virtual_name;
+ if (FileUtil::IsDirectory(physical_name)) {
+ return true;
+ }
+
+ // Verify filename
+ if (Common::ToLower(virtual_name) == "main") {
+ is_main_found = true;
+ } else if (Common::ToLower(virtual_name) == "main.npdm") {
+ is_npdm_found = true;
+ return true;
+ } else if (Common::ToLower(virtual_name) == "rtld") {
+ is_rtld_found = true;
+ } else if (Common::ToLower(virtual_name) == "sdk") {
+ is_sdk_found = true;
+ } else {
+ // Continue searching
+ return true;
+ }
+
+ // Verify file is an NSO
+ FileUtil::IOFile file(physical_name, "rb");
+ if (AppLoader_NSO::IdentifyType(file, physical_name) != FileType::NSO) {
+ return false;
+ }
+
+ // We are done if we've found and verified all required NSOs
+ return !(is_main_found && is_npdm_found && is_rtld_found && is_sdk_found);
+ };
+
+ // Search the directory recursively, looking for the required modules
+ const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP;
+ FileUtil::ForeachDirectoryEntry(nullptr, directory, callback);
-FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) {
- if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) {
+ if (is_main_found && is_npdm_found && is_rtld_found && is_sdk_found) {
return FileType::DeconstructedRomDirectory;
}
@@ -61,13 +106,14 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}
+ if (!file.IsOpen()) {
+ return ResultStatus::Error;
+ }
- const FileSys::VirtualDir dir = file->GetContainingDirectory();
- const FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
- if (npdm == nullptr)
- return ResultStatus::ErrorInvalidFormat;
+ const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP;
+ const std::string npdm_path = directory + DIR_SEP + "main.npdm";
- ResultStatus result = metadata.Load(npdm);
+ ResultStatus result = metadata.Load(npdm_path);
if (result != ResultStatus::Success) {
return result;
}
@@ -82,10 +128,9 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
+ const std::string path = directory + DIR_SEP + module;
const VAddr load_addr = next_load_addr;
- const FileSys::VirtualFile module_file = dir->GetFile(module);
- if (module_file != nullptr)
- next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr);
+ next_load_addr = AppLoader_NSO::LoadModule(path, load_addr);
if (next_load_addr) {
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
} else {
@@ -102,20 +147,42 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
metadata.GetMainThreadStackSize());
// Find the RomFS by searching for a ".romfs" file in this directory
- const auto& files = dir->GetFiles();
- const auto romfs_iter =
- std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
- return file->GetName().find(".romfs") != std::string::npos;
- });
-
- // TODO(DarkLordZach): Identify RomFS if its a subdirectory.
- const auto romfs = (romfs_iter == files.end()) ? nullptr : *romfs_iter;
+ filepath_romfs = FindRomFS(directory);
- if (romfs != nullptr)
- Service::FileSystem::RegisterRomFS(romfs);
+ // Register the RomFS if a ".romfs" file was found
+ if (!filepath_romfs.empty()) {
+ Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this),
+ Service::FileSystem::Type::RomFS);
+ }
is_loaded = true;
return ResultStatus::Success;
}
+ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(
+ std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) {
+
+ if (filepath_romfs.empty()) {
+ LOG_DEBUG(Loader, "No RomFS available");
+ return ResultStatus::ErrorNotUsed;
+ }
+
+ // We reopen the file, to allow its position to be independent
+ romfs_file = std::make_shared<FileUtil::IOFile>(filepath_romfs, "rb");
+ if (!romfs_file->IsOpen()) {
+ return ResultStatus::Error;
+ }
+
+ offset = 0;
+ size = romfs_file->GetSize();
+
+ LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset);
+ LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size);
+
+ // Reset read pointer
+ file.Seek(0, SEEK_SET);
+
+ return ResultStatus::Success;
+}
+
} // namespace Loader
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 494220756..23295d911 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -20,22 +20,28 @@ namespace Loader {
*/
class AppLoader_DeconstructedRomDirectory final : public AppLoader {
public:
- explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file);
+ AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file, std::string filepath);
/**
* Returns the type of the file
- * @param file std::shared_ptr<VfsFile> open file
+ * @param file FileUtil::IOFile open file
+ * @param filepath Path of the file that we are opening.
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
- static FileType IdentifyType(const FileSys::VirtualFile& file);
+ static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath);
FileType GetFileType() override {
- return IdentifyType(file);
+ return IdentifyType(file, filepath);
}
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
+ ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
+ u64& size) override;
+
private:
+ std::string filepath_romfs;
+ std::string filepath;
FileSys::ProgramMetadata metadata;
};
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 4bfd5f536..b69e5c6ef 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -365,17 +365,20 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const
namespace Loader {
-AppLoader_ELF::AppLoader_ELF(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
+AppLoader_ELF::AppLoader_ELF(FileUtil::IOFile&& file, std::string filename)
+ : AppLoader(std::move(file)), filename(std::move(filename)) {}
-FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) {
+FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file, const std::string&) {
static constexpr u16 ELF_MACHINE_ARM{0x28};
u32 magic = 0;
- if (4 != file->ReadObject(&magic))
+ file.Seek(0, SEEK_SET);
+ if (1 != file.ReadArray<u32>(&magic, 1))
return FileType::Error;
u16 machine = 0;
- if (2 != file->ReadObject(&machine, 18))
+ file.Seek(18, SEEK_SET);
+ if (1 != file.ReadArray<u16>(&machine, 1))
return FileType::Error;
if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine)
@@ -388,13 +391,20 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded)
return ResultStatus::ErrorAlreadyLoaded;
- std::vector<u8> buffer = file->ReadAllBytes();
- if (buffer.size() != file->GetSize())
+ if (!file.IsOpen())
+ return ResultStatus::Error;
+
+ // Reset read pointer in case this file has been read before.
+ file.Seek(0, SEEK_SET);
+
+ size_t size = file.GetSize();
+ std::unique_ptr<u8[]> buffer(new u8[size]);
+ if (file.ReadBytes(&buffer[0], size) != size)
return ResultStatus::Error;
ElfReader elf_reader(&buffer[0]);
SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR);
- codeset->name = file->GetName();
+ codeset->name = filename;
process->LoadModule(codeset, codeset->entrypoint);
process->svc_access_mask.set();
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index b8fb982d0..ee741a789 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -16,20 +16,24 @@ namespace Loader {
/// Loads an ELF/AXF file
class AppLoader_ELF final : public AppLoader {
public:
- explicit AppLoader_ELF(FileSys::VirtualFile file);
+ AppLoader_ELF(FileUtil::IOFile&& file, std::string filename);
/**
* Returns the type of the file
- * @param file std::shared_ptr<VfsFile> open file
+ * @param file FileUtil::IOFile open file
+ * @param filepath Path of the file that we are opening.
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
- static FileType IdentifyType(const FileSys::VirtualFile& file);
+ static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath);
FileType GetFileType() override {
- return IdentifyType(file);
+ return IdentifyType(file, filename);
}
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
+
+private:
+ std::string filename;
};
} // namespace Loader
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 1574345a1..8831d8e83 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -6,7 +6,6 @@
#include <string>
#include "common/logging/log.h"
#include "common/string_util.h"
-#include "core/file_sys/vfs_real.h"
#include "core/hle/kernel/process.h"
#include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/elf.h"
@@ -22,11 +21,11 @@ const std::initializer_list<Kernel::AddressMapping> default_address_mappings = {
{0x1F000000, 0x600000, false}, // entire VRAM
};
-FileType IdentifyFile(FileSys::VirtualFile file) {
+FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath) {
FileType type;
#define CHECK_TYPE(loader) \
- type = AppLoader_##loader::IdentifyType(file); \
+ type = AppLoader_##loader::IdentifyType(file, filepath); \
if (FileType::Error != type) \
return type;
@@ -42,22 +41,25 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
}
FileType IdentifyFile(const std::string& file_name) {
- return IdentifyFile(FileSys::VirtualFile(std::make_shared<FileSys::RealVfsFile>(file_name)));
-}
+ FileUtil::IOFile file(file_name, "rb");
+ if (!file.IsOpen()) {
+ LOG_ERROR(Loader, "Failed to load file {}", file_name);
+ return FileType::Unknown;
+ }
-FileType GuessFromFilename(const std::string& name) {
- if (name == "main")
- return FileType::DeconstructedRomDirectory;
+ return IdentifyFile(file, file_name);
+}
- const std::string extension = Common::ToLower(FileUtil::GetExtensionFromFilename(name));
+FileType GuessFromExtension(const std::string& extension_) {
+ std::string extension = Common::ToLower(extension_);
- if (extension == "elf")
+ if (extension == ".elf")
return FileType::ELF;
- if (extension == "nro")
+ else if (extension == ".nro")
return FileType::NRO;
- if (extension == "nso")
+ else if (extension == ".nso")
return FileType::NSO;
- if (extension == "nca")
+ else if (extension == ".nca")
return FileType::NCA;
return FileType::Unknown;
@@ -91,47 +93,58 @@ const char* GetFileTypeString(FileType type) {
* @param filepath the file full path (with name)
* @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
*/
-static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileType type) {
+static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileType type,
+ const std::string& filename,
+ const std::string& filepath) {
switch (type) {
// Standard ELF file format.
case FileType::ELF:
- return std::make_unique<AppLoader_ELF>(std::move(file));
+ return std::make_unique<AppLoader_ELF>(std::move(file), filename);
// NX NSO file format.
case FileType::NSO:
- return std::make_unique<AppLoader_NSO>(std::move(file));
+ return std::make_unique<AppLoader_NSO>(std::move(file), filepath);
// NX NRO file format.
case FileType::NRO:
- return std::make_unique<AppLoader_NRO>(std::move(file));
+ return std::make_unique<AppLoader_NRO>(std::move(file), filepath);
// NX NCA file format.
case FileType::NCA:
- return std::make_unique<AppLoader_NCA>(std::move(file));
+ return std::make_unique<AppLoader_NCA>(std::move(file), filepath);
// NX deconstructed ROM directory.
case FileType::DeconstructedRomDirectory:
- return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file));
+ return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file), filepath);
default:
return nullptr;
}
}
-std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file) {
- FileType type = IdentifyFile(file);
- FileType filename_type = GuessFromFilename(file->GetName());
+std::unique_ptr<AppLoader> GetLoader(const std::string& filename) {
+ FileUtil::IOFile file(filename, "rb");
+ if (!file.IsOpen()) {
+ LOG_ERROR(Loader, "Failed to load file {}", filename);
+ return nullptr;
+ }
+
+ std::string filename_filename, filename_extension;
+ Common::SplitPath(filename, nullptr, &filename_filename, &filename_extension);
+
+ FileType type = IdentifyFile(file, filename);
+ FileType filename_type = GuessFromExtension(filename_extension);
if (type != filename_type) {
- LOG_WARNING(Loader, "File {} has a different type than its extension.", file->GetName());
+ LOG_WARNING(Loader, "File {} has a different type than its extension.", filename);
if (FileType::Unknown == type)
type = filename_type;
}
- LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
+ LOG_DEBUG(Loader, "Loading file {} as {}...", filename, GetFileTypeString(type));
- return GetFileLoader(std::move(file), type);
+ return GetFileLoader(std::move(file), type, filename_filename, filename);
}
} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 1da9e8099..b76f7b13d 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -13,7 +13,6 @@
#include <boost/optional.hpp>
#include "common/common_types.h"
#include "common/file_util.h"
-#include "core/file_sys/vfs.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
@@ -37,9 +36,10 @@ enum class FileType {
/**
* Identifies the type of a bootable file based on the magic value in its header.
* @param file open file
+ * @param filepath Path of the file that we are opening.
* @return FileType of file
*/
-FileType IdentifyFile(FileSys::VirtualFile file);
+FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath);
/**
* Identifies the type of a bootable file based on the magic value in its header.
@@ -50,12 +50,12 @@ FileType IdentifyFile(FileSys::VirtualFile file);
FileType IdentifyFile(const std::string& file_name);
/**
- * Guess the type of a bootable file from its name
- * @param name String name of bootable file
+ * Guess the type of a bootable file from its extension
+ * @param extension String extension of bootable 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 GuessFromFilename(const std::string& name);
+FileType GuessFromExtension(const std::string& extension);
/**
* Convert a FileType into a string which can be displayed to the user.
@@ -79,7 +79,7 @@ enum class ResultStatus {
/// Interface for loading an application
class AppLoader : NonCopyable {
public:
- AppLoader(FileSys::VirtualFile file) : file(std::move(file)) {}
+ AppLoader(FileUtil::IOFile&& file) : file(std::move(file)) {}
virtual ~AppLoader() {}
/**
@@ -154,20 +154,26 @@ public:
/**
* Get the RomFS of the application
* Since the RomFS can be huge, we return a file reference instead of copying to a buffer
- * @param file The file containing the RomFS
+ * @param romfs_file The file containing the RomFS
+ * @param offset The offset the romfs begins on
+ * @param size The size of the romfs
* @return ResultStatus result of function
*/
- virtual ResultStatus ReadRomFS(FileSys::VirtualFile& dir) {
+ virtual ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
+ u64& size) {
return ResultStatus::ErrorNotImplemented;
}
/**
* Get the update RomFS of the application
* Since the RomFS can be huge, we return a file reference instead of copying to a buffer
- * @param file The file containing the RomFS
+ * @param romfs_file The file containing the RomFS
+ * @param offset The offset the romfs begins on
+ * @param size The size of the romfs
* @return ResultStatus result of function
*/
- virtual ResultStatus ReadUpdateRomFS(FileSys::VirtualFile& file) {
+ virtual ResultStatus ReadUpdateRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
+ u64& size) {
return ResultStatus::ErrorNotImplemented;
}
@@ -181,7 +187,7 @@ public:
}
protected:
- FileSys::VirtualFile file;
+ FileUtil::IOFile file;
bool is_loaded = false;
};
@@ -196,6 +202,6 @@ extern const std::initializer_list<Kernel::AddressMapping> default_address_mappi
* @param filename String filename of bootable file
* @return best loader for this file
*/
-std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file);
+std::unique_ptr<AppLoader> GetLoader(const std::string& filename);
} // namespace Loader
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 874f42b91..da064f8e3 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -4,11 +4,13 @@
#include <vector>
+#include "common/common_funcs.h"
#include "common/file_util.h"
#include "common/logging/log.h"
+#include "common/swap.h"
#include "core/core.h"
-#include "core/file_sys/content_archive.h"
#include "core/file_sys/program_metadata.h"
+#include "core/file_sys/romfs_factory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -18,15 +20,208 @@
namespace Loader {
-AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file) : AppLoader(file) {}
+// Media offsets in headers are stored divided by 512. Mult. by this to get real offset.
+constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200;
-FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
- // TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support.
- FileSys::NCAHeader header{};
- if (sizeof(FileSys::NCAHeader) != file->ReadObject(&header))
+constexpr u64 SECTION_HEADER_SIZE = 0x200;
+constexpr u64 SECTION_HEADER_OFFSET = 0x400;
+
+enum class NcaContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 };
+
+enum class NcaSectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 };
+
+struct NcaSectionTableEntry {
+ u32_le media_offset;
+ u32_le media_end_offset;
+ INSERT_PADDING_BYTES(0x8);
+};
+static_assert(sizeof(NcaSectionTableEntry) == 0x10, "NcaSectionTableEntry has incorrect size.");
+
+struct NcaHeader {
+ std::array<u8, 0x100> rsa_signature_1;
+ std::array<u8, 0x100> rsa_signature_2;
+ u32_le magic;
+ u8 is_system;
+ NcaContentType content_type;
+ u8 crypto_type;
+ u8 key_index;
+ u64_le size;
+ u64_le title_id;
+ INSERT_PADDING_BYTES(0x4);
+ u32_le sdk_version;
+ u8 crypto_type_2;
+ INSERT_PADDING_BYTES(15);
+ std::array<u8, 0x10> rights_id;
+ std::array<NcaSectionTableEntry, 0x4> section_tables;
+ std::array<std::array<u8, 0x20>, 0x4> hash_tables;
+ std::array<std::array<u8, 0x10>, 0x4> key_area;
+ INSERT_PADDING_BYTES(0xC0);
+};
+static_assert(sizeof(NcaHeader) == 0x400, "NcaHeader has incorrect size.");
+
+struct NcaSectionHeaderBlock {
+ INSERT_PADDING_BYTES(3);
+ NcaSectionFilesystemType filesystem_type;
+ u8 crypto_type;
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(NcaSectionHeaderBlock) == 0x8, "NcaSectionHeaderBlock has incorrect size.");
+
+struct Pfs0Superblock {
+ NcaSectionHeaderBlock header_block;
+ std::array<u8, 0x20> hash;
+ u32_le size;
+ INSERT_PADDING_BYTES(4);
+ u64_le hash_table_offset;
+ u64_le hash_table_size;
+ u64_le pfs0_header_offset;
+ u64_le pfs0_size;
+ INSERT_PADDING_BYTES(432);
+};
+static_assert(sizeof(Pfs0Superblock) == 0x200, "Pfs0Superblock has incorrect size.");
+
+static bool IsValidNca(const NcaHeader& header) {
+ return header.magic == Common::MakeMagic('N', 'C', 'A', '2') ||
+ header.magic == Common::MakeMagic('N', 'C', 'A', '3');
+}
+
+// TODO(DarkLordZach): Add support for encrypted.
+class Nca final {
+ std::vector<FileSys::PartitionFilesystem> pfs;
+ std::vector<u64> pfs_offset;
+
+ u64 romfs_offset = 0;
+ u64 romfs_size = 0;
+
+ boost::optional<u8> exefs_id = boost::none;
+
+ FileUtil::IOFile file;
+ std::string path;
+
+ u64 GetExeFsFileOffset(const std::string& file_name) const;
+ u64 GetExeFsFileSize(const std::string& file_name) const;
+
+public:
+ ResultStatus Load(FileUtil::IOFile&& file, std::string path);
+
+ FileSys::PartitionFilesystem GetPfs(u8 id) const;
+
+ u64 GetRomFsOffset() const;
+ u64 GetRomFsSize() const;
+
+ std::vector<u8> GetExeFsFile(const std::string& file_name);
+};
+
+static bool IsPfsExeFs(const FileSys::PartitionFilesystem& pfs) {
+ // According to switchbrew, an exefs must only contain these two files:
+ return pfs.GetFileSize("main") > 0 && pfs.GetFileSize("main.npdm") > 0;
+}
+
+ResultStatus Nca::Load(FileUtil::IOFile&& in_file, std::string in_path) {
+ file = std::move(in_file);
+ path = in_path;
+ file.Seek(0, SEEK_SET);
+ std::array<u8, sizeof(NcaHeader)> header_array{};
+ if (sizeof(NcaHeader) != file.ReadBytes(header_array.data(), sizeof(NcaHeader)))
+ LOG_CRITICAL(Loader, "File reader errored out during header read.");
+
+ NcaHeader header{};
+ std::memcpy(&header, header_array.data(), sizeof(NcaHeader));
+ if (!IsValidNca(header))
+ return ResultStatus::ErrorInvalidFormat;
+
+ int number_sections =
+ std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
+ [](NcaSectionTableEntry entry) { return entry.media_offset > 0; });
+
+ for (int i = 0; i < number_sections; ++i) {
+ // Seek to beginning of this section.
+ file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET);
+ std::array<u8, sizeof(NcaSectionHeaderBlock)> array{};
+ if (sizeof(NcaSectionHeaderBlock) !=
+ file.ReadBytes(array.data(), sizeof(NcaSectionHeaderBlock)))
+ LOG_CRITICAL(Loader, "File reader errored out during header read.");
+
+ NcaSectionHeaderBlock block{};
+ std::memcpy(&block, array.data(), sizeof(NcaSectionHeaderBlock));
+
+ if (block.filesystem_type == NcaSectionFilesystemType::ROMFS) {
+ romfs_offset = header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
+ romfs_size =
+ header.section_tables[i].media_end_offset * MEDIA_OFFSET_MULTIPLIER - romfs_offset;
+ } else if (block.filesystem_type == NcaSectionFilesystemType::PFS0) {
+ Pfs0Superblock sb{};
+ // Seek back to beginning of this section.
+ file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET);
+ if (sizeof(Pfs0Superblock) != file.ReadBytes(&sb, sizeof(Pfs0Superblock)))
+ LOG_CRITICAL(Loader, "File reader errored out during header read.");
+
+ u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
+ MEDIA_OFFSET_MULTIPLIER) +
+ sb.pfs0_header_offset;
+ FileSys::PartitionFilesystem npfs{};
+ ResultStatus status = npfs.Load(path, offset);
+
+ if (status == ResultStatus::Success) {
+ pfs.emplace_back(std::move(npfs));
+ pfs_offset.emplace_back(offset);
+ }
+ }
+ }
+
+ for (size_t i = 0; i < pfs.size(); ++i) {
+ if (IsPfsExeFs(pfs[i]))
+ exefs_id = i;
+ }
+
+ return ResultStatus::Success;
+}
+
+FileSys::PartitionFilesystem Nca::GetPfs(u8 id) const {
+ return pfs[id];
+}
+
+u64 Nca::GetExeFsFileOffset(const std::string& file_name) const {
+ if (exefs_id == boost::none)
+ return 0;
+ return pfs[*exefs_id].GetFileOffset(file_name) + pfs_offset[*exefs_id];
+}
+
+u64 Nca::GetExeFsFileSize(const std::string& file_name) const {
+ if (exefs_id == boost::none)
+ return 0;
+ return pfs[*exefs_id].GetFileSize(file_name);
+}
+
+u64 Nca::GetRomFsOffset() const {
+ return romfs_offset;
+}
+
+u64 Nca::GetRomFsSize() const {
+ return romfs_size;
+}
+
+std::vector<u8> Nca::GetExeFsFile(const std::string& file_name) {
+ std::vector<u8> out(GetExeFsFileSize(file_name));
+ file.Seek(GetExeFsFileOffset(file_name), SEEK_SET);
+ file.ReadBytes(out.data(), GetExeFsFileSize(file_name));
+ return out;
+}
+
+AppLoader_NCA::AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath)
+ : AppLoader(std::move(file)), filepath(std::move(filepath)) {}
+
+FileType AppLoader_NCA::IdentifyType(FileUtil::IOFile& file, const std::string&) {
+ file.Seek(0, SEEK_SET);
+ std::array<u8, 0x400> header_enc_array{};
+ if (0x400 != file.ReadBytes(header_enc_array.data(), 0x400))
return FileType::Error;
- if (IsValidNCA(header) && header.content_type == FileSys::NCAContentType::Program)
+ // TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support.
+ NcaHeader header{};
+ std::memcpy(&header, header_enc_array.data(), sizeof(NcaHeader));
+
+ if (IsValidNca(header) && header.content_type == NcaContentType::Program)
return FileType::NCA;
return FileType::Error;
@@ -36,22 +231,17 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}
+ if (!file.IsOpen()) {
+ return ResultStatus::Error;
+ }
- nca = std::make_unique<FileSys::NCA>(file);
- ResultStatus result = nca->GetStatus();
+ nca = std::make_unique<Nca>();
+ ResultStatus result = nca->Load(std::move(file), filepath);
if (result != ResultStatus::Success) {
return result;
}
- if (nca->GetType() != FileSys::NCAContentType::Program)
- return ResultStatus::ErrorInvalidFormat;
-
- auto exefs = nca->GetExeFS();
-
- if (exefs == nullptr)
- return ResultStatus::ErrorInvalidFormat;
-
- result = metadata.Load(exefs->GetFile("main.npdm"));
+ result = metadata.Load(nca->GetExeFsFile("main.npdm"));
if (result != ResultStatus::Success) {
return result;
}
@@ -66,8 +256,7 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
const VAddr load_addr = next_load_addr;
-
- next_load_addr = AppLoader_NSO::LoadModule(exefs->GetFile(module), load_addr);
+ next_load_addr = AppLoader_NSO::LoadModule(module, nca->GetExeFsFile(module), load_addr);
if (next_load_addr) {
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
} else {
@@ -83,11 +272,28 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
metadata.GetMainThreadStackSize());
+ if (nca->GetRomFsSize() > 0)
+ Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this),
+ Service::FileSystem::Type::RomFS);
+
is_loaded = true;
+ return ResultStatus::Success;
+}
+
+ResultStatus AppLoader_NCA::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
+ u64& size) {
+ if (nca->GetRomFsSize() == 0) {
+ LOG_DEBUG(Loader, "No RomFS available");
+ return ResultStatus::ErrorNotUsed;
+ }
+
+ romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb");
+
+ offset = nca->GetRomFsOffset();
+ size = nca->GetRomFsSize();
- const auto romfs = nca->GetRomFS();
- if (romfs != nullptr)
- Service::FileSystem::RegisterRomFS(romfs);
+ LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset);
+ LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size);
return ResultStatus::Success;
}
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index a5639f149..3b6c451d0 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -6,37 +6,44 @@
#include <string>
#include "common/common_types.h"
-#include "core/file_sys/content_archive.h"
+#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/kernel.h"
#include "core/loader/loader.h"
namespace Loader {
+class Nca;
+
/// Loads an NCA file
class AppLoader_NCA final : public AppLoader {
public:
- explicit AppLoader_NCA(FileSys::VirtualFile file);
+ AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath);
/**
* Returns the type of the file
- * @param file std::shared_ptr<VfsFile> open file
+ * @param file FileUtil::IOFile open file
+ * @param filepath Path of the file that we are opening.
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
- static FileType IdentifyType(const FileSys::VirtualFile& file);
+ static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath);
FileType GetFileType() override {
- return IdentifyType(file);
+ return IdentifyType(file, filepath);
}
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
+ ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
+ u64& size) override;
+
~AppLoader_NCA();
private:
+ std::string filepath;
FileSys::ProgramMetadata metadata;
- std::unique_ptr<FileSys::NCA> nca;
+ std::unique_ptr<Nca> nca;
};
} // namespace Loader
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 08b7aad7a..3853cfa1a 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -47,12 +47,14 @@ struct ModHeader {
};
static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
-AppLoader_NRO::AppLoader_NRO(FileSys::VirtualFile file) : AppLoader(file) {}
+AppLoader_NRO::AppLoader_NRO(FileUtil::IOFile&& file, std::string filepath)
+ : AppLoader(std::move(file)), filepath(std::move(filepath)) {}
-FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& file) {
+FileType AppLoader_NRO::IdentifyType(FileUtil::IOFile& file, const std::string&) {
// Read NSO header
NroHeader nro_header{};
- if (sizeof(NroHeader) != file->ReadObject(&nro_header)) {
+ file.Seek(0, SEEK_SET);
+ if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) {
return FileType::Error;
}
if (nro_header.magic == Common::MakeMagic('N', 'R', 'O', '0')) {
@@ -65,10 +67,16 @@ static constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
-bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
+bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) {
+ FileUtil::IOFile file(path, "rb");
+ if (!file.IsOpen()) {
+ return {};
+ }
+
// Read NSO header
NroHeader nro_header{};
- if (sizeof(NroHeader) != file->ReadObject(&nro_header)) {
+ file.Seek(0, SEEK_SET);
+ if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) {
return {};
}
if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
@@ -77,9 +85,10 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
// Build program image
Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
- std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size));
- if (program_image.size() != PageAlignSize(nro_header.file_size))
- return {};
+ std::vector<u8> program_image;
+ program_image.resize(PageAlignSize(nro_header.file_size));
+ file.Seek(0, SEEK_SET);
+ file.ReadBytes(program_image.data(), nro_header.file_size);
for (int i = 0; i < nro_header.segments.size(); ++i) {
codeset->segments[i].addr = nro_header.segments[i].offset;
@@ -102,7 +111,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
// Load codeset for current process
- codeset->name = file->GetName();
+ codeset->name = path;
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
Core::CurrentProcess()->LoadModule(codeset, load_base);
@@ -113,11 +122,14 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}
+ if (!file.IsOpen()) {
+ return ResultStatus::Error;
+ }
// Load NRO
static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR};
- if (!LoadNro(file, base_addr)) {
+ if (!LoadNro(filepath, base_addr)) {
return ResultStatus::ErrorInvalidFormat;
}
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 2c03d06bb..599adb253 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -15,23 +15,26 @@ namespace Loader {
/// Loads an NRO file
class AppLoader_NRO final : public AppLoader, Linker {
public:
- AppLoader_NRO(FileSys::VirtualFile file);
+ AppLoader_NRO(FileUtil::IOFile&& file, std::string filepath);
/**
* Returns the type of the file
- * @param file std::shared_ptr<VfsFile> open file
+ * @param file FileUtil::IOFile open file
+ * @param filepath Path of the file that we are opening.
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
- static FileType IdentifyType(const FileSys::VirtualFile& file);
+ static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath);
FileType GetFileType() override {
- return IdentifyType(file);
+ return IdentifyType(file, filepath);
}
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
private:
- bool LoadNro(FileSys::VirtualFile file, VAddr load_base);
+ bool LoadNro(const std::string& path, VAddr load_base);
+
+ std::string filepath;
};
} // namespace Loader
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 2ed12a5ab..7f84e4b1b 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -37,7 +37,6 @@ struct NsoHeader {
std::array<u32_le, 3> segments_compressed_size;
};
static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
-static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable.");
struct ModHeader {
u32_le magic;
@@ -50,11 +49,15 @@ struct ModHeader {
};
static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
-AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
+AppLoader_NSO::AppLoader_NSO(FileUtil::IOFile&& file, std::string filepath)
+ : AppLoader(std::move(file)), filepath(std::move(filepath)) {}
-FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
+FileType AppLoader_NSO::IdentifyType(FileUtil::IOFile& file, const std::string&) {
u32 magic = 0;
- file->ReadObject(&magic);
+ file.Seek(0, SEEK_SET);
+ if (1 != file.ReadArray<u32>(&magic, 1)) {
+ return FileType::Error;
+ }
if (Common::MakeMagic('N', 'S', 'O', '0') == magic) {
return FileType::NSO;
@@ -95,27 +98,80 @@ static constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
-VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) {
- if (file == nullptr)
+VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>& file_data,
+ VAddr load_base) {
+ if (file_data.size() < sizeof(NsoHeader))
return {};
- if (file->GetSize() < sizeof(NsoHeader))
+ NsoHeader nso_header;
+ std::memcpy(&nso_header, file_data.data(), sizeof(NsoHeader));
+
+ if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
return {};
- NsoHeader nso_header{};
- if (sizeof(NsoHeader) != file->ReadObject(&nso_header))
+ // Build program image
+ Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
+ std::vector<u8> program_image;
+ for (int i = 0; i < nso_header.segments.size(); ++i) {
+ std::vector<u8> compressed_data(nso_header.segments_compressed_size[i]);
+ for (int j = 0; j < nso_header.segments_compressed_size[i]; ++j)
+ compressed_data[j] = file_data[nso_header.segments[i].offset + j];
+ std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]);
+ program_image.resize(nso_header.segments[i].location);
+ program_image.insert(program_image.end(), data.begin(), data.end());
+ codeset->segments[i].addr = nso_header.segments[i].location;
+ codeset->segments[i].offset = nso_header.segments[i].location;
+ codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
+ }
+
+ // MOD header pointer is at .text offset + 4
+ u32 module_offset;
+ std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
+
+ // Read MOD header
+ ModHeader mod_header{};
+ // Default .bss to size in segment header if MOD0 section doesn't exist
+ u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
+ std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader));
+ const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
+ if (has_mod_header) {
+ // Resize program image to include .bss section and page align each section
+ bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
+ }
+ codeset->data.size += bss_size;
+ const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)};
+ program_image.resize(image_size);
+
+ // Load codeset for current process
+ codeset->name = name;
+ codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
+ Core::CurrentProcess()->LoadModule(codeset, load_base);
+
+ return load_base + image_size;
+}
+
+VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) {
+ FileUtil::IOFile file(path, "rb");
+ if (!file.IsOpen()) {
return {};
+ }
- if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
+ // Read NSO header
+ NsoHeader nso_header{};
+ file.Seek(0, SEEK_SET);
+ if (sizeof(NsoHeader) != file.ReadBytes(&nso_header, sizeof(NsoHeader))) {
+ return {};
+ }
+ if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
return {};
+ }
// Build program image
Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
std::vector<u8> program_image;
for (int i = 0; i < nso_header.segments.size(); ++i) {
- const std::vector<u8> compressed_data =
- file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
- std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]);
+ std::vector<u8> data =
+ ReadSegment(file, nso_header.segments[i], nso_header.segments_compressed_size[i]);
program_image.resize(nso_header.segments[i].location);
program_image.insert(program_image.end(), data.begin(), data.end());
codeset->segments[i].addr = nso_header.segments[i].location;
@@ -142,7 +198,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) {
program_image.resize(image_size);
// Load codeset for current process
- codeset->name = file->GetName();
+ codeset->name = path;
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
Core::CurrentProcess()->LoadModule(codeset, load_base);
@@ -153,10 +209,13 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}
+ if (!file.IsOpen()) {
+ return ResultStatus::Error;
+ }
// Load module
- LoadModule(file, Memory::PROCESS_IMAGE_VADDR);
- LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), Memory::PROCESS_IMAGE_VADDR);
+ LoadModule(filepath, Memory::PROCESS_IMAGE_VADDR);
+ LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", filepath, Memory::PROCESS_IMAGE_VADDR);
process->svc_access_mask.set();
process->address_mappings = default_address_mappings;
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 3f7567500..386f4d39a 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -15,22 +15,29 @@ namespace Loader {
/// Loads an NSO file
class AppLoader_NSO final : public AppLoader, Linker {
public:
- explicit AppLoader_NSO(FileSys::VirtualFile file);
+ AppLoader_NSO(FileUtil::IOFile&& file, std::string filepath);
/**
* Returns the type of the file
- * @param file std::shared_ptr<VfsFile> open file
+ * @param file FileUtil::IOFile open file
+ * @param filepath Path of the file that we are opening.
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
- static FileType IdentifyType(const FileSys::VirtualFile& file);
+ static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath);
FileType GetFileType() override {
- return IdentifyType(file);
+ return IdentifyType(file, filepath);
}
- static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base);
+ static VAddr LoadModule(const std::string& name, const std::vector<u8>& file_data,
+ VAddr load_base);
+
+ static VAddr LoadModule(const std::string& path, VAddr load_base);
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
+
+private:
+ std::string filepath;
};
} // namespace Loader
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 3b70efeec..2bc1782ad 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -194,6 +194,13 @@ enum class UniformType : u64 {
Double = 5,
};
+enum class IMinMaxExchange : u64 {
+ None = 0,
+ XLo = 1,
+ XMed = 2,
+ XHi = 3,
+};
+
union Instruction {
Instruction& operator=(const Instruction& instr) {
value = instr.value;
@@ -279,6 +286,13 @@ union Instruction {
} alu_integer;
union {
+ BitField<39, 3, u64> pred;
+ BitField<42, 1, u64> negate_pred;
+ BitField<43, 2, IMinMaxExchange> exchange;
+ BitField<48, 1, u64> is_signed;
+ } imnmx;
+
+ union {
BitField<54, 1, u64> saturate;
BitField<56, 1, u64> negate_a;
} iadd32i;
@@ -700,9 +714,9 @@ private:
INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"),
INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"),
INST("0011100-01100---", Id::FMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"),
- INST("0100110000100---", Id::IMNMX_C, Type::Arithmetic, "FMNMX_IMM"),
- INST("0101110000100---", Id::IMNMX_R, Type::Arithmetic, "FMNMX_IMM"),
- INST("0011100-00100---", Id::IMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"),
+ INST("0100110000100---", Id::IMNMX_C, Type::ArithmeticInteger, "IMNMX_C"),
+ INST("0101110000100---", Id::IMNMX_R, Type::ArithmeticInteger, "IMNMX_R"),
+ INST("0011100-00100---", Id::IMNMX_IMM, Type::ArithmeticInteger, "IMNMX_IMM"),
INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"),
INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"),
INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"),
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index bacb389e1..ea138d402 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -775,10 +775,13 @@ void RasterizerOpenGL::SyncCullMode() {
state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face);
state.cull.mode = MaxwellToGL::CullFace(regs.cull.cull_face);
+ const bool flip_triangles{regs.screen_y_control.triangle_rast_flip == 0 ||
+ regs.viewport_transform[0].scale_y < 0.0f};
+
// If the GPU is configured to flip the rasterized triangles, then we need to flip the
// notion of front and back. Note: We flip the triangles when the value of the register is 0
// because OpenGL already does it for us.
- if (regs.screen_y_control.triangle_rast_flip == 0) {
+ if (flip_triangles) {
if (state.cull.front_face == GL_CCW)
state.cull.front_face = GL_CW;
else if (state.cull.front_face == GL_CW)
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 57d7763ff..323ff7408 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -102,6 +102,8 @@ 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_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
// DepthStencil formats
@@ -191,8 +193,9 @@ 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::ASTC_2D_4X4>, MortonCopy<true, PixelFormat::Z24S8>,
- MortonCopy<true, PixelFormat::S8Z24>, MortonCopy<true, PixelFormat::Z32F>,
+ MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
+ MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>,
+ MortonCopy<true, PixelFormat::Z32F>,
};
static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
@@ -206,7 +209,8 @@ 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 the DXT1/DXT23/DXT45/DXN1 formats is not yet supported
+ // TODO(Subv): Swizzling the DXT1/DXT23/DXT45/DXN1/BC7U formats is not yet supported
+ nullptr,
nullptr,
nullptr,
nullptr,
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index b4d7f8ebe..1bedae992 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -35,14 +35,15 @@ struct SurfaceParams {
DXT23 = 9,
DXT45 = 10,
DXN1 = 11, // This is also known as BC4
- ASTC_2D_4X4 = 12,
+ BC7U = 12,
+ ASTC_2D_4X4 = 13,
MaxColorFormat,
// DepthStencil formats
- Z24S8 = 13,
- S8Z24 = 14,
- Z32F = 15,
+ Z24S8 = 14,
+ S8Z24 = 15,
+ Z32F = 16,
MaxDepthStencilFormat,
@@ -92,6 +93,7 @@ struct SurfaceParams {
4, // DXT23
4, // DXT45
4, // DXN1
+ 4, // BC7U
4, // ASTC_2D_4X4
1, // Z24S8
1, // S8Z24
@@ -119,6 +121,7 @@ struct SurfaceParams {
128, // DXT23
128, // DXT45
64, // DXN1
+ 128, // BC7U
32, // ASTC_2D_4X4
32, // Z24S8
32, // S8Z24
@@ -192,6 +195,8 @@ struct SurfaceParams {
return PixelFormat::DXT45;
case Tegra::Texture::TextureFormat::DXN1:
return PixelFormat::DXN1;
+ case Tegra::Texture::TextureFormat::BC7U:
+ return PixelFormat::BC7U;
case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
return PixelFormat::ASTC_2D_4X4;
default:
@@ -227,6 +232,8 @@ struct SurfaceParams {
return Tegra::Texture::TextureFormat::DXT45;
case PixelFormat::DXN1:
return Tegra::Texture::TextureFormat::DXN1;
+ case PixelFormat::BC7U:
+ return Tegra::Texture::TextureFormat::BC7U;
case PixelFormat::ASTC_2D_4X4:
return Tegra::Texture::TextureFormat::ASTC_2D_4X4;
default:
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index e817aca5a..5914077e8 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1127,6 +1127,20 @@ private:
WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b);
break;
}
+ case OpCode::Id::IMNMX_C:
+ case OpCode::Id::IMNMX_R:
+ case OpCode::Id::IMNMX_IMM: {
+ ASSERT_MSG(instr.imnmx.exchange == Tegra::Shader::IMinMaxExchange::None,
+ "Unimplemented");
+ std::string condition =
+ GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0);
+ std::string parameters = op_a + ',' + op_b;
+ regs.SetRegisterToInteger(instr.gpr0, instr.imnmx.is_signed, 0,
+ '(' + condition + ") ? min(" + parameters + ") : max(" +
+ parameters + ')',
+ 1, 1);
+ break;
+ }
default: {
LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}",
opcode->GetName());
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index d5ab4e4f9..b3937b2fe 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -52,6 +52,7 @@ u32 BytesPerPixel(TextureFormat format) {
return 8;
case TextureFormat::DXT23:
case TextureFormat::DXT45:
+ case TextureFormat::BC7U:
// In this case a 'pixel' actually refers to a 4x4 tile.
return 16;
case TextureFormat::ASTC_2D_4X4:
@@ -98,6 +99,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width,
case TextureFormat::DXT23:
case TextureFormat::DXT45:
case TextureFormat::DXN1:
+ case TextureFormat::BC7U:
// In the DXT and DXN formats, each 4x4 tile is swizzled instead of just individual pixel
// values.
CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data,
@@ -155,6 +157,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
case TextureFormat::DXT23:
case TextureFormat::DXT45:
case TextureFormat::DXN1:
+ case TextureFormat::BC7U:
case TextureFormat::ASTC_2D_4X4:
case TextureFormat::A8R8G8B8:
case TextureFormat::A2B10G10R10:
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 5c17cd0d9..833085559 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -127,13 +127,14 @@ void GRenderWindow::moveContext() {
}
void GRenderWindow::SwapBuffers() {
-#if !defined(QT_NO_DEBUG)
- // Qt debug runtime prints a bogus warning on the console if you haven't called makeCurrent
- // since the last time you called swapBuffers. This presumably means something if you're using
- // QGLWidget the "regular" way, but in our multi-threaded use case is harmless since we never
- // call doneCurrent in this thread.
+ // In our multi-threaded QGLWidget use case we shouldn't need to call `makeCurrent`,
+ // since we never call `doneCurrent` in this thread.
+ // However:
+ // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called
+ // since the last time `swapBuffers` was executed;
+ // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks.
child->makeCurrent();
-#endif
+
child->swapBuffers();
}
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index be6cd6da4..5a708dc73 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -12,7 +12,6 @@
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "common/string_util.h"
-#include "core/file_sys/vfs_real.h"
#include "core/loader/loader.h"
#include "game_list.h"
#include "game_list_p.h"
@@ -405,8 +404,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
bool is_dir = FileUtil::IsDirectory(physical_name);
if (!is_dir &&
(HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
- std::unique_ptr<Loader::AppLoader> loader =
- Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(physical_name));
+ std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
if (!loader)
return true;