Compare commits

..

111 Commits

Author SHA1 Message Date
Hedges
26d74fefdc Merge 85d02d07fc into 316b933a31 2018-07-12 16:54:02 +00:00
Jarek Syrylak
85d02d07fc This auto should not actually be const 2018-07-12 17:53:47 +01:00
Jarek Syrylak
5a9ba8342f More tidy up as per comments on pull request 2018-07-12 17:41:14 +01:00
Jarek Syrylak
21a2512cfb More tidy up as per comments on pull request 2018-07-12 15:46:31 +01:00
Jarek Syrylak
33ac8e9a85 Tidy up as per comments on pull request 2018-07-12 12:23:12 +01:00
Jarek Syrylak
eca5df2573 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-12 09:38:35 +01:00
Jarek Syrylak
39840bd511 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-11 17:23:37 +01:00
Jarek Syrylak
7b31bed043 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-11 10:04:28 +01:00
Jarek Syrylak
d909d700ce Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-10 17:50:16 +01:00
Jarek Syrylak
198805b0fa Revert bad merge/rebase 2018-07-10 17:22:24 +01:00
Jarek Syrylak
05a019d4ee Merge branch 'master' of https://github.com/Hedges/yuzu 2018-07-10 16:39:04 +01:00
Jarek Syrylak
4b22a64d63 Compile fixes for Linux and macOS 2018-07-10 16:38:24 +01:00
Jarek Syrylak
e652110fda Register all loaded modules with GDBStub 2018-07-10 16:38:23 +01:00
Jarek Syrylak
9cb1f759ca Avoid crash in IsValidVirtualAddress() 2018-07-10 16:38:16 +01:00
Jarek Syrylak
4a657f5fb6 Register module with GDBStub. 2018-07-10 16:38:08 +01:00
Jarek Syrylak
20a3fbcc5a Fix handler for libraries query. 2018-07-10 16:38:01 +01:00
Jarek Syrylak
490893f119 Register NRO module. 2018-07-10 16:38:01 +01:00
Jarek Syrylak
430903a1ba Fix splitpath on Windows. 2018-07-10 16:38:00 +01:00
Jarek Syrylak
f7e6e204da Some cleanup. 2018-07-10 16:38:00 +01:00
Jarek Syrylak
1ffd10ce18 Add support for NSO modules. 2018-07-10 16:38:00 +01:00
Jarek Syrylak
c2ad530279 Initial support for floating point registers (wip). 2018-07-10 16:37:46 +01:00
Jarek Syrylak
625558e7b0 Reload registers from thread context after they have been changed in GDBStub. 2018-07-10 16:37:46 +01:00
Jarek Syrylak
fb8e336e95 Tidy up. 2018-07-10 16:37:45 +01:00
Jarek Syrylak
68e5f369c1 More improvements and diagnostics for GDBStub. 2018-07-10 16:37:45 +01:00
Jarek Syrylak
2f465d7572 More improvements and diagnostics for GDBStub. 2018-07-10 16:37:38 +01:00
Jarek Syrylak
c3ec14616f More improvements and diagnostics for GDBStub. 2018-07-10 16:37:38 +01:00
Jarek Syrylak
7e7f94f559 More improvements and diagnostics for GDBStub. 2018-07-10 16:37:38 +01:00
Jarek Syrylak
6bd605cb61 Work towards improving handling of threads in GDBStub. 2018-07-10 16:37:37 +01:00
Jarek Syrylak
b412594127 Extra diagnostics for multi-threaded breaks. 2018-07-10 16:37:37 +01:00
Jarek Syrylak
7f342ce736 In step mode only signal trap from current thread, in run mode signal always. 2018-07-10 16:37:37 +01:00
Jarek Syrylak
b5e29babc5 Looking for a thread by its id should _not_ set it as a current thread!
Returning proper list of threads for better integration with VS debugger.
2018-07-10 16:37:37 +01:00
Jarek Syrylak
008a2d35d8 Only send traps from GDB's current thread, otherwise we will always be thrown back to main thread when stepping. 2018-07-10 16:37:36 +01:00
Jarek Syrylak
f8f0913e2e Tidy up. 2018-07-10 16:37:36 +01:00
Jarek Syrylak
fdf73c177b Replaced htonll with swap64. 2018-07-10 16:37:36 +01:00
Jarek Syrylak
7212fd5227 Applied clang-format. 2018-07-10 16:37:35 +01:00
Jarek Syrylak
341517a5f2 GDB Stub should work now. 2018-07-10 16:37:35 +01:00
Jarek Syrylak
31bc72dd5b Compile fixes for Linux and macOS 2018-07-10 11:28:36 +01:00
Jarek Syrylak
bee01b8e6c Register all loaded modules with GDBStub 2018-07-10 11:02:34 +01:00
Jarek Syrylak
813fab2046 Avoid crash in IsValidVirtualAddress() 2018-07-10 10:25:34 +01:00
Jarek Syrylak
2b091f642f Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-10 10:21:50 +01:00
Jarek Syrylak
e39769b78a Merge branch 'master' of https://github.com/Hedges/yuzu 2018-07-09 09:52:19 +01:00
Jarek Syrylak
20f6ac28f9 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-08 11:01:43 +01:00
Jarek Syrylak
75767354c6 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-07 22:21:35 +01:00
Jarek Syrylak
35a3eb3467 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-07 10:14:07 +01:00
Jarek Syrylak
cdf1682f79 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-07 10:13:45 +01:00
Jarek Syrylak
7246b03e45 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-06 10:18:00 +01:00
Jarek Syrylak
1ba63bd4d7 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-05 19:33:38 +01:00
Jarek Syrylak
36460b5d58 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-05 17:54:26 +01:00
Jarek Syrylak
732b8305de Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-05 09:53:51 +01:00
Jarek Syrylak
22c008ea20 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-04 22:13:56 +01:00
Jarek Syrylak
c30cf74ef8 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-04 10:02:31 +01:00
Jarek Syrylak
3274826d28 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-03 19:00:24 +01:00
Jarek Syrylak
f69c7d738b Merge branch 'master' of https://github.com/Hedges/yuzu 2018-07-03 19:00:12 +01:00
Jarek Syrylak
c3e39280e6 Register module with GDBStub. 2018-07-03 09:59:38 +01:00
Jarek Syrylak
f18b555c4d Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-03 09:57:20 +01:00
Jarek Syrylak
9e73c92f3e Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-02 18:53:03 +01:00
Jarek Syrylak
ab7567e048 Merge branch 'master' of https://github.com/Hedges/yuzu 2018-07-02 09:55:15 +01:00
Jarek Syrylak
1cb53177cc Fix handler for libraries query. 2018-07-02 00:15:21 +01:00
Jarek Syrylak
38acb2a835 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-07-01 22:08:17 +01:00
Jarek Syrylak
aa020b7a9b Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-30 12:30:22 +01:00
Jarek Syrylak
90539362bc Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-29 00:10:19 +01:00
Jarek Syrylak
99acda51d2 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-28 09:56:31 +01:00
Jarek Syrylak
e7b518e8dd Register NRO module. 2018-06-27 23:47:51 +01:00
Jarek Syrylak
917db5cafe Fix splitpath on Windows. 2018-06-27 23:47:27 +01:00
Jarek Syrylak
1f73848198 Merge branch 'master' of https://github.com/Hedges/yuzu 2018-06-27 19:23:23 +01:00
Jarek Syrylak
82058c7275 Some cleanup. 2018-06-27 17:35:57 +01:00
Jarek Syrylak
1a18c59964 Add support for NSO modules. 2018-06-27 16:22:30 +01:00
Jarek Syrylak
f2d69f0dd9 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-27 10:35:09 +01:00
Jarek Syrylak
d4ec24bdbb Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-26 19:30:21 +01:00
Jarek Syrylak
f684b02719 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-26 10:10:05 +01:00
Jarek Syrylak
5c5950bd9e Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-25 10:00:26 +01:00
Jarek Syrylak
538418e4b0 Initial support for floating point registers (wip). 2018-06-24 23:06:07 +01:00
Jarek Syrylak
72fd238ff2 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-24 11:22:59 +01:00
Jarek Syrylak
74e3b223b7 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-23 13:11:42 +01:00
Jarek Syrylak
5d0afdeae6 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-22 22:15:37 +01:00
Jarek Syrylak
b0c05f5e86 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-21 10:05:27 +01:00
Jarek Syrylak
7f6bf91050 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-19 17:33:56 +01:00
Jarek Syrylak
ffa6a3dd5c Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-19 09:46:32 +01:00
Jarek Syrylak
a8026d328f Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-18 15:01:58 +01:00
Jarek Syrylak
b0067bb3e5 Merge branch 'master' of https://github.com/Hedges/yuzu 2018-06-12 19:16:27 +01:00
Jarek Syrylak
a35755dab8 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-12 16:41:20 +01:00
Jarek Syrylak
c5445c764d Merge branch 'master' of https://github.com/Hedges/yuzu 2018-06-11 17:08:25 +01:00
Jarek Syrylak
c40e897bc0 Reload registers from thread context after they have been changed in GDBStub. 2018-06-11 14:53:47 +01:00
Jarek Syrylak
01c168a460 Tidy up. 2018-06-11 09:50:40 +01:00
Jarek Syrylak
098fb7438e Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-11 09:42:25 +01:00
Jarek Syrylak
1d1646e53f Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-10 23:40:10 +01:00
Jarek Syrylak
4ab40a08ff Merge branch 'master' of https://github.com/Hedges/yuzu 2018-06-09 16:59:18 +01:00
Jarek Syrylak
6b37634175 More improvements and diagnostics for GDBStub. 2018-06-09 16:41:41 +01:00
Jarek Syrylak
e0ceb4ed70 More improvements and diagnostics for GDBStub. 2018-06-09 15:16:56 +01:00
Jarek Syrylak
806de51c6a More improvements and diagnostics for GDBStub. 2018-06-09 10:58:18 +01:00
Jarek Syrylak
f21550c836 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-09 10:25:29 +01:00
Jarek Syrylak
2adb30a137 More improvements and diagnostics for GDBStub. 2018-06-09 10:24:55 +01:00
Jarek Syrylak
1c002e1acc Work towards improving handling of threads in GDBStub. 2018-06-08 18:35:34 +01:00
Jarek Syrylak
d0d834ad2d Extra diagnostics for multi-threaded breaks. 2018-06-08 01:03:24 +01:00
Jarek Syrylak
56a5d60276 In step mode only signal trap from current thread, in run mode signal always. 2018-06-07 23:40:03 +01:00
Jarek Syrylak
4346150b6f Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-07 19:45:08 +01:00
Jarek Syrylak
586c14670e Merge branch 'master' of https://github.com/Hedges/yuzu 2018-06-07 19:44:48 +01:00
Jarek Syrylak
5ce0d95f1c Looking for a thread by its id should _not_ set it as a current thread!
Returning proper list of threads for better integration with VS debugger.
2018-06-07 18:20:33 +01:00
Jarek Syrylak
f76bceb714 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-07 09:53:34 +01:00
Jarek Syrylak
1615d56d0f Merge branch 'master' of https://github.com/Hedges/yuzu 2018-06-06 21:12:27 +01:00
Jarek Syrylak
2f61f8ff13 Only send traps from GDB's current thread, otherwise we will always be thrown back to main thread when stepping. 2018-06-06 15:05:23 +01:00
Jarek Syrylak
cbadda89c1 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-06 14:59:23 +01:00
Jarek Syrylak
ae86329474 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-06 09:45:23 +01:00
Jarek Syrylak
b0d52753c1 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-05 19:22:30 +01:00
Jarek Syrylak
8171771fa8 Merge branch 'master' of https://github.com/Hedges/yuzu 2018-06-05 19:22:05 +01:00
Jarek Syrylak
f3ea3523b2 Tidy up. 2018-06-05 12:56:02 +01:00
Jarek Syrylak
84963f2f20 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-04 23:37:50 +01:00
Jarek Syrylak
33a4892279 Merge branch 'master' of https://github.com/yuzu-emu/yuzu 2018-06-04 22:23:05 +01:00
Jarek Syrylak
f417fbcd47 Replaced htonll with swap64. 2018-06-04 17:05:07 +01:00
Jarek Syrylak
62fd45d040 Applied clang-format. 2018-06-04 16:46:08 +01:00
Jarek Syrylak
a78a5003fa GDB Stub should work now. 2018-06-04 16:03:01 +01:00
130 changed files with 2508 additions and 2794 deletions

