summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt31
-rw-r--r--src/core/arm/arm_interface.h14
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp42
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h26
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp42
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h18
-rw-r--r--src/core/constants.cpp17
-rw-r--r--src/core/constants.h17
-rw-r--r--src/core/core.cpp117
-rw-r--r--src/core/core.h44
-rw-r--r--src/core/core_cpu.cpp21
-rw-r--r--src/core/core_cpu.h8
-rw-r--r--src/core/core_timing.cpp2
-rw-r--r--src/core/core_timing.h2
-rw-r--r--src/core/core_timing_util.cpp44
-rw-r--r--src/core/core_timing_util.h53
-rw-r--r--src/core/cpu_core_manager.cpp9
-rw-r--r--src/core/cpu_core_manager.h7
-rw-r--r--src/core/crypto/key_manager.cpp7
-rw-r--r--src/core/file_sys/card_image.cpp46
-rw-r--r--src/core/file_sys/cheat_engine.cpp492
-rw-r--r--src/core/file_sys/cheat_engine.h234
-rw-r--r--src/core/file_sys/content_archive.h15
-rw-r--r--src/core/file_sys/control_metadata.cpp14
-rw-r--r--src/core/file_sys/control_metadata.h35
-rw-r--r--src/core/file_sys/errors.h3
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp4
-rw-r--r--src/core/file_sys/ips_layer.cpp3
-rw-r--r--src/core/file_sys/nca_metadata.cpp8
-rw-r--r--src/core/file_sys/nca_metadata.h12
-rw-r--r--src/core/file_sys/patch_manager.cpp107
-rw-r--r--src/core/file_sys/patch_manager.h11
-rw-r--r--src/core/file_sys/program_metadata.cpp27
-rw-r--r--src/core/file_sys/program_metadata.h2
-rw-r--r--src/core/file_sys/registered_cache.cpp291
-rw-r--r--src/core/file_sys/registered_cache.h156
-rw-r--r--src/core/file_sys/romfs_factory.cpp2
-rw-r--r--src/core/file_sys/savedata_factory.cpp8
-rw-r--r--src/core/file_sys/savedata_factory.h11
-rw-r--r--src/core/file_sys/submission_package.cpp26
-rw-r--r--src/core/file_sys/submission_package.h11
-rw-r--r--src/core/file_sys/system_archive/system_archive.cpp3
-rw-r--r--src/core/file_sys/system_archive/system_version.cpp52
-rw-r--r--src/core/file_sys/system_archive/system_version.h16
-rw-r--r--src/core/file_sys/xts_archive.cpp2
-rw-r--r--src/core/frontend/applets/error.cpp34
-rw-r--r--src/core/frontend/applets/error.h37
-rw-r--r--src/core/frontend/applets/general_frontend.cpp27
-rw-r--r--src/core/frontend/applets/general_frontend.h28
-rw-r--r--src/core/frontend/applets/profile_select.cpp5
-rw-r--r--src/core/frontend/applets/profile_select.h8
-rw-r--r--src/core/frontend/emu_window.cpp8
-rw-r--r--src/core/frontend/emu_window.h44
-rw-r--r--src/core/frontend/framebuffer_layout.cpp13
-rw-r--r--src/core/frontend/framebuffer_layout.h21
-rw-r--r--src/core/gdbstub/gdbstub.cpp2
-rw-r--r--src/core/hle/ipc.h44
-rw-r--r--src/core/hle/ipc_helpers.h54
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp44
-rw-r--r--src/core/hle/kernel/address_arbiter.h16
-rw-r--r--src/core/hle/kernel/client_port.cpp15
-rw-r--r--src/core/hle/kernel/client_port.h2
-rw-r--r--src/core/hle/kernel/client_session.cpp14
-rw-r--r--src/core/hle/kernel/client_session.h11
-rw-r--r--src/core/hle/kernel/code_set.cpp12
-rw-r--r--src/core/hle/kernel/code_set.h89
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp31
-rw-r--r--src/core/hle/kernel/hle_ipc.h23
-rw-r--r--src/core/hle/kernel/kernel.cpp31
-rw-r--r--src/core/hle/kernel/kernel.h10
-rw-r--r--src/core/hle/kernel/mutex.cpp35
-rw-r--r--src/core/hle/kernel/mutex.h20
-rw-r--r--src/core/hle/kernel/object.cpp2
-rw-r--r--src/core/hle/kernel/object.h6
-rw-r--r--src/core/hle/kernel/process.cpp104
-rw-r--r--src/core/hle/kernel/process.h140
-rw-r--r--src/core/hle/kernel/readable_event.cpp5
-rw-r--r--src/core/hle/kernel/readable_event.h4
-rw-r--r--src/core/hle/kernel/resource_limit.cpp7
-rw-r--r--src/core/hle/kernel/resource_limit.h13
-rw-r--r--src/core/hle/kernel/scheduler.cpp79
-rw-r--r--src/core/hle/kernel/scheduler.h12
-rw-r--r--src/core/hle/kernel/server_port.cpp13
-rw-r--r--src/core/hle/kernel/server_port.h44
-rw-r--r--src/core/hle/kernel/server_session.cpp100
-rw-r--r--src/core/hle/kernel/server_session.h64
-rw-r--r--src/core/hle/kernel/shared_memory.cpp16
-rw-r--r--src/core/hle/kernel/shared_memory.h12
-rw-r--r--src/core/hle/kernel/svc.cpp903
-rw-r--r--src/core/hle/kernel/svc.h6
-rw-r--r--src/core/hle/kernel/svc_wrap.h357
-rw-r--r--src/core/hle/kernel/thread.cpp111
-rw-r--r--src/core/hle/kernel/thread.h113
-rw-r--r--src/core/hle/kernel/transfer_memory.cpp81
-rw-r--r--src/core/hle/kernel/transfer_memory.h103
-rw-r--r--src/core/hle/kernel/vm_manager.cpp179
-rw-r--r--src/core/hle/kernel/vm_manager.h128
-rw-r--r--src/core/hle/kernel/wait_object.cpp31
-rw-r--r--src/core/hle/kernel/wait_object.h4
-rw-r--r--src/core/hle/kernel/writable_event.h2
-rw-r--r--src/core/hle/result.h22
-rw-r--r--src/core/hle/service/acc/acc.cpp93
-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.cpp11
-rw-r--r--src/core/hle/service/acc/acc_su.h4
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp15
-rw-r--r--src/core/hle/service/acc/acc_u0.h4
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp11
-rw-r--r--src/core/hle/service/acc/acc_u1.h4
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp28
-rw-r--r--src/core/hle/service/acc/profile_manager.h66
-rw-r--r--src/core/hle/service/am/am.cpp294
-rw-r--r--src/core/hle/service/am/am.h34
-rw-r--r--src/core/hle/service/am/applet_ae.cpp8
-rw-r--r--src/core/hle/service/am/applets/applets.cpp103
-rw-r--r--src/core/hle/service/am/applets/applets.h72
-rw-r--r--src/core/hle/service/am/applets/error.cpp182
-rw-r--r--src/core/hle/service/am/applets/error.h47
-rw-r--r--src/core/hle/service/am/applets/general_backend.cpp (renamed from src/core/hle/service/am/applets/stub_applet.cpp)68
-rw-r--r--src/core/hle/service/am/applets/general_backend.h48
-rw-r--r--src/core/hle/service/am/applets/profile_select.cpp14
-rw-r--r--src/core/hle/service/am/applets/profile_select.h9
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp7
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.h4
-rw-r--r--src/core/hle/service/am/applets/stub_applet.h24
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp6
-rw-r--r--src/core/hle/service/am/applets/web_browser.h4
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp79
-rw-r--r--src/core/hle/service/apm/interface.cpp2
-rw-r--r--src/core/hle/service/audio/audctl.cpp30
-rw-r--r--src/core/hle/service/audio/audctl.h4
-rw-r--r--src/core/hle/service/audio/audin_u.cpp5
-rw-r--r--src/core/hle/service/audio/audout_u.cpp34
-rw-r--r--src/core/hle/service/audio/audrec_u.cpp4
-rw-r--r--src/core/hle/service/audio/audren_u.cpp365
-rw-r--r--src/core/hle/service/audio/audren_u.h2
-rw-r--r--src/core/hle/service/audio/hwopus.cpp206
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp7
-rw-r--r--src/core/hle/service/btm/btm.cpp8
-rw-r--r--src/core/hle/service/caps/caps.cpp89
-rw-r--r--src/core/hle/service/fatal/fatal.cpp89
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp30
-rw-r--r--src/core/hle/service/filesystem/filesystem.h4
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp116
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h1
-rw-r--r--src/core/hle/service/friend/friend.cpp5
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h30
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/npad.h102
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h4
-rw-r--r--src/core/hle/service/hid/hid.cpp25
-rw-r--r--src/core/hle/service/hid/hid.h5
-rw-r--r--src/core/hle/service/ldn/ldn.cpp54
-rw-r--r--src/core/hle/service/ldr/ldr.cpp157
-rw-r--r--src/core/hle/service/lm/lm.cpp2
-rw-r--r--src/core/hle/service/mii/mii.cpp341
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp416
-rw-r--r--src/core/hle/service/mii/mii_manager.h273
-rw-r--r--src/core/hle/service/ncm/ncm.cpp80
-rw-r--r--src/core/hle/service/nfc/nfc.cpp2
-rw-r--r--src/core/hle/service/nfp/nfp.cpp8
-rw-r--r--src/core/hle/service/nifm/nifm.cpp8
-rw-r--r--src/core/hle/service/nim/nim.cpp2
-rw-r--r--src/core/hle/service/npns/npns.cpp3
-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/nvdevice.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp17
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp3
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp2
-rw-r--r--src/core/hle/service/nvdrv/interface.h2
-rw-r--r--src/core/hle/service/nvdrv/nvmemp.h2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp2
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp16
-rw-r--r--src/core/hle/service/pctl/module.cpp7
-rw-r--r--src/core/hle/service/pm/pm.cpp21
-rw-r--r--src/core/hle/service/service.cpp12
-rw-r--r--src/core/hle/service/service.h2
-rw-r--r--src/core/hle/service/set/set.cpp60
-rw-r--r--src/core/hle/service/set/set_cal.cpp2
-rw-r--r--src/core/hle/service/set/set_cal.h2
-rw-r--r--src/core/hle/service/set/set_sys.cpp106
-rw-r--r--src/core/hle/service/set/set_sys.h2
-rw-r--r--src/core/hle/service/sm/controller.cpp2
-rw-r--r--src/core/hle/service/sm/sm.h2
-rw-r--r--src/core/hle/service/sockets/bsd.cpp5
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp12
-rw-r--r--src/core/hle/service/spl/module.cpp4
-rw-r--r--src/core/hle/service/ssl/ssl.cpp12
-rw-r--r--src/core/hle/service/time/interface.cpp10
-rw-r--r--src/core/hle/service/time/time.cpp9
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp2
-rw-r--r--src/core/hle/service/vi/vi.cpp45
-rw-r--r--src/core/hle/service/vi/vi.h40
-rw-r--r--src/core/hle/service/vi/vi_m.cpp12
-rw-r--r--src/core/hle/service/vi/vi_m.h19
-rw-r--r--src/core/hle/service/vi/vi_s.cpp12
-rw-r--r--src/core/hle/service/vi/vi_s.h19
-rw-r--r--src/core/hle/service/vi/vi_u.cpp12
-rw-r--r--src/core/hle/service/vi/vi_u.h19
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp40
-rw-r--r--src/core/loader/deconstructed_rom_directory.h2
-rw-r--r--src/core/loader/elf.cpp18
-rw-r--r--src/core/loader/elf.h2
-rw-r--r--src/core/loader/linker.cpp147
-rw-r--r--src/core/loader/linker.h36
-rw-r--r--src/core/loader/loader.h19
-rw-r--r--src/core/loader/nax.cpp30
-rw-r--r--src/core/loader/nax.h2
-rw-r--r--src/core/loader/nca.cpp26
-rw-r--r--src/core/loader/nca.h2
-rw-r--r--src/core/loader/nro.cpp17
-rw-r--r--src/core/loader/nro.h6
-rw-r--r--src/core/loader/nso.cpp130
-rw-r--r--src/core/loader/nso.h45
-rw-r--r--src/core/loader/nsp.cpp38
-rw-r--r--src/core/loader/nsp.h2
-rw-r--r--src/core/loader/xci.cpp28
-rw-r--r--src/core/loader/xci.h4
-rw-r--r--src/core/memory.cpp234
-rw-r--r--src/core/memory.h104
-rw-r--r--src/core/memory_hook.cpp11
-rw-r--r--src/core/memory_hook.h47
-rw-r--r--src/core/memory_setup.h19
-rw-r--r--src/core/perf_stats.cpp10
-rw-r--r--src/core/settings.cpp4
-rw-r--r--src/core/settings.h2
-rw-r--r--src/core/telemetry_session.cpp54
-rw-r--r--src/core/telemetry_session.h32
236 files changed, 8681 insertions, 3594 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 8ccb2d5f0..a2e2e976e 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
@@ -31,6 +33,8 @@ add_library(core STATIC
file_sys/bis_factory.h
file_sys/card_image.cpp
file_sys/card_image.h
+ file_sys/cheat_engine.cpp
+ file_sys/cheat_engine.h
file_sys/content_archive.cpp
file_sys/content_archive.h
file_sys/control_metadata.cpp
@@ -68,6 +72,8 @@ add_library(core STATIC
file_sys/system_archive/ng_word.h
file_sys/system_archive/system_archive.cpp
file_sys/system_archive/system_archive.h
+ file_sys/system_archive/system_version.cpp
+ file_sys/system_archive/system_version.h
file_sys/vfs.cpp
file_sys/vfs.h
file_sys/vfs_concat.cpp
@@ -84,6 +90,10 @@ add_library(core STATIC
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
file_sys/xts_archive.h
+ frontend/applets/error.cpp
+ frontend/applets/error.h
+ frontend/applets/general_frontend.cpp
+ frontend/applets/general_frontend.h
frontend/applets/profile_select.cpp
frontend/applets/profile_select.h
frontend/applets/software_keyboard.cpp
@@ -107,6 +117,8 @@ add_library(core STATIC
hle/kernel/client_port.h
hle/kernel/client_session.cpp
hle/kernel/client_session.h
+ hle/kernel/code_set.cpp
+ hle/kernel/code_set.h
hle/kernel/errors.h
hle/kernel/handle_table.cpp
hle/kernel/handle_table.h
@@ -140,6 +152,8 @@ add_library(core STATIC
hle/kernel/svc_wrap.h
hle/kernel/thread.cpp
hle/kernel/thread.h
+ hle/kernel/transfer_memory.cpp
+ hle/kernel/transfer_memory.h
hle/kernel/vm_manager.cpp
hle/kernel/vm_manager.h
hle/kernel/wait_object.cpp
@@ -169,12 +183,14 @@ add_library(core STATIC
hle/service/am/applet_oe.h
hle/service/am/applets/applets.cpp
hle/service/am/applets/applets.h
+ hle/service/am/applets/error.cpp
+ hle/service/am/applets/error.h
+ hle/service/am/applets/general_backend.cpp
+ hle/service/am/applets/general_backend.h
hle/service/am/applets/profile_select.cpp
hle/service/am/applets/profile_select.h
hle/service/am/applets/software_keyboard.cpp
hle/service/am/applets/software_keyboard.h
- hle/service/am/applets/stub_applet.cpp
- hle/service/am/applets/stub_applet.h
hle/service/am/applets/web_browser.cpp
hle/service/am/applets/web_browser.h
hle/service/am/idle.cpp
@@ -296,6 +312,8 @@ add_library(core STATIC
hle/service/mig/mig.h
hle/service/mii/mii.cpp
hle/service/mii/mii.h
+ hle/service/mii/mii_manager.cpp
+ hle/service/mii/mii_manager.h
hle/service/mm/mm_u.cpp
hle/service/mm/mm_u.h
hle/service/ncm/ncm.cpp
@@ -312,6 +330,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
@@ -419,8 +440,6 @@ add_library(core STATIC
loader/deconstructed_rom_directory.h
loader/elf.cpp
loader/elf.h
- loader/linker.cpp
- loader/linker.h
loader/loader.cpp
loader/loader.h
loader/nax.cpp
@@ -437,8 +456,6 @@ add_library(core STATIC
loader/xci.h
memory.cpp
memory.h
- memory_hook.cpp
- memory_hook.h
memory_setup.h
perf_stats.cpp
perf_stats.h
@@ -454,7 +471,7 @@ add_library(core STATIC
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 lz4_static mbedtls opus unicorn open_source_archives)
+target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt 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.h b/src/core/arm/arm_interface.h
index 4dfd41b43..978b1518f 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -7,6 +7,10 @@
#include <array>
#include "common/common_types.h"
+namespace Common {
+struct PageTable;
+}
+
namespace Kernel {
enum class VMAPermission : u8;
}
@@ -49,8 +53,14 @@ public:
/// Clear all instruction cache
virtual void ClearInstructionCache() = 0;
- /// Notify CPU emulation that page tables have changed
- virtual void PageTableChanged() = 0;
+ /// Notifies CPU emulation that the current page table has changed.
+ ///
+ /// @param new_page_table The new page table.
+ /// @param new_address_space_size_in_bits The new usable size of the address space in bits.
+ /// This can be either 32, 36, or 39 on official software.
+ ///
+ virtual void PageTableChanged(Common::PageTable& new_page_table,
+ std::size_t new_address_space_size_in_bits) = 0;
/**
* Set the Program Counter to an address
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 9b7ca4030..44307fa19 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -12,6 +12,7 @@
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
+#include "core/core_timing_util.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc.h"
@@ -25,7 +26,6 @@ using Vector = Dynarmic::A64::Vector;
class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks {
public:
explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {}
- ~ARM_Dynarmic_Callbacks() = default;
u8 MemoryRead8(u64 vaddr) override {
return Memory::Read8(vaddr);
@@ -99,7 +99,7 @@ public:
}
void CallSVC(u32 swi) override {
- Kernel::CallSVC(swi);
+ Kernel::CallSVC(parent.system, swi);
}
void AddTicks(u64 ticks) override {
@@ -112,14 +112,14 @@ public:
// Always execute at least one tick.
amortized_ticks = std::max<u64>(amortized_ticks, 1);
- parent.core_timing.AddTicks(amortized_ticks);
+ parent.system.CoreTiming().AddTicks(amortized_ticks);
num_interpreted_instructions = 0;
}
u64 GetTicksRemaining() override {
- return std::max(parent.core_timing.GetDowncount(), 0);
+ return std::max(parent.system.CoreTiming().GetDowncount(), 0);
}
u64 GetCNTPCT() override {
- return parent.core_timing.GetTicks();
+ return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks());
}
ARM_Dynarmic& parent;
@@ -128,18 +128,16 @@ public:
u64 tpidr_el0 = 0;
};
-std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
- auto* current_process = Core::CurrentProcess();
- auto** const page_table = current_process->VMManager().page_table.pointers.data();
-
+std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit(Common::PageTable& page_table,
+ std::size_t address_space_bits) const {
Dynarmic::A64::UserConfig config;
// Callbacks
config.callbacks = cb.get();
// Memory
- config.page_table = reinterpret_cast<void**>(page_table);
- config.page_table_address_space_bits = current_process->VMManager().GetAddressSpaceWidth();
+ config.page_table = reinterpret_cast<void**>(page_table.pointers.data());
+ config.page_table_address_space_bits = address_space_bits;
config.silently_mirror_page_table = false;
// Multi-process state
@@ -151,7 +149,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
config.tpidr_el0 = &cb->tpidr_el0;
config.dczid_el0 = 4;
config.ctr_el0 = 0x8444c004;
- config.cntfrq_el0 = 19200000; // Value from fusee.
+ config.cntfrq_el0 = Timing::CNTFREQ;
// Unpredictable instructions
config.define_unpredictable_behaviour = true;
@@ -163,7 +161,6 @@ MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)
void ARM_Dynarmic::Run() {
MICROPROFILE_SCOPE(ARM_Jit_Dynarmic);
- ASSERT(Memory::GetCurrentPageTable() == current_page_table);
jit->Run();
}
@@ -172,16 +169,11 @@ void ARM_Dynarmic::Step() {
cb->InterpreterFallback(jit->GetPC(), 1);
}
-ARM_Dynarmic::ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
+ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
std::size_t core_index)
- : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{core_timing},
- core_index{core_index}, core_timing{core_timing},
- exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
- ThreadContext ctx{};
- inner_unicorn.SaveContext(ctx);
- PageTableChanged();
- LoadContext(ctx);
-}
+ : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
+ core_index{core_index}, system{system},
+ exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
ARM_Dynarmic::~ARM_Dynarmic() = default;
@@ -276,9 +268,9 @@ void ARM_Dynarmic::ClearExclusiveState() {
jit->ClearExclusiveState();
}
-void ARM_Dynarmic::PageTableChanged() {
- jit = MakeJit();
- current_page_table = Memory::GetCurrentPageTable();
+void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table,
+ std::size_t new_address_space_size_in_bits) {
+ jit = MakeJit(page_table, new_address_space_size_in_bits);
}
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {}
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index 6cc458296..b701e97a3 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -12,24 +12,16 @@
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
-namespace Memory {
-struct PageTable;
-}
-
-namespace Core::Timing {
-class CoreTiming;
-}
-
namespace Core {
class ARM_Dynarmic_Callbacks;
class DynarmicExclusiveMonitor;
+class System;
class ARM_Dynarmic final : public ARM_Interface {
public:
- ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
- std::size_t core_index);
- ~ARM_Dynarmic();
+ ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
+ ~ARM_Dynarmic() override;
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
Kernel::VMAPermission perms) override;
@@ -56,10 +48,12 @@ public:
void ClearExclusiveState() override;
void ClearInstructionCache() override;
- void PageTableChanged() override;
+ void PageTableChanged(Common::PageTable& new_page_table,
+ std::size_t new_address_space_size_in_bits) override;
private:
- std::unique_ptr<Dynarmic::A64::Jit> MakeJit() const;
+ std::unique_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable& page_table,
+ std::size_t address_space_bits) const;
friend class ARM_Dynarmic_Callbacks;
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
@@ -67,16 +61,14 @@ private:
ARM_Unicorn inner_unicorn;
std::size_t core_index;
- Timing::CoreTiming& core_timing;
+ System& system;
DynarmicExclusiveMonitor& exclusive_monitor;
-
- Memory::PageTable* current_page_table = nullptr;
};
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
public:
explicit DynarmicExclusiveMonitor(std::size_t core_count);
- ~DynarmicExclusiveMonitor();
+ ~DynarmicExclusiveMonitor() override;
void SetExclusive(std::size_t core_index, VAddr addr) override;
void ClearExclusive() override;
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index a542a098b..4e07fe8b5 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -10,7 +10,6 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/svc.h"
-#include "core/memory.h"
namespace Core {
@@ -49,20 +48,6 @@ static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_
}
}
-static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) {
- u32 esr{};
- CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
-
- auto ec = esr >> 26;
- auto iss = esr & 0xFFFFFF;
-
- switch (ec) {
- case 0x15: // SVC
- Kernel::CallSVC(iss);
- break;
- }
-}
-
static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value,
void* user_data) {
ARM_Interface::ThreadContext ctx{};
@@ -72,7 +57,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
return {};
}
-ARM_Unicorn::ARM_Unicorn(Timing::CoreTiming& core_timing) : core_timing{core_timing} {
+ARM_Unicorn::ARM_Unicorn(System& system) : system{system} {
CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
auto fpv = 3 << 20;
@@ -177,7 +162,7 @@ void ARM_Unicorn::Run() {
if (GDBStub::IsServerEnabled()) {
ExecuteInstructions(std::max(4000000, 0));
} else {
- ExecuteInstructions(std::max(core_timing.GetDowncount(), 0));
+ ExecuteInstructions(std::max(system.CoreTiming().GetDowncount(), 0));
}
}
@@ -190,14 +175,15 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
- core_timing.AddTicks(num_instructions);
+ system.CoreTiming().AddTicks(num_instructions);
if (GDBStub::IsServerEnabled()) {
- if (last_bkpt_hit) {
+ if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) {
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
}
+
Kernel::Thread* thread = Kernel::GetCurrentThread();
SaveContext(thread->GetContext());
- if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
+ if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
last_bkpt_hit = false;
GDBStub::Break();
GDBStub::SendTrap(thread, 5);
@@ -272,4 +258,20 @@ void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
last_bkpt_hit = true;
}
+void ARM_Unicorn::InterruptHook(uc_engine* uc, u32 int_no, void* user_data) {
+ u32 esr{};
+ CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
+
+ const auto ec = esr >> 26;
+ const auto iss = esr & 0xFFFFFF;
+
+ auto* const arm_instance = static_cast<ARM_Unicorn*>(user_data);
+
+ switch (ec) {
+ case 0x15: // SVC
+ Kernel::CallSVC(arm_instance->system, iss);
+ break;
+ }
+}
+
} // namespace Core
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index dbd6955ea..34e974b4d 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -9,16 +9,14 @@
#include "core/arm/arm_interface.h"
#include "core/gdbstub/gdbstub.h"
-namespace Core::Timing {
-class CoreTiming;
-}
-
namespace Core {
+class System;
+
class ARM_Unicorn final : public ARM_Interface {
public:
- explicit ARM_Unicorn(Timing::CoreTiming& core_timing);
- ~ARM_Unicorn();
+ explicit ARM_Unicorn(System& system);
+ ~ARM_Unicorn() override;
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
Kernel::VMAPermission perms) override;
@@ -43,14 +41,16 @@ public:
void Run() override;
void Step() override;
void ClearInstructionCache() override;
- void PageTableChanged() override{};
+ void PageTableChanged(Common::PageTable&, std::size_t) override {}
void RecordBreak(GDBStub::BreakpointAddress bkpt);
private:
+ static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
+
uc_engine* uc{};
- Timing::CoreTiming& core_timing;
+ System& system;
GDBStub::BreakpointAddress last_bkpt{};
- bool last_bkpt_hit;
+ bool last_bkpt_hit = false;
};
} // namespace Core
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 eba2177d1..ff0721079 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -3,9 +3,7 @@
// Refer to the license.txt file included.
#include <array>
-#include <map>
#include <memory>
-#include <thread>
#include <utility>
#include "common/file_util.h"
@@ -17,6 +15,7 @@
#include "core/core_timing.h"
#include "core/cpu_core_manager.h"
#include "core/file_sys/mode.h"
+#include "core/file_sys/registered_cache.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/gdbstub/gdbstub.h"
@@ -25,19 +24,15 @@
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
-#include "core/hle/service/am/applets/software_keyboard.h"
+#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
#include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
-#include "frontend/applets/profile_select.h"
-#include "frontend/applets/software_keyboard.h"
-#include "frontend/applets/web_browser.h"
+#include "file_sys/cheat_engine.h"
#include "video_core/debug_utils/debug_utils.h"
-#include "video_core/gpu_asynch.h"
-#include "video_core/gpu_synch.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -79,7 +74,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
return vfs->OpenFile(path, FileSys::Mode::Read);
}
struct System::Impl {
- explicit Impl(System& system) : kernel{system} {}
+ explicit Impl(System& system) : kernel{system}, cpu_core_manager{system} {}
Cpu& CurrentCpuCore() {
return cpu_core_manager.GetCurrentCore();
@@ -97,6 +92,7 @@ struct System::Impl {
LOG_DEBUG(HW_Memory, "initialized OK");
core_timing.Initialize();
+ cpu_core_manager.Initialize();
kernel.Initialize();
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
@@ -107,17 +103,11 @@ struct System::Impl {
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
+ if (content_provider == nullptr)
+ content_provider = std::make_unique<FileSys::ContentProviderUnion>();
/// Create default implementations of applets if one is not provided.
- if (profile_selector == nullptr)
- profile_selector = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
- if (software_keyboard == nullptr)
- software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
- if (web_browser == nullptr)
- web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
-
- auto main_process = Kernel::Process::Create(kernel, "main");
- kernel.MakeCurrentProcess(main_process.get());
+ applet_manager.SetDefaultAppletsIfMissing();
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
@@ -130,15 +120,9 @@ struct System::Impl {
return ResultStatus::ErrorVideoCore;
}
- is_powered_on = true;
+ gpu_core = VideoCore::CreateGPU(system);
- if (Settings::values.use_asynchronous_gpu_emulation) {
- gpu_core = std::make_unique<VideoCommon::GPUAsynch>(system, *renderer);
- } else {
- gpu_core = std::make_unique<VideoCommon::GPUSynch>(system, *renderer);
- }
-
- cpu_core_manager.Initialize(system);
+ is_powered_on = true;
LOG_DEBUG(Core, "Initialized OK");
@@ -152,20 +136,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) {
@@ -175,7 +149,9 @@ struct System::Impl {
return init_result;
}
- const Loader::ResultStatus load_result{app_loader->Load(*kernel.CurrentProcess())};
+ 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) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
Shutdown();
@@ -183,6 +159,16 @@ struct System::Impl {
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
static_cast<u32>(load_result));
}
+ kernel.MakeCurrentProcess(main_process.get());
+
+ // Main process has been loaded and been made current.
+ // Begin GPU and CPU execution.
+ gpu_core->Start();
+ cpu_core_manager.StartThreads();
+
+ // All threads are started, begin main process execution, now that we're in the clear.
+ main_process->Run(load_parameters->main_thread_priority,
+ load_parameters->main_thread_stack_size);
status = ResultStatus::Success;
return status;
@@ -205,6 +191,7 @@ struct System::Impl {
GDBStub::Shutdown();
Service::Shutdown();
service_manager.reset();
+ cheat_engine.reset();
telemetry_session.reset();
gpu_core.reset();
@@ -219,9 +206,7 @@ struct System::Impl {
app_loader.reset();
// Clear all applets
- profile_selector.reset();
- software_keyboard.reset();
- web_browser.reset();
+ applet_manager.ClearAll();
LOG_DEBUG(Core, "Shutdown OK");
}
@@ -247,6 +232,8 @@ struct System::Impl {
Kernel::KernelCore kernel;
/// RealVfsFilesystem instance
FileSys::VirtualFilesystem virtual_filesystem;
+ /// ContentProviderUnion instance
+ std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
@@ -255,10 +242,10 @@ struct System::Impl {
CpuCoreManager cpu_core_manager;
bool is_powered_on = false;
+ std::unique_ptr<FileSys::CheatEngine> cheat_engine;
+
/// Frontend applets
- std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
- std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
- std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser;
+ Service::AM::Applets::AppletManager applet_manager;
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -453,6 +440,13 @@ Tegra::DebugContext* System::GetGPUDebugContext() const {
return impl->debug_context.get();
}
+void System::RegisterCheatList(const std::vector<FileSys::CheatList>& list,
+ const std::string& build_id, VAddr code_region_start,
+ VAddr code_region_end) {
+ impl->cheat_engine = std::make_unique<FileSys::CheatEngine>(*this, list, build_id,
+ code_region_start, code_region_end);
+}
+
void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
impl->virtual_filesystem = std::move(vfs);
}
@@ -461,32 +455,41 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
return impl->virtual_filesystem;
}
-void System::SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet) {
- impl->profile_selector = std::move(applet);
+void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) {
+ impl->applet_manager.SetAppletFrontendSet(std::move(set));
+}
+
+void System::SetDefaultAppletFrontendSet() {
+ impl->applet_manager.SetDefaultAppletFrontendSet();
+}
+
+Service::AM::Applets::AppletManager& System::GetAppletManager() {
+ return impl->applet_manager;
}
-const Frontend::ProfileSelectApplet& System::GetProfileSelector() const {
- return *impl->profile_selector;
+const Service::AM::Applets::AppletManager& System::GetAppletManager() const {
+ return impl->applet_manager;
}
-void System::SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet) {
- impl->software_keyboard = std::move(applet);
+void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
+ impl->content_provider = std::move(provider);
}
-const Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
- return *impl->software_keyboard;
+FileSys::ContentProvider& System::GetContentProvider() {
+ return *impl->content_provider;
}
-void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) {
- impl->web_browser = std::move(applet);
+const FileSys::ContentProvider& System::GetContentProvider() const {
+ return *impl->content_provider;
}
-Frontend::WebBrowserApplet& System::GetWebBrowser() {
- return *impl->web_browser;
+void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
+ FileSys::ContentProvider* provider) {
+ impl->content_provider->SetSlot(slot, provider);
}
-const Frontend::WebBrowserApplet& System::GetWebBrowser() const {
- return *impl->web_browser;
+void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) {
+ impl->content_provider->ClearSlot(slot);
}
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
diff --git a/src/core/core.h b/src/core/core.h
index ba76a41d8..20959de54 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -14,12 +14,13 @@
namespace Core::Frontend {
class EmuWindow;
-class ProfileSelectApplet;
-class SoftwareKeyboardApplet;
-class WebBrowserApplet;
} // namespace Core::Frontend
namespace FileSys {
+class CheatList;
+class ContentProvider;
+class ContentProviderUnion;
+enum class ContentProviderUnionSlot;
class VfsFilesystem;
} // namespace FileSys
@@ -34,9 +35,18 @@ class AppLoader;
enum class ResultStatus : u16;
} // namespace Loader
-namespace Service::SM {
+namespace Service {
+
+namespace AM::Applets {
+struct AppletFrontendSet;
+class AppletManager;
+} // namespace AM::Applets
+
+namespace SM {
class ServiceManager;
-} // namespace Service::SM
+} // namespace SM
+
+} // namespace Service
namespace Tegra {
class DebugContext;
@@ -88,7 +98,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
@@ -253,18 +262,27 @@ public:
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
- void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
+ void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id,
+ VAddr code_region_start, VAddr code_region_end);
+
+ void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set);
+
+ void SetDefaultAppletFrontendSet();
+
+ Service::AM::Applets::AppletManager& GetAppletManager();
+
+ const Service::AM::Applets::AppletManager& GetAppletManager() const;
- const Frontend::ProfileSelectApplet& GetProfileSelector() const;
+ void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider);
- void SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet);
+ FileSys::ContentProvider& GetContentProvider();
- const Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
+ const FileSys::ContentProvider& GetContentProvider() const;
- void SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet);
+ void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
+ FileSys::ContentProvider* provider);
- Frontend::WebBrowserApplet& GetWebBrowser();
- const Frontend::WebBrowserApplet& GetWebBrowser() const;
+ void ClearContentProvider(FileSys::ContentProviderUnionSlot slot);
private:
System();
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 54aa21a3a..ba63c3e61 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -11,6 +11,7 @@
#endif
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
+#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/hle/kernel/scheduler.h"
@@ -21,7 +22,7 @@
namespace Core {
void CpuBarrier::NotifyEnd() {
- std::unique_lock<std::mutex> lock(mutex);
+ std::unique_lock lock{mutex};
end = true;
condition.notify_all();
}
@@ -33,7 +34,7 @@ bool CpuBarrier::Rendezvous() {
}
if (!end) {
- std::unique_lock<std::mutex> lock(mutex);
+ std::unique_lock lock{mutex};
--cores_waiting;
if (!cores_waiting) {
@@ -49,21 +50,21 @@ bool CpuBarrier::Rendezvous() {
return false;
}
-Cpu::Cpu(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
- CpuBarrier& cpu_barrier, std::size_t core_index)
- : cpu_barrier{cpu_barrier}, core_timing{core_timing}, core_index{core_index} {
+Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
+ std::size_t core_index)
+ : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
- arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index);
+ arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
#else
- arm_interface = std::make_unique<ARM_Unicorn>();
+ arm_interface = std::make_unique<ARM_Unicorn>(system);
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
} else {
- arm_interface = std::make_unique<ARM_Unicorn>(core_timing);
+ arm_interface = std::make_unique<ARM_Unicorn>(system);
}
- scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface);
+ scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface);
}
Cpu::~Cpu() = default;
@@ -130,7 +131,7 @@ void Cpu::Reschedule() {
reschedule_pending = false;
// Lock the global kernel mutex when we manipulate the HLE state
- std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
+ std::lock_guard lock{HLE::g_hle_lock};
scheduler->Reschedule();
}
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
index e2204c6b0..7589beb8c 100644
--- a/src/core/core_cpu.h
+++ b/src/core/core_cpu.h
@@ -15,6 +15,10 @@ namespace Kernel {
class Scheduler;
}
+namespace Core {
+class System;
+}
+
namespace Core::Timing {
class CoreTiming;
}
@@ -45,8 +49,8 @@ private:
class Cpu {
public:
- Cpu(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
- CpuBarrier& cpu_barrier, std::size_t core_index);
+ Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
+ std::size_t core_index);
~Cpu();
void RunLoop(bool tight_loop = true);
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index a0dd5db24..41adb2302 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -186,7 +186,7 @@ void CoreTiming::Advance() {
Event evt = std::move(event_queue.front());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
event_queue.pop_back();
- evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time));
+ evt.type->callback(evt.userdata, global_timer - evt.time);
}
is_global_timer_sane = false;
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 59163bae1..9d2efde37 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -15,7 +15,7 @@
namespace Core::Timing {
/// A callback that may be scheduled for a particular core timing event.
-using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
+using TimedCallback = std::function<void(u64 userdata, s64 cycles_late)>;
/// Contains the characteristics of a particular event.
struct EventType {
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp
index 88ff70233..a10472a95 100644
--- a/src/core/core_timing_util.cpp
+++ b/src/core/core_timing_util.cpp
@@ -7,57 +7,51 @@
#include <cinttypes>
#include <limits>
#include "common/logging/log.h"
+#include "common/uint128.h"
namespace Core::Timing {
constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE;
-s64 usToCycles(s64 us) {
- if (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 (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 (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 (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;
+ return (BASE_CLOCK_RATE * ns.count()) / 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;
+u64 CpuCyclesToClockCycles(u64 ticks) {
+ const u128 temporal = Common::Multiply64Into128(ticks, CNTFREQ);
+ return Common::Divide128On32(temporal, static_cast<u32>(BASE_CLOCK_RATE)).first;
}
} // namespace Core::Timing
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h
index 513cfac1b..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 {
@@ -11,54 +12,24 @@ namespace Core::Timing {
// The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz
// The exact value used is of course unverified.
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);
-}
+s64 msToCycles(std::chrono::milliseconds ms);
+s64 usToCycles(std::chrono::microseconds us);
+s64 nsToCycles(std::chrono::nanoseconds ns);
-inline s64 nsToCycles(int ns) {
- return BASE_CLOCK_RATE * static_cast<s64>(ns) / 1000000000;
+inline std::chrono::milliseconds CyclesToMs(s64 cycles) {
+ return std::chrono::milliseconds(cycles * 1000 / BASE_CLOCK_RATE);
}
-s64 nsToCycles(s64 ns);
-
-s64 nsToCycles(u64 ns);
-
-inline u64 cyclesToNs(s64 cycles) {
- return cycles * 1000000000 / BASE_CLOCK_RATE;
+inline std::chrono::nanoseconds CyclesToNs(s64 cycles) {
+ return std::chrono::nanoseconds(cycles * 1000000000 / BASE_CLOCK_RATE);
}
-inline s64 cyclesToUs(s64 cycles) {
- return cycles * 1000000 / BASE_CLOCK_RATE;
+inline std::chrono::microseconds CyclesToUs(s64 cycles) {
+ return std::chrono::microseconds(cycles * 1000000 / BASE_CLOCK_RATE);
}
-inline u64 cyclesToMs(s64 cycles) {
- return cycles * 1000 / BASE_CLOCK_RATE;
-}
+u64 CpuCyclesToClockCycles(u64 ticks);
} // namespace Core::Timing
diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp
index 2ddb3610d..8fcb4eeb1 100644
--- a/src/core/cpu_core_manager.cpp
+++ b/src/core/cpu_core_manager.cpp
@@ -19,18 +19,19 @@ void RunCpuCore(const System& system, Cpu& cpu_state) {
}
} // Anonymous namespace
-CpuCoreManager::CpuCoreManager() = default;
+CpuCoreManager::CpuCoreManager(System& system) : system{system} {}
CpuCoreManager::~CpuCoreManager() = default;
-void CpuCoreManager::Initialize(System& system) {
+void CpuCoreManager::Initialize() {
barrier = std::make_unique<CpuBarrier>();
exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
for (std::size_t index = 0; index < cores.size(); ++index) {
- cores[index] =
- std::make_unique<Cpu>(system.CoreTiming(), *exclusive_monitor, *barrier, index);
+ cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index);
}
+}
+void CpuCoreManager::StartThreads() {
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
diff --git a/src/core/cpu_core_manager.h b/src/core/cpu_core_manager.h
index a4d70ec56..2cbbf8216 100644
--- a/src/core/cpu_core_manager.h
+++ b/src/core/cpu_core_manager.h
@@ -18,7 +18,7 @@ class System;
class CpuCoreManager {
public:
- CpuCoreManager();
+ explicit CpuCoreManager(System& system);
CpuCoreManager(const CpuCoreManager&) = delete;
CpuCoreManager(CpuCoreManager&&) = delete;
@@ -27,7 +27,8 @@ public:
CpuCoreManager& operator=(const CpuCoreManager&) = delete;
CpuCoreManager& operator=(CpuCoreManager&&) = delete;
- void Initialize(System& system);
+ void Initialize();
+ void StartThreads();
void Shutdown();
Cpu& GetCore(std::size_t index);
@@ -54,6 +55,8 @@ private:
/// Map of guest threads to CPU cores
std::map<std::thread::id, Cpu*> thread_to_cpu;
+
+ System& system;
};
} // namespace Core
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index dfac9a4b3..6dd633363 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -22,6 +22,7 @@
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
#include "core/crypto/partition_data_manager.h"
@@ -571,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);
}
@@ -582,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;
@@ -794,7 +795,7 @@ void KeyManager::DeriveBase() {
void KeyManager::DeriveETicket(PartitionDataManager& data) {
// ETicket keys
- const auto es = Service::FileSystem::GetUnionContents().GetEntry(
+ const auto es = Core::System::GetInstance().GetContentProvider().GetEntry(
0x0100000000000033, FileSys::ContentRecordType::Program);
if (es == nullptr)
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/cheat_engine.cpp b/src/core/file_sys/cheat_engine.cpp
new file mode 100644
index 000000000..b06c2f20a
--- /dev/null
+++ b/src/core/file_sys/cheat_engine.cpp
@@ -0,0 +1,492 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <locale>
+#include "common/hex_util.h"
+#include "common/microprofile.h"
+#include "common/swap.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/core_timing_util.h"
+#include "core/file_sys/cheat_engine.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/service/hid/controllers/npad.h"
+#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace FileSys {
+
+constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
+constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
+
+u64 Cheat::Address() const {
+ u64 out;
+ std::memcpy(&out, raw.data(), sizeof(u64));
+ return Common::swap64(out) & 0xFFFFFFFFFF;
+}
+
+u64 Cheat::ValueWidth(u64 offset) const {
+ return Value(offset, width);
+}
+
+u64 Cheat::Value(u64 offset, u64 width) const {
+ u64 out;
+ std::memcpy(&out, raw.data() + offset, sizeof(u64));
+ out = Common::swap64(out);
+ if (width == 8)
+ return out;
+ return out & ((1ull << (width * CHAR_BIT)) - 1);
+}
+
+u32 Cheat::KeypadValue() const {
+ u32 out;
+ std::memcpy(&out, raw.data(), sizeof(u32));
+ return Common::swap32(out) & 0x0FFFFFFF;
+}
+
+void CheatList::SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end,
+ VAddr heap_end, MemoryWriter writer, MemoryReader reader) {
+ this->main_region_begin = main_begin;
+ this->main_region_end = main_end;
+ this->heap_region_begin = heap_begin;
+ this->heap_region_end = heap_end;
+ this->writer = writer;
+ this->reader = reader;
+}
+
+MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
+
+void CheatList::Execute() {
+ MICROPROFILE_SCOPE(Cheat_Engine);
+
+ std::fill(scratch.begin(), scratch.end(), 0);
+ in_standard = false;
+ for (std::size_t i = 0; i < master_list.size(); ++i) {
+ LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, master_list[i].first);
+ current_block = i;
+ ExecuteBlock(master_list[i].second);
+ }
+
+ in_standard = true;
+ for (std::size_t i = 0; i < standard_list.size(); ++i) {
+ LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, standard_list[i].first);
+ current_block = i;
+ ExecuteBlock(standard_list[i].second);
+ }
+}
+
+CheatList::CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard)
+ : master_list{std::move(master)}, standard_list{std::move(standard)}, system{&system_} {}
+
+bool CheatList::EvaluateConditional(const Cheat& cheat) const {
+ using ComparisonFunction = bool (*)(u64, u64);
+ constexpr std::array<ComparisonFunction, 6> comparison_functions{
+ [](u64 a, u64 b) { return a > b; }, [](u64 a, u64 b) { return a >= b; },
+ [](u64 a, u64 b) { return a < b; }, [](u64 a, u64 b) { return a <= b; },
+ [](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; },
+ };
+
+ if (cheat.type == CodeType::ConditionalInput) {
+ const auto applet_resource =
+ system->ServiceManager().GetService<Service::HID::Hid>("hid")->GetAppletResource();
+ if (applet_resource == nullptr) {
+ LOG_WARNING(
+ Common_Filesystem,
+ "Attempted to evaluate input conditional, but applet resource is not initialized!");
+ return false;
+ }
+
+ const auto press_state =
+ applet_resource
+ ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
+ .GetAndResetPressState();
+ return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0;
+ }
+
+ ASSERT(cheat.type == CodeType::Conditional);
+
+ const auto offset =
+ cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
+ ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6);
+ auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())];
+ const auto addr = cheat.Address() + offset;
+
+ return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8));
+}
+
+void CheatList::ProcessBlockPairs(const Block& block) {
+ block_pairs.clear();
+
+ u64 scope = 0;
+ std::map<u64, u64> pairs;
+
+ for (std::size_t i = 0; i < block.size(); ++i) {
+ const auto& cheat = block[i];
+
+ switch (cheat.type) {
+ case CodeType::Conditional:
+ case CodeType::ConditionalInput:
+ pairs.insert_or_assign(scope, i);
+ ++scope;
+ break;
+ case CodeType::EndConditional: {
+ --scope;
+ const auto idx = pairs.at(scope);
+ block_pairs.insert_or_assign(idx, i);
+ break;
+ }
+ case CodeType::Loop: {
+ if (cheat.end_of_loop) {
+ --scope;
+ const auto idx = pairs.at(scope);
+ block_pairs.insert_or_assign(idx, i);
+ } else {
+ pairs.insert_or_assign(scope, i);
+ ++scope;
+ }
+ break;
+ }
+ }
+ }
+}
+
+void CheatList::WriteImmediate(const Cheat& cheat) {
+ const auto offset =
+ cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
+ const auto& register_3 = scratch.at(cheat.register_3);
+
+ const auto addr = cheat.Address() + offset + register_3;
+ LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr,
+ cheat.Value(8, cheat.width));
+ writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(8));
+}
+
+void CheatList::BeginConditional(const Cheat& cheat) {
+ if (EvaluateConditional(cheat)) {
+ return;
+ }
+
+ const auto iter = block_pairs.find(current_index);
+ ASSERT(iter != block_pairs.end());
+ current_index = iter->second - 1;
+}
+
+void CheatList::EndConditional(const Cheat& cheat) {
+ LOG_DEBUG(Common_Filesystem, "Ending conditional block.");
+}
+
+void CheatList::Loop(const Cheat& cheat) {
+ if (cheat.end_of_loop.Value())
+ ASSERT(!cheat.end_of_loop.Value());
+
+ auto& register_3 = scratch.at(cheat.register_3);
+ const auto iter = block_pairs.find(current_index);
+ ASSERT(iter != block_pairs.end());
+ ASSERT(iter->first < iter->second);
+
+ const s32 initial_value = static_cast<s32>(cheat.Value(4, sizeof(s32)));
+ for (s32 i = initial_value; i >= 0; --i) {
+ register_3 = static_cast<u64>(i);
+ for (std::size_t c = iter->first + 1; c < iter->second; ++c) {
+ current_index = c;
+ ExecuteSingleCheat(
+ (in_standard ? standard_list : master_list)[current_block].second[c]);
+ }
+ }
+
+ current_index = iter->second;
+}
+
+void CheatList::LoadImmediate(const Cheat& cheat) {
+ auto& register_3 = scratch.at(cheat.register_3);
+
+ LOG_DEBUG(Common_Filesystem, "setting register={:01X} equal to value={:016X}", cheat.register_3,
+ cheat.Value(4, 8));
+ register_3 = cheat.Value(4, 8);
+}
+
+void CheatList::LoadIndexed(const Cheat& cheat) {
+ const auto offset =
+ cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
+ auto& register_3 = scratch.at(cheat.register_3);
+
+ const auto addr = (cheat.load_from_register.Value() ? register_3 : offset) + cheat.Address();
+ LOG_DEBUG(Common_Filesystem, "writing indexed value to register={:01X}, addr={:016X}",
+ cheat.register_3, addr);
+ register_3 = reader(cheat.width, SanitizeAddress(addr));
+}
+
+void CheatList::StoreIndexed(const Cheat& cheat) {
+ const auto& register_3 = scratch.at(cheat.register_3);
+
+ const auto addr =
+ register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0);
+ LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}",
+ cheat.Value(4, cheat.width), addr);
+ writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(4));
+}
+
+void CheatList::RegisterArithmetic(const Cheat& cheat) {
+ using ArithmeticFunction = u64 (*)(u64, u64);
+ constexpr std::array<ArithmeticFunction, 5> arithmetic_functions{
+ [](u64 a, u64 b) { return a + b; }, [](u64 a, u64 b) { return a - b; },
+ [](u64 a, u64 b) { return a * b; }, [](u64 a, u64 b) { return a << b; },
+ [](u64 a, u64 b) { return a >> b; },
+ };
+
+ using ArithmeticOverflowCheck = bool (*)(u64, u64);
+ constexpr std::array<ArithmeticOverflowCheck, 5> arithmetic_overflow_checks{
+ [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); }, // a + b
+ [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); }, // a - b
+ [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); }, // a * b
+ [](u64 a, u64 b) { return b >= 64 || (a & ~((1ull << (64 - b)) - 1)) != 0; }, // a << b
+ [](u64 a, u64 b) { return b >= 64 || (a & ((1ull << b) - 1)) != 0; }, // a >> b
+ };
+
+ static_assert(sizeof(arithmetic_functions) == sizeof(arithmetic_overflow_checks),
+ "Missing or have extra arithmetic overflow checks compared to functions!");
+
+ auto& register_3 = scratch.at(cheat.register_3);
+
+ ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5);
+ auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())];
+ auto* overflow_function =
+ arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())];
+ LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}",
+ cheat.register_3, cheat.ValueWidth(4));
+
+ if (overflow_function(register_3, cheat.ValueWidth(4))) {
+ LOG_WARNING(Common_Filesystem,
+ "overflow will occur when performing arithmetic operation={:02X} with operands "
+ "a={:016X}, b={:016X}!",
+ static_cast<u8>(cheat.arithmetic_op.Value()), register_3, cheat.ValueWidth(4));
+ }
+
+ register_3 = function(register_3, cheat.ValueWidth(4));
+}
+
+void CheatList::BeginConditionalInput(const Cheat& cheat) {
+ if (EvaluateConditional(cheat))
+ return;
+
+ const auto iter = block_pairs.find(current_index);
+ ASSERT(iter != block_pairs.end());
+ current_index = iter->second - 1;
+}
+
+VAddr CheatList::SanitizeAddress(VAddr in) const {
+ if ((in < main_region_begin || in >= main_region_end) &&
+ (in < heap_region_begin || in >= heap_region_end)) {
+ LOG_ERROR(Common_Filesystem,
+ "Cheat attempting to access memory at invalid address={:016X}, if this persists, "
+ "the cheat may be incorrect. However, this may be normal early in execution if "
+ "the game has not properly set up yet.",
+ in);
+ return 0; ///< Invalid addresses will hard crash
+ }
+
+ return in;
+}
+
+void CheatList::ExecuteSingleCheat(const Cheat& cheat) {
+ using CheatOperationFunction = void (CheatList::*)(const Cheat&);
+ constexpr std::array<CheatOperationFunction, 9> cheat_operation_functions{
+ &CheatList::WriteImmediate, &CheatList::BeginConditional,
+ &CheatList::EndConditional, &CheatList::Loop,
+ &CheatList::LoadImmediate, &CheatList::LoadIndexed,
+ &CheatList::StoreIndexed, &CheatList::RegisterArithmetic,
+ &CheatList::BeginConditionalInput,
+ };
+
+ const auto index = static_cast<u8>(cheat.type.Value());
+ ASSERT(index < sizeof(cheat_operation_functions));
+ const auto op = cheat_operation_functions[index];
+ (this->*op)(cheat);
+}
+
+void CheatList::ExecuteBlock(const Block& block) {
+ encountered_loops.clear();
+
+ ProcessBlockPairs(block);
+ for (std::size_t i = 0; i < block.size(); ++i) {
+ current_index = i;
+ ExecuteSingleCheat(block[i]);
+ i = current_index;
+ }
+}
+
+CheatParser::~CheatParser() = default;
+
+CheatList CheatParser::MakeCheatList(const Core::System& system, CheatList::ProgramSegment master,
+ CheatList::ProgramSegment standard) const {
+ return {system, std::move(master), std::move(standard)};
+}
+
+TextCheatParser::~TextCheatParser() = default;
+
+CheatList TextCheatParser::Parse(const Core::System& system, const std::vector<u8>& data) const {
+ std::stringstream ss;
+ ss.write(reinterpret_cast<const char*>(data.data()), data.size());
+
+ std::vector<std::string> lines;
+ std::string stream_line;
+ while (std::getline(ss, stream_line)) {
+ // Remove a trailing \r
+ if (!stream_line.empty() && stream_line.back() == '\r')
+ stream_line.pop_back();
+ lines.push_back(std::move(stream_line));
+ }
+
+ CheatList::ProgramSegment master_list;
+ CheatList::ProgramSegment standard_list;
+
+ for (std::size_t i = 0; i < lines.size(); ++i) {
+ auto line = lines[i];
+
+ if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
+ const auto master = line[0] == '{';
+ const auto begin = master ? line.find('{') : line.find('[');
+ const auto end = master ? line.rfind('}') : line.rfind(']');
+
+ ASSERT(begin != std::string::npos && end != std::string::npos);
+
+ const std::string patch_name{line.begin() + begin + 1, line.begin() + end};
+ CheatList::Block block{};
+
+ while (i < lines.size() - 1) {
+ line = lines[++i];
+ if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
+ --i;
+ break;
+ }
+
+ if (line.size() < 8)
+ continue;
+
+ Cheat out{};
+ out.raw = ParseSingleLineCheat(line);
+ block.push_back(out);
+ }
+
+ (master ? master_list : standard_list).emplace_back(patch_name, block);
+ }
+ }
+
+ return MakeCheatList(system, master_list, standard_list);
+}
+
+std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line) const {
+ std::array<u8, 16> out{};
+
+ if (line.size() < 8)
+ return out;
+
+ const auto word1 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data(), 8});
+ std::memcpy(out.data(), word1.data(), sizeof(u32));
+
+ if (line.size() < 17 || line[8] != ' ')
+ return out;
+
+ const auto word2 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 9, 8});
+ std::memcpy(out.data() + sizeof(u32), word2.data(), sizeof(u32));
+
+ if (line.size() < 26 || line[17] != ' ') {
+ // Perform shifting in case value is truncated early.
+ const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
+ if (type == CodeType::Loop || type == CodeType::LoadImmediate ||
+ type == CodeType::StoreIndexed || type == CodeType::RegisterArithmetic) {
+ std::memcpy(out.data() + 8, out.data() + 4, sizeof(u32));
+ std::memset(out.data() + 4, 0, sizeof(u32));
+ }
+
+ return out;
+ }
+
+ const auto word3 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 18, 8});
+ std::memcpy(out.data() + 2 * sizeof(u32), word3.data(), sizeof(u32));
+
+ if (line.size() < 35 || line[26] != ' ') {
+ // Perform shifting in case value is truncated early.
+ const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
+ if (type == CodeType::WriteImmediate || type == CodeType::Conditional) {
+ std::memcpy(out.data() + 12, out.data() + 8, sizeof(u32));
+ std::memset(out.data() + 8, 0, sizeof(u32));
+ }
+
+ return out;
+ }
+
+ const auto word4 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 27, 8});
+ std::memcpy(out.data() + 3 * sizeof(u32), word4.data(), sizeof(u32));
+
+ return out;
+}
+
+namespace {
+u64 MemoryReadImpl(u32 width, VAddr addr) {
+ switch (width) {
+ case 1:
+ return Memory::Read8(addr);
+ case 2:
+ return Memory::Read16(addr);
+ case 4:
+ return Memory::Read32(addr);
+ case 8:
+ return Memory::Read64(addr);
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+}
+
+void MemoryWriteImpl(u32 width, VAddr addr, u64 value) {
+ switch (width) {
+ case 1:
+ Memory::Write8(addr, static_cast<u8>(value));
+ break;
+ case 2:
+ Memory::Write16(addr, static_cast<u16>(value));
+ break;
+ case 4:
+ Memory::Write32(addr, static_cast<u32>(value));
+ break;
+ case 8:
+ Memory::Write64(addr, value);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+} // Anonymous namespace
+
+CheatEngine::CheatEngine(Core::System& system, std::vector<CheatList> cheats_,
+ const std::string& build_id, VAddr code_region_start,
+ VAddr code_region_end)
+ : cheats{std::move(cheats_)}, core_timing{system.CoreTiming()} {
+ event = core_timing.RegisterEvent(
+ "CheatEngine::FrameCallback::" + build_id,
+ [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
+ core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
+
+ const auto& vm_manager = system.CurrentProcess()->VMManager();
+ for (auto& list : this->cheats) {
+ list.SetMemoryParameters(code_region_start, vm_manager.GetHeapRegionBaseAddress(),
+ code_region_end, vm_manager.GetHeapRegionEndAddress(),
+ &MemoryWriteImpl, &MemoryReadImpl);
+ }
+}
+
+CheatEngine::~CheatEngine() {
+ core_timing.UnscheduleEvent(event, 0);
+}
+
+void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {
+ for (auto& list : cheats) {
+ list.Execute();
+ }
+
+ core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/cheat_engine.h b/src/core/file_sys/cheat_engine.h
new file mode 100644
index 000000000..ac22a82cb
--- /dev/null
+++ b/src/core/file_sys/cheat_engine.h
@@ -0,0 +1,234 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <map>
+#include <set>
+#include <vector>
+#include "common/bit_field.h"
+#include "common/common_types.h"
+
+namespace Core {
+class System;
+}
+
+namespace Core::Timing {
+class CoreTiming;
+struct EventType;
+} // namespace Core::Timing
+
+namespace FileSys {
+
+enum class CodeType : u32 {
+ // 0TMR00AA AAAAAAAA YYYYYYYY YYYYYYYY
+ // Writes a T sized value Y to the address A added to the value of register R in memory domain M
+ WriteImmediate = 0,
+
+ // 1TMC00AA AAAAAAAA YYYYYYYY YYYYYYYY
+ // Compares the T sized value Y to the value at address A in memory domain M using the
+ // conditional function C. If success, continues execution. If failure, jumps to the matching
+ // EndConditional statement.
+ Conditional = 1,
+
+ // 20000000
+ // Terminates a Conditional or ConditionalInput block.
+ EndConditional = 2,
+
+ // 300R0000 VVVVVVVV
+ // Starts looping V times, storing the current count in register R.
+ // Loop block is terminated with a matching 310R0000.
+ Loop = 3,
+
+ // 400R0000 VVVVVVVV VVVVVVVV
+ // Sets the value of register R to the value V.
+ LoadImmediate = 4,
+
+ // 5TMRI0AA AAAAAAAA
+ // Sets the value of register R to the value of width T at address A in memory domain M, with
+ // the current value of R added to the address if I == 1.
+ LoadIndexed = 5,
+
+ // 6T0RIFG0 VVVVVVVV VVVVVVVV
+ // Writes the value V of width T to the memory address stored in register R. Adds the value of
+ // register G to the final calculation if F is nonzero. Increments the value of register R by T
+ // after operation if I is nonzero.
+ StoreIndexed = 6,
+
+ // 7T0RA000 VVVVVVVV
+ // Performs the arithmetic operation A on the value in register R and the value V of width T,
+ // storing the result in register R.
+ RegisterArithmetic = 7,
+
+ // 8KKKKKKK
+ // Checks to see if any of the buttons defined by the bitmask K are pressed. If any are,
+ // execution continues. If none are, execution skips to the next EndConditional command.
+ ConditionalInput = 8,
+};
+
+enum class MemoryType : u32 {
+ // Addressed relative to start of main NSO
+ MainNSO = 0,
+
+ // Addressed relative to start of heap
+ Heap = 1,
+};
+
+enum class ArithmeticOp : u32 {
+ Add = 0,
+ Sub = 1,
+ Mult = 2,
+ LShift = 3,
+ RShift = 4,
+};
+
+enum class ComparisonOp : u32 {
+ GreaterThan = 1,
+ GreaterThanEqual = 2,
+ LessThan = 3,
+ LessThanEqual = 4,
+ Equal = 5,
+ Inequal = 6,
+};
+
+union Cheat {
+ std::array<u8, 16> raw;
+
+ BitField<4, 4, CodeType> type;
+ BitField<0, 4, u32> width; // Can be 1, 2, 4, or 8. Measured in bytes.
+ BitField<0, 4, u32> end_of_loop;
+ BitField<12, 4, MemoryType> memory_type;
+ BitField<8, 4, u32> register_3;
+ BitField<8, 4, ComparisonOp> comparison_op;
+ BitField<20, 4, u32> load_from_register;
+ BitField<20, 4, u32> increment_register;
+ BitField<20, 4, ArithmeticOp> arithmetic_op;
+ BitField<16, 4, u32> add_additional_register;
+ BitField<28, 4, u32> register_6;
+
+ u64 Address() const;
+ u64 ValueWidth(u64 offset) const;
+ u64 Value(u64 offset, u64 width) const;
+ u32 KeypadValue() const;
+};
+
+class CheatParser;
+
+// Represents a full collection of cheats for a game. The Execute function should be called every
+// interval that all cheats should be executed. Clients should not directly instantiate this class
+// (hence private constructor), they should instead receive an instance from CheatParser, which
+// guarantees the list is always in an acceptable state.
+class CheatList {
+public:
+ friend class CheatParser;
+
+ using Block = std::vector<Cheat>;
+ using ProgramSegment = std::vector<std::pair<std::string, Block>>;
+
+ // (width in bytes, address, value)
+ using MemoryWriter = void (*)(u32, VAddr, u64);
+ // (width in bytes, address) -> value
+ using MemoryReader = u64 (*)(u32, VAddr);
+
+ void SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, VAddr heap_end,
+ MemoryWriter writer, MemoryReader reader);
+
+ void Execute();
+
+private:
+ CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard);
+
+ void ProcessBlockPairs(const Block& block);
+ void ExecuteSingleCheat(const Cheat& cheat);
+
+ void ExecuteBlock(const Block& block);
+
+ bool EvaluateConditional(const Cheat& cheat) const;
+
+ // Individual cheat operations
+ void WriteImmediate(const Cheat& cheat);
+ void BeginConditional(const Cheat& cheat);
+ void EndConditional(const Cheat& cheat);
+ void Loop(const Cheat& cheat);
+ void LoadImmediate(const Cheat& cheat);
+ void LoadIndexed(const Cheat& cheat);
+ void StoreIndexed(const Cheat& cheat);
+ void RegisterArithmetic(const Cheat& cheat);
+ void BeginConditionalInput(const Cheat& cheat);
+
+ VAddr SanitizeAddress(VAddr in) const;
+
+ // Master Codes are defined as codes that cannot be disabled and are run prior to all
+ // others.
+ ProgramSegment master_list;
+ // All other codes
+ ProgramSegment standard_list;
+
+ bool in_standard = false;
+
+ // 16 (0x0-0xF) scratch registers that can be used by cheats
+ std::array<u64, 16> scratch{};
+
+ MemoryWriter writer = nullptr;
+ MemoryReader reader = nullptr;
+
+ u64 main_region_begin{};
+ u64 heap_region_begin{};
+ u64 main_region_end{};
+ u64 heap_region_end{};
+
+ u64 current_block{};
+ // The current index of the cheat within the current Block
+ u64 current_index{};
+
+ // The 'stack' of the program. When a conditional or loop statement is encountered, its index is
+ // pushed onto this queue. When a end block is encountered, the condition is checked.
+ std::map<u64, u64> block_pairs;
+
+ std::set<u64> encountered_loops;
+
+ const Core::System* system;
+};
+
+// Intermediary class that parses a text file or other disk format for storing cheats into a
+// CheatList object, that can be used for execution.
+class CheatParser {
+public:
+ virtual ~CheatParser();
+
+ virtual CheatList Parse(const Core::System& system, const std::vector<u8>& data) const = 0;
+
+protected:
+ CheatList MakeCheatList(const Core::System& system_, CheatList::ProgramSegment master,
+ CheatList::ProgramSegment standard) const;
+};
+
+// CheatParser implementation that parses text files
+class TextCheatParser final : public CheatParser {
+public:
+ ~TextCheatParser() override;
+
+ CheatList Parse(const Core::System& system, const std::vector<u8>& data) const override;
+
+private:
+ std::array<u8, 16> ParseSingleLineCheat(const std::string& line) const;
+};
+
+// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
+class CheatEngine final {
+public:
+ CheatEngine(Core::System& system_, std::vector<CheatList> cheats_, const std::string& build_id,
+ VAddr code_region_start, VAddr code_region_end);
+ ~CheatEngine();
+
+private:
+ void FrameCallback(u64 userdata, s64 cycles_late);
+
+ std::vector<CheatList> cheats;
+
+ Core::Timing::EventType* event;
+ Core::Timing::CoreTiming& core_timing;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 5d4d05c82..15b9e6624 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -24,13 +24,26 @@ namespace FileSys {
union NCASectionHeader;
+/// Describes the type of content within an NCA archive.
enum class NCAContentType : u8 {
+ /// Executable-related data
Program = 0,
+
+ /// Metadata.
Meta = 1,
+
+ /// Access control data.
Control = 2,
+
+ /// Information related to the game manual
+ /// e.g. Legal information, etc.
Manual = 3,
+
+ /// System data.
Data = 4,
- Data_Unknown5 = 5, ///< Seems to be used on some system archives
+
+ /// Data that can be accessed by applications.
+ PublicData = 5,
};
enum class NCASectionCryptoType : u8 {
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 83c184750..f155a1341 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -67,7 +67,7 @@ std::string NACP::GetDeveloperName(Language language) const {
}
u64 NACP::GetTitleId() const {
- return raw.title_id;
+ return raw.save_data_owner_id;
}
u64 NACP::GetDLCBaseTitleId() const {
@@ -80,11 +80,19 @@ std::string NACP::GetVersionString() const {
}
u64 NACP::GetDefaultNormalSaveSize() const {
- return raw.normal_save_data_size;
+ return raw.user_account_save_data_size;
}
u64 NACP::GetDefaultJournalSaveSize() const {
- return raw.journal_sava_data_size;
+ 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 {
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 7b9cdc910..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;
@@ -38,23 +39,35 @@ struct RawNACP {
u8 video_capture_mode;
bool data_loss_confirmation;
INSERT_PADDING_BYTES(1);
- u64_le title_id;
+ u64_le presence_group_id;
std::array<u8, 0x20> rating_age;
std::array<char, 0x10> version_string;
u64_le dlc_base_title_id;
- u64_le title_id_2;
- u64_le normal_save_data_size;
- u64_le journal_sava_data_size;
- INSERT_PADDING_BYTES(0x18);
- u64_le product_code;
+ u64_le save_data_owner_id;
+ u64_le user_account_save_data_size;
+ u64_le user_account_save_data_journal_size;
+ u64_le device_save_data_size;
+ u64_le device_save_data_journal_size;
+ u64_le bcat_delivery_cache_storage_size;
+ char application_error_code_category[8];
std::array<u64_le, 0x8> local_communication;
u8 logo_type;
u8 logo_handling;
bool runtime_add_on_content_install;
INSERT_PADDING_BYTES(5);
- u64_le title_id_update;
- std::array<u8, 0x40> bcat_passphrase;
- INSERT_PADDING_BYTES(0xEC0);
+ u64_le seed_for_pseudo_device_id;
+ std::array<u8, 0x41> bcat_passphrase;
+ INSERT_PADDING_BYTES(7);
+ u64_le user_account_save_data_max_size;
+ u64_le user_account_save_data_max_journal_size;
+ u64_le device_save_data_max_size;
+ u64_le device_save_data_max_journal_size;
+ u64_le temporary_storage_size;
+ u64_le cache_storage_size;
+ u64_le cache_storage_journal_size;
+ u64_le cache_storage_data_and_journal_max_size;
+ u64_le cache_storage_max_index;
+ INSERT_PADDING_BYTES(0xE70);
};
static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size.");
@@ -97,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/errors.h b/src/core/file_sys/errors.h
index e4a4ee4ab..bb4654366 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -11,6 +11,9 @@ namespace FileSys {
constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
+constexpr ResultCode ERROR_OUT_OF_BOUNDS{ErrorModule::FS, 3005};
+constexpr ResultCode ERROR_FAILED_MOUNT_ARCHIVE{ErrorModule::FS, 3223};
+constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::FS, 6001};
constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp
index 47b7526c7..d126ae8dd 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.cpp
+++ b/src/core/file_sys/fsmitm_romfsbuild.cpp
@@ -23,6 +23,7 @@
*/
#include <cstring>
+#include <string_view>
#include "common/alignment.h"
#include "common/assert.h"
#include "core/file_sys/fsmitm_romfsbuild.h"
@@ -97,7 +98,8 @@ struct RomFSBuildFileContext {
VirtualFile source;
};
-static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, std::size_t path_len) {
+static u32 romfs_calc_path_hash(u32 parent, std::string_view path, u32 start,
+ std::size_t path_len) {
u32 hash = parent ^ 123456789;
for (u32 i = 0; i < path_len; i++) {
hash = (hash >> 5) | (hash << 27);
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.cpp b/src/core/file_sys/nca_metadata.cpp
index 6f34b7836..93d0df6b9 100644
--- a/src/core/file_sys/nca_metadata.cpp
+++ b/src/core/file_sys/nca_metadata.cpp
@@ -10,14 +10,6 @@
namespace FileSys {
-bool operator>=(TitleType lhs, TitleType rhs) {
- return static_cast<std::size_t>(lhs) >= static_cast<std::size_t>(rhs);
-}
-
-bool operator<=(TitleType lhs, TitleType rhs) {
- return static_cast<std::size_t>(lhs) <= static_cast<std::size_t>(rhs);
-}
-
CNMT::CNMT(VirtualFile file) {
if (file->ReadObject(&header) != sizeof(CNMTHeader))
return;
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index a05d155f4..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"
@@ -29,9 +30,6 @@ enum class TitleType : u8 {
DeltaTitle = 0x83,
};
-bool operator>=(TitleType lhs, TitleType rhs);
-bool operator<=(TitleType lhs, TitleType rhs);
-
enum class ContentRecordType : u8 {
Meta = 0,
Program = 1,
@@ -72,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 61706966e..da823c37b 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -7,8 +7,10 @@
#include <cstddef>
#include <cstring>
+#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/ips_layer.h"
@@ -19,6 +21,7 @@
#include "core/file_sys/vfs_vector.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
+#include "core/loader/nso.h"
#include "core/settings.h"
namespace FileSys {
@@ -31,14 +34,6 @@ constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
"subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
};
-struct NSOBuildHeader {
- u32_le magic;
- INSERT_PADDING_BYTES(0x3C);
- std::array<u8, 0x20> build_id;
- INSERT_PADDING_BYTES(0xA0);
-};
-static_assert(sizeof(NSOBuildHeader) == 0x100, "NSOBuildHeader has incorrect size.");
-
std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
std::array<u8, sizeof(u32)> bytes{};
bytes[0] = version % SINGLE_BYTE_MODULUS;
@@ -75,7 +70,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
}
}
- const auto installed = Service::FileSystem::GetUnionContents();
+ const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto update_disabled =
@@ -147,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);
@@ -161,32 +156,35 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
return out;
}
-std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
- if (nso.size() < 0x100)
+std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::string& name) const {
+ if (nso.size() < sizeof(Loader::NSOHeader)) {
return nso;
+ }
- NSOBuildHeader header;
- std::memcpy(&header, nso.data(), sizeof(NSOBuildHeader));
+ Loader::NSOHeader header;
+ std::memcpy(&header, nso.data(), sizeof(header));
- if (header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
+ if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
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) {
- LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id);
+ LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
+ title_id);
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
- const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id));
+ const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id));
file->Resize(nso.size());
file->WriteBytes(nso);
}
}
- LOG_INFO(Loader, "Patching NSO for build_id={}", build_id);
+ LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
auto patch_dirs = load_dir->GetSubdirectories();
@@ -212,14 +210,16 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
}
}
- if (out.size() < 0x100)
+ if (out.size() < sizeof(Loader::NSOHeader)) {
return nso;
- std::memcpy(out.data(), &header, sizeof(NSOBuildHeader));
+ }
+
+ std::memcpy(out.data(), &header, sizeof(header));
return out;
}
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);
@@ -232,6 +232,57 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
return !CollectPatches(patch_dirs, build_id).empty();
}
+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::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));
+
+ if (file == nullptr) {
+ LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
+ title_id, build_id);
+ return std::nullopt;
+ }
+
+ std::vector<u8> data(file->GetSize());
+ if (file->Read(data.data(), data.size()) != data.size()) {
+ LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
+ title_id, build_id);
+ return std::nullopt;
+ }
+
+ TextCheatParser parser;
+ return parser.Parse(system, data);
+}
+
+std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
+ const std::array<u8, 32>& build_id_) const {
+ const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+ auto patch_dirs = load_dir->GetSubdirectories();
+ std::sort(patch_dirs.begin(), patch_dirs.end(),
+ [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
+
+ std::vector<CheatList> out;
+ out.reserve(patch_dirs.size());
+ for (const auto& subdir : patch_dirs) {
+ auto cheats_dir = subdir->GetSubdirectory("cheats");
+ if (cheats_dir != nullptr) {
+ auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true);
+ if (res.has_value()) {
+ out.push_back(std::move(*res));
+ continue;
+ }
+
+ res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false);
+ if (res.has_value())
+ out.push_back(std::move(*res));
+ }
+ }
+
+ return out;
+}
+
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
@@ -296,7 +347,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
if (romfs == nullptr)
return romfs;
- const auto installed = Service::FileSystem::GetUnionContents();
+ const auto& installed = Core::System::GetInstance().GetContentProvider();
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
@@ -343,7 +394,7 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
VirtualFile update_raw) const {
std::map<std::string, std::string, std::less<>> out;
- const auto installed = Service::FileSystem::GetUnionContents();
+ const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto& disabled = Settings::values.disabled_addons[title_id];
// Game Updates
@@ -403,6 +454,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
}
if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
AppendCommaIfNotEmpty(types, "LayeredFS");
+ if (IsDirValidAndNonEmpty(mod->GetSubdirectory("cheats")))
+ AppendCommaIfNotEmpty(types, "Cheats");
if (types.empty())
continue;
@@ -415,10 +468,10 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
// DLC
const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
- std::vector<RegisteredCacheEntry> dlc_match;
+ std::vector<ContentProviderEntry> dlc_match;
dlc_match.reserve(dlc_entries.size());
std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
- [this, &installed](const RegisteredCacheEntry& entry) {
+ [this, &installed](const ContentProviderEntry& entry) {
return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id &&
installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
});
@@ -441,7 +494,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
}
std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
- const auto installed{Service::FileSystem::GetUnionContents()};
+ const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control);
if (base_control_nca == nullptr)
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index b8a1652fd..769f8c6f0 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -8,9 +8,14 @@
#include <memory>
#include <string>
#include "common/common_types.h"
+#include "core/file_sys/cheat_engine.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/vfs.h"
+namespace Core {
+class System;
+}
+
namespace FileSys {
class NCA;
@@ -39,12 +44,16 @@ public:
// Currently tracked NSO patches:
// - IPS
// - IPSwitch
- std::vector<u8> PatchNSO(const std::vector<u8>& nso) const;
+ std::vector<u8> PatchNSO(const std::vector<u8>& nso, const std::string& name) const;
// Checks to see if PatchNSO() will have any effect given the NSO's build ID.
// Used to prevent expensive copies in NSO loader.
bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const;
+ // Creates a CheatList object with all
+ std::vector<CheatList> CreateCheatList(const Core::System& system,
+ const std::array<u8, 0x20>& build_id) const;
+
// Currently tracked RomFS patches:
// - Game Updates
// - LayeredFS
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index d3e00437f..d863253f8 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include <cstddef>
-#include <cstring>
#include <vector>
#include "common/logging/log.h"
@@ -17,28 +16,30 @@ ProgramMetadata::ProgramMetadata() = default;
ProgramMetadata::~ProgramMetadata() = default;
Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
- std::size_t total_size = static_cast<std::size_t>(file->GetSize());
- if (total_size < sizeof(Header))
+ const std::size_t total_size = file->GetSize();
+ if (total_size < sizeof(Header)) {
return Loader::ResultStatus::ErrorBadNPDMHeader;
+ }
- // TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable.
- std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header));
- if (sizeof(Header) != npdm_header_data.size())
+ if (sizeof(Header) != file->ReadObject(&npdm_header)) {
return Loader::ResultStatus::ErrorBadNPDMHeader;
- std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header));
+ }
- std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset);
- if (sizeof(AcidHeader) != acid_header_data.size())
+ if (sizeof(AcidHeader) != file->ReadObject(&acid_header, npdm_header.acid_offset)) {
return Loader::ResultStatus::ErrorBadACIDHeader;
- std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader));
+ }
- if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset))
+ if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) {
return Loader::ResultStatus::ErrorBadACIHeader;
+ }
- if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset))
+ if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) {
return Loader::ResultStatus::ErrorBadFileAccessControl;
- if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
+ }
+
+ if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) {
return Loader::ResultStatus::ErrorBadFileAccessHeader;
+ }
aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32));
const u64 read_size = aci_header.kac_size;
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 0033ba347..7de5b9cf9 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -58,7 +58,6 @@ public:
void Print() const;
private:
- // TODO(DarkLordZach): BitField is not trivially copyable.
struct Header {
std::array<char, 4> magic;
std::array<u8, 8> reserved;
@@ -85,7 +84,6 @@ private:
static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong");
- // TODO(DarkLordZach): BitField is not trivially copyable.
struct AcidHeader {
std::array<u8, 0x100> signature;
std::array<u8, 0x100> nca_modulus;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 128199063..58917e094 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -23,19 +23,19 @@ namespace FileSys {
// The size of blocks to use when vfs raw copying into nand.
constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000;
-std::string RegisteredCacheEntry::DebugInfo() const {
+std::string ContentProviderEntry::DebugInfo() const {
return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type));
}
-bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
+bool operator<(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type);
}
-bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
+bool operator==(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type);
}
-bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
+bool operator!=(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
return !operator==(lhs, rhs);
}
@@ -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) {
@@ -84,7 +85,7 @@ static std::string GetCNMTName(TitleType type, u64 title_id) {
return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id);
}
-static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
+ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
switch (type) {
case NCAContentType::Program:
// TODO(DarkLordZach): Differentiate between Program and Patch
@@ -94,7 +95,7 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
case NCAContentType::Control:
return ContentRecordType::Control;
case NCAContentType::Data:
- case NCAContentType::Data_Unknown5:
+ case NCAContentType::PublicData:
return ContentRecordType::Data;
case NCAContentType::Manual:
// TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
@@ -104,6 +105,28 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
}
}
+ContentProvider::~ContentProvider() = default;
+
+bool ContentProvider::HasEntry(ContentProviderEntry entry) const {
+ return HasEntry(entry.title_id, entry.type);
+}
+
+VirtualFile ContentProvider::GetEntryUnparsed(ContentProviderEntry entry) const {
+ return GetEntryUnparsed(entry.title_id, entry.type);
+}
+
+VirtualFile ContentProvider::GetEntryRaw(ContentProviderEntry entry) const {
+ return GetEntryRaw(entry.title_id, entry.type);
+}
+
+std::unique_ptr<NCA> ContentProvider::GetEntry(ContentProviderEntry entry) const {
+ return GetEntry(entry.title_id, entry.type);
+}
+
+std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
+ return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
+}
+
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
std::string_view path) const {
const auto file = dir->GetFileRelative(path);
@@ -161,8 +184,8 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
return file;
}
-static std::optional<NcaID> CheckMapForContentRecord(
- const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) {
+static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id,
+ ContentRecordType type) {
if (map.find(title_id) == map.end())
return {};
@@ -268,7 +291,7 @@ void RegisteredCache::Refresh() {
AccumulateYuzuMeta();
}
-RegisteredCache::RegisteredCache(VirtualDir dir_, RegisteredCacheParsingFunction parsing_function)
+RegisteredCache::RegisteredCache(VirtualDir dir_, ContentProviderParsingFunction parsing_function)
: dir(std::move(dir_)), parser(std::move(parsing_function)) {
Refresh();
}
@@ -279,19 +302,11 @@ bool RegisteredCache::HasEntry(u64 title_id, ContentRecordType type) const {
return GetEntryRaw(title_id, type) != nullptr;
}
-bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const {
- return GetEntryRaw(entry) != nullptr;
-}
-
VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
const auto id = GetNcaIDFromMetadata(title_id, type);
return id ? GetFileAtID(*id) : nullptr;
}
-VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const {
- return GetEntryUnparsed(entry.title_id, entry.type);
-}
-
std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
const auto meta_iter = meta.find(title_id);
if (meta_iter != meta.end())
@@ -309,10 +324,6 @@ VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) c
return id ? parser(GetFileAtID(*id), *id) : nullptr;
}
-VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
- return GetEntryRaw(entry.title_id, entry.type);
-}
-
std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
const auto raw = GetEntryRaw(title_id, type);
if (raw == nullptr)
@@ -320,10 +331,6 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
return std::make_unique<NCA>(raw, nullptr, 0, keys);
}
-std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
- return GetEntry(entry.title_id, entry.type);
-}
-
template <typename T>
void RegisteredCache::IterateAllMetadata(
std::vector<T>& out, std::function<T(const CNMT&, const ContentRecord&)> proc,
@@ -348,25 +355,14 @@ void RegisteredCache::IterateAllMetadata(
}
}
-std::vector<RegisteredCacheEntry> RegisteredCache::ListEntries() const {
- std::vector<RegisteredCacheEntry> out;
- IterateAllMetadata<RegisteredCacheEntry>(
- out,
- [](const CNMT& c, const ContentRecord& r) {
- return RegisteredCacheEntry{c.GetTitleID(), r.type};
- },
- [](const CNMT& c, const ContentRecord& r) { return true; });
- return out;
-}
-
-std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
+std::vector<ContentProviderEntry> RegisteredCache::ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
- std::vector<RegisteredCacheEntry> out;
- IterateAllMetadata<RegisteredCacheEntry>(
+ std::vector<ContentProviderEntry> out;
+ IterateAllMetadata<ContentProviderEntry>(
out,
[](const CNMT& c, const ContentRecord& r) {
- return RegisteredCacheEntry{c.GetTitleID(), r.type};
+ return ContentProviderEntry{c.GetTitleID(), r.type};
},
[&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
if (title_type && *title_type != c.GetType())
@@ -381,10 +377,11 @@ std::vector<RegisteredCacheEntry> 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,
@@ -521,37 +518,56 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
}) != yuzu_meta.end();
}
-RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches)
- : caches(std::move(caches)) {}
+ContentProviderUnion::~ContentProviderUnion() = default;
-void RegisteredCacheUnion::Refresh() {
- for (const auto& c : caches)
- c->Refresh();
+void ContentProviderUnion::SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider) {
+ providers[slot] = provider;
}
-bool RegisteredCacheUnion::HasEntry(u64 title_id, ContentRecordType type) const {
- return std::any_of(caches.begin(), caches.end(), [title_id, type](const auto& cache) {
- return cache->HasEntry(title_id, type);
- });
+void ContentProviderUnion::ClearSlot(ContentProviderUnionSlot slot) {
+ providers[slot] = nullptr;
}
-bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const {
- return HasEntry(entry.title_id, entry.type);
+void ContentProviderUnion::Refresh() {
+ for (auto& provider : providers) {
+ if (provider.second == nullptr)
+ continue;
+
+ provider.second->Refresh();
+ }
}
-std::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
- for (const auto& c : caches) {
- const auto res = c->GetEntryVersion(title_id);
- if (res)
+bool ContentProviderUnion::HasEntry(u64 title_id, ContentRecordType type) const {
+ for (const auto& provider : providers) {
+ if (provider.second == nullptr)
+ continue;
+
+ if (provider.second->HasEntry(title_id, type))
+ return true;
+ }
+
+ return false;
+}
+
+std::optional<u32> ContentProviderUnion::GetEntryVersion(u64 title_id) const {
+ for (const auto& provider : providers) {
+ if (provider.second == nullptr)
+ continue;
+
+ const auto res = provider.second->GetEntryVersion(title_id);
+ if (res != std::nullopt)
return res;
}
- return {};
+ return std::nullopt;
}
-VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
- for (const auto& c : caches) {
- const auto res = c->GetEntryUnparsed(title_id, type);
+VirtualFile ContentProviderUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
+ for (const auto& provider : providers) {
+ if (provider.second == nullptr)
+ continue;
+
+ const auto res = provider.second->GetEntryUnparsed(title_id, type);
if (res != nullptr)
return res;
}
@@ -559,13 +575,12 @@ VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordTy
return nullptr;
}
-VirtualFile RegisteredCacheUnion::GetEntryUnparsed(RegisteredCacheEntry entry) const {
- return GetEntryUnparsed(entry.title_id, entry.type);
-}
+VirtualFile ContentProviderUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const {
+ for (const auto& provider : providers) {
+ if (provider.second == nullptr)
+ continue;
-VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const {
- for (const auto& c : caches) {
- const auto res = c->GetEntryRaw(title_id, type);
+ const auto res = provider.second->GetEntryRaw(title_id, type);
if (res != nullptr)
return res;
}
@@ -573,30 +588,56 @@ VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType ty
return nullptr;
}
-VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const {
- return GetEntryRaw(entry.title_id, entry.type);
-}
+std::unique_ptr<NCA> ContentProviderUnion::GetEntry(u64 title_id, ContentRecordType type) const {
+ for (const auto& provider : providers) {
+ if (provider.second == nullptr)
+ continue;
-std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
- const auto raw = GetEntryRaw(title_id, type);
- if (raw == nullptr)
- return nullptr;
- return std::make_unique<NCA>(raw);
+ auto res = provider.second->GetEntry(title_id, type);
+ if (res != nullptr)
+ return res;
+ }
+
+ return nullptr;
}
-std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
- return GetEntry(entry.title_id, entry.type);
+std::vector<ContentProviderEntry> ContentProviderUnion::ListEntriesFilter(
+ std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
+ std::optional<u64> title_id) const {
+ std::vector<ContentProviderEntry> out;
+
+ for (const auto& provider : providers) {
+ if (provider.second == nullptr)
+ continue;
+
+ const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id);
+ std::copy(vec.begin(), vec.end(), std::back_inserter(out));
+ }
+
+ std::sort(out.begin(), out.end());
+ out.erase(std::unique(out.begin(), out.end()), out.end());
+ return out;
}
-std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
- std::vector<RegisteredCacheEntry> out;
- for (const auto& c : caches) {
- c->IterateAllMetadata<RegisteredCacheEntry>(
- out,
- [](const CNMT& c, const ContentRecord& r) {
- return RegisteredCacheEntry{c.GetTitleID(), r.type};
- },
- [](const CNMT& c, const ContentRecord& r) { return true; });
+std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>>
+ContentProviderUnion::ListEntriesFilterOrigin(std::optional<ContentProviderUnionSlot> origin,
+ std::optional<TitleType> title_type,
+ std::optional<ContentRecordType> record_type,
+ std::optional<u64> title_id) const {
+ std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> out;
+
+ for (const auto& provider : providers) {
+ if (provider.second == nullptr)
+ continue;
+
+ if (origin.has_value() && *origin != provider.first)
+ continue;
+
+ const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id);
+ std::transform(vec.begin(), vec.end(), std::back_inserter(out),
+ [&provider](const ContentProviderEntry& entry) {
+ return std::make_pair(provider.first, entry);
+ });
}
std::sort(out.begin(), out.end());
@@ -604,25 +645,61 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
return out;
}
-std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
+ManualContentProvider::~ManualContentProvider() = default;
+
+void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type,
+ u64 title_id, VirtualFile file) {
+ entries.insert_or_assign({title_type, content_type, title_id}, file);
+}
+
+void ManualContentProvider::ClearAllEntries() {
+ entries.clear();
+}
+
+void ManualContentProvider::Refresh() {}
+
+bool ManualContentProvider::HasEntry(u64 title_id, ContentRecordType type) const {
+ return GetEntryRaw(title_id, type) != nullptr;
+}
+
+std::optional<u32> ManualContentProvider::GetEntryVersion(u64 title_id) const {
+ return std::nullopt;
+}
+
+VirtualFile ManualContentProvider::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
+ return GetEntryRaw(title_id, type);
+}
+
+VirtualFile ManualContentProvider::GetEntryRaw(u64 title_id, ContentRecordType type) const {
+ const auto iter =
+ std::find_if(entries.begin(), entries.end(), [title_id, type](const auto& entry) {
+ const auto [title_type, content_type, e_title_id] = entry.first;
+ return content_type == type && e_title_id == title_id;
+ });
+ if (iter == entries.end())
+ return nullptr;
+ return iter->second;
+}
+
+std::unique_ptr<NCA> ManualContentProvider::GetEntry(u64 title_id, ContentRecordType type) const {
+ const auto res = GetEntryRaw(title_id, type);
+ if (res == nullptr)
+ return nullptr;
+ return std::make_unique<NCA>(res, nullptr, 0, keys);
+}
+
+std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
- std::vector<RegisteredCacheEntry> out;
- for (const auto& c : caches) {
- c->IterateAllMetadata<RegisteredCacheEntry>(
- out,
- [](const CNMT& c, const ContentRecord& r) {
- return RegisteredCacheEntry{c.GetTitleID(), r.type};
- },
- [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
- if (title_type && *title_type != c.GetType())
- return false;
- if (record_type && *record_type != r.type)
- return false;
- if (title_id && *title_id != c.GetTitleID())
- return false;
- return true;
- });
+ std::vector<ContentProviderEntry> out;
+
+ for (const auto& entry : entries) {
+ const auto [e_title_type, e_content_type, e_title_id] = entry.first;
+ if ((title_type == std::nullopt || e_title_type == *title_type) &&
+ (record_type == std::nullopt || e_content_type == *record_type) &&
+ (title_id == std::nullopt || e_title_id == *title_id)) {
+ out.emplace_back(ContentProviderEntry{e_title_id, e_content_type});
+ }
}
std::sort(out.begin(), out.end());
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 3b77af4e0..ec9052653 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -21,12 +21,13 @@ class NSP;
class XCI;
enum class ContentRecordType : u8;
+enum class NCAContentType : u8;
enum class TitleType : u8;
struct ContentRecord;
using NcaID = std::array<u8, 0x10>;
-using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
+using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>;
enum class InstallResult {
@@ -36,7 +37,7 @@ enum class InstallResult {
ErrorMetaFailed,
};
-struct RegisteredCacheEntry {
+struct ContentProviderEntry {
u64 title_id;
ContentRecordType type;
@@ -47,12 +48,46 @@ constexpr u64 GetUpdateTitleID(u64 base_title_id) {
return base_title_id | 0x800;
}
+ContentRecordType GetCRTypeFromNCAType(NCAContentType type);
+
// boost flat_map requires operator< for O(log(n)) lookups.
-bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
+bool operator<(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
// std unique requires operator== to identify duplicates.
-bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
-bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
+bool operator==(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
+bool operator!=(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
+
+class ContentProvider {
+public:
+ virtual ~ContentProvider();
+
+ virtual void Refresh() = 0;
+
+ virtual bool HasEntry(u64 title_id, ContentRecordType type) const = 0;
+ virtual bool HasEntry(ContentProviderEntry entry) const;
+
+ virtual std::optional<u32> GetEntryVersion(u64 title_id) const = 0;
+
+ virtual VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const = 0;
+ virtual VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const;
+
+ virtual VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const = 0;
+ virtual VirtualFile GetEntryRaw(ContentProviderEntry entry) const;
+
+ virtual std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const = 0;
+ virtual std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const;
+
+ virtual std::vector<ContentProviderEntry> ListEntries() const;
+
+ // If a parameter is not std::nullopt, it will be filtered for from all entries.
+ virtual std::vector<ContentProviderEntry> ListEntriesFilter(
+ std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
+ std::optional<u64> title_id = {}) const = 0;
+
+protected:
+ // A single instance of KeyManager to be used by GetEntry()
+ Core::Crypto::KeyManager keys;
+};
/*
* A class that catalogues NCAs in the registered directory structure.
@@ -67,39 +102,32 @@ bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs
* (This impl also supports substituting the nca dir for an nca file, as that's more convenient
* when 4GB splitting can be ignored.)
*/
-class RegisteredCache {
- friend class RegisteredCacheUnion;
-
+class RegisteredCache : public ContentProvider {
public:
// Parsing function defines the conversion from raw file to NCA. If there are other steps
// besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
// parsing function.
explicit RegisteredCache(VirtualDir dir,
- RegisteredCacheParsingFunction parsing_function =
+ ContentProviderParsingFunction parsing_function =
[](const VirtualFile& file, const NcaID& id) { return file; });
- ~RegisteredCache();
+ ~RegisteredCache() override;
- void Refresh();
+ void Refresh() override;
- bool HasEntry(u64 title_id, ContentRecordType type) const;
- bool HasEntry(RegisteredCacheEntry entry) const;
+ bool HasEntry(u64 title_id, ContentRecordType type) const override;
- std::optional<u32> GetEntryVersion(u64 title_id) const;
+ std::optional<u32> GetEntryVersion(u64 title_id) const override;
- VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
- VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
+ VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
- VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
- VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
+ VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
- std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
- std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
+ std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
- std::vector<RegisteredCacheEntry> ListEntries() const;
// If a parameter is not std::nullopt, it will be filtered for from all entries.
- std::vector<RegisteredCacheEntry> ListEntriesFilter(
+ std::vector<ContentProviderEntry> ListEntriesFilter(
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
- std::optional<u64> title_id = {}) const;
+ std::optional<u64> title_id = {}) const override;
// Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
// there is a meta NCA and all of them are accessible.
@@ -131,46 +159,70 @@ private:
bool RawInstallYuzuMeta(const CNMT& cnmt);
VirtualDir dir;
- RegisteredCacheParsingFunction parser;
- Core::Crypto::KeyManager keys;
+ ContentProviderParsingFunction parser;
// maps tid -> NcaID of meta
- boost::container::flat_map<u64, NcaID> meta_id;
+ std::map<u64, NcaID> meta_id;
// maps tid -> meta
- boost::container::flat_map<u64, CNMT> meta;
+ std::map<u64, CNMT> meta;
// maps tid -> meta for CNMT in yuzu_meta
- boost::container::flat_map<u64, CNMT> yuzu_meta;
+ std::map<u64, CNMT> yuzu_meta;
};
-// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface.
-class RegisteredCacheUnion {
-public:
- explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches);
-
- void Refresh();
-
- bool HasEntry(u64 title_id, ContentRecordType type) const;
- bool HasEntry(RegisteredCacheEntry entry) const;
-
- std::optional<u32> GetEntryVersion(u64 title_id) const;
-
- VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
- VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
-
- VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
- VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
-
- std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
- std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
+enum class ContentProviderUnionSlot {
+ SysNAND, ///< System NAND
+ UserNAND, ///< User NAND
+ SDMC, ///< SD Card
+ FrontendManual, ///< Frontend-defined game list or similar
+};
- std::vector<RegisteredCacheEntry> ListEntries() const;
- // If a parameter is not std::nullopt, it will be filtered for from all entries.
- std::vector<RegisteredCacheEntry> ListEntriesFilter(
+// Combines multiple ContentProvider(s) (i.e. SysNAND, UserNAND, SDMC) into one interface.
+class ContentProviderUnion : public ContentProvider {
+public:
+ ~ContentProviderUnion() override;
+
+ void SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider);
+ void ClearSlot(ContentProviderUnionSlot slot);
+
+ void Refresh() override;
+ bool HasEntry(u64 title_id, ContentRecordType type) const override;
+ std::optional<u32> GetEntryVersion(u64 title_id) const override;
+ VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
+ VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
+ std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
+ std::vector<ContentProviderEntry> ListEntriesFilter(
+ std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
+ std::optional<u64> title_id) const override;
+
+ std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> ListEntriesFilterOrigin(
+ std::optional<ContentProviderUnionSlot> origin = {},
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const;
private:
- std::vector<RegisteredCache*> caches;
+ std::map<ContentProviderUnionSlot, ContentProvider*> providers;
+};
+
+class ManualContentProvider : public ContentProvider {
+public:
+ ~ManualContentProvider() override;
+
+ void AddEntry(TitleType title_type, ContentRecordType content_type, u64 title_id,
+ VirtualFile file);
+ void ClearAllEntries();
+
+ void Refresh() override;
+ bool HasEntry(u64 title_id, ContentRecordType type) const override;
+ std::optional<u32> GetEntryVersion(u64 title_id) const override;
+ VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
+ VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
+ std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
+ std::vector<ContentProviderEntry> ListEntriesFilter(
+ std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
+ std::optional<u64> title_id) const override;
+
+private:
+ std::map<std::tuple<TitleType, ContentRecordType, u64>, VirtualFile> entries;
};
} // namespace FileSys
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index 6ad1e4f86..b2ccb2926 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -48,7 +48,7 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte
switch (storage) {
case StorageId::None:
- res = Service::FileSystem::GetUnionContents().GetEntry(title_id, type);
+ res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type);
break;
case StorageId::NandSystem:
res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type);
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 1913dc956..7974b031d 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -16,8 +16,10 @@ namespace FileSys {
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
std::string SaveDataDescriptor::DebugInfo() const {
- return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]",
- static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id);
+ return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, "
+ "rank={}, index={}]",
+ static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
+ static_cast<u8>(rank), index);
}
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
@@ -28,7 +30,7 @@ SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save
SaveDataFactory::~SaveDataFactory() = default;
-ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) {
+ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) {
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
if (meta.zero_1 != 0) {
LOG_WARNING(Service_FS,
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index 3a1caf292..b73654571 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -32,12 +32,19 @@ enum class SaveDataType : u8 {
CacheStorage = 5,
};
+enum class SaveDataRank : u8 {
+ Primary,
+ Secondary,
+};
+
struct SaveDataDescriptor {
u64_le title_id;
u128 user_id;
u64_le save_id;
SaveDataType type;
- INSERT_PADDING_BYTES(7);
+ SaveDataRank rank;
+ u16_le index;
+ INSERT_PADDING_BYTES(4);
u64_le zero_1;
u64_le zero_2;
u64_le zero_3;
@@ -57,7 +64,7 @@ public:
explicit SaveDataFactory(VirtualDir dir);
~SaveDataFactory();
- ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
+ ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta);
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index e1a4210db..d0428a457 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -143,11 +143,12 @@ std::multimap<u64, std::shared_ptr<NCA>> NSP::GetNCAsByTitleID() const {
return out;
}
-std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> NSP::GetNCAs() const {
+std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>>
+NSP::GetNCAs() const {
return ncas;
}
-std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const {
+std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType title_type) const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
@@ -155,14 +156,14 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const {
if (title_id_iter == ncas.end())
return nullptr;
- const auto type_iter = title_id_iter->second.find(type);
+ const auto type_iter = title_id_iter->second.find({title_type, type});
if (type_iter == title_id_iter->second.end())
return nullptr;
return type_iter->second;
}
-VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const {
+VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
const auto nca = GetNCA(title_id, type);
@@ -234,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[ContentRecordType::Meta] = nca;
+ 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 "
@@ -252,13 +255,14 @@ 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)) {
- ncas_title[rec.type] = std::move(next_nca);
+ ncas_title[{cnmt.GetType(), rec.type}] = std::move(next_nca);
}
}
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index 9a28ed5bb..ee9b6ce17 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -42,9 +42,12 @@ public:
// Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML)
std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const;
std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const;
- std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> GetNCAs() const;
- std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type) const;
- VirtualFile GetNCAFile(u64 title_id, ContentRecordType type) const;
+ std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> GetNCAs()
+ const;
+ std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type,
+ TitleType title_type = TitleType::Application) const;
+ VirtualFile GetNCAFile(u64 title_id, ContentRecordType type,
+ TitleType title_type = TitleType::Application) const;
std::vector<Core::Crypto::Key128> GetTitlekey() const;
std::vector<VirtualFile> GetFiles() const override;
@@ -67,7 +70,7 @@ private:
std::shared_ptr<PartitionFilesystem> pfs;
// Map title id -> {map type -> NCA}
- std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
+ std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas;
std::vector<VirtualFile> ticket_files;
Core::Crypto::KeyManager keys;
diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp
index e3e79f40a..c9722ed77 100644
--- a/src/core/file_sys/system_archive/system_archive.cpp
+++ b/src/core/file_sys/system_archive/system_archive.cpp
@@ -6,6 +6,7 @@
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/ng_word.h"
#include "core/file_sys/system_archive/system_archive.h"
+#include "core/file_sys/system_archive/system_version.h"
namespace FileSys::SystemArchive {
@@ -30,7 +31,7 @@ constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHI
{0x0100000000000806, "NgWord", &NgWord1},
{0x0100000000000807, "SsidList", nullptr},
{0x0100000000000808, "Dictionary", nullptr},
- {0x0100000000000809, "SystemVersion", nullptr},
+ {0x0100000000000809, "SystemVersion", &SystemVersion},
{0x010000000000080A, "AvatarImage", nullptr},
{0x010000000000080B, "LocalNews", nullptr},
{0x010000000000080C, "Eula", nullptr},
diff --git a/src/core/file_sys/system_archive/system_version.cpp b/src/core/file_sys/system_archive/system_version.cpp
new file mode 100644
index 000000000..6e22f97b0
--- /dev/null
+++ b/src/core/file_sys/system_archive/system_version.cpp
@@ -0,0 +1,52 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/file_sys/system_archive/system_version.h"
+#include "core/file_sys/vfs_vector.h"
+
+namespace FileSys::SystemArchive {
+
+namespace SystemVersionData {
+
+// This section should reflect the best system version to describe yuzu's HLE api.
+// TODO(DarkLordZach): Update when HLE gets better.
+
+constexpr u8 VERSION_MAJOR = 5;
+constexpr u8 VERSION_MINOR = 1;
+constexpr u8 VERSION_MICRO = 0;
+
+constexpr u8 REVISION_MAJOR = 3;
+constexpr u8 REVISION_MINOR = 0;
+
+constexpr char PLATFORM_STRING[] = "NX";
+constexpr char VERSION_HASH[] = "23f9df53e25709d756e0c76effcb2473bd3447dd";
+constexpr char DISPLAY_VERSION[] = "5.1.0";
+constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 5.1.0-3.0";
+
+} // namespace SystemVersionData
+
+std::string GetLongDisplayVersion() {
+ return SystemVersionData::DISPLAY_TITLE;
+}
+
+VirtualDir SystemVersion() {
+ VirtualFile file = std::make_shared<VectorVfsFile>(std::vector<u8>(0x100), "file");
+ file->WriteObject(SystemVersionData::VERSION_MAJOR, 0);
+ file->WriteObject(SystemVersionData::VERSION_MINOR, 1);
+ file->WriteObject(SystemVersionData::VERSION_MICRO, 2);
+ file->WriteObject(SystemVersionData::REVISION_MAJOR, 4);
+ file->WriteObject(SystemVersionData::REVISION_MINOR, 5);
+ file->WriteArray(SystemVersionData::PLATFORM_STRING,
+ std::min<u64>(sizeof(SystemVersionData::PLATFORM_STRING), 0x20ULL), 0x8);
+ file->WriteArray(SystemVersionData::VERSION_HASH,
+ std::min<u64>(sizeof(SystemVersionData::VERSION_HASH), 0x40ULL), 0x28);
+ file->WriteArray(SystemVersionData::DISPLAY_VERSION,
+ std::min<u64>(sizeof(SystemVersionData::DISPLAY_VERSION), 0x18ULL), 0x68);
+ file->WriteArray(SystemVersionData::DISPLAY_TITLE,
+ std::min<u64>(sizeof(SystemVersionData::DISPLAY_TITLE), 0x80ULL), 0x80);
+ return std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{file},
+ std::vector<VirtualDir>{}, "data");
+}
+
+} // namespace FileSys::SystemArchive
diff --git a/src/core/file_sys/system_archive/system_version.h b/src/core/file_sys/system_archive/system_version.h
new file mode 100644
index 000000000..deed79b26
--- /dev/null
+++ b/src/core/file_sys/system_archive/system_version.h
@@ -0,0 +1,16 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+#include "core/file_sys/vfs_types.h"
+
+namespace FileSys::SystemArchive {
+
+std::string GetLongDisplayVersion();
+
+VirtualDir SystemVersion();
+
+} // namespace FileSys::SystemArchive
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/applets/error.cpp b/src/core/frontend/applets/error.cpp
new file mode 100644
index 000000000..4002a9211
--- /dev/null
+++ b/src/core/frontend/applets/error.cpp
@@ -0,0 +1,34 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/frontend/applets/error.h"
+
+namespace Core::Frontend {
+
+ErrorApplet::~ErrorApplet() = default;
+
+void DefaultErrorApplet::ShowError(ResultCode error, std::function<void()> finished) const {
+ LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})",
+ static_cast<u32>(error.module.Value()), error.description.Value(), error.raw);
+}
+
+void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
+ std::function<void()> finished) const {
+ LOG_CRITICAL(
+ Service_Fatal,
+ "Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}",
+ static_cast<u32>(error.module.Value()), error.description.Value(), error.raw, time.count());
+}
+
+void DefaultErrorApplet::ShowCustomErrorText(ResultCode error, std::string main_text,
+ std::string detail_text,
+ std::function<void()> finished) const {
+ LOG_CRITICAL(Service_Fatal,
+ "Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})",
+ static_cast<u32>(error.module.Value()), error.description.Value(), error.raw);
+ LOG_CRITICAL(Service_Fatal, " Main Text: {}", main_text);
+ LOG_CRITICAL(Service_Fatal, " Detail Text: {}", detail_text);
+}
+
+} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/error.h b/src/core/frontend/applets/error.h
new file mode 100644
index 000000000..699df940d
--- /dev/null
+++ b/src/core/frontend/applets/error.h
@@ -0,0 +1,37 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <chrono>
+#include <functional>
+
+#include "core/hle/result.h"
+
+namespace Core::Frontend {
+
+class ErrorApplet {
+public:
+ virtual ~ErrorApplet();
+
+ virtual void ShowError(ResultCode error, std::function<void()> finished) const = 0;
+
+ virtual void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
+ std::function<void()> finished) const = 0;
+
+ virtual void ShowCustomErrorText(ResultCode error, std::string dialog_text,
+ std::string fullscreen_text,
+ std::function<void()> finished) const = 0;
+};
+
+class DefaultErrorApplet final : public ErrorApplet {
+public:
+ void ShowError(ResultCode error, std::function<void()> finished) const override;
+ void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
+ std::function<void()> finished) const override;
+ void ShowCustomErrorText(ResultCode error, std::string main_text, std::string detail_text,
+ std::function<void()> finished) const override;
+};
+
+} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/general_frontend.cpp b/src/core/frontend/applets/general_frontend.cpp
new file mode 100644
index 000000000..b974f2289
--- /dev/null
+++ b/src/core/frontend/applets/general_frontend.cpp
@@ -0,0 +1,27 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/logging/log.h"
+#include "core/frontend/applets/general_frontend.h"
+
+namespace Core::Frontend {
+
+PhotoViewerApplet::~PhotoViewerApplet() = default;
+
+DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() {}
+
+void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id,
+ std::function<void()> finished) const {
+ LOG_INFO(Service_AM,
+ "Application requested frontend to display stored photos for title_id={:016X}",
+ title_id);
+ finished();
+}
+
+void DefaultPhotoViewerApplet::ShowAllPhotos(std::function<void()> finished) const {
+ LOG_INFO(Service_AM, "Application requested frontend to display all stored photos.");
+ finished();
+}
+
+} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/general_frontend.h b/src/core/frontend/applets/general_frontend.h
new file mode 100644
index 000000000..d4506c999
--- /dev/null
+++ b/src/core/frontend/applets/general_frontend.h
@@ -0,0 +1,28 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <functional>
+#include "common/common_types.h"
+
+namespace Core::Frontend {
+
+class PhotoViewerApplet {
+public:
+ virtual ~PhotoViewerApplet();
+
+ virtual void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const = 0;
+ virtual void ShowAllPhotos(std::function<void()> finished) const = 0;
+};
+
+class DefaultPhotoViewerApplet final : public PhotoViewerApplet {
+public:
+ ~DefaultPhotoViewerApplet() override;
+
+ void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const override;
+ void ShowAllPhotos(std::function<void()> finished) const override;
+};
+
+} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/profile_select.cpp b/src/core/frontend/applets/profile_select.cpp
index fbf5f2a9e..4df3574d2 100644
--- a/src/core/frontend/applets/profile_select.cpp
+++ b/src/core/frontend/applets/profile_select.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "core/frontend/applets/profile_select.h"
+#include "core/hle/service/acc/profile_manager.h"
#include "core/settings.h"
namespace Core::Frontend {
@@ -10,9 +11,9 @@ namespace Core::Frontend {
ProfileSelectApplet::~ProfileSelectApplet() = default;
void DefaultProfileSelectApplet::SelectProfile(
- std::function<void(std::optional<Service::Account::UUID>)> callback) const {
+ std::function<void(std::optional<Common::UUID>)> callback) const {
Service::Account::ProfileManager manager;
- callback(manager.GetUser(Settings::values.current_user).value_or(Service::Account::UUID{}));
+ callback(manager.GetUser(Settings::values.current_user).value_or(Common::UUID{}));
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
}
diff --git a/src/core/frontend/applets/profile_select.h b/src/core/frontend/applets/profile_select.h
index fc8f7ae94..3506b9885 100644
--- a/src/core/frontend/applets/profile_select.h
+++ b/src/core/frontend/applets/profile_select.h
@@ -6,7 +6,7 @@
#include <functional>
#include <optional>
-#include "core/hle/service/acc/profile_manager.h"
+#include "common/uuid.h"
namespace Core::Frontend {
@@ -14,14 +14,12 @@ class ProfileSelectApplet {
public:
virtual ~ProfileSelectApplet();
- virtual void SelectProfile(
- std::function<void(std::optional<Service::Account::UUID>)> callback) const = 0;
+ virtual void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const = 0;
};
class DefaultProfileSelectApplet final : public ProfileSelectApplet {
public:
- void SelectProfile(
- std::function<void(std::optional<Service::Account::UUID>)> callback) const override;
+ void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const override;
};
} // namespace Core::Frontend
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index e29afd630..eda466a5d 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -10,6 +10,8 @@
namespace Core::Frontend {
+GraphicsContext::~GraphicsContext() = default;
+
class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
public std::enable_shared_from_this<TouchState> {
public:
@@ -30,7 +32,7 @@ private:
explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
std::tuple<float, float, bool> GetStatus() const override {
if (auto state = touch_state.lock()) {
- std::lock_guard<std::mutex> guard(state->mutex);
+ std::lock_guard guard{state->mutex};
return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
}
return std::make_tuple(0.0f, 0.0f, false);
@@ -81,7 +83,7 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
return;
- std::lock_guard<std::mutex> guard(touch_state->mutex);
+ std::lock_guard guard{touch_state->mutex};
touch_state->touch_x = static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
touch_state->touch_y = static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
@@ -91,7 +93,7 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
}
void EmuWindow::TouchReleased() {
- std::lock_guard<std::mutex> guard(touch_state->mutex);
+ std::lock_guard guard{touch_state->mutex};
touch_state->touch_pressed = false;
touch_state->touch_x = 0;
touch_state->touch_y = 0;
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index d0bcb4660..4a9912641 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -13,6 +13,25 @@
namespace Core::Frontend {
/**
+ * Represents a graphics context that can be used for background computation or drawing. If the
+ * graphics backend doesn't require the context, then the implementation of these methods can be
+ * stubs
+ */
+class GraphicsContext {
+public:
+ virtual ~GraphicsContext();
+
+ /// Makes the graphics context current for the caller thread
+ virtual void MakeCurrent() = 0;
+
+ /// Releases (dunno if this is the "right" word) the context from the caller thread
+ virtual void DoneCurrent() = 0;
+
+ /// Swap buffers to display the next frame
+ virtual void SwapBuffers() = 0;
+};
+
+/**
* Abstraction class used to provide an interface between emulation code and the frontend
* (e.g. SDL, QGLWidget, GLFW, etc...).
*
@@ -30,7 +49,7 @@ namespace Core::Frontend {
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
* re-read the upper points again and think about it if you don't see this.
*/
-class EmuWindow {
+class EmuWindow : public GraphicsContext {
public:
/// Data structure to store emuwindow configuration
struct WindowConfig {
@@ -40,17 +59,21 @@ public:
std::pair<unsigned, unsigned> min_client_area_size;
};
- /// Swap buffers to display the next frame
- virtual void SwapBuffers() = 0;
-
/// Polls window events
virtual void PollEvents() = 0;
- /// Makes the graphics context current for the caller thread
- virtual void MakeCurrent() = 0;
-
- /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
- virtual void DoneCurrent() = 0;
+ /**
+ * Returns a GraphicsContext that the frontend provides that is shared with the emu window. This
+ * context can be used from other threads for background graphics computation. If the frontend
+ * is using a graphics backend that doesn't need anything specific to run on a different thread,
+ * then it can use a stubbed implemenation for GraphicsContext.
+ *
+ * If the return value is null, then the core should assume that the frontend cannot provide a
+ * Shared Context
+ */
+ virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const {
+ return nullptr;
+ }
/**
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
@@ -146,8 +169,7 @@ private:
* For the request to be honored, EmuWindow implementations will usually reimplement this
* function.
*/
- virtual void OnMinimalClientAreaChangeRequest(
- const std::pair<unsigned, unsigned>& minimal_size) {
+ virtual void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned>) {
// By default, ignore this request and do nothing.
}
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/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index dafb32aae..afa812598 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -1030,7 +1030,7 @@ static void Step() {
/// Tell the CPU if we hit a memory breakpoint.
bool IsMemoryBreak() {
- if (IsConnected()) {
+ if (!IsConnected()) {
return false;
}
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 455d1f346..fae54bcc7 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -39,10 +39,10 @@ struct CommandHeader {
union {
u32_le raw_low;
BitField<0, 16, CommandType> type;
- BitField<16, 4, u32_le> num_buf_x_descriptors;
- BitField<20, 4, u32_le> num_buf_a_descriptors;
- BitField<24, 4, u32_le> num_buf_b_descriptors;
- BitField<28, 4, u32_le> num_buf_w_descriptors;
+ BitField<16, 4, u32> num_buf_x_descriptors;
+ BitField<20, 4, u32> num_buf_a_descriptors;
+ BitField<24, 4, u32> num_buf_b_descriptors;
+ BitField<28, 4, u32> num_buf_w_descriptors;
};
enum class BufferDescriptorCFlag : u32 {
@@ -53,28 +53,28 @@ struct CommandHeader {
union {
u32_le raw_high;
- BitField<0, 10, u32_le> data_size;
+ BitField<0, 10, u32> data_size;
BitField<10, 4, BufferDescriptorCFlag> buf_c_descriptor_flags;
- BitField<31, 1, u32_le> enable_handle_descriptor;
+ BitField<31, 1, u32> enable_handle_descriptor;
};
};
static_assert(sizeof(CommandHeader) == 8, "CommandHeader size is incorrect");
union HandleDescriptorHeader {
u32_le raw_high;
- BitField<0, 1, u32_le> send_current_pid;
- BitField<1, 4, u32_le> num_handles_to_copy;
- BitField<5, 4, u32_le> num_handles_to_move;
+ BitField<0, 1, u32> send_current_pid;
+ BitField<1, 4, u32> num_handles_to_copy;
+ BitField<5, 4, u32> num_handles_to_move;
};
static_assert(sizeof(HandleDescriptorHeader) == 4, "HandleDescriptorHeader size is incorrect");
struct BufferDescriptorX {
union {
- BitField<0, 6, u32_le> counter_bits_0_5;
- BitField<6, 3, u32_le> address_bits_36_38;
- BitField<9, 3, u32_le> counter_bits_9_11;
- BitField<12, 4, u32_le> address_bits_32_35;
- BitField<16, 16, u32_le> size;
+ BitField<0, 6, u32> counter_bits_0_5;
+ BitField<6, 3, u32> address_bits_36_38;
+ BitField<9, 3, u32> counter_bits_9_11;
+ BitField<12, 4, u32> address_bits_32_35;
+ BitField<16, 16, u32> size;
};
u32_le address_bits_0_31;
@@ -103,10 +103,10 @@ struct BufferDescriptorABW {
u32_le address_bits_0_31;
union {
- BitField<0, 2, u32_le> flags;
- BitField<2, 3, u32_le> address_bits_36_38;
- BitField<24, 4, u32_le> size_bits_32_35;
- BitField<28, 4, u32_le> address_bits_32_35;
+ BitField<0, 2, u32> flags;
+ BitField<2, 3, u32> address_bits_36_38;
+ BitField<24, 4, u32> size_bits_32_35;
+ BitField<28, 4, u32> address_bits_32_35;
};
VAddr Address() const {
@@ -128,8 +128,8 @@ struct BufferDescriptorC {
u32_le address_bits_0_31;
union {
- BitField<0, 16, u32_le> address_bits_32_47;
- BitField<16, 16, u32_le> size;
+ BitField<0, 16, u32> address_bits_32_47;
+ BitField<16, 16, u32> size;
};
VAddr Address() const {
@@ -167,8 +167,8 @@ struct DomainMessageHeader {
struct {
union {
BitField<0, 8, CommandType> command;
- BitField<8, 8, u32_le> input_object_count;
- BitField<16, 16, u32_le> size;
+ BitField<8, 8, u32> input_object_count;
+ BitField<16, 16, u32> size;
};
u32_le object_id;
INSERT_PADDING_WORDS(2);
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 90f276ee8..5bb139483 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -19,9 +19,12 @@
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_session.h"
+#include "core/hle/result.h"
namespace IPC {
+constexpr ResultCode ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301};
+
class RequestHelperBase {
protected:
Kernel::HLERequestContext* context = nullptr;
@@ -136,10 +139,8 @@ public:
context->AddDomainObject(std::move(iface));
} else {
auto& kernel = Core::System::GetInstance().Kernel();
- auto sessions =
+ auto [server, client] =
Kernel::ServerSession::CreateSessionPair(kernel, iface->GetServiceName());
- auto server = std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions);
- auto client = std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions);
iface->ClientConnected(server);
context->AddMoveObject(std::move(client));
}
@@ -272,6 +273,20 @@ inline void ResponseBuilder::Push(u64 value) {
}
template <>
+inline void ResponseBuilder::Push(float value) {
+ u32 integral;
+ std::memcpy(&integral, &value, sizeof(u32));
+ Push(integral);
+}
+
+template <>
+inline void ResponseBuilder::Push(double value) {
+ u64 integral;
+ std::memcpy(&integral, &value, sizeof(u64));
+ Push(integral);
+}
+
+template <>
inline void ResponseBuilder::Push(bool value) {
Push(static_cast<u8>(value));
}
@@ -350,7 +365,7 @@ public:
template <class T>
std::shared_ptr<T> PopIpcInterface() {
ASSERT(context->Session()->IsDomain());
- ASSERT(context->GetDomainMessageHeader()->input_object_count > 0);
+ ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
return context->GetDomainRequestHandler<T>(Pop<u32>() - 1);
}
};
@@ -362,6 +377,11 @@ inline u32 RequestParser::Pop() {
return cmdbuf[index++];
}
+template <>
+inline s32 RequestParser::Pop() {
+ return static_cast<s32>(Pop<u32>());
+}
+
template <typename T>
void RequestParser::PopRaw(T& value) {
std::memcpy(&value, cmdbuf + index, sizeof(T));
@@ -393,11 +413,37 @@ inline u64 RequestParser::Pop() {
}
template <>
+inline s8 RequestParser::Pop() {
+ return static_cast<s8>(Pop<u8>());
+}
+
+template <>
+inline s16 RequestParser::Pop() {
+ return static_cast<s16>(Pop<u16>());
+}
+
+template <>
inline s64 RequestParser::Pop() {
return static_cast<s64>(Pop<u64>());
}
template <>
+inline float RequestParser::Pop() {
+ const u32 value = Pop<u32>();
+ float real;
+ std::memcpy(&real, &value, sizeof(real));
+ return real;
+}
+
+template <>
+inline double RequestParser::Pop() {
+ const u64 value = Pop<u64>();
+ double real;
+ std::memcpy(&real, &value, sizeof(real));
+ return real;
+}
+
+template <>
inline bool RequestParser::Pop() {
return Pop<u8>() != 0;
}
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 9780a7849..c8842410b 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -26,7 +26,7 @@ void WakeThreads(const std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_
// them all.
std::size_t last = waiting_threads.size();
if (num_to_wake > 0) {
- last = num_to_wake;
+ last = std::min(last, static_cast<std::size_t>(num_to_wake));
}
// Signal the waiting threads.
@@ -42,7 +42,21 @@ void WakeThreads(const std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_
AddressArbiter::AddressArbiter(Core::System& system) : system{system} {}
AddressArbiter::~AddressArbiter() = default;
-ResultCode AddressArbiter::SignalToAddress(VAddr address, s32 num_to_wake) {
+ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 value,
+ s32 num_to_wake) {
+ switch (type) {
+ case SignalType::Signal:
+ return SignalToAddressOnly(address, num_to_wake);
+ case SignalType::IncrementAndSignalIfEqual:
+ return IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
+ case SignalType::ModifyByWaitingCountAndSignalIfEqual:
+ return ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, num_to_wake);
+ default:
+ return ERR_INVALID_ENUM_VALUE;
+ }
+}
+
+ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
const std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
WakeThreads(waiting_threads, num_to_wake);
return RESULT_SUCCESS;
@@ -60,7 +74,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32
}
Memory::Write32(address, static_cast<u32>(value + 1));
- return SignalToAddress(address, num_to_wake);
+ return SignalToAddressOnly(address, num_to_wake);
}
ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
@@ -76,9 +90,9 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a
// Determine the modified value depending on the waiting count.
s32 updated_value;
if (waiting_threads.empty()) {
- updated_value = value - 1;
- } else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
updated_value = value + 1;
+ } else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
+ updated_value = value - 1;
} else {
updated_value = value;
}
@@ -92,6 +106,20 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a
return RESULT_SUCCESS;
}
+ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s32 value,
+ s64 timeout_ns) {
+ switch (type) {
+ case ArbitrationType::WaitIfLessThan:
+ return WaitForAddressIfLessThan(address, value, timeout_ns, false);
+ case ArbitrationType::DecrementAndWaitIfLessThan:
+ return WaitForAddressIfLessThan(address, value, timeout_ns, true);
+ case ArbitrationType::WaitIfEqual:
+ return WaitForAddressIfEqual(address, value, timeout_ns);
+ default:
+ return ERR_INVALID_ENUM_VALUE;
+ }
+}
+
ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
bool should_decrement) {
// Ensure that we can read the address.
@@ -113,7 +141,7 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6
return RESULT_TIMEOUT;
}
- return WaitForAddress(address, timeout);
+ return WaitForAddressImpl(address, timeout);
}
ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
@@ -130,10 +158,10 @@ ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 t
return RESULT_TIMEOUT;
}
- return WaitForAddress(address, timeout);
+ return WaitForAddressImpl(address, timeout);
}
-ResultCode AddressArbiter::WaitForAddress(VAddr address, s64 timeout) {
+ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) {
SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread();
current_thread->SetArbiterWaitAddress(address);
current_thread->SetStatus(ThreadStatus::WaitArb);
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index e0c36f2e3..ed0d0e69f 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -4,8 +4,10 @@
#pragma once
+#include <vector>
+
#include "common/common_types.h"
-#include "core/hle/kernel/address_arbiter.h"
+#include "core/hle/kernel/object.h"
union ResultCode;
@@ -40,8 +42,15 @@ public:
AddressArbiter(AddressArbiter&&) = default;
AddressArbiter& operator=(AddressArbiter&&) = delete;
+ /// Signals an address being waited on with a particular signaling type.
+ ResultCode SignalToAddress(VAddr address, SignalType type, s32 value, s32 num_to_wake);
+
+ /// Waits on an address with a particular arbitration type.
+ ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns);
+
+private:
/// Signals an address being waited on.
- ResultCode SignalToAddress(VAddr address, s32 num_to_wake);
+ ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake);
/// Signals an address being waited on and increments its value if equal to the value argument.
ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
@@ -59,9 +68,8 @@ public:
/// Waits on an address if the value passed is equal to the argument value.
ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
-private:
// Waits on the given address with a timeout in nanoseconds
- ResultCode WaitForAddress(VAddr address, s64 timeout);
+ ResultCode WaitForAddressImpl(VAddr address, s64 timeout);
// Gets the threads waiting on an address.
std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const;
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index d4c91d529..744b1697d 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <tuple>
-
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h"
@@ -31,17 +29,18 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
active_sessions++;
// Create a new session pair, let the created sessions inherit the parent port's HLE handler.
- auto sessions = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this);
+ auto [server, client] = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this);
- if (server_port->hle_handler)
- server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
- else
- server_port->pending_sessions.push_back(std::get<SharedPtr<ServerSession>>(sessions));
+ if (server_port->HasHLEHandler()) {
+ server_port->GetHLEHandler()->ClientConnected(server);
+ } else {
+ server_port->AppendPendingSession(server);
+ }
// Wake the threads waiting on the ServerPort
server_port->WakeupAllWaitingThreads();
- return MakeResult(std::get<SharedPtr<ClientSession>>(sessions));
+ return MakeResult(client);
}
void ClientPort::ConnectionClosed() {
diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h
index 6cd607206..4921ad4f0 100644
--- a/src/core/hle/kernel/client_port.h
+++ b/src/core/hle/kernel/client_port.h
@@ -25,7 +25,7 @@ public:
return name;
}
- static const HandleType HANDLE_TYPE = HandleType::ClientPort;
+ static constexpr HandleType HANDLE_TYPE = HandleType::ClientPort;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index 704e82824..c17baa50a 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -17,21 +17,11 @@ ClientSession::~ClientSession() {
// This destructor will be called automatically when the last ClientSession handle is closed by
// the emulated application.
- // Local references to ServerSession and SessionRequestHandler are necessary to guarantee they
+ // A local reference to the ServerSession is necessary to guarantee it
// will be kept alive until after ClientDisconnected() returns.
SharedPtr<ServerSession> server = parent->server;
if (server) {
- std::shared_ptr<SessionRequestHandler> hle_handler = server->hle_handler;
- if (hle_handler)
- hle_handler->ClientDisconnected(server);
-
- // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set
- // their WaitSynchronization result to 0xC920181A.
-
- // Clean up the list of client threads with pending requests, they are unneeded now that the
- // client endpoint is closed.
- server->pending_requesting_threads.clear();
- server->currently_handling = nullptr;
+ server->ClientDisconnected();
}
parent->client = nullptr;
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index 4c18de69c..09cdff588 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -29,21 +29,22 @@ public:
return name;
}
- static const HandleType HANDLE_TYPE = HandleType::ClientSession;
+ static constexpr HandleType HANDLE_TYPE = HandleType::ClientSession;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
ResultCode SendSyncRequest(SharedPtr<Thread> thread);
- std::string name; ///< Name of client port (optional)
+private:
+ explicit ClientSession(KernelCore& kernel);
+ ~ClientSession() override;
/// The parent session, which links to the server endpoint.
std::shared_ptr<Session> parent;
-private:
- explicit ClientSession(KernelCore& kernel);
- ~ClientSession() override;
+ /// Name of the client session (optional)
+ std::string name;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/code_set.cpp b/src/core/hle/kernel/code_set.cpp
new file mode 100644
index 000000000..1f434e9af
--- /dev/null
+++ b/src/core/hle/kernel/code_set.cpp
@@ -0,0 +1,12 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/code_set.h"
+
+namespace Kernel {
+
+CodeSet::CodeSet() = default;
+CodeSet::~CodeSet() = default;
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/code_set.h b/src/core/hle/kernel/code_set.h
new file mode 100644
index 000000000..879957dcb
--- /dev/null
+++ b/src/core/hle/kernel/code_set.h
@@ -0,0 +1,89 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <vector>
+
+#include "common/common_types.h"
+
+namespace Kernel {
+
+/**
+ * Represents executable data that may be loaded into a kernel process.
+ *
+ * A code set consists of three basic segments:
+ * - A code (AKA text) segment,
+ * - A read-only data segment (rodata)
+ * - A data segment
+ *
+ * The code segment is the portion of the object file that contains
+ * executable instructions.
+ *
+ * The read-only data segment in the portion of the object file that
+ * contains (as one would expect) read-only data, such as fixed constant
+ * values and data structures.
+ *
+ * The data segment is similar to the read-only data segment -- it contains
+ * variables and data structures that have predefined values, however,
+ * entities within this segment can be modified.
+ */
+struct CodeSet final {
+ /// A single segment within a code set.
+ struct Segment final {
+ /// The byte offset that this segment is located at.
+ std::size_t offset = 0;
+
+ /// The address to map this segment to.
+ VAddr addr = 0;
+
+ /// The size of this segment in bytes.
+ u32 size = 0;
+ };
+
+ explicit CodeSet();
+ ~CodeSet();
+
+ CodeSet(const CodeSet&) = delete;
+ CodeSet& operator=(const CodeSet&) = delete;
+
+ CodeSet(CodeSet&&) = default;
+ CodeSet& operator=(CodeSet&&) = default;
+
+ Segment& CodeSegment() {
+ return segments[0];
+ }
+
+ const Segment& CodeSegment() const {
+ return segments[0];
+ }
+
+ Segment& RODataSegment() {
+ return segments[1];
+ }
+
+ const Segment& RODataSegment() const {
+ return segments[1];
+ }
+
+ Segment& DataSegment() {
+ return segments[2];
+ }
+
+ const Segment& DataSegment() const {
+ return segments[2];
+ }
+
+ /// The overall data that backs this code set.
+ std::vector<u8> memory;
+
+ /// The segments that comprise this code set.
+ std::array<Segment, 3> segments;
+
+ /// The entry point address for this code set.
+ VAddr entrypoint = 0;
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 5dd855db8..f3da525d6 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -43,7 +43,7 @@ void SessionRequestHandler::ClientDisconnected(const SharedPtr<ServerSession>& s
}
SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
- SharedPtr<Thread> thread, const std::string& reason, u64 timeout, WakeupCallback&& callback,
+ const std::string& reason, u64 timeout, WakeupCallback&& callback,
SharedPtr<WritableEvent> writable_event) {
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
thread->SetWakeupCallback([context = *this, callback](
@@ -58,7 +58,7 @@ SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
auto& kernel = Core::System::GetInstance().Kernel();
if (!writable_event) {
// Create event if not provided
- const auto pair = WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ const auto pair = WritableEvent::CreateEventPair(kernel, ResetType::Automatic,
"HLE Pause Event: " + reason);
writable_event = pair.writable;
}
@@ -76,8 +76,9 @@ SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
return writable_event;
}
-HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
- : server_session(std::move(server_session)) {
+HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session,
+ SharedPtr<Thread> thread)
+ : server_session(std::move(server_session)), thread(std::move(thread)) {
cmd_buf[0] = 0;
}
@@ -86,7 +87,7 @@ HLERequestContext::~HLERequestContext() = default;
void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf,
bool incoming) {
IPC::RequestParser rp(src_cmdbuf);
- command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
+ command_header = rp.PopRaw<IPC::CommandHeader>();
if (command_header->type == IPC::CommandType::Close) {
// Close does not populate the rest of the IPC header
@@ -95,8 +96,7 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_
// If handle descriptor is present, add size of it
if (command_header->enable_handle_descriptor) {
- handle_descriptor_header =
- std::make_shared<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
+ handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>();
if (handle_descriptor_header->send_current_pid) {
rp.Skip(2, false);
}
@@ -140,16 +140,15 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_
// If this is an incoming message, only CommandType "Request" has a domain header
// All outgoing domain messages have the domain header, if only incoming has it
if (incoming || domain_message_header) {
- domain_message_header =
- std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
+ domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
} else {
- if (Session()->IsDomain())
+ if (Session()->IsDomain()) {
LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
+ }
}
}
- data_payload_header =
- std::make_shared<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
+ data_payload_header = rp.PopRaw<IPC::DataPayloadHeader>();
data_payload_offset = rp.GetCurrentOffset();
@@ -264,11 +263,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
// Write the domain objects to the command buffer, these go after the raw untranslated data.
// TODO(Subv): This completely ignores C buffers.
std::size_t domain_offset = size - domain_message_header->num_objects;
- auto& request_handlers = server_session->domain_request_handlers;
- for (auto& object : domain_objects) {
- request_handlers.emplace_back(object);
- dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size());
+ for (const auto& object : domain_objects) {
+ server_session->AppendDomainRequestHandler(object);
+ dst_cmdbuf[domain_offset++] =
+ static_cast<u32_le>(server_session->NumDomainRequestHandlers());
}
}
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 0107acea4..ccf5e56aa 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -6,6 +6,7 @@
#include <array>
#include <memory>
+#include <optional>
#include <string>
#include <type_traits>
#include <vector>
@@ -96,7 +97,7 @@ protected:
*/
class HLERequestContext {
public:
- explicit HLERequestContext(SharedPtr<ServerSession> session);
+ explicit HLERequestContext(SharedPtr<ServerSession> session, SharedPtr<Thread> thread);
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.
@@ -118,7 +119,6 @@ public:
/**
* Puts the specified guest thread to sleep until the returned event is signaled or until the
* specified timeout expires.
- * @param thread Thread to be put to sleep.
* @param reason Reason for pausing the thread, to be used for debugging purposes.
* @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback
* invoked with a Timeout reason.
@@ -129,8 +129,8 @@ public:
* created.
* @returns Event that when signaled will resume the thread and call the callback function.
*/
- SharedPtr<WritableEvent> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
- u64 timeout, WakeupCallback&& callback,
+ SharedPtr<WritableEvent> SleepClientThread(const std::string& reason, u64 timeout,
+ WakeupCallback&& callback,
SharedPtr<WritableEvent> writable_event = nullptr);
/// Populates this context with data from the requesting process/thread.
@@ -168,12 +168,12 @@ public:
return buffer_c_desciptors;
}
- const IPC::DomainMessageHeader* GetDomainMessageHeader() const {
- return domain_message_header.get();
+ const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
+ return domain_message_header.value();
}
bool HasDomainMessageHeader() const {
- return domain_message_header != nullptr;
+ return domain_message_header.has_value();
}
/// Helper function to read a buffer using the appropriate buffer descriptor
@@ -267,15 +267,16 @@ private:
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
SharedPtr<Kernel::ServerSession> server_session;
+ SharedPtr<Thread> thread;
// TODO(yuriks): Check common usage of this and optimize size accordingly
boost::container::small_vector<SharedPtr<Object>, 8> move_objects;
boost::container::small_vector<SharedPtr<Object>, 8> copy_objects;
boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;
- std::shared_ptr<IPC::CommandHeader> command_header;
- std::shared_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
- std::shared_ptr<IPC::DataPayloadHeader> data_payload_header;
- std::shared_ptr<IPC::DomainMessageHeader> domain_message_header;
+ std::optional<IPC::CommandHeader> command_header;
+ std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header;
+ std::optional<IPC::DataPayloadHeader> data_payload_header;
+ std::optional<IPC::DomainMessageHeader> domain_message_header;
std::vector<IPC::BufferDescriptorX> buffer_x_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 04ea9349e..757e5f21f 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -21,6 +21,7 @@
#include "core/hle/kernel/thread.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
+#include "core/memory.h"
namespace Kernel {
@@ -29,12 +30,12 @@ namespace Kernel {
* @param thread_handle The handle of the thread that's been awoken
* @param cycles_late The number of CPU cycles that have passed since the desired wakeup time
*/
-static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) {
+static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_late) {
const auto proper_handle = static_cast<Handle>(thread_handle);
const auto& system = Core::System::GetInstance();
// Lock the global kernel mutex when we enter the kernel HLE.
- std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
+ std::lock_guard lock{HLE::g_hle_lock};
SharedPtr<Thread> thread =
system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle);
@@ -45,8 +46,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
bool resume = true;
- if (thread->GetStatus() == ThreadStatus::WaitSynchAny ||
- thread->GetStatus() == ThreadStatus::WaitSynchAll ||
+ if (thread->GetStatus() == ThreadStatus::WaitSynch ||
thread->GetStatus() == ThreadStatus::WaitHLEEvent) {
// Remove the thread from each of its waiting objects' waitlists
for (const auto& object : thread->GetWaitObjects()) {
@@ -62,7 +62,8 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
if (thread->GetMutexWaitAddress() != 0 || thread->GetCondVarWaitAddress() != 0 ||
thread->GetWaitHandle() != 0) {
- ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
+ ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex ||
+ thread->GetStatus() == ThreadStatus::WaitCondVar);
thread->SetMutexWaitAddress(0);
thread->SetCondVarWaitAddress(0);
thread->SetWaitHandle(0);
@@ -87,7 +88,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
}
struct KernelCore::Impl {
- explicit Impl(Core::System& system) : address_arbiter{system}, system{system} {}
+ explicit Impl(Core::System& system) : system{system} {}
void Initialize(KernelCore& kernel) {
Shutdown();
@@ -114,7 +115,7 @@ struct KernelCore::Impl {
// Creates the default system resource limit
void InitializeSystemResourceLimit(KernelCore& kernel) {
- system_resource_limit = ResourceLimit::Create(kernel, "System");
+ system_resource_limit = ResourceLimit::Create(kernel);
// If setting the default system values fails, then something seriously wrong has occurred.
ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000)
@@ -138,8 +139,6 @@ struct KernelCore::Impl {
std::vector<SharedPtr<Process>> process_list;
Process* current_process = nullptr;
- Kernel::AddressArbiter address_arbiter;
-
SharedPtr<ResourceLimit> system_resource_limit;
Core::Timing::EventType* thread_wakeup_event_type = nullptr;
@@ -182,6 +181,12 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
void KernelCore::MakeCurrentProcess(Process* process) {
impl->current_process = process;
+
+ if (process == nullptr) {
+ return;
+ }
+
+ Memory::SetCurrentPageTable(*process);
}
Process* KernelCore::CurrentProcess() {
@@ -192,12 +197,8 @@ const Process* KernelCore::CurrentProcess() const {
return impl->current_process;
}
-AddressArbiter& KernelCore::AddressArbiter() {
- return impl->address_arbiter;
-}
-
-const AddressArbiter& KernelCore::AddressArbiter() const {
- return impl->address_arbiter;
+const std::vector<SharedPtr<Process>>& KernelCore::GetProcessList() const {
+ return impl->process_list;
}
void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 4d292aca9..6b8738599 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -8,9 +8,6 @@
#include <unordered_map>
#include "core/hle/kernel/object.h"
-template <typename T>
-class ResultVal;
-
namespace Core {
class System;
}
@@ -75,11 +72,8 @@ public:
/// Retrieves a const pointer to the current process.
const Process* CurrentProcess() const;
- /// Provides a reference to the kernel's address arbiter.
- Kernel::AddressArbiter& AddressArbiter();
-
- /// Provides a const reference to the kernel's address arbiter.
- const Kernel::AddressArbiter& AddressArbiter() const;
+ /// Retrieves the list of processes.
+ const std::vector<SharedPtr<Process>>& GetProcessList() const;
/// Adds a port to the named port table
void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 0743670ad..98e87313b 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <map>
#include <utility>
#include <vector>
@@ -10,8 +9,11 @@
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
#include "core/memory.h"
@@ -57,41 +59,47 @@ static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_t
}
}
-ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle holding_thread_handle,
+Mutex::Mutex(Core::System& system) : system{system} {}
+Mutex::~Mutex() = default;
+
+ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle) {
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
return ERR_INVALID_ADDRESS;
}
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
+ Thread* const current_thread = system.CurrentScheduler().GetCurrentThread();
SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
SharedPtr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle);
// TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
// thread.
- ASSERT(requesting_thread == GetCurrentThread());
+ ASSERT(requesting_thread == current_thread);
- u32 addr_value = Memory::Read32(address);
+ const u32 addr_value = Memory::Read32(address);
// If the mutex isn't being held, just return success.
if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
return RESULT_SUCCESS;
}
- if (holding_thread == nullptr)
+ if (holding_thread == nullptr) {
return ERR_INVALID_HANDLE;
+ }
// Wait until the mutex is released
- GetCurrentThread()->SetMutexWaitAddress(address);
- GetCurrentThread()->SetWaitHandle(requesting_thread_handle);
+ current_thread->SetMutexWaitAddress(address);
+ current_thread->SetWaitHandle(requesting_thread_handle);
- GetCurrentThread()->SetStatus(ThreadStatus::WaitMutex);
- GetCurrentThread()->InvalidateWakeupCallback();
+ current_thread->SetStatus(ThreadStatus::WaitMutex);
+ current_thread->InvalidateWakeupCallback();
// Update the lock holder thread's priority to prevent priority inversion.
- holding_thread->AddMutexWaiter(GetCurrentThread());
+ holding_thread->AddMutexWaiter(current_thread);
- Core::System::GetInstance().PrepareReschedule();
+ system.PrepareReschedule();
return RESULT_SUCCESS;
}
@@ -102,7 +110,8 @@ ResultCode Mutex::Release(VAddr address) {
return ERR_INVALID_ADDRESS;
}
- auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address);
+ auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
+ auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address);
// There are no more threads waiting for the mutex, release it completely.
if (thread == nullptr) {
@@ -111,7 +120,7 @@ ResultCode Mutex::Release(VAddr address) {
}
// Transfer the ownership of the mutex from the previous owner to the new one.
- TransferMutexOwnership(address, GetCurrentThread(), thread);
+ TransferMutexOwnership(address, current_thread, thread);
u32 mutex_value = thread->GetWaitHandle();
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 81e62d497..b904de2e8 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -5,32 +5,34 @@
#pragma once
#include "common/common_types.h"
-#include "core/hle/kernel/object.h"
union ResultCode;
-namespace Kernel {
+namespace Core {
+class System;
+}
-class HandleTable;
-class Thread;
+namespace Kernel {
class Mutex final {
public:
+ explicit Mutex(Core::System& system);
+ ~Mutex();
+
/// Flag that indicates that a mutex still has threads waiting for it.
static constexpr u32 MutexHasWaitersFlag = 0x40000000;
/// Mask of the bits in a mutex address value that contain the mutex owner.
static constexpr u32 MutexOwnerMask = 0xBFFFFFFF;
/// Attempts to acquire a mutex at the specified address.
- static ResultCode TryAcquire(HandleTable& handle_table, VAddr address,
- Handle holding_thread_handle, Handle requesting_thread_handle);
+ ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
+ Handle requesting_thread_handle);
/// Releases the mutex at the specified address.
- static ResultCode Release(VAddr address);
+ ResultCode Release(VAddr address);
private:
- Mutex() = default;
- ~Mutex() = default;
+ Core::System& system;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp
index 8870463d0..10431e94c 100644
--- a/src/core/hle/kernel/object.cpp
+++ b/src/core/hle/kernel/object.cpp
@@ -23,7 +23,7 @@ bool Object::IsWaitable() const {
case HandleType::Unknown:
case HandleType::WritableEvent:
case HandleType::SharedMemory:
- case HandleType::AddressArbiter:
+ case HandleType::TransferMemory:
case HandleType::ResourceLimit:
case HandleType::ClientPort:
case HandleType::ClientSession:
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
index 4c2505908..2821176a7 100644
--- a/src/core/hle/kernel/object.h
+++ b/src/core/hle/kernel/object.h
@@ -22,9 +22,9 @@ enum class HandleType : u32 {
WritableEvent,
ReadableEvent,
SharedMemory,
+ TransferMemory,
Thread,
Process,
- AddressArbiter,
ResourceLimit,
ClientPort,
ServerPort,
@@ -33,8 +33,8 @@ enum class HandleType : u32 {
};
enum class ResetType {
- OneShot, ///< Reset automatically on object acquisition
- Sticky, ///< Never reset automatically
+ Automatic, ///< Reset automatically on object acquisition
+ Manual, ///< Never reset automatically
};
class Object : NonCopyable {
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 8009150e0..2b81a8d4f 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -5,10 +5,12 @@
#include <algorithm>
#include <memory>
#include <random>
+#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/program_metadata.h"
+#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
@@ -26,36 +28,30 @@ namespace {
*
* @param owner_process The parent process for the main thread
* @param kernel The kernel instance to create the main thread under.
- * @param entry_point The address at which the thread should start execution
* @param priority The priority to give the main thread
*/
-void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
- // Setup page table so we can write to memory
- SetCurrentPageTable(&owner_process.VMManager().page_table);
-
- // Initialize new "main" thread
- const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
+void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
+ const auto& vm_manager = owner_process.VMManager();
+ const VAddr entry_point = vm_manager.GetCodeRegionBaseAddress();
+ const VAddr stack_top = vm_manager.GetTLSIORegionEndAddress();
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0,
owner_process.GetIdealCore(), stack_top, owner_process);
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
// Register 1 must be a handle to the main thread
- const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
- thread->SetGuestHandle(guest_handle);
- thread->GetContext().cpu_registers[1] = guest_handle;
+ const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
+ thread->GetContext().cpu_registers[1] = thread_handle;
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
thread->ResumeFromWait();
}
} // Anonymous namespace
-CodeSet::CodeSet() = default;
-CodeSet::~CodeSet() = default;
-
-SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
- SharedPtr<Process> process(new Process(kernel));
+SharedPtr<Process> Process::Create(Core::System& system, std::string name) {
+ auto& kernel = system.Kernel();
+ SharedPtr<Process> process(new Process(system));
process->name = std::move(name);
process->resource_limit = kernel.GetSystemResourceLimit();
process->status = ProcessStatus::Created;
@@ -76,6 +72,34 @@ 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);
+}
+
+void Process::UnregisterThread(const Thread* thread) {
+ thread_list.remove(thread);
+}
+
ResultCode Process::ClearSignalState() {
if (status == ProcessStatus::Exited) {
LOG_ERROR(Kernel, "called on a terminated process instance.");
@@ -108,20 +132,23 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
return handle_table.SetSize(capabilities.GetHandleTableSize());
}
-void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
+void Process::Run(s32 main_thread_priority, u64 stack_size) {
+ // The kernel always ensures that the given stack size is page aligned.
+ main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
+
// Allocate and map the main thread stack
// TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
// of the user address space.
+ const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size;
vm_manager
- .MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size,
- std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
- MemoryState::Stack)
+ .MapMemoryBlock(mapping_address, std::make_shared<std::vector<u8>>(main_thread_stack_size),
+ 0, main_thread_stack_size, MemoryState::Stack)
.Unwrap();
vm_manager.LogLayout();
ChangeStatus(ProcessStatus::Running);
- SetupMainThread(*this, kernel, entry_point, main_thread_priority);
+ SetupMainThread(*this, kernel, main_thread_priority);
}
void Process::PrepareForTermination() {
@@ -132,19 +159,17 @@ void Process::PrepareForTermination() {
if (thread->GetOwnerProcess() != this)
continue;
- if (thread == GetCurrentThread())
+ if (thread == system.CurrentScheduler().GetCurrentThread())
continue;
// TODO(Subv): When are the other running/ready threads terminated?
- ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynchAny ||
- thread->GetStatus() == ThreadStatus::WaitSynchAll,
+ ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynch,
"Exiting processes with non-waiting threads is currently unimplemented");
thread->Stop();
}
};
- const auto& system = Core::System::GetInstance();
stop_threads(system.Scheduler(0).GetThreadList());
stop_threads(system.Scheduler(1).GetThreadList());
stop_threads(system.Scheduler(2).GetThreadList());
@@ -212,35 +237,36 @@ void Process::FreeTLSSlot(VAddr tls_address) {
}
void Process::LoadModule(CodeSet module_, VAddr base_addr) {
- const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
+ const auto memory = std::make_shared<std::vector<u8>>(std::move(module_.memory));
+
+ const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
const auto vma = vm_manager
- .MapMemoryBlock(segment.addr + base_addr, module_.memory,
- segment.offset, segment.size, memory_state)
+ .MapMemoryBlock(segment.addr + base_addr, memory, segment.offset,
+ segment.size, memory_state)
.Unwrap();
vm_manager.Reprotect(vma, permissions);
};
// Map CodeSet segments
- MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
- MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
- MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
-
- // Clear instruction cache in CPU JIT
- Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
- Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
- Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
- Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
+ MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::Code);
+ MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeData);
+ MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData);
+
+ code_memory_size += module_.memory.size();
}
-Kernel::Process::Process(KernelCore& kernel) : WaitObject{kernel} {}
-Kernel::Process::~Process() {}
+Process::Process(Core::System& system)
+ : WaitObject{system.Kernel()}, vm_manager{system},
+ address_arbiter{system}, mutex{system}, system{system} {}
+
+Process::~Process() = default;
void Process::Acquire(Thread* thread) {
ASSERT_MSG(!ShouldWait(thread), "Object unavailable!");
}
-bool Process::ShouldWait(Thread* thread) const {
+bool Process::ShouldWait(const Thread* thread) const {
return !is_signaled;
}
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index dcc57ae9f..29e016983 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -7,17 +7,22 @@
#include <array>
#include <bitset>
#include <cstddef>
-#include <memory>
+#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"
+#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
+namespace Core {
+class System;
+}
+
namespace FileSys {
class ProgramMetadata;
}
@@ -28,13 +33,7 @@ class KernelCore;
class ResourceLimit;
class Thread;
-struct AddressMapping {
- // Address and size must be page-aligned
- VAddr address;
- u64 size;
- bool read_only;
- bool unk_flag;
-};
+struct CodeSet;
enum class MemoryRegion : u16 {
APPLICATION = 1,
@@ -60,46 +59,6 @@ enum class ProcessStatus {
DebugBreak,
};
-struct CodeSet final {
- struct Segment {
- std::size_t offset = 0;
- VAddr addr = 0;
- u32 size = 0;
- };
-
- explicit CodeSet();
- ~CodeSet();
-
- Segment& CodeSegment() {
- return segments[0];
- }
-
- const Segment& CodeSegment() const {
- return segments[0];
- }
-
- Segment& RODataSegment() {
- return segments[1];
- }
-
- const Segment& RODataSegment() const {
- return segments[1];
- }
-
- Segment& DataSegment() {
- return segments[2];
- }
-
- const Segment& DataSegment() const {
- return segments[2];
- }
-
- std::shared_ptr<std::vector<u8>> memory;
-
- std::array<Segment, 3> segments;
- VAddr entrypoint = 0;
-};
-
class Process final : public WaitObject {
public:
enum : u64 {
@@ -116,7 +75,7 @@ public:
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
- static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
+ static SharedPtr<Process> Create(Core::System& system, std::string name);
std::string GetTypeName() const override {
return "Process";
@@ -125,7 +84,7 @@ public:
return name;
}
- static const HandleType HANDLE_TYPE = HandleType::Process;
+ static constexpr HandleType HANDLE_TYPE = HandleType::Process;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
@@ -150,6 +109,26 @@ public:
return handle_table;
}
+ /// Gets a reference to the process' address arbiter.
+ AddressArbiter& GetAddressArbiter() {
+ return address_arbiter;
+ }
+
+ /// Gets a const reference to the process' address arbiter.
+ const AddressArbiter& GetAddressArbiter() const {
+ return address_arbiter;
+ }
+
+ /// Gets a reference to the process' mutex lock.
+ Mutex& GetMutex() {
+ return mutex;
+ }
+
+ /// Gets a const reference to the process' mutex lock
+ const Mutex& GetMutex() const {
+ return mutex;
+ }
+
/// Gets the current status of the process
ProcessStatus GetStatus() const {
return status;
@@ -207,6 +186,33 @@ 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;
+ }
+
+ /// Registers a thread as being created under this process,
+ /// adding it to this process' thread list.
+ void RegisterThread(const Thread* thread);
+
+ /// Unregisters a thread from this process, removing it
+ /// from this process' thread list.
+ void UnregisterThread(const Thread* thread);
+
/// Clears the signaled state of the process if and only if it's signaled.
///
/// @pre The process must not be already terminated. If this is called on a
@@ -229,9 +235,12 @@ public:
ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
/**
- * Applies address space changes and launches the process main thread.
+ * Starts the main application thread for this process.
+ *
+ * @param main_thread_priority The priority for the main thread.
+ * @param stack_size The stack size for the main thread in bytes.
*/
- void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size);
+ void Run(s32 main_thread_priority, u64 stack_size);
/**
* Prepares a process for termination by stopping all of its threads
@@ -251,11 +260,11 @@ public:
void FreeTLSSlot(VAddr tls_address);
private:
- explicit Process(KernelCore& kernel);
+ explicit Process(Core::System& system);
~Process() override;
/// Checks if the specified thread should wait until this process is available.
- bool ShouldWait(Thread* thread) const override;
+ bool ShouldWait(const Thread* thread) const override;
/// Acquires/locks this process for the specified thread if it's available.
void Acquire(Thread* thread) override;
@@ -268,6 +277,12 @@ private:
/// Memory manager for this process.
Kernel::VMManager vm_manager;
+ /// Size of the main thread's stack in bytes.
+ u64 main_thread_stack_size = 0;
+
+ /// Size of the loaded code memory in bytes.
+ u64 code_memory_size = 0;
+
/// Current status of the process
ProcessStatus status;
@@ -309,9 +324,24 @@ private:
/// Per-process handle table for storing created object handles in.
HandleTable handle_table;
+ /// Per-process address arbiter.
+ AddressArbiter address_arbiter;
+
+ /// The per-process mutex lock instance used for handling various
+ /// forms of services, such as lock arbitration, and condition
+ /// variable related facilities.
+ Mutex mutex;
+
/// Random values for svcGetInfo RandomEntropy
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
+ /// List of threads that are running with this process as their owner.
+ std::list<const Thread*> thread_list;
+
+ /// System context
+ Core::System& system;
+
+ /// Name of this process
std::string name;
};
diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp
index 0e5083f70..06463cd26 100644
--- a/src/core/hle/kernel/readable_event.cpp
+++ b/src/core/hle/kernel/readable_event.cpp
@@ -14,15 +14,16 @@ namespace Kernel {
ReadableEvent::ReadableEvent(KernelCore& kernel) : WaitObject{kernel} {}
ReadableEvent::~ReadableEvent() = default;
-bool ReadableEvent::ShouldWait(Thread* thread) const {
+bool ReadableEvent::ShouldWait(const Thread* thread) const {
return !signaled;
}
void ReadableEvent::Acquire(Thread* thread) {
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
- if (reset_type == ResetType::OneShot)
+ if (reset_type == ResetType::Automatic) {
signaled = false;
+ }
}
void ReadableEvent::Signal() {
diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h
index 77a9c362c..84215f572 100644
--- a/src/core/hle/kernel/readable_event.h
+++ b/src/core/hle/kernel/readable_event.h
@@ -31,12 +31,12 @@ public:
return reset_type;
}
- static const HandleType HANDLE_TYPE = HandleType::ReadableEvent;
+ static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
- bool ShouldWait(Thread* thread) const override;
+ bool ShouldWait(const Thread* thread) const override;
void Acquire(Thread* thread) override;
/// Unconditionally clears the readable event's state.
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp
index 2f9695005..173f69915 100644
--- a/src/core/hle/kernel/resource_limit.cpp
+++ b/src/core/hle/kernel/resource_limit.cpp
@@ -16,11 +16,8 @@ constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
ResourceLimit::~ResourceLimit() = default;
-SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string name) {
- SharedPtr<ResourceLimit> resource_limit(new ResourceLimit(kernel));
-
- resource_limit->name = std::move(name);
- return resource_limit;
+SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel) {
+ return new ResourceLimit(kernel);
}
s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h
index 59dc11c22..2613a6bb5 100644
--- a/src/core/hle/kernel/resource_limit.h
+++ b/src/core/hle/kernel/resource_limit.h
@@ -31,19 +31,17 @@ constexpr bool IsValidResourceType(ResourceType type) {
class ResourceLimit final : public Object {
public:
- /**
- * Creates a resource limit object.
- */
- static SharedPtr<ResourceLimit> Create(KernelCore& kernel, std::string name = "Unknown");
+ /// Creates a resource limit object.
+ static SharedPtr<ResourceLimit> Create(KernelCore& kernel);
std::string GetTypeName() const override {
return "ResourceLimit";
}
std::string GetName() const override {
- return name;
+ return GetTypeName();
}
- static const HandleType HANDLE_TYPE = HandleType::ResourceLimit;
+ static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
@@ -95,9 +93,6 @@ private:
ResourceArray limits{};
/// Current resource limit values.
ResourceArray values{};
-
- /// Name of resource limit object.
- std::string name;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 44f30d070..e8447b69a 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -19,7 +19,8 @@ namespace Kernel {
std::mutex Scheduler::scheduler_mutex;
-Scheduler::Scheduler(Core::ARM_Interface& cpu_core) : cpu_core(cpu_core) {}
+Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core)
+ : cpu_core{cpu_core}, system{system} {}
Scheduler::~Scheduler() {
for (auto& thread : thread_list) {
@@ -28,8 +29,8 @@ Scheduler::~Scheduler() {
}
bool Scheduler::HaveReadyThreads() const {
- std::lock_guard<std::mutex> lock(scheduler_mutex);
- return ready_queue.get_first() != nullptr;
+ std::lock_guard lock{scheduler_mutex};
+ return !ready_queue.empty();
}
Thread* Scheduler::GetCurrentThread() const {
@@ -45,23 +46,28 @@ Thread* Scheduler::PopNextReadyThread() {
Thread* thread = GetCurrentThread();
if (thread && thread->GetStatus() == ThreadStatus::Running) {
+ if (ready_queue.empty()) {
+ return thread;
+ }
// We have to do better than the current thread.
// This call returns null when that's not possible.
- next = ready_queue.pop_first_better(thread->GetPriority());
- if (!next) {
- // Otherwise just keep going with the current thread
+ next = ready_queue.front();
+ if (next == nullptr || next->GetPriority() >= thread->GetPriority()) {
next = thread;
}
} else {
- next = ready_queue.pop_first();
+ if (ready_queue.empty()) {
+ return nullptr;
+ }
+ next = ready_queue.front();
}
return next;
}
void Scheduler::SwitchContext(Thread* new_thread) {
- Thread* const previous_thread = GetCurrentThread();
- Process* const previous_process = Core::CurrentProcess();
+ Thread* previous_thread = GetCurrentThread();
+ Process* const previous_process = system.Kernel().CurrentProcess();
UpdateLastContextSwitchTime(previous_thread, previous_process);
@@ -74,7 +80,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
if (previous_thread->GetStatus() == ThreadStatus::Running) {
// This is only the case when a reschedule is triggered without the current thread
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
- ready_queue.push_front(previous_thread->GetPriority(), previous_thread);
+ ready_queue.add(previous_thread, previous_thread->GetPriority(), false);
previous_thread->SetStatus(ThreadStatus::Ready);
}
}
@@ -89,13 +95,12 @@ void Scheduler::SwitchContext(Thread* new_thread) {
current_thread = new_thread;
- ready_queue.remove(new_thread->GetPriority(), new_thread);
+ ready_queue.remove(new_thread, new_thread->GetPriority());
new_thread->SetStatus(ThreadStatus::Running);
auto* const thread_owner_process = current_thread->GetOwnerProcess();
if (previous_process != thread_owner_process) {
- Core::System::GetInstance().Kernel().MakeCurrentProcess(thread_owner_process);
- SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table);
+ system.Kernel().MakeCurrentProcess(thread_owner_process);
}
cpu_core.LoadContext(new_thread->GetContext());
@@ -111,7 +116,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
const u64 prev_switch_ticks = last_context_switch_time;
- const u64 most_recent_switch_ticks = Core::System::GetInstance().CoreTiming().GetTicks();
+ const u64 most_recent_switch_ticks = system.CoreTiming().GetTicks();
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
if (thread != nullptr) {
@@ -126,7 +131,7 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
}
void Scheduler::Reschedule() {
- std::lock_guard<std::mutex> lock(scheduler_mutex);
+ std::lock_guard lock{scheduler_mutex};
Thread* cur = GetCurrentThread();
Thread* next = PopNextReadyThread();
@@ -142,51 +147,54 @@ void Scheduler::Reschedule() {
SwitchContext(next);
}
-void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) {
- std::lock_guard<std::mutex> lock(scheduler_mutex);
+void Scheduler::AddThread(SharedPtr<Thread> thread) {
+ std::lock_guard lock{scheduler_mutex};
thread_list.push_back(std::move(thread));
- ready_queue.prepare(priority);
}
void Scheduler::RemoveThread(Thread* thread) {
- std::lock_guard<std::mutex> lock(scheduler_mutex);
+ std::lock_guard lock{scheduler_mutex};
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
}
void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
- std::lock_guard<std::mutex> lock(scheduler_mutex);
+ std::lock_guard lock{scheduler_mutex};
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
- ready_queue.push_back(priority, thread);
+ ready_queue.add(thread, priority);
}
void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
- std::lock_guard<std::mutex> lock(scheduler_mutex);
+ std::lock_guard lock{scheduler_mutex};
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
- ready_queue.remove(priority, thread);
+ ready_queue.remove(thread, priority);
}
void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
- std::lock_guard<std::mutex> lock(scheduler_mutex);
+ std::lock_guard lock{scheduler_mutex};
+ if (thread->GetPriority() == priority) {
+ return;
+ }
// If thread was ready, adjust queues
if (thread->GetStatus() == ThreadStatus::Ready)
- ready_queue.move(thread, thread->GetPriority(), priority);
- else
- ready_queue.prepare(priority);
+ ready_queue.adjust(thread, thread->GetPriority(), priority);
}
Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const {
- std::lock_guard<std::mutex> lock(scheduler_mutex);
+ std::lock_guard lock{scheduler_mutex};
const u32 mask = 1U << core;
- return ready_queue.get_first_filter([mask, maximum_priority](Thread const* thread) {
- return (thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority;
- });
+ for (auto* thread : ready_queue) {
+ if ((thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority) {
+ return thread;
+ }
+ }
+ return nullptr;
}
void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
@@ -198,8 +206,7 @@ void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
ASSERT(thread->GetPriority() < THREADPRIO_COUNT);
// Yield this thread -- sleep for zero time and force reschedule to different thread
- WaitCurrentThread_Sleep();
- GetCurrentThread()->WakeAfterDelay(0);
+ GetCurrentThread()->Sleep(0);
}
void Scheduler::YieldWithLoadBalancing(Thread* thread) {
@@ -214,8 +221,7 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) {
ASSERT(priority < THREADPRIO_COUNT);
// Sleep for zero time to be able to force reschedule to different thread
- WaitCurrentThread_Sleep();
- GetCurrentThread()->WakeAfterDelay(0);
+ GetCurrentThread()->Sleep(0);
Thread* suggested_thread = nullptr;
@@ -223,8 +229,7 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) {
// Take the first non-nullptr one
for (unsigned cur_core = 0; cur_core < Core::NUM_CPU_CORES; ++cur_core) {
const auto res =
- Core::System::GetInstance().CpuCore(cur_core).Scheduler().GetNextSuggestedThread(
- core, priority);
+ system.CpuCore(cur_core).Scheduler().GetNextSuggestedThread(core, priority);
// If scheduler provides a suggested thread
if (res != nullptr) {
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index 97ced4dfc..b29bf7be8 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -7,13 +7,14 @@
#include <mutex>
#include <vector>
#include "common/common_types.h"
-#include "common/thread_queue_list.h"
+#include "common/multi_level_queue.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
namespace Core {
class ARM_Interface;
-}
+class System;
+} // namespace Core
namespace Kernel {
@@ -21,7 +22,7 @@ class Process;
class Scheduler final {
public:
- explicit Scheduler(Core::ARM_Interface& cpu_core);
+ explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core);
~Scheduler();
/// Returns whether there are any threads that are ready to run.
@@ -37,7 +38,7 @@ public:
u64 GetLastContextSwitchTicks() const;
/// Adds a new thread to the scheduler
- void AddThread(SharedPtr<Thread> thread, u32 priority);
+ void AddThread(SharedPtr<Thread> thread);
/// Removes a thread from the scheduler
void RemoveThread(Thread* thread);
@@ -155,13 +156,14 @@ private:
std::vector<SharedPtr<Thread>> thread_list;
/// Lists only ready thread ids.
- Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
+ Common::MultiLevelQueue<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
SharedPtr<Thread> current_thread = nullptr;
Core::ARM_Interface& cpu_core;
u64 last_context_switch_time = 0;
+ Core::System& system;
static std::mutex scheduler_mutex;
};
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index d6ceeb2da..02e7c60e6 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -26,7 +26,11 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
return MakeResult(std::move(session));
}
-bool ServerPort::ShouldWait(Thread* thread) const {
+void ServerPort::AppendPendingSession(SharedPtr<ServerSession> pending_session) {
+ pending_sessions.push_back(std::move(pending_session));
+}
+
+bool ServerPort::ShouldWait(const Thread* thread) const {
// If there are no pending sessions, we wait until a new one is added.
return pending_sessions.empty();
}
@@ -35,9 +39,8 @@ void ServerPort::Acquire(Thread* thread) {
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
}
-std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair(
- KernelCore& kernel, u32 max_sessions, std::string name) {
-
+ServerPort::PortPair ServerPort::CreatePortPair(KernelCore& kernel, u32 max_sessions,
+ std::string name) {
SharedPtr<ServerPort> server_port(new ServerPort(kernel));
SharedPtr<ClientPort> client_port(new ClientPort(kernel));
@@ -47,7 +50,7 @@ std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortP
client_port->max_sessions = max_sessions;
client_port->active_sessions = 0;
- return std::make_tuple(std::move(server_port), std::move(client_port));
+ return std::make_pair(std::move(server_port), std::move(client_port));
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
index e52f8245f..dc88a1ebd 100644
--- a/src/core/hle/kernel/server_port.h
+++ b/src/core/hle/kernel/server_port.h
@@ -6,7 +6,7 @@
#include <memory>
#include <string>
-#include <tuple>
+#include <utility>
#include <vector>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
@@ -22,6 +22,9 @@ class SessionRequestHandler;
class ServerPort final : public WaitObject {
public:
+ using HLEHandler = std::shared_ptr<SessionRequestHandler>;
+ using PortPair = std::pair<SharedPtr<ServerPort>, SharedPtr<ClientPort>>;
+
/**
* Creates a pair of ServerPort and an associated ClientPort.
*
@@ -30,8 +33,8 @@ public:
* @param name Optional name of the ports
* @return The created port tuple
*/
- static std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> CreatePortPair(
- KernelCore& kernel, u32 max_sessions, std::string name = "UnknownPort");
+ static PortPair CreatePortPair(KernelCore& kernel, u32 max_sessions,
+ std::string name = "UnknownPort");
std::string GetTypeName() const override {
return "ServerPort";
@@ -40,7 +43,7 @@ public:
return name;
}
- static const HandleType HANDLE_TYPE = HandleType::ServerPort;
+ static constexpr HandleType HANDLE_TYPE = HandleType::ServerPort;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
@@ -51,29 +54,44 @@ public:
*/
ResultVal<SharedPtr<ServerSession>> Accept();
+ /// Whether or not this server port has an HLE handler available.
+ bool HasHLEHandler() const {
+ return hle_handler != nullptr;
+ }
+
+ /// Gets the HLE handler for this port.
+ HLEHandler GetHLEHandler() const {
+ return hle_handler;
+ }
+
/**
* Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
* will inherit a reference to this handler.
*/
- void SetHleHandler(std::shared_ptr<SessionRequestHandler> hle_handler_) {
+ void SetHleHandler(HLEHandler hle_handler_) {
hle_handler = std::move(hle_handler_);
}
- std::string name; ///< Name of port (optional)
+ /// Appends a ServerSession to the collection of ServerSessions
+ /// waiting to be accepted by this port.
+ void AppendPendingSession(SharedPtr<ServerSession> pending_session);
+
+ bool ShouldWait(const Thread* thread) const override;
+ void Acquire(Thread* thread) override;
+
+private:
+ explicit ServerPort(KernelCore& kernel);
+ ~ServerPort() override;
/// ServerSessions waiting to be accepted by the port
std::vector<SharedPtr<ServerSession>> pending_sessions;
/// This session's HLE request handler template (optional)
/// ServerSessions created from this port inherit a reference to this handler.
- std::shared_ptr<SessionRequestHandler> hle_handler;
-
- bool ShouldWait(Thread* thread) const override;
- void Acquire(Thread* thread) override;
+ HLEHandler hle_handler;
-private:
- explicit ServerPort(KernelCore& kernel);
- ~ServerPort() override;
+ /// Name of the port (optional)
+ std::string name;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 027434f92..30b2bfb5a 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -28,11 +28,9 @@ ServerSession::~ServerSession() {
// the emulated application.
// Decrease the port's connection count.
- if (parent->port)
+ if (parent->port) {
parent->port->ConnectionClosed();
-
- // TODO(Subv): Wake up all the ClientSession's waiting threads and set
- // the SendSyncRequest result to 0xC920181A.
+ }
parent->server = nullptr;
}
@@ -46,7 +44,7 @@ ResultVal<SharedPtr<ServerSession>> ServerSession::Create(KernelCore& kernel, st
return MakeResult(std::move(server_session));
}
-bool ServerSession::ShouldWait(Thread* thread) const {
+bool ServerSession::ShouldWait(const Thread* thread) const {
// Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
if (parent->client == nullptr)
return false;
@@ -63,42 +61,68 @@ void ServerSession::Acquire(Thread* thread) {
pending_requesting_threads.pop_back();
}
+void ServerSession::ClientDisconnected() {
+ // We keep a shared pointer to the hle handler to keep it alive throughout
+ // the call to ClientDisconnected, as ClientDisconnected invalidates the
+ // hle_handler member itself during the course of the function executing.
+ std::shared_ptr<SessionRequestHandler> handler = hle_handler;
+ if (handler) {
+ // Note that after this returns, this server session's hle_handler is
+ // invalidated (set to null).
+ handler->ClientDisconnected(this);
+ }
+
+ // Clean up the list of client threads with pending requests, they are unneeded now that the
+ // client endpoint is closed.
+ pending_requesting_threads.clear();
+ currently_handling = nullptr;
+}
+
+void ServerSession::AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler) {
+ domain_request_handlers.push_back(std::move(handler));
+}
+
+std::size_t ServerSession::NumDomainRequestHandlers() const {
+ return domain_request_handlers.size();
+}
+
ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
- auto* const domain_message_header = context.GetDomainMessageHeader();
- if (domain_message_header) {
- // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
- context.SetDomainRequestHandlers(domain_request_handlers);
-
- // If there is a DomainMessageHeader, then this is CommandType "Request"
- const u32 object_id{context.GetDomainMessageHeader()->object_id};
- switch (domain_message_header->command) {
- case IPC::DomainMessageHeader::CommandType::SendMessage:
- if (object_id > domain_request_handlers.size()) {
- LOG_CRITICAL(IPC,
- "object_id {} is too big! This probably means a recent service call "
- "to {} needed to return a new interface!",
- object_id, name);
- UNREACHABLE();
- return RESULT_SUCCESS; // Ignore error if asserts are off
- }
- return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
-
- case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
- LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
-
- domain_request_handlers[object_id - 1] = nullptr;
-
- IPC::ResponseBuilder rb{context, 2};
- rb.Push(RESULT_SUCCESS);
- return RESULT_SUCCESS;
- }
+ if (!context.HasDomainMessageHeader()) {
+ return RESULT_SUCCESS;
+ }
+
+ // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
+ context.SetDomainRequestHandlers(domain_request_handlers);
+
+ // If there is a DomainMessageHeader, then this is CommandType "Request"
+ const auto& domain_message_header = context.GetDomainMessageHeader();
+ const u32 object_id{domain_message_header.object_id};
+ switch (domain_message_header.command) {
+ case IPC::DomainMessageHeader::CommandType::SendMessage:
+ if (object_id > domain_request_handlers.size()) {
+ LOG_CRITICAL(IPC,
+ "object_id {} is too big! This probably means a recent service call "
+ "to {} needed to return a new interface!",
+ object_id, name);
+ UNREACHABLE();
+ return RESULT_SUCCESS; // Ignore error if asserts are off
}
+ return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
+
+ case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
+ LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
- LOG_CRITICAL(IPC, "Unknown domain command={}",
- static_cast<int>(domain_message_header->command.Value()));
- ASSERT(false);
+ domain_request_handlers[object_id - 1] = nullptr;
+
+ IPC::ResponseBuilder rb{context, 2};
+ rb.Push(RESULT_SUCCESS);
+ return RESULT_SUCCESS;
+ }
}
+ LOG_CRITICAL(IPC, "Unknown domain command={}",
+ static_cast<int>(domain_message_header.command.Value()));
+ ASSERT(false);
return RESULT_SUCCESS;
}
@@ -106,7 +130,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
// The ServerSession received a sync request, this means that there's new data available
// from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
// similar.
- Kernel::HLERequestContext context(this);
+ Kernel::HLERequestContext context(this, thread);
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
@@ -175,6 +199,6 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(KernelCore& kernel,
client_session->parent = parent;
server_session->parent = parent;
- return std::make_tuple(std::move(server_session), std::move(client_session));
+ return std::make_pair(std::move(server_session), std::move(client_session));
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index e0e9d64c8..738df30f8 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -6,6 +6,7 @@
#include <memory>
#include <string>
+#include <utility>
#include <vector>
#include "core/hle/kernel/object.h"
@@ -41,12 +42,24 @@ public:
return "ServerSession";
}
- static const HandleType HANDLE_TYPE = HandleType::ServerSession;
+ std::string GetName() const override {
+ return name;
+ }
+
+ static constexpr HandleType HANDLE_TYPE = HandleType::ServerSession;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
- using SessionPair = std::tuple<SharedPtr<ServerSession>, SharedPtr<ClientSession>>;
+ Session* GetParent() {
+ return parent.get();
+ }
+
+ const Session* GetParent() const {
+ return parent.get();
+ }
+
+ using SessionPair = std::pair<SharedPtr<ServerSession>, SharedPtr<ClientSession>>;
/**
* Creates a pair of ServerSession and an associated ClientSession.
@@ -74,27 +87,20 @@ public:
*/
ResultCode HandleSyncRequest(SharedPtr<Thread> thread);
- bool ShouldWait(Thread* thread) const override;
+ bool ShouldWait(const Thread* thread) const override;
void Acquire(Thread* thread) override;
- std::string name; ///< The name of this session (optional)
- std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint.
- std::shared_ptr<SessionRequestHandler>
- hle_handler; ///< This session's HLE request handler (applicable when not a domain)
+ /// Called when a client disconnection occurs.
+ void ClientDisconnected();
- /// This is the list of domain request handlers (after conversion to a domain)
- std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
+ /// Adds a new domain request handler to the collection of request handlers within
+ /// this ServerSession instance.
+ void AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler);
- /// List of threads that are pending a response after a sync request. This list is processed in
- /// a LIFO manner, thus, the last request will be dispatched first.
- /// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test.
- std::vector<SharedPtr<Thread>> pending_requesting_threads;
-
- /// Thread whose request is currently being handled. A request is considered "handled" when a
- /// response is sent via svcReplyAndReceive.
- /// TODO(Subv): Find a better name for this.
- SharedPtr<Thread> currently_handling;
+ /// Retrieves the total number of domain request handlers that have been
+ /// appended to this ServerSession instance.
+ std::size_t NumDomainRequestHandlers() const;
/// Returns true if the session has been converted to a domain, otherwise False
bool IsDomain() const {
@@ -129,8 +135,30 @@ private:
/// object handle.
ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context);
+ /// The parent session, which links to the client endpoint.
+ std::shared_ptr<Session> parent;
+
+ /// This session's HLE request handler (applicable when not a domain)
+ std::shared_ptr<SessionRequestHandler> hle_handler;
+
+ /// This is the list of domain request handlers (after conversion to a domain)
+ std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
+
+ /// List of threads that are pending a response after a sync request. This list is processed in
+ /// a LIFO manner, thus, the last request will be dispatched first.
+ /// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test.
+ std::vector<SharedPtr<Thread>> pending_requesting_threads;
+
+ /// Thread whose request is currently being handled. A request is considered "handled" when a
+ /// response is sent via svcReplyAndReceive.
+ /// TODO(Subv): Find a better name for this.
+ SharedPtr<Thread> currently_handling;
+
/// When set to True, converts the session to a domain at the end of the command
bool convert_to_domain{};
+
+ /// The name of this session (optional)
+ std::string name;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index 22d0c1dd5..f15c5ee36 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -6,11 +6,9 @@
#include "common/assert.h"
#include "common/logging/log.h"
-#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/shared_memory.h"
-#include "core/memory.h"
namespace Kernel {
@@ -34,8 +32,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_
shared_memory->backing_block_offset = 0;
// Refresh the address mappings for the current process.
- if (Core::CurrentProcess() != nullptr) {
- Core::CurrentProcess()->VMManager().RefreshMemoryBlockMappings(
+ if (kernel.CurrentProcess() != nullptr) {
+ kernel.CurrentProcess()->VMManager().RefreshMemoryBlockMappings(
shared_memory->backing_block.get());
}
} else {
@@ -120,7 +118,15 @@ ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermi
ConvertPermissions(permissions));
}
-ResultCode SharedMemory::Unmap(Process& target_process, VAddr address) {
+ResultCode SharedMemory::Unmap(Process& target_process, VAddr address, u64 unmap_size) {
+ if (unmap_size != size) {
+ LOG_ERROR(Kernel,
+ "Invalid size passed to Unmap. Size must be equal to the size of the "
+ "memory managed. Shared memory size=0x{:016X}, Unmap size=0x{:016X}",
+ size, unmap_size);
+ return ERR_INVALID_SIZE;
+ }
+
// TODO(Subv): Verify what happens if the application tries to unmap an address that is not
// mapped to a SharedMemory.
return target_process.VMManager().UnmapRange(address, size);
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index dab2a6bea..c2b6155e1 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -76,7 +76,7 @@ public:
return name;
}
- static const HandleType HANDLE_TYPE = HandleType::SharedMemory;
+ static constexpr HandleType HANDLE_TYPE = HandleType::SharedMemory;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
@@ -104,11 +104,17 @@ public:
/**
* Unmaps a shared memory block from the specified address in system memory
+ *
* @param target_process Process from which to unmap the memory block.
- * @param address Address in system memory where the shared memory block is mapped
+ * @param address Address in system memory where the shared memory block is mapped.
+ * @param unmap_size The amount of bytes to unmap from this shared memory instance.
+ *
* @return Result code of the unmap operation
+ *
+ * @pre The given size to unmap must be the same size as the amount of memory managed by
+ * the SharedMemory instance itself, otherwise ERR_INVALID_SIZE will be returned.
*/
- ResultCode Unmap(Process& target_process, VAddr address);
+ ResultCode Unmap(Process& target_process, VAddr address, u64 unmap_size);
/**
* Gets a pointer to the shared memory block
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 7f5c0cc86..f9c606bc5 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -32,6 +32,7 @@
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_wrap.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/transfer_memory.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
@@ -130,16 +131,15 @@ enum class ResourceLimitValueType {
LimitValue,
};
-ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_type,
- ResourceLimitValueType value_type) {
+ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit,
+ u32 resource_type, ResourceLimitValueType value_type) {
const auto type = static_cast<ResourceType>(resource_type);
if (!IsValidResourceType(type)) {
LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
return ERR_INVALID_ENUM_VALUE;
}
- const auto& kernel = Core::System::GetInstance().Kernel();
- const auto* const current_process = kernel.CurrentProcess();
+ const auto* const current_process = system.Kernel().CurrentProcess();
ASSERT(current_process != nullptr);
const auto resource_limit_object =
@@ -159,7 +159,7 @@ ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_ty
} // Anonymous namespace
/// Set the process heap to a given Size. It can both extend and shrink the heap.
-static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
+static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) {
LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size);
// Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB.
@@ -174,11 +174,8 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
return ERR_INVALID_SIZE;
}
- auto& vm_manager = Core::CurrentProcess()->VMManager();
- const VAddr heap_base = vm_manager.GetHeapRegionBaseAddress();
- const auto alloc_result =
- vm_manager.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite);
-
+ auto& vm_manager = system.Kernel().CurrentProcess()->VMManager();
+ const auto alloc_result = vm_manager.SetHeapSize(heap_size);
if (alloc_result.Failed()) {
return alloc_result.Code();
}
@@ -187,7 +184,7 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
return RESULT_SUCCESS;
}
-static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
+static ResultCode SetMemoryPermission(Core::System& system, VAddr addr, u64 size, u32 prot) {
LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot);
if (!Common::Is4KBAligned(addr)) {
@@ -219,7 +216,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
return ERR_INVALID_MEMORY_PERMISSIONS;
}
- auto* const current_process = Core::CurrentProcess();
+ auto* const current_process = system.Kernel().CurrentProcess();
auto& vm_manager = current_process->VMManager();
if (!vm_manager.IsWithinAddressSpace(addr, size)) {
@@ -244,7 +241,8 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
return vm_manager.ReprotectRange(addr, size, converted_permissions);
}
-static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attribute) {
+static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
+ u32 attribute) {
LOG_DEBUG(Kernel_SVC,
"called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
size, mask, attribute);
@@ -282,7 +280,7 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr
return ERR_INVALID_COMBINATION;
}
- auto& vm_manager = Core::CurrentProcess()->VMManager();
+ auto& vm_manager = system.Kernel().CurrentProcess()->VMManager();
if (!vm_manager.IsWithinAddressSpace(address, size)) {
LOG_ERROR(Kernel_SVC,
"Given address (0x{:016X}) is outside the bounds of the address space.", address);
@@ -293,11 +291,11 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr
}
/// Maps a memory range into a different range.
-static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
+static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
- auto& vm_manager = Core::CurrentProcess()->VMManager();
+ auto& vm_manager = system.Kernel().CurrentProcess()->VMManager();
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
if (result.IsError()) {
@@ -308,11 +306,11 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
}
/// Unmaps a region that was previously mapped with svcMapMemory
-static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
+static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
- auto& vm_manager = Core::CurrentProcess()->VMManager();
+ auto& vm_manager = system.Kernel().CurrentProcess()->VMManager();
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
if (result.IsError()) {
@@ -323,7 +321,8 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
}
/// Connect to an OS service given the port name, returns the handle to the port to out
-static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) {
+static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
+ VAddr port_name_address) {
if (!Memory::IsValidVirtualAddress(port_name_address)) {
LOG_ERROR(Kernel_SVC,
"Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
@@ -342,8 +341,8 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
- auto& kernel = Core::System::GetInstance().Kernel();
- auto it = kernel.FindNamedPort(port_name);
+ auto& kernel = system.Kernel();
+ const auto it = kernel.FindNamedPort(port_name);
if (!kernel.IsValidNamedPort(it)) {
LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
return ERR_NOT_FOUND;
@@ -355,14 +354,14 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
CASCADE_RESULT(client_session, client_port->Connect());
// Return the client session
- auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
CASCADE_RESULT(*out_handle, handle_table.Create(client_session));
return RESULT_SUCCESS;
}
/// Makes a blocking IPC call to an OS service.
-static ResultCode SendSyncRequest(Handle handle) {
- const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle);
if (!session) {
LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
@@ -371,18 +370,18 @@ static ResultCode SendSyncRequest(Handle handle) {
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
- Core::System::GetInstance().PrepareReschedule();
+ system.PrepareReschedule();
// TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server
// responds and cause a reschedule.
- return session->SendSyncRequest(GetCurrentThread());
+ return session->SendSyncRequest(system.CurrentScheduler().GetCurrentThread());
}
/// Get the ID for the specified thread.
-static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) {
+static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
- const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle);
@@ -394,10 +393,10 @@ static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) {
}
/// Gets the ID of the specified process or a specified thread's owning process.
-static ResultCode GetProcessId(u64* process_id, Handle handle) {
+static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
- const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const SharedPtr<Process> process = handle_table.Get<Process>(handle);
if (process) {
*process_id = process->GetProcessID();
@@ -425,7 +424,7 @@ static ResultCode GetProcessId(u64* process_id, Handle handle) {
/// Default thread wakeup callback for WaitSynchronization
static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, std::size_t index) {
- ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
+ ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch);
if (reason == ThreadWakeupReason::Timeout) {
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
@@ -439,8 +438,8 @@ static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thr
};
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
-static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 handle_count,
- s64 nano_seconds) {
+static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address,
+ u64 handle_count, s64 nano_seconds) {
LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}",
handles_address, handle_count, nano_seconds);
@@ -459,11 +458,11 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
return ERR_OUT_OF_RANGE;
}
- auto* const thread = GetCurrentThread();
+ auto* const thread = system.CurrentScheduler().GetCurrentThread();
using ObjectPtr = Thread::ThreadWaitObjects::value_type;
Thread::ThreadWaitObjects objects(handle_count);
- const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
for (u64 i = 0; i < handle_count; ++i) {
const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle));
@@ -503,38 +502,36 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
}
thread->SetWaitObjects(std::move(objects));
- thread->SetStatus(ThreadStatus::WaitSynchAny);
+ thread->SetStatus(ThreadStatus::WaitSynch);
// Create an event to wake the thread up after the specified nanosecond delay has passed
thread->WakeAfterDelay(nano_seconds);
thread->SetWakeupCallback(DefaultThreadWakeupCallback);
- Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
+ system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
return RESULT_TIMEOUT;
}
/// Resumes a thread waiting on WaitSynchronization
-static ResultCode CancelSynchronization(Handle thread_handle) {
+static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
- const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
- const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
+ SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
thread_handle);
return ERR_INVALID_HANDLE;
}
- ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
- thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
- thread->ResumeFromWait();
+ thread->CancelWait();
return RESULT_SUCCESS;
}
/// Attempts to locks a mutex, creating it if it does not already exist
-static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
- Handle requesting_thread_handle) {
+static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle,
+ VAddr mutex_addr, Handle requesting_thread_handle) {
LOG_TRACE(Kernel_SVC,
"called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, "
"requesting_current_thread_handle=0x{:08X}",
@@ -551,13 +548,13 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
return ERR_INVALID_ADDRESS;
}
- auto& handle_table = Core::CurrentProcess()->GetHandleTable();
- return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
- requesting_thread_handle);
+ auto* const current_process = system.Kernel().CurrentProcess();
+ return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle,
+ requesting_thread_handle);
}
/// Unlock a mutex
-static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
+static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) {
LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr);
if (Memory::IsKernelVirtualAddress(mutex_addr)) {
@@ -571,7 +568,8 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
return ERR_INVALID_ADDRESS;
}
- return Mutex::Release(mutex_addr);
+ auto* const current_process = system.Kernel().CurrentProcess();
+ return current_process->GetMutex().Release(mutex_addr);
}
enum class BreakType : u32 {
@@ -593,7 +591,7 @@ struct BreakReason {
};
/// Break program execution
-static void Break(u32 reason, u64 info1, u64 info2) {
+static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
BreakReason break_reason{reason};
bool has_dumped_buffer{};
@@ -671,22 +669,24 @@ static void Break(u32 reason, u64 info1, u64 info2) {
Debug_Emulated,
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
+
handle_debug_buffer(info1, info2);
- Core::System::GetInstance()
- .ArmInterface(static_cast<std::size_t>(GetCurrentThread()->GetProcessorID()))
- .LogBacktrace();
+
+ auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
+ const auto thread_processor_id = current_thread->GetProcessorID();
+ system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
ASSERT(false);
- Core::CurrentProcess()->PrepareForTermination();
+ system.Kernel().CurrentProcess()->PrepareForTermination();
// Kill the current thread
- GetCurrentThread()->Stop();
- Core::System::GetInstance().PrepareReschedule();
+ current_thread->Stop();
+ system.PrepareReschedule();
}
}
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
-static void OutputDebugString(VAddr address, u64 len) {
+static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) {
if (len == 0) {
return;
}
@@ -697,7 +697,8 @@ static void OutputDebugString(VAddr address, u64 len) {
}
/// Gets system/memory information for the current process
-static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) {
+static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 handle,
+ u64 info_sub_id) {
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
info_sub_id, handle);
@@ -709,13 +710,13 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
MapRegionSize = 3,
HeapRegionBaseAddr = 4,
HeapRegionSize = 5,
- TotalMemoryUsage = 6,
- TotalHeapUsage = 7,
+ TotalPhysicalMemoryAvailable = 6,
+ TotalPhysicalMemoryUsed = 7,
IsCurrentProcessBeingDebugged = 8,
RegisterResourceLimit = 9,
IdleTickCount = 10,
RandomEntropy = 11,
- PerformanceCounter = 0xF0000002,
+ ThreadTickCount = 0xF0000002,
// 2.0.0+
ASLRRegionBaseAddr = 12,
ASLRRegionSize = 13,
@@ -729,7 +730,9 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
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);
@@ -745,17 +748,20 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
case GetInfoType::ASLRRegionSize:
case GetInfoType::NewMapRegionBaseAddr:
case GetInfoType::NewMapRegionSize:
- case GetInfoType::TotalMemoryUsage:
- case GetInfoType::TotalHeapUsage:
+ 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;
}
- const auto& current_process_handle_table = Core::CurrentProcess()->GetHandleTable();
+ const auto& current_process_handle_table =
+ system.Kernel().CurrentProcess()->GetHandleTable();
const auto process = current_process_handle_table.Get<Process>(static_cast<Handle>(handle));
if (!process) {
return ERR_INVALID_HANDLE;
@@ -802,12 +808,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
*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::TotalHeapUsage:
- *result = process->VMManager().GetTotalHeapUsage();
+ case GetInfoType::TotalPhysicalMemoryUsed:
+ *result = process->GetTotalPhysicalMemoryUsed();
return RESULT_SUCCESS;
case GetInfoType::IsVirtualAddressMemoryEnabled:
@@ -824,6 +830,14 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
*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;
}
@@ -845,7 +859,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
return ERR_INVALID_COMBINATION;
}
- Process* const current_process = Core::CurrentProcess();
+ Process* const current_process = system.Kernel().CurrentProcess();
HandleTable& handle_table = current_process->GetHandleTable();
const auto resource_limit = current_process->GetResourceLimit();
if (!resource_limit) {
@@ -876,7 +890,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
return ERR_INVALID_COMBINATION;
}
- *result = Core::CurrentProcess()->GetRandomEntropy(info_sub_id);
+ *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id);
return RESULT_SUCCESS;
case GetInfoType::PrivilegedProcessId:
@@ -893,15 +907,14 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
return ERR_INVALID_COMBINATION;
}
- const auto thread =
- Core::CurrentProcess()->GetHandleTable().Get<Thread>(static_cast<Handle>(handle));
+ const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<Thread>(
+ static_cast<Handle>(handle));
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
static_cast<Handle>(handle));
return ERR_INVALID_HANDLE;
}
- const auto& system = Core::System::GetInstance();
const auto& core_timing = system.CoreTiming();
const auto& scheduler = system.CurrentScheduler();
const auto* const current_thread = scheduler.GetCurrentThread();
@@ -928,13 +941,13 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
}
/// Sets the thread activity
-static ResultCode SetThreadActivity(Handle handle, u32 activity) {
+static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
if (activity > static_cast<u32>(ThreadActivity::Paused)) {
return ERR_INVALID_ENUM_VALUE;
}
- const auto* current_process = Core::CurrentProcess();
+ const auto* current_process = system.Kernel().CurrentProcess();
const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
@@ -951,7 +964,7 @@ static ResultCode SetThreadActivity(Handle handle, u32 activity) {
return ERR_INVALID_HANDLE;
}
- if (thread == GetCurrentThread()) {
+ if (thread == system.CurrentScheduler().GetCurrentThread()) {
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
return ERR_BUSY;
}
@@ -961,10 +974,10 @@ static ResultCode SetThreadActivity(Handle handle, u32 activity) {
}
/// Gets the thread context
-static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
+static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle);
- const auto* current_process = Core::CurrentProcess();
+ const auto* current_process = system.Kernel().CurrentProcess();
const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
@@ -981,7 +994,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
return ERR_INVALID_HANDLE;
}
- if (thread == GetCurrentThread()) {
+ if (thread == system.CurrentScheduler().GetCurrentThread()) {
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
return ERR_BUSY;
}
@@ -1002,10 +1015,10 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
}
/// Gets the priority for the specified thread
-static ResultCode GetThreadPriority(u32* priority, Handle handle) {
+static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) {
LOG_TRACE(Kernel_SVC, "called");
- const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
@@ -1017,7 +1030,7 @@ static ResultCode GetThreadPriority(u32* priority, Handle handle) {
}
/// Sets the priority for the specified thread
-static ResultCode SetThreadPriority(Handle handle, u32 priority) {
+static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) {
LOG_TRACE(Kernel_SVC, "called");
if (priority > THREADPRIO_LOWEST) {
@@ -1028,7 +1041,7 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
return ERR_INVALID_THREAD_PRIORITY;
}
- const auto* const current_process = Core::CurrentProcess();
+ const auto* const current_process = system.Kernel().CurrentProcess();
SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
if (!thread) {
@@ -1038,18 +1051,18 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
thread->SetPriority(priority);
- Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
+ system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
return RESULT_SUCCESS;
}
/// Get which CPU core is executing the current thread
-static u32 GetCurrentProcessorNumber() {
+static u32 GetCurrentProcessorNumber(Core::System& system) {
LOG_TRACE(Kernel_SVC, "called");
- return GetCurrentThread()->GetProcessorID();
+ return system.CurrentScheduler().GetCurrentThread()->GetProcessorID();
}
-static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size,
- u32 permissions) {
+static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr,
+ u64 size, u32 permissions) {
LOG_TRACE(Kernel_SVC,
"called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
shared_memory_handle, addr, size, permissions);
@@ -1083,7 +1096,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
return ERR_INVALID_MEMORY_PERMISSIONS;
}
- auto* const current_process = Core::CurrentProcess();
+ auto* const current_process = system.Kernel().CurrentProcess();
auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
if (!shared_memory) {
LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",
@@ -1101,7 +1114,8 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare);
}
-static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
+static ResultCode UnmapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr,
+ u64 size) {
LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}",
shared_memory_handle, addr, size);
@@ -1126,7 +1140,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
return ERR_INVALID_ADDRESS_STATE;
}
- auto* const current_process = Core::CurrentProcess();
+ auto* const current_process = system.Kernel().CurrentProcess();
auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
if (!shared_memory) {
LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",
@@ -1141,13 +1155,14 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
return ERR_INVALID_MEMORY_RANGE;
}
- return shared_memory->Unmap(*current_process, addr);
+ return shared_memory->Unmap(*current_process, addr, size);
}
-static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address,
- Handle process_handle, VAddr address) {
+static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
+ VAddr page_info_address, Handle process_handle,
+ VAddr address) {
LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
- const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
if (!process) {
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
@@ -1173,20 +1188,156 @@ static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_
return RESULT_SUCCESS;
}
-static ResultCode QueryMemory(VAddr memory_info_address, VAddr page_info_address,
- VAddr query_address) {
+static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address,
+ VAddr page_info_address, VAddr query_address) {
LOG_TRACE(Kernel_SVC,
"called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, "
"query_address=0x{:016X}",
memory_info_address, page_info_address, query_address);
- return QueryProcessMemory(memory_info_address, page_info_address, CurrentProcess,
+ return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess,
query_address);
}
+static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
+ u64 src_address, u64 size) {
+ LOG_DEBUG(Kernel_SVC,
+ "called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
+ "src_address=0x{:016X}, size=0x{:016X}",
+ process_handle, dst_address, src_address, size);
+
+ if (!Common::Is4KBAligned(src_address)) {
+ LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
+ src_address);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (!Common::Is4KBAligned(dst_address)) {
+ LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
+ dst_address);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (size == 0 || !Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!IsValidAddressRange(dst_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination address range overflows the address space (dst_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ dst_address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (!IsValidAddressRange(src_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source address range overflows the address space (src_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ src_address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
+ auto process = handle_table.Get<Process>(process_handle);
+ if (!process) {
+ LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
+ process_handle);
+ return ERR_INVALID_HANDLE;
+ }
+
+ auto& vm_manager = process->VMManager();
+ if (!vm_manager.IsWithinAddressSpace(src_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source address range is not within the address space (src_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ src_address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (!vm_manager.IsWithinASLRRegion(dst_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ dst_address, size);
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ return vm_manager.MapCodeMemory(dst_address, src_address, size);
+}
+
+static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle,
+ u64 dst_address, u64 src_address, u64 size) {
+ LOG_DEBUG(Kernel_SVC,
+ "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
+ "size=0x{:016X}",
+ process_handle, dst_address, src_address, size);
+
+ if (!Common::Is4KBAligned(dst_address)) {
+ LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
+ dst_address);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (!Common::Is4KBAligned(src_address)) {
+ LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
+ src_address);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (size == 0 || Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!IsValidAddressRange(dst_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination address range overflows the address space (dst_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ dst_address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (!IsValidAddressRange(src_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source address range overflows the address space (src_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ src_address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
+ auto process = handle_table.Get<Process>(process_handle);
+ if (!process) {
+ LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
+ process_handle);
+ return ERR_INVALID_HANDLE;
+ }
+
+ auto& vm_manager = process->VMManager();
+ if (!vm_manager.IsWithinAddressSpace(src_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source address range is not within the address space (src_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ src_address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (!vm_manager.IsWithinASLRRegion(dst_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ dst_address, size);
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ return vm_manager.UnmapCodeMemory(dst_address, src_address, size);
+}
+
/// Exits the current process
-static void ExitProcess() {
- auto* current_process = Core::CurrentProcess();
+static void ExitProcess(Core::System& system) {
+ auto* current_process = system.Kernel().CurrentProcess();
LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
@@ -1195,20 +1346,20 @@ static void ExitProcess() {
current_process->PrepareForTermination();
// Kill the current thread
- GetCurrentThread()->Stop();
+ system.CurrentScheduler().GetCurrentThread()->Stop();
- Core::System::GetInstance().PrepareReschedule();
+ system.PrepareReschedule();
}
/// Creates a new thread
-static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top,
- u32 priority, s32 processor_id) {
- LOG_TRACE(Kernel_SVC,
+static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
+ VAddr stack_top, u32 priority, s32 processor_id) {
+ LOG_DEBUG(Kernel_SVC,
"called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, "
"threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
entry_point, arg, stack_top, priority, processor_id, *out_handle);
- auto* const current_process = Core::CurrentProcess();
+ auto* const current_process = system.Kernel().CurrentProcess();
if (processor_id == THREADPROCESSORID_IDEAL) {
// Set the target CPU to the one specified by the process.
@@ -1239,31 +1390,33 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
return ERR_INVALID_THREAD_PRIORITY;
}
- const std::string name = fmt::format("thread-{:X}", entry_point);
- auto& kernel = Core::System::GetInstance().Kernel();
+ auto& kernel = system.Kernel();
CASCADE_RESULT(SharedPtr<Thread> thread,
- Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
+ Thread::Create(kernel, "", entry_point, priority, arg, processor_id, stack_top,
*current_process));
- const auto new_guest_handle = current_process->GetHandleTable().Create(thread);
- if (new_guest_handle.Failed()) {
+ const auto new_thread_handle = current_process->GetHandleTable().Create(thread);
+ if (new_thread_handle.Failed()) {
LOG_ERROR(Kernel_SVC, "Failed to create handle with error=0x{:X}",
- new_guest_handle.Code().raw);
- return new_guest_handle.Code();
+ new_thread_handle.Code().raw);
+ return new_thread_handle.Code();
}
- thread->SetGuestHandle(*new_guest_handle);
- *out_handle = *new_guest_handle;
+ *out_handle = *new_thread_handle;
+
+ // Set the thread name for debugging purposes.
+ thread->SetName(
+ fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle));
- Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
+ system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
return RESULT_SUCCESS;
}
/// Starts the thread for the provided handle
-static ResultCode StartThread(Handle thread_handle) {
- LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
+static ResultCode StartThread(Core::System& system, Handle thread_handle) {
+ LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
- const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
@@ -1276,23 +1429,25 @@ static ResultCode StartThread(Handle thread_handle) {
thread->ResumeFromWait();
if (thread->GetStatus() == ThreadStatus::Ready) {
- Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
+ system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
}
return RESULT_SUCCESS;
}
/// Called when a thread exits
-static void ExitThread() {
- LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC());
+static void ExitThread(Core::System& system) {
+ LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
- ExitCurrentThread();
- Core::System::GetInstance().PrepareReschedule();
+ auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
+ current_thread->Stop();
+ system.CurrentScheduler().RemoveThread(current_thread);
+ system.PrepareReschedule();
}
/// Sleep the current thread
-static void SleepThread(s64 nanoseconds) {
- LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
+static void SleepThread(Core::System& system, s64 nanoseconds) {
+ LOG_DEBUG(Kernel_SVC, "called nanoseconds={}", nanoseconds);
enum class SleepType : s64 {
YieldWithoutLoadBalancing = 0,
@@ -1300,72 +1455,91 @@ static void SleepThread(s64 nanoseconds) {
YieldAndWaitForLoadBalancing = -2,
};
+ auto& scheduler = system.CurrentScheduler();
+ auto* const current_thread = scheduler.GetCurrentThread();
+
if (nanoseconds <= 0) {
- auto& scheduler{Core::System::GetInstance().CurrentScheduler()};
switch (static_cast<SleepType>(nanoseconds)) {
case SleepType::YieldWithoutLoadBalancing:
- scheduler.YieldWithoutLoadBalancing(GetCurrentThread());
+ scheduler.YieldWithoutLoadBalancing(current_thread);
break;
case SleepType::YieldWithLoadBalancing:
- scheduler.YieldWithLoadBalancing(GetCurrentThread());
+ scheduler.YieldWithLoadBalancing(current_thread);
break;
case SleepType::YieldAndWaitForLoadBalancing:
- scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread());
+ scheduler.YieldAndWaitForLoadBalancing(current_thread);
break;
default:
UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
}
} else {
- // Sleep current thread and check for next thread to schedule
- WaitCurrentThread_Sleep();
-
- // Create an event to wake the thread up after the specified nanosecond delay has passed
- GetCurrentThread()->WakeAfterDelay(nanoseconds);
+ current_thread->Sleep(nanoseconds);
}
// Reschedule all CPU cores
- for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i)
- Core::System::GetInstance().CpuCore(i).PrepareReschedule();
+ for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) {
+ system.CpuCore(i).PrepareReschedule();
+ }
}
/// Wait process wide key atomic
-static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_variable_addr,
- Handle thread_handle, s64 nano_seconds) {
+static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_addr,
+ VAddr condition_variable_addr, Handle thread_handle,
+ s64 nano_seconds) {
LOG_TRACE(
Kernel_SVC,
"called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
- const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ if (Memory::IsKernelVirtualAddress(mutex_addr)) {
+ LOG_ERROR(
+ Kernel_SVC,
+ "Given mutex address must not be within the kernel address space. address=0x{:016X}",
+ mutex_addr);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (!Common::IsWordAligned(mutex_addr)) {
+ LOG_ERROR(Kernel_SVC, "Given mutex address must be word-aligned. address=0x{:016X}",
+ mutex_addr);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ auto* const current_process = system.Kernel().CurrentProcess();
+ const auto& handle_table = current_process->GetHandleTable();
SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
ASSERT(thread);
- CASCADE_CODE(Mutex::Release(mutex_addr));
+ const auto release_result = current_process->GetMutex().Release(mutex_addr);
+ if (release_result.IsError()) {
+ return release_result;
+ }
- SharedPtr<Thread> current_thread = GetCurrentThread();
+ SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread();
current_thread->SetCondVarWaitAddress(condition_variable_addr);
current_thread->SetMutexWaitAddress(mutex_addr);
current_thread->SetWaitHandle(thread_handle);
- current_thread->SetStatus(ThreadStatus::WaitMutex);
+ current_thread->SetStatus(ThreadStatus::WaitCondVar);
current_thread->InvalidateWakeupCallback();
current_thread->WakeAfterDelay(nano_seconds);
// Note: Deliberately don't attempt to inherit the lock owner's priority.
- Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
+ system.CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
return RESULT_SUCCESS;
}
/// Signal process wide key
-static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) {
+static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr,
+ s32 target) {
LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}",
condition_variable_addr, target);
- const auto RetrieveWaitingThreads = [](std::size_t core_index,
- std::vector<SharedPtr<Thread>>& waiting_threads,
- VAddr condvar_addr) {
- const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
+ const auto RetrieveWaitingThreads = [&system](std::size_t core_index,
+ std::vector<SharedPtr<Thread>>& waiting_threads,
+ VAddr condvar_addr) {
+ const auto& scheduler = system.Scheduler(core_index);
const auto& thread_list = scheduler.GetThreadList();
for (const auto& thread : thread_list) {
@@ -1390,10 +1564,10 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
// them all.
std::size_t last = waiting_threads.size();
if (target != -1)
- last = target;
+ last = std::min(waiting_threads.size(), static_cast<std::size_t>(target));
// If there are no threads waiting on this condition variable, just exit
- if (last > waiting_threads.size())
+ if (last == 0)
return RESULT_SUCCESS;
for (std::size_t index = 0; index < last; ++index) {
@@ -1401,9 +1575,11 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr);
- std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex();
+ // liberate Cond Var Thread.
+ thread->SetCondVarWaitAddress(0);
- auto& monitor = Core::System::GetInstance().Monitor();
+ const std::size_t current_core = system.CurrentCoreIndex();
+ auto& monitor = system.Monitor();
// Atomically read the value of the mutex.
u32 mutex_val = 0;
@@ -1419,10 +1595,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
}
} while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(),
thread->GetWaitHandle()));
-
if (mutex_val == 0) {
// We were able to acquire the mutex, resume this thread.
- ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
+ ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar);
thread->ResumeFromWait();
auto* const lock_owner = thread->GetLockOwner();
@@ -1432,8 +1607,8 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
thread->SetLockOwner(nullptr);
thread->SetMutexWaitAddress(0);
- thread->SetCondVarWaitAddress(0);
thread->SetWaitHandle(0);
+ system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
} else {
// Atomically signal that the mutex now has a waiting thread.
do {
@@ -1449,15 +1624,14 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
// The mutex is already owned by some other thread, make this thread wait on it.
const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
- const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
auto owner = handle_table.Get<Thread>(owner_handle);
ASSERT(owner);
- ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
+ ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar);
thread->InvalidateWakeupCallback();
+ thread->SetStatus(ThreadStatus::WaitMutex);
owner->AddMutexWaiter(thread);
-
- Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
}
}
@@ -1465,75 +1639,56 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
}
// Wait for an address (via Address Arbiter)
-static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout) {
+static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value,
+ s64 timeout) {
LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}",
address, type, value, timeout);
+
// If the passed address is a kernel virtual address, return invalid memory state.
if (Memory::IsKernelVirtualAddress(address)) {
LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
return ERR_INVALID_ADDRESS_STATE;
}
+
// If the address is not properly aligned to 4 bytes, return invalid address.
if (!Common::IsWordAligned(address)) {
LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address);
return ERR_INVALID_ADDRESS;
}
- auto& address_arbiter = Core::System::GetInstance().Kernel().AddressArbiter();
- switch (static_cast<AddressArbiter::ArbitrationType>(type)) {
- case AddressArbiter::ArbitrationType::WaitIfLessThan:
- return address_arbiter.WaitForAddressIfLessThan(address, value, timeout, false);
- case AddressArbiter::ArbitrationType::DecrementAndWaitIfLessThan:
- return address_arbiter.WaitForAddressIfLessThan(address, value, timeout, true);
- case AddressArbiter::ArbitrationType::WaitIfEqual:
- return address_arbiter.WaitForAddressIfEqual(address, value, timeout);
- default:
- LOG_ERROR(Kernel_SVC,
- "Invalid arbitration type, expected WaitIfLessThan, DecrementAndWaitIfLessThan "
- "or WaitIfEqual but got {}",
- type);
- return ERR_INVALID_ENUM_VALUE;
- }
+ const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type);
+ auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter();
+ return address_arbiter.WaitForAddress(address, arbitration_type, value, timeout);
}
// Signals to an address (via Address Arbiter)
-static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to_wake) {
+static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value,
+ s32 num_to_wake) {
LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}",
address, type, value, num_to_wake);
+
// If the passed address is a kernel virtual address, return invalid memory state.
if (Memory::IsKernelVirtualAddress(address)) {
LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
return ERR_INVALID_ADDRESS_STATE;
}
+
// If the address is not properly aligned to 4 bytes, return invalid address.
if (!Common::IsWordAligned(address)) {
LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address);
return ERR_INVALID_ADDRESS;
}
- auto& address_arbiter = Core::System::GetInstance().Kernel().AddressArbiter();
- switch (static_cast<AddressArbiter::SignalType>(type)) {
- case AddressArbiter::SignalType::Signal:
- return address_arbiter.SignalToAddress(address, num_to_wake);
- case AddressArbiter::SignalType::IncrementAndSignalIfEqual:
- return address_arbiter.IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
- case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual:
- return address_arbiter.ModifyByWaitingCountAndSignalToAddressIfEqual(address, value,
- num_to_wake);
- default:
- LOG_ERROR(Kernel_SVC,
- "Invalid signal type, expected Signal, IncrementAndSignalIfEqual "
- "or ModifyByWaitingCountAndSignalIfEqual but got {}",
- type);
- return ERR_INVALID_ENUM_VALUE;
- }
+ const auto signal_type = static_cast<AddressArbiter::SignalType>(type);
+ auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter();
+ return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake);
}
/// This returns the total CPU ticks elapsed since the CPU was powered-on
-static u64 GetSystemTick() {
+static u64 GetSystemTick(Core::System& system) {
LOG_TRACE(Kernel_SVC, "called");
- auto& core_timing = Core::System::GetInstance().CoreTiming();
+ auto& core_timing = system.CoreTiming();
const u64 result{core_timing.GetTicks()};
// Advance time to defeat dumb games that busy-wait for the frame to end.
@@ -1543,18 +1698,18 @@ static u64 GetSystemTick() {
}
/// Close a handle
-static ResultCode CloseHandle(Handle handle) {
+static ResultCode CloseHandle(Core::System& system, Handle handle) {
LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
- auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
return handle_table.Close(handle);
}
/// Clears the signaled state of an event or process.
-static ResultCode ResetSignal(Handle handle) {
+static ResultCode ResetSignal(Core::System& system, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
- const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
auto event = handle_table.Get<ReadableEvent>(handle);
if (event) {
@@ -1571,7 +1726,8 @@ static ResultCode ResetSignal(Handle handle) {
}
/// Creates a TransferMemory object
-static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) {
+static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size,
+ u32 permissions) {
LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
permissions);
@@ -1599,19 +1755,129 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
return ERR_INVALID_MEMORY_PERMISSIONS;
}
- auto& kernel = Core::System::GetInstance().Kernel();
- auto process = kernel.CurrentProcess();
- auto& handle_table = process->GetHandleTable();
- const auto shared_mem_handle = SharedMemory::Create(kernel, process, size, perms, perms, addr);
+ auto& kernel = system.Kernel();
+ auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms);
- CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
+ auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
+ const auto result = handle_table.Create(std::move(transfer_mem_handle));
+ if (result.Failed()) {
+ return result.Code();
+ }
+
+ *handle = *result;
return RESULT_SUCCESS;
}
-static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) {
+static ResultCode MapTransferMemory(Core::System& system, Handle handle, VAddr address, u64 size,
+ u32 permission_raw) {
+ LOG_DEBUG(Kernel_SVC,
+ "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}, permissions=0x{:08X}",
+ handle, address, size, permission_raw);
+
+ if (!Common::Is4KBAligned(address)) {
+ LOG_ERROR(Kernel_SVC, "Transfer memory addresses must be 4KB aligned (size=0x{:016X}).",
+ address);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (size == 0 || !Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Transfer memory sizes must be 4KB aligned and not be zero (size=0x{:016X}).",
+ size);
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!IsValidAddressRange(address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Given address and size overflows the 64-bit range (address=0x{:016X}, "
+ "size=0x{:016X}).",
+ address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ const auto permissions = static_cast<MemoryPermission>(permission_raw);
+ if (permissions != MemoryPermission::None && permissions != MemoryPermission::Read &&
+ permissions != MemoryPermission::ReadWrite) {
+ LOG_ERROR(Kernel_SVC, "Invalid transfer memory permissions given (permissions=0x{:08X}).",
+ permission_raw);
+ return ERR_INVALID_STATE;
+ }
+
+ const auto& kernel = system.Kernel();
+ const auto* const current_process = kernel.CurrentProcess();
+ const auto& handle_table = current_process->GetHandleTable();
+
+ auto transfer_memory = handle_table.Get<TransferMemory>(handle);
+ if (!transfer_memory) {
+ LOG_ERROR(Kernel_SVC, "Nonexistent transfer memory handle given (handle=0x{:08X}).",
+ handle);
+ return ERR_INVALID_HANDLE;
+ }
+
+ if (!current_process->VMManager().IsWithinASLRRegion(address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Given address and size don't fully fit within the ASLR region "
+ "(address=0x{:016X}, size=0x{:016X}).",
+ address, size);
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ return transfer_memory->MapMemory(address, size, permissions);
+}
+
+static ResultCode UnmapTransferMemory(Core::System& system, Handle handle, VAddr address,
+ u64 size) {
+ LOG_DEBUG(Kernel_SVC, "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}", handle,
+ address, size);
+
+ if (!Common::Is4KBAligned(address)) {
+ LOG_ERROR(Kernel_SVC, "Transfer memory addresses must be 4KB aligned (size=0x{:016X}).",
+ address);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (size == 0 || !Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Transfer memory sizes must be 4KB aligned and not be zero (size=0x{:016X}).",
+ size);
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!IsValidAddressRange(address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Given address and size overflows the 64-bit range (address=0x{:016X}, "
+ "size=0x{:016X}).",
+ address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ const auto& kernel = system.Kernel();
+ const auto* const current_process = kernel.CurrentProcess();
+ const auto& handle_table = current_process->GetHandleTable();
+
+ auto transfer_memory = handle_table.Get<TransferMemory>(handle);
+ if (!transfer_memory) {
+ LOG_ERROR(Kernel_SVC, "Nonexistent transfer memory handle given (handle=0x{:08X}).",
+ handle);
+ return ERR_INVALID_HANDLE;
+ }
+
+ if (!current_process->VMManager().IsWithinASLRRegion(address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Given address and size don't fully fit within the ASLR region "
+ "(address=0x{:016X}, size=0x{:016X}).",
+ address, size);
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ return transfer_memory->UnmapMemory(address, size);
+}
+
+static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core,
+ u64* mask) {
LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
- const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
@@ -1625,57 +1891,65 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask)
return RESULT_SUCCESS;
}
-static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
- LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:016X}, core=0x{:X}", thread_handle,
- mask, core);
+static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core,
+ u64 affinity_mask) {
+ LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}",
+ thread_handle, core, affinity_mask);
- const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
- const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
- if (!thread) {
- LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
- thread_handle);
- return ERR_INVALID_HANDLE;
- }
+ const auto* const current_process = system.Kernel().CurrentProcess();
if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
- const u8 ideal_cpu_core = thread->GetOwnerProcess()->GetIdealCore();
+ const u8 ideal_cpu_core = current_process->GetIdealCore();
ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
// Set the target CPU to the ideal core specified by the process.
core = ideal_cpu_core;
- mask = 1ULL << core;
- }
-
- if (mask == 0) {
- LOG_ERROR(Kernel_SVC, "Mask is 0");
- return ERR_INVALID_COMBINATION;
- }
+ affinity_mask = 1ULL << core;
+ } else {
+ const u64 core_mask = current_process->GetCoreMask();
+
+ if ((core_mask | affinity_mask) != core_mask) {
+ LOG_ERROR(
+ Kernel_SVC,
+ "Invalid processor ID specified (core_mask=0x{:08X}, affinity_mask=0x{:016X})",
+ core_mask, affinity_mask);
+ return ERR_INVALID_PROCESSOR_ID;
+ }
- /// This value is used to only change the affinity mask without changing the current ideal core.
- static constexpr u32 OnlyChangeMask = static_cast<u32>(-3);
+ if (affinity_mask == 0) {
+ LOG_ERROR(Kernel_SVC, "Specfified affinity mask is zero.");
+ return ERR_INVALID_COMBINATION;
+ }
- if (core == OnlyChangeMask) {
- core = thread->GetIdealCore();
- } else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
- LOG_ERROR(Kernel_SVC, "Invalid core specified, got {}", core);
- return ERR_INVALID_PROCESSOR_ID;
+ if (core < Core::NUM_CPU_CORES) {
+ if ((affinity_mask & (1ULL << core)) == 0) {
+ LOG_ERROR(Kernel_SVC,
+ "Core is not enabled for the current mask, core={}, mask={:016X}", core,
+ affinity_mask);
+ return ERR_INVALID_COMBINATION;
+ }
+ } else if (core != static_cast<u32>(THREADPROCESSORID_DONT_CARE) &&
+ core != static_cast<u32>(THREADPROCESSORID_DONT_UPDATE)) {
+ LOG_ERROR(Kernel_SVC, "Invalid processor ID specified (core={}).", core);
+ return ERR_INVALID_PROCESSOR_ID;
+ }
}
- // Error out if the input core isn't enabled in the input mask.
- if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
- LOG_ERROR(Kernel_SVC, "Core is not enabled for the current mask, core={}, mask={:016X}",
- core, mask);
- return ERR_INVALID_COMBINATION;
+ const auto& handle_table = current_process->GetHandleTable();
+ const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
+ if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
+ thread_handle);
+ return ERR_INVALID_HANDLE;
}
- thread->ChangeCore(core, mask);
-
+ thread->ChangeCore(core, affinity_mask);
return RESULT_SUCCESS;
}
-static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permissions,
- u32 remote_permissions) {
+static ResultCode CreateSharedMemory(Core::System& system, Handle* handle, u64 size,
+ u32 local_permissions, u32 remote_permissions) {
LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size,
local_permissions, remote_permissions);
if (size == 0) {
@@ -1711,7 +1985,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
return ERR_INVALID_MEMORY_PERMISSIONS;
}
- auto& kernel = Core::System::GetInstance().Kernel();
+ auto& kernel = system.Kernel();
auto process = kernel.CurrentProcess();
auto& handle_table = process->GetHandleTable();
auto shared_mem_handle = SharedMemory::Create(kernel, process, size, local_perms, remote_perms);
@@ -1720,12 +1994,12 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
return RESULT_SUCCESS;
}
-static ResultCode CreateEvent(Handle* write_handle, Handle* read_handle) {
+static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) {
LOG_DEBUG(Kernel_SVC, "called");
- auto& kernel = Core::System::GetInstance().Kernel();
+ auto& kernel = system.Kernel();
const auto [readable_event, writable_event] =
- WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent");
+ WritableEvent::CreateEventPair(kernel, ResetType::Manual, "CreateEvent");
HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable();
@@ -1748,10 +2022,10 @@ static ResultCode CreateEvent(Handle* write_handle, Handle* read_handle) {
return RESULT_SUCCESS;
}
-static ResultCode ClearEvent(Handle handle) {
+static ResultCode ClearEvent(Core::System& system, Handle handle) {
LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
- const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
auto writable_event = handle_table.Get<WritableEvent>(handle);
if (writable_event) {
@@ -1769,10 +2043,10 @@ static ResultCode ClearEvent(Handle handle) {
return ERR_INVALID_HANDLE;
}
-static ResultCode SignalEvent(Handle handle) {
+static ResultCode SignalEvent(Core::System& system, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle);
- HandleTable& handle_table = Core::CurrentProcess()->GetHandleTable();
+ HandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
auto writable_event = handle_table.Get<WritableEvent>(handle);
if (!writable_event) {
@@ -1784,7 +2058,7 @@ static ResultCode SignalEvent(Handle handle) {
return RESULT_SUCCESS;
}
-static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
+static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
// This function currently only allows retrieving a process' status.
@@ -1792,7 +2066,7 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
Status,
};
- const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const auto process = handle_table.Get<Process>(process_handle);
if (!process) {
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
@@ -1810,10 +2084,10 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
return RESULT_SUCCESS;
}
-static ResultCode CreateResourceLimit(Handle* out_handle) {
+static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) {
LOG_DEBUG(Kernel_SVC, "called");
- auto& kernel = Core::System::GetInstance().Kernel();
+ auto& kernel = system.Kernel();
auto resource_limit = ResourceLimit::Create(kernel);
auto* const current_process = kernel.CurrentProcess();
@@ -1828,11 +2102,11 @@ static ResultCode CreateResourceLimit(Handle* out_handle) {
return RESULT_SUCCESS;
}
-static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_limit,
- u32 resource_type) {
+static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_value,
+ Handle resource_limit, u32 resource_type) {
LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type);
- const auto limit_value = RetrieveResourceLimitValue(resource_limit, resource_type,
+ const auto limit_value = RetrieveResourceLimitValue(system, resource_limit, resource_type,
ResourceLimitValueType::LimitValue);
if (limit_value.Failed()) {
return limit_value.Code();
@@ -1842,11 +2116,11 @@ static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_lim
return RESULT_SUCCESS;
}
-static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_limit,
- u32 resource_type) {
+static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_value,
+ Handle resource_limit, u32 resource_type) {
LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type);
- const auto current_value = RetrieveResourceLimitValue(resource_limit, resource_type,
+ const auto current_value = RetrieveResourceLimitValue(system, resource_limit, resource_type,
ResourceLimitValueType::CurrentValue);
if (current_value.Failed()) {
return current_value.Code();
@@ -1856,7 +2130,8 @@ static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_l
return RESULT_SUCCESS;
}
-static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource_type, u64 value) {
+static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resource_limit,
+ u32 resource_type, u64 value) {
LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit,
resource_type, value);
@@ -1866,8 +2141,7 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource
return ERR_INVALID_ENUM_VALUE;
}
- auto& kernel = Core::System::GetInstance().Kernel();
- auto* const current_process = kernel.CurrentProcess();
+ auto* const current_process = system.Kernel().CurrentProcess();
ASSERT(current_process != nullptr);
auto resource_limit_object =
@@ -1891,9 +2165,86 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource
return RESULT_SUCCESS;
}
+static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
+ VAddr out_process_ids, u32 out_process_ids_size) {
+ LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
+ out_process_ids, out_process_ids_size);
+
+ // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail.
+ if ((out_process_ids_size & 0xF0000000) != 0) {
+ LOG_ERROR(Kernel_SVC,
+ "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}",
+ out_process_ids_size);
+ return ERR_OUT_OF_RANGE;
+ }
+
+ const auto& kernel = system.Kernel();
+ const auto& vm_manager = kernel.CurrentProcess()->VMManager();
+ const auto total_copy_size = out_process_ids_size * sizeof(u64);
+
+ if (out_process_ids_size > 0 &&
+ !vm_manager.IsWithinAddressSpace(out_process_ids, total_copy_size)) {
+ LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
+ out_process_ids, out_process_ids + total_copy_size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ const auto& process_list = kernel.GetProcessList();
+ const auto num_processes = process_list.size();
+ const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes);
+
+ for (std::size_t i = 0; i < copy_amount; ++i) {
+ Memory::Write64(out_process_ids, process_list[i]->GetProcessID());
+ out_process_ids += sizeof(u64);
+ }
+
+ *out_num_processes = static_cast<u32>(num_processes);
+ return RESULT_SUCCESS;
+}
+
+static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
+ u32 out_thread_ids_size, Handle debug_handle) {
+ // TODO: Handle this case when debug events are supported.
+ UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
+
+ LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
+ out_thread_ids, out_thread_ids_size);
+
+ // If the size is negative or larger than INT32_MAX / sizeof(u64)
+ if ((out_thread_ids_size & 0xF0000000) != 0) {
+ LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
+ out_thread_ids_size);
+ return ERR_OUT_OF_RANGE;
+ }
+
+ const auto* const current_process = system.Kernel().CurrentProcess();
+ const auto& vm_manager = current_process->VMManager();
+ const auto total_copy_size = out_thread_ids_size * sizeof(u64);
+
+ if (out_thread_ids_size > 0 &&
+ !vm_manager.IsWithinAddressSpace(out_thread_ids, total_copy_size)) {
+ LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
+ out_thread_ids, out_thread_ids + total_copy_size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ const auto& thread_list = current_process->GetThreadList();
+ const auto num_threads = thread_list.size();
+ const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
+
+ auto list_iter = thread_list.cbegin();
+ for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
+ Memory::Write64(out_thread_ids, (*list_iter)->GetThreadID());
+ out_thread_ids += sizeof(u64);
+ }
+
+ *out_num_threads = static_cast<u32>(num_threads);
+ return RESULT_SUCCESS;
+}
+
namespace {
struct FunctionDef {
- using Func = void();
+ using Func = void(Core::System&);
u32 id;
Func* func;
@@ -1956,7 +2307,7 @@ static const FunctionDef SVC_Table[] = {
{0x33, SvcWrap<GetThreadContext>, "GetThreadContext"},
{0x34, SvcWrap<WaitForAddress>, "WaitForAddress"},
{0x35, SvcWrap<SignalToAddress>, "SignalToAddress"},
- {0x36, nullptr, "Unknown"},
+ {0x36, nullptr, "SynchronizePreemptionState"},
{0x37, nullptr, "Unknown"},
{0x38, nullptr, "Unknown"},
{0x39, nullptr, "Unknown"},
@@ -1983,8 +2334,8 @@ static const FunctionDef SVC_Table[] = {
{0x4E, nullptr, "ReadWriteRegister"},
{0x4F, nullptr, "SetProcessActivity"},
{0x50, SvcWrap<CreateSharedMemory>, "CreateSharedMemory"},
- {0x51, nullptr, "MapTransferMemory"},
- {0x52, nullptr, "UnmapTransferMemory"},
+ {0x51, SvcWrap<MapTransferMemory>, "MapTransferMemory"},
+ {0x52, SvcWrap<UnmapTransferMemory>, "UnmapTransferMemory"},
{0x53, nullptr, "CreateInterruptEvent"},
{0x54, nullptr, "QueryPhysicalAddress"},
{0x55, nullptr, "QueryIoMapping"},
@@ -2003,8 +2354,8 @@ static const FunctionDef SVC_Table[] = {
{0x62, nullptr, "TerminateDebugProcess"},
{0x63, nullptr, "GetDebugEvent"},
{0x64, nullptr, "ContinueDebugEvent"},
- {0x65, nullptr, "GetProcessList"},
- {0x66, nullptr, "GetThreadList"},
+ {0x65, SvcWrap<GetProcessList>, "GetProcessList"},
+ {0x66, SvcWrap<GetThreadList>, "GetThreadList"},
{0x67, nullptr, "GetDebugThreadContext"},
{0x68, nullptr, "SetDebugThreadContext"},
{0x69, nullptr, "QueryDebugProcessMemory"},
@@ -2021,8 +2372,8 @@ static const FunctionDef SVC_Table[] = {
{0x74, nullptr, "MapProcessMemory"},
{0x75, nullptr, "UnmapProcessMemory"},
{0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"},
- {0x77, nullptr, "MapProcessCodeMemory"},
- {0x78, nullptr, "UnmapProcessCodeMemory"},
+ {0x77, SvcWrap<MapProcessCodeMemory>, "MapProcessCodeMemory"},
+ {0x78, SvcWrap<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
{0x79, nullptr, "CreateProcess"},
{0x7A, nullptr, "StartProcess"},
{0x7B, nullptr, "TerminateProcess"},
@@ -2042,16 +2393,16 @@ static const FunctionDef* GetSVCInfo(u32 func_num) {
MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
-void CallSVC(u32 immediate) {
+void CallSVC(Core::System& system, u32 immediate) {
MICROPROFILE_SCOPE(Kernel_SVC);
// Lock the global kernel mutex when we enter the kernel HLE.
- std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
+ std::lock_guard lock{HLE::g_hle_lock};
const FunctionDef* info = GetSVCInfo(immediate);
if (info) {
if (info->func) {
- info->func();
+ info->func(system);
} else {
LOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name);
}
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index c37ae0f98..c5539ac1c 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -6,8 +6,12 @@
#include "common/common_types.h"
+namespace Core {
+class System;
+}
+
namespace Kernel {
-void CallSVC(u32 immediate);
+void CallSVC(Core::System& system, u32 immediate);
} // namespace Kernel
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 2a2c2c5ea..865473c6f 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -11,270 +11,319 @@
namespace Kernel {
-static inline u64 Param(int n) {
- return Core::CurrentArmInterface().GetReg(n);
+static inline u64 Param(const Core::System& system, int n) {
+ return system.CurrentArmInterface().GetReg(n);
}
/**
* HLE a function return from the current ARM userland process
- * @param res Result to return
+ * @param system System context
+ * @param result Result to return
*/
-static inline void FuncReturn(u64 res) {
- Core::CurrentArmInterface().SetReg(0, res);
+static inline void FuncReturn(Core::System& system, u64 result) {
+ system.CurrentArmInterface().SetReg(0, result);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type ResultCode
-template <ResultCode func(u64)>
-void SvcWrap() {
- FuncReturn(func(Param(0)).raw);
+template <ResultCode func(Core::System&, u64)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, Param(system, 0)).raw);
}
-template <ResultCode func(u32)>
-void SvcWrap() {
- FuncReturn(func(static_cast<u32>(Param(0))).raw);
+template <ResultCode func(Core::System&, u32)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
}
-template <ResultCode func(u32, u32)>
-void SvcWrap() {
- FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw);
+template <ResultCode func(Core::System&, u32, u32)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(
+ system,
+ func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
+}
+
+template <ResultCode func(Core::System&, u32, u64, u64, u64)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
+ Param(system, 2), Param(system, 3))
+ .raw);
}
-template <ResultCode func(u32*)>
-void SvcWrap() {
+template <ResultCode func(Core::System&, u32*)>
+void SvcWrap(Core::System& system) {
u32 param = 0;
- const u32 retval = func(&param).raw;
- Core::CurrentArmInterface().SetReg(1, param);
- FuncReturn(retval);
+ const u32 retval = func(system, &param).raw;
+ system.CurrentArmInterface().SetReg(1, param);
+ FuncReturn(system, retval);
}
-template <ResultCode func(u32*, u32)>
-void SvcWrap() {
+template <ResultCode func(Core::System&, u32*, u32)>
+void SvcWrap(Core::System& system) {
u32 param_1 = 0;
- u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw;
- Core::CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(retval);
+ const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
+ system.CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(system, retval);
}
-template <ResultCode func(u32*, u32*)>
-void SvcWrap() {
+template <ResultCode func(Core::System&, u32*, u32*)>
+void SvcWrap(Core::System& system) {
u32 param_1 = 0;
u32 param_2 = 0;
- const u32 retval = func(&param_1, &param_2).raw;
+ const u32 retval = func(system, &param_1, &param_2).raw;
- auto& arm_interface = Core::CurrentArmInterface();
+ auto& arm_interface = system.CurrentArmInterface();
arm_interface.SetReg(1, param_1);
arm_interface.SetReg(2, param_2);
- FuncReturn(retval);
+ FuncReturn(system, retval);
}
-template <ResultCode func(u32*, u64)>
-void SvcWrap() {
+template <ResultCode func(Core::System&, u32*, u64)>
+void SvcWrap(Core::System& system) {
u32 param_1 = 0;
- const u32 retval = func(&param_1, Param(1)).raw;
- Core::CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(retval);
+ const u32 retval = func(system, &param_1, Param(system, 1)).raw;
+ system.CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(system, retval);
}
-template <ResultCode func(u64*, u32)>
-void SvcWrap() {
+template <ResultCode func(Core::System&, u32*, u64, u32)>
+void SvcWrap(Core::System& system) {
+ u32 param_1 = 0;
+ const u32 retval =
+ func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw;
+
+ system.CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(system, retval);
+}
+
+template <ResultCode func(Core::System&, u64*, u32)>
+void SvcWrap(Core::System& system) {
u64 param_1 = 0;
- const u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw;
- Core::CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(retval);
+ const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
+
+ system.CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(system, retval);
}
-template <ResultCode func(u64, s32)>
-void SvcWrap() {
- FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw);
+template <ResultCode func(Core::System&, u64, s32)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, Param(system, 0), static_cast<s32>(Param(system, 1))).raw);
}
-template <ResultCode func(u64, u32)>
-void SvcWrap() {
- FuncReturn(func(Param(0), static_cast<u32>(Param(1))).raw);
+template <ResultCode func(Core::System&, u64, u32)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw);
}
-template <ResultCode func(u64*, u64)>
-void SvcWrap() {
+template <ResultCode func(Core::System&, u64*, u64)>
+void SvcWrap(Core::System& system) {
u64 param_1 = 0;
- u32 retval = func(&param_1, Param(1)).raw;
- Core::CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(retval);
+ const u32 retval = func(system, &param_1, Param(system, 1)).raw;
+
+ system.CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(system, retval);
}
-template <ResultCode func(u64*, u32, u32)>
-void SvcWrap() {
+template <ResultCode func(Core::System&, u64*, u32, u32)>
+void SvcWrap(Core::System& system) {
u64 param_1 = 0;
- u32 retval = func(&param_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw;
- Core::CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(retval);
+ const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1)),
+ static_cast<u32>(Param(system, 2)))
+ .raw;
+
+ system.CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(system, retval);
}
-template <ResultCode func(u32, u64)>
-void SvcWrap() {
- FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw);
+template <ResultCode func(Core::System&, u32, u64)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw);
}
-template <ResultCode func(u32, u32, u64)>
-void SvcWrap() {
- FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw);
+template <ResultCode func(Core::System&, u32, u32, u64)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
+ static_cast<u32>(Param(system, 1)), Param(system, 2))
+ .raw);
}
-template <ResultCode func(u32, u32*, u64*)>
-void SvcWrap() {
+template <ResultCode func(Core::System&, u32, u32*, u64*)>
+void SvcWrap(Core::System& system) {
u32 param_1 = 0;
u64 param_2 = 0;
- ResultCode retval = func(static_cast<u32>(Param(2)), &param_1, &param_2);
- Core::CurrentArmInterface().SetReg(1, param_1);
- Core::CurrentArmInterface().SetReg(2, param_2);
- FuncReturn(retval.raw);
-}
+ const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
-template <ResultCode func(u64, u64, u32, u32)>
-void SvcWrap() {
- FuncReturn(
- func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw);
+ system.CurrentArmInterface().SetReg(1, param_1);
+ system.CurrentArmInterface().SetReg(2, param_2);
+ FuncReturn(system, retval.raw);
}
-template <ResultCode func(u64, u64, u32, u64)>
-void SvcWrap() {
- FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2)), Param(3)).raw);
+template <ResultCode func(Core::System&, u64, u64, u32, u32)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
+ static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
+ .raw);
}
-template <ResultCode func(u32, u64, u32)>
-void SvcWrap() {
- FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw);
+template <ResultCode func(Core::System&, u64, u64, u32, u64)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
+ static_cast<u32>(Param(system, 2)), Param(system, 3))
+ .raw);
}
-template <ResultCode func(u64, u64, u64)>
-void SvcWrap() {
- FuncReturn(func(Param(0), Param(1), Param(2)).raw);
+template <ResultCode func(Core::System&, u32, u64, u32)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
+ static_cast<u32>(Param(system, 2)))
+ .raw);
}
-template <ResultCode func(u64, u64, u32)>
-void SvcWrap() {
- FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw);
+template <ResultCode func(Core::System&, u64, u64, u64)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw);
}
-template <ResultCode func(u32, u64, u64, u32)>
-void SvcWrap() {
+template <ResultCode func(Core::System&, u64, u64, u32)>
+void SvcWrap(Core::System& system) {
FuncReturn(
- func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw);
+ system,
+ func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
}
-template <ResultCode func(u32, u64, u64)>
-void SvcWrap() {
- FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw);
+template <ResultCode func(Core::System&, u32, u64, u64, u32)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
+ Param(system, 2), static_cast<u32>(Param(system, 3)))
+ .raw);
}
-template <ResultCode func(u32*, u64, u64, s64)>
-void SvcWrap() {
+template <ResultCode func(Core::System&, u32, u64, u64)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(
+ system,
+ func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw);
+}
+
+template <ResultCode func(Core::System&, u32*, u64, u64, s64)>
+void SvcWrap(Core::System& system) {
u32 param_1 = 0;
- ResultCode retval =
- func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3)));
- Core::CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(retval.raw);
+ const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
+ static_cast<s64>(Param(system, 3)))
+ .raw;
+
+ system.CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(system, retval);
}
-template <ResultCode func(u64, u64, u32, s64)>
-void SvcWrap() {
- FuncReturn(
- func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw);
+template <ResultCode func(Core::System&, u64, u64, u32, s64)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
+ static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
+ .raw);
}
-template <ResultCode func(u64*, u64, u64, u64)>
-void SvcWrap() {
+template <ResultCode func(Core::System&, u64*, u64, u64, u64)>
+void SvcWrap(Core::System& system) {
u64 param_1 = 0;
- u32 retval = func(&param_1, Param(1), Param(2), Param(3)).raw;
- Core::CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(retval);
+ const u32 retval =
+ func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3)).raw;
+
+ system.CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(system, retval);
}
-template <ResultCode func(u32*, u64, u64, u64, u32, s32)>
-void SvcWrap() {
+template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)>
+void SvcWrap(Core::System& system) {
u32 param_1 = 0;
- u32 retval = func(&param_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)),
- static_cast<s32>(Param(5)))
- .raw;
- Core::CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(retval);
+ const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3),
+ static_cast<u32>(Param(system, 4)), static_cast<s32>(Param(system, 5)))
+ .raw;
+
+ system.CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(system, retval);
}
-template <ResultCode func(u32*, u64, u64, u32)>
-void SvcWrap() {
+template <ResultCode func(Core::System&, u32*, u64, u64, u32)>
+void SvcWrap(Core::System& system) {
u32 param_1 = 0;
- u32 retval = func(&param_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw;
- Core::CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(retval);
+ const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2),
+ static_cast<u32>(Param(system, 3)))
+ .raw;
+
+ system.CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(system, retval);
}
-template <ResultCode func(Handle*, u64, u32, u32)>
-void SvcWrap() {
+template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
+void SvcWrap(Core::System& system) {
u32 param_1 = 0;
- u32 retval =
- func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw;
- Core::CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(retval);
+ const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
+ static_cast<u32>(Param(system, 3)))
+ .raw;
+
+ system.CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(system, retval);
}
-template <ResultCode func(u64, u32, s32, s64)>
-void SvcWrap() {
- FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
- static_cast<s64>(Param(3)))
- .raw);
+template <ResultCode func(Core::System&, u64, u32, s32, s64)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
+ static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
+ .raw);
}
-template <ResultCode func(u64, u32, s32, s32)>
-void SvcWrap() {
- FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
- static_cast<s32>(Param(3)))
- .raw);
+template <ResultCode func(Core::System&, u64, u32, s32, s32)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
+ static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
+ .raw);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32
-template <u32 func()>
-void SvcWrap() {
- FuncReturn(func());
+template <u32 func(Core::System&)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u64
-template <u64 func()>
-void SvcWrap() {
- FuncReturn(func());
+template <u64 func(Core::System&)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Function wrappers that return type void
-template <void func()>
-void SvcWrap() {
- func();
+template <void func(Core::System&)>
+void SvcWrap(Core::System& system) {
+ func(system);
}
-template <void func(s64)>
-void SvcWrap() {
- func(static_cast<s64>(Param(0)));
+template <void func(Core::System&, s64)>
+void SvcWrap(Core::System& system) {
+ func(system, static_cast<s64>(Param(system, 0)));
}
-template <void func(u64, u64 len)>
-void SvcWrap() {
- func(Param(0), Param(1));
+template <void func(Core::System&, u64, u64)>
+void SvcWrap(Core::System& system) {
+ func(system, Param(system, 0), Param(system, 1));
}
-template <void func(u64, u64, u64)>
-void SvcWrap() {
- func(Param(0), Param(1), Param(2));
+template <void func(Core::System&, u64, u64, u64)>
+void SvcWrap(Core::System& system) {
+ func(system, Param(system, 0), Param(system, 1), Param(system, 2));
}
-template <void func(u32, u64, u64)>
-void SvcWrap() {
- func(static_cast<u32>(Param(0)), Param(1), Param(2));
+template <void func(Core::System&, u32, u64, u64)>
+void SvcWrap(Core::System& system) {
+ func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2));
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index eb54d6651..c73a40977 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -7,8 +7,6 @@
#include <optional>
#include <vector>
-#include <boost/range/algorithm_ext/erase.hpp>
-
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
@@ -30,7 +28,7 @@
namespace Kernel {
-bool Thread::ShouldWait(Thread* thread) const {
+bool Thread::ShouldWait(const Thread* thread) const {
return status != ThreadStatus::Dead;
}
@@ -64,21 +62,12 @@ void Thread::Stop() {
}
wait_objects.clear();
+ owner_process->UnregisterThread(this);
+
// Mark the TLS slot in the thread's page as free.
owner_process->FreeTLSSlot(tls_address);
}
-void WaitCurrentThread_Sleep() {
- Thread* thread = GetCurrentThread();
- thread->SetStatus(ThreadStatus::WaitSleep);
-}
-
-void ExitCurrentThread() {
- Thread* thread = GetCurrentThread();
- thread->Stop();
- Core::System::GetInstance().CurrentScheduler().RemoveThread(thread);
-}
-
void Thread::WakeAfterDelay(s64 nanoseconds) {
// Don't schedule a wakeup if the thread wants to wait forever
if (nanoseconds == -1)
@@ -86,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() {
@@ -112,12 +101,12 @@ void Thread::ResumeFromWait() {
ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects");
switch (status) {
- case ThreadStatus::WaitSynchAll:
- case ThreadStatus::WaitSynchAny:
+ case ThreadStatus::WaitSynch:
case ThreadStatus::WaitHLEEvent:
case ThreadStatus::WaitSleep:
case ThreadStatus::WaitIPC:
case ThreadStatus::WaitMutex:
+ case ThreadStatus::WaitCondVar:
case ThreadStatus::WaitArb:
break;
@@ -152,6 +141,12 @@ void Thread::ResumeFromWait() {
ChangeScheduler();
}
+void Thread::CancelWait() {
+ ASSERT(GetStatus() == ThreadStatus::WaitSynch);
+ SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
+ ResumeFromWait();
+}
+
/**
* Resets a thread context, making it ready to be scheduled and run by the CPU
* @param context Thread context to reset
@@ -211,9 +206,11 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
thread->owner_process = &owner_process;
thread->scheduler = &system.Scheduler(processor_id);
- thread->scheduler->AddThread(thread, priority);
+ thread->scheduler->AddThread(thread);
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
+ thread->owner_process->RegisterThread(thread.get());
+
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
// to initialize the context
ResetThreadContext(thread->context, stack_top, entry_point, arg);
@@ -228,11 +225,6 @@ void Thread::SetPriority(u32 priority) {
UpdatePriority();
}
-void Thread::BoostPriority(u32 priority) {
- scheduler->SetThreadPriority(this, priority);
- current_priority = priority;
-}
-
void Thread::SetWaitSynchronizationResult(ResultCode result) {
context.cpu_registers[0] = result.raw;
}
@@ -241,16 +233,16 @@ void Thread::SetWaitSynchronizationOutput(s32 output) {
context.cpu_registers[1] = output;
}
-s32 Thread::GetWaitObjectIndex(WaitObject* object) const {
+s32 Thread::GetWaitObjectIndex(const WaitObject* object) const {
ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything");
- auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object);
+ const auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object);
return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1);
}
VAddr Thread::GetCommandBufferAddress() const {
// Offset from the start of TLS at which the IPC command buffer begins.
- static constexpr int CommandHeaderOffset = 0x80;
- return GetTLSAddress() + CommandHeaderOffset;
+ constexpr u64 command_header_offset = 0x80;
+ return GetTLSAddress() + command_header_offset;
}
void Thread::SetStatus(ThreadStatus new_status) {
@@ -269,8 +261,8 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
if (thread->lock_owner == this) {
// If the thread is already waiting for this thread to release the mutex, ensure that the
// waiters list is consistent and return without doing anything.
- auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
- ASSERT(itr != wait_mutex_threads.end());
+ const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
+ ASSERT(iter != wait_mutex_threads.end());
return;
}
@@ -278,11 +270,16 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
ASSERT(thread->lock_owner == nullptr);
// Ensure that the thread is not already in the list of mutex waiters
- auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
- ASSERT(itr == wait_mutex_threads.end());
-
+ const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
+ ASSERT(iter == wait_mutex_threads.end());
+
+ // Keep the list in an ordered fashion
+ const auto insertion_point = std::find_if(
+ wait_mutex_threads.begin(), wait_mutex_threads.end(),
+ [&thread](const auto& entry) { return entry->GetPriority() > thread->GetPriority(); });
+ wait_mutex_threads.insert(insertion_point, thread);
thread->lock_owner = this;
- wait_mutex_threads.emplace_back(std::move(thread));
+
UpdatePriority();
}
@@ -290,32 +287,44 @@ void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
ASSERT(thread->lock_owner == this);
// Ensure that the thread is in the list of mutex waiters
- auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
- ASSERT(itr != wait_mutex_threads.end());
+ const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
+ ASSERT(iter != wait_mutex_threads.end());
+
+ wait_mutex_threads.erase(iter);
- boost::remove_erase(wait_mutex_threads, thread);
thread->lock_owner = nullptr;
UpdatePriority();
}
void Thread::UpdatePriority() {
- // Find the highest priority among all the threads that are waiting for this thread's lock
+ // If any of the threads waiting on the mutex have a higher priority
+ // (taking into account priority inheritance), then this thread inherits
+ // that thread's priority.
u32 new_priority = nominal_priority;
- for (const auto& thread : wait_mutex_threads) {
- if (thread->nominal_priority < new_priority)
- new_priority = thread->nominal_priority;
+ if (!wait_mutex_threads.empty()) {
+ if (wait_mutex_threads.front()->current_priority < new_priority) {
+ new_priority = wait_mutex_threads.front()->current_priority;
+ }
}
- if (new_priority == current_priority)
+ if (new_priority == current_priority) {
return;
+ }
scheduler->SetThreadPriority(this, new_priority);
-
current_priority = new_priority;
+ if (!lock_owner) {
+ return;
+ }
+
+ // Ensure that the thread is within the correct location in the waiting list.
+ auto old_owner = lock_owner;
+ lock_owner->RemoveMutexWaiter(this);
+ old_owner->AddMutexWaiter(this);
+
// Recursively update the priority of the thread that depends on the priority of this one.
- if (lock_owner)
- lock_owner->UpdatePriority();
+ lock_owner->UpdatePriority();
}
void Thread::ChangeCore(u32 core, u64 mask) {
@@ -347,7 +356,7 @@ void Thread::ChangeScheduler() {
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler
scheduler->RemoveThread(this);
- next_scheduler.AddThread(this, current_priority);
+ next_scheduler.AddThread(this);
}
processor_id = *new_processor_id;
@@ -362,7 +371,7 @@ void Thread::ChangeScheduler() {
system.CpuCore(processor_id).PrepareReschedule();
}
-bool Thread::AllWaitObjectsReady() {
+bool Thread::AllWaitObjectsReady() const {
return std::none_of(
wait_objects.begin(), wait_objects.end(),
[this](const SharedPtr<WaitObject>& object) { return object->ShouldWait(this); });
@@ -391,6 +400,14 @@ void Thread::SetActivity(ThreadActivity value) {
}
}
+void Thread::Sleep(s64 nanoseconds) {
+ // Sleep current thread and check for next thread to schedule
+ SetStatus(ThreadStatus::WaitSleep);
+
+ // Create an event to wake the thread up after the specified nanosecond delay has passed
+ WakeAfterDelay(nanoseconds);
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index c48b21aba..b4b9cda7c 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -30,12 +30,21 @@ enum ThreadPriority : u32 {
};
enum ThreadProcessorId : s32 {
- THREADPROCESSORID_IDEAL = -2, ///< Run thread on the ideal core specified by the process.
- THREADPROCESSORID_0 = 0, ///< Run thread on core 0
- THREADPROCESSORID_1 = 1, ///< Run thread on core 1
- THREADPROCESSORID_2 = 2, ///< Run thread on core 2
- THREADPROCESSORID_3 = 3, ///< Run thread on core 3
- THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
+ /// Indicates that no particular processor core is preferred.
+ THREADPROCESSORID_DONT_CARE = -1,
+
+ /// Run thread on the ideal core specified by the process.
+ THREADPROCESSORID_IDEAL = -2,
+
+ /// Indicates that the preferred processor ID shouldn't be updated in
+ /// a core mask setting operation.
+ THREADPROCESSORID_DONT_UPDATE = -3,
+
+ THREADPROCESSORID_0 = 0, ///< Run thread on core 0
+ THREADPROCESSORID_1 = 1, ///< Run thread on core 1
+ THREADPROCESSORID_2 = 2, ///< Run thread on core 2
+ THREADPROCESSORID_3 = 3, ///< Run thread on core 3
+ THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
/// Allowed CPU mask
THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) |
@@ -49,9 +58,9 @@ enum class ThreadStatus {
WaitHLEEvent, ///< Waiting for hle event to finish
WaitSleep, ///< Waiting due to a SleepThread SVC
WaitIPC, ///< Waiting for the reply from an IPC request
- WaitSynchAny, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
- WaitSynchAll, ///< Waiting due to WaitSynchronizationN with wait_all = true
- WaitMutex, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc
+ WaitSynch, ///< Waiting due to WaitSynchronization
+ WaitMutex, ///< Waiting due to an ArbitrateLock svc
+ WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc
WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc
Dormant, ///< Created but not yet made ready
Dead ///< Run to completion, or forcefully terminated
@@ -101,16 +110,21 @@ public:
std::string GetName() const override {
return name;
}
+
+ void SetName(std::string new_name) {
+ name = std::move(new_name);
+ }
+
std::string GetTypeName() const override {
return "Thread";
}
- static const HandleType HANDLE_TYPE = HandleType::Thread;
+ static constexpr HandleType HANDLE_TYPE = HandleType::Thread;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
- bool ShouldWait(Thread* thread) const override;
+ bool ShouldWait(const Thread* thread) const override;
void Acquire(Thread* thread) override;
/**
@@ -135,12 +149,6 @@ public:
*/
void SetPriority(u32 priority);
- /**
- * Temporarily boosts the thread's priority until the next time it is scheduled
- * @param priority The new priority
- */
- void BoostPriority(u32 priority);
-
/// Adds a thread to the list of threads that are waiting for a lock held by this thread.
void AddMutexWaiter(SharedPtr<Thread> thread);
@@ -169,11 +177,17 @@ public:
return tls_memory;
}
- /**
- * Resumes a thread from waiting
- */
+ /// Resumes a thread from waiting
void ResumeFromWait();
+ /// Cancels a waiting operation that this thread may or may not be within.
+ ///
+ /// When the thread is within a waiting state, this will set the thread's
+ /// waiting result to signal a canceled wait. The function will then resume
+ /// this thread.
+ ///
+ void CancelWait();
+
/**
* Schedules an event to wake up the specified thread after the specified delay
* @param nanoseconds The time this thread will be allowed to sleep for
@@ -184,27 +198,30 @@ public:
void CancelWakeupTimer();
/**
- * Sets the result after the thread awakens (from either WaitSynchronization SVC)
+ * Sets the result after the thread awakens (from svcWaitSynchronization)
* @param result Value to set to the returned result
*/
void SetWaitSynchronizationResult(ResultCode result);
/**
- * Sets the output parameter value after the thread awakens (from WaitSynchronizationN SVC only)
+ * Sets the output parameter value after the thread awakens (from svcWaitSynchronization)
* @param output Value to set to the output parameter
*/
void SetWaitSynchronizationOutput(s32 output);
/**
* Retrieves the index that this particular object occupies in the list of objects
- * that the thread passed to WaitSynchronizationN, starting the search from the last element.
- * It is used to set the output value of WaitSynchronizationN when the thread is awakened.
+ * that the thread passed to WaitSynchronization, starting the search from the last element.
+ *
+ * It is used to set the output index of WaitSynchronization when the thread is awakened.
+ *
* When a thread wakes up due to an object signal, the kernel will use the index of the last
* matching object in the wait objects list in case of having multiple instances of the same
* object in the list.
+ *
* @param object Object to query the index of.
*/
- s32 GetWaitObjectIndex(WaitObject* object) const;
+ s32 GetWaitObjectIndex(const WaitObject* object) const;
/**
* Stops a thread, invalidating it from further use
@@ -238,13 +255,9 @@ public:
*/
VAddr GetCommandBufferAddress() const;
- /**
- * Returns whether this thread is waiting for all the objects in
- * its wait list to become ready, as a result of a WaitSynchronizationN call
- * with wait_all = true.
- */
- bool IsSleepingOnWaitAll() const {
- return status == ThreadStatus::WaitSynchAll;
+ /// Returns whether this thread is waiting on objects from a WaitSynchronization call.
+ bool IsSleepingOnWait() const {
+ return status == ThreadStatus::WaitSynch;
}
ThreadContext& GetContext() {
@@ -298,7 +311,7 @@ public:
}
/// Determines whether all the objects this thread is waiting on are ready.
- bool AllWaitObjectsReady();
+ bool AllWaitObjectsReady() const;
const MutexWaitingThreads& GetMutexWaitingThreads() const {
return wait_mutex_threads;
@@ -344,10 +357,6 @@ public:
arb_wait_address = address;
}
- void SetGuestHandle(Handle handle) {
- guest_handle = handle;
- }
-
bool HasWakeupCallback() const {
return wakeup_callback != nullptr;
}
@@ -383,6 +392,9 @@ public:
void SetActivity(ThreadActivity value);
+ /// Sleeps this thread for the given amount of nanoseconds.
+ void Sleep(s64 nanoseconds);
+
private:
explicit Thread(KernelCore& kernel);
~Thread() override;
@@ -398,8 +410,14 @@ private:
VAddr entry_point = 0;
VAddr stack_top = 0;
- u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application
- u32 current_priority = 0; ///< Current thread priority, can be temporarily changed
+ /// Nominal thread priority, as set by the emulated application.
+ /// The nominal priority is the thread priority without priority
+ /// inheritance taken into account.
+ u32 nominal_priority = 0;
+
+ /// Current thread priority. This may change over the course of the
+ /// thread's lifetime in order to facilitate priority inheritance.
+ u32 current_priority = 0;
u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
u64 last_running_ticks = 0; ///< CPU tick when thread was last running
@@ -413,7 +431,7 @@ private:
Process* owner_process;
/// Objects that the thread is waiting on, in the same order as they were
- /// passed to WaitSynchronization1/N.
+ /// passed to WaitSynchronization.
ThreadWaitObjects wait_objects;
/// List of threads that are waiting for a mutex that is held by this thread.
@@ -432,14 +450,11 @@ private:
/// If waiting for an AddressArbiter, this is the address being waited on.
VAddr arb_wait_address{0};
- /// Handle used by guest emulated application to access this thread
- Handle guest_handle = 0;
-
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
Handle callback_handle = 0;
/// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
- /// was waiting via WaitSynchronizationN then the object will be the last object that became
+ /// was waiting via WaitSynchronization then the object will be the last object that became
/// available. In case of a timeout, the object will be nullptr.
WakeupCallback wakeup_callback;
@@ -460,14 +475,4 @@ private:
*/
Thread* GetCurrentThread();
-/**
- * Waits the current thread on a sleep
- */
-void WaitCurrentThread_Sleep();
-
-/**
- * Stops the current thread and removes it from the thread_list
- */
-void ExitCurrentThread();
-
} // namespace Kernel
diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp
new file mode 100644
index 000000000..26c4e5e67
--- /dev/null
+++ b/src/core/hle/kernel/transfer_memory.cpp
@@ -0,0 +1,81 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/shared_memory.h"
+#include "core/hle/kernel/transfer_memory.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {}
+TransferMemory::~TransferMemory() = default;
+
+SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address, u64 size,
+ MemoryPermission permissions) {
+ SharedPtr<TransferMemory> transfer_memory{new TransferMemory(kernel)};
+
+ transfer_memory->base_address = base_address;
+ transfer_memory->memory_size = size;
+ transfer_memory->owner_permissions = permissions;
+ transfer_memory->owner_process = kernel.CurrentProcess();
+
+ return transfer_memory;
+}
+
+const u8* TransferMemory::GetPointer() const {
+ return backing_block.get()->data();
+}
+
+u64 TransferMemory::GetSize() const {
+ return memory_size;
+}
+
+ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission permissions) {
+ if (memory_size != size) {
+ return ERR_INVALID_SIZE;
+ }
+
+ if (owner_permissions != permissions) {
+ return ERR_INVALID_STATE;
+ }
+
+ if (is_mapped) {
+ return ERR_INVALID_STATE;
+ }
+
+ backing_block = std::make_shared<std::vector<u8>>(size);
+
+ const auto map_state = owner_permissions == MemoryPermission::None
+ ? MemoryState::TransferMemoryIsolated
+ : MemoryState::TransferMemory;
+ auto& vm_manager = owner_process->VMManager();
+ const auto map_result = vm_manager.MapMemoryBlock(address, backing_block, 0, size, map_state);
+ if (map_result.Failed()) {
+ return map_result.Code();
+ }
+
+ is_mapped = true;
+ return RESULT_SUCCESS;
+}
+
+ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) {
+ if (memory_size != size) {
+ return ERR_INVALID_SIZE;
+ }
+
+ auto& vm_manager = owner_process->VMManager();
+ const auto result = vm_manager.UnmapRange(address, size);
+
+ if (result.IsError()) {
+ return result;
+ }
+
+ is_mapped = false;
+ return RESULT_SUCCESS;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h
new file mode 100644
index 000000000..a140b1e2b
--- /dev/null
+++ b/src/core/hle/kernel/transfer_memory.h
@@ -0,0 +1,103 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "core/hle/kernel/object.h"
+
+union ResultCode;
+
+namespace Kernel {
+
+class KernelCore;
+class Process;
+
+enum class MemoryPermission : u32;
+
+/// Defines the interface for transfer memory objects.
+///
+/// Transfer memory is typically used for the purpose of
+/// transferring memory between separate process instances,
+/// thus the name.
+///
+class TransferMemory final : public Object {
+public:
+ static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory;
+
+ static SharedPtr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, u64 size,
+ MemoryPermission permissions);
+
+ TransferMemory(const TransferMemory&) = delete;
+ TransferMemory& operator=(const TransferMemory&) = delete;
+
+ TransferMemory(TransferMemory&&) = delete;
+ TransferMemory& operator=(TransferMemory&&) = delete;
+
+ std::string GetTypeName() const override {
+ return "TransferMemory";
+ }
+
+ std::string GetName() const override {
+ return GetTypeName();
+ }
+
+ HandleType GetHandleType() const override {
+ return HANDLE_TYPE;
+ }
+
+ /// Gets a pointer to the backing block of this instance.
+ const u8* GetPointer() const;
+
+ /// Gets the size of the memory backing this instance in bytes.
+ u64 GetSize() const;
+
+ /// Attempts to map transfer memory with the given range and memory permissions.
+ ///
+ /// @param address The base address to being mapping memory at.
+ /// @param size The size of the memory to map, in bytes.
+ /// @param permissions The memory permissions to check against when mapping memory.
+ ///
+ /// @pre The given address, size, and memory permissions must all match
+ /// the same values that were given when creating the transfer memory
+ /// instance.
+ ///
+ ResultCode MapMemory(VAddr address, u64 size, MemoryPermission permissions);
+
+ /// Unmaps the transfer memory with the given range
+ ///
+ /// @param address The base address to begin unmapping memory at.
+ /// @param size The size of the memory to unmap, in bytes.
+ ///
+ /// @pre The given address and size must be the same as the ones used
+ /// to create the transfer memory instance.
+ ///
+ ResultCode UnmapMemory(VAddr address, u64 size);
+
+private:
+ explicit TransferMemory(KernelCore& kernel);
+ ~TransferMemory() override;
+
+ /// Memory block backing this instance.
+ std::shared_ptr<std::vector<u8>> backing_block;
+
+ /// The base address for the memory managed by this instance.
+ VAddr base_address = 0;
+
+ /// Size of the memory, in bytes, that this instance manages.
+ u64 memory_size = 0;
+
+ /// The memory permissions that are applied to this instance.
+ MemoryPermission owner_permissions{};
+
+ /// The process that this transfer memory instance was created under.
+ Process* owner_process = nullptr;
+
+ /// Whether or not this transfer memory instance has mapped memory.
+ bool is_mapped = false;
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 05c59af34..c929c2a52 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -7,29 +7,29 @@
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
+#include "common/memory_hook.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
-#include "core/memory_hook.h"
#include "core/memory_setup.h"
namespace Kernel {
namespace {
const char* GetMemoryStateName(MemoryState state) {
static constexpr const char* names[] = {
- "Unmapped", "Io",
- "Normal", "CodeStatic",
- "CodeMutable", "Heap",
- "Shared", "Unknown1",
- "ModuleCodeStatic", "ModuleCodeMutable",
- "IpcBuffer0", "Stack",
- "ThreadLocal", "TransferMemoryIsolated",
- "TransferMemory", "ProcessMemory",
- "Inaccessible", "IpcBuffer1",
- "IpcBuffer3", "KernelStack",
+ "Unmapped", "Io",
+ "Normal", "Code",
+ "CodeData", "Heap",
+ "Shared", "Unknown1",
+ "ModuleCode", "ModuleCodeData",
+ "IpcBuffer0", "Stack",
+ "ThreadLocal", "TransferMemoryIsolated",
+ "TransferMemory", "ProcessMemory",
+ "Inaccessible", "IpcBuffer1",
+ "IpcBuffer3", "KernelStack",
};
return names[ToSvcMemoryState(state)];
@@ -62,15 +62,13 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
return true;
}
-VMManager::VMManager() {
+VMManager::VMManager(Core::System& system) : system{system} {
// Default to assuming a 39-bit address space. This way we have a sane
// starting point with executables that don't provide metadata.
Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
}
-VMManager::~VMManager() {
- Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
-}
+VMManager::~VMManager() = default;
void VMManager::Reset(FileSys::ProgramAddressSpaceType type) {
Clear();
@@ -111,7 +109,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
VirtualMemoryArea& final_vma = vma_handle->second;
ASSERT(final_vma.size == size);
- auto& system = Core::System::GetInstance();
system.ArmInterface(0).MapBackingMemory(target, size, block->data() + offset,
VMAPermission::ReadWriteExecute);
system.ArmInterface(1).MapBackingMemory(target, size, block->data() + offset,
@@ -140,7 +137,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
VirtualMemoryArea& final_vma = vma_handle->second;
ASSERT(final_vma.size == size);
- auto& system = Core::System::GetInstance();
system.ArmInterface(0).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
system.ArmInterface(1).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
system.ArmInterface(2).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
@@ -177,7 +173,7 @@ ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
MemoryState state,
- Memory::MemoryHookPointer mmio_handler) {
+ Common::MemoryHookPointer mmio_handler) {
// This is the appropriately sized VMA that will turn into our allocation.
CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
VirtualMemoryArea& final_vma = vma_handle->second;
@@ -223,7 +219,6 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
ASSERT(FindVMA(target)->second.size >= size);
- auto& system = Core::System::GetInstance();
system.ArmInterface(0).UnmapMemory(target, size);
system.ArmInterface(1).UnmapMemory(target, size);
system.ArmInterface(2).UnmapMemory(target, size);
@@ -256,57 +251,130 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
return RESULT_SUCCESS;
}
-ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
- if (!IsWithinHeapRegion(target, size)) {
- return ERR_INVALID_ADDRESS;
+ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
+ if (size > GetHeapRegionSize()) {
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ // No need to do any additional work if the heap is already the given size.
+ if (size == GetCurrentHeapSize()) {
+ return MakeResult(heap_region_base);
}
if (heap_memory == nullptr) {
// Initialize heap
- heap_memory = std::make_shared<std::vector<u8>>();
- heap_start = heap_end = target;
+ heap_memory = std::make_shared<std::vector<u8>>(size);
+ heap_end = heap_region_base + size;
} else {
- UnmapRange(heap_start, heap_end - heap_start);
+ UnmapRange(heap_region_base, GetCurrentHeapSize());
}
- // If necessary, expand backing vector to cover new heap extents.
- if (target < heap_start) {
- heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
- heap_start = target;
+ // If necessary, expand backing vector to cover new heap extents in
+ // the case of allocating. Otherwise, shrink the backing memory,
+ // if a smaller heap has been requested.
+ const u64 old_heap_size = GetCurrentHeapSize();
+ if (size > old_heap_size) {
+ const u64 alloc_size = size - old_heap_size;
+
+ heap_memory->insert(heap_memory->end(), alloc_size, 0);
RefreshMemoryBlockMappings(heap_memory.get());
- }
- if (target + size > heap_end) {
- heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
- heap_end = target + size;
+ } else if (size < old_heap_size) {
+ heap_memory->resize(size);
+ heap_memory->shrink_to_fit();
+
RefreshMemoryBlockMappings(heap_memory.get());
}
- ASSERT(heap_end - heap_start == heap_memory->size());
- CASCADE_RESULT(auto vma, MapMemoryBlock(target, heap_memory, target - heap_start, size,
- MemoryState::Heap));
- Reprotect(vma, perms);
+ heap_end = heap_region_base + size;
+ ASSERT(GetCurrentHeapSize() == heap_memory->size());
- heap_used = size;
+ const auto mapping_result =
+ MapMemoryBlock(heap_region_base, heap_memory, 0, size, MemoryState::Heap);
+ if (mapping_result.Failed()) {
+ return mapping_result.Code();
+ }
- return MakeResult<VAddr>(heap_end - size);
+ return MakeResult<VAddr>(heap_region_base);
}
-ResultCode VMManager::HeapFree(VAddr target, u64 size) {
- if (!IsWithinHeapRegion(target, size)) {
- return ERR_INVALID_ADDRESS;
+ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
+ constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
+ const auto src_check_result = CheckRangeState(
+ src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::All,
+ VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
+
+ if (src_check_result.Failed()) {
+ return src_check_result.Code();
}
- if (size == 0) {
- return RESULT_SUCCESS;
+ const auto mirror_result =
+ MirrorMemory(dst_address, src_address, size, MemoryState::ModuleCode);
+ if (mirror_result.IsError()) {
+ return mirror_result;
}
- const ResultCode result = UnmapRange(target, size);
- if (result.IsError()) {
- return result;
+ // Ensure we lock the source memory region.
+ const auto src_vma_result = CarveVMARange(src_address, size);
+ if (src_vma_result.Failed()) {
+ return src_vma_result.Code();
}
+ auto src_vma_iter = *src_vma_result;
+ src_vma_iter->second.attribute = MemoryAttribute::Locked;
+ Reprotect(src_vma_iter, VMAPermission::Read);
- heap_used -= size;
- return RESULT_SUCCESS;
+ // The destination memory region is fine as is, however we need to make it read-only.
+ return ReprotectRange(dst_address, size, VMAPermission::Read);
+}
+
+ResultCode VMManager::UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
+ constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
+ const auto src_check_result = CheckRangeState(
+ src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::None,
+ VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, ignore_attribute);
+
+ if (src_check_result.Failed()) {
+ return src_check_result.Code();
+ }
+
+ // Yes, the kernel only checks the first page of the region.
+ const auto dst_check_result =
+ CheckRangeState(dst_address, Memory::PAGE_SIZE, MemoryState::FlagModule,
+ MemoryState::FlagModule, VMAPermission::None, VMAPermission::None,
+ MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
+
+ if (dst_check_result.Failed()) {
+ return dst_check_result.Code();
+ }
+
+ const auto dst_memory_state = std::get<MemoryState>(*dst_check_result);
+ const auto dst_contiguous_check_result = CheckRangeState(
+ dst_address, size, MemoryState::All, dst_memory_state, VMAPermission::None,
+ VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
+
+ if (dst_contiguous_check_result.Failed()) {
+ return dst_contiguous_check_result.Code();
+ }
+
+ const auto unmap_result = UnmapRange(dst_address, size);
+ if (unmap_result.IsError()) {
+ return unmap_result;
+ }
+
+ // With the mirrored portion unmapped, restore the original region's traits.
+ const auto src_vma_result = CarveVMARange(src_address, size);
+ if (src_vma_result.Failed()) {
+ return src_vma_result.Code();
+ }
+ auto src_vma_iter = *src_vma_result;
+ src_vma_iter->second.state = MemoryState::Heap;
+ src_vma_iter->second.attribute = MemoryAttribute::None;
+ Reprotect(src_vma_iter, VMAPermission::ReadWrite);
+
+ if (dst_memory_state == MemoryState::ModuleCode) {
+ system.InvalidateCpuInstructionCaches();
+ }
+
+ return unmap_result;
}
MemoryInfo VMManager::QueryMemory(VAddr address) const {
@@ -598,6 +666,7 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
heap_region_base = map_region_end;
heap_region_end = heap_region_base + heap_region_size;
+ heap_end = heap_region_base;
new_map_region_base = heap_region_end;
new_map_region_end = new_map_region_base + new_map_region_size;
@@ -624,7 +693,7 @@ void VMManager::ClearPageTable() {
std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
page_table.special_regions.clear();
std::fill(page_table.attributes.begin(), page_table.attributes.end(),
- Memory::PageType::Unmapped);
+ Common::PageType::Unmapped);
}
VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask,
@@ -687,15 +756,11 @@ 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;
}
-u64 VMManager::GetTotalHeapUsage() const {
- return heap_used;
-}
-
VAddr VMManager::GetAddressSpaceBaseAddress() const {
return address_space_base;
}
@@ -778,6 +843,10 @@ u64 VMManager::GetHeapRegionSize() const {
return heap_region_end - heap_region_base;
}
+u64 VMManager::GetCurrentHeapSize() const {
+ return heap_end - heap_region_base;
+}
+
bool VMManager::IsWithinHeapRegion(VAddr address, u64 size) const {
return IsInsideAddressRange(address, size, GetHeapRegionBaseAddress(),
GetHeapRegionEndAddress());
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 88e0b3c02..dfbf7a894 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -9,9 +9,14 @@
#include <tuple>
#include <vector>
#include "common/common_types.h"
+#include "common/memory_hook.h"
+#include "common/page_table.h"
#include "core/hle/result.h"
#include "core/memory.h"
-#include "core/memory_hook.h"
+
+namespace Core {
+class System;
+}
namespace FileSys {
enum class ProgramAddressSpaceType : u8;
@@ -42,6 +47,9 @@ enum class VMAPermission : u8 {
ReadExecute = Read | Execute,
WriteExecute = Write | Execute,
ReadWriteExecute = Read | Write | Execute,
+
+ // Used as a wildcard when checking permissions across memory ranges
+ All = 0xFF,
};
constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) {
@@ -151,6 +159,9 @@ enum class MemoryState : u32 {
FlagUncached = 1U << 24,
FlagCodeMemory = 1U << 25,
+ // Wildcard used in range checking to indicate all states.
+ All = 0xFFFFFFFF,
+
// Convenience flag sets to reduce repetition
IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1,
@@ -164,12 +175,12 @@ enum class MemoryState : u32 {
Unmapped = 0x00,
Io = 0x01 | FlagMapped,
Normal = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed,
- CodeStatic = 0x03 | CodeFlags | FlagMapProcess,
- CodeMutable = 0x04 | CodeFlags | FlagMapProcess | FlagCodeMemory,
+ Code = 0x03 | CodeFlags | FlagMapProcess,
+ CodeData = 0x04 | DataFlags | FlagMapProcess | FlagCodeMemory,
Heap = 0x05 | DataFlags | FlagCodeMemory,
Shared = 0x06 | FlagMapped | FlagMemoryPoolAllocated,
- ModuleCodeStatic = 0x08 | CodeFlags | FlagModule | FlagMapProcess,
- ModuleCodeMutable = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory,
+ ModuleCode = 0x08 | CodeFlags | FlagModule | FlagMapProcess,
+ ModuleCodeData = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory,
IpcBuffer0 = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated |
IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned,
@@ -290,7 +301,7 @@ struct VirtualMemoryArea {
// Settings for type = MMIO
/// Physical address of the register area this VMA maps to.
PAddr paddr = 0;
- Memory::MemoryHookPointer mmio_handler = nullptr;
+ Common::MemoryHookPointer mmio_handler = nullptr;
/// Tests if this area can be merged to the right with `next`.
bool CanBeMergedWith(const VirtualMemoryArea& next) const;
@@ -314,7 +325,7 @@ class VMManager final {
public:
using VMAHandle = VMAMap::const_iterator;
- VMManager();
+ explicit VMManager(Core::System& system);
~VMManager();
/// Clears the address space map, re-initializing with a single free area.
@@ -368,7 +379,7 @@ public:
* @param mmio_handler The handler that will implement read and write for this MMIO region.
*/
ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state,
- Memory::MemoryHookPointer mmio_handler);
+ Common::MemoryHookPointer mmio_handler);
/// Unmaps a range of addresses, splitting VMAs as necessary.
ResultCode UnmapRange(VAddr target, u64 size);
@@ -379,11 +390,84 @@ public:
/// Changes the permissions of a range of addresses, splitting VMAs as necessary.
ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms);
- ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
- ResultCode HeapFree(VAddr target, u64 size);
-
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state);
+ /// Attempts to allocate a heap with the given size.
+ ///
+ /// @param size The size of the heap to allocate in bytes.
+ ///
+ /// @note If a heap is currently allocated, and this is called
+ /// with a size that is equal to the size of the current heap,
+ /// then this function will do nothing and return the current
+ /// heap's starting address, as there's no need to perform
+ /// any additional heap allocation work.
+ ///
+ /// @note If a heap is currently allocated, and this is called
+ /// with a size less than the current heap's size, then
+ /// this function will attempt to shrink the heap.
+ ///
+ /// @note If a heap is currently allocated, and this is called
+ /// with a size larger than the current heap's size, then
+ /// this function will attempt to extend the size of the heap.
+ ///
+ /// @returns A result indicating either success or failure.
+ /// <p>
+ /// If successful, this function will return a result
+ /// containing the starting address to the allocated heap.
+ /// <p>
+ /// If unsuccessful, this function will return a result
+ /// containing an error code.
+ ///
+ /// @pre The given size must lie within the allowable heap
+ /// memory region managed by this VMManager instance.
+ /// Failure to abide by this will result in ERR_OUT_OF_MEMORY
+ /// being returned as the result.
+ ///
+ ResultVal<VAddr> SetHeapSize(u64 size);
+
+ /// Maps a region of memory as code memory.
+ ///
+ /// @param dst_address The base address of the region to create the aliasing memory region.
+ /// @param src_address The base address of the region to be aliased.
+ /// @param size The total amount of memory to map in bytes.
+ ///
+ /// @pre Both memory regions lie within the actual addressable address space.
+ ///
+ /// @post After this function finishes execution, assuming success, then the address range
+ /// [dst_address, dst_address+size) will alias the memory region,
+ /// [src_address, src_address+size).
+ /// <p>
+ /// What this also entails is as follows:
+ /// 1. The aliased region gains the Locked memory attribute.
+ /// 2. The aliased region becomes read-only.
+ /// 3. The aliasing region becomes read-only.
+ /// 4. The aliasing region is created with a memory state of MemoryState::CodeModule.
+ ///
+ ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size);
+
+ /// Unmaps a region of memory designated as code module memory.
+ ///
+ /// @param dst_address The base address of the memory region aliasing the source memory region.
+ /// @param src_address The base address of the memory region being aliased.
+ /// @param size The size of the memory region to unmap in bytes.
+ ///
+ /// @pre Both memory ranges lie within the actual addressable address space.
+ ///
+ /// @pre The memory region being unmapped has been previously been mapped
+ /// by a call to MapCodeMemory.
+ ///
+ /// @post After execution of the function, if successful. the aliasing memory region
+ /// will be unmapped and the aliased region will have various traits about it
+ /// restored to what they were prior to the original mapping call preceding
+ /// this function call.
+ /// <p>
+ /// What this also entails is as follows:
+ /// 1. The state of the memory region will now indicate a general heap region.
+ /// 2. All memory attributes for the memory region are cleared.
+ /// 3. Memory permissions for the region are restored to user read/write.
+ ///
+ ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size);
+
/// Queries the memory manager for information about the given address.
///
/// @param address The address to query the memory manager about for information.
@@ -415,10 +499,7 @@ public:
void LogLayout() const;
/// Gets the total memory usage, used by svcGetInfo
- u64 GetTotalMemoryUsage() const;
-
- /// Gets the total heap usage, used by svcGetInfo
- u64 GetTotalHeapUsage() const;
+ u64 GetTotalPhysicalMemoryAvailable() const;
/// Gets the address space base address
VAddr GetAddressSpaceBaseAddress() const;
@@ -468,6 +549,13 @@ public:
/// Gets the total size of the heap region in bytes.
u64 GetHeapRegionSize() const;
+ /// Gets the total size of the current heap in bytes.
+ ///
+ /// @note This is the current allocated heap size, not the size
+ /// of the region it's allowed to exist within.
+ ///
+ u64 GetCurrentHeapSize() const;
+
/// Determines whether or not the specified range is within the heap region.
bool IsWithinHeapRegion(VAddr address, u64 size) const;
@@ -509,7 +597,7 @@ public:
/// Each VMManager has its own page table, which is set as the main one when the owning process
/// is scheduled.
- Memory::PageTable page_table;
+ Common::PageTable page_table{Memory::PAGE_BITS};
private:
using VMAIter = VMAMap::iterator;
@@ -624,9 +712,11 @@ private:
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
std::shared_ptr<std::vector<u8>> heap_memory;
- // The left/right bounds of the address space covered by heap_memory.
- VAddr heap_start = 0;
+
+ // The end of the currently allocated heap. This is not an inclusive
+ // end of the range. This is essentially 'base_address + current_size'.
VAddr heap_end = 0;
- u64 heap_used = 0;
+
+ Core::System& system;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
index 90580ed93..0e96ba872 100644
--- a/src/core/hle/kernel/wait_object.cpp
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -30,7 +30,7 @@ void WaitObject::RemoveWaitingThread(Thread* thread) {
waiting_threads.erase(itr);
}
-SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
+SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
Thread* candidate = nullptr;
u32 candidate_priority = THREADPRIO_LOWEST + 1;
@@ -38,8 +38,7 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
const ThreadStatus thread_status = thread->GetStatus();
// The list of waiting threads must not contain threads that are not waiting to be awakened.
- ASSERT_MSG(thread_status == ThreadStatus::WaitSynchAny ||
- thread_status == ThreadStatus::WaitSynchAll ||
+ ASSERT_MSG(thread_status == ThreadStatus::WaitSynch ||
thread_status == ThreadStatus::WaitHLEEvent,
"Inconsistent thread statuses in waiting_threads");
@@ -49,10 +48,10 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
if (ShouldWait(thread.get()))
continue;
- // A thread is ready to run if it's either in ThreadStatus::WaitSynchAny or
- // in ThreadStatus::WaitSynchAll and the rest of the objects it is waiting on are ready.
+ // A thread is ready to run if it's either in ThreadStatus::WaitSynch
+ // and the rest of the objects it is waiting on are ready.
bool ready_to_run = true;
- if (thread_status == ThreadStatus::WaitSynchAll) {
+ if (thread_status == ThreadStatus::WaitSynch) {
ready_to_run = thread->AllWaitObjectsReady();
}
@@ -68,33 +67,35 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) {
ASSERT(!ShouldWait(thread.get()));
- if (!thread)
+ if (!thread) {
return;
+ }
- if (!thread->IsSleepingOnWaitAll()) {
- Acquire(thread.get());
- } else {
+ if (thread->IsSleepingOnWait()) {
for (const auto& object : thread->GetWaitObjects()) {
ASSERT(!object->ShouldWait(thread.get()));
object->Acquire(thread.get());
}
+ } else {
+ Acquire(thread.get());
}
const std::size_t index = thread->GetWaitObjectIndex(this);
- for (const auto& object : thread->GetWaitObjects())
+ for (const auto& object : thread->GetWaitObjects()) {
object->RemoveWaitingThread(thread.get());
+ }
thread->ClearWaitObjects();
thread->CancelWakeupTimer();
bool resume = true;
-
- if (thread->HasWakeupCallback())
+ if (thread->HasWakeupCallback()) {
resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index);
-
- if (resume)
+ }
+ if (resume) {
thread->ResumeFromWait();
+ }
}
void WaitObject::WakeupAllWaitingThreads() {
diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h
index 5987fb971..3271a30a7 100644
--- a/src/core/hle/kernel/wait_object.h
+++ b/src/core/hle/kernel/wait_object.h
@@ -24,7 +24,7 @@ public:
* @param thread The thread about which we're deciding.
* @return True if the current thread should wait due to this object being unavailable
*/
- virtual bool ShouldWait(Thread* thread) const = 0;
+ virtual bool ShouldWait(const Thread* thread) const = 0;
/// Acquire/lock the object for the specified thread if it is available
virtual void Acquire(Thread* thread) = 0;
@@ -54,7 +54,7 @@ public:
void WakeupWaitingThread(SharedPtr<Thread> thread);
/// Obtains the highest priority thread that is ready to run from this object's waiting list.
- SharedPtr<Thread> GetHighestPriorityReadyThread();
+ SharedPtr<Thread> GetHighestPriorityReadyThread() const;
/// Get a const reference to the waiting threads list for debug use
const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const;
diff --git a/src/core/hle/kernel/writable_event.h b/src/core/hle/kernel/writable_event.h
index c9068dd3d..d00c92a6b 100644
--- a/src/core/hle/kernel/writable_event.h
+++ b/src/core/hle/kernel/writable_event.h
@@ -37,7 +37,7 @@ public:
return name;
}
- static const HandleType HANDLE_TYPE = HandleType::WritableEvent;
+ static constexpr HandleType HANDLE_TYPE = HandleType::WritableEvent;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 1ed144481..8a3701151 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -13,14 +13,6 @@
// All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes
/**
- * Detailed description of the error. Code 0 always means success.
- */
-enum class ErrorDescription : u32 {
- Success = 0,
- RemoteProcessDead = 301,
-};
-
-/**
* Identifies the module which caused the error. Error codes can be propagated through a call
* chain, meaning that this doesn't always correspond to the module where the API call made is
* contained.
@@ -120,30 +112,18 @@ enum class ErrorModule : u32 {
ShopN = 811,
};
-/// Encapsulates a CTR-OS error code, allowing it to be separated into its constituent fields.
+/// Encapsulates a Horizon OS error code, allowing it to be separated into its constituent fields.
union ResultCode {
u32 raw;
BitField<0, 9, ErrorModule> module;
BitField<9, 13, u32> description;
- // The last bit of `level` is checked by apps and the kernel to determine if a result code is an
- // error
- BitField<31, 1, u32> is_error;
-
constexpr explicit ResultCode(u32 raw) : raw(raw) {}
- constexpr ResultCode(ErrorModule module, ErrorDescription description)
- : ResultCode(module, static_cast<u32>(description)) {}
-
constexpr ResultCode(ErrorModule module_, u32 description_)
: raw(module.FormatValue(module_) | description.FormatValue(description_)) {}
- constexpr ResultCode& operator=(const ResultCode& o) {
- raw = o.raw;
- return *this;
- }
-
constexpr bool IsSuccess() const {
return raw == 0;
}
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 1f8ed265e..025714e5a 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -10,31 +10,23 @@
#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(UUID uuid) {
+static std::string GetImagePath(Common::UUID uuid) {
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
}
@@ -46,7 +38,7 @@ static constexpr u32 SanitizeJPEGSize(std::size_t size) {
class IProfile final : public ServiceFramework<IProfile> {
public:
- explicit IProfile(UUID user_id, ProfileManager& profile_manager)
+ explicit IProfile(Common::UUID user_id, ProfileManager& profile_manager)
: ServiceFramework("IProfile"), profile_manager(profile_manager), user_id(user_id) {
static const FunctionInfo functions[] = {
{0, &IProfile::Get, "Get"},
@@ -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,19 +116,20 @@ 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()));
}
}
const ProfileManager& profile_manager;
- UUID user_id; ///< The user id this profile refers to.
+ Common::UUID user_id; ///< The user id this profile refers to.
};
class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
public:
IManagerForApplication() : ServiceFramework("IManagerForApplication") {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &IManagerForApplication::CheckAvailability, "CheckAvailability"},
{1, &IManagerForApplication::GetAccountId, "GetAccountId"},
@@ -145,7 +138,10 @@ public:
{130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"},
{150, nullptr, "CreateAuthorizationRequest"},
{160, nullptr, "StoreOpenContext"},
+ {170, nullptr, "LoadNetworkServiceLicenseKindAsync"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
@@ -175,7 +171,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- UUID user_id = rp.PopRaw<UUID>();
+ Common::UUID user_id = rp.PopRaw<Common::UUID>();
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 3};
@@ -201,12 +197,12 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
- rb.PushRaw<UUID>(profile_manager->GetLastOpenedUser());
+ rb.PushRaw<Common::UUID>(profile_manager->GetLastOpenedUser());
}
void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- UUID user_id = rp.PopRaw<UUID>();
+ Common::UUID user_id = rp.PopRaw<Common::UUID>();
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -221,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);
@@ -234,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
@@ -241,15 +262,15 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
IPC::ResponseBuilder rb{ctx, 6};
if (profile_manager->GetUserCount() != 1) {
rb.Push(RESULT_SUCCESS);
- rb.PushRaw<u128>(INVALID_UUID);
+ rb.PushRaw<u128>(Common::INVALID_UUID);
return;
}
const auto user_list = profile_manager->GetAllUsers();
if (std::all_of(user_list.begin(), user_list.end(),
- [](const auto& user) { return user.uuid == INVALID_UUID; })) {
+ [](const auto& user) { return user.uuid == Common::INVALID_UUID; })) {
rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code
- rb.PushRaw<u128>(INVALID_UUID);
+ rb.PushRaw<u128>(Common::INVALID_UUID);
return;
}
@@ -259,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 5e2030355..1b7ec3ed0 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -6,8 +6,10 @@
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"},
{1, &ACC_SU::GetUserExistence, "GetUserExistence"},
@@ -19,6 +21,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
{51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
{60, nullptr, "ListOpenContextStoredUsers"},
+ {99, nullptr, "DebugActivateOpenContextRetention"},
{100, nullptr, "GetUserRegistrationNotifier"},
{101, nullptr, "GetUserStateChangeNotifier"},
{102, nullptr, "GetBaasAccountManagerForSystemService"},
@@ -29,6 +32,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{111, nullptr, "ClearSaveDataThumbnail"},
{112, nullptr, "LoadSaveDataThumbnail"},
{113, nullptr, "GetSaveDataThumbnailExistence"},
+ {130, nullptr, "ActivateOpenContextRetention"},
+ {140, nullptr, "ListQualifiedUsers"},
{190, nullptr, "GetUserLastOpenedApplication"},
{191, nullptr, "ActivateOpenContextHolder"},
{200, nullptr, "BeginUserRegistration"},
@@ -48,6 +53,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{998, nullptr, "DebugSetUserStateClose"},
{999, nullptr, "DebugSetUserStateOpen"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
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 a4d705b45..2f239e8c0 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -6,8 +6,10 @@
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"},
{1, &ACC_U0::GetUserExistence, "GetUserExistence"},
@@ -19,7 +21,8 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
{51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
{60, nullptr, "ListOpenContextStoredUsers"},
- {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
+ {99, nullptr, "DebugActivateOpenContextRetention"},
+ {100, &ACC_U0::InitializeApplicationInfoOld, "InitializeApplicationInfoOld"},
{101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
{102, nullptr, "AuthenticateApplicationAsync"},
{103, nullptr, "CheckNetworkServiceAvailabilityAsync"},
@@ -27,7 +30,13 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{111, nullptr, "ClearSaveDataThumbnail"},
{120, nullptr, "CreateGuestLoginRequest"},
{130, nullptr, "LoadOpenContext"},
+ {131, nullptr, "ListOpenContextStoredUsers"},
+ {140, nullptr, "InitializeApplicationInfo"},
+ {141, nullptr, "ListQualifiedUsers"},
+ {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
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 8fffc93b5..6520b3968 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -6,8 +6,10 @@
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"},
{1, &ACC_U1::GetUserExistence, "GetUserExistence"},
@@ -19,6 +21,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
{51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
{60, nullptr, "ListOpenContextStoredUsers"},
+ {99, nullptr, "DebugActivateOpenContextRetention"},
{100, nullptr, "GetUserRegistrationNotifier"},
{101, nullptr, "GetUserStateChangeNotifier"},
{102, nullptr, "GetBaasAccountManagerForSystemService"},
@@ -29,12 +32,16 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{111, nullptr, "ClearSaveDataThumbnail"},
{112, nullptr, "LoadSaveDataThumbnail"},
{113, nullptr, "GetSaveDataThumbnailExistence"},
+ {130, nullptr, "ActivateOpenContextRetention"},
+ {140, nullptr, "ListQualifiedUsers"},
{190, nullptr, "GetUserLastOpenedApplication"},
{191, nullptr, "ActivateOpenContextHolder"},
{997, nullptr, "DebugInvalidateTokenCacheForUser"},
{998, nullptr, "DebugSetUserStateClose"},
{999, nullptr, "DebugSetUserStateOpen"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
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/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 1316d0b07..49aa5908b 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -13,6 +13,8 @@
namespace Service::Account {
+using Common::UUID;
+
struct UserRaw {
UUID uuid;
UUID uuid2;
@@ -35,26 +37,6 @@ constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/";
-UUID UUID::Generate() {
- std::random_device device;
- std::mt19937 gen(device());
- std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
- return UUID{distribution(gen), distribution(gen)};
-}
-
-std::string UUID::Format() const {
- return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
-}
-
-std::string UUID::FormatSwitch() const {
- std::array<u8, 16> s{};
- std::memcpy(s.data(), uuid.data(), sizeof(u128));
- return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
- ":02x}{:02x}{:02x}{:02x}{:02x}",
- s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
- s[12], s[13], s[14], s[15]);
-}
-
ProfileManager::ProfileManager() {
ParseUserSaveFile();
@@ -217,7 +199,7 @@ bool ProfileManager::UserExists(UUID uuid) const {
bool ProfileManager::UserExistsIndex(std::size_t index) const {
if (index >= MAX_USERS)
return false;
- return profiles[index].user_uuid.uuid != INVALID_UUID;
+ return profiles[index].user_uuid.uuid != Common::INVALID_UUID;
}
/// Opens a specific user
@@ -311,7 +293,7 @@ bool ProfileManager::RemoveUser(UUID uuid) {
bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
const auto index = GetUserIndex(uuid);
- if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) {
+ if (!index || profile_new.user_uuid == UUID(Common::INVALID_UUID)) {
return false;
}
@@ -342,7 +324,7 @@ void ProfileManager::ParseUserSaveFile() {
}
for (const auto& user : data.users) {
- if (user.uuid == UUID(INVALID_UUID)) {
+ if (user.uuid == UUID(Common::INVALID_UUID)) {
continue;
}
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index c4ce2e0b3..fd7abb541 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -9,47 +9,15 @@
#include "common/common_types.h"
#include "common/swap.h"
+#include "common/uuid.h"
#include "core/hle/result.h"
namespace Service::Account {
constexpr std::size_t MAX_USERS = 8;
-constexpr u128 INVALID_UUID{{0, 0}};
-
-struct UUID {
- // UUIDs which are 0 are considered invalid!
- u128 uuid = INVALID_UUID;
- UUID() = default;
- explicit UUID(const u128& id) : uuid{id} {}
- explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
-
- explicit operator bool() const {
- return uuid != INVALID_UUID;
- }
-
- bool operator==(const UUID& rhs) const {
- return uuid == rhs.uuid;
- }
-
- bool operator!=(const UUID& rhs) const {
- return !operator==(rhs);
- }
-
- // TODO(ogniK): Properly generate uuids based on RFC-4122
- static UUID Generate();
-
- // Set the UUID to {0,0} to be considered an invalid user
- void Invalidate() {
- uuid = INVALID_UUID;
- }
-
- std::string Format() const;
- std::string FormatSwitch() const;
-};
-static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
constexpr std::size_t profile_username_size = 32;
using ProfileUsername = std::array<u8, profile_username_size>;
-using UserIDArray = std::array<UUID, MAX_USERS>;
+using UserIDArray = std::array<Common::UUID, MAX_USERS>;
/// Contains extra data related to a user.
/// TODO: RE this structure
@@ -66,7 +34,7 @@ static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect
/// This holds general information about a users profile. This is where we store all the information
/// based on a specific user
struct ProfileInfo {
- UUID user_uuid;
+ Common::UUID user_uuid;
ProfileUsername username;
u64 creation_time;
ProfileData data; // TODO(ognik): Work out what this is
@@ -74,7 +42,7 @@ struct ProfileInfo {
};
struct ProfileBase {
- UUID user_uuid;
+ Common::UUID user_uuid;
u64_le timestamp;
ProfileUsername username;
@@ -96,33 +64,33 @@ public:
~ProfileManager();
ResultCode AddUser(const ProfileInfo& user);
- ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username);
- ResultCode CreateNewUser(UUID uuid, const std::string& username);
- std::optional<UUID> GetUser(std::size_t index) const;
- std::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
+ ResultCode CreateNewUser(Common::UUID uuid, const ProfileUsername& username);
+ ResultCode CreateNewUser(Common::UUID uuid, const std::string& username);
+ std::optional<Common::UUID> GetUser(std::size_t index) const;
+ std::optional<std::size_t> GetUserIndex(const Common::UUID& uuid) const;
std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const;
- bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
+ bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const;
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
ProfileData& data) const;
- bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const;
+ bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, ProfileData& data) const;
bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
ProfileData& data) const;
std::size_t GetUserCount() const;
std::size_t GetOpenUserCount() const;
- bool UserExists(UUID uuid) const;
+ bool UserExists(Common::UUID uuid) const;
bool UserExistsIndex(std::size_t index) const;
- void OpenUser(UUID uuid);
- void CloseUser(UUID uuid);
+ void OpenUser(Common::UUID uuid);
+ void CloseUser(Common::UUID uuid);
UserIDArray GetOpenUsers() const;
UserIDArray GetAllUsers() const;
- UUID GetLastOpenedUser() const;
+ Common::UUID GetLastOpenedUser() const;
bool CanSystemRegisterUser() const;
- bool RemoveUser(UUID uuid);
- bool SetProfileBase(UUID uuid, const ProfileBase& profile_new);
+ bool RemoveUser(Common::UUID uuid);
+ bool SetProfileBase(Common::UUID uuid, const ProfileBase& profile_new);
private:
void ParseUserSaveFile();
@@ -132,7 +100,7 @@ private:
std::array<ProfileInfo, MAX_USERS> profiles{};
std::size_t user_count = 0;
- UUID last_opened_user{INVALID_UUID};
+ Common::UUID last_opened_user{Common::INVALID_UUID};
};
}; // namespace Service::Account
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 3f009d2b7..4a7bf4acb 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -2,18 +2,20 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <array>
#include <cinttypes>
#include <cstring>
-#include <stack>
#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"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/shared_memory.h"
+#include "core/hle/kernel/transfer_memory.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/am.h"
@@ -22,7 +24,6 @@
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/am/applets/profile_select.h"
#include "core/hle/service/am/applets/software_keyboard.h"
-#include "core/hle/service/am/applets/stub_applet.h"
#include "core/hle/service/am/applets/web_browser.h"
#include "core/hle/service/am/idle.h"
#include "core/hle/service/am/omm.h"
@@ -30,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"
@@ -42,12 +45,6 @@ constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3};
constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
-enum class AppletId : u32 {
- ProfileSelect = 0x10,
- SoftwareKeyboard = 0x11,
- LibAppletOff = 0x17,
-};
-
constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
struct LaunchParameters {
@@ -93,38 +90,84 @@ void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx)
}
IAudioController::IAudioController() : ServiceFramework("IAudioController") {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"},
- {1, &IAudioController::GetMainAppletExpectedMasterVolume,
- "GetMainAppletExpectedMasterVolume"},
- {2, &IAudioController::GetLibraryAppletExpectedMasterVolume,
- "GetLibraryAppletExpectedMasterVolume"},
- {3, nullptr, "ChangeMainAppletMasterVolume"},
- {4, nullptr, "SetTransparentVolumeRate"},
+ {1, &IAudioController::GetMainAppletExpectedMasterVolume, "GetMainAppletExpectedMasterVolume"},
+ {2, &IAudioController::GetLibraryAppletExpectedMasterVolume, "GetLibraryAppletExpectedMasterVolume"},
+ {3, &IAudioController::ChangeMainAppletMasterVolume, "ChangeMainAppletMasterVolume"},
+ {4, &IAudioController::SetTransparentAudioRate, "SetTransparentVolumeRate"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
IAudioController::~IAudioController() = default;
void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const float main_applet_volume_tmp = rp.Pop<float>();
+ const float library_applet_volume_tmp = rp.Pop<float>();
+
+ LOG_DEBUG(Service_AM, "called. main_applet_volume={}, library_applet_volume={}",
+ main_applet_volume_tmp, library_applet_volume_tmp);
+
+ // Ensure the volume values remain within the 0-100% range
+ main_applet_volume = std::clamp(main_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
+ library_applet_volume =
+ std::clamp(library_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ LOG_DEBUG(Service_AM, "called. main_applet_volume={}", main_applet_volume);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push(volume);
+ rb.Push(main_applet_volume);
}
void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ LOG_DEBUG(Service_AM, "called. library_applet_volume={}", library_applet_volume);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push(volume);
+ rb.Push(library_applet_volume);
+}
+
+void IAudioController::ChangeMainAppletMasterVolume(Kernel::HLERequestContext& ctx) {
+ struct Parameters {
+ float volume;
+ s64 fade_time_ns;
+ };
+ static_assert(sizeof(Parameters) == 16);
+
+ IPC::RequestParser rp{ctx};
+ const auto parameters = rp.PopRaw<Parameters>();
+
+ LOG_DEBUG(Service_AM, "called. volume={}, fade_time_ns={}", parameters.volume,
+ parameters.fade_time_ns);
+
+ main_applet_volume = std::clamp(parameters.volume, min_allowed_volume, max_allowed_volume);
+ fade_time_ns = std::chrono::nanoseconds{parameters.fade_time_ns};
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void IAudioController::SetTransparentAudioRate(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const float transparent_volume_rate_tmp = rp.Pop<float>();
+
+ LOG_DEBUG(Service_AM, "called. transparent_volume_rate={}", transparent_volume_rate_tmp);
+
+ // Clamp volume range to 0-100%.
+ transparent_volume_rate =
+ std::clamp(transparent_volume_rate_tmp, min_allowed_volume, max_allowed_volume);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
}
IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {
@@ -169,7 +212,22 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
IDisplayController::~IDisplayController() = default;
-IDebugFunctions::IDebugFunctions() : ServiceFramework("IDebugFunctions") {}
+IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "NotifyMessageToHomeMenuForDebug"},
+ {1, nullptr, "OpenMainApplication"},
+ {10, nullptr, "EmulateButtonEvent"},
+ {20, nullptr, "InvalidateTransitionLayer"},
+ {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"},
+ {40, nullptr, "GetAppletResourceUsageInfo"},
+ {41, nullptr, "SetCpuBoostModeForApplet"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
IDebugFunctions::~IDebugFunctions() = default;
ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
@@ -179,8 +237,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
{0, nullptr, "Exit"},
{1, &ISelfController::LockExit, "LockExit"},
{2, &ISelfController::UnlockExit, "UnlockExit"},
- {3, nullptr, "EnterFatalSection"},
- {4, nullptr, "LeaveFatalSection"},
+ {3, &ISelfController::EnterFatalSection, "EnterFatalSection"},
+ {4, &ISelfController::LeaveFatalSection, "LeaveFatalSection"},
{9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
{10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
{11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"},
@@ -196,6 +254,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
{40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
{41, nullptr, "IsSystemBufferSharingEnabled"},
{42, nullptr, "GetSystemSharedLayerHandle"},
+ {43, nullptr, "GetSystemSharedBufferHandle"},
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
{51, nullptr, "ApproveToDisplay"},
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
@@ -209,9 +268,11 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
{68, nullptr, "SetAutoSleepDisabled"},
{69, nullptr, "IsAutoSleepDisabled"},
{70, nullptr, "ReportMultimediaError"},
+ {71, nullptr, "GetCurrentIlluminanceEx"},
{80, nullptr, "SetWirelessPriorityMode"},
{90, nullptr, "GetAccumulatedSuspendedTickValue"},
- {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"},
+ {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"},
+ {100, nullptr, "SetAlbumImageTakenNotificationEnabled"},
{1000, nullptr, "GetDebugStorageChannel"},
};
// clang-format on
@@ -219,47 +280,65 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
+ 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;
-void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
- // Takes 3 input u8s with each field located immediately after the previous
- // u8, these are bool flags. No output.
+void ISelfController::LockExit(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
- IPC::RequestParser rp{ctx};
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- struct FocusHandlingModeParams {
- u8 unknown0;
- u8 unknown1;
- u8 unknown2;
- };
- auto flags = rp.PopRaw<FocusHandlingModeParams>();
+void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+void ISelfController::EnterFatalSection(Kernel::HLERequestContext& ctx) {
+ ++num_fatal_sections_entered;
+ LOG_DEBUG(Service_AM, "called. Num fatal sections entered: {}", num_fatal_sections_entered);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
+void ISelfController::LeaveFatalSection(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called.");
- bool flag = rp.Pop<bool>();
- LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag);
+ // Entry and exit of fatal sections must be balanced.
+ if (num_fatal_sections_entered == 0) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultCode{ErrorModule::AM, 512});
+ return;
+ }
+
+ --num_fatal_sections_entered;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ launchable_event.writable->Signal();
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(launchable_event.readable);
+}
+
void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
@@ -277,40 +356,52 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont
rb.Push(RESULT_SUCCESS);
}
-void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
- // Takes 3 input u8s with each field located immediately after the previous
- // u8, these are bool flags. No output.
+void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- bool enabled = rp.Pop<bool>();
- LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled);
+ bool flag = rp.Pop<bool>();
+ LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void ISelfController::LockExit(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
+ // Takes 3 input u8s with each field located immediately after the previous
+ // u8, these are bool flags. No output.
+ IPC::RequestParser rp{ctx};
+
+ struct FocusHandlingModeParams {
+ u8 unknown0;
+ u8 unknown1;
+ u8 unknown2;
+ };
+ const auto flags = rp.PopRaw<FocusHandlingModeParams>();
+
+ LOG_WARNING(Service_AM, "(STUBBED) called. unknown0={}, unknown1={}, unknown2={}",
+ flags.unknown0, flags.unknown1, flags.unknown2);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) {
+void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
+ // Takes 3 input u8s with each field located immediately after the previous
+ // u8, these are bool flags. No output.
+ IPC::RequestParser rp{ctx};
- launchable_event.writable->Signal();
+ bool enabled = rp.Pop<bool>();
+ LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled);
- IPC::ResponseBuilder rb{ctx, 2, 1};
+ IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(launchable_event.readable);
}
void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) {
@@ -358,12 +449,23 @@ 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::Sticky,
+ on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
"AMMessageQueue:OnMessageRecieved");
on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::OneShot, "AMMessageQueue:OperationModeChanged");
+ kernel, Kernel::ResetType::Automatic, "AMMessageQueue:OperationModeChanged");
}
AppletMessageQueue::~AppletMessageQueue() = default;
@@ -431,11 +533,20 @@ ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_q
{50, nullptr, "IsVrModeEnabled"},
{51, nullptr, "SetVrModeEnabled"},
{52, nullptr, "SwitchLcdBacklight"},
+ {53, nullptr, "BeginVrModeEx"},
+ {54, nullptr, "EndVrModeEx"},
{55, nullptr, "IsInControllerFirmwareUpdateSection"},
{60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
{61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
{62, nullptr, "GetHdcpAuthenticationState"},
{63, nullptr, "GetHdcpAuthenticationStateChangeEvent"},
+ {64, nullptr, "SetTvPowerStateMatchingMode"},
+ {65, nullptr, "GetApplicationIdByContentActionName"},
+ {66, nullptr, "SetCpuBoostMode"},
+ {80, nullptr, "PerformSystemButtonPressingIfInFocus"},
+ {90, nullptr, "SetPerformanceConfigurationChangedNotification"},
+ {91, nullptr, "GetCurrentPerformanceConfiguration"},
+ {200, nullptr, "GetOperationModeSystemInfo"},
};
// clang-format on
@@ -744,6 +855,7 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
+ return;
}
std::memcpy(backing.buffer.data() + offset, data.data(), data.size());
@@ -766,6 +878,7 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
+ return;
}
ctx.WriteBuffer(backing.buffer.data() + offset, size);
@@ -788,30 +901,16 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
ILibraryAppletCreator::~ILibraryAppletCreator() = default;
-static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
- switch (id) {
- case AppletId::ProfileSelect:
- return std::make_shared<Applets::ProfileSelect>();
- case AppletId::SoftwareKeyboard:
- return std::make_shared<Applets::SoftwareKeyboard>();
- case AppletId::LibAppletOff:
- return std::make_shared<Applets::WebBrowser>();
- default:
- LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!",
- static_cast<u32>(id));
- return std::make_shared<Applets::StubApplet>();
- }
-}
-
void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto applet_id = rp.PopRaw<AppletId>();
+ const auto applet_id = rp.PopRaw<Applets::AppletId>();
const auto applet_mode = rp.PopRaw<u32>();
LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}",
static_cast<u32>(applet_id), applet_mode);
- const auto applet = GetAppletFromId(applet_id);
+ const auto& applet_manager{Core::System::GetInstance().GetAppletManager()};
+ const auto applet = applet_manager.GetApplet(applet_id);
if (applet == nullptr) {
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id));
@@ -847,19 +946,19 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
rp.SetCurrentOffset(3);
const auto handle{rp.Pop<Kernel::Handle>()};
- const auto shared_mem =
- Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::SharedMemory>(
+ const auto transfer_mem =
+ Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(
handle);
- if (shared_mem == nullptr) {
+ if (transfer_mem == nullptr) {
LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(-1));
return;
}
- const u8* mem_begin = shared_mem->GetPointer();
- const u8* mem_end = mem_begin + shared_mem->GetSize();
+ const u8* const mem_begin = transfer_mem->GetPointer();
+ const u8* const mem_end = mem_begin + transfer_mem->GetSize();
std::vector<u8> memory{mem_begin, mem_end};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -875,6 +974,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
{11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
{12, nullptr, "CreateApplicationAndRequestToStart"},
{13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"},
+ {14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"},
+ {15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"},
{20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
{21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
@@ -1019,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) {
@@ -1148,6 +1281,7 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat
{2, nullptr, "StartSleepSequence"},
{3, nullptr, "StartShutdownSequence"},
{4, nullptr, "StartRebootSequence"},
+ {9, nullptr, "IsAutoPowerDownRequested"},
{10, nullptr, "LoadAndApplyIdlePolicySettings"},
{11, nullptr, "NotifyCecSettingsChanged"},
{12, nullptr, "SetDefaultHomeButtonLongPressTime"},
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index b6113cfdd..1fa069e56 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -4,6 +4,7 @@
#pragma once
+#include <chrono>
#include <memory>
#include <queue>
#include "core/hle/kernel/writable_event.h"
@@ -81,8 +82,21 @@ private:
void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx);
void GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
void GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
+ void ChangeMainAppletMasterVolume(Kernel::HLERequestContext& ctx);
+ void SetTransparentAudioRate(Kernel::HLERequestContext& ctx);
- u32 volume{100};
+ static constexpr float min_allowed_volume = 0.0f;
+ static constexpr float max_allowed_volume = 1.0f;
+
+ float main_applet_volume{0.25f};
+ float library_applet_volume{max_allowed_volume};
+ float transparent_volume_rate{min_allowed_volume};
+
+ // Volume transition fade time in nanoseconds.
+ // e.g. If the main applet volume was 0% and was changed to 50%
+ // with a fade of 50ns, then over the course of 50ns,
+ // the volume will gradually fade up to 50%
+ std::chrono::nanoseconds fade_time_ns{0};
};
class IDisplayController final : public ServiceFramework<IDisplayController> {
@@ -103,24 +117,30 @@ public:
~ISelfController() override;
private:
- void SetFocusHandlingMode(Kernel::HLERequestContext& ctx);
- void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx);
- void SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx);
- void SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx);
- void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx);
void LockExit(Kernel::HLERequestContext& ctx);
void UnlockExit(Kernel::HLERequestContext& ctx);
+ void EnterFatalSection(Kernel::HLERequestContext& ctx);
+ void LeaveFatalSection(Kernel::HLERequestContext& ctx);
void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx);
+ void SetScreenShotPermission(Kernel::HLERequestContext& ctx);
+ void SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx);
+ void SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx);
+ void SetFocusHandlingMode(Kernel::HLERequestContext& ctx);
+ void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx);
+ void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx);
void SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx);
void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
- void SetScreenShotPermission(Kernel::HLERequestContext& ctx);
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;
};
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index b888f861d..488add8e7 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -16,6 +16,7 @@ public:
std::shared_ptr<AppletMessageQueue> msg_queue)
: ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)),
msg_queue(std::move(msg_queue)) {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"},
@@ -25,8 +26,11 @@ public:
{10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"},
{11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
{20, &ILibraryAppletProxy::GetApplicationFunctions, "GetApplicationFunctions"},
+ {21, nullptr, "GetAppletCommonFunctions"},
{1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
@@ -113,6 +117,7 @@ public:
std::shared_ptr<AppletMessageQueue> msg_queue)
: ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)),
msg_queue(std::move(msg_queue)) {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &ISystemAppletProxy::GetSelfController, "GetSelfController"},
@@ -124,8 +129,11 @@ public:
{20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
{21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
{22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"},
+ {23, nullptr, "GetAppletCommonFunctions"},
{1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index a6064c63f..14fa92318 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -5,22 +5,32 @@
#include <cstring>
#include "common/assert.h"
#include "core/core.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/hle/kernel/readable_event.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applets.h"
+#include "core/hle/service/am/applets/error.h"
+#include "core/hle/service/am/applets/general_backend.h"
+#include "core/hle/service/am/applets/profile_select.h"
+#include "core/hle/service/am/applets/software_keyboard.h"
+#include "core/hle/service/am/applets/web_browser.h"
namespace Service::AM::Applets {
AppletDataBroker::AppletDataBroker() {
auto& kernel = Core::System::GetInstance().Kernel();
state_changed_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:StateChangedEvent");
+ kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:StateChangedEvent");
pop_out_data_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopDataOutEvent");
+ kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopDataOutEvent");
pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
+ kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
}
AppletDataBroker::~AppletDataBroker() = default;
@@ -111,4 +121,91 @@ 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;
+
+void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
+ if (set.error != nullptr)
+ frontend.error = std::move(set.error);
+ if (set.photo_viewer != nullptr)
+ frontend.photo_viewer = std::move(set.photo_viewer);
+ if (set.profile_select != nullptr)
+ frontend.profile_select = std::move(set.profile_select);
+ if (set.software_keyboard != nullptr)
+ frontend.software_keyboard = std::move(set.software_keyboard);
+ if (set.web_browser != nullptr)
+ frontend.web_browser = std::move(set.web_browser);
+}
+
+void AppletManager::SetDefaultAppletFrontendSet() {
+ frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
+ frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
+ frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
+ frontend.software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
+ frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
+}
+
+void AppletManager::SetDefaultAppletsIfMissing() {
+ if (frontend.error == nullptr) {
+ frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
+ }
+
+ if (frontend.photo_viewer == nullptr) {
+ frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
+ }
+
+ if (frontend.profile_select == nullptr) {
+ frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
+ }
+
+ if (frontend.software_keyboard == nullptr) {
+ frontend.software_keyboard =
+ std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
+ }
+
+ if (frontend.web_browser == nullptr) {
+ frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
+ }
+}
+
+void AppletManager::ClearAll() {
+ frontend = {};
+}
+
+std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
+ switch (id) {
+ case AppletId::Error:
+ return std::make_shared<Error>(*frontend.error);
+ case AppletId::ProfileSelect:
+ return std::make_shared<ProfileSelect>(*frontend.profile_select);
+ case AppletId::SoftwareKeyboard:
+ return std::make_shared<SoftwareKeyboard>(*frontend.software_keyboard);
+ case AppletId::PhotoViewer:
+ return std::make_shared<PhotoViewer>(*frontend.photo_viewer);
+ case AppletId::LibAppletOff:
+ return std::make_shared<WebBrowser>(*frontend.web_browser);
+ default:
+ UNIMPLEMENTED_MSG(
+ "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
+ static_cast<u8>(id));
+ return std::make_shared<StubApplet>();
+ }
+}
+
} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 37424c379..b46e10a4a 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -12,12 +12,43 @@
union ResultCode;
+namespace Core::Frontend {
+class ErrorApplet;
+class PhotoViewerApplet;
+class ProfileSelectApplet;
+class SoftwareKeyboardApplet;
+class WebBrowserApplet;
+} // namespace Core::Frontend
+
namespace Service::AM {
class IStorage;
namespace Applets {
+enum class AppletId : u32 {
+ OverlayDisplay = 0x02,
+ QLaunch = 0x03,
+ Starter = 0x04,
+ Auth = 0x0A,
+ Cabinet = 0x0B,
+ Controller = 0x0C,
+ DataErase = 0x0D,
+ Error = 0x0E,
+ NetConnect = 0x0F,
+ ProfileSelect = 0x10,
+ SoftwareKeyboard = 0x11,
+ MiiEdit = 0x12,
+ LibAppletWeb = 0x13,
+ LibAppletShop = 0x14,
+ PhotoViewer = 0x15,
+ Settings = 0x16,
+ LibAppletOff = 0x17,
+ LibAppletWhitelisted = 0x18,
+ LibAppletAuth = 0x19,
+ MyPage = 0x1A,
+};
+
class AppletDataBroker final {
public:
AppletDataBroker();
@@ -105,5 +136,46 @@ protected:
bool initialized = false;
};
+struct AppletFrontendSet {
+ 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 {
+public:
+ AppletManager();
+ ~AppletManager();
+
+ void SetAppletFrontendSet(AppletFrontendSet set);
+ void SetDefaultAppletFrontendSet();
+ void SetDefaultAppletsIfMissing();
+ void ClearAll();
+
+ std::shared_ptr<Applet> GetApplet(AppletId id) const;
+
+private:
+ AppletFrontendSet frontend;
+};
+
} // namespace Applets
} // namespace Service::AM
diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp
new file mode 100644
index 000000000..04774bedc
--- /dev/null
+++ b/src/core/hle/service/am/applets/error.cpp
@@ -0,0 +1,182 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include <cstring>
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/frontend/applets/error.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applets/error.h"
+
+namespace Service::AM::Applets {
+
+#pragma pack(push, 4)
+struct ShowError {
+ u8 mode;
+ bool jump;
+ INSERT_PADDING_BYTES(4);
+ bool use_64bit_error_code;
+ INSERT_PADDING_BYTES(1);
+ u64 error_code_64;
+ u32 error_code_32;
+};
+static_assert(sizeof(ShowError) == 0x14, "ShowError has incorrect size.");
+#pragma pack(pop)
+
+struct ShowErrorRecord {
+ u8 mode;
+ bool jump;
+ INSERT_PADDING_BYTES(6);
+ u64 error_code_64;
+ u64 posix_time;
+};
+static_assert(sizeof(ShowErrorRecord) == 0x18, "ShowErrorRecord has incorrect size.");
+
+struct SystemErrorArg {
+ u8 mode;
+ bool jump;
+ INSERT_PADDING_BYTES(6);
+ u64 error_code_64;
+ std::array<char, 8> language_code;
+ std::array<char, 0x800> main_text;
+ std::array<char, 0x800> detail_text;
+};
+static_assert(sizeof(SystemErrorArg) == 0x1018, "SystemErrorArg has incorrect size.");
+
+struct ApplicationErrorArg {
+ u8 mode;
+ bool jump;
+ INSERT_PADDING_BYTES(6);
+ u32 error_code;
+ std::array<char, 8> language_code;
+ std::array<char, 0x800> main_text;
+ std::array<char, 0x800> detail_text;
+};
+static_assert(sizeof(ApplicationErrorArg) == 0x1014, "ApplicationErrorArg has incorrect size.");
+
+union Error::ErrorArguments {
+ ShowError error;
+ ShowErrorRecord error_record;
+ SystemErrorArg system_error;
+ ApplicationErrorArg application_error;
+};
+
+namespace {
+template <typename T>
+void CopyArgumentData(const std::vector<u8>& data, T& variable) {
+ ASSERT(data.size() >= sizeof(T));
+ std::memcpy(&variable, data.data(), sizeof(T));
+}
+
+ResultCode Decode64BitError(u64 error) {
+ const auto description = (error >> 32) & 0x1FFF;
+ auto module = error & 0x3FF;
+ if (module >= 2000)
+ module -= 2000;
+ module &= 0x1FF;
+ return {static_cast<ErrorModule>(module), static_cast<u32>(description)};
+}
+
+} // Anonymous namespace
+
+Error::Error(const Core::Frontend::ErrorApplet& frontend) : frontend(frontend) {}
+
+Error::~Error() = default;
+
+void Error::Initialize() {
+ Applet::Initialize();
+ args = std::make_unique<ErrorArguments>();
+ complete = false;
+
+ const auto storage = broker.PopNormalDataToApplet();
+ ASSERT(storage != nullptr);
+ const auto data = storage->GetData();
+
+ ASSERT(!data.empty());
+ std::memcpy(&mode, data.data(), sizeof(ErrorAppletMode));
+
+ switch (mode) {
+ case ErrorAppletMode::ShowError:
+ CopyArgumentData(data, args->error);
+ if (args->error.use_64bit_error_code) {
+ error_code = Decode64BitError(args->error.error_code_64);
+ } else {
+ error_code = ResultCode(args->error.error_code_32);
+ }
+ break;
+ case ErrorAppletMode::ShowSystemError:
+ CopyArgumentData(data, args->system_error);
+ error_code = ResultCode(Decode64BitError(args->system_error.error_code_64));
+ break;
+ case ErrorAppletMode::ShowApplicationError:
+ CopyArgumentData(data, args->application_error);
+ error_code = ResultCode(args->application_error.error_code);
+ break;
+ case ErrorAppletMode::ShowErrorRecord:
+ CopyArgumentData(data, args->error_record);
+ error_code = Decode64BitError(args->error_record.error_code_64);
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", static_cast<u8>(mode));
+ }
+}
+
+bool Error::TransactionComplete() const {
+ return complete;
+}
+
+ResultCode Error::GetStatus() const {
+ return RESULT_SUCCESS;
+}
+
+void Error::ExecuteInteractive() {
+ UNREACHABLE_MSG("Unexpected interactive applet data!");
+}
+
+void Error::Execute() {
+ if (complete) {
+ return;
+ }
+
+ const auto callback = [this] { DisplayCompleted(); };
+
+ switch (mode) {
+ case ErrorAppletMode::ShowError:
+ frontend.ShowError(error_code, callback);
+ break;
+ case ErrorAppletMode::ShowSystemError:
+ case ErrorAppletMode::ShowApplicationError: {
+ const auto system = mode == ErrorAppletMode::ShowSystemError;
+ const auto& main_text =
+ system ? args->system_error.main_text : args->application_error.main_text;
+ 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);
+ break;
+ }
+ case ErrorAppletMode::ShowErrorRecord:
+ frontend.ShowErrorWithTimestamp(
+ error_code, std::chrono::seconds{args->error_record.posix_time}, callback);
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", static_cast<u8>(mode));
+ DisplayCompleted();
+ }
+}
+
+void Error::DisplayCompleted() {
+ complete = true;
+ broker.PushNormalDataFromApplet(IStorage{{}});
+ broker.SignalStateChanged();
+}
+
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/error.h b/src/core/hle/service/am/applets/error.h
new file mode 100644
index 000000000..a3590d181
--- /dev/null
+++ b/src/core/hle/service/am/applets/error.h
@@ -0,0 +1,47 @@
+// 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"
+#include "core/hle/service/am/applets/applets.h"
+
+namespace Service::AM::Applets {
+
+enum class ErrorAppletMode : u8 {
+ ShowError = 0,
+ ShowSystemError = 1,
+ ShowApplicationError = 2,
+ ShowEula = 3,
+ ShowErrorPctl = 4,
+ ShowErrorRecord = 5,
+ ShowUpdateEula = 8,
+};
+
+class Error final : public Applet {
+public:
+ explicit Error(const Core::Frontend::ErrorApplet& frontend);
+ ~Error() override;
+
+ void Initialize() override;
+
+ bool TransactionComplete() const override;
+ ResultCode GetStatus() const override;
+ void ExecuteInteractive() override;
+ void Execute() override;
+
+ void DisplayCompleted();
+
+private:
+ union ErrorArguments;
+
+ const Core::Frontend::ErrorApplet& frontend;
+ ResultCode error_code = RESULT_SUCCESS;
+ ErrorAppletMode mode = ErrorAppletMode::ShowError;
+ std::unique_ptr<ErrorArguments> args;
+
+ bool complete = false;
+};
+
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/stub_applet.cpp b/src/core/hle/service/am/applets/general_backend.cpp
index ed166b87d..76fc8906d 100644
--- a/src/core/hle/service/am/applets/stub_applet.cpp
+++ b/src/core/hle/service/am/applets/general_backend.cpp
@@ -2,34 +2,87 @@
// 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"
#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/frontend/applets/general_frontend.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/result.h"
#include "core/hle/service/am/am.h"
-#include "core/hle/service/am/applets/stub_applet.h"
+#include "core/hle/service/am/applets/general_backend.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));
}
}
+PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {}
+
+PhotoViewer::~PhotoViewer() = default;
+
+void PhotoViewer::Initialize() {
+ Applet::Initialize();
+ complete = false;
+
+ const auto storage = broker.PopNormalDataToApplet();
+ ASSERT(storage != nullptr);
+ const auto data = storage->GetData();
+ ASSERT(!data.empty());
+ mode = static_cast<PhotoViewerAppletMode>(data[0]);
+}
+
+bool PhotoViewer::TransactionComplete() const {
+ return complete;
+}
+
+ResultCode PhotoViewer::GetStatus() const {
+ return RESULT_SUCCESS;
+}
+
+void PhotoViewer::ExecuteInteractive() {
+ UNREACHABLE_MSG("Unexpected interactive applet data.");
+}
+
+void PhotoViewer::Execute() {
+ if (complete)
+ return;
+
+ const auto callback = [this] { ViewFinished(); };
+ switch (mode) {
+ case PhotoViewerAppletMode::CurrentApp:
+ frontend.ShowPhotosForApplication(Core::CurrentProcess()->GetTitleID(), callback);
+ break;
+ case PhotoViewerAppletMode::AllApps:
+ frontend.ShowAllPhotos(callback);
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", static_cast<u8>(mode));
+ }
+}
+
+void PhotoViewer::ViewFinished() {
+ broker.PushNormalDataFromApplet(IStorage{{}});
+ broker.SignalStateChanged();
+}
+
StubApplet::StubApplet() = default;
StubApplet::~StubApplet() = default;
@@ -67,4 +120,5 @@ void StubApplet::Execute() {
broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
broker.SignalStateChanged();
}
+
} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h
new file mode 100644
index 000000000..2dd255d7c
--- /dev/null
+++ b/src/core/hle/service/am/applets/general_backend.h
@@ -0,0 +1,48 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/am/applets/applets.h"
+
+namespace Service::AM::Applets {
+
+enum class PhotoViewerAppletMode : u8 {
+ CurrentApp = 0,
+ AllApps = 1,
+};
+
+class PhotoViewer final : public Applet {
+public:
+ explicit PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend);
+ ~PhotoViewer() override;
+
+ void Initialize() override;
+ bool TransactionComplete() const override;
+ ResultCode GetStatus() const override;
+ void ExecuteInteractive() override;
+ void Execute() override;
+
+ void ViewFinished();
+
+private:
+ const Core::Frontend::PhotoViewerApplet& frontend;
+ bool complete = false;
+ PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp;
+};
+
+class StubApplet final : public Applet {
+public:
+ StubApplet();
+ ~StubApplet() override;
+
+ void Initialize() override;
+
+ bool TransactionComplete() const override;
+ ResultCode GetStatus() const override;
+ void ExecuteInteractive() override;
+ void Execute() override;
+};
+
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp
index 14e2a1fee..57b5419e8 100644
--- a/src/core/hle/service/am/applets/profile_select.cpp
+++ b/src/core/hle/service/am/applets/profile_select.cpp
@@ -15,7 +15,9 @@ namespace Service::AM::Applets {
constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1};
-ProfileSelect::ProfileSelect() = default;
+ProfileSelect::ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend)
+ : frontend(frontend) {}
+
ProfileSelect::~ProfileSelect() = default;
void ProfileSelect::Initialize() {
@@ -51,21 +53,19 @@ void ProfileSelect::Execute() {
return;
}
- const auto& frontend{Core::System::GetInstance().GetProfileSelector()};
-
- frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); });
+ frontend.SelectProfile([this](std::optional<Common::UUID> uuid) { SelectionComplete(uuid); });
}
-void ProfileSelect::SelectionComplete(std::optional<Account::UUID> uuid) {
+void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
UserSelectionOutput output{};
- if (uuid.has_value() && uuid->uuid != Account::INVALID_UUID) {
+ if (uuid.has_value() && uuid->uuid != Common::INVALID_UUID) {
output.result = 0;
output.uuid_selected = uuid->uuid;
} else {
status = ERR_USER_CANCELLED_SELECTION;
output.result = ERR_USER_CANCELLED_SELECTION.raw;
- output.uuid_selected = Account::INVALID_UUID;
+ output.uuid_selected = Common::INVALID_UUID;
}
final_data = std::vector<u8>(sizeof(UserSelectionOutput));
diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h
index 787485f22..563cd744a 100644
--- a/src/core/hle/service/am/applets/profile_select.h
+++ b/src/core/hle/service/am/applets/profile_select.h
@@ -7,7 +7,8 @@
#include <vector>
#include "common/common_funcs.h"
-#include "core/hle/service/acc/profile_manager.h"
+#include "common/uuid.h"
+#include "core/hle/result.h"
#include "core/hle/service/am/applets/applets.h"
namespace Service::AM::Applets {
@@ -28,7 +29,7 @@ static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has inco
class ProfileSelect final : public Applet {
public:
- ProfileSelect();
+ explicit ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend);
~ProfileSelect() override;
void Initialize() override;
@@ -38,9 +39,11 @@ public:
void ExecuteInteractive() override;
void Execute() override;
- void SelectionComplete(std::optional<Account::UUID> uuid);
+ void SelectionComplete(std::optional<Common::UUID> uuid);
private:
+ const Core::Frontend::ProfileSelectApplet& frontend;
+
UserSelectionConfig config;
bool complete = false;
ResultCode status = RESULT_SUCCESS;
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index 8c5bd6059..e197990f7 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -39,7 +39,8 @@ static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
return params;
}
-SoftwareKeyboard::SoftwareKeyboard() = default;
+SoftwareKeyboard::SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend)
+ : frontend(frontend) {}
SoftwareKeyboard::~SoftwareKeyboard() = default;
@@ -90,8 +91,6 @@ void SoftwareKeyboard::ExecuteInteractive() {
if (status == INTERACTIVE_STATUS_OK) {
complete = true;
} else {
- const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
-
std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
std::memcpy(string.data(), data.data() + 4, string.size() * 2);
frontend.SendTextCheckDialog(
@@ -106,8 +105,6 @@ void SoftwareKeyboard::Execute() {
return;
}
- const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
-
const auto parameters = ConvertToFrontendParameters(config, initial_text);
frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); },
diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h
index b93a30d28..0fbc43e51 100644
--- a/src/core/hle/service/am/applets/software_keyboard.h
+++ b/src/core/hle/service/am/applets/software_keyboard.h
@@ -55,7 +55,7 @@ static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect siz
class SoftwareKeyboard final : public Applet {
public:
- SoftwareKeyboard();
+ explicit SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend);
~SoftwareKeyboard() override;
void Initialize() override;
@@ -68,6 +68,8 @@ public:
void WriteText(std::optional<std::u16string> text);
private:
+ const Core::Frontend::SoftwareKeyboardApplet& frontend;
+
KeyboardConfig config;
std::u16string initial_text;
bool complete = false;
diff --git a/src/core/hle/service/am/applets/stub_applet.h b/src/core/hle/service/am/applets/stub_applet.h
deleted file mode 100644
index 7d8dc968d..000000000
--- a/src/core/hle/service/am/applets/stub_applet.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "core/hle/service/am/applets/applets.h"
-
-namespace Service::AM::Applets {
-
-class StubApplet final : public Applet {
-public:
- StubApplet();
- ~StubApplet() override;
-
- void Initialize() override;
-
- bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
- void ExecuteInteractive() override;
- void Execute() override;
-};
-
-} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 9b0aa7f5f..7878f5136 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -86,7 +86,7 @@ static FileSys::VirtualFile GetManualRomFS() {
if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success)
return out;
- const auto& installed{FileSystem::GetUnionContents()};
+ const auto& installed{Core::System::GetInstance().GetContentProvider()};
const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(),
FileSys::ContentRecordType::Manual);
@@ -95,7 +95,7 @@ static FileSys::VirtualFile GetManualRomFS() {
return nullptr;
}
-WebBrowser::WebBrowser() = default;
+WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend) : frontend(frontend) {}
WebBrowser::~WebBrowser() = default;
@@ -152,8 +152,6 @@ void WebBrowser::Execute() {
return;
}
- auto& frontend{Core::System::GetInstance().GetWebBrowser()};
-
frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
}
diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h
index b9e228fac..7e0f34c7d 100644
--- a/src/core/hle/service/am/applets/web_browser.h
+++ b/src/core/hle/service/am/applets/web_browser.h
@@ -12,7 +12,7 @@ namespace Service::AM::Applets {
class WebBrowser final : public Applet {
public:
- WebBrowser();
+ WebBrowser(Core::Frontend::WebBrowserApplet& frontend);
~WebBrowser() override;
void Initialize() override;
@@ -32,6 +32,8 @@ public:
void Finalize();
private:
+ Core::Frontend::WebBrowserApplet& frontend;
+
bool complete = false;
bool unpacked = false;
ResultCode status = RESULT_SUCCESS;
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index b506bc3dd..d3e97776b 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -9,7 +9,6 @@
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/nca_metadata.h"
-#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/hle/ipc_helpers.h"
@@ -18,7 +17,6 @@
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/aoc/aoc_u.h"
-#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/settings.h"
@@ -33,11 +31,11 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
static std::vector<u64> AccumulateAOCTitleIDs() {
std::vector<u64> add_on_content;
- const auto rcu = FileSystem::GetUnionContents();
+ const auto& rcu = Core::System::GetInstance().GetContentProvider();
const auto list =
rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
std::transform(list.begin(), list.end(), std::back_inserter(add_on_content),
- [](const FileSys::RegisteredCacheEntry& rce) { return rce.title_id; });
+ [](const FileSys::ContentProviderEntry& rce) { return rce.title_id; });
add_on_content.erase(
std::remove_if(
add_on_content.begin(), add_on_content.end(),
@@ -50,6 +48,7 @@ static std::vector<u64> AccumulateAOCTitleIDs() {
}
AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs()) {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "CountAddOnContentByApplicationId"},
{1, nullptr, "ListAddOnContentByApplicationId"},
@@ -60,18 +59,29 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
{6, nullptr, "PrepareAddOnContentByApplicationId"},
{7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"},
{8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"},
+ {100, nullptr, "CreateEcPurchasedEventManager"},
};
+ // clang-format on
+
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
+ aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
"GetAddOnContentListChanged:Event");
}
AOC_U::~AOC_U() = default;
void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_AOC, "called");
+ struct Parameters {
+ u64 process_id;
+ };
+ static_assert(sizeof(Parameters) == 8);
+
+ IPC::RequestParser rp{ctx};
+ const auto params = rp.PopRaw<Parameters>();
+
+ LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
@@ -90,23 +100,32 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
}
void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
+ struct Parameters {
+ u32 offset;
+ u32 count;
+ u64 process_id;
+ };
+ static_assert(sizeof(Parameters) == 16);
+
IPC::RequestParser rp{ctx};
+ const auto [offset, count, process_id] = rp.PopRaw<Parameters>();
- const auto offset = rp.PopRaw<u32>();
- auto count = rp.PopRaw<u32>();
- LOG_DEBUG(Service_AOC, "called with offset={}, count={}", offset, count);
+ LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
+ process_id);
const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
std::vector<u32> out;
- for (size_t i = 0; i < add_on_content.size(); ++i) {
- if ((add_on_content[i] & DLC_BASE_TITLE_ID_MASK) == current)
- out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF));
- }
-
const auto& disabled = Settings::values.disabled_addons[current];
- if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end())
- out = {};
+ if (std::find(disabled.begin(), disabled.end(), "DLC") == disabled.end()) {
+ for (u64 content_id : add_on_content) {
+ if ((content_id & DLC_BASE_TITLE_ID_MASK) != current) {
+ continue;
+ }
+
+ out.push_back(static_cast<u32>(content_id & 0x7FF));
+ }
+ }
if (out.size() < offset) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -115,22 +134,31 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
return;
}
- count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
+ const auto out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
std::rotate(out.begin(), out.begin() + offset, out.end());
- out.resize(count);
+ out.resize(out_count);
ctx.WriteBuffer(out);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push(count);
+ rb.Push(out_count);
}
void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_AOC, "called");
+ struct Parameters {
+ u64 process_id;
+ };
+ static_assert(sizeof(Parameters) == 8);
+
+ IPC::RequestParser rp{ctx};
+ const auto params = rp.PopRaw<Parameters>();
+
+ LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
+
const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
FileSys::PatchManager pm{title_id};
@@ -144,10 +172,17 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
}
void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
+ struct Parameters {
+ s32 addon_index;
+ u64 process_id;
+ };
+ static_assert(sizeof(Parameters) == 16);
+
IPC::RequestParser rp{ctx};
+ const auto [addon_index, process_id] = rp.PopRaw<Parameters>();
- const auto aoc_id = rp.PopRaw<u32>();
- LOG_WARNING(Service_AOC, "(STUBBED) called with aoc_id={:08X}", aoc_id);
+ LOG_WARNING(Service_AOC, "(STUBBED) called with addon_index={}, process_id={}", addon_index,
+ process_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp
index fcacbab72..d058c0245 100644
--- a/src/core/hle/service/apm/interface.cpp
+++ b/src/core/hle/service/apm/interface.cpp
@@ -87,6 +87,8 @@ APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} {
{3, nullptr, "GetLastThrottlingState"},
{4, nullptr, "ClearLastThrottlingState"},
{5, nullptr, "LoadAndApplySettings"},
+ {6, nullptr, "SetCpuBoostMode"},
+ {7, nullptr, "GetCurrentPerformanceConfiguration"},
};
// clang-format on
diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp
index b6b71f966..6a01d4d29 100644
--- a/src/core/hle/service/audio/audctl.cpp
+++ b/src/core/hle/service/audio/audctl.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
#include "core/hle/service/audio/audctl.h"
namespace Service::Audio {
@@ -11,8 +13,8 @@ AudCtl::AudCtl() : ServiceFramework{"audctl"} {
static const FunctionInfo functions[] = {
{0, nullptr, "GetTargetVolume"},
{1, nullptr, "SetTargetVolume"},
- {2, nullptr, "GetTargetVolumeMin"},
- {3, nullptr, "GetTargetVolumeMax"},
+ {2, &AudCtl::GetTargetVolumeMin, "GetTargetVolumeMin"},
+ {3, &AudCtl::GetTargetVolumeMax, "GetTargetVolumeMax"},
{4, nullptr, "IsTargetMute"},
{5, nullptr, "SetTargetMute"},
{6, nullptr, "IsTargetConnected"},
@@ -44,4 +46,28 @@ AudCtl::AudCtl() : ServiceFramework{"audctl"} {
AudCtl::~AudCtl() = default;
+void AudCtl::GetTargetVolumeMin(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Audio, "called.");
+
+ // This service function is currently hardcoded on the
+ // actual console to this value (as of 8.0.0).
+ constexpr s32 target_min_volume = 0;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(target_min_volume);
+}
+
+void AudCtl::GetTargetVolumeMax(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Audio, "called.");
+
+ // This service function is currently hardcoded on the
+ // actual console to this value (as of 8.0.0).
+ constexpr s32 target_max_volume = 15;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(target_max_volume);
+}
+
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audctl.h b/src/core/hle/service/audio/audctl.h
index 9d2d9e83b..c7fafc02e 100644
--- a/src/core/hle/service/audio/audctl.h
+++ b/src/core/hle/service/audio/audctl.h
@@ -12,6 +12,10 @@ class AudCtl final : public ServiceFramework<AudCtl> {
public:
explicit AudCtl();
~AudCtl() override;
+
+private:
+ void GetTargetVolumeMin(Kernel::HLERequestContext& ctx);
+ void GetTargetVolumeMax(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 088410564..d7f1d348d 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -2,9 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "common/logging/log.h"
-#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/audio/audin_u.h"
namespace Service::Audio {
@@ -28,12 +25,12 @@ public:
{11, nullptr, "GetAudioInBufferCount"},
{12, nullptr, "SetAudioInDeviceGain"},
{13, nullptr, "GetAudioInDeviceGain"},
+ {14, nullptr, "FlushAudioInBuffers"},
};
// clang-format on
RegisterHandlers(functions);
}
- ~IAudioIn() = default;
};
AudInU::AudInU() : ServiceFramework("audin:u") {
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index bbe813490..7db6eb08d 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -44,7 +44,7 @@ public:
std::string&& unique_name)
: ServiceFramework("IAudioOut"), audio_core(audio_core),
device_name(std::move(device_name)), audio_params(audio_params) {
-
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
{1, &IAudioOut::StartAudioOut, "StartAudioOut"},
@@ -58,13 +58,16 @@ public:
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
{10, nullptr, "GetAudioOutPlayedSampleCount"},
{11, nullptr, "FlushAudioOutBuffers"},
+ {12, &IAudioOut::SetAudioOutVolume, "SetAudioOutVolume"},
+ {13, &IAudioOut::GetAudioOutVolume, "GetAudioOutVolume"},
};
+ // clang-format on
RegisterHandlers(functions);
// This is the event handle used to check if the audio buffer was released
auto& system = Core::System::GetInstance();
buffer_event = Kernel::WritableEvent::CreateEventPair(
- system.Kernel(), Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
+ system.Kernel(), Kernel::ResetType::Manual, "IAudioOutBufferReleased");
stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
audio_params.channel_count, std::move(unique_name),
@@ -107,7 +110,9 @@ private:
void StopAudioOut(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
- audio_core.StopStream(stream);
+ if (stream->IsPlaying()) {
+ audio_core.StopStream(stream);
+ }
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -148,7 +153,6 @@ private:
void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
- IPC::RequestParser rp{ctx};
const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
@@ -179,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;
@@ -192,12 +215,9 @@ private:
void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
- IPC::RequestParser rp{ctx};
-
ctx.WriteBuffer(DefaultDevice);
IPC::ResponseBuilder rb{ctx, 3};
-
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(1); // Amount of audio devices
}
diff --git a/src/core/hle/service/audio/audrec_u.cpp b/src/core/hle/service/audio/audrec_u.cpp
index 6956a2e64..1a5aed9ed 100644
--- a/src/core/hle/service/audio/audrec_u.cpp
+++ b/src/core/hle/service/audio/audrec_u.cpp
@@ -2,9 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "common/logging/log.h"
-#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/audio/audrec_u.h"
namespace Service::Audio {
@@ -30,7 +27,6 @@ public:
RegisterHandlers(functions);
}
- ~IFinalOutputRecorder() = default;
};
AudRecU::AudRecU() : ServiceFramework("audrec:u") {
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index c9de10a24..75db0c2dc 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -8,8 +8,10 @@
#include "audio_core/audio_renderer.h"
#include "common/alignment.h"
+#include "common/bit_util.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
+#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
@@ -45,7 +47,7 @@ public:
auto& system = Core::System::GetInstance();
system_event = Kernel::WritableEvent::CreateEventPair(
- system.Kernel(), Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
+ system.Kernel(), Kernel::ResetType::Manual, "IAudioRenderer:SystemEvent");
renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params,
system_event.writable);
}
@@ -177,14 +179,13 @@ public:
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IAudioOutBufferReleasedEvent");
}
private:
void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
- IPC::RequestParser rp{ctx};
constexpr std::array<char, 15> audio_interface{{"AudioInterface"}};
ctx.WriteBuffer(audio_interface);
@@ -195,13 +196,13 @@ private:
}
void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
-
IPC::RequestParser rp{ctx};
- f32 volume = static_cast<f32>(rp.Pop<u32>());
+ const f32 volume = rp.Pop<f32>();
- auto file_buffer = ctx.ReadBuffer();
- auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
+ const auto device_name_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(device_name_buffer);
+
+ LOG_WARNING(Service_Audio, "(STUBBED) called. name={}, volume={}", name, volume);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -209,7 +210,6 @@ private:
void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
- IPC::RequestParser rp{ctx};
constexpr std::array<char, 12> audio_interface{{"AudioDevice"}};
ctx.WriteBuffer(audio_interface);
@@ -263,64 +263,304 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
OpenAudioRendererImpl(ctx);
}
+static u64 CalculateNumPerformanceEntries(const AudioCore::AudioRendererParameter& params) {
+ // +1 represents the final mix.
+ return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
+ 1;
+}
+
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
LOG_DEBUG(Service_Audio, "called");
- u64 buffer_sz = Common::AlignUp(4 * params.mix_buffer_count, 0x40);
- buffer_sz += params.submix_count * 1024;
- buffer_sz += 0x940 * (params.submix_count + 1);
- buffer_sz += 0x3F0 * params.voice_count;
- buffer_sz += Common::AlignUp(8 * (params.submix_count + 1), 0x10);
- buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10);
- buffer_sz += Common::AlignUp(
- (0x3C0 * (params.sink_count + params.submix_count) + 4 * params.sample_count) *
- (params.mix_buffer_count + 6),
- 0x40);
-
- if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
- const u32 count = params.submix_count + 1;
- u64 node_count = Common::AlignUp(count, 0x40);
- const u64 node_state_buffer_sz =
- 4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8);
- u64 edge_matrix_buffer_sz = 0;
- node_count = Common::AlignUp(count * count, 0x40);
- if (node_count >> 31 != 0) {
- edge_matrix_buffer_sz = (node_count | 7) / 8;
- } else {
- edge_matrix_buffer_sz = node_count / 8;
+ // Several calculations below align the sizes being calculated
+ // onto a 64 byte boundary.
+ static constexpr u64 buffer_alignment_size = 64;
+
+ // Some calculations that calculate portions of the buffer
+ // that will contain information, on the other hand, align
+ // the result of some of their calcularions on a 16 byte boundary.
+ static constexpr u64 info_field_alignment_size = 16;
+
+ // Maximum detail entries that may exist at one time for performance
+ // frame statistics.
+ static constexpr u64 max_perf_detail_entries = 100;
+
+ // Size of the data structure representing the bulk of the voice-related state.
+ static constexpr u64 voice_state_size = 0x100;
+
+ // Size of the upsampler manager data structure
+ constexpr u64 upsampler_manager_size = 0x48;
+
+ // Calculates the part of the size that relates to mix buffers.
+ const auto calculate_mix_buffer_sizes = [](const AudioCore::AudioRendererParameter& params) {
+ // As of 8.0.0 this is the maximum on voice channels.
+ constexpr u64 max_voice_channels = 6;
+
+ // The service expects the sample_count member of the parameters to either be
+ // a value of 160 or 240, so the maximum sample count is assumed in order
+ // to adequately handle all values at runtime.
+ constexpr u64 default_max_sample_count = 240;
+
+ const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels;
+
+ u64 size = 0;
+ size += total_mix_buffers * (sizeof(s32) * params.sample_count);
+ size += total_mix_buffers * (sizeof(s32) * default_max_sample_count);
+ size += u64{params.submix_count} + params.sink_count;
+ size = Common::AlignUp(size, buffer_alignment_size);
+ size += Common::AlignUp(params.unknown_30, buffer_alignment_size);
+ size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size);
+ return size;
+ };
+
+ // Calculates the portion of the size related to the mix data (and the sorting thereof).
+ const auto calculate_mix_info_size = [this](const AudioCore::AudioRendererParameter& params) {
+ // The size of the mixing info data structure.
+ constexpr u64 mix_info_size = 0x940;
+
+ // Consists of total submixes with the final mix included.
+ const u64 total_mix_count = u64{params.submix_count} + 1;
+
+ // The total number of effects that may be available to the audio renderer at any time.
+ constexpr u64 max_effects = 256;
+
+ // Calculates the part of the size related to the audio node state.
+ // This will only be used if the audio revision supports the splitter.
+ const auto calculate_node_state_size = [](std::size_t num_nodes) {
+ // Internally within a nodestate, it appears to use a data structure
+ // similar to a std::bitset<64> twice.
+ constexpr u64 bit_size = Common::BitSize<u64>();
+ constexpr u64 num_bitsets = 2;
+
+ // Node state instances have three states internally for performing
+ // depth-first searches of nodes. Initialized, Found, and Done Sorting.
+ constexpr u64 num_states = 3;
+
+ u64 size = 0;
+ size += (num_nodes * num_nodes) * sizeof(s32);
+ size += num_states * (num_nodes * sizeof(s32));
+ size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>());
+ return size;
+ };
+
+ // Calculates the part of the size related to the adjacency (aka edge) matrix.
+ const auto calculate_edge_matrix_size = [](std::size_t num_nodes) {
+ return (num_nodes * num_nodes) * sizeof(s32);
+ };
+
+ u64 size = 0;
+ size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size);
+ size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size);
+ size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count,
+ info_field_alignment_size);
+
+ if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
+ size += Common::AlignUp(calculate_node_state_size(total_mix_count) +
+ calculate_edge_matrix_size(total_mix_count),
+ info_field_alignment_size);
}
- buffer_sz += Common::AlignUp(node_state_buffer_sz + edge_matrix_buffer_sz, 0x10);
- }
- buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50;
- if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
- buffer_sz += 0xE0 * params.num_splitter_send_channels;
- buffer_sz += 0x20 * params.splitter_count;
- buffer_sz += Common::AlignUp(4 * params.num_splitter_send_channels, 0x10);
- }
- buffer_sz = Common::AlignUp(buffer_sz, 0x40) + 0x170 * params.sink_count;
- u64 output_sz = buffer_sz + 0x280 * params.sink_count + 0x4B0 * params.effect_count +
- ((params.voice_count * 256) | 0x40);
-
- if (params.performance_frame_count >= 1) {
- output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count +
- 16 * params.voice_count + 16) +
- 0x658) *
- (params.performance_frame_count + 1) +
- 0xc0,
- 0x40) +
- output_sz;
- }
- output_sz = Common::AlignUp(output_sz + 0x1807e, 0x1000);
+ return size;
+ };
- IPC::ResponseBuilder rb{ctx, 4};
+ // Calculates the part of the size related to voice channel info.
+ const auto calculate_voice_info_size = [](const AudioCore::AudioRendererParameter& params) {
+ constexpr u64 voice_info_size = 0x220;
+ constexpr u64 voice_resource_size = 0xD0;
+
+ u64 size = 0;
+ size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size);
+ size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size);
+ size +=
+ Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size);
+ size += Common::AlignUp(voice_state_size * params.voice_count, info_field_alignment_size);
+ return size;
+ };
+
+ // Calculates the part of the size related to memory pools.
+ const auto calculate_memory_pools_size = [](const AudioCore::AudioRendererParameter& params) {
+ const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
+ const u64 memory_pool_info_size = 0x20;
+ return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
+ };
+
+ // Calculates the part of the size related to the splitter context.
+ const auto calculate_splitter_context_size =
+ [this](const AudioCore::AudioRendererParameter& params) -> u64 {
+ if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
+ return 0;
+ }
+
+ constexpr u64 splitter_info_size = 0x20;
+ constexpr u64 splitter_destination_data_size = 0xE0;
+
+ u64 size = 0;
+ size += params.num_splitter_send_channels;
+ size +=
+ Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size);
+ size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels,
+ info_field_alignment_size);
+ return size;
+ };
+
+ // Calculates the part of the size related to the upsampler info.
+ const auto calculate_upsampler_info_size = [](const AudioCore::AudioRendererParameter& params) {
+ constexpr u64 upsampler_info_size = 0x280;
+ // Yes, using the buffer size over info alignment size is intentional here.
+ return Common::AlignUp(upsampler_info_size * (u64{params.submix_count} + params.sink_count),
+ buffer_alignment_size);
+ };
+
+ // Calculates the part of the size related to effect info.
+ const auto calculate_effect_info_size = [](const AudioCore::AudioRendererParameter& params) {
+ constexpr u64 effect_info_size = 0x2B0;
+ return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
+ };
+
+ // Calculates the part of the size related to audio sink info.
+ const auto calculate_sink_info_size = [](const AudioCore::AudioRendererParameter& params) {
+ const u64 sink_info_size = 0x170;
+ return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
+ };
+
+ // Calculates the part of the size related to voice state info.
+ const auto calculate_voice_state_size = [](const AudioCore::AudioRendererParameter& params) {
+ const u64 voice_state_size = 0x100;
+ const u64 additional_size = buffer_alignment_size - 1;
+ return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
+ info_field_alignment_size);
+ };
+
+ // Calculates the part of the size related to performance statistics.
+ const auto calculate_perf_size = [this](const AudioCore::AudioRendererParameter& params) {
+ // Extra size value appended to the end of the calculation.
+ constexpr u64 appended = 128;
+
+ // Whether or not we assume the newer version of performance metrics data structures.
+ const bool is_v2 =
+ IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision);
+
+ // Data structure sizes
+ constexpr u64 perf_statistics_size = 0x0C;
+ const u64 header_size = is_v2 ? 0x30 : 0x18;
+ const u64 entry_size = is_v2 ? 0x18 : 0x10;
+ const u64 detail_size = is_v2 ? 0x18 : 0x10;
+
+ const u64 entry_count = CalculateNumPerformanceEntries(params);
+ const u64 size_per_frame =
+ header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries);
+
+ u64 size = 0;
+ size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1,
+ buffer_alignment_size);
+ size += Common::AlignUp(perf_statistics_size, buffer_alignment_size);
+ size += appended;
+ return size;
+ };
+
+ // Calculates the part of the size that relates to the audio command buffer.
+ const auto calculate_command_buffer_size =
+ [this](const AudioCore::AudioRendererParameter& params) {
+ constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
+
+ if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
+ constexpr u64 command_buffer_size = 0x18000;
+
+ return command_buffer_size + alignment;
+ }
+
+ // When the variadic command buffer is supported, this means
+ // the command generator for the audio renderer can issue commands
+ // that are (as one would expect), variable in size. So what we need to do
+ // is determine the maximum possible size for a few command data structures
+ // then multiply them by the amount of present commands indicated by the given
+ // respective audio parameters.
+
+ constexpr u64 max_biquad_filters = 2;
+ constexpr u64 max_mix_buffers = 24;
+
+ constexpr u64 biquad_filter_command_size = 0x2C;
+
+ constexpr u64 depop_mix_command_size = 0x24;
+ constexpr u64 depop_setup_command_size = 0x50;
+
+ constexpr u64 effect_command_max_size = 0x540;
+
+ constexpr u64 mix_command_size = 0x1C;
+ constexpr u64 mix_ramp_command_size = 0x24;
+ constexpr u64 mix_ramp_grouped_command_size = 0x13C;
+
+ constexpr u64 perf_command_size = 0x28;
+
+ constexpr u64 sink_command_size = 0x130;
+
+ constexpr u64 submix_command_max_size =
+ depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
+
+ constexpr u64 volume_command_size = 0x1C;
+ constexpr u64 volume_ramp_command_size = 0x20;
+
+ constexpr u64 voice_biquad_filter_command_size =
+ biquad_filter_command_size * max_biquad_filters;
+ constexpr u64 voice_data_command_size = 0x9C;
+ const u64 voice_command_max_size =
+ (params.splitter_count * depop_setup_command_size) +
+ (voice_data_command_size + voice_biquad_filter_command_size +
+ volume_ramp_command_size + mix_ramp_grouped_command_size);
+
+ // Now calculate the individual elements that comprise the size and add them together.
+ const u64 effect_commands_size = params.effect_count * effect_command_max_size;
+
+ const u64 final_mix_commands_size =
+ depop_mix_command_size + volume_command_size * max_mix_buffers;
+
+ const u64 perf_commands_size =
+ perf_command_size *
+ (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
+
+ const u64 sink_commands_size = params.sink_count * sink_command_size;
+
+ const u64 splitter_commands_size =
+ params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
+
+ const u64 submix_commands_size = params.submix_count * submix_command_max_size;
+
+ const u64 voice_commands_size = params.voice_count * voice_command_max_size;
+
+ return effect_commands_size + final_mix_commands_size + perf_commands_size +
+ sink_commands_size + splitter_commands_size + submix_commands_size +
+ voice_commands_size + alignment;
+ };
+
+ IPC::RequestParser rp{ctx};
+ const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
+
+ u64 size = 0;
+ size += calculate_mix_buffer_sizes(params);
+ size += calculate_mix_info_size(params);
+ size += calculate_voice_info_size(params);
+ size += upsampler_manager_size;
+ size += calculate_memory_pools_size(params);
+ size += calculate_splitter_context_size(params);
+
+ size = Common::AlignUp(size, buffer_alignment_size);
+
+ size += calculate_upsampler_info_size(params);
+ size += calculate_effect_info_size(params);
+ size += calculate_sink_info_size(params);
+ size += calculate_voice_state_size(params);
+ size += calculate_perf_size(params);
+ size += calculate_command_buffer_size(params);
+
+ // finally, 4KB page align the size, and we're done.
+ size = Common::AlignUp(size, 4096);
+
+ IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push<u64>(output_sz);
+ rb.Push<u64>(size);
- LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", output_sz);
+ LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size);
}
void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
@@ -358,10 +598,15 @@ void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
}
bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
- u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap
+ // Byte swap
+ const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0');
+
switch (feature) {
case AudioFeatures::Splitter:
- return version_num >= 2u;
+ return version_num >= 2U;
+ case AudioFeatures::PerformanceMetricsVersion2:
+ case AudioFeatures::VariadicCommandBuffer:
+ return version_num >= 5U;
default:
return false;
}
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index e55d25973..1d3c8df61 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -28,6 +28,8 @@ private:
enum class AudioFeatures : u32 {
Splitter,
+ PerformanceMetricsVersion2,
+ VariadicCommandBuffer,
};
bool IsFeatureSupported(AudioFeatures feature, u32_le revision) const;
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 11eba4a12..cb4a1160d 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -8,44 +8,34 @@
#include <vector>
#include <opus.h>
+#include <opus_multistream.h>
-#include "common/common_funcs.h"
+#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/audio/hwopus.h"
namespace Service::Audio {
-
+namespace {
struct OpusDeleter {
- void operator()(void* ptr) const {
- operator delete(ptr);
+ void operator()(OpusMSDecoder* ptr) const {
+ opus_multistream_decoder_destroy(ptr);
}
};
-class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
-public:
- IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoder, OpusDeleter> decoder, u32 sample_rate,
- u32 channel_count)
- : ServiceFramework("IHardwareOpusDecoderManager"), decoder(std::move(decoder)),
- sample_rate(sample_rate), channel_count(channel_count) {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"},
- {1, nullptr, "SetContext"},
- {2, nullptr, "DecodeInterleavedForMultiStreamOld"},
- {3, nullptr, "SetContextForMultiStream"},
- {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"},
- {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"},
- {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
- {7, nullptr, "DecodeInterleavedForMultiStream"},
- };
- // clang-format on
+using OpusDecoderPtr = std::unique_ptr<OpusMSDecoder, OpusDeleter>;
- RegisterHandlers(functions);
- }
+struct OpusPacketHeader {
+ // Packet size in bytes.
+ u32_be size;
+ // Indicates the final range of the codec's entropy coder.
+ u32_be final_range;
+};
+static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size");
-private:
+class OpusDecoderState {
+public:
/// Describes extra behavior that may be asked of the decoding context.
enum class ExtraBehavior {
/// No extra behavior.
@@ -55,30 +45,27 @@ private:
ResetContext,
};
- void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Audio, "called");
-
- DecodeInterleavedHelper(ctx, nullptr, ExtraBehavior::None);
- }
-
- void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Audio, "called");
-
- u64 performance = 0;
- DecodeInterleavedHelper(ctx, &performance, ExtraBehavior::None);
- }
-
- void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Audio, "called");
-
- IPC::RequestParser rp{ctx};
- const auto extra_behavior =
- rp.Pop<bool>() ? ExtraBehavior::ResetContext : ExtraBehavior::None;
+ enum class PerfTime {
+ Disabled,
+ Enabled,
+ };
- u64 performance = 0;
- DecodeInterleavedHelper(ctx, &performance, extra_behavior);
+ explicit OpusDecoderState(OpusDecoderPtr decoder, u32 sample_rate, u32 channel_count)
+ : decoder{std::move(decoder)}, sample_rate{sample_rate}, channel_count{channel_count} {}
+
+ // Decodes interleaved Opus packets. Optionally allows reporting time taken to
+ // perform the decoding, as well as any relevant extra behavior.
+ void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
+ ExtraBehavior extra_behavior) {
+ if (perf_time == PerfTime::Disabled) {
+ DecodeInterleavedHelper(ctx, nullptr, extra_behavior);
+ } else {
+ u64 performance = 0;
+ DecodeInterleavedHelper(ctx, &performance, extra_behavior);
+ }
}
+private:
void DecodeInterleavedHelper(Kernel::HLERequestContext& ctx, u64* performance,
ExtraBehavior extra_behavior) {
u32 consumed = 0;
@@ -89,8 +76,7 @@ private:
ResetDecoderContext();
}
- if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples,
- performance)) {
+ if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) {
LOG_ERROR(Audio, "Failed to decode opus data");
IPC::ResponseBuilder rb{ctx, 2};
// TODO(ogniK): Use correct error code
@@ -109,27 +95,27 @@ private:
ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
}
- bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input,
- std::vector<opus_int16>& output, u64* out_performance_time) {
+ bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,
+ std::vector<opus_int16>& output, u64* out_performance_time) const {
const auto start_time = std::chrono::high_resolution_clock::now();
const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
- if (sizeof(OpusHeader) > input.size()) {
+ if (sizeof(OpusPacketHeader) > input.size()) {
LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}",
- sizeof(OpusHeader), input.size());
+ sizeof(OpusPacketHeader), input.size());
return false;
}
- OpusHeader hdr{};
- std::memcpy(&hdr, input.data(), sizeof(OpusHeader));
- if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) {
+ OpusPacketHeader hdr{};
+ std::memcpy(&hdr, input.data(), sizeof(OpusPacketHeader));
+ if (sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size) > input.size()) {
LOG_ERROR(Audio, "Input does not fit in the opus header size. data_sz={}, input_sz={}",
- sizeof(OpusHeader) + static_cast<u32>(hdr.sz), input.size());
+ sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size), input.size());
return false;
}
- const auto frame = input.data() + sizeof(OpusHeader);
+ const auto frame = input.data() + sizeof(OpusPacketHeader);
const auto decoded_sample_count = opus_packet_get_nb_samples(
- frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)),
+ frame, static_cast<opus_int32>(input.size() - sizeof(OpusPacketHeader)),
static_cast<opus_int32>(sample_rate));
if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) {
LOG_ERROR(
@@ -141,18 +127,18 @@ private:
const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count));
const auto out_sample_count =
- opus_decode(decoder.get(), frame, hdr.sz, output.data(), frame_size, 0);
+ opus_multistream_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0);
if (out_sample_count < 0) {
LOG_ERROR(Audio,
"Incorrect sample count received from opus_decode, "
"output_sample_count={}, frame_size={}, data_sz_from_hdr={}",
- out_sample_count, frame_size, static_cast<u32>(hdr.sz));
+ out_sample_count, frame_size, static_cast<u32>(hdr.size));
return false;
}
const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
sample_count = out_sample_count;
- consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz);
+ consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size);
if (out_performance_time != nullptr) {
*out_performance_time =
std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count();
@@ -164,25 +150,86 @@ private:
void ResetDecoderContext() {
ASSERT(decoder != nullptr);
- opus_decoder_ctl(decoder.get(), OPUS_RESET_STATE);
+ opus_multistream_decoder_ctl(decoder.get(), OPUS_RESET_STATE);
}
- struct OpusHeader {
- u32_be sz; // Needs to be BE for some odd reason
- INSERT_PADDING_WORDS(1);
- };
- static_assert(sizeof(OpusHeader) == 0x8, "OpusHeader is an invalid size");
-
- std::unique_ptr<OpusDecoder, OpusDeleter> decoder;
+ OpusDecoderPtr decoder;
u32 sample_rate;
u32 channel_count;
};
-static std::size_t WorkerBufferSize(u32 channel_count) {
+class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
+public:
+ explicit IHardwareOpusDecoderManager(OpusDecoderState decoder_state)
+ : ServiceFramework("IHardwareOpusDecoderManager"), decoder_state{std::move(decoder_state)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"},
+ {1, nullptr, "SetContext"},
+ {2, nullptr, "DecodeInterleavedForMultiStreamOld"},
+ {3, nullptr, "SetContextForMultiStream"},
+ {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"},
+ {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"},
+ {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
+ {7, nullptr, "DecodeInterleavedForMultiStream"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Audio, "called");
+
+ decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Disabled,
+ OpusDecoderState::ExtraBehavior::None);
+ }
+
+ void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Audio, "called");
+
+ decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled,
+ OpusDecoderState::ExtraBehavior::None);
+ }
+
+ void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Audio, "called");
+
+ IPC::RequestParser rp{ctx};
+ const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext
+ : OpusDecoderState::ExtraBehavior::None;
+
+ decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
+ }
+
+ OpusDecoderState decoder_state;
+};
+
+std::size_t WorkerBufferSize(u32 channel_count) {
ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
- return opus_decoder_get_size(static_cast<int>(channel_count));
+ constexpr int num_streams = 1;
+ const int num_stereo_streams = channel_count == 2 ? 1 : 0;
+ return opus_multistream_decoder_get_size(num_streams, num_stereo_streams);
}
+// Creates the mapping table that maps the input channels to the particular
+// output channels. In the stereo case, we map the left and right input channels
+// to the left and right output channels respectively.
+//
+// However, in the monophonic case, we only map the one available channel
+// to the sole output channel. We specify 255 for the would-be right channel
+// as this is a special value defined by Opus to indicate to the decoder to
+// ignore that channel.
+std::array<u8, 2> CreateMappingTable(u32 channel_count) {
+ if (channel_count == 2) {
+ return {{0, 1}};
+ }
+
+ return {{0, 255}};
+}
+} // Anonymous namespace
+
void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto sample_rate = rp.Pop<u32>();
@@ -220,10 +267,15 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
const std::size_t worker_sz = WorkerBufferSize(channel_count);
ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
- std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
- static_cast<OpusDecoder*>(operator new(worker_sz))};
- if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
- LOG_ERROR(Audio, "Failed to init opus decoder with error={}", err);
+ const int num_stereo_streams = channel_count == 2 ? 1 : 0;
+ const auto mapping_table = CreateMappingTable(channel_count);
+
+ int error = 0;
+ OpusDecoderPtr decoder{
+ opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1,
+ num_stereo_streams, mapping_table.data(), &error)};
+ if (error != OPUS_OK || decoder == nullptr) {
+ LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
IPC::ResponseBuilder rb{ctx, 2};
// TODO(ogniK): Use correct error code
rb.Push(ResultCode(-1));
@@ -232,8 +284,8 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IHardwareOpusDecoderManager>(std::move(decoder), sample_rate,
- channel_count);
+ rb.PushIpcInterface<IHardwareOpusDecoderManager>(
+ OpusDecoderState{std::move(decoder), sample_rate, channel_count});
}
HwOpus::HwOpus() : ServiceFramework("hwopus") {
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index 59ef603e1..3c7ca2c44 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -34,8 +34,8 @@ public:
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- register_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
- "BT:RegisterEvent");
+ register_event = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent");
}
private:
@@ -154,7 +154,8 @@ public:
{96, nullptr, "GetLeHidEventInfo"},
{97, nullptr, "RegisterBleHidEvent"},
{98, nullptr, "SetLeScanParameter"},
- {256, nullptr, "GetIsManufacturingMode"}
+ {256, nullptr, "GetIsManufacturingMode"},
+ {257, nullptr, "EmulateBluetoothCrash"},
};
// clang-format on
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index 4f15c3f19..b439ee7ec 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -57,13 +57,13 @@ public:
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IBtmUserCore:ScanEvent");
connection_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConnectionEvent");
+ kernel, Kernel::ResetType::Automatic, "IBtmUserCore:ConnectionEvent");
service_discovery = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery");
- config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ kernel, Kernel::ResetType::Automatic, "IBtmUserCore:Discovery");
+ config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IBtmUserCore:ConfigEvent");
}
diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp
index ae7b0720b..907f464ab 100644
--- a/src/core/hle/service/caps/caps.cpp
+++ b/src/core/hle/service/caps/caps.cpp
@@ -15,32 +15,41 @@ public:
explicit CAPS_A() : ServiceFramework{"caps:a"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "Unknown1"},
- {1, nullptr, "Unknown2"},
- {2, nullptr, "Unknown3"},
- {3, nullptr, "Unknown4"},
- {4, nullptr, "Unknown5"},
- {5, nullptr, "Unknown6"},
- {6, nullptr, "Unknown7"},
- {7, nullptr, "Unknown8"},
- {8, nullptr, "Unknown9"},
- {9, nullptr, "Unknown10"},
- {10, nullptr, "Unknown11"},
- {11, nullptr, "Unknown12"},
- {12, nullptr, "Unknown13"},
- {13, nullptr, "Unknown14"},
- {14, nullptr, "Unknown15"},
- {301, nullptr, "Unknown16"},
- {401, nullptr, "Unknown17"},
- {501, nullptr, "Unknown18"},
- {1001, nullptr, "Unknown19"},
- {1002, nullptr, "Unknown20"},
- {8001, nullptr, "Unknown21"},
- {8002, nullptr, "Unknown22"},
- {8011, nullptr, "Unknown23"},
- {8012, nullptr, "Unknown24"},
- {8021, nullptr, "Unknown25"},
- {10011, nullptr, "Unknown26"},
+ {0, nullptr, "GetAlbumFileCount"},
+ {1, nullptr, "GetAlbumFileList"},
+ {2, nullptr, "LoadAlbumFile"},
+ {3, nullptr, "DeleteAlbumFile"},
+ {4, nullptr, "StorageCopyAlbumFile"},
+ {5, nullptr, "IsAlbumMounted"},
+ {6, nullptr, "GetAlbumUsage"},
+ {7, nullptr, "GetAlbumFileSize"},
+ {8, nullptr, "LoadAlbumFileThumbnail"},
+ {9, nullptr, "LoadAlbumScreenShotImage"},
+ {10, nullptr, "LoadAlbumScreenShotThumbnailImage"},
+ {11, nullptr, "GetAlbumEntryFromApplicationAlbumEntry"},
+ {12, nullptr, "Unknown12"},
+ {13, nullptr, "Unknown13"},
+ {14, nullptr, "Unknown14"},
+ {15, nullptr, "Unknown15"},
+ {16, nullptr, "Unknown16"},
+ {17, nullptr, "Unknown17"},
+ {18, nullptr, "Unknown18"},
+ {202, nullptr, "SaveEditedScreenShot"},
+ {301, nullptr, "GetLastThumbnail"},
+ {401, nullptr, "GetAutoSavingStorage"},
+ {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"},
+ {1001, nullptr, "Unknown1001"},
+ {1002, nullptr, "Unknown1002"},
+ {1003, nullptr, "Unknown1003"},
+ {8001, nullptr, "ForceAlbumUnmounted"},
+ {8002, nullptr, "ResetAlbumMountStatus"},
+ {8011, nullptr, "RefreshAlbumCache"},
+ {8012, nullptr, "GetAlbumCache"},
+ {8013, nullptr, "Unknown8013"},
+ {8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"},
+ {10011, nullptr, "SetInternalErrorConversionEnabled"},
+ {50000, nullptr, "Unknown50000"},
+ {60002, nullptr, "Unknown60002"},
};
// clang-format on
@@ -53,16 +62,17 @@ public:
explicit CAPS_C() : ServiceFramework{"caps:c"} {
// clang-format off
static const FunctionInfo functions[] = {
- {2001, nullptr, "Unknown1"},
- {2002, nullptr, "Unknown2"},
- {2011, nullptr, "Unknown3"},
- {2012, nullptr, "Unknown4"},
- {2013, nullptr, "Unknown5"},
- {2014, nullptr, "Unknown6"},
- {2101, nullptr, "Unknown7"},
- {2102, nullptr, "Unknown8"},
- {2201, nullptr, "Unknown9"},
- {2301, nullptr, "Unknown10"},
+ {33, nullptr, "Unknown33"},
+ {2001, nullptr, "Unknown2001"},
+ {2002, nullptr, "Unknown2002"},
+ {2011, nullptr, "Unknown2011"},
+ {2012, nullptr, "Unknown2012"},
+ {2013, nullptr, "Unknown2013"},
+ {2014, nullptr, "Unknown2014"},
+ {2101, nullptr, "Unknown2101"},
+ {2102, nullptr, "Unknown2102"},
+ {2201, nullptr, "Unknown2201"},
+ {2301, nullptr, "Unknown2301"},
};
// clang-format on
@@ -127,11 +137,18 @@ public:
explicit CAPS_U() : ServiceFramework{"caps:u"} {
// clang-format off
static const FunctionInfo functions[] = {
+ {32, nullptr, "SetShimLibraryVersion"},
{102, nullptr, "GetAlbumFileListByAruid"},
{103, nullptr, "DeleteAlbumFileByAruid"},
{104, nullptr, "GetAlbumFileSizeByAruid"},
+ {105, nullptr, "DeleteAlbumFileByAruidForDebug"},
{110, nullptr, "LoadAlbumScreenShotImageByAruid"},
{120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"},
+ {130, nullptr, "PrecheckToCreateContentsByAruid"},
+ {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
+ {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
+ {142, nullptr, "GetAlbumFileList3AaeAruid"},
+ {143, nullptr, "GetAlbumFileList4AaeUidAruid"},
{60002, nullptr, "OpenAccessorSessionForApplication"},
};
// clang-format on
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index 770590d0b..2c229bcad 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -25,21 +25,34 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
Module::Interface::~Interface() = default;
struct FatalInfo {
- std::array<u64_le, 31> registers{}; // TODO(ogniK): See if this actually is registers or
- // not(find a game which has non zero valeus)
- u64_le unk0{};
- u64_le unk1{};
- u64_le unk2{};
- u64_le unk3{};
- u64_le unk4{};
- u64_le unk5{};
- u64_le unk6{};
+ enum class Architecture : s32 {
+ AArch64,
+ AArch32,
+ };
+
+ const char* ArchAsString() const {
+ return arch == Architecture::AArch64 ? "AArch64" : "AArch32";
+ }
+
+ std::array<u64_le, 31> registers{};
+ u64_le sp{};
+ u64_le pc{};
+ u64_le pstate{};
+ u64_le afsr0{};
+ u64_le afsr1{};
+ u64_le esr{};
+ u64_le far{};
std::array<u64_le, 32> backtrace{};
- u64_le unk7{};
- u64_le unk8{};
+ u64_le program_entry_point{};
+
+ // Bit flags that indicate which registers have been set with values
+ // for this context. The service itself uses these to determine which
+ // registers to specifically print out.
+ u64_le set_flags{};
+
u32_le backtrace_size{};
- u32_le unk9{};
+ Architecture arch{};
u32_le unk10{}; // TODO(ogniK): Is this even used or is it just padding?
};
static_assert(sizeof(FatalInfo) == 0x250, "FatalInfo is an invalid size");
@@ -52,36 +65,36 @@ enum class FatalType : u32 {
static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
const auto title_id = Core::CurrentProcess()->GetTitleID();
- std::string crash_report =
- fmt::format("Yuzu {}-{} crash report\n"
- "Title ID: {:016x}\n"
- "Result: 0x{:X} ({:04}-{:04d})\n"
- "\n",
- Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
- 2000 + static_cast<u32>(error_code.module.Value()),
- static_cast<u32>(error_code.description.Value()), info.unk8, info.unk7);
+ std::string crash_report = fmt::format(
+ "Yuzu {}-{} crash report\n"
+ "Title ID: {:016x}\n"
+ "Result: 0x{:X} ({:04}-{:04d})\n"
+ "Set flags: 0x{:16X}\n"
+ "Program entry point: 0x{:16X}\n"
+ "\n",
+ Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
+ 2000 + static_cast<u32>(error_code.module.Value()),
+ static_cast<u32>(error_code.description.Value()), info.set_flags, info.program_entry_point);
if (info.backtrace_size != 0x0) {
crash_report += "Registers:\n";
- // TODO(ogniK): This is just a guess, find a game which actually has non zero values
for (size_t i = 0; i < info.registers.size(); i++) {
crash_report +=
fmt::format(" X[{:02d}]: {:016x}\n", i, info.registers[i]);
}
- crash_report += fmt::format(" Unknown 0: {:016x}\n", info.unk0);
- crash_report += fmt::format(" Unknown 1: {:016x}\n", info.unk1);
- crash_report += fmt::format(" Unknown 2: {:016x}\n", info.unk2);
- crash_report += fmt::format(" Unknown 3: {:016x}\n", info.unk3);
- crash_report += fmt::format(" Unknown 4: {:016x}\n", info.unk4);
- crash_report += fmt::format(" Unknown 5: {:016x}\n", info.unk5);
- crash_report += fmt::format(" Unknown 6: {:016x}\n", info.unk6);
+ crash_report += fmt::format(" SP: {:016x}\n", info.sp);
+ crash_report += fmt::format(" PC: {:016x}\n", info.pc);
+ crash_report += fmt::format(" PSTATE: {:016x}\n", info.pstate);
+ crash_report += fmt::format(" AFSR0: {:016x}\n", info.afsr0);
+ crash_report += fmt::format(" AFSR1: {:016x}\n", info.afsr1);
+ crash_report += fmt::format(" ESR: {:016x}\n", info.esr);
+ crash_report += fmt::format(" FAR: {:016x}\n", info.far);
crash_report += "\nBacktrace:\n";
for (size_t i = 0; i < info.backtrace_size; i++) {
crash_report +=
fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
}
- crash_report += fmt::format("\nUnknown 7: 0x{:016x}\n", info.unk7);
- crash_report += fmt::format("Unknown 8: 0x{:016x}\n", info.unk8);
- crash_report += fmt::format("Unknown 9: 0x{:016x}\n", info.unk9);
+
+ crash_report += fmt::format("Architecture: {}\n", info.ArchAsString());
crash_report += fmt::format("Unknown 10: 0x{:016x}\n", info.unk10);
}
@@ -125,13 +138,13 @@ static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const F
case FatalType::ErrorReport:
GenerateErrorReport(error_code, info);
break;
- };
+ }
}
void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp{ctx};
- auto error_code = rp.Pop<ResultCode>();
+ const auto error_code = rp.Pop<ResultCode>();
ThrowFatalError(error_code, FatalType::ErrorScreen, {});
IPC::ResponseBuilder rb{ctx, 2};
@@ -141,8 +154,8 @@ void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp(ctx);
- auto error_code = rp.Pop<ResultCode>();
- auto fatal_type = rp.PopEnum<FatalType>();
+ const auto error_code = rp.Pop<ResultCode>();
+ const auto fatal_type = rp.PopEnum<FatalType>();
ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy
IPC::ResponseBuilder rb{ctx, 2};
@@ -152,9 +165,9 @@ void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp(ctx);
- auto error_code = rp.Pop<ResultCode>();
- auto fatal_type = rp.PopEnum<FatalType>();
- auto fatal_info = ctx.ReadBuffer();
+ const auto error_code = rp.Pop<ResultCode>();
+ const auto fatal_type = rp.PopEnum<FatalType>();
+ const auto fatal_info = ctx.ReadBuffer();
FatalInfo info{};
ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!");
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index c6da2df43..1ebfeb4bf 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -197,13 +197,16 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
FileSys::Mode mode) const {
- std::string path(FileUtil::SanitizePath(path_));
- auto npath = path;
- while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\'))
- npath = npath.substr(1);
+ const std::string path(FileUtil::SanitizePath(path_));
+ std::string_view npath = path;
+ while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) {
+ npath.remove_prefix(1);
+ }
+
auto file = backing->GetFileRelative(npath);
- if (file == nullptr)
+ if (file == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND;
+ }
if (mode == FileSys::Mode::Append) {
return MakeResult<FileSys::VirtualFile>(
@@ -319,15 +322,15 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora
}
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
- FileSys::SaveDataDescriptor save_struct) {
+ const FileSys::SaveDataDescriptor& descriptor) {
LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
- static_cast<u8>(space), save_struct.DebugInfo());
+ static_cast<u8>(space), descriptor.DebugInfo());
if (save_data_factory == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
- return save_data_factory->Open(space, save_struct);
+ return save_data_factory->Open(space, descriptor);
}
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) {
@@ -388,11 +391,6 @@ void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
}
-FileSys::RegisteredCacheUnion GetUnionContents() {
- return FileSys::RegisteredCacheUnion{
- {GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}};
-}
-
FileSys::RegisteredCache* GetSystemNANDContents() {
LOG_TRACE(Service_FS, "Opening System NAND Contents");
@@ -457,6 +455,10 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
if (bis_factory == nullptr) {
bis_factory =
std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
+ Core::System::GetInstance().RegisterContentProvider(
+ FileSys::ContentProviderUnionSlot::SysNAND, bis_factory->GetSystemNANDContents());
+ Core::System::GetInstance().RegisterContentProvider(
+ FileSys::ContentProviderUnionSlot::UserNAND, bis_factory->GetUserNANDContents());
}
if (save_data_factory == nullptr) {
@@ -465,6 +467,8 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
if (sdmc_factory == nullptr) {
sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
+ Core::System::GetInstance().RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC,
+ sdmc_factory->GetSDMCContents());
}
}
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 6fd5e7b23..6481f237c 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -46,7 +46,7 @@ ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess();
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type);
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
- FileSys::SaveDataDescriptor save_struct);
+ const FileSys::SaveDataDescriptor& descriptor);
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
ResultVal<FileSys::VirtualDir> OpenSDMC();
@@ -54,8 +54,6 @@ FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
FileSys::SaveDataSize new_value);
-FileSys::RegisteredCacheUnion GetUnionContents();
-
FileSys::RegisteredCache* GetSystemNANDContents();
FileSys::RegisteredCache* GetUserNANDContents();
FileSys::RegisteredCache* GetSDMCContents();
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 54959edd8..e7df8fd98 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -115,11 +115,12 @@ private:
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const u64 unk = rp.Pop<u64>();
+ const u64 option = rp.Pop<u64>();
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
- LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
+ LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
+ length);
// Error checking
if (length < 0) {
@@ -148,11 +149,12 @@ private:
void Write(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const u64 unk = rp.Pop<u64>();
+ const u64 option = rp.Pop<u64>();
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
- LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
+ LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
+ length);
// Error checking
if (length < 0) {
@@ -250,10 +252,7 @@ private:
u64 next_entry_index = 0;
void Read(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 unk = rp.Pop<u64>();
-
- LOG_DEBUG(Service_FS, "called, unk=0x{:X}", unk);
+ LOG_DEBUG(Service_FS, "called.");
// Calculate how many entries we can fit in the output buffer
const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
@@ -315,61 +314,53 @@ public:
void CreateFile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto file_buffer = ctx.ReadBuffer();
- std::string name = Common::StringFromBuffer(file_buffer);
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
- u64 mode = rp.Pop<u64>();
- u32 size = rp.Pop<u32>();
+ const u64 mode = rp.Pop<u64>();
+ const u32 size = rp.Pop<u32>();
- LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size);
+ LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, mode, size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.CreateFile(name, size));
}
void DeleteFile(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- auto file_buffer = ctx.ReadBuffer();
- std::string name = Common::StringFromBuffer(file_buffer);
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
- LOG_DEBUG(Service_FS, "called file {}", name);
+ LOG_DEBUG(Service_FS, "called. file={}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.DeleteFile(name));
}
void CreateDirectory(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- auto file_buffer = ctx.ReadBuffer();
- std::string name = Common::StringFromBuffer(file_buffer);
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
- LOG_DEBUG(Service_FS, "called directory {}", name);
+ LOG_DEBUG(Service_FS, "called. directory={}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.CreateDirectory(name));
}
void DeleteDirectory(Kernel::HLERequestContext& ctx) {
- const IPC::RequestParser rp{ctx};
-
const auto file_buffer = ctx.ReadBuffer();
- std::string name = Common::StringFromBuffer(file_buffer);
+ const std::string name = Common::StringFromBuffer(file_buffer);
- LOG_DEBUG(Service_FS, "called directory {}", name);
+ LOG_DEBUG(Service_FS, "called. directory={}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.DeleteDirectory(name));
}
void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) {
- const IPC::RequestParser rp{ctx};
-
const auto file_buffer = ctx.ReadBuffer();
- std::string name = Common::StringFromBuffer(file_buffer);
+ const std::string name = Common::StringFromBuffer(file_buffer);
- LOG_DEBUG(Service_FS, "called directory {}", name);
+ LOG_DEBUG(Service_FS, "called. directory={}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.DeleteDirectoryRecursively(name));
@@ -386,18 +377,16 @@ public:
}
void RenameFile(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
std::vector<u8> buffer;
buffer.resize(ctx.BufferDescriptorX()[0].Size());
Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
- std::string src_name = Common::StringFromBuffer(buffer);
+ const std::string src_name = Common::StringFromBuffer(buffer);
buffer.resize(ctx.BufferDescriptorX()[1].Size());
Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size());
- std::string dst_name = Common::StringFromBuffer(buffer);
+ const std::string dst_name = Common::StringFromBuffer(buffer);
- LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name);
+ LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.RenameFile(src_name, dst_name));
@@ -406,12 +395,12 @@ public:
void OpenFile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto file_buffer = ctx.ReadBuffer();
- std::string name = Common::StringFromBuffer(file_buffer);
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
- auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
+ const auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
- LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode));
+ LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, static_cast<u32>(mode));
auto result = backend.OpenFile(name, mode);
if (result.Failed()) {
@@ -430,13 +419,13 @@ public:
void OpenDirectory(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto file_buffer = ctx.ReadBuffer();
- std::string name = Common::StringFromBuffer(file_buffer);
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
// TODO(Subv): Implement this filter.
- u32 filter_flags = rp.Pop<u32>();
+ const u32 filter_flags = rp.Pop<u32>();
- LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags);
+ LOG_DEBUG(Service_FS, "called. directory={}, filter={}", name, filter_flags);
auto result = backend.OpenDirectory(name);
if (result.Failed()) {
@@ -453,12 +442,10 @@ public:
}
void GetEntryType(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- auto file_buffer = ctx.ReadBuffer();
- std::string name = Common::StringFromBuffer(file_buffer);
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
- LOG_DEBUG(Service_FS, "called file {}", name);
+ LOG_DEBUG(Service_FS, "called. file={}", name);
auto result = backend.GetEntryType(name);
if (result.Failed()) {
@@ -616,7 +603,9 @@ private:
u64_le save_id;
u64_le title_id;
u64_le save_image_size;
- INSERT_PADDING_BYTES(0x28);
+ u16_le index;
+ FileSys::SaveDataRank rank;
+ INSERT_PADDING_BYTES(0x25);
};
static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
@@ -675,10 +664,13 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{100, nullptr, "OpenImageDirectoryFileSystem"},
{110, nullptr, "OpenContentStorageFileSystem"},
{120, nullptr, "OpenCloudBackupWorkStorageFileSystem"},
+ {130, nullptr, "OpenCustomStorageFileSystem"},
{200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"},
{201, nullptr, "OpenDataStorageByProgramId"},
{202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"},
{203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"},
+ {204, nullptr, "OpenDataFileSystemByProgramIndex"},
+ {205, nullptr, "OpenDataStorageByProgramIndex"},
{400, nullptr, "OpenDeviceOperator"},
{500, nullptr, "OpenSdCardDetectionEventNotifier"},
{501, nullptr, "OpenGameCardDetectionEventNotifier"},
@@ -702,6 +694,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"},
{615, nullptr, "QuerySaveDataInternalStorageTotalSize"},
{616, nullptr, "GetSaveDataCommitId"},
+ {617, nullptr, "UnregisterExternalKey"},
{620, nullptr, "SetSdCardEncryptionSeed"},
{630, nullptr, "SetSdCardAccessibility"},
{631, nullptr, "IsSdCardAccessible"},
@@ -712,6 +705,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{710, nullptr, "ResolveAccessFailure"},
{720, nullptr, "AbandonAccessFailure"},
{800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
+ {810, nullptr, "RegisterProgramIndexMapInfo"},
{1000, nullptr, "SetBisRootForHost"},
{1001, nullptr, "SetSaveDataSize"},
{1002, nullptr, "SetSaveDataRootPath"},
@@ -722,6 +716,8 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{1007, nullptr, "RegisterUpdatePartition"},
{1008, nullptr, "OpenRegisteredUpdatePartition"},
{1009, nullptr, "GetAndClearMemoryReportInfo"},
+ {1010, nullptr, "SetDataStorageRedirectTarget"},
+ {1011, nullptr, "OutputAccessLogToSdCard2"},
{1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
{1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
{1200, nullptr, "OpenMultiCommitManager"},
@@ -733,7 +729,10 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
FSP_SRV::~FSP_SRV() = default;
void FSP_SRV::SetCurrentProcess(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_FS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ current_process_id = rp.Pop<u64>();
+
+ LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -776,16 +775,17 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
}
void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
+ LOG_INFO(Service_FS, "called.");
- auto space_id = rp.PopRaw<FileSys::SaveDataSpaceId>();
- auto unk = rp.Pop<u32>();
- LOG_INFO(Service_FS, "called with unknown={:08X}", unk);
-
- auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
+ struct Parameters {
+ FileSys::SaveDataSpaceId save_data_space_id;
+ FileSys::SaveDataDescriptor descriptor;
+ };
- auto dir = OpenSaveData(space_id, save_struct);
+ IPC::RequestParser rp{ctx};
+ const auto parameters = rp.PopRaw<Parameters>();
+ auto dir = OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
if (dir.Failed()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 3a5f4e200..d7572ba7a 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -32,6 +32,7 @@ private:
void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
FileSys::VirtualFile romfs;
+ u64 current_process_id = 0;
};
} // namespace Service::FileSystem
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index d9225d624..5100e376c 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -12,6 +12,7 @@ namespace Service::Friend {
class IFriendService final : public ServiceFramework<IFriendService> {
public:
IFriendService() : ServiceFramework("IFriendService") {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetCompletionEvent"},
{1, nullptr, "Cancel"},
@@ -24,8 +25,7 @@ public:
{10400, nullptr, "GetBlockedUserListIds"},
{10500, nullptr, "GetProfileList"},
{10600, nullptr, "DeclareOpenOnlinePlaySession"},
- {10601, &IFriendService::DeclareCloseOnlinePlaySession,
- "DeclareCloseOnlinePlaySession"},
+ {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"},
{10610, &IFriendService::UpdateUserPresence, "UpdateUserPresence"},
{10700, nullptr, "GetPlayHistoryRegistrationKey"},
{10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"},
@@ -88,6 +88,7 @@ public:
{30830, nullptr, "ClearPlayLog"},
{49900, nullptr, "DeleteNetworkServiceAccountCache"},
};
+ // clang-format on
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 929035034..e584b92ec 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -41,20 +41,20 @@ private:
struct PadState {
union {
u32_le raw{};
- BitField<0, 1, u32_le> a;
- BitField<1, 1, u32_le> b;
- BitField<2, 1, u32_le> x;
- BitField<3, 1, u32_le> y;
- BitField<4, 1, u32_le> l;
- BitField<5, 1, u32_le> r;
- BitField<6, 1, u32_le> zl;
- BitField<7, 1, u32_le> zr;
- BitField<8, 1, u32_le> plus;
- BitField<9, 1, u32_le> minus;
- BitField<10, 1, u32_le> d_left;
- BitField<11, 1, u32_le> d_up;
- BitField<12, 1, u32_le> d_right;
- BitField<13, 1, u32_le> d_down;
+ BitField<0, 1, u32> a;
+ BitField<1, 1, u32> b;
+ BitField<2, 1, u32> x;
+ BitField<3, 1, u32> y;
+ BitField<4, 1, u32> l;
+ BitField<5, 1, u32> r;
+ BitField<6, 1, u32> zl;
+ BitField<7, 1, u32> zr;
+ BitField<8, 1, u32> plus;
+ BitField<9, 1, u32> minus;
+ BitField<10, 1, u32> d_left;
+ BitField<11, 1, u32> d_up;
+ BitField<12, 1, u32> d_right;
+ BitField<13, 1, u32> d_down;
};
};
static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
@@ -62,7 +62,7 @@ private:
struct Attributes {
union {
u32_le raw{};
- BitField<0, 1, u32_le> connected;
+ BitField<0, 1, u32> connected;
};
};
static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index e7fc7a619..fdd6d79a2 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -170,7 +170,7 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
void Controller_NPad::OnInit() {
auto& kernel = Core::System::GetInstance().Kernel();
styleset_changed_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
+ kernel, Kernel::ResetType::Automatic, "npad:NpadStyleSetChanged");
if (!IsControllerActivated()) {
return;
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 18c7a94e6..4ff50b3cd 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -39,13 +39,13 @@ public:
union {
u32_le raw{};
- BitField<0, 1, u32_le> pro_controller;
- BitField<1, 1, u32_le> handheld;
- BitField<2, 1, u32_le> joycon_dual;
- BitField<3, 1, u32_le> joycon_left;
- BitField<4, 1, u32_le> joycon_right;
+ BitField<0, 1, u32> pro_controller;
+ BitField<1, 1, u32> handheld;
+ BitField<2, 1, u32> joycon_dual;
+ BitField<3, 1, u32> joycon_left;
+ BitField<4, 1, u32> joycon_right;
- BitField<6, 1, u32_le> pokeball; // TODO(ogniK): Confirm when possible
+ BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
};
};
static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
@@ -150,43 +150,43 @@ private:
union {
u64_le raw{};
// Button states
- BitField<0, 1, u64_le> a;
- BitField<1, 1, u64_le> b;
- BitField<2, 1, u64_le> x;
- BitField<3, 1, u64_le> y;
- BitField<4, 1, u64_le> l_stick;
- BitField<5, 1, u64_le> r_stick;
- BitField<6, 1, u64_le> l;
- BitField<7, 1, u64_le> r;
- BitField<8, 1, u64_le> zl;
- BitField<9, 1, u64_le> zr;
- BitField<10, 1, u64_le> plus;
- BitField<11, 1, u64_le> minus;
+ BitField<0, 1, u64> a;
+ BitField<1, 1, u64> b;
+ BitField<2, 1, u64> x;
+ BitField<3, 1, u64> y;
+ BitField<4, 1, u64> l_stick;
+ BitField<5, 1, u64> r_stick;
+ BitField<6, 1, u64> l;
+ BitField<7, 1, u64> r;
+ BitField<8, 1, u64> zl;
+ BitField<9, 1, u64> zr;
+ BitField<10, 1, u64> plus;
+ BitField<11, 1, u64> minus;
// D-Pad
- BitField<12, 1, u64_le> d_left;
- BitField<13, 1, u64_le> d_up;
- BitField<14, 1, u64_le> d_right;
- BitField<15, 1, u64_le> d_down;
+ BitField<12, 1, u64> d_left;
+ BitField<13, 1, u64> d_up;
+ BitField<14, 1, u64> d_right;
+ BitField<15, 1, u64> d_down;
// Left JoyStick
- BitField<16, 1, u64_le> l_stick_left;
- BitField<17, 1, u64_le> l_stick_up;
- BitField<18, 1, u64_le> l_stick_right;
- BitField<19, 1, u64_le> l_stick_down;
+ BitField<16, 1, u64> l_stick_left;
+ BitField<17, 1, u64> l_stick_up;
+ BitField<18, 1, u64> l_stick_right;
+ BitField<19, 1, u64> l_stick_down;
// Right JoyStick
- BitField<20, 1, u64_le> r_stick_left;
- BitField<21, 1, u64_le> r_stick_up;
- BitField<22, 1, u64_le> r_stick_right;
- BitField<23, 1, u64_le> r_stick_down;
+ BitField<20, 1, u64> r_stick_left;
+ BitField<21, 1, u64> r_stick_up;
+ BitField<22, 1, u64> r_stick_right;
+ BitField<23, 1, u64> r_stick_down;
// Not always active?
- BitField<24, 1, u64_le> left_sl;
- BitField<25, 1, u64_le> left_sr;
+ BitField<24, 1, u64> left_sl;
+ BitField<25, 1, u64> left_sr;
- BitField<26, 1, u64_le> right_sl;
- BitField<27, 1, u64_le> right_sr;
+ BitField<26, 1, u64> right_sl;
+ BitField<27, 1, u64> right_sr;
};
};
static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
@@ -200,12 +200,12 @@ private:
struct ConnectionState {
union {
u32_le raw{};
- BitField<0, 1, u32_le> IsConnected;
- BitField<1, 1, u32_le> IsWired;
- BitField<2, 1, u32_le> IsLeftJoyConnected;
- BitField<3, 1, u32_le> IsLeftJoyWired;
- BitField<4, 1, u32_le> IsRightJoyConnected;
- BitField<5, 1, u32_le> IsRightJoyWired;
+ BitField<0, 1, u32> IsConnected;
+ BitField<1, 1, u32> IsWired;
+ BitField<2, 1, u32> IsLeftJoyConnected;
+ BitField<3, 1, u32> IsLeftJoyWired;
+ BitField<4, 1, u32> IsRightJoyConnected;
+ BitField<5, 1, u32> IsRightJoyWired;
};
};
static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
@@ -240,23 +240,23 @@ private:
struct NPadProperties {
union {
s64_le raw{};
- BitField<11, 1, s64_le> is_vertical;
- BitField<12, 1, s64_le> is_horizontal;
- BitField<13, 1, s64_le> use_plus;
- BitField<14, 1, s64_le> use_minus;
+ BitField<11, 1, s64> is_vertical;
+ BitField<12, 1, s64> is_horizontal;
+ BitField<13, 1, s64> use_plus;
+ BitField<14, 1, s64> use_minus;
};
};
struct NPadDevice {
union {
u32_le raw{};
- BitField<0, 1, s32_le> pro_controller;
- BitField<1, 1, s32_le> handheld;
- BitField<2, 1, s32_le> handheld_left;
- BitField<3, 1, s32_le> handheld_right;
- BitField<4, 1, s32_le> joycon_left;
- BitField<5, 1, s32_le> joycon_right;
- BitField<6, 1, s32_le> pokeball;
+ BitField<0, 1, s32> pro_controller;
+ BitField<1, 1, s32> handheld;
+ BitField<2, 1, s32> handheld_left;
+ BitField<3, 1, s32> handheld_right;
+ BitField<4, 1, s32> joycon_left;
+ BitField<5, 1, s32> joycon_right;
+ BitField<6, 1, s32> pokeball;
};
};
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 012b6e0dd..76fc340e9 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -33,8 +33,8 @@ private:
struct Attributes {
union {
u32 raw{};
- BitField<0, 1, u32_le> start_touch;
- BitField<1, 1, u32_le> end_touch;
+ BitField<0, 1, u32> start_touch;
+ BitField<1, 1, u32> end_touch;
};
};
static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 8a6de83a2..a4ad95d96 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -36,9 +36,9 @@ namespace Service::HID {
// Updating period for each HID device.
// TODO(ogniK): Find actual polling rate of hid
-constexpr u64 pad_update_ticks = Core::Timing::BASE_CLOCK_RATE / 66;
-constexpr u64 accelerometer_update_ticks = Core::Timing::BASE_CLOCK_RATE / 100;
-constexpr u64 gyroscope_update_ticks = Core::Timing::BASE_CLOCK_RATE / 100;
+constexpr s64 pad_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 66);
+constexpr s64 accelerometer_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
+constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
@@ -75,7 +75,7 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
// Register update callbacks
auto& core_timing = Core::System::GetInstance().CoreTiming();
pad_update_event =
- core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, int cycles_late) {
+ core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) {
UpdateControllers(userdata, cycles_late);
});
@@ -106,7 +106,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
rb.PushCopyObjects(shared_mem);
}
-void IAppletResource::UpdateControllers(u64 userdata, int cycles_late) {
+void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) {
auto& core_timing = Core::System::GetInstance().CoreTiming();
const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
@@ -210,6 +210,7 @@ Hid::Hid() : ServiceFramework("hid") {
{131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"},
{132, nullptr, "EnableUnintendedHomeButtonInputProtection"},
{133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
+ {134, nullptr, "SetNpadAnalogStickUseCenterClamp"},
{200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
{201, &Hid::SendVibrationValue, "SendVibrationValue"},
{202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
@@ -221,6 +222,7 @@ Hid::Hid() : ServiceFramework("hid") {
{208, nullptr, "GetActualVibrationGcErmCommand"},
{209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
{210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
+ {211, nullptr, "IsVibrationDeviceMounted"},
{300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
{301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
{302, nullptr, "StopConsoleSixAxisSensor"},
@@ -265,6 +267,7 @@ Hid::Hid() : ServiceFramework("hid") {
{523, nullptr, "SetIsPalmaPairedConnectable"},
{524, nullptr, "PairPalma"},
{525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
+ {526, nullptr, "CancelWritePalmaWaveEntry"},
{1000, nullptr, "SetNpadCommunicationMode"},
{1001, nullptr, "GetNpadCommunicationMode"},
};
@@ -797,12 +800,22 @@ public:
{232, nullptr, "EnableShipmentMode"},
{233, nullptr, "ClearPairingInfo"},
{234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
+ {235, nullptr, "EnableAnalogStickPower"},
{301, nullptr, "GetAbstractedPadHandles"},
{302, nullptr, "GetAbstractedPadState"},
{303, nullptr, "GetAbstractedPadsState"},
{321, nullptr, "SetAutoPilotVirtualPadState"},
{322, nullptr, "UnsetAutoPilotVirtualPadState"},
{323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
+ {324, nullptr, "AttachHdlsWorkBuffer"},
+ {325, nullptr, "ReleaseHdlsWorkBuffer"},
+ {326, nullptr, "DumpHdlsNpadAssignmentState"},
+ {327, nullptr, "DumpHdlsStates"},
+ {328, nullptr, "ApplyHdlsNpadAssignmentState"},
+ {329, nullptr, "ApplyHdlsStateList"},
+ {330, nullptr, "AttachHdlsVirtualDevice"},
+ {331, nullptr, "DetachHdlsVirtualDevice"},
+ {332, nullptr, "SetHdlsState"},
{350, nullptr, "AddRegisteredDevice"},
{400, nullptr, "DisableExternalMcuOnNxDevice"},
{401, nullptr, "DisableRailDeviceFiltering"},
@@ -825,6 +838,7 @@ public:
{131, nullptr, "ActivateSleepButton"},
{141, nullptr, "AcquireCaptureButtonEventHandle"},
{151, nullptr, "ActivateCaptureButton"},
+ {161, nullptr, "GetPlatformConfig"},
{210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
{211, nullptr, "GetNpadsWithNfc"},
{212, nullptr, "AcquireNfcActivateEventHandle"},
@@ -894,6 +908,7 @@ public:
{827, nullptr, "IsAnalogStickButtonPressed"},
{828, nullptr, "IsAnalogStickInReleasePosition"},
{829, nullptr, "IsAnalogStickInCircumference"},
+ {830, nullptr, "SetNotificationLedPattern"},
{850, nullptr, "IsUsbFullKeyControllerEnabled"},
{851, nullptr, "EnableUsbFullKeyController"},
{852, nullptr, "IsUsbConnected"},
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 7cc58db4c..d3660cad2 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -4,6 +4,9 @@
#pragma once
+#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/hle/service/service.h"
+
#include "controllers/controller_base.h"
#include "core/hle/service/service.h"
@@ -62,7 +65,7 @@ private:
}
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
- void UpdateControllers(u64 userdata, int cycles_late);
+ void UpdateControllers(u64 userdata, s64 cycles_late);
Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index e250595e3..ed5059047 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -52,9 +52,11 @@ public:
}
};
-class ILocalCommunicationService final : public ServiceFramework<ILocalCommunicationService> {
+class ISystemLocalCommunicationService final
+ : public ServiceFramework<ISystemLocalCommunicationService> {
public:
- explicit ILocalCommunicationService(const char* name) : ServiceFramework{name} {
+ explicit ISystemLocalCommunicationService()
+ : ServiceFramework{"ISystemLocalCommunicationService"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetState"},
@@ -84,6 +86,50 @@ public:
{304, nullptr, "Disconnect"},
{400, nullptr, "InitializeSystem"},
{401, nullptr, "FinalizeSystem"},
+ {402, nullptr, "SetOperationMode"},
+ {403, nullptr, "InitializeSystem2"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class IUserLocalCommunicationService final
+ : public ServiceFramework<IUserLocalCommunicationService> {
+public:
+ explicit IUserLocalCommunicationService() : ServiceFramework{"IUserLocalCommunicationService"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetState"},
+ {1, nullptr, "GetNetworkInfo"},
+ {2, nullptr, "GetIpv4Address"},
+ {3, nullptr, "GetDisconnectReason"},
+ {4, nullptr, "GetSecurityParameter"},
+ {5, nullptr, "GetNetworkConfig"},
+ {100, nullptr, "AttachStateChangeEvent"},
+ {101, nullptr, "GetNetworkInfoLatestUpdate"},
+ {102, nullptr, "Scan"},
+ {103, nullptr, "ScanPrivate"},
+ {104, nullptr, "SetWirelessControllerRestriction"},
+ {200, nullptr, "OpenAccessPoint"},
+ {201, nullptr, "CloseAccessPoint"},
+ {202, nullptr, "CreateNetwork"},
+ {203, nullptr, "CreateNetworkPrivate"},
+ {204, nullptr, "DestroyNetwork"},
+ {205, nullptr, "Reject"},
+ {206, nullptr, "SetAdvertiseData"},
+ {207, nullptr, "SetStationAcceptPolicy"},
+ {208, nullptr, "AddAcceptFilterEntry"},
+ {209, nullptr, "ClearAcceptFilter"},
+ {300, nullptr, "OpenStation"},
+ {301, nullptr, "CloseStation"},
+ {302, nullptr, "Connect"},
+ {303, nullptr, "ConnectPrivate"},
+ {304, nullptr, "Disconnect"},
+ {400, nullptr, "Initialize"},
+ {401, nullptr, "Finalize"},
+ {402, nullptr, "SetOperationMode"},
};
// clang-format on
@@ -108,7 +154,7 @@ public:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ILocalCommunicationService>("ISystemLocalCommunicationService");
+ rb.PushIpcInterface<ISystemLocalCommunicationService>();
}
};
@@ -129,7 +175,7 @@ public:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ILocalCommunicationService>("IUserLocalCommunicationService");
+ rb.PushIpcInterface<IUserLocalCommunicationService>();
}
};
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 9df7ac50f..b839303ac 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -86,6 +86,7 @@ public:
{2, &RelocatableObject::LoadNrr, "LoadNrr"},
{3, &RelocatableObject::UnloadNrr, "UnloadNrr"},
{4, &RelocatableObject::Initialize, "Initialize"},
+ {10, nullptr, "LoadNrrEx"},
};
// clang-format on
@@ -93,12 +94,18 @@ public:
}
void LoadNrr(Kernel::HLERequestContext& ctx) {
+ struct Parameters {
+ u64_le process_id;
+ u64_le nrr_address;
+ u64_le nrr_size;
+ };
+
IPC::RequestParser rp{ctx};
- rp.Skip(2, false);
- const VAddr nrr_addr{rp.Pop<VAddr>()};
- const u64 nrr_size{rp.Pop<u64>()};
- LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}, nrr_size={:016X}", nrr_addr,
- nrr_size);
+ const auto [process_id, nrr_address, nrr_size] = rp.PopRaw<Parameters>();
+
+ LOG_DEBUG(Service_LDR,
+ "called with process_id={:016X}, nrr_address={:016X}, nrr_size={:016X}",
+ process_id, nrr_address, nrr_size);
if (!initialized) {
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
@@ -116,24 +123,26 @@ public:
}
// NRR Address does not fall on 0x1000 byte boundary
- if (!Common::Is4KBAligned(nrr_addr)) {
- LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
+ if (!Common::Is4KBAligned(nrr_address)) {
+ LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!",
+ nrr_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ALIGNMENT);
return;
}
// NRR Size is zero or causes overflow
- if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) {
+ if (nrr_address + nrr_size <= nrr_address || nrr_size == 0 ||
+ !Common::Is4KBAligned(nrr_size)) {
LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})",
- nrr_addr, nrr_size);
+ nrr_address, nrr_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_SIZE);
return;
}
// Read NRR data from memory
std::vector<u8> nrr_data(nrr_size);
- Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size);
+ Memory::ReadBlock(nrr_address, nrr_data.data(), nrr_size);
NRRHeader header;
std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
@@ -174,7 +183,7 @@ public:
hashes.emplace_back(hash);
}
- nrr.insert_or_assign(nrr_addr, std::move(hashes));
+ nrr.insert_or_assign(nrr_address, std::move(hashes));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -188,23 +197,30 @@ public:
return;
}
+ struct Parameters {
+ u64_le process_id;
+ u64_le nrr_address;
+ };
+
IPC::RequestParser rp{ctx};
- rp.Skip(2, false);
- const auto nrr_addr{rp.Pop<VAddr>()};
- LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}", nrr_addr);
+ const auto [process_id, nrr_address] = rp.PopRaw<Parameters>();
+
+ LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nrr_addr={:016X}", process_id,
+ nrr_address);
- if (!Common::Is4KBAligned(nrr_addr)) {
- LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
+ if (!Common::Is4KBAligned(nrr_address)) {
+ LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!",
+ nrr_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ALIGNMENT);
return;
}
- const auto iter = nrr.find(nrr_addr);
+ const auto iter = nrr.find(nrr_address);
if (iter == nrr.end()) {
LOG_ERROR(Service_LDR,
"Attempting to unload NRR which has not been loaded! (addr={:016X})",
- nrr_addr);
+ nrr_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_NRR_ADDRESS);
return;
@@ -216,16 +232,22 @@ public:
}
void LoadNro(Kernel::HLERequestContext& ctx) {
+ struct Parameters {
+ u64_le process_id;
+ u64_le image_address;
+ u64_le image_size;
+ u64_le bss_address;
+ u64_le bss_size;
+ };
+
IPC::RequestParser rp{ctx};
- rp.Skip(2, false);
- const VAddr nro_addr{rp.Pop<VAddr>()};
- const u64 nro_size{rp.Pop<u64>()};
- const VAddr bss_addr{rp.Pop<VAddr>()};
- const u64 bss_size{rp.Pop<u64>()};
- LOG_DEBUG(
- Service_LDR,
- "called with nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, bss_size={:016X}",
- nro_addr, nro_size, bss_addr, bss_size);
+ const auto [process_id, nro_address, nro_size, bss_address, bss_size] =
+ rp.PopRaw<Parameters>();
+
+ LOG_DEBUG(Service_LDR,
+ "called with pid={:016X}, nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, "
+ "bss_size={:016X}",
+ process_id, nro_address, nro_size, bss_address, bss_size);
if (!initialized) {
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
@@ -243,8 +265,9 @@ public:
}
// NRO Address does not fall on 0x1000 byte boundary
- if (!Common::Is4KBAligned(nro_addr)) {
- LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr);
+ if (!Common::Is4KBAligned(nro_address)) {
+ LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!",
+ nro_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ALIGNMENT);
return;
@@ -252,15 +275,15 @@ public:
// NRO Size or BSS Size is zero or causes overflow
const auto nro_size_valid =
- nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size);
- const auto bss_size_valid =
- nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr);
+ nro_size != 0 && nro_address + nro_size > nro_address && Common::Is4KBAligned(nro_size);
+ const auto bss_size_valid = nro_size + bss_size >= nro_size &&
+ (bss_size == 0 || bss_address + bss_size > bss_address);
if (!nro_size_valid || !bss_size_valid) {
LOG_ERROR(Service_LDR,
"NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, "
"bss_address={:016X}, bss_size={:016X})",
- nro_addr, nro_size, bss_addr, bss_size);
+ nro_address, nro_size, bss_address, bss_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_SIZE);
return;
@@ -268,7 +291,7 @@ public:
// Read NRO data from memory
std::vector<u8> nro_data(nro_size);
- Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
+ Memory::ReadBlock(nro_address, nro_data.data(), nro_size);
SHA256Hash hash{};
mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0);
@@ -287,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;
@@ -318,18 +341,18 @@ public:
return;
}
- ASSERT(vm_manager
- .MirrorMemory(*map_address, nro_addr, nro_size,
- Kernel::MemoryState::ModuleCodeStatic)
- .IsSuccess());
- ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess());
+ ASSERT(
+ vm_manager
+ .MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode)
+ .IsSuccess());
+ ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess());
if (bss_size > 0) {
ASSERT(vm_manager
- .MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
- Kernel::MemoryState::ModuleCodeStatic)
+ .MirrorMemory(*map_address + nro_size, bss_address, bss_size,
+ Kernel::MemoryState::ModuleCode)
.IsSuccess());
- ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess());
+ ASSERT(vm_manager.UnmapRange(bss_address, bss_size).IsSuccess());
}
vm_manager.ReprotectRange(*map_address, header.text_size,
@@ -349,13 +372,6 @@ public:
}
void UnloadNro(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- rp.Skip(2, false);
- const VAddr mapped_addr{rp.PopRaw<VAddr>()};
- const VAddr heap_addr{rp.PopRaw<VAddr>()};
- LOG_DEBUG(Service_LDR, "called with mapped_addr={:016X}, heap_addr={:016X}", mapped_addr,
- heap_addr);
-
if (!initialized) {
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
IPC::ResponseBuilder rb{ctx, 2};
@@ -363,22 +379,30 @@ public:
return;
}
- if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) {
- LOG_ERROR(Service_LDR,
- "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, "
- "bss_addr={:016X})!",
- mapped_addr, heap_addr);
+ struct Parameters {
+ u64_le process_id;
+ u64_le nro_address;
+ };
+
+ IPC::RequestParser rp{ctx};
+ const auto [process_id, nro_address] = rp.PopRaw<Parameters>();
+ LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nro_address=0x{:016X}", process_id,
+ nro_address);
+
+ if (!Common::Is4KBAligned(nro_address)) {
+ LOG_ERROR(Service_LDR, "NRO address has invalid alignment (nro_address=0x{:016X})",
+ nro_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ALIGNMENT);
return;
}
- const auto iter = nro.find(mapped_addr);
+ const auto iter = nro.find(nro_address);
if (iter == nro.end()) {
LOG_ERROR(Service_LDR,
- "The NRO attempting to unmap was not mapped or has an invalid address "
- "(actual {:016X})!",
- mapped_addr);
+ "The NRO attempting to be unmapped was not mapped or has an invalid address "
+ "(nro_address=0x{:016X})!",
+ nro_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_NRO_ADDRESS);
return;
@@ -387,11 +411,7 @@ public:
auto& vm_manager = Core::CurrentProcess()->VMManager();
const auto& nro_size = iter->second.size;
- ASSERT(vm_manager
- .MirrorMemory(heap_addr, mapped_addr, nro_size,
- Kernel::MemoryState::ModuleCodeStatic)
- .IsSuccess());
- ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess());
+ ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess());
Core::System::GetInstance().InvalidateCpuInstructionCaches();
@@ -461,11 +481,10 @@ private:
std::map<VAddr, NROInfo> nro;
std::map<VAddr, std::vector<SHA256Hash>> nrr;
- bool IsValidNROHash(const SHA256Hash& hash) {
- return std::any_of(
- nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) {
- return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
- });
+ bool IsValidNROHash(const SHA256Hash& hash) const {
+ return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) {
+ return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
+ });
}
static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) {
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index 1f462e087..2a61593e2 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -42,7 +42,7 @@ private:
union {
BitField<0, 16, Flags> flags;
BitField<16, 8, Severity> severity;
- BitField<24, 8, u32_le> verbosity;
+ BitField<24, 8, u32> verbosity;
};
u32_le payload_size;
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
index a6197124a..ce84e25ed 100644
--- a/src/core/hle/service/mii/mii.cpp
+++ b/src/core/hle/service/mii/mii.cpp
@@ -4,42 +4,50 @@
#include <memory>
+#include <fmt/ostream.h>
+
#include "common/logging/log.h"
+#include "common/string_util.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/mii/mii.h"
+#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::Mii {
+constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
+constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
+constexpr ResultCode ERROR_NOT_IN_TEST_MODE{ErrorModule::Mii, 99};
+
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
public:
explicit IDatabaseService() : ServiceFramework{"IDatabaseService"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "IsUpdated"},
- {1, nullptr, "IsFullDatabase"},
- {2, nullptr, "GetCount"},
- {3, nullptr, "Get"},
- {4, nullptr, "Get1"},
+ {0, &IDatabaseService::IsUpdated, "IsUpdated"},
+ {1, &IDatabaseService::IsFullDatabase, "IsFullDatabase"},
+ {2, &IDatabaseService::GetCount, "GetCount"},
+ {3, &IDatabaseService::Get, "Get"},
+ {4, &IDatabaseService::Get1, "Get1"},
{5, nullptr, "UpdateLatest"},
- {6, nullptr, "BuildRandom"},
- {7, nullptr, "BuildDefault"},
- {8, nullptr, "Get2"},
- {9, nullptr, "Get3"},
+ {6, &IDatabaseService::BuildRandom, "BuildRandom"},
+ {7, &IDatabaseService::BuildDefault, "BuildDefault"},
+ {8, &IDatabaseService::Get2, "Get2"},
+ {9, &IDatabaseService::Get3, "Get3"},
{10, nullptr, "UpdateLatest1"},
- {11, nullptr, "FindIndex"},
- {12, nullptr, "Move"},
- {13, nullptr, "AddOrReplace"},
- {14, nullptr, "Delete"},
- {15, nullptr, "DestroyFile"},
- {16, nullptr, "DeleteFile"},
- {17, nullptr, "Format"},
+ {11, &IDatabaseService::FindIndex, "FindIndex"},
+ {12, &IDatabaseService::Move, "Move"},
+ {13, &IDatabaseService::AddOrReplace, "AddOrReplace"},
+ {14, &IDatabaseService::Delete, "Delete"},
+ {15, &IDatabaseService::DestroyFile, "DestroyFile"},
+ {16, &IDatabaseService::DeleteFile, "DeleteFile"},
+ {17, &IDatabaseService::Format, "Format"},
{18, nullptr, "Import"},
{19, nullptr, "Export"},
{20, nullptr, "IsBrokenDatabaseWithClearFlag"},
- {21, nullptr, "GetIndex"},
+ {21, &IDatabaseService::GetIndex, "GetIndex"},
{22, nullptr, "SetInterfaceVersion"},
{23, nullptr, "Convert"},
};
@@ -47,6 +55,305 @@ public:
RegisterHandlers(functions);
}
+
+private:
+ template <typename OutType>
+ std::vector<u8> SerializeArray(OutType (MiiManager::*getter)(u32) const, u32 offset,
+ u32 requested_size, u32& read_size) {
+ read_size = std::min(requested_size, db.Size() - offset);
+
+ std::vector<u8> out(read_size * sizeof(OutType));
+
+ for (u32 i = 0; i < read_size; ++i) {
+ const auto obj = (db.*getter)(offset + i);
+ std::memcpy(out.data() + i * sizeof(OutType), &obj, sizeof(OutType));
+ }
+
+ return out;
+ }
+
+ void IsUpdated(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto source{rp.PopRaw<Source>()};
+
+ LOG_DEBUG(Service_Mii, "called with source={}", source);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(db.CheckUpdatedFlag());
+ db.ResetUpdatedFlag();
+ }
+
+ void IsFullDatabase(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Mii, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(db.Full());
+ }
+
+ void GetCount(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto source{rp.PopRaw<Source>()};
+
+ LOG_DEBUG(Service_Mii, "called with source={}", source);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(db.Size());
+ }
+
+ // Gets Miis from database at offset and index in format MiiInfoElement
+ void Get(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto size{rp.PopRaw<u32>()};
+ const auto source{rp.PopRaw<Source>()};
+
+ LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
+ offsets[0], source);
+
+ u32 read_size{};
+ ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfoElement, offsets[0], size, read_size));
+ offsets[0] += read_size;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(read_size);
+ }
+
+ // Gets Miis from database at offset and index in format MiiInfo
+ void Get1(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto size{rp.PopRaw<u32>()};
+ const auto source{rp.PopRaw<Source>()};
+
+ LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
+ offsets[1], source);
+
+ u32 read_size{};
+ ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfo, offsets[1], size, read_size));
+ offsets[1] += read_size;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(read_size);
+ }
+
+ void BuildRandom(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto [unknown1, unknown2, unknown3] = rp.PopRaw<RandomParameters>();
+
+ if (unknown1 > 3) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ LOG_ERROR(Service_Mii, "Invalid unknown1 value: {}", unknown1);
+ return;
+ }
+
+ if (unknown2 > 2) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ LOG_ERROR(Service_Mii, "Invalid unknown2 value: {}", unknown2);
+ return;
+ }
+
+ if (unknown3 > 3) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ LOG_ERROR(Service_Mii, "Invalid unknown3 value: {}", unknown3);
+ return;
+ }
+
+ LOG_DEBUG(Service_Mii, "called with param_1={:08X}, param_2={:08X}, param_3={:08X}",
+ unknown1, unknown2, unknown3);
+
+ const auto info = db.CreateRandom({unknown1, unknown2, unknown3});
+ IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<MiiInfo>(info);
+ }
+
+ void BuildDefault(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto index{rp.PopRaw<u32>()};
+
+ if (index > 5) {
+ LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}",
+ index);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ return;
+ }
+
+ LOG_DEBUG(Service_Mii, "called with index={:08X}", index);
+
+ const auto info = db.CreateDefault(index);
+ IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<MiiInfo>(info);
+ }
+
+ // Gets Miis from database at offset and index in format MiiStoreDataElement
+ void Get2(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto size{rp.PopRaw<u32>()};
+ const auto source{rp.PopRaw<Source>()};
+
+ LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
+ offsets[2], source);
+
+ u32 read_size{};
+ ctx.WriteBuffer(
+ SerializeArray(&MiiManager::GetStoreDataElement, offsets[2], size, read_size));
+ offsets[2] += read_size;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(read_size);
+ }
+
+ // Gets Miis from database at offset and index in format MiiStoreData
+ void Get3(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto size{rp.PopRaw<u32>()};
+ const auto source{rp.PopRaw<Source>()};
+
+ LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
+ offsets[3], source);
+
+ u32 read_size{};
+ ctx.WriteBuffer(SerializeArray(&MiiManager::GetStoreData, offsets[3], size, read_size));
+ offsets[3] += read_size;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(read_size);
+ }
+
+ void FindIndex(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto uuid{rp.PopRaw<Common::UUID>()};
+ const auto unknown{rp.PopRaw<bool>()};
+
+ LOG_DEBUG(Service_Mii, "called with uuid={}, unknown={}", uuid.FormatSwitch(), unknown);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+
+ const auto index = db.IndexOf(uuid);
+ if (index > MAX_MIIS) {
+ // TODO(DarkLordZach): Find a better error code
+ rb.Push(ResultCode(-1));
+ rb.Push(index);
+ } else {
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(index);
+ }
+ }
+
+ void Move(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto uuid{rp.PopRaw<Common::UUID>()};
+ const auto index{rp.PopRaw<s32>()};
+
+ if (index < 0) {
+ LOG_ERROR(Service_Mii, "Index cannot be negative but is {:08X}!", index);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ return;
+ }
+
+ LOG_DEBUG(Service_Mii, "called with uuid={}, index={:08X}", uuid.FormatSwitch(), index);
+
+ const auto success = db.Move(uuid, index);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ // TODO(DarkLordZach): Find a better error code
+ rb.Push(success ? RESULT_SUCCESS : ResultCode(-1));
+ }
+
+ void AddOrReplace(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto data{rp.PopRaw<MiiStoreData>()};
+
+ LOG_DEBUG(Service_Mii, "called with Mii data uuid={}, name={}", data.uuid.FormatSwitch(),
+ Common::UTF16ToUTF8(data.Name()));
+
+ const auto success = db.AddOrReplace(data);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ // TODO(DarkLordZach): Find a better error code
+ rb.Push(success ? RESULT_SUCCESS : ResultCode(-1));
+ }
+
+ void Delete(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto uuid{rp.PopRaw<Common::UUID>()};
+
+ LOG_DEBUG(Service_Mii, "called with uuid={}", uuid.FormatSwitch());
+
+ const auto success = db.Remove(uuid);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(success ? RESULT_SUCCESS : ERROR_CANNOT_FIND_ENTRY);
+ }
+
+ void DestroyFile(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Mii, "called");
+
+ if (!db.IsTestModeEnabled()) {
+ LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot destory database file.");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NOT_IN_TEST_MODE);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(db.DestroyFile());
+ }
+
+ void DeleteFile(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Mii, "called");
+
+ if (!db.IsTestModeEnabled()) {
+ LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot delete database file.");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NOT_IN_TEST_MODE);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(db.DeleteFile());
+ }
+
+ void Format(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Mii, "called");
+
+ db.Clear();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetIndex(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto info{rp.PopRaw<MiiInfo>()};
+
+ LOG_DEBUG(Service_Mii, "called with Mii info uuid={}, name={}", info.uuid.FormatSwitch(),
+ Common::UTF16ToUTF8(info.Name()));
+
+ const auto index = db.IndexOf(info);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(index);
+ }
+
+ MiiManager db;
+
+ // Last read offsets of Get functions
+ std::array<u32, 4> offsets{};
};
class MiiDBModule final : public ServiceFramework<MiiDBModule> {
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
new file mode 100644
index 000000000..131b01d62
--- /dev/null
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -0,0 +1,416 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <cstring>
+#include "common/assert.h"
+#include "common/file_util.h"
+#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "core/hle/service/mii/mii_manager.h"
+
+namespace Service::Mii {
+
+namespace {
+
+constexpr char MII_SAVE_DATABASE_PATH[] = "/system/save/8000000000000030/MiiDatabase.dat";
+constexpr std::array<char16_t, 11> DEFAULT_MII_NAME = {u'y', u'u', u'z', u'u', u'\0'};
+
+// This value was retrieved from HW test
+constexpr MiiStoreData DEFAULT_MII = {
+ {
+ 0x21, 0x40, 0x40, 0x01, 0x08, 0x01, 0x13, 0x08, 0x08, 0x02, 0x17, 0x8C, 0x06, 0x01,
+ 0x69, 0x6D, 0x8A, 0x6A, 0x82, 0x14, 0x00, 0x00, 0x00, 0x20, 0x64, 0x72, 0x44, 0x44,
+ },
+ {'y', 'u', 'z', 'u', '\0'},
+ Common::UUID{1, 0},
+ 0,
+ 0,
+};
+
+// Default values taken from multiple real databases
+const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0};
+
+constexpr std::array<const char*, 4> SOURCE_NAMES{
+ "Database",
+ "Default",
+ "Account",
+ "Friend",
+};
+
+template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
+std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
+ std::array<T, DestArraySize> out{};
+ std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
+ return out;
+}
+
+MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
+ MiiStoreBitFields bf{};
+ std::memcpy(&bf, data.data.data(), sizeof(MiiStoreBitFields));
+ return {
+ data.uuid,
+ ResizeArray<char16_t, 10, 11>(data.name),
+ static_cast<u8>(bf.font_region.Value()),
+ static_cast<u8>(bf.favorite_color.Value()),
+ static_cast<u8>(bf.gender.Value()),
+ static_cast<u8>(bf.height.Value()),
+ static_cast<u8>(bf.weight.Value()),
+ static_cast<u8>(bf.mii_type.Value()),
+ static_cast<u8>(bf.mii_region.Value()),
+ static_cast<u8>(bf.face_type.Value()),
+ static_cast<u8>(bf.face_color.Value()),
+ static_cast<u8>(bf.face_wrinkle.Value()),
+ static_cast<u8>(bf.face_makeup.Value()),
+ static_cast<u8>(bf.hair_type.Value()),
+ static_cast<u8>(bf.hair_color.Value()),
+ static_cast<bool>(bf.hair_flip.Value()),
+ static_cast<u8>(bf.eye_type.Value()),
+ static_cast<u8>(bf.eye_color.Value()),
+ static_cast<u8>(bf.eye_scale.Value()),
+ static_cast<u8>(bf.eye_aspect.Value()),
+ static_cast<u8>(bf.eye_rotate.Value()),
+ static_cast<u8>(bf.eye_x.Value()),
+ static_cast<u8>(bf.eye_y.Value()),
+ static_cast<u8>(bf.eyebrow_type.Value()),
+ static_cast<u8>(bf.eyebrow_color.Value()),
+ static_cast<u8>(bf.eyebrow_scale.Value()),
+ static_cast<u8>(bf.eyebrow_aspect.Value()),
+ static_cast<u8>(bf.eyebrow_rotate.Value()),
+ static_cast<u8>(bf.eyebrow_x.Value()),
+ static_cast<u8>(bf.eyebrow_y.Value()),
+ static_cast<u8>(bf.nose_type.Value()),
+ static_cast<u8>(bf.nose_scale.Value()),
+ static_cast<u8>(bf.nose_y.Value()),
+ static_cast<u8>(bf.mouth_type.Value()),
+ static_cast<u8>(bf.mouth_color.Value()),
+ static_cast<u8>(bf.mouth_scale.Value()),
+ static_cast<u8>(bf.mouth_aspect.Value()),
+ static_cast<u8>(bf.mouth_y.Value()),
+ static_cast<u8>(bf.facial_hair_color.Value()),
+ static_cast<u8>(bf.beard_type.Value()),
+ static_cast<u8>(bf.mustache_type.Value()),
+ static_cast<u8>(bf.mustache_scale.Value()),
+ static_cast<u8>(bf.mustache_y.Value()),
+ static_cast<u8>(bf.glasses_type.Value()),
+ static_cast<u8>(bf.glasses_color.Value()),
+ static_cast<u8>(bf.glasses_scale.Value()),
+ static_cast<u8>(bf.glasses_y.Value()),
+ static_cast<u8>(bf.mole_type.Value()),
+ static_cast<u8>(bf.mole_scale.Value()),
+ static_cast<u8>(bf.mole_x.Value()),
+ static_cast<u8>(bf.mole_y.Value()),
+ 0x00,
+ };
+}
+MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) {
+ MiiStoreData out{};
+ out.name = ResizeArray<char16_t, 11, 10>(info.name);
+ out.uuid = info.uuid;
+
+ MiiStoreBitFields bf{};
+
+ bf.hair_type.Assign(info.hair_type);
+ bf.mole_type.Assign(info.mole_type);
+ bf.height.Assign(info.height);
+ bf.hair_flip.Assign(info.hair_flip);
+ bf.weight.Assign(info.weight);
+ bf.hair_color.Assign(info.hair_color);
+
+ bf.gender.Assign(info.gender);
+ bf.eye_color.Assign(info.eye_color);
+ bf.eyebrow_color.Assign(info.eyebrow_color);
+ bf.mouth_color.Assign(info.mouth_color);
+ bf.facial_hair_color.Assign(info.facial_hair_color);
+
+ bf.mii_type.Assign(info.mii_type);
+ bf.glasses_color.Assign(info.glasses_color);
+ bf.font_region.Assign(info.font_region);
+ bf.eye_type.Assign(info.eye_type);
+ bf.mii_region.Assign(info.mii_region);
+ bf.mouth_type.Assign(info.mouth_type);
+ bf.glasses_scale.Assign(info.glasses_scale);
+ bf.eye_y.Assign(info.eye_y);
+
+ bf.mustache_type.Assign(info.mustache_type);
+ bf.eyebrow_type.Assign(info.eyebrow_type);
+ bf.beard_type.Assign(info.beard_type);
+ bf.nose_type.Assign(info.nose_type);
+ bf.mouth_aspect.Assign(info.mouth_aspect_ratio);
+ bf.nose_y.Assign(info.nose_y);
+ bf.eyebrow_aspect.Assign(info.eyebrow_aspect_ratio);
+ bf.mouth_y.Assign(info.mouth_y);
+
+ bf.eye_rotate.Assign(info.eye_rotate);
+ bf.mustache_y.Assign(info.mustache_y);
+ bf.eye_aspect.Assign(info.eye_aspect_ratio);
+ bf.glasses_y.Assign(info.glasses_y);
+ bf.eye_scale.Assign(info.eye_scale);
+ bf.mole_x.Assign(info.mole_x);
+ bf.mole_y.Assign(info.mole_y);
+
+ bf.glasses_type.Assign(info.glasses_type);
+ bf.face_type.Assign(info.face_type);
+ bf.favorite_color.Assign(info.favorite_color);
+ bf.face_wrinkle.Assign(info.face_wrinkle);
+ bf.face_color.Assign(info.face_color);
+ bf.eye_x.Assign(info.eye_x);
+ bf.face_makeup.Assign(info.face_makeup);
+
+ bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
+ bf.eyebrow_scale.Assign(info.eyebrow_scale);
+ bf.eyebrow_y.Assign(info.eyebrow_y);
+ bf.eyebrow_x.Assign(info.eyebrow_x);
+ bf.mouth_scale.Assign(info.mouth_scale);
+ bf.nose_scale.Assign(info.nose_scale);
+ bf.mole_scale.Assign(info.mole_scale);
+ bf.mustache_scale.Assign(info.mustache_scale);
+
+ std::memcpy(out.data.data(), &bf, sizeof(MiiStoreBitFields));
+
+ return out;
+}
+
+} // namespace
+
+std::ostream& operator<<(std::ostream& os, Source source) {
+ os << SOURCE_NAMES.at(static_cast<std::size_t>(source));
+ return os;
+}
+
+std::u16string MiiInfo::Name() const {
+ return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
+}
+
+bool operator==(const MiiInfo& lhs, const MiiInfo& rhs) {
+ return std::memcmp(&lhs, &rhs, sizeof(MiiInfo)) == 0;
+}
+
+bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs) {
+ return !operator==(lhs, rhs);
+}
+
+std::u16string MiiStoreData::Name() const {
+ return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
+}
+
+MiiManager::MiiManager() = default;
+
+MiiManager::~MiiManager() = default;
+
+MiiInfo MiiManager::CreateRandom(RandomParameters params) {
+ LOG_WARNING(Service_Mii,
+ "(STUBBED) called with params={:08X}{:08X}{:08X}, returning default Mii",
+ params.unknown_1, params.unknown_2, params.unknown_3);
+
+ return ConvertStoreDataToInfo(CreateMiiWithUniqueUUID());
+}
+
+MiiInfo MiiManager::CreateDefault(u32 index) {
+ const auto new_mii = CreateMiiWithUniqueUUID();
+
+ database.miis.at(index) = new_mii;
+
+ EnsureDatabasePartition();
+ return ConvertStoreDataToInfo(new_mii);
+}
+
+bool MiiManager::CheckUpdatedFlag() const {
+ return updated_flag;
+}
+
+void MiiManager::ResetUpdatedFlag() {
+ updated_flag = false;
+}
+
+bool MiiManager::IsTestModeEnabled() const {
+ return is_test_mode_enabled;
+}
+
+bool MiiManager::Empty() const {
+ return Size() == 0;
+}
+
+bool MiiManager::Full() const {
+ return Size() == MAX_MIIS;
+}
+
+void MiiManager::Clear() {
+ updated_flag = true;
+ std::fill(database.miis.begin(), database.miis.end(), MiiStoreData{});
+}
+
+u32 MiiManager::Size() const {
+ return static_cast<u32>(std::count_if(database.miis.begin(), database.miis.end(),
+ [](const MiiStoreData& elem) { return elem.uuid; }));
+}
+
+MiiInfo MiiManager::GetInfo(u32 index) const {
+ return ConvertStoreDataToInfo(GetStoreData(index));
+}
+
+MiiInfoElement MiiManager::GetInfoElement(u32 index) const {
+ return {GetInfo(index), Source::Database};
+}
+
+MiiStoreData MiiManager::GetStoreData(u32 index) const {
+ return database.miis.at(index);
+}
+
+MiiStoreDataElement MiiManager::GetStoreDataElement(u32 index) const {
+ return {GetStoreData(index), Source::Database};
+}
+
+bool MiiManager::Remove(Common::UUID uuid) {
+ const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
+ [uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
+
+ if (iter == database.miis.end())
+ return false;
+
+ updated_flag = true;
+ *iter = MiiStoreData{};
+ EnsureDatabasePartition();
+ return true;
+}
+
+u32 MiiManager::IndexOf(Common::UUID uuid) const {
+ const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
+ [uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
+
+ if (iter == database.miis.end())
+ return INVALID_INDEX;
+
+ return static_cast<u32>(std::distance(database.miis.begin(), iter));
+}
+
+u32 MiiManager::IndexOf(const MiiInfo& info) const {
+ const auto iter =
+ std::find_if(database.miis.begin(), database.miis.end(), [&info](const MiiStoreData& elem) {
+ return ConvertStoreDataToInfo(elem) == info;
+ });
+
+ if (iter == database.miis.end())
+ return INVALID_INDEX;
+
+ return static_cast<u32>(std::distance(database.miis.begin(), iter));
+}
+
+bool MiiManager::Move(Common::UUID uuid, u32 new_index) {
+ const auto index = IndexOf(uuid);
+
+ if (index == INVALID_INDEX || new_index >= MAX_MIIS)
+ return false;
+
+ updated_flag = true;
+ const auto moving = database.miis[index];
+ const auto replacing = database.miis[new_index];
+ if (replacing.uuid) {
+ database.miis[index] = replacing;
+ database.miis[new_index] = moving;
+ } else {
+ database.miis[index] = MiiStoreData{};
+ database.miis[new_index] = moving;
+ }
+
+ EnsureDatabasePartition();
+ return true;
+}
+
+bool MiiManager::AddOrReplace(const MiiStoreData& data) {
+ const auto index = IndexOf(data.uuid);
+
+ updated_flag = true;
+ if (index == INVALID_INDEX) {
+ const auto size = Size();
+ if (size == MAX_MIIS)
+ return false;
+ database.miis[size] = data;
+ } else {
+ database.miis[index] = data;
+ }
+
+ return true;
+}
+
+bool MiiManager::DestroyFile() {
+ database = DEFAULT_MII_DATABASE;
+ updated_flag = false;
+ return DeleteFile();
+}
+
+bool MiiManager::DeleteFile() {
+ const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
+ return FileUtil::Exists(path) && FileUtil::Delete(path);
+}
+
+void MiiManager::WriteToFile() {
+ const auto raw_path =
+ FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030";
+ if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
+ FileUtil::Delete(raw_path);
+
+ const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
+
+ if (!FileUtil::CreateFullPath(path)) {
+ LOG_WARNING(Service_Mii,
+ "Failed to create full path of MiiDatabase.dat. Create the directory "
+ "nand/system/save/8000000000000030 to mitigate this "
+ "issue.");
+ return;
+ }
+
+ FileUtil::IOFile save(path, "wb");
+
+ if (!save.IsOpen()) {
+ LOG_WARNING(Service_Mii, "Failed to write save data to file... No changes to user data "
+ "made in current session will be saved.");
+ return;
+ }
+
+ save.Resize(sizeof(MiiDatabase));
+ if (save.WriteBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
+ LOG_WARNING(Service_Mii, "Failed to write all data to save file... Data may be malformed "
+ "and/or regenerated on next run.");
+ save.Resize(0);
+ }
+}
+
+void MiiManager::ReadFromFile() {
+ FileUtil::IOFile save(
+ FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH, "rb");
+
+ if (!save.IsOpen()) {
+ LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
+ "blank Mii database with no Miis.");
+ std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
+ return;
+ }
+
+ if (save.ReadBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
+ LOG_WARNING(Service_ACC, "MiiDatabase.dat is smaller than expected... Generating new blank "
+ "Mii database with no Miis.");
+ std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
+ return;
+ }
+
+ EnsureDatabasePartition();
+}
+
+MiiStoreData MiiManager::CreateMiiWithUniqueUUID() const {
+ auto new_mii = DEFAULT_MII;
+
+ do {
+ new_mii.uuid = Common::UUID::Generate();
+ } while (IndexOf(new_mii.uuid) != INVALID_INDEX);
+
+ return new_mii;
+}
+
+void MiiManager::EnsureDatabasePartition() {
+ std::stable_partition(database.miis.begin(), database.miis.end(),
+ [](const MiiStoreData& elem) { return elem.uuid; });
+}
+
+} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
new file mode 100644
index 000000000..38ad78a0d
--- /dev/null
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -0,0 +1,273 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/uuid.h"
+
+namespace Service::Mii {
+
+constexpr std::size_t MAX_MIIS = 100;
+constexpr u32 INVALID_INDEX = 0xFFFFFFFF;
+
+struct RandomParameters {
+ u32 unknown_1;
+ u32 unknown_2;
+ u32 unknown_3;
+};
+static_assert(sizeof(RandomParameters) == 0xC, "RandomParameters has incorrect size.");
+
+enum class Source : u32 {
+ Database = 0,
+ Default = 1,
+ Account = 2,
+ Friend = 3,
+};
+
+std::ostream& operator<<(std::ostream& os, Source source);
+
+struct MiiInfo {
+ Common::UUID uuid;
+ std::array<char16_t, 11> name;
+ u8 font_region;
+ u8 favorite_color;
+ u8 gender;
+ u8 height;
+ u8 weight;
+ u8 mii_type;
+ u8 mii_region;
+ u8 face_type;
+ u8 face_color;
+ u8 face_wrinkle;
+ u8 face_makeup;
+ u8 hair_type;
+ u8 hair_color;
+ bool hair_flip;
+ u8 eye_type;
+ u8 eye_color;
+ u8 eye_scale;
+ u8 eye_aspect_ratio;
+ u8 eye_rotate;
+ u8 eye_x;
+ u8 eye_y;
+ u8 eyebrow_type;
+ u8 eyebrow_color;
+ u8 eyebrow_scale;
+ u8 eyebrow_aspect_ratio;
+ u8 eyebrow_rotate;
+ u8 eyebrow_x;
+ u8 eyebrow_y;
+ u8 nose_type;
+ u8 nose_scale;
+ u8 nose_y;
+ u8 mouth_type;
+ u8 mouth_color;
+ u8 mouth_scale;
+ u8 mouth_aspect_ratio;
+ u8 mouth_y;
+ u8 facial_hair_color;
+ u8 beard_type;
+ u8 mustache_type;
+ u8 mustache_scale;
+ u8 mustache_y;
+ u8 glasses_type;
+ u8 glasses_color;
+ u8 glasses_scale;
+ u8 glasses_y;
+ u8 mole_type;
+ u8 mole_scale;
+ u8 mole_x;
+ u8 mole_y;
+ INSERT_PADDING_BYTES(1);
+
+ std::u16string Name() const;
+};
+static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
+static_assert(std::has_unique_object_representations_v<MiiInfo>,
+ "All bits of MiiInfo must contribute to its value.");
+
+bool operator==(const MiiInfo& lhs, const MiiInfo& rhs);
+bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs);
+
+#pragma pack(push, 4)
+struct MiiInfoElement {
+ MiiInfo info;
+ Source source;
+};
+static_assert(sizeof(MiiInfoElement) == 0x5C, "MiiInfoElement has incorrect size.");
+
+struct MiiStoreBitFields {
+ union {
+ u32 word_0;
+
+ BitField<24, 8, u32> hair_type;
+ BitField<23, 1, u32> mole_type;
+ BitField<16, 7, u32> height;
+ BitField<15, 1, u32> hair_flip;
+ BitField<8, 7, u32> weight;
+ BitField<0, 7, u32> hair_color;
+ };
+
+ union {
+ u32 word_1;
+
+ BitField<31, 1, u32> gender;
+ BitField<24, 7, u32> eye_color;
+ BitField<16, 7, u32> eyebrow_color;
+ BitField<8, 7, u32> mouth_color;
+ BitField<0, 7, u32> facial_hair_color;
+ };
+
+ union {
+ u32 word_2;
+
+ BitField<31, 1, u32> mii_type;
+ BitField<24, 7, u32> glasses_color;
+ BitField<22, 2, u32> font_region;
+ BitField<16, 6, u32> eye_type;
+ BitField<14, 2, u32> mii_region;
+ BitField<8, 6, u32> mouth_type;
+ BitField<5, 3, u32> glasses_scale;
+ BitField<0, 5, u32> eye_y;
+ };
+
+ union {
+ u32 word_3;
+
+ BitField<29, 3, u32> mustache_type;
+ BitField<24, 5, u32> eyebrow_type;
+ BitField<21, 3, u32> beard_type;
+ BitField<16, 5, u32> nose_type;
+ BitField<13, 3, u32> mouth_aspect;
+ BitField<8, 5, u32> nose_y;
+ BitField<5, 3, u32> eyebrow_aspect;
+ BitField<0, 5, u32> mouth_y;
+ };
+
+ union {
+ u32 word_4;
+
+ BitField<29, 3, u32> eye_rotate;
+ BitField<24, 5, u32> mustache_y;
+ BitField<21, 3, u32> eye_aspect;
+ BitField<16, 5, u32> glasses_y;
+ BitField<13, 3, u32> eye_scale;
+ BitField<8, 5, u32> mole_x;
+ BitField<0, 5, u32> mole_y;
+ };
+
+ union {
+ u32 word_5;
+
+ BitField<24, 5, u32> glasses_type;
+ BitField<20, 4, u32> face_type;
+ BitField<16, 4, u32> favorite_color;
+ BitField<12, 4, u32> face_wrinkle;
+ BitField<8, 4, u32> face_color;
+ BitField<4, 4, u32> eye_x;
+ BitField<0, 4, u32> face_makeup;
+ };
+
+ union {
+ u32 word_6;
+
+ BitField<28, 4, u32> eyebrow_rotate;
+ BitField<24, 4, u32> eyebrow_scale;
+ BitField<20, 4, u32> eyebrow_y;
+ BitField<16, 4, u32> eyebrow_x;
+ BitField<12, 4, u32> mouth_scale;
+ BitField<8, 4, u32> nose_scale;
+ BitField<4, 4, u32> mole_scale;
+ BitField<0, 4, u32> mustache_scale;
+ };
+};
+static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size.");
+static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
+ "MiiStoreBitFields is not trivially copyable.");
+
+struct MiiStoreData {
+ // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
+ // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
+ // not suitable for our uses.
+ std::array<u8, 0x1C> data;
+ static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
+
+ std::array<char16_t, 10> name;
+ Common::UUID uuid;
+ u16 crc_1;
+ u16 crc_2;
+
+ std::u16string Name() const;
+};
+static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
+
+struct MiiStoreDataElement {
+ MiiStoreData data;
+ Source source;
+};
+static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
+
+struct MiiDatabase {
+ u32 magic; // 'NFDB'
+ std::array<MiiStoreData, MAX_MIIS> miis;
+ INSERT_PADDING_BYTES(1);
+ u8 count;
+ u16 crc;
+};
+static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
+#pragma pack(pop)
+
+// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
+// with providing an easy interface for HLE emulation of the mii service.
+class MiiManager {
+public:
+ MiiManager();
+ ~MiiManager();
+
+ MiiInfo CreateRandom(RandomParameters params);
+ MiiInfo CreateDefault(u32 index);
+
+ bool CheckUpdatedFlag() const;
+ void ResetUpdatedFlag();
+
+ bool IsTestModeEnabled() const;
+
+ bool Empty() const;
+ bool Full() const;
+
+ void Clear();
+
+ u32 Size() const;
+
+ MiiInfo GetInfo(u32 index) const;
+ MiiInfoElement GetInfoElement(u32 index) const;
+ MiiStoreData GetStoreData(u32 index) const;
+ MiiStoreDataElement GetStoreDataElement(u32 index) const;
+
+ bool Remove(Common::UUID uuid);
+ u32 IndexOf(Common::UUID uuid) const;
+ u32 IndexOf(const MiiInfo& info) const;
+
+ bool Move(Common::UUID uuid, u32 new_index);
+ bool AddOrReplace(const MiiStoreData& data);
+
+ bool DestroyFile();
+ bool DeleteFile();
+
+private:
+ void WriteToFile();
+ void ReadFromFile();
+
+ MiiStoreData CreateMiiWithUniqueUUID() const;
+
+ void EnsureDatabasePartition();
+
+ MiiDatabase database;
+ bool updated_flag = false;
+ bool is_test_mode_enabled = false;
+};
+
+}; // namespace Service::Mii
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/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index 5c62d42ba..ca88bf97f 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -150,7 +150,7 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.PushRaw<u8>(Settings::values.enable_nfc);
+ rb.PushRaw<u8>(true);
}
void GetStateOld(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 1c4482e47..a5cb06f8a 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -26,7 +26,7 @@ constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)) {
auto& kernel = Core::System::GetInstance().Kernel();
- nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IUser:NFCTagDetected");
}
@@ -67,9 +67,9 @@ public:
auto& kernel = Core::System::GetInstance().Kernel();
deactivate_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
+ kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent");
availability_change_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::OneShot, "IUser:AvailabilityChangeEvent");
+ kernel, Kernel::ResetType::Automatic, "IUser:AvailabilityChangeEvent");
}
private:
@@ -335,7 +335,7 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
}
bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
- std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
+ std::lock_guard lock{HLE::g_hle_lock};
if (buffer.size() < sizeof(AmiiboFile)) {
return false;
}
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 60479bb45..76b12b482 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -15,12 +15,16 @@ namespace Service::NIFM {
class IScanRequest final : public ServiceFramework<IScanRequest> {
public:
explicit IScanRequest() : ServiceFramework("IScanRequest") {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Submit"},
{1, nullptr, "IsProcessing"},
{2, nullptr, "GetResult"},
{3, nullptr, "GetSystemEventReadableHandle"},
+ {4, nullptr, "SetChannels"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
};
@@ -58,9 +62,9 @@ public:
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IRequest:Event1");
- event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IRequest:Event2");
}
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index 0dabcd23b..f319a3ca1 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -141,7 +141,7 @@ public:
auto& kernel = Core::System::GetInstance().Kernel();
finished_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::OneShot,
+ kernel, Kernel::ResetType::Automatic,
"IEnsureNetworkClockAvailabilityService:FinishEvent");
}
diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp
index ccb6f9da9..8751522ca 100644
--- a/src/core/hle/service/npns/npns.cpp
+++ b/src/core/hle/service/npns/npns.cpp
@@ -45,7 +45,7 @@ public:
{114, nullptr, "AttachJid"},
{115, nullptr, "DetachJid"},
{201, nullptr, "RequestChangeStateForceTimed"},
- {102, nullptr, "RequestChangeStateForceAsync"},
+ {202, nullptr, "RequestChangeStateForceAsync"},
};
// clang-format on
@@ -73,6 +73,7 @@ public:
{103, nullptr, "GetState"},
{104, nullptr, "GetStatistics"},
{111, nullptr, "GetJid"},
+ {120, nullptr, "CreateNotificationReceiver"},
};
// clang-format on
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/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 0f02a1a18..4f6042b00 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -19,11 +19,11 @@ public:
virtual ~nvdevice() = default;
union Ioctl {
u32_le raw;
- BitField<0, 8, u32_le> cmd;
- BitField<8, 8, u32_le> group;
- BitField<16, 14, u32_le> length;
- BitField<30, 1, u32_le> is_in;
- BitField<31, 1, u32_le> is_out;
+ BitField<0, 8, u32> cmd;
+ BitField<8, 8, u32> group;
+ BitField<16, 14, u32> length;
+ BitField<30, 1, u32> is_in;
+ BitField<31, 1, u32> is_out;
};
/**
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index ace71169f..12f3ef825 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -18,7 +18,7 @@ class nvmap;
class nvdisp_disp0 final : public nvdevice {
public:
explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev);
- ~nvdisp_disp0();
+ ~nvdisp_disp0() override;
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index a34b9e753..af62d33d2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -10,6 +10,7 @@
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
+#include "core/memory.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h"
@@ -88,7 +89,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
for (const auto& entry : entries) {
LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
entry.offset, entry.nvmap_handle, entry.pages);
- Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10;
+ GPUVAddr offset = static_cast<GPUVAddr>(entry.offset) << 0x10;
auto object = nvmap_dev->GetObject(entry.nvmap_handle);
if (!object) {
LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle);
@@ -101,7 +102,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
u64 size = static_cast<u64>(entry.pages) << 0x10;
ASSERT(size <= object->size);
- Tegra::GPUVAddr returned = gpu.MemoryManager().MapBufferEx(object->addr, offset, size);
+ GPUVAddr returned = gpu.MemoryManager().MapBufferEx(object->addr, offset, size);
ASSERT(returned == offset);
}
std::memcpy(output.data(), entries.data(), output.size());
@@ -172,16 +173,8 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
return 0;
}
- auto& system_instance = Core::System::GetInstance();
-
- // Remove this memory region from the rasterizer cache.
- auto& gpu = system_instance.GPU();
- auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
- ASSERT(cpu_addr);
- gpu.FlushAndInvalidateRegion(*cpu_addr, itr->second.size);
-
- params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
-
+ params.offset = Core::System::GetInstance().GPU().MemoryManager().UnmapBuffer(params.offset,
+ itr->second.size);
buffer_mappings.erase(itr->second.offset);
std::memcpy(output.data(), &params, output.size());
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/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index 3b9ab4b14..b60fc748b 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -129,7 +129,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"NVDRV::query_event");
}
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index fe311b069..5b4889910 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -17,7 +17,7 @@ namespace Service::Nvidia {
class NVDRV final : public ServiceFramework<NVDRV> {
public:
NVDRV(std::shared_ptr<Module> nvdrv, const char* name);
- ~NVDRV();
+ ~NVDRV() override;
private:
void Open(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvdrv/nvmemp.h b/src/core/hle/service/nvdrv/nvmemp.h
index 5a4dfc1f9..6eafb1346 100644
--- a/src/core/hle/service/nvdrv/nvmemp.h
+++ b/src/core/hle/service/nvdrv/nvmemp.h
@@ -11,7 +11,7 @@ namespace Service::Nvidia {
class NVMEMP final : public ServiceFramework<NVMEMP> {
public:
NVMEMP();
- ~NVMEMP();
+ ~NVMEMP() override;
private:
void Cmd0(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 4d150fc71..5731e815f 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -16,7 +16,7 @@ namespace Service::NVFlinger {
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
auto& kernel = Core::System::GetInstance().Kernel();
- buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
+ buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
"BufferQueue NativeHandle");
}
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index fc496b654..3c5c53e24 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -21,12 +21,13 @@
#include "core/hle/service/vi/display/vi_display.h"
#include "core/hle/service/vi/layer/vi_layer.h"
#include "core/perf_stats.h"
+#include "core/settings.h"
#include "video_core/renderer_base.h"
namespace Service::NVFlinger {
-constexpr std::size_t SCREEN_REFRESH_RATE = 60;
-constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
+constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
+constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30);
NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} {
displays.emplace_back(0, "Default");
@@ -36,13 +37,15 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t
displays.emplace_back(4, "Null");
// Schedule the screen composition events
- composition_event =
- core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
+ const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks;
+
+ composition_event = core_timing.RegisterEvent(
+ "ScreenComposition", [this, ticks](u64 userdata, s64 cycles_late) {
Compose();
- this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event);
+ this->core_timing.ScheduleEvent(ticks - cycles_late, composition_event);
});
- core_timing.ScheduleEvent(frame_ticks, composition_event);
+ core_timing.ScheduleEvent(ticks, composition_event);
}
NVFlinger::~NVFlinger() {
@@ -62,6 +65,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
const auto itr =
std::find_if(displays.begin(), displays.end(),
[&](const VI::Display& display) { return display.GetName() == name; });
+
if (itr == displays.end()) {
return {};
}
diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp
index 6081f41e1..c75b4ee34 100644
--- a/src/core/hle/service/pctl/module.cpp
+++ b/src/core/hle/service/pctl/module.cpp
@@ -12,10 +12,10 @@ namespace Service::PCTL {
class IParentalControlService final : public ServiceFramework<IParentalControlService> {
public:
IParentalControlService() : ServiceFramework("IParentalControlService") {
+ // clang-format off
static const FunctionInfo functions[] = {
{1, &IParentalControlService::Initialize, "Initialize"},
- {1001, &IParentalControlService::CheckFreeCommunicationPermission,
- "CheckFreeCommunicationPermission"},
+ {1001, &IParentalControlService::CheckFreeCommunicationPermission, "CheckFreeCommunicationPermission"},
{1002, nullptr, "ConfirmLaunchApplicationPermission"},
{1003, nullptr, "ConfirmResumeApplicationPermission"},
{1004, nullptr, "ConfirmSnsPostPermission"},
@@ -30,6 +30,7 @@ public:
{1013, nullptr, "ConfirmStereoVisionPermission"},
{1014, nullptr, "ConfirmPlayableApplicationVideoOld"},
{1015, nullptr, "ConfirmPlayableApplicationVideo"},
+ {1016, nullptr, "ConfirmShowNewsPermission"},
{1031, nullptr, "IsRestrictionEnabled"},
{1032, nullptr, "GetSafetyLevel"},
{1033, nullptr, "SetSafetyLevel"},
@@ -45,6 +46,7 @@ public:
{1045, nullptr, "UpdateFreeCommunicationApplicationList"},
{1046, nullptr, "DisableFeaturesForReset"},
{1047, nullptr, "NotifyApplicationDownloadStarted"},
+ {1048, nullptr, "NotifyNetworkProfileCreated"},
{1061, nullptr, "ConfirmStereoVisionRestrictionConfigurable"},
{1062, nullptr, "GetStereoVisionRestriction"},
{1063, nullptr, "SetStereoVisionRestriction"},
@@ -63,6 +65,7 @@ public:
{1411, nullptr, "GetPairingAccountInfo"},
{1421, nullptr, "GetAccountNickname"},
{1424, nullptr, "GetAccountState"},
+ {1425, nullptr, "RequestPostEvents"},
{1432, nullptr, "GetSynchronizationEvent"},
{1451, nullptr, "StartPlayTimer"},
{1452, nullptr, "StopPlayTimer"},
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index 6b27dc4a3..ebcc41a43 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -42,15 +42,18 @@ private:
class DebugMonitor final : public ServiceFramework<DebugMonitor> {
public:
explicit DebugMonitor() : ServiceFramework{"pm:dmnt"} {
+ // clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "IsDebugMode"},
- {1, nullptr, "GetDebugProcesses"},
- {2, nullptr, "StartDebugProcess"},
- {3, nullptr, "GetTitlePid"},
- {4, nullptr, "EnableDebugForTitleId"},
- {5, nullptr, "GetApplicationPid"},
- {6, nullptr, "EnableDebugForApplication"},
+ {0, nullptr, "GetDebugProcesses"},
+ {1, nullptr, "StartDebugProcess"},
+ {2, nullptr, "GetTitlePid"},
+ {3, nullptr, "EnableDebugForTitleId"},
+ {4, nullptr, "GetApplicationPid"},
+ {5, nullptr, "EnableDebugForApplication"},
+ {6, nullptr, "DisableDebug"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
};
@@ -68,6 +71,7 @@ public:
class Shell final : public ServiceFramework<Shell> {
public:
explicit Shell() : ServiceFramework{"pm:shell"} {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "LaunchProcess"},
{1, nullptr, "TerminateProcessByPid"},
@@ -77,7 +81,10 @@ public:
{5, nullptr, "NotifyBootFinished"},
{6, nullptr, "GetApplicationPid"},
{7, nullptr, "BoostSystemMemoryResourceLimit"},
+ {8, nullptr, "EnableAdditionalSystemThreads"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
};
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 117f87a45..6c69f899e 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -11,7 +11,6 @@
#include "core/hle/ipc.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_port.h"
@@ -76,7 +75,8 @@ namespace Service {
* Creates a function string for logging, complete with the name (or header code, depending
* on what's passed in) the port name, and all the cmd_buff arguments.
*/
-[[maybe_unused]] static std::string MakeFunctionString(const char* name, const char* port_name,
+[[maybe_unused]] static std::string MakeFunctionString(std::string_view name,
+ std::string_view port_name,
const u32* cmd_buff) {
// Number of params == bits 0-5 + bits 6-11
int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
@@ -158,9 +158,7 @@ void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) {
return ReportUnimplementedFunction(ctx, info);
}
- LOG_TRACE(
- Service, "{}",
- MakeFunctionString(info->name, GetServiceName().c_str(), ctx.CommandBuffer()).c_str());
+ LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
handler_invoker(this, info->handler_callback, ctx);
}
@@ -169,7 +167,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
case IPC::CommandType::Close: {
IPC::ResponseBuilder rb{context, 2};
rb.Push(RESULT_SUCCESS);
- return ResultCode(ErrorModule::HIPC, ErrorDescription::RemoteProcessDead);
+ return IPC::ERR_REMOTE_PROCESS_DEAD;
}
case IPC::CommandType::ControlWithContext:
case IPC::CommandType::Control: {
@@ -202,7 +200,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/service.h b/src/core/hle/service/service.h
index 830790269..abbfe5524 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -90,7 +90,7 @@ private:
Kernel::HLERequestContext& ctx);
ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker);
- ~ServiceFrameworkBase();
+ ~ServiceFrameworkBase() override;
void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info);
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 1afc43f75..298d85011 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -2,16 +2,15 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <chrono>
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/client_session.h"
#include "core/hle/service/set/set.h"
#include "core/settings.h"
namespace Service::Set {
-
+namespace {
constexpr std::array<LanguageCode, 17> available_language_codes = {{
LanguageCode::JA,
LanguageCode::EN_US,
@@ -32,41 +31,35 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
LanguageCode::ZH_HANT,
}};
-constexpr std::size_t pre4_0_0_max_entries = 0xF;
-constexpr std::size_t post4_0_0_max_entries = 0x40;
+constexpr std::size_t pre4_0_0_max_entries = 15;
+constexpr std::size_t post4_0_0_max_entries = 17;
constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
-LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
- return available_language_codes.at(index);
+void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_language_codes) {
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(static_cast<u32>(num_language_codes));
}
-template <std::size_t size>
-static std::array<LanguageCode, size> MakeLanguageCodeSubset() {
- std::array<LanguageCode, size> arr;
- std::copy_n(available_language_codes.begin(), size, arr.begin());
- return arr;
+void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_size) {
+ const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode);
+ const std::size_t copy_amount = std::min(requested_amount, max_size);
+ const std::size_t copy_size = copy_amount * sizeof(LanguageCode);
+
+ ctx.WriteBuffer(available_language_codes.data(), copy_size);
+ PushResponseLanguageCode(ctx, copy_amount);
}
+} // Anonymous namespace
-static void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t max_size) {
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(RESULT_SUCCESS);
- if (available_language_codes.size() > max_size) {
- rb.Push(static_cast<u32>(max_size));
- } else {
- rb.Push(static_cast<u32>(available_language_codes.size()));
- }
+LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
+ return available_language_codes.at(index);
}
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
- if (available_language_codes.size() > pre4_0_0_max_entries) {
- ctx.WriteBuffer(MakeLanguageCodeSubset<pre4_0_0_max_entries>());
- } else {
- ctx.WriteBuffer(available_language_codes);
- }
- PushResponseLanguageCode(ctx, pre4_0_0_max_entries);
+ GetAvailableLanguageCodesImpl(ctx, pre4_0_0_max_entries);
}
void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
@@ -87,12 +80,7 @@ void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
- if (available_language_codes.size() > post4_0_0_max_entries) {
- ctx.WriteBuffer(MakeLanguageCodeSubset<post4_0_0_max_entries>());
- } else {
- ctx.WriteBuffer(available_language_codes);
- }
- PushResponseLanguageCode(ctx, post4_0_0_max_entries);
+ GetAvailableLanguageCodesImpl(ctx, post4_0_0_max_entries);
}
void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
@@ -102,9 +90,9 @@ void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
}
void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) {
- PushResponseLanguageCode(ctx, post4_0_0_max_entries);
-
LOG_DEBUG(Service_SET, "called");
+
+ PushResponseLanguageCode(ctx, post4_0_0_max_entries);
}
void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
@@ -116,6 +104,7 @@ void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
}
SET::SET() : ServiceFramework("set") {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &SET::GetLanguageCode, "GetLanguageCode"},
{1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"},
@@ -126,7 +115,10 @@ SET::SET() : ServiceFramework("set") {
{6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"},
{7, nullptr, "GetKeyCodeMap"},
{8, nullptr, "GetQuestFlag"},
+ {9, nullptr, "GetKeyCodeMap2"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp
index 34654bb07..5981c575c 100644
--- a/src/core/hle/service/set/set_cal.cpp
+++ b/src/core/hle/service/set/set_cal.cpp
@@ -40,7 +40,7 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") {
{30, nullptr, "GetAmiiboEcqvBlsCertificate"},
{31, nullptr, "GetAmiiboEcqvBlsRootCertificate"},
{32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"},
- {33, nullptr, "GetBatteryVersion"},
+ {41, nullptr, "GetBatteryVersion"},
};
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/set/set_cal.h b/src/core/hle/service/set/set_cal.h
index 583036eac..a0677e815 100644
--- a/src/core/hle/service/set/set_cal.h
+++ b/src/core/hle/service/set/set_cal.h
@@ -11,7 +11,7 @@ namespace Service::Set {
class SET_CAL final : public ServiceFramework<SET_CAL> {
public:
explicit SET_CAL();
- ~SET_CAL();
+ ~SET_CAL() override;
};
} // namespace Service::Set
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index c9b4da5b0..98d0cfdfd 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -2,13 +2,88 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/assert.h"
#include "common/logging/log.h"
+#include "core/file_sys/errors.h"
+#include "core/file_sys/system_archive/system_version.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
+#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/set/set_sys.h"
namespace Service::Set {
+namespace {
+constexpr u64 SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET = 0x05;
+
+enum class GetFirmwareVersionType {
+ Version1,
+ Version2,
+};
+
+void GetFirmwareVersionImpl(Kernel::HLERequestContext& ctx, GetFirmwareVersionType type) {
+ LOG_WARNING(Service_SET, "called - Using hardcoded firmware version '{}'",
+ FileSys::SystemArchive::GetLongDisplayVersion());
+
+ ASSERT_MSG(ctx.GetWriteBufferSize() == 0x100,
+ "FirmwareVersion output buffer must be 0x100 bytes in size!");
+
+ // Instead of using the normal procedure of checking for the real system archive and if it
+ // doesn't exist, synthesizing one, I feel that that would lead to strange bugs because a
+ // used is using a really old or really new SystemVersion title. The synthesized one ensures
+ // consistence (currently reports as 5.1.0-0.0)
+ const auto archive = FileSys::SystemArchive::SystemVersion();
+
+ const auto early_exit_failure = [&ctx](const std::string& desc, ResultCode code) {
+ LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).",
+ desc.c_str());
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(code);
+ };
+
+ if (archive == nullptr) {
+ early_exit_failure("The system version archive couldn't be synthesized.",
+ FileSys::ERROR_FAILED_MOUNT_ARCHIVE);
+ return;
+ }
+
+ const auto ver_file = archive->GetFile("file");
+ if (ver_file == nullptr) {
+ early_exit_failure("The system version archive didn't contain the file 'file'.",
+ FileSys::ERROR_INVALID_ARGUMENT);
+ return;
+ }
+
+ auto data = ver_file->ReadAllBytes();
+ if (data.size() != 0x100) {
+ early_exit_failure("The system version file 'file' was not the correct size.",
+ FileSys::ERROR_OUT_OF_BOUNDS);
+ return;
+ }
+
+ // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will
+ // zero out the REVISION_MINOR field.
+ if (type == GetFirmwareVersionType::Version1) {
+ data[SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET] = 0;
+ }
+
+ ctx.WriteBuffer(data);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+} // Anonymous namespace
+
+void SET_SYS::GetFirmwareVersion(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_SET, "called");
+ GetFirmwareVersionImpl(ctx, GetFirmwareVersionType::Version1);
+}
+
+void SET_SYS::GetFirmwareVersion2(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_SET, "called");
+ GetFirmwareVersionImpl(ctx, GetFirmwareVersionType::Version2);
+}
+
void SET_SYS::GetColorSetId(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
@@ -29,12 +104,13 @@ void SET_SYS::SetColorSetId(Kernel::HLERequestContext& ctx) {
}
SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "SetLanguageCode"},
{1, nullptr, "SetNetworkSettings"},
{2, nullptr, "GetNetworkSettings"},
- {3, nullptr, "GetFirmwareVersion"},
- {4, nullptr, "GetFirmwareVersion2"},
+ {3, &SET_SYS::GetFirmwareVersion, "GetFirmwareVersion"},
+ {4, &SET_SYS::GetFirmwareVersion2, "GetFirmwareVersion2"},
{5, nullptr, "GetFirmwareVersionDigest"},
{7, nullptr, "GetLockScreenFlag"},
{8, nullptr, "SetLockScreenFlag"},
@@ -177,7 +253,33 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
{147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"},
{148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"},
{149, nullptr, "GetRebootlessSystemUpdateVersion"},
+ {150, nullptr, "GetDeviceTimeZoneLocationUpdatedTime"},
+ {151, nullptr, "SetDeviceTimeZoneLocationUpdatedTime"},
+ {152, nullptr, "GetUserSystemClockAutomaticCorrectionUpdatedTime"},
+ {153, nullptr, "SetUserSystemClockAutomaticCorrectionUpdatedTime"},
+ {154, nullptr, "GetAccountOnlineStorageSettings"},
+ {155, nullptr, "SetAccountOnlineStorageSettings"},
+ {156, nullptr, "GetPctlReadyFlag"},
+ {157, nullptr, "SetPctlReadyFlag"},
+ {162, nullptr, "GetPtmBatteryVersion"},
+ {163, nullptr, "SetPtmBatteryVersion"},
+ {164, nullptr, "GetUsb30HostEnableFlag"},
+ {165, nullptr, "SetUsb30HostEnableFlag"},
+ {166, nullptr, "GetUsb30DeviceEnableFlag"},
+ {167, nullptr, "SetUsb30DeviceEnableFlag"},
+ {168, nullptr, "GetThemeId"},
+ {169, nullptr, "SetThemeId"},
+ {170, nullptr, "GetChineseTraditionalInputMethod"},
+ {171, nullptr, "SetChineseTraditionalInputMethod"},
+ {172, nullptr, "GetPtmCycleCountReliability"},
+ {173, nullptr, "SetPtmCycleCountReliability"},
+ {175, nullptr, "GetThemeSettings"},
+ {176, nullptr, "SetThemeSettings"},
+ {177, nullptr, "GetThemeKey"},
+ {178, nullptr, "SetThemeKey"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h
index f602f3c77..13ee2cf46 100644
--- a/src/core/hle/service/set/set_sys.h
+++ b/src/core/hle/service/set/set_sys.h
@@ -20,6 +20,8 @@ private:
BasicBlack = 1,
};
+ void GetFirmwareVersion(Kernel::HLERequestContext& ctx);
+ void GetFirmwareVersion2(Kernel::HLERequestContext& ctx);
void GetColorSetId(Kernel::HLERequestContext& ctx);
void SetColorSetId(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp
index 74da4d5e6..e9ee73710 100644
--- a/src/core/hle/service/sm/controller.cpp
+++ b/src/core/hle/service/sm/controller.cpp
@@ -30,7 +30,7 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(RESULT_SUCCESS);
- Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->parent->client};
+ Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->GetParent()->client};
rb.PushMoveObjects(session);
LOG_DEBUG(Service, "session={}", session->GetObjectId());
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index bef25433e..b9d6381b4 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -67,7 +67,7 @@ public:
if (port == nullptr) {
return nullptr;
}
- return std::static_pointer_cast<T>(port->hle_handler);
+ return std::static_pointer_cast<T>(port->GetHLEHandler());
}
void InvokeControlRequest(Kernel::HLERequestContext& context);
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 4342f3b2d..884ad173b 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -73,6 +73,7 @@ void BSD::Close(Kernel::HLERequestContext& ctx) {
}
BSD::BSD(const char* name) : ServiceFramework(name) {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &BSD::RegisterClient, "RegisterClient"},
{1, &BSD::StartMonitoring, "StartMonitoring"},
@@ -105,7 +106,11 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
{28, nullptr, "GetResourceStatistics"},
{29, nullptr, "RecvMMsg"},
{30, nullptr, "SendMMsg"},
+ {31, nullptr, "EventFd"},
+ {32, nullptr, "RegisterResourceStatisticsName"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp
index 13ab1d31e..852e71e4b 100644
--- a/src/core/hle/service/sockets/sfdnsres.cpp
+++ b/src/core/hle/service/sockets/sfdnsres.cpp
@@ -8,12 +8,20 @@
namespace Service::Sockets {
void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) {
+ struct Parameters {
+ u8 use_nsd_resolve;
+ u32 unknown;
+ u64 process_id;
+ };
+
IPC::RequestParser rp{ctx};
+ const auto parameters = rp.PopRaw<Parameters>();
- LOG_WARNING(Service, "(STUBBED) called");
+ LOG_WARNING(Service,
+ "(STUBBED) called. use_nsd_resolve={}, unknown=0x{:08X}, process_id=0x{:016X}",
+ parameters.use_nsd_resolve, parameters.unknown, parameters.process_id);
IPC::ResponseBuilder rb{ctx, 2};
-
rb.Push(RESULT_SUCCESS);
}
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp
index 8db0c2f13..e724d4ab8 100644
--- a/src/core/hle/service/spl/module.cpp
+++ b/src/core/hle/service/spl/module.cpp
@@ -26,9 +26,7 @@ Module::Interface::~Interface() = default;
void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SPL, "called");
- IPC::RequestParser rp{ctx};
-
- std::size_t size = ctx.GetWriteBufferSize();
+ const std::size_t size = ctx.GetWriteBufferSize();
std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max());
std::vector<u8> data(size);
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp
index af40a1815..65040c077 100644
--- a/src/core/hle/service/ssl/ssl.cpp
+++ b/src/core/hle/service/ssl/ssl.cpp
@@ -64,13 +64,19 @@ public:
};
RegisterHandlers(functions);
}
- ~ISslContext() = default;
private:
void SetOption(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_SSL, "(STUBBED) called");
+ struct Parameters {
+ u8 enable;
+ u32 option;
+ };
IPC::RequestParser rp{ctx};
+ const auto parameters = rp.PopRaw<Parameters>();
+
+ LOG_WARNING(Service_SSL, "(STUBBED) called. enable={}, option={}", parameters.enable,
+ parameters.option);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -97,6 +103,8 @@ public:
{4, nullptr, "DebugIoctl"},
{5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"},
{6, nullptr, "FlushSessionCache"},
+ {7, nullptr, "SetDebugOption"},
+ {8, nullptr, "GetDebugOption"},
};
// clang-format on
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index b3a196f65..8d122ae33 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -8,6 +8,7 @@ namespace Service::Time {
Time::Time(std::shared_ptr<Module> time, const char* name)
: Module::Interface(std::move(time), name) {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
{1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
@@ -15,18 +16,23 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
{3, &Time::GetTimeZoneService, "GetTimeZoneService"},
{4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
{5, nullptr, "GetEphemeralNetworkSystemClock"},
+ {20, nullptr, "GetSharedMemoryNativeHandle"},
+ {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"},
+ {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"},
{50, nullptr, "SetStandardSteadyClockInternalOffset"},
{100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
{101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
{102, nullptr, "GetStandardUserSystemClockInitialYear"},
{200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
+ {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
{300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
{400, &Time::GetClockSnapshot, "GetClockSnapshot"},
{401, nullptr, "GetClockSnapshotFromSystemClockContext"},
- {500, &Time::CalculateStandardUserSystemClockDifferenceByUser,
- "CalculateStandardUserSystemClockDifferenceByUser"},
+ {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
{501, nullptr, "CalculateSpanBetween"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
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/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index 01d80311b..a8d088305 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -17,7 +17,7 @@ namespace Service::VI {
Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} {
auto& kernel = Core::System::GetInstance().Kernel();
- vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
+ vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
fmt::format("Display VSync Event {}", id));
}
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index a975767bb..f1fa6ccd1 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -24,6 +24,7 @@
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
#include "core/hle/service/nvflinger/nvflinger.h"
+#include "core/hle/service/service.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_m.h"
#include "core/hle/service/vi/vi_s.h"
@@ -33,6 +34,7 @@
namespace Service::VI {
constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1};
+constexpr ResultCode ERR_PERMISSION_DENIED{ErrorModule::VI, 5};
constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6};
constexpr ResultCode ERR_NOT_FOUND{ErrorModule::VI, 7};
@@ -496,7 +498,6 @@ public:
};
RegisterHandlers(functions);
}
- ~IHOSBinderDriver() = default;
private:
enum class TransactionId {
@@ -555,7 +556,7 @@ private:
} else {
// Wait the current thread until a buffer becomes available
ctx.SleepClientThread(
- Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1,
+ "IHOSBinderDriver::DequeueBuffer", -1,
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
Kernel::ThreadWakeupReason reason) {
// Repeat TransactParcel DequeueBuffer when a buffer is available
@@ -690,7 +691,6 @@ public:
};
RegisterHandlers(functions);
}
- ~ISystemDisplayService() = default;
private:
void SetLayerZ(Kernel::HLERequestContext& ctx) {
@@ -816,7 +816,6 @@ public:
};
RegisterHandlers(functions);
}
- ~IManagerDisplayService() = default;
private:
void CloseDisplay(Kernel::HLERequestContext& ctx) {
@@ -882,7 +881,6 @@ private:
class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
public:
explicit IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
- ~IApplicationDisplayService() = default;
private:
enum class ConvertedScaleMode : u64 {
@@ -1035,7 +1033,6 @@ private:
void ListDisplays(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
- IPC::RequestParser rp{ctx};
DisplayInfo display_info;
display_info.width *= static_cast<u64>(Settings::values.resolution_factor);
display_info.height *= static_cast<u64>(Settings::values.resolution_factor);
@@ -1203,26 +1200,40 @@ IApplicationDisplayService::IApplicationDisplayService(
RegisterHandlers(functions);
}
-Module::Interface::Interface(std::shared_ptr<Module> module, const char* name,
- std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
- : ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {}
+static bool IsValidServiceAccess(Permission permission, Policy policy) {
+ if (permission == Permission::User) {
+ return policy == Policy::User;
+ }
+
+ if (permission == Permission::System || permission == Permission::Manager) {
+ return policy == Policy::User || policy == Policy::Compositor;
+ }
+
+ return false;
+}
-Module::Interface::~Interface() = default;
+void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
+ std::shared_ptr<NVFlinger::NVFlinger> nv_flinger,
+ Permission permission) {
+ IPC::RequestParser rp{ctx};
+ const auto policy = rp.PopEnum<Policy>();
-void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
+ if (!IsValidServiceAccess(permission, policy)) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_PERMISSION_DENIED);
+ return;
+ }
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
+ rb.PushIpcInterface<IApplicationDisplayService>(std::move(nv_flinger));
}
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) {
- auto module = std::make_shared<Module>();
- std::make_shared<VI_M>(module, nv_flinger)->InstallAsService(service_manager);
- std::make_shared<VI_S>(module, nv_flinger)->InstallAsService(service_manager);
- std::make_shared<VI_U>(module, nv_flinger)->InstallAsService(service_manager);
+ std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager);
+ std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager);
+ std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager);
}
} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index e3963502a..6b66f8b81 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -4,12 +4,21 @@
#pragma once
-#include "core/hle/service/service.h"
+#include <memory>
+#include "common/common_types.h"
+
+namespace Kernel {
+class HLERequestContext;
+}
namespace Service::NVFlinger {
class NVFlinger;
}
+namespace Service::SM {
+class ServiceManager;
+}
+
namespace Service::VI {
enum class DisplayResolution : u32 {
@@ -19,22 +28,25 @@ enum class DisplayResolution : u32 {
UndockedHeight = 720,
};
-class Module final {
-public:
- class Interface : public ServiceFramework<Interface> {
- public:
- explicit Interface(std::shared_ptr<Module> module, const char* name,
- std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
- ~Interface() override;
-
- void GetDisplayService(Kernel::HLERequestContext& ctx);
+/// Permission level for a particular VI service instance
+enum class Permission {
+ User,
+ System,
+ Manager,
+};
- protected:
- std::shared_ptr<Module> module;
- std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
- };
+/// A policy type that may be requested via GetDisplayService and
+/// GetDisplayServiceWithProxyNameExchange
+enum class Policy {
+ User,
+ Compositor,
};
+namespace detail {
+void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
+ std::shared_ptr<NVFlinger::NVFlinger> nv_flinger, Permission permission);
+} // namespace detail
+
/// Registers all VI services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp
index 207c06b16..06070087f 100644
--- a/src/core/hle/service/vi/vi_m.cpp
+++ b/src/core/hle/service/vi/vi_m.cpp
@@ -2,12 +2,14 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/logging/log.h"
+#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_m.h"
namespace Service::VI {
-VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
- : Module::Interface(std::move(module), "vi:m", std::move(nv_flinger)) {
+VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
+ : ServiceFramework{"vi:m"}, nv_flinger{std::move(nv_flinger)} {
static const FunctionInfo functions[] = {
{2, &VI_M::GetDisplayService, "GetDisplayService"},
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -17,4 +19,10 @@ VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
VI_M::~VI_M() = default;
+void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_VI, "called");
+
+ detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::Manager);
+}
+
} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h
index 487d58d50..290e06689 100644
--- a/src/core/hle/service/vi/vi_m.h
+++ b/src/core/hle/service/vi/vi_m.h
@@ -4,14 +4,27 @@
#pragma once
-#include "core/hle/service/vi/vi.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class HLERequestContext;
+}
+
+namespace Service::NVFlinger {
+class NVFlinger;
+}
namespace Service::VI {
-class VI_M final : public Module::Interface {
+class VI_M final : public ServiceFramework<VI_M> {
public:
- explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
+ explicit VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
~VI_M() override;
+
+private:
+ void GetDisplayService(Kernel::HLERequestContext& ctx);
+
+ std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp
index 920e6a1f6..57c596cc4 100644
--- a/src/core/hle/service/vi/vi_s.cpp
+++ b/src/core/hle/service/vi/vi_s.cpp
@@ -2,12 +2,14 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/logging/log.h"
+#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_s.h"
namespace Service::VI {
-VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
- : Module::Interface(std::move(module), "vi:s", std::move(nv_flinger)) {
+VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
+ : ServiceFramework{"vi:s"}, nv_flinger{std::move(nv_flinger)} {
static const FunctionInfo functions[] = {
{1, &VI_S::GetDisplayService, "GetDisplayService"},
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -17,4 +19,10 @@ VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
VI_S::~VI_S() = default;
+void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_VI, "called");
+
+ detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::System);
+}
+
} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h
index bbc31148f..47804dc0b 100644
--- a/src/core/hle/service/vi/vi_s.h
+++ b/src/core/hle/service/vi/vi_s.h
@@ -4,14 +4,27 @@
#pragma once
-#include "core/hle/service/vi/vi.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class HLERequestContext;
+}
+
+namespace Service::NVFlinger {
+class NVFlinger;
+}
namespace Service::VI {
-class VI_S final : public Module::Interface {
+class VI_S final : public ServiceFramework<VI_S> {
public:
- explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
+ explicit VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
~VI_S() override;
+
+private:
+ void GetDisplayService(Kernel::HLERequestContext& ctx);
+
+ std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp
index d81e410d6..9d5ceb608 100644
--- a/src/core/hle/service/vi/vi_u.cpp
+++ b/src/core/hle/service/vi/vi_u.cpp
@@ -2,12 +2,14 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/logging/log.h"
+#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_u.h"
namespace Service::VI {
-VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
- : Module::Interface(std::move(module), "vi:u", std::move(nv_flinger)) {
+VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
+ : ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
static const FunctionInfo functions[] = {
{0, &VI_U::GetDisplayService, "GetDisplayService"},
};
@@ -16,4 +18,10 @@ VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
VI_U::~VI_U() = default;
+void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_VI, "called");
+
+ detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::User);
+}
+
} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h
index b92f28c92..19bdb73b0 100644
--- a/src/core/hle/service/vi/vi_u.h
+++ b/src/core/hle/service/vi/vi_u.h
@@ -4,14 +4,27 @@
#pragma once
-#include "core/hle/service/vi/vi.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class HLERequestContext;
+}
+
+namespace Service::NVFlinger {
+class NVFlinger;
+}
namespace Service::VI {
-class VI_U final : public Module::Interface {
+class VI_U final : public ServiceFramework<VI_U> {
public:
- explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
+ explicit VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
~VI_U() override;
+
+private:
+ void GetDisplayService(Kernel::HLERequestContext& ctx);
+
+ std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
} // namespace Service::VI
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 07aa7a1cd..10b13fb1d 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -86,25 +86,29 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::Virtua
return FileType::Error;
}
-ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) {
+AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load(
+ Kernel::Process& process) {
if (is_loaded) {
- return ResultStatus::ErrorAlreadyLoaded;
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
}
if (dir == nullptr) {
- if (file == nullptr)
- return ResultStatus::ErrorNullFile;
+ if (file == nullptr) {
+ return {ResultStatus::ErrorNullFile, {}};
+ }
+
dir = file->GetContainingDirectory();
}
// Read meta to determine title ID
FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
- if (npdm == nullptr)
- return ResultStatus::ErrorMissingNPDM;
+ if (npdm == nullptr) {
+ return {ResultStatus::ErrorMissingNPDM, {}};
+ }
- ResultStatus result = metadata.Load(npdm);
+ const ResultStatus result = metadata.Load(npdm);
if (result != ResultStatus::Success) {
- return result;
+ return {result, {}};
}
if (override_update) {
@@ -114,23 +118,24 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
// Reread in case PatchExeFS affected the main.npdm
npdm = dir->GetFile("main.npdm");
- if (npdm == nullptr)
- return ResultStatus::ErrorMissingNPDM;
+ if (npdm == nullptr) {
+ return {ResultStatus::ErrorMissingNPDM, {}};
+ }
- ResultStatus result2 = metadata.Load(npdm);
+ const ResultStatus result2 = metadata.Load(npdm);
if (result2 != ResultStatus::Success) {
- return result2;
+ return {result2, {}};
}
metadata.Print();
const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit ||
arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
- return ResultStatus::Error32BitISA;
+ return {ResultStatus::Error32BitISA, {}};
}
if (process.LoadFromMetadata(metadata).IsError()) {
- return ResultStatus::ErrorUnableToParseKernelMetadata;
+ return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
}
const FileSys::PatchManager pm(metadata.GetTitleID());
@@ -150,7 +155,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
const auto tentative_next_load_addr =
AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments, pm);
if (!tentative_next_load_addr) {
- return ResultStatus::ErrorLoadingNSO;
+ return {ResultStatus::ErrorLoadingNSO, {}};
}
next_load_addr = *tentative_next_load_addr;
@@ -159,8 +164,6 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
}
- process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize());
-
// Find the RomFS by searching for a ".romfs" file in this directory
const auto& files = dir->GetFiles();
const auto romfs_iter =
@@ -175,7 +178,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
}
is_loaded = true;
- return ResultStatus::Success;
+ return {ResultStatus::Success,
+ LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}};
}
ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) {
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 1615cb5a8..1a65c16a4 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -37,7 +37,7 @@ public:
return IdentifyType(file);
}
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 6057c7f26..6d4b02375 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -9,6 +9,7 @@
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
+#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/loader/elf.h"
@@ -340,7 +341,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
}
codeset.entrypoint = base_addr + header->e_entry;
- codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
+ codeset.memory = std::move(program_image);
LOG_DEBUG(Loader, "Done loading.");
@@ -381,13 +382,15 @@ FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
-ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
- if (is_loaded)
- return ResultStatus::ErrorAlreadyLoaded;
+AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) {
+ if (is_loaded) {
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
+ }
std::vector<u8> buffer = file->ReadAllBytes();
- if (buffer.size() != file->GetSize())
- return ResultStatus::ErrorIncorrectELFFileSize;
+ if (buffer.size() != file->GetSize()) {
+ return {ResultStatus::ErrorIncorrectELFFileSize, {}};
+ }
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
ElfReader elf_reader(&buffer[0]);
@@ -395,10 +398,9 @@ ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
const VAddr entry_point = codeset.entrypoint;
process.LoadModule(std::move(codeset), entry_point);
- process.Run(entry_point, 48, Memory::DEFAULT_STACK_SIZE);
is_loaded = true;
- return ResultStatus::Success;
+ return {ResultStatus::Success, LoadParameters{48, Memory::DEFAULT_STACK_SIZE}};
}
} // namespace Loader
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index a2d33021c..7ef7770a6 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -26,7 +26,7 @@ public:
return IdentifyType(file);
}
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
};
} // namespace Loader
diff --git a/src/core/loader/linker.cpp b/src/core/loader/linker.cpp
deleted file mode 100644
index 57ca8c3ee..000000000
--- a/src/core/loader/linker.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <vector>
-
-#include "common/common_funcs.h"
-#include "common/logging/log.h"
-#include "common/swap.h"
-#include "core/loader/linker.h"
-#include "core/memory.h"
-
-namespace Loader {
-
-enum class RelocationType : u32 { ABS64 = 257, GLOB_DAT = 1025, JUMP_SLOT = 1026, RELATIVE = 1027 };
-
-enum DynamicType : u32 {
- DT_NULL = 0,
- DT_PLTRELSZ = 2,
- DT_STRTAB = 5,
- DT_SYMTAB = 6,
- DT_RELA = 7,
- DT_RELASZ = 8,
- DT_STRSZ = 10,
- DT_JMPREL = 23,
-};
-
-struct Elf64_Rela {
- u64_le offset;
- RelocationType type;
- u32_le symbol;
- s64_le addend;
-};
-static_assert(sizeof(Elf64_Rela) == 0x18, "Elf64_Rela has incorrect size.");
-
-struct Elf64_Dyn {
- u64_le tag;
- u64_le value;
-};
-static_assert(sizeof(Elf64_Dyn) == 0x10, "Elf64_Dyn has incorrect size.");
-
-struct Elf64_Sym {
- u32_le name;
- INSERT_PADDING_BYTES(0x2);
- u16_le shndx;
- u64_le value;
- u64_le size;
-};
-static_assert(sizeof(Elf64_Sym) == 0x18, "Elf64_Sym has incorrect size.");
-
-void Linker::WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
- u64 relocation_offset, u64 size, VAddr load_base) {
- for (u64 i = 0; i < size; i += sizeof(Elf64_Rela)) {
- Elf64_Rela rela;
- std::memcpy(&rela, &program_image[relocation_offset + i], sizeof(Elf64_Rela));
-
- const Symbol& symbol = symbols[rela.symbol];
- switch (rela.type) {
- case RelocationType::RELATIVE: {
- const u64 value = load_base + rela.addend;
- if (!symbol.name.empty()) {
- exports[symbol.name] = value;
- }
- std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
- break;
- }
- case RelocationType::JUMP_SLOT:
- case RelocationType::GLOB_DAT:
- if (!symbol.value) {
- imports[symbol.name] = {rela.offset + load_base, 0};
- } else {
- exports[symbol.name] = symbol.value;
- std::memcpy(&program_image[rela.offset], &symbol.value, sizeof(u64));
- }
- break;
- case RelocationType::ABS64:
- if (!symbol.value) {
- imports[symbol.name] = {rela.offset + load_base, rela.addend};
- } else {
- const u64 value = symbol.value + rela.addend;
- exports[symbol.name] = value;
- std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
- }
- break;
- default:
- LOG_CRITICAL(Loader, "Unknown relocation type: {}", static_cast<int>(rela.type));
- break;
- }
- }
-}
-
-void Linker::Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base) {
- std::map<u64, u64> dynamic;
- while (dynamic_section_offset < program_image.size()) {
- Elf64_Dyn dyn;
- std::memcpy(&dyn, &program_image[dynamic_section_offset], sizeof(Elf64_Dyn));
- dynamic_section_offset += sizeof(Elf64_Dyn);
-
- if (dyn.tag == DT_NULL) {
- break;
- }
- dynamic[dyn.tag] = dyn.value;
- }
-
- u64 offset = dynamic[DT_SYMTAB];
- std::vector<Symbol> symbols;
- while (offset < program_image.size()) {
- Elf64_Sym sym;
- std::memcpy(&sym, &program_image[offset], sizeof(Elf64_Sym));
- offset += sizeof(Elf64_Sym);
-
- if (sym.name >= dynamic[DT_STRSZ]) {
- break;
- }
-
- std::string name = reinterpret_cast<char*>(&program_image[dynamic[DT_STRTAB] + sym.name]);
- if (sym.value) {
- exports[name] = load_base + sym.value;
- symbols.emplace_back(std::move(name), load_base + sym.value);
- } else {
- symbols.emplace_back(std::move(name), 0);
- }
- }
-
- if (dynamic.find(DT_RELA) != dynamic.end()) {
- WriteRelocations(program_image, symbols, dynamic[DT_RELA], dynamic[DT_RELASZ], load_base);
- }
-
- if (dynamic.find(DT_JMPREL) != dynamic.end()) {
- WriteRelocations(program_image, symbols, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ],
- load_base);
- }
-}
-
-void Linker::ResolveImports() {
- // Resolve imports
- for (const auto& import : imports) {
- const auto& search = exports.find(import.first);
- if (search != exports.end()) {
- Memory::Write64(import.second.ea, search->second + import.second.addend);
- } else {
- LOG_ERROR(Loader, "Unresolved import: {}", import.first);
- }
- }
-}
-
-} // namespace Loader
diff --git a/src/core/loader/linker.h b/src/core/loader/linker.h
deleted file mode 100644
index 107625837..000000000
--- a/src/core/loader/linker.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <map>
-#include <string>
-#include "common/common_types.h"
-
-namespace Loader {
-
-class Linker {
-protected:
- struct Symbol {
- Symbol(std::string&& name, u64 value) : name(std::move(name)), value(value) {}
- std::string name;
- u64 value;
- };
-
- struct Import {
- VAddr ea;
- s64 addend;
- };
-
- void WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
- u64 relocation_offset, u64 size, VAddr load_base);
- void Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base);
-
- void ResolveImports();
-
- std::map<std::string, Import> imports;
- std::map<std::string, VAddr> exports;
-};
-
-} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index bb925f4a6..869406b75 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -131,6 +131,12 @@ std::ostream& operator<<(std::ostream& os, ResultStatus status);
/// Interface for loading an application
class AppLoader : NonCopyable {
public:
+ struct LoadParameters {
+ s32 main_thread_priority;
+ u64 main_thread_stack_size;
+ };
+ using LoadResult = std::pair<ResultStatus, std::optional<LoadParameters>>;
+
explicit AppLoader(FileSys::VirtualFile file);
virtual ~AppLoader();
@@ -145,18 +151,7 @@ public:
* @param process The newly created process.
* @return The status result of the operation.
*/
- virtual ResultStatus 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);
- }
+ virtual LoadResult Load(Kernel::Process& process) = 0;
/**
* Get the code (typically .code section) of the application
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index 93a970d10..34efef09a 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -41,31 +41,37 @@ FileType AppLoader_NAX::GetFileType() const {
return IdentifyTypeImpl(*nax);
}
-ResultStatus AppLoader_NAX::Load(Kernel::Process& process) {
+AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) {
if (is_loaded) {
- return ResultStatus::ErrorAlreadyLoaded;
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
}
- if (nax->GetStatus() != ResultStatus::Success)
- return nax->GetStatus();
+ const auto nax_status = nax->GetStatus();
+ if (nax_status != ResultStatus::Success) {
+ return {nax_status, {}};
+ }
const auto nca = nax->AsNCA();
if (nca == nullptr) {
- if (!Core::Crypto::KeyManager::KeyFileExists(false))
- return ResultStatus::ErrorMissingProductionKeyFile;
- return ResultStatus::ErrorNAXInconvertibleToNCA;
+ if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
+ return {ResultStatus::ErrorMissingProductionKeyFile, {}};
+ }
+
+ return {ResultStatus::ErrorNAXInconvertibleToNCA, {}};
}
- if (nca->GetStatus() != ResultStatus::Success)
- return nca->GetStatus();
+ const auto nca_status = nca->GetStatus();
+ if (nca_status != ResultStatus::Success) {
+ return {nca_status, {}};
+ }
const auto result = nca_loader->Load(process);
- if (result != ResultStatus::Success)
+ if (result.first != ResultStatus::Success) {
return result;
+ }
is_loaded = true;
-
- return ResultStatus::Success;
+ return result;
}
ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) {
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index f40079574..00f1659c1 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -33,7 +33,7 @@ public:
FileType GetFileType() const override;
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index ce8196fcf..b3f8f1083 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -30,36 +30,38 @@ FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
-ResultStatus AppLoader_NCA::Load(Kernel::Process& process) {
+AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) {
if (is_loaded) {
- return ResultStatus::ErrorAlreadyLoaded;
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
}
const auto result = nca->GetStatus();
if (result != ResultStatus::Success) {
- return result;
+ return {result, {}};
}
- if (nca->GetType() != FileSys::NCAContentType::Program)
- return ResultStatus::ErrorNCANotProgram;
+ if (nca->GetType() != FileSys::NCAContentType::Program) {
+ return {ResultStatus::ErrorNCANotProgram, {}};
+ }
const auto exefs = nca->GetExeFS();
-
- if (exefs == nullptr)
- return ResultStatus::ErrorNoExeFS;
+ if (exefs == nullptr) {
+ return {ResultStatus::ErrorNoExeFS, {}};
+ }
directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true);
const auto load_result = directory_loader->Load(process);
- if (load_result != ResultStatus::Success)
+ if (load_result.first != ResultStatus::Success) {
return load_result;
+ }
- if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0)
+ if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) {
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
+ }
is_loaded = true;
-
- return ResultStatus::Success;
+ return load_result;
}
ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index b9f077468..94f0ed677 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -33,7 +33,7 @@ public:
return IdentifyType(file);
}
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 4fad0c0dd..6a0ca389b 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -14,6 +14,7 @@
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/vfs_offset.h"
#include "core/gdbstub/gdbstub.h"
+#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -186,7 +187,7 @@ static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,
program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
// Load codeset for current process
- codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
+ codeset.memory = std::move(program_image);
process.LoadModule(std::move(codeset), load_base);
// Register module with GDBStub
@@ -200,25 +201,25 @@ bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& fi
return LoadNroImpl(process, file.ReadAllBytes(), file.GetName(), load_base);
}
-ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
+AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
if (is_loaded) {
- return ResultStatus::ErrorAlreadyLoaded;
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
}
// Load NRO
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
if (!LoadNro(process, *file, base_address)) {
- return ResultStatus::ErrorLoadingNRO;
+ return {ResultStatus::ErrorLoadingNRO, {}};
}
- if (romfs != nullptr)
+ if (romfs != nullptr) {
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
-
- process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
+ }
is_loaded = true;
- return ResultStatus::Success;
+ return {ResultStatus::Success,
+ LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}};
}
ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 013d629c0..1ffdae805 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -4,10 +4,10 @@
#pragma once
+#include <memory>
#include <string>
#include <vector>
#include "common/common_types.h"
-#include "core/loader/linker.h"
#include "core/loader/loader.h"
namespace FileSys {
@@ -21,7 +21,7 @@ class Process;
namespace Loader {
/// Loads an NRO file
-class AppLoader_NRO final : public AppLoader, Linker {
+class AppLoader_NRO final : public AppLoader {
public:
explicit AppLoader_NRO(FileSys::VirtualFile file);
~AppLoader_NRO() override;
@@ -37,7 +37,7 @@ public:
return IdentifyType(file);
}
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadProgramId(u64& out_program_id) override;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 6ded0b707..80090b792 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -4,13 +4,17 @@
#include <cinttypes>
#include <vector>
-#include <lz4.h>
+
#include "common/common_funcs.h"
#include "common/file_util.h"
+#include "common/hex_util.h"
#include "common/logging/log.h"
+#include "common/lz4_compression.h"
#include "common/swap.h"
+#include "core/core.h"
#include "core/file_sys/patch_manager.h"
#include "core/gdbstub/gdbstub.h"
+#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/loader/nso.h"
@@ -18,36 +22,8 @@
#include "core/settings.h"
namespace Loader {
-
-struct NsoSegmentHeader {
- u32_le offset;
- u32_le location;
- u32_le size;
- union {
- u32_le alignment;
- u32_le bss_size;
- };
-};
-static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
-
-struct NsoHeader {
- u32_le magic;
- u32_le version;
- INSERT_PADDING_WORDS(1);
- u8 flags;
- std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
- std::array<u8, 0x20> build_id;
- std::array<u32_le, 3> segments_compressed_size;
-
- bool IsSegmentCompressed(size_t segment_num) const {
- ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
- return ((flags >> segment_num) & 1);
- }
-};
-static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
-static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable.");
-
-struct ModHeader {
+namespace {
+struct MODHeader {
u32_le magic;
u32_le dynamic_offset;
u32_le bss_start_offset;
@@ -56,7 +32,28 @@ struct ModHeader {
u32_le eh_frame_hdr_end_offset;
u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
};
-static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
+static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
+
+std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
+ const NSOSegmentHeader& header) {
+ const std::vector<u8> uncompressed_data =
+ Common::Compression::DecompressDataLZ4(compressed_data, header.size);
+
+ ASSERT_MSG(uncompressed_data.size() == header.size, "{} != {}", header.size,
+ uncompressed_data.size());
+
+ return uncompressed_data;
+}
+
+constexpr u32 PageAlignSize(u32 size) {
+ return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
+}
+} // Anonymous namespace
+
+bool NSOHeader::IsSegmentCompressed(size_t segment_num) const {
+ ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
+ return ((flags >> segment_num) & 1) != 0;
+}
AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
@@ -73,38 +70,22 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::NSO;
}
-static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
- const NsoSegmentHeader& header) {
- std::vector<u8> uncompressed_data(header.size);
- const int bytes_uncompressed =
- LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
- reinterpret_cast<char*>(uncompressed_data.data()),
- static_cast<int>(compressed_data.size()), header.size);
-
- ASSERT_MSG(bytes_uncompressed == static_cast<int>(header.size) &&
- bytes_uncompressed == static_cast<int>(uncompressed_data.size()),
- "{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
-
- return uncompressed_data;
-}
-
-static constexpr u32 PageAlignSize(u32 size) {
- return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
-}
-
std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
const FileSys::VfsFile& file, VAddr load_base,
bool should_pass_arguments,
std::optional<FileSys::PatchManager> pm) {
- if (file.GetSize() < sizeof(NsoHeader))
+ if (file.GetSize() < sizeof(NSOHeader)) {
return {};
+ }
- NsoHeader nso_header{};
- if (sizeof(NsoHeader) != file.ReadObject(&nso_header))
+ NSOHeader nso_header{};
+ if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) {
return {};
+ }
- if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
+ if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
return {};
+ }
// Build program image
Kernel::CodeSet codeset;
@@ -140,10 +121,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
// Read MOD header
- ModHeader mod_header{};
+ MODHeader mod_header{};
// Default .bss to size in segment header if MOD0 section doesn't exist
u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
- std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader));
+ std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(MODHeader));
const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
if (has_mod_header) {
// Resize program image to include .bss section and page align each section
@@ -155,17 +136,29 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
// Apply patches if necessary
if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
- std::vector<u8> pi_header(program_image.size() + 0x100);
- std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
- std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
+ std::vector<u8> pi_header;
+ pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
+ reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
+ pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(),
+ program_image.end());
- pi_header = pm->PatchNSO(pi_header);
+ pi_header = pm->PatchNSO(pi_header, file.GetName());
- std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size());
+ std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin());
+ }
+
+ // Apply cheats if they exist and the program has a valid title ID
+ if (pm) {
+ auto& system = Core::System::GetInstance();
+ const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
+ if (!cheats.empty()) {
+ system.RegisterCheatList(cheats, Common::HexToString(nso_header.build_id), load_base,
+ load_base + program_image.size());
+ }
}
// Load codeset for current process
- codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
+ codeset.memory = std::move(program_image);
process.LoadModule(std::move(codeset), load_base);
// Register module with GDBStub
@@ -174,22 +167,21 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
return load_base + image_size;
}
-ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
+AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
if (is_loaded) {
- return ResultStatus::ErrorAlreadyLoaded;
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
}
// Load module
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
if (!LoadModule(process, *file, base_address, true)) {
- return ResultStatus::ErrorLoadingNSO;
+ return {ResultStatus::ErrorLoadingNSO, {}};
}
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
- process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
-
is_loaded = true;
- return ResultStatus::Success;
+ return {ResultStatus::Success,
+ LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}};
}
} // namespace Loader
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 135b6ea5a..fdce9191c 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -4,10 +4,12 @@
#pragma once
+#include <array>
#include <optional>
+#include <type_traits>
#include "common/common_types.h"
+#include "common/swap.h"
#include "core/file_sys/patch_manager.h"
-#include "core/loader/linker.h"
#include "core/loader/loader.h"
namespace Kernel {
@@ -16,6 +18,43 @@ class Process;
namespace Loader {
+struct NSOSegmentHeader {
+ u32_le offset;
+ u32_le location;
+ u32_le size;
+ union {
+ u32_le alignment;
+ u32_le bss_size;
+ };
+};
+static_assert(sizeof(NSOSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
+
+struct NSOHeader {
+ using SHA256Hash = std::array<u8, 0x20>;
+
+ struct RODataRelativeExtent {
+ u32_le data_offset;
+ u32_le size;
+ };
+
+ u32_le magic;
+ u32_le version;
+ u32 reserved;
+ u32_le flags;
+ std::array<NSOSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
+ std::array<u8, 0x20> build_id;
+ std::array<u32_le, 3> segments_compressed_size;
+ std::array<u8, 0x1C> padding;
+ RODataRelativeExtent api_info_extent;
+ RODataRelativeExtent dynstr_extent;
+ RODataRelativeExtent dynsyn_extent;
+ std::array<SHA256Hash, 3> segment_hashes;
+
+ bool IsSegmentCompressed(size_t segment_num) const;
+};
+static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
+static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
+
constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
struct NSOArgumentHeader {
@@ -26,7 +65,7 @@ struct NSOArgumentHeader {
static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size.");
/// Loads an NSO file
-class AppLoader_NSO final : public AppLoader, Linker {
+class AppLoader_NSO final : public AppLoader {
public:
explicit AppLoader_NSO(FileSys::VirtualFile file);
@@ -45,7 +84,7 @@ public:
VAddr load_base, bool should_pass_arguments,
std::optional<FileSys::PatchManager> pm = {});
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
};
} // namespace Loader
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 7da1f8960..ad56bbb38 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -72,37 +72,45 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
-ResultStatus AppLoader_NSP::Load(Kernel::Process& process) {
+AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
if (is_loaded) {
- return ResultStatus::ErrorAlreadyLoaded;
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
}
- if (title_id == 0)
- return ResultStatus::ErrorNSPMissingProgramNCA;
+ if (title_id == 0) {
+ return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
+ }
- if (nsp->GetStatus() != ResultStatus::Success)
- return nsp->GetStatus();
+ const auto nsp_status = nsp->GetStatus();
+ if (nsp_status != ResultStatus::Success) {
+ return {nsp_status, {}};
+ }
- if (nsp->GetProgramStatus(title_id) != ResultStatus::Success)
- return nsp->GetProgramStatus(title_id);
+ const auto nsp_program_status = nsp->GetProgramStatus(title_id);
+ if (nsp_program_status != ResultStatus::Success) {
+ return {nsp_program_status, {}};
+ }
if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
- if (!Core::Crypto::KeyManager::KeyFileExists(false))
- return ResultStatus::ErrorMissingProductionKeyFile;
- return ResultStatus::ErrorNSPMissingProgramNCA;
+ if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
+ return {ResultStatus::ErrorMissingProductionKeyFile, {}};
+ }
+
+ return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
}
const auto result = secondary_loader->Load(process);
- if (result != ResultStatus::Success)
+ if (result.first != ResultStatus::Success) {
return result;
+ }
FileSys::VirtualFile update_raw;
- if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr)
+ if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
Service::FileSystem::SetPackedUpdate(std::move(update_raw));
+ }
is_loaded = true;
-
- return ResultStatus::Success;
+ return result;
}
ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& file) {
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 953a1b508..85e870bdf 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -35,7 +35,7 @@ public:
return IdentifyType(file);
}
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 89f7bbf77..1e285a053 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -48,31 +48,35 @@ FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
-ResultStatus AppLoader_XCI::Load(Kernel::Process& process) {
+AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) {
if (is_loaded) {
- return ResultStatus::ErrorAlreadyLoaded;
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
}
- if (xci->GetStatus() != ResultStatus::Success)
- return xci->GetStatus();
+ if (xci->GetStatus() != ResultStatus::Success) {
+ return {xci->GetStatus(), {}};
+ }
- if (xci->GetProgramNCAStatus() != ResultStatus::Success)
- return xci->GetProgramNCAStatus();
+ if (xci->GetProgramNCAStatus() != ResultStatus::Success) {
+ return {xci->GetProgramNCAStatus(), {}};
+ }
- if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false))
- return ResultStatus::ErrorMissingProductionKeyFile;
+ if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false)) {
+ return {ResultStatus::ErrorMissingProductionKeyFile, {}};
+ }
const auto result = nca_loader->Load(process);
- if (result != ResultStatus::Success)
+ if (result.first != ResultStatus::Success) {
return result;
+ }
FileSys::VirtualFile update_raw;
- if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr)
+ if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
Service::FileSystem::SetPackedUpdate(std::move(update_raw));
+ }
is_loaded = true;
-
- return ResultStatus::Success;
+ return result;
}
ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& file) {
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index d6995b61e..ae7145b14 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -22,7 +22,7 @@ class AppLoader_NCA;
class AppLoader_XCI final : public AppLoader {
public:
explicit AppLoader_XCI(FileSys::VirtualFile file);
- ~AppLoader_XCI();
+ ~AppLoader_XCI() override;
/**
* Returns the type of the file
@@ -35,7 +35,7 @@ public:
return IdentifyType(file);
}
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 6591c45d2..f18f6226b 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -10,6 +10,7 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
+#include "common/page_table.h"
#include "common/swap.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
@@ -18,57 +19,35 @@
#include "core/hle/lock.h"
#include "core/memory.h"
#include "core/memory_setup.h"
+#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
namespace Memory {
-static PageTable* current_page_table = nullptr;
+static Common::PageTable* current_page_table = nullptr;
-void SetCurrentPageTable(PageTable* page_table) {
- current_page_table = page_table;
+void SetCurrentPageTable(Kernel::Process& process) {
+ current_page_table = &process.VMManager().page_table;
- auto& system = Core::System::GetInstance();
- if (system.IsPoweredOn()) {
- system.ArmInterface(0).PageTableChanged();
- system.ArmInterface(1).PageTableChanged();
- system.ArmInterface(2).PageTableChanged();
- system.ArmInterface(3).PageTableChanged();
- }
-}
-
-PageTable* GetCurrentPageTable() {
- return current_page_table;
-}
-
-PageTable::PageTable() = default;
-
-PageTable::PageTable(std::size_t address_space_width_in_bits) {
- Resize(address_space_width_in_bits);
-}
+ const std::size_t address_space_width = process.VMManager().GetAddressSpaceWidth();
-PageTable::~PageTable() = default;
-
-void PageTable::Resize(std::size_t address_space_width_in_bits) {
- const std::size_t num_page_table_entries = 1ULL << (address_space_width_in_bits - PAGE_BITS);
-
- pointers.resize(num_page_table_entries);
- attributes.resize(num_page_table_entries);
-
- // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
- // vector size is subsequently decreased (via resize), the vector might not automatically
- // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
- // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
-
- pointers.shrink_to_fit();
- attributes.shrink_to_fit();
+ auto& system = Core::System::GetInstance();
+ system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width);
+ system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width);
+ system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width);
+ system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width);
}
-static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) {
+static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
+ Common::PageType type) {
LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
(base + size) * PAGE_SIZE);
- RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE,
- FlushMode::FlushAndInvalidate);
+ // During boot, current_page_table might not be set yet, in which case we need not flush
+ if (Core::System::GetInstance().IsPoweredOn()) {
+ Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS,
+ size * PAGE_SIZE);
+ }
VAddr end = base + size;
ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
@@ -88,41 +67,47 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa
}
}
-void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target) {
+void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
- MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
+ MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
}
-void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer mmio_handler) {
+void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
+ Common::MemoryHookPointer mmio_handler) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
- MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
+ MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Special);
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
- SpecialRegion region{SpecialRegion::Type::IODevice, std::move(mmio_handler)};
- page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region}));
+ Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice, std::move(mmio_handler)};
+ page_table.special_regions.add(
+ std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
}
-void UnmapRegion(PageTable& page_table, VAddr base, u64 size) {
+void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
- MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
+ MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Unmapped);
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
page_table.special_regions.erase(interval);
}
-void AddDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) {
+void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
+ Common::MemoryHookPointer hook) {
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
- SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)};
- page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region}));
+ Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
+ page_table.special_regions.add(
+ std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
}
-void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) {
+void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
+ Common::MemoryHookPointer hook) {
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
- SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)};
- page_table.special_regions.subtract(std::make_pair(interval, std::set<SpecialRegion>{region}));
+ Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
+ page_table.special_regions.subtract(
+ std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
}
/**
@@ -171,19 +156,19 @@ T Read(const VAddr vaddr) {
return value;
}
- PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
+ Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
- case PageType::Unmapped:
+ case Common::PageType::Unmapped:
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
return 0;
- case PageType::Memory:
+ case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
break;
- case PageType::RasterizerCachedMemory: {
- RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Flush);
-
+ case Common::PageType::RasterizerCachedMemory: {
+ auto host_ptr{GetPointerFromVMA(vaddr)};
+ Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T));
T value;
- std::memcpy(&value, GetPointerFromVMA(vaddr), sizeof(T));
+ std::memcpy(&value, host_ptr, sizeof(T));
return value;
}
default:
@@ -201,18 +186,19 @@ void Write(const VAddr vaddr, const T data) {
return;
}
- PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
+ Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
- case PageType::Unmapped:
+ case Common::PageType::Unmapped:
LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
static_cast<u32>(data), vaddr);
return;
- case PageType::Memory:
+ case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
break;
- case PageType::RasterizerCachedMemory: {
- RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Invalidate);
- std::memcpy(GetPointerFromVMA(vaddr), &data, sizeof(T));
+ case Common::PageType::RasterizerCachedMemory: {
+ auto host_ptr{GetPointerFromVMA(vaddr)};
+ Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T));
+ std::memcpy(host_ptr, &data, sizeof(T));
break;
}
default:
@@ -227,10 +213,10 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
if (page_pointer)
return true;
- if (page_table.attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory)
+ if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory)
return true;
- if (page_table.attributes[vaddr >> PAGE_BITS] != PageType::Special)
+ if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special)
return false;
return false;
@@ -250,7 +236,8 @@ u8* GetPointer(const VAddr vaddr) {
return page_pointer + (vaddr & PAGE_MASK);
}
- if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) {
+ if (current_page_table->attributes[vaddr >> PAGE_BITS] ==
+ Common::PageType::RasterizerCachedMemory) {
return GetPointerFromVMA(vaddr);
}
@@ -284,20 +271,20 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
- PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
+ Common::PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
if (cached) {
// Switch page type to cached if now cached
switch (page_type) {
- case PageType::Unmapped:
+ case Common::PageType::Unmapped:
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
- case PageType::Memory:
- page_type = PageType::RasterizerCachedMemory;
+ case Common::PageType::Memory:
+ page_type = Common::PageType::RasterizerCachedMemory;
current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
break;
- case PageType::RasterizerCachedMemory:
+ case Common::PageType::RasterizerCachedMemory:
// There can be more than one GPU region mapped per CPU region, so it's common that
// this area is already marked as cached.
break;
@@ -307,23 +294,23 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
} else {
// Switch page type to uncached if now uncached
switch (page_type) {
- case PageType::Unmapped:
+ case Common::PageType::Unmapped:
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
- case PageType::Memory:
+ case Common::PageType::Memory:
// There can be more than one GPU region mapped per CPU region, so it's common that
// this area is already unmarked as cached.
break;
- case PageType::RasterizerCachedMemory: {
+ case Common::PageType::RasterizerCachedMemory: {
u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
if (pointer == nullptr) {
// It's possible that this function has been called while updating the pagetable
// after unmapping a VMA. In that case the underlying VMA will no longer exist,
// and we should just leave the pagetable entry blank.
- page_type = PageType::Unmapped;
+ page_type = Common::PageType::Unmapped;
} else {
- page_type = PageType::Memory;
+ page_type = Common::PageType::Memory;
current_page_table->pointers[vaddr >> PAGE_BITS] = pointer;
}
break;
@@ -335,47 +322,6 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
}
}
-void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
- auto& system_instance = Core::System::GetInstance();
-
- // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
- // null here
- if (!system_instance.IsPoweredOn()) {
- return;
- }
-
- const VAddr end = start + size;
-
- const auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
- if (start >= region_end || end <= region_start) {
- // No overlap with region
- return;
- }
-
- const VAddr overlap_start = std::max(start, region_start);
- const VAddr overlap_end = std::min(end, region_end);
- const VAddr overlap_size = overlap_end - overlap_start;
-
- auto& gpu = system_instance.GPU();
- switch (mode) {
- case FlushMode::Flush:
- gpu.FlushRegion(overlap_start, overlap_size);
- break;
- case FlushMode::Invalidate:
- gpu.InvalidateRegion(overlap_start, overlap_size);
- break;
- case FlushMode::FlushAndInvalidate:
- gpu.FlushAndInvalidateRegion(overlap_start, overlap_size);
- break;
- }
- };
-
- const auto& vm_manager = Core::CurrentProcess()->VMManager();
-
- CheckRegion(vm_manager.GetCodeRegionBaseAddress(), vm_manager.GetCodeRegionEndAddress());
- CheckRegion(vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionEndAddress());
-}
-
u8 Read8(const VAddr addr) {
return Read<u8>(addr);
}
@@ -406,24 +352,24 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
switch (page_table.attributes[page_index]) {
- case PageType::Unmapped: {
+ case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory,
"Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, src_addr, size);
std::memset(dest_buffer, 0, copy_amount);
break;
}
- case PageType::Memory: {
+ case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
const u8* src_ptr = page_table.pointers[page_index] + page_offset;
std::memcpy(dest_buffer, src_ptr, copy_amount);
break;
}
- case PageType::RasterizerCachedMemory: {
- RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
- FlushMode::Flush);
- std::memcpy(dest_buffer, GetPointerFromVMA(process, current_vaddr), copy_amount);
+ case Common::PageType::RasterizerCachedMemory: {
+ const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
+ Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
+ std::memcpy(dest_buffer, host_ptr, copy_amount);
break;
}
default:
@@ -470,23 +416,23 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
switch (page_table.attributes[page_index]) {
- case PageType::Unmapped: {
+ case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory,
"Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, dest_addr, size);
break;
}
- case PageType::Memory: {
+ case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
u8* dest_ptr = page_table.pointers[page_index] + page_offset;
std::memcpy(dest_ptr, src_buffer, copy_amount);
break;
}
- case PageType::RasterizerCachedMemory: {
- RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
- FlushMode::Invalidate);
- std::memcpy(GetPointerFromVMA(process, current_vaddr), src_buffer, copy_amount);
+ case Common::PageType::RasterizerCachedMemory: {
+ const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
+ Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
+ std::memcpy(host_ptr, src_buffer, copy_amount);
break;
}
default:
@@ -516,23 +462,23 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std:
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
switch (page_table.attributes[page_index]) {
- case PageType::Unmapped: {
+ case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory,
"Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, dest_addr, size);
break;
}
- case PageType::Memory: {
+ case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
u8* dest_ptr = page_table.pointers[page_index] + page_offset;
std::memset(dest_ptr, 0, copy_amount);
break;
}
- case PageType::RasterizerCachedMemory: {
- RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
- FlushMode::Invalidate);
- std::memset(GetPointerFromVMA(process, current_vaddr), 0, copy_amount);
+ case Common::PageType::RasterizerCachedMemory: {
+ const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
+ Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
+ std::memset(host_ptr, 0, copy_amount);
break;
}
default:
@@ -558,23 +504,23 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
switch (page_table.attributes[page_index]) {
- case PageType::Unmapped: {
+ case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory,
"Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, src_addr, size);
ZeroBlock(process, dest_addr, copy_amount);
break;
}
- case PageType::Memory: {
+ case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
const u8* src_ptr = page_table.pointers[page_index] + page_offset;
WriteBlock(process, dest_addr, src_ptr, copy_amount);
break;
}
- case PageType::RasterizerCachedMemory: {
- RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
- FlushMode::Flush);
- WriteBlock(process, dest_addr, GetPointerFromVMA(process, current_vaddr), copy_amount);
+ case Common::PageType::RasterizerCachedMemory: {
+ const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
+ Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
+ WriteBlock(process, dest_addr, host_ptr, copy_amount);
break;
}
default:
diff --git a/src/core/memory.h b/src/core/memory.h
index 1acf5ce8c..04e2c5f1d 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -6,11 +6,11 @@
#include <cstddef>
#include <string>
-#include <tuple>
-#include <vector>
-#include <boost/icl/interval_map.hpp>
#include "common/common_types.h"
-#include "core/memory_hook.h"
+
+namespace Common {
+struct PageTable;
+}
namespace Kernel {
class Process;
@@ -26,83 +26,8 @@ constexpr std::size_t PAGE_BITS = 12;
constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS;
constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
-enum class PageType : u8 {
- /// Page is unmapped and should cause an access error.
- Unmapped,
- /// Page is mapped to regular memory. This is the only type you can get pointers to.
- Memory,
- /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
- /// invalidation
- RasterizerCachedMemory,
- /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
- Special,
-};
-
-struct SpecialRegion {
- enum class Type {
- DebugHook,
- IODevice,
- } type;
-
- MemoryHookPointer handler;
-
- bool operator<(const SpecialRegion& other) const {
- return std::tie(type, handler) < std::tie(other.type, other.handler);
- }
-
- bool operator==(const SpecialRegion& other) const {
- return std::tie(type, handler) == std::tie(other.type, other.handler);
- }
-};
-
-/**
- * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
- * mimics the way a real CPU page table works.
- */
-struct PageTable {
- explicit PageTable();
- explicit PageTable(std::size_t address_space_width_in_bits);
- ~PageTable();
-
- /**
- * Resizes the page table to be able to accomodate enough pages within
- * a given address space.
- *
- * @param address_space_width_in_bits The address size width in bits.
- */
- void Resize(std::size_t address_space_width_in_bits);
-
- /**
- * Vector of memory pointers backing each page. An entry can only be non-null if the
- * corresponding entry in the `attributes` vector is of type `Memory`.
- */
- std::vector<u8*> pointers;
-
- /**
- * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
- * of type `Special`.
- */
- boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions;
-
- /**
- * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
- * the corresponding entry in `pointers` MUST be set to null.
- */
- std::vector<PageType> attributes;
-};
-
/// Virtual user-space memory regions
enum : VAddr {
- /// Read-only page containing kernel and system configuration values.
- CONFIG_MEMORY_VADDR = 0x1FF80000,
- CONFIG_MEMORY_SIZE = 0x00001000,
- CONFIG_MEMORY_VADDR_END = CONFIG_MEMORY_VADDR + CONFIG_MEMORY_SIZE,
-
- /// Usually read-only page containing mostly values read from hardware.
- SHARED_PAGE_VADDR = 0x1FF81000,
- SHARED_PAGE_SIZE = 0x00001000,
- SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE,
-
/// TLS (Thread-Local Storage) related.
TLS_ENTRY_SIZE = 0x200,
@@ -115,9 +40,9 @@ enum : VAddr {
KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE,
};
-/// Currently active page table
-void SetCurrentPageTable(PageTable* page_table);
-PageTable* GetCurrentPageTable();
+/// Changes the currently active page table to that of
+/// the given process instance.
+void SetCurrentPageTable(Kernel::Process& process);
/// Determines if the given VAddr is valid for the specified process.
bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
@@ -147,24 +72,9 @@ u8* GetPointer(VAddr vaddr);
std::string ReadCString(VAddr vaddr, std::size_t max_length);
-enum class FlushMode {
- /// Write back modified surfaces to RAM
- Flush,
- /// Remove region from the cache
- Invalidate,
- /// Write back modified surfaces to RAM, and also remove them from the cache
- FlushAndInvalidate,
-};
-
/**
* Mark each page touching the region as cached.
*/
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
-/**
- * Flushes and invalidates any externally cached rasterizer resources touching the given virtual
- * address region.
- */
-void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode);
-
} // namespace Memory
diff --git a/src/core/memory_hook.cpp b/src/core/memory_hook.cpp
deleted file mode 100644
index c61c6c1fb..000000000
--- a/src/core/memory_hook.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2018 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "core/memory_hook.h"
-
-namespace Memory {
-
-MemoryHook::~MemoryHook() = default;
-
-} // namespace Memory
diff --git a/src/core/memory_hook.h b/src/core/memory_hook.h
deleted file mode 100644
index 940777107..000000000
--- a/src/core/memory_hook.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-#include <optional>
-
-#include "common/common_types.h"
-
-namespace Memory {
-
-/**
- * Memory hooks have two purposes:
- * 1. To allow reads and writes to a region of memory to be intercepted. This is used to implement
- * texture forwarding and memory breakpoints for debugging.
- * 2. To allow for the implementation of MMIO devices.
- *
- * A hook may be mapped to multiple regions of memory.
- *
- * If a std::nullopt or false is returned from a function, the read/write request is passed through
- * to the underlying memory region.
- */
-class MemoryHook {
-public:
- virtual ~MemoryHook();
-
- virtual std::optional<bool> IsValidAddress(VAddr addr) = 0;
-
- virtual std::optional<u8> Read8(VAddr addr) = 0;
- virtual std::optional<u16> Read16(VAddr addr) = 0;
- virtual std::optional<u32> Read32(VAddr addr) = 0;
- virtual std::optional<u64> Read64(VAddr addr) = 0;
-
- virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0;
-
- virtual bool Write8(VAddr addr, u8 data) = 0;
- virtual bool Write16(VAddr addr, u16 data) = 0;
- virtual bool Write32(VAddr addr, u32 data) = 0;
- virtual bool Write64(VAddr addr, u64 data) = 0;
-
- virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0;
-};
-
-using MemoryHookPointer = std::shared_ptr<MemoryHook>;
-} // namespace Memory
diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h
index 9a1a4f4be..5225ee8e2 100644
--- a/src/core/memory_setup.h
+++ b/src/core/memory_setup.h
@@ -5,7 +5,11 @@
#pragma once
#include "common/common_types.h"
-#include "core/memory_hook.h"
+#include "common/memory_hook.h"
+
+namespace Common {
+struct PageTable;
+}
namespace Memory {
@@ -17,7 +21,7 @@ namespace Memory {
* @param size The amount of bytes to map. Must be page-aligned.
* @param target Buffer with the memory backing the mapping. Must be of length at least `size`.
*/
-void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target);
+void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target);
/**
* Maps a region of the emulated process address space as a IO region.
@@ -26,11 +30,14 @@ void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target);
* @param size The amount of bytes to map. Must be page-aligned.
* @param mmio_handler The handler that backs the mapping.
*/
-void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer mmio_handler);
+void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
+ Common::MemoryHookPointer mmio_handler);
-void UnmapRegion(PageTable& page_table, VAddr base, u64 size);
+void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size);
-void AddDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook);
-void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook);
+void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
+ Common::MemoryHookPointer hook);
+void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
+ Common::MemoryHookPointer hook);
} // namespace Memory
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index c716a462b..4afd6c8a3 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -18,13 +18,13 @@ using std::chrono::microseconds;
namespace Core {
void PerfStats::BeginSystemFrame() {
- std::lock_guard<std::mutex> lock(object_mutex);
+ std::lock_guard lock{object_mutex};
frame_begin = Clock::now();
}
void PerfStats::EndSystemFrame() {
- std::lock_guard<std::mutex> lock(object_mutex);
+ std::lock_guard lock{object_mutex};
auto frame_end = Clock::now();
accumulated_frametime += frame_end - frame_begin;
@@ -35,13 +35,13 @@ void PerfStats::EndSystemFrame() {
}
void PerfStats::EndGameFrame() {
- std::lock_guard<std::mutex> lock(object_mutex);
+ std::lock_guard lock{object_mutex};
game_frames += 1;
}
PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
- std::lock_guard<std::mutex> lock(object_mutex);
+ std::lock_guard lock{object_mutex};
const auto now = Clock::now();
// Walltime elapsed since stats were reset
@@ -67,7 +67,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
}
double PerfStats::GetLastFrameTimeScale() {
- std::lock_guard<std::mutex> lock(object_mutex);
+ std::lock_guard lock{object_mutex};
constexpr double FRAME_LENGTH = 1.0 / 60;
return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 2e232e1e7..6d32ebea3 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -82,7 +82,6 @@ void LogSetting(const std::string& name, const T& value) {
void LogSettings() {
LOG_INFO(Config, "yuzu Configuration:");
LogSetting("System_UseDockedMode", Settings::values.use_docked_mode);
- LogSetting("System_EnableNfc", Settings::values.enable_nfc);
LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
LogSetting("System_CurrentUser", Settings::values.current_user);
LogSetting("System_LanguageIndex", Settings::values.language_index);
@@ -91,7 +90,10 @@ 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_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation);
+ LogSetting("Renderer_UseAsynchronousGpuEmulation",
+ Settings::values.use_asynchronous_gpu_emulation);
LogSetting("Audio_OutputEngine", Settings::values.sink_id);
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
diff --git a/src/core/settings.h b/src/core/settings.h
index cdfb2f742..b84390745 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -349,7 +349,6 @@ struct TouchscreenInput {
struct Values {
// System
bool use_docked_mode;
- bool enable_nfc;
std::optional<u32> rng_seed;
// Measured in seconds since epoch
std::optional<std::chrono::seconds> custom_rtc;
@@ -394,6 +393,7 @@ struct Values {
bool use_disk_shader_cache;
bool use_accurate_gpu_emulation;
bool use_asynchronous_gpu_emulation;
+ bool force_30fps_mode;
float bg_red;
float bg_green;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index e1db06811..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,13 +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
- backend = std::make_unique<WebService::TelemetryJson>(
+ auto backend = std::make_unique<WebService::TelemetryJson>(
Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token);
#else
- backend = std::make_unique<Telemetry::NullVisitor>();
+ 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());
@@ -118,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);
@@ -168,24 +186,10 @@ 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);
-
- // Complete the session, submitting to web service if necessary
- // This is just a placeholder to wrap up the session once the core completes and this is
- // destroyed. This will be moved elsewhere once we are actually doing real I/O with the service.
- 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>(
+ Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token);
field_collection.Accept(*backend);
return backend->SubmitTestcase();
#else
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index 023612b79..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.
@@ -39,7 +64,6 @@ public:
private:
Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session
- std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields
};
/**