Compare commits
141 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28dc52e7ff | ||
|
|
966bd43a9d | ||
|
|
29f082775b | ||
|
|
0e05a9d58f | ||
|
|
581406af18 | ||
|
|
481d8716e0 | ||
|
|
a9c25ab9e4 | ||
|
|
81ff9e2473 | ||
|
|
74bce4d68f | ||
|
|
e5a0a23553 | ||
|
|
ad45d68871 | ||
|
|
49cb4fa37b | ||
|
|
92fcc6d15a | ||
|
|
2c87f10267 | ||
|
|
7e59e907ef | ||
|
|
80e4dbdce7 | ||
|
|
2ba4d878e5 | ||
|
|
dd321dc85f | ||
|
|
54df9fe29e | ||
|
|
cdb19e71fe | ||
|
|
722e7c05de | ||
|
|
e10483a878 | ||
|
|
acdc770cfb | ||
|
|
19038db489 | ||
|
|
145ae36963 | ||
|
|
38c1c500ab | ||
|
|
d46e0acb3c | ||
|
|
e450b0bbac | ||
|
|
b2647dba33 | ||
|
|
1c4365d928 | ||
|
|
97187b7ef6 | ||
|
|
6664d7b2c5 | ||
|
|
03c26d3406 | ||
|
|
0bc323bafb | ||
|
|
60a184455c | ||
|
|
1069eced84 | ||
|
|
4aa9779ae1 | ||
|
|
cc1fe93297 | ||
|
|
aee93f98f9 | ||
|
|
31930a3334 | ||
|
|
678c18aa5c | ||
|
|
9afcbba8e4 | ||
|
|
7992dee8e9 | ||
|
|
de0ab806df | ||
|
|
d08457f879 | ||
|
|
86e70cf302 | ||
|
|
0b33d38e9b | ||
|
|
e7fc3d13ed | ||
|
|
a6830e61b8 | ||
|
|
aca218aea0 | ||
|
|
9b9c586dff | ||
|
|
76754f5705 | ||
|
|
5bb80ab009 | ||
|
|
da5fcbf501 | ||
|
|
c31412c433 | ||
|
|
352b56367c | ||
|
|
7a5eda5914 | ||
|
|
adf26ae668 | ||
|
|
6383653a8d | ||
|
|
0cc347462d | ||
|
|
938e45eb83 | ||
|
|
c5a849212f | ||
|
|
f1cb425d92 | ||
|
|
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 | ||
|
|
9a87ece837 | ||
|
|
942def7831 | ||
|
|
ca142f35c0 | ||
|
|
abefe29398 | ||
|
|
bed2d6c425 | ||
|
|
a25d79cfaa | ||
|
|
f5f6292810 |
@@ -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) \
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -50,11 +50,11 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
std::vector<FileSys::VirtualFile> concat;
|
||||
for (u8 i = 0; i < 0x10; ++i) {
|
||||
auto next = dir->GetFile(fmt::format("{:02X}", i));
|
||||
if (next)
|
||||
if (next != nullptr)
|
||||
concat.push_back(std::move(next));
|
||||
else {
|
||||
next = dir->GetFile(fmt::format("{:02x}", i));
|
||||
if (next)
|
||||
if (next != nullptr)
|
||||
concat.push_back(std::move(next));
|
||||
else
|
||||
break;
|
||||
@@ -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
|
||||
|
||||
@@ -136,7 +136,7 @@ void ClearPendingEvents() {
|
||||
}
|
||||
|
||||
void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
|
||||
ASSERT(event_type);
|
||||
ASSERT(event_type != nullptr);
|
||||
s64 timeout = GetTicks() + cycles_into_future;
|
||||
// If this event needs to be scheduled before the next advance(), force one early
|
||||
if (!is_global_timer_sane)
|
||||
|
||||
@@ -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) {
|
||||
@@ -889,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
|
||||
|
||||
@@ -294,7 +294,7 @@ FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
|
||||
auto upper = name;
|
||||
std::transform(upper.begin(), upper.end(), upper.begin(), [](u8 c) { return std::toupper(c); });
|
||||
for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
|
||||
if (dir->GetFile(fname))
|
||||
if (dir->GetFile(fname) != nullptr)
|
||||
return dir->GetFile(fname);
|
||||
}
|
||||
|
||||
@@ -325,7 +325,7 @@ PartitionDataManager::PartitionDataManager(const FileSys::VirtualDir& sysdata_di
|
||||
PartitionDataManager::~PartitionDataManager() = default;
|
||||
|
||||
bool PartitionDataManager::HasBoot0() const {
|
||||
return boot0;
|
||||
return boot0 != nullptr;
|
||||
}
|
||||
|
||||
FileSys::VirtualFile PartitionDataManager::GetBoot0Raw() const {
|
||||
@@ -400,7 +400,7 @@ std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(std::size_t revisio
|
||||
}
|
||||
|
||||
bool PartitionDataManager::HasFuses() const {
|
||||
return fuses;
|
||||
return fuses != nullptr;
|
||||
}
|
||||
|
||||
FileSys::VirtualFile PartitionDataManager::GetFusesRaw() const {
|
||||
@@ -416,7 +416,7 @@ std::array<u8, 16> PartitionDataManager::GetSecureBootKey() const {
|
||||
}
|
||||
|
||||
bool PartitionDataManager::HasKFuses() const {
|
||||
return kfuses;
|
||||
return kfuses != nullptr;
|
||||
}
|
||||
|
||||
FileSys::VirtualFile PartitionDataManager::GetKFusesRaw() const {
|
||||
@@ -424,7 +424,7 @@ FileSys::VirtualFile PartitionDataManager::GetKFusesRaw() const {
|
||||
}
|
||||
|
||||
bool PartitionDataManager::HasPackage2(Package2Type type) const {
|
||||
return package2.at(static_cast<size_t>(type));
|
||||
return package2.at(static_cast<size_t>(type)) != nullptr;
|
||||
}
|
||||
|
||||
FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) const {
|
||||
@@ -571,7 +571,7 @@ std::array<u8, 16> PartitionDataManager::GetAESKeyGenerationSource(Package2Type
|
||||
}
|
||||
|
||||
bool PartitionDataManager::HasProdInfo() const {
|
||||
return prodinfo;
|
||||
return prodinfo != nullptr;
|
||||
}
|
||||
|
||||
FileSys::VirtualFile PartitionDataManager::GetProdInfoRaw() const {
|
||||
@@ -587,7 +587,7 @@ void PartitionDataManager::DecryptProdInfo(std::array<u8, 0x20> bis_key) {
|
||||
|
||||
std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const {
|
||||
std::array<u8, 0x240> out{};
|
||||
if (prodinfo_decrypted)
|
||||
if (prodinfo_decrypted != nullptr)
|
||||
prodinfo_decrypted->Read(out.data(), out.size(), 0x3890);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ XCI::XCI(VirtualFile file_)
|
||||
for (XCIPartition partition :
|
||||
{XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
|
||||
auto raw = main_hfs.GetFile(partition_names[static_cast<std::size_t>(partition)]);
|
||||
if (raw)
|
||||
if (raw != nullptr)
|
||||
partitions[static_cast<std::size_t>(partition)] =
|
||||
std::make_shared<PartitionFilesystem>(raw);
|
||||
}
|
||||
@@ -123,7 +123,7 @@ u64 XCI::GetProgramTitleID() const {
|
||||
}
|
||||
|
||||
bool XCI::HasProgramNCA() const {
|
||||
return program;
|
||||
return program != nullptr;
|
||||
}
|
||||
|
||||
VirtualFile XCI::GetProgramNCAFile() const {
|
||||
@@ -147,7 +147,7 @@ std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
|
||||
|
||||
VirtualFile XCI::GetNCAFileByType(NCAContentType type) const {
|
||||
auto nca = GetNCAByType(type);
|
||||
if (nca)
|
||||
if (nca != nullptr)
|
||||
return nca->GetBaseFile();
|
||||
return nullptr;
|
||||
}
|
||||
@@ -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
|
||||
@@ -352,7 +351,7 @@ bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTable
|
||||
const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
|
||||
|
||||
auto dec = Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
|
||||
if (dec) {
|
||||
if (dec != nullptr) {
|
||||
auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
|
||||
|
||||
if (npfs->GetStatus() == Loader::ResultStatus::Success) {
|
||||
@@ -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"
|
||||
@@ -70,7 +71,7 @@ static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size.");
|
||||
|
||||
inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
|
||||
// According to switchbrew, an exefs must only contain these two files:
|
||||
return pfs->GetFile("main") && pfs->GetFile("main.npdm");
|
||||
return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
|
||||
}
|
||||
|
||||
// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
|
||||
@@ -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;
|
||||
|
||||
@@ -145,7 +145,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext,
|
||||
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
|
||||
child->path = parent->path + "/" + kv.first;
|
||||
|
||||
if (ext && ext->GetFileRelative(child->path + ".stub"))
|
||||
if (ext != nullptr && ext->GetFileRelative(child->path + ".stub") != nullptr)
|
||||
continue;
|
||||
|
||||
// Sanity check on path_len
|
||||
@@ -161,7 +161,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext,
|
||||
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
|
||||
child->path = parent->path + "/" + kv.first;
|
||||
|
||||
if (ext && ext->GetFileRelative(child->path + ".stub"))
|
||||
if (ext != nullptr && ext->GetFileRelative(child->path + ".stub") != nullptr)
|
||||
continue;
|
||||
|
||||
// Sanity check on path_len
|
||||
@@ -169,12 +169,12 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext,
|
||||
|
||||
child->source = root_romfs->GetFileRelative(child->path);
|
||||
|
||||
if (ext) {
|
||||
if (ext != nullptr) {
|
||||
const auto ips = ext->GetFileRelative(child->path + ".ips");
|
||||
|
||||
if (ips) {
|
||||
if (ips != nullptr) {
|
||||
auto patched = PatchIPS(child->source, ips);
|
||||
if (patched)
|
||||
if (patched != nullptr)
|
||||
child->source = std::move(patched);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -61,13 +61,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) {
|
||||
if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
|
||||
update->GetExeFS()) {
|
||||
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;
|
||||
@@ -79,7 +78,7 @@ static std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& pa
|
||||
out.reserve(patch_dirs.size());
|
||||
for (const auto& subdir : patch_dirs) {
|
||||
auto exefs_dir = subdir->GetSubdirectory("exefs");
|
||||
if (exefs_dir) {
|
||||
if (exefs_dir != nullptr) {
|
||||
for (const auto& file : exefs_dir->GetFiles()) {
|
||||
if (file->GetExtension() == "ips") {
|
||||
auto name = file->GetName();
|
||||
@@ -134,14 +133,14 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
|
||||
LOG_INFO(Loader, " - Applying IPS patch from mod \"{}\"",
|
||||
patch_file->GetContainingDirectory()->GetParentDirectory()->GetName());
|
||||
const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), patch_file);
|
||||
if (patched)
|
||||
if (patched != nullptr)
|
||||
out = patched->ReadAllBytes();
|
||||
} else if (patch_file->GetExtension() == "pchtxt") {
|
||||
LOG_INFO(Loader, " - Applying IPSwitch patch from mod \"{}\"",
|
||||
patch_file->GetContainingDirectory()->GetParentDirectory()->GetName());
|
||||
const IPSwitchCompiler compiler{patch_file};
|
||||
const auto patched = compiler.Apply(std::make_shared<VectorVfsFile>(out));
|
||||
if (patched)
|
||||
if (patched != nullptr)
|
||||
out = patched->ReadAllBytes();
|
||||
}
|
||||
}
|
||||
@@ -188,11 +187,11 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
|
||||
layers_ext.reserve(patch_dirs.size() + 1);
|
||||
for (const auto& subdir : patch_dirs) {
|
||||
auto romfs_dir = subdir->GetSubdirectory("romfs");
|
||||
if (romfs_dir)
|
||||
if (romfs_dir != nullptr)
|
||||
layers.push_back(std::move(romfs_dir));
|
||||
|
||||
auto ext_dir = subdir->GetSubdirectory("romfs_ext");
|
||||
if (ext_dir)
|
||||
if (ext_dir != nullptr)
|
||||
layers_ext.push_back(std::move(ext_dir));
|
||||
}
|
||||
layers.push_back(std::move(extracted));
|
||||
@@ -232,16 +231,18 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
|
||||
// Game Updates
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
const auto update = installed->GetEntryRaw(update_tid, type);
|
||||
if (update) {
|
||||
if (update != nullptr) {
|
||||
const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset);
|
||||
if (new_nca->GetStatus() == Loader::ResultStatus::Success && new_nca->GetRomFS()) {
|
||||
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) {
|
||||
} else if (update_raw != nullptr) {
|
||||
const auto new_nca = std::make_shared<NCA>(update_raw, romfs, ivfc_offset);
|
||||
if (new_nca->GetStatus() == Loader::ResultStatus::Success && new_nca->GetRomFS()) {
|
||||
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
|
||||
new_nca->GetRomFS() != nullptr) {
|
||||
LOG_INFO(Loader, " RomFS: Update (PACKED) applied successfully");
|
||||
romfs = new_nca->GetRomFS();
|
||||
}
|
||||
@@ -261,7 +262,7 @@ static void AppendCommaIfNotEmpty(std::string& to, const std::string& with) {
|
||||
}
|
||||
|
||||
static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
|
||||
return dir && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
|
||||
return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
|
||||
}
|
||||
|
||||
std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
|
||||
@@ -274,26 +275,25 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
|
||||
PatchManager update{update_tid};
|
||||
auto [nacp, discard_icon_file] = update.GetControlMetadata();
|
||||
|
||||
if (nacp) {
|
||||
if (nacp != nullptr) {
|
||||
out.insert_or_assign("Update", nacp->GetVersionString());
|
||||
} 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) {
|
||||
} else if (update_raw != nullptr) {
|
||||
out.insert_or_assign("Update", "PACKED");
|
||||
}
|
||||
}
|
||||
|
||||
// General Mods (LayeredFS and IPS)
|
||||
const auto mod_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
|
||||
if (mod_dir && mod_dir->GetSize() > 0) {
|
||||
if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
|
||||
for (const auto& mod : mod_dir->GetSubdirectories()) {
|
||||
std::string types;
|
||||
|
||||
@@ -382,7 +382,7 @@ std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(cons
|
||||
VirtualFile icon_file;
|
||||
for (const auto& language : FileSys::LANGUAGE_NAMES) {
|
||||
icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat");
|
||||
if (icon_file)
|
||||
if (icon_file != nullptr)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,9 +106,9 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
|
||||
|
||||
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
|
||||
std::string_view path) const {
|
||||
if (dir->GetFileRelative(path))
|
||||
if (dir->GetFileRelative(path) != nullptr)
|
||||
return dir->GetFileRelative(path);
|
||||
if (dir->GetDirectoryRelative(path)) {
|
||||
if (dir->GetDirectoryRelative(path) != nullptr) {
|
||||
const auto nca_dir = dir->GetDirectoryRelative(path);
|
||||
VirtualFile file = nullptr;
|
||||
|
||||
@@ -120,11 +120,11 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
|
||||
// Since the files are a two-digit hex number, max is FF.
|
||||
for (std::size_t i = 0; i < 0x100; ++i) {
|
||||
auto next = nca_dir->GetFile(fmt::format("{:02X}", i));
|
||||
if (next) {
|
||||
if (next != nullptr) {
|
||||
concat.push_back(std::move(next));
|
||||
} else {
|
||||
next = nca_dir->GetFile(fmt::format("{:02x}", i));
|
||||
if (next)
|
||||
if (next != nullptr)
|
||||
concat.push_back(std::move(next));
|
||||
else
|
||||
break;
|
||||
@@ -153,34 +153,34 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
||||
for (u8 i = 0; i < 4; ++i) {
|
||||
const auto path = GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0);
|
||||
file = OpenFileOrDirectoryConcat(dir, path);
|
||||
if (file)
|
||||
if (file != nullptr)
|
||||
return file;
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -274,26 +274,23 @@ RegisteredCache::RegisteredCache(VirtualDir dir_, RegisteredCacheParsingFunction
|
||||
RegisteredCache::~RegisteredCache() = default;
|
||||
|
||||
bool RegisteredCache::HasEntry(u64 title_id, ContentRecordType type) const {
|
||||
return GetEntryRaw(title_id, type);
|
||||
return GetEntryRaw(title_id, type) != nullptr;
|
||||
}
|
||||
|
||||
bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const {
|
||||
return GetEntryRaw(entry);
|
||||
return GetEntryRaw(entry) != nullptr;
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -337,7 +331,7 @@ void RegisteredCache::IterateAllMetadata(
|
||||
if (filter(cnmt, EMPTY_META_CONTENT_RECORD))
|
||||
out.push_back(proc(cnmt, EMPTY_META_CONTENT_RECORD));
|
||||
for (const auto& rec : cnmt.GetContentRecords()) {
|
||||
if (GetFileAtID(rec.nca_id) && filter(cnmt, rec)) {
|
||||
if (GetFileAtID(rec.nca_id) != nullptr && filter(cnmt, rec)) {
|
||||
out.push_back(proc(cnmt, rec));
|
||||
}
|
||||
}
|
||||
@@ -345,7 +339,7 @@ void RegisteredCache::IterateAllMetadata(
|
||||
for (const auto& kv : yuzu_meta) {
|
||||
const auto& cnmt = kv.second;
|
||||
for (const auto& rec : cnmt.GetContentRecords()) {
|
||||
if (GetFileAtID(rec.nca_id) && filter(cnmt, rec)) {
|
||||
if (GetFileAtID(rec.nca_id) != nullptr && filter(cnmt, rec)) {
|
||||
out.push_back(proc(cnmt, rec));
|
||||
}
|
||||
}
|
||||
@@ -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,22 +462,22 @@ 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);
|
||||
|
||||
if (GetFileAtID(id) && !overwrite_if_exists) {
|
||||
if (GetFileAtID(id) != nullptr && !overwrite_if_exists) {
|
||||
LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping...");
|
||||
return InstallResult::ErrorAlreadyExists;
|
||||
}
|
||||
|
||||
if (GetFileAtID(id)) {
|
||||
if (GetFileAtID(id) != nullptr) {
|
||||
LOG_WARNING(Loader, "Overwriting existing NCA...");
|
||||
VirtualDir c_dir;
|
||||
{ c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); }
|
||||
@@ -543,20 +537,20 @@ 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 {
|
||||
for (const auto& c : caches) {
|
||||
const auto res = c->GetEntryUnparsed(title_id, type);
|
||||
if (res)
|
||||
if (res != nullptr)
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -570,7 +564,7 @@ VirtualFile RegisteredCacheUnion::GetEntryUnparsed(RegisteredCacheEntry entry) c
|
||||
VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const {
|
||||
for (const auto& c : caches) {
|
||||
const auto res = c->GetEntryRaw(title_id, type);
|
||||
if (res)
|
||||
if (res != nullptr)
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -166,7 +166,7 @@ VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const {
|
||||
if (extracted)
|
||||
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
|
||||
const auto nca = GetNCA(title_id, type);
|
||||
if (nca)
|
||||
if (nca != nullptr)
|
||||
return nca->GetBaseFile();
|
||||
return nullptr;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -31,9 +31,9 @@ bool VfsFilesystem::IsWritable() const {
|
||||
|
||||
VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
|
||||
const auto path = FileUtil::SanitizePath(path_);
|
||||
if (root->GetFileRelative(path))
|
||||
if (root->GetFileRelative(path) != nullptr)
|
||||
return VfsEntryType::File;
|
||||
if (root->GetDirectoryRelative(path))
|
||||
if (root->GetDirectoryRelative(path) != nullptr)
|
||||
return VfsEntryType::Directory;
|
||||
|
||||
return VfsEntryType::None;
|
||||
@@ -65,7 +65,7 @@ VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view
|
||||
if (old_file == nullptr)
|
||||
return nullptr;
|
||||
auto new_file = OpenFile(new_path, Mode::Read);
|
||||
if (new_file)
|
||||
if (new_file != nullptr)
|
||||
return nullptr;
|
||||
new_file = CreateFile(new_path, Mode::Write);
|
||||
if (new_file == nullptr)
|
||||
@@ -115,7 +115,7 @@ VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_
|
||||
if (old_dir == nullptr)
|
||||
return nullptr;
|
||||
auto new_dir = OpenDirectory(new_path, Mode::Read);
|
||||
if (new_dir)
|
||||
if (new_dir != nullptr)
|
||||
return nullptr;
|
||||
new_dir = CreateDirectory(new_path, Mode::Write);
|
||||
if (new_dir == nullptr)
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,7 +26,7 @@ VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> dir
|
||||
std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFileRelative(std::string_view path) const {
|
||||
for (const auto& layer : dirs) {
|
||||
const auto file = layer->GetFileRelative(path);
|
||||
if (file)
|
||||
if (file != nullptr)
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetDirectoryRelative(
|
||||
std::vector<VirtualDir> out;
|
||||
for (const auto& layer : dirs) {
|
||||
auto dir = layer->GetDirectoryRelative(path);
|
||||
if (dir)
|
||||
if (dir != nullptr)
|
||||
out.push_back(std::move(dir));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -260,7 +260,7 @@ std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t o
|
||||
}
|
||||
|
||||
bool RealVfsFile::Rename(std::string_view name) {
|
||||
return base.MoveFile(path, parent_path + DIR_SEP + std::string(name));
|
||||
return base.MoveFile(path, parent_path + DIR_SEP + std::string(name)) != nullptr;
|
||||
}
|
||||
|
||||
bool RealVfsFile::Close() {
|
||||
@@ -404,7 +404,7 @@ bool RealVfsDirectory::DeleteFile(std::string_view name) {
|
||||
|
||||
bool RealVfsDirectory::Rename(std::string_view name) {
|
||||
const std::string new_name = (parent_path + DIR_SEP).append(name);
|
||||
return base.MoveFile(path, new_name);
|
||||
return base.MoveFile(path, new_name) != nullptr;
|
||||
}
|
||||
|
||||
std::string RealVfsDirectory::GetFullPath() const {
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -117,8 +117,7 @@ public:
|
||||
|
||||
AlignWithPadding();
|
||||
|
||||
const bool request_has_domain_header{context.GetDomainMessageHeader()};
|
||||
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);
|
||||
|
||||
@@ -19,7 +19,7 @@ HandleTable::HandleTable() {
|
||||
}
|
||||
|
||||
ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
|
||||
DEBUG_ASSERT(obj);
|
||||
DEBUG_ASSERT(obj != nullptr);
|
||||
|
||||
u16 slot = next_free_slot;
|
||||
if (slot >= generations.size()) {
|
||||
@@ -68,7 +68,7 @@ bool HandleTable::IsValid(Handle handle) const {
|
||||
std::size_t slot = GetSlot(handle);
|
||||
u16 generation = GetGeneration(handle);
|
||||
|
||||
return slot < MAX_COUNT && objects[slot] && generations[slot] == generation;
|
||||
return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation;
|
||||
}
|
||||
|
||||
SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
|
||||
|
||||
@@ -241,12 +241,12 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
// services don't deal with handles directly. However, the guest applications might check
|
||||
// for specific values in each of these descriptors.
|
||||
for (auto& object : copy_objects) {
|
||||
ASSERT(object);
|
||||
ASSERT(object != nullptr);
|
||||
dst_cmdbuf[current_offset++] = handle_table.Create(object).Unwrap();
|
||||
}
|
||||
|
||||
for (auto& object : move_objects) {
|
||||
ASSERT(object);
|
||||
ASSERT(object != nullptr);
|
||||
dst_cmdbuf[current_offset++] = handle_table.Create(object).Unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -72,7 +72,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
|
||||
// Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
|
||||
// and don't have a lock owner unless SignalProcessWideKey was called first and the thread
|
||||
// wasn't awakened due to the mutex already being acquired.
|
||||
if (lock_owner) {
|
||||
if (lock_owner != nullptr) {
|
||||
lock_owner->RemoveMutexWaiter(thread);
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -94,7 +94,7 @@ using SharedPtr = boost::intrusive_ptr<T>;
|
||||
*/
|
||||
template <typename T>
|
||||
inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) {
|
||||
if (object && object->GetHandleType() == T::HANDLE_TYPE) {
|
||||
if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
|
||||
return boost::static_pointer_cast<T>(object);
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
@@ -232,6 +232,12 @@ 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) {
|
||||
|
||||
@@ -202,6 +202,16 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads process-specifics configuration info with metadata provided
|
||||
* by an executable.
|
||||
@@ -305,6 +315,9 @@ 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;
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -27,13 +28,17 @@ Scheduler::~Scheduler() {
|
||||
|
||||
bool Scheduler::HaveReadyThreads() const {
|
||||
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
||||
return ready_queue.get_first();
|
||||
return ready_queue.get_first() != nullptr;
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ bool ServerSession::ShouldWait(Thread* thread) const {
|
||||
if (parent->client == nullptr)
|
||||
return false;
|
||||
// Wait if we have no pending requests, or if we're currently handling a request.
|
||||
return pending_requesting_threads.empty() || currently_handling;
|
||||
return pending_requesting_threads.empty() || currently_handling != nullptr;
|
||||
}
|
||||
|
||||
void ServerSession::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,10 +111,10 @@ 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) {
|
||||
} else if (hle_handler != nullptr) {
|
||||
// If this ServerSession has an associated HLE handler, forward the request to it.
|
||||
result = hle_handler->HandleSyncRequest(context);
|
||||
}
|
||||
@@ -124,7 +124,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
|
||||
// svcReplyAndReceive for LLE servers.
|
||||
thread->SetStatus(ThreadStatus::WaitIPC);
|
||||
|
||||
if (hle_handler) {
|
||||
if (hle_handler != nullptr) {
|
||||
// For HLE services, we put the request threads to sleep for a short duration to
|
||||
// simulate IPC overhead, but only if the HLE handler didn't put the thread to sleep for
|
||||
// other reasons like an async callback. The IPC overhead is needed to prevent
|
||||
|
||||
@@ -34,7 +34,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce
|
||||
shared_memory->backing_block_offset = 0;
|
||||
|
||||
// Refresh the address mappings for the current process.
|
||||
if (Core::CurrentProcess()) {
|
||||
if (Core::CurrentProcess() != nullptr) {
|
||||
Core::CurrentProcess()->VMManager().RefreshMemoryBlockMappings(
|
||||
shared_memory->backing_block.get());
|
||||
}
|
||||
|
||||
@@ -395,16 +395,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 +459,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 +468,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 +495,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();
|
||||
|
||||
@@ -529,6 +588,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();
|
||||
}
|
||||
@@ -931,7 +1020,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
|
||||
thread->ResumeFromWait();
|
||||
|
||||
auto* const lock_owner = thread->GetLockOwner();
|
||||
if (lock_owner) {
|
||||
if (lock_owner != nullptr) {
|
||||
lock_owner->RemoveMutexWaiter(thread);
|
||||
}
|
||||
|
||||
@@ -1045,7 +1134,7 @@ static ResultCode ResetSignal(Handle handle) {
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
auto event = handle_table.Get<Event>(handle);
|
||||
|
||||
ASSERT(event);
|
||||
ASSERT(event != nullptr);
|
||||
|
||||
event->Clear();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
@@ -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,7 +142,7 @@ void Thread::ResumeFromWait() {
|
||||
|
||||
status = ThreadStatus::Ready;
|
||||
|
||||
boost::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
|
||||
std::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
|
||||
if (!new_processor_id) {
|
||||
new_processor_id = processor_id;
|
||||
}
|
||||
@@ -369,7 +369,7 @@ void Thread::ChangeCore(u32 core, u64 mask) {
|
||||
return;
|
||||
}
|
||||
|
||||
boost::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
|
||||
std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
|
||||
|
||||
if (!new_processor_id) {
|
||||
new_processor_id = processor_id;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -334,7 +342,7 @@ public:
|
||||
}
|
||||
|
||||
bool HasWakeupCallback() const {
|
||||
return wakeup_callback;
|
||||
return wakeup_callback != nullptr;
|
||||
}
|
||||
|
||||
void SetWakeupCallback(WakeupCallback callback) {
|
||||
@@ -378,7 +386,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;
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
|
||||
std::shared_ptr<std::vector<u8>> block,
|
||||
std::size_t offset, u64 size,
|
||||
MemoryState state) {
|
||||
ASSERT(block);
|
||||
ASSERT(block != nullptr);
|
||||
ASSERT(offset + size <= block->size());
|
||||
|
||||
// This is the appropriately sized VMA that will turn into our allocation.
|
||||
@@ -121,7 +121,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
|
||||
|
||||
ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* memory, u64 size,
|
||||
MemoryState state) {
|
||||
ASSERT(memory);
|
||||
ASSERT(memory != nullptr);
|
||||
|
||||
// This is the appropriately sized VMA that will turn into our allocation.
|
||||
CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -68,7 +68,7 @@ private:
|
||||
// Specialization of DynamicObjectCast for WaitObjects
|
||||
template <>
|
||||
inline SharedPtr<WaitObject> DynamicObjectCast<WaitObject>(SharedPtr<Object> object) {
|
||||
if (object && object->IsWaitable()) {
|
||||
if (object != nullptr && object->IsWaitable()) {
|
||||
return boost::static_pointer_cast<WaitObject>(object);
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
@@ -242,6 +242,28 @@ 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;
|
||||
}
|
||||
auto user_list = profile_manager->GetAllUsers();
|
||||
if (user_list.empty()) {
|
||||
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"},
|
||||
|
||||
@@ -195,7 +195,7 @@ 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) != std::nullopt;
|
||||
return GetUserIndex(uuid).has_value();
|
||||
}
|
||||
|
||||
bool ProfileManager::UserExistsIndex(std::size_t index) const {
|
||||
|
||||
@@ -57,7 +57,8 @@ struct UUID {
|
||||
};
|
||||
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>;
|
||||
|
||||
|
||||
@@ -743,7 +743,7 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
Account::ProfileManager profile_manager{};
|
||||
const auto uuid = profile_manager.GetUser(Settings::values.current_user);
|
||||
ASSERT(uuid != std::nullopt);
|
||||
ASSERT(uuid);
|
||||
params.current_user = uuid->uuid;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -135,7 +135,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
|
||||
return c_res;
|
||||
|
||||
auto dest = backing->GetFileRelative(dest_path);
|
||||
ASSERT_MSG(dest, "Newly created file with success cannot be found.");
|
||||
ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
|
||||
|
||||
ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
|
||||
"Could not write all of the bytes but everything else has succeded.");
|
||||
@@ -220,9 +220,9 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
|
||||
if (filename.empty())
|
||||
return MakeResult(FileSys::EntryType::Directory);
|
||||
|
||||
if (dir->GetFile(filename))
|
||||
if (dir->GetFile(filename) != nullptr)
|
||||
return MakeResult(FileSys::EntryType::File);
|
||||
if (dir->GetSubdirectory(filename))
|
||||
if (dir->GetSubdirectory(filename) != nullptr)
|
||||
return MakeResult(FileSys::EntryType::Directory);
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
@@ -273,8 +273,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 +329,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};
|
||||
|
||||
|
||||
@@ -392,8 +392,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;
|
||||
}
|
||||
@@ -427,6 +429,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,11 +132,11 @@ public:
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "BindNoticeEvent"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{1, nullptr, "UnbindNoticeEvent"},
|
||||
{2, nullptr, "GetStatus"},
|
||||
{3, nullptr, "GetNotice"},
|
||||
{4, nullptr, "Unknown2"},
|
||||
{5, nullptr, "Unknown3"},
|
||||
{4, nullptr, "EnablePowerRequestNotice"},
|
||||
{5, nullptr, "DisablePowerRequestNotice"},
|
||||
{6, nullptr, "ReplyPowerRequest"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
@@ -506,9 +507,9 @@ private:
|
||||
IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
|
||||
const u32 width{request.data.width};
|
||||
const u32 height{request.data.height};
|
||||
boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
|
||||
std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
|
||||
|
||||
if (slot != boost::none) {
|
||||
if (slot) {
|
||||
// Buffer is available
|
||||
IGBPDequeueBufferResponseParcel response{*slot};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
@@ -520,7 +521,7 @@ private:
|
||||
Kernel::ThreadWakeupReason reason) {
|
||||
// Repeat TransactParcel DequeueBuffer when a buffer is available
|
||||
auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
|
||||
std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
|
||||
IGBPDequeueBufferResponseParcel response{*slot};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
@@ -28,7 +28,7 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys
|
||||
|
||||
// Title ID
|
||||
const auto npdm = dir->GetFile("main.npdm");
|
||||
if (npdm) {
|
||||
if (npdm != nullptr) {
|
||||
const auto res = metadata.Load(npdm);
|
||||
if (res == ResultStatus::Success)
|
||||
title_id = metadata.GetTitleID();
|
||||
@@ -38,7 +38,7 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys
|
||||
FileSys::VirtualFile icon_file = nullptr;
|
||||
for (const auto& language : FileSys::LANGUAGE_NAMES) {
|
||||
icon_file = dir->GetFile("icon_" + std::string(language) + ".dat");
|
||||
if (icon_file) {
|
||||
if (icon_file != nullptr) {
|
||||
icon_data = icon_file->ReadAllBytes();
|
||||
break;
|
||||
}
|
||||
@@ -68,7 +68,7 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys
|
||||
nacp_file = *nacp_iter;
|
||||
}
|
||||
|
||||
if (nacp_file) {
|
||||
if (nacp_file != nullptr) {
|
||||
FileSys::NACP nacp(nacp_file);
|
||||
name = nacp.GetApplicationName();
|
||||
}
|
||||
@@ -167,7 +167,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
|
||||
});
|
||||
|
||||
// Register the RomFS if a ".romfs" file was found
|
||||
if (romfs_iter != files.end() && *romfs_iter) {
|
||||
if (romfs_iter != files.end() && *romfs_iter != nullptr) {
|
||||
romfs = *romfs_iter;
|
||||
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
|
||||
}
|
||||
|
||||
@@ -351,7 +351,7 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const
|
||||
for (int i = firstSection; i < header->e_shnum; i++) {
|
||||
const char* secname = GetSectionName(i);
|
||||
|
||||
if (secname && strcmp(name, secname) == 0)
|
||||
if (secname != nullptr && strcmp(name, secname) == 0)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
@@ -145,7 +146,7 @@ public:
|
||||
* information.
|
||||
* @returns A pair with the optional system mode, and and the status.
|
||||
*/
|
||||
virtual std::pair<boost::optional<u32>, ResultStatus> LoadKernelSystemMode() {
|
||||
virtual std::pair<std::optional<u32>, ResultStatus> LoadKernelSystemMode() {
|
||||
// 96MB allocated to the application.
|
||||
return std::make_pair(2, ResultStatus::Success);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ ResultStatus AppLoader_NCA::Load(Kernel::Process& process) {
|
||||
if (load_result != ResultStatus::Success)
|
||||
return load_result;
|
||||
|
||||
if (nca->GetRomFS() && nca->GetRomFS()->GetSize() > 0)
|
||||
if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0)
|
||||
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
|
||||
|
||||
is_loaded = true;
|
||||
|
||||
@@ -127,18 +127,23 @@ static constexpr u32 PageAlignSize(u32 size) {
|
||||
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
|
||||
}
|
||||
|
||||
bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
|
||||
// Read NSO header
|
||||
NroHeader nro_header{};
|
||||
if (sizeof(NroHeader) != file.ReadObject(&nro_header)) {
|
||||
/*static*/ bool AppLoader_NRO::LoadNro(const std::vector<u8>& data, const std::string& name,
|
||||
VAddr load_base) {
|
||||
|
||||
if (data.size() < sizeof(NroHeader)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Read NSO header
|
||||
NroHeader nro_header{};
|
||||
std::memcpy(&nro_header, data.data(), sizeof(NroHeader));
|
||||
if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Build program image
|
||||
std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size));
|
||||
std::vector<u8> program_image(PageAlignSize(nro_header.file_size));
|
||||
std::memcpy(program_image.data(), data.data(), program_image.size());
|
||||
if (program_image.size() != PageAlignSize(nro_header.file_size)) {
|
||||
return {};
|
||||
}
|
||||
@@ -182,11 +187,15 @@ bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
|
||||
Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
|
||||
|
||||
// Register module with GDBStub
|
||||
GDBStub::RegisterModule(file.GetName(), load_base, load_base);
|
||||
GDBStub::RegisterModule(name, load_base, load_base);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
|
||||
return AppLoader_NRO::LoadNro(file.ReadAllBytes(), file.GetName(), load_base);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
|
||||
if (is_loaded) {
|
||||
return ResultStatus::ErrorAlreadyLoaded;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/loader/linker.h"
|
||||
#include "core/loader/loader.h"
|
||||
@@ -40,6 +41,8 @@ public:
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
bool IsRomFSUpdatable() const override;
|
||||
|
||||
static bool LoadNro(const std::vector<u8>& data, const std::string& name, VAddr load_base);
|
||||
|
||||
private:
|
||||
bool LoadNro(const FileSys::VfsFile& file, VAddr load_base);
|
||||
|
||||
|
||||
@@ -36,6 +36,16 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
|
||||
|
||||
std::tie(nacp_file, icon_file) =
|
||||
FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
|
||||
|
||||
if (nsp->IsExtractedType()) {
|
||||
secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
|
||||
} else {
|
||||
if (title_id == 0)
|
||||
return;
|
||||
|
||||
secondary_loader = std::make_unique<AppLoader_NCA>(
|
||||
nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
|
||||
}
|
||||
}
|
||||
|
||||
AppLoader_NSP::~AppLoader_NSP() = default;
|
||||
@@ -45,14 +55,14 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
|
||||
|
||||
if (nsp.GetStatus() == ResultStatus::Success) {
|
||||
// Extracted Type case
|
||||
if (nsp.IsExtractedType() && nsp.GetExeFS() && FileSys::IsDirectoryExeFS(nsp.GetExeFS()) &&
|
||||
nsp.GetRomFS()) {
|
||||
if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr &&
|
||||
FileSys::IsDirectoryExeFS(nsp.GetExeFS()) && nsp.GetRomFS() != nullptr) {
|
||||
return FileType::NSP;
|
||||
}
|
||||
|
||||
// Non-Ectracted Type case
|
||||
if (!nsp.IsExtractedType() &&
|
||||
nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) &&
|
||||
nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr &&
|
||||
AppLoader_NCA::IdentifyType(nsp.GetNCAFile(
|
||||
nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program)) == FileType::NCA) {
|
||||
return FileType::NSP;
|
||||
@@ -67,26 +77,19 @@ ResultStatus AppLoader_NSP::Load(Kernel::Process& process) {
|
||||
return ResultStatus::ErrorAlreadyLoaded;
|
||||
}
|
||||
|
||||
if (nsp->IsExtractedType()) {
|
||||
secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
|
||||
} else {
|
||||
if (title_id == 0)
|
||||
return ResultStatus::ErrorNSPMissingProgramNCA;
|
||||
if (title_id == 0)
|
||||
return ResultStatus::ErrorNSPMissingProgramNCA;
|
||||
|
||||
secondary_loader = std::make_unique<AppLoader_NCA>(
|
||||
nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
|
||||
if (nsp->GetStatus() != ResultStatus::Success)
|
||||
return nsp->GetStatus();
|
||||
|
||||
if (nsp->GetStatus() != ResultStatus::Success)
|
||||
return nsp->GetStatus();
|
||||
if (nsp->GetProgramStatus(title_id) != ResultStatus::Success)
|
||||
return nsp->GetProgramStatus(title_id);
|
||||
|
||||
if (nsp->GetProgramStatus(title_id) != ResultStatus::Success)
|
||||
return nsp->GetProgramStatus(title_id);
|
||||
|
||||
if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
|
||||
if (!Core::Crypto::KeyManager::KeyFileExists(false))
|
||||
return ResultStatus::ErrorMissingProductionKeyFile;
|
||||
return ResultStatus::ErrorNSPMissingProgramNCA;
|
||||
}
|
||||
if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
|
||||
if (!Core::Crypto::KeyManager::KeyFileExists(false))
|
||||
return ResultStatus::ErrorMissingProductionKeyFile;
|
||||
return ResultStatus::ErrorNSPMissingProgramNCA;
|
||||
}
|
||||
|
||||
const auto result = secondary_loader->Load(process);
|
||||
@@ -94,7 +97,7 @@ ResultStatus AppLoader_NSP::Load(Kernel::Process& process) {
|
||||
return result;
|
||||
|
||||
FileSys::VirtualFile update_raw;
|
||||
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw)
|
||||
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr)
|
||||
Service::FileSystem::SetPackedUpdate(std::move(update_raw));
|
||||
|
||||
is_loaded = true;
|
||||
|
||||
@@ -39,7 +39,7 @@ FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) {
|
||||
FileSys::XCI xci(file);
|
||||
|
||||
if (xci.GetStatus() == ResultStatus::Success &&
|
||||
xci.GetNCAByType(FileSys::NCAContentType::Program) &&
|
||||
xci.GetNCAByType(FileSys::NCAContentType::Program) != nullptr &&
|
||||
AppLoader_NCA::IdentifyType(xci.GetNCAFileByType(FileSys::NCAContentType::Program)) ==
|
||||
FileType::NCA) {
|
||||
return FileType::XCI;
|
||||
@@ -67,7 +67,7 @@ ResultStatus AppLoader_XCI::Load(Kernel::Process& process) {
|
||||
return result;
|
||||
|
||||
FileSys::VirtualFile update_raw;
|
||||
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw)
|
||||
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr)
|
||||
Service::FileSystem::SetPackedUpdate(std::move(update_raw));
|
||||
|
||||
is_loaded = true;
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
@@ -70,7 +70,7 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa
|
||||
page_table.pointers[base] = memory;
|
||||
|
||||
base += 1;
|
||||
if (memory)
|
||||
if (memory != nullptr)
|
||||
memory += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <boost/optional.hpp>
|
||||
#include <optional>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Memory {
|
||||
@@ -18,19 +19,19 @@ namespace Memory {
|
||||
*
|
||||
* A hook may be mapped to multiple regions of memory.
|
||||
*
|
||||
* If a boost::none or false is returned from a function, the read/write request is passed through
|
||||
* If a std::nullopt or false is returned from a function, the read/write request is passed through
|
||||
* to the underlying memory region.
|
||||
*/
|
||||
class MemoryHook {
|
||||
public:
|
||||
virtual ~MemoryHook();
|
||||
|
||||
virtual boost::optional<bool> IsValidAddress(VAddr addr) = 0;
|
||||
virtual std::optional<bool> IsValidAddress(VAddr addr) = 0;
|
||||
|
||||
virtual boost::optional<u8> Read8(VAddr addr) = 0;
|
||||
virtual boost::optional<u16> Read16(VAddr addr) = 0;
|
||||
virtual boost::optional<u32> Read32(VAddr addr) = 0;
|
||||
virtual boost::optional<u64> Read64(VAddr addr) = 0;
|
||||
virtual std::optional<u8> Read8(VAddr addr) = 0;
|
||||
virtual std::optional<u16> Read16(VAddr addr) = 0;
|
||||
virtual std::optional<u32> Read32(VAddr addr) = 0;
|
||||
virtual std::optional<u64> Read64(VAddr addr) = 0;
|
||||
|
||||
virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0;
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ TelemetrySession::TelemetrySession() {
|
||||
|
||||
if (name.empty()) {
|
||||
auto [nacp, icon_file] = FileSys::PatchManager(program_id).GetControlMetadata();
|
||||
if (nacp)
|
||||
if (nacp != nullptr)
|
||||
name = nacp->GetApplicationName();
|
||||
}
|
||||
|
||||
@@ -184,4 +184,13 @@ TelemetrySession::~TelemetrySession() {
|
||||
backend = nullptr;
|
||||
}
|
||||
|
||||
bool TelemetrySession::SubmitTestcase() {
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
field_collection.Accept(*backend);
|
||||
return backend->SubmitTestcase();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -31,6 +31,12 @@ public:
|
||||
field_collection.AddField(type, name, std::move(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits a Testcase.
|
||||
* @returns A bool indicating whether the submission succeeded
|
||||
*/
|
||||
bool SubmitTestcase();
|
||||
|
||||
private:
|
||||
Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session
|
||||
std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields
|
||||
|
||||
@@ -64,11 +64,11 @@ void TestEnvironment::ClearWriteRecords() {
|
||||
|
||||
TestEnvironment::TestMemory::~TestMemory() {}
|
||||
|
||||
boost::optional<bool> TestEnvironment::TestMemory::IsValidAddress(VAddr addr) {
|
||||
std::optional<bool> TestEnvironment::TestMemory::IsValidAddress(VAddr addr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) {
|
||||
std::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) {
|
||||
const auto iter = data.find(addr);
|
||||
|
||||
if (iter == data.end()) {
|
||||
@@ -79,15 +79,15 @@ boost::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) {
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
boost::optional<u16> TestEnvironment::TestMemory::Read16(VAddr addr) {
|
||||
std::optional<u16> TestEnvironment::TestMemory::Read16(VAddr addr) {
|
||||
return *Read8(addr) | static_cast<u16>(*Read8(addr + 1)) << 8;
|
||||
}
|
||||
|
||||
boost::optional<u32> TestEnvironment::TestMemory::Read32(VAddr addr) {
|
||||
std::optional<u32> TestEnvironment::TestMemory::Read32(VAddr addr) {
|
||||
return *Read16(addr) | static_cast<u32>(*Read16(addr + 2)) << 16;
|
||||
}
|
||||
|
||||
boost::optional<u64> TestEnvironment::TestMemory::Read64(VAddr addr) {
|
||||
std::optional<u64> TestEnvironment::TestMemory::Read64(VAddr addr) {
|
||||
return *Read32(addr) | static_cast<u64>(*Read32(addr + 4)) << 32;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,12 +64,12 @@ private:
|
||||
|
||||
~TestMemory() override;
|
||||
|
||||
boost::optional<bool> IsValidAddress(VAddr addr) override;
|
||||
std::optional<bool> IsValidAddress(VAddr addr) override;
|
||||
|
||||
boost::optional<u8> Read8(VAddr addr) override;
|
||||
boost::optional<u16> Read16(VAddr addr) override;
|
||||
boost::optional<u32> Read32(VAddr addr) override;
|
||||
boost::optional<u64> Read64(VAddr addr) override;
|
||||
std::optional<u8> Read8(VAddr addr) override;
|
||||
std::optional<u16> Read16(VAddr addr) override;
|
||||
std::optional<u32> Read32(VAddr addr) override;
|
||||
std::optional<u64> Read64(VAddr addr) override;
|
||||
|
||||
bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) override;
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ add_library(video_core STATIC
|
||||
renderer_opengl/gl_rasterizer.h
|
||||
renderer_opengl/gl_rasterizer_cache.cpp
|
||||
renderer_opengl/gl_rasterizer_cache.h
|
||||
renderer_opengl/gl_resource_manager.cpp
|
||||
renderer_opengl/gl_resource_manager.h
|
||||
renderer_opengl/gl_shader_cache.cpp
|
||||
renderer_opengl/gl_shader_cache.h
|
||||
@@ -51,6 +52,10 @@ add_library(video_core STATIC
|
||||
renderer_opengl/maxwell_to_gl.h
|
||||
renderer_opengl/renderer_opengl.cpp
|
||||
renderer_opengl/renderer_opengl.h
|
||||
renderer_opengl/utils.cpp
|
||||
renderer_opengl/utils.h
|
||||
surface.cpp
|
||||
surface.h
|
||||
textures/astc.cpp
|
||||
textures/astc.h
|
||||
textures/decoders.cpp
|
||||
|
||||
@@ -81,7 +81,7 @@ void GPU::ProcessCommandLists(const std::vector<CommandListHeader>& commands) {
|
||||
for (auto entry : commands) {
|
||||
Tegra::GPUVAddr address = entry.Address();
|
||||
u32 size = entry.sz;
|
||||
const boost::optional<VAddr> head_address = memory_manager->GpuToCpuAddress(address);
|
||||
const std::optional<VAddr> head_address = memory_manager->GpuToCpuAddress(address);
|
||||
VAddr current_addr = *head_address;
|
||||
while (current_addr < *head_address + size * sizeof(CommandHeader)) {
|
||||
const CommandHeader header = {Memory::Read32(current_addr)};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
@@ -19,21 +20,56 @@ namespace Tegra::Engines {
|
||||
constexpr u32 MacroRegistersStart = 0xE00;
|
||||
|
||||
Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
|
||||
: memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {}
|
||||
: memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {
|
||||
InitializeRegisterDefaults();
|
||||
}
|
||||
|
||||
void Maxwell3D::InitializeRegisterDefaults() {
|
||||
// Initializes registers to their default values - what games expect them to be at boot. This is
|
||||
// for certain registers that may not be explicitly set by games.
|
||||
|
||||
// Reset all registers to zero
|
||||
std::memset(®s, 0, sizeof(regs));
|
||||
|
||||
// Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
|
||||
// needed for ARMS.
|
||||
for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) {
|
||||
regs.viewport[viewport].depth_range_near = 0.0f;
|
||||
regs.viewport[viewport].depth_range_far = 1.0f;
|
||||
}
|
||||
// Doom and Bomberman seems to use the uninitialized registers and just enable blend
|
||||
// so initialize blend registers with sane values
|
||||
regs.blend.equation_rgb = Regs::Blend::Equation::Add;
|
||||
regs.blend.factor_source_rgb = Regs::Blend::Factor::One;
|
||||
regs.blend.factor_dest_rgb = Regs::Blend::Factor::Zero;
|
||||
regs.blend.equation_a = Regs::Blend::Equation::Add;
|
||||
regs.blend.factor_source_a = Regs::Blend::Factor::One;
|
||||
regs.blend.factor_dest_a = Regs::Blend::Factor::Zero;
|
||||
for (std::size_t blend_index = 0; blend_index < Regs::NumRenderTargets; blend_index++) {
|
||||
regs.independent_blend[blend_index].equation_rgb = Regs::Blend::Equation::Add;
|
||||
regs.independent_blend[blend_index].factor_source_rgb = Regs::Blend::Factor::One;
|
||||
regs.independent_blend[blend_index].factor_dest_rgb = Regs::Blend::Factor::Zero;
|
||||
regs.independent_blend[blend_index].equation_a = Regs::Blend::Equation::Add;
|
||||
regs.independent_blend[blend_index].factor_source_a = Regs::Blend::Factor::One;
|
||||
regs.independent_blend[blend_index].factor_dest_a = Regs::Blend::Factor::Zero;
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
|
||||
// Reset the current macro.
|
||||
executing_macro = 0;
|
||||
|
||||
// The requested macro must have been uploaded already.
|
||||
auto macro_code = uploaded_macros.find(method);
|
||||
if (macro_code == uploaded_macros.end()) {
|
||||
LOG_ERROR(HW_GPU, "Macro {:04X} was not uploaded", method);
|
||||
// Lookup the macro offset
|
||||
const u32 entry{(method - MacroRegistersStart) >> 1};
|
||||
const auto& search{macro_offsets.find(entry)};
|
||||
if (search == macro_offsets.end()) {
|
||||
LOG_CRITICAL(HW_GPU, "macro not found for method 0x{:X}!", method);
|
||||
UNREACHABLE();
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute the current macro.
|
||||
macro_interpreter.Execute(macro_code->second, std::move(parameters));
|
||||
macro_interpreter.Execute(search->second, std::move(parameters));
|
||||
}
|
||||
|
||||
void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
|
||||
@@ -79,6 +115,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
|
||||
ProcessMacroUpload(value);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(macros.bind): {
|
||||
ProcessMacroBind(value);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
|
||||
@@ -140,16 +180,20 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessMacroUpload(u32 data) {
|
||||
// Store the uploaded macro code to interpret them when they're called.
|
||||
auto& macro = uploaded_macros[regs.macros.entry * 2 + MacroRegistersStart];
|
||||
macro.push_back(data);
|
||||
ASSERT_MSG(regs.macros.upload_address < macro_memory.size(),
|
||||
"upload_address exceeded macro_memory size!");
|
||||
macro_memory[regs.macros.upload_address++] = data;
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessMacroBind(u32 data) {
|
||||
macro_offsets[regs.macros.entry] = data;
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessQueryGet() {
|
||||
GPUVAddr sequence_address = regs.query.QueryAddress();
|
||||
// Since the sequence address is given as a GPU VAddr, we have to convert it to an application
|
||||
// VAddr before writing.
|
||||
boost::optional<VAddr> address = memory_manager.GpuToCpuAddress(sequence_address);
|
||||
std::optional<VAddr> address = memory_manager.GpuToCpuAddress(sequence_address);
|
||||
|
||||
// TODO(Subv): Support the other query units.
|
||||
ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
|
||||
@@ -267,7 +311,7 @@ void Maxwell3D::ProcessCBData(u32 value) {
|
||||
// Don't allow writing past the end of the buffer.
|
||||
ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size);
|
||||
|
||||
boost::optional<VAddr> address =
|
||||
std::optional<VAddr> address =
|
||||
memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos);
|
||||
|
||||
Memory::Write32(*address, value);
|
||||
@@ -280,7 +324,7 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
|
||||
GPUVAddr tic_base_address = regs.tic.TICAddress();
|
||||
|
||||
GPUVAddr tic_address_gpu = tic_base_address + tic_index * sizeof(Texture::TICEntry);
|
||||
boost::optional<VAddr> tic_address_cpu = memory_manager.GpuToCpuAddress(tic_address_gpu);
|
||||
std::optional<VAddr> tic_address_cpu = memory_manager.GpuToCpuAddress(tic_address_gpu);
|
||||
|
||||
Texture::TICEntry tic_entry;
|
||||
Memory::ReadBlock(*tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry));
|
||||
@@ -304,7 +348,7 @@ Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
|
||||
GPUVAddr tsc_base_address = regs.tsc.TSCAddress();
|
||||
|
||||
GPUVAddr tsc_address_gpu = tsc_base_address + tsc_index * sizeof(Texture::TSCEntry);
|
||||
boost::optional<VAddr> tsc_address_cpu = memory_manager.GpuToCpuAddress(tsc_address_gpu);
|
||||
std::optional<VAddr> tsc_address_cpu = memory_manager.GpuToCpuAddress(tsc_address_gpu);
|
||||
|
||||
Texture::TSCEntry tsc_entry;
|
||||
Memory::ReadBlock(*tsc_address_cpu, &tsc_entry, sizeof(Texture::TSCEntry));
|
||||
@@ -368,7 +412,7 @@ Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage,
|
||||
|
||||
ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size);
|
||||
|
||||
boost::optional<VAddr> tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address);
|
||||
std::optional<VAddr> tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address);
|
||||
Texture::TextureHandle tex_handle{Memory::Read32(*tex_address_cpu)};
|
||||
|
||||
Texture::FullTextureInfo tex_info{};
|
||||
|
||||
@@ -462,6 +462,16 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct ColorMask {
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 4, u32> R;
|
||||
BitField<4, 4, u32> G;
|
||||
BitField<8, 4, u32> B;
|
||||
BitField<12, 4, u32> A;
|
||||
};
|
||||
};
|
||||
|
||||
bool IsShaderConfigEnabled(std::size_t index) const {
|
||||
// The VertexB is always enabled.
|
||||
if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
|
||||
@@ -475,12 +485,13 @@ public:
|
||||
INSERT_PADDING_WORDS(0x45);
|
||||
|
||||
struct {
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u32 upload_address;
|
||||
u32 data;
|
||||
u32 entry;
|
||||
u32 bind;
|
||||
} macros;
|
||||
|
||||
INSERT_PADDING_WORDS(0x189);
|
||||
INSERT_PADDING_WORDS(0x188);
|
||||
|
||||
u32 tfb_enabled;
|
||||
|
||||
@@ -570,7 +581,11 @@ public:
|
||||
u32 stencil_back_mask;
|
||||
u32 stencil_back_func_mask;
|
||||
|
||||
INSERT_PADDING_WORDS(0x13);
|
||||
INSERT_PADDING_WORDS(0xC);
|
||||
|
||||
u32 color_mask_common;
|
||||
|
||||
INSERT_PADDING_WORDS(0x6);
|
||||
|
||||
u32 rt_separate_frag_data;
|
||||
|
||||
@@ -645,8 +660,14 @@ public:
|
||||
ComparisonOp depth_test_func;
|
||||
float alpha_test_ref;
|
||||
ComparisonOp alpha_test_func;
|
||||
|
||||
INSERT_PADDING_WORDS(0x9);
|
||||
u32 draw_tfb_stride;
|
||||
struct {
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
float a;
|
||||
} blend_color;
|
||||
INSERT_PADDING_WORDS(0x4);
|
||||
|
||||
struct {
|
||||
u32 separate_alpha;
|
||||
@@ -723,7 +744,11 @@ public:
|
||||
StencilOp stencil_back_op_zpass;
|
||||
ComparisonOp stencil_back_func_func;
|
||||
|
||||
INSERT_PADDING_WORDS(0x17);
|
||||
INSERT_PADDING_WORDS(0x4);
|
||||
|
||||
u32 framebuffer_srgb;
|
||||
|
||||
INSERT_PADDING_WORDS(0x12);
|
||||
|
||||
union {
|
||||
BitField<2, 1, u32> coord_origin;
|
||||
@@ -751,7 +776,14 @@ public:
|
||||
};
|
||||
} draw;
|
||||
|
||||
INSERT_PADDING_WORDS(0x6B);
|
||||
INSERT_PADDING_WORDS(0xA);
|
||||
|
||||
struct {
|
||||
u32 enabled;
|
||||
u32 index;
|
||||
} primitive_restart;
|
||||
|
||||
INSERT_PADDING_WORDS(0x5F);
|
||||
|
||||
struct {
|
||||
u32 start_addr_high;
|
||||
@@ -829,8 +861,9 @@ public:
|
||||
BitField<6, 4, u32> RT;
|
||||
BitField<10, 11, u32> layer;
|
||||
} clear_buffers;
|
||||
|
||||
INSERT_PADDING_WORDS(0x4B);
|
||||
INSERT_PADDING_WORDS(0xB);
|
||||
std::array<ColorMask, NumRenderTargets> color_mask;
|
||||
INSERT_PADDING_WORDS(0x38);
|
||||
|
||||
struct {
|
||||
u32 query_address_high;
|
||||
@@ -983,10 +1016,25 @@ public:
|
||||
/// Returns the texture information for a specific texture in a specific shader stage.
|
||||
Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const;
|
||||
|
||||
/// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than
|
||||
/// we've seen used.
|
||||
using MacroMemory = std::array<u32, 0x40000>;
|
||||
|
||||
/// Gets a reference to macro memory.
|
||||
const MacroMemory& GetMacroMemory() const {
|
||||
return macro_memory;
|
||||
}
|
||||
|
||||
private:
|
||||
void InitializeRegisterDefaults();
|
||||
|
||||
VideoCore::RasterizerInterface& rasterizer;
|
||||
|
||||
std::unordered_map<u32, std::vector<u32>> uploaded_macros;
|
||||
/// Start offsets of each macro in macro_memory
|
||||
std::unordered_map<u32, u32> macro_offsets;
|
||||
|
||||
/// Memory for macro code
|
||||
MacroMemory macro_memory;
|
||||
|
||||
/// Macro method that is currently being executed / being fed parameters.
|
||||
u32 executing_macro = 0;
|
||||
@@ -1009,9 +1057,12 @@ private:
|
||||
*/
|
||||
void CallMacroMethod(u32 method, std::vector<u32> parameters);
|
||||
|
||||
/// Handles writes to the macro uploading registers.
|
||||
/// Handles writes to the macro uploading register.
|
||||
void ProcessMacroUpload(u32 data);
|
||||
|
||||
/// Handles writes to the macro bind register.
|
||||
void ProcessMacroBind(u32 data);
|
||||
|
||||
/// Handles a write to the CLEAR_BUFFERS register.
|
||||
void ProcessClearBuffers();
|
||||
|
||||
@@ -1045,6 +1096,7 @@ ASSERT_REG_POSITION(scissor_test, 0x380);
|
||||
ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
|
||||
ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
|
||||
ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
|
||||
ASSERT_REG_POSITION(color_mask_common, 0x3E4);
|
||||
ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB);
|
||||
ASSERT_REG_POSITION(zeta, 0x3F8);
|
||||
ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
|
||||
@@ -1057,6 +1109,10 @@ ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
|
||||
ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB);
|
||||
ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2);
|
||||
ASSERT_REG_POSITION(depth_test_func, 0x4C3);
|
||||
ASSERT_REG_POSITION(alpha_test_ref, 0x4C4);
|
||||
ASSERT_REG_POSITION(alpha_test_func, 0x4C5);
|
||||
ASSERT_REG_POSITION(draw_tfb_stride, 0x4C6);
|
||||
ASSERT_REG_POSITION(blend_color, 0x4C7);
|
||||
ASSERT_REG_POSITION(blend, 0x4CF);
|
||||
ASSERT_REG_POSITION(stencil_enable, 0x4E0);
|
||||
ASSERT_REG_POSITION(stencil_front_op_fail, 0x4E1);
|
||||
@@ -1077,14 +1133,17 @@ ASSERT_REG_POSITION(stencil_back_op_fail, 0x566);
|
||||
ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567);
|
||||
ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568);
|
||||
ASSERT_REG_POSITION(stencil_back_func_func, 0x569);
|
||||
ASSERT_REG_POSITION(framebuffer_srgb, 0x56E);
|
||||
ASSERT_REG_POSITION(point_coord_replace, 0x581);
|
||||
ASSERT_REG_POSITION(code_address, 0x582);
|
||||
ASSERT_REG_POSITION(draw, 0x585);
|
||||
ASSERT_REG_POSITION(primitive_restart, 0x591);
|
||||
ASSERT_REG_POSITION(index_array, 0x5F2);
|
||||
ASSERT_REG_POSITION(instanced_arrays, 0x620);
|
||||
ASSERT_REG_POSITION(cull, 0x646);
|
||||
ASSERT_REG_POSITION(logic_op, 0x671);
|
||||
ASSERT_REG_POSITION(clear_buffers, 0x674);
|
||||
ASSERT_REG_POSITION(color_mask, 0x680);
|
||||
ASSERT_REG_POSITION(query, 0x6C0);
|
||||
ASSERT_REG_POSITION(vertex_array[0], 0x700);
|
||||
ASSERT_REG_POSITION(independent_blend, 0x780);
|
||||
|
||||
@@ -5,12 +5,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -208,6 +207,16 @@ enum class UniformType : u64 {
|
||||
Double = 5,
|
||||
};
|
||||
|
||||
enum class StoreType : u64 {
|
||||
Unsigned8 = 0,
|
||||
Signed8 = 1,
|
||||
Unsigned16 = 2,
|
||||
Signed16 = 3,
|
||||
Bytes32 = 4,
|
||||
Bytes64 = 5,
|
||||
Bytes128 = 6,
|
||||
};
|
||||
|
||||
enum class IMinMaxExchange : u64 {
|
||||
None = 0,
|
||||
XLo = 1,
|
||||
@@ -568,6 +577,10 @@ union Instruction {
|
||||
BitField<55, 1, u64> saturate;
|
||||
} fmul32;
|
||||
|
||||
union {
|
||||
BitField<52, 1, u64> generates_cc;
|
||||
} op_32;
|
||||
|
||||
union {
|
||||
BitField<48, 1, u64> is_signed;
|
||||
} shift;
|
||||
@@ -747,6 +760,18 @@ union Instruction {
|
||||
BitField<44, 2, u64> unknown;
|
||||
} ld_c;
|
||||
|
||||
union {
|
||||
BitField<48, 3, StoreType> type;
|
||||
} ldst_sl;
|
||||
|
||||
union {
|
||||
BitField<44, 2, u64> unknown;
|
||||
} ld_l;
|
||||
|
||||
union {
|
||||
BitField<44, 2, u64> unknown;
|
||||
} st_l;
|
||||
|
||||
union {
|
||||
BitField<0, 3, u64> pred0;
|
||||
BitField<3, 3, u64> pred3;
|
||||
@@ -1209,6 +1234,8 @@ union Instruction {
|
||||
BitField<61, 1, u64> is_b_imm;
|
||||
BitField<60, 1, u64> is_b_gpr;
|
||||
BitField<59, 1, u64> is_c_gpr;
|
||||
BitField<20, 24, s64> smem_imm;
|
||||
BitField<0, 5, ControlCode> flow_control_code;
|
||||
|
||||
Attribute attribute;
|
||||
Sampler sampler;
|
||||
@@ -1232,8 +1259,12 @@ public:
|
||||
BRA,
|
||||
PBK,
|
||||
LD_A,
|
||||
LD_L,
|
||||
LD_S,
|
||||
LD_C,
|
||||
ST_A,
|
||||
ST_L,
|
||||
ST_S,
|
||||
LDG, // Load from global memory
|
||||
STG, // Store in global memory
|
||||
TEX,
|
||||
@@ -1429,7 +1460,7 @@ public:
|
||||
Type type;
|
||||
};
|
||||
|
||||
static boost::optional<const Matcher&> Decode(Instruction instr) {
|
||||
static std::optional<std::reference_wrapper<const Matcher>> Decode(Instruction instr) {
|
||||
static const auto table{GetDecodeTable()};
|
||||
|
||||
const auto matches_instruction = [instr](const auto& matcher) {
|
||||
@@ -1437,7 +1468,8 @@ public:
|
||||
};
|
||||
|
||||
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
|
||||
return iter != table.end() ? boost::optional<const Matcher&>(*iter) : boost::none;
|
||||
return iter != table.end() ? std::optional<std::reference_wrapper<const Matcher>>(*iter)
|
||||
: std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -1490,8 +1522,12 @@ private:
|
||||
INST("111000110100---", Id::BRK, Type::Flow, "BRK"),
|
||||
INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
|
||||
INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
|
||||
INST("1110111101001---", Id::LD_S, Type::Memory, "LD_S"),
|
||||
INST("1110111101000---", Id::LD_L, Type::Memory, "LD_L"),
|
||||
INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
|
||||
INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
|
||||
INST("1110111101011---", Id::ST_S, Type::Memory, "ST_S"),
|
||||
INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"),
|
||||
INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
|
||||
INST("1110111011011---", Id::STG, Type::Memory, "STG"),
|
||||
INST("110000----111---", Id::TEX, Type::Memory, "TEX"),
|
||||
@@ -1627,4 +1663,4 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Tegra::Shader
|
||||
} // namespace Tegra::Shader
|
||||
|
||||
@@ -96,6 +96,11 @@ struct Header {
|
||||
}
|
||||
} ps;
|
||||
};
|
||||
|
||||
u64 GetLocalMemorySize() {
|
||||
return (common1.shader_local_memory_low_size |
|
||||
(common2.shader_local_memory_high_size << 24));
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Header) == 0x50, "Incorrect structure size");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user