2
externals/fmt vendored

View File

@@ -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,71 +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 GetPathWithoutTop(std::string path) {
if (path.empty())
return "";
while (path[0] == '\\' || path[0] == '/') {
path = path.substr(1);
if (path.empty())
return "";
}
const auto name_bck_index = path.find_first_of('\\');
const auto name_fwd_index = path.find_first_of('/');
return path.substr(std::min<size_t>(name_bck_index, name_fwd_index) + 1);
return path.substr(std::min<size_t>(name_bck_index, name_fwd_index) + 1);
}
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) {
@@ -886,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) {
@@ -902,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 {
@@ -920,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 {
@@ -932,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

View File

@@ -150,34 +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 up to the last '/' or '\' in the path.
std::string GetParentPath(const std::string& path);
// Gets all of the text after the first '/' or '\' in the path.
std::string GetPathWithoutTop(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
@@ -200,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);
}
@@ -245,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);
@@ -253,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

View File

@@ -5,7 +5,6 @@
#include <algorithm>
#include <array>
#include <chrono>
#include <climits>
#include <condition_variable>
#include <memory>
#include <thread>
@@ -84,10 +83,8 @@ private:
}
};
while (true) {
{
std::unique_lock<std::mutex> lock(message_mutex);
message_cv.wait(lock, [&] { return !running || message_queue.Pop(entry); });
}
std::unique_lock<std::mutex> lock(message_mutex);
message_cv.wait(lock, [&] { return !running || message_queue.Pop(entry); });
if (!running) {
break;
}
@@ -95,7 +92,7 @@ private:
}
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case
// where a system is repeatedly spamming logs even on close.
const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100;
constexpr int MAX_LOGS_TO_WRITE = 100;
int logs_written = 0;
while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
write_logs(entry);
@@ -285,4 +282,4 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
Impl::Instance().PushEntry(std::move(entry));
}
} // namespace Log
} // namespace Log

View File

@@ -94,11 +94,4 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
bool Filter::CheckMessage(Class log_class, Level level) const {
return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]);
}
bool Filter::IsDebug() const {
return std::any_of(class_levels.begin(), class_levels.end(), [](const Level& l) {
return static_cast<u8>(l) <= static_cast<u8>(Level::Debug);
});
}
} // namespace Log

View File

@@ -47,9 +47,6 @@ public:
/// Matches class/level combination against the filter, returning true if it passed.
bool CheckMessage(Class log_class, Level level) const;
/// Returns true if any logging classes are set to debug
bool IsDebug() const;
private:
std::array<Level, (size_t)Class::Count> class_levels;
};

View File

@@ -103,7 +103,7 @@ template <typename... Args>
void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function, const char* format, const Args&... args) {
FmtLogMessageImpl(log_class, log_level, filename, line_num, function, format,
fmt::make_format_args(args...));
fmt::make_args(args...));
}
} // namespace Log

View File

