summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt12
-rw-r--r--src/core/arm/arm_interface.cpp201
-rw-r--r--src/core/arm/arm_interface.h11
-rw-r--r--src/core/constants.cpp17
-rw-r--r--src/core/constants.h17
-rw-r--r--src/core/core.cpp28
-rw-r--r--src/core/core.h5
-rw-r--r--src/core/core_timing_util.cpp42
-rw-r--r--src/core/core_timing_util.h52
-rw-r--r--src/core/crypto/key_manager.cpp4
-rw-r--r--src/core/file_sys/card_image.cpp46
-rw-r--r--src/core/file_sys/content_archive.cpp4
-rw-r--r--src/core/file_sys/control_metadata.cpp8
-rw-r--r--src/core/file_sys/control_metadata.h5
-rw-r--r--src/core/file_sys/ips_layer.cpp3
-rw-r--r--src/core/file_sys/nca_metadata.h9
-rw-r--r--src/core/file_sys/patch_manager.cpp8
-rw-r--r--src/core/file_sys/registered_cache.cpp14
-rw-r--r--src/core/file_sys/submission_package.cpp13
-rw-r--r--src/core/file_sys/xts_archive.cpp2
-rw-r--r--src/core/frontend/framebuffer_layout.cpp13
-rw-r--r--src/core/frontend/framebuffer_layout.h21
-rw-r--r--src/core/hle/kernel/process.cpp18
-rw-r--r--src/core/hle/kernel/process.h14
-rw-r--r--src/core/hle/kernel/svc.cpp34
-rw-r--r--src/core/hle/kernel/thread.cpp4
-rw-r--r--src/core/hle/kernel/vm_manager.cpp6
-rw-r--r--src/core/hle/kernel/vm_manager.h2
-rw-r--r--src/core/hle/service/acc/acc.cpp71
-rw-r--r--src/core/hle/service/acc/acc.h9
-rw-r--r--src/core/hle/service/acc/acc_aa.cpp5
-rw-r--r--src/core/hle/service/acc/acc_aa.h4
-rw-r--r--src/core/hle/service/acc/acc_su.cpp5
-rw-r--r--src/core/hle/service/acc/acc_su.h4
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp9
-rw-r--r--src/core/hle/service/acc/acc_u0.h4
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp5
-rw-r--r--src/core/hle/service/acc/acc_u1.h4
-rw-r--r--src/core/hle/service/am/am.cpp58
-rw-r--r--src/core/hle/service/am/am.h3
-rw-r--r--src/core/hle/service/am/applets/applets.cpp49
-rw-r--r--src/core/hle/service/am/applets/applets.h43
-rw-r--r--src/core/hle/service/am/applets/error.cpp19
-rw-r--r--src/core/hle/service/am/applets/general_backend.cpp22
-rw-r--r--src/core/hle/service/am/applets/general_backend.h5
-rw-r--r--src/core/hle/service/audio/audout_u.cpp23
-rw-r--r--src/core/hle/service/fatal/fatal.cpp26
-rw-r--r--src/core/hle/service/ldr/ldr.cpp2
-rw-r--r--src/core/hle/service/ncm/ncm.cpp80
-rw-r--r--src/core/hle/service/ns/errors.h12
-rw-r--r--src/core/hle/service/ns/language.cpp392
-rw-r--r--src/core/hle/service/ns/language.h45
-rw-r--r--src/core/hle/service/ns/ns.cpp862
-rw-r--r--src/core/hle/service/ns/ns.h82
-rw-r--r--src/core/hle/service/ns/ns_language.h42
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp3
-rw-r--r--src/core/hle/service/prepo/prepo.cpp25
-rw-r--r--src/core/hle/service/service.cpp5
-rw-r--r--src/core/hle/service/time/time.cpp9
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp11
-rw-r--r--src/core/loader/deconstructed_rom_directory.h4
-rw-r--r--src/core/loader/loader.h17
-rw-r--r--src/core/loader/nax.cpp4
-rw-r--r--src/core/loader/nax.h2
-rw-r--r--src/core/loader/nca.cpp9
-rw-r--r--src/core/loader/nca.h2
-rw-r--r--src/core/loader/nso.cpp13
-rw-r--r--src/core/loader/nso.h5
-rw-r--r--src/core/loader/nsp.cpp4
-rw-r--r--src/core/loader/nsp.h2
-rw-r--r--src/core/loader/xci.cpp4
-rw-r--r--src/core/loader/xci.h2
-rw-r--r--src/core/reporter.cpp353
-rw-r--r--src/core/reporter.h56
-rw-r--r--src/core/settings.cpp1
-rw-r--r--src/core/settings.h2
-rw-r--r--src/core/telemetry_session.cpp59
-rw-r--r--src/core/telemetry_session.h31
-rw-r--r--src/core/tracer/citrace.h100
-rw-r--r--src/core/tracer/recorder.cpp208
-rw-r--r--src/core/tracer/recorder.h87
81 files changed, 2388 insertions, 1128 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 60caf288d..cdb3bf6ab 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -5,6 +5,8 @@ add_library(core STATIC
arm/exclusive_monitor.h
arm/unicorn/arm_unicorn.cpp
arm/unicorn/arm_unicorn.h
+ constants.cpp
+ constants.h
core.cpp
core.h
core_cpu.cpp
@@ -330,6 +332,9 @@ add_library(core STATIC
hle/service/nim/nim.h
hle/service/npns/npns.cpp
hle/service/npns/npns.h
+ hle/service/ns/errors.h
+ hle/service/ns/language.cpp
+ hle/service/ns/language.h
hle/service/ns/ns.cpp
hle/service/ns/ns.h
hle/service/ns/pl_u.cpp
@@ -458,19 +463,18 @@ add_library(core STATIC
memory_setup.h
perf_stats.cpp
perf_stats.h
+ reporter.cpp
+ reporter.h
settings.cpp
settings.h
telemetry_session.cpp
telemetry_session.h
- tracer/citrace.h
- tracer/recorder.cpp
- tracer/recorder.h
)
create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
-target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt mbedtls opus unicorn open_source_archives)
+target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt json-headers mbedtls opus unicorn open_source_archives)
if (ENABLE_WEB_SERVICE)
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
target_link_libraries(core PRIVATE web_service)
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 2223cbeed..372612c9b 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -2,26 +2,213 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <map>
+#include <optional>
+#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
+#include "core/core.h"
+#include "core/loader/loader.h"
#include "core/memory.h"
namespace Core {
-void ARM_Interface::LogBacktrace() const {
- VAddr fp = GetReg(29);
- VAddr lr = GetReg(30);
- const VAddr sp = GetReg(13);
- const VAddr pc = GetPC();
- LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
+namespace {
+
+constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
+constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
+constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
+constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
+
+enum class ELFSymbolType : u8 {
+ None = 0,
+ Object = 1,
+ Function = 2,
+ Section = 3,
+ File = 4,
+ Common = 5,
+ TLS = 6,
+};
+
+enum class ELFSymbolBinding : u8 {
+ Local = 0,
+ Global = 1,
+ Weak = 2,
+};
+
+enum class ELFSymbolVisibility : u8 {
+ Default = 0,
+ Internal = 1,
+ Hidden = 2,
+ Protected = 3,
+};
+
+struct ELFSymbol {
+ u32 name_index;
+ union {
+ u8 info;
+
+ BitField<0, 4, ELFSymbolType> type;
+ BitField<4, 4, ELFSymbolBinding> binding;
+ };
+ ELFSymbolVisibility visibility;
+ u16 sh_index;
+ u64 value;
+ u64 size;
+};
+static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
+
+using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
+
+Symbols GetSymbols(VAddr text_offset) {
+ const auto mod_offset = text_offset + Memory::Read32(text_offset + 4);
+
+ if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
+ Memory::Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
+ return {};
+ }
+
+ const auto dynamic_offset = Memory::Read32(mod_offset + 0x4) + mod_offset;
+
+ VAddr string_table_offset{};
+ VAddr symbol_table_offset{};
+ u64 symbol_entry_size{};
+
+ VAddr dynamic_index = dynamic_offset;
+ while (true) {
+ const auto tag = Memory::Read64(dynamic_index);
+ const auto value = Memory::Read64(dynamic_index + 0x8);
+ dynamic_index += 0x10;
+
+ if (tag == ELF_DYNAMIC_TAG_NULL) {
+ break;
+ }
+
+ if (tag == ELF_DYNAMIC_TAG_STRTAB) {
+ string_table_offset = value;
+ } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
+ symbol_table_offset = value;
+ } else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
+ symbol_entry_size = value;
+ }
+ }
+
+ if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
+ return {};
+ }
+
+ const auto string_table_address = text_offset + string_table_offset;
+ const auto symbol_table_address = text_offset + symbol_table_offset;
+
+ Symbols out;
+
+ VAddr symbol_index = symbol_table_address;
+ while (symbol_index < string_table_address) {
+ ELFSymbol symbol{};
+ Memory::ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
+
+ VAddr string_offset = string_table_address + symbol.name_index;
+ std::string name;
+ for (u8 c = Memory::Read8(string_offset); c != 0; c = Memory::Read8(++string_offset)) {
+ name += static_cast<char>(c);
+ }
+
+ symbol_index += symbol_entry_size;
+ out.push_back({symbol, name});
+ }
+
+ return out;
+}
+
+std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) {
+ const auto iter =
+ std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) {
+ const auto& [symbol, name] = pair;
+ const auto end_address = symbol.value + symbol.size;
+ return func_address >= symbol.value && func_address < end_address;
+ });
+
+ if (iter == symbols.end()) {
+ return std::nullopt;
+ }
+
+ return iter->second;
+}
+
+} // Anonymous namespace
+
+constexpr u64 SEGMENT_BASE = 0x7100000000ull;
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
+ std::vector<BacktraceEntry> out;
+
+ auto fp = GetReg(29);
+ auto lr = GetReg(30);
+
while (true) {
- LOG_ERROR(Core_ARM, "{:016X}", lr);
+ out.push_back({"", 0, lr, 0});
if (!fp) {
break;
}
lr = Memory::Read64(fp + 8) - 4;
fp = Memory::Read64(fp);
}
+
+ std::map<VAddr, std::string> modules;
+ auto& loader{System::GetInstance().GetAppLoader()};
+ if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
+ return {};
+ }
+
+ std::map<std::string, Symbols> symbols;
+ for (const auto& module : modules) {
+ symbols.insert_or_assign(module.second, GetSymbols(module.first));
+ }
+
+ for (auto& entry : out) {
+ VAddr base = 0;
+ for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) {
+ const auto& module{*iter};
+ if (entry.original_address >= module.first) {
+ entry.module = module.second;
+ base = module.first;
+ break;
+ }
+ }
+
+ entry.offset = entry.original_address - base;
+ entry.address = SEGMENT_BASE + entry.offset;
+
+ if (entry.module.empty())
+ entry.module = "unknown";
+
+ const auto symbol_set = symbols.find(entry.module);
+ if (symbol_set != symbols.end()) {
+ const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
+ if (symbol.has_value()) {
+ // TODO(DarkLordZach): Add demangling of symbol names.
+ entry.name = *symbol;
+ }
+ }
+ }
+
+ return out;
+}
+
+void ARM_Interface::LogBacktrace() const {
+ const VAddr sp = GetReg(13);
+ const VAddr pc = GetPC();
+ LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
+ LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
+ "Offset", "Symbol");
+ LOG_ERROR(Core_ARM, "");
+
+ const auto backtrace = GetBacktrace();
+ for (const auto& entry : backtrace) {
+ LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address,
+ entry.original_address, entry.offset, entry.name);
+ }
}
+
} // namespace Core
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 978b1518f..c6691a8e1 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -5,6 +5,7 @@
#pragma once
#include <array>
+#include <vector>
#include "common/common_types.h"
namespace Common {
@@ -152,6 +153,16 @@ public:
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
+ struct BacktraceEntry {
+ std::string module;
+ u64 address;
+ u64 original_address;
+ u64 offset;
+ std::string name;
+ };
+
+ std::vector<BacktraceEntry> GetBacktrace() const;
+
/// fp (= r29) points to the last frame record.
/// Note that this is the frame record for the *previous* frame, not the current one.
/// Note we need to subtract 4 from our last read to get the proper address
diff --git a/src/core/constants.cpp b/src/core/constants.cpp
new file mode 100644
index 000000000..dccb3e03c
--- /dev/null
+++ b/src/core/constants.cpp
@@ -0,0 +1,17 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/constants.h"
+
+namespace Core::Constants {
+const std::array<u8, 107> ACCOUNT_BACKUP_JPEG{{
+ 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
+ 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
+ 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
+ 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
+ 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
+ 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
+ 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
+}};
+}
diff --git a/src/core/constants.h b/src/core/constants.h
new file mode 100644
index 000000000..6d0ec022a
--- /dev/null
+++ b/src/core/constants.h
@@ -0,0 +1,17 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+// This is to consolidate system-wide constants that are used by multiple components of yuzu.
+// This is especially to prevent the case of something in frontend duplicating a constexpr array or
+// directly including some service header for the sole purpose of data.
+namespace Core::Constants {
+
+// ACC Service - Blank JPEG used as user icon in absentia of real one.
+extern const std::array<u8, 107> ACCOUNT_BACKUP_JPEG;
+
+} // namespace Core::Constants
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 7106151bd..b72a1fd6a 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -18,11 +18,6 @@
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
-#include "core/frontend/applets/error.h"
-#include "core/frontend/applets/general_frontend.h"
-#include "core/frontend/applets/profile_select.h"
-#include "core/frontend/applets/software_keyboard.h"
-#include "core/frontend/applets/web_browser.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/kernel.h"
@@ -34,12 +29,10 @@
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
#include "core/perf_stats.h"
+#include "core/reporter.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "file_sys/cheat_engine.h"
-#include "frontend/applets/profile_select.h"
-#include "frontend/applets/software_keyboard.h"
-#include "frontend/applets/web_browser.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -82,7 +75,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
return vfs->OpenFile(path, FileSys::Mode::Read);
}
struct System::Impl {
- explicit Impl(System& system) : kernel{system}, cpu_core_manager{system} {}
+ explicit Impl(System& system) : kernel{system}, cpu_core_manager{system}, reporter{system} {}
Cpu& CurrentCpuCore() {
return cpu_core_manager.GetCurrentCore();
@@ -144,20 +137,10 @@ struct System::Impl {
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
const std::string& filepath) {
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
-
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
return ResultStatus::ErrorGetLoader;
}
- std::pair<std::optional<u32>, Loader::ResultStatus> system_mode =
- app_loader->LoadKernelSystemMode();
-
- if (system_mode.second != Loader::ResultStatus::Success) {
- LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
- static_cast<int>(system_mode.second));
-
- return ResultStatus::ErrorSystemMode;
- }
ResultStatus init_result{Init(system, emu_window)};
if (init_result != ResultStatus::Success) {
@@ -167,6 +150,7 @@ struct System::Impl {
return init_result;
}
+ telemetry_session->AddInitialInfo(*app_loader);
auto main_process = Kernel::Process::Create(system, "main");
const auto [load_result, load_parameters] = app_loader->Load(*main_process);
if (load_result != Loader::ResultStatus::Success) {
@@ -270,6 +254,8 @@ struct System::Impl {
/// Telemetry session for this emulation session
std::unique_ptr<Core::TelemetrySession> telemetry_session;
+ Reporter reporter;
+
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
@@ -509,6 +495,10 @@ void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) {
impl->content_provider->ClearSlot(slot);
}
+const Reporter& System::GetReporter() const {
+ return impl->reporter;
+}
+
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
return impl->Init(*this, emu_window);
}
diff --git a/src/core/core.h b/src/core/core.h
index a9a756a4c..226ef4630 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -8,6 +8,7 @@
#include <memory>
#include <string>
+#include <map>
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
#include "core/hle/kernel/object.h"
@@ -68,6 +69,7 @@ class Cpu;
class ExclusiveMonitor;
class FrameLimiter;
class PerfStats;
+class Reporter;
class TelemetrySession;
struct PerfStatsResults;
@@ -98,7 +100,6 @@ public:
Success, ///< Succeeded
ErrorNotInitialized, ///< Error trying to use core prior to initialization
ErrorGetLoader, ///< Error finding the correct application loader
- ErrorSystemMode, ///< Error determining the system mode
ErrorSystemFiles, ///< Error in finding system files
ErrorSharedFont, ///< Error in finding shared font
ErrorVideoCore, ///< Error in the video core
@@ -285,6 +286,8 @@ public:
void ClearContentProvider(FileSys::ContentProviderUnionSlot slot);
+ const Reporter& GetReporter() const;
+
private:
System();
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp
index c0f08cddb..a10472a95 100644
--- a/src/core/core_timing_util.cpp
+++ b/src/core/core_timing_util.cpp
@@ -13,52 +13,40 @@ namespace Core::Timing {
constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE;
-s64 usToCycles(s64 us) {
- if (static_cast<u64>(us / 1000000) > MAX_VALUE_TO_MULTIPLY) {
+s64 msToCycles(std::chrono::milliseconds ms) {
+ if (static_cast<u64>(ms.count() / 1000) > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
- if (static_cast<u64>(us) > MAX_VALUE_TO_MULTIPLY) {
+ if (static_cast<u64>(ms.count()) > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
- return BASE_CLOCK_RATE * (us / 1000000);
+ return BASE_CLOCK_RATE * (ms.count() / 1000);
}
- return (BASE_CLOCK_RATE * us) / 1000000;
+ return (BASE_CLOCK_RATE * ms.count()) / 1000;
}
-s64 usToCycles(u64 us) {
- if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) {
+s64 usToCycles(std::chrono::microseconds us) {
+ if (static_cast<u64>(us.count() / 1000000) > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
- if (us > MAX_VALUE_TO_MULTIPLY) {
+ if (static_cast<u64>(us.count()) > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
- return BASE_CLOCK_RATE * static_cast<s64>(us / 1000000);
+ return BASE_CLOCK_RATE * (us.count() / 1000000);
}
- return (BASE_CLOCK_RATE * static_cast<s64>(us)) / 1000000;
+ return (BASE_CLOCK_RATE * us.count()) / 1000000;
}
-s64 nsToCycles(s64 ns) {
- if (static_cast<u64>(ns / 1000000000) > MAX_VALUE_TO_MULTIPLY) {
+s64 nsToCycles(std::chrono::nanoseconds ns) {
+ if (static_cast<u64>(ns.count() / 1000000000) > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
- if (static_cast<u64>(ns) > MAX_VALUE_TO_MULTIPLY) {
+ if (static_cast<u64>(ns.count()) > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
- return BASE_CLOCK_RATE * (ns / 1000000000);
+ return BASE_CLOCK_RATE * (ns.count() / 1000000000);
}
- return (BASE_CLOCK_RATE * ns) / 1000000000;
-}
-
-s64 nsToCycles(u64 ns) {
- if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) {
- LOG_ERROR(Core_Timing, "Integer overflow, use max value");
- return std::numeric_limits<s64>::max();
- }
- if (ns > MAX_VALUE_TO_MULTIPLY) {
- LOG_DEBUG(Core_Timing, "Time very big, do rounding");
- return BASE_CLOCK_RATE * (static_cast<s64>(ns) / 1000000000);
- }
- return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000;
+ return (BASE_CLOCK_RATE * ns.count()) / 1000000000;
}
u64 CpuCyclesToClockCycles(u64 ticks) {
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h
index 679aa3123..cdd84d70f 100644
--- a/src/core/core_timing_util.h
+++ b/src/core/core_timing_util.h
@@ -4,6 +4,7 @@
#pragma once
+#include <chrono>
#include "common/common_types.h"
namespace Core::Timing {
@@ -13,53 +14,20 @@ namespace Core::Timing {
constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked
constexpr u64 CNTFREQ = 19200000; // Value from fusee.
-inline s64 msToCycles(int ms) {
- // since ms is int there is no way to overflow
- return BASE_CLOCK_RATE * static_cast<s64>(ms) / 1000;
-}
-
-inline s64 msToCycles(float ms) {
- return static_cast<s64>(BASE_CLOCK_RATE * (0.001f) * ms);
-}
-
-inline s64 msToCycles(double ms) {
- return static_cast<s64>(BASE_CLOCK_RATE * (0.001) * ms);
-}
-
-inline s64 usToCycles(float us) {
- return static_cast<s64>(BASE_CLOCK_RATE * (0.000001f) * us);
-}
-
-inline s64 usToCycles(int us) {
- return (BASE_CLOCK_RATE * static_cast<s64>(us) / 1000000);
-}
-
-s64 usToCycles(s64 us);
-
-s64 usToCycles(u64 us);
-
-inline s64 nsToCycles(float ns) {
- return static_cast<s64>(BASE_CLOCK_RATE * (0.000000001f) * ns);
-}
-
-inline s64 nsToCycles(int ns) {
- return BASE_CLOCK_RATE * static_cast<s64>(ns) / 1000000000;
-}
-
-s64 nsToCycles(s64 ns);
-
-s64 nsToCycles(u64 ns);
+s64 msToCycles(std::chrono::milliseconds ms);
+s64 usToCycles(std::chrono::microseconds us);
+s64 nsToCycles(std::chrono::nanoseconds ns);
-inline u64 cyclesToNs(s64 cycles) {
- return cycles * 1000000000 / BASE_CLOCK_RATE;
+inline std::chrono::milliseconds CyclesToMs(s64 cycles) {
+ return std::chrono::milliseconds(cycles * 1000 / BASE_CLOCK_RATE);
}
-inline s64 cyclesToUs(s64 cycles) {
- return cycles * 1000000 / BASE_CLOCK_RATE;
+inline std::chrono::nanoseconds CyclesToNs(s64 cycles) {
+ return std::chrono::nanoseconds(cycles * 1000000000 / BASE_CLOCK_RATE);
}
-inline u64 cyclesToMs(s64 cycles) {
- return cycles * 1000 / BASE_CLOCK_RATE;
+inline std::chrono::microseconds CyclesToUs(s64 cycles) {
+ return std::chrono::microseconds(cycles * 1000000 / BASE_CLOCK_RATE);
}
u64 CpuCyclesToClockCycles(u64 ticks);
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index dc006e2bb..6dd633363 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -572,7 +572,7 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
<< "# If you are experiencing issues involving keys, it may help to delete this file\n";
}
- file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key));
+ file << fmt::format("\n{} = {}", keyname, Common::HexToString(key));
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, category == KeyCategory::Title);
}
@@ -583,7 +583,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
Key128 rights_id;
std::memcpy(rights_id.data(), &field2, sizeof(u64));
std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
- WriteKeyToFile(KeyCategory::Title, Common::HexArrayToString(rights_id), key);
+ WriteKeyToFile(KeyCategory::Title, Common::HexToString(rights_id), key);
}
auto category = KeyCategory::Standard;
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 2c145bd09..626ed0042 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -18,11 +18,16 @@
namespace FileSys {
-constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", "logo"};
+constexpr std::array partition_names{
+ "update",
+ "normal",
+ "secure",
+ "logo",
+};
XCI::XCI(VirtualFile file_)
: file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
- partitions(0x4) {
+ partitions(partition_names.size()) {
if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
status = Loader::ResultStatus::ErrorBadXCIHeader;
return;
@@ -43,23 +48,24 @@ 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 != nullptr)
- partitions[static_cast<std::size_t>(partition)] =
- std::make_shared<PartitionFilesystem>(raw);
+ const auto partition_idx = static_cast<std::size_t>(partition);
+ auto raw = main_hfs.GetFile(partition_names[partition_idx]);
+
+ if (raw != nullptr) {
+ partitions[partition_idx] = std::make_shared<PartitionFilesystem>(std::move(raw));
+ }
}
secure_partition = std::make_shared<NSP>(
main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]));
- const auto secure_ncas = secure_partition->GetNCAsCollapsed();
- std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas));
-
+ ncas = secure_partition->GetNCAsCollapsed();
program =
secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program);
program_nca_status = secure_partition->GetProgramStatus(secure_partition->GetProgramTitleID());
- if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA)
+ if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA) {
program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
+ }
auto result = AddNCAFromPartition(XCIPartition::Update);
if (result != Loader::ResultStatus::Success) {
@@ -147,8 +153,9 @@ std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
VirtualFile XCI::GetNCAFileByType(NCAContentType type) const {
auto nca = GetNCAByType(type);
- if (nca != nullptr)
+ if (nca != nullptr) {
return nca->GetBaseFile();
+ }
return nullptr;
}
@@ -169,17 +176,22 @@ VirtualDir XCI::GetParentDirectory() const {
}
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
- if (partitions[static_cast<std::size_t>(part)] == nullptr) {
+ const auto partition_index = static_cast<std::size_t>(part);
+ const auto& partition = partitions[partition_index];
+
+ if (partition == nullptr) {
return Loader::ResultStatus::ErrorXCIMissingPartition;
}
- for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
- if (file->GetExtension() != "nca")
+ for (const VirtualFile& file : partition->GetFiles()) {
+ if (file->GetExtension() != "nca") {
continue;
+ }
+
auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
- // TODO(DarkLordZach): Add proper Rev1+ Support
- if (nca->IsUpdate())
+ if (nca->IsUpdate()) {
continue;
+ }
if (nca->GetType() == NCAContentType::Program) {
program_nca_status = nca->GetStatus();
}
@@ -188,7 +200,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
} else {
const u16 error_id = static_cast<u16>(nca->GetStatus());
LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})",
- partition_names[static_cast<std::size_t>(part)], nca->GetName(), error_id,
+ partition_names[partition_index], nca->GetName(), error_id,
nca->GetStatus());
}
}
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 5aa3b600b..ce5c69b41 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -452,13 +452,13 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
switch (s_header.raw.header.crypto_type) {
case NCASectionCryptoType::NONE:
- LOG_DEBUG(Crypto, "called with mode=NONE");
+ LOG_TRACE(Crypto, "called with mode=NONE");
return in;
case NCASectionCryptoType::CTR:
// During normal BKTR decryption, this entire function is skipped. This is for the metadata,
// which uses the same CTR as usual.
case NCASectionCryptoType::BKTR:
- LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
+ LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
{
std::optional<Core::Crypto::Key128> key = {};
if (has_rights_id) {
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 60ea9ad12..f155a1341 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -87,6 +87,14 @@ u64 NACP::GetDefaultJournalSaveSize() const {
return raw.user_account_save_data_journal_size;
}
+bool NACP::GetUserAccountSwitchLock() const {
+ return raw.user_account_switch_lock != 0;
+}
+
+u32 NACP::GetSupportedLanguages() const {
+ return raw.supported_languages;
+}
+
std::vector<u8> NACP::GetRawBytes() const {
std::vector<u8> out(sizeof(RawNACP));
std::memcpy(out.data(), &raw, sizeof(RawNACP));
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 280710ddf..2d8c251ac 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -30,7 +30,8 @@ struct RawNACP {
std::array<LanguageEntry, 16> language_entries;
std::array<u8, 0x25> isbn;
u8 startup_user_account;
- INSERT_PADDING_BYTES(2);
+ u8 user_account_switch_lock;
+ u8 addon_content_registration_type;
u32_le application_attribute;
u32_le supported_languages;
u32_le parental_control;
@@ -109,7 +110,9 @@ public:
std::string GetVersionString() const;
u64 GetDefaultNormalSaveSize() const;
u64 GetDefaultJournalSaveSize() const;
+ u32 GetSupportedLanguages() const;
std::vector<u8> GetRawBytes() const;
+ bool GetUserAccountSwitchLock() const;
private:
RawNACP raw{};
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index 485c4913a..a08a70efd 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -287,7 +287,6 @@ void IPSwitchCompiler::Parse() {
} else {
// hex replacement
const auto value = patch_line.substr(9);
- replace.reserve(value.size() / 2);
replace = Common::HexStringToVector(value, is_little_endian);
}
@@ -295,7 +294,7 @@ void IPSwitchCompiler::Parse() {
LOG_INFO(Loader,
"[IPSwitchCompiler ('{}')] - Patching value at offset 0x{:08X} "
"with byte string '{}'",
- patch_text->GetName(), offset, Common::HexVectorToString(replace));
+ patch_text->GetName(), offset, Common::HexToString(replace));
}
patch.records.insert_or_assign(offset, std::move(replace));
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index 50bf38471..84d5cd1e0 100644
--- a/src/core/file_sys/nca_metadata.h
+++ b/src/core/file_sys/nca_metadata.h
@@ -4,6 +4,7 @@
#pragma once
+#include <array>
#include <memory>
#include <vector>
#include "common/common_funcs.h"
@@ -69,11 +70,15 @@ struct CNMTHeader {
u64_le title_id;
u32_le title_version;
TitleType type;
- INSERT_PADDING_BYTES(1);
+ u8 reserved;
u16_le table_offset;
u16_le number_content_entries;
u16_le number_meta_entries;
- INSERT_PADDING_BYTES(12);
+ u8 attributes;
+ std::array<u8, 2> reserved2;
+ u8 is_committed;
+ u32_le required_download_system_version;
+ std::array<u8, 4> reserved3;
};
static_assert(sizeof(CNMTHeader) == 0x20, "CNMTHeader has incorrect size.");
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 78dbadee3..da823c37b 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -142,7 +142,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
if (!compiler.IsValid())
continue;
- auto this_build_id = Common::HexArrayToString(compiler.GetBuildID());
+ auto this_build_id = Common::HexToString(compiler.GetBuildID());
this_build_id =
this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1);
@@ -168,7 +168,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
return nso;
}
- const auto build_id_raw = Common::HexArrayToString(header.build_id);
+ const auto build_id_raw = Common::HexToString(header.build_id);
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
if (Settings::values.dump_nso) {
@@ -219,7 +219,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
}
bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
- const auto build_id_raw = Common::HexArrayToString(build_id_);
+ const auto build_id_raw = Common::HexToString(build_id_);
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
@@ -235,7 +235,7 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& system, u64 title_id,
const std::array<u8, 0x20>& build_id_,
const VirtualDir& base_path, bool upper) {
- const auto build_id_raw = Common::HexArrayToString(build_id_, upper);
+ const auto build_id_raw = Common::HexToString(build_id_, upper);
const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 3946ff871..58917e094 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -53,13 +53,14 @@ static bool FollowsNcaIdFormat(std::string_view name) {
static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper,
bool within_two_digit) {
- if (!within_two_digit)
- return fmt::format("/{}.nca", Common::HexArrayToString(nca_id, second_hex_upper));
+ if (!within_two_digit) {
+ return fmt::format("/{}.nca", Common::HexToString(nca_id, second_hex_upper));
+ }
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
return fmt::format("/000000{:02X}/{}.nca", hash[0],
- Common::HexArrayToString(nca_id, second_hex_upper));
+ Common::HexToString(nca_id, second_hex_upper));
}
static std::string GetCNMTName(TitleType type, u64 title_id) {
@@ -376,10 +377,11 @@ std::vector<ContentProviderEntry> RegisteredCache::ListEntriesFilter(
}
static std::shared_ptr<NCA> GetNCAFromNSPForID(const NSP& nsp, const NcaID& id) {
- const auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
- if (file == nullptr)
+ auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexToString(id, false)));
+ if (file == nullptr) {
return nullptr;
- return std::make_shared<NCA>(file);
+ }
+ return std::make_shared<NCA>(std::move(file));
}
InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_exists,
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index c69caae0f..d0428a457 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -235,16 +235,18 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
const auto section0 = nca->GetSubdirectories()[0];
for (const auto& inner_file : section0->GetFiles()) {
- if (inner_file->GetExtension() != "cnmt")
+ if (inner_file->GetExtension() != "cnmt") {
continue;
+ }
const CNMT cnmt(inner_file);
auto& ncas_title = ncas[cnmt.GetTitleID()];
ncas_title[{cnmt.GetType(), ContentRecordType::Meta}] = nca;
for (const auto& rec : cnmt.GetContentRecords()) {
- const auto id_string = Common::HexArrayToString(rec.nca_id, false);
- const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
+ const auto id_string = Common::HexToString(rec.nca_id, false);
+ auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
+
if (next_file == nullptr) {
LOG_WARNING(Service_FS,
"NCA with ID {}.nca is listed in content metadata, but cannot "
@@ -253,9 +255,10 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
continue;
}
- auto next_nca = std::make_shared<NCA>(next_file, nullptr, 0, keys);
- if (next_nca->GetType() == NCAContentType::Program)
+ auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0, keys);
+ if (next_nca->GetType() == NCAContentType::Program) {
program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
+ }
if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
(next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
(cnmt.GetTitleID() & 0x800) != 0)) {
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index eec51c64e..4bc5cb2ee 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -66,7 +66,7 @@ NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
- Common::HexArrayToString(nca_id, false)));
+ Common::HexToString(nca_id, false)));
}
NAX::~NAX() = default;
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index a1357179f..d6d2cf3f0 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -20,7 +20,7 @@ static Common::Rectangle<T> MaxRectangle(Common::Rectangle<T> window_area,
static_cast<T>(std::round(scale * screen_aspect_ratio))};
}
-FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) {
+FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
ASSERT(width > 0);
ASSERT(height > 0);
// The drawing code needs at least somewhat valid values for both screens
@@ -29,22 +29,23 @@ FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) {
const float emulation_aspect_ratio{static_cast<float>(ScreenUndocked::Height) /
ScreenUndocked::Width};
- Common::Rectangle<unsigned> screen_window_area{0, 0, width, height};
- Common::Rectangle<unsigned> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
+ const auto window_aspect_ratio = static_cast<float>(height) / width;
- float window_aspect_ratio = static_cast<float>(height) / width;
+ const Common::Rectangle<u32> screen_window_area{0, 0, width, height};
+ Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
if (window_aspect_ratio < emulation_aspect_ratio) {
screen = screen.TranslateX((screen_window_area.GetWidth() - screen.GetWidth()) / 2);
} else {
screen = screen.TranslateY((height - screen.GetHeight()) / 2);
}
+
res.screen = screen;
return res;
}
-FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale) {
- int width, height;
+FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
+ u32 width, height;
if (Settings::values.use_docked_mode) {
width = ScreenDocked::WidthDocked * res_scale;
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index c2c63d08c..d2370adde 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -8,15 +8,22 @@
namespace Layout {
-enum ScreenUndocked : unsigned { Width = 1280, Height = 720 };
-enum ScreenDocked : unsigned { WidthDocked = 1920, HeightDocked = 1080 };
+enum ScreenUndocked : u32 {
+ Width = 1280,
+ Height = 720,
+};
+
+enum ScreenDocked : u32 {
+ WidthDocked = 1920,
+ HeightDocked = 1080,
+};
/// Describes the layout of the window framebuffer
struct FramebufferLayout {
- unsigned width{ScreenUndocked::Width};
- unsigned height{ScreenUndocked::Height};
+ u32 width{ScreenUndocked::Width};
+ u32 height{ScreenUndocked::Height};
- Common::Rectangle<unsigned> screen;
+ Common::Rectangle<u32> screen;
/**
* Returns the ration of pixel size of the screen, compared to the native size of the undocked
@@ -33,12 +40,12 @@ struct FramebufferLayout {
* @param height Window framebuffer height in pixels
* @return Newly created FramebufferLayout object with default screen regions initialized
*/
-FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height);
+FramebufferLayout DefaultFrameLayout(u32 width, u32 height);
/**
* Convenience method to get frame layout by resolution scale
* @param res_scale resolution scale factor
*/
-FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale);
+FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale);
} // namespace Layout
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 0775a89fb..2b81a8d4f 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -48,7 +48,7 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
}
} // Anonymous namespace
-SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) {
+SharedPtr<Process> Process::Create(Core::System& system, std::string name) {
auto& kernel = system.Kernel();
SharedPtr<Process> process(new Process(system));
@@ -72,10 +72,26 @@ SharedPtr<ResourceLimit> Process::GetResourceLimit() const {
return resource_limit;
}
+u64 Process::GetTotalPhysicalMemoryAvailable() const {
+ return vm_manager.GetTotalPhysicalMemoryAvailable();
+}
+
+u64 Process::GetTotalPhysicalMemoryAvailableWithoutMmHeap() const {
+ // TODO: Subtract the personal heap size from this when the
+ // personal heap is implemented.
+ return GetTotalPhysicalMemoryAvailable();
+}
+
u64 Process::GetTotalPhysicalMemoryUsed() const {
return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size;
}
+u64 Process::GetTotalPhysicalMemoryUsedWithoutMmHeap() const {
+ // TODO: Subtract the personal heap size from this when the
+ // personal heap is implemented.
+ return GetTotalPhysicalMemoryUsed();
+}
+
void Process::RegisterThread(const Thread* thread) {
thread_list.push_back(thread);
}
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index bf3b7eef3..29e016983 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -10,7 +10,6 @@
#include <list>
#include <string>
#include <vector>
-#include <boost/container/static_vector.hpp>
#include "common/common_types.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/handle_table.h"
@@ -76,7 +75,7 @@ public:
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
- static SharedPtr<Process> Create(Core::System& system, std::string&& name);
+ static SharedPtr<Process> Create(Core::System& system, std::string name);
std::string GetTypeName() const override {
return "Process";
@@ -187,9 +186,20 @@ public:
return random_entropy.at(index);
}
+ /// Retrieves the total physical memory available to this process in bytes.
+ u64 GetTotalPhysicalMemoryAvailable() const;
+
+ /// Retrieves the total physical memory available to this process in bytes,
+ /// without the size of the personal heap added to it.
+ u64 GetTotalPhysicalMemoryAvailableWithoutMmHeap() const;
+
/// Retrieves the total physical memory used by this process in bytes.
u64 GetTotalPhysicalMemoryUsed() const;
+ /// Retrieves the total physical memory used by this process in bytes,
+ /// without the size of the personal heap added to it.
+ u64 GetTotalPhysicalMemoryUsedWithoutMmHeap() const;
+
/// Gets the list of all threads created with this process as their owner.
const std::list<const Thread*>& GetThreadList() const {
return thread_list;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 5a5851f66..de6363ff2 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -38,6 +38,7 @@
#include "core/hle/result.h"
#include "core/hle/service/service.h"
#include "core/memory.h"
+#include "core/reporter.h"
namespace Kernel {
namespace {
@@ -594,6 +595,7 @@ struct BreakReason {
static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
BreakReason break_reason{reason};
bool has_dumped_buffer{};
+ std::vector<u8> debug_buffer;
const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
if (sz == 0 || addr == 0 || has_dumped_buffer) {
@@ -605,7 +607,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
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);
+ debug_buffer.resize(sz);
Memory::ReadBlock(addr, debug_buffer.data(), sz);
std::string hexdump;
for (std::size_t i = 0; i < debug_buffer.size(); i++) {
@@ -664,6 +666,10 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
break;
}
+ system.GetReporter().SaveSvcBreakReport(
+ static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger, info1,
+ info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt);
+
if (!break_reason.signal_debugger) {
LOG_CRITICAL(
Debug_Emulated,
@@ -710,13 +716,13 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
MapRegionSize = 3,
HeapRegionBaseAddr = 4,
HeapRegionSize = 5,
- TotalMemoryUsage = 6,
+ TotalPhysicalMemoryAvailable = 6,
TotalPhysicalMemoryUsed = 7,
IsCurrentProcessBeingDebugged = 8,
RegisterResourceLimit = 9,
IdleTickCount = 10,
RandomEntropy = 11,
- PerformanceCounter = 0xF0000002,
+ ThreadTickCount = 0xF0000002,
// 2.0.0+
ASLRRegionBaseAddr = 12,
ASLRRegionSize = 13,
@@ -730,7 +736,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
PrivilegedProcessId = 19,
// 5.0.0+
UserExceptionContextAddr = 20,
- ThreadTickCount = 0xF0000002,
+ // 6.0.0+
+ TotalPhysicalMemoryAvailableWithoutMmHeap = 21,
+ TotalPhysicalMemoryUsedWithoutMmHeap = 22,
};
const auto info_id_type = static_cast<GetInfoType>(info_id);
@@ -746,12 +754,14 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
case GetInfoType::ASLRRegionSize:
case GetInfoType::NewMapRegionBaseAddr:
case GetInfoType::NewMapRegionSize:
- case GetInfoType::TotalMemoryUsage:
+ case GetInfoType::TotalPhysicalMemoryAvailable:
case GetInfoType::TotalPhysicalMemoryUsed:
case GetInfoType::IsVirtualAddressMemoryEnabled:
case GetInfoType::PersonalMmHeapUsage:
case GetInfoType::TitleId:
- case GetInfoType::UserExceptionContextAddr: {
+ case GetInfoType::UserExceptionContextAddr:
+ case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap:
+ case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap: {
if (info_sub_id != 0) {
return ERR_INVALID_ENUM_VALUE;
}
@@ -804,8 +814,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
*result = process->VMManager().GetNewMapRegionSize();
return RESULT_SUCCESS;
- case GetInfoType::TotalMemoryUsage:
- *result = process->VMManager().GetTotalMemoryUsage();
+ case GetInfoType::TotalPhysicalMemoryAvailable:
+ *result = process->GetTotalPhysicalMemoryAvailable();
return RESULT_SUCCESS;
case GetInfoType::TotalPhysicalMemoryUsed:
@@ -826,6 +836,14 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
*result = 0;
return RESULT_SUCCESS;
+ case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap:
+ *result = process->GetTotalPhysicalMemoryAvailable();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap:
+ *result = process->GetTotalPhysicalMemoryUsedWithoutMmHeap();
+ return RESULT_SUCCESS;
+
default:
break;
}
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 2abf9efca..c73a40977 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -75,9 +75,9 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
// This function might be called from any thread so we have to be cautious and use the
// thread-safe version of ScheduleEvent.
+ const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
Core::System::GetInstance().CoreTiming().ScheduleEventThreadsafe(
- Core::Timing::nsToCycles(nanoseconds), kernel.ThreadWakeupCallbackEventType(),
- callback_handle);
+ cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle);
}
void Thread::CancelWakeupTimer() {
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 48b13cfdd..c929c2a52 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -68,9 +68,7 @@ VMManager::VMManager(Core::System& system) : system{system} {
Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
}
-VMManager::~VMManager() {
- Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
-}
+VMManager::~VMManager() = default;
void VMManager::Reset(FileSys::ProgramAddressSpaceType type) {
Clear();
@@ -758,7 +756,7 @@ VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, Memo
std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask));
}
-u64 VMManager::GetTotalMemoryUsage() const {
+u64 VMManager::GetTotalPhysicalMemoryAvailable() const {
LOG_WARNING(Kernel, "(STUBBED) called");
return 0xF8000000;
}
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index ec84d9a70..dfbf7a894 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -499,7 +499,7 @@ public:
void LogLayout() const;
/// Gets the total memory usage, used by svcGetInfo
- u64 GetTotalMemoryUsage() const;
+ u64 GetTotalPhysicalMemoryAvailable() const;
/// Gets the address space base address
VAddr GetAddressSpaceBaseAddress() const;
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 86bf53d08..025714e5a 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -10,30 +10,22 @@
#include "common/logging/log.h"
#include "common/string_util.h"
#include "common/swap.h"
+#include "core/constants.h"
#include "core/core_timing.h"
+#include "core/file_sys/control_metadata.h"
+#include "core/file_sys/patch_manager.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/service/acc/acc.h"
#include "core/hle/service/acc/acc_aa.h"
#include "core/hle/service/acc/acc_su.h"
#include "core/hle/service/acc/acc_u0.h"
#include "core/hle/service/acc/acc_u1.h"
#include "core/hle/service/acc/profile_manager.h"
+#include "core/loader/loader.h"
namespace Service::Account {
-// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
-// used as a backup should the one on disk not exist
-constexpr u32 backup_jpeg_size = 107;
-constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{
- 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
- 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
- 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
- 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
- 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
- 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
- 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
-}};
-
static std::string GetImagePath(Common::UUID uuid) {
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
@@ -101,8 +93,8 @@ private:
if (!image.IsOpen()) {
LOG_WARNING(Service_ACC,
"Failed to load user provided image! Falling back to built-in backup...");
- ctx.WriteBuffer(backup_jpeg);
- rb.Push<u32>(backup_jpeg_size);
+ ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG);
+ rb.Push<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size());
return;
}
@@ -124,7 +116,7 @@ private:
if (!image.IsOpen()) {
LOG_WARNING(Service_ACC,
"Failed to load user provided image! Falling back to built-in backup...");
- rb.Push<u32>(backup_jpeg_size);
+ rb.Push<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size());
} else {
rb.Push<u32>(SanitizeJPEGSize(image.GetSize()));
}
@@ -225,7 +217,7 @@ void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestCon
rb.Push(profile_manager->CanSystemRegisterUser());
}
-void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) {
+void Module::Interface::InitializeApplicationInfoOld(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -238,6 +230,31 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
rb.PushIpcInterface<IManagerForApplication>();
}
+void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_ACC, "called");
+ FileSys::NACP nacp;
+ const auto res = system.GetAppLoader().ReadControlData(nacp);
+
+ bool is_locked = false;
+
+ if (res != Loader::ResultStatus::Success) {
+ FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
+ auto nacp_unique = pm.GetControlMetadata().first;
+
+ if (nacp_unique != nullptr) {
+ is_locked = nacp_unique->GetUserAccountSwitchLock();
+ } else {
+ LOG_ERROR(Service_ACC, "nacp_unique is null!");
+ }
+ } else {
+ is_locked = nacp.GetUserAccountSwitchLock();
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(is_locked);
+}
+
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
@@ -263,19 +280,25 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
}
Module::Interface::Interface(std::shared_ptr<Module> module,
- std::shared_ptr<ProfileManager> profile_manager, const char* name)
+ std::shared_ptr<ProfileManager> profile_manager, Core::System& system,
+ const char* name)
: ServiceFramework(name), module(std::move(module)),
- profile_manager(std::move(profile_manager)) {}
+ profile_manager(std::move(profile_manager)), system(system) {}
Module::Interface::~Interface() = default;
-void InstallInterfaces(SM::ServiceManager& service_manager) {
+void InstallInterfaces(Core::System& system) {
auto module = std::make_shared<Module>();
auto profile_manager = std::make_shared<ProfileManager>();
- std::make_shared<ACC_AA>(module, profile_manager)->InstallAsService(service_manager);
- std::make_shared<ACC_SU>(module, profile_manager)->InstallAsService(service_manager);
- std::make_shared<ACC_U0>(module, profile_manager)->InstallAsService(service_manager);
- std::make_shared<ACC_U1>(module, profile_manager)->InstallAsService(service_manager);
+
+ std::make_shared<ACC_AA>(module, profile_manager, system)
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<ACC_SU>(module, profile_manager, system)
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<ACC_U0>(module, profile_manager, system)
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<ACC_U1>(module, profile_manager, system)
+ ->InstallAsService(system.ServiceManager());
}
} // namespace Service::Account
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index 89b2104fa..350f123a0 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -15,7 +15,8 @@ public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> module,
- std::shared_ptr<ProfileManager> profile_manager, const char* name);
+ std::shared_ptr<ProfileManager> profile_manager, Core::System& system,
+ const char* name);
~Interface() override;
void GetUserCount(Kernel::HLERequestContext& ctx);
@@ -24,18 +25,20 @@ public:
void ListOpenUsers(Kernel::HLERequestContext& ctx);
void GetLastOpenedUser(Kernel::HLERequestContext& ctx);
void GetProfile(Kernel::HLERequestContext& ctx);
- void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
+ void InitializeApplicationInfoOld(Kernel::HLERequestContext& ctx);
void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);
void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx);
+ void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> module;
std::shared_ptr<ProfileManager> profile_manager;
+ Core::System& system;
};
};
/// Registers all ACC services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(Core::System& system);
} // namespace Service::Account
diff --git a/src/core/hle/service/acc/acc_aa.cpp b/src/core/hle/service/acc/acc_aa.cpp
index e84d9f7cf..3bac6bcd1 100644
--- a/src/core/hle/service/acc/acc_aa.cpp
+++ b/src/core/hle/service/acc/acc_aa.cpp
@@ -6,8 +6,9 @@
namespace Service::Account {
-ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
- : Module::Interface(std::move(module), std::move(profile_manager), "acc:aa") {
+ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system)
+ : Module::Interface(std::move(module), std::move(profile_manager), system, "acc:aa") {
static const FunctionInfo functions[] = {
{0, nullptr, "EnsureCacheAsync"},
{1, nullptr, "LoadCache"},
diff --git a/src/core/hle/service/acc/acc_aa.h b/src/core/hle/service/acc/acc_aa.h
index 9edb0421b..932c04890 100644
--- a/src/core/hle/service/acc/acc_aa.h
+++ b/src/core/hle/service/acc/acc_aa.h
@@ -10,8 +10,8 @@ namespace Service::Account {
class ACC_AA final : public Module::Interface {
public:
- explicit ACC_AA(std::shared_ptr<Module> module,
- std::shared_ptr<ProfileManager> profile_manager);
+ explicit ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system);
~ACC_AA() override;
};
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index d66233cad..1b7ec3ed0 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -6,8 +6,9 @@
namespace Service::Account {
-ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
- : Module::Interface(std::move(module), std::move(profile_manager), "acc:su") {
+ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system)
+ : Module::Interface(std::move(module), std::move(profile_manager), system, "acc:su") {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ACC_SU::GetUserCount, "GetUserCount"},
diff --git a/src/core/hle/service/acc/acc_su.h b/src/core/hle/service/acc/acc_su.h
index fcced063a..0a700d9bf 100644
--- a/src/core/hle/service/acc/acc_su.h
+++ b/src/core/hle/service/acc/acc_su.h
@@ -10,8 +10,8 @@ namespace Service::Account {
class ACC_SU final : public Module::Interface {
public:
- explicit ACC_SU(std::shared_ptr<Module> module,
- std::shared_ptr<ProfileManager> profile_manager);
+ explicit ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system);
~ACC_SU() override;
};
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index 182f7c7e5..2f239e8c0 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -6,8 +6,9 @@
namespace Service::Account {
-ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
- : Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") {
+ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system)
+ : Module::Interface(std::move(module), std::move(profile_manager), system, "acc:u0") {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ACC_U0::GetUserCount, "GetUserCount"},
@@ -21,7 +22,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
{60, nullptr, "ListOpenContextStoredUsers"},
{99, nullptr, "DebugActivateOpenContextRetention"},
- {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
+ {100, &ACC_U0::InitializeApplicationInfoOld, "InitializeApplicationInfoOld"},
{101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
{102, nullptr, "AuthenticateApplicationAsync"},
{103, nullptr, "CheckNetworkServiceAvailabilityAsync"},
@@ -32,7 +33,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{131, nullptr, "ListOpenContextStoredUsers"},
{140, nullptr, "InitializeApplicationInfo"},
{141, nullptr, "ListQualifiedUsers"},
- {150, nullptr, "IsUserAccountSwitchLocked"},
+ {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"},
};
// clang-format on
diff --git a/src/core/hle/service/acc/acc_u0.h b/src/core/hle/service/acc/acc_u0.h
index a1290e0bd..3bd9c3164 100644
--- a/src/core/hle/service/acc/acc_u0.h
+++ b/src/core/hle/service/acc/acc_u0.h
@@ -10,8 +10,8 @@ namespace Service::Account {
class ACC_U0 final : public Module::Interface {
public:
- explicit ACC_U0(std::shared_ptr<Module> module,
- std::shared_ptr<ProfileManager> profile_manager);
+ explicit ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system);
~ACC_U0() override;
};
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp
index 2dd17d935..6520b3968 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -6,8 +6,9 @@
namespace Service::Account {
-ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
- : Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") {
+ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system)
+ : Module::Interface(std::move(module), std::move(profile_manager), system, "acc:u1") {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ACC_U1::GetUserCount, "GetUserCount"},
diff --git a/src/core/hle/service/acc/acc_u1.h b/src/core/hle/service/acc/acc_u1.h
index 9e79daee3..829f8a744 100644
--- a/src/core/hle/service/acc/acc_u1.h
+++ b/src/core/hle/service/acc/acc_u1.h
@@ -10,8 +10,8 @@ namespace Service::Account {
class ACC_U1 final : public Module::Interface {
public:
- explicit ACC_U1(std::shared_ptr<Module> module,
- std::shared_ptr<ProfileManager> profile_manager);
+ explicit ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system);
~ACC_U1() override;
};
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 1a32a109f..4a7bf4acb 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -8,6 +8,8 @@
#include <cstring>
#include "audio_core/audio_renderer.h"
#include "core/core.h"
+#include "core/file_sys/control_metadata.h"
+#include "core/file_sys/patch_manager.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/kernel.h"
@@ -29,9 +31,11 @@
#include "core/hle/service/am/tcap.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvflinger/nvflinger.h"
#include "core/hle/service/pm/pm.h"
#include "core/hle/service/set/set.h"
+#include "core/hle/service/sm/sm.h"
#include "core/hle/service/vi/vi.h"
#include "core/settings.h"
@@ -267,7 +271,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
{71, nullptr, "GetCurrentIlluminanceEx"},
{80, nullptr, "SetWirelessPriorityMode"},
{90, nullptr, "GetAccumulatedSuspendedTickValue"},
- {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"},
+ {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"},
{100, nullptr, "SetAlbumImageTakenNotificationEnabled"},
{1000, nullptr, "GetDebugStorageChannel"},
};
@@ -278,6 +282,11 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
auto& kernel = Core::System::GetInstance().Kernel();
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
"ISelfController:LaunchableEvent");
+
+ // TODO(ogniK): Figure out where, when and why this event gets signalled
+ accumulated_suspended_tick_changed_event = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::Manual, "ISelfController:AccumulatedSuspendedTickChangedEvent");
+ accumulated_suspended_tick_changed_event.writable->Signal(); // Is signalled on creation
}
ISelfController::~ISelfController() = default;
@@ -440,6 +449,17 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
rb.Push<u32>(idle_time_detection_extension);
}
+void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx) {
+ // The implementation of this function is fine as is, the reason we're labelling it as stubbed
+ // is because we're currently unsure when and where accumulated_suspended_tick_changed_event is
+ // actually signalled for the time being.
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(accumulated_suspended_tick_changed_event.readable);
+}
+
AppletMessageQueue::AppletMessageQueue() {
auto& kernel = Core::System::GetInstance().Kernel();
on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
@@ -1100,10 +1120,42 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
// TODO(bunnei): This should be configurable
LOG_DEBUG(Service_AM, "called");
+ // Get supported languages from NACP, if possible
+ // Default to 0 (all languages supported)
+ u32 supported_languages = 0;
+ FileSys::PatchManager pm{Core::System::GetInstance().CurrentProcess()->GetTitleID()};
+
+ const auto res = pm.GetControlMetadata();
+ if (res.first != nullptr) {
+ supported_languages = res.first->GetSupportedLanguages();
+ }
+
+ // Call IApplicationManagerInterface implementation.
+ auto& service_manager = Core::System::GetInstance().ServiceManager();
+ auto ns_am2 = service_manager.GetService<Service::NS::NS>("ns:am2");
+ auto app_man = ns_am2->GetApplicationManagerInterface();
+
+ // Get desired application language
+ const auto res_lang = app_man->GetApplicationDesiredLanguage(supported_languages);
+ if (res_lang.Failed()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res_lang.Code());
+ return;
+ }
+
+ // Convert to settings language code.
+ const auto res_code = app_man->ConvertApplicationLanguageToLanguageCode(*res_lang);
+ if (res_code.Failed()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res_code.Code());
+ return;
+ }
+
+ LOG_DEBUG(Service_AM, "got desired_language={:016X}", *res_code);
+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push(
- static_cast<u64>(Service::Set::GetLanguageCodeFromIndex(Settings::values.language_index)));
+ rb.Push(*res_code);
}
void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 991b7d47c..1fa069e56 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -133,9 +133,12 @@ private:
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
+ void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
Kernel::EventPair launchable_event;
+ Kernel::EventPair accumulated_suspended_tick_changed_event;
+
u32 idle_time_detection_extension = 0;
u64 num_fatal_sections_entered = 0;
};
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index e812c66e9..e3e4ead03 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -35,12 +35,28 @@ AppletDataBroker::AppletDataBroker() {
AppletDataBroker::~AppletDataBroker() = default;
+AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() const {
+ std::vector<std::vector<u8>> out_normal;
+
+ for (const auto& storage : in_channel) {
+ out_normal.push_back(storage->GetData());
+ }
+
+ std::vector<std::vector<u8>> out_interactive;
+
+ for (const auto& storage : in_interactive_channel) {
+ out_interactive.push_back(storage->GetData());
+ }
+
+ return {std::move(out_normal), std::move(out_interactive)};
+}
+
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
if (out_channel.empty())
return nullptr;
auto out = std::move(out_channel.front());
- out_channel.pop();
+ out_channel.pop_front();
return out;
}
@@ -49,7 +65,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
return nullptr;
auto out = std::move(in_channel.front());
- in_channel.pop();
+ in_channel.pop_front();
return out;
}
@@ -58,7 +74,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
return nullptr;
auto out = std::move(out_interactive_channel.front());
- out_interactive_channel.pop();
+ out_interactive_channel.pop_front();
return out;
}
@@ -67,25 +83,25 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
return nullptr;
auto out = std::move(in_interactive_channel.front());
- in_interactive_channel.pop();
+ in_interactive_channel.pop_front();
return out;
}
void AppletDataBroker::PushNormalDataFromGame(IStorage storage) {
- in_channel.push(std::make_unique<IStorage>(storage));
+ in_channel.push_back(std::make_unique<IStorage>(storage));
}
void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
- out_channel.push(std::make_unique<IStorage>(storage));
+ out_channel.push_back(std::make_unique<IStorage>(storage));
pop_out_data_event.writable->Signal();
}
void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
- in_interactive_channel.push(std::make_unique<IStorage>(storage));
+ in_interactive_channel.push_back(std::make_unique<IStorage>(storage));
}
void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
- out_interactive_channel.push(std::make_unique<IStorage>(storage));
+ out_interactive_channel.push_back(std::make_unique<IStorage>(storage));
pop_interactive_out_data_event.writable->Signal();
}
@@ -121,6 +137,21 @@ void Applet::Initialize() {
initialized = true;
}
+AppletFrontendSet::AppletFrontendSet() = default;
+
+AppletFrontendSet::AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer,
+ ProfileSelect profile_select,
+ SoftwareKeyboard software_keyboard, WebBrowser web_browser)
+ : error{std::move(error)}, photo_viewer{std::move(photo_viewer)}, profile_select{std::move(
+ profile_select)},
+ software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)} {}
+
+AppletFrontendSet::~AppletFrontendSet() = default;
+
+AppletFrontendSet::AppletFrontendSet(AppletFrontendSet&&) noexcept = default;
+
+AppletFrontendSet& AppletFrontendSet::operator=(AppletFrontendSet&&) noexcept = default;
+
AppletManager::AppletManager() = default;
AppletManager::~AppletManager() = default;
@@ -189,7 +220,7 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
UNIMPLEMENTED_MSG(
"No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
static_cast<u8>(id));
- return std::make_shared<StubApplet>();
+ return std::make_shared<StubApplet>(id);
}
}
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 7f932672c..05ae739ca 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -54,6 +54,14 @@ public:
AppletDataBroker();
~AppletDataBroker();
+ struct RawChannelData {
+ std::vector<std::vector<u8>> normal;
+ std::vector<std::vector<u8>> interactive;
+ };
+
+ // Retrieves but does not pop the data sent to applet.
+ RawChannelData PeekDataToAppletForDebug() const;
+
std::unique_ptr<IStorage> PopNormalDataToGame();
std::unique_ptr<IStorage> PopNormalDataToApplet();
@@ -76,16 +84,16 @@ private:
// Queues are named from applet's perspective
// PopNormalDataToApplet and PushNormalDataFromGame
- std::queue<std::unique_ptr<IStorage>> in_channel;
+ std::deque<std::unique_ptr<IStorage>> in_channel;
// PopNormalDataToGame and PushNormalDataFromApplet
- std::queue<std::unique_ptr<IStorage>> out_channel;
+ std::deque<std::unique_ptr<IStorage>> out_channel;
// PopInteractiveDataToApplet and PushInteractiveDataFromGame
- std::queue<std::unique_ptr<IStorage>> in_interactive_channel;
+ std::deque<std::unique_ptr<IStorage>> in_interactive_channel;
// PopInteractiveDataToGame and PushInteractiveDataFromApplet
- std::queue<std::unique_ptr<IStorage>> out_interactive_channel;
+ std::deque<std::unique_ptr<IStorage>> out_interactive_channel;
Kernel::EventPair state_changed_event;
@@ -137,11 +145,28 @@ protected:
};
struct AppletFrontendSet {
- std::unique_ptr<Core::Frontend::ErrorApplet> error;
- std::unique_ptr<Core::Frontend::PhotoViewerApplet> photo_viewer;
- std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_select;
- std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
- std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser;
+ using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
+ using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
+ using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
+ using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
+ using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
+
+ AppletFrontendSet();
+ AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer, ProfileSelect profile_select,
+ SoftwareKeyboard software_keyboard, WebBrowser web_browser);
+ ~AppletFrontendSet();
+
+ AppletFrontendSet(const AppletFrontendSet&) = delete;
+ AppletFrontendSet& operator=(const AppletFrontendSet&) = delete;
+
+ AppletFrontendSet(AppletFrontendSet&&) noexcept;
+ AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
+
+ ErrorApplet error;
+ PhotoViewer photo_viewer;
+ ProfileSelect profile_select;
+ SoftwareKeyboard software_keyboard;
+ WebBrowser web_browser;
};
class AppletManager {
diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp
index 04774bedc..af3a900f8 100644
--- a/src/core/hle/service/am/applets/error.cpp
+++ b/src/core/hle/service/am/applets/error.cpp
@@ -9,8 +9,10 @@
#include "common/string_util.h"
#include "core/core.h"
#include "core/frontend/applets/error.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/error.h"
+#include "core/reporter.h"
namespace Service::AM::Applets {
@@ -143,9 +145,12 @@ void Error::Execute() {
}
const auto callback = [this] { DisplayCompleted(); };
+ const auto title_id = Core::CurrentProcess()->GetTitleID();
+ const auto& reporter{Core::System::GetInstance().GetReporter()};
switch (mode) {
case ErrorAppletMode::ShowError:
+ reporter.SaveErrorReport(title_id, error_code);
frontend.ShowError(error_code, callback);
break;
case ErrorAppletMode::ShowSystemError:
@@ -156,14 +161,18 @@ void Error::Execute() {
const auto& detail_text =
system ? args->system_error.detail_text : args->application_error.detail_text;
- frontend.ShowCustomErrorText(
- error_code,
- Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()),
- Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()),
- callback);
+ const auto main_text_string =
+ Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size());
+ const auto detail_text_string =
+ Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size());
+
+ reporter.SaveErrorReport(title_id, error_code, main_text_string, detail_text_string);
+ frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback);
break;
}
case ErrorAppletMode::ShowErrorRecord:
+ reporter.SaveErrorReport(title_id, error_code,
+ fmt::format("{:016X}", args->error_record.posix_time));
frontend.ShowErrorWithTimestamp(
error_code, std::chrono::seconds{args->error_record.posix_time}, callback);
break;
diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp
index c591b9ac2..54c155dd8 100644
--- a/src/core/hle/service/am/applets/general_backend.cpp
+++ b/src/core/hle/service/am/applets/general_backend.cpp
@@ -2,7 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <string>
+#include <string_view>
#include "common/assert.h"
#include "common/hex_util.h"
@@ -13,24 +13,25 @@
#include "core/hle/result.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/general_backend.h"
+#include "core/reporter.h"
namespace Service::AM::Applets {
-static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) {
+static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) {
std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet();
for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
const auto data = storage->GetData();
LOG_INFO(Service_AM,
- "called (STUBBED), during {} recieved normal data with size={:08X}, data={}",
- prefix, data.size(), Common::HexVectorToString(data));
+ "called (STUBBED), during {} received normal data with size={:08X}, data={}",
+ prefix, data.size(), Common::HexToString(data));
}
storage = broker.PopInteractiveDataToApplet();
for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) {
const auto data = storage->GetData();
LOG_INFO(Service_AM,
- "called (STUBBED), during {} recieved interactive data with size={:08X}, data={}",
- prefix, data.size(), Common::HexVectorToString(data));
+ "called (STUBBED), during {} received interactive data with size={:08X}, data={}",
+ prefix, data.size(), Common::HexToString(data));
}
}
@@ -83,13 +84,20 @@ void PhotoViewer::ViewFinished() {
broker.SignalStateChanged();
}
-StubApplet::StubApplet() = default;
+StubApplet::StubApplet(AppletId id) : id(id) {}
StubApplet::~StubApplet() = default;
void StubApplet::Initialize() {
LOG_WARNING(Service_AM, "called (STUBBED)");
Applet::Initialize();
+
+ const auto data = broker.PeekDataToAppletForDebug();
+ Core::System::GetInstance().GetReporter().SaveUnimplementedAppletReport(
+ static_cast<u32>(id), common_args.arguments_version, common_args.library_version,
+ common_args.theme_color, common_args.play_startup_sound, common_args.system_tick,
+ data.normal, data.interactive);
+
LogCurrentStorage(broker, "Initialize");
}
diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h
index 2dd255d7c..fb68a2543 100644
--- a/src/core/hle/service/am/applets/general_backend.h
+++ b/src/core/hle/service/am/applets/general_backend.h
@@ -34,7 +34,7 @@ private:
class StubApplet final : public Applet {
public:
- StubApplet();
+ explicit StubApplet(AppletId id);
~StubApplet() override;
void Initialize() override;
@@ -43,6 +43,9 @@ public:
ResultCode GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
+
+private:
+ AppletId id;
};
} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 6ba41b20a..7db6eb08d 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -58,8 +58,8 @@ public:
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
{10, nullptr, "GetAudioOutPlayedSampleCount"},
{11, nullptr, "FlushAudioOutBuffers"},
- {12, nullptr, "SetAudioOutVolume"},
- {13, nullptr, "GetAudioOutVolume"},
+ {12, &IAudioOut::SetAudioOutVolume, "SetAudioOutVolume"},
+ {13, &IAudioOut::GetAudioOutVolume, "GetAudioOutVolume"},
};
// clang-format on
RegisterHandlers(functions);
@@ -183,6 +183,25 @@ private:
rb.Push(static_cast<u32>(stream->GetQueueSize()));
}
+ void SetAudioOutVolume(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const float volume = rp.Pop<float>();
+ LOG_DEBUG(Service_Audio, "called, volume={}", volume);
+
+ stream->SetVolume(volume);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetAudioOutVolume(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(stream->GetVolume());
+ }
+
AudioCore::AudioOut& audio_core;
AudioCore::StreamPtr stream;
std::string device_name;
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index 2c229bcad..fe49c2161 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -16,6 +16,7 @@
#include "core/hle/service/fatal/fatal.h"
#include "core/hle/service/fatal/fatal_p.h"
#include "core/hle/service/fatal/fatal_u.h"
+#include "core/reporter.h"
namespace Service::Fatal {
@@ -100,27 +101,10 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
LOG_ERROR(Service_Fatal, "{}", crash_report);
- const std::string crashreport_dir =
- FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "crash_logs";
-
- if (!FileUtil::CreateFullPath(crashreport_dir)) {
- LOG_ERROR(
- Service_Fatal,
- "Unable to create crash report directory. Possible log directory permissions issue.");
- return;
- }
-
- const std::time_t t = std::time(nullptr);
- const std::string crashreport_filename =
- fmt::format("{}/{:016x}-{:%F-%H%M%S}.log", crashreport_dir, title_id, *std::localtime(&t));
-
- auto file = FileUtil::IOFile(crashreport_filename, "wb");
- if (file.IsOpen()) {
- file.WriteString(crash_report);
- LOG_ERROR(Service_Fatal, "Saving error report to {}", crashreport_filename);
- } else {
- LOG_ERROR(Service_Fatal, "Failed to save error report to {}", crashreport_filename);
- }
+ Core::System::GetInstance().GetReporter().SaveCrashReport(
+ title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc,
+ info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace,
+ info.backtrace_size, info.ArchAsString(), info.unk10);
}
static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 5af925515..b839303ac 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -310,7 +310,7 @@ public:
if (!IsValidNROHash(hash)) {
LOG_ERROR(Service_LDR,
"NRO hash is not present in any currently loaded NRRs (hash={})!",
- Common::HexArrayToString(hash));
+ Common::HexToString(hash));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_MISSING_NRR_HASH);
return;
diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp
index 5d31f638f..b405a4b66 100644
--- a/src/core/hle/service/ncm/ncm.cpp
+++ b/src/core/hle/service/ncm/ncm.cpp
@@ -4,15 +4,89 @@
#include <memory>
+#include "core/file_sys/romfs_factory.h"
+#include "core/hle/ipc_helpers.h"
#include "core/hle/service/ncm/ncm.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::NCM {
-class LocationResolver final : public ServiceFramework<LocationResolver> {
+class ILocationResolver final : public ServiceFramework<ILocationResolver> {
public:
- explicit LocationResolver() : ServiceFramework{"lr"} {
+ explicit ILocationResolver(FileSys::StorageId id)
+ : ServiceFramework{"ILocationResolver"}, storage(id) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "ResolveProgramPath"},
+ {1, nullptr, "RedirectProgramPath"},
+ {2, nullptr, "ResolveApplicationControlPath"},
+ {3, nullptr, "ResolveApplicationHtmlDocumentPath"},
+ {4, nullptr, "ResolveDataPath"},
+ {5, nullptr, "RedirectApplicationControlPath"},
+ {6, nullptr, "RedirectApplicationHtmlDocumentPath"},
+ {7, nullptr, "ResolveApplicationLegalInformationPath"},
+ {8, nullptr, "RedirectApplicationLegalInformationPath"},
+ {9, nullptr, "Refresh"},
+ {10, nullptr, "RedirectProgramPath2"},
+ {11, nullptr, "Refresh2"},
+ {12, nullptr, "DeleteProgramPath"},
+ {13, nullptr, "DeleteApplicationControlPath"},
+ {14, nullptr, "DeleteApplicationHtmlDocumentPath"},
+ {15, nullptr, "DeleteApplicationLegalInformationPath"},
+ {16, nullptr, ""},
+ {17, nullptr, ""},
+ {18, nullptr, ""},
+ {19, nullptr, ""},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ FileSys::StorageId storage;
+};
+
+class IRegisteredLocationResolver final : public ServiceFramework<IRegisteredLocationResolver> {
+public:
+ explicit IRegisteredLocationResolver() : ServiceFramework{"IRegisteredLocationResolver"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "ResolveProgramPath"},
+ {1, nullptr, "RegisterProgramPath"},
+ {2, nullptr, "UnregisterProgramPath"},
+ {3, nullptr, "RedirectProgramPath"},
+ {4, nullptr, "ResolveHtmlDocumentPath"},
+ {5, nullptr, "RegisterHtmlDocumentPath"},
+ {6, nullptr, "UnregisterHtmlDocumentPath"},
+ {7, nullptr, "RedirectHtmlDocumentPath"},
+ {8, nullptr, ""},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class IAddOnContentLocationResolver final : public ServiceFramework<IAddOnContentLocationResolver> {
+public:
+ explicit IAddOnContentLocationResolver() : ServiceFramework{"IAddOnContentLocationResolver"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "ResolveAddOnContentPath"},
+ {1, nullptr, "RegisterAddOnContentStorage"},
+ {2, nullptr, "UnregisterAllAddOnContentPath"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class LR final : public ServiceFramework<LR> {
+public:
+ explicit LR() : ServiceFramework{"lr"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "OpenLocationResolver"},
@@ -52,7 +126,7 @@ public:
};
void InstallInterfaces(SM::ServiceManager& sm) {
- std::make_shared<LocationResolver>()->InstallAsService(sm);
+ std::make_shared<LR>()->InstallAsService(sm);
std::make_shared<NCM>()->InstallAsService(sm);
}
diff --git a/src/core/hle/service/ns/errors.h b/src/core/hle/service/ns/errors.h
new file mode 100644
index 000000000..f4aea8a65
--- /dev/null
+++ b/src/core/hle/service/ns/errors.h
@@ -0,0 +1,12 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Service::NS {
+
+constexpr ResultCode ERR_APPLICATION_LANGUAGE_NOT_FOUND{ErrorModule::NS, 300};
+} \ No newline at end of file
diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp
new file mode 100644
index 000000000..29c4a820c
--- /dev/null
+++ b/src/core/hle/service/ns/language.cpp
@@ -0,0 +1,392 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/ns/language.h"
+#include "core/hle/service/set/set.h"
+
+namespace Service::NS {
+
+constexpr ApplicationLanguagePriorityList priority_list_american_english = {{
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_british_english = {{
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_japanese = {{
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_french = {{
+ ApplicationLanguage::French,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_german = {{
+ ApplicationLanguage::German,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::French,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_latin_american_spanish = {{
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_spanish = {{
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_italian = {{
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_dutch = {{
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_canadian_french = {{
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_portuguese = {{
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_russian = {{
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_korean = {{
+ ApplicationLanguage::Korean,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_traditional_chinese = {{
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_simplified_chinese = {{
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Korean,
+}};
+
+const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(
+ const ApplicationLanguage lang) {
+ switch (lang) {
+ case ApplicationLanguage::AmericanEnglish:
+ return &priority_list_american_english;
+ case ApplicationLanguage::BritishEnglish:
+ return &priority_list_british_english;
+ case ApplicationLanguage::Japanese:
+ return &priority_list_japanese;
+ case ApplicationLanguage::French:
+ return &priority_list_french;
+ case ApplicationLanguage::German:
+ return &priority_list_german;
+ case ApplicationLanguage::LatinAmericanSpanish:
+ return &priority_list_latin_american_spanish;
+ case ApplicationLanguage::Spanish:
+ return &priority_list_spanish;
+ case ApplicationLanguage::Italian:
+ return &priority_list_italian;
+ case ApplicationLanguage::Dutch:
+ return &priority_list_dutch;
+ case ApplicationLanguage::CanadianFrench:
+ return &priority_list_canadian_french;
+ case ApplicationLanguage::Portuguese:
+ return &priority_list_portuguese;
+ case ApplicationLanguage::Russian:
+ return &priority_list_russian;
+ case ApplicationLanguage::Korean:
+ return &priority_list_korean;
+ case ApplicationLanguage::TraditionalChinese:
+ return &priority_list_traditional_chinese;
+ case ApplicationLanguage::SimplifiedChinese:
+ return &priority_list_simplified_chinese;
+ default:
+ return nullptr;
+ }
+}
+
+std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
+ const Set::LanguageCode language_code) {
+ switch (language_code) {
+ case Set::LanguageCode::EN_US:
+ return ApplicationLanguage::AmericanEnglish;
+ case Set::LanguageCode::EN_GB:
+ return ApplicationLanguage::BritishEnglish;
+ case Set::LanguageCode::JA:
+ return ApplicationLanguage::Japanese;
+ case Set::LanguageCode::FR:
+ return ApplicationLanguage::French;
+ case Set::LanguageCode::DE:
+ return ApplicationLanguage::German;
+ case Set::LanguageCode::ES_419:
+ return ApplicationLanguage::LatinAmericanSpanish;
+ case Set::LanguageCode::ES:
+ return ApplicationLanguage::Spanish;
+ case Set::LanguageCode::IT:
+ return ApplicationLanguage::Italian;
+ case Set::LanguageCode::NL:
+ return ApplicationLanguage::Dutch;
+ case Set::LanguageCode::FR_CA:
+ return ApplicationLanguage::CanadianFrench;
+ case Set::LanguageCode::PT:
+ return ApplicationLanguage::Portuguese;
+ case Set::LanguageCode::RU:
+ return ApplicationLanguage::Russian;
+ case Set::LanguageCode::KO:
+ return ApplicationLanguage::Korean;
+ case Set::LanguageCode::ZH_HANT:
+ return ApplicationLanguage::TraditionalChinese;
+ case Set::LanguageCode::ZH_HANS:
+ return ApplicationLanguage::SimplifiedChinese;
+ default:
+ return std::nullopt;
+ }
+}
+
+std::optional<Set::LanguageCode> ConvertToLanguageCode(const ApplicationLanguage lang) {
+ switch (lang) {
+ case ApplicationLanguage::AmericanEnglish:
+ return Set::LanguageCode::EN_US;
+ case ApplicationLanguage::BritishEnglish:
+ return Set::LanguageCode::EN_GB;
+ case ApplicationLanguage::Japanese:
+ return Set::LanguageCode::JA;
+ case ApplicationLanguage::French:
+ return Set::LanguageCode::FR;
+ case ApplicationLanguage::German:
+ return Set::LanguageCode::DE;
+ case ApplicationLanguage::LatinAmericanSpanish:
+ return Set::LanguageCode::ES_419;
+ case ApplicationLanguage::Spanish:
+ return Set::LanguageCode::ES;
+ case ApplicationLanguage::Italian:
+ return Set::LanguageCode::IT;
+ case ApplicationLanguage::Dutch:
+ return Set::LanguageCode::NL;
+ case ApplicationLanguage::CanadianFrench:
+ return Set::LanguageCode::FR_CA;
+ case ApplicationLanguage::Portuguese:
+ return Set::LanguageCode::PT;
+ case ApplicationLanguage::Russian:
+ return Set::LanguageCode::RU;
+ case ApplicationLanguage::Korean:
+ return Set::LanguageCode::KO;
+ case ApplicationLanguage::TraditionalChinese:
+ return Set::LanguageCode::ZH_HANT;
+ case ApplicationLanguage::SimplifiedChinese:
+ return Set::LanguageCode::ZH_HANS;
+ default:
+ return std::nullopt;
+ }
+}
+} // namespace Service::NS \ No newline at end of file
diff --git a/src/core/hle/service/ns/language.h b/src/core/hle/service/ns/language.h
new file mode 100644
index 000000000..e9829f9d2
--- /dev/null
+++ b/src/core/hle/service/ns/language.h
@@ -0,0 +1,45 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <optional>
+#include <string>
+#include "common/common_types.h"
+
+namespace Service::Set {
+enum class LanguageCode : u64;
+}
+
+namespace Service::NS {
+/// This is nn::ns::detail::ApplicationLanguage
+enum class ApplicationLanguage : u8 {
+ AmericanEnglish = 0,
+ BritishEnglish,
+ Japanese,
+ French,
+ German,
+ LatinAmericanSpanish,
+ Spanish,
+ Italian,
+ Dutch,
+ CanadianFrench,
+ Portuguese,
+ Russian,
+ Korean,
+ TraditionalChinese,
+ SimplifiedChinese,
+ Count
+};
+using ApplicationLanguagePriorityList =
+ const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
+
+constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
+ return 1U << static_cast<u32>(lang);
+}
+
+const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
+std::optional<ApplicationLanguage> ConvertToApplicationLanguage(Set::LanguageCode language_code);
+std::optional<Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
+} // namespace Service::NS \ No newline at end of file
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 0eb04037a..ce88a2941 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -7,445 +7,507 @@
#include "core/file_sys/patch_manager.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/service/ns/errors.h"
+#include "core/hle/service/ns/language.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/ns/pl_u.h"
+#include "core/hle/service/set/set.h"
+#include "core/settings.h"
namespace Service::NS {
-class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
-public:
- explicit IAccountProxyInterface() : ServiceFramework{"IAccountProxyInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "CreateUserAccount"},
- };
- // clang-format on
+IAccountProxyInterface::IAccountProxyInterface() : ServiceFramework{"IAccountProxyInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "CreateUserAccount"},
+ };
+ // clang-format on
- RegisterHandlers(functions);
- }
-};
+ RegisterHandlers(functions);
+}
-class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
-public:
- explicit IApplicationManagerInterface() : ServiceFramework{"IApplicationManagerInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "ListApplicationRecord"},
- {1, nullptr, "GenerateApplicationRecordCount"},
- {2, nullptr, "GetApplicationRecordUpdateSystemEvent"},
- {3, nullptr, "GetApplicationViewDeprecated"},
- {4, nullptr, "DeleteApplicationEntity"},
- {5, nullptr, "DeleteApplicationCompletely"},
- {6, nullptr, "IsAnyApplicationEntityRedundant"},
- {7, nullptr, "DeleteRedundantApplicationEntity"},
- {8, nullptr, "IsApplicationEntityMovable"},
- {9, nullptr, "MoveApplicationEntity"},
- {11, nullptr, "CalculateApplicationOccupiedSize"},
- {16, nullptr, "PushApplicationRecord"},
- {17, nullptr, "ListApplicationRecordContentMeta"},
- {19, nullptr, "LaunchApplicationOld"},
- {21, nullptr, "GetApplicationContentPath"},
- {22, nullptr, "TerminateApplication"},
- {23, nullptr, "ResolveApplicationContentPath"},
- {26, nullptr, "BeginInstallApplication"},
- {27, nullptr, "DeleteApplicationRecord"},
- {30, nullptr, "RequestApplicationUpdateInfo"},
- {32, nullptr, "CancelApplicationDownload"},
- {33, nullptr, "ResumeApplicationDownload"},
- {35, nullptr, "UpdateVersionList"},
- {36, nullptr, "PushLaunchVersion"},
- {37, nullptr, "ListRequiredVersion"},
- {38, nullptr, "CheckApplicationLaunchVersion"},
- {39, nullptr, "CheckApplicationLaunchRights"},
- {40, nullptr, "GetApplicationLogoData"},
- {41, nullptr, "CalculateApplicationDownloadRequiredSize"},
- {42, nullptr, "CleanupSdCard"},
- {43, nullptr, "CheckSdCardMountStatus"},
- {44, nullptr, "GetSdCardMountStatusChangedEvent"},
- {45, nullptr, "GetGameCardAttachmentEvent"},
- {46, nullptr, "GetGameCardAttachmentInfo"},
- {47, nullptr, "GetTotalSpaceSize"},
- {48, nullptr, "GetFreeSpaceSize"},
- {49, nullptr, "GetSdCardRemovedEvent"},
- {52, nullptr, "GetGameCardUpdateDetectionEvent"},
- {53, nullptr, "DisableApplicationAutoDelete"},
- {54, nullptr, "EnableApplicationAutoDelete"},
- {55, nullptr, "GetApplicationDesiredLanguage"},
- {56, nullptr, "SetApplicationTerminateResult"},
- {57, nullptr, "ClearApplicationTerminateResult"},
- {58, nullptr, "GetLastSdCardMountUnexpectedResult"},
- {59, nullptr, "ConvertApplicationLanguageToLanguageCode"},
- {60, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
- {61, nullptr, "GetBackgroundDownloadStressTaskInfo"},
- {62, nullptr, "GetGameCardStopper"},
- {63, nullptr, "IsSystemProgramInstalled"},
- {64, nullptr, "StartApplyDeltaTask"},
- {65, nullptr, "GetRequestServerStopper"},
- {66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"},
- {67, nullptr, "CancelApplicationApplyDelta"},
- {68, nullptr, "ResumeApplicationApplyDelta"},
- {69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"},
- {70, nullptr, "ResumeAll"},
- {71, nullptr, "GetStorageSize"},
- {80, nullptr, "RequestDownloadApplication"},
- {81, nullptr, "RequestDownloadAddOnContent"},
- {82, nullptr, "DownloadApplication"},
- {83, nullptr, "CheckApplicationResumeRights"},
- {84, nullptr, "GetDynamicCommitEvent"},
- {85, nullptr, "RequestUpdateApplication2"},
- {86, nullptr, "EnableApplicationCrashReport"},
- {87, nullptr, "IsApplicationCrashReportEnabled"},
- {90, nullptr, "BoostSystemMemoryResourceLimit"},
- {91, nullptr, "DeprecatedLaunchApplication"},
- {92, nullptr, "GetRunningApplicationProgramId"},
- {93, nullptr, "GetMainApplicationProgramIndex"},
- {94, nullptr, "LaunchApplication"},
- {95, nullptr, "GetApplicationLaunchInfo"},
- {96, nullptr, "AcquireApplicationLaunchInfo"},
- {97, nullptr, "GetMainApplicationProgramIndex2"},
- {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
- {100, nullptr, "ResetToFactorySettings"},
- {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
- {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
- {200, nullptr, "CalculateUserSaveDataStatistics"},
- {201, nullptr, "DeleteUserSaveDataAll"},
- {210, nullptr, "DeleteUserSystemSaveData"},
- {211, nullptr, "DeleteSaveData"},
- {220, nullptr, "UnregisterNetworkServiceAccount"},
- {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
- {300, nullptr, "GetApplicationShellEvent"},
- {301, nullptr, "PopApplicationShellEventInfo"},
- {302, nullptr, "LaunchLibraryApplet"},
- {303, nullptr, "TerminateLibraryApplet"},
- {304, nullptr, "LaunchSystemApplet"},
- {305, nullptr, "TerminateSystemApplet"},
- {306, nullptr, "LaunchOverlayApplet"},
- {307, nullptr, "TerminateOverlayApplet"},
- {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
- {401, nullptr, "InvalidateAllApplicationControlCache"},
- {402, nullptr, "RequestDownloadApplicationControlData"},
- {403, nullptr, "GetMaxApplicationControlCacheCount"},
- {404, nullptr, "InvalidateApplicationControlCache"},
- {405, nullptr, "ListApplicationControlCacheEntryInfo"},
- {406, nullptr, "GetApplicationControlProperty"},
- {502, nullptr, "RequestCheckGameCardRegistration"},
- {503, nullptr, "RequestGameCardRegistrationGoldPoint"},
- {504, nullptr, "RequestRegisterGameCard"},
- {505, nullptr, "GetGameCardMountFailureEvent"},
- {506, nullptr, "IsGameCardInserted"},
- {507, nullptr, "EnsureGameCardAccess"},
- {508, nullptr, "GetLastGameCardMountFailureResult"},
- {509, nullptr, "ListApplicationIdOnGameCard"},
- {600, nullptr, "CountApplicationContentMeta"},
- {601, nullptr, "ListApplicationContentMetaStatus"},
- {602, nullptr, "ListAvailableAddOnContent"},
- {603, nullptr, "GetOwnedApplicationContentMetaStatus"},
- {604, nullptr, "RegisterContentsExternalKey"},
- {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
- {606, nullptr, "GetContentMetaStorage"},
- {607, nullptr, "ListAvailableAddOnContent"},
- {700, nullptr, "PushDownloadTaskList"},
- {701, nullptr, "ClearTaskStatusList"},
- {702, nullptr, "RequestDownloadTaskList"},
- {703, nullptr, "RequestEnsureDownloadTask"},
- {704, nullptr, "ListDownloadTaskStatus"},
- {705, nullptr, "RequestDownloadTaskListData"},
- {800, nullptr, "RequestVersionList"},
- {801, nullptr, "ListVersionList"},
- {802, nullptr, "RequestVersionListData"},
- {900, nullptr, "GetApplicationRecord"},
- {901, nullptr, "GetApplicationRecordProperty"},
- {902, nullptr, "EnableApplicationAutoUpdate"},
- {903, nullptr, "DisableApplicationAutoUpdate"},
- {904, nullptr, "TouchApplication"},
- {905, nullptr, "RequestApplicationUpdate"},
- {906, nullptr, "IsApplicationUpdateRequested"},
- {907, nullptr, "WithdrawApplicationUpdateRequest"},
- {908, nullptr, "ListApplicationRecordInstalledContentMeta"},
- {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
- {910, nullptr, "HasApplicationRecord"},
- {911, nullptr, "SetPreInstalledApplication"},
- {912, nullptr, "ClearPreInstalledApplicationFlag"},
- {1000, nullptr, "RequestVerifyApplicationDeprecated"},
- {1001, nullptr, "CorruptApplicationForDebug"},
- {1002, nullptr, "RequestVerifyAddOnContentsRights"},
- {1003, nullptr, "RequestVerifyApplication"},
- {1004, nullptr, "CorruptContentForDebug"},
- {1200, nullptr, "NeedsUpdateVulnerability"},
- {1300, nullptr, "IsAnyApplicationEntityInstalled"},
- {1301, nullptr, "DeleteApplicationContentEntities"},
- {1302, nullptr, "CleanupUnrecordedApplicationEntity"},
- {1303, nullptr, "CleanupAddOnContentsWithNoRights"},
- {1304, nullptr, "DeleteApplicationContentEntity"},
- {1305, nullptr, "TryDeleteRunningApplicationEntity"},
- {1306, nullptr, "TryDeleteRunningApplicationCompletely"},
- {1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
- {1308, nullptr, "DeleteApplicationCompletelyForDebug"},
- {1309, nullptr, "CleanupUnavailableAddOnContents"},
- {1400, nullptr, "PrepareShutdown"},
- {1500, nullptr, "FormatSdCard"},
- {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
- {1502, nullptr, "GetLastSdCardFormatUnexpectedResult"},
- {1504, nullptr, "InsertSdCard"},
- {1505, nullptr, "RemoveSdCard"},
- {1600, nullptr, "GetSystemSeedForPseudoDeviceId"},
- {1601, nullptr, "ResetSystemSeedForPseudoDeviceId"},
- {1700, nullptr, "ListApplicationDownloadingContentMeta"},
- {1701, nullptr, "GetApplicationView"},
- {1702, nullptr, "GetApplicationDownloadTaskStatus"},
- {1703, nullptr, "GetApplicationViewDownloadErrorContext"},
- {1800, nullptr, "IsNotificationSetupCompleted"},
- {1801, nullptr, "GetLastNotificationInfoCount"},
- {1802, nullptr, "ListLastNotificationInfo"},
- {1803, nullptr, "ListNotificationTask"},
- {1900, nullptr, "IsActiveAccount"},
- {1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"},
- {1902, nullptr, "GetApplicationTicketInfo"},
- {2000, nullptr, "GetSystemDeliveryInfo"},
- {2001, nullptr, "SelectLatestSystemDeliveryInfo"},
- {2002, nullptr, "VerifyDeliveryProtocolVersion"},
- {2003, nullptr, "GetApplicationDeliveryInfo"},
- {2004, nullptr, "HasAllContentsToDeliver"},
- {2005, nullptr, "CompareApplicationDeliveryInfo"},
- {2006, nullptr, "CanDeliverApplication"},
- {2007, nullptr, "ListContentMetaKeyToDeliverApplication"},
- {2008, nullptr, "NeedsSystemUpdateToDeliverApplication"},
- {2009, nullptr, "EstimateRequiredSize"},
- {2010, nullptr, "RequestReceiveApplication"},
- {2011, nullptr, "CommitReceiveApplication"},
- {2012, nullptr, "GetReceiveApplicationProgress"},
- {2013, nullptr, "RequestSendApplication"},
- {2014, nullptr, "GetSendApplicationProgress"},
- {2015, nullptr, "CompareSystemDeliveryInfo"},
- {2016, nullptr, "ListNotCommittedContentMeta"},
- {2017, nullptr, "CreateDownloadTask"},
- {2018, nullptr, "GetApplicationDeliveryInfoHash"},
- {2050, nullptr, "GetApplicationRightsOnClient"},
- {2100, nullptr, "GetApplicationTerminateResult"},
- {2101, nullptr, "GetRawApplicationTerminateResult"},
- {2150, nullptr, "CreateRightsEnvironment"},
- {2151, nullptr, "DestroyRightsEnvironment"},
- {2152, nullptr, "ActivateRightsEnvironment"},
- {2153, nullptr, "DeactivateRightsEnvironment"},
- {2154, nullptr, "ForceActivateRightsContextForExit"},
- {2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
- {2161, nullptr, "SetUsersToRightsEnvironment"},
- {2170, nullptr, "GetRightsEnvironmentStatus"},
- {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
- {2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
- {2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
- {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
- {2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
- {2199, nullptr, "GetRightsEnvironmentCountForDebug"},
- {2200, nullptr, "GetGameCardApplicationCopyIdentifier"},
- {2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
- {2250, nullptr, "RequestReportActiveELicence"},
- {2300, nullptr, "ListEventLog"},
- };
- // clang-format on
+IAccountProxyInterface::~IAccountProxyInterface() = default;
+
+IApplicationManagerInterface::IApplicationManagerInterface()
+ : ServiceFramework{"IApplicationManagerInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "ListApplicationRecord"},
+ {1, nullptr, "GenerateApplicationRecordCount"},
+ {2, nullptr, "GetApplicationRecordUpdateSystemEvent"},
+ {3, nullptr, "GetApplicationViewDeprecated"},
+ {4, nullptr, "DeleteApplicationEntity"},
+ {5, nullptr, "DeleteApplicationCompletely"},
+ {6, nullptr, "IsAnyApplicationEntityRedundant"},
+ {7, nullptr, "DeleteRedundantApplicationEntity"},
+ {8, nullptr, "IsApplicationEntityMovable"},
+ {9, nullptr, "MoveApplicationEntity"},
+ {11, nullptr, "CalculateApplicationOccupiedSize"},
+ {16, nullptr, "PushApplicationRecord"},
+ {17, nullptr, "ListApplicationRecordContentMeta"},
+ {19, nullptr, "LaunchApplicationOld"},
+ {21, nullptr, "GetApplicationContentPath"},
+ {22, nullptr, "TerminateApplication"},
+ {23, nullptr, "ResolveApplicationContentPath"},
+ {26, nullptr, "BeginInstallApplication"},
+ {27, nullptr, "DeleteApplicationRecord"},
+ {30, nullptr, "RequestApplicationUpdateInfo"},
+ {32, nullptr, "CancelApplicationDownload"},
+ {33, nullptr, "ResumeApplicationDownload"},
+ {35, nullptr, "UpdateVersionList"},
+ {36, nullptr, "PushLaunchVersion"},
+ {37, nullptr, "ListRequiredVersion"},
+ {38, nullptr, "CheckApplicationLaunchVersion"},
+ {39, nullptr, "CheckApplicationLaunchRights"},
+ {40, nullptr, "GetApplicationLogoData"},
+ {41, nullptr, "CalculateApplicationDownloadRequiredSize"},
+ {42, nullptr, "CleanupSdCard"},
+ {43, nullptr, "CheckSdCardMountStatus"},
+ {44, nullptr, "GetSdCardMountStatusChangedEvent"},
+ {45, nullptr, "GetGameCardAttachmentEvent"},
+ {46, nullptr, "GetGameCardAttachmentInfo"},
+ {47, nullptr, "GetTotalSpaceSize"},
+ {48, nullptr, "GetFreeSpaceSize"},
+ {49, nullptr, "GetSdCardRemovedEvent"},
+ {52, nullptr, "GetGameCardUpdateDetectionEvent"},
+ {53, nullptr, "DisableApplicationAutoDelete"},
+ {54, nullptr, "EnableApplicationAutoDelete"},
+ {55, &IApplicationManagerInterface::GetApplicationDesiredLanguage, "GetApplicationDesiredLanguage"},
+ {56, nullptr, "SetApplicationTerminateResult"},
+ {57, nullptr, "ClearApplicationTerminateResult"},
+ {58, nullptr, "GetLastSdCardMountUnexpectedResult"},
+ {59, &IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode, "ConvertApplicationLanguageToLanguageCode"},
+ {60, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
+ {61, nullptr, "GetBackgroundDownloadStressTaskInfo"},
+ {62, nullptr, "GetGameCardStopper"},
+ {63, nullptr, "IsSystemProgramInstalled"},
+ {64, nullptr, "StartApplyDeltaTask"},
+ {65, nullptr, "GetRequestServerStopper"},
+ {66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"},
+ {67, nullptr, "CancelApplicationApplyDelta"},
+ {68, nullptr, "ResumeApplicationApplyDelta"},
+ {69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"},
+ {70, nullptr, "ResumeAll"},
+ {71, nullptr, "GetStorageSize"},
+ {80, nullptr, "RequestDownloadApplication"},
+ {81, nullptr, "RequestDownloadAddOnContent"},
+ {82, nullptr, "DownloadApplication"},
+ {83, nullptr, "CheckApplicationResumeRights"},
+ {84, nullptr, "GetDynamicCommitEvent"},
+ {85, nullptr, "RequestUpdateApplication2"},
+ {86, nullptr, "EnableApplicationCrashReport"},
+ {87, nullptr, "IsApplicationCrashReportEnabled"},
+ {90, nullptr, "BoostSystemMemoryResourceLimit"},
+ {91, nullptr, "DeprecatedLaunchApplication"},
+ {92, nullptr, "GetRunningApplicationProgramId"},
+ {93, nullptr, "GetMainApplicationProgramIndex"},
+ {94, nullptr, "LaunchApplication"},
+ {95, nullptr, "GetApplicationLaunchInfo"},
+ {96, nullptr, "AcquireApplicationLaunchInfo"},
+ {97, nullptr, "GetMainApplicationProgramIndex2"},
+ {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
+ {100, nullptr, "ResetToFactorySettings"},
+ {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
+ {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
+ {200, nullptr, "CalculateUserSaveDataStatistics"},
+ {201, nullptr, "DeleteUserSaveDataAll"},
+ {210, nullptr, "DeleteUserSystemSaveData"},
+ {211, nullptr, "DeleteSaveData"},
+ {220, nullptr, "UnregisterNetworkServiceAccount"},
+ {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
+ {300, nullptr, "GetApplicationShellEvent"},
+ {301, nullptr, "PopApplicationShellEventInfo"},
+ {302, nullptr, "LaunchLibraryApplet"},
+ {303, nullptr, "TerminateLibraryApplet"},
+ {304, nullptr, "LaunchSystemApplet"},
+ {305, nullptr, "TerminateSystemApplet"},
+ {306, nullptr, "LaunchOverlayApplet"},
+ {307, nullptr, "TerminateOverlayApplet"},
+ {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
+ {401, nullptr, "InvalidateAllApplicationControlCache"},
+ {402, nullptr, "RequestDownloadApplicationControlData"},
+ {403, nullptr, "GetMaxApplicationControlCacheCount"},
+ {404, nullptr, "InvalidateApplicationControlCache"},
+ {405, nullptr, "ListApplicationControlCacheEntryInfo"},
+ {406, nullptr, "GetApplicationControlProperty"},
+ {502, nullptr, "RequestCheckGameCardRegistration"},
+ {503, nullptr, "RequestGameCardRegistrationGoldPoint"},
+ {504, nullptr, "RequestRegisterGameCard"},
+ {505, nullptr, "GetGameCardMountFailureEvent"},
+ {506, nullptr, "IsGameCardInserted"},
+ {507, nullptr, "EnsureGameCardAccess"},
+ {508, nullptr, "GetLastGameCardMountFailureResult"},
+ {509, nullptr, "ListApplicationIdOnGameCard"},
+ {600, nullptr, "CountApplicationContentMeta"},
+ {601, nullptr, "ListApplicationContentMetaStatus"},
+ {602, nullptr, "ListAvailableAddOnContent"},
+ {603, nullptr, "GetOwnedApplicationContentMetaStatus"},
+ {604, nullptr, "RegisterContentsExternalKey"},
+ {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
+ {606, nullptr, "GetContentMetaStorage"},
+ {607, nullptr, "ListAvailableAddOnContent"},
+ {700, nullptr, "PushDownloadTaskList"},
+ {701, nullptr, "ClearTaskStatusList"},
+ {702, nullptr, "RequestDownloadTaskList"},
+ {703, nullptr, "RequestEnsureDownloadTask"},
+ {704, nullptr, "ListDownloadTaskStatus"},
+ {705, nullptr, "RequestDownloadTaskListData"},
+ {800, nullptr, "RequestVersionList"},
+ {801, nullptr, "ListVersionList"},
+ {802, nullptr, "RequestVersionListData"},
+ {900, nullptr, "GetApplicationRecord"},
+ {901, nullptr, "GetApplicationRecordProperty"},
+ {902, nullptr, "EnableApplicationAutoUpdate"},
+ {903, nullptr, "DisableApplicationAutoUpdate"},
+ {904, nullptr, "TouchApplication"},
+ {905, nullptr, "RequestApplicationUpdate"},
+ {906, nullptr, "IsApplicationUpdateRequested"},
+ {907, nullptr, "WithdrawApplicationUpdateRequest"},
+ {908, nullptr, "ListApplicationRecordInstalledContentMeta"},
+ {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
+ {910, nullptr, "HasApplicationRecord"},
+ {911, nullptr, "SetPreInstalledApplication"},
+ {912, nullptr, "ClearPreInstalledApplicationFlag"},
+ {1000, nullptr, "RequestVerifyApplicationDeprecated"},
+ {1001, nullptr, "CorruptApplicationForDebug"},
+ {1002, nullptr, "RequestVerifyAddOnContentsRights"},
+ {1003, nullptr, "RequestVerifyApplication"},
+ {1004, nullptr, "CorruptContentForDebug"},
+ {1200, nullptr, "NeedsUpdateVulnerability"},
+ {1300, nullptr, "IsAnyApplicationEntityInstalled"},
+ {1301, nullptr, "DeleteApplicationContentEntities"},
+ {1302, nullptr, "CleanupUnrecordedApplicationEntity"},
+ {1303, nullptr, "CleanupAddOnContentsWithNoRights"},
+ {1304, nullptr, "DeleteApplicationContentEntity"},
+ {1305, nullptr, "TryDeleteRunningApplicationEntity"},
+ {1306, nullptr, "TryDeleteRunningApplicationCompletely"},
+ {1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
+ {1308, nullptr, "DeleteApplicationCompletelyForDebug"},
+ {1309, nullptr, "CleanupUnavailableAddOnContents"},
+ {1400, nullptr, "PrepareShutdown"},
+ {1500, nullptr, "FormatSdCard"},
+ {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
+ {1502, nullptr, "GetLastSdCardFormatUnexpectedResult"},
+ {1504, nullptr, "InsertSdCard"},
+ {1505, nullptr, "RemoveSdCard"},
+ {1600, nullptr, "GetSystemSeedForPseudoDeviceId"},
+ {1601, nullptr, "ResetSystemSeedForPseudoDeviceId"},
+ {1700, nullptr, "ListApplicationDownloadingContentMeta"},
+ {1701, nullptr, "GetApplicationView"},
+ {1702, nullptr, "GetApplicationDownloadTaskStatus"},
+ {1703, nullptr, "GetApplicationViewDownloadErrorContext"},
+ {1800, nullptr, "IsNotificationSetupCompleted"},
+ {1801, nullptr, "GetLastNotificationInfoCount"},
+ {1802, nullptr, "ListLastNotificationInfo"},
+ {1803, nullptr, "ListNotificationTask"},
+ {1900, nullptr, "IsActiveAccount"},
+ {1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"},
+ {1902, nullptr, "GetApplicationTicketInfo"},
+ {2000, nullptr, "GetSystemDeliveryInfo"},
+ {2001, nullptr, "SelectLatestSystemDeliveryInfo"},
+ {2002, nullptr, "VerifyDeliveryProtocolVersion"},
+ {2003, nullptr, "GetApplicationDeliveryInfo"},
+ {2004, nullptr, "HasAllContentsToDeliver"},
+ {2005, nullptr, "CompareApplicationDeliveryInfo"},
+ {2006, nullptr, "CanDeliverApplication"},
+ {2007, nullptr, "ListContentMetaKeyToDeliverApplication"},
+ {2008, nullptr, "NeedsSystemUpdateToDeliverApplication"},
+ {2009, nullptr, "EstimateRequiredSize"},
+ {2010, nullptr, "RequestReceiveApplication"},
+ {2011, nullptr, "CommitReceiveApplication"},
+ {2012, nullptr, "GetReceiveApplicationProgress"},
+ {2013, nullptr, "RequestSendApplication"},
+ {2014, nullptr, "GetSendApplicationProgress"},
+ {2015, nullptr, "CompareSystemDeliveryInfo"},
+ {2016, nullptr, "ListNotCommittedContentMeta"},
+ {2017, nullptr, "CreateDownloadTask"},
+ {2018, nullptr, "GetApplicationDeliveryInfoHash"},
+ {2050, nullptr, "GetApplicationRightsOnClient"},
+ {2100, nullptr, "GetApplicationTerminateResult"},
+ {2101, nullptr, "GetRawApplicationTerminateResult"},
+ {2150, nullptr, "CreateRightsEnvironment"},
+ {2151, nullptr, "DestroyRightsEnvironment"},
+ {2152, nullptr, "ActivateRightsEnvironment"},
+ {2153, nullptr, "DeactivateRightsEnvironment"},
+ {2154, nullptr, "ForceActivateRightsContextForExit"},
+ {2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
+ {2161, nullptr, "SetUsersToRightsEnvironment"},
+ {2170, nullptr, "GetRightsEnvironmentStatus"},
+ {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
+ {2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
+ {2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
+ {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
+ {2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
+ {2199, nullptr, "GetRightsEnvironmentCountForDebug"},
+ {2200, nullptr, "GetGameCardApplicationCopyIdentifier"},
+ {2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
+ {2250, nullptr, "RequestReportActiveELicence"},
+ {2300, nullptr, "ListEventLog"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
- RegisterHandlers(functions);
- }
+IApplicationManagerInterface::~IApplicationManagerInterface() = default;
+
+void IApplicationManagerInterface::GetApplicationControlData(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto flag = rp.PopRaw<u64>();
+ LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
- void GetApplicationControlData(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto flag = rp.PopRaw<u64>();
- LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
-
- const auto title_id = rp.PopRaw<u64>();
-
- const auto size = ctx.GetWriteBufferSize();
-
- const FileSys::PatchManager pm{title_id};
- const auto control = pm.GetControlMetadata();
-
- std::vector<u8> out;
-
- if (control.first != nullptr) {
- if (size < 0x4000) {
- LOG_ERROR(Service_NS,
- "output buffer is too small! (actual={:016X}, expected_min=0x4000)",
- size);
- IPC::ResponseBuilder rb{ctx, 2};
- // TODO(DarkLordZach): Find a better error code for this.
- rb.Push(ResultCode(-1));
- return;
- }
-
- out.resize(0x4000);
- const auto bytes = control.first->GetRawBytes();
- std::memcpy(out.data(), bytes.data(), bytes.size());
- } else {
- LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
- title_id);
- out.resize(std::min<u64>(0x4000, size));
+ const auto title_id = rp.PopRaw<u64>();
+
+ const auto size = ctx.GetWriteBufferSize();
+
+ const FileSys::PatchManager pm{title_id};
+ const auto control = pm.GetControlMetadata();
+
+ std::vector<u8> out;
+
+ if (control.first != nullptr) {
+ if (size < 0x4000) {
+ LOG_ERROR(Service_NS,
+ "output buffer is too small! (actual={:016X}, expected_min=0x4000)", size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ // TODO(DarkLordZach): Find a better error code for this.
+ rb.Push(ResultCode(-1));
+ return;
}
- if (control.second != nullptr) {
- if (size < 0x4000 + control.second->GetSize()) {
- LOG_ERROR(Service_NS,
- "output buffer is too small! (actual={:016X}, expected_min={:016X})",
- size, 0x4000 + control.second->GetSize());
- IPC::ResponseBuilder rb{ctx, 2};
- // TODO(DarkLordZach): Find a better error code for this.
- rb.Push(ResultCode(-1));
- return;
- }
-
- out.resize(0x4000 + control.second->GetSize());
- control.second->Read(out.data() + 0x4000, control.second->GetSize());
- } else {
- LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
- title_id);
+ out.resize(0x4000);
+ const auto bytes = control.first->GetRawBytes();
+ std::memcpy(out.data(), bytes.data(), bytes.size());
+ } else {
+ LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
+ title_id);
+ out.resize(std::min<u64>(0x4000, size));
+ }
+
+ if (control.second != nullptr) {
+ if (size < 0x4000 + control.second->GetSize()) {
+ LOG_ERROR(Service_NS,
+ "output buffer is too small! (actual={:016X}, expected_min={:016X})", size,
+ 0x4000 + control.second->GetSize());
+ IPC::ResponseBuilder rb{ctx, 2};
+ // TODO(DarkLordZach): Find a better error code for this.
+ rb.Push(ResultCode(-1));
+ return;
}
- ctx.WriteBuffer(out);
+ out.resize(0x4000 + control.second->GetSize());
+ control.second->Read(out.data() + 0x4000, control.second->GetSize());
+ } else {
+ LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
+ title_id);
+ }
+
+ ctx.WriteBuffer(out);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(static_cast<u32>(out.size()));
+}
+
+void IApplicationManagerInterface::GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto supported_languages = rp.Pop<u32>();
+ const auto res = GetApplicationDesiredLanguage(supported_languages);
+ if (res.Succeeded()) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(static_cast<u32>(out.size()));
+ rb.Push<u32>(*res);
+ } else {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res.Code());
}
-};
+}
-class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
-public:
- explicit IApplicationVersionInterface() : ServiceFramework{"IApplicationVersionInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetLaunchRequiredVersion"},
- {1, nullptr, "UpgradeLaunchRequiredVersion"},
- {35, nullptr, "UpdateVersionList"},
- {36, nullptr, "PushLaunchVersion"},
- {37, nullptr, "ListRequiredVersion"},
- {800, nullptr, "RequestVersionList"},
- {801, nullptr, "ListVersionList"},
- {802, nullptr, "RequestVersionListData"},
- {1000, nullptr, "PerformAutoUpdate"},
- };
- // clang-format on
+ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
+ const u32 supported_languages) {
+ LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
- RegisterHandlers(functions);
- }
-};
+ // Get language code from settings
+ const auto language_code = Set::GetLanguageCodeFromIndex(Settings::values.language_index);
-class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
-public:
- explicit IContentManagerInterface() : ServiceFramework{"IContentManagerInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {11, nullptr, "CalculateApplicationOccupiedSize"},
- {43, nullptr, "CheckSdCardMountStatus"},
- {47, nullptr, "GetTotalSpaceSize"},
- {48, nullptr, "GetFreeSpaceSize"},
- {600, nullptr, "CountApplicationContentMeta"},
- {601, nullptr, "ListApplicationContentMetaStatus"},
- {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
- {607, nullptr, "IsAnyApplicationRunning"},
- };
- // clang-format on
+ // Convert to application language, get priority list
+ const auto application_language = ConvertToApplicationLanguage(language_code);
+ if (application_language == std::nullopt) {
+ return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
+ }
+ const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
+ if (!priority_list) {
+ return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
+ }
- RegisterHandlers(functions);
+ // Try to find a valid language.
+ for (const auto lang : *priority_list) {
+ const auto supported_flag = GetSupportedLanguageFlag(lang);
+ if (supported_languages == 0 || (supported_languages & supported_flag) == supported_flag) {
+ return MakeResult(static_cast<u8>(lang));
+ }
}
-};
-class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
-public:
- explicit IDocumentInterface() : ServiceFramework{"IDocumentInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {21, nullptr, "GetApplicationContentPath"},
- {23, nullptr, "ResolveApplicationContentPath"},
- {93, nullptr, "GetRunningApplicationProgramId"},
- };
- // clang-format on
+ return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
+}
- RegisterHandlers(functions);
- }
-};
+void IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
+ Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto application_language = rp.Pop<u8>();
-class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
-public:
- explicit IDownloadTaskInterface() : ServiceFramework{"IDownloadTaskInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {701, nullptr, "ClearTaskStatusList"},
- {702, nullptr, "RequestDownloadTaskList"},
- {703, nullptr, "RequestEnsureDownloadTask"},
- {704, nullptr, "ListDownloadTaskStatus"},
- {705, nullptr, "RequestDownloadTaskListData"},
- {706, nullptr, "TryCommitCurrentApplicationDownloadTask"},
- {707, nullptr, "EnableAutoCommit"},
- {708, nullptr, "DisableAutoCommit"},
- {709, nullptr, "TriggerDynamicCommitEvent"},
- };
- // clang-format on
+ const auto res = ConvertApplicationLanguageToLanguageCode(application_language);
+ if (res.Succeeded()) {
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(*res);
+ } else {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res.Code());
+ }
+}
- RegisterHandlers(functions);
+ResultVal<u64> IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
+ u8 application_language) {
+ const auto language_code =
+ ConvertToLanguageCode(static_cast<ApplicationLanguage>(application_language));
+ if (language_code == std::nullopt) {
+ return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
}
-};
-class IECommerceInterface final : public ServiceFramework<IECommerceInterface> {
-public:
- explicit IECommerceInterface() : ServiceFramework{"IECommerceInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "RequestLinkDevice"},
- {1, nullptr, "RequestCleanupAllPreInstalledApplications"},
- {2, nullptr, "RequestCleanupPreInstalledApplication"},
- {3, nullptr, "RequestSyncRights"},
- {4, nullptr, "RequestUnlinkDevice"},
- {5, nullptr, "RequestRevokeAllELicense"},
- };
- // clang-format on
+ return MakeResult(static_cast<u64>(*language_code));
+}
- RegisterHandlers(functions);
- }
-};
+IApplicationVersionInterface::IApplicationVersionInterface()
+ : ServiceFramework{"IApplicationVersionInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetLaunchRequiredVersion"},
+ {1, nullptr, "UpgradeLaunchRequiredVersion"},
+ {35, nullptr, "UpdateVersionList"},
+ {36, nullptr, "PushLaunchVersion"},
+ {37, nullptr, "ListRequiredVersion"},
+ {800, nullptr, "RequestVersionList"},
+ {801, nullptr, "ListVersionList"},
+ {802, nullptr, "RequestVersionListData"},
+ {1000, nullptr, "PerformAutoUpdate"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
-class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> {
-public:
- explicit IFactoryResetInterface() : ServiceFramework{"IFactoryResetInterface"} {
- // clang-format off
+IApplicationVersionInterface::~IApplicationVersionInterface() = default;
+
+IContentManagerInterface::IContentManagerInterface()
+ : ServiceFramework{"IContentManagerInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {11, nullptr, "CalculateApplicationOccupiedSize"},
+ {43, nullptr, "CheckSdCardMountStatus"},
+ {47, nullptr, "GetTotalSpaceSize"},
+ {48, nullptr, "GetFreeSpaceSize"},
+ {600, nullptr, "CountApplicationContentMeta"},
+ {601, nullptr, "ListApplicationContentMetaStatus"},
+ {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
+ {607, nullptr, "IsAnyApplicationRunning"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IContentManagerInterface::~IContentManagerInterface() = default;
+
+IDocumentInterface::IDocumentInterface() : ServiceFramework{"IDocumentInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {21, nullptr, "GetApplicationContentPath"},
+ {23, nullptr, "ResolveApplicationContentPath"},
+ {93, nullptr, "GetRunningApplicationProgramId"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IDocumentInterface::~IDocumentInterface() = default;
+
+IDownloadTaskInterface::IDownloadTaskInterface() : ServiceFramework{"IDownloadTaskInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {701, nullptr, "ClearTaskStatusList"},
+ {702, nullptr, "RequestDownloadTaskList"},
+ {703, nullptr, "RequestEnsureDownloadTask"},
+ {704, nullptr, "ListDownloadTaskStatus"},
+ {705, nullptr, "RequestDownloadTaskListData"},
+ {706, nullptr, "TryCommitCurrentApplicationDownloadTask"},
+ {707, nullptr, "EnableAutoCommit"},
+ {708, nullptr, "DisableAutoCommit"},
+ {709, nullptr, "TriggerDynamicCommitEvent"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IDownloadTaskInterface::~IDownloadTaskInterface() = default;
+
+IECommerceInterface::IECommerceInterface() : ServiceFramework{"IECommerceInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "RequestLinkDevice"},
+ {1, nullptr, "RequestCleanupAllPreInstalledApplications"},
+ {2, nullptr, "RequestCleanupPreInstalledApplication"},
+ {3, nullptr, "RequestSyncRights"},
+ {4, nullptr, "RequestUnlinkDevice"},
+ {5, nullptr, "RequestRevokeAllELicense"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IECommerceInterface::~IECommerceInterface() = default;
+
+IFactoryResetInterface::IFactoryResetInterface::IFactoryResetInterface()
+ : ServiceFramework{"IFactoryResetInterface"} {
+ // clang-format off
static const FunctionInfo functions[] = {
{100, nullptr, "ResetToFactorySettings"},
{101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
{102, nullptr, "ResetToFactorySettingsForRefurbishment"},
};
- // clang-format on
+ // clang-format on
- RegisterHandlers(functions);
- }
-};
-
-class NS final : public ServiceFramework<NS> {
-public:
- explicit NS(const char* name) : ServiceFramework{name} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
- {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
- {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
- {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
- {7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
- {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
- {7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"},
- {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
- };
- // clang-format on
+ RegisterHandlers(functions);
+}
- RegisterHandlers(functions);
- }
+IFactoryResetInterface::~IFactoryResetInterface() = default;
+
+NS::NS(const char* name) : ServiceFramework{name} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
+ {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
+ {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
+ {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
+ {7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
+ {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
+ {7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"},
+ {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
-private:
- template <typename T>
- void PushInterface(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NS, "called");
+NS::~NS() = default;
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<T>();
- }
-};
+std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const {
+ return GetInterface<IApplicationManagerInterface>();
+}
class NS_DEV final : public ServiceFramework<NS_DEV> {
public:
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index b81ca8f1e..0e8256cb4 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -8,6 +8,88 @@
namespace Service::NS {
+class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
+public:
+ explicit IAccountProxyInterface();
+ ~IAccountProxyInterface() override;
+};
+
+class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
+public:
+ explicit IApplicationManagerInterface();
+ ~IApplicationManagerInterface() override;
+
+ ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages);
+ ResultVal<u64> ConvertApplicationLanguageToLanguageCode(u8 application_language);
+
+private:
+ void GetApplicationControlData(Kernel::HLERequestContext& ctx);
+ void GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx);
+ void ConvertApplicationLanguageToLanguageCode(Kernel::HLERequestContext& ctx);
+};
+
+class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
+public:
+ explicit IApplicationVersionInterface();
+ ~IApplicationVersionInterface() override;
+};
+
+class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
+public:
+ explicit IContentManagerInterface();
+ ~IContentManagerInterface() override;
+};
+
+class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
+public:
+ explicit IDocumentInterface();
+ ~IDocumentInterface() override;
+};
+
+class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
+public:
+ explicit IDownloadTaskInterface();
+ ~IDownloadTaskInterface() override;
+};
+
+class IECommerceInterface final : public ServiceFramework<IECommerceInterface> {
+public:
+ explicit IECommerceInterface();
+ ~IECommerceInterface() override;
+};
+
+class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> {
+public:
+ explicit IFactoryResetInterface();
+ ~IFactoryResetInterface() override;
+};
+
+class NS final : public ServiceFramework<NS> {
+public:
+ explicit NS(const char* name);
+ ~NS() override;
+
+ std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const;
+
+private:
+ template <typename T>
+ void PushInterface(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NS, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<T>();
+ }
+
+ template <typename T>
+ std::shared_ptr<T> GetInterface() const {
+ static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>,
+ "Not a base of ServiceFrameworkBase");
+
+ return std::make_shared<T>();
+ }
+};
+
/// Registers all NS services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
diff --git a/src/core/hle/service/ns/ns_language.h b/src/core/hle/service/ns/ns_language.h
new file mode 100644
index 000000000..59ac85a19
--- /dev/null
+++ b/src/core/hle/service/ns/ns_language.h
@@ -0,0 +1,42 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+#include <optional>
+#include <string>
+#include "common/common_types.h"
+#include "core/hle/service/set/set.h"
+
+namespace Service::NS {
+/// This is nn::ns::detail::ApplicationLanguage
+enum class ApplicationLanguage : u8 {
+ AmericanEnglish = 0,
+ BritishEnglish,
+ Japanese,
+ French,
+ German,
+ LatinAmericanSpanish,
+ Spanish,
+ Italian,
+ Dutch,
+ CanadianFrench,
+ Portuguese,
+ Russian,
+ Korean,
+ TraditionalChinese,
+ SimplifiedChinese,
+ Count
+};
+using ApplicationLanguagePriorityList =
+ const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
+
+constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
+ return 1U << static_cast<u32>(lang);
+}
+
+const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
+std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
+ Service::Set::LanguageCode language_code);
+std::optional<Service::Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
+} // namespace Service::NS \ No newline at end of file
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 45812d238..0e28755bd 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -185,7 +185,8 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
IoctlGetGpuTime params{};
std::memcpy(&params, input.data(), input.size());
- params.gpu_time = Core::Timing::cyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks());
+ const auto ns = Core::Timing::CyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks());
+ params.gpu_time = static_cast<u64_le>(ns.count());
std::memcpy(output.data(), &params, output.size());
return 0;
}
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index e4fcee9f8..7e134f5c1 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -2,10 +2,18 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <json.hpp>
+#include "common/file_util.h"
+#include "common/hex_util.h"
#include "common/logging/log.h"
+#include "common/scm_rev.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/prepo/prepo.h"
#include "core/hle/service/service.h"
+#include "core/reporter.h"
+#include "core/settings.h"
namespace Service::PlayReport {
@@ -40,8 +48,21 @@ public:
private:
void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) {
- // TODO(ogniK): Do we want to add play report?
- LOG_WARNING(Service_PREPO, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto user_id = rp.PopRaw<u128>();
+ const auto process_id = rp.PopRaw<u64>();
+
+ const auto data1 = ctx.ReadBuffer(0);
+ const auto data2 = ctx.ReadBuffer(1);
+
+ LOG_DEBUG(
+ Service_PREPO,
+ "called, user_id={:016X}{:016X}, unk1={:016X}, data1_size={:016X}, data2_size={:016X}",
+ user_id[1], user_id[0], process_id, data1.size(), data2.size());
+
+ const auto& reporter{Core::System::GetInstance().GetReporter()};
+ reporter.SavePlayReport(Core::CurrentProcess()->GetTitleID(), process_id, {data1, data2},
+ user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 00806b0ed..b2954eb34 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -68,6 +68,7 @@
#include "core/hle/service/usb/usb.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/wlan/wlan.h"
+#include "core/reporter.h"
namespace Service {
@@ -148,6 +149,8 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext
}
buf.push_back('}');
+ Core::System::GetInstance().GetReporter().SaveUnimplementedFunctionReport(
+ ctx, ctx.GetCommand(), function_name, service_name);
UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf));
}
@@ -200,7 +203,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
SM::ServiceManager::InstallInterfaces(sm);
- Account::InstallInterfaces(*sm);
+ Account::InstallInterfaces(system);
AM::InstallInterfaces(*sm, nv_flinger);
AOC::InstallInterfaces(*sm);
APM::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index aa115935d..346bad80d 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -108,8 +108,9 @@ private:
LOG_DEBUG(Service_Time, "called");
const auto& core_timing = Core::System::GetInstance().CoreTiming();
- const SteadyClockTimePoint steady_clock_time_point{
- Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000};
+ const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
+ const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000),
+ {}};
IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(steady_clock_time_point);
@@ -284,8 +285,8 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
}
const auto& core_timing = Core::System::GetInstance().CoreTiming();
- const SteadyClockTimePoint steady_clock_time_point{
- Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000, {}};
+ const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
+ const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}};
CalendarTime calendar_time{};
calendar_time.year = tm->tm_year + 1900;
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 10b13fb1d..f9e88be2b 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -141,6 +141,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
const FileSys::PatchManager pm(metadata.GetTitleID());
// Load NSO modules
+ modules.clear();
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
VAddr next_load_addr = base_address;
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
@@ -159,6 +160,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
}
next_load_addr = *tentative_next_load_addr;
+ modules.insert_or_assign(load_addr, module);
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
// Register module with GDBStub
GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
@@ -212,4 +214,13 @@ bool AppLoader_DeconstructedRomDirectory::IsRomFSUpdatable() const {
return false;
}
+ResultStatus AppLoader_DeconstructedRomDirectory::ReadNSOModules(Modules& modules) {
+ if (!is_loaded) {
+ return ResultStatus::ErrorNotInitialized;
+ }
+
+ modules = this->modules;
+ return ResultStatus::Success;
+}
+
} // namespace Loader
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 1a65c16a4..1c0a354a4 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -45,6 +45,8 @@ public:
ResultStatus ReadTitle(std::string& title) override;
bool IsRomFSUpdatable() const override;
+ ResultStatus ReadNSOModules(Modules& modules) override;
+
private:
FileSys::ProgramMetadata metadata;
FileSys::VirtualFile romfs;
@@ -54,6 +56,8 @@ private:
std::string name;
u64 title_id{};
bool override_update;
+
+ Modules modules;
};
} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 4068a60f8..227ecc704 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -159,17 +159,6 @@ public:
virtual LoadResult Load(Kernel::Process& process) = 0;
/**
- * Loads the system mode that this application needs.
- * This function defaults to 2 (96MB allocated to the application) if it can't read the
- * information.
- * @returns A pair with the optional system mode, and and the status.
- */
- virtual std::pair<std::optional<u32>, ResultStatus> LoadKernelSystemMode() {
- // 96MB allocated to the application.
- return std::make_pair(2, ResultStatus::Success);
- }
-
- /**
* Get the code (typically .code section) of the application
* @param buffer Reference to buffer to store data
* @return ResultStatus result of function
@@ -283,6 +272,12 @@ public:
return ResultStatus::ErrorNotImplemented;
}
+ using Modules = std::map<VAddr, std::string>;
+
+ virtual ResultStatus ReadNSOModules(Modules& modules) {
+ return ResultStatus::ErrorNotImplemented;
+ }
+
protected:
FileSys::VirtualFile file;
bool is_loaded = false;
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index 34efef09a..a152981a0 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -94,4 +94,8 @@ ResultStatus AppLoader_NAX::ReadLogo(std::vector<u8>& buffer) {
return nca_loader->ReadLogo(buffer);
}
+ResultStatus AppLoader_NAX::ReadNSOModules(Modules& modules) {
+ return nca_loader->ReadNSOModules(modules);
+}
+
} // namespace Loader
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index 00f1659c1..eaec9bf58 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -42,6 +42,8 @@ public:
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
+ ResultStatus ReadNSOModules(Modules& modules) override;
+
private:
std::unique_ptr<FileSys::NAX> nax;
std::unique_ptr<AppLoader_NCA> nca_loader;
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index b3f8f1083..0f65fb637 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -105,4 +105,13 @@ ResultStatus AppLoader_NCA::ReadLogo(std::vector<u8>& buffer) {
buffer = logo->GetFile("NintendoLogo.png")->ReadAllBytes();
return ResultStatus::Success;
}
+
+ResultStatus AppLoader_NCA::ReadNSOModules(Modules& modules) {
+ if (directory_loader == nullptr) {
+ return ResultStatus::ErrorNotInitialized;
+ }
+
+ return directory_loader->ReadNSOModules(modules);
+}
+
} // namespace Loader
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index 94f0ed677..e47dc0e47 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -42,6 +42,8 @@ public:
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
+ ResultStatus ReadNSOModules(Modules& modules) override;
+
private:
std::unique_ptr<FileSys::NCA> nca;
std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 62c090353..29311404a 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -152,8 +152,8 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
auto& system = Core::System::GetInstance();
const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
if (!cheats.empty()) {
- system.RegisterCheatList(cheats, Common::HexArrayToString(nso_header.build_id),
- load_base, load_base + program_image.size());
+ system.RegisterCheatList(cheats, Common::HexToString(nso_header.build_id), load_base,
+ load_base + program_image.size());
}
}
@@ -172,11 +172,15 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
+ modules.clear();
+
// Load module
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
if (!LoadModule(process, *file, base_address, true)) {
return {ResultStatus::ErrorLoadingNSO, {}};
}
+
+ modules.insert_or_assign(base_address, file->GetName());
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
is_loaded = true;
@@ -184,4 +188,9 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}};
}
+ResultStatus AppLoader_NSO::ReadNSOModules(Modules& modules) {
+ modules = this->modules;
+ return ResultStatus::Success;
+}
+
} // namespace Loader
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index fdce9191c..58cbe162d 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -85,6 +85,11 @@ public:
std::optional<FileSys::PatchManager> pm = {});
LoadResult Load(Kernel::Process& process) override;
+
+ ResultStatus ReadNSOModules(Modules& modules) override;
+
+private:
+ Modules modules;
};
} // namespace Loader
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index ad56bbb38..3a22ec2c6 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -183,4 +183,8 @@ ResultStatus AppLoader_NSP::ReadLogo(std::vector<u8>& buffer) {
return secondary_loader->ReadLogo(buffer);
}
+ResultStatus AppLoader_NSP::ReadNSOModules(Modules& modules) {
+ return secondary_loader->ReadNSOModules(modules);
+}
+
} // namespace Loader
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 85e870bdf..868b028d3 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -49,6 +49,8 @@ public:
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
+ ResultStatus ReadNSOModules(Modules& modules) override;
+
private:
std::unique_ptr<FileSys::NSP> nsp;
std::unique_ptr<AppLoader> secondary_loader;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 1e285a053..a5c4d3688 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -149,4 +149,8 @@ ResultStatus AppLoader_XCI::ReadLogo(std::vector<u8>& buffer) {
return nca_loader->ReadLogo(buffer);
}
+ResultStatus AppLoader_XCI::ReadNSOModules(Modules& modules) {
+ return nca_loader->ReadNSOModules(modules);
+}
+
} // namespace Loader
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index ae7145b14..618ae2f47 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -49,6 +49,8 @@ public:
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
+ ResultStatus ReadNSOModules(Modules& modules) override;
+
private:
std::unique_ptr<FileSys::XCI> xci;
std::unique_ptr<AppLoader_NCA> nca_loader;
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
new file mode 100644
index 000000000..8fe621aa0
--- /dev/null
+++ b/src/core/reporter.cpp
@@ -0,0 +1,353 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <fstream>
+#include <json.hpp>
+#include "common/file_util.h"
+#include "common/hex_util.h"
+#include "common/scm_rev.h"
+#include "core/arm/arm_interface.h"
+#include "core/core.h"
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/result.h"
+#include "core/reporter.h"
+#include "core/settings.h"
+#include "fmt/time.h"
+
+namespace {
+
+std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) {
+ return fmt::format("{}{}/{:016X}_{}.json", FileUtil::GetUserPath(FileUtil::UserPath::LogDir),
+ type, title_id, timestamp);
+}
+
+std::string GetTimestamp() {
+ const auto time = std::time(nullptr);
+ return fmt::format("{:%FT%H-%M-%S}", *std::localtime(&time));
+}
+
+using namespace nlohmann;
+
+void SaveToFile(const json& json, const std::string& filename) {
+ if (!FileUtil::CreateFullPath(filename))
+ LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename);
+
+ std::ofstream file(
+ FileUtil::SanitizePath(filename, FileUtil::DirectorySeparator::PlatformDefault));
+ file << std::setw(4) << json << std::endl;
+}
+
+json GetYuzuVersionData() {
+ return {
+ {"scm_rev", std::string(Common::g_scm_rev)},
+ {"scm_branch", std::string(Common::g_scm_branch)},
+ {"scm_desc", std::string(Common::g_scm_desc)},
+ {"build_name", std::string(Common::g_build_name)},
+ {"build_date", std::string(Common::g_build_date)},
+ {"build_fullname", std::string(Common::g_build_fullname)},
+ {"build_version", std::string(Common::g_build_version)},
+ {"shader_cache_version", std::string(Common::g_shader_cache_version)},
+ };
+}
+
+json GetReportCommonData(u64 title_id, ResultCode result, const std::string& timestamp,
+ std::optional<u128> user_id = {}) {
+ auto out = json{
+ {"title_id", fmt::format("{:016X}", title_id)},
+ {"result_raw", fmt::format("{:08X}", result.raw)},
+ {"result_module", fmt::format("{:08X}", static_cast<u32>(result.module.Value()))},
+ {"result_description", fmt::format("{:08X}", result.description.Value())},
+ {"timestamp", timestamp},
+ };
+ if (user_id.has_value())
+ out["user_id"] = fmt::format("{:016X}{:016X}", (*user_id)[1], (*user_id)[0]);
+ return out;
+}
+
+json GetProcessorStateData(const std::string& architecture, u64 entry_point, u64 sp, u64 pc,
+ u64 pstate, std::array<u64, 31> registers,
+ std::optional<std::array<u64, 32>> backtrace = {}) {
+ auto out = json{
+ {"entry_point", fmt::format("{:016X}", entry_point)},
+ {"sp", fmt::format("{:016X}", sp)},
+ {"pc", fmt::format("{:016X}", pc)},
+ {"pstate", fmt::format("{:016X}", pstate)},
+ {"architecture", architecture},
+ };
+
+ auto registers_out = json::object();
+ for (std::size_t i = 0; i < registers.size(); ++i) {
+ registers_out[fmt::format("X{:02d}", i)] = fmt::format("{:016X}", registers[i]);
+ }
+
+ out["registers"] = std::move(registers_out);
+
+ if (backtrace.has_value()) {
+ auto backtrace_out = json::array();
+ for (const auto& entry : *backtrace) {
+ backtrace_out.push_back(fmt::format("{:016X}", entry));
+ }
+ out["backtrace"] = std::move(backtrace_out);
+ }
+
+ return out;
+}
+
+json GetProcessorStateDataAuto(Core::System& system) {
+ const auto* process{system.CurrentProcess()};
+ const auto& vm_manager{process->VMManager()};
+ auto& arm{system.CurrentArmInterface()};
+
+ Core::ARM_Interface::ThreadContext context{};
+ arm.SaveContext(context);
+
+ return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32",
+ vm_manager.GetCodeRegionBaseAddress(), context.sp, context.pc,
+ context.pstate, context.cpu_registers);
+}
+
+json GetBacktraceData(Core::System& system) {
+ auto out = json::array();
+ const auto& backtrace{system.CurrentArmInterface().GetBacktrace()};
+ for (const auto& entry : backtrace) {
+ out.push_back({
+ {"module", entry.module},
+ {"address", fmt::format("{:016X}", entry.address)},
+ {"original_address", fmt::format("{:016X}", entry.original_address)},
+ {"offset", fmt::format("{:016X}", entry.offset)},
+ {"symbol_name", entry.name},
+ });
+ }
+
+ return out;
+}
+
+json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& system) {
+ json out;
+
+ out["yuzu_version"] = GetYuzuVersionData();
+ out["report_common"] = GetReportCommonData(title_id, RESULT_SUCCESS, timestamp);
+ out["processor_state"] = GetProcessorStateDataAuto(system);
+ out["backtrace"] = GetBacktraceData(system);
+
+ return out;
+}
+
+template <bool read_value, typename DescriptorType>
+json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer) {
+ auto buffer_out = json::array();
+ for (const auto& desc : buffer) {
+ auto entry = json{
+ {"address", fmt::format("{:016X}", desc.Address())},
+ {"size", fmt::format("{:016X}", desc.Size())},
+ };
+
+ if constexpr (read_value) {
+ std::vector<u8> data(desc.Size());
+ Memory::ReadBlock(desc.Address(), data.data(), desc.Size());
+ entry["data"] = Common::HexVectorToString(data);
+ }
+
+ buffer_out.push_back(std::move(entry));
+ }
+
+ return buffer_out;
+}
+
+json GetHLERequestContextData(Kernel::HLERequestContext& ctx) {
+ json out;
+
+ auto cmd_buf = json::array();
+ for (std::size_t i = 0; i < IPC::COMMAND_BUFFER_LENGTH; ++i) {
+ cmd_buf.push_back(fmt::format("{:08X}", ctx.CommandBuffer()[i]));
+ }
+
+ out["command_buffer"] = std::move(cmd_buf);
+
+ out["buffer_descriptor_a"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorA());
+ out["buffer_descriptor_b"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorB());
+ out["buffer_descriptor_c"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorC());
+ out["buffer_descriptor_x"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorX());
+
+ return std::move(out);
+}
+
+} // Anonymous namespace
+
+namespace Core {
+
+Reporter::Reporter(Core::System& system) : system(system) {}
+
+Reporter::~Reporter() = default;
+
+void Reporter::SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point,
+ u64 sp, u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far,
+ const std::array<u64, 31>& registers,
+ const std::array<u64, 32>& backtrace, u32 backtrace_size,
+ const std::string& arch, u32 unk10) const {
+ if (!IsReportingEnabled())
+ return;
+
+ const auto timestamp = GetTimestamp();
+ json out;
+
+ out["yuzu_version"] = GetYuzuVersionData();
+ out["report_common"] = GetReportCommonData(title_id, result, timestamp);
+
+ auto proc_out = GetProcessorStateData(arch, entry_point, sp, pc, pstate, registers, backtrace);
+ proc_out["set_flags"] = fmt::format("{:016X}", set_flags);
+ proc_out["afsr0"] = fmt::format("{:016X}", afsr0);
+ proc_out["afsr1"] = fmt::format("{:016X}", afsr1);
+ proc_out["esr"] = fmt::format("{:016X}", esr);
+ proc_out["far"] = fmt::format("{:016X}", far);
+ proc_out["backtrace_size"] = fmt::format("{:08X}", backtrace_size);
+ proc_out["unknown_10"] = fmt::format("{:08X}", unk10);
+
+ out["processor_state"] = std::move(proc_out);
+
+ SaveToFile(std::move(out), GetPath("crash_report", title_id, timestamp));
+}
+
+void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2,
+ std::optional<std::vector<u8>> resolved_buffer) const {
+ if (!IsReportingEnabled())
+ return;
+
+ const auto timestamp = GetTimestamp();
+ const auto title_id = system.CurrentProcess()->GetTitleID();
+ auto out = GetFullDataAuto(timestamp, title_id, system);
+
+ auto break_out = json{
+ {"type", fmt::format("{:08X}", type)},
+ {"signal_debugger", fmt::format("{}", signal_debugger)},
+ {"info1", fmt::format("{:016X}", info1)},
+ {"info2", fmt::format("{:016X}", info2)},
+ };
+
+ if (resolved_buffer.has_value()) {
+ break_out["debug_buffer"] = Common::HexVectorToString(*resolved_buffer);
+ }
+
+ out["svc_break"] = std::move(break_out);
+
+ SaveToFile(std::move(out), GetPath("svc_break_report", title_id, timestamp));
+}
+
+void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id,
+ const std::string& name,
+ const std::string& service_name) const {
+ if (!IsReportingEnabled())
+ return;
+
+ const auto timestamp = GetTimestamp();
+ const auto title_id = system.CurrentProcess()->GetTitleID();
+ auto out = GetFullDataAuto(timestamp, title_id, system);
+
+ auto function_out = GetHLERequestContextData(ctx);
+ function_out["command_id"] = command_id;
+ function_out["function_name"] = name;
+ function_out["service_name"] = service_name;
+
+ out["function"] = std::move(function_out);
+
+ SaveToFile(std::move(out), GetPath("unimpl_func_report", title_id, timestamp));
+}
+
+void Reporter::SaveUnimplementedAppletReport(
+ u32 applet_id, u32 common_args_version, u32 library_version, u32 theme_color,
+ bool startup_sound, u64 system_tick, std::vector<std::vector<u8>> normal_channel,
+ std::vector<std::vector<u8>> interactive_channel) const {
+ if (!IsReportingEnabled())
+ return;
+
+ const auto timestamp = GetTimestamp();
+ const auto title_id = system.CurrentProcess()->GetTitleID();
+ auto out = GetFullDataAuto(timestamp, title_id, system);
+
+ out["applet_common_args"] = {
+ {"applet_id", fmt::format("{:02X}", applet_id)},
+ {"common_args_version", fmt::format("{:08X}", common_args_version)},
+ {"library_version", fmt::format("{:08X}", library_version)},
+ {"theme_color", fmt::format("{:08X}", theme_color)},
+ {"startup_sound", fmt::format("{}", startup_sound)},
+ {"system_tick", fmt::format("{:016X}", system_tick)},
+ };
+
+ auto normal_out = json::array();
+ for (const auto& data : normal_channel) {
+ normal_out.push_back(Common::HexVectorToString(data));
+ }
+
+ auto interactive_out = json::array();
+ for (const auto& data : interactive_channel) {
+ interactive_out.push_back(Common::HexVectorToString(data));
+ }
+
+ out["applet_normal_data"] = std::move(normal_out);
+ out["applet_interactive_data"] = std::move(interactive_out);
+
+ SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp));
+}
+
+void Reporter::SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data,
+ std::optional<u128> user_id) const {
+ if (!IsReportingEnabled())
+ return;
+
+ const auto timestamp = GetTimestamp();
+ json out;
+
+ out["yuzu_version"] = GetYuzuVersionData();
+ out["report_common"] = GetReportCommonData(title_id, RESULT_SUCCESS, timestamp, user_id);
+
+ auto data_out = json::array();
+ for (const auto& d : data) {
+ data_out.push_back(Common::HexVectorToString(d));
+ }
+
+ out["play_report_process_id"] = fmt::format("{:016X}", process_id);
+ out["play_report_data"] = std::move(data_out);
+
+ SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp));
+}
+
+void Reporter::SaveErrorReport(u64 title_id, ResultCode result,
+ std::optional<std::string> custom_text_main,
+ std::optional<std::string> custom_text_detail) const {
+ if (!IsReportingEnabled())
+ return;
+
+ const auto timestamp = GetTimestamp();
+ json out;
+
+ out["yuzu_version"] = GetYuzuVersionData();
+ out["report_common"] = GetReportCommonData(title_id, result, timestamp);
+ out["processor_state"] = GetProcessorStateDataAuto(system);
+ out["backtrace"] = GetBacktraceData(system);
+
+ out["error_custom_text"] = {
+ {"main", *custom_text_main},
+ {"detail", *custom_text_detail},
+ };
+
+ SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp));
+}
+
+void Reporter::SaveUserReport() const {
+ if (!IsReportingEnabled())
+ return;
+
+ const auto timestamp = GetTimestamp();
+ const auto title_id = system.CurrentProcess()->GetTitleID();
+
+ SaveToFile(GetFullDataAuto(timestamp, title_id, system),
+ GetPath("user_report", title_id, timestamp));
+}
+
+bool Reporter::IsReportingEnabled() const {
+ return Settings::values.reporting_services;
+}
+
+} // namespace Core
diff --git a/src/core/reporter.h b/src/core/reporter.h
new file mode 100644
index 000000000..3de19c0f7
--- /dev/null
+++ b/src/core/reporter.h
@@ -0,0 +1,56 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <optional>
+#include <vector>
+#include "common/common_types.h"
+
+union ResultCode;
+
+namespace Kernel {
+class HLERequestContext;
+} // namespace Kernel
+
+namespace Core {
+
+class Reporter {
+public:
+ explicit Reporter(Core::System& system);
+ ~Reporter();
+
+ void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp,
+ u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far,
+ const std::array<u64, 31>& registers, const std::array<u64, 32>& backtrace,
+ u32 backtrace_size, const std::string& arch, u32 unk10) const;
+
+ void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2,
+ std::optional<std::vector<u8>> resolved_buffer = {}) const;
+
+ void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id,
+ const std::string& name,
+ const std::string& service_name) const;
+
+ void SaveUnimplementedAppletReport(u32 applet_id, u32 common_args_version, u32 library_version,
+ u32 theme_color, bool startup_sound, u64 system_tick,
+ std::vector<std::vector<u8>> normal_channel,
+ std::vector<std::vector<u8>> interactive_channel) const;
+
+ void SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data,
+ std::optional<u128> user_id = {}) const;
+
+ void SaveErrorReport(u64 title_id, ResultCode result,
+ std::optional<std::string> custom_text_main = {},
+ std::optional<std::string> custom_text_detail = {}) const;
+
+ void SaveUserReport() const;
+
+private:
+ bool IsReportingEnabled() const;
+
+ Core::System& system;
+};
+
+} // namespace Core
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index c1365879b..6d32ebea3 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -90,7 +90,6 @@ void LogSettings() {
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
- LogSetting("Renderer_UseCompatibilityProfile", Settings::values.use_compatibility_profile);
LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation);
LogSetting("Renderer_UseAsynchronousGpuEmulation",
diff --git a/src/core/settings.h b/src/core/settings.h
index 5ff3634aa..e2ffcaaf7 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -390,7 +390,6 @@ struct Values {
float resolution_factor;
bool use_frame_limit;
u16 frame_limit;
- bool use_compatibility_profile;
bool use_disk_shader_cache;
bool use_accurate_gpu_emulation;
bool use_asynchronous_gpu_emulation;
@@ -416,6 +415,7 @@ struct Values {
std::string program_args;
bool dump_exefs;
bool dump_nso;
+ bool reporting_services;
// WebService
bool enable_telemetry;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 4b17bada5..90d06830f 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -12,7 +12,6 @@
#include "common/file_util.h"
#include "common/logging/log.h"
-#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/loader/loader.h"
@@ -101,7 +100,30 @@ bool VerifyLogin(const std::string& username, const std::string& token) {
#endif
}
-TelemetrySession::TelemetrySession() {
+TelemetrySession::TelemetrySession() = default;
+
+TelemetrySession::~TelemetrySession() {
+ // Log one-time session end information
+ const s64 shutdown_time{std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::system_clock::now().time_since_epoch())
+ .count()};
+ AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time);
+
+#ifdef ENABLE_WEB_SERVICE
+ auto backend = std::make_unique<WebService::TelemetryJson>(
+ Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token);
+#else
+ auto backend = std::make_unique<Telemetry::NullVisitor>();
+#endif
+
+ // Complete the session, submitting to the web service backend if necessary
+ field_collection.Accept(*backend);
+ if (Settings::values.enable_telemetry) {
+ backend->Complete();
+ }
+}
+
+void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
// Log one-time top-level information
AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId());
@@ -112,26 +134,28 @@ TelemetrySession::TelemetrySession() {
AddField(Telemetry::FieldType::Session, "Init_Time", init_time);
u64 program_id{};
- const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadProgramId(program_id)};
+ const Loader::ResultStatus res{app_loader.ReadProgramId(program_id)};
if (res == Loader::ResultStatus::Success) {
const std::string formatted_program_id{fmt::format("{:016X}", program_id)};
AddField(Telemetry::FieldType::Session, "ProgramId", formatted_program_id);
std::string name;
- System::GetInstance().GetAppLoader().ReadTitle(name);
+ app_loader.ReadTitle(name);
if (name.empty()) {
auto [nacp, icon_file] = FileSys::PatchManager(program_id).GetControlMetadata();
- if (nacp != nullptr)
+ if (nacp != nullptr) {
name = nacp->GetApplicationName();
+ }
}
- if (!name.empty())
+ if (!name.empty()) {
AddField(Telemetry::FieldType::Session, "ProgramName", name);
+ }
}
AddField(Telemetry::FieldType::Session, "ProgramFormat",
- static_cast<u8>(System::GetInstance().GetAppLoader().GetFileType()));
+ static_cast<u8>(app_loader.GetFileType()));
// Log application information
Telemetry::AppendBuildInfo(field_collection);
@@ -162,27 +186,6 @@ TelemetrySession::TelemetrySession() {
Settings::values.use_docked_mode);
}
-TelemetrySession::~TelemetrySession() {
- // Log one-time session end information
- const s64 shutdown_time{std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::system_clock::now().time_since_epoch())
- .count()};
- AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time);
-
-#ifdef ENABLE_WEB_SERVICE
- auto backend = std::make_unique<WebService::TelemetryJson>(
- Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token);
-#else
- auto backend = std::make_unique<Telemetry::NullVisitor>();
-#endif
-
- // Complete the session, submitting to web service if necessary
- field_collection.Accept(*backend);
- if (Settings::values.enable_telemetry)
- backend->Complete();
- backend = nullptr;
-}
-
bool TelemetrySession::SubmitTestcase() {
#ifdef ENABLE_WEB_SERVICE
auto backend = std::make_unique<WebService::TelemetryJson>(
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index cae5a45a0..17ac22377 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -4,10 +4,13 @@
#pragma once
-#include <memory>
#include <string>
#include "common/telemetry.h"
+namespace Loader {
+class AppLoader;
+}
+
namespace Core {
/**
@@ -15,11 +18,33 @@ namespace Core {
* session, logging any one-time fields. Interfaces with the telemetry backend used for submitting
* data to the web service. Submits session data on close.
*/
-class TelemetrySession : NonCopyable {
+class TelemetrySession {
public:
- TelemetrySession();
+ explicit TelemetrySession();
~TelemetrySession();
+ TelemetrySession(const TelemetrySession&) = delete;
+ TelemetrySession& operator=(const TelemetrySession&) = delete;
+
+ TelemetrySession(TelemetrySession&&) = delete;
+ TelemetrySession& operator=(TelemetrySession&&) = delete;
+
+ /**
+ * Adds the initial telemetry info necessary when starting up a title.
+ *
+ * This includes information such as:
+ * - Telemetry ID
+ * - Initialization time
+ * - Title ID
+ * - Title name
+ * - Title file format
+ * - Miscellaneous settings values.
+ *
+ * @param app_loader The application loader to use to retrieve
+ * title-specific information.
+ */
+ void AddInitialInfo(Loader::AppLoader& app_loader);
+
/**
* Wrapper around the Telemetry::FieldCollection::AddField method.
* @param type Type of the field to add.
diff --git a/src/core/tracer/citrace.h b/src/core/tracer/citrace.h
deleted file mode 100644
index 21fdc127a..000000000
--- a/src/core/tracer/citrace.h
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/common_types.h"
-
-namespace CiTrace {
-
-// NOTE: Things are stored in little-endian
-
-#pragma pack(1)
-
-struct CTHeader {
- static const char* ExpectedMagicWord() {
- return "CiTr";
- }
-
- static u32 ExpectedVersion() {
- return 1;
- }
-
- char magic[4];
- u32 version;
- u32 header_size;
-
- struct {
- // NOTE: Register range sizes are technically hardware-constants, but the actual limits
- // aren't known. Hence we store the presumed limits along the offsets.
- // Sizes are given in u32 units.
- u32 gpu_registers;
- u32 gpu_registers_size;
- u32 lcd_registers;
- u32 lcd_registers_size;
- u32 pica_registers;
- u32 pica_registers_size;
- u32 default_attributes;
- u32 default_attributes_size;
- u32 vs_program_binary;
- u32 vs_program_binary_size;
- u32 vs_swizzle_data;
- u32 vs_swizzle_data_size;
- u32 vs_float_uniforms;
- u32 vs_float_uniforms_size;
- u32 gs_program_binary;
- u32 gs_program_binary_size;
- u32 gs_swizzle_data;
- u32 gs_swizzle_data_size;
- u32 gs_float_uniforms;
- u32 gs_float_uniforms_size;
-
- // Other things we might want to store here:
- // - Initial framebuffer data, maybe even a full copy of FCRAM/VRAM
- // - Lookup tables for fragment lighting
- // - Lookup tables for procedural textures
- } initial_state_offsets;
-
- u32 stream_offset;
- u32 stream_size;
-};
-
-enum CTStreamElementType : u32 {
- FrameMarker = 0xE1,
- MemoryLoad = 0xE2,
- RegisterWrite = 0xE3,
-};
-
-struct CTMemoryLoad {
- u32 file_offset;
- u32 size;
- u32 physical_address;
- u32 pad;
-};
-
-struct CTRegisterWrite {
- u32 physical_address;
-
- enum : u32 {
- SIZE_8 = 0xD1,
- SIZE_16 = 0xD2,
- SIZE_32 = 0xD3,
- SIZE_64 = 0xD4,
- } size;
-
- // TODO: Make it clearer which bits of this member are used for sizes other than 32 bits
- u64 value;
-};
-
-struct CTStreamElement {
- CTStreamElementType type;
-
- union {
- CTMemoryLoad memory_load;
- CTRegisterWrite register_write;
- };
-};
-
-#pragma pack()
-} // namespace CiTrace
diff --git a/src/core/tracer/recorder.cpp b/src/core/tracer/recorder.cpp
deleted file mode 100644
index 73cacb47f..000000000
--- a/src/core/tracer/recorder.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <cstring>
-#include "common/assert.h"
-#include "common/file_util.h"
-#include "common/logging/log.h"
-#include "core/tracer/recorder.h"
-
-namespace CiTrace {
-
-Recorder::Recorder(const InitialState& initial_state) : initial_state(initial_state) {}
-
-void Recorder::Finish(const std::string& filename) {
- // Setup CiTrace header
- CTHeader header;
- std::memcpy(header.magic, CTHeader::ExpectedMagicWord(), 4);
- header.version = CTHeader::ExpectedVersion();
- header.header_size = sizeof(CTHeader);
-
- // Calculate file offsets
- auto& initial = header.initial_state_offsets;
-
- initial.gpu_registers_size = static_cast<u32>(initial_state.gpu_registers.size());
- initial.lcd_registers_size = static_cast<u32>(initial_state.lcd_registers.size());
- initial.pica_registers_size = static_cast<u32>(initial_state.pica_registers.size());
- initial.default_attributes_size = static_cast<u32>(initial_state.default_attributes.size());
- initial.vs_program_binary_size = static_cast<u32>(initial_state.vs_program_binary.size());
- initial.vs_swizzle_data_size = static_cast<u32>(initial_state.vs_swizzle_data.size());
- initial.vs_float_uniforms_size = static_cast<u32>(initial_state.vs_float_uniforms.size());
- initial.gs_program_binary_size = static_cast<u32>(initial_state.gs_program_binary.size());
- initial.gs_swizzle_data_size = static_cast<u32>(initial_state.gs_swizzle_data.size());
- initial.gs_float_uniforms_size = static_cast<u32>(initial_state.gs_float_uniforms.size());
- header.stream_size = static_cast<u32>(stream.size());
-
- initial.gpu_registers = sizeof(header);
- initial.lcd_registers = initial.gpu_registers + initial.gpu_registers_size * sizeof(u32);
- initial.pica_registers = initial.lcd_registers + initial.lcd_registers_size * sizeof(u32);
- ;
- initial.default_attributes = initial.pica_registers + initial.pica_registers_size * sizeof(u32);
- initial.vs_program_binary =
- initial.default_attributes + initial.default_attributes_size * sizeof(u32);
- initial.vs_swizzle_data =
- initial.vs_program_binary + initial.vs_program_binary_size * sizeof(u32);
- initial.vs_float_uniforms =
- initial.vs_swizzle_data + initial.vs_swizzle_data_size * sizeof(u32);
- initial.gs_program_binary =
- initial.vs_float_uniforms + initial.vs_float_uniforms_size * sizeof(u32);
- initial.gs_swizzle_data =
- initial.gs_program_binary + initial.gs_program_binary_size * sizeof(u32);
- initial.gs_float_uniforms =
- initial.gs_swizzle_data + initial.gs_swizzle_data_size * sizeof(u32);
- header.stream_offset = initial.gs_float_uniforms + initial.gs_float_uniforms_size * sizeof(u32);
-
- // Iterate through stream elements, update relevant stream element data
- for (auto& stream_element : stream) {
- switch (stream_element.data.type) {
- case MemoryLoad: {
- auto& file_offset = memory_regions[stream_element.hash];
- if (!stream_element.uses_existing_data) {
- file_offset = header.stream_offset;
- }
- stream_element.data.memory_load.file_offset = file_offset;
- break;
- }
-
- default:
- // Other commands don't use any extra data
- DEBUG_ASSERT(stream_element.extra_data.size() == 0);
- break;
- }
- header.stream_offset += static_cast<u32>(stream_element.extra_data.size());
- }
-
- try {
- // Open file and write header
- FileUtil::IOFile file(filename, "wb");
- std::size_t written = file.WriteObject(header);
- if (written != 1 || file.Tell() != initial.gpu_registers)
- throw "Failed to write header";
-
- // Write initial state
- written =
- file.WriteArray(initial_state.gpu_registers.data(), initial_state.gpu_registers.size());
- if (written != initial_state.gpu_registers.size() || file.Tell() != initial.lcd_registers)
- throw "Failed to write GPU registers";
-
- written =
- file.WriteArray(initial_state.lcd_registers.data(), initial_state.lcd_registers.size());
- if (written != initial_state.lcd_registers.size() || file.Tell() != initial.pica_registers)
- throw "Failed to write LCD registers";
-
- written = file.WriteArray(initial_state.pica_registers.data(),
- initial_state.pica_registers.size());
- if (written != initial_state.pica_registers.size() ||
- file.Tell() != initial.default_attributes)
- throw "Failed to write Pica registers";
-
- written = file.WriteArray(initial_state.default_attributes.data(),
- initial_state.default_attributes.size());
- if (written != initial_state.default_attributes.size() ||
- file.Tell() != initial.vs_program_binary)
- throw "Failed to write default vertex attributes";
-
- written = file.WriteArray(initial_state.vs_program_binary.data(),
- initial_state.vs_program_binary.size());
- if (written != initial_state.vs_program_binary.size() ||
- file.Tell() != initial.vs_swizzle_data)
- throw "Failed to write vertex shader program binary";
-
- written = file.WriteArray(initial_state.vs_swizzle_data.data(),
- initial_state.vs_swizzle_data.size());
- if (written != initial_state.vs_swizzle_data.size() ||
- file.Tell() != initial.vs_float_uniforms)
- throw "Failed to write vertex shader swizzle data";
-
- written = file.WriteArray(initial_state.vs_float_uniforms.data(),
- initial_state.vs_float_uniforms.size());
- if (written != initial_state.vs_float_uniforms.size() ||
- file.Tell() != initial.gs_program_binary)
- throw "Failed to write vertex shader float uniforms";
-
- written = file.WriteArray(initial_state.gs_program_binary.data(),
- initial_state.gs_program_binary.size());
- if (written != initial_state.gs_program_binary.size() ||
- file.Tell() != initial.gs_swizzle_data)
- throw "Failed to write geomtry shader program binary";
-
- written = file.WriteArray(initial_state.gs_swizzle_data.data(),
- initial_state.gs_swizzle_data.size());
- if (written != initial_state.gs_swizzle_data.size() ||
- file.Tell() != initial.gs_float_uniforms)
- throw "Failed to write geometry shader swizzle data";
-
- written = file.WriteArray(initial_state.gs_float_uniforms.data(),
- initial_state.gs_float_uniforms.size());
- if (written != initial_state.gs_float_uniforms.size() ||
- file.Tell() != initial.gs_float_uniforms + sizeof(u32) * initial.gs_float_uniforms_size)
- throw "Failed to write geometry shader float uniforms";
-
- // Iterate through stream elements, write "extra data"
- for (const auto& stream_element : stream) {
- if (stream_element.extra_data.size() == 0)
- continue;
-
- written =
- file.WriteBytes(stream_element.extra_data.data(), stream_element.extra_data.size());
- if (written != stream_element.extra_data.size())
- throw "Failed to write extra data";
- }
-
- if (file.Tell() != header.stream_offset)
- throw "Unexpected end of extra data";
-
- // Write actual stream elements
- for (const auto& stream_element : stream) {
- if (1 != file.WriteObject(stream_element.data))
- throw "Failed to write stream element";
- }
- } catch (const char* str) {
- LOG_ERROR(HW_GPU, "Writing CiTrace file failed: {}", str);
- }
-}
-
-void Recorder::FrameFinished() {
- stream.push_back({{FrameMarker}});
-}
-
-void Recorder::MemoryAccessed(const u8* data, u32 size, u32 physical_address) {
- StreamElement element = {{MemoryLoad}};
- element.data.memory_load.size = size;
- element.data.memory_load.physical_address = physical_address;
-
- // Compute hash over given memory region to check if the contents are already stored internally
- boost::crc_32_type result;
- result.process_bytes(data, size);
- element.hash = result.checksum();
-
- element.uses_existing_data = (memory_regions.find(element.hash) != memory_regions.end());
- if (!element.uses_existing_data) {
- element.extra_data.resize(size);
- memcpy(element.extra_data.data(), data, size);
- memory_regions.insert({element.hash, 0}); // file offset will be initialized in Finish()
- }
-
- stream.push_back(element);
-}
-
-template <typename T>
-void Recorder::RegisterWritten(u32 physical_address, T value) {
- StreamElement element = {{RegisterWrite}};
- element.data.register_write.size =
- (sizeof(T) == 1) ? CTRegisterWrite::SIZE_8
- : (sizeof(T) == 2) ? CTRegisterWrite::SIZE_16
- : (sizeof(T) == 4) ? CTRegisterWrite::SIZE_32
- : CTRegisterWrite::SIZE_64;
- element.data.register_write.physical_address = physical_address;
- element.data.register_write.value = value;
-
- stream.push_back(element);
-}
-
-template void Recorder::RegisterWritten(u32, u8);
-template void Recorder::RegisterWritten(u32, u16);
-template void Recorder::RegisterWritten(u32, u32);
-template void Recorder::RegisterWritten(u32, u64);
-} // namespace CiTrace
diff --git a/src/core/tracer/recorder.h b/src/core/tracer/recorder.h
deleted file mode 100644
index e1cefd5fe..000000000
--- a/src/core/tracer/recorder.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <string>
-#include <unordered_map>
-#include <vector>
-#include <boost/crc.hpp>
-#include "common/common_types.h"
-#include "core/tracer/citrace.h"
-
-namespace CiTrace {
-
-class Recorder {
-public:
- struct InitialState {
- std::vector<u32> gpu_registers;
- std::vector<u32> lcd_registers;
- std::vector<u32> pica_registers;
- std::vector<u32> default_attributes;
- std::vector<u32> vs_program_binary;
- std::vector<u32> vs_swizzle_data;
- std::vector<u32> vs_float_uniforms;
- std::vector<u32> gs_program_binary;
- std::vector<u32> gs_swizzle_data;
- std::vector<u32> gs_float_uniforms;
- };
-
- /**
- * Recorder constructor
- * @param initial_state Initial recorder state
- */
- explicit Recorder(const InitialState& initial_state);
-
- /// Finish recording of this Citrace and save it using the given filename.
- void Finish(const std::string& filename);
-
- /// Mark end of a frame
- void FrameFinished();
-
- /**
- * Store a copy of the given memory range in the recording.
- * @note Use this whenever the GPU is about to access a particular memory region.
- * @note The implementation will make sure to minimize redundant memory updates.
- */
- void MemoryAccessed(const u8* data, u32 size, u32 physical_address);
-
- /**
- * Record a register write.
- * @note Use this whenever a GPU-related MMIO register has been written to.
- */
- template <typename T>
- void RegisterWritten(u32 physical_address, T value);
-
-private:
- // Initial state of recording start
- InitialState initial_state;
-
- // Command stream
- struct StreamElement {
- CTStreamElement data;
-
- /**
- * Extra data to store along "core" data.
- * This is e.g. used for data used in MemoryUpdates.
- */
- std::vector<u8> extra_data;
-
- /// Optional CRC hash (e.g. for hashing memory regions)
- boost::crc_32_type::value_type hash;
-
- /// If true, refer to data already written to the output file instead of extra_data
- bool uses_existing_data;
- };
-
- std::vector<StreamElement> stream;
-
- /**
- * Internal cache which maps hashes of memory contents to file offsets at which those memory
- * contents are stored.
- */
- std::unordered_map<boost::crc_32_type::value_type /*hash*/, u32 /*file_offset*/> memory_regions;
-};
-
-} // namespace CiTrace