diff options
| author | 2017-10-09 23:56:20 -0400 | |
|---|---|---|
| committer | 2017-10-09 23:56:20 -0400 | |
| commit | b1d5db1cf60344b6b081c9d03cb6ccc3264326cd (patch) | |
| tree | fde377c4ba3c0f92c032e6f5ec8627aae37270ef /src/core | |
| parent | loader: Various improvements for NSO/NRO loaders. (diff) | |
| parent | Merge pull request #2996 from MerryMage/split-travis (diff) | |
| download | yuzu-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')
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 | ||
| 395 | create_directory_groups(${SRCS} ${HEADERS}) | 405 | create_directory_groups(${SRCS} ${HEADERS}) |
| 396 | add_library(core STATIC ${SRCS} ${HEADERS}) | 406 | add_library(core STATIC ${SRCS} ${HEADERS}) |
| 397 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) | 407 | target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) |
| 398 | target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt lz4_static) | 408 | target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt lz4_static) |
| 399 | if (ENABLE_WEB_SERVICE) | 409 | if (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 | |||
| 160 | protected: | 162 | protected: |
| 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 | ||
| 17 | static void InterpreterFallback(u64 pc, Dynarmic::Jit* jit, void* user_arg) { | 17 | static 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 | ||
| 39 | static bool IsReadOnlyMemory(u64 vaddr) { | 21 | static 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 | ||
| 76 | static Dynarmic::UserCallbacks GetUserCallbacks( | 58 | static 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 | ||
| 97 | ARM_Dynarmic::ARM_Dynarmic(PrivilegeMode initial_mode) { | 77 | ARM_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 | |
| 80 | void ARM_Dynarmic::MapBackingMemory(VAddr address, size_t size, u8* memory, Kernel::VMAPermission perms) { | ||
| 100 | } | 81 | } |
| 101 | 82 | ||
| 102 | void ARM_Dynarmic::SetPC(u64 pc) { | 83 | void 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 | ||
| 99 | const u128& ARM_Dynarmic::GetExtReg(int index) const { | ||
| 100 | return jit->ExtRegs64()[index]; | ||
| 101 | } | ||
| 102 | |||
| 103 | void ARM_Dynarmic::SetExtReg(int index, u128& value) { | ||
| 104 | jit->ExtRegs64()[index] = value; | ||
| 105 | } | ||
| 106 | |||
| 118 | u32 ARM_Dynarmic::GetVFPReg(int index) const { | 107 | u32 ARM_Dynarmic::GetVFPReg(int index) const { |
| 119 | return jit->ExtRegs()[index]; | 108 | return {}; |
| 120 | } | 109 | } |
| 121 | 110 | ||
| 122 | void ARM_Dynarmic::SetVFPReg(int index, u32 value) { | 111 | void ARM_Dynarmic::SetVFPReg(int index, u32 value) { |
| 123 | jit->ExtRegs()[index] = value; | ||
| 124 | } | 112 | } |
| 125 | 113 | ||
| 126 | u32 ARM_Dynarmic::GetVFPSystemReg(VFPSystemRegister reg) const { | 114 | u32 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 | ||
| 135 | void ARM_Dynarmic::SetVFPSystemReg(VFPSystemRegister reg, u32 value) { | 118 | void 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 | ||
| 144 | u32 ARM_Dynarmic::GetCPSR() const { | 121 | u32 ARM_Dynarmic::GetCPSR() const { |
| @@ -150,11 +127,10 @@ void ARM_Dynarmic::SetCPSR(u32 cpsr) { | |||
| 150 | } | 127 | } |
| 151 | 128 | ||
| 152 | u32 ARM_Dynarmic::GetCP15Register(CP15Register reg) { | 129 | u32 ARM_Dynarmic::GetCP15Register(CP15Register reg) { |
| 153 | return interpreter_state->CP15[reg]; | 130 | return {}; |
| 154 | } | 131 | } |
| 155 | 132 | ||
| 156 | void ARM_Dynarmic::SetCP15Register(CP15Register reg, u32 value) { | 133 | void ARM_Dynarmic::SetCP15Register(CP15Register reg, u32 value) { |
| 157 | interpreter_state->CP15[reg] = value; | ||
| 158 | } | 134 | } |
| 159 | 135 | ||
| 160 | VAddr ARM_Dynarmic::GetTlsAddress() const { | 136 | VAddr 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 | ||
| 168 | void ARM_Dynarmic::AddTicks(u64 ticks) { | ||
| 169 | down_count -= ticks; | ||
| 170 | if (down_count < 0) { | ||
| 171 | CoreTiming::Advance(); | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); | 144 | MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); |
| 176 | 145 | ||
| 177 | void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { | 146 | void 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 | ||
| 185 | void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { | 155 | void 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 | ||
| 201 | void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) { | 168 | void 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() { | |||
| 223 | void ARM_Dynarmic::ClearInstructionCache() { | 187 | void ARM_Dynarmic::ClearInstructionCache() { |
| 224 | jit->ClearCache(); | 188 | jit->ClearCache(); |
| 225 | } | 189 | } |
| 190 | |||
| 191 | void 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 | ||
| 14 | namespace Memory { | ||
| 15 | struct PageTable; | ||
| 16 | } // namespace Memory | ||
| 17 | |||
| 13 | class ARM_Dynarmic final : public ARM_Interface { | 18 | class ARM_Dynarmic final : public ARM_Interface { |
| 14 | public: | 19 | public: |
| 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 | ||
| 42 | private: | 50 | private: |
| 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 | ||
| 32 | void ARM_DynCom::PageTableChanged() { | ||
| 33 | ClearInstructionCache(); | ||
| 34 | } | ||
| 35 | |||
| 32 | u64 ARM_DynCom::GetPC() const { | 36 | u64 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 | ||
| 48 | const u128& ARM_DynCom::GetExtReg(int index) const { | ||
| 49 | return {}; | ||
| 50 | } | ||
| 51 | |||
| 52 | void ARM_DynCom::SetExtReg(int index, u128& value) { | ||
| 53 | } | ||
| 54 | |||
| 44 | u32 ARM_DynCom::GetVFPReg(int index) const { | 55 | u32 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 { | |||
| 80 | void ARM_DynCom::SetTlsAddress(VAddr /*address*/) { | 91 | void ARM_DynCom::SetTlsAddress(VAddr /*address*/) { |
| 81 | } | 92 | } |
| 82 | 93 | ||
| 83 | void ARM_DynCom::AddTicks(u64 ticks) { | ||
| 84 | down_count -= ticks; | ||
| 85 | if (down_count < 0) | ||
| 86 | CoreTiming::Advance(); | ||
| 87 | } | ||
| 88 | |||
| 89 | void ARM_DynCom::ExecuteInstructions(int num_instructions) { | 94 | void 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 | ||
| 99 | void ARM_DynCom::SaveContext(ThreadContext& ctx) { | 104 | void 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 | ||
| 841 | static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) { | 841 | static 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 | ||
| 874 | static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) { | 874 | static 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; |
| 1626 | DISPATCH : { | 1626 | DISPATCH : { |
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 | ||
| 235 | private: | 235 | private: |
| 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 | ||
| 24 | namespace Core { | 27 | namespace 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 | ||
| 138 | System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { | 141 | System::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 @@ | |||
| 14 | class EmuWindow; | 15 | class EmuWindow; |
| 15 | class ARM_Interface; | 16 | class ARM_Interface; |
| 16 | 17 | ||
| 17 | namespace Loader { | ||
| 18 | class AppLoader; | ||
| 19 | } | ||
| 20 | |||
| 21 | namespace Core { | 18 | namespace Core { |
| 22 | 19 | ||
| 23 | class System { | 20 | class 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 | |||
| 122 | private: | 123 | private: |
| 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; | |||
| 57 | static s64 last_global_time_ticks; | 57 | static s64 last_global_time_ticks; |
| 58 | static s64 last_global_time_us; | 58 | static s64 last_global_time_us; |
| 59 | 59 | ||
| 60 | static s64 down_count = 0; ///< A decreasing counter of remaining cycles before the next event, | ||
| 61 | /// decreased by the cpu run loop | ||
| 62 | |||
| 60 | static std::recursive_mutex external_event_section; | 63 | static 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 | ||
| 148 | void Init() { | 151 | void 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 | ||
| 191 | void AddTicks(u64 ticks) { | ||
| 192 | down_count -= ticks; | ||
| 193 | if (down_count < 0) { | ||
| 194 | Advance(); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 188 | u64 GetTicks() { | 198 | u64 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 | ||
| 192 | u64 GetIdleTicks() { | 202 | u64 GetIdleTicks() { |
| @@ -460,18 +470,18 @@ void MoveEvents() { | |||
| 460 | } | 470 | } |
| 461 | 471 | ||
| 462 | void ForceCheck() { | 472 | void 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 | ||
| 471 | void Advance() { | 481 | void 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 | ||
| 508 | void Idle(int max_idle) { | 518 | void 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 | ||
| 534 | std::string GetScheduledEventsSummary() { | 544 | std::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(); | |||
| 67 | typedef void (*MHzChangeCallback)(); | 67 | typedef void (*MHzChangeCallback)(); |
| 68 | typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback; | 68 | typedef 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 | */ | ||
| 74 | void AddTicks(u64 ticks); | ||
| 75 | |||
| 70 | u64 GetTicks(); | 76 | u64 GetTicks(); |
| 71 | u64 GetIdleTicks(); | 77 | u64 GetIdleTicks(); |
| 72 | u64 GetGlobalTimeUs(); | 78 | u64 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 | ||
| 95 | std::vector<u8> Path::AsBinary() const { | 97 | std::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 | ||
| 27 | static std::string GetNCCHPath(const std::string& mount_point, u32 high, u32 low) { | 30 | static 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 | ||
| 32 | ArchiveFactory_NCCH::ArchiveFactory_NCCH(const std::string& nand_directory) | 45 | ArchiveFactory_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 | ||
| 123 | ResultCode SDMCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { | 123 | ResultCode 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 | ||
| 262 | ResultCode SDMCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { | 280 | ResultCode 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 | ||
| 220 | ArchiveFactory_SelfNCCH::ArchiveFactory_SelfNCCH(Loader::AppLoader& app_loader) { | 232 | void 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 | ||
| 242 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path) { | 280 | ResultVal<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 | ||
| 247 | ResultCode ArchiveFactory_SelfNCCH::Format(const Path&, const FileSys::ArchiveFormatInfo&) { | 287 | ResultCode 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 |
| 30 | class ArchiveFactory_SelfNCCH final : public ArchiveFactory { | 35 | class ArchiveFactory_SelfNCCH final : public ArchiveFactory { |
| 31 | public: | 36 | public: |
| 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 | ||
| 41 | private: | 49 | private: |
| 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 | |||
| 17 | namespace FileSys { | ||
| 18 | |||
| 19 | static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs | ||
| 20 | static 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 | */ | ||
| 28 | static 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 | */ | ||
| 41 | static 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 | |||
| 98 | NCCHContainer::NCCHContainer(const std::string& filepath) : filepath(filepath) { | ||
| 99 | file = FileUtil::IOFile(filepath, "rb"); | ||
| 100 | } | ||
| 101 | |||
| 102 | Loader::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 | |||
| 115 | Loader::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 | |||
| 210 | Loader::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 | |||
| 243 | Loader::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 | |||
| 300 | Loader::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 | |||
| 333 | Loader::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 | |||
| 370 | Loader::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 | |||
| 387 | Loader::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 | |||
| 399 | bool NCCHContainer::HasExeFS() { | ||
| 400 | Loader::ResultStatus result = Load(); | ||
| 401 | if (result != Loader::ResultStatus::Success) | ||
| 402 | return false; | ||
| 403 | |||
| 404 | return has_exefs; | ||
| 405 | } | ||
| 406 | |||
| 407 | bool NCCHContainer::HasRomFS() { | ||
| 408 | Loader::ResultStatus result = Load(); | ||
| 409 | if (result != Loader::ResultStatus::Success) | ||
| 410 | return false; | ||
| 411 | |||
| 412 | return has_romfs; | ||
| 413 | } | ||
| 414 | |||
| 415 | bool 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 | |||
| 20 | struct 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 | |||
| 52 | static_assert(sizeof(NCCH_Header) == 0x200, "NCCH header structure size is wrong"); | ||
| 53 | |||
| 54 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 55 | // ExeFS (executable file system) headers | ||
| 56 | |||
| 57 | struct ExeFs_SectionHeader { | ||
| 58 | char name[8]; | ||
| 59 | u32 offset; | ||
| 60 | u32 size; | ||
| 61 | }; | ||
| 62 | |||
| 63 | struct 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 | |||
| 72 | struct ExHeader_SystemInfoFlags { | ||
| 73 | u8 reserved[5]; | ||
| 74 | u8 flag; | ||
| 75 | u8 remaster_version[2]; | ||
| 76 | }; | ||
| 77 | |||
| 78 | struct ExHeader_CodeSegmentInfo { | ||
| 79 | u32 address; | ||
| 80 | u32 num_max_pages; | ||
| 81 | u32 code_size; | ||
| 82 | }; | ||
| 83 | |||
| 84 | struct 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 | |||
| 95 | struct ExHeader_DependencyList { | ||
| 96 | u8 program_id[0x30][8]; | ||
| 97 | }; | ||
| 98 | |||
| 99 | struct ExHeader_SystemInfo { | ||
| 100 | u64 save_data_size; | ||
| 101 | u64_le jump_id; | ||
| 102 | u8 reserved_2[0x30]; | ||
| 103 | }; | ||
| 104 | |||
| 105 | struct 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 | |||
| 113 | struct 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 | |||
| 132 | struct ExHeader_ARM11_KernelCaps { | ||
| 133 | u32_le descriptors[28]; | ||
| 134 | u8 reserved[0x10]; | ||
| 135 | }; | ||
| 136 | |||
| 137 | struct ExHeader_ARM9_AccessControl { | ||
| 138 | u8 descriptors[15]; | ||
| 139 | u8 descversion; | ||
| 140 | }; | ||
| 141 | |||
| 142 | struct 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 | |||
| 158 | static_assert(sizeof(ExHeader_Header) == 0x800, "ExHeader structure size is wrong"); | ||
| 159 | |||
| 160 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 161 | // FileSys namespace | ||
| 162 | |||
| 163 | namespace 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 | */ | ||
| 169 | class NCCHContainer { | ||
| 170 | public: | ||
| 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 | |||
| 256 | private: | ||
| 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 | ||
| 108 | ResultCode SaveDataArchive::RenameFile(const Path& src_path, const Path& dest_path) const { | 108 | ResultCode 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 | ||
| 249 | ResultCode SaveDataArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { | 267 | ResultCode 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 | |||
| 16 | namespace FileSys { | ||
| 17 | |||
| 18 | static 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 | |||
| 34 | Loader::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 | |||
| 71 | Loader::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 | |||
| 124 | u64 TitleMetadata::GetTitleID() const { | ||
| 125 | return tmd_body.title_id; | ||
| 126 | } | ||
| 127 | |||
| 128 | u32 TitleMetadata::GetTitleType() const { | ||
| 129 | return tmd_body.title_type; | ||
| 130 | } | ||
| 131 | |||
| 132 | u16 TitleMetadata::GetTitleVersion() const { | ||
| 133 | return tmd_body.title_version; | ||
| 134 | } | ||
| 135 | |||
| 136 | u64 TitleMetadata::GetSystemVersion() const { | ||
| 137 | return tmd_body.system_version; | ||
| 138 | } | ||
| 139 | |||
| 140 | size_t TitleMetadata::GetContentCount() const { | ||
| 141 | return tmd_chunks.size(); | ||
| 142 | } | ||
| 143 | |||
| 144 | u32 TitleMetadata::GetBootContentID() const { | ||
| 145 | return tmd_chunks[TMDContentIndex::Main].id; | ||
| 146 | } | ||
| 147 | |||
| 148 | u32 TitleMetadata::GetManualContentID() const { | ||
| 149 | return tmd_chunks[TMDContentIndex::Manual].id; | ||
| 150 | } | ||
| 151 | |||
| 152 | u32 TitleMetadata::GetDLPContentID() const { | ||
| 153 | return tmd_chunks[TMDContentIndex::DLP].id; | ||
| 154 | } | ||
| 155 | |||
| 156 | void TitleMetadata::SetTitleID(u64 title_id) { | ||
| 157 | tmd_body.title_id = title_id; | ||
| 158 | } | ||
| 159 | |||
| 160 | void TitleMetadata::SetTitleType(u32 type) { | ||
| 161 | tmd_body.title_type = type; | ||
| 162 | } | ||
| 163 | |||
| 164 | void TitleMetadata::SetTitleVersion(u16 version) { | ||
| 165 | tmd_body.title_version = version; | ||
| 166 | } | ||
| 167 | |||
| 168 | void TitleMetadata::SetSystemVersion(u64 version) { | ||
| 169 | tmd_body.system_version = version; | ||
| 170 | } | ||
| 171 | |||
| 172 | void TitleMetadata::AddContentChunk(const ContentChunk& chunk) { | ||
| 173 | tmd_chunks.push_back(chunk); | ||
| 174 | } | ||
| 175 | |||
| 176 | void 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 | |||
| 12 | namespace Loader { | ||
| 13 | enum class ResultStatus; | ||
| 14 | } | ||
| 15 | |||
| 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 17 | // FileSys namespace | ||
| 18 | |||
| 19 | namespace FileSys { | ||
| 20 | |||
| 21 | enum TMDSignatureType : u32 { | ||
| 22 | Rsa4096Sha1 = 0x10000, | ||
| 23 | Rsa2048Sha1 = 0x10001, | ||
| 24 | EllipticSha1 = 0x10002, | ||
| 25 | Rsa4096Sha256 = 0x10003, | ||
| 26 | Rsa2048Sha256 = 0x10004, | ||
| 27 | EcdsaSha256 = 0x10005 | ||
| 28 | }; | ||
| 29 | |||
| 30 | enum 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 | */ | ||
| 43 | class TitleMetadata { | ||
| 44 | public: | ||
| 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 | |||
| 114 | private: | ||
| 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 | ||
| 11 | class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>, | ||
| 12 | public std::enable_shared_from_this<TouchState> { | ||
| 13 | public: | ||
| 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 | |||
| 25 | private: | ||
| 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 | |||
| 42 | EmuWindow::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 | |||
| 50 | EmuWindow::~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 | ||
| 49 | void EmuWindow::TouchReleased() { | 93 | void 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 | ||
| 55 | void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { | 100 | void 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 | ||
| 65 | void 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 | |||
| 78 | void 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 | |||
| 88 | void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { | 110 | void 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 | ||
| 182 | protected: | 103 | protected: |
| 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 | ||
| 144 | FramebufferLayout 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 | |||
| 144 | FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) { | 178 | FramebufferLayout 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 | |||
| 54 | FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped); | 54 | FramebufferLayout 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 | */ | ||
| 65 | FramebufferLayout 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 | ||
| 15 | namespace Input { | 16 | namespace Input { |
| 16 | 17 | ||
| @@ -107,4 +108,28 @@ using ButtonDevice = InputDevice<bool>; | |||
| 107 | */ | 108 | */ |
| 108 | using AnalogDevice = InputDevice<std::tuple<float, float>>; | 109 | using 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 | */ | ||
| 127 | using 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 | */ | ||
| 133 | using 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 | |||
| 10 | namespace Motion { | ||
| 11 | |||
| 12 | static constexpr int update_millisecond = 100; | ||
| 13 | static constexpr auto update_duration = | ||
| 14 | std::chrono::duration_cast<std::chrono::steady_clock::duration>( | ||
| 15 | std::chrono::milliseconds(update_millisecond)); | ||
| 16 | |||
| 17 | MotionEmu::MotionEmu(EmuWindow& emu_window) | ||
| 18 | : motion_emu_thread(&MotionEmu::MotionEmuThread, this, std::ref(emu_window)) {} | ||
| 19 | |||
| 20 | MotionEmu::~MotionEmu() { | ||
| 21 | if (motion_emu_thread.joinable()) { | ||
| 22 | shutdown_event.Set(); | ||
| 23 | motion_emu_thread.join(); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | void 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 | |||
| 63 | void MotionEmu::BeginTilt(int x, int y) { | ||
| 64 | mouse_origin = Math::MakeVec(x, y); | ||
| 65 | is_tilting = true; | ||
| 66 | } | ||
| 67 | |||
| 68 | void 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 | |||
| 83 | void 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 | |||
| 9 | class EmuWindow; | ||
| 10 | |||
| 11 | namespace Motion { | ||
| 12 | |||
| 13 | class MotionEmu final { | ||
| 14 | public: | ||
| 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 | |||
| 37 | private: | ||
| 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() { | |||
| 670 | static void WriteMemory() { | 672 | static 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 | ||
| 84 | void MiiSelector::Update() {} | 84 | void 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 { | |||
| 16 | namespace Applets { | 16 | namespace Applets { |
| 17 | 17 | ||
| 18 | struct MiiConfig { | 18 | struct 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 | |||
| 39 | static_assert(sizeof(MiiConfig) == 0x104, "MiiConfig structure has incorrect size"); | 33 | static_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") |
| 43 | ASSERT_REG_POSITION(unk_008, 0x08); | 37 | ASSERT_REG_POSITION(title, 0x08); |
| 44 | ASSERT_REG_POSITION(unk_08C, 0x8C); | 38 | ASSERT_REG_POSITION(show_guest_miis, 0x8C); |
| 45 | ASSERT_REG_POSITION(unk_090, 0x90); | 39 | ASSERT_REG_POSITION(initially_selected_mii_index, 0x90); |
| 46 | ASSERT_REG_POSITION(unk_094, 0x94); | 40 | ASSERT_REG_POSITION(guest_mii_whitelist, 0x94); |
| 47 | ASSERT_REG_POSITION(unk_0FE, 0xFE); | ||
| 48 | #undef ASSERT_REG_POSITION | 41 | #undef ASSERT_REG_POSITION |
| 49 | 42 | ||
| 50 | struct MiiResult { | 43 | struct 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 | }; |
| 58 | static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size"); | 53 | static_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") |
| 62 | ASSERT_REG_POSITION(unk_0C, 0x0C); | 57 | ASSERT_REG_POSITION(selected_mii_data, 0x0C); |
| 63 | ASSERT_REG_POSITION(unk_6C, 0x6C); | 58 | ASSERT_REG_POSITION(guest_mii_name, 0x6C); |
| 64 | #undef ASSERT_REG_POSITION | 59 | #undef ASSERT_REG_POSITION |
| 65 | 60 | ||
| 66 | class MiiSelector final : public Applet { | 61 | class 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 | */ | ||
| 32 | static 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 | ||
| 30 | template <ResultCode func(u64)> | ||
| 31 | void Wrap() { | ||
| 32 | FuncReturn(func(PARAM(0)).raw); | ||
| 33 | } | ||
| 34 | |||
| 35 | template <ResultCode func(u32, u64, u32)> | ||
| 36 | void Wrap() { | ||
| 37 | FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw); | ||
| 38 | } | ||
| 39 | |||
| 40 | template <ResultCode func(u64, u32)> | ||
| 41 | void Wrap() { | ||
| 42 | FuncReturn(func(PARAM(0), PARAM(1)).raw); | ||
| 43 | } | ||
| 44 | |||
| 45 | template <ResultCode func(u64, u64, u64)> | ||
| 46 | void Wrap() { | ||
| 47 | FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw); | ||
| 48 | } | ||
| 49 | |||
| 50 | template <ResultCode func(u64*, u64, u64, u64)> | ||
| 51 | void Wrap() { | ||
| 52 | u64 param_1 = 0; | ||
| 53 | u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3)).raw; | ||
| 54 | Core::CPU().SetReg(1, param_1); | ||
| 55 | FuncReturn(retval); | ||
| 56 | } | ||
| 57 | |||
| 40 | template <ResultCode func(u32, u32, u32, u32)> | 58 | template <ResultCode func(u32, u32, u32, u32)> |
| 41 | void Wrap() { | 59 | void 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 | ||
| 61 | template <ResultCode func(s32*, u32*, s32, bool, s64)> | 79 | template <ResultCode func(s32*, VAddr, s32, bool, s64)> |
| 62 | void Wrap() { | 80 | void Wrap() { |
| 63 | s32 param_1 = 0; | 81 | s32 param_1 = 0; |
| 64 | s32 retval = func(¶m_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(¶m_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 | ||
| 72 | template <ResultCode func(s32*, u32*, s32, u32)> | 90 | template <ResultCode func(s32*, VAddr, s32, u32)> |
| 73 | void Wrap() { | 91 | void Wrap() { |
| 74 | s32 param_1 = 0; | 92 | s32 param_1 = 0; |
| 75 | u32 retval = | 93 | u32 retval = func(¶m_1, PARAM(1), (s32)PARAM(2), PARAM(3)).raw; |
| 76 | func(¶m_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 | ||
| 105 | template <ResultCode func(u32, u64*)> | ||
| 106 | void Wrap() { | ||
| 107 | u64 param_1 = 0; | ||
| 108 | u32 retval = func(PARAM(0), ¶m_1).raw; | ||
| 109 | Core::CPU().SetReg(1, param_1); | ||
| 110 | FuncReturn(retval); | ||
| 111 | } | ||
| 112 | |||
| 88 | template <ResultCode func(u32*)> | 113 | template <ResultCode func(u32*)> |
| 89 | void Wrap() { | 114 | void 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 | ||
| 103 | template <ResultCode func(MemoryInfo*, PageInfo*, u32)> | 128 | template <ResultCode func(MemoryInfo*, PageInfo*, u64)> |
| 104 | void Wrap() { | 129 | void 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 | ||
| 142 | template <ResultCode func(u32*, u32)> | 168 | template <ResultCode func(u32*, u64)> |
| 143 | void Wrap() { | 169 | void Wrap() { |
| 144 | u32 param_1 = 0; | 170 | u32 param_1 = 0; |
| 145 | u32 retval = func(¶m_1, PARAM(1)).raw; | 171 | u32 retval = func(¶m_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 | ||
| 155 | template <ResultCode func(s64*, u32, u32*, u32)> | ||
| 156 | void Wrap() { | ||
| 157 | FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), | ||
| 158 | (u32*)Memory::GetPointer(PARAM(2)), (s32)PARAM(3)) | ||
| 159 | .raw); | ||
| 160 | } | ||
| 161 | |||
| 162 | template <ResultCode func(u32*, const char*)> | ||
| 163 | void Wrap() { | ||
| 164 | u32 param_1 = 0; | ||
| 165 | u32 retval = func(¶m_1, (char*)Memory::GetPointer(PARAM(1))).raw; | ||
| 166 | Core::CPU().SetReg(1, param_1); | ||
| 167 | FuncReturn(retval); | ||
| 168 | } | ||
| 169 | |||
| 170 | template <ResultCode func(u32*, s32, s32)> | 181 | template <ResultCode func(u32*, s32, s32)> |
| 171 | void Wrap() { | 182 | void 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 | ||
| 225 | template <ResultCode func(Kernel::Handle*, Kernel::Handle*, const char*, u32)> | 236 | template <ResultCode func(Kernel::Handle*, Kernel::Handle*, VAddr, u32)> |
| 226 | void Wrap() { | 237 | void 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(¶m_1, ¶m_2, | 240 | u32 retval = func(¶m_1, ¶m_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 | ||
| 256 | template <ResultCode func(u32, u32, u32)> | ||
| 257 | void 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 | ||
| 271 | template <void func(const char*, int len)> | 285 | template <void func(VAddr, int len)> |
| 272 | void Wrap() { | 286 | void Wrap() { |
| 273 | func((char*)Memory::GetPointer(PARAM(0)), PARAM(1)); | 287 | func(PARAM(0), PARAM(1)); |
| 274 | } | 288 | } |
| 275 | 289 | ||
| 276 | template <void func(u8)> | 290 | template <void func(u64, u64, u64)> |
| 277 | void Wrap() { | 291 | void 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 | ||
| 125 | inline u32 StaticBufferDesc(u32 size, u8 buffer_id) { | 125 | inline 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 | ||
| 163 | inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { | 163 | inline 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 | ||
| 193 | inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) { | 193 | inline 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 | ||
| 198 | inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, u32 size, | 198 | inline 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 | ||
| 38 | u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) { | 38 | u32 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 | ||
| 43 | void HLERequestContext::ClearIncomingObjects() { | 43 | void 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 | ||
| 13 | namespace Kernel { | 14 | namespace 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 | ||
| 89 | public: | 92 | public: |
| @@ -129,4 +132,4 @@ void Init(u32 system_mode); | |||
| 129 | /// Shutdown the kernel | 132 | /// Shutdown the kernel |
| 130 | void Shutdown(); | 133 | void 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 | ||
| 25 | namespace Kernel { | 24 | namespace Kernel { |
| 26 | 25 | ||
| 27 | static MemoryRegionInfo memory_regions[3]; | 26 | MemoryRegionInfo 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 | ||
| 99 | std::array<u8, Memory::VRAM_SIZE> vram; | ||
| 100 | std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram; | ||
| 101 | |||
| 102 | void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) { | 98 | void 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 | ||
| 27 | void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); | 27 | void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); |
| 28 | void MapSharedPages(VMManager& address_space); | 28 | void MapSharedPages(VMManager& address_space); |
| 29 | |||
| 30 | extern 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) { | |||
| 25 | Mutex::Mutex() {} | 25 | Mutex::Mutex() {} |
| 26 | Mutex::~Mutex() {} | 26 | Mutex::~Mutex() {} |
| 27 | 27 | ||
| 28 | SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) { | 28 | SharedPtr<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 | ||
| 135 | void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { | 136 | void 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 | ||
| 64 | s32 ResourceLimit::GetMaxResourceValue(u32 resource) const { | 64 | u32 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 { | |||
| 13 | Semaphore::Semaphore() {} | 13 | Semaphore::Semaphore() {} |
| 14 | Semaphore::~Semaphore() {} | 14 | Semaphore::~Semaphore() {} |
| 15 | 15 | ||
| 16 | ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_count, | 16 | ResultVal<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 | ||
| 112 | Thread* ArbitrateHighestPriorityThread(u32 address) { | 112 | Thread* 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 | ||
| 354 | ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority, | 371 | ResultVal<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 | ||
| 450 | void Thread::SetPriority(s32 priority) { | 467 | void 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 | ||
| 462 | void Thread::UpdatePriority() { | 479 | void 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 | ||
| 471 | void Thread::BoostPriority(s32 priority) { | 488 | void 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 | ||
| 480 | SharedPtr<Thread> SetupMainThread(VAddr entry_point, s32 priority) { | 497 | SharedPtr<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) { | |||
| 525 | s32 Thread::GetWaitObjectIndex(WaitObject* object) const { | 541 | s32 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 | |||
| 547 | VAddr 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 | ||
| 18 | enum ThreadPriority : s32 { | 18 | enum 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 | ||
| 44 | enum 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 | |||
| 44 | namespace Kernel { | 49 | namespace Kernel { |
| 45 | 50 | ||
| 46 | class Mutex; | 51 | class 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 | |||
| 208 | private: | 225 | private: |
| 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 | */ |
| 219 | SharedPtr<Thread> SetupMainThread(VAddr entry_point, s32 priority); | 237 | SharedPtr<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 | */ |
| 277 | const std::vector<SharedPtr<Thread>>& GetThreadList(); | 295 | const 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) { | |||
| 328 | void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { | 338 | void 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 | ||
| 14 | namespace Kernel { | 15 | namespace 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 | */ |
| 104 | class VMManager final { | 105 | class VMManager final { |
| 105 | // TODO(yuriks): Make page tables switchable to support multiple VMManagers | ||
| 106 | public: | 106 | public: |
| 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 | |||
| 187 | private: | 191 | private: |
| 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 | ||
| 35 | SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { | 35 | SharedPtr<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 | |||
| 9 | namespace HLE { | ||
| 10 | std::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 | |||
| 9 | namespace 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 | */ | ||
| 17 | extern 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; | |||
| 33 | static bool shared_font_relocated = false; | 35 | static bool shared_font_relocated = false; |
| 34 | 36 | ||
| 35 | static Kernel::SharedPtr<Kernel::Mutex> lock; | 37 | static Kernel::SharedPtr<Kernel::Mutex> lock; |
| 36 | static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event | ||
| 37 | static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event | ||
| 38 | 38 | ||
| 39 | static u32 cpu_percent; ///< CPU time available to the running application | 39 | static u32 cpu_percent; ///< CPU time available to the running application |
| 40 | 40 | ||
| @@ -43,43 +43,344 @@ static u8 unknown_ns_state_field; | |||
| 43 | 43 | ||
| 44 | static ScreencapPostPermission screen_capture_post_permission; | 44 | static 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. |
| 47 | static MessageParameter next_parameter; | 47 | /// TODO(Subv): Use std::optional once we migrate to C++17. |
| 48 | static boost::optional<MessageParameter> next_parameter; | ||
| 49 | |||
| 50 | enum class AppletPos { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4 }; | ||
| 51 | |||
| 52 | static constexpr size_t NumAppletSlot = 4; | ||
| 53 | |||
| 54 | enum class AppletSlot : u8 { | ||
| 55 | Application, | ||
| 56 | SystemApplet, | ||
| 57 | HomeMenu, | ||
| 58 | LibraryApplet, | ||
| 59 | |||
| 60 | // An invalid tag | ||
| 61 | Error, | ||
| 62 | }; | ||
| 63 | |||
| 64 | union 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 | |||
| 74 | struct 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. | ||
| 84 | static std::array<AppletSlotData, NumAppletSlot> applet_slots = {}; | ||
| 85 | |||
| 86 | // This overload returns nullptr if no applet with the specified id has been started. | ||
| 87 | static 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 | |||
| 147 | static 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 | ||
| 49 | void SendParameter(const MessageParameter& parameter) { | 170 | void 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 | ||
| 55 | void Initialize(Service::Interface* self) { | 183 | void 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. | 225 | static 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 | |||
| 279 | static 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 | |||
| 344 | static 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 | ||
| 74 | void GetSharedFont(Service::Interface* self) { | 362 | void 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) { | |||
| 128 | void Enable(Service::Interface* self) { | 434 | void 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 | ||
| 137 | void GetAppletManInfo(Service::Interface* self) { | 455 | void GetAppletManInfo(Service::Interface* self) { |
| @@ -149,22 +467,27 @@ void GetAppletManInfo(Service::Interface* self) { | |||
| 149 | 467 | ||
| 150 | void IsRegistered(Service::Interface* self) { | 468 | void 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 | ||
| 170 | void InquireNotification(Service::Interface* self) { | 493 | void 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 | ||
| 216 | void ReceiveParameter(Service::Interface* self) { | 544 | void 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 | ||
| 246 | void GlanceParameter(Service::Interface* self) { | 593 | void 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 | ||
| 276 | void CancelParameter(Service::Interface* self) { | 643 | void 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 | ||
| 292 | void PrepareToStartApplication(Service::Interface* self) { | 676 | void 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 | |||
| 785 | void 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 | ||
| 397 | void PreloadLibraryApplet(Service::Interface* self) { | 799 | void 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 | ||
| 412 | void StartLibraryApplet(Service::Interface* self) { | 818 | void 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 | ||
| 439 | void CancelLibraryApplet(Service::Interface* self) { | 852 | void 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 | ||
| 650 | static 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 | |||
| 704 | static 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 | |||
| 751 | static 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 | |||
| 769 | void Init() { | 1063 | void 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 | ||
| 807 | void Shutdown() { | 1106 | void 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 |
| 74 | enum class AppletId : u32 { | 74 | enum 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 | ||
| 122 | namespace ErrCodes { | ||
| 123 | enum { | ||
| 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 |
| 120 | void SendParameter(const MessageParameter& parameter); | 130 | void SendParameter(const MessageParameter& parameter); |
| 121 | 131 | ||
| @@ -410,6 +420,16 @@ void GetAppCpuTimeLimit(Service::Interface* self); | |||
| 410 | void PrepareToStartLibraryApplet(Service::Interface* self); | 420 | void 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 | */ | ||
| 430 | void 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 | ||
| 142 | void GetCountryCodeID(Service::Interface* self) { | 142 | void 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 | ||
| 171 | static u32 GetRegionValue() { | 171 | u32 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 | */ |
| 102 | void GetCountryCodeID(Service::Interface* self); | 102 | void GetCountryCodeID(Service::Interface* self); |
| 103 | 103 | ||
| 104 | u32 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 { | |||
| 8 | namespace DLP { | 8 | namespace DLP { |
| 9 | 9 | ||
| 10 | const Interface::FunctionInfo FunctionTable[] = { | 10 | const 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 | ||
| 14 | DLP_CLNT_Interface::DLP_CLNT_Interface() { | 33 | DLP_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 { | |||
| 8 | namespace DLP { | 8 | namespace DLP { |
| 9 | 9 | ||
| 10 | const Interface::FunctionInfo FunctionTable[] = { | 10 | const 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 | ||
| 14 | DLP_FKCL_Interface::DLP_FKCL_Interface() { | 30 | DLP_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 @@ | |||
| 11 | namespace Service { | 11 | namespace Service { |
| 12 | namespace DLP { | 12 | namespace DLP { |
| 13 | 13 | ||
| 14 | static void unk_0x000E0040(Interface* self) { | 14 | static 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 | ||
| 37 | DLP_SRVR_Interface::DLP_SRVR_Interface() { | 42 | DLP_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 | ||
| 53 | static constexpr Kernel::Handle INVALID_HANDLE{}; | 54 | static 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 | |||
| 573 | void 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 | ||
| 569 | void UnregisterArchiveTypes() { | 585 | void 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 |
| 22 | static constexpr char SDCARD_ID[]{"00000000000000000000000000000000"}; | 22 | static constexpr char SDCARD_ID[]{"00000000000000000000000000000000"}; |
| 23 | 23 | ||
| 24 | namespace Loader { | ||
| 25 | class AppLoader; | ||
| 26 | } | ||
| 27 | |||
| 24 | namespace Service { | 28 | namespace Service { |
| 25 | namespace FS { | 29 | namespace FS { |
| 26 | 30 | ||
| @@ -259,6 +263,9 @@ void ArchiveInit(); | |||
| 259 | /// Shutdown archives | 263 | /// Shutdown archives |
| 260 | void ArchiveShutdown(); | 264 | void ArchiveShutdown(); |
| 261 | 265 | ||
| 266 | /// Registers a new NCCH file with the SelfNCCH archive factory | ||
| 267 | void RegisterSelfNCCH(Loader::AppLoader& app_loader); | ||
| 268 | |||
| 262 | /// Register all archive types | 269 | /// Register all archive types |
| 263 | void RegisterArchiveTypes(); | 270 | void 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 | ||
| 23 | namespace Service { | 23 | namespace Service { |
| 24 | namespace HID { | 24 | namespace HID { |
| @@ -50,10 +50,15 @@ constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234; | |||
| 50 | constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; | 50 | constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; |
| 51 | constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; | 51 | constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; |
| 52 | 52 | ||
| 53 | constexpr float accelerometer_coef = 512.0f; // measured from hw test result | ||
| 54 | constexpr float gyroscope_coef = 14.375f; // got from hwtest GetGyroscopeLowRawToDpsCoefficient call | ||
| 55 | |||
| 53 | static std::atomic<bool> is_device_reload_pending; | 56 | static std::atomic<bool> is_device_reload_pending; |
| 54 | static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> | 57 | static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> |
| 55 | buttons; | 58 | buttons; |
| 56 | static std::unique_ptr<Input::AnalogDevice> circle_pad; | 59 | static std::unique_ptr<Input::AnalogDevice> circle_pad; |
| 60 | static std::unique_ptr<Input::MotionDevice> motion_device; | ||
| 61 | static std::unique_ptr<Input::TouchDevice> touch_device; | ||
| 57 | 62 | ||
| 58 | DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) { | 63 | DirectionState 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 | ||
| 95 | static void UnloadInputDevices() { | 102 | static 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 | ||
| 102 | static void UpdatePadCallback(u64 userdata, int cycles_late) { | 111 | static 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 | */ |
| 25 | struct PadState { | 25 | struct 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 { | |||
| 18 | namespace IR { | 18 | namespace IR { |
| 19 | 19 | ||
| 20 | union PadState { | 20 | union 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 @@ | |||
| 14 | namespace Service { | 16 | namespace Service { |
| 15 | namespace NIM { | 17 | namespace NIM { |
| 16 | 18 | ||
| 19 | static Kernel::SharedPtr<Kernel::Event> nim_system_update_event; | ||
| 20 | |||
| 21 | void 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 | |||
| 17 | void CheckSysUpdateAvailable(Service::Interface* self) { | 29 | void 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 | ||
| 34 | void Shutdown() {} | 48 | void 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; | |||
| 11 | namespace NIM { | 11 | namespace 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 | */ | ||
| 22 | void 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 | |||
| 8 | namespace Service { | ||
| 9 | namespace NS { | ||
| 10 | |||
| 11 | void 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 | |||
| 9 | namespace Service { | ||
| 10 | namespace NS { | ||
| 11 | |||
| 12 | /// Registers all NS services with the specified service manager. | ||
| 13 | void 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 | |||
| 7 | namespace Service { | ||
| 8 | namespace NS { | ||
| 9 | |||
| 10 | NS_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 | |||
| 31 | NS_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 | ||
| 9 | namespace Service { | 10 | namespace Service { |
| 10 | namespace NS { | 11 | namespace NS { |
| 11 | 12 | ||
| 12 | class NS_S final : public Interface { | 13 | /// Interface to "ns:s" service |
| 14 | class NS_S final : public ServiceFramework<NS_S> { | ||
| 13 | public: | 15 | public: |
| 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 | |||
| 7 | namespace Service { | ||
| 8 | namespace NS { | ||
| 9 | |||
| 10 | const 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 | |||
| 28 | NS_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 | ||
| 21 | namespace Service { | 27 | namespace Service { |
| 22 | namespace NWM { | 28 | namespace 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 | */ |
| 39 | static NodeList node_info(1); | 45 | static NodeList node_info; |
| 46 | |||
| 47 | // Node information about our own system. | ||
| 48 | static 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. |
| 42 | static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events; | 51 | static 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. |
| 52 | static int beacon_broadcast_event; | 61 | static int beacon_broadcast_event; |
| 53 | 62 | ||
| 63 | // Mutex to synchronize access to the connection status between the emulation thread and the | ||
| 64 | // network thread. | ||
| 65 | static 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. | ||
| 69 | static 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. | ||
| 73 | constexpr size_t MaxBeaconFrames = 15; | ||
| 74 | |||
| 75 | // List of the last <MaxBeaconFrames> beacons received from the network. | ||
| 76 | static 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 | */ | ||
| 81 | std::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. | ||
| 100 | void 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 | */ | ||
| 108 | static 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. | ||
| 120 | void 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 | |||
| 139 | void 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 | |||
| 161 | static 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 | */ | ||
| 249 | void 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 | ||
| 269 | void 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 | */ | ||
| 300 | void 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 | |||
| 326 | static 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. | ||
| 338 | void 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 | */ | ||
| 625 | static 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 | */ |
| 643 | void OnClientConnected(u16 network_node_id) { | 962 | void 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 | ||
| 657 | const Interface::FunctionInfo FunctionTable[] = { | 977 | const 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>; | |||
| 42 | enum class NetworkStatus { | 42 | enum 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 | |||
| 85 | static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset."); | 86 | static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset."); |
| 86 | static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size."); | 87 | static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size."); |
| 87 | 88 | ||
| 89 | /// Additional block tag ids in the Beacon and Association Response frames | ||
| 90 | enum 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 | |||
| 88 | class NWM_UDS final : public Interface { | 100 | class NWM_UDS final : public Interface { |
| 89 | public: | 101 | public: |
| 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 | ||
| 328 | std::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 { | |||
| 17 | using MacAddress = std::array<u8, 6>; | 17 | using MacAddress = std::array<u8, 6>; |
| 18 | constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32}; | 18 | constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32}; |
| 19 | 19 | ||
| 20 | /// Additional block tag ids in the Beacon frames | ||
| 21 | enum 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 | ||
| 136 | static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size."); | 125 | static_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. | ||
| 140 | struct 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 | */ |
| 162 | std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes); | 137 | std::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 | */ | ||
| 168 | std::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 | |||
| 9 | namespace Service { | ||
| 10 | namespace NWM { | ||
| 11 | |||
| 12 | // Note: These values were taken from a packet capture of an o3DS XL | ||
| 13 | // broadcasting a Super Smash Bros. 4 lobby. | ||
| 14 | constexpr u16 DefaultExtraCapabilities = 0x0431; | ||
| 15 | |||
| 16 | std::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 | |||
| 26 | AuthenticationSeq 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 | */ | ||
| 39 | static 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 | |||
| 58 | std::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 | |||
| 78 | std::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 | |||
| 13 | namespace Service { | ||
| 14 | namespace NWM { | ||
| 15 | |||
| 16 | /// Sequence number of the 802.11 authentication frames. | ||
| 17 | enum class AuthenticationSeq : u16 { SEQ1 = 1, SEQ2 = 2 }; | ||
| 18 | |||
| 19 | enum class AuthAlgorithm : u16 { OpenSystem = 0 }; | ||
| 20 | |||
| 21 | enum class AuthStatus : u16 { Successful = 0 }; | ||
| 22 | |||
| 23 | enum class AssocStatus : u16 { Successful = 0 }; | ||
| 24 | |||
| 25 | struct 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 | |||
| 31 | static_assert(sizeof(AuthenticationFrame) == 6, "AuthenticationFrame has wrong size"); | ||
| 32 | |||
| 33 | struct AssociationResponseFrame { | ||
| 34 | u16_le capabilities; | ||
| 35 | u16_le status_code; | ||
| 36 | u16_le assoc_id; | ||
| 37 | }; | ||
| 38 | |||
| 39 | static_assert(sizeof(AssociationResponseFrame) == 6, "AssociationResponseFrame has wrong size"); | ||
| 40 | |||
| 41 | /// Generates an 802.11 authentication frame, starting at the frame body. | ||
| 42 | std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq); | ||
| 43 | |||
| 44 | /// Returns the sequence number from the body of an Authentication frame. | ||
| 45 | AuthenticationSeq 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. | ||
| 49 | std::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. | ||
| 53 | std::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, | |||
| 266 | std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, | 267 | std::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 | ||
| 278 | std::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 | |||
| 299 | EtherType 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 | |||
| 307 | u16 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 | |||
| 314 | NodeInfo 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 | |||
| 329 | NodeInfo 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 | |||
| 339 | std::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 | |||
| 364 | EAPoLLogoffPacket 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 | ||
| 13 | namespace Service { | 14 | namespace Service { |
| @@ -67,6 +68,49 @@ struct DataFrameCryptoCTR { | |||
| 67 | 68 | ||
| 68 | static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); | 69 | static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); |
| 69 | 70 | ||
| 71 | struct 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 | |||
| 79 | static_assert(sizeof(EAPoLNodeInfo) == 0x28, "EAPoLNodeInfo has the wrong size"); | ||
| 80 | |||
| 81 | constexpr u16 EAPoLStartMagic = 0x201; | ||
| 82 | |||
| 83 | /* | ||
| 84 | * Nintendo EAPoLStartPacket, is used to initaliaze a connection between client and host | ||
| 85 | */ | ||
| 86 | struct 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 | |||
| 95 | static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size"); | ||
| 96 | |||
| 97 | constexpr u16 EAPoLLogoffMagic = 0x202; | ||
| 98 | |||
| 99 | struct 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 | |||
| 112 | static_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 | |||
| 74 | std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, | 118 | std::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 | */ | ||
| 126 | std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info); | ||
| 127 | |||
| 128 | /* | ||
| 129 | * Returns the EtherType of the specified 802.11 frame. | ||
| 130 | */ | ||
| 131 | EtherType 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 | */ | ||
| 137 | u16 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 | */ | ||
| 143 | NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame); | ||
| 144 | |||
| 145 | /* | ||
| 146 | * Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo. | ||
| 147 | */ | ||
| 148 | NodeInfo 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 | */ | ||
| 155 | std::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 | */ | ||
| 161 | EAPoLLogoffPacket 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 |
| 33 | constexpr ResultCode ERR_NAME_CONTAINS_NUL(7, ErrorModule::SRV, ErrorSummary::WrongArgument, | 33 | constexpr ResultCode ERR_NAME_CONTAINS_NUL(7, ErrorModule::SRV, ErrorSummary::WrongArgument, |
| 34 | ErrorLevel::Permanent); // 0xD9006407 | 34 | ErrorLevel::Permanent); // 0xD9006407 |
| 35 | constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorDescription::AlreadyExists, ErrorModule::OS, | ||
| 36 | ErrorSummary::WrongArgument, | ||
| 37 | ErrorLevel::Permanent); // 0xD9001BFC | ||
| 35 | 38 | ||
| 36 | class ServiceManager { | 39 | class ServiceManager { |
| 37 | public: | 40 | public: |
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 | ||
| 188 | void 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 | |||
| 187 | SRV::SRV(std::shared_ptr<ServiceManager> service_manager) | 211 | SRV::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 { | |||
| 29 | Regs g_regs; | 29 | Regs g_regs; |
| 30 | 30 | ||
| 31 | /// 268MHz CPU clocks / 60Hz frames per second | 31 | /// 268MHz CPU clocks / 60Hz frames per second |
| 32 | const u64 frame_ticks = BASE_CLOCK_RATE_ARM11 / SCREEN_REFRESH_RATE; | 32 | const u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE_ARM11 / SCREEN_REFRESH_RATE); |
| 33 | /// Event id for CoreTiming | 33 | /// Event id for CoreTiming |
| 34 | static int vblank_event; | 34 | static 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 |
| 517 | static void VBlankCallback(u64 userdata, int cycles_late) { | 517 | static 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 | ||
| 94 | using Kernel::SharedPtr; | ||
| 95 | using Kernel::CodeSet; | 94 | using Kernel::CodeSet; |
| 95 | using Kernel::SharedPtr; | ||
| 96 | 96 | ||
| 97 | static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, | 97 | static 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 | ||
| 258 | ResultStatus AppLoader_THREEDSX::Load() { | 258 | ResultStatus 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 | ||
| 16 | using Kernel::SharedPtr; | ||
| 17 | using Kernel::CodeSet; | 16 | using Kernel::CodeSet; |
| 17 | using 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 | ||
| 385 | ResultStatus AppLoader_ELF::Load() { | 385 | ResultStatus 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 | ||
| 35 | private: | 35 | private: |
| 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 | ||
| 17 | namespace Kernel { | 18 | namespace Kernel { |
| 18 | struct AddressMapping; | 19 | struct AddressMapping; |
| 19 | } | 20 | class 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 | |||
| 171 | protected: | 196 | protected: |
| 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 | */ |
| 187 | std::unique_ptr<AppLoader> GetLoader(const std::string& filename); | 212 | std::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 | ||
| 25 | namespace Loader { | 30 | namespace Loader { |
| 26 | 31 | ||
| 27 | static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs | 32 | static const u64 UPDATE_MASK = 0x0000000e00000000; |
| 28 | static 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 | */ | ||
| 36 | static 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 | */ | ||
| 49 | static 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 | ||
| 109 | FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { | 34 | FileType 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 | ||
| 49 | static 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 | |||
| 124 | std::pair<boost::optional<u32>, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode() { | 68 | std::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 | ||
| 136 | ResultStatus AppLoader_NCCH::LoadExec() { | 81 | ResultStatus 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 | ||
| 197 | ResultStatus 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 | |||
| 248 | ResultStatus 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 | |||
| 321 | void AppLoader_NCCH::ParseRegionLockoutInfo() { | 148 | void 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 | ||
| 338 | ResultStatus AppLoader_NCCH::Load() { | 165 | ResultStatus 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 | ||
| 366 | ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) { | 208 | ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) { |
| 367 | return LoadSectionExeFS(".code", buffer); | 209 | return overlay_ncch->LoadSectionExeFS(".code", buffer); |
| 368 | } | 210 | } |
| 369 | 211 | ||
| 370 | ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) { | 212 | ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) { |
| 371 | return LoadSectionExeFS("icon", buffer); | 213 | return overlay_ncch->LoadSectionExeFS("icon", buffer); |
| 372 | } | 214 | } |
| 373 | 215 | ||
| 374 | ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) { | 216 | ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) { |
| 375 | return LoadSectionExeFS("banner", buffer); | 217 | return overlay_ncch->LoadSectionExeFS("banner", buffer); |
| 376 | } | 218 | } |
| 377 | 219 | ||
| 378 | ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) { | 220 | ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) { |
| 379 | return LoadSectionExeFS("logo", buffer); | 221 | return overlay_ncch->LoadSectionExeFS("logo", buffer); |
| 380 | } | 222 | } |
| 381 | 223 | ||
| 382 | ResultStatus AppLoader_NCCH::ReadProgramId(u64& out_program_id) { | 224 | ResultStatus 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 | ||
| 394 | ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, | 232 | ResultStatus 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... | 237 | ResultStatus 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) | 245 | ResultStatus 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 | |||
| 16 | struct 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 | |||
| 48 | static_assert(sizeof(NCCH_Header) == 0x200, "NCCH header structure size is wrong"); | ||
| 49 | |||
| 50 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 51 | // ExeFS (executable file system) headers | ||
| 52 | |||
| 53 | struct ExeFs_SectionHeader { | ||
| 54 | char name[8]; | ||
| 55 | u32 offset; | ||
| 56 | u32 size; | ||
| 57 | }; | ||
| 58 | |||
| 59 | struct 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 | |||
| 68 | struct ExHeader_SystemInfoFlags { | ||
| 69 | u8 reserved[5]; | ||
| 70 | u8 flag; | ||
| 71 | u8 remaster_version[2]; | ||
| 72 | }; | ||
| 73 | |||
| 74 | struct ExHeader_CodeSegmentInfo { | ||
| 75 | u32 address; | ||
| 76 | u32 num_max_pages; | ||
| 77 | u32 code_size; | ||
| 78 | }; | ||
| 79 | |||
| 80 | struct 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 | |||
| 91 | struct ExHeader_DependencyList { | ||
| 92 | u8 program_id[0x30][8]; | ||
| 93 | }; | ||
| 94 | |||
| 95 | struct ExHeader_SystemInfo { | ||
| 96 | u64 save_data_size; | ||
| 97 | u8 jump_id[8]; | ||
| 98 | u8 reserved_2[0x30]; | ||
| 99 | }; | ||
| 100 | |||
| 101 | struct 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 | |||
| 109 | struct 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 | |||
| 128 | struct ExHeader_ARM11_KernelCaps { | ||
| 129 | u32_le descriptors[28]; | ||
| 130 | u8 reserved[0x10]; | ||
| 131 | }; | ||
| 132 | |||
| 133 | struct ExHeader_ARM9_AccessControl { | ||
| 134 | u8 descriptors[15]; | ||
| 135 | u8 descversion; | ||
| 136 | }; | ||
| 137 | |||
| 138 | struct 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 | |||
| 154 | static_assert(sizeof(ExHeader_Header) == 0x800, "ExHeader structure size is wrong"); | ||
| 155 | |||
| 156 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 157 | // Loader namespace | 14 | // Loader namespace |
| 158 | 15 | ||
| 159 | namespace Loader { | 16 | namespace Loader { |
| @@ -162,7 +19,8 @@ namespace Loader { | |||
| 162 | class AppLoader_NCCH final : public AppLoader { | 19 | class AppLoader_NCCH final : public AppLoader { |
| 163 | public: | 20 | public: |
| 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 | ||
| 211 | private: | 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 | ||
| 62 | private: | ||
| 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 | ||
| 135 | ResultStatus AppLoader_NRO::Load() { | 135 | ResultStatus 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 | ||
| 36 | private: | 36 | private: |
| 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 | ||
| 147 | ResultStatus AppLoader_NSO::Load() { | 147 | ResultStatus 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 | ||
| 37 | private: | 37 | private: |
| 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 | ||
| 18 | namespace Memory { | 22 | namespace Memory { |
| 19 | 23 | ||
| 20 | enum class PageType { | 24 | static std::array<u8, Memory::VRAM_SIZE> vram; |
| 21 | /// Page is unmapped and should cause an access error. | 25 | static 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 | |||
| 35 | struct SpecialRegion { | ||
| 36 | VAddr base; | ||
| 37 | u64 size; | ||
| 38 | MMIORegionPointer handler; | ||
| 39 | }; | ||
| 40 | 26 | ||
| 41 | /** | 27 | static 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 | 29 | void 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(); |
| 47 | struct 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`. | 36 | PageTable* GetCurrentPageTable() { |
| 51 | */ | 37 | return current_page_table; |
| 52 | std::map<u64, u8*> pointers; | 38 | } |
| 53 | 39 | ||
| 54 | /** | 40 | static 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 | ||
| 74 | static PageTable main_page_table; | ||
| 75 | /// Currently active page table | ||
| 76 | static PageTable* current_page_table = &main_page_table; | ||
| 77 | |||
| 78 | //std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() { | ||
| 79 | // return ¤t_page_table->pointers; | ||
| 80 | //} | ||
| 81 | |||
| 82 | static 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 | ||
| 103 | void InitMemoryMap() { | 61 | void 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 | |||
| 109 | void 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 | ||
| 115 | void MapIoRegion(VAddr base, u64 size, MMIORegionPointer mmio_handler) { | 67 | void 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 | ||
| 123 | void UnmapRegion(VAddr base, u64 size) { | 75 | void 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 | */ |
| 133 | static u8* GetPointerFromVMA(VAddr vaddr) { | 85 | static 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 | */ | ||
| 114 | static 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 | */ |
| 161 | static MMIORegionPointer GetMMIOHandler(VAddr vaddr) { | 121 | static 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 | ||
| 131 | static 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 | |||
| 171 | template <typename T> | 136 | template <typename T> |
| 172 | T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr); | 137 | T 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 | ||
| 249 | bool IsValidVirtualAddress(const VAddr vaddr) { | 220 | bool 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 | ||
| 241 | bool IsValidVirtualAddress(const VAddr vaddr) { | ||
| 242 | return IsValidVirtualAddress(*Kernel::g_current_process, vaddr); | ||
| 243 | } | ||
| 244 | |||
| 268 | bool IsValidPhysicalAddress(const PAddr paddr) { | 245 | bool 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 | ||
| 273 | u8* GetPointer(const VAddr vaddr) { | 249 | u8* 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 | ||
| 301 | u8* GetPhysicalPointer(PAddr address) { | 277 | u8* 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 | ||
| 307 | void RasterizerMarkRegionCached(PAddr start, u64 size, int count_delta) { | 337 | void 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 | ||
| 371 | void RasterizerFlushRegion(PAddr start, u64 size) { | 416 | void 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 | ||
| 377 | void RasterizerFlushAndInvalidateRegion(PAddr start, u64 size) { | 422 | void 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 | ||
| 385 | void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) { | 430 | void 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 | ||
| 436 | void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) { | 481 | void 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 | ||
| 538 | void 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 | |||
| 487 | void Write8(const VAddr addr, const u8 data) { | 542 | void 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 | ||
| 503 | void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) { | 558 | void 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 | ||
| 614 | void 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 | |||
| 554 | void ZeroBlock(const VAddr dest_addr, const size_t size) { | 618 | void 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) { | |||
| 721 | PAddr VirtualToPhysicalAddress(const VAddr addr) { | 789 | PAddr 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 | |||
| 16 | namespace Kernel { | ||
| 17 | class Process; | ||
| 18 | } | ||
| 12 | 19 | ||
| 13 | namespace Memory { | 20 | namespace Memory { |
| 14 | 21 | ||
| @@ -19,7 +26,60 @@ namespace Memory { | |||
| 19 | const int PAGE_BITS = 12; | 26 | const int PAGE_BITS = 12; |
| 20 | const u64 PAGE_SIZE = 1 << PAGE_BITS; | 27 | const u64 PAGE_SIZE = 1 << PAGE_BITS; |
| 21 | const u64 PAGE_MASK = PAGE_SIZE - 1; | 28 | const u64 PAGE_MASK = PAGE_SIZE - 1; |
| 22 | const size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (64 - PAGE_BITS); | 29 | const size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (32 - PAGE_BITS); |
| 30 | |||
| 31 | enum 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 | |||
| 46 | struct 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 | */ | ||
| 58 | struct 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 |
| 25 | enum : PAddr { | 85 | enum : 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 | ||
| 190 | void SetCurrentPageTable(PageTable* page_table); | ||
| 191 | PageTable* GetCurrentPageTable(); | ||
| 192 | |||
| 193 | /// Determines if the given VAddr is valid for the specified process. | ||
| 194 | bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr); | ||
| 129 | bool IsValidVirtualAddress(const VAddr addr); | 195 | bool IsValidVirtualAddress(const VAddr addr); |
| 196 | |||
| 130 | bool IsValidPhysicalAddress(const PAddr addr); | 197 | bool IsValidPhysicalAddress(const PAddr addr); |
| 131 | 198 | ||
| 132 | u8 Read8(VAddr addr); | 199 | u8 Read8(VAddr addr); |
| @@ -139,7 +206,11 @@ void Write16(VAddr addr, u16 data); | |||
| 139 | void Write32(VAddr addr, u32 data); | 206 | void Write32(VAddr addr, u32 data); |
| 140 | void Write64(VAddr addr, u64 data); | 207 | void Write64(VAddr addr, u64 data); |
| 141 | 208 | ||
| 209 | void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, | ||
| 210 | size_t size); | ||
| 142 | void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size); | 211 | void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size); |
| 212 | void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, | ||
| 213 | size_t size); | ||
| 143 | void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size); | 214 | void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size); |
| 144 | void ZeroBlock(const VAddr dest_addr, const size_t size); | 215 | void ZeroBlock(const VAddr dest_addr, const size_t size); |
| 145 | void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); | 216 | void 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 | */ |
| 175 | u8* GetPhysicalPointer(PAddr address); | 244 | u8* 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 | */ |
| 181 | void RasterizerMarkRegionCached(PAddr start, u64 size, int count_delta); | 250 | void 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 | */ |
| 186 | void RasterizerFlushRegion(PAddr start, u64 size); | 255 | void 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 | */ |
| 191 | void RasterizerFlushAndInvalidateRegion(PAddr start, u64 size); | 260 | void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size); |
| 192 | 261 | ||
| 193 | enum class FlushMode { | 262 | enum 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 | */ |
| 204 | void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode); | 273 | void 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 | ||
| 10 | namespace Memory { | 10 | namespace Memory { |
| 11 | 11 | ||
| 12 | void 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 | */ |
| 21 | void MapMemoryRegion(VAddr base, u64 size, u8* target); | 20 | void 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 | */ |
| 29 | void MapIoRegion(VAddr base, u64 size, MMIORegionPointer mmio_handler); | 29 | void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler); |
| 30 | 30 | ||
| 31 | void UnmapRegion(VAddr base, u64 size); | 31 | void 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 | ||
| 20 | namespace NativeButton { | 21 | namespace NativeButton { |
| @@ -70,7 +71,7 @@ enum Values { | |||
| 70 | static const std::array<const char*, NumAnalogs> mapping = {{ | 71 | static 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 | ||
| 75 | struct Values { | 76 | struct 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 { | |||
| 136 | static constexpr int REGION_VALUE_AUTO_SELECT = -1; | 143 | static constexpr int REGION_VALUE_AUTO_SELECT = -1; |
| 137 | 144 | ||
| 138 | void Apply(); | 145 | void 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 | ||
| 17 | namespace Core { | 21 | namespace Core { |
| @@ -28,23 +32,94 @@ static const char* CpuVendorToStr(Common::CPUVendor vendor) { | |||
| 28 | UNREACHABLE(); | 32 | UNREACHABLE(); |
| 29 | } | 33 | } |
| 30 | 34 | ||
| 35 | static 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 | |||
| 42 | u64 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 | |||
| 66 | u64 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 | |||
| 79 | std::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 | |||
| 31 | TelemetrySession::TelemetrySession() { | 90 | TelemetrySession::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 | */ | ||
| 43 | u64 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 | */ | ||
| 49 | u64 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 | */ | ||
| 58 | std::future<bool> VerifyLogin(std::string username, std::string token, std::function<void()> func); | ||
| 59 | |||
| 38 | } // namespace Core | 60 | } // namespace Core |