@@ -52,14 +52,27 @@ public:
template <typename T>
class Field : public FieldInterface {
public:
Field(FieldType type, std::string name, T value)
Field(FieldType type, std::string name, const T& value)
: name(std::move(name)), type(type), value(value) {}
Field(FieldType type, std::string name, T&& value)
: name(std::move(name)), type(type), value(std::move(value)) {}
Field(const Field&) = default;
Field& operator=(const Field&) = default;
Field(const Field& other) : Field(other.type, other.name, other.value) {}
Field(Field&&) = default;
Field& operator=(Field&& other) = default;
Field& operator=(const Field& other) {
type = other.type;
name = other.name;
value = other.value;
return *this;
}
Field& operator=(Field&& other) {
type = other.type;
name = std::move(other.name);
value = std::move(other.value);
return *this;
}
void Accept(VisitorInterface& visitor) const override;
@@ -81,11 +94,11 @@ public:
return value;
}
bool operator==(const Field& other) const {
inline bool operator==(const Field<T>& other) {
return (type == other.type) && (name == other.name) && (value == other.value);
}
bool operator!=(const Field& other) const {
inline bool operator!=(const Field<T>& other) {
return !(*this == other);
}

View File

@@ -8,27 +8,27 @@ 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/mode.h
file_sys/filesystem.cpp
file_sys/filesystem.h
file_sys/partition_filesystem.cpp
file_sys/partition_filesystem.h
file_sys/path_parser.cpp
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/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
file_sys/storage.h
frontend/emu_window.cpp
frontend/emu_window.h
frontend/framebuffer_layout.cpp

View File

@@ -116,8 +116,6 @@ public:
*/
virtual void LoadContext(const ThreadContext& ctx) = 0;
virtual void ClearExclusiveState() = 0;
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
};

View File

@@ -226,10 +226,6 @@ void ARM_Dynarmic::ClearInstructionCache() {
jit->ClearCache();
}
void ARM_Dynarmic::ClearExclusiveState() {
jit->ClearExclusiveState();
}
void ARM_Dynarmic::PageTableChanged() {
jit = MakeJit(cb);
current_page_table = Memory::GetCurrentPageTable();

View File

@@ -39,7 +39,6 @@ public:
void LoadContext(const ThreadContext& ctx) override;
void PrepareReschedule() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
void PageTableChanged() override;

View File

@@ -263,8 +263,6 @@ void ARM_Unicorn::PrepareReschedule() {
CHECKED(uc_emu_stop(uc));
}
void ARM_Unicorn::ClearExclusiveState() {}
void ARM_Unicorn::ClearInstructionCache() {}
void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {

View File

@@ -31,7 +31,6 @@ public:
void SaveContext(ThreadContext& ctx) override;
void LoadContext(const ThreadContext& ctx) override;
void PrepareReschedule() override;
void ClearExclusiveState() override;
void ExecuteInstructions(int num_instructions);
void Run() override;
void Step() override;

View File

@@ -19,20 +19,17 @@
#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 {
/*static*/ System System::s_instance;
System::System() = default;
System::~System() = default;
/// Runs a CPU core while the system is powered on
static void RunCpuCore(std::shared_ptr<Cpu> cpu_state) {
while (Core::System::GetInstance().IsPoweredOn()) {
while (Core::System().GetInstance().IsPoweredOn()) {
cpu_state->RunLoop(true);
}
}
@@ -87,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);

View File

@@ -168,8 +168,6 @@ public:
}
private:
System();
/// Returns the currently running CPU core
Cpu& CurrentCpuCore();

View File

@@ -1,167 +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

View File

@@ -1,95 +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.");
inline 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;
}
inline 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

View File

@@ -8,17 +8,13 @@
#include <cstddef>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/file_sys/filesystem.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
enum EntryType : u8 {
Directory = 0,
File = 1,
};
// Structure of a directory entry, from
// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
const size_t FILENAME_LENGTH = 0x300;

View File

@@ -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

View File

@@ -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

View File

@@ -11,9 +11,7 @@ namespace FileSys {
namespace ErrCodes {
enum {
NotFound = 1,
TitleNotFound = 1002,
SdCardNotFound = 2001,
RomFSNotFound = 2520,
SaveDataNotFound = 1002,
};
}

View File

@@ -0,0 +1,122 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstddef>
#include <iomanip>
#include <sstream>
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/file_sys/filesystem.h"
#include "core/memory.h"
namespace FileSys {
Path::Path(LowPathType type, u32 size, u32 pointer) : type(type) {
switch (type) {
case Binary: {
binary.resize(size);
Memory::ReadBlock(pointer, binary.data(), binary.size());
break;
}
case Char: {
string.resize(size - 1); // Data is always null-terminated.
Memory::ReadBlock(pointer, &string[0], string.size());
break;
}
case Wchar: {
u16str.resize(size / 2 - 1); // Data is always null-terminated.
Memory::ReadBlock(pointer, &u16str[0], u16str.size() * sizeof(char16_t));
break;
}
default:
break;
}
}
std::string Path::DebugStr() const {
switch (GetType()) {
case Invalid:
default:
return "[Invalid]";
case Empty:
return "[Empty]";
case Binary: {
std::stringstream res;
res << "[Binary: ";
for (unsigned byte : binary)
res << std::hex << std::setw(2) << std::setfill('0') << byte;
res << ']';
return res.str();
}
case Char:
return "[Char: " + AsString() + ']';
case Wchar:
return "[Wchar: " + AsString() + ']';
}
}
std::string Path::AsString() const {
switch (GetType()) {
case Char:
return string;
case Wchar:
return Common::UTF16ToUTF8(u16str);
case Empty:
return {};
case Invalid:
case Binary:
default:
// TODO(yuriks): Add assert
LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!");
return {};
}
}
std::u16string Path::AsU16Str() const {
switch (GetType()) {
case Char:
return Common::UTF8ToUTF16(string);
case Wchar:
return u16str;
case Empty:
return {};
case Invalid:
case Binary:
// TODO(yuriks): Add assert
LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!");
return {};
}
UNREACHABLE();
}
std::vector<u8> Path::AsBinary() const {
switch (GetType()) {
case Binary:
return binary;
case Char:
return std::vector<u8>(string.begin(), string.end());
case Wchar: {
// use two u8 for each character of u16str
std::vector<u8> to_return(u16str.size() * 2);
for (size_t i = 0; i < u16str.size(); ++i) {
u16 tmp_char = u16str.at(i);
to_return[i * 2] = (tmp_char & 0xFF00) >> 8;
to_return[i * 2 + 1] = (tmp_char & 0x00FF);
}
return to_return;
}
case Empty:
return {};
case Invalid:
default:
// TODO(yuriks): Add assert
LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!");
return {};
}
}
} // namespace FileSys

View File

@@ -0,0 +1,201 @@
// 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 <utility>
#include <vector>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/result.h"
namespace FileSys {
class StorageBackend;
class DirectoryBackend;
// Path string type
enum LowPathType : u32 {
Invalid = 0,
Empty = 1,
Binary = 2,
Char = 3,
Wchar = 4,
};
enum EntryType : u8 {
Directory = 0,
File = 1,
};
enum class Mode : u32 {
Read = 1,
Write = 2,
Append = 4,
};
class Path {
public:
Path() : type(Invalid) {}
Path(const char* path) : type(Char), string(path) {}
Path(std::vector<u8> binary_data) : type(Binary), binary(std::move(binary_data)) {}
Path(LowPathType type, u32 size, u32 pointer);
LowPathType GetType() const {
return type;
}
/**
* Gets the string representation of the path for debugging
* @return String representation of the path for debugging
*/
std::string DebugStr() const;
std::string AsString() const;
std::u16string AsU16Str() const;
std::vector<u8> AsBinary() const;
private:
LowPathType type;
std::vector<u8> binary;
std::string string;
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

View File

@@ -1,17 +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_types.h"
namespace FileSys {
enum class Mode : u32 {
Read = 1,
Write = 2,
Append = 4,
};
} // namespace FileSys

View File

@@ -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) {
// At least be as large as the header
if (file->GetSize() < sizeof(Header)) {
status = Loader::ResultStatus::Error;
return;
}
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))
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);
memcpy(&pfs_header, file_data.data(), sizeof(Header));
return result;
}
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;
FileEntry 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]));
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;
return Loader::ResultStatus::Success;
}
Loader::ResultStatus PartitionFilesystem::GetStatus() const {
return status;
u32 PartitionFilesystem::GetNumEntries() const {
return pfs_header.num_entries;
}
std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const {
return pfs_files;
u64 PartitionFilesystem::GetEntryOffset(u32 index) const {
if (index > GetNumEntries())
return 0;
return content_offset + pfs_entries[index].fs_entry.offset;
}
std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const {
return {};
u64 PartitionFilesystem::GetEntrySize(u32 index) const {
if (index > GetNumEntries())
return 0;
return pfs_entries[index].fs_entry.size;
}
std::string PartitionFilesystem::GetName() const {
return is_hfs ? "HFS0" : "PFS0";
std::string PartitionFilesystem::GetEntryName(u32 index) const {
if (index > GetNumEntries())
return "";
return pfs_entries[index].name;
}
std::shared_ptr<VfsDirectory> PartitionFilesystem::GetParentDirectory() const {
// TODO(DarkLordZach): Add support for nested containers.
return nullptr;
u64 PartitionFilesystem::GetFileOffset(const std::string& name) const {
for (u32 i = 0; i < pfs_header.num_entries; i++) {
if (pfs_entries[i].name == name)
return content_offset + pfs_entries[i].fs_entry.offset;
}
return 0;
}
void PartitionFilesystem::PrintDebugInfo() const {
LOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic);
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;
}
return 0;
}
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_files[i]->GetName(), pfs_files[i]->GetSize(),
dynamic_cast<OffsetVfsFile*>(pfs_files[i].get())->GetOffset());
pfs_entries[i].name.c_str(), pfs_entries[i].fs_entry.size,
GetFileOffset(pfs_entries[i].name));
}
}
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;
pfs_files[iter - pfs_files.begin()] = pfs_files.back();
pfs_files.pop_back();
pfs_dirs.emplace_back(dir);
return true;
}
} // namespace FileSys

View File

@@ -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

View File

@@ -0,0 +1,98 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <set>
#include "common/file_util.h"
#include "common/string_util.h"
#include "core/file_sys/path_parser.h"
namespace FileSys {
PathParser::PathParser(const Path& path) {
if (path.GetType() != LowPathType::Char && path.GetType() != LowPathType::Wchar) {
is_valid = false;
return;
}
auto path_string = path.AsString();
if (path_string.size() == 0 || path_string[0] != '/') {
is_valid = false;
return;
}
// Filter out invalid characters for the host system.
// Although some of these characters are valid on 3DS, they are unlikely to be used by games.
if (std::find_if(path_string.begin(), path_string.end(), [](char c) {
static const std::set<char> invalid_chars{'<', '>', '\\', '|', ':', '\"', '*', '?'};
return invalid_chars.find(c) != invalid_chars.end();
}) != path_string.end()) {
is_valid = false;
return;
}
Common::SplitString(path_string, '/', path_sequence);
auto begin = path_sequence.begin();
auto end = path_sequence.end();
end = std::remove_if(begin, end, [](std::string& str) { return str == "" || str == "."; });
path_sequence = std::vector<std::string>(begin, end);
// checks if the path is out of bounds.
int level = 0;
for (auto& node : path_sequence) {
if (node == "..") {
--level;
if (level < 0) {
is_valid = false;
return;
}
} else {
++level;
}
}
is_valid = true;
is_root = level == 0;
}
PathParser::HostStatus PathParser::GetHostStatus(const std::string& mount_point) const {
auto path = mount_point;
if (!FileUtil::IsDirectory(path))
return InvalidMountPoint;
if (path_sequence.empty()) {
return DirectoryFound;
}
for (auto iter = path_sequence.begin(); iter != path_sequence.end() - 1; iter++) {
if (path.back() != '/')
path += '/';
path += *iter;
if (!FileUtil::Exists(path))
return PathNotFound;
if (FileUtil::IsDirectory(path))
continue;
return FileInPath;
}
path += "/" + path_sequence.back();
if (!FileUtil::Exists(path))
return NotFound;
if (FileUtil::IsDirectory(path))
return DirectoryFound;
return FileFound;
}
std::string PathParser::BuildHostPath(const std::string& mount_point) const {
std::string path = mount_point;
for (auto& node : path_sequence) {
if (path.back() != '/')
path += '/';
path += node;
}
return path;
}
} // namespace FileSys

View File

@@ -0,0 +1,61 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <vector>
#include "core/file_sys/filesystem.h"
namespace FileSys {
/**
* A helper class parsing and verifying a string-type Path.
* Every archives with a sub file system should use this class to parse the path argument and check
* the status of the file / directory in question on the host file system.
*/
class PathParser {
public:
explicit PathParser(const Path& path);
/**
* Checks if the Path is valid.
* This function should be called once a PathParser is constructed.
* A Path is valid if:
* - it is a string path (with type LowPathType::Char or LowPathType::Wchar),
* - it starts with "/" (this seems a hard requirement in real 3DS),
* - it doesn't contain invalid characters, and
* - it doesn't go out of the root directory using "..".
*/
bool IsValid() const {
return is_valid;
}
/// Checks if the Path represents the root directory.
bool IsRootDirectory() const {
return is_root;
}
enum HostStatus {
InvalidMountPoint,
PathNotFound, // "/a/b/c" when "a" doesn't exist
FileInPath, // "/a/b/c" when "a" is a file
FileFound, // "/a/b/c" when "c" is a file
DirectoryFound, // "/a/b/c" when "c" is a directory
NotFound // "/a/b/c" when "a/b/" exists but "c" doesn't exist
};
/// Checks the status of the specified file / directory by the Path on the host file system.
HostStatus GetHostStatus(const std::string& mount_point) const;
/// Builds a full path on the host file system.
std::string BuildHostPath(const std::string& mount_point) const;
private:
std::vector<std::string> path_sequence;
bool is_valid{};
bool is_root{};
};
} // namespace FileSys

View File

@@ -9,29 +9,40 @@
namespace FileSys {
Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
size_t total_size = static_cast<size_t>(file->GetSize());
Loader::ResultStatus ProgramMetadata::Load(const std::string& file_path) {
FileUtil::IOFile file(file_path, "rb");
if (!file.IsOpen())
return Loader::ResultStatus::Error;
std::vector<u8> file_data(file.GetSize());
if (!file.ReadBytes(file_data.data(), file_data.size()))
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);
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;
// 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));
size_t header_offset = offset;
memcpy(&npdm_header, &file_data[offset], sizeof(Header));
std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset);
if (sizeof(AcidHeader) != acid_header_data.size())
return Loader::ResultStatus::Error;
std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader));
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));
if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset))
return Loader::ResultStatus::Error;
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 Loader::ResultStatus::Error;
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;
}

View File

@@ -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;

View File

