summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGravatar bunnei2017-10-09 23:56:20 -0400
committerGravatar bunnei2017-10-09 23:56:20 -0400
commitb1d5db1cf60344b6b081c9d03cb6ccc3264326cd (patch)
treefde377c4ba3c0f92c032e6f5ec8627aae37270ef /src/core
parentloader: Various improvements for NSO/NRO loaders. (diff)
parentMerge pull request #2996 from MerryMage/split-travis (diff)
downloadyuzu-b1d5db1cf60344b6b081c9d03cb6ccc3264326cd.tar.gz
yuzu-b1d5db1cf60344b6b081c9d03cb6ccc3264326cd.tar.xz
yuzu-b1d5db1cf60344b6b081c9d03cb6ccc3264326cd.zip
Merge remote-tracking branch 'upstream/master' into nx
# Conflicts: # src/core/CMakeLists.txt # src/core/arm/dynarmic/arm_dynarmic.cpp # src/core/arm/dyncom/arm_dyncom.cpp # src/core/hle/kernel/process.cpp # src/core/hle/kernel/thread.cpp # src/core/hle/kernel/thread.h # src/core/hle/kernel/vm_manager.cpp # src/core/loader/3dsx.cpp # src/core/loader/elf.cpp # src/core/loader/ncch.cpp # src/core/memory.cpp # src/core/memory.h # src/core/memory_setup.h
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt20
-rw-r--r--src/core/arm/arm_interface.h22
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp93
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h17
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp19
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h5
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp8
-rw-r--r--src/core/arm/skyeye_common/armstate.h2
-rw-r--r--src/core/core.cpp12
-rw-r--r--src/core/core.h9
-rw-r--r--src/core/core_timing.cpp36
-rw-r--r--src/core/core_timing.h6
-rw-r--r--src/core/file_sys/archive_backend.cpp2
-rw-r--r--src/core/file_sys/archive_ncch.cpp29
-rw-r--r--src/core/file_sys/archive_sdmc.cpp41
-rw-r--r--src/core/file_sys/archive_selfncch.cpp60
-rw-r--r--src/core/file_sys/archive_selfncch.h13
-rw-r--r--src/core/file_sys/ncch_container.cpp423
-rw-r--r--src/core/file_sys/ncch_container.h274
-rw-r--r--src/core/file_sys/savedata_archive.cpp41
-rw-r--r--src/core/file_sys/title_metadata.cpp212
-rw-r--r--src/core/file_sys/title_metadata.h125
-rw-r--r--src/core/frontend/emu_window.cpp97
-rw-r--r--src/core/frontend/emu_window.h114
-rw-r--r--src/core/frontend/framebuffer_layout.cpp36
-rw-r--r--src/core/frontend/framebuffer_layout.h11
-rw-r--r--src/core/frontend/input.h25
-rw-r--r--src/core/frontend/motion_emu.cpp89
-rw-r--r--src/core/frontend/motion_emu.h52
-rw-r--r--src/core/gdbstub/gdbstub.cpp24
-rw-r--r--src/core/hle/applets/erreula.cpp4
-rw-r--r--src/core/hle/applets/mii_selector.cpp10
-rw-r--r--src/core/hle/applets/mii_selector.h57
-rw-r--r--src/core/hle/applets/mint.cpp4
-rw-r--r--src/core/hle/applets/swkbd.cpp4
-rw-r--r--src/core/hle/function_wrappers.h108
-rw-r--r--src/core/hle/ipc.h8
-rw-r--r--src/core/hle/ipc_helpers.h12
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp2
-rw-r--r--src/core/hle/kernel/kernel.h5
-rw-r--r--src/core/hle/kernel/memory.cpp30
-rw-r--r--src/core/hle/kernel/memory.h2
-rw-r--r--src/core/hle/kernel/mutex.cpp5
-rw-r--r--src/core/hle/kernel/mutex.h3
-rw-r--r--src/core/hle/kernel/process.cpp3
-rw-r--r--src/core/hle/kernel/resource_limit.cpp2
-rw-r--r--src/core/hle/kernel/resource_limit.h2
-rw-r--r--src/core/hle/kernel/semaphore.cpp3
-rw-r--r--src/core/hle/kernel/semaphore.h3
-rw-r--r--src/core/hle/kernel/shared_memory.cpp30
-rw-r--r--src/core/hle/kernel/shared_memory.h2
-rw-r--r--src/core/hle/kernel/thread.cpp68
-rw-r--r--src/core/hle/kernel/thread.h48
-rw-r--r--src/core/hle/kernel/vm_manager.cpp19
-rw-r--r--src/core/hle/kernel/vm_manager.h6
-rw-r--r--src/core/hle/kernel/wait_object.cpp13
-rw-r--r--src/core/hle/lock.cpp11
-rw-r--r--src/core/hle/lock.h18
-rw-r--r--src/core/hle/service/apt/apt.cpp729
-rw-r--r--src/core/hle/service/apt/apt.h20
-rw-r--r--src/core/hle/service/apt/apt_s.cpp8
-rw-r--r--src/core/hle/service/cam/cam.cpp2
-rw-r--r--src/core/hle/service/cfg/cfg.cpp6
-rw-r--r--src/core/hle/service/cfg/cfg.h2
-rw-r--r--src/core/hle/service/csnd_snd.cpp2
-rw-r--r--src/core/hle/service/dlp/dlp_clnt.cpp21
-rw-r--r--src/core/hle/service/dlp/dlp_fkcl.cpp18
-rw-r--r--src/core/hle/service/dlp/dlp_srvr.cpp9
-rw-r--r--src/core/hle/service/dsp_dsp.cpp7
-rw-r--r--src/core/hle/service/fs/archive.cpp20
-rw-r--r--src/core/hle/service/fs/archive.h7
-rw-r--r--src/core/hle/service/hid/hid.cpp44
-rw-r--r--src/core/hle/service/hid/hid.h2
-rw-r--r--src/core/hle/service/ir/ir_rst.cpp2
-rw-r--r--src/core/hle/service/ldr_ro/cro_helper.h6
-rw-r--r--src/core/hle/service/nim/nim.cpp18
-rw-r--r--src/core/hle/service/nim/nim.h11
-rw-r--r--src/core/hle/service/nim/nim_u.cpp2
-rw-r--r--src/core/hle/service/ns/ns.cpp16
-rw-r--r--src/core/hle/service/ns/ns.h16
-rw-r--r--src/core/hle/service/ns/ns_s.cpp34
-rw-r--r--src/core/hle/service/ns/ns_s.h (renamed from src/core/hle/service/ns_s.h)9
-rw-r--r--src/core/hle/service/ns_s.cpp33
-rw-r--r--src/core/hle/service/nwm/nwm_uds.cpp485
-rw-r--r--src/core/hle/service/nwm/nwm_uds.h12
-rw-r--r--src/core/hle/service/nwm/uds_beacon.cpp7
-rw-r--r--src/core/hle/service/nwm/uds_beacon.h30
-rw-r--r--src/core/hle/service/nwm/uds_connection.cpp88
-rw-r--r--src/core/hle/service/nwm/uds_connection.h56
-rw-r--r--src/core/hle/service/nwm/uds_data.cpp103
-rw-r--r--src/core/hle/service/nwm/uds_data.h86
-rw-r--r--src/core/hle/service/service.cpp5
-rw-r--r--src/core/hle/service/sm/sm.cpp4
-rw-r--r--src/core/hle/service/sm/sm.h3
-rw-r--r--src/core/hle/service/sm/srv.cpp28
-rw-r--r--src/core/hle/service/sm/srv.h1
-rw-r--r--src/core/hw/gpu.cpp20
-rw-r--r--src/core/hw/gpu.h4
-rw-r--r--src/core/loader/3dsx.cpp20
-rw-r--r--src/core/loader/3dsx.h2
-rw-r--r--src/core/loader/elf.cpp16
-rw-r--r--src/core/loader/elf.h2
-rw-r--r--src/core/loader/loader.h35
-rw-r--r--src/core/loader/ncch.cpp371
-rw-r--r--src/core/loader/ncch.h203
-rw-r--r--src/core/loader/nro.cpp12
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp12
-rw-r--r--src/core/loader/nso.h2
-rw-r--r--src/core/memory.cpp354
-rw-r--r--src/core/memory.h91
-rw-r--r--src/core/memory_setup.h10
-rw-r--r--src/core/settings.cpp2
-rw-r--r--src/core/settings.h11
-rw-r--r--src/core/telemetry_session.cpp86
-rw-r--r--src/core/telemetry_session.h22
116 files changed, 3915 insertions, 1713 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 33ce8dc81..8b25eaf0a 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -6,6 +6,8 @@ set(SRCS
6 arm/dyncom/arm_dyncom_interpreter.cpp 6 arm/dyncom/arm_dyncom_interpreter.cpp
7 arm/dyncom/arm_dyncom_thumb.cpp 7 arm/dyncom/arm_dyncom_thumb.cpp
8 arm/dyncom/arm_dyncom_trans.cpp 8 arm/dyncom/arm_dyncom_trans.cpp
9 arm/unicorn/arm_unicorn.cpp
10 arm/unicorn/unicorn_dynload.c
9 arm/skyeye_common/armstate.cpp 11 arm/skyeye_common/armstate.cpp
10 arm/skyeye_common/armsupp.cpp 12 arm/skyeye_common/armsupp.cpp
11 arm/skyeye_common/vfp/vfp.cpp 13 arm/skyeye_common/vfp/vfp.cpp
@@ -26,14 +28,15 @@ set(SRCS
26 file_sys/archive_systemsavedata.cpp 28 file_sys/archive_systemsavedata.cpp
27 file_sys/disk_archive.cpp 29 file_sys/disk_archive.cpp
28 file_sys/ivfc_archive.cpp 30 file_sys/ivfc_archive.cpp
31 file_sys/ncch_container.cpp
29 file_sys/path_parser.cpp 32 file_sys/path_parser.cpp
30 file_sys/savedata_archive.cpp 33 file_sys/savedata_archive.cpp
34 file_sys/title_metadata.cpp
31 frontend/camera/blank_camera.cpp 35 frontend/camera/blank_camera.cpp
32 frontend/camera/factory.cpp 36 frontend/camera/factory.cpp
33 frontend/camera/interface.cpp 37 frontend/camera/interface.cpp
34 frontend/emu_window.cpp 38 frontend/emu_window.cpp
35 frontend/framebuffer_layout.cpp 39 frontend/framebuffer_layout.cpp
36 frontend/motion_emu.cpp
37 gdbstub/gdbstub.cpp 40 gdbstub/gdbstub.cpp
38 hle/config_mem.cpp 41 hle/config_mem.cpp
39 hle/applets/applet.cpp 42 hle/applets/applet.cpp
@@ -60,6 +63,7 @@ set(SRCS
60 hle/kernel/timer.cpp 63 hle/kernel/timer.cpp
61 hle/kernel/vm_manager.cpp 64 hle/kernel/vm_manager.cpp
62 hle/kernel/wait_object.cpp 65 hle/kernel/wait_object.cpp
66 hle/lock.cpp
63 hle/romfs.cpp 67 hle/romfs.cpp
64 hle/service/ac/ac.cpp 68 hle/service/ac/ac.cpp
65 hle/service/ac/ac_i.cpp 69 hle/service/ac/ac_i.cpp
@@ -135,7 +139,8 @@ set(SRCS
135 hle/service/nim/nim_aoc.cpp 139 hle/service/nim/nim_aoc.cpp
136 hle/service/nim/nim_s.cpp 140 hle/service/nim/nim_s.cpp
137 hle/service/nim/nim_u.cpp 141 hle/service/nim/nim_u.cpp
138 hle/service/ns_s.cpp 142 hle/service/ns/ns.cpp
143 hle/service/ns/ns_s.cpp
139 hle/service/nwm/nwm.cpp 144 hle/service/nwm/nwm.cpp
140 hle/service/nwm/nwm_cec.cpp 145 hle/service/nwm/nwm_cec.cpp
141 hle/service/nwm/nwm_ext.cpp 146 hle/service/nwm/nwm_ext.cpp
@@ -145,6 +150,7 @@ set(SRCS
145 hle/service/nwm/nwm_tst.cpp 150 hle/service/nwm/nwm_tst.cpp
146 hle/service/nwm/nwm_uds.cpp 151 hle/service/nwm/nwm_uds.cpp
147 hle/service/nwm/uds_beacon.cpp 152 hle/service/nwm/uds_beacon.cpp
153 hle/service/nwm/uds_connection.cpp
148 hle/service/nwm/uds_data.cpp 154 hle/service/nwm/uds_data.cpp
149 hle/service/pm_app.cpp 155 hle/service/pm_app.cpp
150 hle/service/ptm/ptm.cpp 156 hle/service/ptm/ptm.cpp
@@ -198,6 +204,8 @@ set(HEADERS
198 arm/dyncom/arm_dyncom_run.h 204 arm/dyncom/arm_dyncom_run.h
199 arm/dyncom/arm_dyncom_thumb.h 205 arm/dyncom/arm_dyncom_thumb.h
200 arm/dyncom/arm_dyncom_trans.h 206 arm/dyncom/arm_dyncom_trans.h
207 arm/unicorn/arm_unicorn.h
208 arm/unicorn/unicorn_dynload.h
201 arm/skyeye_common/arm_regformat.h 209 arm/skyeye_common/arm_regformat.h
202 arm/skyeye_common/armstate.h 210 arm/skyeye_common/armstate.h
203 arm/skyeye_common/armsupp.h 211 arm/skyeye_common/armsupp.h
@@ -229,7 +237,6 @@ set(HEADERS
229 frontend/emu_window.h 237 frontend/emu_window.h
230 frontend/framebuffer_layout.h 238 frontend/framebuffer_layout.h
231 frontend/input.h 239 frontend/input.h
232 frontend/motion_emu.h
233 gdbstub/gdbstub.h 240 gdbstub/gdbstub.h
234 hle/config_mem.h 241 hle/config_mem.h
235 hle/function_wrappers.h 242 hle/function_wrappers.h
@@ -261,6 +268,7 @@ set(HEADERS
261 hle/kernel/timer.h 268 hle/kernel/timer.h
262 hle/kernel/vm_manager.h 269 hle/kernel/vm_manager.h
263 hle/kernel/wait_object.h 270 hle/kernel/wait_object.h
271 hle/lock.h
264 hle/result.h 272 hle/result.h
265 hle/romfs.h 273 hle/romfs.h
266 hle/service/ac/ac.h 274 hle/service/ac/ac.h
@@ -337,7 +345,8 @@ set(HEADERS
337 hle/service/nim/nim_aoc.h 345 hle/service/nim/nim_aoc.h
338 hle/service/nim/nim_s.h 346 hle/service/nim/nim_s.h
339 hle/service/nim/nim_u.h 347 hle/service/nim/nim_u.h
340 hle/service/ns_s.h 348 hle/service/ns/ns.h
349 hle/service/ns/ns_s.h
341 hle/service/nwm/nwm.h 350 hle/service/nwm/nwm.h
342 hle/service/nwm/nwm_cec.h 351 hle/service/nwm/nwm_cec.h
343 hle/service/nwm/nwm_ext.h 352 hle/service/nwm/nwm_ext.h
@@ -347,6 +356,7 @@ set(HEADERS
347 hle/service/nwm/nwm_tst.h 356 hle/service/nwm/nwm_tst.h
348 hle/service/nwm/nwm_uds.h 357 hle/service/nwm/nwm_uds.h
349 hle/service/nwm/uds_beacon.h 358 hle/service/nwm/uds_beacon.h
359 hle/service/nwm/uds_connection.h
350 hle/service/nwm/uds_data.h 360 hle/service/nwm/uds_data.h
351 hle/service/pm_app.h 361 hle/service/pm_app.h
352 hle/service/ptm/ptm.h 362 hle/service/ptm/ptm.h
@@ -394,7 +404,7 @@ set(HEADERS
394 404
395create_directory_groups(${SRCS} ${HEADERS}) 405create_directory_groups(${SRCS} ${HEADERS})
396add_library(core STATIC ${SRCS} ${HEADERS}) 406add_library(core STATIC ${SRCS} ${HEADERS})
397target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 407target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
398target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt lz4_static) 408target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt lz4_static)
399if (ENABLE_WEB_SERVICE) 409if (ENABLE_WEB_SERVICE)
400 target_link_libraries(core PUBLIC json-headers web_service) 410 target_link_libraries(core PUBLIC json-headers web_service)
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index f613556dd..0b3096347 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hle/kernel/vm_manager.h"
8#include "core/arm/skyeye_common/arm_regformat.h" 9#include "core/arm/skyeye_common/arm_regformat.h"
9#include "core/arm/skyeye_common/vfp/asm_vfp.h" 10#include "core/arm/skyeye_common/vfp/asm_vfp.h"
10 11
@@ -19,10 +20,11 @@ public:
19 u64 sp; 20 u64 sp;
20 u64 pc; 21 u64 pc;
21 u64 cpsr; 22 u64 cpsr;
22 u64 fpu_registers[64]; 23 u128 fpu_registers[32];
23 u64 fpscr; 24 u64 fpscr;
24 u64 fpexc; 25 u64 fpexc;
25 26
27
26 // TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT 28 // TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT
27 VAddr tls_address; 29 VAddr tls_address;
28 }; 30 };
@@ -41,9 +43,14 @@ public:
41 Run(1); 43 Run(1);
42 } 44 }
43 45
46 virtual void MapBackingMemory(VAddr address, size_t size, u8* memory, Kernel::VMAPermission perms) {}
47
44 /// Clear all instruction cache 48 /// Clear all instruction cache
45 virtual void ClearInstructionCache() = 0; 49 virtual void ClearInstructionCache() = 0;
46 50
51 /// Notify CPU emulation that page tables have changed
52 virtual void PageTableChanged() = 0;
53
47 /** 54 /**
48 * Set the Program Counter to an address 55 * Set the Program Counter to an address
49 * @param addr Address to set PC to 56 * @param addr Address to set PC to
@@ -70,6 +77,10 @@ public:
70 */ 77 */
71 virtual void SetReg(int index, u64 value) = 0; 78 virtual void SetReg(int index, u64 value) = 0;
72 79
80 virtual const u128& GetExtReg(int index) const = 0;
81
82 virtual void SetExtReg(int index, u128& value) = 0;
83
73 /** 84 /**
74 * Gets the value of a VFP register 85 * Gets the value of a VFP register
75 * @param index Register index (0-31) 86 * @param index Register index (0-31)
@@ -129,12 +140,6 @@ public:
129 virtual void SetTlsAddress(VAddr address) = 0; 140 virtual void SetTlsAddress(VAddr address) = 0;
130 141
131 /** 142 /**
132 * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
133 * @param ticks Number of ticks to advance the CPU core
134 */
135 virtual void AddTicks(u64 ticks) = 0;
136
137 /**
138 * Saves the current CPU context 143 * Saves the current CPU context
139 * @param ctx Thread context to save 144 * @param ctx Thread context to save
140 */ 145 */
@@ -154,9 +159,6 @@ public:
154 return num_instructions; 159 return num_instructions;
155 } 160 }
156 161
157 s64 down_count = 0; ///< A decreasing counter of remaining cycles before the next event,
158 /// decreased by the cpu run loop
159
160protected: 162protected:
161 /** 163 /**
162 * Executes the given number of instructions 164 * Executes the given number of instructions
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 0ea1d76e4..6dcab5bab 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -16,24 +16,6 @@
16 16
17static void InterpreterFallback(u64 pc, Dynarmic::Jit* jit, void* user_arg) { 17static void InterpreterFallback(u64 pc, Dynarmic::Jit* jit, void* user_arg) {
18 UNIMPLEMENTED_MSG("InterpreterFallback for ARM64 JIT does not exist!"); 18 UNIMPLEMENTED_MSG("InterpreterFallback for ARM64 JIT does not exist!");
19 //ARMul_State* state = static_cast<ARMul_State*>(user_arg);
20
21 //state->Reg = jit->Regs();
22 //state->Cpsr = jit->Cpsr();
23 //state->Reg[15] = static_cast<u32>(pc);
24 //state->ExtReg = jit->ExtRegs();
25 //state->VFP[VFP_FPSCR] = jit->Fpscr();
26 //state->NumInstrsToExecute = 1;
27
28 //InterpreterMainLoop(state);
29
30 //bool is_thumb = (state->Cpsr & (1 << 5)) != 0;
31 //state->Reg[15] &= (is_thumb ? 0xFFFFFFFE : 0xFFFFFFFC);
32
33 //jit->Regs() = state->Reg;
34 //jit->Cpsr() = state->Cpsr;
35 //jit->ExtRegs() = state->ExtReg;
36 //jit->SetFpscr(state->VFP[VFP_FPSCR]);
37} 19}
38 20
39static bool IsReadOnlyMemory(u64 vaddr) { 21static bool IsReadOnlyMemory(u64 vaddr) {
@@ -73,11 +55,10 @@ void MemoryWrite64(const u64 addr, const u64 data) {
73 Memory::Write64(static_cast<VAddr>(addr), data); 55 Memory::Write64(static_cast<VAddr>(addr), data);
74} 56}
75 57
76static Dynarmic::UserCallbacks GetUserCallbacks( 58static Dynarmic::UserCallbacks GetUserCallbacks(ARM_Dynarmic* this_) {
77 const std::shared_ptr<ARMul_State>& interpeter_state) {
78 Dynarmic::UserCallbacks user_callbacks{}; 59 Dynarmic::UserCallbacks user_callbacks{};
79 //user_callbacks.InterpreterFallback = &InterpreterFallback; 60 user_callbacks.InterpreterFallback = &InterpreterFallback;
80 //user_callbacks.user_arg = static_cast<void*>(interpeter_state.get()); 61 user_callbacks.user_arg = static_cast<void*>(this_);
81 user_callbacks.CallSVC = &SVC::CallSVC; 62 user_callbacks.CallSVC = &SVC::CallSVC;
82 user_callbacks.memory.IsReadOnlyMemory = &IsReadOnlyMemory; 63 user_callbacks.memory.IsReadOnlyMemory = &IsReadOnlyMemory;
83 user_callbacks.memory.ReadCode = &MemoryRead32; 64 user_callbacks.memory.ReadCode = &MemoryRead32;
@@ -90,13 +71,13 @@ static Dynarmic::UserCallbacks GetUserCallbacks(
90 user_callbacks.memory.Write32 = &MemoryWrite32; 71 user_callbacks.memory.Write32 = &MemoryWrite32;
91 user_callbacks.memory.Write64 = &MemoryWrite64; 72 user_callbacks.memory.Write64 = &MemoryWrite64;
92 //user_callbacks.page_table = Memory::GetCurrentPageTablePointers(); 73 //user_callbacks.page_table = Memory::GetCurrentPageTablePointers();
93 user_callbacks.coprocessors[15] = std::make_shared<DynarmicCP15>(interpeter_state);
94 return user_callbacks; 74 return user_callbacks;
95} 75}
96 76
97ARM_Dynarmic::ARM_Dynarmic(PrivilegeMode initial_mode) { 77ARM_Dynarmic::ARM_Dynarmic(PrivilegeMode initial_mode) {
98 interpreter_state = std::make_shared<ARMul_State>(initial_mode); 78}
99 jit = std::make_unique<Dynarmic::Jit>(GetUserCallbacks(interpreter_state), Dynarmic::Arch::ARM64); 79
80void ARM_Dynarmic::MapBackingMemory(VAddr address, size_t size, u8* memory, Kernel::VMAPermission perms) {
100} 81}
101 82
102void ARM_Dynarmic::SetPC(u64 pc) { 83void ARM_Dynarmic::SetPC(u64 pc) {
@@ -115,30 +96,26 @@ void ARM_Dynarmic::SetReg(int index, u64 value) {
115 jit->Regs64()[index] = value; 96 jit->Regs64()[index] = value;
116} 97}
117 98
99const u128& ARM_Dynarmic::GetExtReg(int index) const {
100 return jit->ExtRegs64()[index];
101}
102
103void ARM_Dynarmic::SetExtReg(int index, u128& value) {
104 jit->ExtRegs64()[index] = value;
105}
106
118u32 ARM_Dynarmic::GetVFPReg(int index) const { 107u32 ARM_Dynarmic::GetVFPReg(int index) const {
119 return jit->ExtRegs()[index]; 108 return {};
120} 109}
121 110
122void ARM_Dynarmic::SetVFPReg(int index, u32 value) { 111void ARM_Dynarmic::SetVFPReg(int index, u32 value) {
123 jit->ExtRegs()[index] = value;
124} 112}
125 113
126u32 ARM_Dynarmic::GetVFPSystemReg(VFPSystemRegister reg) const { 114u32 ARM_Dynarmic::GetVFPSystemReg(VFPSystemRegister reg) const {
127 if (reg == VFP_FPSCR) { 115 return {};
128 return jit->Fpscr();
129 }
130
131 // Dynarmic does not implement and/or expose other VFP registers, fallback to interpreter state
132 return interpreter_state->VFP[reg];
133} 116}
134 117
135void ARM_Dynarmic::SetVFPSystemReg(VFPSystemRegister reg, u32 value) { 118void ARM_Dynarmic::SetVFPSystemReg(VFPSystemRegister reg, u32 value) {
136 if (reg == VFP_FPSCR) {
137 jit->SetFpscr(value);
138 }
139
140 // Dynarmic does not implement and/or expose other VFP registers, fallback to interpreter state
141 interpreter_state->VFP[reg] = value;
142} 119}
143 120
144u32 ARM_Dynarmic::GetCPSR() const { 121u32 ARM_Dynarmic::GetCPSR() const {
@@ -150,11 +127,10 @@ void ARM_Dynarmic::SetCPSR(u32 cpsr) {
150} 127}
151 128
152u32 ARM_Dynarmic::GetCP15Register(CP15Register reg) { 129u32 ARM_Dynarmic::GetCP15Register(CP15Register reg) {
153 return interpreter_state->CP15[reg]; 130 return {};
154} 131}
155 132
156void ARM_Dynarmic::SetCP15Register(CP15Register reg, u32 value) { 133void ARM_Dynarmic::SetCP15Register(CP15Register reg, u32 value) {
157 interpreter_state->CP15[reg] = value;
158} 134}
159 135
160VAddr ARM_Dynarmic::GetTlsAddress() const { 136VAddr ARM_Dynarmic::GetTlsAddress() const {
@@ -165,51 +141,39 @@ void ARM_Dynarmic::SetTlsAddress(VAddr address) {
165 jit->TlsAddr() = address; 141 jit->TlsAddr() = address;
166} 142}
167 143
168void ARM_Dynarmic::AddTicks(u64 ticks) {
169 down_count -= ticks;
170 if (down_count < 0) {
171 CoreTiming::Advance();
172 }
173}
174
175MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); 144MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
176 145
177void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { 146void ARM_Dynarmic::ExecuteInstructions(int num_instructions) {
147 ASSERT(Memory::GetCurrentPageTable() == current_page_table);
178 MICROPROFILE_SCOPE(ARM_Jit); 148 MICROPROFILE_SCOPE(ARM_Jit);
179 149
180 unsigned ticks_executed = jit->Run(1 /*static_cast<unsigned>(num_instructions)*/); 150 std::size_t ticks_executed = jit->Run(static_cast<unsigned>(num_instructions));
181 151
182 AddTicks(ticks_executed); 152 CoreTiming::AddTicks(ticks_executed);
183} 153}
184 154
185void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { 155void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
186 memcpy(ctx.cpu_registers, jit->Regs64().data(), sizeof(ctx.cpu_registers)); 156 memcpy(ctx.cpu_registers, jit->Regs64().data(), sizeof(ctx.cpu_registers));
187 //memcpy(ctx.fpu_registers, jit->ExtRegs().data(), sizeof(ctx.fpu_registers)); 157 memcpy(ctx.fpu_registers, jit->ExtRegs64().data(), sizeof(ctx.fpu_registers));
188 158
189 ctx.lr = jit->Regs64()[30]; 159 ctx.lr = jit->Regs64()[30];
190 ctx.sp = jit->Regs64()[31]; 160 ctx.sp = jit->Regs64()[31];
191 ctx.pc = jit->Regs64()[32]; 161 ctx.pc = jit->Regs64()[32];
192 ctx.cpsr = jit->Cpsr(); 162 ctx.cpsr = jit->Cpsr();
193 163
194 ctx.fpscr = jit->Fpscr();
195 ctx.fpexc = interpreter_state->VFP[VFP_FPEXC];
196
197 // TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT 164 // TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT
198 ctx.tls_address = jit->TlsAddr(); 165 ctx.tls_address = jit->TlsAddr();
199} 166}
200 167
201void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) { 168void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) {
202 memcpy(jit->Regs64().data(), ctx.cpu_registers, sizeof(ctx.cpu_registers)); 169 memcpy(jit->Regs64().data(), ctx.cpu_registers, sizeof(ctx.cpu_registers));
203 //memcpy(jit->ExtRegs().data(), ctx.fpu_registers, sizeof(ctx.fpu_registers)); 170 memcpy(jit->ExtRegs64().data(), ctx.fpu_registers, sizeof(ctx.fpu_registers));
204 171
205 jit->Regs64()[30] = ctx.lr; 172 jit->Regs64()[30] = ctx.lr;
206 jit->Regs64()[31] = ctx.sp; 173 jit->Regs64()[31] = ctx.sp;
207 jit->Regs64()[32] = ctx.pc; 174 jit->Regs64()[32] = ctx.pc;
208 jit->Cpsr() = ctx.cpsr; 175 jit->Cpsr() = ctx.cpsr;
209 176
210 jit->SetFpscr(ctx.fpscr);
211 interpreter_state->VFP[VFP_FPEXC] = ctx.fpexc;
212
213 // TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT 177 // TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT
214 jit->TlsAddr() = ctx.tls_address; 178 jit->TlsAddr() = ctx.tls_address;
215} 179}
@@ -223,3 +187,16 @@ void ARM_Dynarmic::PrepareReschedule() {
223void ARM_Dynarmic::ClearInstructionCache() { 187void ARM_Dynarmic::ClearInstructionCache() {
224 jit->ClearCache(); 188 jit->ClearCache();
225} 189}
190
191void ARM_Dynarmic::PageTableChanged() {
192 current_page_table = Memory::GetCurrentPageTable();
193
194 auto iter = jits.find(current_page_table);
195 if (iter != jits.end()) {
196 jit = iter->second.get();
197 return;
198 }
199
200 jit = new Dynarmic::Jit(GetUserCallbacks(this), Dynarmic::Arch::ARM64);
201 jits.emplace(current_page_table, std::unique_ptr<Dynarmic::Jit>(jit));
202}
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index fcdc1c0e0..6567359b0 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -4,20 +4,29 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <map>
7#include <memory> 8#include <memory>
8#include <dynarmic/dynarmic.h> 9#include <dynarmic/dynarmic.h>
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "core/arm/arm_interface.h" 11#include "core/arm/arm_interface.h"
11#include "core/arm/skyeye_common/armstate.h" 12#include "core/arm/skyeye_common/armstate.h"
12 13
14namespace Memory {
15struct PageTable;
16} // namespace Memory
17
13class ARM_Dynarmic final : public ARM_Interface { 18class ARM_Dynarmic final : public ARM_Interface {
14public: 19public:
15 ARM_Dynarmic(PrivilegeMode initial_mode); 20 ARM_Dynarmic(PrivilegeMode initial_mode);
16 21
22 void MapBackingMemory(VAddr address, size_t size, u8* memory, Kernel::VMAPermission perms) override;
23
17 void SetPC(u64 pc) override; 24 void SetPC(u64 pc) override;
18 u64 GetPC() const override; 25 u64 GetPC() const override;
19 u64 GetReg(int index) const override; 26 u64 GetReg(int index) const override;
20 void SetReg(int index, u64 value) override; 27 void SetReg(int index, u64 value) override;
28 const u128& GetExtReg(int index) const override;
29 void SetExtReg(int index, u128& value) override;
21 u32 GetVFPReg(int index) const override; 30 u32 GetVFPReg(int index) const override;
22 void SetVFPReg(int index, u32 value) override; 31 void SetVFPReg(int index, u32 value) override;
23 u32 GetVFPSystemReg(VFPSystemRegister reg) const override; 32 u32 GetVFPSystemReg(VFPSystemRegister reg) const override;
@@ -29,8 +38,6 @@ public:
29 VAddr GetTlsAddress() const override; 38 VAddr GetTlsAddress() const override;
30 void SetTlsAddress(VAddr address) override; 39 void SetTlsAddress(VAddr address) override;
31 40
32 void AddTicks(u64 ticks) override;
33
34 void SaveContext(ThreadContext& ctx) override; 41 void SaveContext(ThreadContext& ctx) override;
35 void LoadContext(const ThreadContext& ctx) override; 42 void LoadContext(const ThreadContext& ctx) override;
36 43
@@ -38,8 +45,10 @@ public:
38 void ExecuteInstructions(int num_instructions) override; 45 void ExecuteInstructions(int num_instructions) override;
39 46
40 void ClearInstructionCache() override; 47 void ClearInstructionCache() override;
48 void PageTableChanged() override;
41 49
42private: 50private:
43 std::unique_ptr<Dynarmic::Jit> jit; 51 Dynarmic::Jit* jit = nullptr;
44 std::shared_ptr<ARMul_State> interpreter_state; 52 Memory::PageTable* current_page_table = nullptr;
53 std::map<Memory::PageTable*, std::unique_ptr<Dynarmic::Jit>> jits;
45}; 54};
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index 99758fc2a..5ebf7a2f1 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -29,6 +29,10 @@ void ARM_DynCom::SetPC(u64 pc) {
29 state->Reg[15] = pc; 29 state->Reg[15] = pc;
30} 30}
31 31
32void ARM_DynCom::PageTableChanged() {
33 ClearInstructionCache();
34}
35
32u64 ARM_DynCom::GetPC() const { 36u64 ARM_DynCom::GetPC() const {
33 return state->Reg[15]; 37 return state->Reg[15];
34} 38}
@@ -41,6 +45,13 @@ void ARM_DynCom::SetReg(int index, u64 value) {
41 state->Reg[index] = value; 45 state->Reg[index] = value;
42} 46}
43 47
48const u128& ARM_DynCom::GetExtReg(int index) const {
49 return {};
50}
51
52void ARM_DynCom::SetExtReg(int index, u128& value) {
53}
54
44u32 ARM_DynCom::GetVFPReg(int index) const { 55u32 ARM_DynCom::GetVFPReg(int index) const {
45 return state->ExtReg[index]; 56 return state->ExtReg[index];
46} 57}
@@ -80,12 +91,6 @@ VAddr ARM_DynCom::GetTlsAddress() const {
80void ARM_DynCom::SetTlsAddress(VAddr /*address*/) { 91void ARM_DynCom::SetTlsAddress(VAddr /*address*/) {
81} 92}
82 93
83void ARM_DynCom::AddTicks(u64 ticks) {
84 down_count -= ticks;
85 if (down_count < 0)
86 CoreTiming::Advance();
87}
88
89void ARM_DynCom::ExecuteInstructions(int num_instructions) { 94void ARM_DynCom::ExecuteInstructions(int num_instructions) {
90 state->NumInstrsToExecute = num_instructions; 95 state->NumInstrsToExecute = num_instructions;
91 96
@@ -93,7 +98,7 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) {
93 // executing one instruction at a time. Otherwise, if a block is being executed, more 98 // executing one instruction at a time. Otherwise, if a block is being executed, more
94 // instructions may actually be executed than specified. 99 // instructions may actually be executed than specified.
95 unsigned ticks_executed = InterpreterMainLoop(state.get()); 100 unsigned ticks_executed = InterpreterMainLoop(state.get());
96 AddTicks(ticks_executed); 101 CoreTiming::AddTicks(ticks_executed);
97} 102}
98 103
99void ARM_DynCom::SaveContext(ThreadContext& ctx) { 104void ARM_DynCom::SaveContext(ThreadContext& ctx) {
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h
index 44e674ae2..cc3c0f3da 100644
--- a/src/core/arm/dyncom/arm_dyncom.h
+++ b/src/core/arm/dyncom/arm_dyncom.h
@@ -16,11 +16,14 @@ public:
16 ~ARM_DynCom(); 16 ~ARM_DynCom();
17 17
18 void ClearInstructionCache() override; 18 void ClearInstructionCache() override;
19 void PageTableChanged() override;
19 20
20 void SetPC(u64 pc) override; 21 void SetPC(u64 pc) override;
21 u64 GetPC() const override; 22 u64 GetPC() const override;
22 u64 GetReg(int index) const override; 23 u64 GetReg(int index) const override;
23 void SetReg(int index, u64 value) override; 24 void SetReg(int index, u64 value) override;
25 const u128& GetExtReg(int index) const override;
26 void SetExtReg(int index, u128& value) override;
24 u32 GetVFPReg(int index) const override; 27 u32 GetVFPReg(int index) const override;
25 void SetVFPReg(int index, u32 value) override; 28 void SetVFPReg(int index, u32 value) override;
26 u32 GetVFPSystemReg(VFPSystemRegister reg) const override; 29 u32 GetVFPSystemReg(VFPSystemRegister reg) const override;
@@ -32,8 +35,6 @@ public:
32 VAddr GetTlsAddress() const override; 35 VAddr GetTlsAddress() const override;
33 void SetTlsAddress(VAddr address) override; 36 void SetTlsAddress(VAddr address) override;
34 37
35 void AddTicks(u64 ticks) override;
36
37 void SaveContext(ThreadContext& ctx) override; 38 void SaveContext(ThreadContext& ctx) override;
38 void LoadContext(const ThreadContext& ctx) override; 39 void LoadContext(const ThreadContext& ctx) override;
39 40
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index f4fbb8d04..3522d1e82 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -759,7 +759,7 @@ static ThumbDecodeStatus DecodeThumbInstruction(u32 inst, u32 addr, u32* arm_ins
759 ThumbDecodeStatus ret = TranslateThumbInstruction(addr, inst, arm_inst, inst_size); 759 ThumbDecodeStatus ret = TranslateThumbInstruction(addr, inst, arm_inst, inst_size);
760 if (ret == ThumbDecodeStatus::BRANCH) { 760 if (ret == ThumbDecodeStatus::BRANCH) {
761 int inst_index; 761 int inst_index;
762 int table_length = arm_instruction_trans_len; 762 int table_length = static_cast<int>(arm_instruction_trans_len);
763 u32 tinstr = GetThumbInstruction(inst, addr); 763 u32 tinstr = GetThumbInstruction(inst, addr);
764 764
765 switch ((tinstr & 0xF800) >> 11) { 765 switch ((tinstr & 0xF800) >> 11) {
@@ -838,7 +838,7 @@ static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, cons
838 return inst_size; 838 return inst_size;
839} 839}
840 840
841static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) { 841static int InterpreterTranslateBlock(ARMul_State* cpu, std::size_t& bb_start, u32 addr) {
842 MICROPROFILE_SCOPE(DynCom_Decode); 842 MICROPROFILE_SCOPE(DynCom_Decode);
843 843
844 // Decode instruction, get index 844 // Decode instruction, get index
@@ -871,7 +871,7 @@ static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr)
871 return KEEP_GOING; 871 return KEEP_GOING;
872} 872}
873 873
874static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) { 874static int InterpreterTranslateSingle(ARMul_State* cpu, std::size_t& bb_start, u32 addr) {
875 MICROPROFILE_SCOPE(DynCom_Decode); 875 MICROPROFILE_SCOPE(DynCom_Decode);
876 876
877 ARM_INST_PTR inst_base = nullptr; 877 ARM_INST_PTR inst_base = nullptr;
@@ -1620,7 +1620,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
1620 unsigned int addr; 1620 unsigned int addr;
1621 unsigned int num_instrs = 0; 1621 unsigned int num_instrs = 0;
1622 1622
1623 int ptr; 1623 std::size_t ptr;
1624 1624
1625 LOAD_NZCVT; 1625 LOAD_NZCVT;
1626DISPATCH : { 1626DISPATCH : {
diff --git a/src/core/arm/skyeye_common/armstate.h b/src/core/arm/skyeye_common/armstate.h
index 1a707ff7e..893877797 100644
--- a/src/core/arm/skyeye_common/armstate.h
+++ b/src/core/arm/skyeye_common/armstate.h
@@ -230,7 +230,7 @@ public:
230 230
231 // TODO(bunnei): Move this cache to a better place - it should be per codeset (likely per 231 // TODO(bunnei): Move this cache to a better place - it should be per codeset (likely per
232 // process for our purposes), not per ARMul_State (which tracks CPU core state). 232 // process for our purposes), not per ARMul_State (which tracks CPU core state).
233 std::unordered_map<u32, int> instruction_cache; 233 std::unordered_map<u32, std::size_t> instruction_cache;
234 234
235private: 235private:
236 void ResetMPCoreCP15Registers(); 236 void ResetMPCoreCP15Registers();
diff --git a/src/core/core.cpp b/src/core/core.cpp
index d08f18623..c5448630f 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -9,16 +9,19 @@
9#include "core/arm/arm_interface.h" 9#include "core/arm/arm_interface.h"
10#include "core/arm/dynarmic/arm_dynarmic.h" 10#include "core/arm/dynarmic/arm_dynarmic.h"
11#include "core/arm/dyncom/arm_dyncom.h" 11#include "core/arm/dyncom/arm_dyncom.h"
12#include "core/arm/unicorn/arm_unicorn.h"
12#include "core/core.h" 13#include "core/core.h"
13#include "core/core_timing.h" 14#include "core/core_timing.h"
14#include "core/gdbstub/gdbstub.h" 15#include "core/gdbstub/gdbstub.h"
15#include "core/hle/kernel/kernel.h" 16#include "core/hle/kernel/kernel.h"
17#include "core/hle/kernel/process.h"
16#include "core/hle/kernel/thread.h" 18#include "core/hle/kernel/thread.h"
17#include "core/hle/service/service.h" 19#include "core/hle/service/service.h"
18#include "core/hw/hw.h" 20#include "core/hw/hw.h"
19#include "core/loader/loader.h" 21#include "core/loader/loader.h"
20#include "core/memory_setup.h" 22#include "core/memory_setup.h"
21#include "core/settings.h" 23#include "core/settings.h"
24#include "network/network.h"
22#include "video_core/video_core.h" 25#include "video_core/video_core.h"
23 26
24namespace Core { 27namespace Core {
@@ -99,7 +102,7 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
99 return init_result; 102 return init_result;
100 } 103 }
101 104
102 const Loader::ResultStatus load_result{app_loader->Load()}; 105 const Loader::ResultStatus load_result{app_loader->Load(Kernel::g_current_process)};
103 if (Loader::ResultStatus::Success != load_result) { 106 if (Loader::ResultStatus::Success != load_result) {
104 LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result); 107 LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result);
105 System::Shutdown(); 108 System::Shutdown();
@@ -136,7 +139,6 @@ void System::Reschedule() {
136} 139}
137 140
138System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { 141System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
139 Memory::InitMemoryMap();
140 LOG_DEBUG(HW_Memory, "initialized OK"); 142 LOG_DEBUG(HW_Memory, "initialized OK");
141 143
142 if (Settings::values.use_cpu_jit) { 144 if (Settings::values.use_cpu_jit) {
@@ -188,8 +190,12 @@ void System::Shutdown() {
188 cpu_core = nullptr; 190 cpu_core = nullptr;
189 app_loader = nullptr; 191 app_loader = nullptr;
190 telemetry_session = nullptr; 192 telemetry_session = nullptr;
193 if (auto room_member = Network::GetRoomMember().lock()) {
194 Network::GameInfo game_info{};
195 room_member->SendGameInfo(game_info);
196 }
191 197
192 LOG_DEBUG(Core, "Shutdown OK"); 198 LOG_DEBUG(Core, "Shutdown OK");
193} 199}
194 200
195} // namespace 201} // namespace Core
diff --git a/src/core/core.h b/src/core/core.h
index 4e3b6b409..9805cc694 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/loader/loader.h"
10#include "core/memory.h" 11#include "core/memory.h"
11#include "core/perf_stats.h" 12#include "core/perf_stats.h"
12#include "core/telemetry_session.h" 13#include "core/telemetry_session.h"
@@ -14,10 +15,6 @@
14class EmuWindow; 15class EmuWindow;
15class ARM_Interface; 16class ARM_Interface;
16 17
17namespace Loader {
18class AppLoader;
19}
20
21namespace Core { 18namespace Core {
22 19
23class System { 20class System {
@@ -119,6 +116,10 @@ public:
119 return status_details; 116 return status_details;
120 } 117 }
121 118
119 Loader::AppLoader& GetAppLoader() const {
120 return *app_loader;
121 }
122
122private: 123private:
123 /** 124 /**
124 * Initialize the emulated system. 125 * Initialize the emulated system.
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 276ecfdf6..5e2a5d00f 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -57,6 +57,9 @@ static s64 idled_cycles;
57static s64 last_global_time_ticks; 57static s64 last_global_time_ticks;
58static s64 last_global_time_us; 58static s64 last_global_time_us;
59 59
60static s64 down_count = 0; ///< A decreasing counter of remaining cycles before the next event,
61 /// decreased by the cpu run loop
62
60static std::recursive_mutex external_event_section; 63static std::recursive_mutex external_event_section;
61 64
62// Warning: not included in save state. 65// Warning: not included in save state.
@@ -146,7 +149,7 @@ void UnregisterAllEvents() {
146} 149}
147 150
148void Init() { 151void Init() {
149 Core::CPU().down_count = INITIAL_SLICE_LENGTH; 152 down_count = INITIAL_SLICE_LENGTH;
150 g_slice_length = INITIAL_SLICE_LENGTH; 153 g_slice_length = INITIAL_SLICE_LENGTH;
151 global_timer = 0; 154 global_timer = 0;
152 idled_cycles = 0; 155 idled_cycles = 0;
@@ -185,8 +188,15 @@ void Shutdown() {
185 } 188 }
186} 189}
187 190
191void AddTicks(u64 ticks) {
192 down_count -= ticks;
193 if (down_count < 0) {
194 Advance();
195 }
196}
197
188u64 GetTicks() { 198u64 GetTicks() {
189 return (u64)global_timer + g_slice_length - Core::CPU().down_count; 199 return (u64)global_timer + g_slice_length - down_count;
190} 200}
191 201
192u64 GetIdleTicks() { 202u64 GetIdleTicks() {
@@ -460,18 +470,18 @@ void MoveEvents() {
460} 470}
461 471
462void ForceCheck() { 472void ForceCheck() {
463 s64 cycles_executed = g_slice_length - Core::CPU().down_count; 473 s64 cycles_executed = g_slice_length - down_count;
464 global_timer += cycles_executed; 474 global_timer += cycles_executed;
465 // This will cause us to check for new events immediately. 475 // This will cause us to check for new events immediately.
466 Core::CPU().down_count = 0; 476 down_count = 0;
467 // But let's not eat a bunch more time in Advance() because of this. 477 // But let's not eat a bunch more time in Advance() because of this.
468 g_slice_length = 0; 478 g_slice_length = 0;
469} 479}
470 480
471void Advance() { 481void Advance() {
472 s64 cycles_executed = g_slice_length - Core::CPU().down_count; 482 s64 cycles_executed = g_slice_length - down_count;
473 global_timer += cycles_executed; 483 global_timer += cycles_executed;
474 Core::CPU().down_count = g_slice_length; 484 down_count = g_slice_length;
475 485
476 if (has_ts_events) 486 if (has_ts_events)
477 MoveEvents(); 487 MoveEvents();
@@ -480,7 +490,7 @@ void Advance() {
480 if (!first) { 490 if (!first) {
481 if (g_slice_length < 10000) { 491 if (g_slice_length < 10000) {
482 g_slice_length += 10000; 492 g_slice_length += 10000;
483 Core::CPU().down_count += g_slice_length; 493 down_count += g_slice_length;
484 } 494 }
485 } else { 495 } else {
486 // Note that events can eat cycles as well. 496 // Note that events can eat cycles as well.
@@ -490,7 +500,7 @@ void Advance() {
490 500
491 const int diff = target - g_slice_length; 501 const int diff = target - g_slice_length;
492 g_slice_length += diff; 502 g_slice_length += diff;
493 Core::CPU().down_count += diff; 503 down_count += diff;
494 } 504 }
495 if (advance_callback) 505 if (advance_callback)
496 advance_callback(static_cast<int>(cycles_executed)); 506 advance_callback(static_cast<int>(cycles_executed));
@@ -506,12 +516,12 @@ void LogPendingEvents() {
506} 516}
507 517
508void Idle(int max_idle) { 518void Idle(int max_idle) {
509 s64 cycles_down = Core::CPU().down_count; 519 s64 cycles_down = down_count;
510 if (max_idle != 0 && cycles_down > max_idle) 520 if (max_idle != 0 && cycles_down > max_idle)
511 cycles_down = max_idle; 521 cycles_down = max_idle;
512 522
513 if (first && cycles_down > 0) { 523 if (first && cycles_down > 0) {
514 s64 cycles_executed = g_slice_length - Core::CPU().down_count; 524 s64 cycles_executed = g_slice_length - down_count;
515 s64 cycles_next_event = first->time - global_timer; 525 s64 cycles_next_event = first->time - global_timer;
516 526
517 if (cycles_next_event < cycles_executed + cycles_down) { 527 if (cycles_next_event < cycles_executed + cycles_down) {
@@ -526,9 +536,9 @@ void Idle(int max_idle) {
526 cycles_down / (float)(g_clock_rate_arm11 * 0.001f)); 536 cycles_down / (float)(g_clock_rate_arm11 * 0.001f));
527 537
528 idled_cycles += cycles_down; 538 idled_cycles += cycles_down;
529 Core::CPU().down_count -= cycles_down; 539 down_count -= cycles_down;
530 if (Core::CPU().down_count == 0) 540 if (down_count == 0)
531 Core::CPU().down_count = -1; 541 down_count = -1;
532} 542}
533 543
534std::string GetScheduledEventsSummary() { 544std::string GetScheduledEventsSummary() {
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index d2f85cd4d..897350801 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -67,6 +67,12 @@ void Shutdown();
67typedef void (*MHzChangeCallback)(); 67typedef void (*MHzChangeCallback)();
68typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback; 68typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback;
69 69
70/**
71* Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
72* @param ticks Number of ticks to advance the CPU core
73*/
74void AddTicks(u64 ticks);
75
70u64 GetTicks(); 76u64 GetTicks();
71u64 GetIdleTicks(); 77u64 GetIdleTicks();
72u64 GetGlobalTimeUs(); 78u64 GetGlobalTimeUs();
diff --git a/src/core/file_sys/archive_backend.cpp b/src/core/file_sys/archive_backend.cpp
index 1fae0ede0..87a240d7a 100644
--- a/src/core/file_sys/archive_backend.cpp
+++ b/src/core/file_sys/archive_backend.cpp
@@ -90,6 +90,8 @@ std::u16string Path::AsU16Str() const {
90 LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!"); 90 LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!");
91 return {}; 91 return {};
92 } 92 }
93
94 UNREACHABLE();
93} 95}
94 96
95std::vector<u8> Path::AsBinary() const { 97std::vector<u8> Path::AsBinary() const {
diff --git a/src/core/file_sys/archive_ncch.cpp b/src/core/file_sys/archive_ncch.cpp
index 6d9007731..e8c5be983 100644
--- a/src/core/file_sys/archive_ncch.cpp
+++ b/src/core/file_sys/archive_ncch.cpp
@@ -13,7 +13,10 @@
13#include "core/file_sys/archive_ncch.h" 13#include "core/file_sys/archive_ncch.h"
14#include "core/file_sys/errors.h" 14#include "core/file_sys/errors.h"
15#include "core/file_sys/ivfc_archive.h" 15#include "core/file_sys/ivfc_archive.h"
16#include "core/file_sys/ncch_container.h"
17#include "core/file_sys/title_metadata.h"
16#include "core/hle/service/fs/archive.h" 18#include "core/hle/service/fs/archive.h"
19#include "core/loader/loader.h"
17 20
18//////////////////////////////////////////////////////////////////////////////////////////////////// 21////////////////////////////////////////////////////////////////////////////////////////////////////
19// FileSys namespace 22// FileSys namespace
@@ -25,8 +28,18 @@ static std::string GetNCCHContainerPath(const std::string& nand_directory) {
25} 28}
26 29
27static std::string GetNCCHPath(const std::string& mount_point, u32 high, u32 low) { 30static std::string GetNCCHPath(const std::string& mount_point, u32 high, u32 low) {
28 return Common::StringFromFormat("%s%08x/%08x/content/00000000.app.romfs", mount_point.c_str(), 31 u32 content_id = 0;
29 high, low); 32
33 // TODO(shinyquagsire23): Title database should be doing this path lookup
34 std::string content_path =
35 Common::StringFromFormat("%s%08x/%08x/content/", mount_point.c_str(), high, low);
36 std::string tmd_path = content_path + "00000000.tmd";
37 TitleMetadata tmd(tmd_path);
38 if (tmd.Load() == Loader::ResultStatus::Success) {
39 content_id = tmd.GetBootContentID();
40 }
41
42 return Common::StringFromFormat("%s%08x.app", content_path.c_str(), content_id);
30} 43}
31 44
32ArchiveFactory_NCCH::ArchiveFactory_NCCH(const std::string& nand_directory) 45ArchiveFactory_NCCH::ArchiveFactory_NCCH(const std::string& nand_directory)
@@ -38,9 +51,14 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_NCCH::Open(const Path&
38 u32 high = data[1]; 51 u32 high = data[1];
39 u32 low = data[0]; 52 u32 low = data[0];
40 std::string file_path = GetNCCHPath(mount_point, high, low); 53 std::string file_path = GetNCCHPath(mount_point, high, low);
41 auto file = std::make_shared<FileUtil::IOFile>(file_path, "rb");
42 54
43 if (!file->IsOpen()) { 55 std::shared_ptr<FileUtil::IOFile> romfs_file;
56 u64 romfs_offset = 0;
57 u64 romfs_size = 0;
58 auto ncch_container = NCCHContainer(file_path);
59
60 if (ncch_container.ReadRomFS(romfs_file, romfs_offset, romfs_size) !=
61 Loader::ResultStatus::Success) {
44 // High Title ID of the archive: The category (https://3dbrew.org/wiki/Title_list). 62 // High Title ID of the archive: The category (https://3dbrew.org/wiki/Title_list).
45 constexpr u32 shared_data_archive = 0x0004009B; 63 constexpr u32 shared_data_archive = 0x0004009B;
46 constexpr u32 system_data_archive = 0x000400DB; 64 constexpr u32 system_data_archive = 0x000400DB;
@@ -74,9 +92,8 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_NCCH::Open(const Path&
74 } 92 }
75 return ERROR_NOT_FOUND; 93 return ERROR_NOT_FOUND;
76 } 94 }
77 auto size = file->GetSize();
78 95
79 auto archive = std::make_unique<IVFCArchive>(file, 0, size); 96 auto archive = std::make_unique<IVFCArchive>(romfs_file, romfs_offset, romfs_size);
80 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 97 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
81} 98}
82 99
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp
index 679909d06..fe3dce5d4 100644
--- a/src/core/file_sys/archive_sdmc.cpp
+++ b/src/core/file_sys/archive_sdmc.cpp
@@ -121,7 +121,25 @@ ResultCode SDMCArchive::DeleteFile(const Path& path) const {
121} 121}
122 122
123ResultCode SDMCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { 123ResultCode SDMCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
124 if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) { 124 const PathParser path_parser_src(src_path);
125
126 // TODO: Verify these return codes with HW
127 if (!path_parser_src.IsValid()) {
128 LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str());
129 return ERROR_INVALID_PATH;
130 }
131
132 const PathParser path_parser_dest(dest_path);
133
134 if (!path_parser_dest.IsValid()) {
135 LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str());
136 return ERROR_INVALID_PATH;
137 }
138
139 const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
140 const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
141
142 if (FileUtil::Rename(src_path_full, dest_path_full)) {
125 return RESULT_SUCCESS; 143 return RESULT_SUCCESS;
126 } 144 }
127 145
@@ -260,8 +278,27 @@ ResultCode SDMCArchive::CreateDirectory(const Path& path) const {
260} 278}
261 279
262ResultCode SDMCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { 280ResultCode SDMCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
263 if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) 281 const PathParser path_parser_src(src_path);
282
283 // TODO: Verify these return codes with HW
284 if (!path_parser_src.IsValid()) {
285 LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str());
286 return ERROR_INVALID_PATH;
287 }
288
289 const PathParser path_parser_dest(dest_path);
290
291 if (!path_parser_dest.IsValid()) {
292 LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str());
293 return ERROR_INVALID_PATH;
294 }
295
296 const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
297 const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
298
299 if (FileUtil::Rename(src_path_full, dest_path_full)) {
264 return RESULT_SUCCESS; 300 return RESULT_SUCCESS;
301 }
265 302
266 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't 303 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
267 // exist or similar. Verify. 304 // exist or similar. Verify.
diff --git a/src/core/file_sys/archive_selfncch.cpp b/src/core/file_sys/archive_selfncch.cpp
index 298a37a44..3222000cf 100644
--- a/src/core/file_sys/archive_selfncch.cpp
+++ b/src/core/file_sys/archive_selfncch.cpp
@@ -3,12 +3,14 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array> 5#include <array>
6#include <cinttypes>
6#include "common/common_types.h" 7#include "common/common_types.h"
7#include "common/logging/log.h" 8#include "common/logging/log.h"
8#include "common/swap.h" 9#include "common/swap.h"
9#include "core/file_sys/archive_selfncch.h" 10#include "core/file_sys/archive_selfncch.h"
10#include "core/file_sys/errors.h" 11#include "core/file_sys/errors.h"
11#include "core/file_sys/ivfc_archive.h" 12#include "core/file_sys/ivfc_archive.h"
13#include "core/hle/kernel/process.h"
12 14
13//////////////////////////////////////////////////////////////////////////////////////////////////// 15////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace 16// FileSys namespace
@@ -102,8 +104,7 @@ public:
102 104
103 switch (static_cast<SelfNCCHFilePathType>(file_path.type)) { 105 switch (static_cast<SelfNCCHFilePathType>(file_path.type)) {
104 case SelfNCCHFilePathType::UpdateRomFS: 106 case SelfNCCHFilePathType::UpdateRomFS:
105 LOG_WARNING(Service_FS, "(STUBBED) open update RomFS"); 107 return OpenUpdateRomFS();
106 return OpenRomFS();
107 108
108 case SelfNCCHFilePathType::RomFS: 109 case SelfNCCHFilePathType::RomFS:
109 return OpenRomFS(); 110 return OpenRomFS();
@@ -179,6 +180,17 @@ private:
179 } 180 }
180 } 181 }
181 182
183 ResultVal<std::unique_ptr<FileBackend>> OpenUpdateRomFS() const {
184 if (ncch_data.update_romfs_file) {
185 return MakeResult<std::unique_ptr<FileBackend>>(std::make_unique<IVFCFile>(
186 ncch_data.update_romfs_file, ncch_data.update_romfs_offset,
187 ncch_data.update_romfs_size));
188 } else {
189 LOG_INFO(Service_FS, "Unable to read update RomFS");
190 return ERROR_ROMFS_NOT_FOUND;
191 }
192 }
193
182 ResultVal<std::unique_ptr<FileBackend>> OpenExeFS(const std::string& filename) const { 194 ResultVal<std::unique_ptr<FileBackend>> OpenExeFS(const std::string& filename) const {
183 if (filename == "icon") { 195 if (filename == "icon") {
184 if (ncch_data.icon) { 196 if (ncch_data.icon) {
@@ -217,31 +229,59 @@ private:
217 NCCHData ncch_data; 229 NCCHData ncch_data;
218}; 230};
219 231
220ArchiveFactory_SelfNCCH::ArchiveFactory_SelfNCCH(Loader::AppLoader& app_loader) { 232void ArchiveFactory_SelfNCCH::Register(Loader::AppLoader& app_loader) {
233 u64 program_id = 0;
234 if (app_loader.ReadProgramId(program_id) != Loader::ResultStatus::Success) {
235 LOG_WARNING(
236 Service_FS,
237 "Could not read program id when registering with SelfNCCH, this might be a 3dsx file");
238 }
239
240 LOG_DEBUG(Service_FS, "Registering program %016" PRIX64 " with the SelfNCCH archive factory",
241 program_id);
242
243 if (ncch_data.find(program_id) != ncch_data.end()) {
244 LOG_WARNING(Service_FS, "Registering program %016" PRIX64
245 " with SelfNCCH will override existing mapping",
246 program_id);
247 }
248
249 NCCHData& data = ncch_data[program_id];
250
221 std::shared_ptr<FileUtil::IOFile> romfs_file_; 251 std::shared_ptr<FileUtil::IOFile> romfs_file_;
222 if (Loader::ResultStatus::Success == 252 if (Loader::ResultStatus::Success ==
223 app_loader.ReadRomFS(romfs_file_, ncch_data.romfs_offset, ncch_data.romfs_size)) { 253 app_loader.ReadRomFS(romfs_file_, data.romfs_offset, data.romfs_size)) {
254
255 data.romfs_file = std::move(romfs_file_);
256 }
257
258 std::shared_ptr<FileUtil::IOFile> update_romfs_file;
259 if (Loader::ResultStatus::Success ==
260 app_loader.ReadUpdateRomFS(update_romfs_file, data.update_romfs_offset,
261 data.update_romfs_size)) {
224 262
225 ncch_data.romfs_file = std::move(romfs_file_); 263 data.update_romfs_file = std::move(update_romfs_file);
226 } 264 }
227 265
228 std::vector<u8> buffer; 266 std::vector<u8> buffer;
229 267
230 if (Loader::ResultStatus::Success == app_loader.ReadIcon(buffer)) 268 if (Loader::ResultStatus::Success == app_loader.ReadIcon(buffer))
231 ncch_data.icon = std::make_shared<std::vector<u8>>(std::move(buffer)); 269 data.icon = std::make_shared<std::vector<u8>>(std::move(buffer));
232 270
233 buffer.clear(); 271 buffer.clear();
234 if (Loader::ResultStatus::Success == app_loader.ReadLogo(buffer)) 272 if (Loader::ResultStatus::Success == app_loader.ReadLogo(buffer))
235 ncch_data.logo = std::make_shared<std::vector<u8>>(std::move(buffer)); 273 data.logo = std::make_shared<std::vector<u8>>(std::move(buffer));
236 274
237 buffer.clear(); 275 buffer.clear();
238 if (Loader::ResultStatus::Success == app_loader.ReadBanner(buffer)) 276 if (Loader::ResultStatus::Success == app_loader.ReadBanner(buffer))
239 ncch_data.banner = std::make_shared<std::vector<u8>>(std::move(buffer)); 277 data.banner = std::make_shared<std::vector<u8>>(std::move(buffer));
240} 278}
241 279
242ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path) { 280ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path) {
243 auto archive = std::make_unique<SelfNCCHArchive>(ncch_data); 281 //auto archive = std::make_unique<SelfNCCHArchive>(
244 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 282 // ncch_data[Kernel::g_current_process->codeset->program_id]);
283 //return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
284 return {};
245} 285}
246 286
247ResultCode ArchiveFactory_SelfNCCH::Format(const Path&, const FileSys::ArchiveFormatInfo&) { 287ResultCode ArchiveFactory_SelfNCCH::Format(const Path&, const FileSys::ArchiveFormatInfo&) {
diff --git a/src/core/file_sys/archive_selfncch.h b/src/core/file_sys/archive_selfncch.h
index f1b971296..0d6d6766e 100644
--- a/src/core/file_sys/archive_selfncch.h
+++ b/src/core/file_sys/archive_selfncch.h
@@ -6,6 +6,7 @@
6 6
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include <unordered_map>
9#include <vector> 10#include <vector>
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "core/file_sys/archive_backend.h" 12#include "core/file_sys/archive_backend.h"
@@ -24,12 +25,19 @@ struct NCCHData {
24 std::shared_ptr<FileUtil::IOFile> romfs_file; 25 std::shared_ptr<FileUtil::IOFile> romfs_file;
25 u64 romfs_offset = 0; 26 u64 romfs_offset = 0;
26 u64 romfs_size = 0; 27 u64 romfs_size = 0;
28
29 std::shared_ptr<FileUtil::IOFile> update_romfs_file;
30 u64 update_romfs_offset = 0;
31 u64 update_romfs_size = 0;
27}; 32};
28 33
29/// File system interface to the SelfNCCH archive 34/// File system interface to the SelfNCCH archive
30class ArchiveFactory_SelfNCCH final : public ArchiveFactory { 35class ArchiveFactory_SelfNCCH final : public ArchiveFactory {
31public: 36public:
32 explicit ArchiveFactory_SelfNCCH(Loader::AppLoader& app_loader); 37 ArchiveFactory_SelfNCCH() = default;
38
39 /// Registers a loaded application so that we can open its SelfNCCH archive when requested.
40 void Register(Loader::AppLoader& app_loader);
33 41
34 std::string GetName() const override { 42 std::string GetName() const override {
35 return "SelfNCCH"; 43 return "SelfNCCH";
@@ -39,7 +47,8 @@ public:
39 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; 47 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
40 48
41private: 49private:
42 NCCHData ncch_data; 50 /// Mapping of ProgramId -> NCCHData
51 std::unordered_map<u64, NCCHData> ncch_data;
43}; 52};
44 53
45} // namespace FileSys 54} // namespace FileSys
diff --git a/src/core/file_sys/ncch_container.cpp b/src/core/file_sys/ncch_container.cpp
new file mode 100644
index 000000000..b9fb940c7
--- /dev/null
+++ b/src/core/file_sys/ncch_container.cpp
@@ -0,0 +1,423 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cinttypes>
6#include <cstring>
7#include <memory>
8#include "common/common_types.h"
9#include "common/logging/log.h"
10#include "core/core.h"
11#include "core/file_sys/ncch_container.h"
12#include "core/loader/loader.h"
13
14////////////////////////////////////////////////////////////////////////////////////////////////////
15// FileSys namespace
16
17namespace FileSys {
18
19static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs
20static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
21
22/**
23 * Get the decompressed size of an LZSS compressed ExeFS file
24 * @param buffer Buffer of compressed file
25 * @param size Size of compressed buffer
26 * @return Size of decompressed buffer
27 */
28static u32 LZSS_GetDecompressedSize(const u8* buffer, u32 size) {
29 u32 offset_size = *(u32*)(buffer + size - 4);
30 return offset_size + size;
31}
32
33/**
34 * Decompress ExeFS file (compressed with LZSS)
35 * @param compressed Compressed buffer
36 * @param compressed_size Size of compressed buffer
37 * @param decompressed Decompressed buffer
38 * @param decompressed_size Size of decompressed buffer
39 * @return True on success, otherwise false
40 */
41static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decompressed,
42 u32 decompressed_size) {
43 const u8* footer = compressed + compressed_size - 8;
44 u32 buffer_top_and_bottom = *reinterpret_cast<const u32*>(footer);
45 u32 out = decompressed_size;
46 u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF);
47 u32 stop_index = compressed_size - (buffer_top_and_bottom & 0xFFFFFF);
48
49 memset(decompressed, 0, decompressed_size);
50 memcpy(decompressed, compressed, compressed_size);
51
52 while (index > stop_index) {
53 u8 control = compressed[--index];
54
55 for (unsigned i = 0; i < 8; i++) {
56 if (index <= stop_index)
57 break;
58 if (index <= 0)
59 break;
60 if (out <= 0)
61 break;
62
63 if (control & 0x80) {
64 // Check if compression is out of bounds
65 if (index < 2)
66 return false;
67 index -= 2;
68
69 u32 segment_offset = compressed[index] | (compressed[index + 1] << 8);
70 u32 segment_size = ((segment_offset >> 12) & 15) + 3;
71 segment_offset &= 0x0FFF;
72 segment_offset += 2;
73
74 // Check if compression is out of bounds
75 if (out < segment_size)
76 return false;
77
78 for (unsigned j = 0; j < segment_size; j++) {
79 // Check if compression is out of bounds
80 if (out + segment_offset >= decompressed_size)
81 return false;
82
83 u8 data = decompressed[out + segment_offset];
84 decompressed[--out] = data;
85 }
86 } else {
87 // Check if compression is out of bounds
88 if (out < 1)
89 return false;
90 decompressed[--out] = compressed[--index];
91 }
92 control <<= 1;
93 }
94 }
95 return true;
96}
97
98NCCHContainer::NCCHContainer(const std::string& filepath) : filepath(filepath) {
99 file = FileUtil::IOFile(filepath, "rb");
100}
101
102Loader::ResultStatus NCCHContainer::OpenFile(const std::string& filepath) {
103 this->filepath = filepath;
104 file = FileUtil::IOFile(filepath, "rb");
105
106 if (!file.IsOpen()) {
107 LOG_WARNING(Service_FS, "Failed to open %s", filepath.c_str());
108 return Loader::ResultStatus::Error;
109 }
110
111 LOG_DEBUG(Service_FS, "Opened %s", filepath.c_str());
112 return Loader::ResultStatus::Success;
113}
114
115Loader::ResultStatus NCCHContainer::Load() {
116 if (is_loaded)
117 return Loader::ResultStatus::Success;
118
119 if (file.IsOpen()) {
120 // Reset read pointer in case this file has been read before.
121 file.Seek(0, SEEK_SET);
122
123 if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header))
124 return Loader::ResultStatus::Error;
125
126 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
127 if (Loader::MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
128 LOG_DEBUG(Service_FS, "Only loading the first (bootable) NCCH within the NCSD file!");
129 ncch_offset = 0x4000;
130 file.Seek(ncch_offset, SEEK_SET);
131 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
132 }
133
134 // Verify we are loading the correct file type...
135 if (Loader::MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic)
136 return Loader::ResultStatus::ErrorInvalidFormat;
137
138 has_header = true;
139
140 // System archives and DLC don't have an extended header but have RomFS
141 if (ncch_header.extended_header_size) {
142 if (file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)) !=
143 sizeof(ExHeader_Header))
144 return Loader::ResultStatus::Error;
145
146 is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
147 u32 entry_point = exheader_header.codeset_info.text.address;
148 u32 code_size = exheader_header.codeset_info.text.code_size;
149 u32 stack_size = exheader_header.codeset_info.stack_size;
150 u32 bss_size = exheader_header.codeset_info.bss_size;
151 u32 core_version = exheader_header.arm11_system_local_caps.core_version;
152 u8 priority = exheader_header.arm11_system_local_caps.priority;
153 u8 resource_limit_category =
154 exheader_header.arm11_system_local_caps.resource_limit_category;
155
156 LOG_DEBUG(Service_FS, "Name: %s",
157 exheader_header.codeset_info.name);
158 LOG_DEBUG(Service_FS, "Program ID: %016" PRIX64,
159 ncch_header.program_id);
160 LOG_DEBUG(Service_FS, "Code compressed: %s", is_compressed ? "yes" : "no");
161 LOG_DEBUG(Service_FS, "Entry point: 0x%08X", entry_point);
162 LOG_DEBUG(Service_FS, "Code size: 0x%08X", code_size);
163 LOG_DEBUG(Service_FS, "Stack size: 0x%08X", stack_size);
164 LOG_DEBUG(Service_FS, "Bss size: 0x%08X", bss_size);
165 LOG_DEBUG(Service_FS, "Core version: %d", core_version);
166 LOG_DEBUG(Service_FS, "Thread priority: 0x%X", priority);
167 LOG_DEBUG(Service_FS, "Resource limit category: %d", resource_limit_category);
168 LOG_DEBUG(Service_FS, "System Mode: %d",
169 static_cast<int>(exheader_header.arm11_system_local_caps.system_mode));
170
171 if (exheader_header.system_info.jump_id != ncch_header.program_id) {
172 LOG_ERROR(Service_FS,
173 "ExHeader Program ID mismatch: the ROM is probably encrypted.");
174 return Loader::ResultStatus::ErrorEncrypted;
175 }
176
177 has_exheader = true;
178 }
179
180 // DLC can have an ExeFS and a RomFS but no extended header
181 if (ncch_header.exefs_size) {
182 exefs_offset = ncch_header.exefs_offset * kBlockSize;
183 u32 exefs_size = ncch_header.exefs_size * kBlockSize;
184
185 LOG_DEBUG(Service_FS, "ExeFS offset: 0x%08X", exefs_offset);
186 LOG_DEBUG(Service_FS, "ExeFS size: 0x%08X", exefs_size);
187
188 file.Seek(exefs_offset + ncch_offset, SEEK_SET);
189 if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header))
190 return Loader::ResultStatus::Error;
191
192 exefs_file = FileUtil::IOFile(filepath, "rb");
193 has_exefs = true;
194 }
195
196 if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0)
197 has_romfs = true;
198 }
199
200 LoadOverrides();
201
202 // We need at least one of these or overrides, practically
203 if (!(has_exefs || has_romfs || is_tainted))
204 return Loader::ResultStatus::Error;
205
206 is_loaded = true;
207 return Loader::ResultStatus::Success;
208}
209
210Loader::ResultStatus NCCHContainer::LoadOverrides() {
211 // Check for split-off files, mark the archive as tainted if we will use them
212 std::string romfs_override = filepath + ".romfs";
213 if (FileUtil::Exists(romfs_override)) {
214 is_tainted = true;
215 }
216
217 // If we have a split-off exefs file/folder, it takes priority
218 std::string exefs_override = filepath + ".exefs";
219 std::string exefsdir_override = filepath + ".exefsdir/";
220 if (FileUtil::Exists(exefs_override)) {
221 exefs_file = FileUtil::IOFile(exefs_override, "rb");
222
223 if (exefs_file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) == sizeof(ExeFs_Header)) {
224 LOG_DEBUG(Service_FS, "Loading ExeFS section from %s", exefs_override.c_str());
225 exefs_offset = 0;
226 is_tainted = true;
227 has_exefs = true;
228 } else {
229 exefs_file = FileUtil::IOFile(filepath, "rb");
230 }
231 } else if (FileUtil::Exists(exefsdir_override) && FileUtil::IsDirectory(exefsdir_override)) {
232 is_tainted = true;
233 }
234
235 if (is_tainted)
236 LOG_WARNING(Service_FS,
237 "Loaded NCCH %s is tainted, application behavior may not be as expected!",
238 filepath.c_str());
239
240 return Loader::ResultStatus::Success;
241}
242
243Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) {
244 Loader::ResultStatus result = Load();
245 if (result != Loader::ResultStatus::Success)
246 return result;
247
248 // Check if we have files that can drop-in and replace
249 result = LoadOverrideExeFSSection(name, buffer);
250 if (result == Loader::ResultStatus::Success || !has_exefs)
251 return result;
252
253 // If we don't have any separate files, we'll need a full ExeFS
254 if (!exefs_file.IsOpen())
255 return Loader::ResultStatus::Error;
256
257 LOG_DEBUG(Service_FS, "%d sections:", kMaxSections);
258 // Iterate through the ExeFs archive until we find a section with the specified name...
259 for (unsigned section_number = 0; section_number < kMaxSections; section_number++) {
260 const auto& section = exefs_header.section[section_number];
261
262 // Load the specified section...
263 if (strcmp(section.name, name) == 0) {
264 LOG_DEBUG(Service_FS, "%d - offset: 0x%08X, size: 0x%08X, name: %s", section_number,
265 section.offset, section.size, section.name);
266
267 s64 section_offset =
268 (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset);
269 exefs_file.Seek(section_offset, SEEK_SET);
270
271 if (strcmp(section.name, ".code") == 0 && is_compressed) {
272 // Section is compressed, read compressed .code section...
273 std::unique_ptr<u8[]> temp_buffer;
274 try {
275 temp_buffer.reset(new u8[section.size]);
276 } catch (std::bad_alloc&) {
277 return Loader::ResultStatus::ErrorMemoryAllocationFailed;
278 }
279
280 if (exefs_file.ReadBytes(&temp_buffer[0], section.size) != section.size)
281 return Loader::ResultStatus::Error;
282
283 // Decompress .code section...
284 u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], section.size);
285 buffer.resize(decompressed_size);
286 if (!LZSS_Decompress(&temp_buffer[0], section.size, &buffer[0], decompressed_size))
287 return Loader::ResultStatus::ErrorInvalidFormat;
288 } else {
289 // Section is uncompressed...
290 buffer.resize(section.size);
291 if (exefs_file.ReadBytes(&buffer[0], section.size) != section.size)
292 return Loader::ResultStatus::Error;
293 }
294 return Loader::ResultStatus::Success;
295 }
296 }
297 return Loader::ResultStatus::ErrorNotUsed;
298}
299
300Loader::ResultStatus NCCHContainer::LoadOverrideExeFSSection(const char* name,
301 std::vector<u8>& buffer) {
302 std::string override_name;
303
304 // Map our section name to the extracted equivalent
305 if (!strcmp(name, ".code"))
306 override_name = "code.bin";
307 else if (!strcmp(name, "icon"))
308 override_name = "code.bin";
309 else if (!strcmp(name, "banner"))
310 override_name = "banner.bnr";
311 else if (!strcmp(name, "logo"))
312 override_name = "logo.bcma.lz";
313 else
314 return Loader::ResultStatus::Error;
315
316 std::string section_override = filepath + ".exefsdir/" + override_name;
317 FileUtil::IOFile section_file(section_override, "rb");
318
319 if (section_file.IsOpen()) {
320 auto section_size = section_file.GetSize();
321 buffer.resize(section_size);
322
323 section_file.Seek(0, SEEK_SET);
324 if (section_file.ReadBytes(&buffer[0], section_size) == section_size) {
325 LOG_WARNING(Service_FS, "File %s overriding built-in ExeFS file",
326 section_override.c_str());
327 return Loader::ResultStatus::Success;
328 }
329 }
330 return Loader::ResultStatus::ErrorNotUsed;
331}
332
333Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file,
334 u64& offset, u64& size) {
335 Loader::ResultStatus result = Load();
336 if (result != Loader::ResultStatus::Success)
337 return result;
338
339 if (ReadOverrideRomFS(romfs_file, offset, size) == Loader::ResultStatus::Success)
340 return Loader::ResultStatus::Success;
341
342 if (!has_romfs) {
343 LOG_DEBUG(Service_FS, "RomFS requested from NCCH which has no RomFS");
344 return Loader::ResultStatus::ErrorNotUsed;
345 }
346
347 if (!file.IsOpen())
348 return Loader::ResultStatus::Error;
349
350 u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000;
351 u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000;
352
353 LOG_DEBUG(Service_FS, "RomFS offset: 0x%08X", romfs_offset);
354 LOG_DEBUG(Service_FS, "RomFS size: 0x%08X", romfs_size);
355
356 if (file.GetSize() < romfs_offset + romfs_size)
357 return Loader::ResultStatus::Error;
358
359 // We reopen the file, to allow its position to be independent from file's
360 romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb");
361 if (!romfs_file->IsOpen())
362 return Loader::ResultStatus::Error;
363
364 offset = romfs_offset;
365 size = romfs_size;
366
367 return Loader::ResultStatus::Success;
368}
369
370Loader::ResultStatus NCCHContainer::ReadOverrideRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file,
371 u64& offset, u64& size) {
372 // Check for RomFS overrides
373 std::string split_filepath = filepath + ".romfs";
374 if (FileUtil::Exists(split_filepath)) {
375 romfs_file = std::make_shared<FileUtil::IOFile>(split_filepath, "rb");
376 if (romfs_file->IsOpen()) {
377 LOG_WARNING(Service_FS, "File %s overriding built-in RomFS", split_filepath.c_str());
378 offset = 0;
379 size = romfs_file->GetSize();
380 return Loader::ResultStatus::Success;
381 }
382 }
383
384 return Loader::ResultStatus::ErrorNotUsed;
385}
386
387Loader::ResultStatus NCCHContainer::ReadProgramId(u64_le& program_id) {
388 Loader::ResultStatus result = Load();
389 if (result != Loader::ResultStatus::Success)
390 return result;
391
392 if (!has_header)
393 return Loader::ResultStatus::ErrorNotUsed;
394
395 program_id = ncch_header.program_id;
396 return Loader::ResultStatus::Success;
397}
398
399bool NCCHContainer::HasExeFS() {
400 Loader::ResultStatus result = Load();
401 if (result != Loader::ResultStatus::Success)
402 return false;
403
404 return has_exefs;
405}
406
407bool NCCHContainer::HasRomFS() {
408 Loader::ResultStatus result = Load();
409 if (result != Loader::ResultStatus::Success)
410 return false;
411
412 return has_romfs;
413}
414
415bool NCCHContainer::HasExHeader() {
416 Loader::ResultStatus result = Load();
417 if (result != Loader::ResultStatus::Success)
418 return false;
419
420 return has_exheader;
421}
422
423} // namespace FileSys
diff --git a/src/core/file_sys/ncch_container.h b/src/core/file_sys/ncch_container.h
new file mode 100644
index 000000000..2cc9d13dc
--- /dev/null
+++ b/src/core/file_sys/ncch_container.h
@@ -0,0 +1,274 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstddef>
8#include <memory>
9#include <string>
10#include <vector>
11#include "common/bit_field.h"
12#include "common/common_types.h"
13#include "common/file_util.h"
14#include "common/swap.h"
15#include "core/core.h"
16
17////////////////////////////////////////////////////////////////////////////////////////////////////
18/// NCCH header (Note: "NCCH" appears to be a publicly unknown acronym)
19
20struct NCCH_Header {
21 u8 signature[0x100];
22 u32_le magic;
23 u32_le content_size;
24 u8 partition_id[8];
25 u16_le maker_code;
26 u16_le version;
27 u8 reserved_0[4];
28 u64_le program_id;
29 u8 reserved_1[0x10];
30 u8 logo_region_hash[0x20];
31 u8 product_code[0x10];
32 u8 extended_header_hash[0x20];
33 u32_le extended_header_size;
34 u8 reserved_2[4];
35 u8 flags[8];
36 u32_le plain_region_offset;
37 u32_le plain_region_size;
38 u32_le logo_region_offset;
39 u32_le logo_region_size;
40 u32_le exefs_offset;
41 u32_le exefs_size;
42 u32_le exefs_hash_region_size;
43 u8 reserved_3[4];
44 u32_le romfs_offset;
45 u32_le romfs_size;
46 u32_le romfs_hash_region_size;
47 u8 reserved_4[4];
48 u8 exefs_super_block_hash[0x20];
49 u8 romfs_super_block_hash[0x20];
50};
51
52static_assert(sizeof(NCCH_Header) == 0x200, "NCCH header structure size is wrong");
53
54////////////////////////////////////////////////////////////////////////////////////////////////////
55// ExeFS (executable file system) headers
56
57struct ExeFs_SectionHeader {
58 char name[8];
59 u32 offset;
60 u32 size;
61};
62
63struct ExeFs_Header {
64 ExeFs_SectionHeader section[8];
65 u8 reserved[0x80];
66 u8 hashes[8][0x20];
67};
68
69////////////////////////////////////////////////////////////////////////////////////////////////////
70// ExHeader (executable file system header) headers
71
72struct ExHeader_SystemInfoFlags {
73 u8 reserved[5];
74 u8 flag;
75 u8 remaster_version[2];
76};
77
78struct ExHeader_CodeSegmentInfo {
79 u32 address;
80 u32 num_max_pages;
81 u32 code_size;
82};
83
84struct ExHeader_CodeSetInfo {
85 u8 name[8];
86 ExHeader_SystemInfoFlags flags;
87 ExHeader_CodeSegmentInfo text;
88 u32 stack_size;
89 ExHeader_CodeSegmentInfo ro;
90 u8 reserved[4];
91 ExHeader_CodeSegmentInfo data;
92 u32 bss_size;
93};
94
95struct ExHeader_DependencyList {
96 u8 program_id[0x30][8];
97};
98
99struct ExHeader_SystemInfo {
100 u64 save_data_size;
101 u64_le jump_id;
102 u8 reserved_2[0x30];
103};
104
105struct ExHeader_StorageInfo {
106 u8 ext_save_data_id[8];
107 u8 system_save_data_id[8];
108 u8 reserved[8];
109 u8 access_info[7];
110 u8 other_attributes;
111};
112
113struct ExHeader_ARM11_SystemLocalCaps {
114 u64_le program_id;
115 u32_le core_version;
116 u8 reserved_flags[2];
117 union {
118 u8 flags0;
119 BitField<0, 2, u8> ideal_processor;
120 BitField<2, 2, u8> affinity_mask;
121 BitField<4, 4, u8> system_mode;
122 };
123 u8 priority;
124 u8 resource_limit_descriptor[0x10][2];
125 ExHeader_StorageInfo storage_info;
126 u8 service_access_control[0x20][8];
127 u8 ex_service_access_control[0x2][8];
128 u8 reserved[0xf];
129 u8 resource_limit_category;
130};
131
132struct ExHeader_ARM11_KernelCaps {
133 u32_le descriptors[28];
134 u8 reserved[0x10];
135};
136
137struct ExHeader_ARM9_AccessControl {
138 u8 descriptors[15];
139 u8 descversion;
140};
141
142struct ExHeader_Header {
143 ExHeader_CodeSetInfo codeset_info;
144 ExHeader_DependencyList dependency_list;
145 ExHeader_SystemInfo system_info;
146 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
147 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
148 ExHeader_ARM9_AccessControl arm9_access_control;
149 struct {
150 u8 signature[0x100];
151 u8 ncch_public_key_modulus[0x100];
152 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
153 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
154 ExHeader_ARM9_AccessControl arm9_access_control;
155 } access_desc;
156};
157
158static_assert(sizeof(ExHeader_Header) == 0x800, "ExHeader structure size is wrong");
159
160////////////////////////////////////////////////////////////////////////////////////////////////////
161// FileSys namespace
162
163namespace FileSys {
164
165/**
166 * Helper which implements an interface to deal with NCCH containers which can
167 * contain ExeFS archives or RomFS archives for games or other applications.
168 */
169class NCCHContainer {
170public:
171 NCCHContainer(const std::string& filepath);
172 NCCHContainer() {}
173
174 Loader::ResultStatus OpenFile(const std::string& filepath);
175
176 /**
177 * Ensure ExeFS and exheader is loaded and ready for reading sections
178 * @return ResultStatus result of function
179 */
180 Loader::ResultStatus Load();
181
182 /**
183 * Attempt to find overridden sections for the NCCH and mark the container as tainted
184 * if any are found.
185 * @return ResultStatus result of function
186 */
187 Loader::ResultStatus LoadOverrides();
188
189 /**
190 * Reads an application ExeFS section of an NCCH file (e.g. .code, .logo, etc.)
191 * @param name Name of section to read out of NCCH file
192 * @param buffer Vector to read data into
193 * @return ResultStatus result of function
194 */
195 Loader::ResultStatus LoadSectionExeFS(const char* name, std::vector<u8>& buffer);
196
197 /**
198 * Reads an application ExeFS section from external files instead of an NCCH file,
199 * (e.g. code.bin, logo.bcma.lz, icon.icn, banner.bnr)
200 * @param name Name of section to read from external files
201 * @param buffer Vector to read data into
202 * @return ResultStatus result of function
203 */
204 Loader::ResultStatus LoadOverrideExeFSSection(const char* name, std::vector<u8>& buffer);
205
206 /**
207 * Get the RomFS of the NCCH container
208 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer
209 * @param romfs_file The file containing the RomFS
210 * @param offset The offset the romfs begins on
211 * @param size The size of the romfs
212 * @return ResultStatus result of function
213 */
214 Loader::ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
215 u64& size);
216
217 /**
218 * Get the override RomFS of the NCCH container
219 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer
220 * @param romfs_file The file containing the RomFS
221 * @param offset The offset the romfs begins on
222 * @param size The size of the romfs
223 * @return ResultStatus result of function
224 */
225 Loader::ResultStatus ReadOverrideRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file,
226 u64& offset, u64& size);
227
228 /**
229 * Get the Program ID of the NCCH container
230 * @return ResultStatus result of function
231 */
232 Loader::ResultStatus ReadProgramId(u64_le& program_id);
233
234 /**
235 * Checks whether the NCCH container contains an ExeFS
236 * @return bool check result
237 */
238 bool HasExeFS();
239
240 /**
241 * Checks whether the NCCH container contains a RomFS
242 * @return bool check result
243 */
244 bool HasRomFS();
245
246 /**
247 * Checks whether the NCCH container contains an ExHeader
248 * @return bool check result
249 */
250 bool HasExHeader();
251
252 NCCH_Header ncch_header;
253 ExeFs_Header exefs_header;
254 ExHeader_Header exheader_header;
255
256private:
257 bool has_header = false;
258 bool has_exheader = false;
259 bool has_exefs = false;
260 bool has_romfs = false;
261
262 bool is_tainted = false; // Are there parts of this container being overridden?
263 bool is_loaded = false;
264 bool is_compressed = false;
265
266 u32 ncch_offset = 0; // Offset to NCCH header, can be 0 or after NCSD header
267 u32 exefs_offset = 0;
268
269 std::string filepath;
270 FileUtil::IOFile file;
271 FileUtil::IOFile exefs_file;
272};
273
274} // namespace FileSys
diff --git a/src/core/file_sys/savedata_archive.cpp b/src/core/file_sys/savedata_archive.cpp
index f540c4a93..f8f811ba0 100644
--- a/src/core/file_sys/savedata_archive.cpp
+++ b/src/core/file_sys/savedata_archive.cpp
@@ -106,7 +106,25 @@ ResultCode SaveDataArchive::DeleteFile(const Path& path) const {
106} 106}
107 107
108ResultCode SaveDataArchive::RenameFile(const Path& src_path, const Path& dest_path) const { 108ResultCode SaveDataArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
109 if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) { 109 const PathParser path_parser_src(src_path);
110
111 // TODO: Verify these return codes with HW
112 if (!path_parser_src.IsValid()) {
113 LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str());
114 return ERROR_INVALID_PATH;
115 }
116
117 const PathParser path_parser_dest(dest_path);
118
119 if (!path_parser_dest.IsValid()) {
120 LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str());
121 return ERROR_INVALID_PATH;
122 }
123
124 const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
125 const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
126
127 if (FileUtil::Rename(src_path_full, dest_path_full)) {
110 return RESULT_SUCCESS; 128 return RESULT_SUCCESS;
111 } 129 }
112 130
@@ -247,8 +265,27 @@ ResultCode SaveDataArchive::CreateDirectory(const Path& path) const {
247} 265}
248 266
249ResultCode SaveDataArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { 267ResultCode SaveDataArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
250 if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) 268 const PathParser path_parser_src(src_path);
269
270 // TODO: Verify these return codes with HW
271 if (!path_parser_src.IsValid()) {
272 LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str());
273 return ERROR_INVALID_PATH;
274 }
275
276 const PathParser path_parser_dest(dest_path);
277
278 if (!path_parser_dest.IsValid()) {
279 LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str());
280 return ERROR_INVALID_PATH;
281 }
282
283 const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
284 const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
285
286 if (FileUtil::Rename(src_path_full, dest_path_full)) {
251 return RESULT_SUCCESS; 287 return RESULT_SUCCESS;
288 }
252 289
253 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't 290 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
254 // exist or similar. Verify. 291 // exist or similar. Verify.
diff --git a/src/core/file_sys/title_metadata.cpp b/src/core/file_sys/title_metadata.cpp
new file mode 100644
index 000000000..1ef8840a0
--- /dev/null
+++ b/src/core/file_sys/title_metadata.cpp
@@ -0,0 +1,212 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cinttypes>
6#include <cryptopp/sha.h>
7#include "common/alignment.h"
8#include "common/file_util.h"
9#include "common/logging/log.h"
10#include "core/file_sys/title_metadata.h"
11#include "core/loader/loader.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace
15
16namespace FileSys {
17
18static u32 GetSignatureSize(u32 signature_type) {
19 switch (signature_type) {
20 case Rsa4096Sha1:
21 case Rsa4096Sha256:
22 return 0x200;
23
24 case Rsa2048Sha1:
25 case Rsa2048Sha256:
26 return 0x100;
27
28 case EllipticSha1:
29 case EcdsaSha256:
30 return 0x3C;
31 }
32}
33
34Loader::ResultStatus TitleMetadata::Load() {
35 FileUtil::IOFile file(filepath, "rb");
36 if (!file.IsOpen())
37 return Loader::ResultStatus::Error;
38
39 if (!file.ReadBytes(&signature_type, sizeof(u32_be)))
40 return Loader::ResultStatus::Error;
41
42 // Signature lengths are variable, and the body follows the signature
43 u32 signature_size = GetSignatureSize(signature_type);
44
45 tmd_signature.resize(signature_size);
46 if (!file.ReadBytes(&tmd_signature[0], signature_size))
47 return Loader::ResultStatus::Error;
48
49 // The TMD body start position is rounded to the nearest 0x40 after the signature
50 size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40);
51 file.Seek(body_start, SEEK_SET);
52
53 // Read our TMD body, then load the amount of ContentChunks specified
54 if (file.ReadBytes(&tmd_body, sizeof(TitleMetadata::Body)) != sizeof(TitleMetadata::Body))
55 return Loader::ResultStatus::Error;
56
57 for (u16 i = 0; i < tmd_body.content_count; i++) {
58 ContentChunk chunk;
59 if (file.ReadBytes(&chunk, sizeof(ContentChunk)) == sizeof(ContentChunk)) {
60 tmd_chunks.push_back(chunk);
61 } else {
62 LOG_ERROR(Service_FS, "Malformed TMD %s, failed to load content chunk index %u!",
63 filepath.c_str(), i);
64 return Loader::ResultStatus::ErrorInvalidFormat;
65 }
66 }
67
68 return Loader::ResultStatus::Success;
69}
70
71Loader::ResultStatus TitleMetadata::Save() {
72 FileUtil::IOFile file(filepath, "wb");
73 if (!file.IsOpen())
74 return Loader::ResultStatus::Error;
75
76 if (!file.WriteBytes(&signature_type, sizeof(u32_be)))
77 return Loader::ResultStatus::Error;
78
79 // Signature lengths are variable, and the body follows the signature
80 u32 signature_size = GetSignatureSize(signature_type);
81
82 if (!file.WriteBytes(tmd_signature.data(), signature_size))
83 return Loader::ResultStatus::Error;
84
85 // The TMD body start position is rounded to the nearest 0x40 after the signature
86 size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40);
87 file.Seek(body_start, SEEK_SET);
88
89 // Update our TMD body values and hashes
90 tmd_body.content_count = static_cast<u16>(tmd_chunks.size());
91
92 // TODO(shinyquagsire23): Do TMDs with more than one contentinfo exist?
93 // For now we'll just adjust the first index to hold all content chunks
94 // and ensure that no further content info data exists.
95 tmd_body.contentinfo = {};
96 tmd_body.contentinfo[0].index = 0;
97 tmd_body.contentinfo[0].command_count = static_cast<u16>(tmd_chunks.size());
98
99 CryptoPP::SHA256 chunk_hash;
100 for (u16 i = 0; i < tmd_body.content_count; i++) {
101 chunk_hash.Update(reinterpret_cast<u8*>(&tmd_chunks[i]), sizeof(ContentChunk));
102 }
103 chunk_hash.Final(tmd_body.contentinfo[0].hash.data());
104
105 CryptoPP::SHA256 contentinfo_hash;
106 for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) {
107 chunk_hash.Update(reinterpret_cast<u8*>(&tmd_body.contentinfo[i]), sizeof(ContentInfo));
108 }
109 chunk_hash.Final(tmd_body.contentinfo_hash.data());
110
111 // Write our TMD body, then write each of our ContentChunks
112 if (file.WriteBytes(&tmd_body, sizeof(TitleMetadata::Body)) != sizeof(TitleMetadata::Body))
113 return Loader::ResultStatus::Error;
114
115 for (u16 i = 0; i < tmd_body.content_count; i++) {
116 ContentChunk chunk = tmd_chunks[i];
117 if (file.WriteBytes(&chunk, sizeof(ContentChunk)) != sizeof(ContentChunk))
118 return Loader::ResultStatus::Error;
119 }
120
121 return Loader::ResultStatus::Success;
122}
123
124u64 TitleMetadata::GetTitleID() const {
125 return tmd_body.title_id;
126}
127
128u32 TitleMetadata::GetTitleType() const {
129 return tmd_body.title_type;
130}
131
132u16 TitleMetadata::GetTitleVersion() const {
133 return tmd_body.title_version;
134}
135
136u64 TitleMetadata::GetSystemVersion() const {
137 return tmd_body.system_version;
138}
139
140size_t TitleMetadata::GetContentCount() const {
141 return tmd_chunks.size();
142}
143
144u32 TitleMetadata::GetBootContentID() const {
145 return tmd_chunks[TMDContentIndex::Main].id;
146}
147
148u32 TitleMetadata::GetManualContentID() const {
149 return tmd_chunks[TMDContentIndex::Manual].id;
150}
151
152u32 TitleMetadata::GetDLPContentID() const {
153 return tmd_chunks[TMDContentIndex::DLP].id;
154}
155
156void TitleMetadata::SetTitleID(u64 title_id) {
157 tmd_body.title_id = title_id;
158}
159
160void TitleMetadata::SetTitleType(u32 type) {
161 tmd_body.title_type = type;
162}
163
164void TitleMetadata::SetTitleVersion(u16 version) {
165 tmd_body.title_version = version;
166}
167
168void TitleMetadata::SetSystemVersion(u64 version) {
169 tmd_body.system_version = version;
170}
171
172void TitleMetadata::AddContentChunk(const ContentChunk& chunk) {
173 tmd_chunks.push_back(chunk);
174}
175
176void TitleMetadata::Print() const {
177 LOG_DEBUG(Service_FS, "%s - %u chunks", filepath.c_str(),
178 static_cast<u32>(tmd_body.content_count));
179
180 // Content info describes ranges of content chunks
181 LOG_DEBUG(Service_FS, "Content info:");
182 for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) {
183 if (tmd_body.contentinfo[i].command_count == 0)
184 break;
185
186 LOG_DEBUG(Service_FS, " Index %04X, Command Count %04X",
187 static_cast<u32>(tmd_body.contentinfo[i].index),
188 static_cast<u32>(tmd_body.contentinfo[i].command_count));
189 }
190
191 // For each content info, print their content chunk range
192 for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) {
193 u16 index = static_cast<u16>(tmd_body.contentinfo[i].index);
194 u16 count = static_cast<u16>(tmd_body.contentinfo[i].command_count);
195
196 if (count == 0)
197 continue;
198
199 LOG_DEBUG(Service_FS, "Content chunks for content info index %zu:", i);
200 for (u16 j = index; j < index + count; j++) {
201 // Don't attempt to print content we don't have
202 if (j > tmd_body.content_count)
203 break;
204
205 const ContentChunk& chunk = tmd_chunks[j];
206 LOG_DEBUG(Service_FS, " ID %08X, Index %04X, Type %04x, Size %016" PRIX64,
207 static_cast<u32>(chunk.id), static_cast<u32>(chunk.index),
208 static_cast<u32>(chunk.type), static_cast<u64>(chunk.size));
209 }
210 }
211}
212} // namespace FileSys
diff --git a/src/core/file_sys/title_metadata.h b/src/core/file_sys/title_metadata.h
new file mode 100644
index 000000000..1fc157bf3
--- /dev/null
+++ b/src/core/file_sys/title_metadata.h
@@ -0,0 +1,125 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include <vector>
9#include "common/common_types.h"
10#include "common/swap.h"
11
12namespace Loader {
13enum class ResultStatus;
14}
15
16////////////////////////////////////////////////////////////////////////////////////////////////////
17// FileSys namespace
18
19namespace FileSys {
20
21enum TMDSignatureType : u32 {
22 Rsa4096Sha1 = 0x10000,
23 Rsa2048Sha1 = 0x10001,
24 EllipticSha1 = 0x10002,
25 Rsa4096Sha256 = 0x10003,
26 Rsa2048Sha256 = 0x10004,
27 EcdsaSha256 = 0x10005
28};
29
30enum TMDContentTypeFlag : u16 {
31 Encrypted = 1 << 1,
32 Disc = 1 << 2,
33 CFM = 1 << 3,
34 Optional = 1 << 14,
35 Shared = 1 << 15
36};
37
38/**
39 * Helper which implements an interface to read and write Title Metadata (TMD) files.
40 * If a file path is provided and the file exists, it can be parsed and used, otherwise
41 * it must be created. The TMD file can then be interpreted, modified and/or saved.
42 */
43class TitleMetadata {
44public:
45 struct ContentChunk {
46 u32_be id;
47 u16_be index;
48 u16_be type;
49 u64_be size;
50 std::array<u8, 0x20> hash;
51 };
52
53 static_assert(sizeof(ContentChunk) == 0x30, "TMD ContentChunk structure size is wrong");
54
55 struct ContentInfo {
56 u16_be index;
57 u16_be command_count;
58 std::array<u8, 0x20> hash;
59 };
60
61 static_assert(sizeof(ContentInfo) == 0x24, "TMD ContentInfo structure size is wrong");
62
63#pragma pack(push, 1)
64
65 struct Body {
66 std::array<u8, 0x40> issuer;
67 u8 version;
68 u8 ca_crl_version;
69 u8 signer_crl_version;
70 u8 reserved;
71 u64_be system_version;
72 u64_be title_id;
73 u32_be title_type;
74 u16_be group_id;
75 u32_be savedata_size;
76 u32_be srl_private_savedata_size;
77 std::array<u8, 4> reserved_2;
78 u8 srl_flag;
79 std::array<u8, 0x31> reserved_3;
80 u32_be access_rights;
81 u16_be title_version;
82 u16_be content_count;
83 u16_be boot_content;
84 std::array<u8, 2> reserved_4;
85 std::array<u8, 0x20> contentinfo_hash;
86 std::array<ContentInfo, 64> contentinfo;
87 };
88
89 static_assert(sizeof(Body) == 0x9C4, "TMD body structure size is wrong");
90
91#pragma pack(pop)
92
93 explicit TitleMetadata(std::string& path) : filepath(std::move(path)) {}
94 Loader::ResultStatus Load();
95 Loader::ResultStatus Save();
96
97 u64 GetTitleID() const;
98 u32 GetTitleType() const;
99 u16 GetTitleVersion() const;
100 u64 GetSystemVersion() const;
101 size_t GetContentCount() const;
102 u32 GetBootContentID() const;
103 u32 GetManualContentID() const;
104 u32 GetDLPContentID() const;
105
106 void SetTitleID(u64 title_id);
107 void SetTitleType(u32 type);
108 void SetTitleVersion(u16 version);
109 void SetSystemVersion(u64 version);
110 void AddContentChunk(const ContentChunk& chunk);
111
112 void Print() const;
113
114private:
115 enum TMDContentIndex { Main = 0, Manual = 1, DLP = 2 };
116
117 Body tmd_body;
118 u32_be signature_type;
119 std::vector<u8> tmd_signature;
120 std::vector<ContentChunk> tmd_chunks;
121
122 std::string filepath;
123};
124
125} // namespace FileSys
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 4f7d54a33..e67394177 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -2,14 +2,55 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <cmath> 5#include <cmath>
7#include "common/assert.h" 6#include <mutex>
8#include "core/3ds.h"
9#include "core/core.h"
10#include "core/frontend/emu_window.h" 7#include "core/frontend/emu_window.h"
8#include "core/frontend/input.h"
11#include "core/settings.h" 9#include "core/settings.h"
12 10
11class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
12 public std::enable_shared_from_this<TouchState> {
13public:
14 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage&) override {
15 return std::make_unique<Device>(shared_from_this());
16 }
17
18 std::mutex mutex;
19
20 bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false
21
22 float touch_x = 0.0f; ///< Touchpad X-position
23 float touch_y = 0.0f; ///< Touchpad Y-position
24
25private:
26 class Device : public Input::TouchDevice {
27 public:
28 explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
29 std::tuple<float, float, bool> GetStatus() const override {
30 if (auto state = touch_state.lock()) {
31 std::lock_guard<std::mutex> guard(state->mutex);
32 return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
33 }
34 return std::make_tuple(0.0f, 0.0f, false);
35 }
36
37 private:
38 std::weak_ptr<TouchState> touch_state;
39 };
40};
41
42EmuWindow::EmuWindow() {
43 // TODO: Find a better place to set this.
44 config.min_client_area_size = std::make_pair(400u, 480u);
45 active_config = config;
46 touch_state = std::make_shared<TouchState>();
47 Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
48}
49
50EmuWindow::~EmuWindow() {
51 Input::UnregisterFactory<Input::TouchDevice>("emu_window");
52}
53
13/** 54/**
14 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout 55 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
15 * @param layout FramebufferLayout object describing the framebuffer size and screen positions 56 * @param layout FramebufferLayout object describing the framebuffer size and screen positions
@@ -38,22 +79,26 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
38 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) 79 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
39 return; 80 return;
40 81
41 touch_x = Core::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) / 82 std::lock_guard<std::mutex> guard(touch_state->mutex);
42 (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left); 83 touch_state->touch_x =
43 touch_y = Core::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) / 84 static_cast<float>(framebuffer_x - framebuffer_layout.bottom_screen.left) /
44 (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top); 85 (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
86 touch_state->touch_y =
87 static_cast<float>(framebuffer_y - framebuffer_layout.bottom_screen.top) /
88 (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
45 89
46 touch_pressed = true; 90 touch_state->touch_pressed = true;
47} 91}
48 92
49void EmuWindow::TouchReleased() { 93void EmuWindow::TouchReleased() {
50 touch_pressed = false; 94 std::lock_guard<std::mutex> guard(touch_state->mutex);
51 touch_x = 0; 95 touch_state->touch_pressed = false;
52 touch_y = 0; 96 touch_state->touch_x = 0;
97 touch_state->touch_y = 0;
53} 98}
54 99
55void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { 100void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
56 if (!touch_pressed) 101 if (!touch_state->touch_pressed)
57 return; 102 return;
58 103
59 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) 104 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
@@ -62,29 +107,6 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
62 TouchPressed(framebuffer_x, framebuffer_y); 107 TouchPressed(framebuffer_x, framebuffer_y);
63} 108}
64 109
65void EmuWindow::AccelerometerChanged(float x, float y, float z) {
66 constexpr float coef = 512;
67
68 std::lock_guard<std::mutex> lock(accel_mutex);
69
70 // TODO(wwylele): do a time stretch as it in GyroscopeChanged
71 // The time stretch formula should be like
72 // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
73 accel_x = static_cast<s16>(x * coef);
74 accel_y = static_cast<s16>(y * coef);
75 accel_z = static_cast<s16>(z * coef);
76}
77
78void EmuWindow::GyroscopeChanged(float x, float y, float z) {
79 constexpr float FULL_FPS = 60;
80 float coef = GetGyroscopeRawToDpsCoefficient();
81 float stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale();
82 std::lock_guard<std::mutex> lock(gyro_mutex);
83 gyro_x = static_cast<s16>(x * coef * stretch);
84 gyro_y = static_cast<s16>(y * coef * stretch);
85 gyro_z = static_cast<s16>(z * coef * stretch);
86}
87
88void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { 110void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
89 Layout::FramebufferLayout layout; 111 Layout::FramebufferLayout layout;
90 if (Settings::values.custom_layout == true) { 112 if (Settings::values.custom_layout == true) {
@@ -97,6 +119,9 @@ void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height)
97 case Settings::LayoutOption::LargeScreen: 119 case Settings::LayoutOption::LargeScreen:
98 layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen); 120 layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen);
99 break; 121 break;
122 case Settings::LayoutOption::SideScreen:
123 layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen);
124 break;
100 case Settings::LayoutOption::Default: 125 case Settings::LayoutOption::Default:
101 default: 126 default:
102 layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen); 127 layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen);
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 9414123a4..c10dee51b 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -4,11 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <mutex> 7#include <memory>
8#include <tuple> 8#include <tuple>
9#include <utility> 9#include <utility>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/math_util.h"
12#include "core/frontend/framebuffer_layout.h" 11#include "core/frontend/framebuffer_layout.h"
13 12
14/** 13/**
@@ -69,84 +68,6 @@ public:
69 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); 68 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
70 69
71 /** 70 /**
72 * Signal accelerometer state has changed.
73 * @param x X-axis accelerometer value
74 * @param y Y-axis accelerometer value
75 * @param z Z-axis accelerometer value
76 * @note all values are in unit of g (gravitational acceleration).
77 * e.g. x = 1.0 means 9.8m/s^2 in x direction.
78 * @see GetAccelerometerState for axis explanation.
79 */
80 void AccelerometerChanged(float x, float y, float z);
81
82 /**
83 * Signal gyroscope state has changed.
84 * @param x X-axis accelerometer value
85 * @param y Y-axis accelerometer value
86 * @param z Z-axis accelerometer value
87 * @note all values are in deg/sec.
88 * @see GetGyroscopeState for axis explanation.
89 */
90 void GyroscopeChanged(float x, float y, float z);
91
92 /**
93 * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
94 * @note This should be called by the core emu thread to get a state set by the window thread.
95 * @todo Fix this function to be thread-safe.
96 * @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and
97 * `pressed` is true if the touch screen is currently being pressed
98 */
99 std::tuple<u16, u16, bool> GetTouchState() const {
100 return std::make_tuple(touch_x, touch_y, touch_pressed);
101 }
102
103 /**
104 * Gets the current accelerometer state (acceleration along each three axis).
105 * Axis explained:
106 * +x is the same direction as LEFT on D-pad.
107 * +y is normal to the touch screen, pointing outward.
108 * +z is the same direction as UP on D-pad.
109 * Units:
110 * 1 unit of return value = 1/512 g (measured by hw test),
111 * where g is the gravitational acceleration (9.8 m/sec2).
112 * @note This should be called by the core emu thread to get a state set by the window thread.
113 * @return std::tuple of (x, y, z)
114 */
115 std::tuple<s16, s16, s16> GetAccelerometerState() {
116 std::lock_guard<std::mutex> lock(accel_mutex);
117 return std::make_tuple(accel_x, accel_y, accel_z);
118 }
119
120 /**
121 * Gets the current gyroscope state (angular rates about each three axis).
122 * Axis explained:
123 * +x is the same direction as LEFT on D-pad.
124 * +y is normal to the touch screen, pointing outward.
125 * +z is the same direction as UP on D-pad.
126 * Orientation is determined by right-hand rule.
127 * Units:
128 * 1 unit of return value = (1/coef) deg/sec,
129 * where coef is the return value of GetGyroscopeRawToDpsCoefficient().
130 * @note This should be called by the core emu thread to get a state set by the window thread.
131 * @return std::tuple of (x, y, z)
132 */
133 std::tuple<s16, s16, s16> GetGyroscopeState() {
134 std::lock_guard<std::mutex> lock(gyro_mutex);
135 return std::make_tuple(gyro_x, gyro_y, gyro_z);
136 }
137
138 /**
139 * Gets the coefficient for units conversion of gyroscope state.
140 * The conversion formula is r = coefficient * v,
141 * where v is angular rate in deg/sec,
142 * and r is the gyroscope state.
143 * @return float-type coefficient
144 */
145 f32 GetGyroscopeRawToDpsCoefficient() const {
146 return 14.375f; // taken from hw test, and gyroscope's document
147 }
148
149 /**
150 * Returns currently active configuration. 71 * Returns currently active configuration.
151 * @note Accesses to the returned object need not be consistent because it may be modified in 72 * @note Accesses to the returned object need not be consistent because it may be modified in
152 * another thread 73 * another thread
@@ -180,21 +101,8 @@ public:
180 void UpdateCurrentFramebufferLayout(unsigned width, unsigned height); 101 void UpdateCurrentFramebufferLayout(unsigned width, unsigned height);
181 102
182protected: 103protected:
183 EmuWindow() { 104 EmuWindow();
184 // TODO: Find a better place to set this. 105 virtual ~EmuWindow();
185 config.min_client_area_size = std::make_pair(400u, 480u);
186 active_config = config;
187 touch_x = 0;
188 touch_y = 0;
189 touch_pressed = false;
190 accel_x = 0;
191 accel_y = -512;
192 accel_z = 0;
193 gyro_x = 0;
194 gyro_y = 0;
195 gyro_z = 0;
196 }
197 virtual ~EmuWindow() {}
198 106
199 /** 107 /**
200 * Processes any pending configuration changes from the last SetConfig call. 108 * Processes any pending configuration changes from the last SetConfig call.
@@ -250,20 +158,8 @@ private:
250 /// ProcessConfigurationChanges) 158 /// ProcessConfigurationChanges)
251 WindowConfig active_config; ///< Internal active configuration 159 WindowConfig active_config; ///< Internal active configuration
252 160
253 bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false 161 class TouchState;
254 162 std::shared_ptr<TouchState> touch_state;
255 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
256 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
257
258 std::mutex accel_mutex;
259 s16 accel_x; ///< Accelerometer X-axis value in native 3DS units
260 s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units
261 s16 accel_z; ///< Accelerometer Z-axis value in native 3DS units
262
263 std::mutex gyro_mutex;
264 s16 gyro_x; ///< Gyroscope X-axis value in native 3DS units
265 s16 gyro_y; ///< Gyroscope Y-axis value in native 3DS units
266 s16 gyro_z; ///< Gyroscope Z-axis value in native 3DS units
267 163
268 /** 164 /**
269 * Clip the provided coordinates to be inside the touchscreen area. 165 * Clip the provided coordinates to be inside the touchscreen area.
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index d2d02f9ff..e9f778fcb 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -141,6 +141,40 @@ FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool swapped
141 return res; 141 return res;
142} 142}
143 143
144FramebufferLayout SideFrameLayout(unsigned width, unsigned height, bool swapped) {
145 ASSERT(width > 0);
146 ASSERT(height > 0);
147
148 FramebufferLayout res{width, height, true, true, {}, {}};
149 // Aspect ratio of both screens side by side
150 const float emulation_aspect_ratio = static_cast<float>(Core::kScreenTopHeight) /
151 (Core::kScreenTopWidth + Core::kScreenBottomWidth);
152 float window_aspect_ratio = static_cast<float>(height) / width;
153 MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height};
154 // Find largest Rectangle that can fit in the window size with the given aspect ratio
155 MathUtil::Rectangle<unsigned> screen_rect =
156 maxRectangle(screen_window_area, emulation_aspect_ratio);
157 // Find sizes of top and bottom screen
158 MathUtil::Rectangle<unsigned> top_screen = maxRectangle(screen_rect, TOP_SCREEN_ASPECT_RATIO);
159 MathUtil::Rectangle<unsigned> bot_screen = maxRectangle(screen_rect, BOT_SCREEN_ASPECT_RATIO);
160
161 if (window_aspect_ratio < emulation_aspect_ratio) {
162 // Apply borders to the left and right sides of the window.
163 u32 shift_horizontal = (screen_window_area.GetWidth() - screen_rect.GetWidth()) / 2;
164 top_screen = top_screen.TranslateX(shift_horizontal);
165 bot_screen = bot_screen.TranslateX(shift_horizontal);
166 } else {
167 // Window is narrower than the emulation content => apply borders to the top and bottom
168 u32 shift_vertical = (screen_window_area.GetHeight() - screen_rect.GetHeight()) / 2;
169 top_screen = top_screen.TranslateY(shift_vertical);
170 bot_screen = bot_screen.TranslateY(shift_vertical);
171 }
172 // Move the top screen to the right if we are swapped.
173 res.top_screen = swapped ? top_screen.TranslateX(bot_screen.GetWidth()) : top_screen;
174 res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateX(top_screen.GetWidth());
175 return res;
176}
177
144FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) { 178FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) {
145 ASSERT(width > 0); 179 ASSERT(width > 0);
146 ASSERT(height > 0); 180 ASSERT(height > 0);
@@ -158,4 +192,4 @@ FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) {
158 res.bottom_screen = bot_screen; 192 res.bottom_screen = bot_screen;
159 return res; 193 return res;
160} 194}
161} 195} // namespace Layout
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 9a7738969..4983cf103 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -54,6 +54,17 @@ FramebufferLayout SingleFrameLayout(unsigned width, unsigned height, bool is_swa
54FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped); 54FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped);
55 55
56/** 56/**
57* Factory method for constructing a Frame with the Top screen and bottom
58* screen side by side
59* This is useful for devices with small screens, like the GPDWin
60* @param width Window framebuffer width in pixels
61* @param height Window framebuffer height in pixels
62* @param is_swapped if true, the bottom screen will be the left display
63* @return Newly created FramebufferLayout object with default screen regions initialized
64*/
65FramebufferLayout SideFrameLayout(unsigned width, unsigned height, bool is_swapped);
66
67/**
57 * Factory method for constructing a custom FramebufferLayout 68 * Factory method for constructing a custom FramebufferLayout
58 * @param width Window framebuffer width in pixels 69 * @param width Window framebuffer width in pixels
59 * @param height Window framebuffer height in pixels 70 * @param height Window framebuffer height in pixels
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 0a5713dc0..8c256beb5 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -11,6 +11,7 @@
11#include <utility> 11#include <utility>
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/param_package.h" 13#include "common/param_package.h"
14#include "common/vector_math.h"
14 15
15namespace Input { 16namespace Input {
16 17
@@ -107,4 +108,28 @@ using ButtonDevice = InputDevice<bool>;
107 */ 108 */
108using AnalogDevice = InputDevice<std::tuple<float, float>>; 109using AnalogDevice = InputDevice<std::tuple<float, float>>;
109 110
111/**
112 * A motion device is an input device that returns a tuple of accelerometer state vector and
113 * gyroscope state vector.
114 *
115 * For both vectors:
116 * x+ is the same direction as LEFT on D-pad.
117 * y+ is normal to the touch screen, pointing outward.
118 * z+ is the same direction as UP on D-pad.
119 *
120 * For accelerometer state vector
121 * Units: g (gravitational acceleration)
122 *
123 * For gyroscope state vector:
124 * Orientation is determined by right-hand rule.
125 * Units: deg/sec
126 */
127using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>>>;
128
129/**
130 * A touch device is an input device that returns a tuple of two floats and a bool. The floats are
131 * x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed.
132 */
133using TouchDevice = InputDevice<std::tuple<float, float, bool>>;
134
110} // namespace Input 135} // namespace Input
diff --git a/src/core/frontend/motion_emu.cpp b/src/core/frontend/motion_emu.cpp
deleted file mode 100644
index 9a5b3185d..000000000
--- a/src/core/frontend/motion_emu.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/math_util.h"
6#include "common/quaternion.h"
7#include "core/frontend/emu_window.h"
8#include "core/frontend/motion_emu.h"
9
10namespace Motion {
11
12static constexpr int update_millisecond = 100;
13static constexpr auto update_duration =
14 std::chrono::duration_cast<std::chrono::steady_clock::duration>(
15 std::chrono::milliseconds(update_millisecond));
16
17MotionEmu::MotionEmu(EmuWindow& emu_window)
18 : motion_emu_thread(&MotionEmu::MotionEmuThread, this, std::ref(emu_window)) {}
19
20MotionEmu::~MotionEmu() {
21 if (motion_emu_thread.joinable()) {
22 shutdown_event.Set();
23 motion_emu_thread.join();
24 }
25}
26
27void MotionEmu::MotionEmuThread(EmuWindow& emu_window) {
28 auto update_time = std::chrono::steady_clock::now();
29 Math::Quaternion<float> q = MakeQuaternion(Math::Vec3<float>(), 0);
30 Math::Quaternion<float> old_q;
31
32 while (!shutdown_event.WaitUntil(update_time)) {
33 update_time += update_duration;
34 old_q = q;
35
36 {
37 std::lock_guard<std::mutex> guard(tilt_mutex);
38
39 // Find the quaternion describing current 3DS tilting
40 q = MakeQuaternion(Math::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x),
41 tilt_angle);
42 }
43
44 auto inv_q = q.Inverse();
45
46 // Set the gravity vector in world space
47 auto gravity = Math::MakeVec(0.0f, -1.0f, 0.0f);
48
49 // Find the angular rate vector in world space
50 auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
51 angular_rate *= 1000 / update_millisecond / MathUtil::PI * 180;
52
53 // Transform the two vectors from world space to 3DS space
54 gravity = QuaternionRotate(inv_q, gravity);
55 angular_rate = QuaternionRotate(inv_q, angular_rate);
56
57 // Update the sensor state
58 emu_window.AccelerometerChanged(gravity.x, gravity.y, gravity.z);
59 emu_window.GyroscopeChanged(angular_rate.x, angular_rate.y, angular_rate.z);
60 }
61}
62
63void MotionEmu::BeginTilt(int x, int y) {
64 mouse_origin = Math::MakeVec(x, y);
65 is_tilting = true;
66}
67
68void MotionEmu::Tilt(int x, int y) {
69 constexpr float SENSITIVITY = 0.01f;
70 auto mouse_move = Math::MakeVec(x, y) - mouse_origin;
71 if (is_tilting) {
72 std::lock_guard<std::mutex> guard(tilt_mutex);
73 if (mouse_move.x == 0 && mouse_move.y == 0) {
74 tilt_angle = 0;
75 } else {
76 tilt_direction = mouse_move.Cast<float>();
77 tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * SENSITIVITY, 0.0f,
78 MathUtil::PI * 0.5f);
79 }
80 }
81}
82
83void MotionEmu::EndTilt() {
84 std::lock_guard<std::mutex> guard(tilt_mutex);
85 tilt_angle = 0;
86 is_tilting = false;
87}
88
89} // namespace Motion
diff --git a/src/core/frontend/motion_emu.h b/src/core/frontend/motion_emu.h
deleted file mode 100644
index 99d41a726..000000000
--- a/src/core/frontend/motion_emu.h
+++ /dev/null
@@ -1,52 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6#include "common/thread.h"
7#include "common/vector_math.h"
8
9class EmuWindow;
10
11namespace Motion {
12
13class MotionEmu final {
14public:
15 MotionEmu(EmuWindow& emu_window);
16 ~MotionEmu();
17
18 /**
19 * Signals that a motion sensor tilt has begun.
20 * @param x the x-coordinate of the cursor
21 * @param y the y-coordinate of the cursor
22 */
23 void BeginTilt(int x, int y);
24
25 /**
26 * Signals that a motion sensor tilt is occurring.
27 * @param x the x-coordinate of the cursor
28 * @param y the y-coordinate of the cursor
29 */
30 void Tilt(int x, int y);
31
32 /**
33 * Signals that a motion sensor tilt has ended.
34 */
35 void EndTilt();
36
37private:
38 Math::Vec2<int> mouse_origin;
39
40 std::mutex tilt_mutex;
41 Math::Vec2<float> tilt_direction;
42 float tilt_angle = 0;
43
44 bool is_tilting = false;
45
46 Common::Event shutdown_event;
47 std::thread motion_emu_thread;
48
49 void MotionEmuThread(EmuWindow& emu_window);
50};
51
52} // namespace Motion
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 123fe7cd4..d6be16ef6 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -644,7 +644,7 @@ static void ReadMemory() {
644 644
645 auto start_offset = command_buffer + 1; 645 auto start_offset = command_buffer + 1;
646 auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); 646 auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
647 PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset)); 647 VAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
648 648
649 start_offset = addr_pos + 1; 649 start_offset = addr_pos + 1;
650 u32 len = 650 u32 len =
@@ -656,12 +656,14 @@ static void ReadMemory() {
656 SendReply("E01"); 656 SendReply("E01");
657 } 657 }
658 658
659 const u8* data = Memory::GetPointer(addr); 659 if (!Memory::IsValidVirtualAddress(addr)) {
660 if (!data) {
661 return SendReply("E00"); 660 return SendReply("E00");
662 } 661 }
663 662
664 MemToGdbHex(reply, data, len); 663 std::vector<u8> data(len);
664 Memory::ReadBlock(addr, data.data(), len);
665
666 MemToGdbHex(reply, data.data(), len);
665 reply[len * 2] = '\0'; 667 reply[len * 2] = '\0';
666 SendReply(reinterpret_cast<char*>(reply)); 668 SendReply(reinterpret_cast<char*>(reply));
667} 669}
@@ -670,18 +672,20 @@ static void ReadMemory() {
670static void WriteMemory() { 672static void WriteMemory() {
671 auto start_offset = command_buffer + 1; 673 auto start_offset = command_buffer + 1;
672 auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); 674 auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
673 PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset)); 675 VAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
674 676
675 start_offset = addr_pos + 1; 677 start_offset = addr_pos + 1;
676 auto len_pos = std::find(start_offset, command_buffer + command_length, ':'); 678 auto len_pos = std::find(start_offset, command_buffer + command_length, ':');
677 u32 len = HexToInt(start_offset, static_cast<u32>(len_pos - start_offset)); 679 u32 len = HexToInt(start_offset, static_cast<u32>(len_pos - start_offset));
678 680
679 u8* dst = Memory::GetPointer(addr); 681 if (!Memory::IsValidVirtualAddress(addr)) {
680 if (!dst) {
681 return SendReply("E00"); 682 return SendReply("E00");
682 } 683 }
683 684
684 GdbHexToMem(dst, len_pos + 1, len); 685 std::vector<u8> data(len);
686
687 GdbHexToMem(data.data(), len_pos + 1, len);
688 Memory::WriteBlock(addr, data.data(), len);
685 SendReply("OK"); 689 SendReply("OK");
686} 690}
687 691
@@ -946,7 +950,7 @@ static void Init(u16 port) {
946 WSAStartup(MAKEWORD(2, 2), &InitData); 950 WSAStartup(MAKEWORD(2, 2), &InitData);
947#endif 951#endif
948 952
949 int tmpsock = socket(PF_INET, SOCK_STREAM, 0); 953 int tmpsock = static_cast<int>(socket(PF_INET, SOCK_STREAM, 0));
950 if (tmpsock == -1) { 954 if (tmpsock == -1) {
951 LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket"); 955 LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket");
952 } 956 }
@@ -973,7 +977,7 @@ static void Init(u16 port) {
973 sockaddr_in saddr_client; 977 sockaddr_in saddr_client;
974 sockaddr* client_addr = reinterpret_cast<sockaddr*>(&saddr_client); 978 sockaddr* client_addr = reinterpret_cast<sockaddr*>(&saddr_client);
975 socklen_t client_addrlen = sizeof(saddr_client); 979 socklen_t client_addrlen = sizeof(saddr_client);
976 gdbserver_socket = accept(tmpsock, client_addr, &client_addrlen); 980 gdbserver_socket = static_cast<int>(accept(tmpsock, client_addr, &client_addrlen));
977 if (gdbserver_socket < 0) { 981 if (gdbserver_socket < 0) {
978 // In the case that we couldn't start the server for whatever reason, just start CPU 982 // In the case that we couldn't start the server for whatever reason, just start CPU
979 // execution like normal. 983 // execution like normal.
diff --git a/src/core/hle/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp
index 75d7fd9fc..518f371f5 100644
--- a/src/core/hle/applets/erreula.cpp
+++ b/src/core/hle/applets/erreula.cpp
@@ -31,8 +31,8 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param
31 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); 31 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
32 // Create a SharedMemory that directly points to this heap block. 32 // Create a SharedMemory that directly points to this heap block.
33 framebuffer_memory = Kernel::SharedMemory::CreateForApplet( 33 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
34 heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite, 34 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
35 MemoryPermission::ReadWrite, "ErrEula Memory"); 35 "ErrEula Memory");
36 36
37 // Send the response message with the newly created SharedMemory 37 // Send the response message with the newly created SharedMemory
38 Service::APT::MessageParameter result; 38 Service::APT::MessageParameter result;
diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp
index 89f08daa2..f225c23a5 100644
--- a/src/core/hle/applets/mii_selector.cpp
+++ b/src/core/hle/applets/mii_selector.cpp
@@ -38,8 +38,8 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
38 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); 38 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
39 // Create a SharedMemory that directly points to this heap block. 39 // Create a SharedMemory that directly points to this heap block.
40 framebuffer_memory = Kernel::SharedMemory::CreateForApplet( 40 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
41 heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite, 41 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
42 MemoryPermission::ReadWrite, "MiiSelector Memory"); 42 "MiiSelector Memory");
43 43
44 // Send the response message with the newly created SharedMemory 44 // Send the response message with the newly created SharedMemory
45 Service::APT::MessageParameter result; 45 Service::APT::MessageParameter result;
@@ -66,7 +66,7 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa
66 // continue. 66 // continue.
67 MiiResult result; 67 MiiResult result;
68 memset(&result, 0, sizeof(result)); 68 memset(&result, 0, sizeof(result));
69 result.result_code = 0; 69 result.return_code = 0;
70 70
71 // Let the application know that we're closing 71 // Let the application know that we're closing
72 Service::APT::MessageParameter message; 72 Service::APT::MessageParameter message;
@@ -82,5 +82,5 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa
82} 82}
83 83
84void MiiSelector::Update() {} 84void MiiSelector::Update() {}
85} 85} // namespace Applets
86} // namespace 86} // namespace HLE
diff --git a/src/core/hle/applets/mii_selector.h b/src/core/hle/applets/mii_selector.h
index ec00e29d2..136ce8948 100644
--- a/src/core/hle/applets/mii_selector.h
+++ b/src/core/hle/applets/mii_selector.h
@@ -16,51 +16,46 @@ namespace HLE {
16namespace Applets { 16namespace Applets {
17 17
18struct MiiConfig { 18struct MiiConfig {
19 u8 unk_000; 19 u8 enable_cancel_button;
20 u8 unk_001; 20 u8 enable_guest_mii;
21 u8 unk_002; 21 u8 show_on_top_screen;
22 u8 unk_003; 22 INSERT_PADDING_BYTES(5);
23 u8 unk_004; 23 u16 title[0x40];
24 INSERT_PADDING_BYTES(4);
25 u8 show_guest_miis;
24 INSERT_PADDING_BYTES(3); 26 INSERT_PADDING_BYTES(3);
25 u16 unk_008; 27 u32 initially_selected_mii_index;
26 INSERT_PADDING_BYTES(0x82); 28 u8 guest_mii_whitelist[6];
27 u8 unk_08C; 29 u8 user_mii_whitelist[0x64];
28 INSERT_PADDING_BYTES(3);
29 u16 unk_090;
30 INSERT_PADDING_BYTES(2); 30 INSERT_PADDING_BYTES(2);
31 u32 unk_094; 31 u32 magic_value;
32 u16 unk_098;
33 u8 unk_09A[0x64];
34 u8 unk_0FE;
35 u8 unk_0FF;
36 u32 unk_100;
37}; 32};
38
39static_assert(sizeof(MiiConfig) == 0x104, "MiiConfig structure has incorrect size"); 33static_assert(sizeof(MiiConfig) == 0x104, "MiiConfig structure has incorrect size");
40#define ASSERT_REG_POSITION(field_name, position) \ 34#define ASSERT_REG_POSITION(field_name, position) \
41 static_assert(offsetof(MiiConfig, field_name) == position, \ 35 static_assert(offsetof(MiiConfig, field_name) == position, \
42 "Field " #field_name " has invalid position") 36 "Field " #field_name " has invalid position")
43ASSERT_REG_POSITION(unk_008, 0x08); 37ASSERT_REG_POSITION(title, 0x08);
44ASSERT_REG_POSITION(unk_08C, 0x8C); 38ASSERT_REG_POSITION(show_guest_miis, 0x8C);
45ASSERT_REG_POSITION(unk_090, 0x90); 39ASSERT_REG_POSITION(initially_selected_mii_index, 0x90);
46ASSERT_REG_POSITION(unk_094, 0x94); 40ASSERT_REG_POSITION(guest_mii_whitelist, 0x94);
47ASSERT_REG_POSITION(unk_0FE, 0xFE);
48#undef ASSERT_REG_POSITION 41#undef ASSERT_REG_POSITION
49 42
50struct MiiResult { 43struct MiiResult {
51 u32 result_code; 44 u32 return_code;
52 u8 unk_04; 45 u32 is_guest_mii_selected;
53 INSERT_PADDING_BYTES(7); 46 u32 selected_guest_mii_index;
54 u8 unk_0C[0x60]; 47 // TODO(mailwl): expand to Mii Format structure: https://www.3dbrew.org/wiki/Mii
55 u8 unk_6C[0x16]; 48 u8 selected_mii_data[0x5C];
56 INSERT_PADDING_BYTES(2); 49 INSERT_PADDING_BYTES(2);
50 u16 mii_data_checksum;
51 u16 guest_mii_name[0xC];
57}; 52};
58static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size"); 53static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size");
59#define ASSERT_REG_POSITION(field_name, position) \ 54#define ASSERT_REG_POSITION(field_name, position) \
60 static_assert(offsetof(MiiResult, field_name) == position, \ 55 static_assert(offsetof(MiiResult, field_name) == position, \
61 "Field " #field_name " has invalid position") 56 "Field " #field_name " has invalid position")
62ASSERT_REG_POSITION(unk_0C, 0x0C); 57ASSERT_REG_POSITION(selected_mii_data, 0x0C);
63ASSERT_REG_POSITION(unk_6C, 0x6C); 58ASSERT_REG_POSITION(guest_mii_name, 0x6C);
64#undef ASSERT_REG_POSITION 59#undef ASSERT_REG_POSITION
65 60
66class MiiSelector final : public Applet { 61class MiiSelector final : public Applet {
@@ -79,5 +74,5 @@ private:
79 74
80 MiiConfig config; 75 MiiConfig config;
81}; 76};
82} 77} // namespace Applets
83} // namespace 78} // namespace HLE
diff --git a/src/core/hle/applets/mint.cpp b/src/core/hle/applets/mint.cpp
index 31a79ea17..50d79190b 100644
--- a/src/core/hle/applets/mint.cpp
+++ b/src/core/hle/applets/mint.cpp
@@ -31,8 +31,8 @@ ResultCode Mint::ReceiveParameter(const Service::APT::MessageParameter& paramete
31 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); 31 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
32 // Create a SharedMemory that directly points to this heap block. 32 // Create a SharedMemory that directly points to this heap block.
33 framebuffer_memory = Kernel::SharedMemory::CreateForApplet( 33 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
34 heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite, 34 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
35 MemoryPermission::ReadWrite, "Mint Memory"); 35 "Mint Memory");
36 36
37 // Send the response message with the newly created SharedMemory 37 // Send the response message with the newly created SharedMemory
38 Service::APT::MessageParameter result; 38 Service::APT::MessageParameter result;
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp
index fdf8807b0..0bc471a3a 100644
--- a/src/core/hle/applets/swkbd.cpp
+++ b/src/core/hle/applets/swkbd.cpp
@@ -41,8 +41,8 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
41 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); 41 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
42 // Create a SharedMemory that directly points to this heap block. 42 // Create a SharedMemory that directly points to this heap block.
43 framebuffer_memory = Kernel::SharedMemory::CreateForApplet( 43 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
44 heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite, 44 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
45 MemoryPermission::ReadWrite, "SoftwareKeyboard Memory"); 45 "SoftwareKeyboard Memory");
46 46
47 // Send the response message with the newly created SharedMemory 47 // Send the response message with the newly created SharedMemory
48 Service::APT::MessageParameter result; 48 Service::APT::MessageParameter result;
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index bc81c06b4..31fda6db3 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -24,19 +24,37 @@ static inline void FuncReturn(u64 res) {
24 Core::CPU().SetReg(0, res); 24 Core::CPU().SetReg(0, res);
25} 25}
26 26
27/**
28 * HLE a function return (64-bit) from the current ARM11 userland process
29 * @param res Result to return (64-bit)
30 * @todo Verify that this function is correct
31 */
32static inline void FuncReturn64(u64 res) {
33 Core::CPU().SetReg(0, (u32)(res & 0xFFFFFFFF));
34 Core::CPU().SetReg(1, (u32)((res >> 32) & 0xFFFFFFFF));
35}
36
37//////////////////////////////////////////////////////////////////////////////////////////////////// 27////////////////////////////////////////////////////////////////////////////////////////////////////
38// Function wrappers that return type ResultCode 28// Function wrappers that return type ResultCode
39 29
30template <ResultCode func(u64)>
31void Wrap() {
32 FuncReturn(func(PARAM(0)).raw);
33}
34
35template <ResultCode func(u32, u64, u32)>
36void Wrap() {
37 FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw);
38}
39
40template <ResultCode func(u64, u32)>
41void Wrap() {
42 FuncReturn(func(PARAM(0), PARAM(1)).raw);
43}
44
45template <ResultCode func(u64, u64, u64)>
46void Wrap() {
47 FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw);
48}
49
50template <ResultCode func(u64*, u64, u64, u64)>
51void Wrap() {
52 u64 param_1 = 0;
53 u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3)).raw;
54 Core::CPU().SetReg(1, param_1);
55 FuncReturn(retval);
56}
57
40template <ResultCode func(u32, u32, u32, u32)> 58template <ResultCode func(u32, u32, u32, u32)>
41void Wrap() { 59void Wrap() {
42 FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)).raw); 60 FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)).raw);
@@ -58,22 +76,21 @@ void Wrap() {
58 FuncReturn(retval); 76 FuncReturn(retval);
59} 77}
60 78
61template <ResultCode func(s32*, u32*, s32, bool, s64)> 79template <ResultCode func(s32*, VAddr, s32, bool, s64)>
62void Wrap() { 80void Wrap() {
63 s32 param_1 = 0; 81 s32 param_1 = 0;
64 s32 retval = func(&param_1, (Kernel::Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), 82 s32 retval =
65 (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))) 83 func(&param_1, PARAM(1), (s32)PARAM(2), (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0)))
66 .raw; 84 .raw;
67 85
68 Core::CPU().SetReg(1, (u32)param_1); 86 Core::CPU().SetReg(1, (u32)param_1);
69 FuncReturn(retval); 87 FuncReturn(retval);
70} 88}
71 89
72template <ResultCode func(s32*, u32*, s32, u32)> 90template <ResultCode func(s32*, VAddr, s32, u32)>
73void Wrap() { 91void Wrap() {
74 s32 param_1 = 0; 92 s32 param_1 = 0;
75 u32 retval = 93 u32 retval = func(&param_1, PARAM(1), (s32)PARAM(2), PARAM(3)).raw;
76 func(&param_1, (Kernel::Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), PARAM(3)).raw;
77 94
78 Core::CPU().SetReg(1, (u32)param_1); 95 Core::CPU().SetReg(1, (u32)param_1);
79 FuncReturn(retval); 96 FuncReturn(retval);
@@ -85,6 +102,14 @@ void Wrap() {
85 func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(5) << 32) | PARAM(4))).raw); 102 func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(5) << 32) | PARAM(4))).raw);
86} 103}
87 104
105template <ResultCode func(u32, u64*)>
106void Wrap() {
107 u64 param_1 = 0;
108 u32 retval = func(PARAM(0), &param_1).raw;
109 Core::CPU().SetReg(1, param_1);
110 FuncReturn(retval);
111}
112
88template <ResultCode func(u32*)> 113template <ResultCode func(u32*)>
89void Wrap() { 114void Wrap() {
90 u32 param_1 = 0; 115 u32 param_1 = 0;
@@ -100,16 +125,17 @@ void Wrap() {
100 FuncReturn(retval); 125 FuncReturn(retval);
101} 126}
102 127
103template <ResultCode func(MemoryInfo*, PageInfo*, u32)> 128template <ResultCode func(MemoryInfo*, PageInfo*, u64)>
104void Wrap() { 129void Wrap() {
105 MemoryInfo memory_info = {}; 130 MemoryInfo memory_info = {};
106 PageInfo page_info = {}; 131 PageInfo page_info = {};
107 u32 retval = func(&memory_info, &page_info, PARAM(2)).raw; 132 u32 retval = func(&memory_info, &page_info, PARAM(2)).raw;
108 Core::CPU().SetReg(1, memory_info.base_address); 133
109 Core::CPU().SetReg(2, memory_info.size); 134 Memory::Write64(PARAM(0), memory_info.base_address);
110 Core::CPU().SetReg(3, memory_info.permission); 135 Memory::Write64(PARAM(0) + 8, memory_info.size);
111 Core::CPU().SetReg(4, memory_info.state); 136 Memory::Write64(PARAM(0) + 16, memory_info.permission);
112 Core::CPU().SetReg(5, page_info.flags); 137 Memory::Write64(PARAM(0) + 24, memory_info.state);
138
113 FuncReturn(retval); 139 FuncReturn(retval);
114} 140}
115 141
@@ -139,7 +165,7 @@ void Wrap() {
139 FuncReturn(func(PARAM(0), (s32)PARAM(1)).raw); 165 FuncReturn(func(PARAM(0), (s32)PARAM(1)).raw);
140} 166}
141 167
142template <ResultCode func(u32*, u32)> 168template <ResultCode func(u32*, u64)>
143void Wrap() { 169void Wrap() {
144 u32 param_1 = 0; 170 u32 param_1 = 0;
145 u32 retval = func(&param_1, PARAM(1)).raw; 171 u32 retval = func(&param_1, PARAM(1)).raw;
@@ -152,21 +178,6 @@ void Wrap() {
152 FuncReturn(func(PARAM(0)).raw); 178 FuncReturn(func(PARAM(0)).raw);
153} 179}
154 180
155template <ResultCode func(s64*, u32, u32*, u32)>
156void Wrap() {
157 FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1),
158 (u32*)Memory::GetPointer(PARAM(2)), (s32)PARAM(3))
159 .raw);
160}
161
162template <ResultCode func(u32*, const char*)>
163void Wrap() {
164 u32 param_1 = 0;
165 u32 retval = func(&param_1, (char*)Memory::GetPointer(PARAM(1))).raw;
166 Core::CPU().SetReg(1, param_1);
167 FuncReturn(retval);
168}
169
170template <ResultCode func(u32*, s32, s32)> 181template <ResultCode func(u32*, s32, s32)>
171void Wrap() { 182void Wrap() {
172 u32 param_1 = 0; 183 u32 param_1 = 0;
@@ -222,13 +233,11 @@ void Wrap() {
222 FuncReturn(func(PARAM(0), PARAM(1)).raw); 233 FuncReturn(func(PARAM(0), PARAM(1)).raw);
223} 234}
224 235
225template <ResultCode func(Kernel::Handle*, Kernel::Handle*, const char*, u32)> 236template <ResultCode func(Kernel::Handle*, Kernel::Handle*, VAddr, u32)>
226void Wrap() { 237void Wrap() {
227 Kernel::Handle param_1 = 0; 238 Kernel::Handle param_1 = 0;
228 Kernel::Handle param_2 = 0; 239 Kernel::Handle param_2 = 0;
229 u32 retval = func(&param_1, &param_2, 240 u32 retval = func(&param_1, &param_2, PARAM(2), PARAM(3)).raw;
230 reinterpret_cast<const char*>(Memory::GetPointer(PARAM(2))), PARAM(3))
231 .raw;
232 Core::CPU().SetReg(1, param_1); 241 Core::CPU().SetReg(1, param_1);
233 Core::CPU().SetReg(2, param_2); 242 Core::CPU().SetReg(2, param_2);
234 FuncReturn(retval); 243 FuncReturn(retval);
@@ -244,6 +253,11 @@ void Wrap() {
244 FuncReturn(retval); 253 FuncReturn(retval);
245} 254}
246 255
256template <ResultCode func(u32, u32, u32)>
257void Wrap() {
258 FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw);
259}
260
247//////////////////////////////////////////////////////////////////////////////////////////////////// 261////////////////////////////////////////////////////////////////////////////////////////////////////
248// Function wrappers that return type u32 262// Function wrappers that return type u32
249 263
@@ -268,14 +282,14 @@ void Wrap() {
268 func(((s64)PARAM(1) << 32) | PARAM(0)); 282 func(((s64)PARAM(1) << 32) | PARAM(0));
269} 283}
270 284
271template <void func(const char*, int len)> 285template <void func(VAddr, int len)>
272void Wrap() { 286void Wrap() {
273 func((char*)Memory::GetPointer(PARAM(0)), PARAM(1)); 287 func(PARAM(0), PARAM(1));
274} 288}
275 289
276template <void func(u8)> 290template <void func(u64, u64, u64)>
277void Wrap() { 291void Wrap() {
278 func((u8)PARAM(0)); 292 func(PARAM(0), PARAM(1), PARAM(2));
279} 293}
280 294
281#undef PARAM 295#undef PARAM
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index f7f96125a..87ed85df6 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -122,11 +122,11 @@ union StaticBufferDescInfo {
122 BitField<14, 18, u32> size; 122 BitField<14, 18, u32> size;
123}; 123};
124 124
125inline u32 StaticBufferDesc(u32 size, u8 buffer_id) { 125inline u32 StaticBufferDesc(size_t size, u8 buffer_id) {
126 StaticBufferDescInfo info{}; 126 StaticBufferDescInfo info{};
127 info.descriptor_type.Assign(StaticBuffer); 127 info.descriptor_type.Assign(StaticBuffer);
128 info.buffer_id.Assign(buffer_id); 128 info.buffer_id.Assign(buffer_id);
129 info.size.Assign(size); 129 info.size.Assign(static_cast<u32>(size));
130 return info.raw; 130 return info.raw;
131} 131}
132 132
@@ -160,11 +160,11 @@ union MappedBufferDescInfo {
160 BitField<4, 28, u32> size; 160 BitField<4, 28, u32> size;
161}; 161};
162 162
163inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { 163inline u32 MappedBufferDesc(size_t size, MappedBufferPermissions perms) {
164 MappedBufferDescInfo info{}; 164 MappedBufferDescInfo info{};
165 info.flags.Assign(MappedBuffer); 165 info.flags.Assign(MappedBuffer);
166 info.perms.Assign(perms); 166 info.perms.Assign(perms);
167 info.size.Assign(size); 167 info.size.Assign(static_cast<u32>(size));
168 return info.raw; 168 return info.raw;
169} 169}
170 170
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index f0d89cffe..7cb95cbac 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -117,9 +117,9 @@ public:
117 117
118 void PushCurrentPIDHandle(); 118 void PushCurrentPIDHandle();
119 119
120 void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id); 120 void PushStaticBuffer(VAddr buffer_vaddr, size_t size, u8 buffer_id);
121 121
122 void PushMappedBuffer(VAddr buffer_vaddr, u32 size, MappedBufferPermissions perms); 122 void PushMappedBuffer(VAddr buffer_vaddr, size_t size, MappedBufferPermissions perms);
123}; 123};
124 124
125/// Push /// 125/// Push ///
@@ -190,12 +190,12 @@ inline void RequestBuilder::PushCurrentPIDHandle() {
190 Push(u32(0)); 190 Push(u32(0));
191} 191}
192 192
193inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) { 193inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, size_t size, u8 buffer_id) {
194 Push(StaticBufferDesc(size, buffer_id)); 194 Push(StaticBufferDesc(size, buffer_id));
195 Push(buffer_vaddr); 195 Push(buffer_vaddr);
196} 196}
197 197
198inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, u32 size, 198inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, size_t size,
199 MappedBufferPermissions perms) { 199 MappedBufferPermissions perms) {
200 Push(MappedBufferDesc(size, perms)); 200 Push(MappedBufferDesc(size, perms));
201 Push(buffer_vaddr); 201 Push(buffer_vaddr);
@@ -227,8 +227,8 @@ public:
227 bool validateHeader = true) { 227 bool validateHeader = true) {
228 if (validateHeader) 228 if (validateHeader)
229 ValidateHeader(); 229 ValidateHeader();
230 Header builderHeader{ 230 Header builderHeader{MakeHeader(static_cast<u16>(header.command_id), normal_params_size,
231 MakeHeader(header.command_id, normal_params_size, translate_params_size)}; 231 translate_params_size)};
232 if (context != nullptr) 232 if (context != nullptr)
233 return {*context, builderHeader}; 233 return {*context, builderHeader};
234 else 234 else
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 5ebe2eca4..6020e9764 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -37,7 +37,7 @@ SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const
37 37
38u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) { 38u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) {
39 request_handles.push_back(std::move(object)); 39 request_handles.push_back(std::move(object));
40 return request_handles.size() - 1; 40 return static_cast<u32>(request_handles.size() - 1);
41} 41}
42 42
43void HLERequestContext::ClearIncomingObjects() { 43void HLERequestContext::ClearIncomingObjects() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 9cf288b08..73fab3981 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -8,6 +8,7 @@
8#include <string> 8#include <string>
9#include <utility> 9#include <utility>
10#include <boost/smart_ptr/intrusive_ptr.hpp> 10#include <boost/smart_ptr/intrusive_ptr.hpp>
11#include "common/assert.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12 13
13namespace Kernel { 14namespace Kernel {
@@ -84,6 +85,8 @@ public:
84 case HandleType::ClientSession: 85 case HandleType::ClientSession:
85 return false; 86 return false;
86 } 87 }
88
89 UNREACHABLE();
87 } 90 }
88 91
89public: 92public:
@@ -129,4 +132,4 @@ void Init(u32 system_mode);
129/// Shutdown the kernel 132/// Shutdown the kernel
130void Shutdown(); 133void Shutdown();
131 134
132} // namespace 135} // namespace Kernel
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
index 496d07cb5..7f27e9655 100644
--- a/src/core/hle/kernel/memory.cpp
+++ b/src/core/hle/kernel/memory.cpp
@@ -8,7 +8,6 @@
8#include <memory> 8#include <memory>
9#include <utility> 9#include <utility>
10#include <vector> 10#include <vector>
11#include "audio_core/audio_core.h"
12#include "common/assert.h" 11#include "common/assert.h"
13#include "common/common_types.h" 12#include "common/common_types.h"
14#include "common/logging/log.h" 13#include "common/logging/log.h"
@@ -24,7 +23,7 @@
24 23
25namespace Kernel { 24namespace Kernel {
26 25
27static MemoryRegionInfo memory_regions[3]; 26MemoryRegionInfo memory_regions[3];
28 27
29/// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system 28/// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system
30/// memory configuration type. 29/// memory configuration type.
@@ -96,9 +95,6 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) {
96 } 95 }
97} 96}
98 97
99std::array<u8, Memory::VRAM_SIZE> vram;
100std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram;
101
102void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) { 98void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) {
103 using namespace Memory; 99 using namespace Memory;
104 100
@@ -143,30 +139,14 @@ void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mappin
143 return; 139 return;
144 } 140 }
145 141
146 // TODO(yuriks): Use GetPhysicalPointer when that becomes independent of the virtual 142 u8* target_pointer = Memory::GetPhysicalPointer(area->paddr_base + offset_into_region);
147 // mappings.
148 u8* target_pointer = nullptr;
149 switch (area->paddr_base) {
150 case VRAM_PADDR:
151 target_pointer = vram.data();
152 break;
153 case DSP_RAM_PADDR:
154 target_pointer = AudioCore::GetDspMemory().data();
155 break;
156 case N3DS_EXTRA_RAM_PADDR:
157 target_pointer = n3ds_extra_ram.data();
158 break;
159 default:
160 UNREACHABLE();
161 }
162 143
163 // TODO(yuriks): This flag seems to have some other effect, but it's unknown what 144 // TODO(yuriks): This flag seems to have some other effect, but it's unknown what
164 MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO; 145 MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO;
165 146
166 auto vma = address_space 147 auto vma =
167 .MapBackingMemory(mapping.address, target_pointer + offset_into_region, 148 address_space.MapBackingMemory(mapping.address, target_pointer, mapping.size, memory_state)
168 mapping.size, memory_state) 149 .Unwrap();
169 .Unwrap();
170 address_space.Reprotect(vma, 150 address_space.Reprotect(vma,
171 mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite); 151 mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite);
172} 152}
diff --git a/src/core/hle/kernel/memory.h b/src/core/hle/kernel/memory.h
index 08c1a9989..da6bb3563 100644
--- a/src/core/hle/kernel/memory.h
+++ b/src/core/hle/kernel/memory.h
@@ -26,4 +26,6 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region);
26 26
27void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); 27void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping);
28void MapSharedPages(VMManager& address_space); 28void MapSharedPages(VMManager& address_space);
29
30extern MemoryRegionInfo memory_regions[3];
29} // namespace Kernel 31} // namespace Kernel
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index cef961289..30dade552 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -25,10 +25,11 @@ void ReleaseThreadMutexes(Thread* thread) {
25Mutex::Mutex() {} 25Mutex::Mutex() {}
26Mutex::~Mutex() {} 26Mutex::~Mutex() {}
27 27
28SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) { 28SharedPtr<Mutex> Mutex::Create(bool initial_locked, VAddr addr, std::string name) {
29 SharedPtr<Mutex> mutex(new Mutex); 29 SharedPtr<Mutex> mutex(new Mutex);
30 30
31 mutex->lock_count = 0; 31 mutex->lock_count = 0;
32 mutex->addr = addr;
32 mutex->name = std::move(name); 33 mutex->name = std::move(name);
33 mutex->holding_thread = nullptr; 34 mutex->holding_thread = nullptr;
34 35
@@ -90,7 +91,7 @@ void Mutex::UpdatePriority() {
90 if (!holding_thread) 91 if (!holding_thread)
91 return; 92 return;
92 93
93 s32 best_priority = THREADPRIO_LOWEST; 94 u32 best_priority = THREADPRIO_LOWEST;
94 for (auto& waiter : GetWaitingThreads()) { 95 for (auto& waiter : GetWaitingThreads()) {
95 if (waiter->current_priority < best_priority) 96 if (waiter->current_priority < best_priority)
96 best_priority = waiter->current_priority; 97 best_priority = waiter->current_priority;
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index bacacd690..503d3ee75 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -21,7 +21,7 @@ public:
21 * @param name Optional name of mutex 21 * @param name Optional name of mutex
22 * @return Pointer to new Mutex object 22 * @return Pointer to new Mutex object
23 */ 23 */
24 static SharedPtr<Mutex> Create(bool initial_locked, std::string name = "Unknown"); 24 static SharedPtr<Mutex> Create(bool initial_locked, VAddr addr, std::string name = "Unknown");
25 25
26 std::string GetTypeName() const override { 26 std::string GetTypeName() const override {
27 return "Mutex"; 27 return "Mutex";
@@ -39,6 +39,7 @@ public:
39 u32 priority; ///< The priority of the mutex, used for priority inheritance. 39 u32 priority; ///< The priority of the mutex, used for priority inheritance.
40 std::string name; ///< Name of mutex (optional) 40 std::string name; ///< Name of mutex (optional)
41 SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex 41 SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex
42 VAddr addr;
42 43
43 /** 44 /**
44 * Elevate the mutex priority to the best priority 45 * Elevate the mutex priority to the best priority
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 84ebdbc58..9e145866f 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -129,7 +129,8 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
129 } 129 }
130 130
131 vm_manager.LogLayout(Log::Level::Debug); 131 vm_manager.LogLayout(Log::Level::Debug);
132 Kernel::SetupMainThread(entry_point, main_thread_priority); 132
133 Kernel::SetupMainThread(entry_point, main_thread_priority, this);
133} 134}
134 135
135void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { 136void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp
index a8f10a3ee..517dc47a8 100644
--- a/src/core/hle/kernel/resource_limit.cpp
+++ b/src/core/hle/kernel/resource_limit.cpp
@@ -61,7 +61,7 @@ s32 ResourceLimit::GetCurrentResourceValue(u32 resource) const {
61 } 61 }
62} 62}
63 63
64s32 ResourceLimit::GetMaxResourceValue(u32 resource) const { 64u32 ResourceLimit::GetMaxResourceValue(u32 resource) const {
65 switch (resource) { 65 switch (resource) {
66 case PRIORITY: 66 case PRIORITY:
67 return max_priority; 67 return max_priority;
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h
index 6cdfbcf8d..42874eb8d 100644
--- a/src/core/hle/kernel/resource_limit.h
+++ b/src/core/hle/kernel/resource_limit.h
@@ -67,7 +67,7 @@ public:
67 * @param resource Requested resource type 67 * @param resource Requested resource type
68 * @returns The max value of the resource type 68 * @returns The max value of the resource type
69 */ 69 */
70 s32 GetMaxResourceValue(u32 resource) const; 70 u32 GetMaxResourceValue(u32 resource) const;
71 71
72 /// Name of resource limit object. 72 /// Name of resource limit object.
73 std::string name; 73 std::string name;
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
index fcf586728..2605b2595 100644
--- a/src/core/hle/kernel/semaphore.cpp
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -13,7 +13,7 @@ namespace Kernel {
13Semaphore::Semaphore() {} 13Semaphore::Semaphore() {}
14Semaphore::~Semaphore() {} 14Semaphore::~Semaphore() {}
15 15
16ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_count, 16ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_count, VAddr address,
17 std::string name) { 17 std::string name) {
18 18
19 if (initial_count > max_count) 19 if (initial_count > max_count)
@@ -25,6 +25,7 @@ ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_cou
25 // and the rest is reserved for the caller thread 25 // and the rest is reserved for the caller thread
26 semaphore->max_count = max_count; 26 semaphore->max_count = max_count;
27 semaphore->available_count = initial_count; 27 semaphore->available_count = initial_count;
28 semaphore->address = address;
28 semaphore->name = std::move(name); 29 semaphore->name = std::move(name);
29 30
30 return MakeResult<SharedPtr<Semaphore>>(std::move(semaphore)); 31 return MakeResult<SharedPtr<Semaphore>>(std::move(semaphore));
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h
index 7b0cacf2e..77c491a24 100644
--- a/src/core/hle/kernel/semaphore.h
+++ b/src/core/hle/kernel/semaphore.h
@@ -22,7 +22,7 @@ public:
22 * @param name Optional name of semaphore 22 * @param name Optional name of semaphore
23 * @return The created semaphore 23 * @return The created semaphore
24 */ 24 */
25 static ResultVal<SharedPtr<Semaphore>> Create(s32 initial_count, s32 max_count, 25 static ResultVal<SharedPtr<Semaphore>> Create(s32 initial_count, s32 max_count, VAddr address,
26 std::string name = "Unknown"); 26 std::string name = "Unknown");
27 27
28 std::string GetTypeName() const override { 28 std::string GetTypeName() const override {
@@ -39,6 +39,7 @@ public:
39 39
40 s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have 40 s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have
41 s32 available_count; ///< Number of free slots left in the semaphore 41 s32 available_count; ///< Number of free slots left in the semaphore
42 VAddr address;
42 std::string name; ///< Name of semaphore (optional) 43 std::string name; ///< Name of semaphore (optional)
43 44
44 bool ShouldWait(Thread* thread) const override; 45 bool ShouldWait(Thread* thread) const override;
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index a7b66142f..d45daca35 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -42,7 +42,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
42 memory_region->used += size; 42 memory_region->used += size;
43 43
44 shared_memory->linear_heap_phys_address = 44 shared_memory->linear_heap_phys_address =
45 Memory::FCRAM_PADDR + memory_region->base + shared_memory->backing_block_offset; 45 Memory::FCRAM_PADDR + memory_region->base +
46 static_cast<PAddr>(shared_memory->backing_block_offset);
46 47
47 // Increase the amount of used linear heap memory for the owner process. 48 // Increase the amount of used linear heap memory for the owner process.
48 if (shared_memory->owner_process != nullptr) { 49 if (shared_memory->owner_process != nullptr) {
@@ -54,22 +55,19 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
54 Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); 55 Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
55 } 56 }
56 } else { 57 } else {
57 // TODO(Subv): What happens if an application tries to create multiple memory blocks
58 // pointing to the same address?
59 auto& vm_manager = shared_memory->owner_process->vm_manager; 58 auto& vm_manager = shared_memory->owner_process->vm_manager;
60 // The memory is already available and mapped in the owner process. 59 // The memory is already available and mapped in the owner process.
61 auto vma = vm_manager.FindVMA(address)->second; 60 auto vma = vm_manager.FindVMA(address);
62 // Copy it over to our own storage 61 ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
63 shared_memory->backing_block = std::make_shared<std::vector<u8>>( 62 ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
64 vma.backing_block->data() + vma.offset, vma.backing_block->data() + vma.offset + size); 63
65 shared_memory->backing_block_offset = 0; 64 // The returned VMA might be a bigger one encompassing the desired address.
66 // Unmap the existing pages 65 auto vma_offset = address - vma->first;
67 vm_manager.UnmapRange(address, size); 66 ASSERT_MSG(vma_offset + size <= vma->second.size,
68 // Map our own block into the address space 67 "Shared memory exceeds bounds of mapped block");
69 vm_manager.MapMemoryBlock(address, shared_memory->backing_block, 0, size, 68
70 MemoryState::Shared); 69 shared_memory->backing_block = vma->second.backing_block;
71 // Reprotect the block with the new permissions 70 shared_memory->backing_block_offset = vma->second.offset + vma_offset;
72 vm_manager.ReprotectRange(address, size, ConvertPermissions(permissions));
73 } 71 }
74 72
75 shared_memory->base_address = address; 73 shared_memory->base_address = address;
@@ -183,4 +181,4 @@ u8* SharedMemory::GetPointer(u32 offset) {
183 return backing_block->data() + backing_block_offset + offset; 181 return backing_block->data() + backing_block_offset + offset;
184} 182}
185 183
186} // namespace 184} // namespace Kernel
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 94b335ed1..93a6f2182 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -114,7 +114,7 @@ public:
114 /// Backing memory for this shared memory block. 114 /// Backing memory for this shared memory block.
115 std::shared_ptr<std::vector<u8>> backing_block; 115 std::shared_ptr<std::vector<u8>> backing_block;
116 /// Offset into the backing block for this shared memory. 116 /// Offset into the backing block for this shared memory.
117 u32 backing_block_offset; 117 size_t backing_block_offset;
118 /// Size of the memory block. Page-aligned. 118 /// Size of the memory block. Page-aligned.
119 u32 size; 119 u32 size;
120 /// Permission restrictions applied to the process which created the block. 120 /// Permission restrictions applied to the process which created the block.
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index c01d08ebb..75df49ac2 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -111,7 +111,7 @@ void Thread::Stop() {
111 111
112Thread* ArbitrateHighestPriorityThread(u32 address) { 112Thread* ArbitrateHighestPriorityThread(u32 address) {
113 Thread* highest_priority_thread = nullptr; 113 Thread* highest_priority_thread = nullptr;
114 s32 priority = THREADPRIO_LOWEST; 114 u32 priority = THREADPRIO_LOWEST;
115 115
116 // Iterate through threads, find highest priority thread that is waiting to be arbitrated... 116 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
117 for (auto& thread : thread_list) { 117 for (auto& thread : thread_list) {
@@ -171,15 +171,24 @@ static void SwitchContext(Thread* new_thread) {
171 // Cancel any outstanding wakeup events for this thread 171 // Cancel any outstanding wakeup events for this thread
172 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); 172 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle);
173 173
174 auto previous_process = Kernel::g_current_process;
175
174 current_thread = new_thread; 176 current_thread = new_thread;
175 177
176 ready_queue.remove(new_thread->current_priority, new_thread); 178 ready_queue.remove(new_thread->current_priority, new_thread);
177 new_thread->status = THREADSTATUS_RUNNING; 179 new_thread->status = THREADSTATUS_RUNNING;
178 180
181 if (previous_process != current_thread->owner_process) {
182 Kernel::g_current_process = current_thread->owner_process;
183 SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
184 }
185
179 Core::CPU().LoadContext(new_thread->context); 186 Core::CPU().LoadContext(new_thread->context);
180 Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); 187 Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress());
181 } else { 188 } else {
182 current_thread = nullptr; 189 current_thread = nullptr;
190 // Note: We do not reset the current process and current page table when idling because
191 // technically we haven't changed processes, our threads are just paused.
183 } 192 }
184} 193}
185 194
@@ -238,12 +247,15 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
238 247
239 if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || 248 if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
240 thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { 249 thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) {
241 thread->wait_set_output = false; 250
251 // Invoke the wakeup callback before clearing the wait objects
252 if (thread->wakeup_callback)
253 thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr);
254
242 // Remove the thread from each of its waiting objects' waitlists 255 // Remove the thread from each of its waiting objects' waitlists
243 for (auto& object : thread->wait_objects) 256 for (auto& object : thread->wait_objects)
244 object->RemoveWaitingThread(thread.get()); 257 object->RemoveWaitingThread(thread.get());
245 thread->wait_objects.clear(); 258 thread->wait_objects.clear();
246 thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
247 } 259 }
248 260
249 thread->ResumeFromWait(); 261 thread->ResumeFromWait();
@@ -269,6 +281,9 @@ void Thread::ResumeFromWait() {
269 break; 281 break;
270 282
271 case THREADSTATUS_READY: 283 case THREADSTATUS_READY:
284 // The thread's wakeup callback must have already been cleared when the thread was first
285 // awoken.
286 ASSERT(wakeup_callback == nullptr);
272 // If the thread is waiting on multiple wait objects, it might be awoken more than once 287 // If the thread is waiting on multiple wait objects, it might be awoken more than once
273 // before actually resuming. We can ignore subsequent wakeups if the thread status has 288 // before actually resuming. We can ignore subsequent wakeups if the thread status has
274 // already been set to THREADSTATUS_READY. 289 // already been set to THREADSTATUS_READY.
@@ -284,6 +299,8 @@ void Thread::ResumeFromWait() {
284 return; 299 return;
285 } 300 }
286 301
302 wakeup_callback = nullptr;
303
287 ready_queue.push_back(current_priority, this); 304 ready_queue.push_back(current_priority, this);
288 status = THREADSTATUS_READY; 305 status = THREADSTATUS_READY;
289 Core::System::GetInstance().PrepareReschedule(); 306 Core::System::GetInstance().PrepareReschedule();
@@ -302,7 +319,7 @@ static void DebugThreadQueue() {
302 } 319 }
303 320
304 for (auto& t : thread_list) { 321 for (auto& t : thread_list) {
305 s32 priority = ready_queue.contains(t.get()); 322 u32 priority = ready_queue.contains(t.get());
306 if (priority != -1) { 323 if (priority != -1) {
307 LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); 324 LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId());
308 } 325 }
@@ -352,7 +369,8 @@ static void ResetThreadContext(ARM_Interface::ThreadContext& context, VAddr stac
352} 369}
353 370
354ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority, 371ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority,
355 u32 arg, s32 processor_id, VAddr stack_top) { 372 u32 arg, s32 processor_id, VAddr stack_top,
373 SharedPtr<Process> owner_process) {
356 // Check if priority is in ranged. Lowest priority -> highest priority id. 374 // Check if priority is in ranged. Lowest priority -> highest priority id.
357 if (priority > THREADPRIO_LOWEST) { 375 if (priority > THREADPRIO_LOWEST) {
358 LOG_ERROR(Kernel_SVC, "Invalid thread priority: %d", priority); 376 LOG_ERROR(Kernel_SVC, "Invalid thread priority: %d", priority);
@@ -366,7 +384,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
366 384
367 // TODO(yuriks): Other checks, returning 0xD9001BEA 385 // TODO(yuriks): Other checks, returning 0xD9001BEA
368 386
369 if (!Memory::IsValidVirtualAddress(entry_point)) { 387 if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) {
370 LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point); 388 LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point);
371 // TODO: Verify error 389 // TODO: Verify error
372 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, 390 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
@@ -385,15 +403,14 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
385 thread->nominal_priority = thread->current_priority = priority; 403 thread->nominal_priority = thread->current_priority = priority;
386 thread->last_running_ticks = CoreTiming::GetTicks(); 404 thread->last_running_ticks = CoreTiming::GetTicks();
387 thread->processor_id = processor_id; 405 thread->processor_id = processor_id;
388 thread->wait_set_output = false;
389 thread->wait_objects.clear(); 406 thread->wait_objects.clear();
390 thread->wait_address = 0; 407 thread->wait_address = 0;
391 thread->name = std::move(name); 408 thread->name = std::move(name);
392 thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); 409 thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap();
393 thread->owner_process = g_current_process; 410 thread->owner_process = owner_process;
394 411
395 // Find the next available TLS index, and mark it as used 412 // Find the next available TLS index, and mark it as used
396 auto& tls_slots = Kernel::g_current_process->tls_slots; 413 auto& tls_slots = owner_process->tls_slots;
397 bool needs_allocation = true; 414 bool needs_allocation = true;
398 u32 available_page; // Which allocated page has free space 415 u32 available_page; // Which allocated page has free space
399 u32 available_slot; // Which slot within the page is free 416 u32 available_slot; // Which slot within the page is free
@@ -412,18 +429,18 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
412 return ERR_OUT_OF_MEMORY; 429 return ERR_OUT_OF_MEMORY;
413 } 430 }
414 431
415 u32 offset = linheap_memory->size(); 432 size_t offset = linheap_memory->size();
416 433
417 // Allocate some memory from the end of the linear heap for this region. 434 // Allocate some memory from the end of the linear heap for this region.
418 linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0); 435 linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0);
419 memory_region->used += Memory::PAGE_SIZE; 436 memory_region->used += Memory::PAGE_SIZE;
420 Kernel::g_current_process->linear_heap_used += Memory::PAGE_SIZE; 437 owner_process->linear_heap_used += Memory::PAGE_SIZE;
421 438
422 tls_slots.emplace_back(0); // The page is completely available at the start 439 tls_slots.emplace_back(0); // The page is completely available at the start
423 available_page = tls_slots.size() - 1; 440 available_page = static_cast<u32>(tls_slots.size() - 1);
424 available_slot = 0; // Use the first slot in the new page 441 available_slot = 0; // Use the first slot in the new page
425 442
426 auto& vm_manager = Kernel::g_current_process->vm_manager; 443 auto& vm_manager = owner_process->vm_manager;
427 vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); 444 vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
428 445
429 // Map the page to the current process' address space. 446 // Map the page to the current process' address space.
@@ -447,7 +464,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
447 return MakeResult<SharedPtr<Thread>>(std::move(thread)); 464 return MakeResult<SharedPtr<Thread>>(std::move(thread));
448} 465}
449 466
450void Thread::SetPriority(s32 priority) { 467void Thread::SetPriority(u32 priority) {
451 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, 468 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
452 "Invalid priority value."); 469 "Invalid priority value.");
453 // If thread was ready, adjust queues 470 // If thread was ready, adjust queues
@@ -460,7 +477,7 @@ void Thread::SetPriority(s32 priority) {
460} 477}
461 478
462void Thread::UpdatePriority() { 479void Thread::UpdatePriority() {
463 s32 best_priority = nominal_priority; 480 u32 best_priority = nominal_priority;
464 for (auto& mutex : held_mutexes) { 481 for (auto& mutex : held_mutexes) {
465 if (mutex->priority < best_priority) 482 if (mutex->priority < best_priority)
466 best_priority = mutex->priority; 483 best_priority = mutex->priority;
@@ -468,7 +485,7 @@ void Thread::UpdatePriority() {
468 BoostPriority(best_priority); 485 BoostPriority(best_priority);
469} 486}
470 487
471void Thread::BoostPriority(s32 priority) { 488void Thread::BoostPriority(u32 priority) {
472 // If thread was ready, adjust queues 489 // If thread was ready, adjust queues
473 if (status == THREADSTATUS_READY) 490 if (status == THREADSTATUS_READY)
474 ready_queue.move(this, current_priority, priority); 491 ready_queue.move(this, current_priority, priority);
@@ -477,21 +494,20 @@ void Thread::BoostPriority(s32 priority) {
477 current_priority = priority; 494 current_priority = priority;
478} 495}
479 496
480SharedPtr<Thread> SetupMainThread(VAddr entry_point, s32 priority) { 497SharedPtr<Thread> SetupMainThread(u32 entry_point, u32 priority, SharedPtr<Process> owner_process) {
481 DEBUG_ASSERT(!GetCurrentThread()); 498 // Setup page table so we can write to memory
499 SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
482 500
483 // Initialize new "main" thread 501 // Initialize new "main" thread
484 auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, 502 auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
485 Memory::HEAP_VADDR_END); 503 Memory::HEAP_VADDR_END, owner_process);
486 504
487 SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); 505 SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
488 506
489 thread->context.fpscr = 507 thread->context.fpscr =
490 FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010 508 FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010
491 509
492 // Run new "main" thread 510 // Note: The newly created thread will be run when the scheduler fires.
493 SwitchContext(thread.get());
494
495 return thread; 511 return thread;
496} 512}
497 513
@@ -525,7 +541,13 @@ void Thread::SetWaitSynchronizationOutput(s32 output) {
525s32 Thread::GetWaitObjectIndex(WaitObject* object) const { 541s32 Thread::GetWaitObjectIndex(WaitObject* object) const {
526 ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); 542 ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything");
527 auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); 543 auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object);
528 return std::distance(match, wait_objects.rend()) - 1; 544 return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1);
545}
546
547VAddr Thread::GetCommandBufferAddress() const {
548 // Offset from the start of TLS at which the IPC command buffer begins.
549 static constexpr int CommandHeaderOffset = 0x80;
550 return GetTLSAddress() + CommandHeaderOffset;
529} 551}
530 552
531//////////////////////////////////////////////////////////////////////////////////////////////////// 553////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 2cadb91db..fafcab156 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -15,7 +15,7 @@
15#include "core/hle/kernel/wait_object.h" 15#include "core/hle/kernel/wait_object.h"
16#include "core/hle/result.h" 16#include "core/hle/result.h"
17 17
18enum ThreadPriority : s32 { 18enum ThreadPriority : u32 {
19 THREADPRIO_HIGHEST = 0, ///< Highest thread priority 19 THREADPRIO_HIGHEST = 0, ///< Highest thread priority
20 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps 20 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
21 THREADPRIO_DEFAULT = 48, ///< Default thread priority for userland apps 21 THREADPRIO_DEFAULT = 48, ///< Default thread priority for userland apps
@@ -41,6 +41,11 @@ enum ThreadStatus {
41 THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated 41 THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
42}; 42};
43 43
44enum class ThreadWakeupReason {
45 Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal.
46 Timeout // The thread was woken up due to a wait timeout.
47};
48
44namespace Kernel { 49namespace Kernel {
45 50
46class Mutex; 51class Mutex;
@@ -56,10 +61,12 @@ public:
56 * @param arg User data to pass to the thread 61 * @param arg User data to pass to the thread
57 * @param processor_id The ID(s) of the processors on which the thread is desired to be run 62 * @param processor_id The ID(s) of the processors on which the thread is desired to be run
58 * @param stack_top The address of the thread's stack top 63 * @param stack_top The address of the thread's stack top
64 * @param owner_process The parent process for the thread
59 * @return A shared pointer to the newly created thread 65 * @return A shared pointer to the newly created thread
60 */ 66 */
61 static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, u32 priority, 67 static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, u32 priority,
62 u32 arg, s32 processor_id, VAddr stack_top); 68 u32 arg, s32 processor_id, VAddr stack_top,
69 SharedPtr<Process> owner_process);
63 70
64 std::string GetName() const override { 71 std::string GetName() const override {
65 return name; 72 return name;
@@ -80,7 +87,7 @@ public:
80 * Gets the thread's current priority 87 * Gets the thread's current priority
81 * @return The current thread's priority 88 * @return The current thread's priority
82 */ 89 */
83 s32 GetPriority() const { 90 u32 GetPriority() const {
84 return current_priority; 91 return current_priority;
85 } 92 }
86 93
@@ -88,7 +95,7 @@ public:
88 * Sets the thread's current priority 95 * Sets the thread's current priority
89 * @param priority The new priority 96 * @param priority The new priority
90 */ 97 */
91 void SetPriority(s32 priority); 98 void SetPriority(u32 priority);
92 99
93 /** 100 /**
94 * Boost's a thread's priority to the best priority among the thread's held mutexes. 101 * Boost's a thread's priority to the best priority among the thread's held mutexes.
@@ -100,7 +107,7 @@ public:
100 * Temporarily boosts the thread's priority until the next time it is scheduled 107 * Temporarily boosts the thread's priority until the next time it is scheduled
101 * @param priority The new priority 108 * @param priority The new priority
102 */ 109 */
103 void BoostPriority(s32 priority); 110 void BoostPriority(u32 priority);
104 111
105 /** 112 /**
106 * Gets the thread's thread ID 113 * Gets the thread's thread ID
@@ -116,9 +123,9 @@ public:
116 void ResumeFromWait(); 123 void ResumeFromWait();
117 124
118 /** 125 /**
119 * Schedules an event to wake up the specified thread after the specified delay 126 * Schedules an event to wake up the specified thread after the specified delay
120 * @param nanoseconds The time this thread will be allowed to sleep for 127 * @param nanoseconds The time this thread will be allowed to sleep for
121 */ 128 */
122 void WakeAfterDelay(s64 nanoseconds); 129 void WakeAfterDelay(s64 nanoseconds);
123 130
124 /** 131 /**
@@ -157,6 +164,12 @@ public:
157 return tls_address; 164 return tls_address;
158 } 165 }
159 166
167 /*
168 * Returns the address of the current thread's command buffer, located in the TLS.
169 * @returns VAddr of the thread's command buffer.
170 */
171 VAddr GetCommandBufferAddress() const;
172
160 /** 173 /**
161 * Returns whether this thread is waiting for all the objects in 174 * Returns whether this thread is waiting for all the objects in
162 * its wait list to become ready, as a result of a WaitSynchronizationN call 175 * its wait list to become ready, as a result of a WaitSynchronizationN call
@@ -174,8 +187,8 @@ public:
174 VAddr entry_point; 187 VAddr entry_point;
175 VAddr stack_top; 188 VAddr stack_top;
176 189
177 s32 nominal_priority; ///< Nominal thread priority, as set by the emulated application 190 u32 nominal_priority; ///< Nominal thread priority, as set by the emulated application
178 s32 current_priority; ///< Current thread priority, can be temporarily changed 191 u32 current_priority; ///< Current thread priority, can be temporarily changed
179 192
180 u64 last_running_ticks; ///< CPU tick when thread was last running 193 u64 last_running_ticks; ///< CPU tick when thread was last running
181 194
@@ -197,14 +210,18 @@ public:
197 210
198 VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address 211 VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address
199 212
200 /// True if the WaitSynchronizationN output parameter should be set on thread wakeup.
201 bool wait_set_output;
202
203 std::string name; 213 std::string name;
204 214
205 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. 215 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
206 Handle callback_handle; 216 Handle callback_handle;
207 217
218 using WakeupCallback = void(ThreadWakeupReason reason, SharedPtr<Thread> thread,
219 SharedPtr<WaitObject> object);
220 // Callback that will be invoked when the thread is resumed from a waiting state. If the thread
221 // was waiting via WaitSynchronizationN then the object will be the last object that became
222 // available. In case of a timeout, the object will be nullptr.
223 std::function<WakeupCallback> wakeup_callback;
224
208private: 225private:
209 Thread(); 226 Thread();
210 ~Thread() override; 227 ~Thread() override;
@@ -214,9 +231,10 @@ private:
214 * Sets up the primary application thread 231 * Sets up the primary application thread
215 * @param entry_point The address at which the thread should start execution 232 * @param entry_point The address at which the thread should start execution
216 * @param priority The priority to give the main thread 233 * @param priority The priority to give the main thread
234 * @param owner_process The parent process for the main thread
217 * @return A shared pointer to the main thread 235 * @return A shared pointer to the main thread
218 */ 236 */
219SharedPtr<Thread> SetupMainThread(VAddr entry_point, s32 priority); 237SharedPtr<Thread> SetupMainThread(u32 entry_point, u32 priority, SharedPtr<Process> owner_process);
220 238
221/** 239/**
222 * Returns whether there are any threads that are ready to run. 240 * Returns whether there are any threads that are ready to run.
@@ -276,4 +294,4 @@ void ThreadingShutdown();
276 */ 294 */
277const std::vector<SharedPtr<Thread>>& GetThreadList(); 295const std::vector<SharedPtr<Thread>>& GetThreadList();
278 296
279} // namespace 297} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index f70c32501..9762ef535 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -4,8 +4,10 @@
4 4
5#include <iterator> 5#include <iterator>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "core/arm/arm_interface.h"
7#include "core/hle/kernel/errors.h" 8#include "core/hle/kernel/errors.h"
8#include "core/hle/kernel/vm_manager.h" 9#include "core/hle/kernel/vm_manager.h"
10#include "core/core.h"
9#include "core/memory.h" 11#include "core/memory.h"
10#include "core/memory_setup.h" 12#include "core/memory_setup.h"
11#include "core/mmio.h" 13#include "core/mmio.h"
@@ -56,6 +58,10 @@ void VMManager::Reset() {
56 initial_vma.size = MAX_ADDRESS; 58 initial_vma.size = MAX_ADDRESS;
57 vma_map.emplace(initial_vma.base, initial_vma); 59 vma_map.emplace(initial_vma.base, initial_vma);
58 60
61 page_table.pointers.fill(nullptr);
62 page_table.attributes.fill(Memory::PageType::Unmapped);
63 page_table.cached_res_count.fill(0);
64
59 //UpdatePageTableForVMA(initial_vma); 65 //UpdatePageTableForVMA(initial_vma);
60} 66}
61 67
@@ -79,6 +85,8 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
79 VirtualMemoryArea& final_vma = vma_handle->second; 85 VirtualMemoryArea& final_vma = vma_handle->second;
80 ASSERT(final_vma.size == size); 86 ASSERT(final_vma.size == size);
81 87
88 Core::CPU().MapBackingMemory(target, size, block->data() + offset, VMAPermission::ReadWriteExecute);
89
82 final_vma.type = VMAType::AllocatedMemoryBlock; 90 final_vma.type = VMAType::AllocatedMemoryBlock;
83 final_vma.permissions = VMAPermission::ReadWrite; 91 final_vma.permissions = VMAPermission::ReadWrite;
84 final_vma.meminfo_state = state; 92 final_vma.meminfo_state = state;
@@ -98,6 +106,8 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
98 VirtualMemoryArea& final_vma = vma_handle->second; 106 VirtualMemoryArea& final_vma = vma_handle->second;
99 ASSERT(final_vma.size == size); 107 ASSERT(final_vma.size == size);
100 108
109 Core::CPU().MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
110
101 final_vma.type = VMAType::BackingMemory; 111 final_vma.type = VMAType::BackingMemory;
102 final_vma.permissions = VMAPermission::ReadWrite; 112 final_vma.permissions = VMAPermission::ReadWrite;
103 final_vma.meminfo_state = state; 113 final_vma.meminfo_state = state;
@@ -328,16 +338,17 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
328void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { 338void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
329 switch (vma.type) { 339 switch (vma.type) {
330 case VMAType::Free: 340 case VMAType::Free:
331 Memory::UnmapRegion(vma.base, vma.size); 341 Memory::UnmapRegion(page_table, vma.base, vma.size);
332 break; 342 break;
333 case VMAType::AllocatedMemoryBlock: 343 case VMAType::AllocatedMemoryBlock:
334 Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_block->data() + vma.offset); 344 Memory::MapMemoryRegion(page_table, vma.base, vma.size,
345 vma.backing_block->data() + vma.offset);
335 break; 346 break;
336 case VMAType::BackingMemory: 347 case VMAType::BackingMemory:
337 Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory); 348 Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory);
338 break; 349 break;
339 case VMAType::MMIO: 350 case VMAType::MMIO:
340 Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler); 351 Memory::MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler);
341 break; 352 break;
342 } 353 }
343} 354}
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index aa2265ce6..cb5bb8243 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -9,6 +9,7 @@
9#include <vector> 9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/result.h" 11#include "core/hle/result.h"
12#include "core/memory.h"
12#include "core/mmio.h" 13#include "core/mmio.h"
13 14
14namespace Kernel { 15namespace Kernel {
@@ -102,7 +103,6 @@ struct VirtualMemoryArea {
102 * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ 103 * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/
103 */ 104 */
104class VMManager final { 105class VMManager final {
105 // TODO(yuriks): Make page tables switchable to support multiple VMManagers
106public: 106public:
107 /** 107 /**
108 * The maximum amount of address space managed by the kernel. Addresses above this are never 108 * The maximum amount of address space managed by the kernel. Addresses above this are never
@@ -184,6 +184,10 @@ public:
184 /// Dumps the address space layout to the log, for debugging 184 /// Dumps the address space layout to the log, for debugging
185 void LogLayout(Log::Level log_level) const; 185 void LogLayout(Log::Level log_level) const;
186 186
187 /// Each VMManager has its own page table, which is set as the main one when the owning process
188 /// is scheduled.
189 Memory::PageTable page_table;
190
187private: 191private:
188 using VMAIter = decltype(vma_map)::iterator; 192 using VMAIter = decltype(vma_map)::iterator;
189 193
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
index f245eda6c..469554908 100644
--- a/src/core/hle/kernel/wait_object.cpp
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -34,7 +34,7 @@ void WaitObject::RemoveWaitingThread(Thread* thread) {
34 34
35SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { 35SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
36 Thread* candidate = nullptr; 36 Thread* candidate = nullptr;
37 s32 candidate_priority = THREADPRIO_LOWEST + 1; 37 u32 candidate_priority = THREADPRIO_LOWEST + 1;
38 38
39 for (const auto& thread : waiting_threads) { 39 for (const auto& thread : waiting_threads) {
40 // The list of waiting threads must not contain threads that are not waiting to be awakened. 40 // The list of waiting threads must not contain threads that are not waiting to be awakened.
@@ -71,23 +71,20 @@ void WaitObject::WakeupAllWaitingThreads() {
71 while (auto thread = GetHighestPriorityReadyThread()) { 71 while (auto thread = GetHighestPriorityReadyThread()) {
72 if (!thread->IsSleepingOnWaitAll()) { 72 if (!thread->IsSleepingOnWaitAll()) {
73 Acquire(thread.get()); 73 Acquire(thread.get());
74 // Set the output index of the WaitSynchronizationN call to the index of this object.
75 if (thread->wait_set_output) {
76 thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this));
77 thread->wait_set_output = false;
78 }
79 } else { 74 } else {
80 for (auto& object : thread->wait_objects) { 75 for (auto& object : thread->wait_objects) {
81 object->Acquire(thread.get()); 76 object->Acquire(thread.get());
82 } 77 }
83 // Note: This case doesn't update the output index of WaitSynchronizationN.
84 } 78 }
85 79
80 // Invoke the wakeup callback before clearing the wait objects
81 if (thread->wakeup_callback)
82 thread->wakeup_callback(ThreadWakeupReason::Signal, thread, this);
83
86 for (auto& object : thread->wait_objects) 84 for (auto& object : thread->wait_objects)
87 object->RemoveWaitingThread(thread.get()); 85 object->RemoveWaitingThread(thread.get());
88 thread->wait_objects.clear(); 86 thread->wait_objects.clear();
89 87
90 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
91 thread->ResumeFromWait(); 88 thread->ResumeFromWait();
92 } 89 }
93} 90}
diff --git a/src/core/hle/lock.cpp b/src/core/hle/lock.cpp
new file mode 100644
index 000000000..1c24c7ce9
--- /dev/null
+++ b/src/core/hle/lock.cpp
@@ -0,0 +1,11 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <core/hle/lock.h>
8
9namespace HLE {
10std::recursive_mutex g_hle_lock;
11}
diff --git a/src/core/hle/lock.h b/src/core/hle/lock.h
new file mode 100644
index 000000000..5c99fe996
--- /dev/null
+++ b/src/core/hle/lock.h
@@ -0,0 +1,18 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <mutex>
8
9namespace HLE {
10/*
11 * Synchronizes access to the internal HLE kernel structures, it is acquired when a guest
12 * application thread performs a syscall. It should be acquired by any host threads that read or
13 * modify the HLE kernel state. Note: Any operation that directly or indirectly reads from or writes
14 * to the emulated memory is not protected by this mutex, and should be avoided in any threads other
15 * than the CPU thread.
16 */
17extern std::recursive_mutex g_hle_lock;
18} // namespace HLE
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index 5c44b43bb..912ab550d 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <boost/optional.hpp>
5#include "common/common_paths.h" 6#include "common/common_paths.h"
6#include "common/file_util.h" 7#include "common/file_util.h"
7#include "common/logging/log.h" 8#include "common/logging/log.h"
@@ -18,6 +19,7 @@
18#include "core/hle/service/apt/apt_s.h" 19#include "core/hle/service/apt/apt_s.h"
19#include "core/hle/service/apt/apt_u.h" 20#include "core/hle/service/apt/apt_u.h"
20#include "core/hle/service/apt/bcfnt/bcfnt.h" 21#include "core/hle/service/apt/bcfnt/bcfnt.h"
22#include "core/hle/service/cfg/cfg.h"
21#include "core/hle/service/fs/archive.h" 23#include "core/hle/service/fs/archive.h"
22#include "core/hle/service/ptm/ptm.h" 24#include "core/hle/service/ptm/ptm.h"
23#include "core/hle/service/service.h" 25#include "core/hle/service/service.h"
@@ -33,8 +35,6 @@ static bool shared_font_loaded = false;
33static bool shared_font_relocated = false; 35static bool shared_font_relocated = false;
34 36
35static Kernel::SharedPtr<Kernel::Mutex> lock; 37static Kernel::SharedPtr<Kernel::Mutex> lock;
36static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event
37static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event
38 38
39static u32 cpu_percent; ///< CPU time available to the running application 39static u32 cpu_percent; ///< CPU time available to the running application
40 40
@@ -43,43 +43,344 @@ static u8 unknown_ns_state_field;
43 43
44static ScreencapPostPermission screen_capture_post_permission; 44static ScreencapPostPermission screen_capture_post_permission;
45 45
46/// Parameter data to be returned in the next call to Glance/ReceiveParameter 46/// Parameter data to be returned in the next call to Glance/ReceiveParameter.
47static MessageParameter next_parameter; 47/// TODO(Subv): Use std::optional once we migrate to C++17.
48static boost::optional<MessageParameter> next_parameter;
49
50enum class AppletPos { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4 };
51
52static constexpr size_t NumAppletSlot = 4;
53
54enum class AppletSlot : u8 {
55 Application,
56 SystemApplet,
57 HomeMenu,
58 LibraryApplet,
59
60 // An invalid tag
61 Error,
62};
63
64union AppletAttributes {
65 u32 raw;
66
67 BitField<0, 3, u32> applet_pos;
68 BitField<29, 1, u32> is_home_menu;
69
70 AppletAttributes() : raw(0) {}
71 AppletAttributes(u32 attributes) : raw(attributes) {}
72};
73
74struct AppletSlotData {
75 AppletId applet_id;
76 AppletSlot slot;
77 bool registered;
78 AppletAttributes attributes;
79 Kernel::SharedPtr<Kernel::Event> notification_event;
80 Kernel::SharedPtr<Kernel::Event> parameter_event;
81};
82
83// Holds data about the concurrently running applets in the system.
84static std::array<AppletSlotData, NumAppletSlot> applet_slots = {};
85
86// This overload returns nullptr if no applet with the specified id has been started.
87static AppletSlotData* GetAppletSlotData(AppletId id) {
88 auto GetSlot = [](AppletSlot slot) -> AppletSlotData* {
89 return &applet_slots[static_cast<size_t>(slot)];
90 };
91
92 if (id == AppletId::Application) {
93 auto* slot = GetSlot(AppletSlot::Application);
94 if (slot->applet_id != AppletId::None)
95 return slot;
96
97 return nullptr;
98 }
99
100 if (id == AppletId::AnySystemApplet) {
101 auto* system_slot = GetSlot(AppletSlot::SystemApplet);
102 if (system_slot->applet_id != AppletId::None)
103 return system_slot;
104
105 // The Home Menu is also a system applet, but it lives in its own slot to be able to run
106 // concurrently with other system applets.
107 auto* home_slot = GetSlot(AppletSlot::HomeMenu);
108 if (home_slot->applet_id != AppletId::None)
109 return home_slot;
110
111 return nullptr;
112 }
113
114 if (id == AppletId::AnyLibraryApplet || id == AppletId::AnySysLibraryApplet) {
115 auto* slot = GetSlot(AppletSlot::LibraryApplet);
116 if (slot->applet_id == AppletId::None)
117 return nullptr;
118
119 u32 applet_pos = slot->attributes.applet_pos;
120
121 if (id == AppletId::AnyLibraryApplet && applet_pos == static_cast<u32>(AppletPos::Library))
122 return slot;
123
124 if (id == AppletId::AnySysLibraryApplet &&
125 applet_pos == static_cast<u32>(AppletPos::SysLibrary))
126 return slot;
127
128 return nullptr;
129 }
130
131 if (id == AppletId::HomeMenu || id == AppletId::AlternateMenu) {
132 auto* slot = GetSlot(AppletSlot::HomeMenu);
133 if (slot->applet_id != AppletId::None)
134 return slot;
135
136 return nullptr;
137 }
138
139 for (auto& slot : applet_slots) {
140 if (slot.applet_id == id)
141 return &slot;
142 }
143
144 return nullptr;
145}
146
147static AppletSlotData* GetAppletSlotData(AppletAttributes attributes) {
148 // Mapping from AppletPos to AppletSlot
149 static constexpr std::array<AppletSlot, 6> applet_position_slots = {
150 AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet,
151 AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet};
152
153 u32 applet_pos = attributes.applet_pos;
154 if (applet_pos >= applet_position_slots.size())
155 return nullptr;
156
157 AppletSlot slot = applet_position_slots[applet_pos];
158
159 if (slot == AppletSlot::Error)
160 return nullptr;
161
162 // The Home Menu is a system applet, however, it has its own applet slot so that it can run
163 // concurrently with other system applets.
164 if (slot == AppletSlot::SystemApplet && attributes.is_home_menu)
165 return &applet_slots[static_cast<size_t>(AppletSlot::HomeMenu)];
166
167 return &applet_slots[static_cast<size_t>(slot)];
168}
48 169
49void SendParameter(const MessageParameter& parameter) { 170void SendParameter(const MessageParameter& parameter) {
50 next_parameter = parameter; 171 next_parameter = parameter;
51 // Signal the event to let the application know that a new parameter is ready to be read 172 // Signal the event to let the receiver know that a new parameter is ready to be read
52 parameter_event->Signal(); 173 auto* const slot_data = GetAppletSlotData(static_cast<AppletId>(parameter.destination_id));
174 if (slot_data == nullptr) {
175 LOG_DEBUG(Service_APT, "No applet was registered with the id %03X",
176 parameter.destination_id);
177 return;
178 }
179
180 slot_data->parameter_event->Signal();
53} 181}
54 182
55void Initialize(Service::Interface* self) { 183void Initialize(Service::Interface* self) {
56 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080 184 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080
57 u32 app_id = rp.Pop<u32>(); 185 u32 app_id = rp.Pop<u32>();
58 u32 flags = rp.Pop<u32>(); 186 u32 attributes = rp.Pop<u32>();
187
188 LOG_DEBUG(Service_APT, "called app_id=0x%08X, attributes=0x%08X", app_id, attributes);
189
190 auto* const slot_data = GetAppletSlotData(attributes);
191
192 // Note: The real NS service does not check if the attributes value is valid before accessing
193 // the data in the array
194 ASSERT_MSG(slot_data, "Invalid application attributes");
195
196 if (slot_data->registered) {
197 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
198 rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
199 ErrorSummary::InvalidState, ErrorLevel::Status));
200 return;
201 }
202
203 slot_data->applet_id = static_cast<AppletId>(app_id);
204 slot_data->attributes.raw = attributes;
205
59 IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); 206 IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
60 rb.Push(RESULT_SUCCESS); 207 rb.Push(RESULT_SUCCESS);
61 rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).Unwrap(), 208 rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(),
62 Kernel::g_handle_table.Create(parameter_event).Unwrap()); 209 Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap());
210
211 if (slot_data->applet_id == AppletId::Application ||
212 slot_data->applet_id == AppletId::HomeMenu) {
213 // Initialize the APT parameter to wake up the application.
214 next_parameter.emplace();
215 next_parameter->signal = static_cast<u32>(SignalType::Wakeup);
216 next_parameter->sender_id = static_cast<u32>(AppletId::None);
217 next_parameter->destination_id = app_id;
218 // Not signaling the parameter event will cause the application (or Home Menu) to hang
219 // during startup. In the real console, it is usually the Kernel and Home Menu who cause NS
220 // to signal the HomeMenu and Application parameter events, respectively.
221 slot_data->parameter_event->Signal();
222 }
223}
63 224
64 // TODO(bunnei): Check if these events are cleared every time Initialize is called. 225static u32 DecompressLZ11(const u8* in, u8* out) {
65 notification_event->Clear(); 226 u32_le decompressed_size;
66 parameter_event->Clear(); 227 memcpy(&decompressed_size, in, sizeof(u32));
228 in += 4;
229
230 u8 type = decompressed_size & 0xFF;
231 ASSERT(type == 0x11);
232 decompressed_size >>= 8;
67 233
68 ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); 234 u32 current_out_size = 0;
69 lock->Release(); 235 u8 flags = 0, mask = 1;
236 while (current_out_size < decompressed_size) {
237 if (mask == 1) {
238 flags = *(in++);
239 mask = 0x80;
240 } else {
241 mask >>= 1;
242 }
70 243
71 LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); 244 if (flags & mask) {
245 u8 byte1 = *(in++);
246 u32 length = byte1 >> 4;
247 u32 offset;
248 if (length == 0) {
249 u8 byte2 = *(in++);
250 u8 byte3 = *(in++);
251 length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11;
252 offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1;
253 } else if (length == 1) {
254 u8 byte2 = *(in++);
255 u8 byte3 = *(in++);
256 u8 byte4 = *(in++);
257 length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111;
258 offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1;
259 } else {
260 u8 byte2 = *(in++);
261 length = (byte1 >> 4) + 0x1;
262 offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1;
263 }
264
265 for (u32 i = 0; i < length; i++) {
266 *out = *(out - offset);
267 ++out;
268 }
269
270 current_out_size += length;
271 } else {
272 *(out++) = *(in++);
273 current_out_size++;
274 }
275 }
276 return decompressed_size;
277}
278
279static bool LoadSharedFont() {
280 u8 font_region_code;
281 switch (CFG::GetRegionValue()) {
282 case 4: // CHN
283 font_region_code = 2;
284 break;
285 case 5: // KOR
286 font_region_code = 3;
287 break;
288 case 6: // TWN
289 font_region_code = 4;
290 break;
291 default: // JPN/EUR/USA
292 font_region_code = 1;
293 break;
294 }
295
296 const u64_le shared_font_archive_id_low = 0x0004009b00014002 | ((font_region_code - 1) << 8);
297 const u64_le shared_font_archive_id_high = 0x00000001ffffff00;
298 std::vector<u8> shared_font_archive_id(16);
299 std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64));
300 std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64));
301 FileSys::Path archive_path(shared_font_archive_id);
302 auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path);
303 if (archive_result.Failed())
304 return false;
305
306 std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS
307 FileSys::Path file_path(romfs_path);
308 FileSys::Mode open_mode = {};
309 open_mode.read_flag.Assign(1);
310 auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode);
311 if (file_result.Failed())
312 return false;
313
314 auto romfs = std::move(file_result).Unwrap();
315 std::vector<u8> romfs_buffer(romfs->backend->GetSize());
316 romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data());
317 romfs->backend->Close();
318
319 const char16_t* file_name[4] = {u"cbf_std.bcfnt.lz", u"cbf_zh-Hans-CN.bcfnt.lz",
320 u"cbf_ko-Hang-KR.bcfnt.lz", u"cbf_zh-Hant-TW.bcfnt.lz"};
321 const u8* font_file =
322 RomFS::GetFilePointer(romfs_buffer.data(), {file_name[font_region_code - 1]});
323 if (font_file == nullptr)
324 return false;
325
326 struct {
327 u32_le status;
328 u32_le region;
329 u32_le decompressed_size;
330 INSERT_PADDING_WORDS(0x1D);
331 } shared_font_header{};
332 static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size");
333
334 shared_font_header.status = 2; // successfully loaded
335 shared_font_header.region = font_region_code;
336 shared_font_header.decompressed_size =
337 DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80));
338 std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header));
339 *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU"
340
341 return true;
342}
343
344static bool LoadLegacySharedFont() {
345 // This is the legacy method to load shared font.
346 // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
347 // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
348 // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
349 // "shared_font.bin" in the Citra "sysdata" directory.
350 std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
351
352 FileUtil::CreateFullPath(filepath); // Create path if not already created
353 FileUtil::IOFile file(filepath, "rb");
354 if (file.IsOpen()) {
355 file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
356 return true;
357 }
358
359 return false;
72} 360}
73 361
74void GetSharedFont(Service::Interface* self) { 362void GetSharedFont(Service::Interface* self) {
75 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000 363 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000
76 IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); 364 IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
365
366 // Log in telemetry if the game uses the shared font
367 Core::Telemetry().AddField(Telemetry::FieldType::Session, "RequiresSharedFont", true);
368
77 if (!shared_font_loaded) { 369 if (!shared_font_loaded) {
78 LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); 370 // On real 3DS, font loading happens on booting. However, we load it on demand to coordinate
79 rb.Push<u32>(-1); // TODO: Find the right error code 371 // with CFG region auto configuration, which happens later than APT initialization.
80 rb.Skip(1 + 2, true); 372 if (LoadSharedFont()) {
81 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont); 373 shared_font_loaded = true;
82 return; 374 } else if (LoadLegacySharedFont()) {
375 LOG_WARNING(Service_APT, "Loaded shared font by legacy method");
376 shared_font_loaded = true;
377 } else {
378 LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds");
379 rb.Push<u32>(-1); // TODO: Find the right error code
380 rb.Skip(1 + 2, true);
381 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont);
382 return;
383 }
83 } 384 }
84 385
85 // The shared font has to be relocated to the new address before being passed to the 386 // The shared font has to be relocated to the new address before being passed to the
@@ -115,7 +416,12 @@ void GetLockHandle(Service::Interface* self) {
115 // this will cause the app to wait until parameter_event is signaled. 416 // this will cause the app to wait until parameter_event is signaled.
116 u32 applet_attributes = rp.Pop<u32>(); 417 u32 applet_attributes = rp.Pop<u32>();
117 IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); 418 IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
118 rb.Push(RESULT_SUCCESS); // No error 419 rb.Push(RESULT_SUCCESS); // No error
420
421 // TODO(Subv): The output attributes should have an AppletPos of either Library or System |
422 // Library (depending on the type of the last launched applet) if the input attributes'
423 // AppletPos has the Library bit set.
424
119 rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable. 425 rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable.
120 rb.Push<u32>(0); // Least significant bit = power button state 426 rb.Push<u32>(0); // Least significant bit = power button state
121 Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap(); 427 Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap();
@@ -128,10 +434,22 @@ void GetLockHandle(Service::Interface* self) {
128void Enable(Service::Interface* self) { 434void Enable(Service::Interface* self) {
129 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040 435 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040
130 u32 attributes = rp.Pop<u32>(); 436 u32 attributes = rp.Pop<u32>();
437
438 LOG_DEBUG(Service_APT, "called attributes=0x%08X", attributes);
439
131 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 440 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
132 rb.Push(RESULT_SUCCESS); // No error 441
133 parameter_event->Signal(); // Let the application know that it has been started 442 auto* const slot_data = GetAppletSlotData(attributes);
134 LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes); 443
444 if (!slot_data) {
445 rb.Push(ResultCode(ErrCodes::InvalidAppletSlot, ErrorModule::Applet,
446 ErrorSummary::InvalidState, ErrorLevel::Status));
447 return;
448 }
449
450 slot_data->registered = true;
451
452 rb.Push(RESULT_SUCCESS);
135} 453}
136 454
137void GetAppletManInfo(Service::Interface* self) { 455void GetAppletManInfo(Service::Interface* self) {
@@ -149,22 +467,27 @@ void GetAppletManInfo(Service::Interface* self) {
149 467
150void IsRegistered(Service::Interface* self) { 468void IsRegistered(Service::Interface* self) {
151 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040 469 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040
152 u32 app_id = rp.Pop<u32>(); 470 AppletId app_id = static_cast<AppletId>(rp.Pop<u32>());
153 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); 471 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
154 rb.Push(RESULT_SUCCESS); // No error 472 rb.Push(RESULT_SUCCESS); // No error
155 473
156 // TODO(Subv): An application is considered "registered" if it has already called APT::Enable 474 auto* const slot_data = GetAppletSlotData(app_id);
157 // handle this properly once we implement multiprocess support. 475
158 bool is_registered = false; // Set to not registered by default 476 // Check if an LLE applet was registered first, then fallback to HLE applets
477 bool is_registered = slot_data && slot_data->registered;
159 478
160 if (app_id == static_cast<u32>(AppletId::AnyLibraryApplet)) { 479 if (!is_registered) {
161 is_registered = HLE::Applets::IsLibraryAppletRunning(); 480 if (app_id == AppletId::AnyLibraryApplet) {
162 } else if (auto applet = HLE::Applets::Applet::Get(static_cast<AppletId>(app_id))) { 481 is_registered = HLE::Applets::IsLibraryAppletRunning();
163 is_registered = true; // Set to registered 482 } else if (auto applet = HLE::Applets::Applet::Get(app_id)) {
483 // The applet exists, set it as registered.
484 is_registered = true;
485 }
164 } 486 }
487
165 rb.Push(is_registered); 488 rb.Push(is_registered);
166 489
167 LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); 490 LOG_DEBUG(Service_APT, "called app_id=0x%08X", static_cast<u32>(app_id));
168} 491}
169 492
170void InquireNotification(Service::Interface* self) { 493void InquireNotification(Service::Interface* self) {
@@ -186,14 +509,17 @@ void SendParameter(Service::Interface* self) {
186 size_t size; 509 size_t size;
187 VAddr buffer = rp.PopStaticBuffer(&size); 510 VAddr buffer = rp.PopStaticBuffer(&size);
188 511
189 std::shared_ptr<HLE::Applets::Applet> dest_applet = 512 LOG_DEBUG(Service_APT,
190 HLE::Applets::Applet::Get(static_cast<AppletId>(dst_app_id)); 513 "called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X,"
514 "buffer_size=0x%08X, handle=0x%08X, size=0x%08zX, in_param_buffer_ptr=0x%08X",
515 src_app_id, dst_app_id, signal_type, buffer_size, handle, size, buffer);
191 516
192 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 517 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
193 518
194 if (dest_applet == nullptr) { 519 // A new parameter can not be sent if the previous one hasn't been consumed yet
195 LOG_ERROR(Service_APT, "Unknown applet id=0x%08X", dst_app_id); 520 if (next_parameter) {
196 rb.Push<u32>(-1); // TODO(Subv): Find the right error code 521 rb.Push(ResultCode(ErrCodes::ParameterPresent, ErrorModule::Applet,
522 ErrorSummary::InvalidState, ErrorLevel::Status));
197 return; 523 return;
198 } 524 }
199 525
@@ -205,12 +531,14 @@ void SendParameter(Service::Interface* self) {
205 param.buffer.resize(buffer_size); 531 param.buffer.resize(buffer_size);
206 Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size()); 532 Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size());
207 533
208 rb.Push(dest_applet->ReceiveParameter(param)); 534 SendParameter(param);
209 535
210 LOG_WARNING(Service_APT, 536 // If the applet is running in HLE mode, use the HLE interface to communicate with it.
211 "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X," 537 if (auto dest_applet = HLE::Applets::Applet::Get(static_cast<AppletId>(dst_app_id))) {
212 "buffer_size=0x%08X, handle=0x%08X, size=0x%08zX, in_param_buffer_ptr=0x%08X", 538 rb.Push(dest_applet->ReceiveParameter(param));
213 src_app_id, dst_app_id, signal_type, buffer_size, handle, size, buffer); 539 } else {
540 rb.Push(RESULT_SUCCESS);
541 }
214} 542}
215 543
216void ReceiveParameter(Service::Interface* self) { 544void ReceiveParameter(Service::Interface* self) {
@@ -226,21 +554,40 @@ void ReceiveParameter(Service::Interface* self) {
226 "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)", 554 "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
227 buffer_size, static_buff_size); 555 buffer_size, static_buff_size);
228 556
557 LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size);
558
559 if (!next_parameter) {
560 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
561 rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::Applet,
562 ErrorSummary::InvalidState, ErrorLevel::Status));
563 return;
564 }
565
566 if (next_parameter->destination_id != app_id) {
567 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
568 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
569 ErrorLevel::Status));
570 return;
571 }
572
229 IPC::RequestBuilder rb = rp.MakeBuilder(4, 4); 573 IPC::RequestBuilder rb = rp.MakeBuilder(4, 4);
574
230 rb.Push(RESULT_SUCCESS); // No error 575 rb.Push(RESULT_SUCCESS); // No error
231 rb.Push(next_parameter.sender_id); 576 rb.Push(next_parameter->sender_id);
232 rb.Push(next_parameter.signal); // Signal type 577 rb.Push(next_parameter->signal); // Signal type
233 ASSERT_MSG(next_parameter.buffer.size() <= buffer_size, "Input static buffer is too small !"); 578 ASSERT_MSG(next_parameter->buffer.size() <= buffer_size, "Input static buffer is too small !");
234 rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size 579 rb.Push(static_cast<u32>(next_parameter->buffer.size())); // Parameter buffer size
235 580
236 rb.PushMoveHandles((next_parameter.object != nullptr) 581 rb.PushMoveHandles((next_parameter->object != nullptr)
237 ? Kernel::g_handle_table.Create(next_parameter.object).Unwrap() 582 ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap()
238 : 0); 583 : 0);
239 rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0);
240 584
241 Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size()); 585 rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0);
586
587 Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size());
242 588
243 LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); 589 // Clear the parameter
590 next_parameter = boost::none;
244} 591}
245 592
246void GlanceParameter(Service::Interface* self) { 593void GlanceParameter(Service::Interface* self) {
@@ -256,37 +603,74 @@ void GlanceParameter(Service::Interface* self) {
256 "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)", 603 "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
257 buffer_size, static_buff_size); 604 buffer_size, static_buff_size);
258 605
606 LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size);
607
608 if (!next_parameter) {
609 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
610 rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::Applet,
611 ErrorSummary::InvalidState, ErrorLevel::Status));
612 return;
613 }
614
615 if (next_parameter->destination_id != app_id) {
616 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
617 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
618 ErrorLevel::Status));
619 return;
620 }
621
259 IPC::RequestBuilder rb = rp.MakeBuilder(4, 4); 622 IPC::RequestBuilder rb = rp.MakeBuilder(4, 4);
260 rb.Push(RESULT_SUCCESS); // No error 623 rb.Push(RESULT_SUCCESS); // No error
261 rb.Push(next_parameter.sender_id); 624 rb.Push(next_parameter->sender_id);
262 rb.Push(next_parameter.signal); // Signal type 625 rb.Push(next_parameter->signal); // Signal type
263 ASSERT_MSG(next_parameter.buffer.size() <= buffer_size, "Input static buffer is too small !"); 626 ASSERT_MSG(next_parameter->buffer.size() <= buffer_size, "Input static buffer is too small !");
264 rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size 627 rb.Push(static_cast<u32>(next_parameter->buffer.size())); // Parameter buffer size
265 628
266 rb.PushCopyHandles((next_parameter.object != nullptr) 629 rb.PushMoveHandles((next_parameter->object != nullptr)
267 ? Kernel::g_handle_table.Create(next_parameter.object).Unwrap() 630 ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap()
268 : 0); 631 : 0);
269 rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0);
270 632
271 Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size()); 633 rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0);
634
635 Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size());
272 636
273 LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); 637 // Note: The NS module always clears the DSPSleep and DSPWakeup signals even in GlanceParameter.
638 if (next_parameter->signal == static_cast<u32>(SignalType::DspSleep) ||
639 next_parameter->signal == static_cast<u32>(SignalType::DspWakeup))
640 next_parameter = boost::none;
274} 641}
275 642
276void CancelParameter(Service::Interface* self) { 643void CancelParameter(Service::Interface* self) {
277 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xF, 4, 0); // 0xF0100 644 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xF, 4, 0); // 0xF0100
278 645
279 u32 check_sender = rp.Pop<u32>(); 646 bool check_sender = rp.Pop<bool>();
280 u32 sender_appid = rp.Pop<u32>(); 647 u32 sender_appid = rp.Pop<u32>();
281 u32 check_receiver = rp.Pop<u32>(); 648 bool check_receiver = rp.Pop<bool>();
282 u32 receiver_appid = rp.Pop<u32>(); 649 u32 receiver_appid = rp.Pop<u32>();
650
651 bool cancellation_success = true;
652
653 if (!next_parameter) {
654 cancellation_success = false;
655 } else {
656 if (check_sender && next_parameter->sender_id != sender_appid)
657 cancellation_success = false;
658
659 if (check_receiver && next_parameter->destination_id != receiver_appid)
660 cancellation_success = false;
661 }
662
663 if (cancellation_success)
664 next_parameter = boost::none;
665
283 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); 666 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
667
284 rb.Push(RESULT_SUCCESS); // No error 668 rb.Push(RESULT_SUCCESS); // No error
285 rb.Push(true); // Set to Success 669 rb.Push(cancellation_success);
286 670
287 LOG_WARNING(Service_APT, "(STUBBED) called check_sender=0x%08X, sender_appid=0x%08X, " 671 LOG_DEBUG(Service_APT, "called check_sender=%u, sender_appid=0x%08X, "
288 "check_receiver=0x%08X, receiver_appid=0x%08X", 672 "check_receiver=%u, receiver_appid=0x%08X",
289 check_sender, sender_appid, check_receiver, receiver_appid); 673 check_sender, sender_appid, check_receiver, receiver_appid);
290} 674}
291 675
292void PrepareToStartApplication(Service::Interface* self) { 676void PrepareToStartApplication(Service::Interface* self) {
@@ -383,7 +767,12 @@ void PrepareToStartLibraryApplet(Service::Interface* self) {
383 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 1, 0); // 0x180040 767 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 1, 0); // 0x180040
384 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>()); 768 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
385 769
770 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
771
386 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 772 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
773
774 // TODO(Subv): Launch the requested applet application.
775
387 auto applet = HLE::Applets::Applet::Get(applet_id); 776 auto applet = HLE::Applets::Applet::Get(applet_id);
388 if (applet) { 777 if (applet) {
389 LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id); 778 LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id);
@@ -391,14 +780,32 @@ void PrepareToStartLibraryApplet(Service::Interface* self) {
391 } else { 780 } else {
392 rb.Push(HLE::Applets::Applet::Create(applet_id)); 781 rb.Push(HLE::Applets::Applet::Create(applet_id));
393 } 782 }
394 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); 783}
784
785void PrepareToStartNewestHomeMenu(Service::Interface* self) {
786 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); // 0x1A0000
787 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
788
789 // TODO(Subv): This command can only be called by a System Applet (return 0xC8A0CC04 otherwise).
790
791 // This command must return an error when called, otherwise the Home Menu will try to reboot the
792 // system.
793 rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
794 ErrorSummary::InvalidState, ErrorLevel::Status));
795
796 LOG_DEBUG(Service_APT, "called");
395} 797}
396 798
397void PreloadLibraryApplet(Service::Interface* self) { 799void PreloadLibraryApplet(Service::Interface* self) {
398 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040 800 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040
399 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>()); 801 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
400 802
803 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
804
401 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 805 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
806
807 // TODO(Subv): Launch the requested applet application.
808
402 auto applet = HLE::Applets::Applet::Get(applet_id); 809 auto applet = HLE::Applets::Applet::Get(applet_id);
403 if (applet) { 810 if (applet) {
404 LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id); 811 LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id);
@@ -406,34 +813,40 @@ void PreloadLibraryApplet(Service::Interface* self) {
406 } else { 813 } else {
407 rb.Push(HLE::Applets::Applet::Create(applet_id)); 814 rb.Push(HLE::Applets::Applet::Create(applet_id));
408 } 815 }
409 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
410} 816}
411 817
412void StartLibraryApplet(Service::Interface* self) { 818void StartLibraryApplet(Service::Interface* self) {
413 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1E, 2, 4); // 0x1E0084 819 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1E, 2, 4); // 0x1E0084
414 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>()); 820 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
415 std::shared_ptr<HLE::Applets::Applet> applet = HLE::Applets::Applet::Get(applet_id);
416
417 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
418
419 if (applet == nullptr) {
420 LOG_ERROR(Service_APT, "unknown applet id=%08X", applet_id);
421 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0, false);
422 rb.Push<u32>(-1); // TODO(Subv): Find the right error code
423 return;
424 }
425 821
426 size_t buffer_size = rp.Pop<u32>(); 822 size_t buffer_size = rp.Pop<u32>();
427 Kernel::Handle handle = rp.PopHandle(); 823 Kernel::Handle handle = rp.PopHandle();
428 VAddr buffer_addr = rp.PopStaticBuffer(); 824 VAddr buffer_addr = rp.PopStaticBuffer();
429 825
430 AppletStartupParameter parameter; 826 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
431 parameter.object = Kernel::g_handle_table.GetGeneric(handle);
432 parameter.buffer.resize(buffer_size);
433 Memory::ReadBlock(buffer_addr, parameter.buffer.data(), parameter.buffer.size());
434 827
435 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 828 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
436 rb.Push(applet->Start(parameter)); 829
830 // Send the Wakeup signal to the applet
831 MessageParameter param;
832 param.destination_id = static_cast<u32>(applet_id);
833 param.sender_id = static_cast<u32>(AppletId::Application);
834 param.object = Kernel::g_handle_table.GetGeneric(handle);
835 param.signal = static_cast<u32>(SignalType::Wakeup);
836 param.buffer.resize(buffer_size);
837 Memory::ReadBlock(buffer_addr, param.buffer.data(), param.buffer.size());
838 SendParameter(param);
839
840 // In case the applet is being HLEd, attempt to communicate with it.
841 if (auto applet = HLE::Applets::Applet::Get(applet_id)) {
842 AppletStartupParameter parameter;
843 parameter.object = Kernel::g_handle_table.GetGeneric(handle);
844 parameter.buffer.resize(buffer_size);
845 Memory::ReadBlock(buffer_addr, parameter.buffer.data(), parameter.buffer.size());
846 rb.Push(applet->Start(parameter));
847 } else {
848 rb.Push(RESULT_SUCCESS);
849 }
437} 850}
438 851
439void CancelLibraryApplet(Service::Interface* self) { 852void CancelLibraryApplet(Service::Interface* self) {
@@ -647,125 +1060,6 @@ void CheckNew3DS(Service::Interface* self) {
647 LOG_WARNING(Service_APT, "(STUBBED) called"); 1060 LOG_WARNING(Service_APT, "(STUBBED) called");
648} 1061}
649 1062
650static u32 DecompressLZ11(const u8* in, u8* out) {
651 u32_le decompressed_size;
652 memcpy(&decompressed_size, in, sizeof(u32));
653 in += 4;
654
655 u8 type = decompressed_size & 0xFF;
656 ASSERT(type == 0x11);
657 decompressed_size >>= 8;
658
659 u32 current_out_size = 0;
660 u8 flags = 0, mask = 1;
661 while (current_out_size < decompressed_size) {
662 if (mask == 1) {
663 flags = *(in++);
664 mask = 0x80;
665 } else {
666 mask >>= 1;
667 }
668
669 if (flags & mask) {
670 u8 byte1 = *(in++);
671 u32 length = byte1 >> 4;
672 u32 offset;
673 if (length == 0) {
674 u8 byte2 = *(in++);
675 u8 byte3 = *(in++);
676 length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11;
677 offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1;
678 } else if (length == 1) {
679 u8 byte2 = *(in++);
680 u8 byte3 = *(in++);
681 u8 byte4 = *(in++);
682 length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111;
683 offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1;
684 } else {
685 u8 byte2 = *(in++);
686 length = (byte1 >> 4) + 0x1;
687 offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1;
688 }
689
690 for (u32 i = 0; i < length; i++) {
691 *out = *(out - offset);
692 ++out;
693 }
694
695 current_out_size += length;
696 } else {
697 *(out++) = *(in++);
698 current_out_size++;
699 }
700 }
701 return decompressed_size;
702}
703
704static bool LoadSharedFont() {
705 // TODO (wwylele): load different font archive for region CHN/KOR/TWN
706 const u64_le shared_font_archive_id_low = 0x0004009b00014002;
707 const u64_le shared_font_archive_id_high = 0x00000001ffffff00;
708 std::vector<u8> shared_font_archive_id(16);
709 std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64));
710 std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64));
711 FileSys::Path archive_path(shared_font_archive_id);
712 auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path);
713 if (archive_result.Failed())
714 return false;
715
716 std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS
717 FileSys::Path file_path(romfs_path);
718 FileSys::Mode open_mode = {};
719 open_mode.read_flag.Assign(1);
720 auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode);
721 if (file_result.Failed())
722 return false;
723
724 auto romfs = std::move(file_result).Unwrap();
725 std::vector<u8> romfs_buffer(romfs->backend->GetSize());
726 romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data());
727 romfs->backend->Close();
728
729 const u8* font_file = RomFS::GetFilePointer(romfs_buffer.data(), {u"cbf_std.bcfnt.lz"});
730 if (font_file == nullptr)
731 return false;
732
733 struct {
734 u32_le status;
735 u32_le region;
736 u32_le decompressed_size;
737 INSERT_PADDING_WORDS(0x1D);
738 } shared_font_header{};
739 static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size");
740
741 shared_font_header.status = 2; // successfully loaded
742 shared_font_header.region = 1; // region JPN/EUR/USA
743 shared_font_header.decompressed_size =
744 DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80));
745 std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header));
746 *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU"
747
748 return true;
749}
750
751static bool LoadLegacySharedFont() {
752 // This is the legacy method to load shared font.
753 // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
754 // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
755 // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
756 // "shared_font.bin" in the Citra "sysdata" directory.
757 std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
758
759 FileUtil::CreateFullPath(filepath); // Create path if not already created
760 FileUtil::IOFile file(filepath, "rb");
761 if (file.IsOpen()) {
762 file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
763 return true;
764 }
765
766 return false;
767}
768
769void Init() { 1063void Init() {
770 AddService(new APT_A_Interface); 1064 AddService(new APT_A_Interface);
771 AddService(new APT_S_Interface); 1065 AddService(new APT_S_Interface);
@@ -789,19 +1083,24 @@ void Init() {
789 shared_font_loaded = false; 1083 shared_font_loaded = false;
790 } 1084 }
791 1085
792 lock = Kernel::Mutex::Create(false, "APT_U:Lock"); 1086 lock = Kernel::Mutex::Create(false, 0, "APT_U:Lock");
793 1087
794 cpu_percent = 0; 1088 cpu_percent = 0;
795 unknown_ns_state_field = 0; 1089 unknown_ns_state_field = 0;
796 screen_capture_post_permission = 1090 screen_capture_post_permission =
797 ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value 1091 ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value
798 1092
799 // TODO(bunnei): Check if these are created in Initialize or on APT process startup. 1093 for (size_t slot = 0; slot < applet_slots.size(); ++slot) {
800 notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); 1094 auto& slot_data = applet_slots[slot];
801 parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start"); 1095 slot_data.slot = static_cast<AppletSlot>(slot);
802 1096 slot_data.applet_id = AppletId::None;
803 next_parameter.signal = static_cast<u32>(SignalType::Wakeup); 1097 slot_data.attributes.raw = 0;
804 next_parameter.destination_id = 0x300; 1098 slot_data.registered = false;
1099 slot_data.notification_event =
1100 Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Notification");
1101 slot_data.parameter_event =
1102 Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter");
1103 }
805} 1104}
806 1105
807void Shutdown() { 1106void Shutdown() {
@@ -809,10 +1108,14 @@ void Shutdown() {
809 shared_font_loaded = false; 1108 shared_font_loaded = false;
810 shared_font_relocated = false; 1109 shared_font_relocated = false;
811 lock = nullptr; 1110 lock = nullptr;
812 notification_event = nullptr;
813 parameter_event = nullptr;
814 1111
815 next_parameter.object = nullptr; 1112 for (auto& slot : applet_slots) {
1113 slot.registered = false;
1114 slot.notification_event = nullptr;
1115 slot.parameter_event = nullptr;
1116 }
1117
1118 next_parameter = boost::none;
816 1119
817 HLE::Applets::Shutdown(); 1120 HLE::Applets::Shutdown();
818} 1121}
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index ee80926d2..7b79e1f3e 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -72,6 +72,8 @@ enum class SignalType : u32 {
72 72
73/// App Id's used by APT functions 73/// App Id's used by APT functions
74enum class AppletId : u32 { 74enum class AppletId : u32 {
75 None = 0,
76 AnySystemApplet = 0x100,
75 HomeMenu = 0x101, 77 HomeMenu = 0x101,
76 AlternateMenu = 0x103, 78 AlternateMenu = 0x103,
77 Camera = 0x110, 79 Camera = 0x110,
@@ -83,6 +85,7 @@ enum class AppletId : u32 {
83 Miiverse = 0x117, 85 Miiverse = 0x117,
84 MiiversePost = 0x118, 86 MiiversePost = 0x118,
85 AmiiboSettings = 0x119, 87 AmiiboSettings = 0x119,
88 AnySysLibraryApplet = 0x200,
86 SoftwareKeyboard1 = 0x201, 89 SoftwareKeyboard1 = 0x201,
87 Ed1 = 0x202, 90 Ed1 = 0x202,
88 PnoteApp = 0x204, 91 PnoteApp = 0x204,
@@ -116,6 +119,13 @@ enum class ScreencapPostPermission : u32 {
116 DisableScreenshotPostingToMiiverse = 3 119 DisableScreenshotPostingToMiiverse = 3
117}; 120};
118 121
122namespace ErrCodes {
123enum {
124 ParameterPresent = 2,
125 InvalidAppletSlot = 4,
126};
127} // namespace ErrCodes
128
119/// Send a parameter to the currently-running application, which will read it via ReceiveParameter 129/// Send a parameter to the currently-running application, which will read it via ReceiveParameter
120void SendParameter(const MessageParameter& parameter); 130void SendParameter(const MessageParameter& parameter);
121 131
@@ -410,6 +420,16 @@ void GetAppCpuTimeLimit(Service::Interface* self);
410void PrepareToStartLibraryApplet(Service::Interface* self); 420void PrepareToStartLibraryApplet(Service::Interface* self);
411 421
412/** 422/**
423 * APT::PrepareToStartNewestHomeMenu service function
424 * Inputs:
425 * 0 : Command header [0x001A0000]
426 * Outputs:
427 * 0 : Return header
428 * 1 : Result of function
429 */
430void PrepareToStartNewestHomeMenu(Service::Interface* self);
431
432/**
413 * APT::PreloadLibraryApplet service function 433 * APT::PreloadLibraryApplet service function
414 * Inputs: 434 * Inputs:
415 * 0 : Command header [0x00160040] 435 * 0 : Command header [0x00160040]
diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp
index ec5668d05..bb78ee7d7 100644
--- a/src/core/hle/service/apt/apt_s.cpp
+++ b/src/core/hle/service/apt/apt_s.cpp
@@ -17,10 +17,10 @@ const Interface::FunctionInfo FunctionTable[] = {
17 {0x00060040, GetAppletInfo, "GetAppletInfo"}, 17 {0x00060040, GetAppletInfo, "GetAppletInfo"},
18 {0x00070000, nullptr, "GetLastSignaledAppletId"}, 18 {0x00070000, nullptr, "GetLastSignaledAppletId"},
19 {0x00080000, nullptr, "CountRegisteredApplet"}, 19 {0x00080000, nullptr, "CountRegisteredApplet"},
20 {0x00090040, nullptr, "IsRegistered"}, 20 {0x00090040, IsRegistered, "IsRegistered"},
21 {0x000A0040, nullptr, "GetAttribute"}, 21 {0x000A0040, nullptr, "GetAttribute"},
22 {0x000B0040, InquireNotification, "InquireNotification"}, 22 {0x000B0040, InquireNotification, "InquireNotification"},
23 {0x000C0104, nullptr, "SendParameter"}, 23 {0x000C0104, SendParameter, "SendParameter"},
24 {0x000D0080, ReceiveParameter, "ReceiveParameter"}, 24 {0x000D0080, ReceiveParameter, "ReceiveParameter"},
25 {0x000E0080, GlanceParameter, "GlanceParameter"}, 25 {0x000E0080, GlanceParameter, "GlanceParameter"},
26 {0x000F0100, nullptr, "CancelParameter"}, 26 {0x000F0100, nullptr, "CancelParameter"},
@@ -34,11 +34,11 @@ const Interface::FunctionInfo FunctionTable[] = {
34 {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, 34 {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
35 {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, 35 {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
36 {0x00190040, nullptr, "PrepareToStartSystemApplet"}, 36 {0x00190040, nullptr, "PrepareToStartSystemApplet"},
37 {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, 37 {0x001A0000, PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"},
38 {0x001B00C4, nullptr, "StartApplication"}, 38 {0x001B00C4, nullptr, "StartApplication"},
39 {0x001C0000, nullptr, "WakeupApplication"}, 39 {0x001C0000, nullptr, "WakeupApplication"},
40 {0x001D0000, nullptr, "CancelApplication"}, 40 {0x001D0000, nullptr, "CancelApplication"},
41 {0x001E0084, nullptr, "StartLibraryApplet"}, 41 {0x001E0084, StartLibraryApplet, "StartLibraryApplet"},
42 {0x001F0084, nullptr, "StartSystemApplet"}, 42 {0x001F0084, nullptr, "StartSystemApplet"},
43 {0x00200044, nullptr, "StartNewestHomeMenu"}, 43 {0x00200044, nullptr, "StartNewestHomeMenu"},
44 {0x00210000, nullptr, "OrderToCloseApplication"}, 44 {0x00210000, nullptr, "OrderToCloseApplication"},
diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp
index c9f9e9d95..8172edae8 100644
--- a/src/core/hle/service/cam/cam.cpp
+++ b/src/core/hle/service/cam/cam.cpp
@@ -177,7 +177,7 @@ void CompletionEventCallBack(u64 port_id, int) {
177 LOG_ERROR(Service_CAM, "The destination size (%u) doesn't match the source (%zu)!", 177 LOG_ERROR(Service_CAM, "The destination size (%u) doesn't match the source (%zu)!",
178 port.dest_size, buffer_size); 178 port.dest_size, buffer_size);
179 } 179 }
180 Memory::WriteBlock(port.dest, buffer.data(), std::min<u32>(port.dest_size, buffer_size)); 180 Memory::WriteBlock(port.dest, buffer.data(), std::min<size_t>(port.dest_size, buffer_size));
181 } 181 }
182 182
183 port.is_receiving = false; 183 port.is_receiving = false;
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 6624f1711..f78c25fb2 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -141,7 +141,7 @@ void GetCountryCodeString(Service::Interface* self) {
141 141
142void GetCountryCodeID(Service::Interface* self) { 142void GetCountryCodeID(Service::Interface* self) {
143 u32* cmd_buff = Kernel::GetCommandBuffer(); 143 u32* cmd_buff = Kernel::GetCommandBuffer();
144 u16 country_code = cmd_buff[1]; 144 u16 country_code = static_cast<u16>(cmd_buff[1]);
145 u16 country_code_id = 0; 145 u16 country_code_id = 0;
146 146
147 // The following algorithm will fail if the first country code isn't 0. 147 // The following algorithm will fail if the first country code isn't 0.
@@ -168,7 +168,7 @@ void GetCountryCodeID(Service::Interface* self) {
168 cmd_buff[2] = country_code_id; 168 cmd_buff[2] = country_code_id;
169} 169}
170 170
171static u32 GetRegionValue() { 171u32 GetRegionValue() {
172 if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) 172 if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT)
173 return preferred_region_code; 173 return preferred_region_code;
174 174
@@ -681,7 +681,7 @@ void GenerateConsoleUniqueId(u32& random_number, u64& console_id) {
681 CryptoPP::AutoSeededRandomPool rng; 681 CryptoPP::AutoSeededRandomPool rng;
682 random_number = rng.GenerateWord32(0, 0xFFFF); 682 random_number = rng.GenerateWord32(0, 0xFFFF);
683 u64_le local_friend_code_seed; 683 u64_le local_friend_code_seed;
684 rng.GenerateBlock(reinterpret_cast<byte*>(&local_friend_code_seed), 684 rng.GenerateBlock(reinterpret_cast<CryptoPP::byte*>(&local_friend_code_seed),
685 sizeof(local_friend_code_seed)); 685 sizeof(local_friend_code_seed));
686 console_id = (local_friend_code_seed & 0x3FFFFFFFF) | (static_cast<u64>(random_number) << 48); 686 console_id = (local_friend_code_seed & 0x3FFFFFFFF) | (static_cast<u64>(random_number) << 48);
687} 687}
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h
index 1659ebf32..282b6936b 100644
--- a/src/core/hle/service/cfg/cfg.h
+++ b/src/core/hle/service/cfg/cfg.h
@@ -101,6 +101,8 @@ void GetCountryCodeString(Service::Interface* self);
101 */ 101 */
102void GetCountryCodeID(Service::Interface* self); 102void GetCountryCodeID(Service::Interface* self);
103 103
104u32 GetRegionValue();
105
104/** 106/**
105 * CFG::SecureInfoGetRegion service function 107 * CFG::SecureInfoGetRegion service function
106 * Inputs: 108 * Inputs:
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
index 9471ec1ef..aac903ccb 100644
--- a/src/core/hle/service/csnd_snd.cpp
+++ b/src/core/hle/service/csnd_snd.cpp
@@ -47,7 +47,7 @@ static void Initialize(Interface* self) {
47 MemoryPermission::ReadWrite, 0, 47 MemoryPermission::ReadWrite, 0,
48 Kernel::MemoryRegion::BASE, "CSND:SharedMemory"); 48 Kernel::MemoryRegion::BASE, "CSND:SharedMemory");
49 49
50 mutex = Kernel::Mutex::Create(false, "CSND:mutex"); 50 mutex = Kernel::Mutex::Create(false, 0, "CSND:mutex");
51 51
52 cmd_buff[1] = RESULT_SUCCESS.raw; 52 cmd_buff[1] = RESULT_SUCCESS.raw;
53 cmd_buff[2] = IPC::CopyHandleDesc(2); 53 cmd_buff[2] = IPC::CopyHandleDesc(2);
diff --git a/src/core/hle/service/dlp/dlp_clnt.cpp b/src/core/hle/service/dlp/dlp_clnt.cpp
index 56f934b3f..6f2bf2061 100644
--- a/src/core/hle/service/dlp/dlp_clnt.cpp
+++ b/src/core/hle/service/dlp/dlp_clnt.cpp
@@ -8,7 +8,26 @@ namespace Service {
8namespace DLP { 8namespace DLP {
9 9
10const Interface::FunctionInfo FunctionTable[] = { 10const Interface::FunctionInfo FunctionTable[] = {
11 {0x000100C3, nullptr, "Initialize"}, {0x00110000, nullptr, "GetWirelessRebootPassphrase"}, 11 {0x000100C3, nullptr, "Initialize"},
12 {0x00020000, nullptr, "Finalize"},
13 {0x00030000, nullptr, "GetEventDesc"},
14 {0x00040000, nullptr, "GetChannel"},
15 {0x00050180, nullptr, "StartScan"},
16 {0x00060000, nullptr, "StopScan"},
17 {0x00070080, nullptr, "GetServerInfo"},
18 {0x00080100, nullptr, "GetTitleInfo"},
19 {0x00090040, nullptr, "GetTitleInfoInOrder"},
20 {0x000A0080, nullptr, "DeleteScanInfo"},
21 {0x000B0100, nullptr, "PrepareForSystemDownload"},
22 {0x000C0000, nullptr, "StartSystemDownload"},
23 {0x000D0100, nullptr, "StartTitleDownload"},
24 {0x000E0000, nullptr, "GetMyStatus"},
25 {0x000F0040, nullptr, "GetConnectingNodes"},
26 {0x00100040, nullptr, "GetNodeInfo"},
27 {0x00110000, nullptr, "GetWirelessRebootPassphrase"},
28 {0x00120000, nullptr, "StopSession"},
29 {0x00130100, nullptr, "GetCupVersion"},
30 {0x00140100, nullptr, "GetDupAvailability"},
12}; 31};
13 32
14DLP_CLNT_Interface::DLP_CLNT_Interface() { 33DLP_CLNT_Interface::DLP_CLNT_Interface() {
diff --git a/src/core/hle/service/dlp/dlp_fkcl.cpp b/src/core/hle/service/dlp/dlp_fkcl.cpp
index 29b9d52e0..fe6be7d32 100644
--- a/src/core/hle/service/dlp/dlp_fkcl.cpp
+++ b/src/core/hle/service/dlp/dlp_fkcl.cpp
@@ -8,7 +8,23 @@ namespace Service {
8namespace DLP { 8namespace DLP {
9 9
10const Interface::FunctionInfo FunctionTable[] = { 10const Interface::FunctionInfo FunctionTable[] = {
11 {0x00010083, nullptr, "Initialize"}, {0x000F0000, nullptr, "GetWirelessRebootPassphrase"}, 11 {0x00010083, nullptr, "Initialize"},
12 {0x00020000, nullptr, "Finalize"},
13 {0x00030000, nullptr, "GetEventDesc"},
14 {0x00040000, nullptr, "GetChannels"},
15 {0x00050180, nullptr, "StartScan"},
16 {0x00060000, nullptr, "StopScan"},
17 {0x00070080, nullptr, "GetServerInfo"},
18 {0x00080100, nullptr, "GetTitleInfo"},
19 {0x00090040, nullptr, "GetTitleInfoInOrder"},
20 {0x000A0080, nullptr, "DeleteScanInfo"},
21 {0x000B0100, nullptr, "StartFakeSession"},
22 {0x000C0000, nullptr, "GetMyStatus"},
23 {0x000D0040, nullptr, "GetConnectingNodes"},
24 {0x000E0040, nullptr, "GetNodeInfo"},
25 {0x000F0000, nullptr, "GetWirelessRebootPassphrase"},
26 {0x00100000, nullptr, "StopSession"},
27 {0x00110203, nullptr, "Initialize2"},
12}; 28};
13 29
14DLP_FKCL_Interface::DLP_FKCL_Interface() { 30DLP_FKCL_Interface::DLP_FKCL_Interface() {
diff --git a/src/core/hle/service/dlp/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp
index 32cfa2c44..1bcea43d3 100644
--- a/src/core/hle/service/dlp/dlp_srvr.cpp
+++ b/src/core/hle/service/dlp/dlp_srvr.cpp
@@ -11,7 +11,7 @@
11namespace Service { 11namespace Service {
12namespace DLP { 12namespace DLP {
13 13
14static void unk_0x000E0040(Interface* self) { 14static void IsChild(Interface* self) {
15 u32* cmd_buff = Kernel::GetCommandBuffer(); 15 u32* cmd_buff = Kernel::GetCommandBuffer();
16 16
17 cmd_buff[1] = RESULT_SUCCESS.raw; 17 cmd_buff[1] = RESULT_SUCCESS.raw;
@@ -24,14 +24,19 @@ const Interface::FunctionInfo FunctionTable[] = {
24 {0x00010183, nullptr, "Initialize"}, 24 {0x00010183, nullptr, "Initialize"},
25 {0x00020000, nullptr, "Finalize"}, 25 {0x00020000, nullptr, "Finalize"},
26 {0x00030000, nullptr, "GetServerState"}, 26 {0x00030000, nullptr, "GetServerState"},
27 {0x00040000, nullptr, "GetEventDescription"},
27 {0x00050080, nullptr, "StartAccepting"}, 28 {0x00050080, nullptr, "StartAccepting"},
29 {0x00060000, nullptr, "EndAccepting"},
28 {0x00070000, nullptr, "StartDistribution"}, 30 {0x00070000, nullptr, "StartDistribution"},
29 {0x000800C0, nullptr, "SendWirelessRebootPassphrase"}, 31 {0x000800C0, nullptr, "SendWirelessRebootPassphrase"},
30 {0x00090040, nullptr, "AcceptClient"}, 32 {0x00090040, nullptr, "AcceptClient"},
33 {0x000A0040, nullptr, "DisconnectClient"},
31 {0x000B0042, nullptr, "GetConnectingClients"}, 34 {0x000B0042, nullptr, "GetConnectingClients"},
32 {0x000C0040, nullptr, "GetClientInfo"}, 35 {0x000C0040, nullptr, "GetClientInfo"},
33 {0x000D0040, nullptr, "GetClientState"}, 36 {0x000D0040, nullptr, "GetClientState"},
34 {0x000E0040, unk_0x000E0040, "unk_0x000E0040"}, 37 {0x000E0040, IsChild, "IsChild"},
38 {0x000F0303, nullptr, "InitializeWithName"},
39 {0x00100000, nullptr, "GetDupNoticeNeed"},
35}; 40};
36 41
37DLP_SRVR_Interface::DLP_SRVR_Interface() { 42DLP_SRVR_Interface::DLP_SRVR_Interface() {
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index 7d746054f..42f8950f9 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -147,9 +147,10 @@ static void LoadComponent(Service::Interface* self) {
147 LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64, 147 LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64,
148 Common::ComputeHash64(component_data.data(), component_data.size())); 148 Common::ComputeHash64(component_data.data(), component_data.size()));
149 // Some versions of the firmware have the location of DSP structures listed here. 149 // Some versions of the firmware have the location of DSP structures listed here.
150 ASSERT(size > 0x37C); 150 if (size > 0x37C) {
151 LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, 151 LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64,
152 Common::ComputeHash64(component_data.data() + 0x340, 60)); 152 Common::ComputeHash64(component_data.data() + 0x340, 60));
153 }
153 154
154 LOG_WARNING(Service_DSP, 155 LOG_WARNING(Service_DSP,
155 "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X", 156 "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X",
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index 033fbc9aa..4ee7df73c 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -20,6 +20,7 @@
20#include "core/file_sys/archive_savedata.h" 20#include "core/file_sys/archive_savedata.h"
21#include "core/file_sys/archive_sdmc.h" 21#include "core/file_sys/archive_sdmc.h"
22#include "core/file_sys/archive_sdmcwriteonly.h" 22#include "core/file_sys/archive_sdmcwriteonly.h"
23#include "core/file_sys/archive_selfncch.h"
23#include "core/file_sys/archive_systemsavedata.h" 24#include "core/file_sys/archive_systemsavedata.h"
24#include "core/file_sys/directory_backend.h" 25#include "core/file_sys/directory_backend.h"
25#include "core/file_sys/errors.h" 26#include "core/file_sys/errors.h"
@@ -48,7 +49,7 @@ struct hash<Service::FS::ArchiveIdCode> {
48 return std::hash<Type>()(static_cast<Type>(id_code)); 49 return std::hash<Type>()(static_cast<Type>(id_code));
49 } 50 }
50}; 51};
51} 52} // namespace std
52 53
53static constexpr Kernel::Handle INVALID_HANDLE{}; 54static constexpr Kernel::Handle INVALID_HANDLE{};
54 55
@@ -216,7 +217,7 @@ void Directory::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> serve
216 LOG_TRACE(Service_FS, "Read %s: count=%d", GetName().c_str(), count); 217 LOG_TRACE(Service_FS, "Read %s: count=%d", GetName().c_str(), count);
217 218
218 // Number of entries actually read 219 // Number of entries actually read
219 u32 read = backend->Read(entries.size(), entries.data()); 220 u32 read = backend->Read(static_cast<u32>(entries.size()), entries.data());
220 cmd_buff[2] = read; 221 cmd_buff[2] = read;
221 Memory::WriteBlock(address, entries.data(), read * sizeof(FileSys::Entry)); 222 Memory::WriteBlock(address, entries.data(), read * sizeof(FileSys::Entry));
222 break; 223 break;
@@ -564,6 +565,21 @@ void RegisterArchiveTypes() {
564 auto systemsavedata_factory = 565 auto systemsavedata_factory =
565 std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory); 566 std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory);
566 RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); 567 RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData);
568
569 auto selfncch_factory = std::make_unique<FileSys::ArchiveFactory_SelfNCCH>();
570 RegisterArchiveType(std::move(selfncch_factory), ArchiveIdCode::SelfNCCH);
571}
572
573void RegisterSelfNCCH(Loader::AppLoader& app_loader) {
574 auto itr = id_code_map.find(ArchiveIdCode::SelfNCCH);
575 if (itr == id_code_map.end()) {
576 LOG_ERROR(Service_FS,
577 "Could not register a new NCCH because the SelfNCCH archive hasn't been created");
578 return;
579 }
580
581 auto* factory = static_cast<FileSys::ArchiveFactory_SelfNCCH*>(itr->second.get());
582 factory->Register(app_loader);
567} 583}
568 584
569void UnregisterArchiveTypes() { 585void UnregisterArchiveTypes() {
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index 3a3371c88..e3c8fc2ef 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -21,6 +21,10 @@ static constexpr char SYSTEM_ID[]{"00000000000000000000000000000000"};
21/// The scrambled SD card CID, also known as ID1 21/// The scrambled SD card CID, also known as ID1
22static constexpr char SDCARD_ID[]{"00000000000000000000000000000000"}; 22static constexpr char SDCARD_ID[]{"00000000000000000000000000000000"};
23 23
24namespace Loader {
25class AppLoader;
26}
27
24namespace Service { 28namespace Service {
25namespace FS { 29namespace FS {
26 30
@@ -259,6 +263,9 @@ void ArchiveInit();
259/// Shutdown archives 263/// Shutdown archives
260void ArchiveShutdown(); 264void ArchiveShutdown();
261 265
266/// Registers a new NCCH file with the SelfNCCH archive factory
267void RegisterSelfNCCH(Loader::AppLoader& app_loader);
268
262/// Register all archive types 269/// Register all archive types
263void RegisterArchiveTypes(); 270void RegisterArchiveTypes();
264 271
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 2014b8461..379fbd71c 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -7,8 +7,9 @@
7#include <cmath> 7#include <cmath>
8#include <memory> 8#include <memory>
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/3ds.h"
11#include "core/core.h"
10#include "core/core_timing.h" 12#include "core/core_timing.h"
11#include "core/frontend/emu_window.h"
12#include "core/frontend/input.h" 13#include "core/frontend/input.h"
13#include "core/hle/ipc.h" 14#include "core/hle/ipc.h"
14#include "core/hle/kernel/event.h" 15#include "core/hle/kernel/event.h"
@@ -18,7 +19,6 @@
18#include "core/hle/service/hid/hid_spvr.h" 19#include "core/hle/service/hid/hid_spvr.h"
19#include "core/hle/service/hid/hid_user.h" 20#include "core/hle/service/hid/hid_user.h"
20#include "core/hle/service/service.h" 21#include "core/hle/service/service.h"
21#include "video_core/video_core.h"
22 22
23namespace Service { 23namespace Service {
24namespace HID { 24namespace HID {
@@ -50,10 +50,15 @@ constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234;
50constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; 50constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104;
51constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; 51constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101;
52 52
53constexpr float accelerometer_coef = 512.0f; // measured from hw test result
54constexpr float gyroscope_coef = 14.375f; // got from hwtest GetGyroscopeLowRawToDpsCoefficient call
55
53static std::atomic<bool> is_device_reload_pending; 56static std::atomic<bool> is_device_reload_pending;
54static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 57static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
55 buttons; 58 buttons;
56static std::unique_ptr<Input::AnalogDevice> circle_pad; 59static std::unique_ptr<Input::AnalogDevice> circle_pad;
60static std::unique_ptr<Input::MotionDevice> motion_device;
61static std::unique_ptr<Input::TouchDevice> touch_device;
57 62
58DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) { 63DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
59 // 30 degree and 60 degree are angular thresholds for directions 64 // 30 degree and 60 degree are angular thresholds for directions
@@ -90,6 +95,8 @@ static void LoadInputDevices() {
90 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); 95 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
91 circle_pad = Input::CreateDevice<Input::AnalogDevice>( 96 circle_pad = Input::CreateDevice<Input::AnalogDevice>(
92 Settings::values.analogs[Settings::NativeAnalog::CirclePad]); 97 Settings::values.analogs[Settings::NativeAnalog::CirclePad]);
98 motion_device = Input::CreateDevice<Input::MotionDevice>(Settings::values.motion_device);
99 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
93} 100}
94 101
95static void UnloadInputDevices() { 102static void UnloadInputDevices() {
@@ -97,6 +104,8 @@ static void UnloadInputDevices() {
97 button.reset(); 104 button.reset();
98 } 105 }
99 circle_pad.reset(); 106 circle_pad.reset();
107 motion_device.reset();
108 touch_device.reset();
100} 109}
101 110
102static void UpdatePadCallback(u64 userdata, int cycles_late) { 111static void UpdatePadCallback(u64 userdata, int cycles_late) {
@@ -165,8 +174,10 @@ static void UpdatePadCallback(u64 userdata, int cycles_late) {
165 // Get the current touch entry 174 // Get the current touch entry
166 TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index]; 175 TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index];
167 bool pressed = false; 176 bool pressed = false;
168 177 float x, y;
169 std::tie(touch_entry.x, touch_entry.y, pressed) = VideoCore::g_emu_window->GetTouchState(); 178 std::tie(x, y, pressed) = touch_device->GetStatus();
179 touch_entry.x = static_cast<u16>(x * Core::kScreenBottomWidth);
180 touch_entry.y = static_cast<u16>(y * Core::kScreenBottomHeight);
170 touch_entry.valid.Assign(pressed ? 1 : 0); 181 touch_entry.valid.Assign(pressed ? 1 : 0);
171 182
172 // TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which 183 // TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which
@@ -193,10 +204,19 @@ static void UpdateAccelerometerCallback(u64 userdata, int cycles_late) {
193 mem->accelerometer.index = next_accelerometer_index; 204 mem->accelerometer.index = next_accelerometer_index;
194 next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size(); 205 next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size();
195 206
207 Math::Vec3<float> accel;
208 std::tie(accel, std::ignore) = motion_device->GetStatus();
209 accel *= accelerometer_coef;
210 // TODO(wwylele): do a time stretch like the one in UpdateGyroscopeCallback
211 // The time stretch formula should be like
212 // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
213
196 AccelerometerDataEntry& accelerometer_entry = 214 AccelerometerDataEntry& accelerometer_entry =
197 mem->accelerometer.entries[mem->accelerometer.index]; 215 mem->accelerometer.entries[mem->accelerometer.index];
198 std::tie(accelerometer_entry.x, accelerometer_entry.y, accelerometer_entry.z) = 216
199 VideoCore::g_emu_window->GetAccelerometerState(); 217 accelerometer_entry.x = static_cast<s16>(accel.x);
218 accelerometer_entry.y = static_cast<s16>(accel.y);
219 accelerometer_entry.z = static_cast<s16>(accel.z);
200 220
201 // Make up "raw" entry 221 // Make up "raw" entry
202 // TODO(wwylele): 222 // TODO(wwylele):
@@ -227,8 +247,14 @@ static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) {
227 next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size(); 247 next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size();
228 248
229 GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index]; 249 GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index];
230 std::tie(gyroscope_entry.x, gyroscope_entry.y, gyroscope_entry.z) = 250
231 VideoCore::g_emu_window->GetGyroscopeState(); 251 Math::Vec3<float> gyro;
252 std::tie(std::ignore, gyro) = motion_device->GetStatus();
253 double stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale();
254 gyro *= gyroscope_coef * static_cast<float>(stretch);
255 gyroscope_entry.x = static_cast<s16>(gyro.x);
256 gyroscope_entry.y = static_cast<s16>(gyro.y);
257 gyroscope_entry.z = static_cast<s16>(gyro.z);
232 258
233 // Make up "raw" entry 259 // Make up "raw" entry
234 mem->gyroscope.raw_entry.x = gyroscope_entry.x; 260 mem->gyroscope.raw_entry.x = gyroscope_entry.x;
@@ -326,7 +352,7 @@ void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self) {
326 352
327 cmd_buff[1] = RESULT_SUCCESS.raw; 353 cmd_buff[1] = RESULT_SUCCESS.raw;
328 354
329 f32 coef = VideoCore::g_emu_window->GetGyroscopeRawToDpsCoefficient(); 355 f32 coef = gyroscope_coef;
330 memcpy(&cmd_buff[2], &coef, 4); 356 memcpy(&cmd_buff[2], &coef, 4);
331} 357}
332 358
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 1ef972e70..ef25926b5 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -24,7 +24,7 @@ namespace HID {
24 */ 24 */
25struct PadState { 25struct PadState {
26 union { 26 union {
27 u32 hex; 27 u32 hex{};
28 28
29 BitField<0, 1, u32> a; 29 BitField<0, 1, u32> a;
30 BitField<1, 1, u32> b; 30 BitField<1, 1, u32> b;
diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp
index 837413f93..0912d5756 100644
--- a/src/core/hle/service/ir/ir_rst.cpp
+++ b/src/core/hle/service/ir/ir_rst.cpp
@@ -18,7 +18,7 @@ namespace Service {
18namespace IR { 18namespace IR {
19 19
20union PadState { 20union PadState {
21 u32_le hex; 21 u32_le hex{};
22 22
23 BitField<14, 1, u32_le> zl; 23 BitField<14, 1, u32_le> zl;
24 BitField<15, 1, u32_le> zr; 24 BitField<15, 1, u32_le> zr;
diff --git a/src/core/hle/service/ldr_ro/cro_helper.h b/src/core/hle/service/ldr_ro/cro_helper.h
index 3bc10dbdc..57b4fb6df 100644
--- a/src/core/hle/service/ldr_ro/cro_helper.h
+++ b/src/core/hle/service/ldr_ro/cro_helper.h
@@ -413,7 +413,8 @@ private:
413 */ 413 */
414 template <typename T> 414 template <typename T>
415 void GetEntry(std::size_t index, T& data) const { 415 void GetEntry(std::size_t index, T& data) const {
416 Memory::ReadBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); 416 Memory::ReadBlock(GetField(T::TABLE_OFFSET_FIELD) + static_cast<u32>(index * sizeof(T)),
417 &data, sizeof(T));
417 } 418 }
418 419
419 /** 420 /**
@@ -425,7 +426,8 @@ private:
425 */ 426 */
426 template <typename T> 427 template <typename T>
427 void SetEntry(std::size_t index, const T& data) { 428 void SetEntry(std::size_t index, const T& data) {
428 Memory::WriteBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); 429 Memory::WriteBlock(GetField(T::TABLE_OFFSET_FIELD) + static_cast<u32>(index * sizeof(T)),
430 &data, sizeof(T));
429 } 431 }
430 432
431 /** 433 /**
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index d5624fe54..b10d5852b 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -5,6 +5,8 @@
5#include "common/common_types.h" 5#include "common/common_types.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/hle/ipc.h" 7#include "core/hle/ipc.h"
8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/event.h"
8#include "core/hle/service/nim/nim.h" 10#include "core/hle/service/nim/nim.h"
9#include "core/hle/service/nim/nim_aoc.h" 11#include "core/hle/service/nim/nim_aoc.h"
10#include "core/hle/service/nim/nim_s.h" 12#include "core/hle/service/nim/nim_s.h"
@@ -14,6 +16,16 @@
14namespace Service { 16namespace Service {
15namespace NIM { 17namespace NIM {
16 18
19static Kernel::SharedPtr<Kernel::Event> nim_system_update_event;
20
21void CheckForSysUpdateEvent(Service::Interface* self) {
22 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 0, 0); // 0x50000
23 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
24 rb.Push(RESULT_SUCCESS);
25 rb.PushCopyHandles(Kernel::g_handle_table.Create(nim_system_update_event).Unwrap());
26 LOG_TRACE(Service_NIM, "called");
27}
28
17void CheckSysUpdateAvailable(Service::Interface* self) { 29void CheckSysUpdateAvailable(Service::Interface* self) {
18 u32* cmd_buff = Kernel::GetCommandBuffer(); 30 u32* cmd_buff = Kernel::GetCommandBuffer();
19 31
@@ -29,9 +41,13 @@ void Init() {
29 AddService(new NIM_AOC_Interface); 41 AddService(new NIM_AOC_Interface);
30 AddService(new NIM_S_Interface); 42 AddService(new NIM_S_Interface);
31 AddService(new NIM_U_Interface); 43 AddService(new NIM_U_Interface);
44
45 nim_system_update_event = Kernel::Event::Create(ResetType::OneShot, "NIM System Update Event");
32} 46}
33 47
34void Shutdown() {} 48void Shutdown() {
49 nim_system_update_event = nullptr;
50}
35 51
36} // namespace NIM 52} // namespace NIM
37 53
diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h
index c3106f18b..dbf605e5a 100644
--- a/src/core/hle/service/nim/nim.h
+++ b/src/core/hle/service/nim/nim.h
@@ -11,6 +11,17 @@ class Interface;
11namespace NIM { 11namespace NIM {
12 12
13/** 13/**
14 * NIM::CheckForSysUpdateEvent service function
15 * Inputs:
16 * 1 : None
17 * Outputs:
18 * 1 : Result of function, 0 on success, otherwise error code
19 * 2 : Copy handle descriptor
20 * 3 : System Update event handle
21 */
22void CheckForSysUpdateEvent(Service::Interface* self);
23
24/**
14 * NIM::CheckSysUpdateAvailable service function 25 * NIM::CheckSysUpdateAvailable service function
15 * Inputs: 26 * Inputs:
16 * 1 : None 27 * 1 : None
diff --git a/src/core/hle/service/nim/nim_u.cpp b/src/core/hle/service/nim/nim_u.cpp
index 7664bad60..569660278 100644
--- a/src/core/hle/service/nim/nim_u.cpp
+++ b/src/core/hle/service/nim/nim_u.cpp
@@ -12,7 +12,7 @@ const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010000, nullptr, "StartSysUpdate"}, 12 {0x00010000, nullptr, "StartSysUpdate"},
13 {0x00020000, nullptr, "GetUpdateDownloadProgress"}, 13 {0x00020000, nullptr, "GetUpdateDownloadProgress"},
14 {0x00040000, nullptr, "FinishTitlesInstall"}, 14 {0x00040000, nullptr, "FinishTitlesInstall"},
15 {0x00050000, nullptr, "CheckForSysUpdateEvent"}, 15 {0x00050000, CheckForSysUpdateEvent, "CheckForSysUpdateEvent"},
16 {0x00090000, CheckSysUpdateAvailable, "CheckSysUpdateAvailable"}, 16 {0x00090000, CheckSysUpdateAvailable, "CheckSysUpdateAvailable"},
17 {0x000A0000, nullptr, "GetState"}, 17 {0x000A0000, nullptr, "GetState"},
18 {0x000B0000, nullptr, "GetSystemTitleHash"}, 18 {0x000B0000, nullptr, "GetSystemTitleHash"},
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
new file mode 100644
index 000000000..9e19c38bf
--- /dev/null
+++ b/src/core/hle/service/ns/ns.cpp
@@ -0,0 +1,16 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ns/ns.h"
6#include "core/hle/service/ns/ns_s.h"
7
8namespace Service {
9namespace NS {
10
11void InstallInterfaces(SM::ServiceManager& service_manager) {
12 std::make_shared<NS_S>()->InstallAsService(service_manager);
13}
14
15} // namespace NS
16} // namespace Service
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
new file mode 100644
index 000000000..c3d67d98c
--- /dev/null
+++ b/src/core/hle/service/ns/ns.h
@@ -0,0 +1,16 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9namespace Service {
10namespace NS {
11
12/// Registers all NS services with the specified service manager.
13void InstallInterfaces(SM::ServiceManager& service_manager);
14
15} // namespace NS
16} // namespace Service
diff --git a/src/core/hle/service/ns/ns_s.cpp b/src/core/hle/service/ns/ns_s.cpp
new file mode 100644
index 000000000..d952888dc
--- /dev/null
+++ b/src/core/hle/service/ns/ns_s.cpp
@@ -0,0 +1,34 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ns/ns_s.h"
6
7namespace Service {
8namespace NS {
9
10NS_S::NS_S() : ServiceFramework("ns:s", 2) {
11 static const FunctionInfo functions[] = {
12 {0x000100C0, nullptr, "LaunchFIRM"},
13 {0x000200C0, nullptr, "LaunchTitle"},
14 {0x00030000, nullptr, "TerminateApplication"},
15 {0x00040040, nullptr, "TerminateProcess"},
16 {0x000500C0, nullptr, "LaunchApplicationFIRM"},
17 {0x00060042, nullptr, "SetFIRMParams4A0"},
18 {0x00070042, nullptr, "CardUpdateInitialize"},
19 {0x00080000, nullptr, "CardUpdateShutdown"},
20 {0x000D0140, nullptr, "SetTWLBannerHMAC"},
21 {0x000E0000, nullptr, "ShutdownAsync"},
22 {0x00100180, nullptr, "RebootSystem"},
23 {0x00110100, nullptr, "TerminateTitle"},
24 {0x001200C0, nullptr, "SetApplicationCpuTimeLimit"},
25 {0x00150140, nullptr, "LaunchApplication"},
26 {0x00160000, nullptr, "RebootSystemClean"},
27 };
28 RegisterHandlers(functions);
29}
30
31NS_S::~NS_S() = default;
32
33} // namespace NS
34} // namespace Service
diff --git a/src/core/hle/service/ns_s.h b/src/core/hle/service/ns/ns_s.h
index 90288a521..660ae453f 100644
--- a/src/core/hle/service/ns_s.h
+++ b/src/core/hle/service/ns/ns_s.h
@@ -4,18 +4,17 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/kernel/kernel.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace Service { 10namespace Service {
10namespace NS { 11namespace NS {
11 12
12class NS_S final : public Interface { 13/// Interface to "ns:s" service
14class NS_S final : public ServiceFramework<NS_S> {
13public: 15public:
14 NS_S(); 16 NS_S();
15 17 ~NS_S();
16 std::string GetPortName() const override {
17 return "ns:s";
18 }
19}; 18};
20 19
21} // namespace NS 20} // namespace NS
diff --git a/src/core/hle/service/ns_s.cpp b/src/core/hle/service/ns_s.cpp
deleted file mode 100644
index 215c9aacc..000000000
--- a/src/core/hle/service/ns_s.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ns_s.h"
6
7namespace Service {
8namespace NS {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 {0x000100C0, nullptr, "LaunchFIRM"},
12 {0x000200C0, nullptr, "LaunchTitle"},
13 {0x00030000, nullptr, "TerminateApplication"},
14 {0x00040040, nullptr, "TerminateProcess"},
15 {0x000500C0, nullptr, "LaunchApplicationFIRM"},
16 {0x00060042, nullptr, "SetFIRMParams4A0"},
17 {0x00070042, nullptr, "CardUpdateInitialize"},
18 {0x00080000, nullptr, "CardUpdateShutdown"},
19 {0x000D0140, nullptr, "SetTWLBannerHMAC"},
20 {0x000E0000, nullptr, "ShutdownAsync"},
21 {0x00100180, nullptr, "RebootSystem"},
22 {0x00110100, nullptr, "TerminateTitle"},
23 {0x001200C0, nullptr, "SetApplicationCpuTimeLimit"},
24 {0x00150140, nullptr, "LaunchApplication"},
25 {0x00160000, nullptr, "RebootSystemClean"},
26};
27
28NS_S::NS_S() {
29 Register(FunctionTable);
30}
31
32} // namespace NS
33} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp
index 6dbdff044..87a6b0eca 100644
--- a/src/core/hle/service/nwm/nwm_uds.cpp
+++ b/src/core/hle/service/nwm/nwm_uds.cpp
@@ -2,8 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include <array> 6#include <array>
6#include <cstring> 7#include <cstring>
8#include <list>
9#include <mutex>
7#include <unordered_map> 10#include <unordered_map>
8#include <vector> 11#include <vector>
9#include "common/common_types.h" 12#include "common/common_types.h"
@@ -12,11 +15,14 @@
12#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/event.h" 16#include "core/hle/kernel/event.h"
14#include "core/hle/kernel/shared_memory.h" 17#include "core/hle/kernel/shared_memory.h"
18#include "core/hle/lock.h"
15#include "core/hle/result.h" 19#include "core/hle/result.h"
16#include "core/hle/service/nwm/nwm_uds.h" 20#include "core/hle/service/nwm/nwm_uds.h"
17#include "core/hle/service/nwm/uds_beacon.h" 21#include "core/hle/service/nwm/uds_beacon.h"
22#include "core/hle/service/nwm/uds_connection.h"
18#include "core/hle/service/nwm/uds_data.h" 23#include "core/hle/service/nwm/uds_data.h"
19#include "core/memory.h" 24#include "core/memory.h"
25#include "network/network.h"
20 26
21namespace Service { 27namespace Service {
22namespace NWM { 28namespace NWM {
@@ -34,9 +40,12 @@ static ConnectionStatus connection_status{};
34/* Node information about the current network. 40/* Node information about the current network.
35 * The amount of elements in this vector is always the maximum number 41 * The amount of elements in this vector is always the maximum number
36 * of nodes specified in the network configuration. 42 * of nodes specified in the network configuration.
37 * The first node is always the host, so this always contains at least 1 entry. 43 * The first node is always the host.
38 */ 44 */
39static NodeList node_info(1); 45static NodeList node_info;
46
47// Node information about our own system.
48static NodeInfo current_node;
40 49
41// Mapping of bind node ids to their respective events. 50// Mapping of bind node ids to their respective events.
42static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events; 51static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events;
@@ -51,6 +60,298 @@ static NetworkInfo network_info;
51// Event that will generate and send the 802.11 beacon frames. 60// Event that will generate and send the 802.11 beacon frames.
52static int beacon_broadcast_event; 61static int beacon_broadcast_event;
53 62
63// Mutex to synchronize access to the connection status between the emulation thread and the
64// network thread.
65static std::mutex connection_status_mutex;
66
67// Mutex to synchronize access to the list of received beacons between the emulation thread and the
68// network thread.
69static std::mutex beacon_mutex;
70
71// Number of beacons to store before we start dropping the old ones.
72// TODO(Subv): Find a more accurate value for this limit.
73constexpr size_t MaxBeaconFrames = 15;
74
75// List of the last <MaxBeaconFrames> beacons received from the network.
76static std::list<Network::WifiPacket> received_beacons;
77
78/**
79 * Returns a list of received 802.11 beacon frames from the specified sender since the last call.
80 */
81std::list<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) {
82 std::lock_guard<std::mutex> lock(beacon_mutex);
83 if (sender != Network::BroadcastMac) {
84 std::list<Network::WifiPacket> filtered_list;
85 const auto beacon = std::find_if(received_beacons.begin(), received_beacons.end(),
86 [&sender](const Network::WifiPacket& packet) {
87 return packet.transmitter_address == sender;
88 });
89 if (beacon != received_beacons.end()) {
90 filtered_list.push_back(*beacon);
91 // TODO(B3N30): Check if the complete deque is cleared or just the fetched entries
92 received_beacons.erase(beacon);
93 }
94 return filtered_list;
95 }
96 return std::move(received_beacons);
97}
98
99/// Sends a WifiPacket to the room we're currently connected to.
100void SendPacket(Network::WifiPacket& packet) {
101 // TODO(Subv): Implement.
102}
103
104/*
105 * Returns an available index in the nodes array for the
106 * currently-hosted UDS network.
107 */
108static u16 GetNextAvailableNodeId() {
109 for (u16 index = 0; index < connection_status.max_nodes; ++index) {
110 if ((connection_status.node_bitmask & (1 << index)) == 0)
111 return index;
112 }
113
114 // Any connection attempts to an already full network should have been refused.
115 ASSERT_MSG(false, "No available connection slots in the network");
116}
117
118// Inserts the received beacon frame in the beacon queue and removes any older beacons if the size
119// limit is exceeded.
120void HandleBeaconFrame(const Network::WifiPacket& packet) {
121 std::lock_guard<std::mutex> lock(beacon_mutex);
122 const auto unique_beacon =
123 std::find_if(received_beacons.begin(), received_beacons.end(),
124 [&packet](const Network::WifiPacket& new_packet) {
125 return new_packet.transmitter_address == packet.transmitter_address;
126 });
127 if (unique_beacon != received_beacons.end()) {
128 // We already have a beacon from the same mac in the deque, remove the old one;
129 received_beacons.erase(unique_beacon);
130 }
131
132 received_beacons.emplace_back(packet);
133
134 // Discard old beacons if the buffer is full.
135 if (received_beacons.size() > MaxBeaconFrames)
136 received_beacons.pop_front();
137}
138
139void HandleAssociationResponseFrame(const Network::WifiPacket& packet) {
140 auto assoc_result = GetAssociationResult(packet.data);
141
142 ASSERT_MSG(std::get<AssocStatus>(assoc_result) == AssocStatus::Successful,
143 "Could not join network");
144 {
145 std::lock_guard<std::mutex> lock(connection_status_mutex);
146 ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::Connecting));
147 }
148
149 // Send the EAPoL-Start packet to the server.
150 using Network::WifiPacket;
151 WifiPacket eapol_start;
152 eapol_start.channel = network_channel;
153 eapol_start.data = GenerateEAPoLStartFrame(std::get<u16>(assoc_result), current_node);
154 // TODO(B3N30): Encrypt the packet.
155 eapol_start.destination_address = packet.transmitter_address;
156 eapol_start.type = WifiPacket::PacketType::Data;
157
158 SendPacket(eapol_start);
159}
160
161static void HandleEAPoLPacket(const Network::WifiPacket& packet) {
162 std::lock_guard<std::mutex> lock(connection_status_mutex);
163
164 if (GetEAPoLFrameType(packet.data) == EAPoLStartMagic) {
165 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
166 LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u",
167 connection_status.status);
168 return;
169 }
170
171 auto node = DeserializeNodeInfoFromFrame(packet.data);
172
173 if (connection_status.max_nodes == connection_status.total_nodes) {
174 // Reject connection attempt
175 LOG_ERROR(Service_NWM, "Reached maximum nodes, but reject packet wasn't sent.");
176 // TODO(B3N30): Figure out what packet is sent here
177 return;
178 }
179
180 // Get an unused network node id
181 u16 node_id = GetNextAvailableNodeId();
182 node.network_node_id = node_id + 1;
183
184 connection_status.node_bitmask |= 1 << node_id;
185 connection_status.changed_nodes |= 1 << node_id;
186 connection_status.nodes[node_id] = node.network_node_id;
187 connection_status.total_nodes++;
188
189 u8 current_nodes = network_info.total_nodes;
190 node_info[current_nodes] = node;
191
192 network_info.total_nodes++;
193
194 // Send the EAPoL-Logoff packet.
195 using Network::WifiPacket;
196 WifiPacket eapol_logoff;
197 eapol_logoff.channel = network_channel;
198 eapol_logoff.data =
199 GenerateEAPoLLogoffFrame(packet.transmitter_address, node.network_node_id, node_info,
200 network_info.max_nodes, network_info.total_nodes);
201 // TODO(Subv): Encrypt the packet.
202 eapol_logoff.destination_address = packet.transmitter_address;
203 eapol_logoff.type = WifiPacket::PacketType::Data;
204
205 SendPacket(eapol_logoff);
206 // TODO(B3N30): Broadcast updated node list
207 // The 3ds does this presumably to support spectators.
208 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
209 connection_status_event->Signal();
210 } else {
211 if (connection_status.status != static_cast<u32>(NetworkStatus::NotConnected)) {
212 LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u",
213 connection_status.status);
214 return;
215 }
216 auto logoff = ParseEAPoLLogoffFrame(packet.data);
217
218 network_info.total_nodes = logoff.connected_nodes;
219 network_info.max_nodes = logoff.max_nodes;
220
221 connection_status.network_node_id = logoff.assigned_node_id;
222 connection_status.total_nodes = logoff.connected_nodes;
223 connection_status.max_nodes = logoff.max_nodes;
224
225 node_info.clear();
226 node_info.reserve(network_info.max_nodes);
227 for (size_t index = 0; index < logoff.connected_nodes; ++index) {
228 connection_status.node_bitmask |= 1 << index;
229 connection_status.changed_nodes |= 1 << index;
230 connection_status.nodes[index] = logoff.nodes[index].network_node_id;
231
232 node_info.emplace_back(DeserializeNodeInfo(logoff.nodes[index]));
233 }
234
235 // We're now connected, signal the application
236 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsClient);
237 // Some games require ConnectToNetwork to block, for now it doesn't
238 // If blocking is implemented this lock needs to be changed,
239 // otherwise it might cause deadlocks
240 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
241 connection_status_event->Signal();
242 }
243}
244
245/*
246 * Start a connection sequence with an UDS server. The sequence starts by sending an 802.11
247 * authentication frame with SEQ1.
248 */
249void StartConnectionSequence(const MacAddress& server) {
250 using Network::WifiPacket;
251 WifiPacket auth_request;
252 {
253 std::lock_guard<std::mutex> lock(connection_status_mutex);
254 ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected));
255
256 // TODO(Subv): Handle timeout.
257
258 // Send an authentication frame with SEQ1
259 auth_request.channel = network_channel;
260 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1);
261 auth_request.destination_address = server;
262 auth_request.type = WifiPacket::PacketType::Authentication;
263 }
264
265 SendPacket(auth_request);
266}
267
268/// Sends an Association Response frame to the specified mac address
269void SendAssociationResponseFrame(const MacAddress& address) {
270 using Network::WifiPacket;
271 WifiPacket assoc_response;
272
273 {
274 std::lock_guard<std::mutex> lock(connection_status_mutex);
275 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
276 LOG_ERROR(Service_NWM, "Connection sequence aborted, because connection status is %u",
277 connection_status.status);
278 return;
279 }
280
281 assoc_response.channel = network_channel;
282 // TODO(Subv): This will cause multiple clients to end up with the same association id, but
283 // we're not using that for anything.
284 u16 association_id = 1;
285 assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id,
286 network_info.network_id);
287 assoc_response.destination_address = address;
288 assoc_response.type = WifiPacket::PacketType::AssociationResponse;
289 }
290
291 SendPacket(assoc_response);
292}
293
294/*
295 * Handles the authentication request frame and sends the authentication response and association
296 * response frames. Once an Authentication frame with SEQ1 is received by the server, it responds
297 * with an Authentication frame containing SEQ2, and immediately sends an Association response frame
298 * containing the details of the access point and the assigned association id for the new client.
299 */
300void HandleAuthenticationFrame(const Network::WifiPacket& packet) {
301 // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior
302 if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) {
303 using Network::WifiPacket;
304 WifiPacket auth_request;
305 {
306 std::lock_guard<std::mutex> lock(connection_status_mutex);
307 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
308 LOG_ERROR(Service_NWM,
309 "Connection sequence aborted, because connection status is %u",
310 connection_status.status);
311 return;
312 }
313
314 // Respond with an authentication response frame with SEQ2
315 auth_request.channel = network_channel;
316 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2);
317 auth_request.destination_address = packet.transmitter_address;
318 auth_request.type = WifiPacket::PacketType::Authentication;
319 }
320 SendPacket(auth_request);
321
322 SendAssociationResponseFrame(packet.transmitter_address);
323 }
324}
325
326static void HandleDataFrame(const Network::WifiPacket& packet) {
327 switch (GetFrameEtherType(packet.data)) {
328 case EtherType::EAPoL:
329 HandleEAPoLPacket(packet);
330 break;
331 case EtherType::SecureData:
332 // TODO(B3N30): Handle SecureData packets
333 break;
334 }
335}
336
337/// Callback to parse and handle a received wifi packet.
338void OnWifiPacketReceived(const Network::WifiPacket& packet) {
339 switch (packet.type) {
340 case Network::WifiPacket::PacketType::Beacon:
341 HandleBeaconFrame(packet);
342 break;
343 case Network::WifiPacket::PacketType::Authentication:
344 HandleAuthenticationFrame(packet);
345 break;
346 case Network::WifiPacket::PacketType::AssociationResponse:
347 HandleAssociationResponseFrame(packet);
348 break;
349 case Network::WifiPacket::PacketType::Data:
350 HandleDataFrame(packet);
351 break;
352 }
353}
354
54/** 355/**
55 * NWM_UDS::Shutdown service function 356 * NWM_UDS::Shutdown service function
56 * Inputs: 357 * Inputs:
@@ -111,11 +412,10 @@ static void RecvBeaconBroadcastData(Interface* self) {
111 u32 total_size = sizeof(BeaconDataReplyHeader); 412 u32 total_size = sizeof(BeaconDataReplyHeader);
112 413
113 // Retrieve all beacon frames that were received from the desired mac address. 414 // Retrieve all beacon frames that were received from the desired mac address.
114 std::deque<WifiPacket> beacons = 415 auto beacons = GetReceivedBeacons(mac_address);
115 GetReceivedPackets(WifiPacket::PacketType::Beacon, mac_address);
116 416
117 BeaconDataReplyHeader data_reply_header{}; 417 BeaconDataReplyHeader data_reply_header{};
118 data_reply_header.total_entries = beacons.size(); 418 data_reply_header.total_entries = static_cast<u32>(beacons.size());
119 data_reply_header.max_output_size = out_buffer_size; 419 data_reply_header.max_output_size = out_buffer_size;
120 420
121 Memory::WriteBlock(current_buffer_pos, &data_reply_header, sizeof(BeaconDataReplyHeader)); 421 Memory::WriteBlock(current_buffer_pos, &data_reply_header, sizeof(BeaconDataReplyHeader));
@@ -125,8 +425,8 @@ static void RecvBeaconBroadcastData(Interface* self) {
125 for (const auto& beacon : beacons) { 425 for (const auto& beacon : beacons) {
126 BeaconEntryHeader entry{}; 426 BeaconEntryHeader entry{};
127 // TODO(Subv): Figure out what this size is used for. 427 // TODO(Subv): Figure out what this size is used for.
128 entry.unk_size = sizeof(BeaconEntryHeader) + beacon.data.size(); 428 entry.unk_size = static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size());
129 entry.total_size = sizeof(BeaconEntryHeader) + beacon.data.size(); 429 entry.total_size = static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size());
130 entry.wifi_channel = beacon.channel; 430 entry.wifi_channel = beacon.channel;
131 entry.header_size = sizeof(BeaconEntryHeader); 431 entry.header_size = sizeof(BeaconEntryHeader);
132 entry.mac_address = beacon.transmitter_address; 432 entry.mac_address = beacon.transmitter_address;
@@ -137,9 +437,9 @@ static void RecvBeaconBroadcastData(Interface* self) {
137 current_buffer_pos += sizeof(BeaconEntryHeader); 437 current_buffer_pos += sizeof(BeaconEntryHeader);
138 438
139 Memory::WriteBlock(current_buffer_pos, beacon.data.data(), beacon.data.size()); 439 Memory::WriteBlock(current_buffer_pos, beacon.data.data(), beacon.data.size());
140 current_buffer_pos += beacon.data.size(); 440 current_buffer_pos += static_cast<VAddr>(beacon.data.size());
141 441
142 total_size += sizeof(BeaconEntryHeader) + beacon.data.size(); 442 total_size += static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size());
143 } 443 }
144 444
145 // Update the total size in the structure and write it to the buffer again. 445 // Update the total size in the structure and write it to the buffer again.
@@ -174,7 +474,7 @@ static void InitializeWithVersion(Interface* self) {
174 u32 sharedmem_size = rp.Pop<u32>(); 474 u32 sharedmem_size = rp.Pop<u32>();
175 475
176 // Update the node information with the data the game gave us. 476 // Update the node information with the data the game gave us.
177 rp.PopRaw(node_info[0]); 477 rp.PopRaw(current_node);
178 478
179 u16 version = rp.Pop<u16>(); 479 u16 version = rp.Pop<u16>();
180 480
@@ -184,15 +484,22 @@ static void InitializeWithVersion(Interface* self) {
184 484
185 ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size."); 485 ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size.");
186 486
187 // Reset the connection status, it contains all zeros after initialization, 487 {
188 // except for the actual status value. 488 std::lock_guard<std::mutex> lock(connection_status_mutex);
189 connection_status = {}; 489
190 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); 490 // Reset the connection status, it contains all zeros after initialization,
491 // except for the actual status value.
492 connection_status = {};
493 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
494 }
191 495
192 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); 496 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
193 rb.Push(RESULT_SUCCESS); 497 rb.Push(RESULT_SUCCESS);
194 rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap()); 498 rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap());
195 499
500 // TODO(Subv): Connect the OnWifiPacketReceived function to the wifi packet received callback of
501 // the room we're currently in.
502
196 LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X", 503 LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X",
197 sharedmem_size, version, sharedmem_handle); 504 sharedmem_size, version, sharedmem_handle);
198} 505}
@@ -214,12 +521,16 @@ static void GetConnectionStatus(Interface* self) {
214 IPC::RequestBuilder rb = rp.MakeBuilder(13, 0); 521 IPC::RequestBuilder rb = rp.MakeBuilder(13, 0);
215 522
216 rb.Push(RESULT_SUCCESS); 523 rb.Push(RESULT_SUCCESS);
217 rb.PushRaw(connection_status); 524 {
218 525 std::lock_guard<std::mutex> lock(connection_status_mutex);
219 // Reset the bitmask of changed nodes after each call to this 526 rb.PushRaw(connection_status);
220 // function to prevent falsely informing games of outstanding 527
221 // changes in subsequent calls. 528 // Reset the bitmask of changed nodes after each call to this
222 connection_status.changed_nodes = 0; 529 // function to prevent falsely informing games of outstanding
530 // changes in subsequent calls.
531 // TODO(Subv): Find exactly where the NWM module resets this value.
532 connection_status.changed_nodes = 0;
533 }
223 534
224 LOG_DEBUG(Service_NWM, "called"); 535 LOG_DEBUG(Service_NWM, "called");
225} 536}
@@ -300,31 +611,36 @@ static void BeginHostingNetwork(Interface* self) {
300 // The real UDS module throws a fatal error if this assert fails. 611 // The real UDS module throws a fatal error if this assert fails.
301 ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); 612 ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member.");
302 613
303 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost); 614 {
304 615 std::lock_guard<std::mutex> lock(connection_status_mutex);
305 // Ensure the application data size is less than the maximum value. 616 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost);
306 ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, "Data size is too big."); 617
307 618 // Ensure the application data size is less than the maximum value.
308 // Set up basic information for this network. 619 ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize,
309 network_info.oui_value = NintendoOUI; 620 "Data size is too big.");
310 network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo); 621
311 622 // Set up basic information for this network.
312 connection_status.max_nodes = network_info.max_nodes; 623 network_info.oui_value = NintendoOUI;
313 624 network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo);
314 // Resize the nodes list to hold max_nodes. 625
315 node_info.resize(network_info.max_nodes); 626 connection_status.max_nodes = network_info.max_nodes;
316 627
317 // There's currently only one node in the network (the host). 628 // Resize the nodes list to hold max_nodes.
318 connection_status.total_nodes = 1; 629 node_info.resize(network_info.max_nodes);
319 network_info.total_nodes = 1; 630
320 // The host is always the first node 631 // There's currently only one node in the network (the host).
321 connection_status.network_node_id = 1; 632 connection_status.total_nodes = 1;
322 node_info[0].network_node_id = 1; 633 network_info.total_nodes = 1;
323 connection_status.nodes[0] = connection_status.network_node_id; 634 // The host is always the first node
324 // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken. 635 connection_status.network_node_id = 1;
325 connection_status.node_bitmask |= 1; 636 current_node.network_node_id = 1;
326 // Notify the application that the first node was set. 637 connection_status.nodes[0] = connection_status.network_node_id;
327 connection_status.changed_nodes |= 1; 638 // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken.
639 connection_status.node_bitmask |= 1;
640 // Notify the application that the first node was set.
641 connection_status.changed_nodes |= 1;
642 node_info[0] = current_node;
643 }
328 644
329 // If the game has a preferred channel, use that instead. 645 // If the game has a preferred channel, use that instead.
330 if (network_info.channel != 0) 646 if (network_info.channel != 0)
@@ -361,9 +677,13 @@ static void DestroyNetwork(Interface* self) {
361 // Unschedule the beacon broadcast event. 677 // Unschedule the beacon broadcast event.
362 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); 678 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0);
363 679
364 // TODO(Subv): Check if connection_status is indeed reset after this call. 680 {
365 connection_status = {}; 681 std::lock_guard<std::mutex> lock(connection_status_mutex);
366 connection_status.status = static_cast<u8>(NetworkStatus::NotConnected); 682
683 // TODO(Subv): Check if connection_status is indeed reset after this call.
684 connection_status = {};
685 connection_status.status = static_cast<u8>(NetworkStatus::NotConnected);
686 }
367 connection_status_event->Signal(); 687 connection_status_event->Signal();
368 688
369 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 689 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
@@ -406,17 +726,24 @@ static void SendTo(Interface* self) {
406 726
407 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 727 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
408 728
409 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) && 729 u16 network_node_id;
410 connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
411 rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
412 ErrorSummary::InvalidState, ErrorLevel::Status));
413 return;
414 }
415 730
416 if (dest_node_id == connection_status.network_node_id) { 731 {
417 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, 732 std::lock_guard<std::mutex> lock(connection_status_mutex);
418 ErrorSummary::WrongArgument, ErrorLevel::Status)); 733 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) &&
419 return; 734 connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
735 rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
736 ErrorSummary::InvalidState, ErrorLevel::Status));
737 return;
738 }
739
740 if (dest_node_id == connection_status.network_node_id) {
741 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS,
742 ErrorSummary::WrongArgument, ErrorLevel::Status));
743 return;
744 }
745
746 network_node_id = connection_status.network_node_id;
420 } 747 }
421 748
422 // TODO(Subv): Do something with the flags. 749 // TODO(Subv): Do something with the flags.
@@ -433,8 +760,8 @@ static void SendTo(Interface* self) {
433 760
434 // TODO(Subv): Increment the sequence number after each sent packet. 761 // TODO(Subv): Increment the sequence number after each sent packet.
435 u16 sequence_number = 0; 762 u16 sequence_number = 0;
436 std::vector<u8> data_payload = GenerateDataPayload( 763 std::vector<u8> data_payload =
437 data, data_channel, dest_node_id, connection_status.network_node_id, sequence_number); 764 GenerateDataPayload(data, data_channel, dest_node_id, network_node_id, sequence_number);
438 765
439 // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt 766 // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt
440 // and encapsulate the payload. 767 // and encapsulate the payload.
@@ -461,6 +788,7 @@ static void GetChannel(Interface* self) {
461 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); 788 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0);
462 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); 789 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
463 790
791 std::lock_guard<std::mutex> lock(connection_status_mutex);
464 bool is_connected = connection_status.status != static_cast<u32>(NetworkStatus::NotConnected); 792 bool is_connected = connection_status.status != static_cast<u32>(NetworkStatus::NotConnected);
465 793
466 u8 channel = is_connected ? network_channel : 0; 794 u8 channel = is_connected ? network_channel : 0;
@@ -610,37 +938,29 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) {
610 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) 938 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost))
611 return; 939 return;
612 940
613 // TODO(Subv): Actually send the beacon.
614 std::vector<u8> frame = GenerateBeaconFrame(network_info, node_info); 941 std::vector<u8> frame = GenerateBeaconFrame(network_info, node_info);
615 942
943 using Network::WifiPacket;
944 WifiPacket packet;
945 packet.type = WifiPacket::PacketType::Beacon;
946 packet.data = std::move(frame);
947 packet.destination_address = Network::BroadcastMac;
948 packet.channel = network_channel;
949
950 SendPacket(packet);
951
616 // Start broadcasting the network, send a beacon frame every 102.4ms. 952 // Start broadcasting the network, send a beacon frame every 102.4ms.
617 CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late, 953 CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late,
618 beacon_broadcast_event, 0); 954 beacon_broadcast_event, 0);
619} 955}
620 956
621/* 957/*
622 * Returns an available index in the nodes array for the
623 * currently-hosted UDS network.
624 */
625static u32 GetNextAvailableNodeId() {
626 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
627 "Can not accept clients if we're not hosting a network");
628
629 for (unsigned index = 0; index < connection_status.max_nodes; ++index) {
630 if ((connection_status.node_bitmask & (1 << index)) == 0)
631 return index;
632 }
633
634 // Any connection attempts to an already full network should have been refused.
635 ASSERT_MSG(false, "No available connection slots in the network");
636}
637
638/*
639 * Called when a client connects to an UDS network we're hosting, 958 * Called when a client connects to an UDS network we're hosting,
640 * updates the connection status and signals the update event. 959 * updates the connection status and signals the update event.
641 * @param network_node_id Network Node Id of the connecting client. 960 * @param network_node_id Network Node Id of the connecting client.
642 */ 961 */
643void OnClientConnected(u16 network_node_id) { 962void OnClientConnected(u16 network_node_id) {
963 std::lock_guard<std::mutex> lock(connection_status_mutex);
644 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), 964 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
645 "Can not accept clients if we're not hosting a network"); 965 "Can not accept clients if we're not hosting a network");
646 ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes, 966 ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes,
@@ -655,7 +975,7 @@ void OnClientConnected(u16 network_node_id) {
655} 975}
656 976
657const Interface::FunctionInfo FunctionTable[] = { 977const Interface::FunctionInfo FunctionTable[] = {
658 {0x00010442, nullptr, "Initialize (deprecated)"}, 978 {0x000102C2, nullptr, "Initialize (deprecated)"},
659 {0x00020000, nullptr, "Scrap"}, 979 {0x00020000, nullptr, "Scrap"},
660 {0x00030000, Shutdown, "Shutdown"}, 980 {0x00030000, Shutdown, "Shutdown"},
661 {0x00040402, nullptr, "CreateNetwork (deprecated)"}, 981 {0x00040402, nullptr, "CreateNetwork (deprecated)"},
@@ -702,8 +1022,11 @@ NWM_UDS::~NWM_UDS() {
702 connection_status_event = nullptr; 1022 connection_status_event = nullptr;
703 recv_buffer_memory = nullptr; 1023 recv_buffer_memory = nullptr;
704 1024
705 connection_status = {}; 1025 {
706 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); 1026 std::lock_guard<std::mutex> lock(connection_status_mutex);
1027 connection_status = {};
1028 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
1029 }
707 1030
708 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); 1031 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0);
709} 1032}
diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h
index 141f49f9c..f1caaf974 100644
--- a/src/core/hle/service/nwm/nwm_uds.h
+++ b/src/core/hle/service/nwm/nwm_uds.h
@@ -42,6 +42,7 @@ using NodeList = std::vector<NodeInfo>;
42enum class NetworkStatus { 42enum class NetworkStatus {
43 NotConnected = 3, 43 NotConnected = 3,
44 ConnectedAsHost = 6, 44 ConnectedAsHost = 6,
45 Connecting = 7,
45 ConnectedAsClient = 9, 46 ConnectedAsClient = 9,
46 ConnectedAsSpectator = 10, 47 ConnectedAsSpectator = 10,
47}; 48};
@@ -85,6 +86,17 @@ static_assert(offsetof(NetworkInfo, oui_value) == 0xC, "oui_value is at the wron
85static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset."); 86static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset.");
86static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size."); 87static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size.");
87 88
89/// Additional block tag ids in the Beacon and Association Response frames
90enum class TagId : u8 {
91 SSID = 0,
92 SupportedRates = 1,
93 DSParameterSet = 2,
94 TrafficIndicationMap = 5,
95 CountryInformation = 7,
96 ERPInformation = 42,
97 VendorSpecific = 221
98};
99
88class NWM_UDS final : public Interface { 100class NWM_UDS final : public Interface {
89public: 101public:
90 NWM_UDS(); 102 NWM_UDS();
diff --git a/src/core/hle/service/nwm/uds_beacon.cpp b/src/core/hle/service/nwm/uds_beacon.cpp
index 6332b404c..73a80d940 100644
--- a/src/core/hle/service/nwm/uds_beacon.cpp
+++ b/src/core/hle/service/nwm/uds_beacon.cpp
@@ -243,7 +243,7 @@ std::vector<u8> GenerateNintendoFirstEncryptedDataTag(const NetworkInfo& network
243 243
244 EncryptedDataTag tag{}; 244 EncryptedDataTag tag{};
245 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific); 245 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific);
246 tag.header.length = sizeof(tag) - sizeof(TagHeader) + payload_size; 246 tag.header.length = static_cast<u8>(sizeof(tag) - sizeof(TagHeader) + payload_size);
247 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData0); 247 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData0);
248 tag.oui = NintendoOUI; 248 tag.oui = NintendoOUI;
249 249
@@ -279,7 +279,7 @@ std::vector<u8> GenerateNintendoSecondEncryptedDataTag(const NetworkInfo& networ
279 279
280 EncryptedDataTag tag{}; 280 EncryptedDataTag tag{};
281 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific); 281 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific);
282 tag.header.length = tag_length; 282 tag.header.length = static_cast<u8>(tag_length);
283 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData1); 283 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData1);
284 tag.oui = NintendoOUI; 284 tag.oui = NintendoOUI;
285 285
@@ -325,8 +325,5 @@ std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeL
325 return buffer; 325 return buffer;
326} 326}
327 327
328std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender) {
329 return {};
330}
331} // namespace NWM 328} // namespace NWM
332} // namespace Service 329} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_beacon.h b/src/core/hle/service/nwm/uds_beacon.h
index caacf4c6f..50cc76da2 100644
--- a/src/core/hle/service/nwm/uds_beacon.h
+++ b/src/core/hle/service/nwm/uds_beacon.h
@@ -17,17 +17,6 @@ namespace NWM {
17using MacAddress = std::array<u8, 6>; 17using MacAddress = std::array<u8, 6>;
18constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32}; 18constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32};
19 19
20/// Additional block tag ids in the Beacon frames
21enum class TagId : u8 {
22 SSID = 0,
23 SupportedRates = 1,
24 DSParameterSet = 2,
25 TrafficIndicationMap = 5,
26 CountryInformation = 7,
27 ERPInformation = 42,
28 VendorSpecific = 221
29};
30
31/** 20/**
32 * Internal vendor-specific tag ids as stored inside 21 * Internal vendor-specific tag ids as stored inside
33 * VendorSpecific blocks in the Beacon frames. 22 * VendorSpecific blocks in the Beacon frames.
@@ -135,20 +124,6 @@ struct BeaconData {
135 124
136static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size."); 125static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size.");
137 126
138/// Information about a received WiFi packet.
139/// Acts as our own 802.11 header.
140struct WifiPacket {
141 enum class PacketType { Beacon, Data };
142
143 PacketType type; ///< The type of 802.11 frame, Beacon / Data.
144
145 /// Raw 802.11 frame data, starting at the management frame header for management frames.
146 std::vector<u8> data;
147 MacAddress transmitter_address; ///< Mac address of the transmitter.
148 MacAddress destination_address; ///< Mac address of the receiver.
149 u8 channel; ///< WiFi channel where this frame was transmitted.
150};
151
152/** 127/**
153 * Decrypts the beacon data buffer for the network described by `network_info`. 128 * Decrypts the beacon data buffer for the network described by `network_info`.
154 */ 129 */
@@ -161,10 +136,5 @@ void DecryptBeaconData(const NetworkInfo& network_info, std::vector<u8>& buffer)
161 */ 136 */
162std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes); 137std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes);
163 138
164/**
165 * Returns a list of received 802.11 frames from the specified sender
166 * matching the type since the last call.
167 */
168std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender);
169} // namespace NWM 139} // namespace NWM
170} // namespace Service 140} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_connection.cpp b/src/core/hle/service/nwm/uds_connection.cpp
new file mode 100644
index 000000000..c74f51253
--- /dev/null
+++ b/src/core/hle/service/nwm/uds_connection.cpp
@@ -0,0 +1,88 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/nwm/nwm_uds.h"
6#include "core/hle/service/nwm/uds_connection.h"
7#include "fmt/format.h"
8
9namespace Service {
10namespace NWM {
11
12// Note: These values were taken from a packet capture of an o3DS XL
13// broadcasting a Super Smash Bros. 4 lobby.
14constexpr u16 DefaultExtraCapabilities = 0x0431;
15
16std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq) {
17 AuthenticationFrame frame{};
18 frame.auth_seq = static_cast<u16>(seq);
19
20 std::vector<u8> data(sizeof(frame));
21 std::memcpy(data.data(), &frame, sizeof(frame));
22
23 return data;
24}
25
26AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body) {
27 AuthenticationFrame frame;
28 std::memcpy(&frame, body.data(), sizeof(frame));
29
30 return static_cast<AuthenticationSeq>(frame.auth_seq);
31}
32
33/**
34 * Generates an SSID tag of an 802.11 Beacon frame with an 8-byte character representation of the
35 * specified network id as the SSID value.
36 * @param network_id The network id to use.
37 * @returns A buffer with the SSID tag.
38 */
39static std::vector<u8> GenerateSSIDTag(u32 network_id) {
40 constexpr u8 SSIDSize = 8;
41
42 struct {
43 u8 id = static_cast<u8>(TagId::SSID);
44 u8 size = SSIDSize;
45 } tag_header;
46
47 std::vector<u8> buffer(sizeof(tag_header) + SSIDSize);
48
49 std::memcpy(buffer.data(), &tag_header, sizeof(tag_header));
50
51 std::string network_name = fmt::format("{0:08X}", network_id);
52
53 std::memcpy(buffer.data() + sizeof(tag_header), network_name.c_str(), SSIDSize);
54
55 return buffer;
56}
57
58std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id) {
59 AssociationResponseFrame frame{};
60 frame.capabilities = DefaultExtraCapabilities;
61 frame.status_code = static_cast<u16>(status);
62 // The association id is ORed with this magic value (0xC000)
63 constexpr u16 AssociationIdMagic = 0xC000;
64 frame.assoc_id = association_id | AssociationIdMagic;
65
66 std::vector<u8> data(sizeof(frame));
67 std::memcpy(data.data(), &frame, sizeof(frame));
68
69 auto ssid_tag = GenerateSSIDTag(network_id);
70 data.insert(data.end(), ssid_tag.begin(), ssid_tag.end());
71
72 // TODO(Subv): Add the SupportedRates tag.
73 // TODO(Subv): Add the DSParameterSet tag.
74 // TODO(Subv): Add the ERPInformation tag.
75 return data;
76}
77
78std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body) {
79 AssociationResponseFrame frame;
80 memcpy(&frame, body.data(), sizeof(frame));
81
82 constexpr u16 AssociationIdMask = 0x3FFF;
83 return std::make_tuple(static_cast<AssocStatus>(frame.status_code),
84 frame.assoc_id & AssociationIdMask);
85}
86
87} // namespace NWM
88} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_connection.h b/src/core/hle/service/nwm/uds_connection.h
new file mode 100644
index 000000000..a664f8471
--- /dev/null
+++ b/src/core/hle/service/nwm/uds_connection.h
@@ -0,0 +1,56 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <tuple>
8#include <vector>
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/service.h"
12
13namespace Service {
14namespace NWM {
15
16/// Sequence number of the 802.11 authentication frames.
17enum class AuthenticationSeq : u16 { SEQ1 = 1, SEQ2 = 2 };
18
19enum class AuthAlgorithm : u16 { OpenSystem = 0 };
20
21enum class AuthStatus : u16 { Successful = 0 };
22
23enum class AssocStatus : u16 { Successful = 0 };
24
25struct AuthenticationFrame {
26 u16_le auth_algorithm = static_cast<u16>(AuthAlgorithm::OpenSystem);
27 u16_le auth_seq;
28 u16_le status_code = static_cast<u16>(AuthStatus::Successful);
29};
30
31static_assert(sizeof(AuthenticationFrame) == 6, "AuthenticationFrame has wrong size");
32
33struct AssociationResponseFrame {
34 u16_le capabilities;
35 u16_le status_code;
36 u16_le assoc_id;
37};
38
39static_assert(sizeof(AssociationResponseFrame) == 6, "AssociationResponseFrame has wrong size");
40
41/// Generates an 802.11 authentication frame, starting at the frame body.
42std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq);
43
44/// Returns the sequence number from the body of an Authentication frame.
45AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body);
46
47/// Generates an 802.11 association response frame with the specified status, association id and
48/// network id, starting at the frame body.
49std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id);
50
51/// Returns a tuple of (association status, association id) from the body of an AssociationResponse
52/// frame.
53std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body);
54
55} // namespace NWM
56} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp
index 8c6742dba..4b389710f 100644
--- a/src/core/hle/service/nwm/uds_data.cpp
+++ b/src/core/hle/service/nwm/uds_data.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include <cstring> 6#include <cstring>
6#include <cryptopp/aes.h> 7#include <cryptopp/aes.h>
7#include <cryptopp/ccm.h> 8#include <cryptopp/ccm.h>
@@ -197,7 +198,7 @@ static std::vector<u8> DecryptDataFrame(const std::vector<u8>& encrypted_payload
197 df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL); 198 df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL);
198 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); 199 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL);
199 200
200 int size = df.MaxRetrievable(); 201 size_t size = df.MaxRetrievable();
201 202
202 std::vector<u8> pdata(size); 203 std::vector<u8> pdata(size);
203 df.Get(pdata.data(), size); 204 df.Get(pdata.data(), size);
@@ -251,7 +252,7 @@ static std::vector<u8> EncryptDataFrame(const std::vector<u8>& payload,
251 252
252 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); 253 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL);
253 254
254 int size = df.MaxRetrievable(); 255 size_t size = df.MaxRetrievable();
255 256
256 std::vector<u8> cipher(size); 257 std::vector<u8> cipher(size);
257 df.Get(cipher.data(), size); 258 df.Get(cipher.data(), size);
@@ -266,13 +267,107 @@ static std::vector<u8> EncryptDataFrame(const std::vector<u8>& payload,
266std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, 267std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node,
267 u16 src_node, u16 sequence_number) { 268 u16 src_node, u16 sequence_number) {
268 std::vector<u8> buffer = GenerateLLCHeader(EtherType::SecureData); 269 std::vector<u8> buffer = GenerateLLCHeader(EtherType::SecureData);
269 std::vector<u8> securedata_header = 270 std::vector<u8> securedata_header = GenerateSecureDataHeader(
270 GenerateSecureDataHeader(data.size(), channel, dest_node, src_node, sequence_number); 271 static_cast<u16>(data.size()), channel, dest_node, src_node, sequence_number);
271 272
272 buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end()); 273 buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end());
273 buffer.insert(buffer.end(), data.begin(), data.end()); 274 buffer.insert(buffer.end(), data.begin(), data.end());
274 return buffer; 275 return buffer;
275} 276}
276 277
278std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) {
279 EAPoLStartPacket eapol_start{};
280 eapol_start.association_id = association_id;
281 eapol_start.node.friend_code_seed = node_info.friend_code_seed;
282
283 std::copy(node_info.username.begin(), node_info.username.end(),
284 eapol_start.node.username.begin());
285
286 // Note: The network_node_id and unknown bytes seem to be uninitialized in the NWM module.
287 // TODO(B3N30): The last 8 bytes seem to have a fixed value of 07 88 15 00 04 e9 13 00 in
288 // EAPoL-Start packets from different 3DSs to the same host during a Super Smash Bros. 4 game.
289 // Find out what that means.
290
291 std::vector<u8> eapol_buffer(sizeof(EAPoLStartPacket));
292 std::memcpy(eapol_buffer.data(), &eapol_start, sizeof(eapol_start));
293
294 std::vector<u8> buffer = GenerateLLCHeader(EtherType::EAPoL);
295 buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end());
296 return buffer;
297}
298
299EtherType GetFrameEtherType(const std::vector<u8>& frame) {
300 LLCHeader header;
301 std::memcpy(&header, frame.data(), sizeof(header));
302
303 u16 ethertype = header.protocol;
304 return static_cast<EtherType>(ethertype);
305}
306
307u16 GetEAPoLFrameType(const std::vector<u8>& frame) {
308 // Ignore the LLC header
309 u16_be eapol_type;
310 std::memcpy(&eapol_type, frame.data() + sizeof(LLCHeader), sizeof(eapol_type));
311 return eapol_type;
312}
313
314NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame) {
315 EAPoLStartPacket eapol_start;
316
317 // Skip the LLC header
318 std::memcpy(&eapol_start, frame.data() + sizeof(LLCHeader), sizeof(eapol_start));
319
320 NodeInfo node{};
321 node.friend_code_seed = eapol_start.node.friend_code_seed;
322
323 std::copy(eapol_start.node.username.begin(), eapol_start.node.username.end(),
324 node.username.begin());
325
326 return node;
327}
328
329NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node) {
330 NodeInfo node_info{};
331 node_info.friend_code_seed = node.friend_code_seed;
332 node_info.network_node_id = node.network_node_id;
333
334 std::copy(node.username.begin(), node.username.end(), node_info.username.begin());
335
336 return node_info;
337}
338
339std::vector<u8> GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id,
340 const NodeList& nodes, u8 max_nodes, u8 total_nodes) {
341 EAPoLLogoffPacket eapol_logoff{};
342 eapol_logoff.assigned_node_id = network_node_id;
343 eapol_logoff.connected_nodes = total_nodes;
344 eapol_logoff.max_nodes = max_nodes;
345
346 for (size_t index = 0; index < total_nodes; ++index) {
347 const auto& node_info = nodes[index];
348 auto& node = eapol_logoff.nodes[index];
349
350 node.friend_code_seed = node_info.friend_code_seed;
351 node.network_node_id = node_info.network_node_id;
352
353 std::copy(node_info.username.begin(), node_info.username.end(), node.username.begin());
354 }
355
356 std::vector<u8> eapol_buffer(sizeof(EAPoLLogoffPacket));
357 std::memcpy(eapol_buffer.data(), &eapol_logoff, sizeof(eapol_logoff));
358
359 std::vector<u8> buffer = GenerateLLCHeader(EtherType::EAPoL);
360 buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end());
361 return buffer;
362}
363
364EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector<u8>& frame) {
365 EAPoLLogoffPacket eapol_logoff;
366
367 // Skip the LLC header
368 std::memcpy(&eapol_logoff, frame.data() + sizeof(LLCHeader), sizeof(eapol_logoff));
369 return eapol_logoff;
370}
371
277} // namespace NWM 372} // namespace NWM
278} // namespace Service 373} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h
index a23520a41..76bccb1bf 100644
--- a/src/core/hle/service/nwm/uds_data.h
+++ b/src/core/hle/service/nwm/uds_data.h
@@ -8,6 +8,7 @@
8#include <vector> 8#include <vector>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/hle/service/nwm/uds_beacon.h"
11#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
12 13
13namespace Service { 14namespace Service {
@@ -67,6 +68,49 @@ struct DataFrameCryptoCTR {
67 68
68static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); 69static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size");
69 70
71struct EAPoLNodeInfo {
72 u64_be friend_code_seed;
73 std::array<u16_be, 10> username;
74 INSERT_PADDING_BYTES(4);
75 u16_be network_node_id;
76 INSERT_PADDING_BYTES(6);
77};
78
79static_assert(sizeof(EAPoLNodeInfo) == 0x28, "EAPoLNodeInfo has the wrong size");
80
81constexpr u16 EAPoLStartMagic = 0x201;
82
83/*
84 * Nintendo EAPoLStartPacket, is used to initaliaze a connection between client and host
85 */
86struct EAPoLStartPacket {
87 u16_be magic = EAPoLStartMagic;
88 u16_be association_id;
89 // This value is hardcoded to 1 in the NWM module.
90 u16_be unknown = 1;
91 INSERT_PADDING_BYTES(2);
92 EAPoLNodeInfo node;
93};
94
95static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size");
96
97constexpr u16 EAPoLLogoffMagic = 0x202;
98
99struct EAPoLLogoffPacket {
100 u16_be magic = EAPoLLogoffMagic;
101 INSERT_PADDING_BYTES(2);
102 u16_be assigned_node_id;
103 MacAddress client_mac_address;
104 INSERT_PADDING_BYTES(6);
105 u8 connected_nodes;
106 u8 max_nodes;
107 INSERT_PADDING_BYTES(4);
108
109 std::array<EAPoLNodeInfo, UDSMaxNodes> nodes;
110};
111
112static_assert(sizeof(EAPoLLogoffPacket) == 0x298, "EAPoLLogoffPacket has the wrong size");
113
70/** 114/**
71 * Generates an unencrypted 802.11 data payload. 115 * Generates an unencrypted 802.11 data payload.
72 * @returns The generated frame payload. 116 * @returns The generated frame payload.
@@ -74,5 +118,47 @@ static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wron
74std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, 118std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node,
75 u16 src_node, u16 sequence_number); 119 u16 src_node, u16 sequence_number);
76 120
121/*
122 * Generates an unencrypted 802.11 data frame body with the EAPoL-Start format for UDS
123 * communication.
124 * @returns The generated frame body.
125 */
126std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info);
127
128/*
129 * Returns the EtherType of the specified 802.11 frame.
130 */
131EtherType GetFrameEtherType(const std::vector<u8>& frame);
132
133/*
134 * Returns the EAPoL type (Start / Logoff) of the specified 802.11 frame.
135 * Note: The frame *must* be an EAPoL frame.
136 */
137u16 GetEAPoLFrameType(const std::vector<u8>& frame);
138
139/*
140 * Returns a deserialized NodeInfo structure from the information inside an EAPoL-Start packet
141 * encapsulated in an 802.11 data frame.
142 */
143NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame);
144
145/*
146 * Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo.
147 */
148NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node);
149
150/*
151 * Generates an unencrypted 802.11 data frame body with the EAPoL-Logoff format for UDS
152 * communication.
153 * @returns The generated frame body.
154 */
155std::vector<u8> GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id,
156 const NodeList& nodes, u8 max_nodes, u8 total_nodes);
157
158/*
159 * Returns a EAPoLLogoffPacket representing the specified 802.11-encapsulated data frame.
160 */
161EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector<u8>& frame);
162
77} // namespace NWM 163} // namespace NWM
78} // namespace Service 164} // namespace Service
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index aad950e50..f267aad74 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -38,7 +38,7 @@
38#include "core/hle/service/news/news.h" 38#include "core/hle/service/news/news.h"
39#include "core/hle/service/nfc/nfc.h" 39#include "core/hle/service/nfc/nfc.h"
40#include "core/hle/service/nim/nim.h" 40#include "core/hle/service/nim/nim.h"
41#include "core/hle/service/ns_s.h" 41#include "core/hle/service/ns/ns.h"
42#include "core/hle/service/nwm/nwm.h" 42#include "core/hle/service/nwm/nwm.h"
43#include "core/hle/service/pm_app.h" 43#include "core/hle/service/pm_app.h"
44#include "core/hle/service/ptm/ptm.h" 44#include "core/hle/service/ptm/ptm.h"
@@ -215,6 +215,8 @@ void Init() {
215 SM::g_service_manager = std::make_shared<SM::ServiceManager>(); 215 SM::g_service_manager = std::make_shared<SM::ServiceManager>();
216 SM::ServiceManager::InstallInterfaces(SM::g_service_manager); 216 SM::ServiceManager::InstallInterfaces(SM::g_service_manager);
217 217
218 NS::InstallInterfaces(*SM::g_service_manager);
219
218 AddNamedPort(new ERR::ERR_F); 220 AddNamedPort(new ERR::ERR_F);
219 221
220 FS::ArchiveInit(); 222 FS::ArchiveInit();
@@ -246,7 +248,6 @@ void Init() {
246 AddService(new HTTP::HTTP_C); 248 AddService(new HTTP::HTTP_C);
247 AddService(new LDR::LDR_RO); 249 AddService(new LDR::LDR_RO);
248 AddService(new MIC::MIC_U); 250 AddService(new MIC::MIC_U);
249 AddService(new NS::NS_S);
250 AddService(new PM::PM_APP); 251 AddService(new PM::PM_APP);
251 AddService(new SOC::SOC_U); 252 AddService(new SOC::SOC_U);
252 AddService(new SSL::SSL_C); 253 AddService(new SSL::SSL_C);
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 5e7fc68f9..854ab9a05 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -36,6 +36,10 @@ ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService
36 std::string name, unsigned int max_sessions) { 36 std::string name, unsigned int max_sessions) {
37 37
38 CASCADE_CODE(ValidateServiceName(name)); 38 CASCADE_CODE(ValidateServiceName(name));
39
40 if (registered_services.find(name) != registered_services.end())
41 return ERR_ALREADY_REGISTERED;
42
39 Kernel::SharedPtr<Kernel::ServerPort> server_port; 43 Kernel::SharedPtr<Kernel::ServerPort> server_port;
40 Kernel::SharedPtr<Kernel::ClientPort> client_port; 44 Kernel::SharedPtr<Kernel::ClientPort> client_port;
41 std::tie(server_port, client_port) = Kernel::ServerPort::CreatePortPair(max_sessions, name); 45 std::tie(server_port, client_port) = Kernel::ServerPort::CreatePortPair(max_sessions, name);
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 8f0dbf2db..9f60a7965 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -32,6 +32,9 @@ constexpr ResultCode ERR_ACCESS_DENIED(6, ErrorModule::SRV, ErrorSummary::Invali
32 ErrorLevel::Permanent); // 0xD8E06406 32 ErrorLevel::Permanent); // 0xD8E06406
33constexpr ResultCode ERR_NAME_CONTAINS_NUL(7, ErrorModule::SRV, ErrorSummary::WrongArgument, 33constexpr ResultCode ERR_NAME_CONTAINS_NUL(7, ErrorModule::SRV, ErrorSummary::WrongArgument,
34 ErrorLevel::Permanent); // 0xD9006407 34 ErrorLevel::Permanent); // 0xD9006407
35constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorDescription::AlreadyExists, ErrorModule::OS,
36 ErrorSummary::WrongArgument,
37 ErrorLevel::Permanent); // 0xD9001BFC
35 38
36class ServiceManager { 39class ServiceManager {
37public: 40public:
diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp
index 352941e69..fb873981c 100644
--- a/src/core/hle/service/sm/srv.cpp
+++ b/src/core/hle/service/sm/srv.cpp
@@ -13,6 +13,7 @@
13#include "core/hle/kernel/errors.h" 13#include "core/hle/kernel/errors.h"
14#include "core/hle/kernel/hle_ipc.h" 14#include "core/hle/kernel/hle_ipc.h"
15#include "core/hle/kernel/semaphore.h" 15#include "core/hle/kernel/semaphore.h"
16#include "core/hle/kernel/server_port.h"
16#include "core/hle/kernel/server_session.h" 17#include "core/hle/kernel/server_session.h"
17#include "core/hle/service/sm/sm.h" 18#include "core/hle/service/sm/sm.h"
18#include "core/hle/service/sm/srv.h" 19#include "core/hle/service/sm/srv.h"
@@ -61,7 +62,7 @@ void SRV::EnableNotification(Kernel::HLERequestContext& ctx) {
61 IPC::RequestParser rp(ctx, 0x2, 0, 0); 62 IPC::RequestParser rp(ctx, 0x2, 0, 0);
62 63
63 notification_semaphore = 64 notification_semaphore =
64 Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, "SRV:Notification").Unwrap(); 65 Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, 0, "SRV:Notification").Unwrap();
65 66
66 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); 67 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
67 rb.Push(RESULT_SUCCESS); 68 rb.Push(RESULT_SUCCESS);
@@ -184,12 +185,35 @@ void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) {
184 flags); 185 flags);
185} 186}
186 187
188void SRV::RegisterService(Kernel::HLERequestContext& ctx) {
189 IPC::RequestParser rp(ctx, 0x3, 4, 0);
190
191 auto name_buf = rp.PopRaw<std::array<char, 8>>();
192 size_t name_len = rp.Pop<u32>();
193 u32 max_sessions = rp.Pop<u32>();
194
195 std::string name(name_buf.data(), std::min(name_len, name_buf.size()));
196
197 auto port = service_manager->RegisterService(name, max_sessions);
198
199 if (port.Failed()) {
200 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
201 rb.Push(port.Code());
202 LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), port.Code().raw);
203 return;
204 }
205
206 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
207 rb.Push(RESULT_SUCCESS);
208 rb.PushObjects(port.Unwrap());
209}
210
187SRV::SRV(std::shared_ptr<ServiceManager> service_manager) 211SRV::SRV(std::shared_ptr<ServiceManager> service_manager)
188 : ServiceFramework("srv:", 4), service_manager(std::move(service_manager)) { 212 : ServiceFramework("srv:", 4), service_manager(std::move(service_manager)) {
189 static const FunctionInfo functions[] = { 213 static const FunctionInfo functions[] = {
190 {0x00010002, &SRV::RegisterClient, "RegisterClient"}, 214 {0x00010002, &SRV::RegisterClient, "RegisterClient"},
191 {0x00020000, &SRV::EnableNotification, "EnableNotification"}, 215 {0x00020000, &SRV::EnableNotification, "EnableNotification"},
192 {0x00030100, nullptr, "RegisterService"}, 216 {0x00030100, &SRV::RegisterService, "RegisterService"},
193 {0x000400C0, nullptr, "UnregisterService"}, 217 {0x000400C0, nullptr, "UnregisterService"},
194 {0x00050100, &SRV::GetServiceHandle, "GetServiceHandle"}, 218 {0x00050100, &SRV::GetServiceHandle, "GetServiceHandle"},
195 {0x000600C2, nullptr, "RegisterPort"}, 219 {0x000600C2, nullptr, "RegisterPort"},
diff --git a/src/core/hle/service/sm/srv.h b/src/core/hle/service/sm/srv.h
index 75cca5184..aad839563 100644
--- a/src/core/hle/service/sm/srv.h
+++ b/src/core/hle/service/sm/srv.h
@@ -28,6 +28,7 @@ private:
28 void Subscribe(Kernel::HLERequestContext& ctx); 28 void Subscribe(Kernel::HLERequestContext& ctx);
29 void Unsubscribe(Kernel::HLERequestContext& ctx); 29 void Unsubscribe(Kernel::HLERequestContext& ctx);
30 void PublishToSubscriber(Kernel::HLERequestContext& ctx); 30 void PublishToSubscriber(Kernel::HLERequestContext& ctx);
31 void RegisterService(Kernel::HLERequestContext& ctx);
31 32
32 std::shared_ptr<ServiceManager> service_manager; 33 std::shared_ptr<ServiceManager> service_manager;
33 Kernel::SharedPtr<Kernel::Semaphore> notification_semaphore; 34 Kernel::SharedPtr<Kernel::Semaphore> notification_semaphore;
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 6838e449c..d1bfe51e6 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -29,7 +29,7 @@ namespace GPU {
29Regs g_regs; 29Regs g_regs;
30 30
31/// 268MHz CPU clocks / 60Hz frames per second 31/// 268MHz CPU clocks / 60Hz frames per second
32const u64 frame_ticks = BASE_CLOCK_RATE_ARM11 / SCREEN_REFRESH_RATE; 32const u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE_ARM11 / SCREEN_REFRESH_RATE);
33/// Event id for CoreTiming 33/// Event id for CoreTiming
34static int vblank_event; 34static int vblank_event;
35 35
@@ -515,15 +515,15 @@ template void Write<u8>(u32 addr, const u8 data);
515 515
516/// Update hardware 516/// Update hardware
517static void VBlankCallback(u64 userdata, int cycles_late) { 517static void VBlankCallback(u64 userdata, int cycles_late) {
518 VideoCore::g_renderer->SwapBuffers(); 518 //VideoCore::g_renderer->SwapBuffers();
519 519
520 // Signal to GSP that GPU interrupt has occurred 520 //// Signal to GSP that GPU interrupt has occurred
521 // TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub 521 //// TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub
522 // screen, or if both use the same interrupts and these two instead determine the 522 //// screen, or if both use the same interrupts and these two instead determine the
523 // beginning and end of the VBlank period. If needed, split the interrupt firing into 523 //// beginning and end of the VBlank period. If needed, split the interrupt firing into
524 // two different intervals. 524 //// two different intervals.
525 Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC0); 525 //Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC0);
526 Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC1); 526 //Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC1);
527 527
528 // Reschedule recurrent event 528 // Reschedule recurrent event
529 CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event); 529 CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event);
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 21b127fee..e3d0a0e08 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -74,9 +74,9 @@ struct Regs {
74 case PixelFormat::RGB5A1: 74 case PixelFormat::RGB5A1:
75 case PixelFormat::RGBA4: 75 case PixelFormat::RGBA4:
76 return 2; 76 return 2;
77 default:
78 UNIMPLEMENTED();
79 } 77 }
78
79 UNREACHABLE();
80 } 80 }
81 81
82 INSERT_PADDING_WORDS(0x4); 82 INSERT_PADDING_WORDS(0x4);
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
index 209328347..7b0342cc9 100644
--- a/src/core/loader/3dsx.cpp
+++ b/src/core/loader/3dsx.cpp
@@ -91,8 +91,8 @@ static u32 TranslateAddr(u32 addr, const THREEloadinfo* loadinfo, u32* offsets)
91 return loadinfo->seg_addrs[2] + addr - offsets[1]; 91 return loadinfo->seg_addrs[2] + addr - offsets[1];
92} 92}
93 93
94using Kernel::SharedPtr;
95using Kernel::CodeSet; 94using Kernel::CodeSet;
95using Kernel::SharedPtr;
96 96
97static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, 97static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr,
98 SharedPtr<CodeSet>* out_codeset) { 98 SharedPtr<CodeSet>* out_codeset) {
@@ -255,7 +255,7 @@ FileType AppLoader_THREEDSX::IdentifyType(FileUtil::IOFile& file) {
255 return FileType::Error; 255 return FileType::Error;
256} 256}
257 257
258ResultStatus AppLoader_THREEDSX::Load() { 258ResultStatus AppLoader_THREEDSX::Load(Kernel::SharedPtr<Kernel::Process>& process) {
259 if (is_loaded) 259 if (is_loaded)
260 return ResultStatus::ErrorAlreadyLoaded; 260 return ResultStatus::ErrorAlreadyLoaded;
261 261
@@ -267,19 +267,17 @@ ResultStatus AppLoader_THREEDSX::Load() {
267 return ResultStatus::Error; 267 return ResultStatus::Error;
268 codeset->name = filename; 268 codeset->name = filename;
269 269
270 Kernel::g_current_process = Kernel::Process::Create("main"); 270 process = Kernel::Process::Create("main");
271 Kernel::g_current_process->LoadModule(codeset, codeset->entrypoint); 271 process->LoadModule(codeset, codeset->entrypoint);
272 Kernel::g_current_process->svc_access_mask.set(); 272 process->svc_access_mask.set();
273 Kernel::g_current_process->address_mappings = default_address_mappings; 273 process->address_mappings = default_address_mappings;
274 274
275 // Attach the default resource limit (APPLICATION) to the process 275 // Attach the default resource limit (APPLICATION) to the process
276 Kernel::g_current_process->resource_limit = 276 process->resource_limit =
277 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); 277 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
278 process->Run(codeset->entrypoint, 48, Kernel::DEFAULT_STACK_SIZE);
278 279
279 Kernel::g_current_process->Run(codeset->entrypoint, 48, Kernel::DEFAULT_STACK_SIZE); 280 Service::FS::RegisterSelfNCCH(*this);
280
281 Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_SelfNCCH>(*this),
282 Service::FS::ArchiveIdCode::SelfNCCH);
283 281
284 is_loaded = true; 282 is_loaded = true;
285 return ResultStatus::Success; 283 return ResultStatus::Success;
diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h
index 3f376778a..1e59bbb9d 100644
--- a/src/core/loader/3dsx.h
+++ b/src/core/loader/3dsx.h
@@ -31,7 +31,7 @@ public:
31 return IdentifyType(file); 31 return IdentifyType(file);
32 } 32 }
33 33
34 ResultStatus Load() override; 34 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
35 35
36 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 36 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
37 37
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 2efc67ff8..9969a8c39 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -13,8 +13,8 @@
13#include "core/loader/elf.h" 13#include "core/loader/elf.h"
14#include "core/memory.h" 14#include "core/memory.h"
15 15
16using Kernel::SharedPtr;
17using Kernel::CodeSet; 16using Kernel::CodeSet;
17using Kernel::SharedPtr;
18 18
19//////////////////////////////////////////////////////////////////////////////////////////////////// 19////////////////////////////////////////////////////////////////////////////////////////////////////
20// ELF Header Constants 20// ELF Header Constants
@@ -382,7 +382,7 @@ FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file) {
382 return FileType::Error; 382 return FileType::Error;
383} 383}
384 384
385ResultStatus AppLoader_ELF::Load() { 385ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
386 if (is_loaded) 386 if (is_loaded)
387 return ResultStatus::ErrorAlreadyLoaded; 387 return ResultStatus::ErrorAlreadyLoaded;
388 388
@@ -401,16 +401,16 @@ ResultStatus AppLoader_ELF::Load() {
401 SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); 401 SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR);
402 codeset->name = filename; 402 codeset->name = filename;
403 403
404 Kernel::g_current_process = Kernel::Process::Create("main"); 404 process = Kernel::Process::Create("main");
405 Kernel::g_current_process->LoadModule(codeset, codeset->entrypoint); 405 process->LoadModule(codeset, codeset->entrypoint);
406 Kernel::g_current_process->svc_access_mask.set(); 406 process->svc_access_mask.set();
407 Kernel::g_current_process->address_mappings = default_address_mappings; 407 process->address_mappings = default_address_mappings;
408 408
409 // Attach the default resource limit (APPLICATION) to the process 409 // Attach the default resource limit (APPLICATION) to the process
410 Kernel::g_current_process->resource_limit = 410 process->resource_limit =
411 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); 411 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
412 412
413 Kernel::g_current_process->Run(codeset->entrypoint, 48, Kernel::DEFAULT_STACK_SIZE); 413 process->Run(codeset->entrypoint, 48, Kernel::DEFAULT_STACK_SIZE);
414 414
415 is_loaded = true; 415 is_loaded = true;
416 return ResultStatus::Success; 416 return ResultStatus::Success;
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index 862aa90d8..113da5917 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -30,7 +30,7 @@ public:
30 return IdentifyType(file); 30 return IdentifyType(file);
31 } 31 }
32 32
33 ResultStatus Load() override; 33 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
34 34
35private: 35private:
36 std::string filename; 36 std::string filename;
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index ac26f31fa..311785d05 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -13,10 +13,12 @@
13#include <boost/optional.hpp> 13#include <boost/optional.hpp>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/file_util.h" 15#include "common/file_util.h"
16#include "core/hle/kernel/kernel.h"
16 17
17namespace Kernel { 18namespace Kernel {
18struct AddressMapping; 19struct AddressMapping;
19} 20class Process;
21} // namespace Kernel
20 22
21//////////////////////////////////////////////////////////////////////////////////////////////////// 23////////////////////////////////////////////////////////////////////////////////////////////////////
22// Loader namespace 24// Loader namespace
@@ -94,10 +96,11 @@ public:
94 virtual FileType GetFileType() = 0; 96 virtual FileType GetFileType() = 0;
95 97
96 /** 98 /**
97 * Load the application 99 * Load the application and return the created Process instance
98 * @return ResultStatus result of function 100 * @param process The newly created process.
101 * @return The status result of the operation.
99 */ 102 */
100 virtual ResultStatus Load() = 0; 103 virtual ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) = 0;
101 104
102 /** 105 /**
103 * Loads the system mode that this application needs. 106 * Loads the system mode that this application needs.
@@ -168,6 +171,28 @@ public:
168 return ResultStatus::ErrorNotImplemented; 171 return ResultStatus::ErrorNotImplemented;
169 } 172 }
170 173
174 /**
175 * Get the update RomFS of the application
176 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer
177 * @param romfs_file The file containing the RomFS
178 * @param offset The offset the romfs begins on
179 * @param size The size of the romfs
180 * @return ResultStatus result of function
181 */
182 virtual ResultStatus ReadUpdateRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
183 u64& size) {
184 return ResultStatus::ErrorNotImplemented;
185 }
186
187 /**
188 * Get the title of the application
189 * @param title Reference to store the application title into
190 * @return ResultStatus result of function
191 */
192 virtual ResultStatus ReadTitle(std::string& title) {
193 return ResultStatus::ErrorNotImplemented;
194 }
195
171protected: 196protected:
172 FileUtil::IOFile file; 197 FileUtil::IOFile file;
173 bool is_loaded = false; 198 bool is_loaded = false;
@@ -186,4 +211,4 @@ extern const std::initializer_list<Kernel::AddressMapping> default_address_mappi
186 */ 211 */
187std::unique_ptr<AppLoader> GetLoader(const std::string& filename); 212std::unique_ptr<AppLoader> GetLoader(const std::string& filename);
188 213
189} // namespace 214} // namespace Loader
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 728886ea8..e33a37b2e 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -4,13 +4,17 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cinttypes> 6#include <cinttypes>
7#include <codecvt>
7#include <cstring> 8#include <cstring>
9#include <locale>
8#include <memory> 10#include <memory>
9#include "common/logging/log.h" 11#include "common/logging/log.h"
10#include "common/string_util.h" 12#include "common/string_util.h"
11#include "common/swap.h" 13#include "common/swap.h"
12#include "core/core.h" 14#include "core/core.h"
13#include "core/file_sys/archive_selfncch.h" 15#include "core/file_sys/archive_selfncch.h"
16#include "core/file_sys/ncch_container.h"
17#include "core/file_sys/title_metadata.h"
14#include "core/hle/kernel/process.h" 18#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/resource_limit.h" 19#include "core/hle/kernel/resource_limit.h"
16#include "core/hle/service/cfg/cfg.h" 20#include "core/hle/service/cfg/cfg.h"
@@ -18,93 +22,14 @@
18#include "core/loader/ncch.h" 22#include "core/loader/ncch.h"
19#include "core/loader/smdh.h" 23#include "core/loader/smdh.h"
20#include "core/memory.h" 24#include "core/memory.h"
25#include "network/network.h"
21 26
22//////////////////////////////////////////////////////////////////////////////////////////////////// 27////////////////////////////////////////////////////////////////////////////////////////////////////
23// Loader namespace 28// Loader namespace
24 29
25namespace Loader { 30namespace Loader {
26 31
27static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs 32static const u64 UPDATE_MASK = 0x0000000e00000000;
28static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
29
30/**
31 * Get the decompressed size of an LZSS compressed ExeFS file
32 * @param buffer Buffer of compressed file
33 * @param size Size of compressed buffer
34 * @return Size of decompressed buffer
35 */
36static u32 LZSS_GetDecompressedSize(const u8* buffer, u32 size) {
37 u32 offset_size = *(u32*)(buffer + size - 4);
38 return offset_size + size;
39}
40
41/**
42 * Decompress ExeFS file (compressed with LZSS)
43 * @param compressed Compressed buffer
44 * @param compressed_size Size of compressed buffer
45 * @param decompressed Decompressed buffer
46 * @param decompressed_size Size of decompressed buffer
47 * @return True on success, otherwise false
48 */
49static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decompressed,
50 u32 decompressed_size) {
51 const u8* footer = compressed + compressed_size - 8;
52 u32 buffer_top_and_bottom = *reinterpret_cast<const u32*>(footer);
53 u32 out = decompressed_size;
54 u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF);
55 u32 stop_index = compressed_size - (buffer_top_and_bottom & 0xFFFFFF);
56
57 memset(decompressed, 0, decompressed_size);
58 memcpy(decompressed, compressed, compressed_size);
59
60 while (index > stop_index) {
61 u8 control = compressed[--index];
62
63 for (unsigned i = 0; i < 8; i++) {
64 if (index <= stop_index)
65 break;
66 if (index <= 0)
67 break;
68 if (out <= 0)
69 break;
70
71 if (control & 0x80) {
72 // Check if compression is out of bounds
73 if (index < 2)
74 return false;
75 index -= 2;
76
77 u32 segment_offset = compressed[index] | (compressed[index + 1] << 8);
78 u32 segment_size = ((segment_offset >> 12) & 15) + 3;
79 segment_offset &= 0x0FFF;
80 segment_offset += 2;
81
82 // Check if compression is out of bounds
83 if (out < segment_size)
84 return false;
85
86 for (unsigned j = 0; j < segment_size; j++) {
87 // Check if compression is out of bounds
88 if (out + segment_offset >= decompressed_size)
89 return false;
90
91 u8 data = decompressed[out + segment_offset];
92 decompressed[--out] = data;
93 }
94 } else {
95 // Check if compression is out of bounds
96 if (out < 1)
97 return false;
98 decompressed[--out] = compressed[--index];
99 }
100 control <<= 1;
101 }
102 }
103 return true;
104}
105
106////////////////////////////////////////////////////////////////////////////////////////////////////
107// AppLoader_NCCH class
108 33
109FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { 34FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
110 u32 magic; 35 u32 magic;
@@ -121,203 +46,105 @@ FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
121 return FileType::Error; 46 return FileType::Error;
122} 47}
123 48
49static std::string GetUpdateNCCHPath(u64_le program_id) {
50 u32 high = static_cast<u32>((program_id | UPDATE_MASK) >> 32);
51 u32 low = static_cast<u32>((program_id | UPDATE_MASK) & 0xFFFFFFFF);
52
53 // TODO(shinyquagsire23): Title database should be doing this path lookup
54 std::string content_path = Common::StringFromFormat(
55 "%sNintendo 3DS/%s/%s/title/%08x/%08x/content/", FileUtil::GetUserPath(D_SDMC_IDX).c_str(),
56 SYSTEM_ID, SDCARD_ID, high, low);
57 std::string tmd_path = content_path + "00000000.tmd";
58
59 u32 content_id = 0;
60 FileSys::TitleMetadata tmd(tmd_path);
61 if (tmd.Load() == ResultStatus::Success) {
62 content_id = tmd.GetBootContentID();
63 }
64
65 return Common::StringFromFormat("%s%08x.app", content_path.c_str(), content_id);
66}
67
124std::pair<boost::optional<u32>, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode() { 68std::pair<boost::optional<u32>, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode() {
125 if (!is_loaded) { 69 if (!is_loaded) {
126 ResultStatus res = LoadExeFS(); 70 ResultStatus res = base_ncch.Load();
127 if (res != ResultStatus::Success) { 71 if (res != ResultStatus::Success) {
128 return std::make_pair(boost::none, res); 72 return std::make_pair(boost::none, res);
129 } 73 }
130 } 74 }
75
131 // Set the system mode as the one from the exheader. 76 // Set the system mode as the one from the exheader.
132 return std::make_pair(exheader_header.arm11_system_local_caps.system_mode.Value(), 77 return std::make_pair(overlay_ncch->exheader_header.arm11_system_local_caps.system_mode.Value(),
133 ResultStatus::Success); 78 ResultStatus::Success);
134} 79}
135 80
136ResultStatus AppLoader_NCCH::LoadExec() { 81ResultStatus AppLoader_NCCH::LoadExec(Kernel::SharedPtr<Kernel::Process>& process) {
137 using Kernel::SharedPtr;
138 using Kernel::CodeSet; 82 using Kernel::CodeSet;
83 using Kernel::SharedPtr;
139 84
140 if (!is_loaded) 85 if (!is_loaded)
141 return ResultStatus::ErrorNotLoaded; 86 return ResultStatus::ErrorNotLoaded;
142 87
143 std::vector<u8> code; 88 std::vector<u8> code;
144 if (ResultStatus::Success == ReadCode(code)) { 89 u64_le program_id;
90 if (ResultStatus::Success == ReadCode(code) &&
91 ResultStatus::Success == ReadProgramId(program_id)) {
145 std::string process_name = Common::StringFromFixedZeroTerminatedBuffer( 92 std::string process_name = Common::StringFromFixedZeroTerminatedBuffer(
146 (const char*)exheader_header.codeset_info.name, 8); 93 (const char*)overlay_ncch->exheader_header.codeset_info.name, 8);
147 94
148 SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, ncch_header.program_id); 95 SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, program_id);
149 96
150 codeset->code.offset = 0; 97 codeset->code.offset = 0;
151 codeset->code.addr = exheader_header.codeset_info.text.address; 98 codeset->code.addr = overlay_ncch->exheader_header.codeset_info.text.address;
152 codeset->code.size = exheader_header.codeset_info.text.num_max_pages * Memory::PAGE_SIZE; 99 codeset->code.size =
100 overlay_ncch->exheader_header.codeset_info.text.num_max_pages * Memory::PAGE_SIZE;
153 101
154 codeset->rodata.offset = codeset->code.offset + codeset->code.size; 102 codeset->rodata.offset = codeset->code.offset + codeset->code.size;
155 codeset->rodata.addr = exheader_header.codeset_info.ro.address; 103 codeset->rodata.addr = overlay_ncch->exheader_header.codeset_info.ro.address;
156 codeset->rodata.size = exheader_header.codeset_info.ro.num_max_pages * Memory::PAGE_SIZE; 104 codeset->rodata.size =
105 overlay_ncch->exheader_header.codeset_info.ro.num_max_pages * Memory::PAGE_SIZE;
157 106
158 // TODO(yuriks): Not sure if the bss size is added to the page-aligned .data size or just 107 // TODO(yuriks): Not sure if the bss size is added to the page-aligned .data size or just
159 // to the regular size. Playing it safe for now. 108 // to the regular size. Playing it safe for now.
160 u32 bss_page_size = (exheader_header.codeset_info.bss_size + 0xFFF) & ~0xFFF; 109 u32 bss_page_size = (overlay_ncch->exheader_header.codeset_info.bss_size + 0xFFF) & ~0xFFF;
161 code.resize(code.size() + bss_page_size, 0); 110 code.resize(code.size() + bss_page_size, 0);
162 111
163 codeset->data.offset = codeset->rodata.offset + codeset->rodata.size; 112 codeset->data.offset = codeset->rodata.offset + codeset->rodata.size;
164 codeset->data.addr = exheader_header.codeset_info.data.address; 113 codeset->data.addr = overlay_ncch->exheader_header.codeset_info.data.address;
165 codeset->data.size = 114 codeset->data.size =
166 exheader_header.codeset_info.data.num_max_pages * Memory::PAGE_SIZE + bss_page_size; 115 overlay_ncch->exheader_header.codeset_info.data.num_max_pages * Memory::PAGE_SIZE +
116 bss_page_size;
167 117
168 codeset->entrypoint = codeset->code.addr; 118 codeset->entrypoint = codeset->code.addr;
169 codeset->memory = std::make_shared<std::vector<u8>>(std::move(code)); 119 codeset->memory = std::make_shared<std::vector<u8>>(std::move(code));
170 120
171 Kernel::g_current_process = Kernel::Process::Create("main"); 121 process = Kernel::Process::Create("main");
172 Kernel::g_current_process->LoadModule(codeset, codeset->entrypoint); 122 process->LoadModule(codeset, codeset->entrypoint);
173 123
174 // Attach a resource limit to the process based on the resource limit category 124 // Attach a resource limit to the process based on the resource limit category
175 Kernel::g_current_process->resource_limit = 125 process->resource_limit =
176 Kernel::ResourceLimit::GetForCategory(static_cast<Kernel::ResourceLimitCategory>( 126 Kernel::ResourceLimit::GetForCategory(static_cast<Kernel::ResourceLimitCategory>(
177 exheader_header.arm11_system_local_caps.resource_limit_category)); 127 overlay_ncch->exheader_header.arm11_system_local_caps.resource_limit_category));
178 128
179 // Set the default CPU core for this process 129 // Set the default CPU core for this process
180 Kernel::g_current_process->ideal_processor = 130 process->ideal_processor =
181 exheader_header.arm11_system_local_caps.ideal_processor; 131 overlay_ncch->exheader_header.arm11_system_local_caps.ideal_processor;
182 132
183 // Copy data while converting endianness 133 // Copy data while converting endianness
184 std::array<u32, ARRAY_SIZE(exheader_header.arm11_kernel_caps.descriptors)> kernel_caps; 134 std::array<u32, ARRAY_SIZE(overlay_ncch->exheader_header.arm11_kernel_caps.descriptors)>
185 std::copy_n(exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), 135 kernel_caps;
136 std::copy_n(overlay_ncch->exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(),
186 begin(kernel_caps)); 137 begin(kernel_caps));
187 Kernel::g_current_process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size()); 138 process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size());
188 139
189 s32 priority = exheader_header.arm11_system_local_caps.priority; 140 s32 priority = overlay_ncch->exheader_header.arm11_system_local_caps.priority;
190 u32 stack_size = exheader_header.codeset_info.stack_size; 141 u32 stack_size = overlay_ncch->exheader_header.codeset_info.stack_size;
191 Kernel::g_current_process->Run(codeset->entrypoint, priority, stack_size); 142 process->Run(codeset->entrypoint, priority, stack_size);
192 return ResultStatus::Success; 143 return ResultStatus::Success;
193 } 144 }
194 return ResultStatus::Error; 145 return ResultStatus::Error;
195} 146}
196 147
197ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) {
198 if (!file.IsOpen())
199 return ResultStatus::Error;
200
201 ResultStatus result = LoadExeFS();
202 if (result != ResultStatus::Success)
203 return result;
204
205 LOG_DEBUG(Loader, "%d sections:", kMaxSections);
206 // Iterate through the ExeFs archive until we find a section with the specified name...
207 for (unsigned section_number = 0; section_number < kMaxSections; section_number++) {
208 const auto& section = exefs_header.section[section_number];
209
210 // Load the specified section...
211 if (strcmp(section.name, name) == 0) {
212 LOG_DEBUG(Loader, "%d - offset: 0x%08X, size: 0x%08X, name: %s", section_number,
213 section.offset, section.size, section.name);
214
215 s64 section_offset =
216 (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset);
217 file.Seek(section_offset, SEEK_SET);
218
219 if (strcmp(section.name, ".code") == 0 && is_compressed) {
220 // Section is compressed, read compressed .code section...
221 std::unique_ptr<u8[]> temp_buffer;
222 try {
223 temp_buffer.reset(new u8[section.size]);
224 } catch (std::bad_alloc&) {
225 return ResultStatus::ErrorMemoryAllocationFailed;
226 }
227
228 if (file.ReadBytes(&temp_buffer[0], section.size) != section.size)
229 return ResultStatus::Error;
230
231 // Decompress .code section...
232 u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], section.size);
233 buffer.resize(decompressed_size);
234 if (!LZSS_Decompress(&temp_buffer[0], section.size, &buffer[0], decompressed_size))
235 return ResultStatus::ErrorInvalidFormat;
236 } else {
237 // Section is uncompressed...
238 buffer.resize(section.size);
239 if (file.ReadBytes(&buffer[0], section.size) != section.size)
240 return ResultStatus::Error;
241 }
242 return ResultStatus::Success;
243 }
244 }
245 return ResultStatus::ErrorNotUsed;
246}
247
248ResultStatus AppLoader_NCCH::LoadExeFS() {
249 if (is_exefs_loaded)
250 return ResultStatus::Success;
251
252 if (!file.IsOpen())
253 return ResultStatus::Error;
254
255 // Reset read pointer in case this file has been read before.
256 file.Seek(0, SEEK_SET);
257
258 if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header))
259 return ResultStatus::Error;
260
261 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
262 if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
263 LOG_DEBUG(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
264 ncch_offset = 0x4000;
265 file.Seek(ncch_offset, SEEK_SET);
266 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
267 }
268
269 // Verify we are loading the correct file type...
270 if (MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic)
271 return ResultStatus::ErrorInvalidFormat;
272
273 // Read ExHeader...
274
275 if (file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header))
276 return ResultStatus::Error;
277
278 is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
279 entry_point = exheader_header.codeset_info.text.address;
280 code_size = exheader_header.codeset_info.text.code_size;
281 stack_size = exheader_header.codeset_info.stack_size;
282 bss_size = exheader_header.codeset_info.bss_size;
283 core_version = exheader_header.arm11_system_local_caps.core_version;
284 priority = exheader_header.arm11_system_local_caps.priority;
285 resource_limit_category = exheader_header.arm11_system_local_caps.resource_limit_category;
286
287 LOG_DEBUG(Loader, "Name: %s", exheader_header.codeset_info.name);
288 LOG_DEBUG(Loader, "Program ID: %016" PRIX64, ncch_header.program_id);
289 LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no");
290 LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point);
291 LOG_DEBUG(Loader, "Code size: 0x%08X", code_size);
292 LOG_DEBUG(Loader, "Stack size: 0x%08X", stack_size);
293 LOG_DEBUG(Loader, "Bss size: 0x%08X", bss_size);
294 LOG_DEBUG(Loader, "Core version: %d", core_version);
295 LOG_DEBUG(Loader, "Thread priority: 0x%X", priority);
296 LOG_DEBUG(Loader, "Resource limit category: %d", resource_limit_category);
297 LOG_DEBUG(Loader, "System Mode: %d",
298 static_cast<int>(exheader_header.arm11_system_local_caps.system_mode));
299
300 if (exheader_header.arm11_system_local_caps.program_id != ncch_header.program_id) {
301 LOG_ERROR(Loader, "ExHeader Program ID mismatch: the ROM is probably encrypted.");
302 return ResultStatus::ErrorEncrypted;
303 }
304
305 // Read ExeFS...
306
307 exefs_offset = ncch_header.exefs_offset * kBlockSize;
308 u32 exefs_size = ncch_header.exefs_size * kBlockSize;
309
310 LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset);
311 LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size);
312
313 file.Seek(exefs_offset + ncch_offset, SEEK_SET);
314 if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header))
315 return ResultStatus::Error;
316
317 is_exefs_loaded = true;
318 return ResultStatus::Success;
319}
320
321void AppLoader_NCCH::ParseRegionLockoutInfo() { 148void AppLoader_NCCH::ParseRegionLockoutInfo() {
322 std::vector<u8> smdh_buffer; 149 std::vector<u8> smdh_buffer;
323 if (ReadIcon(smdh_buffer) == ResultStatus::Success && smdh_buffer.size() >= sizeof(SMDH)) { 150 if (ReadIcon(smdh_buffer) == ResultStatus::Success && smdh_buffer.size() >= sizeof(SMDH)) {
@@ -335,28 +162,43 @@ void AppLoader_NCCH::ParseRegionLockoutInfo() {
335 } 162 }
336} 163}
337 164
338ResultStatus AppLoader_NCCH::Load() { 165ResultStatus AppLoader_NCCH::Load(Kernel::SharedPtr<Kernel::Process>& process) {
166 u64_le ncch_program_id;
167
339 if (is_loaded) 168 if (is_loaded)
340 return ResultStatus::ErrorAlreadyLoaded; 169 return ResultStatus::ErrorAlreadyLoaded;
341 170
342 ResultStatus result = LoadExeFS(); 171 ResultStatus result = base_ncch.Load();
343 if (result != ResultStatus::Success) 172 if (result != ResultStatus::Success)
344 return result; 173 return result;
345 174
346 std::string program_id{Common::StringFromFormat("%016" PRIX64, ncch_header.program_id)}; 175 ReadProgramId(ncch_program_id);
176 std::string program_id{Common::StringFromFormat("%016" PRIX64, ncch_program_id)};
347 177
348 LOG_INFO(Loader, "Program ID: %s", program_id.c_str()); 178 LOG_INFO(Loader, "Program ID: %s", program_id.c_str());
349 179
180 update_ncch.OpenFile(GetUpdateNCCHPath(ncch_program_id));
181 result = update_ncch.Load();
182 if (result == ResultStatus::Success) {
183 overlay_ncch = &update_ncch;
184 }
185
350 Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id); 186 Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id);
351 187
188 if (auto room_member = Network::GetRoomMember().lock()) {
189 Network::GameInfo game_info;
190 ReadTitle(game_info.name);
191 game_info.id = ncch_program_id;
192 room_member->SendGameInfo(game_info);
193 }
194
352 is_loaded = true; // Set state to loaded 195 is_loaded = true; // Set state to loaded
353 196
354 result = LoadExec(); // Load the executable into memory for booting 197 result = LoadExec(process); // Load the executable into memory for booting
355 if (ResultStatus::Success != result) 198 if (ResultStatus::Success != result)
356 return result; 199 return result;
357 200
358 Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_SelfNCCH>(*this), 201 Service::FS::RegisterSelfNCCH(*this);
359 Service::FS::ArchiveIdCode::SelfNCCH);
360 202
361 ParseRegionLockoutInfo(); 203 ParseRegionLockoutInfo();
362 204
@@ -364,61 +206,58 @@ ResultStatus AppLoader_NCCH::Load() {
364} 206}
365 207
366ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) { 208ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) {
367 return LoadSectionExeFS(".code", buffer); 209 return overlay_ncch->LoadSectionExeFS(".code", buffer);
368} 210}
369 211
370ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) { 212ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) {
371 return LoadSectionExeFS("icon", buffer); 213 return overlay_ncch->LoadSectionExeFS("icon", buffer);
372} 214}
373 215
374ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) { 216ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) {
375 return LoadSectionExeFS("banner", buffer); 217 return overlay_ncch->LoadSectionExeFS("banner", buffer);
376} 218}
377 219
378ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) { 220ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) {
379 return LoadSectionExeFS("logo", buffer); 221 return overlay_ncch->LoadSectionExeFS("logo", buffer);
380} 222}
381 223
382ResultStatus AppLoader_NCCH::ReadProgramId(u64& out_program_id) { 224ResultStatus AppLoader_NCCH::ReadProgramId(u64& out_program_id) {
383 if (!file.IsOpen()) 225 ResultStatus result = base_ncch.ReadProgramId(out_program_id);
384 return ResultStatus::Error;
385
386 ResultStatus result = LoadExeFS();
387 if (result != ResultStatus::Success) 226 if (result != ResultStatus::Success)
388 return result; 227 return result;
389 228
390 out_program_id = ncch_header.program_id;
391 return ResultStatus::Success; 229 return ResultStatus::Success;
392} 230}
393 231
394ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, 232ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
395 u64& size) { 233 u64& size) {
396 if (!file.IsOpen()) 234 return base_ncch.ReadRomFS(romfs_file, offset, size);
397 return ResultStatus::Error; 235}
398 236
399 // Check if the NCCH has a RomFS... 237ResultStatus AppLoader_NCCH::ReadUpdateRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file,
400 if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) { 238 u64& offset, u64& size) {
401 u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; 239 ResultStatus result = update_ncch.ReadRomFS(romfs_file, offset, size);
402 u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000;
403 240
404 LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset); 241 if (result != ResultStatus::Success)
405 LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size); 242 return base_ncch.ReadRomFS(romfs_file, offset, size);
243}
406 244
407 if (file.GetSize() < romfs_offset + romfs_size) 245ResultStatus AppLoader_NCCH::ReadTitle(std::string& title) {
408 return ResultStatus::Error; 246 std::vector<u8> data;
247 Loader::SMDH smdh;
248 ReadIcon(data);
409 249
410 // We reopen the file, to allow its position to be independent from file's 250 if (!Loader::IsValidSMDH(data)) {
411 romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb"); 251 return ResultStatus::ErrorInvalidFormat;
412 if (!romfs_file->IsOpen()) 252 }
413 return ResultStatus::Error;
414 253
415 offset = romfs_offset; 254 memcpy(&smdh, data.data(), sizeof(Loader::SMDH));
416 size = romfs_size;
417 255
418 return ResultStatus::Success; 256 const auto& short_title = smdh.GetShortTitle(SMDH::TitleLanguage::English);
419 } 257 auto title_end = std::find(short_title.begin(), short_title.end(), u'\0');
420 LOG_DEBUG(Loader, "NCCH has no RomFS"); 258 title = Common::UTF16ToUTF8(std::u16string{short_title.begin(), title_end});
421 return ResultStatus::ErrorNotUsed; 259
260 return ResultStatus::Success;
422} 261}
423 262
424} // namespace Loader 263} // namespace Loader
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index 0ebd47fd5..09230ae33 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -5,155 +5,12 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include "common/bit_field.h"
9#include "common/common_types.h" 8#include "common/common_types.h"
10#include "common/swap.h" 9#include "common/swap.h"
10#include "core/file_sys/ncch_container.h"
11#include "core/loader/loader.h" 11#include "core/loader/loader.h"
12 12
13//////////////////////////////////////////////////////////////////////////////////////////////////// 13////////////////////////////////////////////////////////////////////////////////////////////////////
14/// NCCH header (Note: "NCCH" appears to be a publicly unknown acronym)
15
16struct NCCH_Header {
17 u8 signature[0x100];
18 u32_le magic;
19 u32_le content_size;
20 u8 partition_id[8];
21 u16_le maker_code;
22 u16_le version;
23 u8 reserved_0[4];
24 u64_le program_id;
25 u8 reserved_1[0x10];
26 u8 logo_region_hash[0x20];
27 u8 product_code[0x10];
28 u8 extended_header_hash[0x20];
29 u32_le extended_header_size;
30 u8 reserved_2[4];
31 u8 flags[8];
32 u32_le plain_region_offset;
33 u32_le plain_region_size;
34 u32_le logo_region_offset;
35 u32_le logo_region_size;
36 u32_le exefs_offset;
37 u32_le exefs_size;
38 u32_le exefs_hash_region_size;
39 u8 reserved_3[4];
40 u32_le romfs_offset;
41 u32_le romfs_size;
42 u32_le romfs_hash_region_size;
43 u8 reserved_4[4];
44 u8 exefs_super_block_hash[0x20];
45 u8 romfs_super_block_hash[0x20];
46};
47
48static_assert(sizeof(NCCH_Header) == 0x200, "NCCH header structure size is wrong");
49
50////////////////////////////////////////////////////////////////////////////////////////////////////
51// ExeFS (executable file system) headers
52
53struct ExeFs_SectionHeader {
54 char name[8];
55 u32 offset;
56 u32 size;
57};
58
59struct ExeFs_Header {
60 ExeFs_SectionHeader section[8];
61 u8 reserved[0x80];
62 u8 hashes[8][0x20];
63};
64
65////////////////////////////////////////////////////////////////////////////////////////////////////
66// ExHeader (executable file system header) headers
67
68struct ExHeader_SystemInfoFlags {
69 u8 reserved[5];
70 u8 flag;
71 u8 remaster_version[2];
72};
73
74struct ExHeader_CodeSegmentInfo {
75 u32 address;
76 u32 num_max_pages;
77 u32 code_size;
78};
79
80struct ExHeader_CodeSetInfo {
81 u8 name[8];
82 ExHeader_SystemInfoFlags flags;
83 ExHeader_CodeSegmentInfo text;
84 u32 stack_size;
85 ExHeader_CodeSegmentInfo ro;
86 u8 reserved[4];
87 ExHeader_CodeSegmentInfo data;
88 u32 bss_size;
89};
90
91struct ExHeader_DependencyList {
92 u8 program_id[0x30][8];
93};
94
95struct ExHeader_SystemInfo {
96 u64 save_data_size;
97 u8 jump_id[8];
98 u8 reserved_2[0x30];
99};
100
101struct ExHeader_StorageInfo {
102 u8 ext_save_data_id[8];
103 u8 system_save_data_id[8];
104 u8 reserved[8];
105 u8 access_info[7];
106 u8 other_attributes;
107};
108
109struct ExHeader_ARM11_SystemLocalCaps {
110 u64_le program_id;
111 u32_le core_version;
112 u8 reserved_flags[2];
113 union {
114 u8 flags0;
115 BitField<0, 2, u8> ideal_processor;
116 BitField<2, 2, u8> affinity_mask;
117 BitField<4, 4, u8> system_mode;
118 };
119 u8 priority;
120 u8 resource_limit_descriptor[0x10][2];
121 ExHeader_StorageInfo storage_info;
122 u8 service_access_control[0x20][8];
123 u8 ex_service_access_control[0x2][8];
124 u8 reserved[0xf];
125 u8 resource_limit_category;
126};
127
128struct ExHeader_ARM11_KernelCaps {
129 u32_le descriptors[28];
130 u8 reserved[0x10];
131};
132
133struct ExHeader_ARM9_AccessControl {
134 u8 descriptors[15];
135 u8 descversion;
136};
137
138struct ExHeader_Header {
139 ExHeader_CodeSetInfo codeset_info;
140 ExHeader_DependencyList dependency_list;
141 ExHeader_SystemInfo system_info;
142 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
143 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
144 ExHeader_ARM9_AccessControl arm9_access_control;
145 struct {
146 u8 signature[0x100];
147 u8 ncch_public_key_modulus[0x100];
148 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
149 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
150 ExHeader_ARM9_AccessControl arm9_access_control;
151 } access_desc;
152};
153
154static_assert(sizeof(ExHeader_Header) == 0x800, "ExHeader structure size is wrong");
155
156////////////////////////////////////////////////////////////////////////////////////////////////////
157// Loader namespace 14// Loader namespace
158 15
159namespace Loader { 16namespace Loader {
@@ -162,7 +19,8 @@ namespace Loader {
162class AppLoader_NCCH final : public AppLoader { 19class AppLoader_NCCH final : public AppLoader {
163public: 20public:
164 AppLoader_NCCH(FileUtil::IOFile&& file, const std::string& filepath) 21 AppLoader_NCCH(FileUtil::IOFile&& file, const std::string& filepath)
165 : AppLoader(std::move(file)), filepath(filepath) {} 22 : AppLoader(std::move(file)), filepath(filepath), base_ncch(filepath),
23 overlay_ncch(&base_ncch) {}
166 24
167 /** 25 /**
168 * Returns the type of the file 26 * Returns the type of the file
@@ -175,7 +33,7 @@ public:
175 return IdentifyType(file); 33 return IdentifyType(file);
176 } 34 }
177 35
178 ResultStatus Load() override; 36 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
179 37
180 /** 38 /**
181 * Loads the Exheader and returns the system mode for this application. 39 * Loads the Exheader and returns the system mode for this application.
@@ -191,63 +49,30 @@ public:
191 49
192 ResultStatus ReadLogo(std::vector<u8>& buffer) override; 50 ResultStatus ReadLogo(std::vector<u8>& buffer) override;
193 51
194 /**
195 * Get the program id of the application
196 * @param out_program_id Reference to store program id into
197 * @return ResultStatus result of function
198 */
199 ResultStatus ReadProgramId(u64& out_program_id) override; 52 ResultStatus ReadProgramId(u64& out_program_id) override;
200 53
201 /**
202 * Get the RomFS of the application
203 * @param romfs_file Reference to buffer to store data
204 * @param offset Offset in the file to the RomFS
205 * @param size Size of the RomFS in bytes
206 * @return ResultStatus result of function
207 */
208 ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, 54 ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
209 u64& size) override; 55 u64& size) override;
210 56
211private: 57 ResultStatus ReadUpdateRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
212 /** 58 u64& size) override;
213 * Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.)
214 * @param name Name of section to read out of NCCH file
215 * @param buffer Vector to read data into
216 * @return ResultStatus result of function
217 */
218 ResultStatus LoadSectionExeFS(const char* name, std::vector<u8>& buffer);
219 59
220 /** 60 ResultStatus ReadTitle(std::string& title) override;
221 * Loads .code section into memory for booting
222 * @return ResultStatus result of function
223 */
224 ResultStatus LoadExec();
225 61
62private:
226 /** 63 /**
227 * Ensure ExeFS is loaded and ready for reading sections 64 * Loads .code section into memory for booting
65 * @param process The newly created process
228 * @return ResultStatus result of function 66 * @return ResultStatus result of function
229 */ 67 */
230 ResultStatus LoadExeFS(); 68 ResultStatus LoadExec(Kernel::SharedPtr<Kernel::Process>& process);
231 69
232 /// Reads the region lockout info in the SMDH and send it to CFG service 70 /// Reads the region lockout info in the SMDH and send it to CFG service
233 void ParseRegionLockoutInfo(); 71 void ParseRegionLockoutInfo();
234 72
235 bool is_exefs_loaded = false; 73 FileSys::NCCHContainer base_ncch;
236 bool is_compressed = false; 74 FileSys::NCCHContainer update_ncch;
237 75 FileSys::NCCHContainer* overlay_ncch;
238 u32 entry_point = 0;
239 u32 code_size = 0;
240 u32 stack_size = 0;
241 u32 bss_size = 0;
242 u32 core_version = 0;
243 u8 priority = 0;
244 u8 resource_limit_category = 0;
245 u32 ncch_offset = 0; // Offset to NCCH header, can be 0 or after NCSD header
246 u32 exefs_offset = 0;
247
248 NCCH_Header ncch_header;
249 ExeFs_Header exefs_header;
250 ExHeader_Header exheader_header;
251 76
252 std::string filepath; 77 std::string filepath;
253}; 78};
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 753e7e08b..24c2c55a9 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -132,7 +132,7 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) {
132 return true; 132 return true;
133} 133}
134 134
135ResultStatus AppLoader_NRO::Load() { 135ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
136 if (is_loaded) { 136 if (is_loaded) {
137 return ResultStatus::ErrorAlreadyLoaded; 137 return ResultStatus::ErrorAlreadyLoaded;
138 } 138 }
@@ -142,16 +142,16 @@ ResultStatus AppLoader_NRO::Load() {
142 142
143 // Load and relocate "main" and "sdk" NSO 143 // Load and relocate "main" and "sdk" NSO
144 static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; 144 static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR};
145 Kernel::g_current_process = Kernel::Process::Create("main"); 145 process = Kernel::Process::Create("main");
146 if (!LoadNro(filepath, base_addr)) { 146 if (!LoadNro(filepath, base_addr)) {
147 return ResultStatus::ErrorInvalidFormat; 147 return ResultStatus::ErrorInvalidFormat;
148 } 148 }
149 149
150 Kernel::g_current_process->svc_access_mask.set(); 150 process->svc_access_mask.set();
151 Kernel::g_current_process->address_mappings = default_address_mappings; 151 process->address_mappings = default_address_mappings;
152 Kernel::g_current_process->resource_limit = 152 process->resource_limit =
153 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); 153 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
154 Kernel::g_current_process->Run(base_addr, 48, Kernel::DEFAULT_STACK_SIZE); 154 process->Run(base_addr, 48, Kernel::DEFAULT_STACK_SIZE);
155 155
156 ResolveImports(); 156 ResolveImports();
157 157
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index c3c7622fd..c85768c5b 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -31,7 +31,7 @@ public:
31 return IdentifyType(file); 31 return IdentifyType(file);
32 } 32 }
33 33
34 ResultStatus Load() override; 34 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
35 35
36private: 36private:
37 bool LoadNro(const std::string& path, VAddr load_base); 37 bool LoadNro(const std::string& path, VAddr load_base);
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index ac8d12ecc..b1b57d0c0 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -144,7 +144,7 @@ VAddr AppLoader_NSO::LoadNso(const std::string& path, VAddr load_base, bool relo
144 return load_base + image_size; 144 return load_base + image_size;
145} 145}
146 146
147ResultStatus AppLoader_NSO::Load() { 147ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
148 if (is_loaded) { 148 if (is_loaded) {
149 return ResultStatus::ErrorAlreadyLoaded; 149 return ResultStatus::ErrorAlreadyLoaded;
150 } 150 }
@@ -154,7 +154,7 @@ ResultStatus AppLoader_NSO::Load() {
154 154
155 // Load and relocate "rtld" NSO 155 // Load and relocate "rtld" NSO
156 static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; 156 static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR};
157 Kernel::g_current_process = Kernel::Process::Create("main"); 157 process = Kernel::Process::Create("main");
158 VAddr next_base_addr{LoadNso(filepath, base_addr)}; 158 VAddr next_base_addr{LoadNso(filepath, base_addr)};
159 if (!next_base_addr) { 159 if (!next_base_addr) {
160 return ResultStatus::ErrorInvalidFormat; 160 return ResultStatus::ErrorInvalidFormat;
@@ -170,11 +170,11 @@ ResultStatus AppLoader_NSO::Load() {
170 } 170 }
171 } 171 }
172 172
173 Kernel::g_current_process->svc_access_mask.set(); 173 process->svc_access_mask.set();
174 Kernel::g_current_process->address_mappings = default_address_mappings; 174 process->address_mappings = default_address_mappings;
175 Kernel::g_current_process->resource_limit = 175 process->resource_limit =
176 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); 176 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
177 Kernel::g_current_process->Run(base_addr, 48, Kernel::DEFAULT_STACK_SIZE); 177 process->Run(base_addr, 48, Kernel::DEFAULT_STACK_SIZE);
178 178
179 ResolveImports(); 179 ResolveImports();
180 180
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index c29803d81..b6b86c209 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -32,7 +32,7 @@ public:
32 return IdentifyType(file); 32 return IdentifyType(file);
33 } 33 }
34 34
35 ResultStatus Load() override; 35 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
36 36
37private: 37private:
38 VAddr LoadNso(const std::string& path, VAddr load_base, bool relocate = false); 38 VAddr LoadNso(const std::string& path, VAddr load_base, bool relocate = false);
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index ebe16b030..462d68386 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -4,95 +4,53 @@
4 4
5#include <array> 5#include <array>
6#include <cstring> 6#include <cstring>
7#include "audio_core/audio_core.h"
7#include "common/assert.h" 8#include "common/assert.h"
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "common/logging/log.h" 10#include "common/logging/log.h"
10#include "common/swap.h" 11#include "common/swap.h"
12#include "core/arm/arm_interface.h"
13#include "core/core.h"
14#include "core/hle/kernel/memory.h"
11#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
16#include "core/hle/lock.h"
12#include "core/memory.h" 17#include "core/memory.h"
13#include "core/memory_setup.h" 18#include "core/memory_setup.h"
14#include "core/mmio.h"
15#include "video_core/renderer_base.h" 19#include "video_core/renderer_base.h"
16#include "video_core/video_core.h" 20#include "video_core/video_core.h"
17 21
18namespace Memory { 22namespace Memory {
19 23
20enum class PageType { 24static std::array<u8, Memory::VRAM_SIZE> vram;
21 /// Page is unmapped and should cause an access error. 25static std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram;
22 Unmapped,
23 /// Page is mapped to regular memory. This is the only type you can get pointers to.
24 Memory,
25 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
26 /// invalidation
27 RasterizerCachedMemory,
28 /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
29 Special,
30 /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and
31 /// invalidation
32 RasterizerCachedSpecial,
33};
34
35struct SpecialRegion {
36 VAddr base;
37 u64 size;
38 MMIORegionPointer handler;
39};
40 26
41/** 27static PageTable* current_page_table = nullptr;
42 * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely 28
43 * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and 29void SetCurrentPageTable(PageTable* page_table) {
44 * fetching requirements when accessing. In the usual case of an access to regular memory, it only 30 current_page_table = page_table;
45 * requires an indexed fetch and a check for NULL. 31 if (Core::System::GetInstance().IsPoweredOn()) {
46 */ 32 Core::CPU().PageTableChanged();
47struct PageTable { 33 }
48 /** 34}
49 * Array of memory pointers backing each page. An entry can only be non-null if the 35
50 * corresponding entry in the `attributes` array is of type `Memory`. 36PageTable* GetCurrentPageTable() {
51 */ 37 return current_page_table;
52 std::map<u64, u8*> pointers; 38}
53 39
54 /** 40static void MapPages(PageTable& page_table, VAddr base, u32 size, u8* memory, PageType type) {
55 * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
56 * type `Special`.
57 */
58 std::vector<SpecialRegion> special_regions;
59
60 /**
61 * Array of fine grained page attributes. If it is set to any value other than `Memory`, then
62 * the corresponding entry in `pointers` MUST be set to null.
63 */
64 std::map<u64, PageType> attributes;
65
66 /**
67 * Indicates the number of externally cached resources touching a page that should be
68 * flushed before the memory is accessed
69 */
70 std::map<u64, u8> cached_res_count;
71};
72
73/// Singular page table used for the singleton process
74static PageTable main_page_table;
75/// Currently active page table
76static PageTable* current_page_table = &main_page_table;
77
78//std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() {
79// return &current_page_table->pointers;
80//}
81
82static void MapPages(u64 base, u64 size, u8* memory, PageType type) {
83 LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, 41 LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE,
84 (base + size) * PAGE_SIZE); 42 (base + size) * PAGE_SIZE);
85 43
86 RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE, 44 RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE,
87 FlushMode::FlushAndInvalidate); 45 FlushMode::FlushAndInvalidate);
88 46
89 u64 end = base + size; 47 VAddr end = base + size;
90 while (base != end) { 48 while (base != end) {
91 ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base); 49 ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base);
92 50
93 current_page_table->attributes[base] = type; 51 page_table.attributes[base] = type;
94 current_page_table->pointers[base] = memory; 52 page_table.pointers[base] = memory;
95 current_page_table->cached_res_count[base] = 0; 53 page_table.cached_res_count[base] = 0;
96 54
97 base += 1; 55 base += 1;
98 if (memory != nullptr) 56 if (memory != nullptr)
@@ -100,40 +58,34 @@ static void MapPages(u64 base, u64 size, u8* memory, PageType type) {
100 } 58 }
101} 59}
102 60
103void InitMemoryMap() { 61void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target) {
104 //main_page_table.pointers.fill(nullptr);
105 //main_page_table.attributes.fill(PageType::Unmapped);
106 //main_page_table.cached_res_count.fill(0);
107}
108
109void MapMemoryRegion(VAddr base, u64 size, u8* target) {
110 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); 62 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
111 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); 63 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
112 MapPages(base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); 64 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
113} 65}
114 66
115void MapIoRegion(VAddr base, u64 size, MMIORegionPointer mmio_handler) { 67void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler) {
116 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); 68 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
117 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); 69 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
118 MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); 70 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
119 71
120 current_page_table->special_regions.emplace_back(SpecialRegion{base, size, mmio_handler}); 72 page_table.special_regions.emplace_back(SpecialRegion{base, size, mmio_handler});
121} 73}
122 74
123void UnmapRegion(VAddr base, u64 size) { 75void UnmapRegion(PageTable& page_table, VAddr base, u32 size) {
124 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); 76 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
125 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); 77 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
126 MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); 78 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
127} 79}
128 80
129/** 81/**
130 * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) 82 * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
131 * using a VMA from the current process 83 * using a VMA from the current process
132 */ 84 */
133static u8* GetPointerFromVMA(VAddr vaddr) { 85static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) {
134 u8* direct_pointer = nullptr; 86 u8* direct_pointer = nullptr;
135 87
136 auto& vm_manager = Kernel::g_current_process->vm_manager; 88 auto& vm_manager = process.vm_manager;
137 89
138 auto it = vm_manager.FindVMA(vaddr); 90 auto it = vm_manager.FindVMA(vaddr);
139 ASSERT(it != vm_manager.vma_map.end()); 91 ASSERT(it != vm_manager.vma_map.end());
@@ -156,10 +108,18 @@ static u8* GetPointerFromVMA(VAddr vaddr) {
156} 108}
157 109
158/** 110/**
111 * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
112 * using a VMA from the current process.
113 */
114static u8* GetPointerFromVMA(VAddr vaddr) {
115 return GetPointerFromVMA(*Kernel::g_current_process, vaddr);
116}
117
118/**
159 * This function should only be called for virtual addreses with attribute `PageType::Special`. 119 * This function should only be called for virtual addreses with attribute `PageType::Special`.
160 */ 120 */
161static MMIORegionPointer GetMMIOHandler(VAddr vaddr) { 121static MMIORegionPointer GetMMIOHandler(const PageTable& page_table, VAddr vaddr) {
162 for (const auto& region : current_page_table->special_regions) { 122 for (const auto& region : page_table.special_regions) {
163 if (vaddr >= region.base && vaddr < (region.base + region.size)) { 123 if (vaddr >= region.base && vaddr < (region.base + region.size)) {
164 return region.handler; 124 return region.handler;
165 } 125 }
@@ -168,6 +128,11 @@ static MMIORegionPointer GetMMIOHandler(VAddr vaddr) {
168 return nullptr; // Should never happen 128 return nullptr; // Should never happen
169} 129}
170 130
131static MMIORegionPointer GetMMIOHandler(VAddr vaddr) {
132 const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
133 return GetMMIOHandler(page_table, vaddr);
134}
135
171template <typename T> 136template <typename T>
172T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr); 137T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr);
173 138
@@ -181,10 +146,13 @@ T Read(const VAddr vaddr) {
181 return value; 146 return value;
182 } 147 }
183 148
149 // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
150 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
151
184 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 152 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
185 switch (type) { 153 switch (type) {
186 case PageType::Unmapped: 154 case PageType::Unmapped:
187 LOG_ERROR(HW_Memory, "unmapped Read%lu @ 0x%llx", sizeof(T) * 8, vaddr); 155 LOG_ERROR(HW_Memory, "unmapped Read%lu @ 0x%08X", sizeof(T) * 8, vaddr);
188 return 0; 156 return 0;
189 case PageType::Memory: 157 case PageType::Memory:
190 ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); 158 ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr);
@@ -219,10 +187,13 @@ void Write(const VAddr vaddr, const T data) {
219 return; 187 return;
220 } 188 }
221 189
190 // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
191 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
192
222 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 193 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
223 switch (type) { 194 switch (type) {
224 case PageType::Unmapped: 195 case PageType::Unmapped:
225 LOG_ERROR(HW_Memory, "unmapped Write%lu 0x%llx @ 0x%llx", sizeof(data) * 8, (u64)data, 196 LOG_ERROR(HW_Memory, "unmapped Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data,
226 vaddr); 197 vaddr);
227 return; 198 return;
228 case PageType::Memory: 199 case PageType::Memory:
@@ -246,18 +217,20 @@ void Write(const VAddr vaddr, const T data) {
246 } 217 }
247} 218}
248 219
249bool IsValidVirtualAddress(const VAddr vaddr) { 220bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
250 const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; 221 auto& page_table = process.vm_manager.page_table;
222
223 const u8* page_pointer = page_table.pointers[vaddr >> PAGE_BITS];
251 if (page_pointer) 224 if (page_pointer)
252 return true; 225 return true;
253 226
254 if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) 227 if (page_table.attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory)
255 return true; 228 return true;
256 229
257 if (current_page_table->attributes[vaddr >> PAGE_BITS] != PageType::Special) 230 if (page_table.attributes[vaddr >> PAGE_BITS] != PageType::Special)
258 return false; 231 return false;
259 232
260 MMIORegionPointer mmio_region = GetMMIOHandler(vaddr); 233 MMIORegionPointer mmio_region = GetMMIOHandler(page_table, vaddr);
261 if (mmio_region) { 234 if (mmio_region) {
262 return mmio_region->IsValidAddress(vaddr); 235 return mmio_region->IsValidAddress(vaddr);
263 } 236 }
@@ -265,9 +238,12 @@ bool IsValidVirtualAddress(const VAddr vaddr) {
265 return false; 238 return false;
266} 239}
267 240
241bool IsValidVirtualAddress(const VAddr vaddr) {
242 return IsValidVirtualAddress(*Kernel::g_current_process, vaddr);
243}
244
268bool IsValidPhysicalAddress(const PAddr paddr) { 245bool IsValidPhysicalAddress(const PAddr paddr) {
269 boost::optional<VAddr> vaddr = PhysicalToVirtualAddress(paddr); 246 return GetPhysicalPointer(paddr) != nullptr;
270 return vaddr && IsValidVirtualAddress(*vaddr);
271} 247}
272 248
273u8* GetPointer(const VAddr vaddr) { 249u8* GetPointer(const VAddr vaddr) {
@@ -280,7 +256,7 @@ u8* GetPointer(const VAddr vaddr) {
280 return GetPointerFromVMA(vaddr); 256 return GetPointerFromVMA(vaddr);
281 } 257 }
282 258
283 LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%llx", vaddr); 259 LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%08x", vaddr);
284 return nullptr; 260 return nullptr;
285} 261}
286 262
@@ -299,12 +275,66 @@ std::string ReadCString(VAddr vaddr, std::size_t max_length) {
299} 275}
300 276
301u8* GetPhysicalPointer(PAddr address) { 277u8* GetPhysicalPointer(PAddr address) {
302 // TODO(Subv): This call should not go through the application's memory mapping. 278 struct MemoryArea {
303 boost::optional<VAddr> vaddr = PhysicalToVirtualAddress(address); 279 PAddr paddr_base;
304 return vaddr ? GetPointer(*vaddr) : nullptr; 280 u32 size;
281 };
282
283 static constexpr MemoryArea memory_areas[] = {
284 {VRAM_PADDR, VRAM_SIZE},
285 {IO_AREA_PADDR, IO_AREA_SIZE},
286 {DSP_RAM_PADDR, DSP_RAM_SIZE},
287 {FCRAM_PADDR, FCRAM_N3DS_SIZE},
288 {N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE},
289 };
290
291 const auto area =
292 std::find_if(std::begin(memory_areas), std::end(memory_areas), [&](const auto& area) {
293 return address >= area.paddr_base && address < area.paddr_base + area.size;
294 });
295
296 if (area == std::end(memory_areas)) {
297 LOG_ERROR(HW_Memory, "unknown GetPhysicalPointer @ 0x%08X", address);
298 return nullptr;
299 }
300
301 if (area->paddr_base == IO_AREA_PADDR) {
302 LOG_ERROR(HW_Memory, "MMIO mappings are not supported yet. phys_addr=0x%08X", address);
303 return nullptr;
304 }
305
306 u64 offset_into_region = address - area->paddr_base;
307
308 u8* target_pointer = nullptr;
309 switch (area->paddr_base) {
310 case VRAM_PADDR:
311 target_pointer = vram.data() + offset_into_region;
312 break;
313 case DSP_RAM_PADDR:
314 target_pointer = AudioCore::GetDspMemory().data() + offset_into_region;
315 break;
316 case FCRAM_PADDR:
317 for (const auto& region : Kernel::memory_regions) {
318 if (offset_into_region >= region.base &&
319 offset_into_region < region.base + region.size) {
320 target_pointer =
321 region.linear_heap_memory->data() + offset_into_region - region.base;
322 break;
323 }
324 }
325 ASSERT_MSG(target_pointer != nullptr, "Invalid FCRAM address");
326 break;
327 case N3DS_EXTRA_RAM_PADDR:
328 target_pointer = n3ds_extra_ram.data() + offset_into_region;
329 break;
330 default:
331 UNREACHABLE();
332 }
333
334 return target_pointer;
305} 335}
306 336
307void RasterizerMarkRegionCached(PAddr start, u64 size, int count_delta) { 337void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) {
308 if (start == 0) { 338 if (start == 0) {
309 return; 339 return;
310 } 340 }
@@ -314,8 +344,15 @@ void RasterizerMarkRegionCached(PAddr start, u64 size, int count_delta) {
314 344
315 for (unsigned i = 0; i < num_pages; ++i, paddr += PAGE_SIZE) { 345 for (unsigned i = 0; i < num_pages; ++i, paddr += PAGE_SIZE) {
316 boost::optional<VAddr> maybe_vaddr = PhysicalToVirtualAddress(paddr); 346 boost::optional<VAddr> maybe_vaddr = PhysicalToVirtualAddress(paddr);
317 if (!maybe_vaddr) 347 // While the physical <-> virtual mapping is 1:1 for the regions supported by the cache,
348 // some games (like Pokemon Super Mystery Dungeon) will try to use textures that go beyond
349 // the end address of VRAM, causing the Virtual->Physical translation to fail when flushing
350 // parts of the texture.
351 if (!maybe_vaddr) {
352 LOG_ERROR(HW_Memory,
353 "Trying to flush a cached region to an invalid physical address %08X", paddr);
318 continue; 354 continue;
355 }
319 VAddr vaddr = *maybe_vaddr; 356 VAddr vaddr = *maybe_vaddr;
320 357
321 u8& res_count = current_page_table->cached_res_count[vaddr >> PAGE_BITS]; 358 u8& res_count = current_page_table->cached_res_count[vaddr >> PAGE_BITS];
@@ -327,6 +364,10 @@ void RasterizerMarkRegionCached(PAddr start, u64 size, int count_delta) {
327 if (res_count == 0) { 364 if (res_count == 0) {
328 PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; 365 PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
329 switch (page_type) { 366 switch (page_type) {
367 case PageType::Unmapped:
368 // It is not necessary for a process to have this region mapped into its address
369 // space, for example, a system module need not have a VRAM mapping.
370 break;
330 case PageType::Memory: 371 case PageType::Memory:
331 page_type = PageType::RasterizerCachedMemory; 372 page_type = PageType::RasterizerCachedMemory;
332 current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; 373 current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
@@ -345,6 +386,10 @@ void RasterizerMarkRegionCached(PAddr start, u64 size, int count_delta) {
345 if (res_count == 0) { 386 if (res_count == 0) {
346 PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; 387 PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
347 switch (page_type) { 388 switch (page_type) {
389 case PageType::Unmapped:
390 // It is not necessary for a process to have this region mapped into its address
391 // space, for example, a system module need not have a VRAM mapping.
392 break;
348 case PageType::RasterizerCachedMemory: { 393 case PageType::RasterizerCachedMemory: {
349 u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK); 394 u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
350 if (pointer == nullptr) { 395 if (pointer == nullptr) {
@@ -368,13 +413,13 @@ void RasterizerMarkRegionCached(PAddr start, u64 size, int count_delta) {
368 } 413 }
369} 414}
370 415
371void RasterizerFlushRegion(PAddr start, u64 size) { 416void RasterizerFlushRegion(PAddr start, u32 size) {
372 if (VideoCore::g_renderer != nullptr) { 417 if (VideoCore::g_renderer != nullptr) {
373 VideoCore::g_renderer->Rasterizer()->FlushRegion(start, size); 418 VideoCore::g_renderer->Rasterizer()->FlushRegion(start, size);
374 } 419 }
375} 420}
376 421
377void RasterizerFlushAndInvalidateRegion(PAddr start, u64 size) { 422void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size) {
378 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be 423 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
379 // null here 424 // null here
380 if (VideoCore::g_renderer != nullptr) { 425 if (VideoCore::g_renderer != nullptr) {
@@ -382,7 +427,7 @@ void RasterizerFlushAndInvalidateRegion(PAddr start, u64 size) {
382 } 427 }
383} 428}
384 429
385void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) { 430void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode) {
386 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be 431 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
387 // null here 432 // null here
388 if (VideoCore::g_renderer != nullptr) { 433 if (VideoCore::g_renderer != nullptr) {
@@ -398,7 +443,7 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
398 VAddr overlap_end = std::min(end, region_end); 443 VAddr overlap_end = std::min(end, region_end);
399 444
400 PAddr physical_start = TryVirtualToPhysicalAddress(overlap_start).value(); 445 PAddr physical_start = TryVirtualToPhysicalAddress(overlap_start).value();
401 u64 overlap_size = overlap_end - overlap_start; 446 u32 overlap_size = static_cast<u32>(overlap_end - overlap_start);
402 447
403 auto* rasterizer = VideoCore::g_renderer->Rasterizer(); 448 auto* rasterizer = VideoCore::g_renderer->Rasterizer();
404 switch (mode) { 449 switch (mode) {
@@ -433,44 +478,50 @@ u64 Read64(const VAddr addr) {
433 return Read<u64_le>(addr); 478 return Read<u64_le>(addr);
434} 479}
435 480
436void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) { 481void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
482 const size_t size) {
483 auto& page_table = process.vm_manager.page_table;
484
437 size_t remaining_size = size; 485 size_t remaining_size = size;
438 size_t page_index = src_addr >> PAGE_BITS; 486 size_t page_index = src_addr >> PAGE_BITS;
439 size_t page_offset = src_addr & PAGE_MASK; 487 size_t page_offset = src_addr & PAGE_MASK;
440 488
441 while (remaining_size > 0) { 489 while (remaining_size > 0) {
442 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); 490 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
443 const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset; 491 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
444 492
445 switch (current_page_table->attributes[page_index]) { 493 switch (page_table.attributes[page_index]) {
446 case PageType::Unmapped: { 494 case PageType::Unmapped: {
447 LOG_ERROR(HW_Memory, "unmapped ReadBlock @ 0x%llx (start address = 0x%llx, size = %zu)", 495 LOG_ERROR(HW_Memory, "unmapped ReadBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
448 current_vaddr, src_addr, size); 496 current_vaddr, src_addr, size);
449 std::memset(dest_buffer, 0, copy_amount); 497 std::memset(dest_buffer, 0, copy_amount);
450 break; 498 break;
451 } 499 }
452 case PageType::Memory: { 500 case PageType::Memory: {
453 DEBUG_ASSERT(current_page_table->pointers[page_index]); 501 DEBUG_ASSERT(page_table.pointers[page_index]);
454 502
455 const u8* src_ptr = current_page_table->pointers[page_index] + page_offset; 503 const u8* src_ptr = page_table.pointers[page_index] + page_offset;
456 std::memcpy(dest_buffer, src_ptr, copy_amount); 504 std::memcpy(dest_buffer, src_ptr, copy_amount);
457 break; 505 break;
458 } 506 }
459 case PageType::Special: { 507 case PageType::Special: {
460 DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); 508 MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
461 509 DEBUG_ASSERT(handler);
462 GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, dest_buffer, copy_amount); 510 handler->ReadBlock(current_vaddr, dest_buffer, copy_amount);
463 break; 511 break;
464 } 512 }
465 case PageType::RasterizerCachedMemory: { 513 case PageType::RasterizerCachedMemory: {
466 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Flush); 514 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
467 std::memcpy(dest_buffer, GetPointerFromVMA(current_vaddr), copy_amount); 515 FlushMode::Flush);
516 std::memcpy(dest_buffer, GetPointerFromVMA(process, current_vaddr), copy_amount);
468 break; 517 break;
469 } 518 }
470 case PageType::RasterizerCachedSpecial: { 519 case PageType::RasterizerCachedSpecial: {
471 DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); 520 MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
472 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Flush); 521 DEBUG_ASSERT(handler);
473 GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, dest_buffer, copy_amount); 522 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
523 FlushMode::Flush);
524 handler->ReadBlock(current_vaddr, dest_buffer, copy_amount);
474 break; 525 break;
475 } 526 }
476 default: 527 default:
@@ -484,6 +535,10 @@ void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) {
484 } 535 }
485} 536}
486 537
538void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) {
539 ReadBlock(*Kernel::g_current_process, src_addr, dest_buffer, size);
540}
541
487void Write8(const VAddr addr, const u8 data) { 542void Write8(const VAddr addr, const u8 data) {
488 Write<u8>(addr, data); 543 Write<u8>(addr, data);
489} 544}
@@ -500,44 +555,49 @@ void Write64(const VAddr addr, const u64 data) {
500 Write<u64_le>(addr, data); 555 Write<u64_le>(addr, data);
501} 556}
502 557
503void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) { 558void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
559 const size_t size) {
560 auto& page_table = process.vm_manager.page_table;
504 size_t remaining_size = size; 561 size_t remaining_size = size;
505 size_t page_index = dest_addr >> PAGE_BITS; 562 size_t page_index = dest_addr >> PAGE_BITS;
506 size_t page_offset = dest_addr & PAGE_MASK; 563 size_t page_offset = dest_addr & PAGE_MASK;
507 564
508 while (remaining_size > 0) { 565 while (remaining_size > 0) {
509 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); 566 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
510 const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset; 567 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
511 568
512 switch (current_page_table->attributes[page_index]) { 569 switch (page_table.attributes[page_index]) {
513 case PageType::Unmapped: { 570 case PageType::Unmapped: {
514 LOG_ERROR(HW_Memory, 571 LOG_ERROR(HW_Memory,
515 "unmapped WriteBlock @ 0x%llx (start address = 0x%llx, size = %zu)", 572 "unmapped WriteBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
516 current_vaddr, dest_addr, size); 573 current_vaddr, dest_addr, size);
517 break; 574 break;
518 } 575 }
519 case PageType::Memory: { 576 case PageType::Memory: {
520 DEBUG_ASSERT(current_page_table->pointers[page_index]); 577 DEBUG_ASSERT(page_table.pointers[page_index]);
521 578
522 u8* dest_ptr = current_page_table->pointers[page_index] + page_offset; 579 u8* dest_ptr = page_table.pointers[page_index] + page_offset;
523 std::memcpy(dest_ptr, src_buffer, copy_amount); 580 std::memcpy(dest_ptr, src_buffer, copy_amount);
524 break; 581 break;
525 } 582 }
526 case PageType::Special: { 583 case PageType::Special: {
527 DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); 584 MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
528 585 DEBUG_ASSERT(handler);
529 GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, src_buffer, copy_amount); 586 handler->WriteBlock(current_vaddr, src_buffer, copy_amount);
530 break; 587 break;
531 } 588 }
532 case PageType::RasterizerCachedMemory: { 589 case PageType::RasterizerCachedMemory: {
533 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate); 590 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
534 std::memcpy(GetPointerFromVMA(current_vaddr), src_buffer, copy_amount); 591 FlushMode::FlushAndInvalidate);
592 std::memcpy(GetPointerFromVMA(process, current_vaddr), src_buffer, copy_amount);
535 break; 593 break;
536 } 594 }
537 case PageType::RasterizerCachedSpecial: { 595 case PageType::RasterizerCachedSpecial: {
538 DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); 596 MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
539 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate); 597 DEBUG_ASSERT(handler);
540 GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, src_buffer, copy_amount); 598 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
599 FlushMode::FlushAndInvalidate);
600 handler->WriteBlock(current_vaddr, src_buffer, copy_amount);
541 break; 601 break;
542 } 602 }
543 default: 603 default:
@@ -551,6 +611,10 @@ void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size
551 } 611 }
552} 612}
553 613
614void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) {
615 WriteBlock(*Kernel::g_current_process, dest_addr, src_buffer, size);
616}
617
554void ZeroBlock(const VAddr dest_addr, const size_t size) { 618void ZeroBlock(const VAddr dest_addr, const size_t size) {
555 size_t remaining_size = size; 619 size_t remaining_size = size;
556 size_t page_index = dest_addr >> PAGE_BITS; 620 size_t page_index = dest_addr >> PAGE_BITS;
@@ -560,11 +624,11 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) {
560 624
561 while (remaining_size > 0) { 625 while (remaining_size > 0) {
562 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); 626 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
563 const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset; 627 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
564 628
565 switch (current_page_table->attributes[page_index]) { 629 switch (current_page_table->attributes[page_index]) {
566 case PageType::Unmapped: { 630 case PageType::Unmapped: {
567 LOG_ERROR(HW_Memory, "unmapped ZeroBlock @ 0x%llx (start address = 0x%llx, size = %zu)", 631 LOG_ERROR(HW_Memory, "unmapped ZeroBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
568 current_vaddr, dest_addr, size); 632 current_vaddr, dest_addr, size);
569 break; 633 break;
570 } 634 }
@@ -582,13 +646,15 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) {
582 break; 646 break;
583 } 647 }
584 case PageType::RasterizerCachedMemory: { 648 case PageType::RasterizerCachedMemory: {
585 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate); 649 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
650 FlushMode::FlushAndInvalidate);
586 std::memset(GetPointerFromVMA(current_vaddr), 0, copy_amount); 651 std::memset(GetPointerFromVMA(current_vaddr), 0, copy_amount);
587 break; 652 break;
588 } 653 }
589 case PageType::RasterizerCachedSpecial: { 654 case PageType::RasterizerCachedSpecial: {
590 DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); 655 DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
591 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate); 656 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
657 FlushMode::FlushAndInvalidate);
592 GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount); 658 GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount);
593 break; 659 break;
594 } 660 }
@@ -609,11 +675,11 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
609 675
610 while (remaining_size > 0) { 676 while (remaining_size > 0) {
611 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); 677 const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
612 const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset; 678 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
613 679
614 switch (current_page_table->attributes[page_index]) { 680 switch (current_page_table->attributes[page_index]) {
615 case PageType::Unmapped: { 681 case PageType::Unmapped: {
616 LOG_ERROR(HW_Memory, "unmapped CopyBlock @ 0x%llx (start address = 0x%llx, size = %zu)", 682 LOG_ERROR(HW_Memory, "unmapped CopyBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
617 current_vaddr, src_addr, size); 683 current_vaddr, src_addr, size);
618 ZeroBlock(dest_addr, copy_amount); 684 ZeroBlock(dest_addr, copy_amount);
619 break; 685 break;
@@ -633,13 +699,15 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
633 break; 699 break;
634 } 700 }
635 case PageType::RasterizerCachedMemory: { 701 case PageType::RasterizerCachedMemory: {
636 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Flush); 702 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
703 FlushMode::Flush);
637 WriteBlock(dest_addr, GetPointerFromVMA(current_vaddr), copy_amount); 704 WriteBlock(dest_addr, GetPointerFromVMA(current_vaddr), copy_amount);
638 break; 705 break;
639 } 706 }
640 case PageType::RasterizerCachedSpecial: { 707 case PageType::RasterizerCachedSpecial: {
641 DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); 708 DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
642 RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Flush); 709 RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
710 FlushMode::Flush);
643 711
644 std::vector<u8> buffer(copy_amount); 712 std::vector<u8> buffer(copy_amount);
645 GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, buffer.data(), buffer.size()); 713 GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, buffer.data(), buffer.size());
@@ -652,8 +720,8 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
652 720
653 page_index++; 721 page_index++;
654 page_offset = 0; 722 page_offset = 0;
655 dest_addr += copy_amount; 723 dest_addr += static_cast<VAddr>(copy_amount);
656 src_addr += copy_amount; 724 src_addr += static_cast<VAddr>(copy_amount);
657 remaining_size -= copy_amount; 725 remaining_size -= copy_amount;
658 } 726 }
659} 727}
@@ -721,7 +789,7 @@ boost::optional<PAddr> TryVirtualToPhysicalAddress(const VAddr addr) {
721PAddr VirtualToPhysicalAddress(const VAddr addr) { 789PAddr VirtualToPhysicalAddress(const VAddr addr) {
722 auto paddr = TryVirtualToPhysicalAddress(addr); 790 auto paddr = TryVirtualToPhysicalAddress(addr);
723 if (!paddr) { 791 if (!paddr) {
724 LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%llx", addr); 792 LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%08X", addr);
725 // To help with debugging, set bit on address so that it's obviously invalid. 793 // To help with debugging, set bit on address so that it's obviously invalid.
726 return addr | 0x80000000; 794 return addr | 0x80000000;
727 } 795 }
@@ -746,4 +814,4 @@ boost::optional<VAddr> PhysicalToVirtualAddress(const PAddr addr) {
746 return boost::none; 814 return boost::none;
747} 815}
748 816
749} // namespace 817} // namespace Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index e14d68654..9a04b9a16 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -6,9 +6,16 @@
6 6
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <map>
9#include <string> 10#include <string>
11#include <vector>
10#include <boost/optional.hpp> 12#include <boost/optional.hpp>
11#include "common/common_types.h" 13#include "common/common_types.h"
14#include "core/mmio.h"
15
16namespace Kernel {
17class Process;
18}
12 19
13namespace Memory { 20namespace Memory {
14 21
@@ -19,7 +26,60 @@ namespace Memory {
19const int PAGE_BITS = 12; 26const int PAGE_BITS = 12;
20const u64 PAGE_SIZE = 1 << PAGE_BITS; 27const u64 PAGE_SIZE = 1 << PAGE_BITS;
21const u64 PAGE_MASK = PAGE_SIZE - 1; 28const u64 PAGE_MASK = PAGE_SIZE - 1;
22const size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (64 - PAGE_BITS); 29const size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (32 - PAGE_BITS);
30
31enum class PageType {
32 /// Page is unmapped and should cause an access error.
33 Unmapped,
34 /// Page is mapped to regular memory. This is the only type you can get pointers to.
35 Memory,
36 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
37 /// invalidation
38 RasterizerCachedMemory,
39 /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
40 Special,
41 /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and
42 /// invalidation
43 RasterizerCachedSpecial,
44};
45
46struct SpecialRegion {
47 VAddr base;
48 u32 size;
49 MMIORegionPointer handler;
50};
51
52/**
53 * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
54 * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and
55 * fetching requirements when accessing. In the usual case of an access to regular memory, it only
56 * requires an indexed fetch and a check for NULL.
57 */
58struct PageTable {
59 /**
60 * Array of memory pointers backing each page. An entry can only be non-null if the
61 * corresponding entry in the `attributes` array is of type `Memory`.
62 */
63 std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers;
64
65 /**
66 * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
67 * type `Special`.
68 */
69 std::vector<SpecialRegion> special_regions;
70
71 /**
72 * Array of fine grained page attributes. If it is set to any value other than `Memory`, then
73 * the corresponding entry in `pointers` MUST be set to null.
74 */
75 std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
76
77 /**
78 * Indicates the number of externally cached resources touching a page that should be
79 * flushed before the memory is accessed
80 */
81 std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count;
82};
23 83
24/// Physical memory regions as seen from the ARM11 84/// Physical memory regions as seen from the ARM11
25enum : PAddr { 85enum : PAddr {
@@ -126,7 +186,14 @@ enum : VAddr {
126 NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE, 186 NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE,
127}; 187};
128 188
189/// Currently active page table
190void SetCurrentPageTable(PageTable* page_table);
191PageTable* GetCurrentPageTable();
192
193/// Determines if the given VAddr is valid for the specified process.
194bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr);
129bool IsValidVirtualAddress(const VAddr addr); 195bool IsValidVirtualAddress(const VAddr addr);
196
130bool IsValidPhysicalAddress(const PAddr addr); 197bool IsValidPhysicalAddress(const PAddr addr);
131 198
132u8 Read8(VAddr addr); 199u8 Read8(VAddr addr);
@@ -139,7 +206,11 @@ void Write16(VAddr addr, u16 data);
139void Write32(VAddr addr, u32 data); 206void Write32(VAddr addr, u32 data);
140void Write64(VAddr addr, u64 data); 207void Write64(VAddr addr, u64 data);
141 208
209void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
210 size_t size);
142void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size); 211void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size);
212void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
213 size_t size);
143void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size); 214void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size);
144void ZeroBlock(const VAddr dest_addr, const size_t size); 215void ZeroBlock(const VAddr dest_addr, const size_t size);
145void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); 216void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size);
@@ -169,8 +240,6 @@ boost::optional<VAddr> PhysicalToVirtualAddress(PAddr addr);
169 240
170/** 241/**
171 * Gets a pointer to the memory region beginning at the specified physical address. 242 * Gets a pointer to the memory region beginning at the specified physical address.
172 *
173 * @note This is currently implemented using PhysicalToVirtualAddress().
174 */ 243 */
175u8* GetPhysicalPointer(PAddr address); 244u8* GetPhysicalPointer(PAddr address);
176 245
@@ -178,17 +247,17 @@ u8* GetPhysicalPointer(PAddr address);
178 * Adds the supplied value to the rasterizer resource cache counter of each 247 * Adds the supplied value to the rasterizer resource cache counter of each
179 * page touching the region. 248 * page touching the region.
180 */ 249 */
181void RasterizerMarkRegionCached(PAddr start, u64 size, int count_delta); 250void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta);
182 251
183/** 252/**
184 * Flushes any externally cached rasterizer resources touching the given region. 253 * Flushes any externally cached rasterizer resources touching the given region.
185 */ 254 */
186void RasterizerFlushRegion(PAddr start, u64 size); 255void RasterizerFlushRegion(PAddr start, u32 size);
187 256
188/** 257/**
189 * Flushes and invalidates any externally cached rasterizer resources touching the given region. 258 * Flushes and invalidates any externally cached rasterizer resources touching the given region.
190 */ 259 */
191void RasterizerFlushAndInvalidateRegion(PAddr start, u64 size); 260void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size);
192 261
193enum class FlushMode { 262enum class FlushMode {
194 /// Write back modified surfaces to RAM 263 /// Write back modified surfaces to RAM
@@ -201,12 +270,6 @@ enum class FlushMode {
201 * Flushes and invalidates any externally cached rasterizer resources touching the given virtual 270 * Flushes and invalidates any externally cached rasterizer resources touching the given virtual
202 * address region. 271 * address region.
203 */ 272 */
204void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode); 273void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode);
205 274
206/** 275} // namespace Memory
207 * Dynarmic has an optimization to memory accesses when the pointer to the page exists that
208 * can be used by setting up the current page table as a callback. This function is used to
209 * retrieve the current page table for that purpose.
210 */
211//std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers();
212}
diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h
index fc3fda466..c58baa50b 100644
--- a/src/core/memory_setup.h
+++ b/src/core/memory_setup.h
@@ -9,24 +9,24 @@
9 9
10namespace Memory { 10namespace Memory {
11 11
12void InitMemoryMap();
13
14/** 12/**
15 * Maps an allocated buffer onto a region of the emulated process address space. 13 * Maps an allocated buffer onto a region of the emulated process address space.
16 * 14 *
15 * @param page_table The page table of the emulated process.
17 * @param base The address to start mapping at. Must be page-aligned. 16 * @param base The address to start mapping at. Must be page-aligned.
18 * @param size The amount of bytes to map. Must be page-aligned. 17 * @param size The amount of bytes to map. Must be page-aligned.
19 * @param target Buffer with the memory backing the mapping. Must be of length at least `size`. 18 * @param target Buffer with the memory backing the mapping. Must be of length at least `size`.
20 */ 19 */
21void MapMemoryRegion(VAddr base, u64 size, u8* target); 20void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target);
22 21
23/** 22/**
24 * Maps a region of the emulated process address space as a IO region. 23 * Maps a region of the emulated process address space as a IO region.
24 * @param page_table The page table of the emulated process.
25 * @param base The address to start mapping at. Must be page-aligned. 25 * @param base The address to start mapping at. Must be page-aligned.
26 * @param size The amount of bytes to map. Must be page-aligned. 26 * @param size The amount of bytes to map. Must be page-aligned.
27 * @param mmio_handler The handler that backs the mapping. 27 * @param mmio_handler The handler that backs the mapping.
28 */ 28 */
29void MapIoRegion(VAddr base, u64 size, MMIORegionPointer mmio_handler); 29void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler);
30 30
31void UnmapRegion(VAddr base, u64 size); 31void UnmapRegion(PageTable& page_table, VAddr base, u32 size);
32} 32}
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index d4f0429d1..efcf1267d 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -36,4 +36,4 @@ void Apply() {
36 Service::IR::ReloadInputDevices(); 36 Service::IR::ReloadInputDevices();
37} 37}
38 38
39} // namespace 39} // namespace Settings
diff --git a/src/core/settings.h b/src/core/settings.h
index ee16bb90a..8d78cb424 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -15,6 +15,7 @@ enum class LayoutOption {
15 Default, 15 Default,
16 SingleScreen, 16 SingleScreen,
17 LargeScreen, 17 LargeScreen,
18 SideScreen,
18}; 19};
19 20
20namespace NativeButton { 21namespace NativeButton {
@@ -70,7 +71,7 @@ enum Values {
70static const std::array<const char*, NumAnalogs> mapping = {{ 71static const std::array<const char*, NumAnalogs> mapping = {{
71 "circle_pad", "c_stick", 72 "circle_pad", "c_stick",
72}}; 73}};
73} // namespace NumAnalog 74} // namespace NativeAnalog
74 75
75struct Values { 76struct Values {
76 // CheckNew3DS 77 // CheckNew3DS
@@ -79,6 +80,8 @@ struct Values {
79 // Controls 80 // Controls
80 std::array<std::string, NativeButton::NumButtons> buttons; 81 std::array<std::string, NativeButton::NumButtons> buttons;
81 std::array<std::string, NativeAnalog::NumAnalogs> analogs; 82 std::array<std::string, NativeAnalog::NumAnalogs> analogs;
83 std::string motion_device;
84 std::string touch_device;
82 85
83 // Core 86 // Core
84 bool use_cpu_jit; 87 bool use_cpu_jit;
@@ -128,7 +131,11 @@ struct Values {
128 u16 gdbstub_port; 131 u16 gdbstub_port;
129 132
130 // WebService 133 // WebService
134 bool enable_telemetry;
131 std::string telemetry_endpoint_url; 135 std::string telemetry_endpoint_url;
136 std::string verify_endpoint_url;
137 std::string citra_username;
138 std::string citra_token;
132} extern values; 139} extern values;
133 140
134// a special value for Values::region_value indicating that citra will automatically select a region 141// a special value for Values::region_value indicating that citra will automatically select a region
@@ -136,4 +143,4 @@ struct Values {
136static constexpr int REGION_VALUE_AUTO_SELECT = -1; 143static constexpr int REGION_VALUE_AUTO_SELECT = -1;
137 144
138void Apply(); 145void Apply();
139} 146} // namespace Settings
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 841d6cfa1..ca517ff44 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -3,15 +3,19 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring> 5#include <cstring>
6#include <cryptopp/osrng.h>
6 7
7#include "common/assert.h" 8#include "common/assert.h"
9#include "common/file_util.h"
8#include "common/scm_rev.h" 10#include "common/scm_rev.h"
9#include "common/x64/cpu_detect.h" 11#include "common/x64/cpu_detect.h"
12#include "core/core.h"
10#include "core/settings.h" 13#include "core/settings.h"
11#include "core/telemetry_session.h" 14#include "core/telemetry_session.h"
12 15
13#ifdef ENABLE_WEB_SERVICE 16#ifdef ENABLE_WEB_SERVICE
14#include "web_service/telemetry_json.h" 17#include "web_service/telemetry_json.h"
18#include "web_service/verify_login.h"
15#endif 19#endif
16 20
17namespace Core { 21namespace Core {
@@ -28,23 +32,94 @@ static const char* CpuVendorToStr(Common::CPUVendor vendor) {
28 UNREACHABLE(); 32 UNREACHABLE();
29} 33}
30 34
35static u64 GenerateTelemetryId() {
36 u64 telemetry_id{};
37 CryptoPP::AutoSeededRandomPool rng;
38 rng.GenerateBlock(reinterpret_cast<CryptoPP::byte*>(&telemetry_id), sizeof(u64));
39 return telemetry_id;
40}
41
42u64 GetTelemetryId() {
43 u64 telemetry_id{};
44 static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"};
45
46 if (FileUtil::Exists(filename)) {
47 FileUtil::IOFile file(filename, "rb");
48 if (!file.IsOpen()) {
49 LOG_ERROR(Core, "failed to open telemetry_id: %s", filename.c_str());
50 return {};
51 }
52 file.ReadBytes(&telemetry_id, sizeof(u64));
53 } else {
54 FileUtil::IOFile file(filename, "wb");
55 if (!file.IsOpen()) {
56 LOG_ERROR(Core, "failed to open telemetry_id: %s", filename.c_str());
57 return {};
58 }
59 telemetry_id = GenerateTelemetryId();
60 file.WriteBytes(&telemetry_id, sizeof(u64));
61 }
62
63 return telemetry_id;
64}
65
66u64 RegenerateTelemetryId() {
67 const u64 new_telemetry_id{GenerateTelemetryId()};
68 static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"};
69
70 FileUtil::IOFile file(filename, "wb");
71 if (!file.IsOpen()) {
72 LOG_ERROR(Core, "failed to open telemetry_id: %s", filename.c_str());
73 return {};
74 }
75 file.WriteBytes(&new_telemetry_id, sizeof(u64));
76 return new_telemetry_id;
77}
78
79std::future<bool> VerifyLogin(std::string username, std::string token, std::function<void()> func) {
80#ifdef ENABLE_WEB_SERVICE
81 return WebService::VerifyLogin(username, token, Settings::values.verify_endpoint_url, func);
82#else
83 return std::async(std::launch::async, [func{std::move(func)}]() {
84 func();
85 return false;
86 });
87#endif
88}
89
31TelemetrySession::TelemetrySession() { 90TelemetrySession::TelemetrySession() {
32#ifdef ENABLE_WEB_SERVICE 91#ifdef ENABLE_WEB_SERVICE
33 backend = std::make_unique<WebService::TelemetryJson>(); 92 if (Settings::values.enable_telemetry) {
93 backend = std::make_unique<WebService::TelemetryJson>(
94 Settings::values.telemetry_endpoint_url, Settings::values.citra_username,
95 Settings::values.citra_token);
96 } else {
97 backend = std::make_unique<Telemetry::NullVisitor>();
98 }
34#else 99#else
35 backend = std::make_unique<Telemetry::NullVisitor>(); 100 backend = std::make_unique<Telemetry::NullVisitor>();
36#endif 101#endif
102 // Log one-time top-level information
103 AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId());
104
37 // Log one-time session start information 105 // Log one-time session start information
38 const s64 init_time{std::chrono::duration_cast<std::chrono::milliseconds>( 106 const s64 init_time{std::chrono::duration_cast<std::chrono::milliseconds>(
39 std::chrono::system_clock::now().time_since_epoch()) 107 std::chrono::system_clock::now().time_since_epoch())
40 .count()}; 108 .count()};
41 AddField(Telemetry::FieldType::Session, "Init_Time", init_time); 109 AddField(Telemetry::FieldType::Session, "Init_Time", init_time);
110 std::string program_name;
111 const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadTitle(program_name)};
112 if (res == Loader::ResultStatus::Success) {
113 AddField(Telemetry::FieldType::Session, "ProgramName", program_name);
114 }
42 115
43 // Log application information 116 // Log application information
44 const bool is_git_dirty{std::strstr(Common::g_scm_desc, "dirty") != nullptr}; 117 const bool is_git_dirty{std::strstr(Common::g_scm_desc, "dirty") != nullptr};
45 AddField(Telemetry::FieldType::App, "Git_IsDirty", is_git_dirty); 118 AddField(Telemetry::FieldType::App, "Git_IsDirty", is_git_dirty);
46 AddField(Telemetry::FieldType::App, "Git_Branch", Common::g_scm_branch); 119 AddField(Telemetry::FieldType::App, "Git_Branch", Common::g_scm_branch);
47 AddField(Telemetry::FieldType::App, "Git_Revision", Common::g_scm_rev); 120 AddField(Telemetry::FieldType::App, "Git_Revision", Common::g_scm_rev);
121 AddField(Telemetry::FieldType::App, "BuildDate", Common::g_build_date);
122 AddField(Telemetry::FieldType::App, "BuildName", Common::g_build_name);
48 123
49 // Log user system information 124 // Log user system information
50 AddField(Telemetry::FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string); 125 AddField(Telemetry::FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string);
@@ -68,6 +143,15 @@ TelemetrySession::TelemetrySession() {
68 Common::GetCPUCaps().sse4_1); 143 Common::GetCPUCaps().sse4_1);
69 AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_SSE42", 144 AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_SSE42",
70 Common::GetCPUCaps().sse4_2); 145 Common::GetCPUCaps().sse4_2);
146#ifdef __APPLE__
147 AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Apple");
148#elif defined(_WIN32)
149 AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Windows");
150#elif defined(__linux__) || defined(linux) || defined(__linux)
151 AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Linux");
152#else
153 AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Unknown");
154#endif
71 155
72 // Log user configuration information 156 // Log user configuration information
73 AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching", 157 AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching",
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index cf53835c3..550c6ea2d 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <future>
7#include <memory> 8#include <memory>
8#include "common/telemetry.h" 9#include "common/telemetry.h"
9 10
@@ -35,4 +36,25 @@ private:
35 std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields 36 std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields
36}; 37};
37 38
39/**
40 * Gets TelemetryId, a unique identifier used for the user's telemetry sessions.
41 * @returns The current TelemetryId for the session.
42 */
43u64 GetTelemetryId();
44
45/**
46 * Regenerates TelemetryId, a unique identifier used for the user's telemetry sessions.
47 * @returns The new TelemetryId that was generated.
48 */
49u64 RegenerateTelemetryId();
50
51/**
52 * Verifies the username and token.
53 * @param username Citra username to use for authentication.
54 * @param token Citra token to use for authentication.
55 * @param func A function that gets exectued when the verification is finished
56 * @returns Future with bool indicating whether the verification succeeded
57 */
58std::future<bool> VerifyLogin(std::string username, std::string token, std::function<void()> func);
59
38} // namespace Core 60} // namespace Core