Compare commits
304 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c9c33e8ad | ||
|
|
edd5b6f12f | ||
|
|
b725d1fdf7 | ||
|
|
87eca5b209 | ||
|
|
75640c9c71 | ||
|
|
8cb2e7d881 | ||
|
|
f7319b0d3c | ||
|
|
97605e36f7 | ||
|
|
98060c6f7b | ||
|
|
8a537a2021 | ||
|
|
c6c74248fe | ||
|
|
0478308094 | ||
|
|
53b3c3ab7f | ||
|
|
cd47af8af0 | ||
|
|
9761936e02 | ||
|
|
1af13e0802 | ||
|
|
c681690358 | ||
|
|
7384b33c4f | ||
|
|
c95ded3a4a | ||
|
|
e6676afa18 | ||
|
|
ebf1b58a22 | ||
|
|
e1ea8cc721 | ||
|
|
b4f63db04e | ||
|
|
3619b31fc0 | ||
|
|
fcde356f15 | ||
|
|
958fa15a4c | ||
|
|
f9db75fe40 | ||
|
|
3bd503d59c | ||
|
|
09f4150241 | ||
|
|
d2b2b05b6a | ||
|
|
723bd89883 | ||
|
|
70f189d7af | ||
|
|
a80467db57 | ||
|
|
d2a630c41f | ||
|
|
9b12623743 | ||
|
|
c7387e6504 | ||
|
|
f1219e3a87 | ||
|
|
6001af2b89 | ||
|
|
4ed9ef15c4 | ||
|
|
9bc18eada8 | ||
|
|
454cf1dc09 | ||
|
|
81a39181b0 | ||
|
|
004277477a | ||
|
|
b8e885c6e5 | ||
|
|
ab552e4a25 | ||
|
|
ab362aa7e5 | ||
|
|
65bd03d74c | ||
|
|
7f3c2525e6 | ||
|
|
fd72d889bf | ||
|
|
cb1e63ef09 | ||
|
|
c8b3f09876 | ||
|
|
d08b876c9d | ||
|
|
b4a6ce02ce | ||
|
|
9333ee29ca | ||
|
|
2a16fd7ffc | ||
|
|
4b4f883aef | ||
|
|
2c6efda235 | ||
|
|
7474382266 | ||
|
|
a264fac943 | ||
|
|
72b1fae984 | ||
|
|
4e6c64bf8d | ||
|
|
4a6eff3b7b | ||
|
|
e9610ec0dd | ||
|
|
3088e36237 | ||
|
|
eaee73f95d | ||
|
|
c82bccab56 | ||
|
|
1916213311 | ||
|
|
8ea6261547 | ||
|
|
93fca5d9cf | ||
|
|
c21a6c6b37 | ||
|
|
48cd61d9c8 | ||
|
|
ddc242dd51 | ||
|
|
8d4bb10d44 | ||
|
|
84c6134264 | ||
|
|
4f78f5c0df | ||
|
|
e2bf581e3a | ||
|
|
1d36aec267 | ||
|
|
9046f764bf | ||
|
|
9de523fd90 | ||
|
|
29f082775b | ||
|
|
0e05a9d58f | ||
|
|
581406af18 | ||
|
|
481d8716e0 | ||
|
|
d347623d6f | ||
|
|
40db288a2a | ||
|
|
ad2f47b579 | ||
|
|
a9c25ab9e4 | ||
|
|
fd1ef25257 | ||
|
|
81ff9e2473 | ||
|
|
74bce4d68f | ||
|
|
e5a0a23553 | ||
|
|
41e99d8880 | ||
|
|
ad45d68871 | ||
|
|
49cb4fa37b | ||
|
|
92fcc6d15a | ||
|
|
359db6a673 | ||
|
|
0590dd2971 | ||
|
|
d3b9599b2d | ||
|
|
2c87f10267 | ||
|
|
7e59e907ef | ||
|
|
80e4dbdce7 | ||
|
|
2ba4d878e5 | ||
|
|
dd321dc85f | ||
|
|
d34d0bfc87 | ||
|
|
6c8b788d32 | ||
|
|
54df9fe29e | ||
|
|
cdb19e71fe | ||
|
|
722e7c05de | ||
|
|
e10483a878 | ||
|
|
acdc770cfb | ||
|
|
19038db489 | ||
|
|
145ae36963 | ||
|
|
38c1c500ab | ||
|
|
d46e0acb3c | ||
|
|
e450b0bbac | ||
|
|
b2647dba33 | ||
|
|
1c4365d928 | ||
|
|
97187b7ef6 | ||
|
|
6664d7b2c5 | ||
|
|
9249fadb9e | ||
|
|
cb8e4a4633 | ||
|
|
03c26d3406 | ||
|
|
0bc323bafb | ||
|
|
60a184455c | ||
|
|
1069eced84 | ||
|
|
4aa9779ae1 | ||
|
|
cc1fe93297 | ||
|
|
aee93f98f9 | ||
|
|
31930a3334 | ||
|
|
678c18aa5c | ||
|
|
9afcbba8e4 | ||
|
|
7992dee8e9 | ||
|
|
de0ab806df | ||
|
|
d08457f879 | ||
|
|
86e70cf302 | ||
|
|
0b33d38e9b | ||
|
|
e7fc3d13ed | ||
|
|
a6830e61b8 | ||
|
|
9ae972ab4e | ||
|
|
aca218aea0 | ||
|
|
9b9c586dff | ||
|
|
76754f5705 | ||
|
|
5bb80ab009 | ||
|
|
da5fcbf501 | ||
|
|
c31412c433 | ||
|
|
352b56367c | ||
|
|
7a5eda5914 | ||
|
|
adf26ae668 | ||
|
|
6383653a8d | ||
|
|
0cc347462d | ||
|
|
938e45eb83 | ||
|
|
c5a849212f | ||
|
|
f1cb425d92 | ||
|
|
bdaa76c0db | ||
|
|
1c0226815d | ||
|
|
48eb3742b9 | ||
|
|
9078bb9854 | ||
|
|
5ee19add1b | ||
|
|
2e8177f0c9 | ||
|
|
df264d2ccb | ||
|
|
f2f679bf3f | ||
|
|
b04e39107f | ||
|
|
0270906dbf | ||
|
|
655694253a | ||
|
|
5d7167dfca | ||
|
|
9049aedd83 | ||
|
|
80cbd81276 | ||
|
|
e8b565b239 | ||
|
|
3aa8b644a9 | ||
|
|
dde3094058 | ||
|
|
f0e902a7d6 | ||
|
|
87f8181405 | ||
|
|
f4432b5d0c | ||
|
|
258f0f5c31 | ||
|
|
dc85e3bff1 | ||
|
|
bbf3b2da0c | ||
|
|
a973a049b7 | ||
|
|
b77f571d20 | ||
|
|
635d1e5651 | ||
|
|
b5f8a5f0a3 | ||
|
|
f50f065c31 | ||
|
|
351d5a2227 | ||
|
|
aa1cf608ed | ||
|
|
4ddbd9bbaf | ||
|
|
e5ca097e32 | ||
|
|
03150a560e | ||
|
|
585b6a6a50 | ||
|
|
2239d47112 | ||
|
|
b32be35173 | ||
|
|
72d10ce66c | ||
|
|
0d449b77e2 | ||
|
|
6f620b2441 | ||
|
|
1fca683388 | ||
|
|
0287b2be6d | ||
|
|
a9d60c6103 | ||
|
|
1fa31cf74d | ||
|
|
8d89e88d2c | ||
|
|
d63f5acb15 | ||
|
|
d8d557df86 | ||
|
|
a9dc34ea5c | ||
|
|
ed95ce6bb7 | ||
|
|
ac8231ed10 | ||
|
|
705300992e | ||
|
|
7c70746ec4 | ||
|
|
0bf24d310e | ||
|
|
3b1e4c0995 | ||
|
|
9024cbb5b8 | ||
|
|
85ed0af84e | ||
|
|
8eaf857d06 | ||
|
|
12c365b549 | ||
|
|
7de8e36343 | ||
|
|
6594853eb1 | ||
|
|
58444a0376 | ||
|
|
d278f25bda | ||
|
|
72e6b31a07 | ||
|
|
1f98dc30ea | ||
|
|
949d9a7136 | ||
|
|
debabf1fa6 | ||
|
|
476b9f8fc5 | ||
|
|
a94831f2a9 | ||
|
|
8cea598158 | ||
|
|
cebce2a93a | ||
|
|
c2049aa4e5 | ||
|
|
a609b6907a | ||
|
|
ef7b2237d9 | ||
|
|
a141b46b5c | ||
|
|
85285b09b0 | ||
|
|
5172354e29 | ||
|
|
bf7da804c5 | ||
|
|
8806e69f59 | ||
|
|
a6addb5332 | ||
|
|
2347e1b8c5 | ||
|
|
3c63cecb96 | ||
|
|
e54c9e19f3 | ||
|
|
bf0d3e1fea | ||
|
|
9a87ece837 | ||
|
|
2bc2b32662 | ||
|
|
0634aee267 | ||
|
|
942def7831 | ||
|
|
c94db0e071 | ||
|
|
f7a173de6c | ||
|
|
b43cfe6c02 | ||
|
|
fbbb58b226 | ||
|
|
1e3b139cd7 | ||
|
|
6f00628564 | ||
|
|
3ec90dc6ef | ||
|
|
9aa5c1894e | ||
|
|
3a6e76e9b5 | ||
|
|
ca142f35c0 | ||
|
|
abefe29398 | ||
|
|
29f748a658 | ||
|
|
69b35d7615 | ||
|
|
b723390ab1 | ||
|
|
ce2403d975 | ||
|
|
d9590d7dfa | ||
|
|
2694b43d3a | ||
|
|
e6e17a3fc6 | ||
|
|
ddff188c65 | ||
|
|
d14ba122e2 | ||
|
|
2eff8336f4 | ||
|
|
cdd499c261 | ||
|
|
e65f5e4d66 | ||
|
|
1fb4bebb63 | ||
|
|
239dfea34a | ||
|
|
474bc29208 | ||
|
|
b703aba622 | ||
|
|
afb7e5cc05 | ||
|
|
fcf8f53a63 | ||
|
|
77328b0f19 | ||
|
|
c7c346a15d | ||
|
|
6df09f5b76 | ||
|
|
1edf8660bc | ||
|
|
4a31f99a02 | ||
|
|
030847d5fa | ||
|
|
bed2d6c425 | ||
|
|
77e705a8fa | ||
|
|
e7ac42677b | ||
|
|
a1c85b8c55 | ||
|
|
47f081d513 | ||
|
|
2cbc284c2b | ||
|
|
93596d03ec | ||
|
|
7c9f7aeacc | ||
|
|
6949f73149 | ||
|
|
a3d1ede25f | ||
|
|
c7c594a6b8 | ||
|
|
c6529688fc | ||
|
|
257b7bbfee | ||
|
|
a97cdb5eb4 | ||
|
|
f80b80b922 | ||
|
|
6f5bede402 | ||
|
|
bfad41b0c1 | ||
|
|
45f2a2fe29 | ||
|
|
e408bbceed | ||
|
|
702622b8f1 | ||
|
|
19c5cf9c63 | ||
|
|
466960c8ab | ||
|
|
b2a8209c5b | ||
|
|
d3fbf45705 | ||
|
|
aeffd4b436 | ||
|
|
e7e3d5898e | ||
|
|
ed8ca608a0 | ||
|
|
e0ea2f5f6e | ||
|
|
a25d79cfaa | ||
|
|
f5f6292810 |
@@ -143,7 +143,7 @@ struct AuxInfo {
|
||||
std::array<u8, 24> output_mix_buffers;
|
||||
u32_le mix_buffer_count;
|
||||
u32_le sample_rate; // Stored in the aux buffer currently
|
||||
u32_le sampe_count;
|
||||
u32_le sample_count;
|
||||
u64_le send_buffer_info;
|
||||
u64_le send_buffer_base;
|
||||
|
||||
|
||||
@@ -121,7 +121,8 @@ CubebSink::CubebSink(std::string target_device_name) {
|
||||
const auto collection_end{collection.device + collection.count};
|
||||
const auto device{
|
||||
std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
|
||||
return target_device_name == info.friendly_name;
|
||||
return info.friendly_name != nullptr &&
|
||||
target_device_name == info.friendly_name;
|
||||
})};
|
||||
if (device != collection_end) {
|
||||
output_device = device->devid;
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "audio_core/stream.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/settings.h"
|
||||
@@ -104,10 +103,7 @@ void Stream::PlayNextBuffer() {
|
||||
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(AudioOutput, "Audio", "ReleaseActiveBuffer", MP_RGB(100, 100, 255));
|
||||
|
||||
void Stream::ReleaseActiveBuffer() {
|
||||
MICROPROFILE_SCOPE(AudioOutput);
|
||||
ASSERT(active_buffer);
|
||||
released_buffers.push(std::move(active_buffer));
|
||||
release_callback();
|
||||
|
||||
@@ -32,10 +32,10 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
|
||||
// We were given actual_samples number of samples, and num_samples were requested from us.
|
||||
double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
|
||||
|
||||
const double max_latency = 1.0; // seconds
|
||||
const double max_latency = 0.25; // seconds
|
||||
const double max_backlog = m_sample_rate * max_latency;
|
||||
const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
|
||||
if (backlog_fullness > 5.0) {
|
||||
if (backlog_fullness > 4.0) {
|
||||
// Too many samples in backlog: Don't push anymore on
|
||||
num_in = 0;
|
||||
}
|
||||
@@ -49,7 +49,7 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
|
||||
|
||||
// This low-pass filter smoothes out variance in the calculated stretch ratio.
|
||||
// The time-scale determines how responsive this filter is.
|
||||
constexpr double lpf_time_scale = 2.0; // seconds
|
||||
constexpr double lpf_time_scale = 0.712; // seconds
|
||||
const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
|
||||
m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#ifdef _WIN32
|
||||
#include <share.h> // For _SH_DENYWR
|
||||
#include <share.h> // For _SH_DENYWR
|
||||
#include <windows.h> // For OutputDebugStringA
|
||||
#else
|
||||
#define _SH_DENYWR 0
|
||||
#endif
|
||||
@@ -139,12 +140,18 @@ void FileBackend::Write(const Entry& entry) {
|
||||
if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
|
||||
return;
|
||||
}
|
||||
bytes_written += file.WriteString(FormatLogMessage(entry) + '\n');
|
||||
bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
|
||||
if (entry.log_level >= Level::Error) {
|
||||
file.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerBackend::Write(const Entry& entry) {
|
||||
#ifdef _WIN32
|
||||
::OutputDebugStringA(FormatLogMessage(entry).append(1, '\n').c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
|
||||
#define ALL_LOG_CLASSES() \
|
||||
CLS(Log) \
|
||||
@@ -196,6 +203,7 @@ void FileBackend::Write(const Entry& entry) {
|
||||
SUB(Service, NFP) \
|
||||
SUB(Service, NIFM) \
|
||||
SUB(Service, NIM) \
|
||||
SUB(Service, NPNS) \
|
||||
SUB(Service, NS) \
|
||||
SUB(Service, NVDRV) \
|
||||
SUB(Service, PCIE) \
|
||||
@@ -204,10 +212,12 @@ void FileBackend::Write(const Entry& entry) {
|
||||
SUB(Service, PM) \
|
||||
SUB(Service, PREPO) \
|
||||
SUB(Service, PSC) \
|
||||
SUB(Service, PSM) \
|
||||
SUB(Service, SET) \
|
||||
SUB(Service, SM) \
|
||||
SUB(Service, SPL) \
|
||||
SUB(Service, SSL) \
|
||||
SUB(Service, TCAP) \
|
||||
SUB(Service, Time) \
|
||||
SUB(Service, USB) \
|
||||
SUB(Service, VI) \
|
||||
|
||||
@@ -103,6 +103,20 @@ private:
|
||||
std::size_t bytes_written;
|
||||
};
|
||||
|
||||
/**
|
||||
* Backend that writes to Visual Studio's output window
|
||||
*/
|
||||
class DebuggerBackend : public Backend {
|
||||
public:
|
||||
static const char* Name() {
|
||||
return "debugger";
|
||||
}
|
||||
const char* GetName() const override {
|
||||
return Name();
|
||||
}
|
||||
void Write(const Entry& entry) override;
|
||||
};
|
||||
|
||||
void AddBackend(std::unique_ptr<Backend> backend);
|
||||
|
||||
void RemoveBackend(std::string_view backend_name);
|
||||
|
||||
@@ -83,6 +83,7 @@ enum class Class : ClassType {
|
||||
Service_NFP, ///< The NFP service
|
||||
Service_NIFM, ///< The NIFM (Network interface) service
|
||||
Service_NIM, ///< The NIM service
|
||||
Service_NPNS, ///< The NPNS service
|
||||
Service_NS, ///< The NS services
|
||||
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
|
||||
Service_PCIE, ///< The PCIe service
|
||||
@@ -96,6 +97,7 @@ enum class Class : ClassType {
|
||||
Service_SM, ///< The SM (Service manager) service
|
||||
Service_SPL, ///< The SPL service
|
||||
Service_SSL, ///< The SSL service
|
||||
Service_TCAP, ///< The TCAP service.
|
||||
Service_Time, ///< The time service
|
||||
Service_USB, ///< The USB (Universal Serial Bus) service
|
||||
Service_VI, ///< The VI (Video interface) service
|
||||
|
||||
@@ -4,11 +4,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <codecvt>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
#include "common/common_paths.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -33,24 +32,6 @@ std::string ToUpper(std::string str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
// For Debugging. Read out an u8 array.
|
||||
std::string ArrayToString(const u8* data, std::size_t size, int line_len, bool spaces) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::hex;
|
||||
|
||||
for (int line = 0; size; ++data, --size) {
|
||||
oss << std::setw(2) << (int)*data;
|
||||
|
||||
if (line_len == ++line) {
|
||||
oss << '\n';
|
||||
line = 0;
|
||||
} else if (spaces)
|
||||
oss << ' ';
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string StringFromBuffer(const std::vector<u8>& data) {
|
||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||
}
|
||||
@@ -75,40 +56,6 @@ std::string StripQuotes(const std::string& s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
bool TryParse(const std::string& str, u32* const output) {
|
||||
char* endptr = nullptr;
|
||||
|
||||
// Reset errno to a value other than ERANGE
|
||||
errno = 0;
|
||||
|
||||
unsigned long value = strtoul(str.c_str(), &endptr, 0);
|
||||
|
||||
if (!endptr || *endptr)
|
||||
return false;
|
||||
|
||||
if (errno == ERANGE)
|
||||
return false;
|
||||
|
||||
#if ULONG_MAX > UINT_MAX
|
||||
if (value >= 0x100000000ull && value <= 0xFFFFFFFF00000000ull)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
*output = static_cast<u32>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryParse(const std::string& str, bool* const output) {
|
||||
if ("1" == str || "true" == ToLower(str))
|
||||
*output = true;
|
||||
else if ("0" == str || "false" == ToLower(str))
|
||||
*output = false;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string StringFromBool(bool value) {
|
||||
return value ? "True" : "False";
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
@@ -19,44 +17,13 @@ std::string ToLower(std::string str);
|
||||
/// Make a string uppercase
|
||||
std::string ToUpper(std::string str);
|
||||
|
||||
std::string ArrayToString(const u8* data, std::size_t size, int line_len = 20, bool spaces = true);
|
||||
|
||||
std::string StringFromBuffer(const std::vector<u8>& data);
|
||||
|
||||
std::string StripSpaces(const std::string& s);
|
||||
std::string StripQuotes(const std::string& s);
|
||||
|
||||
// Thousand separator. Turns 12345678 into 12,345,678
|
||||
template <typename I>
|
||||
std::string ThousandSeparate(I value, int spaces = 0) {
|
||||
std::ostringstream oss;
|
||||
|
||||
// std::locale("") seems to be broken on many platforms
|
||||
#if defined _WIN32 || (defined __linux__ && !defined __clang__)
|
||||
oss.imbue(std::locale(""));
|
||||
#endif
|
||||
oss << std::setw(spaces) << value;
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string StringFromBool(bool value);
|
||||
|
||||
bool TryParse(const std::string& str, bool* output);
|
||||
bool TryParse(const std::string& str, u32* output);
|
||||
|
||||
template <typename N>
|
||||
static bool TryParse(const std::string& str, N* const output) {
|
||||
std::istringstream iss(str);
|
||||
|
||||
N tmp = 0;
|
||||
if (iss >> tmp) {
|
||||
*output = tmp;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string TabsToSpaces(int tab_size, std::string in);
|
||||
|
||||
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
|
||||
|
||||
@@ -153,6 +153,7 @@ struct VisitorInterface : NonCopyable {
|
||||
|
||||
/// Completion method, called once all fields have been visited
|
||||
virtual void Complete() = 0;
|
||||
virtual bool SubmitTestcase() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -178,6 +179,9 @@ struct NullVisitor : public VisitorInterface {
|
||||
void Visit(const Field<std::chrono::microseconds>& /*field*/) override {}
|
||||
|
||||
void Complete() override {}
|
||||
bool SubmitTestcase() override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/// Appends build-specific information to the given FieldCollection,
|
||||
|
||||
@@ -185,7 +185,7 @@ struct System::Impl {
|
||||
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
||||
return ResultStatus::ErrorGetLoader;
|
||||
}
|
||||
std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
|
||||
std::pair<std::optional<u32>, Loader::ResultStatus> system_mode =
|
||||
app_loader->LoadKernelSystemMode();
|
||||
|
||||
if (system_mode.second != Loader::ResultStatus::Success) {
|
||||
@@ -312,6 +312,10 @@ Cpu& System::CurrentCpuCore() {
|
||||
return impl->CurrentCpuCore();
|
||||
}
|
||||
|
||||
const Cpu& System::CurrentCpuCore() const {
|
||||
return impl->CurrentCpuCore();
|
||||
}
|
||||
|
||||
System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
return impl->RunLoop(tight_loop);
|
||||
}
|
||||
@@ -342,7 +346,11 @@ PerfStatsResults System::GetAndResetPerfStats() {
|
||||
return impl->GetAndResetPerfStats();
|
||||
}
|
||||
|
||||
Core::TelemetrySession& System::TelemetrySession() const {
|
||||
TelemetrySession& System::TelemetrySession() {
|
||||
return *impl->telemetry_session;
|
||||
}
|
||||
|
||||
const TelemetrySession& System::TelemetrySession() const {
|
||||
return *impl->telemetry_session;
|
||||
}
|
||||
|
||||
@@ -350,7 +358,11 @@ ARM_Interface& System::CurrentArmInterface() {
|
||||
return CurrentCpuCore().ArmInterface();
|
||||
}
|
||||
|
||||
std::size_t System::CurrentCoreIndex() {
|
||||
const ARM_Interface& System::CurrentArmInterface() const {
|
||||
return CurrentCpuCore().ArmInterface();
|
||||
}
|
||||
|
||||
std::size_t System::CurrentCoreIndex() const {
|
||||
return CurrentCpuCore().CoreIndex();
|
||||
}
|
||||
|
||||
@@ -358,6 +370,10 @@ Kernel::Scheduler& System::CurrentScheduler() {
|
||||
return CurrentCpuCore().Scheduler();
|
||||
}
|
||||
|
||||
const Kernel::Scheduler& System::CurrentScheduler() const {
|
||||
return CurrentCpuCore().Scheduler();
|
||||
}
|
||||
|
||||
Kernel::Scheduler& System::Scheduler(std::size_t core_index) {
|
||||
return CpuCore(core_index).Scheduler();
|
||||
}
|
||||
@@ -378,6 +394,10 @@ ARM_Interface& System::ArmInterface(std::size_t core_index) {
|
||||
return CpuCore(core_index).ArmInterface();
|
||||
}
|
||||
|
||||
const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
|
||||
return CpuCore(core_index).ArmInterface();
|
||||
}
|
||||
|
||||
Cpu& System::CpuCore(std::size_t core_index) {
|
||||
ASSERT(core_index < NUM_CPU_CORES);
|
||||
return *impl->cpu_cores[core_index];
|
||||
@@ -392,6 +412,10 @@ ExclusiveMonitor& System::Monitor() {
|
||||
return *impl->cpu_exclusive_monitor;
|
||||
}
|
||||
|
||||
const ExclusiveMonitor& System::Monitor() const {
|
||||
return *impl->cpu_exclusive_monitor;
|
||||
}
|
||||
|
||||
Tegra::GPU& System::GPU() {
|
||||
return *impl->gpu_core;
|
||||
}
|
||||
|
||||
@@ -129,11 +129,11 @@ public:
|
||||
*/
|
||||
bool IsPoweredOn() const;
|
||||
|
||||
/**
|
||||
* Returns a reference to the telemetry session for this emulation session.
|
||||
* @returns Reference to the telemetry session.
|
||||
*/
|
||||
Core::TelemetrySession& TelemetrySession() const;
|
||||
/// Gets a reference to the telemetry session for this emulation session.
|
||||
Core::TelemetrySession& TelemetrySession();
|
||||
|
||||
/// Gets a reference to the telemetry session for this emulation session.
|
||||
const Core::TelemetrySession& TelemetrySession() const;
|
||||
|
||||
/// Prepare the core emulation for a reschedule
|
||||
void PrepareReschedule();
|
||||
@@ -144,24 +144,36 @@ public:
|
||||
/// Gets an ARM interface to the CPU core that is currently running
|
||||
ARM_Interface& CurrentArmInterface();
|
||||
|
||||
/// Gets an ARM interface to the CPU core that is currently running
|
||||
const ARM_Interface& CurrentArmInterface() const;
|
||||
|
||||
/// Gets the index of the currently running CPU core
|
||||
std::size_t CurrentCoreIndex();
|
||||
std::size_t CurrentCoreIndex() const;
|
||||
|
||||
/// Gets the scheduler for the CPU core that is currently running
|
||||
Kernel::Scheduler& CurrentScheduler();
|
||||
|
||||
/// Gets an ARM interface to the CPU core with the specified index
|
||||
/// Gets the scheduler for the CPU core that is currently running
|
||||
const Kernel::Scheduler& CurrentScheduler() const;
|
||||
|
||||
/// Gets a reference to an ARM interface for the CPU core with the specified index
|
||||
ARM_Interface& ArmInterface(std::size_t core_index);
|
||||
|
||||
/// Gets a const reference to an ARM interface from the CPU core with the specified index
|
||||
const ARM_Interface& ArmInterface(std::size_t core_index) const;
|
||||
|
||||
/// Gets a CPU interface to the CPU core with the specified index
|
||||
Cpu& CpuCore(std::size_t core_index);
|
||||
|
||||
/// Gets a CPU interface to the CPU core with the specified index
|
||||
const Cpu& CpuCore(std::size_t core_index) const;
|
||||
|
||||
/// Gets the exclusive monitor
|
||||
/// Gets a reference to the exclusive monitor
|
||||
ExclusiveMonitor& Monitor();
|
||||
|
||||
/// Gets a constant reference to the exclusive monitor
|
||||
const ExclusiveMonitor& Monitor() const;
|
||||
|
||||
/// Gets a mutable reference to the GPU interface
|
||||
Tegra::GPU& GPU();
|
||||
|
||||
@@ -230,6 +242,9 @@ private:
|
||||
/// Returns the currently running CPU core
|
||||
Cpu& CurrentCpuCore();
|
||||
|
||||
/// Returns the currently running CPU core
|
||||
const Cpu& CurrentCpuCore() const;
|
||||
|
||||
/**
|
||||
* Initialize the emulated system.
|
||||
* @param emu_window Reference to the host-system window used for video output and keyboard
|
||||
|
||||
@@ -141,28 +141,28 @@ Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source)
|
||||
return mac_key;
|
||||
}
|
||||
|
||||
boost::optional<Key128> DeriveSDSeed() {
|
||||
std::optional<Key128> DeriveSDSeed() {
|
||||
const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
"/system/save/8000000000000043",
|
||||
"rb+");
|
||||
if (!save_43.IsOpen())
|
||||
return boost::none;
|
||||
return {};
|
||||
|
||||
const FileUtil::IOFile sd_private(
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
|
||||
if (!sd_private.IsOpen())
|
||||
return boost::none;
|
||||
return {};
|
||||
|
||||
std::array<u8, 0x10> private_seed{};
|
||||
if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::array<u8, 0x10> buffer{};
|
||||
std::size_t offset = 0;
|
||||
for (; offset + 0x10 < save_43.GetSize(); ++offset) {
|
||||
if (!save_43.Seek(offset, SEEK_SET)) {
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
|
||||
save_43.ReadBytes(buffer.data(), buffer.size());
|
||||
@@ -172,12 +172,12 @@ boost::optional<Key128> DeriveSDSeed() {
|
||||
}
|
||||
|
||||
if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
|
||||
Key128 seed{};
|
||||
if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
return seed;
|
||||
}
|
||||
@@ -291,26 +291,26 @@ static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
|
||||
}
|
||||
|
||||
template <size_t size>
|
||||
static boost::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
|
||||
static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
|
||||
u64 offset = 0;
|
||||
for (size_t i = 0x20; i < data.size() - 0x10; ++i) {
|
||||
if (data[i] == 0x1) {
|
||||
offset = i + 1;
|
||||
break;
|
||||
} else if (data[i] != 0x0) {
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
|
||||
const RSAKeyPair<2048>& key) {
|
||||
std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
|
||||
const RSAKeyPair<2048>& key) {
|
||||
u32 cert_authority;
|
||||
std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
|
||||
if (cert_authority == 0)
|
||||
return boost::none;
|
||||
return {};
|
||||
if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) {
|
||||
LOG_INFO(Crypto,
|
||||
"Attempting to parse ticket with non-standard certificate authority {:08X}.",
|
||||
@@ -321,7 +321,7 @@ boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
|
||||
std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
|
||||
|
||||
if (rights_id == Key128{})
|
||||
return boost::none;
|
||||
return {};
|
||||
|
||||
Key128 key_temp{};
|
||||
|
||||
@@ -356,17 +356,17 @@ boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
|
||||
std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
|
||||
|
||||
if (m_0 != 0)
|
||||
return boost::none;
|
||||
return {};
|
||||
|
||||
m_1 = m_1 ^ MGF1<0x20>(m_2);
|
||||
m_2 = m_2 ^ MGF1<0xDF>(m_1);
|
||||
|
||||
const auto offset = FindTicketOffset(m_2);
|
||||
if (offset == boost::none)
|
||||
return boost::none;
|
||||
ASSERT(offset.get() > 0);
|
||||
if (!offset)
|
||||
return {};
|
||||
ASSERT(*offset > 0);
|
||||
|
||||
std::memcpy(key_temp.data(), m_2.data() + offset.get(), key_temp.size());
|
||||
std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size());
|
||||
|
||||
return std::make_pair(rights_id, key_temp);
|
||||
}
|
||||
@@ -395,7 +395,7 @@ static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_
|
||||
if (base.size() < begin + length)
|
||||
return false;
|
||||
return std::all_of(base.begin() + begin, base.begin() + begin + length,
|
||||
[](u8 c) { return std::isdigit(c); });
|
||||
[](u8 c) { return std::isxdigit(c); });
|
||||
}
|
||||
|
||||
void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
|
||||
@@ -661,8 +661,8 @@ void KeyManager::DeriveSDSeedLazy() {
|
||||
return;
|
||||
|
||||
const auto res = DeriveSDSeed();
|
||||
if (res != boost::none)
|
||||
SetKey(S128KeyType::SDSeed, res.get());
|
||||
if (res)
|
||||
SetKey(S128KeyType::SDSeed, *res);
|
||||
}
|
||||
|
||||
static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
|
||||
@@ -713,7 +713,6 @@ void KeyManager::DeriveBase() {
|
||||
|
||||
const auto sbk = GetKey(S128KeyType::SecureBoot);
|
||||
const auto tsec = GetKey(S128KeyType::TSEC);
|
||||
const auto master_source = GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master));
|
||||
|
||||
for (size_t i = 0; i < revisions.size(); ++i) {
|
||||
if (!revisions[i])
|
||||
@@ -890,9 +889,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
|
||||
|
||||
for (const auto& raw : res) {
|
||||
const auto pair = ParseTicket(raw, rsa_key);
|
||||
if (pair == boost::none)
|
||||
if (!pair)
|
||||
continue;
|
||||
const auto& [rid, key] = pair.value();
|
||||
const auto& [rid, key] = *pair;
|
||||
u128 rights_id;
|
||||
std::memcpy(rights_id.data(), rid.data(), rid.size());
|
||||
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_types.h"
|
||||
#include "core/crypto/partition_data_manager.h"
|
||||
@@ -191,14 +192,14 @@ Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master
|
||||
std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblob,
|
||||
const Key128& key);
|
||||
|
||||
boost::optional<Key128> DeriveSDSeed();
|
||||
std::optional<Key128> DeriveSDSeed();
|
||||
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
|
||||
|
||||
std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save);
|
||||
|
||||
// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset
|
||||
// 0x140-0x144 is zero)
|
||||
boost::optional<std::pair<Key128, Key128>> ParseTicket(
|
||||
const TicketRaw& ticket, const RSAKeyPair<2048>& eticket_extended_key);
|
||||
std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
|
||||
const RSAKeyPair<2048>& eticket_extended_key);
|
||||
|
||||
} // namespace Core::Crypto
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
|
||||
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_)
|
||||
: nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
|
||||
dump_root(std::move(dump_root_)),
|
||||
sysnand_cache(std::make_unique<RegisteredCache>(
|
||||
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
|
||||
usrnand_cache(std::make_unique<RegisteredCache>(
|
||||
@@ -32,4 +33,10 @@ VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
|
||||
return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
|
||||
}
|
||||
|
||||
VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
|
||||
if (title_id == 0)
|
||||
return nullptr;
|
||||
return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -17,17 +17,19 @@ class RegisteredCache;
|
||||
/// registered caches.
|
||||
class BISFactory {
|
||||
public:
|
||||
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
|
||||
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
|
||||
~BISFactory();
|
||||
|
||||
RegisteredCache* GetSystemNANDContents() const;
|
||||
RegisteredCache* GetUserNANDContents() const;
|
||||
|
||||
VirtualDir GetModificationLoadRoot(u64 title_id) const;
|
||||
VirtualDir GetModificationDumpRoot(u64 title_id) const;
|
||||
|
||||
private:
|
||||
VirtualDir nand_root;
|
||||
VirtualDir load_root;
|
||||
VirtualDir dump_root;
|
||||
|
||||
std::unique_ptr<RegisteredCache> sysnand_cache;
|
||||
std::unique_ptr<RegisteredCache> usrnand_cache;
|
||||
|
||||
@@ -168,10 +168,6 @@ VirtualDir XCI::GetParentDirectory() const {
|
||||
return file->GetContainingDirectory();
|
||||
}
|
||||
|
||||
bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
|
||||
if (partitions[static_cast<std::size_t>(part)] == nullptr) {
|
||||
return Loader::ResultStatus::ErrorXCIMissingPartition;
|
||||
|
||||
@@ -94,9 +94,6 @@ public:
|
||||
|
||||
VirtualDir GetParentDirectory() const override;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/crypto/aes_util.h"
|
||||
#include "core/crypto/ctr_encryption_layer.h"
|
||||
@@ -306,18 +305,18 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl
|
||||
subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
|
||||
subsection_buckets.back().entries.push_back({size, {0}, 0});
|
||||
|
||||
boost::optional<Core::Crypto::Key128> key = boost::none;
|
||||
std::optional<Core::Crypto::Key128> key = {};
|
||||
if (encrypted) {
|
||||
if (has_rights_id) {
|
||||
status = Loader::ResultStatus::Success;
|
||||
key = GetTitlekey();
|
||||
if (key == boost::none) {
|
||||
if (!key) {
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekey;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
|
||||
if (key == boost::none) {
|
||||
if (!key) {
|
||||
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
|
||||
return false;
|
||||
}
|
||||
@@ -332,7 +331,7 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl
|
||||
auto bktr = std::make_shared<BKTR>(
|
||||
bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
|
||||
relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted,
|
||||
encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
|
||||
encrypted ? *key : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
|
||||
section.raw.section_ctr);
|
||||
|
||||
// BKTR applies to entire IVFC, so make an offset version to level 6
|
||||
@@ -388,11 +387,11 @@ u8 NCA::GetCryptoRevision() const {
|
||||
return master_key_id;
|
||||
}
|
||||
|
||||
boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
|
||||
std::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
|
||||
const auto master_key_id = GetCryptoRevision();
|
||||
|
||||
if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index))
|
||||
return boost::none;
|
||||
return {};
|
||||
|
||||
std::vector<u8> key_area(header.key_area.begin(), header.key_area.end());
|
||||
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
|
||||
@@ -416,25 +415,25 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
|
||||
return out;
|
||||
}
|
||||
|
||||
boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
|
||||
std::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
|
||||
const auto master_key_id = GetCryptoRevision();
|
||||
|
||||
u128 rights_id{};
|
||||
memcpy(rights_id.data(), header.rights_id.data(), 16);
|
||||
if (rights_id == u128{}) {
|
||||
status = Loader::ResultStatus::ErrorInvalidRightsID;
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
|
||||
if (titlekey == Core::Crypto::Key128{}) {
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekey;
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekek;
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
|
||||
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
|
||||
@@ -458,25 +457,25 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
|
||||
case NCASectionCryptoType::BKTR:
|
||||
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
|
||||
{
|
||||
boost::optional<Core::Crypto::Key128> key = boost::none;
|
||||
std::optional<Core::Crypto::Key128> key = {};
|
||||
if (has_rights_id) {
|
||||
status = Loader::ResultStatus::Success;
|
||||
key = GetTitlekey();
|
||||
if (key == boost::none) {
|
||||
if (!key) {
|
||||
if (status == Loader::ResultStatus::Success)
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekey;
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
key = GetKeyAreaKey(NCASectionCryptoType::CTR);
|
||||
if (key == boost::none) {
|
||||
if (!key) {
|
||||
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(
|
||||
std::move(in), key.value(), starting_offset);
|
||||
auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key,
|
||||
starting_offset);
|
||||
std::vector<u8> iv(16);
|
||||
for (u8 i = 0; i < 8; ++i)
|
||||
iv[i] = s_header.raw.section_ctr[0x8 - i - 1];
|
||||
@@ -546,7 +545,4 @@ u64 NCA::GetBaseIVFCOffset() const {
|
||||
return ivfc_offset;
|
||||
}
|
||||
|
||||
bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
return false;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
@@ -100,9 +101,6 @@ public:
|
||||
// Returns the base ivfc offset used in BKTR patching.
|
||||
u64 GetBaseIVFCOffset() const;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
bool CheckSupportedNCA(const NCAHeader& header);
|
||||
bool HandlePotentialHeaderDecryption();
|
||||
@@ -114,8 +112,8 @@ private:
|
||||
bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry);
|
||||
|
||||
u8 GetCryptoRevision() const;
|
||||
boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
|
||||
boost::optional<Core::Crypto::Key128> GetTitlekey();
|
||||
std::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
|
||||
std::optional<Core::Crypto::Key128> GetTitlekey();
|
||||
VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset);
|
||||
|
||||
std::vector<VirtualDir> dirs;
|
||||
|
||||
@@ -66,4 +66,10 @@ std::string NACP::GetVersionString() const {
|
||||
return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
|
||||
raw->version_string.size());
|
||||
}
|
||||
|
||||
std::vector<u8> NACP::GetRawBytes() const {
|
||||
std::vector<u8> out(sizeof(RawNACP));
|
||||
std::memcpy(out.data(), raw.get(), sizeof(RawNACP));
|
||||
return out;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -81,6 +81,7 @@ public:
|
||||
u64 GetTitleId() const;
|
||||
u64 GetDLCBaseTitleId() const;
|
||||
std::string GetVersionString() const;
|
||||
std::vector<u8> GetRawBytes() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<RawNACP> raw;
|
||||
|
||||
@@ -8,25 +8,10 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
namespace ErrCodes {
|
||||
enum {
|
||||
NotFound = 1,
|
||||
TitleNotFound = 1002,
|
||||
SdCardNotFound = 2001,
|
||||
RomFSNotFound = 2520,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
|
||||
|
||||
// TODO(bunnei): Replace these with correct errors for Switch OS
|
||||
constexpr ResultCode ERROR_INVALID_PATH(-1);
|
||||
constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(-1);
|
||||
constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(-1);
|
||||
constexpr ResultCode ERROR_FILE_NOT_FOUND(-1);
|
||||
constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(-1);
|
||||
constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(-1);
|
||||
constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(-1);
|
||||
constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(-1);
|
||||
constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
|
||||
constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
|
||||
constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
|
||||
constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
|
||||
constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <boost/detail/container_fwd.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
|
||||
@@ -99,16 +99,16 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
||||
u16 rle_size{};
|
||||
if (ips->ReadObject(&rle_size, offset) != sizeof(u16))
|
||||
return nullptr;
|
||||
rle_size = Common::swap16(data_size);
|
||||
rle_size = Common::swap16(rle_size);
|
||||
offset += sizeof(u16);
|
||||
|
||||
const auto data = ips->ReadByte(offset++);
|
||||
if (data == boost::none)
|
||||
if (!data)
|
||||
return nullptr;
|
||||
|
||||
if (real_offset + rle_size > in_data.size())
|
||||
rle_size = static_cast<u16>(in_data.size() - real_offset);
|
||||
std::memset(in_data.data() + real_offset, data.get(), rle_size);
|
||||
std::memset(in_data.data() + real_offset, *data, rle_size);
|
||||
} else { // Standard Patch
|
||||
auto read = data_size;
|
||||
if (real_offset + read > in_data.size())
|
||||
|
||||
@@ -83,7 +83,7 @@ std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const {
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const {
|
||||
return pfs_dirs;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string PartitionFilesystem::GetName() const {
|
||||
@@ -103,18 +103,4 @@ void PartitionFilesystem::PrintDebugInfo() const {
|
||||
pfs_files[i]->GetName(), pfs_files[i]->GetSize());
|
||||
}
|
||||
}
|
||||
|
||||
bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
const auto iter = std::find(pfs_files.begin(), pfs_files.end(), file);
|
||||
if (iter == pfs_files.end())
|
||||
return false;
|
||||
|
||||
const std::ptrdiff_t offset = std::distance(pfs_files.begin(), iter);
|
||||
pfs_files[offset] = std::move(pfs_files.back());
|
||||
pfs_files.pop_back();
|
||||
|
||||
pfs_dirs.emplace_back(std::move(dir));
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -35,9 +35,6 @@ public:
|
||||
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
|
||||
void PrintDebugInfo() const;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
struct Header {
|
||||
u32_le magic;
|
||||
@@ -84,7 +81,6 @@ private:
|
||||
std::size_t content_offset = 0;
|
||||
|
||||
std::vector<VirtualFile> pfs_files;
|
||||
std::vector<VirtualDir> pfs_dirs;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -61,13 +62,12 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
// Game Updates
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
const auto update = installed->GetEntry(update_tid, ContentRecordType::Program);
|
||||
if (update != nullptr) {
|
||||
if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
|
||||
update->GetExeFS() != nullptr) {
|
||||
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
|
||||
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
|
||||
exefs = update->GetExeFS();
|
||||
}
|
||||
|
||||
if (update != nullptr && update->GetExeFS() != nullptr &&
|
||||
update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
|
||||
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
|
||||
FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0)));
|
||||
exefs = update->GetExeFS();
|
||||
}
|
||||
|
||||
return exefs;
|
||||
@@ -120,6 +120,18 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
|
||||
const auto build_id_raw = Common::HexArrayToString(header.build_id);
|
||||
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
|
||||
|
||||
if (Settings::values.dump_nso) {
|
||||
LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id);
|
||||
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
|
||||
if (dump_dir != nullptr) {
|
||||
const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
|
||||
const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id));
|
||||
|
||||
file->Resize(nso.size());
|
||||
file->WriteBytes(nso);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO(Loader, "Patching NSO for build_id={}", build_id);
|
||||
|
||||
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
|
||||
@@ -237,7 +249,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
|
||||
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
|
||||
new_nca->GetRomFS() != nullptr) {
|
||||
LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
|
||||
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
|
||||
FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0)));
|
||||
romfs = new_nca->GetRomFS();
|
||||
}
|
||||
} else if (update_raw != nullptr) {
|
||||
@@ -281,12 +293,11 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
|
||||
} else {
|
||||
if (installed->HasEntry(update_tid, ContentRecordType::Program)) {
|
||||
const auto meta_ver = installed->GetEntryVersion(update_tid);
|
||||
if (meta_ver == boost::none || meta_ver.get() == 0) {
|
||||
if (meta_ver.value_or(0) == 0) {
|
||||
out.insert_or_assign("Update", "");
|
||||
} else {
|
||||
out.insert_or_assign(
|
||||
"Update",
|
||||
FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements));
|
||||
"Update", FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements));
|
||||
}
|
||||
} else if (update_raw != nullptr) {
|
||||
out.insert_or_assign("Update", "PACKED");
|
||||
|
||||
@@ -159,28 +159,28 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
||||
return file;
|
||||
}
|
||||
|
||||
static boost::optional<NcaID> CheckMapForContentRecord(
|
||||
static std::optional<NcaID> CheckMapForContentRecord(
|
||||
const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) {
|
||||
if (map.find(title_id) == map.end())
|
||||
return boost::none;
|
||||
return {};
|
||||
|
||||
const auto& cnmt = map.at(title_id);
|
||||
|
||||
const auto iter = std::find_if(cnmt.GetContentRecords().begin(), cnmt.GetContentRecords().end(),
|
||||
[type](const ContentRecord& rec) { return rec.type == type; });
|
||||
if (iter == cnmt.GetContentRecords().end())
|
||||
return boost::none;
|
||||
return {};
|
||||
|
||||
return boost::make_optional(iter->nca_id);
|
||||
return std::make_optional(iter->nca_id);
|
||||
}
|
||||
|
||||
boost::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
|
||||
ContentRecordType type) const {
|
||||
std::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
|
||||
ContentRecordType type) const {
|
||||
if (type == ContentRecordType::Meta && meta_id.find(title_id) != meta_id.end())
|
||||
return meta_id.at(title_id);
|
||||
|
||||
const auto res1 = CheckMapForContentRecord(yuzu_meta, title_id, type);
|
||||
if (res1 != boost::none)
|
||||
if (res1)
|
||||
return res1;
|
||||
return CheckMapForContentRecord(meta, title_id, type);
|
||||
}
|
||||
@@ -283,17 +283,14 @@ bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const {
|
||||
|
||||
VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
|
||||
const auto id = GetNcaIDFromMetadata(title_id, type);
|
||||
if (id == boost::none)
|
||||
return nullptr;
|
||||
|
||||
return GetFileAtID(id.get());
|
||||
return id ? GetFileAtID(*id) : nullptr;
|
||||
}
|
||||
|
||||
VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const {
|
||||
return GetEntryUnparsed(entry.title_id, entry.type);
|
||||
}
|
||||
|
||||
boost::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
|
||||
std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
|
||||
const auto meta_iter = meta.find(title_id);
|
||||
if (meta_iter != meta.end())
|
||||
return meta_iter->second.GetTitleVersion();
|
||||
@@ -302,15 +299,12 @@ boost::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
|
||||
if (yuzu_meta_iter != yuzu_meta.end())
|
||||
return yuzu_meta_iter->second.GetTitleVersion();
|
||||
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
|
||||
VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const {
|
||||
const auto id = GetNcaIDFromMetadata(title_id, type);
|
||||
if (id == boost::none)
|
||||
return nullptr;
|
||||
|
||||
return parser(GetFileAtID(id.get()), id.get());
|
||||
return id ? parser(GetFileAtID(*id), *id) : nullptr;
|
||||
}
|
||||
|
||||
VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
|
||||
@@ -364,8 +358,8 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntries() const {
|
||||
}
|
||||
|
||||
std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
|
||||
boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type,
|
||||
boost::optional<u64> title_id) const {
|
||||
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
|
||||
std::optional<u64> title_id) const {
|
||||
std::vector<RegisteredCacheEntry> out;
|
||||
IterateAllMetadata<RegisteredCacheEntry>(
|
||||
out,
|
||||
@@ -373,11 +367,11 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
|
||||
return RegisteredCacheEntry{c.GetTitleID(), r.type};
|
||||
},
|
||||
[&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
|
||||
if (title_type != boost::none && title_type.get() != c.GetType())
|
||||
if (title_type && *title_type != c.GetType())
|
||||
return false;
|
||||
if (record_type != boost::none && record_type.get() != r.type)
|
||||
if (record_type && *record_type != r.type)
|
||||
return false;
|
||||
if (title_id != boost::none && title_id.get() != c.GetTitleID())
|
||||
if (title_id && *title_id != c.GetTitleID())
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
@@ -459,7 +453,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType
|
||||
|
||||
InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
|
||||
bool overwrite_if_exists,
|
||||
boost::optional<NcaID> override_id) {
|
||||
std::optional<NcaID> override_id) {
|
||||
const auto in = nca->GetBaseFile();
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
|
||||
@@ -468,12 +462,12 @@ InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const Vfs
|
||||
// game is massive), we're going to cheat and only hash the first MB of the NCA.
|
||||
// Also, for XCIs the NcaID matters, so if the override id isn't none, use that.
|
||||
NcaID id{};
|
||||
if (override_id == boost::none) {
|
||||
if (override_id) {
|
||||
id = *override_id;
|
||||
} else {
|
||||
const auto& data = in->ReadBytes(0x100000);
|
||||
mbedtls_sha256(data.data(), data.size(), hash.data(), 0);
|
||||
memcpy(id.data(), hash.data(), 16);
|
||||
} else {
|
||||
id = override_id.get();
|
||||
}
|
||||
|
||||
std::string path = GetRelativePathFromNcaID(id, false, true);
|
||||
@@ -543,14 +537,14 @@ bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const {
|
||||
return HasEntry(entry.title_id, entry.type);
|
||||
}
|
||||
|
||||
boost::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
|
||||
std::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
|
||||
for (const auto& c : caches) {
|
||||
const auto res = c->GetEntryVersion(title_id);
|
||||
if (res != boost::none)
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
|
||||
VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
|
||||
@@ -609,8 +603,8 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
|
||||
}
|
||||
|
||||
std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
|
||||
boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type,
|
||||
boost::optional<u64> title_id) const {
|
||||
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
|
||||
std::optional<u64> title_id) const {
|
||||
std::vector<RegisteredCacheEntry> out;
|
||||
for (const auto& c : caches) {
|
||||
c->IterateAllMetadata<RegisteredCacheEntry>(
|
||||
@@ -619,11 +613,11 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
|
||||
return RegisteredCacheEntry{c.GetTitleID(), r.type};
|
||||
},
|
||||
[&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
|
||||
if (title_type != boost::none && title_type.get() != c.GetType())
|
||||
if (title_type && *title_type != c.GetType())
|
||||
return false;
|
||||
if (record_type != boost::none && record_type.get() != r.type)
|
||||
if (record_type && *record_type != r.type)
|
||||
return false;
|
||||
if (title_id != boost::none && title_id.get() != c.GetTitleID())
|
||||
if (title_id && *title_id != c.GetTitleID())
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -84,7 +84,7 @@ public:
|
||||
bool HasEntry(u64 title_id, ContentRecordType type) const;
|
||||
bool HasEntry(RegisteredCacheEntry entry) const;
|
||||
|
||||
boost::optional<u32> GetEntryVersion(u64 title_id) const;
|
||||
std::optional<u32> GetEntryVersion(u64 title_id) const;
|
||||
|
||||
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
|
||||
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
|
||||
@@ -96,11 +96,10 @@ public:
|
||||
std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
|
||||
|
||||
std::vector<RegisteredCacheEntry> ListEntries() const;
|
||||
// If a parameter is not boost::none, it will be filtered for from all entries.
|
||||
// If a parameter is not std::nullopt, it will be filtered for from all entries.
|
||||
std::vector<RegisteredCacheEntry> ListEntriesFilter(
|
||||
boost::optional<TitleType> title_type = boost::none,
|
||||
boost::optional<ContentRecordType> record_type = boost::none,
|
||||
boost::optional<u64> title_id = boost::none) const;
|
||||
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
||||
std::optional<u64> title_id = {}) const;
|
||||
|
||||
// Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
|
||||
// there is a meta NCA and all of them are accessible.
|
||||
@@ -125,12 +124,11 @@ private:
|
||||
std::vector<NcaID> AccumulateFiles() const;
|
||||
void ProcessFiles(const std::vector<NcaID>& ids);
|
||||
void AccumulateYuzuMeta();
|
||||
boost::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
|
||||
std::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
|
||||
VirtualFile GetFileAtID(NcaID id) const;
|
||||
VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const;
|
||||
InstallResult RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
|
||||
bool overwrite_if_exists,
|
||||
boost::optional<NcaID> override_id = boost::none);
|
||||
bool overwrite_if_exists, std::optional<NcaID> override_id = {});
|
||||
bool RawInstallYuzuMeta(const CNMT& cnmt);
|
||||
|
||||
VirtualDir dir;
|
||||
@@ -153,7 +151,7 @@ public:
|
||||
bool HasEntry(u64 title_id, ContentRecordType type) const;
|
||||
bool HasEntry(RegisteredCacheEntry entry) const;
|
||||
|
||||
boost::optional<u32> GetEntryVersion(u64 title_id) const;
|
||||
std::optional<u32> GetEntryVersion(u64 title_id) const;
|
||||
|
||||
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
|
||||
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
|
||||
@@ -165,11 +163,10 @@ public:
|
||||
std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
|
||||
|
||||
std::vector<RegisteredCacheEntry> ListEntries() const;
|
||||
// If a parameter is not boost::none, it will be filtered for from all entries.
|
||||
// If a parameter is not std::nullopt, it will be filtered for from all entries.
|
||||
std::vector<RegisteredCacheEntry> ListEntriesFilter(
|
||||
boost::optional<TitleType> title_type = boost::none,
|
||||
boost::optional<ContentRecordType> record_type = boost::none,
|
||||
boost::optional<u64> title_id = boost::none) const;
|
||||
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
||||
std::optional<u64> title_id = {}) const;
|
||||
|
||||
private:
|
||||
std::vector<RegisteredCache*> caches;
|
||||
|
||||
@@ -83,6 +83,24 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
|
||||
return MakeResult<VirtualDir>(std::move(out));
|
||||
}
|
||||
|
||||
VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) const {
|
||||
return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space));
|
||||
}
|
||||
|
||||
std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
|
||||
switch (space) {
|
||||
case SaveDataSpaceId::NandSystem:
|
||||
return "/system/";
|
||||
case SaveDataSpaceId::NandUser:
|
||||
return "/user/";
|
||||
case SaveDataSpaceId::TemporaryStorage:
|
||||
return "/temp/";
|
||||
default:
|
||||
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
|
||||
return "/unrecognized/"; ///< To prevent corruption when ignoring asserts.
|
||||
}
|
||||
}
|
||||
|
||||
std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
|
||||
u128 user_id, u64 save_id) {
|
||||
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
|
||||
@@ -90,21 +108,7 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
|
||||
if (type == SaveDataType::SaveData && title_id == 0)
|
||||
title_id = Core::CurrentProcess()->GetTitleID();
|
||||
|
||||
std::string out;
|
||||
|
||||
switch (space) {
|
||||
case SaveDataSpaceId::NandSystem:
|
||||
out = "/system/";
|
||||
break;
|
||||
case SaveDataSpaceId::NandUser:
|
||||
out = "/user/";
|
||||
break;
|
||||
case SaveDataSpaceId::TemporaryStorage:
|
||||
out = "/temp/";
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
|
||||
}
|
||||
std::string out = GetSaveDataSpaceIdPath(space);
|
||||
|
||||
switch (type) {
|
||||
case SaveDataType::SystemSaveData:
|
||||
|
||||
@@ -52,6 +52,9 @@ public:
|
||||
|
||||
ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
|
||||
|
||||
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
|
||||
|
||||
static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
|
||||
static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
|
||||
u128 user_id, u64 save_id);
|
||||
|
||||
|
||||
@@ -205,10 +205,6 @@ VirtualDir NSP::GetParentDirectory() const {
|
||||
return file->GetContainingDirectory();
|
||||
}
|
||||
|
||||
bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) {
|
||||
exefs = pfs;
|
||||
|
||||
|
||||
@@ -55,9 +55,6 @@ public:
|
||||
|
||||
VirtualDir GetParentDirectory() const override;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files);
|
||||
void ReadNCAs(const std::vector<VirtualFile>& files);
|
||||
|
||||
@@ -167,13 +167,13 @@ std::string VfsFile::GetExtension() const {
|
||||
|
||||
VfsDirectory::~VfsDirectory() = default;
|
||||
|
||||
boost::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
|
||||
std::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
|
||||
u8 out{};
|
||||
std::size_t size = Read(&out, 1, offset);
|
||||
if (size == 1)
|
||||
return out;
|
||||
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
|
||||
@@ -472,10 +472,14 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t blo
|
||||
std::vector<u8> temp(std::min(block_size, src->GetSize()));
|
||||
for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
|
||||
const auto read = std::min(block_size, src->GetSize() - i);
|
||||
const auto block = src->Read(temp.data(), read, i);
|
||||
|
||||
if (dest->Write(temp.data(), read, i) != read)
|
||||
if (src->Read(temp.data(), read, i) != read) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dest->Write(temp.data(), read, i) != read) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
|
||||
@@ -103,8 +105,8 @@ public:
|
||||
// into file. Returns number of bytes successfully written.
|
||||
virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0;
|
||||
|
||||
// Reads exactly one byte at the offset provided, returning boost::none on error.
|
||||
virtual boost::optional<u8> ReadByte(std::size_t offset = 0) const;
|
||||
// Reads exactly one byte at the offset provided, returning std::nullopt on error.
|
||||
virtual std::optional<u8> ReadByte(std::size_t offset = 0) const;
|
||||
// Reads size bytes starting at offset in file into a vector.
|
||||
virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const;
|
||||
// Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(),
|
||||
@@ -262,36 +264,8 @@ public:
|
||||
// item name -> type.
|
||||
virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const;
|
||||
|
||||
// Interprets the file with name file instead as a directory of type directory.
|
||||
// The directory must have a constructor that takes a single argument of type
|
||||
// std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
|
||||
// subdirectory in one call.
|
||||
template <typename Directory>
|
||||
bool InterpretAsDirectory(std::string_view file) {
|
||||
auto file_p = GetFile(file);
|
||||
|
||||
if (file_p == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p));
|
||||
}
|
||||
|
||||
bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function,
|
||||
const std::string& file) {
|
||||
auto file_p = GetFile(file);
|
||||
if (file_p == nullptr)
|
||||
return false;
|
||||
return ReplaceFileWithSubdirectory(file_p, function(file_p));
|
||||
}
|
||||
|
||||
// Returns the full path of this directory as a string, recursively
|
||||
virtual std::string GetFullPath() const;
|
||||
|
||||
protected:
|
||||
// Backend for InterpretAsDirectory.
|
||||
// Removes all references to file and adds a reference to dir in the directory's implementation.
|
||||
virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0;
|
||||
};
|
||||
|
||||
// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work
|
||||
|
||||
@@ -126,7 +126,4 @@ bool LayeredVfsDirectory::Rename(std::string_view name_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LayeredVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
return false;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -39,9 +39,6 @@ public:
|
||||
bool DeleteFile(std::string_view name) override;
|
||||
bool Rename(std::string_view name) override;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
std::vector<VirtualDir> dirs;
|
||||
std::string name;
|
||||
|
||||
@@ -57,11 +57,11 @@ std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t
|
||||
return file->Write(data, TrimToFit(length, r_offset), offset + r_offset);
|
||||
}
|
||||
|
||||
boost::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
|
||||
std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
|
||||
if (r_offset < size)
|
||||
return file->ReadByte(offset + r_offset);
|
||||
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const {
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
bool IsReadable() const override;
|
||||
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
|
||||
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
|
||||
boost::optional<u8> ReadByte(std::size_t offset) const override;
|
||||
std::optional<u8> ReadByte(std::size_t offset) const override;
|
||||
std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override;
|
||||
std::vector<u8> ReadAllBytes() const override;
|
||||
bool WriteByte(u8 data, std::size_t offset) override;
|
||||
|
||||
@@ -430,7 +430,4 @@ std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries()
|
||||
return out;
|
||||
}
|
||||
|
||||
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
return false;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -100,9 +100,6 @@ public:
|
||||
std::string GetFullPath() const override;
|
||||
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
|
||||
|
||||
|
||||
@@ -53,10 +53,10 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
boost::optional<u8> ReadByte(std::size_t offset) const override {
|
||||
std::optional<u8> ReadByte(std::size_t offset) const override {
|
||||
if (offset < size)
|
||||
return value;
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
|
||||
|
||||
@@ -132,11 +132,4 @@ void VectorVfsDirectory::AddFile(VirtualFile file) {
|
||||
void VectorVfsDirectory::AddDirectory(VirtualDir dir) {
|
||||
dirs.push_back(std::move(dir));
|
||||
}
|
||||
|
||||
bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
if (!DeleteFile(file->GetName()))
|
||||
return false;
|
||||
dirs.emplace_back(std::move(dir));
|
||||
return true;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -57,9 +57,6 @@ public:
|
||||
virtual void AddFile(VirtualFile file);
|
||||
virtual void AddDirectory(VirtualDir dir);
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
std::vector<VirtualFile> files;
|
||||
std::vector<VirtualDir> dirs;
|
||||
|
||||
@@ -163,7 +163,4 @@ std::shared_ptr<VfsDirectory> NAX::GetParentDirectory() const {
|
||||
return file->GetContainingDirectory();
|
||||
}
|
||||
|
||||
bool NAX::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
return false;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -51,9 +51,6 @@ public:
|
||||
|
||||
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
Loader::ResultStatus Parse(std::string_view path);
|
||||
|
||||
|
||||
@@ -14,11 +14,6 @@ namespace IPC {
|
||||
/// Size of the command buffer area, in 32-bit words.
|
||||
constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
|
||||
|
||||
// These errors are commonly returned by invalid IPC translations, so alias them here for
|
||||
// convenience.
|
||||
// TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
|
||||
constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS;
|
||||
|
||||
enum class ControlCommand : u32 {
|
||||
ConvertSessionToDomain = 0,
|
||||
ConvertDomainToSession = 1,
|
||||
|
||||
@@ -117,8 +117,7 @@ public:
|
||||
|
||||
AlignWithPadding();
|
||||
|
||||
const bool request_has_domain_header{context.GetDomainMessageHeader() != nullptr};
|
||||
if (context.Session()->IsDomain() && request_has_domain_header) {
|
||||
if (context.Session()->IsDomain() && context.HasDomainMessageHeader()) {
|
||||
IPC::DomainMessageHeader domain_header{};
|
||||
domain_header.num_objects = num_domain_objects;
|
||||
PushRaw(domain_header);
|
||||
|
||||
@@ -10,11 +10,6 @@ namespace Kernel {
|
||||
|
||||
namespace ErrCodes {
|
||||
enum {
|
||||
// TODO(Subv): Remove these 3DS OS error codes.
|
||||
SessionClosedByRemote = 26,
|
||||
NoPendingSessions = 35,
|
||||
InvalidBufferDescriptor = 48,
|
||||
|
||||
// Confirmed Switch OS error codes
|
||||
MaxConnectionsReached = 7,
|
||||
InvalidSize = 101,
|
||||
@@ -26,6 +21,7 @@ enum {
|
||||
InvalidThreadPriority = 112,
|
||||
InvalidProcessorId = 113,
|
||||
InvalidHandle = 114,
|
||||
InvalidPointer = 115,
|
||||
InvalidCombination = 116,
|
||||
Timeout = 117,
|
||||
SynchronizationCanceled = 118,
|
||||
@@ -33,6 +29,7 @@ enum {
|
||||
InvalidEnumValue = 120,
|
||||
NoSuchEntry = 121,
|
||||
AlreadyRegistered = 122,
|
||||
SessionClosed = 123,
|
||||
InvalidState = 125,
|
||||
ResourceLimitExceeded = 132,
|
||||
};
|
||||
@@ -41,18 +38,14 @@ enum {
|
||||
// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always
|
||||
// double check that the code matches before re-using the constant.
|
||||
|
||||
// TODO(bunnei): Replace -1 with correct errors for Switch OS
|
||||
constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull);
|
||||
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1);
|
||||
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed);
|
||||
constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge);
|
||||
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel,
|
||||
ErrCodes::MaxConnectionsReached);
|
||||
constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
|
||||
constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1);
|
||||
constexpr ResultCode ERR_INVALID_COMBINATION(-1);
|
||||
constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel,
|
||||
ErrCodes::InvalidCombination);
|
||||
constexpr ResultCode ERR_OUT_OF_MEMORY(-1);
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
|
||||
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
|
||||
@@ -65,14 +58,8 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::Alrea
|
||||
constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
|
||||
constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
|
||||
ErrCodes::InvalidThreadPriority);
|
||||
constexpr ResultCode ERR_INVALID_POINTER(-1);
|
||||
constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1);
|
||||
constexpr ResultCode ERR_NOT_AUTHORIZED(-1);
|
||||
/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths.
|
||||
constexpr ResultCode ERR_INVALID_HANDLE_OS(-1);
|
||||
constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer);
|
||||
constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
|
||||
constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
|
||||
/// Returned when Accept() is called on a port with no sessions to be accepted.
|
||||
constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1);
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -161,8 +161,12 @@ public:
|
||||
return buffer_c_desciptors;
|
||||
}
|
||||
|
||||
const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
|
||||
return domain_message_header;
|
||||
const IPC::DomainMessageHeader* GetDomainMessageHeader() const {
|
||||
return domain_message_header.get();
|
||||
}
|
||||
|
||||
bool HasDomainMessageHeader() const {
|
||||
return domain_message_header != nullptr;
|
||||
}
|
||||
|
||||
/// Helper function to read a buffer using the appropriate buffer descriptor
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Kernel {
|
||||
*/
|
||||
static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) {
|
||||
const auto proper_handle = static_cast<Handle>(thread_handle);
|
||||
auto& system = Core::System::GetInstance();
|
||||
const auto& system = Core::System::GetInstance();
|
||||
|
||||
// Lock the global kernel mutex when we enter the kernel HLE.
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
@@ -90,7 +90,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
|
||||
/// The timer callback event, called when a timer is fired
|
||||
static void TimerCallback(u64 timer_handle, int cycles_late) {
|
||||
const auto proper_handle = static_cast<Handle>(timer_handle);
|
||||
auto& system = Core::System::GetInstance();
|
||||
const auto& system = Core::System::GetInstance();
|
||||
SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle);
|
||||
|
||||
if (timer == nullptr) {
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
@@ -17,6 +15,7 @@
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -35,6 +34,11 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
|
||||
process->process_id = kernel.CreateNewProcessID();
|
||||
process->svc_access_mask.set();
|
||||
|
||||
std::mt19937 rng(Settings::values.rng_seed.value_or(0));
|
||||
std::uniform_int_distribution<u64> distribution;
|
||||
std::generate(process->random_entropy.begin(), process->random_entropy.end(),
|
||||
[&] { return distribution(rng); });
|
||||
|
||||
kernel.AppendNewProcess(process);
|
||||
return process;
|
||||
}
|
||||
@@ -232,86 +236,24 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
|
||||
MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
|
||||
MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
|
||||
MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
|
||||
|
||||
// Clear instruction cache in CPU JIT
|
||||
Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
|
||||
Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
|
||||
Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
|
||||
Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
|
||||
}
|
||||
|
||||
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
|
||||
if (target < vm_manager.GetHeapRegionBaseAddress() ||
|
||||
target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (heap_memory == nullptr) {
|
||||
// Initialize heap
|
||||
heap_memory = std::make_shared<std::vector<u8>>();
|
||||
heap_start = heap_end = target;
|
||||
} else {
|
||||
vm_manager.UnmapRange(heap_start, heap_end - heap_start);
|
||||
}
|
||||
|
||||
// If necessary, expand backing vector to cover new heap extents.
|
||||
if (target < heap_start) {
|
||||
heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
|
||||
heap_start = target;
|
||||
vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
|
||||
}
|
||||
if (target + size > heap_end) {
|
||||
heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
|
||||
heap_end = target + size;
|
||||
vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
|
||||
}
|
||||
ASSERT(heap_end - heap_start == heap_memory->size());
|
||||
|
||||
CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, heap_memory, target - heap_start,
|
||||
size, MemoryState::Heap));
|
||||
vm_manager.Reprotect(vma, perms);
|
||||
|
||||
heap_used = size;
|
||||
|
||||
return MakeResult<VAddr>(heap_end - size);
|
||||
return vm_manager.HeapAllocate(target, size, perms);
|
||||
}
|
||||
|
||||
ResultCode Process::HeapFree(VAddr target, u32 size) {
|
||||
if (target < vm_manager.GetHeapRegionBaseAddress() ||
|
||||
target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode result = vm_manager.UnmapRange(target, size);
|
||||
if (result.IsError())
|
||||
return result;
|
||||
|
||||
heap_used -= size;
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return vm_manager.HeapFree(target, size);
|
||||
}
|
||||
|
||||
ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
auto vma = vm_manager.FindVMA(src_addr);
|
||||
|
||||
ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
|
||||
ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
|
||||
|
||||
// The returned VMA might be a bigger one encompassing the desired address.
|
||||
auto vma_offset = src_addr - vma->first;
|
||||
ASSERT_MSG(vma_offset + size <= vma->second.size,
|
||||
"Shared memory exceeds bounds of mapped block");
|
||||
|
||||
const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
|
||||
std::size_t backing_block_offset = vma->second.offset + vma_offset;
|
||||
|
||||
CASCADE_RESULT(auto new_vma,
|
||||
vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
|
||||
MemoryState::Mapped));
|
||||
// Protect mirror with permissions from old region
|
||||
vm_manager.Reprotect(new_vma, vma->second.permissions);
|
||||
// Remove permissions from old region
|
||||
vm_manager.Reprotect(vma, VMAPermission::None);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return vm_manager.MirrorMemory(dst_addr, src_addr, size);
|
||||
}
|
||||
|
||||
ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <bitset>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
@@ -119,6 +120,8 @@ struct CodeSet final {
|
||||
|
||||
class Process final : public Object {
|
||||
public:
|
||||
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
|
||||
|
||||
static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
@@ -202,6 +205,21 @@ public:
|
||||
return is_64bit_process;
|
||||
}
|
||||
|
||||
/// Gets the total running time of the process instance in ticks.
|
||||
u64 GetCPUTimeTicks() const {
|
||||
return total_process_running_time_ticks;
|
||||
}
|
||||
|
||||
/// Updates the total running time, adding the given ticks to it.
|
||||
void UpdateCPUTimeTicks(u64 ticks) {
|
||||
total_process_running_time_ticks += ticks;
|
||||
}
|
||||
|
||||
/// Gets 8 bytes of random data for svcGetInfo RandomEntropy
|
||||
u64 GetRandomEntropy(std::size_t index) const {
|
||||
return random_entropy.at(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads process-specifics configuration info with metadata provided
|
||||
* by an executable.
|
||||
@@ -282,17 +300,6 @@ private:
|
||||
u32 allowed_thread_priority_mask = 0xFFFFFFFF;
|
||||
u32 is_virtual_address_memory_enabled = 0;
|
||||
|
||||
// Memory used to back the allocations in the regular heap. A single vector is used to cover
|
||||
// the entire virtual address space extents that bound the allocations, including any holes.
|
||||
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
|
||||
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
|
||||
std::shared_ptr<std::vector<u8>> heap_memory;
|
||||
|
||||
// The left/right bounds of the address space covered by heap_memory.
|
||||
VAddr heap_start = 0;
|
||||
VAddr heap_end = 0;
|
||||
u64 heap_used = 0;
|
||||
|
||||
/// The Thread Local Storage area is allocated as processes create threads,
|
||||
/// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
|
||||
/// holds the TLS for a specific thread. This vector contains which parts are in use for each
|
||||
@@ -305,9 +312,15 @@ private:
|
||||
/// specified by metadata provided to the process during loading.
|
||||
bool is_64bit_process = true;
|
||||
|
||||
/// Total running time for the process in ticks.
|
||||
u64 total_process_running_time_ticks = 0;
|
||||
|
||||
/// Per-process handle table for storing created object handles in.
|
||||
HandleTable handle_table;
|
||||
|
||||
/// Random values for svcGetInfo RandomEntropy
|
||||
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
|
||||
|
||||
std::string name;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
@@ -34,6 +35,10 @@ Thread* Scheduler::GetCurrentThread() const {
|
||||
return current_thread.get();
|
||||
}
|
||||
|
||||
u64 Scheduler::GetLastContextSwitchTicks() const {
|
||||
return last_context_switch_time;
|
||||
}
|
||||
|
||||
Thread* Scheduler::PopNextReadyThread() {
|
||||
Thread* next = nullptr;
|
||||
Thread* thread = GetCurrentThread();
|
||||
@@ -54,7 +59,10 @@ Thread* Scheduler::PopNextReadyThread() {
|
||||
}
|
||||
|
||||
void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
Thread* previous_thread = GetCurrentThread();
|
||||
Thread* const previous_thread = GetCurrentThread();
|
||||
Process* const previous_process = Core::CurrentProcess();
|
||||
|
||||
UpdateLastContextSwitchTime(previous_thread, previous_process);
|
||||
|
||||
// Save context for previous thread
|
||||
if (previous_thread) {
|
||||
@@ -78,8 +86,6 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
// Cancel any outstanding wakeup events for this thread
|
||||
new_thread->CancelWakeupTimer();
|
||||
|
||||
auto* const previous_process = Core::CurrentProcess();
|
||||
|
||||
current_thread = new_thread;
|
||||
|
||||
ready_queue.remove(new_thread->GetPriority(), new_thread);
|
||||
@@ -102,6 +108,22 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
|
||||
const u64 prev_switch_ticks = last_context_switch_time;
|
||||
const u64 most_recent_switch_ticks = CoreTiming::GetTicks();
|
||||
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
|
||||
|
||||
if (thread != nullptr) {
|
||||
thread->UpdateCPUTimeTicks(update_ticks);
|
||||
}
|
||||
|
||||
if (process != nullptr) {
|
||||
process->UpdateCPUTimeTicks(update_ticks);
|
||||
}
|
||||
|
||||
last_context_switch_time = most_recent_switch_ticks;
|
||||
}
|
||||
|
||||
void Scheduler::Reschedule() {
|
||||
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ class ARM_Interface;
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class Process;
|
||||
|
||||
class Scheduler final {
|
||||
public:
|
||||
explicit Scheduler(Core::ARM_Interface& cpu_core);
|
||||
@@ -31,6 +33,9 @@ public:
|
||||
/// Gets the current running thread
|
||||
Thread* GetCurrentThread() const;
|
||||
|
||||
/// Gets the timestamp for the last context switch in ticks.
|
||||
u64 GetLastContextSwitchTicks() const;
|
||||
|
||||
/// Adds a new thread to the scheduler
|
||||
void AddThread(SharedPtr<Thread> thread, u32 priority);
|
||||
|
||||
@@ -64,6 +69,19 @@ private:
|
||||
*/
|
||||
void SwitchContext(Thread* new_thread);
|
||||
|
||||
/**
|
||||
* Called on every context switch to update the internal timestamp
|
||||
* This also updates the running time ticks for the given thread and
|
||||
* process using the following difference:
|
||||
*
|
||||
* ticks += most_recent_ticks - last_context_switch_ticks
|
||||
*
|
||||
* The internal tick timestamp for the scheduler is simply the
|
||||
* most recent tick count retrieved. No special arithmetic is
|
||||
* applied to it.
|
||||
*/
|
||||
void UpdateLastContextSwitchTime(Thread* thread, Process* process);
|
||||
|
||||
/// Lists all thread ids that aren't deleted/etc.
|
||||
std::vector<SharedPtr<Thread>> thread_list;
|
||||
|
||||
@@ -73,6 +91,7 @@ private:
|
||||
SharedPtr<Thread> current_thread = nullptr;
|
||||
|
||||
Core::ARM_Interface& cpu_core;
|
||||
u64 last_context_switch_time = 0;
|
||||
|
||||
static std::mutex scheduler_mutex;
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ ServerPort::~ServerPort() = default;
|
||||
|
||||
ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
|
||||
if (pending_sessions.empty()) {
|
||||
return ERR_NO_PENDING_SESSIONS;
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
auto session = std::move(pending_sessions.back());
|
||||
@@ -28,7 +28,7 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
|
||||
|
||||
bool ServerPort::ShouldWait(Thread* thread) const {
|
||||
// If there are no pending sessions, we wait until a new one is added.
|
||||
return pending_sessions.size() == 0;
|
||||
return pending_sessions.empty();
|
||||
}
|
||||
|
||||
void ServerPort::Acquire(Thread* thread) {
|
||||
|
||||
@@ -63,7 +63,7 @@ void ServerSession::Acquire(Thread* thread) {
|
||||
}
|
||||
|
||||
ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
|
||||
auto& domain_message_header = context.GetDomainMessageHeader();
|
||||
auto* const domain_message_header = context.GetDomainMessageHeader();
|
||||
if (domain_message_header) {
|
||||
// Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
|
||||
context.SetDomainRequestHandlers(domain_request_handlers);
|
||||
@@ -111,7 +111,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
|
||||
|
||||
ResultCode result = RESULT_SUCCESS;
|
||||
// If the session has been converted to a domain, handle the domain request
|
||||
if (IsDomain() && context.GetDomainMessageHeader()) {
|
||||
if (IsDomain() && context.HasDomainMessageHeader()) {
|
||||
result = HandleDomainSyncRequest(context);
|
||||
// If there is no domain header, the regular session handler is used
|
||||
} else if (hle_handler != nullptr) {
|
||||
|
||||
@@ -80,20 +80,19 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
|
||||
|
||||
ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
|
||||
MemoryPermission other_permissions) {
|
||||
|
||||
MemoryPermission own_other_permissions =
|
||||
const MemoryPermission own_other_permissions =
|
||||
target_process == owner_process ? this->permissions : this->other_permissions;
|
||||
|
||||
// Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
|
||||
if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
return ERR_INVALID_MEMORY_PERMISSIONS;
|
||||
}
|
||||
|
||||
// Error out if the requested permissions don't match what the creator process allows.
|
||||
if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
|
||||
LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match",
|
||||
GetObjectId(), address, name);
|
||||
return ERR_INVALID_COMBINATION;
|
||||
return ERR_INVALID_MEMORY_PERMISSIONS;
|
||||
}
|
||||
|
||||
// Error out if the provided permissions are not compatible with what the creator process needs.
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
@@ -395,16 +396,42 @@ struct BreakReason {
|
||||
/// Break program execution
|
||||
static void Break(u32 reason, u64 info1, u64 info2) {
|
||||
BreakReason break_reason{reason};
|
||||
bool has_dumped_buffer{};
|
||||
|
||||
const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
|
||||
if (sz == 0 || addr == 0 || has_dumped_buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This typically is an error code so we're going to assume this is the case
|
||||
if (sz == sizeof(u32)) {
|
||||
LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr));
|
||||
} else {
|
||||
// We don't know what's in here so we'll hexdump it
|
||||
std::vector<u8> debug_buffer(sz);
|
||||
Memory::ReadBlock(addr, debug_buffer.data(), sz);
|
||||
std::string hexdump;
|
||||
for (std::size_t i = 0; i < debug_buffer.size(); i++) {
|
||||
hexdump += fmt::format("{:02X} ", debug_buffer[i]);
|
||||
if (i != 0 && i % 16 == 0) {
|
||||
hexdump += '\n';
|
||||
}
|
||||
}
|
||||
LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump);
|
||||
}
|
||||
has_dumped_buffer = true;
|
||||
};
|
||||
switch (break_reason.break_type) {
|
||||
case BreakType::Panic:
|
||||
LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}",
|
||||
info1, info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
break;
|
||||
case BreakType::AssertionFailed:
|
||||
LOG_CRITICAL(Debug_Emulated,
|
||||
"Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
|
||||
info1, info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
break;
|
||||
case BreakType::PreNROLoad:
|
||||
LOG_WARNING(
|
||||
@@ -433,6 +460,7 @@ static void Break(u32 reason, u64 info1, u64 info2) {
|
||||
Debug_Emulated,
|
||||
"Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}",
|
||||
static_cast<u32>(break_reason.break_type.Value()), info1, info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -441,6 +469,7 @@ static void Break(u32 reason, u64 info1, u64 info2) {
|
||||
Debug_Emulated,
|
||||
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
|
||||
reason, info1, info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
ASSERT(false);
|
||||
|
||||
Core::CurrentProcess()->PrepareForTermination();
|
||||
@@ -467,6 +496,37 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
|
||||
info_sub_id, handle);
|
||||
|
||||
enum class GetInfoType : u64 {
|
||||
// 1.0.0+
|
||||
AllowedCpuIdBitmask = 0,
|
||||
AllowedThreadPrioBitmask = 1,
|
||||
MapRegionBaseAddr = 2,
|
||||
MapRegionSize = 3,
|
||||
HeapRegionBaseAddr = 4,
|
||||
HeapRegionSize = 5,
|
||||
TotalMemoryUsage = 6,
|
||||
TotalHeapUsage = 7,
|
||||
IsCurrentProcessBeingDebugged = 8,
|
||||
ResourceHandleLimit = 9,
|
||||
IdleTickCount = 10,
|
||||
RandomEntropy = 11,
|
||||
PerformanceCounter = 0xF0000002,
|
||||
// 2.0.0+
|
||||
ASLRRegionBaseAddr = 12,
|
||||
ASLRRegionSize = 13,
|
||||
NewMapRegionBaseAddr = 14,
|
||||
NewMapRegionSize = 15,
|
||||
// 3.0.0+
|
||||
IsVirtualAddressMemoryEnabled = 16,
|
||||
PersonalMmHeapUsage = 17,
|
||||
TitleId = 18,
|
||||
// 4.0.0+
|
||||
PrivilegedProcessId = 19,
|
||||
// 5.0.0+
|
||||
UserExceptionContextAddr = 20,
|
||||
ThreadTickCount = 0xF0000002,
|
||||
};
|
||||
|
||||
const auto* current_process = Core::CurrentProcess();
|
||||
const auto& vm_manager = current_process->VMManager();
|
||||
|
||||
@@ -499,7 +559,16 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
*result = 0;
|
||||
break;
|
||||
case GetInfoType::RandomEntropy:
|
||||
*result = 0;
|
||||
if (handle != 0) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
|
||||
return ERR_INVALID_COMBINATION_KERNEL;
|
||||
}
|
||||
|
||||
*result = current_process->GetRandomEntropy(info_sub_id);
|
||||
return RESULT_SUCCESS;
|
||||
break;
|
||||
case GetInfoType::ASLRRegionBaseAddr:
|
||||
*result = vm_manager.GetASLRRegionBaseAddress();
|
||||
@@ -529,6 +598,36 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
"(STUBBED) Attempted to query user exception context address, returned 0");
|
||||
*result = 0;
|
||||
break;
|
||||
case GetInfoType::ThreadTickCount: {
|
||||
constexpr u64 num_cpus = 4;
|
||||
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
|
||||
return ERR_INVALID_COMBINATION_KERNEL;
|
||||
}
|
||||
|
||||
const auto thread =
|
||||
current_process->GetHandleTable().Get<Thread>(static_cast<Handle>(handle));
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
const auto& system = Core::System::GetInstance();
|
||||
const auto& scheduler = system.CurrentScheduler();
|
||||
const auto* const current_thread = scheduler.GetCurrentThread();
|
||||
const bool same_thread = current_thread == thread;
|
||||
|
||||
const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
|
||||
u64 out_ticks = 0;
|
||||
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
|
||||
const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
|
||||
|
||||
out_ticks = thread_ticks + (CoreTiming::GetTicks() - prev_ctx_ticks);
|
||||
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
|
||||
out_ticks = CoreTiming::GetTicks() - prev_ctx_ticks;
|
||||
}
|
||||
|
||||
*result = out_ticks;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
@@ -594,16 +693,17 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
|
||||
}
|
||||
|
||||
const auto* const current_process = Core::CurrentProcess();
|
||||
SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
// Note: The kernel uses the current process's resource limit instead of
|
||||
// the one from the thread owner's resource limit.
|
||||
const ResourceLimit& resource_limit = current_process->GetResourceLimit();
|
||||
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
|
||||
return ERR_NOT_AUTHORIZED;
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
thread->SetPriority(priority);
|
||||
@@ -745,7 +845,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
const ResourceLimit& resource_limit = current_process->GetResourceLimit();
|
||||
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
|
||||
return ERR_NOT_AUTHORIZED;
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
if (processor_id == THREADPROCESSORID_DEFAULT) {
|
||||
|
||||
@@ -24,37 +24,6 @@ struct PageInfo {
|
||||
u64 flags;
|
||||
};
|
||||
|
||||
/// Values accepted by svcGetInfo
|
||||
enum class GetInfoType : u64 {
|
||||
// 1.0.0+
|
||||
AllowedCpuIdBitmask = 0,
|
||||
AllowedThreadPrioBitmask = 1,
|
||||
MapRegionBaseAddr = 2,
|
||||
MapRegionSize = 3,
|
||||
HeapRegionBaseAddr = 4,
|
||||
HeapRegionSize = 5,
|
||||
TotalMemoryUsage = 6,
|
||||
TotalHeapUsage = 7,
|
||||
IsCurrentProcessBeingDebugged = 8,
|
||||
ResourceHandleLimit = 9,
|
||||
IdleTickCount = 10,
|
||||
RandomEntropy = 11,
|
||||
PerformanceCounter = 0xF0000002,
|
||||
// 2.0.0+
|
||||
ASLRRegionBaseAddr = 12,
|
||||
ASLRRegionSize = 13,
|
||||
NewMapRegionBaseAddr = 14,
|
||||
NewMapRegionSize = 15,
|
||||
// 3.0.0+
|
||||
IsVirtualAddressMemoryEnabled = 16,
|
||||
PersonalMmHeapUsage = 17,
|
||||
TitleId = 18,
|
||||
// 4.0.0+
|
||||
PrivilegedProcessId = 19,
|
||||
// 5.0.0+
|
||||
UserExceptionContextAddr = 20,
|
||||
};
|
||||
|
||||
void CallSVC(u32 immediate);
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
@@ -94,7 +94,7 @@ void Thread::CancelWakeupTimer() {
|
||||
CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
}
|
||||
|
||||
static boost::optional<s32> GetNextProcessorId(u64 mask) {
|
||||
static std::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()) {
|
||||
@@ -142,36 +142,7 @@ void Thread::ResumeFromWait() {
|
||||
|
||||
status = ThreadStatus::Ready;
|
||||
|
||||
boost::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
|
||||
if (!new_processor_id) {
|
||||
new_processor_id = processor_id;
|
||||
}
|
||||
if (ideal_core != -1 &&
|
||||
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);
|
||||
|
||||
if (*new_processor_id != processor_id) {
|
||||
// Remove thread from previous core's scheduler
|
||||
scheduler->RemoveThread(this);
|
||||
next_scheduler->AddThread(this, current_priority);
|
||||
}
|
||||
|
||||
processor_id = *new_processor_id;
|
||||
|
||||
// If the thread was ready, unschedule from the previous core and schedule on the new core
|
||||
scheduler->UnscheduleThread(this, current_priority);
|
||||
next_scheduler->ScheduleThread(this, current_priority);
|
||||
|
||||
// Change thread's scheduler
|
||||
scheduler = next_scheduler;
|
||||
|
||||
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
||||
ChangeScheduler();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,42 +335,45 @@ void Thread::UpdatePriority() {
|
||||
void Thread::ChangeCore(u32 core, u64 mask) {
|
||||
ideal_core = core;
|
||||
affinity_mask = mask;
|
||||
ChangeScheduler();
|
||||
}
|
||||
|
||||
void Thread::ChangeScheduler() {
|
||||
if (status != ThreadStatus::Ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
boost::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
|
||||
auto& system = Core::System::GetInstance();
|
||||
std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
|
||||
|
||||
if (!new_processor_id) {
|
||||
new_processor_id = processor_id;
|
||||
}
|
||||
if (ideal_core != -1 &&
|
||||
Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
|
||||
if (ideal_core != -1 && system.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 = system.Scheduler(*new_processor_id);
|
||||
|
||||
if (*new_processor_id != processor_id) {
|
||||
// Remove thread from previous core's scheduler
|
||||
scheduler->RemoveThread(this);
|
||||
next_scheduler->AddThread(this, current_priority);
|
||||
next_scheduler.AddThread(this, current_priority);
|
||||
}
|
||||
|
||||
processor_id = *new_processor_id;
|
||||
|
||||
// If the thread was ready, unschedule from the previous core and schedule on the new core
|
||||
scheduler->UnscheduleThread(this, current_priority);
|
||||
next_scheduler->ScheduleThread(this, current_priority);
|
||||
next_scheduler.ScheduleThread(this, current_priority);
|
||||
|
||||
// Change thread's scheduler
|
||||
scheduler = next_scheduler;
|
||||
scheduler = &next_scheduler;
|
||||
|
||||
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
||||
system.CpuCore(processor_id).PrepareReschedule();
|
||||
}
|
||||
|
||||
bool Thread::AllWaitObjectsReady() {
|
||||
|
||||
@@ -258,6 +258,14 @@ public:
|
||||
return last_running_ticks;
|
||||
}
|
||||
|
||||
u64 GetTotalCPUTimeTicks() const {
|
||||
return total_cpu_time_ticks;
|
||||
}
|
||||
|
||||
void UpdateCPUTimeTicks(u64 ticks) {
|
||||
total_cpu_time_ticks += ticks;
|
||||
}
|
||||
|
||||
s32 GetProcessorID() const {
|
||||
return processor_id;
|
||||
}
|
||||
@@ -366,6 +374,8 @@ private:
|
||||
explicit Thread(KernelCore& kernel);
|
||||
~Thread() override;
|
||||
|
||||
void ChangeScheduler();
|
||||
|
||||
Core::ARM_Interface::ThreadContext context{};
|
||||
|
||||
u32 thread_id = 0;
|
||||
@@ -378,7 +388,8 @@ private:
|
||||
u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application
|
||||
u32 current_priority = 0; ///< Current thread priority, can be temporarily changed
|
||||
|
||||
u64 last_running_ticks = 0; ///< CPU tick when thread was last running
|
||||
u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
|
||||
u64 last_running_ticks = 0; ///< CPU tick when thread was last running
|
||||
|
||||
s32 processor_id = 0;
|
||||
|
||||
|
||||
@@ -143,6 +143,26 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
|
||||
return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
|
||||
}
|
||||
|
||||
ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
|
||||
// Find the first Free VMA.
|
||||
const VAddr base = GetASLRRegionBaseAddress();
|
||||
const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) {
|
||||
if (vma.second.type != VMAType::Free)
|
||||
return false;
|
||||
|
||||
const VAddr vma_end = vma.second.base + vma.second.size;
|
||||
return vma_end > base && vma_end >= base + size;
|
||||
});
|
||||
|
||||
if (vma_handle == vma_map.end()) {
|
||||
// TODO(Subv): Find the correct error code here.
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
const VAddr target = std::max(base, vma_handle->second.base);
|
||||
return MakeResult<VAddr>(target);
|
||||
}
|
||||
|
||||
ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
|
||||
MemoryState state,
|
||||
Memory::MemoryHookPointer mmio_handler) {
|
||||
@@ -223,6 +243,85 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
|
||||
if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
|
||||
target + size < target) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (heap_memory == nullptr) {
|
||||
// Initialize heap
|
||||
heap_memory = std::make_shared<std::vector<u8>>();
|
||||
heap_start = heap_end = target;
|
||||
} else {
|
||||
UnmapRange(heap_start, heap_end - heap_start);
|
||||
}
|
||||
|
||||
// If necessary, expand backing vector to cover new heap extents.
|
||||
if (target < heap_start) {
|
||||
heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
|
||||
heap_start = target;
|
||||
RefreshMemoryBlockMappings(heap_memory.get());
|
||||
}
|
||||
if (target + size > heap_end) {
|
||||
heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
|
||||
heap_end = target + size;
|
||||
RefreshMemoryBlockMappings(heap_memory.get());
|
||||
}
|
||||
ASSERT(heap_end - heap_start == heap_memory->size());
|
||||
|
||||
CASCADE_RESULT(auto vma, MapMemoryBlock(target, heap_memory, target - heap_start, size,
|
||||
MemoryState::Heap));
|
||||
Reprotect(vma, perms);
|
||||
|
||||
heap_used = size;
|
||||
|
||||
return MakeResult<VAddr>(heap_end - size);
|
||||
}
|
||||
|
||||
ResultCode VMManager::HeapFree(VAddr target, u64 size) {
|
||||
if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
|
||||
target + size < target) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
const ResultCode result = UnmapRange(target, size);
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
heap_used -= size;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
const auto vma = FindVMA(src_addr);
|
||||
|
||||
ASSERT_MSG(vma != vma_map.end(), "Invalid memory address");
|
||||
ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
|
||||
|
||||
// The returned VMA might be a bigger one encompassing the desired address.
|
||||
const auto vma_offset = src_addr - vma->first;
|
||||
ASSERT_MSG(vma_offset + size <= vma->second.size,
|
||||
"Shared memory exceeds bounds of mapped block");
|
||||
|
||||
const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
|
||||
const std::size_t backing_block_offset = vma->second.offset + vma_offset;
|
||||
|
||||
CASCADE_RESULT(auto new_vma, MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
|
||||
MemoryState::Mapped));
|
||||
// Protect mirror with permissions from old region
|
||||
Reprotect(new_vma, vma->second.permissions);
|
||||
// Remove permissions from old region
|
||||
Reprotect(vma, VMAPermission::None);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
|
||||
// If this ever proves to have a noticeable performance impact, allow users of the function to
|
||||
// specify a specific range of addresses to limit the scan to.
|
||||
@@ -475,8 +574,7 @@ u64 VMManager::GetTotalMemoryUsage() const {
|
||||
}
|
||||
|
||||
u64 VMManager::GetTotalHeapUsage() const {
|
||||
LOG_WARNING(Kernel, "(STUBBED) called");
|
||||
return 0x0;
|
||||
return heap_used;
|
||||
}
|
||||
|
||||
VAddr VMManager::GetAddressSpaceBaseAddress() const {
|
||||
|
||||
@@ -157,6 +157,14 @@ public:
|
||||
*/
|
||||
ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state);
|
||||
|
||||
/**
|
||||
* Finds the first free address that can hold a region of the desired size.
|
||||
*
|
||||
* @param size Size of the desired region.
|
||||
* @return The found free address.
|
||||
*/
|
||||
ResultVal<VAddr> FindFreeRegion(u64 size) const;
|
||||
|
||||
/**
|
||||
* Maps a memory-mapped IO region at a given address.
|
||||
*
|
||||
@@ -178,6 +186,11 @@ public:
|
||||
/// Changes the permissions of a range of addresses, splitting VMAs as necessary.
|
||||
ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms);
|
||||
|
||||
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
|
||||
ResultCode HeapFree(VAddr target, u64 size);
|
||||
|
||||
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
|
||||
|
||||
/**
|
||||
* Scans all VMAs and updates the page table range of any that use the given vector as backing
|
||||
* memory. This should be called after any operation that causes reallocation of the vector.
|
||||
@@ -335,5 +348,15 @@ private:
|
||||
|
||||
VAddr tls_io_region_base = 0;
|
||||
VAddr tls_io_region_end = 0;
|
||||
|
||||
// Memory used to back the allocations in the regular heap. A single vector is used to cover
|
||||
// the entire virtual address space extents that bound the allocations, including any holes.
|
||||
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
|
||||
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
|
||||
std::shared_ptr<std::vector<u8>> heap_memory;
|
||||
// The left/right bounds of the address space covered by heap_memory.
|
||||
VAddr heap_start = 0;
|
||||
VAddr heap_end = 0;
|
||||
u64 heap_used = 0;
|
||||
};
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
enum class ErrorDescription : u32 {
|
||||
Success = 0,
|
||||
RemoteProcessDead = 301,
|
||||
InvalidOffset = 6061,
|
||||
InvalidLength = 6062,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include "common/common_paths.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
@@ -16,6 +20,7 @@
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
// TODO: RE this structure
|
||||
struct UserData {
|
||||
INSERT_PADDING_WORDS(1);
|
||||
@@ -27,6 +32,29 @@ struct UserData {
|
||||
};
|
||||
static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
|
||||
|
||||
// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
|
||||
// used as a backup should the one on disk not exist
|
||||
constexpr u32 backup_jpeg_size = 107;
|
||||
constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{
|
||||
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
|
||||
0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
|
||||
0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
|
||||
0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
|
||||
0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
|
||||
0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
|
||||
0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
|
||||
}};
|
||||
|
||||
static std::string GetImagePath(UUID uuid) {
|
||||
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
|
||||
}
|
||||
|
||||
static constexpr u32 SanitizeJPEGSize(std::size_t size) {
|
||||
constexpr std::size_t max_jpeg_image_size = 0x20000;
|
||||
return static_cast<u32>(std::min(size, max_jpeg_image_size));
|
||||
}
|
||||
|
||||
class IProfile final : public ServiceFramework<IProfile> {
|
||||
public:
|
||||
explicit IProfile(UUID user_id, ProfileManager& profile_manager)
|
||||
@@ -73,32 +101,42 @@ private:
|
||||
}
|
||||
|
||||
void LoadImage(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
// smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
|
||||
// TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000
|
||||
constexpr u32 jpeg_size = 107;
|
||||
static constexpr std::array<u8, jpeg_size> jpeg{
|
||||
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03,
|
||||
0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a,
|
||||
0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10,
|
||||
0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00,
|
||||
0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01,
|
||||
0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
|
||||
};
|
||||
ctx.WriteBuffer(jpeg);
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(jpeg_size);
|
||||
|
||||
const FileUtil::IOFile image(GetImagePath(user_id), "rb");
|
||||
if (!image.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC,
|
||||
"Failed to load user provided image! Falling back to built-in backup...");
|
||||
ctx.WriteBuffer(backup_jpeg);
|
||||
rb.Push<u32>(backup_jpeg_size);
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 size = SanitizeJPEGSize(image.GetSize());
|
||||
std::vector<u8> buffer(size);
|
||||
image.ReadBytes(buffer.data(), buffer.size());
|
||||
|
||||
ctx.WriteBuffer(buffer.data(), buffer.size());
|
||||
rb.Push<u32>(size);
|
||||
}
|
||||
|
||||
void GetImageSize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
constexpr u32 jpeg_size = 107;
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(jpeg_size);
|
||||
|
||||
const FileUtil::IOFile image(GetImagePath(user_id), "rb");
|
||||
|
||||
if (!image.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC,
|
||||
"Failed to load user provided image! Falling back to built-in backup...");
|
||||
rb.Push<u32>(backup_jpeg_size);
|
||||
} else {
|
||||
rb.Push<u32>(SanitizeJPEGSize(image.GetSize()));
|
||||
}
|
||||
}
|
||||
|
||||
const ProfileManager& profile_manager;
|
||||
@@ -204,6 +242,30 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
}
|
||||
|
||||
void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
// A u8 is passed into this function which we can safely ignore. It's to determine if we have
|
||||
// access to use the network or not by the looks of it
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
if (profile_manager->GetUserCount() != 1) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u128>(INVALID_UUID);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto user_list = profile_manager->GetAllUsers();
|
||||
if (std::all_of(user_list.begin(), user_list.end(),
|
||||
[](const auto& user) { return user.uuid == INVALID_UUID; })) {
|
||||
rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code
|
||||
rb.PushRaw<u128>(INVALID_UUID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Select the first user we have
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u128>(profile_manager->GetUser(0)->uuid);
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)),
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
|
||||
void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);
|
||||
void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
|
||||
void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
|
||||
@@ -17,7 +17,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{5, &ACC_SU::GetProfile, "GetProfile"},
|
||||
{6, nullptr, "GetProfileDigest"},
|
||||
{50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
||||
{51, nullptr, "TrySelectUserWithoutInteraction"},
|
||||
{51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
||||
{60, nullptr, "ListOpenContextStoredUsers"},
|
||||
{100, nullptr, "GetUserRegistrationNotifier"},
|
||||
{101, nullptr, "GetUserStateChangeNotifier"},
|
||||
|
||||
@@ -17,7 +17,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{5, &ACC_U0::GetProfile, "GetProfile"},
|
||||
{6, nullptr, "GetProfileDigest"},
|
||||
{50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
||||
{51, nullptr, "TrySelectUserWithoutInteraction"},
|
||||
{51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
||||
{60, nullptr, "ListOpenContextStoredUsers"},
|
||||
{100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
|
||||
{101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
|
||||
|
||||
@@ -17,7 +17,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{5, &ACC_U1::GetProfile, "GetProfile"},
|
||||
{6, nullptr, "GetProfileDigest"},
|
||||
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
||||
{51, nullptr, "TrySelectUserWithoutInteraction"},
|
||||
{51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
||||
{60, nullptr, "ListOpenContextStoredUsers"},
|
||||
{100, nullptr, "GetUserRegistrationNotifier"},
|
||||
{101, nullptr, "GetUserStateChangeNotifier"},
|
||||
|
||||
@@ -2,42 +2,83 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <random>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
struct UserRaw {
|
||||
UUID uuid;
|
||||
UUID uuid2;
|
||||
u64 timestamp;
|
||||
ProfileUsername username;
|
||||
INSERT_PADDING_BYTES(0x80);
|
||||
};
|
||||
static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
|
||||
|
||||
struct ProfileDataRaw {
|
||||
INSERT_PADDING_BYTES(0x10);
|
||||
std::array<UserRaw, MAX_USERS> users;
|
||||
};
|
||||
static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
|
||||
|
||||
// TODO(ogniK): Get actual error codes
|
||||
constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1);
|
||||
constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2);
|
||||
constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
|
||||
|
||||
const UUID& UUID::Generate() {
|
||||
constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/";
|
||||
|
||||
UUID UUID::Generate() {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
|
||||
uuid[0] = distribution(gen);
|
||||
uuid[1] = distribution(gen);
|
||||
return *this;
|
||||
return UUID{distribution(gen), distribution(gen)};
|
||||
}
|
||||
|
||||
std::string UUID::Format() const {
|
||||
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
|
||||
}
|
||||
|
||||
std::string UUID::FormatSwitch() const {
|
||||
std::array<u8, 16> s{};
|
||||
std::memcpy(s.data(), uuid.data(), sizeof(u128));
|
||||
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
|
||||
":02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
|
||||
s[12], s[13], s[14], s[15]);
|
||||
}
|
||||
|
||||
ProfileManager::ProfileManager() {
|
||||
// TODO(ogniK): Create the default user we have for now until loading/saving users is added
|
||||
auto user_uuid = UUID{1, 0};
|
||||
ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess());
|
||||
OpenUser(user_uuid);
|
||||
ParseUserSaveFile();
|
||||
|
||||
if (user_count == 0)
|
||||
CreateNewUser(UUID::Generate(), "yuzu");
|
||||
|
||||
auto current = std::clamp<int>(Settings::values.current_user, 0, MAX_USERS - 1);
|
||||
if (UserExistsIndex(current))
|
||||
current = 0;
|
||||
|
||||
OpenUser(*GetUser(current));
|
||||
}
|
||||
|
||||
ProfileManager::~ProfileManager() = default;
|
||||
ProfileManager::~ProfileManager() {
|
||||
WriteUserSaveFile();
|
||||
}
|
||||
|
||||
/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
|
||||
/// internal management of the users profiles
|
||||
boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) {
|
||||
std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) {
|
||||
if (user_count >= MAX_USERS) {
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
profiles[user_count] = user;
|
||||
profiles[user_count] = profile;
|
||||
return user_count++;
|
||||
}
|
||||
|
||||
@@ -56,7 +97,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) {
|
||||
|
||||
/// Helper function to register a user to the system
|
||||
ResultCode ProfileManager::AddUser(const ProfileInfo& user) {
|
||||
if (AddToProfiles(user) == boost::none) {
|
||||
if (!AddToProfiles(user)) {
|
||||
return ERROR_TOO_MANY_USERS;
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
@@ -101,31 +142,40 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username)
|
||||
return CreateNewUser(uuid, username_output);
|
||||
}
|
||||
|
||||
std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
|
||||
if (index >= MAX_USERS) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return profiles[index].user_uuid;
|
||||
}
|
||||
|
||||
/// Returns a users profile index based on their user id.
|
||||
boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
|
||||
std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
|
||||
if (!uuid) {
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
auto iter = std::find_if(profiles.begin(), profiles.end(),
|
||||
[&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
|
||||
|
||||
const auto iter = std::find_if(profiles.begin(), profiles.end(),
|
||||
[&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
|
||||
if (iter == profiles.end()) {
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
|
||||
return static_cast<std::size_t>(std::distance(profiles.begin(), iter));
|
||||
}
|
||||
|
||||
/// Returns a users profile index based on their profile
|
||||
boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const {
|
||||
std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const {
|
||||
return GetUserIndex(user.user_uuid);
|
||||
}
|
||||
|
||||
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
|
||||
bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index,
|
||||
ProfileBase& profile) const {
|
||||
if (index == boost::none || index >= MAX_USERS) {
|
||||
bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const {
|
||||
if (!index || index >= MAX_USERS) {
|
||||
return false;
|
||||
}
|
||||
const auto& prof_info = profiles[index.get()];
|
||||
const auto& prof_info = profiles[*index];
|
||||
profile.user_uuid = prof_info.user_uuid;
|
||||
profile.username = prof_info.username;
|
||||
profile.timestamp = prof_info.creation_time;
|
||||
@@ -134,7 +184,7 @@ bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index,
|
||||
|
||||
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
|
||||
bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const {
|
||||
auto idx = GetUserIndex(uuid);
|
||||
const auto idx = GetUserIndex(uuid);
|
||||
return GetProfileBase(idx, profile);
|
||||
}
|
||||
|
||||
@@ -161,26 +211,34 @@ std::size_t ProfileManager::GetOpenUserCount() const {
|
||||
|
||||
/// Checks if a user id exists in our profile manager
|
||||
bool ProfileManager::UserExists(UUID uuid) const {
|
||||
return (GetUserIndex(uuid) != boost::none);
|
||||
return GetUserIndex(uuid).has_value();
|
||||
}
|
||||
|
||||
bool ProfileManager::UserExistsIndex(std::size_t index) const {
|
||||
if (index >= MAX_USERS)
|
||||
return false;
|
||||
return profiles[index].user_uuid.uuid != INVALID_UUID;
|
||||
}
|
||||
|
||||
/// Opens a specific user
|
||||
void ProfileManager::OpenUser(UUID uuid) {
|
||||
auto idx = GetUserIndex(uuid);
|
||||
if (idx == boost::none) {
|
||||
const auto idx = GetUserIndex(uuid);
|
||||
if (!idx) {
|
||||
return;
|
||||
}
|
||||
profiles[idx.get()].is_open = true;
|
||||
|
||||
profiles[*idx].is_open = true;
|
||||
last_opened_user = uuid;
|
||||
}
|
||||
|
||||
/// Closes a specific user
|
||||
void ProfileManager::CloseUser(UUID uuid) {
|
||||
auto idx = GetUserIndex(uuid);
|
||||
if (idx == boost::none) {
|
||||
const auto idx = GetUserIndex(uuid);
|
||||
if (!idx) {
|
||||
return;
|
||||
}
|
||||
profiles[idx.get()].is_open = false;
|
||||
|
||||
profiles[*idx].is_open = false;
|
||||
}
|
||||
|
||||
/// Gets all valid user ids on the system
|
||||
@@ -210,10 +268,10 @@ UUID ProfileManager::GetLastOpenedUser() const {
|
||||
}
|
||||
|
||||
/// Return the users profile base and the unknown arbitary data.
|
||||
bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile,
|
||||
bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
|
||||
ProfileData& data) const {
|
||||
if (GetProfileBase(index, profile)) {
|
||||
data = profiles[index.get()].data;
|
||||
data = profiles[*index].data;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -222,7 +280,7 @@ bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, P
|
||||
/// Return the users profile base and the unknown arbitary data.
|
||||
bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
|
||||
ProfileData& data) const {
|
||||
auto idx = GetUserIndex(uuid);
|
||||
const auto idx = GetUserIndex(uuid);
|
||||
return GetProfileBaseAndData(idx, profile, data);
|
||||
}
|
||||
|
||||
@@ -239,4 +297,97 @@ bool ProfileManager::CanSystemRegisterUser() const {
|
||||
// emulate qlaunch. Update this to dynamically change.
|
||||
}
|
||||
|
||||
bool ProfileManager::RemoveUser(UUID uuid) {
|
||||
const auto index = GetUserIndex(uuid);
|
||||
if (!index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
profiles[*index] = ProfileInfo{};
|
||||
std::stable_partition(profiles.begin(), profiles.end(),
|
||||
[](const ProfileInfo& profile) { return profile.user_uuid; });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
|
||||
const auto index = GetUserIndex(uuid);
|
||||
if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& profile = profiles[*index];
|
||||
profile.user_uuid = profile_new.user_uuid;
|
||||
profile.username = profile_new.username;
|
||||
profile.creation_time = profile_new.timestamp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProfileManager::ParseUserSaveFile() {
|
||||
FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat",
|
||||
"rb");
|
||||
|
||||
if (!save.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
|
||||
"user 'yuzu' with random UUID.");
|
||||
return;
|
||||
}
|
||||
|
||||
ProfileDataRaw data;
|
||||
if (save.ReadBytes(&data, sizeof(ProfileDataRaw)) != sizeof(ProfileDataRaw)) {
|
||||
LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user "
|
||||
"'yuzu' with random UUID.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& user : data.users) {
|
||||
if (user.uuid == UUID(INVALID_UUID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AddUser({user.uuid, user.username, user.timestamp, {}, false});
|
||||
}
|
||||
|
||||
std::stable_partition(profiles.begin(), profiles.end(),
|
||||
[](const ProfileInfo& profile) { return profile.user_uuid; });
|
||||
}
|
||||
|
||||
void ProfileManager::WriteUserSaveFile() {
|
||||
ProfileDataRaw raw{};
|
||||
|
||||
for (std::size_t i = 0; i < MAX_USERS; ++i) {
|
||||
raw.users[i].username = profiles[i].username;
|
||||
raw.users[i].uuid2 = profiles[i].user_uuid;
|
||||
raw.users[i].uuid = profiles[i].user_uuid;
|
||||
raw.users[i].timestamp = profiles[i].creation_time;
|
||||
}
|
||||
|
||||
const auto raw_path =
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010";
|
||||
if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
|
||||
FileUtil::Delete(raw_path);
|
||||
|
||||
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
|
||||
|
||||
if (!FileUtil::CreateFullPath(path)) {
|
||||
LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory "
|
||||
"nand/system/save/8000000000000010/su/avators to mitigate this "
|
||||
"issue.");
|
||||
return;
|
||||
}
|
||||
|
||||
FileUtil::IOFile save(path, "wb");
|
||||
|
||||
if (!save.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
|
||||
"made in current session will be saved.");
|
||||
return;
|
||||
}
|
||||
|
||||
save.Resize(sizeof(ProfileDataRaw));
|
||||
save.WriteBytes(&raw, sizeof(ProfileDataRaw));
|
||||
}
|
||||
|
||||
}; // namespace Service::Account
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
#include "boost/optional.hpp"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -36,19 +36,20 @@ struct UUID {
|
||||
}
|
||||
|
||||
// TODO(ogniK): Properly generate uuids based on RFC-4122
|
||||
const UUID& Generate();
|
||||
static UUID Generate();
|
||||
|
||||
// Set the UUID to {0,0} to be considered an invalid user
|
||||
void Invalidate() {
|
||||
uuid = INVALID_UUID;
|
||||
}
|
||||
std::string Format() const {
|
||||
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
|
||||
}
|
||||
|
||||
std::string Format() const;
|
||||
std::string FormatSwitch() const;
|
||||
};
|
||||
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
|
||||
|
||||
using ProfileUsername = std::array<u8, 0x20>;
|
||||
constexpr std::size_t profile_username_size = 32;
|
||||
using ProfileUsername = std::array<u8, profile_username_size>;
|
||||
using ProfileData = std::array<u8, MAX_DATA>;
|
||||
using UserIDArray = std::array<UUID, MAX_USERS>;
|
||||
|
||||
@@ -81,18 +82,19 @@ static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size");
|
||||
/// objects
|
||||
class ProfileManager {
|
||||
public:
|
||||
ProfileManager(); // TODO(ogniK): Load from system save
|
||||
ProfileManager();
|
||||
~ProfileManager();
|
||||
|
||||
ResultCode AddUser(const ProfileInfo& user);
|
||||
ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username);
|
||||
ResultCode CreateNewUser(UUID uuid, const std::string& username);
|
||||
boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
|
||||
boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
|
||||
bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const;
|
||||
std::optional<UUID> GetUser(std::size_t index) const;
|
||||
std::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
|
||||
std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
|
||||
bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const;
|
||||
bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
|
||||
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
|
||||
bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile,
|
||||
bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
|
||||
ProfileData& data) const;
|
||||
bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const;
|
||||
bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
|
||||
@@ -100,6 +102,7 @@ public:
|
||||
std::size_t GetUserCount() const;
|
||||
std::size_t GetOpenUserCount() const;
|
||||
bool UserExists(UUID uuid) const;
|
||||
bool UserExistsIndex(std::size_t index) const;
|
||||
void OpenUser(UUID uuid);
|
||||
void CloseUser(UUID uuid);
|
||||
UserIDArray GetOpenUsers() const;
|
||||
@@ -108,11 +111,17 @@ public:
|
||||
|
||||
bool CanSystemRegisterUser() const;
|
||||
|
||||
bool RemoveUser(UUID uuid);
|
||||
bool SetProfileBase(UUID uuid, const ProfileBase& profile_new);
|
||||
|
||||
private:
|
||||
void ParseUserSaveFile();
|
||||
void WriteUserSaveFile();
|
||||
std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
|
||||
bool RemoveProfileAtIndex(std::size_t index);
|
||||
|
||||
std::array<ProfileInfo, MAX_USERS> profiles{};
|
||||
std::size_t user_count = 0;
|
||||
boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
|
||||
bool RemoveProfileAtIndex(std::size_t index);
|
||||
UUID last_opened_user{INVALID_UUID};
|
||||
};
|
||||
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <stack>
|
||||
#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/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
#include "core/hle/service/am/applet_oe.h"
|
||||
@@ -26,6 +28,16 @@
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
|
||||
|
||||
struct LaunchParameters {
|
||||
u32_le magic;
|
||||
u32_le is_account_selected;
|
||||
u128 current_user;
|
||||
INSERT_PADDING_BYTES(0x70);
|
||||
};
|
||||
static_assert(sizeof(LaunchParameters) == 0x88);
|
||||
|
||||
IWindowController::IWindowController() : ServiceFramework("IWindowController") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
@@ -326,7 +338,54 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") {
|
||||
AppletMessageQueue::AppletMessageQueue() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
on_new_message = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky,
|
||||
"AMMessageQueue:OnMessageRecieved");
|
||||
on_operation_mode_changed = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"AMMessageQueue:OperationModeChanged");
|
||||
}
|
||||
|
||||
AppletMessageQueue::~AppletMessageQueue() = default;
|
||||
|
||||
const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetMesssageRecieveEvent() const {
|
||||
return on_new_message;
|
||||
}
|
||||
|
||||
const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetOperationModeChangedEvent() const {
|
||||
return on_operation_mode_changed;
|
||||
}
|
||||
|
||||
void AppletMessageQueue::PushMessage(AppletMessage msg) {
|
||||
messages.push(msg);
|
||||
on_new_message->Signal();
|
||||
}
|
||||
|
||||
AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
|
||||
if (messages.empty()) {
|
||||
on_new_message->Clear();
|
||||
return AppletMessage::NoMessage;
|
||||
}
|
||||
auto msg = messages.front();
|
||||
messages.pop();
|
||||
if (messages.empty()) {
|
||||
on_new_message->Clear();
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::size_t AppletMessageQueue::GetMessageCount() const {
|
||||
return messages.size();
|
||||
}
|
||||
|
||||
void AppletMessageQueue::OperationModeChanged() {
|
||||
PushMessage(AppletMessage::OperationModeChanged);
|
||||
PushMessage(AppletMessage::PerformanceModeChanged);
|
||||
on_operation_mode_changed->Signal();
|
||||
}
|
||||
|
||||
ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("ICommonStateGetter"), msg_queue(std::move(msg_queue)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
|
||||
@@ -376,21 +435,19 @@ void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
|
||||
event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(event);
|
||||
rb.PushCopyObjects(msg_queue->GetMesssageRecieveEvent());
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(15);
|
||||
rb.PushEnum<AppletMessageQueue::AppletMessage>(msg_queue->PopMessage());
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
|
||||
@@ -402,13 +459,11 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) {
|
||||
event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(event);
|
||||
rb.PushCopyObjects(msg_queue->GetOperationModeChangedEvent());
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) {
|
||||
@@ -432,7 +487,7 @@ void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld));
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -442,7 +497,7 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked
|
||||
: APM::PerformanceMode::Handheld));
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
|
||||
@@ -724,20 +779,23 @@ void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx
|
||||
}
|
||||
|
||||
void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
|
||||
constexpr std::array<u8, 0x88> data{{
|
||||
0xca, 0x97, 0x94, 0xc7, // Magic
|
||||
1, 0, 0, 0, // IsAccountSelected (bool)
|
||||
1, 0, 0, 0, // User Id (word 0)
|
||||
0, 0, 0, 0, // User Id (word 1)
|
||||
0, 0, 0, 0, // User Id (word 2)
|
||||
0, 0, 0, 0 // User Id (word 3)
|
||||
}};
|
||||
LaunchParameters params{};
|
||||
|
||||
std::vector<u8> buffer(data.begin(), data.end());
|
||||
params.magic = POP_LAUNCH_PARAMETER_MAGIC;
|
||||
params.is_account_selected = 1;
|
||||
|
||||
Account::ProfileManager profile_manager{};
|
||||
const auto uuid = profile_manager.GetUser(Settings::values.current_user);
|
||||
ASSERT(uuid);
|
||||
params.current_user = uuid->uuid;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
std::vector<u8> buffer(sizeof(LaunchParameters));
|
||||
std::memcpy(buffer.data(), ¶ms, buffer.size());
|
||||
|
||||
rb.PushIpcInterface<AM::IStorage>(buffer);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
@@ -825,8 +883,12 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
|
||||
std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager);
|
||||
std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager);
|
||||
auto message_queue = std::make_shared<AppletMessageQueue>();
|
||||
message_queue->PushMessage(
|
||||
AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on game boot
|
||||
|
||||
std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager);
|
||||
std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager);
|
||||
std::make_shared<IdleSys>()->InstallAsService(service_manager);
|
||||
std::make_shared<OMM>()->InstallAsService(service_manager);
|
||||
std::make_shared<SPSM>()->InstallAsService(service_manager);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -39,6 +40,31 @@ enum SystemLanguage {
|
||||
TraditionalChinese = 16,
|
||||
};
|
||||
|
||||
class AppletMessageQueue {
|
||||
public:
|
||||
enum class AppletMessage : u32 {
|
||||
NoMessage = 0,
|
||||
FocusStateChanged = 15,
|
||||
OperationModeChanged = 30,
|
||||
PerformanceModeChanged = 31,
|
||||
};
|
||||
|
||||
AppletMessageQueue();
|
||||
~AppletMessageQueue();
|
||||
|
||||
const Kernel::SharedPtr<Kernel::Event>& GetMesssageRecieveEvent() const;
|
||||
const Kernel::SharedPtr<Kernel::Event>& GetOperationModeChangedEvent() const;
|
||||
void PushMessage(AppletMessage msg);
|
||||
AppletMessage PopMessage();
|
||||
std::size_t GetMessageCount() const;
|
||||
void OperationModeChanged();
|
||||
|
||||
private:
|
||||
std::queue<AppletMessage> messages;
|
||||
Kernel::SharedPtr<Kernel::Event> on_new_message;
|
||||
Kernel::SharedPtr<Kernel::Event> on_operation_mode_changed;
|
||||
};
|
||||
|
||||
class IWindowController final : public ServiceFramework<IWindowController> {
|
||||
public:
|
||||
IWindowController();
|
||||
@@ -102,7 +128,7 @@ private:
|
||||
|
||||
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
|
||||
public:
|
||||
ICommonStateGetter();
|
||||
explicit ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue);
|
||||
~ICommonStateGetter() override;
|
||||
|
||||
private:
|
||||
@@ -126,6 +152,7 @@ private:
|
||||
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> event;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
|
||||
|
||||
@@ -12,8 +12,10 @@ namespace Service::AM {
|
||||
|
||||
class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
|
||||
public:
|
||||
explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)) {
|
||||
explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
{1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"},
|
||||
@@ -32,7 +34,7 @@ private:
|
||||
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ICommonStateGetter>();
|
||||
rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
@@ -93,12 +95,15 @@ private:
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
|
||||
public:
|
||||
explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)) {
|
||||
explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
{1, &ISystemAppletProxy::GetSelfController, "GetSelfController"},
|
||||
@@ -119,7 +124,7 @@ private:
|
||||
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ICommonStateGetter>();
|
||||
rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
@@ -186,31 +191,34 @@ private:
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISystemAppletProxy>(nvflinger);
|
||||
rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) {
|
||||
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
|
||||
@@ -228,4 +236,8 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
|
||||
AppletAE::~AppletAE() = default;
|
||||
|
||||
const std::shared_ptr<AppletMessageQueue>& AppletAE::GetMessageQueue() const {
|
||||
return msg_queue;
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -17,15 +17,19 @@ namespace AM {
|
||||
|
||||
class AppletAE final : public ServiceFramework<AppletAE> {
|
||||
public:
|
||||
explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
|
||||
explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue);
|
||||
~AppletAE() override;
|
||||
|
||||
const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
|
||||
|
||||
private:
|
||||
void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx);
|
||||
void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx);
|
||||
void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
} // namespace AM
|
||||
|
||||
@@ -12,8 +12,10 @@ namespace Service::AM {
|
||||
|
||||
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
|
||||
public:
|
||||
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) {
|
||||
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
@@ -70,7 +72,7 @@ private:
|
||||
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ICommonStateGetter>();
|
||||
rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
@@ -89,17 +91,20 @@ private:
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationProxy>(nvflinger);
|
||||
rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)) {
|
||||
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
|
||||
};
|
||||
@@ -108,4 +113,8 @@ AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
|
||||
AppletOE::~AppletOE() = default;
|
||||
|
||||
const std::shared_ptr<AppletMessageQueue>& AppletOE::GetMessageQueue() const {
|
||||
return msg_queue;
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -17,13 +17,17 @@ namespace AM {
|
||||
|
||||
class AppletOE final : public ServiceFramework<AppletOE> {
|
||||
public:
|
||||
explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
|
||||
explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue);
|
||||
~AppletOE() override;
|
||||
|
||||
const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
|
||||
|
||||
private:
|
||||
void OpenApplicationProxy(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
} // namespace AM
|
||||
|
||||
@@ -24,8 +24,8 @@ namespace Service::AOC {
|
||||
constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
|
||||
constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000;
|
||||
|
||||
static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) {
|
||||
return (aoc & DLC_BASE_TITLE_ID_MASK) == base;
|
||||
static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
|
||||
return (title_id & DLC_BASE_TITLE_ID_MASK) == base;
|
||||
}
|
||||
|
||||
static std::vector<u64> AccumulateAOCTitleIDs() {
|
||||
@@ -74,7 +74,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
|
||||
rb.Push<u32>(static_cast<u32>(
|
||||
std::count_if(add_on_content.begin(), add_on_content.end(),
|
||||
[¤t](u64 tid) { return (tid & DLC_BASE_TITLE_ID_MASK) == current; })));
|
||||
[current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); })));
|
||||
}
|
||||
|
||||
void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -28,13 +28,13 @@ public:
|
||||
{1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
|
||||
{2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
|
||||
{3, &IAudioRenderer::GetState, "GetState"},
|
||||
{4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
|
||||
{4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"},
|
||||
{5, &IAudioRenderer::Start, "Start"},
|
||||
{6, &IAudioRenderer::Stop, "Stop"},
|
||||
{7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
|
||||
{8, nullptr, "SetRenderingTimeLimit"},
|
||||
{9, nullptr, "GetRenderingTimeLimit"},
|
||||
{10, nullptr, "RequestUpdateAuto"},
|
||||
{8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
|
||||
{9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
|
||||
{10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"},
|
||||
{11, nullptr, "ExecuteAudioRendererRendering"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -79,7 +79,7 @@ private:
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
}
|
||||
|
||||
void RequestUpdate(Kernel::HLERequestContext& ctx) {
|
||||
void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
|
||||
ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -110,8 +110,29 @@ private:
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
rendering_time_limit_percent = rp.Pop<u32>();
|
||||
ASSERT(rendering_time_limit_percent >= 0 && rendering_time_limit_percent <= 100);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}",
|
||||
rendering_time_limit_percent);
|
||||
}
|
||||
|
||||
void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(rendering_time_limit_percent);
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> system_event;
|
||||
std::unique_ptr<AudioCore::AudioRenderer> renderer;
|
||||
u32 rendering_time_limit_percent = 100;
|
||||
};
|
||||
|
||||
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
|
||||
|
||||
@@ -161,7 +161,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
|
||||
ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
|
||||
|
||||
std::size_t worker_sz = WorkerBufferSize(channel_count);
|
||||
ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large");
|
||||
ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
|
||||
std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
|
||||
static_cast<OpusDecoder*>(operator new(worker_sz))};
|
||||
if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
|
||||
|
||||
@@ -303,17 +303,27 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
static_cast<u8>(space), save_struct.DebugInfo());
|
||||
|
||||
if (save_data_factory == nullptr) {
|
||||
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound);
|
||||
return FileSys::ERROR_ENTITY_NOT_FOUND;
|
||||
}
|
||||
|
||||
return save_data_factory->Open(space, save_struct);
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) {
|
||||
LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
|
||||
|
||||
if (save_data_factory == nullptr) {
|
||||
return FileSys::ERROR_ENTITY_NOT_FOUND;
|
||||
}
|
||||
|
||||
return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualDir> OpenSDMC() {
|
||||
LOG_TRACE(Service_FS, "Opening SDMC");
|
||||
|
||||
if (sdmc_factory == nullptr) {
|
||||
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SdCardNotFound);
|
||||
return FileSys::ERROR_SD_CARD_NOT_FOUND;
|
||||
}
|
||||
|
||||
return sdmc_factory->Open();
|
||||
@@ -360,6 +370,15 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
|
||||
return bis_factory->GetModificationLoadRoot(title_id);
|
||||
}
|
||||
|
||||
FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
|
||||
LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
|
||||
|
||||
if (bis_factory == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return bis_factory->GetModificationDumpRoot(title_id);
|
||||
}
|
||||
|
||||
void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
|
||||
if (overwrite) {
|
||||
bis_factory = nullptr;
|
||||
@@ -373,13 +392,21 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
|
||||
FileSys::Mode::ReadWrite);
|
||||
auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
|
||||
FileSys::Mode::ReadWrite);
|
||||
auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
|
||||
FileSys::Mode::ReadWrite);
|
||||
|
||||
if (bis_factory == nullptr)
|
||||
bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory);
|
||||
if (save_data_factory == nullptr)
|
||||
if (bis_factory == nullptr) {
|
||||
bis_factory =
|
||||
std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
|
||||
}
|
||||
|
||||
if (save_data_factory == nullptr) {
|
||||
save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
|
||||
if (sdmc_factory == nullptr)
|
||||
}
|
||||
|
||||
if (sdmc_factory == nullptr) {
|
||||
sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
|
||||
}
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) {
|
||||
|
||||
@@ -45,6 +45,7 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora
|
||||
FileSys::ContentRecordType type);
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
FileSys::SaveDataDescriptor save_struct);
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
|
||||
ResultVal<FileSys::VirtualDir> OpenSDMC();
|
||||
|
||||
std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
|
||||
@@ -54,6 +55,7 @@ FileSys::RegisteredCache* GetUserNANDContents();
|
||||
FileSys::RegisteredCache* GetSDMCContents();
|
||||
|
||||
FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
|
||||
FileSys::VirtualDir GetModificationDumpRoot(u64 title_id);
|
||||
|
||||
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
|
||||
// above is called.
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/file_sys/directory.h"
|
||||
@@ -62,12 +63,12 @@ private:
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
|
||||
rb.Push(FileSys::ERROR_INVALID_SIZE);
|
||||
return;
|
||||
}
|
||||
if (offset < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
|
||||
rb.Push(FileSys::ERROR_INVALID_OFFSET);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -107,12 +108,12 @@ private:
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
|
||||
rb.Push(FileSys::ERROR_INVALID_SIZE);
|
||||
return;
|
||||
}
|
||||
if (offset < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
|
||||
rb.Push(FileSys::ERROR_INVALID_OFFSET);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -138,12 +139,12 @@ private:
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
|
||||
rb.Push(FileSys::ERROR_INVALID_SIZE);
|
||||
return;
|
||||
}
|
||||
if (offset < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
|
||||
rb.Push(FileSys::ERROR_INVALID_OFFSET);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -273,8 +274,8 @@ public:
|
||||
{0, &IFileSystem::CreateFile, "CreateFile"},
|
||||
{1, &IFileSystem::DeleteFile, "DeleteFile"},
|
||||
{2, &IFileSystem::CreateDirectory, "CreateDirectory"},
|
||||
{3, nullptr, "DeleteDirectory"},
|
||||
{4, nullptr, "DeleteDirectoryRecursively"},
|
||||
{3, &IFileSystem::DeleteDirectory, "DeleteDirectory"},
|
||||
{4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"},
|
||||
{5, &IFileSystem::RenameFile, "RenameFile"},
|
||||
{6, nullptr, "RenameDirectory"},
|
||||
{7, &IFileSystem::GetEntryType, "GetEntryType"},
|
||||
@@ -329,6 +330,30 @@ public:
|
||||
rb.Push(backend.CreateDirectory(name));
|
||||
}
|
||||
|
||||
void DeleteDirectory(Kernel::HLERequestContext& ctx) {
|
||||
const IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called directory {}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.DeleteDirectory(name));
|
||||
}
|
||||
|
||||
void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) {
|
||||
const IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called directory {}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.DeleteDirectoryRecursively(name));
|
||||
}
|
||||
|
||||
void RenameFile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
@@ -427,7 +452,147 @@ private:
|
||||
VfsDirectoryServiceWrapper backend;
|
||||
};
|
||||
|
||||
class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
|
||||
public:
|
||||
explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space)
|
||||
: ServiceFramework("ISaveDataInfoReader") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
FindAllSaves(space);
|
||||
}
|
||||
|
||||
void ReadSaveDataInfo(Kernel::HLERequestContext& ctx) {
|
||||
// Calculate how many entries we can fit in the output buffer
|
||||
const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo);
|
||||
|
||||
// Cap at total number of entries.
|
||||
const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index);
|
||||
|
||||
// Determine data start and end
|
||||
const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index);
|
||||
const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries);
|
||||
const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
|
||||
|
||||
next_entry_index += actual_entries;
|
||||
|
||||
// Write the data to memory
|
||||
ctx.WriteBuffer(begin, range_size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(static_cast<u32>(actual_entries));
|
||||
}
|
||||
|
||||
private:
|
||||
static u64 stoull_be(std::string_view str) {
|
||||
if (str.size() != 16)
|
||||
return 0;
|
||||
|
||||
const auto bytes = Common::HexStringToArray<0x8>(str);
|
||||
u64 out{};
|
||||
std::memcpy(&out, bytes.data(), sizeof(u64));
|
||||
|
||||
return Common::swap64(out);
|
||||
}
|
||||
|
||||
void FindAllSaves(FileSys::SaveDataSpaceId space) {
|
||||
const auto save_root = OpenSaveDataSpace(space);
|
||||
ASSERT(save_root.Succeeded());
|
||||
|
||||
for (const auto& type : (*save_root)->GetSubdirectories()) {
|
||||
if (type->GetName() == "save") {
|
||||
for (const auto& save_id : type->GetSubdirectories()) {
|
||||
for (const auto& user_id : save_id->GetSubdirectories()) {
|
||||
const auto save_id_numeric = stoull_be(save_id->GetName());
|
||||
auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());
|
||||
std::reverse(user_id_numeric.begin(), user_id_numeric.end());
|
||||
|
||||
if (save_id_numeric != 0) {
|
||||
// System Save Data
|
||||
info.emplace_back(SaveDataInfo{
|
||||
0,
|
||||
space,
|
||||
FileSys::SaveDataType::SystemSaveData,
|
||||
{},
|
||||
user_id_numeric,
|
||||
save_id_numeric,
|
||||
0,
|
||||
user_id->GetSize(),
|
||||
{},
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& title_id : user_id->GetSubdirectories()) {
|
||||
const auto device =
|
||||
std::all_of(user_id_numeric.begin(), user_id_numeric.end(),
|
||||
[](u8 val) { return val == 0; });
|
||||
info.emplace_back(SaveDataInfo{
|
||||
0,
|
||||
space,
|
||||
device ? FileSys::SaveDataType::DeviceSaveData
|
||||
: FileSys::SaveDataType::SaveData,
|
||||
{},
|
||||
user_id_numeric,
|
||||
save_id_numeric,
|
||||
stoull_be(title_id->GetName()),
|
||||
title_id->GetSize(),
|
||||
{},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {
|
||||
// Temporary Storage
|
||||
for (const auto& user_id : type->GetSubdirectories()) {
|
||||
for (const auto& title_id : user_id->GetSubdirectories()) {
|
||||
if (!title_id->GetFiles().empty() ||
|
||||
!title_id->GetSubdirectories().empty()) {
|
||||
auto user_id_numeric =
|
||||
Common::HexStringToArray<0x10>(user_id->GetName());
|
||||
std::reverse(user_id_numeric.begin(), user_id_numeric.end());
|
||||
|
||||
info.emplace_back(SaveDataInfo{
|
||||
0,
|
||||
space,
|
||||
FileSys::SaveDataType::TemporaryStorage,
|
||||
{},
|
||||
user_id_numeric,
|
||||
stoull_be(type->GetName()),
|
||||
stoull_be(title_id->GetName()),
|
||||
title_id->GetSize(),
|
||||
{},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SaveDataInfo {
|
||||
u64_le save_id_unknown;
|
||||
FileSys::SaveDataSpaceId space;
|
||||
FileSys::SaveDataType type;
|
||||
INSERT_PADDING_BYTES(0x6);
|
||||
std::array<u8, 0x10> user_id;
|
||||
u64_le save_id;
|
||||
u64_le title_id;
|
||||
u64_le save_image_size;
|
||||
INSERT_PADDING_BYTES(0x28);
|
||||
};
|
||||
static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
|
||||
|
||||
std::vector<SaveDataInfo> info;
|
||||
u64 next_entry_index = 0;
|
||||
};
|
||||
|
||||
FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "MountContent"},
|
||||
{1, &FSP_SRV::Initialize, "Initialize"},
|
||||
@@ -461,7 +626,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
{58, nullptr, "ReadSaveDataFileSystemExtraData"},
|
||||
{59, nullptr, "WriteSaveDataFileSystemExtraData"},
|
||||
{60, nullptr, "OpenSaveDataInfoReader"},
|
||||
{61, nullptr, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
|
||||
{61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
|
||||
{62, nullptr, "OpenCacheStorageList"},
|
||||
{64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
|
||||
{65, nullptr, "UpdateSaveDataMacForDebug"},
|
||||
@@ -520,6 +685,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
{1009, nullptr, "GetAndClearMemoryReportInfo"},
|
||||
{1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -578,7 +744,7 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
if (dir.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
|
||||
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
|
||||
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -594,6 +760,15 @@ void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
MountSaveData(ctx);
|
||||
}
|
||||
|
||||
void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space));
|
||||
}
|
||||
|
||||
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
@@ -661,7 +836,7 @@ void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
|
||||
static_cast<u8>(storage_id), title_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
|
||||
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
|
||||
}
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
||||
@@ -25,6 +25,7 @@ private:
|
||||
void CreateSaveData(Kernel::HLERequestContext& ctx);
|
||||
void MountSaveData(Kernel::HLERequestContext& ctx);
|
||||
void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
|
||||
void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx);
|
||||
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
|
||||
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
|
||||
void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -40,6 +40,29 @@ enum class JoystickId : std::size_t {
|
||||
Joystick_Right,
|
||||
};
|
||||
|
||||
static std::size_t NPadIdToIndex(u32 npad_id) {
|
||||
switch (npad_id) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
return npad_id;
|
||||
case 8:
|
||||
case NPAD_HANDHELD:
|
||||
return 8;
|
||||
case 9:
|
||||
case NPAD_UNKNOWN:
|
||||
return 9;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Controller_NPad::Controller_NPad() = default;
|
||||
Controller_NPad::~Controller_NPad() = default;
|
||||
|
||||
@@ -108,9 +131,10 @@ void Controller_NPad::OnInit() {
|
||||
styleset_changed_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
|
||||
|
||||
if (!IsControllerActivated())
|
||||
if (!IsControllerActivated()) {
|
||||
return;
|
||||
std::size_t controller{};
|
||||
}
|
||||
|
||||
if (style.raw == 0) {
|
||||
// We want to support all controllers
|
||||
style.handheld.Assign(1);
|
||||
@@ -287,10 +311,11 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
switch (controller_type) {
|
||||
case NPadControllerType::Handheld:
|
||||
handheld_entry.connection_status.raw = 0;
|
||||
handheld_entry.connection_status.IsConnected.Assign(1);
|
||||
if (!Settings::values.use_docked_mode) {
|
||||
handheld_entry.connection_status.IsWired.Assign(1);
|
||||
}
|
||||
handheld_entry.connection_status.IsWired.Assign(1);
|
||||
handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
|
||||
handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
|
||||
handheld_entry.connection_status.IsLeftJoyWired.Assign(1);
|
||||
handheld_entry.connection_status.IsRightJoyWired.Assign(1);
|
||||
handheld_entry.pad_states.raw = pad_state.raw;
|
||||
handheld_entry.l_stick = lstick_entry;
|
||||
handheld_entry.r_stick = rstick_entry;
|
||||
@@ -309,6 +334,7 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
dual_entry.pad_states.raw = pad_state.raw;
|
||||
dual_entry.l_stick = lstick_entry;
|
||||
dual_entry.r_stick = rstick_entry;
|
||||
break;
|
||||
case NPadControllerType::JoyLeft:
|
||||
left_entry.connection_status.raw = 0;
|
||||
|
||||
@@ -369,16 +395,30 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
|
||||
supported_npad_id_types.clear();
|
||||
supported_npad_id_types.resize(length / sizeof(u32));
|
||||
std::memcpy(supported_npad_id_types.data(), data, length);
|
||||
bool had_controller_update = false;
|
||||
for (std::size_t i = 0; i < connected_controllers.size(); i++) {
|
||||
auto& controller = connected_controllers[i];
|
||||
if (!controller.is_connected) {
|
||||
continue;
|
||||
}
|
||||
if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
|
||||
controller.type = DecideBestController(PREFERRED_CONTROLLER);
|
||||
InitNewlyAddedControler(i);
|
||||
const auto best_type = DecideBestController(PREFERRED_CONTROLLER);
|
||||
const bool is_handheld = (best_type == NPadControllerType::Handheld ||
|
||||
PREFERRED_CONTROLLER == NPadControllerType::Handheld);
|
||||
if (is_handheld) {
|
||||
controller.type = NPadControllerType::None;
|
||||
controller.is_connected = false;
|
||||
AddNewController(best_type);
|
||||
} else {
|
||||
controller.type = best_type;
|
||||
InitNewlyAddedControler(i);
|
||||
}
|
||||
had_controller_update = true;
|
||||
}
|
||||
}
|
||||
if (had_controller_update) {
|
||||
styleset_changed_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
|
||||
@@ -391,8 +431,10 @@ std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
|
||||
}
|
||||
|
||||
void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
|
||||
styleset_changed_event->Signal();
|
||||
hold_type = joy_hold_type;
|
||||
}
|
||||
|
||||
Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
|
||||
return hold_type;
|
||||
}
|
||||
@@ -426,6 +468,9 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const {
|
||||
// TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
|
||||
// be signalled at least once, and signaled after a new controller is connected?
|
||||
styleset_changed_event->Signal();
|
||||
return styleset_changed_event;
|
||||
}
|
||||
|
||||
@@ -451,15 +496,11 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
|
||||
}
|
||||
|
||||
void Controller_NPad::ConnectNPad(u32 npad_id) {
|
||||
if (npad_id >= connected_controllers.size())
|
||||
return;
|
||||
connected_controllers[npad_id].is_connected = true;
|
||||
connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
|
||||
}
|
||||
|
||||
void Controller_NPad::DisconnectNPad(u32 npad_id) {
|
||||
if (npad_id >= connected_controllers.size())
|
||||
return;
|
||||
connected_controllers[npad_id].is_connected = false;
|
||||
connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
|
||||
}
|
||||
|
||||
Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
|
||||
|
||||
@@ -96,6 +96,8 @@ public:
|
||||
// TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
|
||||
|
||||
CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
|
||||
|
||||
ReloadInputDevices();
|
||||
}
|
||||
|
||||
void ActivateController(HidController controller) {
|
||||
|
||||
@@ -3,9 +3,13 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/ldr/ldr.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/loader/nro.h"
|
||||
|
||||
namespace Service::LDR {
|
||||
|
||||
@@ -59,16 +63,58 @@ public:
|
||||
explicit RelocatableObject() : ServiceFramework{"ldr:ro"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "LoadNro"},
|
||||
{0, &RelocatableObject::LoadNro, "LoadNro"},
|
||||
{1, nullptr, "UnloadNro"},
|
||||
{2, nullptr, "LoadNrr"},
|
||||
{2, &RelocatableObject::LoadNrr, "LoadNrr"},
|
||||
{3, nullptr, "UnloadNrr"},
|
||||
{4, nullptr, "Initialize"},
|
||||
{4, &RelocatableObject::Initialize, "Initialize"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void LoadNrr(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_LDR, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void LoadNro(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
rp.Skip(2, false);
|
||||
const VAddr nro_addr{rp.Pop<VAddr>()};
|
||||
const u64 nro_size{rp.Pop<u64>()};
|
||||
const VAddr bss_addr{rp.Pop<VAddr>()};
|
||||
const u64 bss_size{rp.Pop<u64>()};
|
||||
|
||||
// Read NRO data from memory
|
||||
std::vector<u8> nro_data(nro_size);
|
||||
Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
|
||||
|
||||
// Load NRO as new executable module
|
||||
const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)};
|
||||
Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr);
|
||||
|
||||
// TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party.
|
||||
// It is currently missing:
|
||||
// - Signature checks with LoadNRR
|
||||
// - Checking if a module has already been loaded
|
||||
// - Using/validating BSS, etc. params (these are used from NRO header instead)
|
||||
// - Error checking
|
||||
// - ...Probably other things
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(addr);
|
||||
LOG_WARNING(Service_LDR, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void Initialize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_LDR, "(STUBBED) called");
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
|
||||
@@ -212,7 +212,7 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
auto amiibo = nfp_interface.GetAmiiboBuffer();
|
||||
TagInfo tag_info{};
|
||||
std::memcpy(tag_info.uuid.data(), amiibo.uuid.data(), sizeof(tag_info.uuid.size()));
|
||||
tag_info.uuid = amiibo.uuid;
|
||||
tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size());
|
||||
|
||||
tag_info.protocol = 1; // TODO(ogniK): Figure out actual values
|
||||
@@ -328,13 +328,15 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushIpcInterface<IUser>(*this);
|
||||
}
|
||||
|
||||
void Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
|
||||
bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
if (buffer.size() < sizeof(AmiiboFile)) {
|
||||
return; // Failed to load file
|
||||
return false;
|
||||
}
|
||||
|
||||
std::memcpy(&amiibo, buffer.data(), sizeof(amiibo));
|
||||
nfc_tag_load->Signal();
|
||||
return true;
|
||||
}
|
||||
const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const {
|
||||
return nfc_tag_load;
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size");
|
||||
|
||||
void CreateUserInterface(Kernel::HLERequestContext& ctx);
|
||||
void LoadAmiibo(const std::vector<u8>& buffer);
|
||||
bool LoadAmiibo(const std::vector<u8>& buffer);
|
||||
const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const;
|
||||
const AmiiboFile& GetAmiiboBuffer() const;
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/ns/ns.h"
|
||||
@@ -118,7 +121,7 @@ public:
|
||||
{305, nullptr, "TerminateSystemApplet"},
|
||||
{306, nullptr, "LaunchOverlayApplet"},
|
||||
{307, nullptr, "TerminateOverlayApplet"},
|
||||
{400, nullptr, "GetApplicationControlData"},
|
||||
{400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
|
||||
{401, nullptr, "InvalidateAllApplicationControlCache"},
|
||||
{402, nullptr, "RequestDownloadApplicationControlData"},
|
||||
{403, nullptr, "GetMaxApplicationControlCacheCount"},
|
||||
@@ -243,6 +246,65 @@ public:
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void GetApplicationControlData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto flag = rp.PopRaw<u64>();
|
||||
LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
|
||||
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto size = ctx.GetWriteBufferSize();
|
||||
|
||||
const FileSys::PatchManager pm{title_id};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
|
||||
std::vector<u8> out;
|
||||
|
||||
if (control.first != nullptr) {
|
||||
if (size < 0x4000) {
|
||||
LOG_ERROR(Service_NS,
|
||||
"output buffer is too small! (actual={:016X}, expected_min=0x4000)",
|
||||
size);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code for this.
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
out.resize(0x4000);
|
||||
const auto bytes = control.first->GetRawBytes();
|
||||
std::memcpy(out.data(), bytes.data(), bytes.size());
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
|
||||
title_id);
|
||||
out.resize(std::min<u64>(0x4000, size));
|
||||
}
|
||||
|
||||
if (control.second != nullptr) {
|
||||
if (size < 0x4000 + control.second->GetSize()) {
|
||||
LOG_ERROR(Service_NS,
|
||||
"output buffer is too small! (actual={:016X}, expected_min={:016X})",
|
||||
size, 0x4000 + control.second->GetSize());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code for this.
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
out.resize(0x4000 + control.second->GetSize());
|
||||
control.second->Read(out.data() + 0x4000, control.second->GetSize());
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
|
||||
title_id);
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(out);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(static_cast<u32>(out.size()));
|
||||
}
|
||||
};
|
||||
|
||||
class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
|
||||
|
||||
@@ -31,7 +31,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
|
||||
boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
|
||||
std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
|
||||
auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
|
||||
// Only consider free buffers. Buffers become free once again after they've been Acquired
|
||||
// and Released by the compositor, see the NVFlinger::Compose method.
|
||||
@@ -44,7 +44,7 @@ boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
|
||||
});
|
||||
|
||||
if (itr == queue.end()) {
|
||||
return boost::none;
|
||||
return {};
|
||||
}
|
||||
|
||||
itr->status = Buffer::Status::Dequeued;
|
||||
@@ -70,12 +70,12 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
|
||||
itr->crop_rect = crop_rect;
|
||||
}
|
||||
|
||||
boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() {
|
||||
std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
|
||||
auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) {
|
||||
return buffer.status == Buffer::Status::Queued;
|
||||
});
|
||||
if (itr == queue.end())
|
||||
return boost::none;
|
||||
return {};
|
||||
itr->status = Buffer::Status::Acquired;
|
||||
return *itr;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/swap.h"
|
||||
@@ -57,9 +58,9 @@ public:
|
||||
/// Rotate source image 90 degrees clockwise
|
||||
Rotate90 = 0x04,
|
||||
/// Rotate source image 180 degrees
|
||||
Roate180 = 0x03,
|
||||
Rotate180 = 0x03,
|
||||
/// Rotate source image 270 degrees clockwise
|
||||
Roate270 = 0x07,
|
||||
Rotate270 = 0x07,
|
||||
};
|
||||
|
||||
struct Buffer {
|
||||
@@ -73,11 +74,11 @@ public:
|
||||
};
|
||||
|
||||
void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
|
||||
boost::optional<u32> DequeueBuffer(u32 width, u32 height);
|
||||
std::optional<u32> DequeueBuffer(u32 width, u32 height);
|
||||
const IGBPBuffer& RequestBuffer(u32 slot) const;
|
||||
void QueueBuffer(u32 slot, BufferTransformFlags transform,
|
||||
const MathUtil::Rectangle<int>& crop_rect);
|
||||
boost::optional<const Buffer&> AcquireBuffer();
|
||||
std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
|
||||
void ReleaseBuffer(u32 slot);
|
||||
u32 Query(QueryType type);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/optional.hpp>
|
||||
#include <optional>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
@@ -134,7 +134,7 @@ void NVFlinger::Compose() {
|
||||
|
||||
MicroProfileFlip();
|
||||
|
||||
if (buffer == boost::none) {
|
||||
if (!buffer) {
|
||||
auto& system_instance = Core::System::GetInstance();
|
||||
|
||||
// There was no queued buffer to draw, render previous frame
|
||||
@@ -143,7 +143,7 @@ void NVFlinger::Compose() {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& igbp_buffer = buffer->igbp_buffer;
|
||||
auto& igbp_buffer = buffer->get().igbp_buffer;
|
||||
|
||||
// Now send the buffer to the GPU for drawing.
|
||||
// TODO(Subv): Support more than just disp0. The display device selection is probably based
|
||||
@@ -152,10 +152,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,
|
||||
buffer->crop_rect);
|
||||
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride,
|
||||
buffer->get().transform, buffer->get().crop_rect);
|
||||
|
||||
buffer_queue->ReleaseBuffer(buffer->slot);
|
||||
buffer_queue->ReleaseBuffer(buffer->get().slot);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,8 +80,8 @@ namespace Service {
|
||||
* Creates a function string for logging, complete with the name (or header code, depending
|
||||
* on what's passed in) the port name, and all the cmd_buff arguments.
|
||||
*/
|
||||
static std::string MakeFunctionString(const char* name, const char* port_name,
|
||||
const u32* cmd_buff) {
|
||||
[[maybe_unused]] static std::string MakeFunctionString(const char* name, const char* port_name,
|
||||
const u32* cmd_buff) {
|
||||
// Number of params == bits 0-5 + bits 6-11
|
||||
int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
|
||||
|
||||
|
||||
@@ -3,18 +3,23 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/spl/csrng.h"
|
||||
#include "core/hle/service/spl/module.h"
|
||||
#include "core/hle/service/spl/spl.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::SPL {
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {}
|
||||
: ServiceFramework(name), module(std::move(module)),
|
||||
rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
@@ -23,8 +28,9 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
std::size_t size = ctx.GetWriteBufferSize();
|
||||
|
||||
std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max());
|
||||
std::vector<u8> data(size);
|
||||
std::generate(data.begin(), data.end(), std::rand);
|
||||
std::generate(data.begin(), data.end(), [&] { return static_cast<u8>(distribution(rng)); });
|
||||
|
||||
ctx.WriteBuffer(data);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <random>
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::SPL {
|
||||
@@ -19,6 +20,9 @@ public:
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
|
||||
private:
|
||||
std::mt19937 rng;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user