@@ -7,19 +7,32 @@
#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 {
RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) {
// Load the RomFS from the app
if (Loader::ResultStatus::Success != app_loader.ReadRomFS(file)) {
if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) {
LOG_ERROR(Service_FS, "Unable to read RomFS!");
}
}
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id) {
// TODO(DarkLordZach): Use title id.
return MakeResult<VirtualFile>(file);
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

View File

@@ -5,21 +5,31 @@
#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 RomFSFactory {
class RomFS_Factory final : public FileSystemFactory {
public:
explicit RomFSFactory(Loader::AppLoader& app_loader);
explicit RomFS_Factory(Loader::AppLoader& app_loader);
ResultVal<VirtualFile> Open(u64 title_id);
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:
VirtualFile file;
std::shared_ptr<FileUtil::IOFile> romfs_file;
u64 data_offset;
u64 data_size;
};
} // namespace FileSys

View File

@@ -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

View File

@@ -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

View File

@@ -6,101 +6,58 @@
#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 {
std::string SaveDataDescriptor::DebugInfo() {
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]",
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id);
}
SaveData_Factory::SaveData_Factory(std::string nand_directory)
: nand_directory(std::move(nand_directory)) {}
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {}
ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path& path) {
std::string save_directory = GetFullPath();
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) {
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
if (meta.zero_1 != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataDescriptor, type is "
"SystemSaveData||SaveData but offset 0x28 is non-zero ({:016X}).",
meta.zero_1);
}
if (meta.zero_2 != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataDescriptor, type is "
"SystemSaveData||SaveData but offset 0x30 is non-zero ({:016X}).",
meta.zero_2);
}
if (meta.zero_3 != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataDescriptor, type is "
"SystemSaveData||SaveData but offset 0x38 is non-zero ({:016X}).",
meta.zero_3);
}
}
if (meta.type == SaveDataType::SystemSaveData && meta.title_id != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataDescriptor, type is SystemSaveData but title_id is "
"non-zero ({:016X}).",
meta.title_id);
}
std::string save_directory =
GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
// TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods.
// But, user_ids don't match so this works for now.
auto out = dir->GetDirectoryRelative(save_directory);
if (out == nullptr) {
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.
out = dir->CreateDirectoryRelative(save_directory);
FileUtil::CreateFullPath(save_directory);
}
// Return an error if the save data doesn't actually exist.
if (out == nullptr) {
if (!FileUtil::IsDirectory(save_directory)) {
// TODO(Subv): Find out correct error code.
return ResultCode(-1);
}
return MakeResult<VirtualDir>(std::move(out));
auto archive = std::make_unique<Disk_FileSystem>(save_directory);
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
}
std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
u128 user_id, u64 save_id) const {
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
// be interpreted as the title id of the current process.
if (type == SaveDataType::SaveData && title_id == 0)
title_id = Core::CurrentProcess()->program_id;
std::string out;
switch (space) {
case SaveDataSpaceId::NandSystem:
out = "/system/save/";
break;
case SaveDataSpaceId::NandUser:
out = "/user/save/";
break;
default:
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
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);
}
switch (type) {
case SaveDataType::SystemSaveData:
return fmt::format("{}{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
case SaveDataType::SaveData:
return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
default:
ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
}
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

View File

@@ -7,52 +7,27 @@
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/filesystem.h"
#include "core/hle/result.h"
namespace FileSys {
enum class SaveDataSpaceId : u8 {
NandSystem = 0,
NandUser = 1,
SdCard = 2,
TemporaryStorage = 3,
};
enum class SaveDataType : u8 {
SystemSaveData = 0,
SaveData = 1,
BcatDeliveryCacheStorage = 2,
DeviceSaveData = 3,
TemporaryStorage = 4,
CacheStorage = 5,
};
struct SaveDataDescriptor {
u64_le title_id;
u128 user_id;
u64_le save_id;
SaveDataType type;
INSERT_PADDING_BYTES(7);
u64_le zero_1;
u64_le zero_2;
u64_le zero_3;
std::string DebugInfo();
};
static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size.");
/// File system interface to the SaveData archive
class SaveDataFactory {
class SaveData_Factory final : public FileSystemFactory {
public:
explicit SaveDataFactory(VirtualDir dir);
explicit SaveData_Factory(std::string nand_directory);
ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
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:
VirtualDir dir;
std::string nand_directory;
std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id,
u64 save_id) const;
std::string GetFullPath() const;
};
} // namespace FileSys

View File

@@ -3,15 +3,37 @@
// 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 {
SDMCFactory::SDMCFactory(VirtualDir dir) : dir(std::move(dir)) {}
SDMC_Factory::SDMC_Factory(std::string sd_directory) : sd_directory(std::move(sd_directory)) {}
ResultVal<VirtualDir> SDMCFactory::Open() {
return MakeResult<VirtualDir>(dir);
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

View File

@@ -4,19 +4,28 @@
#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 SDMCFactory {
class SDMC_Factory final : public FileSystemFactory {
public:
explicit SDMCFactory(VirtualDir dir);
explicit SDMC_Factory(std::string sd_directory);
ResultVal<VirtualDir> Open();
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:
VirtualDir dir;
std::string sd_directory;
};
} // namespace FileSys

View File

@@ -0,0 +1,63 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include "common/common_types.h"
#include "core/hle/result.h"
namespace FileSys {
class StorageBackend : NonCopyable {
public:
StorageBackend() {}
virtual ~StorageBackend() {}
/**
* Read data from the file
* @param offset Offset in bytes to start reading data from
* @param length Length in bytes of data to read from file
* @param buffer Buffer to read data into
* @return Number of bytes read, or error code
*/
virtual ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const = 0;
/**
* Write data to the file
* @param offset Offset in bytes to start writing data to
* @param length Length in bytes of data to write to file
* @param flush The flush parameters (0 == do not flush)
* @param buffer Buffer to read data from
* @return Number of bytes written, or error code
*/
virtual ResultVal<size_t> Write(u64 offset, size_t length, bool flush,
const u8* buffer) const = 0;
/**
* Flushes the file
*/
virtual void Flush() const = 0;
/**
* Set the size of the file in bytes
* @param size New size of the file
* @return true if successful
*/
virtual bool SetSize(u64 size) const = 0;
/**
* Get the size of the file in bytes
* @return Size of the file in bytes
*/
virtual u64 GetSize() const = 0;
/**
* Close the file
* @return true if the file closed correctly
*/
virtual bool Close() const = 0;
};
} // namespace FileSys

View File

@@ -1,238 +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 "common/logging/backend.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 component = 1; component < vec.size() - 1; ++component) {
if (dir == nullptr)
return nullptr;
dir = dir->GetSubdirectory(vec[component]);
}
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())
// TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently
// because of const-ness
return nullptr;
auto dir = GetSubdirectory(vec[0]);
for (size_t component = 1; component < vec.size(); ++component) {
if (dir == nullptr)
return nullptr;
dir = dir->GetSubdirectory(vec[component]);
}
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 sum_sizes = [](const auto& range) {
return std::accumulate(range.begin(), range.end(), 0ULL,
[](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); });
};
const auto file_total = sum_sizes(files);
const auto& sub_dir = GetSubdirectories();
const auto subdir_total = sum_sizes(sub_dir);
return file_total + subdir_total;
}
std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(const std::string& path) {
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 CreateFile(vec[0]);
auto dir = GetSubdirectory(vec[0]);
if (dir == nullptr) {
dir = CreateSubdirectory(vec[0]);
if (dir == nullptr)
return nullptr;
}
return dir->CreateFileRelative(FileUtil::GetPathWithoutTop(path));
}
std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(const std::string& path) {
if (IsRoot())
return CreateFileRelative(path);
return GetParentDirectory()->CreateFileAbsolute(path);
}
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(const std::string& path) {
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 CreateSubdirectory(vec[0]);
auto dir = GetSubdirectory(vec[0]);
if (dir == nullptr) {
dir = CreateSubdirectory(vec[0]);
if (dir == nullptr)
return nullptr;
}
return dir->CreateDirectoryRelative(FileUtil::GetPathWithoutTop(path));
}
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(const std::string& path) {
if (IsRoot())
return CreateDirectoryRelative(path);
return GetParentDirectory()->CreateDirectoryAbsolute(path);
}
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

View File

@@ -1,237 +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;
// Creates a new file at the path relative to this directory. Also creates directories if
// they do not exist and is supported by this implementation. Returns nullptr on any failure.
virtual std::shared_ptr<VfsFile> CreateFileRelative(const std::string& path);
// Creates a new file at the path relative to root of this directory. Also creates directories
// if they do not exist and is supported by this implementation. Returns nullptr on any failure.
virtual std::shared_ptr<VfsFile> CreateFileAbsolute(const std::string& path);
// Creates a new directory at the path relative to this directory. Also creates directories if
// they do not exist and is supported by this implementation. Returns nullptr on any failure.
virtual std::shared_ptr<VfsDirectory> CreateDirectoryRelative(const std::string& path);
// Creates a new directory at the path relative to root of this directory. Also creates
// directories if they do not exist and is supported by this implementation. Returns nullptr on
// any failure.
virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(const std::string& path);
// 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

View File

@@ -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

View File

@@ -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

View File

