Compare commits
208 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0faa13baeb | ||
|
|
e6b3d3a9ea | ||
|
|
d3cfaf95c8 | ||
|
|
0b13ce1435 | ||
|
|
af08034c71 | ||
|
|
2aeb3355e4 | ||
|
|
c6352ffc58 | ||
|
|
ec468c990d | ||
|
|
f43d8ea523 | ||
|
|
2194308245 | ||
|
|
b5c77313de | ||
|
|
dd0446ff43 | ||
|
|
31413f0d2f | ||
|
|
05549e45c5 | ||
|
|
50e2777724 | ||
|
|
b102815f1f | ||
|
|
7244671137 | ||
|
|
ff500a7b68 | ||
|
|
eb9b55eafe | ||
|
|
78dd1cd441 | ||
|
|
df001e83b1 | ||
|
|
b879fb84a2 | ||
|
|
68c1ffdd1c | ||
|
|
7eace8f512 | ||
|
|
38b35e752b | ||
|
|
c945226973 | ||
|
|
a37a47448d | ||
|
|
95103a1b7b | ||
|
|
427fc4ac6b | ||
|
|
e91ba6c057 | ||
|
|
d6c7a05239 | ||
|
|
1034bcc742 | ||
|
|
e5c916a27c | ||
|
|
50d08beed2 | ||
|
|
bbc31ba6af | ||
|
|
130a02f330 | ||
|
|
8176ab3a07 | ||
|
|
9b22f856c2 | ||
|
|
c061c2bf3c | ||
|
|
f3daecafeb | ||
|
|
04f7a7036a | ||
|
|
cbf43225a9 | ||
|
|
f1d7486eac | ||
|
|
b0334af05b | ||
|
|
1bf7ae79c8 | ||
|
|
dc35c3f9d7 | ||
|
|
af2698dcea | ||
|
|
6c1ba02e0c | ||
|
|
3e9b79e088 | ||
|
|
758c357868 | ||
|
|
87053fb3b8 | ||
|
|
5da4c78c6a | ||
|
|
abbf038191 | ||
|
|
2cc0ef83cf | ||
|
|
f317080f40 | ||
|
|
910ad2e110 | ||
|
|
6be342118a | ||
|
|
d6e9b96e2f | ||
|
|
5c47ea1a4e | ||
|
|
0a868641fa | ||
|
|
1edf4dd7ef | ||
|
|
9128271292 | ||
|
|
e0b8a35937 | ||
|
|
ef03d0178a | ||
|
|
25f997097d | ||
|
|
63e64f0131 | ||
|
|
cd4fca8447 | ||
|
|
33fbcb45a7 | ||
|
|
368e1d25be | ||
|
|
85421f3406 | ||
|
|
88ba94e8a2 | ||
|
|
9abc5763b6 | ||
|
|
cf30c4be22 | ||
|
|
4790bb907d | ||
|
|
87a9bb392b | ||
|
|
522bd5b736 | ||
|
|
0b566f43a1 | ||
|
|
5e626c774f | ||
|
|
1371e2fb6a | ||
|
|
b10905c8ae | ||
|
|
55ab369043 | ||
|
|
1831b5ef62 | ||
|
|
e3a30ccc7c | ||
|
|
90ce935f3d | ||
|
|
3f93279047 | ||
|
|
49b0966003 | ||
|
|
89f0acfd36 | ||
|
|
d883d3b64f | ||
|
|
2975f7820e | ||
|
|
b496a9eefe | ||
|
|
29aff8d5ab | ||
|
|
72207577b2 | ||
|
|
2cd3141c30 | ||
|
|
296e68fd43 | ||
|
|
93cba6f699 | ||
|
|
46458e7284 | ||
|
|
10d2ab8098 | ||
|
|
3a4841e403 | ||
|
|
f5d7706ca1 | ||
|
|
079be8032d | ||
|
|
924c473bb3 | ||
|
|
d17e172d92 | ||
|
|
c337272ca9 | ||
|
|
f2f368014e | ||
|
|
3d1e8f750c | ||
|
|
e3da9fc367 | ||
|
|
24a55bba42 | ||
|
|
b87a71b3fe | ||
|
|
c65a8fafa0 | ||
|
|
0aebe6b3d5 | ||
|
|
7c3cc08957 | ||
|
|
3575d367a4 | ||
|
|
f4b98a857b | ||
|
|
c8f3fc9a4b | ||
|
|
f4c69149f9 | ||
|
|
6a03badcbc | ||
|
|
0f148548f3 | ||
|
|
c5803e30d3 | ||
|
|
e3fadb9616 | ||
|
|
4cd52a34b9 | ||
|
|
03c2d049d4 | ||
|
|
49e5de9f03 | ||
|
|
0d1a99edf6 | ||
|
|
c3dd456d51 | ||
|
|
8e28af6f89 | ||
|
|
3d3b10adc7 | ||
|
|
69bfe075b5 | ||
|
|
88a3140c9b | ||
|
|
519035db3d | ||
|
|
5f119bed56 | ||
|
|
170e19d4ea | ||
|
|
3b885691a1 | ||
|
|
56cc1c11ec | ||
|
|
f00ca69a81 | ||
|
|
0722adb471 | ||
|
|
068668780c | ||
|
|
04b9cde4f5 | ||
|
|
7d209b3c9f | ||
|
|
497b81558e | ||
|
|
98762e9601 | ||
|
|
3a96670f2d | ||
|
|
aaec0b7e70 | ||
|
|
f8ab956189 | ||
|
|
3145114190 | ||
|
|
e21190f47f | ||
|
|
2cb3fdca86 | ||
|
|
c324a378ac | ||
|
|
fd1f5c5414 | ||
|
|
6daebaaa57 | ||
|
|
b07f4d6afb | ||
|
|
ad0166a982 | ||
|
|
7e5e4f8d7a | ||
|
|
a7d6c0d6ea | ||
|
|
81739a5448 | ||
|
|
05cb10530f | ||
|
|
b37354cca8 | ||
|
|
9fc0d1d701 | ||
|
|
e066bc75b9 | ||
|
|
8aeff9cf8e | ||
|
|
c4015cd93a | ||
|
|
ce23ae3ede | ||
|
|
64b5e5d5d9 | ||
|
|
52636f67cc | ||
|
|
8bd8d1e3da | ||
|
|
6642011706 | ||
|
|
49c0c081c4 | ||
|
|
4757ffdcce | ||
|
|
274d1fb0fc | ||
|
|
3ff21345b4 | ||
|
|
c1ae841f47 | ||
|
|
316b933a31 | ||
|
|
0cad310e12 | ||
|
|
4f41ffdd41 | ||
|
|
7c7b2b8285 | ||
|
|
b89fb430c7 | ||
|
|
b30c5370b1 | ||
|
|
020d005d8c | ||
|
|
706892de7d | ||
|
|
3d68f6ba6c | ||
|
|
7230ceb584 | ||
|
|
afb26b190f | ||
|
|
e2037821b6 | ||
|
|
12a6996262 | ||
|
|
379a935016 | ||
|
|
04524e76c2 | ||
|
|
3e966be6fc | ||
|
|
0944bfe3cb | ||
|
|
aec90ca506 | ||
|
|
ef2c955db5 | ||
|
|
dacc89b38b | ||
|
|
51a3e93f8e | ||
|
|
0d51cfe2f5 | ||
|
|
eb6cbfdbd8 | ||
|
|
1b3dd30ba8 | ||
|
|
854f474f52 | ||
|
|
639346bcfb | ||
|
|
d990f2355b | ||
|
|
f89b47fdf7 | ||
|
|
913896cbd9 | ||
|
|
3417f46dd5 | ||
|
|
f2c2383c8b | ||
|
|
476e0fae4c | ||
|
|
b8384c0c91 | ||
|
|
9df698fa9c | ||
|
|
70a6691e3b | ||
|
|
b0c92b80b1 | ||
|
|
23dc36ed71 | ||
|
|
5f57a70a7d |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,6 +11,7 @@ src/common/scm_rev.cpp
|
||||
.idea/
|
||||
.vs/
|
||||
.vscode/
|
||||
CMakeLists.txt.user
|
||||
|
||||
# *nix related
|
||||
# Common convention for backup or temporary files
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Reporting Issues
|
||||
|
||||
**The issue tracker is not a support forum.** Unless you can provide precise *technical information* regarding an issue, you *should not post in it*. If you need support, first read the [FAQ](https://github.com/yuzu-emu/yuzu/wiki/FAQ) and then either visit our Discord server, [our forum](https://community.citra-emu.org) or ask in a general emulation forum such as [/r/emulation](https://www.reddit.com/r/emulation/). If you post support questions, generic messages to the developers or vague reports without technical details, they will be closed and locked.
|
||||
**The issue tracker is not a support forum.** Unless you can provide precise *technical information* regarding an issue, you *should not post in it*. If you need support, first read the [FAQ](https://github.com/yuzu-emu/yuzu/wiki/FAQ) and then either visit our [Discord server](https://discordapp.com/invite/u77vRWY), [our forum](https://community.citra-emu.org) or ask in a general emulation forum such as [/r/emulation](https://www.reddit.com/r/emulation/). If you post support questions, generic messages to the developers or vague reports without technical details, they will be closed and locked.
|
||||
|
||||
If you believe you have a valid issue report, please post text or a screenshot from the log (the console window that opens alongside yuzu) and build version (hex string visible in the titlebar and zip filename), as well as your hardware and software information if applicable.
|
||||
|
||||
|
||||
2
externals/catch
vendored
2
externals/catch
vendored
Submodule externals/catch updated: cd76f5730c...d2a130f243
@@ -1,4 +1,4 @@
|
||||
# Copyright 2016 Citra Emulator Project
|
||||
# Copyright 2018 Yuzu Emulator Project
|
||||
# Licensed under GPLv2 or any later version
|
||||
# Refer to the license.txt file included.
|
||||
|
||||
@@ -22,7 +22,7 @@ function(windows_copy_files TARGET SOURCE_DIR DEST_DIR)
|
||||
# cmake adds an extra check for command success which doesn't work too well with robocopy
|
||||
# so trick it into thinking the command was successful with the || cmd /c "exit /b 0"
|
||||
add_custom_command(TARGET ${TARGET} POST_BUILD
|
||||
COMMAND if not exist ${DEST_DIR} mkdir ${DEST_DIR} 2> nul
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${DEST_DIR}
|
||||
COMMAND robocopy ${SOURCE_DIR} ${DEST_DIR} ${ARGN} /NJH /NJS /NDL /NFL /NC /NS /NP || cmd /c "exit /b 0"
|
||||
)
|
||||
endfunction()
|
||||
endfunction()
|
||||
|
||||
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: f7d11baa1c...5a91c94dca
2
externals/fmt
vendored
2
externals/fmt
vendored
Submodule externals/fmt updated: 5859e58ba1...c2ce7e4f07
2
externals/xbyak
vendored
2
externals/xbyak
vendored
Submodule externals/xbyak updated: 2794cde79e...71b75f653f
@@ -52,5 +52,5 @@ __declspec(noinline, noreturn)
|
||||
#define DEBUG_ASSERT_MSG(_a_, _desc_, ...)
|
||||
#endif
|
||||
|
||||
#define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!")
|
||||
#define UNIMPLEMENTED() LOG_CRITICAL(Debug, "Unimplemented code!")
|
||||
#define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM)
|
||||
#include <string>
|
||||
|
||||
#if !defined(ARCHITECTURE_x86_64) && !defined(ARCHITECTURE_ARM)
|
||||
#include <cstdlib> // for exit
|
||||
#endif
|
||||
#include "common/common_types.h"
|
||||
@@ -30,46 +32,12 @@
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#define Crash() __asm__ __volatile__("int $3")
|
||||
#elif defined(_M_ARM)
|
||||
#elif defined(ARCHITECTURE_ARM)
|
||||
#define Crash() __asm__ __volatile__("trap")
|
||||
#else
|
||||
#define Crash() exit(1)
|
||||
#endif
|
||||
|
||||
// GCC 4.8 defines all the rotate functions now
|
||||
// Small issue with GCC's lrotl/lrotr intrinsics is they are still 32bit while we require 64bit
|
||||
#ifdef _rotl
|
||||
#define rotl _rotl
|
||||
#else
|
||||
inline u32 rotl(u32 x, int shift) {
|
||||
shift &= 31;
|
||||
if (!shift)
|
||||
return x;
|
||||
return (x << shift) | (x >> (32 - shift));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _rotr
|
||||
#define rotr _rotr
|
||||
#else
|
||||
inline u32 rotr(u32 x, int shift) {
|
||||
shift &= 31;
|
||||
if (!shift)
|
||||
return x;
|
||||
return (x >> shift) | (x << (32 - shift));
|
||||
}
|
||||
#endif
|
||||
|
||||
inline u64 _rotl64(u64 x, unsigned int shift) {
|
||||
unsigned int n = shift % 64;
|
||||
return (x << n) | (x >> (64 - n));
|
||||
}
|
||||
|
||||
inline u64 _rotr64(u64 x, unsigned int shift) {
|
||||
unsigned int n = shift % 64;
|
||||
return (x >> n) | (x << (64 - n));
|
||||
}
|
||||
|
||||
#else // _MSC_VER
|
||||
|
||||
// Locale Cross-Compatibility
|
||||
@@ -80,17 +48,13 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
}
|
||||
#define Crash() DebugBreak()
|
||||
|
||||
// cstdlib provides these on MSVC
|
||||
#define rotr _rotr
|
||||
#define rotl _rotl
|
||||
|
||||
#endif // _MSC_VER ndef
|
||||
|
||||
// Generic function to get last error message.
|
||||
// Call directly after the command or use the error num.
|
||||
// This function might change the error code.
|
||||
// Defined in Misc.cpp.
|
||||
const char* GetLastErrorMsg();
|
||||
std::string GetLastErrorMsg();
|
||||
|
||||
namespace Common {
|
||||
|
||||
|
||||
@@ -592,7 +592,7 @@ std::string GetBundleDirectory() {
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string& GetExeDirectory() {
|
||||
const std::string& GetExeDirectory() {
|
||||
static std::string exe_path;
|
||||
if (exe_path.empty()) {
|
||||
wchar_t wchar_exe_path[2048];
|
||||
@@ -826,6 +826,20 @@ std::string GetParentPath(const std::string& path) {
|
||||
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('/');
|
||||
|
||||
@@ -133,7 +133,7 @@ std::string GetBundleDirectory();
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string& GetExeDirectory();
|
||||
const std::string& GetExeDirectory();
|
||||
std::string AppDataRoamingDirectory();
|
||||
#endif
|
||||
|
||||
@@ -154,9 +154,12 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
|
||||
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
|
||||
std::vector<std::string> SplitPathComponents(const std::string& filename);
|
||||
|
||||
// Gets all of the text prior to the last '/' or '\' in the path.
|
||||
// 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);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
@@ -83,8 +84,10 @@ 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;
|
||||
}
|
||||
@@ -92,7 +95,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.
|
||||
constexpr int MAX_LOGS_TO_WRITE = 100;
|
||||
const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100;
|
||||
int logs_written = 0;
|
||||
while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
|
||||
write_logs(entry);
|
||||
@@ -282,4 +285,4 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
||||
|
||||
Impl::Instance().PushEntry(std::move(entry));
|
||||
}
|
||||
} // namespace Log
|
||||
} // namespace Log
|
||||
|
||||
@@ -94,4 +94,11 @@ 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
|
||||
|
||||
@@ -47,6 +47,9 @@ 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;
|
||||
};
|
||||
|
||||
@@ -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_args(args...));
|
||||
fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
} // namespace Log
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
|
||||
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
||||
#include <unistd.h>
|
||||
#define PAGE_MASK (getpagesize() - 1)
|
||||
#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
|
||||
@@ -30,7 +30,7 @@ void* AllocateExecutableMemory(size_t size, bool low) {
|
||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
#else
|
||||
static char* map_hint = nullptr;
|
||||
#if defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
|
||||
#if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
||||
// This OS has no flag to enforce allocation below the 4 GB boundary,
|
||||
// but if we hint that we want a low address it is very likely we will
|
||||
// get one.
|
||||
@@ -42,7 +42,7 @@ void* AllocateExecutableMemory(size_t size, bool low) {
|
||||
#endif
|
||||
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANON | MAP_PRIVATE
|
||||
#if defined(ARCHITECTURE_X64) && defined(MAP_32BIT)
|
||||
#if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT)
|
||||
| (low ? MAP_32BIT : 0)
|
||||
#endif
|
||||
,
|
||||
@@ -57,7 +57,7 @@ void* AllocateExecutableMemory(size_t size, bool low) {
|
||||
#endif
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
|
||||
}
|
||||
#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
|
||||
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
||||
else {
|
||||
if (low) {
|
||||
map_hint += size;
|
||||
|
||||
@@ -4,34 +4,28 @@
|
||||
|
||||
#include <cstddef>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
// Neither Android nor OS X support TLS
|
||||
#if defined(__APPLE__) || (ANDROID && __clang__)
|
||||
#define __thread
|
||||
#endif
|
||||
#include "common/common_funcs.h"
|
||||
|
||||
// Generic function to get last error message.
|
||||
// Call directly after the command or use the error num.
|
||||
// This function might change the error code.
|
||||
const char* GetLastErrorMsg() {
|
||||
std::string GetLastErrorMsg() {
|
||||
static const size_t buff_size = 255;
|
||||
char err_str[buff_size];
|
||||
|
||||
#ifdef _WIN32
|
||||
static __declspec(thread) char err_str[buff_size] = {};
|
||||
|
||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
|
||||
#else
|
||||
static __thread char err_str[buff_size] = {};
|
||||
|
||||
// Thread safe (XSI-compliant)
|
||||
strerror_r(errno, err_str, buff_size);
|
||||
#endif
|
||||
|
||||
return err_str;
|
||||
return std::string(err_str, buff_size);
|
||||
}
|
||||
|
||||
@@ -34,18 +34,6 @@ std::string ToUpper(std::string str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
// faster than sscanf
|
||||
bool AsciiToHex(const char* _szValue, u32& result) {
|
||||
char* endptr = nullptr;
|
||||
const u32 value = strtoul(_szValue, &endptr, 16);
|
||||
|
||||
if (!endptr || *endptr)
|
||||
return false;
|
||||
|
||||
result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// For Debugging. Read out an u8 array.
|
||||
std::string ArrayToString(const u8* data, size_t size, int line_len, bool spaces) {
|
||||
std::ostringstream oss;
|
||||
@@ -134,7 +122,7 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
||||
size_t dir_end = full_path.find_last_of("/"
|
||||
// windows needs the : included for something like just "C:" to be considered a directory
|
||||
#ifdef _WIN32
|
||||
":"
|
||||
"\\:"
|
||||
#endif
|
||||
);
|
||||
if (std::string::npos == dir_end)
|
||||
|
||||
@@ -57,9 +57,6 @@ static bool TryParse(const std::string& str, N* const output) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: kill this
|
||||
bool AsciiToHex(const char* _szValue, u32& result);
|
||||
|
||||
std::string TabsToSpaces(int tab_size, const std::string& in);
|
||||
|
||||
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
|
||||
|
||||
@@ -69,7 +69,7 @@ inline u32 swap32(u32 _data) {
|
||||
inline u64 swap64(u64 _data) {
|
||||
return _byteswap_uint64(_data);
|
||||
}
|
||||
#elif _M_ARM
|
||||
#elif ARCHITECTURE_ARM
|
||||
inline u16 swap16(u16 _data) {
|
||||
u32 data = _data;
|
||||
__asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data));
|
||||
@@ -167,7 +167,7 @@ inline double swapd(double f) {
|
||||
|
||||
template <typename T, typename F>
|
||||
struct swap_struct_t {
|
||||
typedef swap_struct_t<T, F> swapped_t;
|
||||
using swapped_t = swap_struct_t;
|
||||
|
||||
protected:
|
||||
T value = T();
|
||||
@@ -177,7 +177,7 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
T const swap() const {
|
||||
T swap() const {
|
||||
return swap(value);
|
||||
}
|
||||
swap_struct_t() = default;
|
||||
@@ -185,39 +185,39 @@ public:
|
||||
|
||||
template <typename S>
|
||||
swapped_t& operator=(const S& source) {
|
||||
value = swap((T)source);
|
||||
value = swap(static_cast<T>(source));
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator s8() const {
|
||||
return (s8)swap();
|
||||
return static_cast<s8>(swap());
|
||||
}
|
||||
operator u8() const {
|
||||
return (u8)swap();
|
||||
return static_cast<u8>(swap());
|
||||
}
|
||||
operator s16() const {
|
||||
return (s16)swap();
|
||||
return static_cast<s16>(swap());
|
||||
}
|
||||
operator u16() const {
|
||||
return (u16)swap();
|
||||
return static_cast<u16>(swap());
|
||||
}
|
||||
operator s32() const {
|
||||
return (s32)swap();
|
||||
return static_cast<s32>(swap());
|
||||
}
|
||||
operator u32() const {
|
||||
return (u32)swap();
|
||||
return static_cast<u32>(swap());
|
||||
}
|
||||
operator s64() const {
|
||||
return (s64)swap();
|
||||
return static_cast<s64>(swap());
|
||||
}
|
||||
operator u64() const {
|
||||
return (u64)swap();
|
||||
return static_cast<u64>(swap());
|
||||
}
|
||||
operator float() const {
|
||||
return (float)swap();
|
||||
return static_cast<float>(swap());
|
||||
}
|
||||
operator double() const {
|
||||
return (double)swap();
|
||||
return static_cast<double>(swap());
|
||||
}
|
||||
|
||||
// +v
|
||||
@@ -253,7 +253,7 @@ public:
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t operator+(const S& i) const {
|
||||
return swap() + (T)i;
|
||||
return swap() + static_cast<T>(i);
|
||||
}
|
||||
// v - 5
|
||||
swapped_t operator-(const swapped_t& i) const {
|
||||
@@ -261,7 +261,7 @@ public:
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t operator-(const S& i) const {
|
||||
return swap() - (T)i;
|
||||
return swap() - static_cast<T>(i);
|
||||
}
|
||||
|
||||
// v += 5
|
||||
@@ -271,7 +271,7 @@ public:
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator+=(const S& i) {
|
||||
value = swap(swap() + (T)i);
|
||||
value = swap(swap() + static_cast<T>(i));
|
||||
return *this;
|
||||
}
|
||||
// v -= 5
|
||||
@@ -281,7 +281,7 @@ public:
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator-=(const S& i) {
|
||||
value = swap(swap() - (T)i);
|
||||
value = swap(swap() - static_cast<T>(i));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -541,7 +541,7 @@ S operator&(const S& i, const swap_struct_t<T, F> v) {
|
||||
|
||||
template <typename S, typename T, typename F>
|
||||
S operator&(const swap_struct_t<T, F> v, const S& i) {
|
||||
return (S)(v.swap() & i);
|
||||
return static_cast<S>(v.swap() & i);
|
||||
}
|
||||
|
||||
// Comparaison
|
||||
@@ -606,51 +606,51 @@ struct swap_double_t {
|
||||
};
|
||||
|
||||
#if COMMON_LITTLE_ENDIAN
|
||||
typedef u32 u32_le;
|
||||
typedef u16 u16_le;
|
||||
typedef u64 u64_le;
|
||||
using u16_le = u16;
|
||||
using u32_le = u32;
|
||||
using u64_le = u64;
|
||||
|
||||
typedef s32 s32_le;
|
||||
typedef s16 s16_le;
|
||||
typedef s64 s64_le;
|
||||
using s16_le = s16;
|
||||
using s32_le = s32;
|
||||
using s64_le = s64;
|
||||
|
||||
typedef float float_le;
|
||||
typedef double double_le;
|
||||
using float_le = float;
|
||||
using double_le = double;
|
||||
|
||||
typedef swap_struct_t<u64, swap_64_t<u64>> u64_be;
|
||||
typedef swap_struct_t<s64, swap_64_t<s64>> s64_be;
|
||||
using u64_be = swap_struct_t<u64, swap_64_t<u64>>;
|
||||
using s64_be = swap_struct_t<s64, swap_64_t<s64>>;
|
||||
|
||||
typedef swap_struct_t<u32, swap_32_t<u32>> u32_be;
|
||||
typedef swap_struct_t<s32, swap_32_t<s32>> s32_be;
|
||||
using u32_be = swap_struct_t<u32, swap_32_t<u32>>;
|
||||
using s32_be = swap_struct_t<s32, swap_32_t<s32>>;
|
||||
|
||||
typedef swap_struct_t<u16, swap_16_t<u16>> u16_be;
|
||||
typedef swap_struct_t<s16, swap_16_t<s16>> s16_be;
|
||||
using u16_be = swap_struct_t<u16, swap_16_t<u16>>;
|
||||
using s16_be = swap_struct_t<s16, swap_16_t<s16>>;
|
||||
|
||||
typedef swap_struct_t<float, swap_float_t<float>> float_be;
|
||||
typedef swap_struct_t<double, swap_double_t<double>> double_be;
|
||||
using float_be = swap_struct_t<float, swap_float_t<float>>;
|
||||
using double_be = swap_struct_t<double, swap_double_t<double>>;
|
||||
#else
|
||||
|
||||
typedef swap_struct_t<u64, swap_64_t<u64>> u64_le;
|
||||
typedef swap_struct_t<s64, swap_64_t<s64>> s64_le;
|
||||
using u64_le = swap_struct_t<u64, swap_64_t<u64>>;
|
||||
using s64_le = swap_struct_t<s64, swap_64_t<s64>>;
|
||||
|
||||
typedef swap_struct_t<u32, swap_32_t<u32>> u32_le;
|
||||
typedef swap_struct_t<s32, swap_32_t<s32>> s32_le;
|
||||
using u32_le = swap_struct_t<u32, swap_32_t<u32>>;
|
||||
using s32_le = swap_struct_t<s32, swap_32_t<s32>>;
|
||||
|
||||
typedef swap_struct_t<u16, swap_16_t<u16>> u16_le;
|
||||
typedef swap_struct_t<s16, swap_16_t<s16>> s16_le;
|
||||
using u16_le = swap_struct_t<u16, swap_16_t<u16>>;
|
||||
using s16_le = swap_struct_t<s16, swap_16_t<s16>>;
|
||||
|
||||
typedef swap_struct_t<float, swap_float_t<float>> float_le;
|
||||
typedef swap_struct_t<double, swap_double_t<double>> double_le;
|
||||
using float_le = swap_struct_t<float, swap_float_t<float>>;
|
||||
using double_le = swap_struct_t<double, swap_double_t<double>>;
|
||||
|
||||
typedef u32 u32_be;
|
||||
typedef u16 u16_be;
|
||||
typedef u64 u64_be;
|
||||
using u16_be = u16;
|
||||
using u32_be = u32;
|
||||
using u64_be = u64;
|
||||
|
||||
typedef s32 s32_be;
|
||||
typedef s16 s16_be;
|
||||
typedef s64 s64_be;
|
||||
using s16_be = s16;
|
||||
using s32_be = s32;
|
||||
using s64_be = s64;
|
||||
|
||||
typedef float float_be;
|
||||
typedef double double_be;
|
||||
using float_be = float;
|
||||
using double_be = double;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -52,27 +52,14 @@ public:
|
||||
template <typename T>
|
||||
class Field : public FieldInterface {
|
||||
public:
|
||||
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)
|
||||
Field(FieldType type, std::string name, T value)
|
||||
: name(std::move(name)), type(type), value(std::move(value)) {}
|
||||
|
||||
Field(const Field& other) : Field(other.type, other.name, other.value) {}
|
||||
Field(const Field&) = default;
|
||||
Field& operator=(const Field&) = 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;
|
||||
}
|
||||
Field(Field&&) = default;
|
||||
Field& operator=(Field&& other) = default;
|
||||
|
||||
void Accept(VisitorInterface& visitor) const override;
|
||||
|
||||
@@ -94,11 +81,11 @@ public:
|
||||
return value;
|
||||
}
|
||||
|
||||
inline bool operator==(const Field<T>& other) {
|
||||
bool operator==(const Field& other) const {
|
||||
return (type == other.type) && (name == other.name) && (value == other.value);
|
||||
}
|
||||
|
||||
inline bool operator!=(const Field<T>& other) {
|
||||
bool operator!=(const Field& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,15 +12,17 @@ add_library(core STATIC
|
||||
file_sys/content_archive.h
|
||||
file_sys/directory.h
|
||||
file_sys/errors.h
|
||||
file_sys/filesystem.cpp
|
||||
file_sys/filesystem.h
|
||||
file_sys/mode.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/storage.h
|
||||
file_sys/romfs_factory.cpp
|
||||
file_sys/romfs_factory.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
|
||||
|
||||
@@ -116,6 +116,8 @@ 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;
|
||||
};
|
||||
|
||||
@@ -226,6 +226,10 @@ void ARM_Dynarmic::ClearInstructionCache() {
|
||||
jit->ClearCache();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::ClearExclusiveState() {
|
||||
jit->ClearExclusiveState();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::PageTableChanged() {
|
||||
jit = MakeJit(cb);
|
||||
current_page_table = Memory::GetCurrentPageTable();
|
||||
|
||||
@@ -39,6 +39,7 @@ public:
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
|
||||
void PrepareReschedule() override;
|
||||
void ClearExclusiveState() override;
|
||||
|
||||
void ClearInstructionCache() override;
|
||||
void PageTableChanged() override;
|
||||
|
||||
@@ -193,11 +193,11 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
|
||||
}
|
||||
Kernel::Thread* thread = Kernel::GetCurrentThread();
|
||||
SaveContext(thread->context);
|
||||
if (last_bkpt_hit) {
|
||||
if (last_bkpt_hit || (num_instructions == 1)) {
|
||||
last_bkpt_hit = false;
|
||||
GDBStub::Break();
|
||||
GDBStub::SendTrap(thread, 5);
|
||||
}
|
||||
GDBStub::SendTrap(thread, 5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,6 +263,8 @@ void ARM_Unicorn::PrepareReschedule() {
|
||||
CHECKED(uc_emu_stop(uc));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::ClearExclusiveState() {}
|
||||
|
||||
void ARM_Unicorn::ClearInstructionCache() {}
|
||||
|
||||
void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
|
||||
|
||||
@@ -31,6 +31,7 @@ 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;
|
||||
|
||||
@@ -26,11 +26,13 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +168,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
System();
|
||||
|
||||
/// Returns the currently running CPU core
|
||||
Cpu& CurrentCpuCore();
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
@@ -17,7 +20,10 @@ constexpr u64 SECTION_HEADER_OFFSET = 0x400;
|
||||
|
||||
constexpr u32 IVFC_MAX_LEVEL = 6;
|
||||
|
||||
enum class NCASectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 };
|
||||
enum class NCASectionFilesystemType : u8 {
|
||||
PFS0 = 0x2,
|
||||
ROMFS = 0x3,
|
||||
};
|
||||
|
||||
struct NCASectionHeaderBlock {
|
||||
INSERT_PADDING_BYTES(3);
|
||||
@@ -58,7 +64,7 @@ struct RomFSSuperblock {
|
||||
};
|
||||
static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size.");
|
||||
|
||||
NCA::NCA(VirtualFile file_) : file(file_) {
|
||||
NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
|
||||
if (sizeof(NCAHeader) != file->ReadObject(&header))
|
||||
LOG_CRITICAL(Loader, "File reader errored out during header read.");
|
||||
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
@@ -11,7 +16,13 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
enum class NCAContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 };
|
||||
enum class NCAContentType : u8 {
|
||||
Program = 0,
|
||||
Meta = 1,
|
||||
Control = 2,
|
||||
Manual = 3,
|
||||
Data = 4,
|
||||
};
|
||||
|
||||
struct NCASectionTableEntry {
|
||||
u32_le media_offset;
|
||||
@@ -42,12 +53,12 @@ struct NCAHeader {
|
||||
};
|
||||
static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size.");
|
||||
|
||||
static bool IsDirectoryExeFS(std::shared_ptr<FileSys::VfsDirectory> pfs) {
|
||||
inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
|
||||
// According to switchbrew, an exefs must only contain these two files:
|
||||
return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
|
||||
}
|
||||
|
||||
static bool IsValidNCA(const NCAHeader& header) {
|
||||
inline bool IsValidNCA(const NCAHeader& header) {
|
||||
return header.magic == Common::MakeMagic('N', 'C', 'A', '2') ||
|
||||
header.magic == Common::MakeMagic('N', 'C', 'A', '3');
|
||||
}
|
||||
|
||||
@@ -8,13 +8,17 @@
|
||||
#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;
|
||||
|
||||
@@ -11,6 +11,9 @@ namespace FileSys {
|
||||
namespace ErrCodes {
|
||||
enum {
|
||||
NotFound = 1,
|
||||
TitleNotFound = 1002,
|
||||
SdCardNotFound = 2001,
|
||||
RomFSNotFound = 2520,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
// 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
|
||||
@@ -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 <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;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
17
src/core/file_sys/mode.h
Normal file
17
src/core/file_sys/mode.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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
|
||||
@@ -11,6 +11,11 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
bool PartitionFilesystem::Header::HasValidMagicValue() const {
|
||||
return magic == Common::MakeMagic('H', 'F', 'S', '0') ||
|
||||
magic == Common::MakeMagic('P', 'F', 'S', '0');
|
||||
}
|
||||
|
||||
PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
|
||||
// At least be as large as the header
|
||||
if (file->GetSize() < sizeof(Header)) {
|
||||
@@ -20,19 +25,17 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
|
||||
|
||||
// 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 (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') &&
|
||||
pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) {
|
||||
if (!pfs_header.HasValidMagicValue()) {
|
||||
status = Loader::ResultStatus::ErrorInvalidFormat;
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
|
||||
is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
|
||||
|
||||
size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry);
|
||||
size_t metadata_size =
|
||||
@@ -40,27 +43,13 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
|
||||
|
||||
// Actually read in now...
|
||||
std::vector<u8> file_data = file->ReadBytes(metadata_size);
|
||||
const size_t total_size = file_data.size();
|
||||
|
||||
if (file_data.size() != metadata_size) {
|
||||
if (total_size != metadata_size) {
|
||||
status = Loader::ResultStatus::Error;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t total_size = file_data.size();
|
||||
if (total_size < sizeof(Header)) {
|
||||
status = Loader::ResultStatus::Error;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&pfs_header, file_data.data(), 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;
|
||||
}
|
||||
|
||||
is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
|
||||
|
||||
size_t entries_offset = sizeof(Header);
|
||||
size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size);
|
||||
content_offset = strtab_offset + pfs_header.strtab_size;
|
||||
|
||||
@@ -42,6 +42,8 @@ private:
|
||||
u32_le num_entries;
|
||||
u32_le strtab_size;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
|
||||
bool HasValidMagicValue() const;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Header) == 0x10, "PFS/HFS header structure size is wrong");
|
||||
@@ -73,11 +75,11 @@ private:
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
Loader::ResultStatus status;
|
||||
Loader::ResultStatus status{};
|
||||
|
||||
Header pfs_header;
|
||||
bool is_hfs;
|
||||
size_t content_offset;
|
||||
Header pfs_header{};
|
||||
bool is_hfs = false;
|
||||
size_t content_offset = 0;
|
||||
|
||||
std::vector<VirtualFile> pfs_files;
|
||||
std::vector<VirtualDir> pfs_dirs;
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
// 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
|
||||
@@ -1,61 +0,0 @@
|
||||
// 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
|
||||
25
src/core/file_sys/romfs_factory.cpp
Normal file
25
src/core/file_sys/romfs_factory.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
|
||||
// Load the RomFS from the app
|
||||
if (Loader::ResultStatus::Success != app_loader.ReadRomFS(file)) {
|
||||
LOG_ERROR(Service_FS, "Unable to read RomFS!");
|
||||
}
|
||||
}
|
||||
|
||||
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id) {
|
||||
// TODO(DarkLordZach): Use title id.
|
||||
return MakeResult<VirtualFile>(file);
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
25
src/core/file_sys/romfs_factory.h
Normal file
25
src/core/file_sys/romfs_factory.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
/// File system interface to the RomFS archive
|
||||
class RomFSFactory {
|
||||
public:
|
||||
explicit RomFSFactory(Loader::AppLoader& app_loader);
|
||||
|
||||
ResultVal<VirtualFile> Open(u64 title_id);
|
||||
|
||||
private:
|
||||
VirtualFile file;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
106
src/core/file_sys/savedata_factory.cpp
Normal file
106
src/core/file_sys/savedata_factory.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
std::string SaveDataDescriptor::DebugInfo() const {
|
||||
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);
|
||||
}
|
||||
|
||||
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {}
|
||||
|
||||
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) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Return an error if the save data doesn't actually exist.
|
||||
if (out == nullptr) {
|
||||
// TODO(Subv): Find out correct error code.
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
return MakeResult<VirtualDir>(std::move(out));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
58
src/core/file_sys/savedata_factory.h
Normal file
58
src/core/file_sys/savedata_factory.h
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/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() const;
|
||||
};
|
||||
static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size.");
|
||||
|
||||
/// File system interface to the SaveData archive
|
||||
class SaveDataFactory {
|
||||
public:
|
||||
explicit SaveDataFactory(VirtualDir dir);
|
||||
|
||||
ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
|
||||
|
||||
private:
|
||||
VirtualDir dir;
|
||||
|
||||
std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id,
|
||||
u64 save_id) const;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
17
src/core/file_sys/sdmc_factory.cpp
Normal file
17
src/core/file_sys/sdmc_factory.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/sdmc_factory.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
SDMCFactory::SDMCFactory(VirtualDir dir) : dir(std::move(dir)) {}
|
||||
|
||||
ResultVal<VirtualDir> SDMCFactory::Open() {
|
||||
return MakeResult<VirtualDir>(dir);
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
22
src/core/file_sys/sdmc_factory.h
Normal file
22
src/core/file_sys/sdmc_factory.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
/// File system interface to the SDCard archive
|
||||
class SDMCFactory {
|
||||
public:
|
||||
explicit SDMCFactory(VirtualDir dir);
|
||||
|
||||
ResultVal<VirtualDir> Open();
|
||||
|
||||
private:
|
||||
VirtualDir dir;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
@@ -1,63 +0,0 @@
|
||||
// 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
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
@@ -54,10 +55,10 @@ std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(const std::string& path)
|
||||
if (vec.size() == 1)
|
||||
return GetFile(vec[0]);
|
||||
auto dir = GetSubdirectory(vec[0]);
|
||||
for (size_t i = 1; i < vec.size() - 1; ++i) {
|
||||
for (size_t component = 1; component < vec.size() - 1; ++component) {
|
||||
if (dir == nullptr)
|
||||
return nullptr;
|
||||
dir = dir->GetSubdirectory(vec[i]);
|
||||
dir = dir->GetSubdirectory(vec[component]);
|
||||
}
|
||||
if (dir == nullptr)
|
||||
return nullptr;
|
||||
@@ -76,11 +77,14 @@ std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(const std::stri
|
||||
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
|
||||
vec.end());
|
||||
if (vec.empty())
|
||||
// return std::shared_ptr<VfsDirectory>(this);
|
||||
// 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 i = 1; i < vec.size(); ++i) {
|
||||
dir = dir->GetSubdirectory(vec[i]);
|
||||
for (size_t component = 1; component < vec.size(); ++component) {
|
||||
if (dir == nullptr)
|
||||
return nullptr;
|
||||
dir = dir->GetSubdirectory(vec[component]);
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
@@ -112,18 +116,65 @@ bool VfsDirectory::IsRoot() const {
|
||||
|
||||
size_t VfsDirectory::GetSize() const {
|
||||
const auto& files = GetFiles();
|
||||
const auto file_total =
|
||||
std::accumulate(files.begin(), files.end(), 0ull,
|
||||
[](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); });
|
||||
const auto 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 =
|
||||
std::accumulate(sub_dir.begin(), sub_dir.end(), 0ull,
|
||||
[](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); });
|
||||
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)
|
||||
|
||||
@@ -173,6 +173,23 @@ struct VfsDirectory : NonCopyable {
|
||||
// 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
|
||||
|
||||
@@ -76,6 +76,10 @@ bool RealVfsFile::Rename(const std::string& name) {
|
||||
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)),
|
||||
@@ -84,20 +88,19 @@ RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
|
||||
if (!FileUtil::Exists(path) && (perms == Mode::Write || perms == Mode::Append))
|
||||
FileUtil::CreateDir(path);
|
||||
unsigned size;
|
||||
if (perms != Mode::Append) {
|
||||
FileUtil::ForeachDirectoryEntry(
|
||||
&size, path,
|
||||
[this](unsigned* entries_out, const std::string& directory,
|
||||
const std::string& filename) {
|
||||
std::string full_path = directory + DIR_SEP + filename;
|
||||
if (FileUtil::IsDirectory(full_path))
|
||||
subdirectories.emplace_back(
|
||||
std::make_shared<RealVfsDirectory>(full_path, perms));
|
||||
else
|
||||
files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
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 {
|
||||
@@ -146,6 +149,12 @@ bool RealVfsDirectory::DeleteSubdirectory(const std::string& 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "core/file_sys/filesystem.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;
|
||||
@@ -25,6 +27,8 @@ struct RealVfsFile : public VfsFile {
|
||||
bool Rename(const std::string& name) override;
|
||||
|
||||
private:
|
||||
bool Close();
|
||||
|
||||
FileUtil::IOFile backing;
|
||||
std::string path;
|
||||
std::string parent_path;
|
||||
|
||||
@@ -61,10 +61,16 @@ const u32 SIGTERM = 15;
|
||||
const u32 MSG_WAITALL = 8;
|
||||
#endif
|
||||
|
||||
const u32 X30_REGISTER = 30;
|
||||
const u32 LR_REGISTER = 30;
|
||||
const u32 SP_REGISTER = 31;
|
||||
const u32 PC_REGISTER = 32;
|
||||
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
|
||||
@@ -130,6 +136,8 @@ static const char* target_xml =
|
||||
</flags>
|
||||
<reg name="cpsr" bitsize="32" type="cpsr_flags"/>
|
||||
</feature>
|
||||
<feature name="org.gnu.gdb.aarch64.fpu">
|
||||
</feature>
|
||||
</target>
|
||||
)";
|
||||
|
||||
@@ -144,6 +152,7 @@ static u32 latest_signal = 0;
|
||||
static bool memory_break = false;
|
||||
|
||||
static Kernel::Thread* current_thread = nullptr;
|
||||
static u32 current_core = 0;
|
||||
|
||||
// Binding to a port within the reserved ports range (0-1023) requires root permissions,
|
||||
// so default to a port outside of that range.
|
||||
@@ -171,20 +180,41 @@ static std::map<u64, Breakpoint> breakpoints_execute;
|
||||
static std::map<u64, Breakpoint> breakpoints_read;
|
||||
static std::map<u64, Breakpoint> breakpoints_write;
|
||||
|
||||
struct Module {
|
||||
std::string name;
|
||||
PAddr beg;
|
||||
PAddr end;
|
||||
};
|
||||
|
||||
static std::vector<Module> modules;
|
||||
|
||||
void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext) {
|
||||
Module module;
|
||||
if (add_elf_ext) {
|
||||
Common::SplitPath(name, nullptr, &module.name, nullptr);
|
||||
module.name += ".elf";
|
||||
} else {
|
||||
module.name = std::move(name);
|
||||
}
|
||||
module.beg = beg;
|
||||
module.end = end;
|
||||
modules.push_back(std::move(module));
|
||||
}
|
||||
|
||||
static Kernel::Thread* FindThreadById(int id) {
|
||||
for (int core = 0; core < Core::NUM_CPU_CORES; core++) {
|
||||
auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
|
||||
for (auto thread : threads) {
|
||||
if (thread->GetThreadId() == id) {
|
||||
current_thread = thread.get();
|
||||
return current_thread;
|
||||
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
|
||||
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
|
||||
for (auto& thread : threads) {
|
||||
if (thread->GetThreadId() == static_cast<u32>(id)) {
|
||||
current_core = core;
|
||||
return thread.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static u64 RegRead(int id, Kernel::Thread* thread = nullptr) {
|
||||
static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) {
|
||||
if (!thread) {
|
||||
return 0;
|
||||
}
|
||||
@@ -197,12 +227,14 @@ static u64 RegRead(int id, Kernel::Thread* thread = nullptr) {
|
||||
return thread->context.pc;
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
return thread->context.cpsr;
|
||||
} else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
|
||||
return thread->context.fpu_registers[id - UC_ARM64_REG_Q0][0];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void RegWrite(int id, u64 val, Kernel::Thread* thread = nullptr) {
|
||||
static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr) {
|
||||
if (!thread) {
|
||||
return;
|
||||
}
|
||||
@@ -215,6 +247,8 @@ static void RegWrite(int id, u64 val, Kernel::Thread* thread = nullptr) {
|
||||
thread->context.pc = val;
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
thread->context.cpsr = val;
|
||||
} else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
|
||||
thread->context.fpu_registers[id - (CPSR_REGISTER + 1)][0] = val;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,7 +568,11 @@ static void HandleQuery() {
|
||||
SendReply("T0");
|
||||
} else if (strncmp(query, "Supported", strlen("Supported")) == 0) {
|
||||
// PacketSize needs to be large enough for target xml
|
||||
SendReply("PacketSize=2000;qXfer:features:read+");
|
||||
std::string buffer = "PacketSize=2000;qXfer:features:read+;qXfer:threads:read+";
|
||||
if (!modules.empty()) {
|
||||
buffer += ";qXfer:libraries:read+";
|
||||
}
|
||||
SendReply(buffer.c_str());
|
||||
} else if (strncmp(query, "Xfer:features:read:target.xml:",
|
||||
strlen("Xfer:features:read:target.xml:")) == 0) {
|
||||
SendReply(target_xml);
|
||||
@@ -543,9 +581,9 @@ static void HandleQuery() {
|
||||
SendReply(buffer.c_str());
|
||||
} else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
|
||||
std::string val = "m";
|
||||
for (int core = 0; core < Core::NUM_CPU_CORES; core++) {
|
||||
auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
|
||||
for (auto thread : threads) {
|
||||
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
|
||||
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
|
||||
for (const auto& thread : threads) {
|
||||
val += fmt::format("{:x}", thread->GetThreadId());
|
||||
val += ",";
|
||||
}
|
||||
@@ -554,6 +592,31 @@ static void HandleQuery() {
|
||||
SendReply(val.c_str());
|
||||
} else if (strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0) {
|
||||
SendReply("l");
|
||||
} else if (strncmp(query, "Xfer:threads:read", strlen("Xfer:threads:read")) == 0) {
|
||||
std::string buffer;
|
||||
buffer += "l<?xml version=\"1.0\"?>";
|
||||
buffer += "<threads>";
|
||||
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
|
||||
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
|
||||
for (const auto& thread : threads) {
|
||||
buffer +=
|
||||
fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
|
||||
thread->GetThreadId(), core, thread->GetThreadId());
|
||||
}
|
||||
}
|
||||
buffer += "</threads>";
|
||||
SendReply(buffer.c_str());
|
||||
} else if (strncmp(query, "Xfer:libraries:read", strlen("Xfer:libraries:read")) == 0) {
|
||||
std::string buffer;
|
||||
buffer += "l<?xml version=\"1.0\"?>";
|
||||
buffer += "<library-list>";
|
||||
for (const auto& module : modules) {
|
||||
buffer +=
|
||||
fmt::format(R"*("<library name = "{}"><segment address = "0x{:x}"/></library>)*",
|
||||
module.name, module.beg);
|
||||
}
|
||||
buffer += "</library-list>";
|
||||
SendReply(buffer.c_str());
|
||||
} else {
|
||||
SendReply("");
|
||||
}
|
||||
@@ -561,33 +624,27 @@ static void HandleQuery() {
|
||||
|
||||
/// Handle set thread command from gdb client.
|
||||
static void HandleSetThread() {
|
||||
if (memcmp(command_buffer, "Hc", 2) == 0 || memcmp(command_buffer, "Hg", 2) == 0) {
|
||||
int thread_id = -1;
|
||||
if (command_buffer[2] != '-') {
|
||||
thread_id = static_cast<int>(HexToInt(
|
||||
command_buffer + 2,
|
||||
command_length - 2 /*strlen(reinterpret_cast<char*>(command_buffer) + 2)*/));
|
||||
}
|
||||
if (thread_id >= 1) {
|
||||
current_thread = FindThreadById(thread_id);
|
||||
}
|
||||
if (!current_thread) {
|
||||
thread_id = 1;
|
||||
current_thread = FindThreadById(thread_id);
|
||||
}
|
||||
if (current_thread) {
|
||||
SendReply("OK");
|
||||
return;
|
||||
}
|
||||
int thread_id = -1;
|
||||
if (command_buffer[2] != '-') {
|
||||
thread_id = static_cast<int>(HexToInt(command_buffer + 2, command_length - 2));
|
||||
}
|
||||
if (thread_id >= 1) {
|
||||
current_thread = FindThreadById(thread_id);
|
||||
}
|
||||
if (!current_thread) {
|
||||
thread_id = 1;
|
||||
current_thread = FindThreadById(thread_id);
|
||||
}
|
||||
if (current_thread) {
|
||||
SendReply("OK");
|
||||
return;
|
||||
}
|
||||
SendReply("E01");
|
||||
}
|
||||
|
||||
/// Handle thread alive command from gdb client.
|
||||
static void HandleThreadAlive() {
|
||||
int thread_id = static_cast<int>(
|
||||
HexToInt(command_buffer + 1,
|
||||
command_length - 1 /*strlen(reinterpret_cast<char*>(command_buffer) + 1)*/));
|
||||
int thread_id = static_cast<int>(HexToInt(command_buffer + 1, command_length - 1));
|
||||
if (thread_id == 0) {
|
||||
thread_id = 1;
|
||||
}
|
||||
@@ -610,16 +667,23 @@ static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) {
|
||||
|
||||
latest_signal = signal;
|
||||
|
||||
std::string buffer;
|
||||
if (full) {
|
||||
buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};", latest_signal, PC_REGISTER,
|
||||
Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER,
|
||||
Common::swap64(RegRead(SP_REGISTER, thread)));
|
||||
} else {
|
||||
buffer = fmt::format("T{:02x};", latest_signal);
|
||||
if (!thread) {
|
||||
full = false;
|
||||
}
|
||||
|
||||
buffer += fmt::format("thread:{:x};", thread->GetThreadId());
|
||||
std::string buffer;
|
||||
if (full) {
|
||||
buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};{:02x}:{:016x}", latest_signal,
|
||||
PC_REGISTER, Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER,
|
||||
Common::swap64(RegRead(SP_REGISTER, thread)), LR_REGISTER,
|
||||
Common::swap64(RegRead(LR_REGISTER, thread)));
|
||||
} else {
|
||||
buffer = fmt::format("T{:02x}", latest_signal);
|
||||
}
|
||||
|
||||
if (thread) {
|
||||
buffer += fmt::format(";thread:{:x};", thread->GetThreadId());
|
||||
}
|
||||
|
||||
SendReply(buffer.c_str());
|
||||
}
|
||||
@@ -680,7 +744,7 @@ static bool IsDataAvailable() {
|
||||
fd_set fd_socket;
|
||||
|
||||
FD_ZERO(&fd_socket);
|
||||
FD_SET(gdbserver_socket, &fd_socket);
|
||||
FD_SET(static_cast<u32>(gdbserver_socket), &fd_socket);
|
||||
|
||||
struct timeval t;
|
||||
t.tv_sec = 0;
|
||||
@@ -711,8 +775,12 @@ static void ReadRegister() {
|
||||
LongToGdbHex(reply, RegRead(id, current_thread));
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
IntToGdbHex(reply, (u32)RegRead(id, current_thread));
|
||||
} 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));
|
||||
} else {
|
||||
return SendReply("E01");
|
||||
LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread));
|
||||
}
|
||||
|
||||
SendReply(reinterpret_cast<char*>(reply));
|
||||
@@ -725,11 +793,11 @@ static void ReadRegisters() {
|
||||
|
||||
u8* bufptr = buffer;
|
||||
|
||||
for (int reg = 0; reg <= SP_REGISTER; reg++) {
|
||||
for (u32 reg = 0; reg <= SP_REGISTER; reg++) {
|
||||
LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));
|
||||
}
|
||||
|
||||
bufptr += (32 * 16);
|
||||
bufptr += 32 * 16;
|
||||
|
||||
LongToGdbHex(bufptr, RegRead(PC_REGISTER, current_thread));
|
||||
|
||||
@@ -739,6 +807,16 @@ static void ReadRegisters() {
|
||||
|
||||
bufptr += 8;
|
||||
|
||||
for (u32 reg = UC_ARM64_REG_Q0; reg <= UC_ARM64_REG_Q0 + 31; reg++) {
|
||||
LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));
|
||||
}
|
||||
|
||||
bufptr += 32 * 32;
|
||||
|
||||
LongToGdbHex(bufptr, RegRead(TODO_DUMMY_REG_998, current_thread));
|
||||
|
||||
bufptr += 8;
|
||||
|
||||
SendReply(reinterpret_cast<char*>(buffer));
|
||||
}
|
||||
|
||||
@@ -759,10 +837,17 @@ static void WriteRegister() {
|
||||
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
RegWrite(id, GdbHexToInt(buffer_ptr), current_thread);
|
||||
} 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);
|
||||
} else {
|
||||
return SendReply("E01");
|
||||
RegWrite(TODO_DUMMY_REG_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");
|
||||
}
|
||||
|
||||
@@ -773,18 +858,25 @@ static void WriteRegisters() {
|
||||
if (command_buffer[0] != 'G')
|
||||
return SendReply("E01");
|
||||
|
||||
for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) {
|
||||
for (u32 i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) {
|
||||
if (reg <= SP_REGISTER) {
|
||||
RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
|
||||
} else if (reg == PC_REGISTER) {
|
||||
RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
|
||||
} else if (reg == CPSR_REGISTER) {
|
||||
RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread);
|
||||
} 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);
|
||||
} 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");
|
||||
}
|
||||
|
||||
@@ -806,6 +898,10 @@ static void ReadMemory() {
|
||||
SendReply("E01");
|
||||
}
|
||||
|
||||
if (addr < Memory::PROCESS_IMAGE_VADDR || addr >= Memory::MAP_REGION_VADDR_END) {
|
||||
return SendReply("E00");
|
||||
}
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(addr)) {
|
||||
return SendReply("E00");
|
||||
}
|
||||
@@ -840,16 +936,18 @@ static void WriteMemory() {
|
||||
}
|
||||
|
||||
void Break(bool is_memory_break) {
|
||||
if (!halt_loop) {
|
||||
halt_loop = true;
|
||||
send_trap = true;
|
||||
}
|
||||
send_trap = true;
|
||||
|
||||
memory_break = is_memory_break;
|
||||
}
|
||||
|
||||
/// Tell the CPU that it should perform a single step.
|
||||
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;
|
||||
halt_loop = true;
|
||||
send_trap = true;
|
||||
@@ -1090,6 +1188,8 @@ static void Init(u16 port) {
|
||||
breakpoints_read.clear();
|
||||
breakpoints_write.clear();
|
||||
|
||||
modules.clear();
|
||||
|
||||
// Start gdb server
|
||||
LOG_INFO(Debug_GDBStub, "Starting GDB server on port {}...", port);
|
||||
|
||||
@@ -1192,8 +1292,12 @@ void SetCpuStepFlag(bool is_step) {
|
||||
|
||||
void SendTrap(Kernel::Thread* thread, int trap) {
|
||||
if (send_trap) {
|
||||
if (!halt_loop || current_thread == thread) {
|
||||
current_thread = thread;
|
||||
SendSignal(thread, trap);
|
||||
}
|
||||
halt_loop = true;
|
||||
send_trap = false;
|
||||
SendSignal(thread, trap);
|
||||
}
|
||||
}
|
||||
}; // namespace GDBStub
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
@@ -51,6 +52,9 @@ bool IsServerEnabled();
|
||||
/// Returns true if there is an active socket connection.
|
||||
bool IsConnected();
|
||||
|
||||
/// Register module.
|
||||
void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext = true);
|
||||
|
||||
/**
|
||||
* Signal to the gdbstub server that it should halt CPU execution.
|
||||
*
|
||||
@@ -80,10 +84,10 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointTy
|
||||
*/
|
||||
bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type);
|
||||
|
||||
// If set to true, the CPU will halt at the beginning of the next CPU loop.
|
||||
/// If set to true, the CPU will halt at the beginning of the next CPU loop.
|
||||
bool GetCpuHaltFlag();
|
||||
|
||||
// If set to true and the CPU is halted, the CPU will step one instruction.
|
||||
/// If set to true and the CPU is halted, the CPU will step one instruction.
|
||||
bool GetCpuStepFlag();
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() <= num_to_wake) {
|
||||
} else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
|
||||
updated_value = value + 1;
|
||||
} else {
|
||||
updated_value = value;
|
||||
@@ -140,7 +140,9 @@ ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool
|
||||
|
||||
s32 cur_value = static_cast<s32>(Memory::Read32(address));
|
||||
if (cur_value < value) {
|
||||
Memory::Write32(address, static_cast<u32>(cur_value - 1));
|
||||
if (should_decrement) {
|
||||
Memory::Write32(address, static_cast<u32>(cur_value - 1));
|
||||
}
|
||||
} else {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// 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"
|
||||
@@ -19,17 +21,18 @@ namespace Kernel {
|
||||
|
||||
void SessionRequestHandler::ClientConnected(SharedPtr<ServerSession> server_session) {
|
||||
server_session->SetHleHandler(shared_from_this());
|
||||
connected_sessions.push_back(server_session);
|
||||
connected_sessions.push_back(std::move(server_session));
|
||||
}
|
||||
|
||||
void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_session) {
|
||||
void SessionRequestHandler::ClientDisconnected(const 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) {
|
||||
WakeupCallback&& callback,
|
||||
Kernel::SharedPtr<Kernel::Event> event) {
|
||||
|
||||
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
|
||||
thread->wakeup_callback =
|
||||
@@ -41,7 +44,12 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
|
||||
return true;
|
||||
};
|
||||
|
||||
auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
|
||||
if (!event) {
|
||||
// Create event if not provided
|
||||
event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
|
||||
}
|
||||
|
||||
event->Clear();
|
||||
thread->status = THREADSTATUS_WAIT_HLE_EVENT;
|
||||
thread->wait_objects = {event};
|
||||
event->AddWaitingThread(thread);
|
||||
@@ -214,8 +222,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_MSG(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
|
||||
ASSERT_MSG(move_objects.size() == handle_descriptor_header->num_handles_to_move);
|
||||
ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
|
||||
ASSERT(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
|
||||
@@ -293,10 +301,6 @@ size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size, int buffe
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t HLERequestContext::WriteBuffer(const std::vector<u8>& buffer, int buffer_index) const {
|
||||
return WriteBuffer(buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
size_t HLERequestContext::GetReadBufferSize(int buffer_index) const {
|
||||
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()};
|
||||
return is_buffer_a ? BufferDescriptorA()[buffer_index].Size()
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include "common/common_types.h"
|
||||
@@ -59,12 +61,12 @@ public:
|
||||
* associated ServerSession.
|
||||
* @param server_session ServerSession associated with the connection.
|
||||
*/
|
||||
void ClientDisconnected(SharedPtr<ServerSession> server_session);
|
||||
void ClientDisconnected(const 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,10 +120,12 @@ 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);
|
||||
u64 timeout, WakeupCallback&& callback,
|
||||
Kernel::SharedPtr<Kernel::Event> event = nullptr);
|
||||
|
||||
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
|
||||
|
||||
@@ -169,8 +173,25 @@ public:
|
||||
/// Helper function to write a buffer using the appropriate buffer descriptor
|
||||
size_t WriteBuffer(const void* buffer, size_t size, int buffer_index = 0) const;
|
||||
|
||||
/// Helper function to write a buffer using the appropriate buffer descriptor
|
||||
size_t WriteBuffer(const std::vector<u8>& buffer, int buffer_index = 0) const;
|
||||
/* Helper function to write a buffer using the appropriate buffer descriptor
|
||||
*
|
||||
* @tparam ContiguousContainer an arbitrary container that satisfies the
|
||||
* ContiguousContainer concept in the C++ standard library.
|
||||
*
|
||||
* @param container The container to write the data of into a buffer.
|
||||
* @param buffer_index The buffer in particular to write to.
|
||||
*/
|
||||
template <typename ContiguousContainer,
|
||||
typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>>
|
||||
size_t WriteBuffer(const ContiguousContainer& container, int buffer_index = 0) const {
|
||||
using ContiguousType = typename ContiguousContainer::value_type;
|
||||
|
||||
static_assert(std::is_trivially_copyable_v<ContiguousType>,
|
||||
"Container to WriteBuffer must contain trivially copyable objects");
|
||||
|
||||
return WriteBuffer(std::data(container), std::size(container) * sizeof(ContiguousType),
|
||||
buffer_index);
|
||||
}
|
||||
|
||||
/// Helper function to get the size of the input buffer
|
||||
size_t GetReadBufferSize(int buffer_index = 0) const;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// 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"
|
||||
|
||||
@@ -11,7 +13,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] = obj;
|
||||
objects[addr] = std::move(obj);
|
||||
}
|
||||
|
||||
void ObjectAddressTable::Close(VAddr addr) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// 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"
|
||||
@@ -85,6 +87,7 @@ 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
|
||||
@@ -112,7 +115,7 @@ void Scheduler::Reschedule() {
|
||||
void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) {
|
||||
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
||||
|
||||
thread_list.push_back(thread);
|
||||
thread_list.push_back(std::move(thread));
|
||||
ready_queue.prepare(priority);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
@@ -158,7 +159,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 = port;
|
||||
parent->port = std::move(port);
|
||||
|
||||
client_session->parent = parent;
|
||||
server_session->parent = parent;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
#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 = owner_process;
|
||||
shared_memory->owner_process = std::move(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 = heap_block;
|
||||
shared_memory->backing_block = std::move(heap_block);
|
||||
shared_memory->backing_block_offset = offset;
|
||||
shared_memory->base_address = Memory::HEAP_VADDR + offset;
|
||||
|
||||
|
||||
@@ -795,8 +795,9 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (core == THREADPROCESSORID_DEFAULT) {
|
||||
ASSERT(thread->owner_process->ideal_processor != THREADPROCESSORID_DEFAULT);
|
||||
if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) {
|
||||
ASSERT(thread->owner_process->ideal_processor !=
|
||||
static_cast<u8>(THREADPROCESSORID_DEFAULT));
|
||||
// Set the target CPU to the one specified in the process' exheader.
|
||||
core = thread->owner_process->ideal_processor;
|
||||
mask = 1ull << core;
|
||||
@@ -811,7 +812,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
|
||||
if (core == OnlyChangeMask) {
|
||||
core = thread->ideal_core;
|
||||
} else if (core >= Core::NUM_CPU_CORES && core != -1) {
|
||||
} else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
|
||||
}
|
||||
|
||||
|
||||
@@ -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, owner_process);
|
||||
Memory::STACK_AREA_VADDR_END, std::move(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
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// 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"
|
||||
@@ -16,30 +17,20 @@
|
||||
namespace Kernel {
|
||||
|
||||
static const char* GetMemoryStateName(MemoryState state) {
|
||||
static const char* names[] = {
|
||||
"Unmapped",
|
||||
"Io",
|
||||
"Normal",
|
||||
"CodeStatic",
|
||||
"CodeMutable",
|
||||
"Heap",
|
||||
"Shared",
|
||||
"Unknown1"
|
||||
"ModuleCodeStatic",
|
||||
"ModuleCodeMutable",
|
||||
"IpcBuffer0",
|
||||
"Mapped",
|
||||
"ThreadLocal",
|
||||
"TransferMemoryIsolated",
|
||||
"TransferMemory",
|
||||
"ProcessMemory",
|
||||
"Unknown2"
|
||||
"IpcBuffer1",
|
||||
"IpcBuffer3",
|
||||
"KernelStack",
|
||||
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",
|
||||
};
|
||||
|
||||
return names[(int)state];
|
||||
return names[static_cast<int>(state)];
|
||||
}
|
||||
|
||||
bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
|
||||
@@ -117,7 +108,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 = block;
|
||||
final_vma.backing_block = std::move(block);
|
||||
final_vma.offset = offset;
|
||||
UpdatePageTableForVMA(final_vma);
|
||||
|
||||
@@ -160,7 +151,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 = mmio_handler;
|
||||
final_vma.mmio_handler = std::move(mmio_handler);
|
||||
UpdatePageTableForVMA(final_vma);
|
||||
|
||||
return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/acc/acc.h"
|
||||
@@ -24,18 +25,17 @@ struct UserData {
|
||||
static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
|
||||
|
||||
struct ProfileBase {
|
||||
u8 user_id[0x10];
|
||||
u128 user_id;
|
||||
u64 timestamp;
|
||||
u8 username[0x20];
|
||||
std::array<u8, 0x20> username;
|
||||
};
|
||||
static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase structure has incorrect size");
|
||||
|
||||
using Uid = std::array<u64, 2>;
|
||||
static constexpr Uid DEFAULT_USER_ID{0x10ull, 0x20ull};
|
||||
static constexpr u128 DEFAULT_USER_ID{1ull, 0ull};
|
||||
|
||||
class IProfile final : public ServiceFramework<IProfile> {
|
||||
public:
|
||||
IProfile() : ServiceFramework("IProfile") {
|
||||
IProfile(u128 user_id) : ServiceFramework("IProfile"), user_id(user_id) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Get"},
|
||||
{1, &IProfile::GetBase, "GetBase"},
|
||||
@@ -48,11 +48,18 @@ public:
|
||||
private:
|
||||
void GetBase(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
// TODO(Subv): Retrieve this information from somewhere.
|
||||
ProfileBase profile_base{};
|
||||
profile_base.user_id = user_id;
|
||||
profile_base.username = {'y', 'u', 'z', 'u'};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 16};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(profile_base);
|
||||
}
|
||||
|
||||
u128 user_id; ///< The user id this profile refers to.
|
||||
};
|
||||
|
||||
class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
|
||||
@@ -95,25 +102,29 @@ void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID};
|
||||
ctx.WriteBuffer(user_ids.data(), user_ids.size());
|
||||
// TODO(Subv): There is only one user for now.
|
||||
const std::vector<u128> user_ids = {DEFAULT_USER_ID};
|
||||
ctx.WriteBuffer(user_ids.data(), user_ids.size() * sizeof(u128));
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID};
|
||||
ctx.WriteBuffer(user_ids.data(), user_ids.size());
|
||||
// TODO(Subv): There is only one user for now.
|
||||
const std::vector<u128> user_ids = {DEFAULT_USER_ID};
|
||||
ctx.WriteBuffer(user_ids.data(), user_ids.size() * sizeof(u128));
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u128 user_id = rp.PopRaw<u128>();
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IProfile>();
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
rb.PushIpcInterface<IProfile>(user_id);
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{:016X}{:016X}", user_id[1], user_id[0]);
|
||||
}
|
||||
|
||||
void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -12,7 +12,7 @@ class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name);
|
||||
explicit Interface(std::shared_ptr<Module> module, const char* name);
|
||||
|
||||
void GetUserExistence(Kernel::HLERequestContext& ctx);
|
||||
void ListAllUsers(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
|
||||
#include <cinttypes>
|
||||
#include <stack>
|
||||
#include "core/file_sys/filesystem.h"
|
||||
#include "core/core.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"
|
||||
@@ -614,25 +615,14 @@ void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
|
||||
|
||||
void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u128 uid = rp.PopRaw<u128>();
|
||||
u128 uid = rp.PopRaw<u128>(); // What does this do?
|
||||
|
||||
LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
FileSys::Path unused;
|
||||
auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData);
|
||||
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(RESULT_SUCCESS);
|
||||
rb.Push<u64>(0);
|
||||
}
|
||||
} // namespace Service::AM
|
||||
|
||||
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
|
||||
// Takes an input u32 Result, no output.
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
|
||||
class ISelfController final : public ServiceFramework<ISelfController> {
|
||||
public:
|
||||
ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
|
||||
explicit ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
|
||||
|
||||
private:
|
||||
void SetFocusHandlingMode(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Service::AM {
|
||||
|
||||
class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
|
||||
public:
|
||||
ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace AM {
|
||||
|
||||
class AppletAE final : public ServiceFramework<AppletAE> {
|
||||
public:
|
||||
AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
|
||||
explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
|
||||
~AppletAE() = default;
|
||||
|
||||
private:
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Service::AM {
|
||||
|
||||
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
|
||||
public:
|
||||
IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace AM {
|
||||
|
||||
class AppletOE final : public ServiceFramework<AppletOE> {
|
||||
public:
|
||||
AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
|
||||
explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
|
||||
~AppletOE() = default;
|
||||
|
||||
private:
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Service::APM {
|
||||
|
||||
class APM final : public ServiceFramework<APM> {
|
||||
public:
|
||||
APM(std::shared_ptr<Module> apm, const char* name);
|
||||
explicit APM(std::shared_ptr<Module> apm, const char* name);
|
||||
~APM() = default;
|
||||
|
||||
private:
|
||||
|
||||
@@ -27,12 +27,12 @@ public:
|
||||
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
|
||||
{1, &IAudioOut::StartAudioOut, "StartAudioOut"},
|
||||
{2, &IAudioOut::StopAudioOut, "StopAudioOut"},
|
||||
{3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"},
|
||||
{3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},
|
||||
{4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
|
||||
{5, &IAudioOut::GetReleasedAudioOutBuffer, "GetReleasedAudioOutBuffer"},
|
||||
{5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"},
|
||||
{6, nullptr, "ContainsAudioOutBuffer"},
|
||||
{7, nullptr, "AppendAudioOutBufferAuto"},
|
||||
{8, nullptr, "GetReleasedAudioOutBufferAuto"},
|
||||
{7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
|
||||
{8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
|
||||
{9, nullptr, "GetAudioOutBufferCount"},
|
||||
{10, nullptr, "GetAudioOutPlayedSampleCount"},
|
||||
{11, nullptr, "FlushAudioOutBuffers"},
|
||||
@@ -96,7 +96,7 @@ private:
|
||||
rb.PushCopyObjects(buffer_event);
|
||||
}
|
||||
|
||||
void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) {
|
||||
void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
@@ -107,7 +107,7 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetReleasedAudioOutBuffer(Kernel::HLERequestContext& ctx) {
|
||||
void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
|
||||
// TODO(st4rk): This is how libtransistor currently implements the
|
||||
@@ -163,12 +163,12 @@ private:
|
||||
AudioState audio_out_state;
|
||||
};
|
||||
|
||||
void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
|
||||
void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const std::string audio_interface = "AudioInterface";
|
||||
ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size());
|
||||
ctx.WriteBuffer(audio_interface);
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
|
||||
|
||||
@@ -179,7 +179,7 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(1);
|
||||
}
|
||||
|
||||
void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
|
||||
void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
|
||||
if (!audio_out_interface) {
|
||||
@@ -196,10 +196,10 @@ void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
AudOutU::AudOutU() : ServiceFramework("audout:u") {
|
||||
static const FunctionInfo functions[] = {{0, &AudOutU::ListAudioOuts, "ListAudioOuts"},
|
||||
{1, &AudOutU::OpenAudioOut, "OpenAudioOut"},
|
||||
{2, nullptr, "ListAudioOutsAuto"},
|
||||
{3, nullptr, "OpenAudioOutAuto"}};
|
||||
static const FunctionInfo functions[] = {{0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"},
|
||||
{1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"},
|
||||
{2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
|
||||
{3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ public:
|
||||
private:
|
||||
std::shared_ptr<IAudioOut> audio_out_interface;
|
||||
|
||||
void ListAudioOuts(Kernel::HLERequestContext& ctx);
|
||||
void OpenAudioOut(Kernel::HLERequestContext& ctx);
|
||||
void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
|
||||
void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
|
||||
|
||||
enum class PcmFormat : u32 {
|
||||
Invalid = 0,
|
||||
|
||||
@@ -17,7 +17,7 @@ constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)};
|
||||
|
||||
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
|
||||
public:
|
||||
IAudioRenderer(AudioRendererParameter audren_params)
|
||||
explicit 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.reserve(worker_params.voice_count);
|
||||
voice_status_list.resize(worker_params.voice_count);
|
||||
}
|
||||
~IAudioRenderer() {
|
||||
CoreTiming::UnscheduleEvent(audio_event, 0);
|
||||
@@ -87,8 +87,6 @@ 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);
|
||||
@@ -178,12 +176,14 @@ private:
|
||||
struct UpdateDataHeader {
|
||||
UpdateDataHeader() {}
|
||||
|
||||
UpdateDataHeader(const AudioRendererParameter& config) {
|
||||
explicit 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 +
|
||||
@@ -299,7 +299,7 @@ private:
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const std::string audio_interface = "AudioInterface";
|
||||
ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size());
|
||||
ctx.WriteBuffer(audio_interface);
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -324,7 +324,7 @@ private:
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const std::string audio_interface = "AudioDevice";
|
||||
ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size());
|
||||
ctx.WriteBuffer(audio_interface);
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -12,7 +12,7 @@ class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name);
|
||||
explicit Interface(std::shared_ptr<Module> module, const char* name);
|
||||
|
||||
void CreateBcatService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name);
|
||||
explicit Interface(std::shared_ptr<Module> module, const char* name);
|
||||
|
||||
void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx);
|
||||
void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "boost/container/flat_map.hpp"
|
||||
#include <utility>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_paths.h"
|
||||
#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/kernel/process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/filesystem/fsp_srv.h"
|
||||
|
||||
@@ -25,14 +25,14 @@ 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 == "\\")
|
||||
if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
|
||||
return base;
|
||||
|
||||
return base->GetDirectoryRelative(dir_name);
|
||||
}
|
||||
|
||||
VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
|
||||
: backing(backing_) {}
|
||||
: backing(std::move(backing_)) {}
|
||||
|
||||
std::string VfsDirectoryServiceWrapper::GetName() const {
|
||||
return backing->GetName();
|
||||
@@ -41,17 +41,29 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
|
||||
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)
|
||||
if (file == nullptr) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultCode(-1);
|
||||
if (!file->Resize(size))
|
||||
}
|
||||
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 (!backing->DeleteFile(FileUtil::GetFilename(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;
|
||||
}
|
||||
|
||||
@@ -60,22 +72,28 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path)
|
||||
if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
|
||||
dir = backing;
|
||||
auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path));
|
||||
if (new_dir == nullptr)
|
||||
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)))
|
||||
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)))
|
||||
if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -86,8 +104,10 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
|
||||
// Use more-optimized vfs implementation rename.
|
||||
if (src == nullptr)
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
if (!src->Rename(FileUtil::GetFilename(dest_path)))
|
||||
if (!src->Rename(FileUtil::GetFilename(dest_path))) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -102,8 +122,10 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
|
||||
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)))
|
||||
if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -115,8 +137,10 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
|
||||
// Use more-optimized vfs implementation rename.
|
||||
if (src == nullptr)
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
if (!src->Rename(FileUtil::GetFilename(dest_path)))
|
||||
if (!src->Rename(FileUtil::GetFilename(dest_path))) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -126,16 +150,20 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
|
||||
"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 file = backing->GetFileRelative(path);
|
||||
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 ((static_cast<u32>(mode) & static_cast<u32>(FileSys::Mode::Append)) != 0) {
|
||||
if (mode == FileSys::Mode::Append) {
|
||||
return MakeResult<FileSys::VirtualFile>(
|
||||
std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize()));
|
||||
}
|
||||
@@ -145,8 +173,10 @@ ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::
|
||||
|
||||
ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) {
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, path);
|
||||
if (dir == nullptr)
|
||||
if (dir == nullptr) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
return MakeResult(dir);
|
||||
}
|
||||
|
||||
@@ -161,103 +191,96 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
|
||||
const std::string& path) const {
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
if (dir == nullptr)
|
||||
return ResultCode(-1);
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
auto filename = FileUtil::GetFilename(path);
|
||||
// TODO(Subv): Some games use the '/' path, find out what this means.
|
||||
if (filename.empty())
|
||||
return MakeResult(FileSys::EntryType::Directory);
|
||||
|
||||
if (dir->GetFile(filename) != nullptr)
|
||||
return MakeResult(FileSys::EntryType::File);
|
||||
if (dir->GetSubdirectory(filename) != nullptr)
|
||||
return MakeResult(FileSys::EntryType::Directory);
|
||||
return ResultCode(-1);
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
// A deferred filesystem for nand save data.
|
||||
// This must be deferred because the directory is dependent on title id, which is not set at
|
||||
// registration time.
|
||||
struct SaveDataDeferredFilesystem : DeferredFilesystem {
|
||||
protected:
|
||||
FileSys::VirtualDir CreateFilesystem() override {
|
||||
u64 title_id = Core::CurrentProcess()->program_id;
|
||||
// TODO(DarkLordZach): Users
|
||||
u32 user_id = 0;
|
||||
std::string nand_directory = fmt::format(
|
||||
"{}save/{:016X}/{:08X}/", FileUtil::GetUserPath(D_NAND_IDX), title_id, user_id);
|
||||
|
||||
auto savedata =
|
||||
std::make_shared<FileSys::RealVfsDirectory>(nand_directory, FileSys::Mode::Write);
|
||||
return savedata;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Map of registered file systems, identified by type. Once an file system is registered here, it
|
||||
* is never removed until UnregisterFileSystems is called.
|
||||
*/
|
||||
static boost::container::flat_map<Type, std::unique_ptr<DeferredFilesystem>> filesystem_map;
|
||||
static FileSys::VirtualFile filesystem_romfs = nullptr;
|
||||
static std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
|
||||
static std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
|
||||
static std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
|
||||
|
||||
ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& factory, Type type) {
|
||||
auto result = filesystem_map.emplace(type, std::move(factory));
|
||||
|
||||
bool inserted = result.second;
|
||||
ASSERT_MSG(inserted, "Tried to register more than one system with same id code");
|
||||
|
||||
auto& filesystem = result.first->second;
|
||||
LOG_DEBUG(Service_FS, "Registered file system with id code 0x{:08X}", static_cast<u32>(type));
|
||||
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");
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode RegisterRomFS(FileSys::VirtualFile filesystem) {
|
||||
ASSERT_MSG(filesystem_romfs == nullptr,
|
||||
"Tried to register more than one system with same id code");
|
||||
|
||||
filesystem_romfs = filesystem;
|
||||
LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(),
|
||||
static_cast<u32>(Type::RomFS));
|
||||
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<FileSys::VirtualDir> OpenFileSystem(Type type) {
|
||||
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;
|
||||
}
|
||||
|
||||
auto itr = filesystem_map.find(type);
|
||||
if (itr == filesystem_map.end()) {
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id) {
|
||||
LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}", title_id);
|
||||
|
||||
if (romfs_factory == nullptr) {
|
||||
// TODO(bunnei): Find a better error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
return MakeResult(itr->second->Get());
|
||||
return romfs_factory->Open(title_id);
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFS() {
|
||||
if (filesystem_romfs == nullptr)
|
||||
return ResultCode(-1);
|
||||
return MakeResult(filesystem_romfs);
|
||||
}
|
||||
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));
|
||||
|
||||
auto itr = filesystem_map.find(type);
|
||||
if (itr == filesystem_map.end()) {
|
||||
// TODO(bunnei): Find a better error code for this
|
||||
return ResultCode(-1);
|
||||
if (save_data_factory == nullptr) {
|
||||
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound);
|
||||
}
|
||||
|
||||
return itr->second->Get()->GetParentDirectory()->DeleteSubdirectory(
|
||||
itr->second->Get()->GetName())
|
||||
? RESULT_SUCCESS
|
||||
: 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();
|
||||
}
|
||||
|
||||
void RegisterFileSystems() {
|
||||
filesystem_map.clear();
|
||||
filesystem_romfs = nullptr;
|
||||
romfs_factory = nullptr;
|
||||
save_data_factory = nullptr;
|
||||
sdmc_factory = nullptr;
|
||||
|
||||
std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX);
|
||||
auto sdcard = std::make_shared<FileSys::RealVfsDirectory>(sd_directory, FileSys::Mode::Write);
|
||||
RegisterFileSystem(std::make_unique<DeferredFilesystem>(sdcard), Type::SDMC);
|
||||
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);
|
||||
|
||||
RegisterFileSystem(std::make_unique<SaveDataDeferredFilesystem>(), Type::SaveData);
|
||||
auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
|
||||
save_data_factory = std::move(savedata);
|
||||
|
||||
auto sdcard = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
|
||||
sdmc_factory = std::move(sdcard);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
|
||||
@@ -6,16 +6,13 @@
|
||||
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
#include "core/file_sys/vfs.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 {
|
||||
@@ -24,12 +21,22 @@ class ServiceManager;
|
||||
|
||||
namespace FileSystem {
|
||||
|
||||
/// Supported FileSystem types
|
||||
enum class Type {
|
||||
RomFS = 1,
|
||||
SaveData = 2,
|
||||
SDMC = 3,
|
||||
};
|
||||
ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
|
||||
ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
|
||||
ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
|
||||
|
||||
// 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();
|
||||
|
||||
// TODO(DarkLordZach): BIS Filesystem
|
||||
// ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS();
|
||||
|
||||
/// 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
|
||||
@@ -126,61 +133,5 @@ private:
|
||||
FileSys::VirtualDir backing;
|
||||
};
|
||||
|
||||
// A class that deferres the creation of a filesystem until a later time.
|
||||
// This is useful if construction depends on a variable not known when the filesystem is registered.
|
||||
// Construct this with a filesystem (VirtualDir) to avoid the deferrence feature or override the
|
||||
// CreateFilesystem method which will be called on first use.
|
||||
struct DeferredFilesystem {
|
||||
DeferredFilesystem() = default;
|
||||
|
||||
explicit DeferredFilesystem(FileSys::VirtualDir vfs_directory) : fs(std::move(vfs_directory)) {}
|
||||
|
||||
FileSys::VirtualDir Get() {
|
||||
if (fs == nullptr)
|
||||
fs = CreateFilesystem();
|
||||
|
||||
return fs;
|
||||
}
|
||||
|
||||
virtual ~DeferredFilesystem() = default;
|
||||
|
||||
protected:
|
||||
virtual FileSys::VirtualDir CreateFilesystem() {
|
||||
return fs;
|
||||
}
|
||||
|
||||
private:
|
||||
FileSys::VirtualDir fs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers a FileSystem, instances of which can later be opened using its IdCode.
|
||||
* @param factory FileSystem backend interface to use
|
||||
* @param type Type used to access this type of FileSystem
|
||||
*/
|
||||
ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& fs, Type type);
|
||||
|
||||
ResultCode RegisterRomFS(FileSys::VirtualFile fs);
|
||||
|
||||
/**
|
||||
* 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<FileSys::VirtualDir> OpenFileSystem(Type type);
|
||||
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFS();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
} // namespace FileSystem
|
||||
} // namespace Service
|
||||
|
||||
@@ -3,23 +3,38 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/directory.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
#include "core/file_sys/storage.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/kernel/process.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_)
|
||||
explicit IStorage(FileSys::VirtualFile backend_)
|
||||
: ServiceFramework("IStorage"), backend(std::move(backend_)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"},
|
||||
@@ -52,13 +67,6 @@ private:
|
||||
|
||||
// Read the data from the Storage backend
|
||||
std::vector<u8> output = backend->ReadBytes(length, offset);
|
||||
auto res = MakeResult<size_t>(output.size());
|
||||
if (res.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
// Write the data to memory
|
||||
ctx.WriteBuffer(output);
|
||||
|
||||
@@ -104,19 +112,13 @@ private:
|
||||
|
||||
// Read the data from the Storage backend
|
||||
std::vector<u8> output = backend->ReadBytes(length, offset);
|
||||
auto res = MakeResult<size_t>(output.size());
|
||||
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>(*res));
|
||||
rb.Push(static_cast<u64>(output.size()));
|
||||
}
|
||||
|
||||
void Write(Kernel::HLERequestContext& ctx) {
|
||||
@@ -139,15 +141,21 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<u8> data = ctx.ReadBuffer();
|
||||
data.resize(length);
|
||||
const std::vector<u8> data = ctx.ReadBuffer();
|
||||
|
||||
ASSERT_MSG(
|
||||
static_cast<s64>(data.size()) <= length,
|
||||
"Attempting to write more data than requested (requested={:016X}, actual={:016X}).",
|
||||
length, data.size());
|
||||
|
||||
// Write the data to the Storage backend
|
||||
auto res = MakeResult<size_t>(backend->WriteBytes(data, offset));
|
||||
if (res.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
return;
|
||||
}
|
||||
const auto write_size =
|
||||
static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length));
|
||||
const std::size_t written = backend->Write(data.data(), write_size, offset);
|
||||
|
||||
ASSERT_MSG(static_cast<s64>(written) == length,
|
||||
"Could not write all bytes to file (requested={:016X}, actual={:016X}).", length,
|
||||
written);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -205,6 +213,7 @@ public:
|
||||
};
|
||||
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);
|
||||
@@ -222,23 +231,20 @@ private:
|
||||
LOG_DEBUG(Service_FS, "called, unk=0x{:X}", unk);
|
||||
|
||||
// Calculate how many entries we can fit in the output buffer
|
||||
u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
|
||||
const 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);
|
||||
const 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);
|
||||
// Determine data start and end
|
||||
const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
|
||||
const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
|
||||
const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
|
||||
|
||||
next_entry_index += actual_entries;
|
||||
|
||||
// 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());
|
||||
|
||||
// Write the data to memory
|
||||
ctx.WriteBuffer(output);
|
||||
ctx.WriteBuffer(begin, range_size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -512,16 +518,6 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void FSP_SRV::TryLoadRomFS() {
|
||||
if (romfs) {
|
||||
return;
|
||||
}
|
||||
auto res = OpenRomFS();
|
||||
if (res.Succeeded()) {
|
||||
romfs = std::move(res.Unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
@@ -532,7 +528,7 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
IFileSystem filesystem(OpenFileSystem(Type::SDMC).Unwrap());
|
||||
IFileSystem filesystem(OpenSDMC().Unwrap());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -542,20 +538,34 @@ void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto save_struct = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
|
||||
auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
u128 uid = rp.PopRaw<u128>();
|
||||
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]);
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called save_struct = {}, uid = {:016X}{:016X}",
|
||||
save_struct.DebugInfo(), uid[1], uid[0]);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
IFileSystem filesystem(OpenFileSystem(Type::SaveData).Unwrap());
|
||||
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>();
|
||||
|
||||
auto dir = OpenSaveData(space_id, save_struct);
|
||||
|
||||
if (dir.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
|
||||
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
|
||||
return;
|
||||
}
|
||||
|
||||
IFileSystem filesystem(std::move(dir.Unwrap()));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -573,8 +583,8 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
TryLoadRomFS();
|
||||
if (!romfs) {
|
||||
auto romfs = OpenRomFS(Core::System::GetInstance().CurrentProcess()->program_id);
|
||||
if (romfs.Failed()) {
|
||||
// TODO (bunnei): Find the right error code to use here
|
||||
LOG_CRITICAL(Service_FS, "no file system interface available!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -582,7 +592,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
IStorage storage(romfs);
|
||||
IStorage storage(std::move(romfs.Unwrap()));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -590,8 +600,16 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called, using OpenDataStorageByCurrentProcess");
|
||||
OpenDataStorageByCurrentProcess(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));
|
||||
}
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
||||
@@ -19,8 +19,6 @@ public:
|
||||
~FSP_SRV() = default;
|
||||
|
||||
private:
|
||||
void TryLoadRomFS();
|
||||
|
||||
void Initialize(Kernel::HLERequestContext& ctx);
|
||||
void MountSdCard(Kernel::HLERequestContext& ctx);
|
||||
void CreateSaveData(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -12,7 +12,7 @@ class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name);
|
||||
explicit Interface(std::shared_ptr<Module> module, const char* name);
|
||||
|
||||
void CreateFriendService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#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"
|
||||
@@ -18,9 +19,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 / 10000;
|
||||
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
|
||||
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
|
||||
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;
|
||||
|
||||
class IAppletResource final : public ServiceFramework<IAppletResource> {
|
||||
public:
|
||||
@@ -63,7 +64,8 @@ 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>);
|
||||
// TODO(shinyquagsire23): gyro, touch, mouse, keyboard
|
||||
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
|
||||
// TODO(shinyquagsire23): gyro, mouse, keyboard
|
||||
}
|
||||
|
||||
void UpdatePadCallback(u64 userdata, int cycles_late) {
|
||||
@@ -75,7 +77,7 @@ private:
|
||||
|
||||
// Set up controllers as neon red+blue Joy-Con attached to console
|
||||
ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header;
|
||||
controller_header.type = ControllerType_Handheld | ControllerType_JoyconPair;
|
||||
controller_header.type = ControllerType_Handheld;
|
||||
controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent;
|
||||
controller_header.right_color_body = JOYCON_BODY_NEON_RED;
|
||||
controller_header.right_color_buttons = JOYCON_BUTTONS_NEON_RED;
|
||||
@@ -83,24 +85,21 @@ private:
|
||||
controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE;
|
||||
|
||||
for (size_t controller = 0; controller < mem.controllers.size(); controller++) {
|
||||
for (int index = 0; index < HID_NUM_LAYOUTS; index++) {
|
||||
// TODO(DarkLordZach): Is this layout/controller config actually invalid?
|
||||
if (controller == Controller_Handheld && index == Layout_Single)
|
||||
continue;
|
||||
|
||||
ControllerLayout& layout = mem.controllers[controller].layouts[index];
|
||||
for (auto& layout : mem.controllers[controller].layouts) {
|
||||
layout.header.num_entries = HID_NUM_ENTRIES;
|
||||
layout.header.max_entry_index = HID_NUM_ENTRIES - 1;
|
||||
|
||||
// HID shared memory stores the state of the past 17 samples in a circlular buffer,
|
||||
// each with a timestamp in number of samples since boot.
|
||||
const ControllerInputEntry& last_entry = layout.entries[layout.header.latest_entry];
|
||||
|
||||
layout.header.timestamp_ticks = CoreTiming::GetTicks();
|
||||
layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES;
|
||||
|
||||
ControllerInputEntry& entry = layout.entries[layout.header.latest_entry];
|
||||
entry.timestamp++;
|
||||
entry.timestamp = last_entry.timestamp + 1;
|
||||
// TODO(shinyquagsire23): Is this always identical to timestamp?
|
||||
entry.timestamp_2++;
|
||||
entry.timestamp_2 = entry.timestamp;
|
||||
|
||||
// TODO(shinyquagsire23): More than just handheld input
|
||||
if (controller != Controller_Handheld)
|
||||
@@ -153,8 +152,6 @@ 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();
|
||||
@@ -166,7 +163,26 @@ private:
|
||||
touchscreen.header.max_entry_index = touchscreen.entries.size();
|
||||
touchscreen.header.timestamp = timestamp;
|
||||
touchscreen.entries[curr_entry].header.timestamp = sample_counter;
|
||||
touchscreen.entries[curr_entry].header.num_touches = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// TODO(shinyquagsire23): Properly implement mouse
|
||||
Mouse& mouse = mem.mouse;
|
||||
@@ -196,8 +212,7 @@ private:
|
||||
keyboard.entries[curr_keyboard_entry].timestamp_2 = keyboard_sample_counter;
|
||||
|
||||
// TODO(shinyquagsire23): Figure out what any of these are
|
||||
for (size_t i = 0; i < mem.unk_input_1.size(); i++) {
|
||||
UnkInput1& input = mem.unk_input_1[i];
|
||||
for (auto& input : mem.unk_input_1) {
|
||||
const u64 last_input_entry = input.header.latest_entry;
|
||||
const u64 curr_input_entry = (input.header.latest_entry + 1) % input.entries.size();
|
||||
const u64 input_sample_counter = input.entries[last_input_entry].timestamp + 1;
|
||||
@@ -211,9 +226,7 @@ private:
|
||||
input.entries[curr_input_entry].timestamp_2 = input_sample_counter;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mem.unk_input_2.size(); i++) {
|
||||
UnkInput2& input = mem.unk_input_2[i];
|
||||
|
||||
for (auto& input : mem.unk_input_2) {
|
||||
input.header.timestamp_ticks = timestamp;
|
||||
input.header.num_entries = 17;
|
||||
input.header.latest_entry = 0;
|
||||
@@ -252,6 +265,7 @@ 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> {
|
||||
|
||||
@@ -380,7 +380,7 @@ static_assert(sizeof(ControllerLayout) == 0x350,
|
||||
|
||||
struct Controller {
|
||||
ControllerHeader header;
|
||||
std::array<ControllerLayout, 7> layouts;
|
||||
std::array<ControllerLayout, HID_NUM_LAYOUTS> layouts;
|
||||
std::array<u8, 0x2a70> unk_1;
|
||||
ControllerMAC mac_left;
|
||||
ControllerMAC mac_right;
|
||||
|
||||
@@ -12,7 +12,7 @@ class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name);
|
||||
explicit Interface(std::shared_ptr<Module> module, const char* name);
|
||||
|
||||
void CreateUserInterface(Kernel::HLERequestContext& ctx);
|
||||
|
||||
|
||||
@@ -148,6 +148,24 @@ private:
|
||||
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(0);
|
||||
}
|
||||
void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(0);
|
||||
}
|
||||
void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(0);
|
||||
}
|
||||
};
|
||||
|
||||
IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
|
||||
@@ -167,11 +185,11 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
|
||||
{14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"},
|
||||
{15, nullptr, "GetCurrentIpConfigInfo"},
|
||||
{16, nullptr, "SetWirelessCommunicationEnabled"},
|
||||
{17, nullptr, "IsWirelessCommunicationEnabled"},
|
||||
{17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"},
|
||||
{18, nullptr, "GetInternetConnectionStatus"},
|
||||
{19, nullptr, "SetEthernetCommunicationEnabled"},
|
||||
{20, nullptr, "IsEthernetCommunicationEnabled"},
|
||||
{21, nullptr, "IsAnyInternetRequestAccepted"},
|
||||
{20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"},
|
||||
{21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"},
|
||||
{22, nullptr, "IsAnyForegroundRequestAccepted"},
|
||||
{23, nullptr, "PutToSleep"},
|
||||
{24, nullptr, "WakeUp"},
|
||||
|
||||
@@ -12,7 +12,7 @@ class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name);
|
||||
explicit Interface(std::shared_ptr<Module> module, const char* name);
|
||||
|
||||
void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx);
|
||||
void CreateGeneralService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -119,7 +119,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
|
||||
void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for
|
||||
LOG_DEBUG(Service_NS, "called, language_code=%lx", language_code);
|
||||
LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code);
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
std::vector<u32> font_codes;
|
||||
std::vector<u32> font_offsets;
|
||||
@@ -132,9 +132,9 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
|
||||
font_sizes.push_back(SHARED_FONT_REGIONS[i].size);
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(font_codes.data(), font_codes.size(), 0);
|
||||
ctx.WriteBuffer(font_offsets.data(), font_offsets.size(), 1);
|
||||
ctx.WriteBuffer(font_sizes.data(), font_sizes.size(), 2);
|
||||
ctx.WriteBuffer(font_codes.data(), font_codes.size() * sizeof(u32), 0);
|
||||
ctx.WriteBuffer(font_offsets.data(), font_offsets.size() * sizeof(u32), 1);
|
||||
ctx.WriteBuffer(font_sizes.data(), font_sizes.size() * sizeof(u32), 2);
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(static_cast<u8>(LoadState::Done)); // Fonts Loaded
|
||||
|
||||
@@ -18,7 +18,8 @@ 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) {
|
||||
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform,
|
||||
const MathUtil::Rectangle<int>& crop_rect) {
|
||||
VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
|
||||
LOG_WARNING(Service,
|
||||
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
|
||||
@@ -26,7 +27,8 @@ 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};
|
||||
addr, offset, width, height, stride, static_cast<PixelFormat>(format),
|
||||
transform, crop_rect};
|
||||
|
||||
Core::System::GetInstance().perf_stats.EndGameFrame();
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#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"
|
||||
|
||||
@@ -16,14 +17,15 @@ class nvmap;
|
||||
|
||||
class nvdisp_disp0 final : public nvdevice {
|
||||
public:
|
||||
nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvdevice(), nvmap_dev(std::move(nvmap_dev)) {}
|
||||
explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : 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);
|
||||
NVFlinger::BufferQueue::BufferTransformFlags transform,
|
||||
const MathUtil::Rectangle<int>& crop_rect);
|
||||
|
||||
private:
|
||||
std::shared_ptr<nvmap> nvmap_dev;
|
||||
|
||||
@@ -18,7 +18,7 @@ class nvmap;
|
||||
|
||||
class nvhost_as_gpu final : public nvdevice {
|
||||
public:
|
||||
nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
|
||||
explicit 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;
|
||||
|
||||
@@ -29,24 +29,9 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<
|
||||
u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IocGetConfigParams params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
LOG_DEBUG(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
|
||||
LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
|
||||
params.param_str.data());
|
||||
|
||||
if (!strcmp(params.domain_str.data(), "nv")) {
|
||||
if (!strcmp(params.param_str.data(), "NV_MEMORY_PROFILER")) {
|
||||
params.config_str[0] = '0';
|
||||
} else if (!strcmp(params.param_str.data(), "NVN_THROUGH_OPENGL")) {
|
||||
params.config_str[0] = '0';
|
||||
} else if (!strcmp(params.param_str.data(), "NVRM_GPU_PREVENT_USE")) {
|
||||
params.config_str[0] = '0';
|
||||
} else {
|
||||
params.config_str[0] = '\0';
|
||||
}
|
||||
} else {
|
||||
UNIMPLEMENTED(); // unknown domain? Only nv has been seen so far on hardware
|
||||
}
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return 0;
|
||||
return 0x30006; // Returns error on production mode
|
||||
}
|
||||
|
||||
u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
|
||||
|
||||
@@ -18,7 +18,7 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8);
|
||||
|
||||
class nvhost_gpu final : public nvdevice {
|
||||
public:
|
||||
nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
|
||||
explicit 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;
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
|
||||
/// Returns a pointer to one of the available devices, identified by its name.
|
||||
template <typename T>
|
||||
std::shared_ptr<T> GetDevice(std::string name) {
|
||||
std::shared_ptr<T> GetDevice(const std::string& name) {
|
||||
auto itr = devices.find(name);
|
||||
if (itr == devices.end())
|
||||
return nullptr;
|
||||
|
||||
@@ -13,8 +13,8 @@ namespace Service {
|
||||
namespace NVFlinger {
|
||||
|
||||
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
|
||||
native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle");
|
||||
native_handle->Signal();
|
||||
buffer_wait_event =
|
||||
Kernel::Event::Create(Kernel::ResetType::Sticky, "BufferQueue NativeHandle");
|
||||
}
|
||||
|
||||
void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
|
||||
@@ -26,10 +26,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
|
||||
LOG_WARNING(Service, "Adding graphics buffer {}", slot);
|
||||
|
||||
queue.emplace_back(buffer);
|
||||
|
||||
if (buffer_wait_event) {
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
|
||||
boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
|
||||
@@ -48,8 +45,6 @@ boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
buffer_wait_event = nullptr;
|
||||
|
||||
itr->status = Buffer::Status::Dequeued;
|
||||
return itr->slot;
|
||||
}
|
||||
@@ -62,13 +57,15 @@ const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
|
||||
return itr->igbp_buffer;
|
||||
}
|
||||
|
||||
void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform) {
|
||||
void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
|
||||
const MathUtil::Rectangle<int>& crop_rect) {
|
||||
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() {
|
||||
@@ -88,9 +85,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
|
||||
ASSERT(itr->status == Buffer::Status::Acquired);
|
||||
itr->status = Buffer::Status::Free;
|
||||
|
||||
if (buffer_wait_event) {
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
|
||||
u32 BufferQueue::Query(QueryType type) {
|
||||
@@ -106,10 +101,5 @@ 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
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/math_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
|
||||
@@ -68,23 +69,24 @@ 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);
|
||||
void QueueBuffer(u32 slot, BufferTransformFlags transform,
|
||||
const MathUtil::Rectangle<int>& crop_rect);
|
||||
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> GetNativeHandle() const {
|
||||
return native_handle;
|
||||
Kernel::SharedPtr<Kernel::Event> GetBufferWaitEvent() const {
|
||||
return buffer_wait_event;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -92,9 +94,6 @@ 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;
|
||||
};
|
||||
|
||||
|
||||
@@ -23,15 +23,10 @@ constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREE
|
||||
|
||||
NVFlinger::NVFlinger() {
|
||||
// Add the different displays to the list of displays.
|
||||
Display default_{0, "Default"};
|
||||
Display external{1, "External"};
|
||||
Display edid{2, "Edid"};
|
||||
Display internal{3, "Internal"};
|
||||
|
||||
displays.emplace_back(default_);
|
||||
displays.emplace_back(external);
|
||||
displays.emplace_back(edid);
|
||||
displays.emplace_back(internal);
|
||||
displays.emplace_back(0, "Default");
|
||||
displays.emplace_back(1, "External");
|
||||
displays.emplace_back(2, "Edid");
|
||||
displays.emplace_back(3, "Internal");
|
||||
|
||||
// Schedule the screen composition events
|
||||
composition_event =
|
||||
@@ -149,12 +144,10 @@ 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);
|
||||
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform,
|
||||
buffer->crop_rect);
|
||||
|
||||
buffer_queue->ReleaseBuffer(buffer->slot);
|
||||
|
||||
// TODO(Subv): Figure out when we should actually signal this event.
|
||||
buffer_queue->GetNativeHandle()->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name);
|
||||
explicit Interface(std::shared_ptr<Module> module, const char* name);
|
||||
|
||||
void CreateService(Kernel::HLERequestContext& ctx);
|
||||
void CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// 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"
|
||||
|
||||
@@ -31,7 +31,7 @@ void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
|
||||
LanguageCode::ZH_HANS,
|
||||
LanguageCode::ZH_HANT,
|
||||
}};
|
||||
ctx.WriteBuffer(available_language_codes.data(), available_language_codes.size());
|
||||
ctx.WriteBuffer(available_language_codes);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user