@@ -1,177 +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;
}
bool RealVfsFile::Close() {
return backing.Close();
}
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)
return;
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) {
auto file = GetFile(name);
if (file == nullptr)
return false;
files.erase(std::find(files.begin(), files.end(), file));
auto real_file = std::static_pointer_cast<RealVfsFile>(file);
real_file->Close();
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

View File

@@ -1,69 +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/mode.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 {
friend struct RealVfsDirectory;
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:
bool Close();
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

View File

@@ -68,10 +68,6 @@ const u32 CPSR_REGISTER = 33;
const u32 UC_ARM64_REG_Q0 = 34;
const u32 FPSCR_REGISTER = 66;
// TODO/WiP - Used while working on support for FPU
const u32 TODO_DUMMY_REG_997 = 997;
const u32 TODO_DUMMY_REG_998 = 998;
// For sample XML files see the GDB source /gdb/features
// GDB also wants the l character at the start
// This XML defines what the registers are for this specific ARM device
@@ -569,7 +565,7 @@ static void HandleQuery() {
} else if (strncmp(query, "Supported", strlen("Supported")) == 0) {
// PacketSize needs to be large enough for target xml
std::string buffer = "PacketSize=2000;qXfer:features:read+;qXfer:threads:read+";
if (!modules.empty()) {
if (modules.size()) {
buffer += ";qXfer:libraries:read+";
}
SendReply(buffer.c_str());
@@ -778,9 +774,10 @@ static void ReadRegister() {
} else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) {
LongToGdbHex(reply, RegRead(id, current_thread));
} else if (id == FPSCR_REGISTER) {
LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread));
LongToGdbHex(reply, RegRead(998, current_thread));
} else {
LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread));
// return SendReply("E01");
LongToGdbHex(reply, RegRead(997, current_thread));
}
SendReply(reinterpret_cast<char*>(reply));
@@ -813,7 +810,7 @@ static void ReadRegisters() {
bufptr += 32 * 32;
LongToGdbHex(bufptr, RegRead(TODO_DUMMY_REG_998, current_thread));
LongToGdbHex(bufptr, RegRead(998, current_thread));
bufptr += 8;
@@ -840,12 +837,12 @@ static void WriteRegister() {
} else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) {
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
} else if (id == FPSCR_REGISTER) {
RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread);
RegWrite(998, GdbHexToLong(buffer_ptr), current_thread);
} else {
RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread);
// return SendReply("E01");
RegWrite(997, GdbHexToLong(buffer_ptr), current_thread);
}
// Update Unicorn context skipping scheduler, no running threads at this point
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
SendReply("OK");
@@ -868,13 +865,12 @@ static void WriteRegisters() {
} else if (reg >= UC_ARM64_REG_Q0 && reg < FPSCR_REGISTER) {
RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
} else if (reg == FPSCR_REGISTER) {
RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread);
RegWrite(998, GdbHexToLong(buffer_ptr + i * 16), current_thread);
} else {
UNIMPLEMENTED();
}
}
// Update Unicorn context skipping scheduler, no running threads at this point
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
SendReply("OK");
@@ -945,7 +941,6 @@ void Break(bool is_memory_break) {
static void Step() {
if (command_length > 1) {
RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread);
// Update Unicorn context skipping scheduler, no running threads at this point
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
}
step_loop = true;

View File

@@ -65,7 +65,7 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num
// Signal the waiting threads.
for (size_t i = 0; i < last; i++) {
ASSERT(waiting_threads[i]->status == THREADSTATUS_WAIT_ARB);
ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB);
waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
waiting_threads[i]->arb_wait_address = 0;
waiting_threads[i]->ResumeFromWait();
@@ -115,7 +115,7 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu
s32 updated_value;
if (waiting_threads.size() == 0) {
updated_value = value - 1;
} else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
} else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) {
updated_value = value + 1;
} else {
updated_value = value;
@@ -140,9 +140,7 @@ ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool
s32 cur_value = static_cast<s32>(Memory::Read32(address));
if (cur_value < value) {
if (should_decrement) {
Memory::Write32(address, static_cast<u32>(cur_value - 1));
}
Memory::Write32(address, static_cast<u32>(cur_value - 1));
} else {
return ERR_INVALID_STATE;
}

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <utility>
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
#include "common/common_funcs.h"
@@ -21,18 +19,17 @@ namespace Kernel {
void SessionRequestHandler::ClientConnected(SharedPtr<ServerSession> server_session) {
server_session->SetHleHandler(shared_from_this());
connected_sessions.push_back(std::move(server_session));
connected_sessions.push_back(server_session);
}
void SessionRequestHandler::ClientDisconnected(const SharedPtr<ServerSession>& server_session) {
void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_session) {
server_session->SetHleHandler(nullptr);
boost::range::remove_erase(connected_sessions, server_session);
}
SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
const std::string& reason, u64 timeout,
WakeupCallback&& callback,
Kernel::SharedPtr<Kernel::Event> event) {
WakeupCallback&& callback) {
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
thread->wakeup_callback =
@@ -44,12 +41,7 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
return true;
};
if (!event) {
// Create event if not provided
event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
}
event->Clear();
auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
thread->status = THREADSTATUS_WAIT_HLE_EVENT;
thread->wait_objects = {event};
event->AddWaitingThread(thread);
@@ -222,8 +214,8 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
(sizeof(IPC::CommandHeader) + sizeof(IPC::HandleDescriptorHeader)) / sizeof(u32);
ASSERT_MSG(!handle_descriptor_header->send_current_pid, "Sending PID is not implemented");
ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move);
ASSERT_MSG(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
ASSERT_MSG(move_objects.size() == handle_descriptor_header->num_handles_to_move);
// We don't make a distinction between copy and move handles when translating since HLE
// services don't deal with handles directly. However, the guest applications might check

View File

@@ -59,12 +59,12 @@ public:
* associated ServerSession.
* @param server_session ServerSession associated with the connection.
*/
void ClientDisconnected(const SharedPtr<ServerSession>& server_session);
void ClientDisconnected(SharedPtr<ServerSession> server_session);
protected:
/// List of sessions that are connected to this handler.
/// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list
/// for the duration of the connection.
// for the duration of the connection.
std::vector<SharedPtr<ServerSession>> connected_sessions;
};
@@ -118,12 +118,10 @@ public:
* @param callback Callback to be invoked when the thread is resumed. This callback must write
* the entire command response once again, regardless of the state of it before this function
* was called.
* @param event Event to use to wake up the thread. If unspecified, an event will be created.
* @returns Event that when signaled will resume the thread and call the callback function.
*/
SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
u64 timeout, WakeupCallback&& callback,
Kernel::SharedPtr<Kernel::Event> event = nullptr);
u64 timeout, WakeupCallback&& callback);
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <utility>
#include "common/assert.h"
#include "core/hle/kernel/object_address_table.h"
@@ -13,7 +11,7 @@ ObjectAddressTable g_object_address_table;
void ObjectAddressTable::Insert(VAddr addr, SharedPtr<Object> obj) {
ASSERT_MSG(objects.find(addr) == objects.end(), "Object already exists with addr=0x{:X}", addr);
objects[addr] = std::move(obj);
objects[addr] = obj;
}
void ObjectAddressTable::Close(VAddr addr) {

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <utility>
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/process.h"
@@ -87,7 +85,6 @@ void Scheduler::SwitchContext(Thread* new_thread) {
cpu_core->LoadContext(new_thread->context);
cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
cpu_core->ClearExclusiveState();
} else {
current_thread = nullptr;
// Note: We do not reset the current process and current page table when idling because
@@ -115,7 +112,7 @@ void Scheduler::Reschedule() {
void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
thread_list.push_back(std::move(thread));
thread_list.push_back(thread);
ready_queue.prepare(priority);
}

View File

@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include <tuple>
#include <utility>
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
@@ -159,7 +158,7 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& n
std::shared_ptr<Session> parent(new Session);
parent->client = client_session.get();
parent->server = server_session.get();
parent->port = std::move(port);
parent->port = port;
client_session->parent = parent;
server_session->parent = parent;

View File

@@ -2,7 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <utility>
#include <cstring>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
@@ -21,7 +21,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
MemoryRegion region, std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory);
shared_memory->owner_process = std::move(owner_process);
shared_memory->owner_process = owner_process;
shared_memory->name = std::move(name);
shared_memory->size = size;
shared_memory->permissions = permissions;
@@ -87,7 +87,7 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(std::shared_ptr<std::vecto
shared_memory->size = size;
shared_memory->permissions = permissions;
shared_memory->other_permissions = other_permissions;
shared_memory->backing_block = std::move(heap_block);
shared_memory->backing_block = heap_block;
shared_memory->backing_block_offset = offset;
shared_memory->base_address = Memory::HEAP_VADDR + offset;

View File

@@ -165,7 +165,7 @@ void Thread::CancelWakeupTimer() {
static boost::optional<s32> GetNextProcessorId(u64 mask) {
for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) {
if (mask & (1ULL << index)) {
if (!Core::System::GetInstance().Scheduler(index)->GetCurrentThread()) {
if (!Core::System().GetInstance().Scheduler(index)->GetCurrentThread()) {
// Core is enabled and not running any threads, use this one
return index;
}
@@ -215,14 +215,14 @@ void Thread::ResumeFromWait() {
new_processor_id = processor_id;
}
if (ideal_core != -1 &&
Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
Core::System().GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
new_processor_id = ideal_core;
}
ASSERT(*new_processor_id < 4);
// Add thread to new core's scheduler
auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id);
auto& next_scheduler = Core::System().GetInstance().Scheduler(*new_processor_id);
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler
@@ -325,7 +325,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
thread->name = std::move(name);
thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap();
thread->owner_process = owner_process;
thread->scheduler = Core::System::GetInstance().Scheduler(processor_id);
thread->scheduler = Core::System().GetInstance().Scheduler(processor_id);
thread->scheduler->AddThread(thread, priority);
// Find the next available TLS index, and mark it as used
@@ -400,7 +400,7 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
// Initialize new "main" thread
auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
Memory::STACK_AREA_VADDR_END, std::move(owner_process));
Memory::STACK_AREA_VADDR_END, owner_process);
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
@@ -481,14 +481,14 @@ void Thread::ChangeCore(u32 core, u64 mask) {
new_processor_id = processor_id;
}
if (ideal_core != -1 &&
Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
Core::System().GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
new_processor_id = ideal_core;
}
ASSERT(*new_processor_id < 4);
// Add thread to new core's scheduler
auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id);
auto& next_scheduler = Core::System().GetInstance().Scheduler(*new_processor_id);
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler

View File

@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include <iterator>
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
@@ -17,20 +16,30 @@
namespace Kernel {
static const char* GetMemoryStateName(MemoryState state) {
static constexpr const char* names[] = {
"Unmapped", "Io",
"Normal", "CodeStatic",
"CodeMutable", "Heap",
"Shared", "Unknown1",
"ModuleCodeStatic", "ModuleCodeMutable",
"IpcBuffer0", "Mapped",
"ThreadLocal", "TransferMemoryIsolated",
"TransferMemory", "ProcessMemory",
"Unknown2", "IpcBuffer1",
"IpcBuffer3", "KernelStack",
static const char* names[] = {
"Unmapped",
"Io",
"Normal",
"CodeStatic",
"CodeMutable",
"Heap",
"Shared",
"Unknown1"
"ModuleCodeStatic",
"ModuleCodeMutable",
"IpcBuffer0",
"Mapped",
"ThreadLocal",
"TransferMemoryIsolated",
"TransferMemory",
"ProcessMemory",
"Unknown2"
"IpcBuffer1",
"IpcBuffer3",
"KernelStack",
};
return names[static_cast<int>(state)];
return names[(int)state];
}
bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
@@ -108,7 +117,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
final_vma.type = VMAType::AllocatedMemoryBlock;
final_vma.permissions = VMAPermission::ReadWrite;
final_vma.meminfo_state = state;
final_vma.backing_block = std::move(block);
final_vma.backing_block = block;
final_vma.offset = offset;
UpdatePageTableForVMA(final_vma);
@@ -151,7 +160,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u6
final_vma.permissions = VMAPermission::ReadWrite;
final_vma.meminfo_state = state;
final_vma.paddr = paddr;
final_vma.mmio_handler = std::move(mmio_handler);
final_vma.mmio_handler = mmio_handler;
UpdatePageTableForVMA(final_vma);
return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));

View File

@@ -12,7 +12,7 @@ class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> module, const char* name);
Interface(std::shared_ptr<Module> module, const char* name);
void GetUserExistence(Kernel::HLERequestContext& ctx);
void ListAllUsers(Kernel::HLERequestContext& ctx);

View File

@@ -4,10 +4,9 @@
#include <cinttypes>
#include <stack>
#include "core/core.h"
#include "core/file_sys/filesystem.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
@@ -615,14 +614,25 @@ void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u128 uid = rp.PopRaw<u128>(); // What does this do?
u128 uid = rp.PopRaw<u128>();
LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
FileSys::Path unused;
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);
// TODO(Subv): Find out the correct error code for this.
rb.Push(ResultCode(ErrorModule::FS, 40));
} else {
rb.Push(RESULT_SUCCESS);
}
rb.Push<u64>(0);
} // namespace Service::AM
}
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
// Takes an input u32 Result, no output.

View File

@@ -72,7 +72,7 @@ public:
class ISelfController final : public ServiceFramework<ISelfController> {
public:
explicit ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
private:
void SetFocusHandlingMode(Kernel::HLERequestContext& ctx);

View File

@@ -12,7 +12,7 @@ namespace Service::AM {
class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
public:
explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)) {
static const FunctionInfo functions[] = {
{0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},

View File

@@ -17,7 +17,7 @@ namespace AM {
class AppletAE final : public ServiceFramework<AppletAE> {
public:
explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
~AppletAE() = default;
private:

View File

@@ -12,7 +12,7 @@ namespace Service::AM {
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
public:
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) {
static const FunctionInfo functions[] = {
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},

View File

@@ -17,7 +17,7 @@ namespace AM {
class AppletOE final : public ServiceFramework<AppletOE> {
public:
explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
~AppletOE() = default;
private:

View File

@@ -10,7 +10,7 @@ namespace Service::APM {
class APM final : public ServiceFramework<APM> {
public:
explicit APM(std::shared_ptr<Module> apm, const char* name);
APM(std::shared_ptr<Module> apm, const char* name);
~APM() = default;
private:

View File

@@ -17,7 +17,7 @@ constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)};
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
explicit IAudioRenderer(AudioRendererParameter audren_params)
IAudioRenderer(AudioRendererParameter audren_params)
: ServiceFramework("IAudioRenderer"), worker_params(audren_params) {
static const FunctionInfo functions[] = {
{0, nullptr, "GetAudioRendererSampleRate"},
@@ -47,7 +47,7 @@ public:
// Start the audio event
CoreTiming::ScheduleEvent(audio_ticks, audio_event);
voice_status_list.resize(worker_params.voice_count);
voice_status_list.reserve(worker_params.voice_count);
}
~IAudioRenderer() {
CoreTiming::UnscheduleEvent(audio_event, 0);
@@ -87,6 +87,8 @@ private:
memory_pool[i].state = MemoryPoolStates::Attached;
else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach)
memory_pool[i].state = MemoryPoolStates::Detached;
else
memory_pool[i].state = mem_pool_info[i].pool_state;
}
std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(),
response_data.memory_pools_size);
@@ -176,14 +178,12 @@ private:
struct UpdateDataHeader {
UpdateDataHeader() {}
explicit UpdateDataHeader(const AudioRendererParameter& config) {
UpdateDataHeader(const AudioRendererParameter& config) {
revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
behavior_size = 0xb0;
memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
voices_size = config.voice_count * 0x10;
voice_resource_size = 0x0;
effects_size = config.effect_count * 0x10;
mixes_size = 0x0;
sinks_size = config.sink_count * 0x20;
performance_manager_size = 0x10;
total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size +

View File

@@ -12,7 +12,7 @@ class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> module, const char* name);
Interface(std::shared_ptr<Module> module, const char* name);
void CreateBcatService(Kernel::HLERequestContext& ctx);

View File

@@ -12,7 +12,7 @@ class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> module, const char* name);
Interface(std::shared_ptr<Module> module, const char* name);
void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx);
void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx);

View File

@@ -2,281 +2,71 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma optimize("", off)
#include "common/assert.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/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs_real.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) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
if (!file->Resize(size)) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const {
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (path == "/" || path == "\\") {
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
return RESULT_SUCCESS;
}
if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
if (!backing->DeleteFile(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
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) {
// TODO(DarkLordZach): Find a better error code for this
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))) {
// TODO(DarkLordZach): Find a better error code for this
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))) {
// TODO(DarkLordZach): Find a better error code for this
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))) {
// TODO(DarkLordZach): Find a better error code for this
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))) {
// TODO(DarkLordZach): Find a better error code for this
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))) {
// TODO(DarkLordZach): Find a better error code for this
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);
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path,
FileSys::Mode mode) const {
auto npath = path;
while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\'))
npath = npath.substr(1);
auto file = backing->GetFileRelative(npath);
if (file == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
if (mode == FileSys::Mode::Append) {
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) {
// TODO(DarkLordZach): Find a better error code for this
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 FileSys::ERROR_PATH_NOT_FOUND;
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 FileSys::ERROR_PATH_NOT_FOUND;
}
/**
* Map of registered file systems, identified by type. Once an file system is registered here, it
* is never removed until UnregisterFileSystems is called.
*/
static std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
static std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
static std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map;
ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second RomFS");
romfs_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered RomFS");
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}", filesystem->GetName(),
static_cast<u32>(type));
return RESULT_SUCCESS;
}
ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second save data");
save_data_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered save data");
return RESULT_SUCCESS;
}
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
FileSys::Path& path) {
LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type));
ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC");
sdmc_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered SDMC");
return RESULT_SUCCESS;
}
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id) {
LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}", title_id);
if (romfs_factory == nullptr) {
auto itr = filesystem_map.find(type);
if (itr == filesystem_map.end()) {
// TODO(bunnei): Find a better error code for this
return ResultCode(-1);
}
return romfs_factory->Open(title_id);
return itr->second->Open(path);
}
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
FileSys::SaveDataDescriptor save_struct) {
LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
static_cast<u8>(space), save_struct.DebugInfo());
ResultCode FormatFileSystem(Type type) {
LOG_TRACE(Service_FS, "Formatting FileSystem with type={}", static_cast<u32>(type));
if (save_data_factory == nullptr) {
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound);
auto itr = filesystem_map.find(type);
if (itr == filesystem_map.end()) {
// TODO(bunnei): Find a better error code for this
return ResultCode(-1);
}
return save_data_factory->Open(space, save_struct);
}
ResultVal<FileSys::VirtualDir> OpenSDMC() {
LOG_TRACE(Service_FS, "Opening SDMC");
if (sdmc_factory == nullptr) {
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SdCardNotFound);
}
return sdmc_factory->Open();
FileSys::Path unused;
return itr->second->Format(unused);
}
void RegisterFileSystems() {
romfs_factory = nullptr;
save_data_factory = nullptr;
sdmc_factory = nullptr;
filesystem_map.clear();
auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>(
FileUtil::GetUserPath(D_NAND_IDX), FileSys::Mode::Write);
auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>(
FileUtil::GetUserPath(D_SDMC_IDX), FileSys::Mode::Write);
std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX);
auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
save_data_factory = std::move(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::SDMCFactory>(std::move(sd_directory));
sdmc_factory = std::move(sdcard);
auto sdcard = std::make_unique<FileSys::SDMC_Factory>(std::move(sd_directory));
RegisterFileSystem(std::move(sdcard), Type::SDMC);
}
void InstallInterfaces(SM::ServiceManager& service_manager) {

View File

@@ -6,13 +6,14 @@
#include <memory>
#include "common/common_types.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/hle/result.h"
namespace FileSys {
class FileSystemBackend;
class FileSystemFactory;
class Path;
} // namespace FileSys
namespace Service {
namespace SM {
@@ -21,117 +22,38 @@ class ServiceManager;
namespace FileSystem {
ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
/// Supported FileSystem types
enum class Type {
RomFS = 1,
SaveData = 2,
SDMC = 3,
};
// TODO(DarkLordZach): BIS Filesystem
// ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id);
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
FileSys::SaveDataDescriptor save_struct);
ResultVal<FileSys::VirtualDir> OpenSDMC();
/**
* 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<FileSys::FileSystemFactory>&& factory, Type type);
// TODO(DarkLordZach): BIS Filesystem
// ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS();
/**
* Opens a file system
* @param type Type of the file system to open
* @param path Path to the file system, used with Binary paths
* @return FileSys::FileSystemBackend interface to the file system
*/
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
FileSys::Path& path);
/**
* Formats a file system
* @param type Type of the file system to format
* @return ResultCode of the operation
*/
ResultCode FormatFileSystem(Type type);
/// Registers all Filesystem services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
// 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;
};
} // namespace FileSystem
} // namespace Service

View File

@@ -8,26 +8,20 @@
#include "core/core.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/filesystem.h"
#include "core/file_sys/storage.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_srv.h"
namespace Service::FileSystem {
enum class StorageId : u8 {
None = 0,
Host = 1,
GameCard = 2,
NandSystem = 3,
NandUser = 4,
SdCard = 5,
};
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"},
@@ -36,7 +30,7 @@ public:
}
private:
FileSys::VirtualFile backend;
std::unique_ptr<FileSys::StorageBackend> backend;
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -58,7 +52,14 @@ private:
}
// Read the data from the Storage backend
std::vector<u8> output = backend->ReadBytes(length, offset);
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());
return;
}
// Write the data to memory
ctx.WriteBuffer(output);
@@ -69,8 +70,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 +81,7 @@ public:
}
private:
FileSys::VirtualFile backend;
std::unique_ptr<FileSys::StorageBackend> backend;
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -103,14 +104,20 @@ private:
}
// Read the data from the Storage backend
std::vector<u8> output = backend->ReadBytes(length, offset);
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());
return;
}
// Write the data to memory
ctx.WriteBuffer(output);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(output.size()));
rb.Push(static_cast<u64>(*res));
}
void Write(Kernel::HLERequestContext& ctx) {
@@ -133,21 +140,14 @@ private:
return;
}
std::vector<u8> data = ctx.ReadBuffer();
std::vector<u8> actual_data(length);
ASSERT_MSG(
data.size() <= length,
"Attempting to write more data than requested (requested={:016X}, actual={:016X}).",
length, data.size());
std::copy(data.begin(), data.end(), actual_data.begin());
// Write the data to the Storage backend
auto written = backend->WriteBytes(data, offset);
ASSERT_MSG(written == length,
"Could not write all bytes to file (requested={:016X}, actual={:016X}).", length,
written);
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());
return;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -155,8 +155,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 +164,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,39 +181,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);
// TODO(DarkLordZach): Verify that this is the correct behavior.
// 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};
@@ -225,31 +204,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);
@@ -259,7 +233,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"},
@@ -294,7 +268,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) {
@@ -306,7 +280,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) {
@@ -318,7 +292,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) {
@@ -336,7 +310,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) {
@@ -349,14 +323,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);
@@ -374,14 +348,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);
@@ -396,7 +370,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());
@@ -416,7 +390,7 @@ public:
}
private:
VfsDirectoryServiceWrapper backend;
std::unique_ptr<FileSys::FileSystemBackend> backend;
};
FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
@@ -513,6 +487,17 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
RegisterHandlers(functions);
}
void FSP_SRV::TryLoadRomFS() {
if (romfs) {
return;
}
FileSys::Path unused;
auto res = OpenFileSystem(Type::RomFS, unused);
if (res.Succeeded()) {
romfs = std::move(res.Unwrap());
}
}
void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
@@ -523,7 +508,8 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
IFileSystem filesystem(OpenSDMC().Unwrap());
FileSys::Path unused;
auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -533,38 +519,33 @@ void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
auto save_struct = rp.PopRaw<std::array<u8, 0x40>>();
auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
u128 uid = rp.PopRaw<u128>();
LOG_WARNING(Service_FS, "(STUBBED) called save_struct = {}, uid = {:016X}{:016X}",
save_struct.DebugInfo(), uid[1], uid[0]);
LOG_WARNING(Service_FS, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
LOG_WARNING(Service_FS, "(STUBBED) called");
auto space_id = rp.PopRaw<FileSys::SaveDataSpaceId>();
auto unk = rp.Pop<u32>();
LOG_INFO(Service_FS, "called with unknown={:08X}", unk);
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
// TODO(Subv): Read the input parameters and mount the requested savedata instead of always
// mounting the current process' savedata.
FileSys::Path unused;
auto filesystem = OpenFileSystem(Type::SaveData, unused);
auto dir = OpenSaveData(space_id, save_struct);
if (dir.Failed()) {
if (filesystem.Failed()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::SaveDataNotFound));
return;
}
IFileSystem filesystem(std::move(dir.Unwrap()));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
rb.PushIpcInterface<IFileSystem>(std::move(filesystem.Unwrap()));
}
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
@@ -578,8 +559,8 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
auto romfs = OpenRomFS(Core::System::GetInstance().CurrentProcess()->program_id);
if (romfs.Failed()) {
TryLoadRomFS();
if (!romfs) {
// TODO (bunnei): Find the right error code to use here
LOG_CRITICAL(Service_FS, "no file system interface available!");
IPC::ResponseBuilder rb{ctx, 2};
@@ -587,24 +568,23 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
return;
}
IStorage storage(std::move(romfs.Unwrap()));
// 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) {
IPC::RequestParser rp{ctx};
auto storage_id = rp.PopRaw<StorageId>();
auto title_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}",
static_cast<u8>(storage_id), title_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
LOG_WARNING(Service_FS, "(STUBBED) called, using OpenDataStorageByCurrentProcess");
OpenDataStorageByCurrentProcess(ctx);
}
} // namespace Service::FileSystem

View File

@@ -19,6 +19,8 @@ public:
~FSP_SRV() = default;
private:
void TryLoadRomFS();
void Initialize(Kernel::HLERequestContext& ctx);
void MountSdCard(Kernel::HLERequestContext& ctx);
void CreateSaveData(Kernel::HLERequestContext& ctx);
@@ -27,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

View File

@@ -12,7 +12,7 @@ class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> module, const char* name);
Interface(std::shared_ptr<Module> module, const char* name);
void CreateFriendService(Kernel::HLERequestContext& ctx);

View File

@@ -5,7 +5,6 @@
#include <atomic>
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/frontend/input.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
@@ -19,9 +18,9 @@ namespace Service::HID {
// Updating period for each HID device.
// TODO(shinyquagsire23): These need better values.
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
@@ -64,8 +63,7 @@ private:
std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
// TODO(shinyquagsire23): gyro, mouse, keyboard
// TODO(shinyquagsire23): gyro, touch, mouse, keyboard
}
void UpdatePadCallback(u64 userdata, int cycles_late) {
@@ -153,6 +151,8 @@ private:
}
}
// TODO(bunnei): Properly implement the touch screen, the below will just write empty data
TouchScreen& touchscreen = mem.touchscreen;
const u64 last_entry = touchscreen.header.latest_entry;
const u64 curr_entry = (last_entry + 1) % touchscreen.entries.size();
@@ -164,26 +164,7 @@ private:
touchscreen.header.max_entry_index = touchscreen.entries.size();
touchscreen.header.timestamp = timestamp;
touchscreen.entries[curr_entry].header.timestamp = sample_counter;
TouchScreenEntryTouch touch_entry{};
auto [x, y, pressed] = touch_device->GetStatus();
touch_entry.timestamp = timestamp;
touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
touch_entry.touch_index = 0;
// TODO(DarkLordZach): Maybe try to derive these from EmuWindow?
touch_entry.diameter_x = 15;
touch_entry.diameter_y = 15;
touch_entry.angle = 0;
// TODO(DarkLordZach): Implement multi-touch support
if (pressed) {
touchscreen.entries[curr_entry].header.num_touches = 1;
touchscreen.entries[curr_entry].touches[0] = touch_entry;
} else {
touchscreen.entries[curr_entry].header.num_touches = 0;
}
touchscreen.entries[curr_entry].header.num_touches = 0;
// TODO(shinyquagsire23): Properly implement mouse
Mouse& mouse = mem.mouse;
@@ -269,7 +250,6 @@ private:
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
buttons;
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
std::unique_ptr<Input::TouchDevice> touch_device;
};
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {

View File

@@ -12,7 +12,7 @@ class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> module, const char* name);
Interface(std::shared_ptr<Module> module, const char* name);
void CreateUserInterface(Kernel::HLERequestContext& ctx);

View File

@@ -12,7 +12,7 @@ class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> module, const char* name);
Interface(std::shared_ptr<Module> module, const char* name);
void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx);
void CreateGeneralService(Kernel::HLERequestContext& ctx);

View File

@@ -18,8 +18,7 @@ u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector
}
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform,
const MathUtil::Rectangle<int>& crop_rect) {
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform) {
VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
LOG_WARNING(Service,
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
@@ -27,8 +26,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
using PixelFormat = Tegra::FramebufferConfig::PixelFormat;
const Tegra::FramebufferConfig framebuffer{
addr, offset, width, height, stride, static_cast<PixelFormat>(format),
transform, crop_rect};
addr, offset, width, height, stride, static_cast<PixelFormat>(format), transform};
Core::System::GetInstance().perf_stats.EndGameFrame();

View File

@@ -7,7 +7,6 @@
#include <memory>
#include <vector>
#include "common/common_types.h"
#include "common/math_util.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
@@ -17,15 +16,14 @@ class nvmap;
class nvdisp_disp0 final : public nvdevice {
public:
explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvdevice(), nvmap_dev(std::move(nvmap_dev)) {}
~nvdisp_disp0() = default;
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
/// Performs a screen flip, drawing the buffer pointed to by the handle.
void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
NVFlinger::BufferQueue::BufferTransformFlags transform,
const MathUtil::Rectangle<int>& crop_rect);
NVFlinger::BufferQueue::BufferTransformFlags transform);
private:
std::shared_ptr<nvmap> nvmap_dev;

View File

@@ -18,7 +18,7 @@ class nvmap;
class nvhost_as_gpu final : public nvdevice {
public:
explicit nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
~nvhost_as_gpu() override = default;
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;

View File

@@ -18,7 +18,7 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8);
class nvhost_gpu final : public nvdevice {
public:
explicit nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
~nvhost_gpu() override = default;
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;

View File

@@ -13,8 +13,8 @@ namespace Service {
namespace NVFlinger {
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
buffer_wait_event =
Kernel::Event::Create(Kernel::ResetType::Sticky, "BufferQueue NativeHandle");
native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle");
native_handle->Signal();
}
void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
@@ -26,7 +26,10 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
LOG_WARNING(Service, "Adding graphics buffer {}", slot);
queue.emplace_back(buffer);
buffer_wait_event->Signal();
if (buffer_wait_event) {
buffer_wait_event->Signal();
}
}
boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
@@ -45,6 +48,8 @@ boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
return boost::none;
}
buffer_wait_event = nullptr;
itr->status = Buffer::Status::Dequeued;
return itr->slot;
}
@@ -57,15 +62,13 @@ const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
return itr->igbp_buffer;
}
void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
const MathUtil::Rectangle<int>& crop_rect) {
void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform) {
auto itr = std::find_if(queue.begin(), queue.end(),
[&](const Buffer& buffer) { return buffer.slot == slot; });
ASSERT(itr != queue.end());
ASSERT(itr->status == Buffer::Status::Dequeued);
itr->status = Buffer::Status::Queued;
itr->transform = transform;
itr->crop_rect = crop_rect;
}
boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() {
@@ -85,7 +88,9 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
ASSERT(itr->status == Buffer::Status::Acquired);
itr->status = Buffer::Status::Free;
buffer_wait_event->Signal();
if (buffer_wait_event) {
buffer_wait_event->Signal();
}
}
u32 BufferQueue::Query(QueryType type) {
@@ -101,5 +106,10 @@ u32 BufferQueue::Query(QueryType type) {
return 0;
}
void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event) {
ASSERT_MSG(!buffer_wait_event, "buffer_wait_event only supports a single waiting thread!");
buffer_wait_event = std::move(wait_event);
}
} // namespace NVFlinger
} // namespace Service

View File

@@ -6,7 +6,6 @@
#include <vector>
#include <boost/optional.hpp>
#include "common/math_util.h"
#include "common/swap.h"
#include "core/hle/kernel/event.h"
@@ -69,24 +68,23 @@ public:
Status status = Status::Free;
IGBPBuffer igbp_buffer;
BufferTransformFlags transform;
MathUtil::Rectangle<int> crop_rect;
};
void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
boost::optional<u32> DequeueBuffer(u32 width, u32 height);
const IGBPBuffer& RequestBuffer(u32 slot) const;
void QueueBuffer(u32 slot, BufferTransformFlags transform,
const MathUtil::Rectangle<int>& crop_rect);
void QueueBuffer(u32 slot, BufferTransformFlags transform);
boost::optional<const Buffer&> AcquireBuffer();
void ReleaseBuffer(u32 slot);
u32 Query(QueryType type);
void SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event);
u32 GetId() const {
return id;
}
Kernel::SharedPtr<Kernel::Event> GetBufferWaitEvent() const {
return buffer_wait_event;
Kernel::SharedPtr<Kernel::Event> GetNativeHandle() const {
return native_handle;
}
private:
@@ -94,6 +92,9 @@ private:
u64 layer_id;
std::vector<Buffer> queue;
Kernel::SharedPtr<Kernel::Event> native_handle;
/// Used to signal waiting thread when no buffers are available
Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
};

View File

@@ -149,10 +149,12 @@ void NVFlinger::Compose() {
ASSERT(nvdisp);
nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform,
buffer->crop_rect);
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform);
buffer_queue->ReleaseBuffer(buffer->slot);
// TODO(Subv): Figure out when we should actually signal this event.
buffer_queue->GetNativeHandle()->Signal();
}
}

View File

@@ -12,7 +12,7 @@ class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> module, const char* name);
Interface(std::shared_ptr<Module> module, const char* name);
void CreateService(Kernel::HLERequestContext& ctx);
void CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx);

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include "core/hle/kernel/event.h"

View File

@@ -22,7 +22,7 @@ namespace Service::SM {
/// Interface to "sm:" service
class SM final : public ServiceFramework<SM> {
public:
explicit SM(std::shared_ptr<ServiceManager> service_manager);
SM(std::shared_ptr<ServiceManager> service_manager);
~SM() override;
private:

View File

@@ -19,9 +19,10 @@ void BSD::RegisterClient(Kernel::HLERequestContext& ctx) {
void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // bsd errno
}
void BSD::Socket(Kernel::HLERequestContext& ctx) {

View File

@@ -12,7 +12,7 @@ class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> module, const char* name);
Interface(std::shared_ptr<Module> module, const char* name);
void GetRandomBytes(Kernel::HLERequestContext& ctx);

View File

@@ -57,7 +57,7 @@ class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> time, const char* name);
Interface(std::shared_ptr<Module> time, const char* name);
void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx);
void GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx);

View File

@@ -7,7 +7,6 @@
#include <memory>
#include <boost/optional.hpp>
#include "common/alignment.h"
#include "common/math_util.h"
#include "common/scope_exit.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
@@ -28,8 +27,8 @@ struct DisplayInfo {
char display_name[0x40]{"Default"};
u64 unknown_1{1};
u64 unknown_2{1};
u64 width{1280};
u64 height{720};
u64 width{1920};
u64 height{1080};
};
static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
@@ -328,8 +327,8 @@ public:
protected:
void SerializeData() override {
// TODO(Subv): Figure out what this value means, writing non-zero here will make libnx
// try to read an IGBPBuffer object from the parcel.
// TODO(Subv): Figure out what this value means, writing non-zero here will make libnx try
// to read an IGBPBuffer object from the parcel.
Write<u32_le>(1);
WriteObject(buffer);
Write<u32_le>(0);
@@ -361,8 +360,8 @@ public:
INSERT_PADDING_WORDS(3);
u32_le timestamp;
s32_le is_auto_timestamp;
s32_le crop_top;
s32_le crop_left;
s32_le crop_top;
s32_le crop_right;
s32_le crop_bottom;
s32_le scaling_mode;
@@ -371,10 +370,6 @@ public:
INSERT_PADDING_WORDS(2);
u32_le fence_is_valid;
std::array<Fence, 2> fences;
MathUtil::Rectangle<int> GetCropRect() const {
return {crop_left, crop_top, crop_right, crop_bottom};
}
};
static_assert(sizeof(Data) == 80, "ParcelData has wrong size");
@@ -500,7 +495,7 @@ private:
ctx.WriteBuffer(response.Serialize());
} else {
// Wait the current thread until a buffer becomes available
ctx.SleepClientThread(
auto wait_event = ctx.SleepClientThread(
Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1,
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
ThreadWakeupReason reason) {
@@ -511,8 +506,8 @@ private:
ctx.WriteBuffer(response.Serialize());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
},
buffer_queue->GetBufferWaitEvent());
});
buffer_queue->SetBufferWaitEvent(std::move(wait_event));
}
} else if (transaction == TransactionId::RequestBuffer) {
IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
@@ -524,8 +519,7 @@ private:
} else if (transaction == TransactionId::QueueBuffer) {
IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
buffer_queue->QueueBuffer(request.data.slot, request.data.transform,
request.data.GetCropRect());
buffer_queue->QueueBuffer(request.data.slot, request.data.transform);
IGBPQueueBufferResponseParcel response{1280, 720};
ctx.WriteBuffer(response.Serialize());
@@ -538,7 +532,7 @@ private:
IGBPQueryResponseParcel response{value};
ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::CancelBuffer) {
LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
LOG_WARNING(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
} else {
ASSERT_MSG(false, "Unimplemented");
}
@@ -571,7 +565,7 @@ private:
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(buffer_queue->GetBufferWaitEvent());
rb.PushCopyObjects(buffer_queue->GetNativeHandle());
}
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
@@ -579,7 +573,7 @@ private:
class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
public:
explicit ISystemDisplayService() : ServiceFramework("ISystemDisplayService") {
ISystemDisplayService() : ServiceFramework("ISystemDisplayService") {
static const FunctionInfo functions[] = {
{1200, nullptr, "GetZOrderCountMin"},
{1202, nullptr, "GetZOrderCountMax"},
@@ -777,7 +771,7 @@ private:
class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
public:
explicit IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
~IApplicationDisplayService() = default;
private:

View File

@@ -24,8 +24,8 @@ class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> module, const char* name,
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
Interface(std::shared_ptr<Module> module, const char* name,
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
void GetDisplayService(Kernel::HLERequestContext& ctx);

View File

@@ -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/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
@@ -46,11 +47,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(const FileSys::VirtualFile& file) {
if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) {
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);
if (is_main_found && is_npdm_found && is_rtld_found && is_sdk_found) {
return FileType::DeconstructedRomDirectory;
}
@@ -62,13 +107,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;
}
@@ -83,10 +129,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);
// Register module with GDBStub
@@ -105,26 +150,41 @@ 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;
});
filepath_romfs = FindRomFS(directory);
// Register the RomFS if a ".romfs" file was found
if (romfs_iter != files.end() && *romfs_iter != nullptr) {
romfs = *romfs_iter;
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
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(FileSys::VirtualFile& dir) {
if (romfs == nullptr)
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;
dir = romfs;
}
// 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;
}

View File

@@ -20,26 +20,29 @@ 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(FileSys::VirtualFile& dir) 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;
FileSys::VirtualFile romfs;
};
} // namespace Loader

View File

@@ -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();

View File

@@ -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

View File

@@ -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;
}
return IdentifyFile(file, file_name);
}
FileType GuessFromFilename(const std::string& name) {
if (name == "main")
return FileType::DeconstructedRomDirectory;
FileType GuessFromExtension(const std::string& extension_) {
std::string extension = Common::ToLower(extension_);
const std::string extension = Common::ToLower(FileUtil::GetExtensionFromFilename(name));
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

View File

@@ -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

View File

@@ -4,13 +4,14 @@
#include <vector>
#include "common/common_funcs.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.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/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
@@ -21,15 +22,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;
@@ -39,22 +233,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;
}
@@ -69,8 +258,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);
// Register module with GDBStub
@@ -88,18 +276,29 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
metadata.GetMainThreadStackSize());
if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0)
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
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(FileSys::VirtualFile& dir) {
if (nca == nullptr || nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0)
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;
dir = nca->GetRomFS();
}
romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb");
offset = nca->GetRomFsOffset();
size = nca->GetRomFsSize();
LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset);
LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size);
return ResultStatus::Success;
}

View File

@@ -6,39 +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(FileSys::VirtualFile& dir) 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

View File

@@ -48,12 +48,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')) {
@@ -66,10 +68,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')) {
@@ -78,9 +86,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;
@@ -103,7 +112,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);
@@ -117,11 +126,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;
}

Some files were not shown because too many files have changed in this diff Show More