summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/scripts/common/post-upload.sh4
-rw-r--r--.ci/scripts/windows/upload.ps110
-rw-r--r--.gitignore2
-rw-r--r--.reuse/dep53
-rw-r--r--dist/english_plurals/README.md19
-rw-r--r--dist/english_plurals/en.ts67
m---------externals/dynarmic0
m---------externals/vcpkg0
-rw-r--r--src/core/CMakeLists.txt5
-rw-r--r--src/core/arm/arm_interface.h11
-rw-r--r--src/core/arm/cpu_interrupt_handler.cpp24
-rw-r--r--src/core/arm/cpu_interrupt_handler.h39
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp32
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h5
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp31
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.cpp31
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.h2
-rw-r--r--src/core/debugger/debugger.cpp9
-rw-r--r--src/core/hid/emulated_controller.cpp30
-rw-r--r--src/core/hid/emulated_controller.h7
-rw-r--r--src/core/hid/hid_types.h12
-rw-r--r--src/core/hle/kernel/kernel.cpp41
-rw-r--r--src/core/hle/kernel/kernel.h8
-rw-r--r--src/core/hle/kernel/physical_core.cpp29
-rw-r--r--src/core/hle/kernel/physical_core.h17
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp77
-rw-r--r--src/core/hle/service/ldn/errors.h12
-rw-r--r--src/core/hle/service/ldn/ldn.cpp436
-rw-r--r--src/core/hle/service/ldn/ldn.h6
-rw-r--r--src/core/hle/service/ldn/ldn_results.h27
-rw-r--r--src/core/hle/service/ldn/ldn_types.h284
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp16
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp12
-rw-r--r--src/yuzu/CMakeLists.txt14
-rw-r--r--src/yuzu/applets/qt_profile_select.cpp1
-rw-r--r--src/yuzu/bootmanager.cpp4
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui2
-rw-r--r--src/yuzu/configuration/configure_ui.cpp1
-rw-r--r--src/yuzu/main.cpp29
-rw-r--r--src/yuzu/main.h2
-rw-r--r--vcpkg.json6
44 files changed, 1098 insertions, 285 deletions
diff --git a/.ci/scripts/common/post-upload.sh b/.ci/scripts/common/post-upload.sh
index 7f910b2b3..0930b7a7b 100644
--- a/.ci/scripts/common/post-upload.sh
+++ b/.ci/scripts/common/post-upload.sh
@@ -8,8 +8,10 @@ cp LICENSE.txt "$DIR_NAME"
8cp README.md "$DIR_NAME" 8cp README.md "$DIR_NAME"
9 9
10if [[ -z "${NO_SOURCE_PACK}" ]]; then 10if [[ -z "${NO_SOURCE_PACK}" ]]; then
11 tar -cJvf "${REV_NAME}-source.tar.xz" src externals CMakeLists.txt README.md LICENSE.txt 11 git clone --depth 1 file://$(readlink -e .) ${REV_NAME}-source
12 tar -cJvf "${REV_NAME}-source.tar.xz" ${REV_NAME}-source
12 cp -v "${REV_NAME}-source.tar.xz" "$DIR_NAME" 13 cp -v "${REV_NAME}-source.tar.xz" "$DIR_NAME"
14 cp -v "${REV_NAME}-source.tar.xz" "${ARTIFACTS_DIR}/"
13fi 15fi
14 16
15tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$DIR_NAME" 17tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$DIR_NAME"
diff --git a/.ci/scripts/windows/upload.ps1 b/.ci/scripts/windows/upload.ps1
index f2368be6f..d463281de 100644
--- a/.ci/scripts/windows/upload.ps1
+++ b/.ci/scripts/windows/upload.ps1
@@ -42,14 +42,10 @@ mkdir $RELEASE_DIST
42mkdir $MSVC_SOURCE 42mkdir $MSVC_SOURCE
43mkdir "artifacts" 43mkdir "artifacts"
44 44
45$CURRENT_DIR = Convert-Path .
46
45# Build a tar.xz for the source of the release 47# Build a tar.xz for the source of the release
46Copy-Item .\LICENSE.txt -Destination $MSVC_SOURCE 48git clone --depth 1 file://$CURRENT_DIR $MSVC_SOURCE
47Copy-Item .\README.md -Destination $MSVC_SOURCE
48Copy-Item .\CMakeLists.txt -Destination $MSVC_SOURCE
49Copy-Item .\src -Recurse -Destination $MSVC_SOURCE
50Copy-Item .\externals -Recurse -Destination $MSVC_SOURCE
51Copy-Item .\dist -Recurse -Destination $MSVC_SOURCE
52Copy-Item .\CMakeModules -Recurse -Destination $MSVC_SOURCE
537z a -r -ttar $MSVC_SOURCE_TAR $MSVC_SOURCE 497z a -r -ttar $MSVC_SOURCE_TAR $MSVC_SOURCE
547z a -r -txz $MSVC_SOURCE_TARXZ $MSVC_SOURCE_TAR 507z a -r -txz $MSVC_SOURCE_TARXZ $MSVC_SOURCE_TAR
55 51
diff --git a/.gitignore b/.gitignore
index 6207765d8..cdf37962a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,7 +7,7 @@ doc-build/
7 7
8# Generated source files 8# Generated source files
9src/common/scm_rev.cpp 9src/common/scm_rev.cpp
10.travis.descriptor.json 10dist/english_plurals/generated_en.ts
11 11
12# Project/editor files 12# Project/editor files
13*.swp 13*.swp
diff --git a/.reuse/dep5 b/.reuse/dep5
index 5251870fb..5f31a4f55 100644
--- a/.reuse/dep5
+++ b/.reuse/dep5
@@ -2,7 +2,8 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
2Comment: It is best to use this file to record copyright information about 2Comment: It is best to use this file to record copyright information about
3 generated, binary and third party files 3 generated, binary and third party files
4 4
5Files: dist/icons/controller/*.png 5Files: dist/english_plurals/*
6 dist/icons/controller/*.png
6 dist/icons/overlay/*.png 7 dist/icons/overlay/*.png
7 dist/languages/* 8 dist/languages/*
8 dist/qt_themes/*/icons/index.theme 9 dist/qt_themes/*/icons/index.theme
diff --git a/dist/english_plurals/README.md b/dist/english_plurals/README.md
new file mode 100644
index 000000000..a4954f6a6
--- /dev/null
+++ b/dist/english_plurals/README.md
@@ -0,0 +1,19 @@
1# English Plurals
2
3Qt has "Translation Rules for Plurals", small example
4
5 // Take a source line like
6 tr("Building: %n shader(s)", "", i)
7
8 // i = 1:
9 Building: 1 shader
10 // i = 2:
11 Building: 2 shaders
12
13For yuzu the source language used is English, for all other languages handling of plurals is handled by Qt and the translation collaboration site. Handling plurals in the source language (English) requires special consideration.
14
15With CMake flag GENERATE_QT_TRANSLATION a generated_en.ts file is created from the source. It ignored by git (`.gitignore` in the project root). It is placed in this directory so that the relative refrences with the source code is correct.
16
17Having the plurals look nice isn't critical, and automation to use translation collaboration sites may require specifing the project language as "Pirate English", so this has been done manually.
18
19The en.ts in this directory is taken from a build, edited in Qt Linguist and then committed. As the code is in XML, using the tool is not strictly required.
diff --git a/dist/english_plurals/en.ts b/dist/english_plurals/en.ts
new file mode 100644
index 000000000..172cd4bba
--- /dev/null
+++ b/dist/english_plurals/en.ts
@@ -0,0 +1,67 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!DOCTYPE TS>
3<TS version="2.1" language="en_US" sourcelanguage="en_US">
4<context>
5 <name>GMainWindow</name>
6 <message numerus="yes">
7 <location filename="../../src/yuzu/main.cpp" line="2322"/>
8 <source>%n file(s) remaining</source>
9 <translation>
10 <numerusform>%n file remaining</numerusform>
11 <numerusform>%n files remaining</numerusform>
12 </translation>
13 </message>
14 <message numerus="yes">
15 <location filename="../../src/yuzu/main.cpp" line="2377"/>
16 <source>%n file(s) were newly installed
17</source>
18 <translation>
19 <numerusform>%n file was newly installed
20</numerusform>
21 <numerusform>%n files were newly installed
22</numerusform>
23 </translation>
24 </message>
25 <message numerus="yes">
26 <location filename="../../src/yuzu/main.cpp" line="2380"/>
27 <source>%n file(s) were overwritten
28</source>
29 <translation>
30 <numerusform>%n file was overwritten
31</numerusform>
32 <numerusform>%n were overwritten
33</numerusform>
34 </translation>
35 </message>
36 <message numerus="yes">
37 <location filename="../../src/yuzu/main.cpp" line="2382"/>
38 <source>%n file(s) failed to install
39</source>
40 <translation>
41 <numerusform>%n file failed to install
42</numerusform>
43 <numerusform>%n files failed to install
44</numerusform>
45 </translation>
46 </message>
47 <message numerus="yes">
48 <location filename="../../src/yuzu/main.cpp" line="3264"/>
49 <source>Building: %n shader(s)</source>
50 <translation>
51 <numerusform>Building: %n shader</numerusform>
52 <numerusform>Building: %n shaders</numerusform>
53 </translation>
54 </message>
55</context>
56<context>
57 <name>GameListSearchField</name>
58 <message numerus="yes">
59 <location filename="../../src/yuzu/game_list.cpp" line="87"/>
60 <source>%1 of %n result(s)</source>
61 <translation>
62 <numerusform>%1 of %n result</numerusform>
63 <numerusform>%1 of %n results</numerusform>
64 </translation>
65 </message>
66</context>
67</TS>
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject 91d1f944e3870e0f3c505b48f5ec00ca9a82b95 Subproject 2d4602a6516c67d547000d4c80bcc5f74976abd
diff --git a/externals/vcpkg b/externals/vcpkg
Subproject cef0b3ec767df6e83806899fe9525f6cf8d7bc9 Subproject 9b22b40c6c61bf0da2d46346dd44a11e90972cc
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 9dbe5bdca..4e39649a8 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -6,8 +6,6 @@ add_library(core STATIC
6 announce_multiplayer_session.h 6 announce_multiplayer_session.h
7 arm/arm_interface.h 7 arm/arm_interface.h
8 arm/arm_interface.cpp 8 arm/arm_interface.cpp
9 arm/cpu_interrupt_handler.cpp
10 arm/cpu_interrupt_handler.h
11 arm/dynarmic/arm_dynarmic_32.cpp 9 arm/dynarmic/arm_dynarmic_32.cpp
12 arm/dynarmic/arm_dynarmic_32.h 10 arm/dynarmic/arm_dynarmic_32.h
13 arm/dynarmic/arm_dynarmic_64.cpp 11 arm/dynarmic/arm_dynarmic_64.cpp
@@ -504,9 +502,10 @@ add_library(core STATIC
504 hle/service/jit/jit.h 502 hle/service/jit/jit.h
505 hle/service/lbl/lbl.cpp 503 hle/service/lbl/lbl.cpp
506 hle/service/lbl/lbl.h 504 hle/service/lbl/lbl.h
507 hle/service/ldn/errors.h 505 hle/service/ldn/ldn_results.h
508 hle/service/ldn/ldn.cpp 506 hle/service/ldn/ldn.cpp
509 hle/service/ldn/ldn.h 507 hle/service/ldn/ldn.h
508 hle/service/ldn/ldn_types.h
510 hle/service/ldr/ldr.cpp 509 hle/service/ldr/ldr.cpp
511 hle/service/ldr/ldr.h 510 hle/service/ldr/ldr.h
512 hle/service/lm/lm.cpp 511 hle/service/lm/lm.cpp
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 73f259525..7d62d030e 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -27,7 +27,6 @@ namespace Core {
27class System; 27class System;
28class CPUInterruptHandler; 28class CPUInterruptHandler;
29 29
30using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
31using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>; 30using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>;
32 31
33/// Generic ARMv8 CPU interface 32/// Generic ARMv8 CPU interface
@@ -36,10 +35,8 @@ public:
36 YUZU_NON_COPYABLE(ARM_Interface); 35 YUZU_NON_COPYABLE(ARM_Interface);
37 YUZU_NON_MOVEABLE(ARM_Interface); 36 YUZU_NON_MOVEABLE(ARM_Interface);
38 37
39 explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_, 38 explicit ARM_Interface(System& system_, bool uses_wall_clock_)
40 bool uses_wall_clock_) 39 : system{system_}, uses_wall_clock{uses_wall_clock_} {}
41 : system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{
42 uses_wall_clock_} {}
43 virtual ~ARM_Interface() = default; 40 virtual ~ARM_Interface() = default;
44 41
45 struct ThreadContext32 { 42 struct ThreadContext32 {
@@ -181,6 +178,9 @@ public:
181 /// Signal an interrupt and ask the core to halt as soon as possible. 178 /// Signal an interrupt and ask the core to halt as soon as possible.
182 virtual void SignalInterrupt() = 0; 179 virtual void SignalInterrupt() = 0;
183 180
181 /// Clear a previous interrupt.
182 virtual void ClearInterrupt() = 0;
183
184 struct BacktraceEntry { 184 struct BacktraceEntry {
185 std::string module; 185 std::string module;
186 u64 address; 186 u64 address;
@@ -208,7 +208,6 @@ public:
208protected: 208protected:
209 /// System context that this ARM interface is running under. 209 /// System context that this ARM interface is running under.
210 System& system; 210 System& system;
211 CPUInterrupts& interrupt_handlers;
212 const WatchpointArray* watchpoints; 211 const WatchpointArray* watchpoints;
213 bool uses_wall_clock; 212 bool uses_wall_clock;
214 213
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp
deleted file mode 100644
index 77b6194d7..000000000
--- a/src/core/arm/cpu_interrupt_handler.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/thread.h"
5#include "core/arm/cpu_interrupt_handler.h"
6
7namespace Core {
8
9CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {}
10
11CPUInterruptHandler::~CPUInterruptHandler() = default;
12
13void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) {
14 if (is_interrupted_) {
15 interrupt_event->Set();
16 }
17 is_interrupted = is_interrupted_;
18}
19
20void CPUInterruptHandler::AwaitInterrupt() {
21 interrupt_event->Wait();
22}
23
24} // namespace Core
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
deleted file mode 100644
index 286e12e53..000000000
--- a/src/core/arm/cpu_interrupt_handler.h
+++ /dev/null
@@ -1,39 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <atomic>
7#include <memory>
8
9namespace Common {
10class Event;
11}
12
13namespace Core {
14
15class CPUInterruptHandler {
16public:
17 CPUInterruptHandler();
18 ~CPUInterruptHandler();
19
20 CPUInterruptHandler(const CPUInterruptHandler&) = delete;
21 CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete;
22
23 CPUInterruptHandler(CPUInterruptHandler&&) = delete;
24 CPUInterruptHandler& operator=(CPUInterruptHandler&&) = delete;
25
26 bool IsInterrupted() const {
27 return is_interrupted;
28 }
29
30 void SetInterrupt(bool is_interrupted);
31
32 void AwaitInterrupt();
33
34private:
35 std::unique_ptr<Common::Event> interrupt_event;
36 std::atomic_bool is_interrupted{false};
37};
38
39} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index b8d2ce224..1638bc41d 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -11,7 +11,6 @@
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/page_table.h" 12#include "common/page_table.h"
13#include "common/settings.h" 13#include "common/settings.h"
14#include "core/arm/cpu_interrupt_handler.h"
15#include "core/arm/dynarmic/arm_dynarmic_32.h" 14#include "core/arm/dynarmic/arm_dynarmic_32.h"
16#include "core/arm/dynarmic/arm_dynarmic_cp15.h" 15#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
17#include "core/arm/dynarmic/arm_exclusive_monitor.h" 16#include "core/arm/dynarmic/arm_exclusive_monitor.h"
@@ -125,7 +124,9 @@ public:
125 } 124 }
126 125
127 void AddTicks(u64 ticks) override { 126 void AddTicks(u64 ticks) override {
128 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); 127 if (parent.uses_wall_clock) {
128 return;
129 }
129 130
130 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a 131 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
131 // rough approximation of the amount of executed ticks in the system, it may be thrown off 132 // rough approximation of the amount of executed ticks in the system, it may be thrown off
@@ -142,7 +143,12 @@ public:
142 } 143 }
143 144
144 u64 GetTicksRemaining() override { 145 u64 GetTicksRemaining() override {
145 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); 146 if (parent.uses_wall_clock) {
147 if (!IsInterrupted()) {
148 return minimum_run_cycles;
149 }
150 return 0U;
151 }
146 152
147 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); 153 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
148 } 154 }
@@ -168,11 +174,15 @@ public:
168 parent.jit.load()->HaltExecution(hr); 174 parent.jit.load()->HaltExecution(hr);
169 } 175 }
170 176
177 bool IsInterrupted() {
178 return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted();
179 }
180
171 ARM_Dynarmic_32& parent; 181 ARM_Dynarmic_32& parent;
172 Core::Memory::Memory& memory; 182 Core::Memory::Memory& memory;
173 std::size_t num_interpreted_instructions{}; 183 std::size_t num_interpreted_instructions{};
174 bool debugger_enabled{}; 184 bool debugger_enabled{};
175 static constexpr u64 minimum_run_cycles = 1000U; 185 static constexpr u64 minimum_run_cycles = 10000U;
176}; 186};
177 187
178std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* page_table) const { 188std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* page_table) const {
@@ -200,7 +210,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
200 210
201 // Timing 211 // Timing
202 config.wall_clock_cntpct = uses_wall_clock; 212 config.wall_clock_cntpct = uses_wall_clock;
203 config.enable_cycle_counting = !uses_wall_clock; 213 config.enable_cycle_counting = true;
204 214
205 // Code cache size 215 // Code cache size
206 config.code_cache_size = 512_MiB; 216 config.code_cache_size = 512_MiB;
@@ -311,11 +321,9 @@ void ARM_Dynarmic_32::RewindBreakpointInstruction() {
311 LoadContext(breakpoint_context); 321 LoadContext(breakpoint_context);
312} 322}
313 323
314ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, 324ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, bool uses_wall_clock_,
315 bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, 325 ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_)
316 std::size_t core_index_) 326 : ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks32>(*this)),
317 : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
318 cb(std::make_unique<DynarmicCallbacks32>(*this)),
319 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_}, 327 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_},
320 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, 328 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
321 null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {} 329 null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {}
@@ -394,6 +402,10 @@ void ARM_Dynarmic_32::SignalInterrupt() {
394 jit.load()->HaltExecution(break_loop); 402 jit.load()->HaltExecution(break_loop);
395} 403}
396 404
405void ARM_Dynarmic_32::ClearInterrupt() {
406 jit.load()->ClearHalt(break_loop);
407}
408
397void ARM_Dynarmic_32::ClearInstructionCache() { 409void ARM_Dynarmic_32::ClearInstructionCache() {
398 jit.load()->ClearCache(); 410 jit.load()->ClearCache();
399} 411}
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 346e9abf8..d24ba2289 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -28,8 +28,8 @@ class System;
28 28
29class ARM_Dynarmic_32 final : public ARM_Interface { 29class ARM_Dynarmic_32 final : public ARM_Interface {
30public: 30public:
31 ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_, 31 ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
32 ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_); 32 std::size_t core_index_);
33 ~ARM_Dynarmic_32() override; 33 ~ARM_Dynarmic_32() override;
34 34
35 void SetPC(u64 pc) override; 35 void SetPC(u64 pc) override;
@@ -56,6 +56,7 @@ public:
56 void LoadContext(const ThreadContext64& ctx) override {} 56 void LoadContext(const ThreadContext64& ctx) override {}
57 57
58 void SignalInterrupt() override; 58 void SignalInterrupt() override;
59 void ClearInterrupt() override;
59 void ClearExclusiveState() override; 60 void ClearExclusiveState() override;
60 61
61 void ClearInstructionCache() override; 62 void ClearInstructionCache() override;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 1a4d37cbc..921a5a734 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -10,7 +10,6 @@
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/page_table.h" 11#include "common/page_table.h"
12#include "common/settings.h" 12#include "common/settings.h"
13#include "core/arm/cpu_interrupt_handler.h"
14#include "core/arm/dynarmic/arm_dynarmic_64.h" 13#include "core/arm/dynarmic/arm_dynarmic_64.h"
15#include "core/arm/dynarmic/arm_exclusive_monitor.h" 14#include "core/arm/dynarmic/arm_exclusive_monitor.h"
16#include "core/core.h" 15#include "core/core.h"
@@ -166,7 +165,9 @@ public:
166 } 165 }
167 166
168 void AddTicks(u64 ticks) override { 167 void AddTicks(u64 ticks) override {
169 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); 168 if (parent.uses_wall_clock) {
169 return;
170 }
170 171
171 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a 172 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
172 // rough approximation of the amount of executed ticks in the system, it may be thrown off 173 // rough approximation of the amount of executed ticks in the system, it may be thrown off
@@ -181,7 +182,12 @@ public:
181 } 182 }
182 183
183 u64 GetTicksRemaining() override { 184 u64 GetTicksRemaining() override {
184 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); 185 if (parent.uses_wall_clock) {
186 if (!IsInterrupted()) {
187 return minimum_run_cycles;
188 }
189 return 0U;
190 }
185 191
186 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); 192 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
187 } 193 }
@@ -211,12 +217,16 @@ public:
211 parent.jit.load()->HaltExecution(hr); 217 parent.jit.load()->HaltExecution(hr);
212 } 218 }
213 219
220 bool IsInterrupted() {
221 return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted();
222 }
223
214 ARM_Dynarmic_64& parent; 224 ARM_Dynarmic_64& parent;
215 Core::Memory::Memory& memory; 225 Core::Memory::Memory& memory;
216 u64 tpidrro_el0 = 0; 226 u64 tpidrro_el0 = 0;
217 u64 tpidr_el0 = 0; 227 u64 tpidr_el0 = 0;
218 bool debugger_enabled{}; 228 bool debugger_enabled{};
219 static constexpr u64 minimum_run_cycles = 1000U; 229 static constexpr u64 minimum_run_cycles = 10000U;
220}; 230};
221 231
222std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* page_table, 232std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* page_table,
@@ -260,7 +270,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
260 270
261 // Timing 271 // Timing
262 config.wall_clock_cntpct = uses_wall_clock; 272 config.wall_clock_cntpct = uses_wall_clock;
263 config.enable_cycle_counting = !uses_wall_clock; 273 config.enable_cycle_counting = true;
264 274
265 // Code cache size 275 // Code cache size
266 config.code_cache_size = 512_MiB; 276 config.code_cache_size = 512_MiB;
@@ -371,10 +381,9 @@ void ARM_Dynarmic_64::RewindBreakpointInstruction() {
371 LoadContext(breakpoint_context); 381 LoadContext(breakpoint_context);
372} 382}
373 383
374ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, 384ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, bool uses_wall_clock_,
375 bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, 385 ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_)
376 std::size_t core_index_) 386 : ARM_Interface{system_, uses_wall_clock_},
377 : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
378 cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_}, 387 cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_},
379 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, 388 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
380 null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {} 389 null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {}
@@ -461,6 +470,10 @@ void ARM_Dynarmic_64::SignalInterrupt() {
461 jit.load()->HaltExecution(break_loop); 470 jit.load()->HaltExecution(break_loop);
462} 471}
463 472
473void ARM_Dynarmic_64::ClearInterrupt() {
474 jit.load()->ClearHalt(break_loop);
475}
476
464void ARM_Dynarmic_64::ClearInstructionCache() { 477void ARM_Dynarmic_64::ClearInstructionCache() {
465 jit.load()->ClearCache(); 478 jit.load()->ClearCache();
466} 479}
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index c77a83ad7..ed1a5eb96 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -20,14 +20,13 @@ class Memory;
20namespace Core { 20namespace Core {
21 21
22class DynarmicCallbacks64; 22class DynarmicCallbacks64;
23class CPUInterruptHandler;
24class DynarmicExclusiveMonitor; 23class DynarmicExclusiveMonitor;
25class System; 24class System;
26 25
27class ARM_Dynarmic_64 final : public ARM_Interface { 26class ARM_Dynarmic_64 final : public ARM_Interface {
28public: 27public:
29 ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_, 28 ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
30 ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_); 29 std::size_t core_index_);
31 ~ARM_Dynarmic_64() override; 30 ~ARM_Dynarmic_64() override;
32 31
33 void SetPC(u64 pc) override; 32 void SetPC(u64 pc) override;
@@ -50,6 +49,7 @@ public:
50 void LoadContext(const ThreadContext64& ctx) override; 49 void LoadContext(const ThreadContext64& ctx) override;
51 50
52 void SignalInterrupt() override; 51 void SignalInterrupt() override;
52 void ClearInterrupt() override;
53 void ClearExclusiveState() override; 53 void ClearExclusiveState() override;
54 54
55 void ClearInstructionCache() override; 55 void ClearInstructionCache() override;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
index e9123c13d..200efe4db 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
@@ -8,6 +8,10 @@
8#include "core/core.h" 8#include "core/core.h"
9#include "core/core_timing.h" 9#include "core/core_timing.h"
10 10
11#ifdef _MSC_VER
12#include <intrin.h>
13#endif
14
11using Callback = Dynarmic::A32::Coprocessor::Callback; 15using Callback = Dynarmic::A32::Coprocessor::Callback;
12using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord; 16using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord;
13using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords; 17using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords;
@@ -47,12 +51,31 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
47 switch (opc2) { 51 switch (opc2) {
48 case 4: 52 case 4:
49 // CP15_DATA_SYNC_BARRIER 53 // CP15_DATA_SYNC_BARRIER
50 // This is a dummy write, we ignore the value written here. 54 return Callback{
51 return &dummy_value; 55 [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
56#ifdef _MSC_VER
57 _mm_mfence();
58 _mm_lfence();
59#else
60 asm volatile("mfence\n\tlfence\n\t" : : : "memory");
61#endif
62 return 0;
63 },
64 std::nullopt,
65 };
52 case 5: 66 case 5:
53 // CP15_DATA_MEMORY_BARRIER 67 // CP15_DATA_MEMORY_BARRIER
54 // This is a dummy write, we ignore the value written here. 68 return Callback{
55 return &dummy_value; 69 [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
70#ifdef _MSC_VER
71 _mm_mfence();
72#else
73 asm volatile("mfence\n\t" : : : "memory");
74#endif
75 return 0;
76 },
77 std::nullopt,
78 };
56 } 79 }
57 } 80 }
58 81
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
index 5b2a51636..d90b3e568 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
@@ -35,6 +35,8 @@ public:
35 ARM_Dynarmic_32& parent; 35 ARM_Dynarmic_32& parent;
36 u32 uprw = 0; 36 u32 uprw = 0;
37 u32 uro = 0; 37 u32 uro = 0;
38
39 friend class ARM_Dynarmic_32;
38}; 40};
39 41
40} // namespace Core 42} // namespace Core
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index ac64d2f9d..e42bdd17d 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -15,6 +15,7 @@
15#include "core/debugger/debugger_interface.h" 15#include "core/debugger/debugger_interface.h"
16#include "core/debugger/gdbstub.h" 16#include "core/debugger/gdbstub.h"
17#include "core/hle/kernel/global_scheduler_context.h" 17#include "core/hle/kernel/global_scheduler_context.h"
18#include "core/hle/kernel/k_scheduler.h"
18 19
19template <typename Readable, typename Buffer, typename Callback> 20template <typename Readable, typename Buffer, typename Callback>
20static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) { 21static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
@@ -230,13 +231,12 @@ private:
230 } 231 }
231 232
232 void PauseEmulation() { 233 void PauseEmulation() {
234 Kernel::KScopedSchedulerLock sl{system.Kernel()};
235
233 // Put all threads to sleep on next scheduler round. 236 // Put all threads to sleep on next scheduler round.
234 for (auto* thread : ThreadList()) { 237 for (auto* thread : ThreadList()) {
235 thread->RequestSuspend(Kernel::SuspendType::Debug); 238 thread->RequestSuspend(Kernel::SuspendType::Debug);
236 } 239 }
237
238 // Signal an interrupt so that scheduler will fire.
239 system.Kernel().InterruptAllPhysicalCores();
240 } 240 }
241 241
242 void ResumeEmulation(Kernel::KThread* except = nullptr) { 242 void ResumeEmulation(Kernel::KThread* except = nullptr) {
@@ -253,7 +253,8 @@ private:
253 253
254 template <typename Callback> 254 template <typename Callback>
255 void MarkResumed(Callback&& cb) { 255 void MarkResumed(Callback&& cb) {
256 std::scoped_lock lk{connection_lock}; 256 Kernel::KScopedSchedulerLock sl{system.Kernel()};
257 std::scoped_lock cl{connection_lock};
257 stopped = false; 258 stopped = false;
258 cb(); 259 cb();
259 } 260 }
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 8c3895937..049602e7d 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/thread.h"
4#include "core/hid/emulated_controller.h" 5#include "core/hid/emulated_controller.h"
5#include "core/hid/input_converter.h" 6#include "core/hid/input_converter.h"
6 7
@@ -84,18 +85,19 @@ void EmulatedController::ReloadFromSettings() {
84 motion_params[index] = Common::ParamPackage(player.motions[index]); 85 motion_params[index] = Common::ParamPackage(player.motions[index]);
85 } 86 }
86 87
88 controller.colors_state.fullkey = {
89 .body = GetNpadColor(player.body_color_left),
90 .button = GetNpadColor(player.button_color_left),
91 };
87 controller.colors_state.left = { 92 controller.colors_state.left = {
88 .body = player.body_color_left, 93 .body = GetNpadColor(player.body_color_left),
89 .button = player.button_color_left, 94 .button = GetNpadColor(player.button_color_left),
90 }; 95 };
91 96 controller.colors_state.left = {
92 controller.colors_state.right = { 97 .body = GetNpadColor(player.body_color_right),
93 .body = player.body_color_right, 98 .button = GetNpadColor(player.button_color_right),
94 .button = player.button_color_right,
95 }; 99 };
96 100
97 controller.colors_state.fullkey = controller.colors_state.left;
98
99 // Other or debug controller should always be a pro controller 101 // Other or debug controller should always be a pro controller
100 if (npad_id_type != NpadIdType::Other) { 102 if (npad_id_type != NpadIdType::Other) {
101 SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); 103 SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
@@ -949,6 +951,9 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
949 // Send a slight vibration to test for rumble support 951 // Send a slight vibration to test for rumble support
950 output_devices[device_index]->SetVibration(test_vibration); 952 output_devices[device_index]->SetVibration(test_vibration);
951 953
954 // Wait for about 15ms to ensure the controller is ready for the stop command
955 std::this_thread::sleep_for(std::chrono::milliseconds(15));
956
952 // Stop any vibration and return the result 957 // Stop any vibration and return the result
953 return output_devices[device_index]->SetVibration(zero_vibration) == 958 return output_devices[device_index]->SetVibration(zero_vibration) ==
954 Common::Input::VibrationError::None; 959 Common::Input::VibrationError::None;
@@ -1310,6 +1315,15 @@ const CameraState& EmulatedController::GetCamera() const {
1310 return controller.camera_state; 1315 return controller.camera_state;
1311} 1316}
1312 1317
1318NpadColor EmulatedController::GetNpadColor(u32 color) {
1319 return {
1320 .r = static_cast<u8>((color >> 16) & 0xFF),
1321 .g = static_cast<u8>((color >> 8) & 0xFF),
1322 .b = static_cast<u8>(color & 0xFF),
1323 .a = 0xff,
1324 };
1325}
1326
1313void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { 1327void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
1314 std::scoped_lock lock{callback_mutex}; 1328 std::scoped_lock lock{callback_mutex};
1315 for (const auto& poller_pair : callback_list) { 1329 for (const auto& poller_pair : callback_list) {
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 823c1700c..cbd7c26d3 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -425,6 +425,13 @@ private:
425 void SetCamera(const Common::Input::CallbackStatus& callback); 425 void SetCamera(const Common::Input::CallbackStatus& callback);
426 426
427 /** 427 /**
428 * Converts a color format from bgra to rgba
429 * @param color in bgra format
430 * @return NpadColor in rgba format
431 */
432 NpadColor GetNpadColor(u32 color);
433
434 /**
428 * Triggers a callback that something has changed on the controller status 435 * Triggers a callback that something has changed on the controller status
429 * @param type Input type of the event to trigger 436 * @param type Input type of the event to trigger
430 * @param is_service_update indicates if this event should only be sent to HID services 437 * @param is_service_update indicates if this event should only be sent to HID services
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index e49223016..e3b1cfbc6 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -327,10 +327,18 @@ struct TouchState {
327}; 327};
328static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); 328static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
329 329
330struct NpadColor {
331 u8 r{};
332 u8 g{};
333 u8 b{};
334 u8 a{};
335};
336static_assert(sizeof(NpadColor) == 4, "NpadColor is an invalid size");
337
330// This is nn::hid::NpadControllerColor 338// This is nn::hid::NpadControllerColor
331struct NpadControllerColor { 339struct NpadControllerColor {
332 u32 body{}; 340 NpadColor body{};
333 u32 button{}; 341 NpadColor button{};
334}; 342};
335static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size"); 343static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size");
336 344
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index f4072e1c3..ce7fa8275 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -17,7 +17,6 @@
17#include "common/thread.h" 17#include "common/thread.h"
18#include "common/thread_worker.h" 18#include "common/thread_worker.h"
19#include "core/arm/arm_interface.h" 19#include "core/arm/arm_interface.h"
20#include "core/arm/cpu_interrupt_handler.h"
21#include "core/arm/exclusive_monitor.h" 20#include "core/arm/exclusive_monitor.h"
22#include "core/core.h" 21#include "core/core.h"
23#include "core/core_timing.h" 22#include "core/core_timing.h"
@@ -82,7 +81,7 @@ struct KernelCore::Impl {
82 81
83 void InitializeCores() { 82 void InitializeCores() {
84 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { 83 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
85 cores[core_id].Initialize((*current_process).Is64BitProcess()); 84 cores[core_id]->Initialize((*current_process).Is64BitProcess());
86 system.Memory().SetCurrentPageTable(*current_process, core_id); 85 system.Memory().SetCurrentPageTable(*current_process, core_id);
87 } 86 }
88 } 87 }
@@ -100,7 +99,9 @@ struct KernelCore::Impl {
100 next_user_process_id = KProcess::ProcessIDMin; 99 next_user_process_id = KProcess::ProcessIDMin;
101 next_thread_id = 1; 100 next_thread_id = 1;
102 101
103 cores.clear(); 102 for (auto& core : cores) {
103 core = nullptr;
104 }
104 105
105 global_handle_table->Finalize(); 106 global_handle_table->Finalize();
106 global_handle_table.reset(); 107 global_handle_table.reset();
@@ -199,7 +200,7 @@ struct KernelCore::Impl {
199 const s32 core{static_cast<s32>(i)}; 200 const s32 core{static_cast<s32>(i)};
200 201
201 schedulers[i] = std::make_unique<Kernel::KScheduler>(system.Kernel()); 202 schedulers[i] = std::make_unique<Kernel::KScheduler>(system.Kernel());
202 cores.emplace_back(i, system, *schedulers[i], interrupts); 203 cores[i] = std::make_unique<Kernel::PhysicalCore>(i, system, *schedulers[i]);
203 204
204 auto* main_thread{Kernel::KThread::Create(system.Kernel())}; 205 auto* main_thread{Kernel::KThread::Create(system.Kernel())};
205 main_thread->SetName(fmt::format("MainThread:{}", core)); 206 main_thread->SetName(fmt::format("MainThread:{}", core));
@@ -761,7 +762,7 @@ struct KernelCore::Impl {
761 std::unordered_set<KAutoObject*> registered_in_use_objects; 762 std::unordered_set<KAutoObject*> registered_in_use_objects;
762 763
763 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; 764 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
764 std::vector<Kernel::PhysicalCore> cores; 765 std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores;
765 766
766 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others 767 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
767 std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES}; 768 std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES};
@@ -785,7 +786,6 @@ struct KernelCore::Impl {
785 Common::ThreadWorker service_threads_manager; 786 Common::ThreadWorker service_threads_manager;
786 787
787 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads; 788 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads;
788 std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
789 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; 789 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
790 790
791 bool is_multicore{}; 791 bool is_multicore{};
@@ -874,11 +874,11 @@ const Kernel::KScheduler& KernelCore::Scheduler(std::size_t id) const {
874} 874}
875 875
876Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { 876Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) {
877 return impl->cores[id]; 877 return *impl->cores[id];
878} 878}
879 879
880const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { 880const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
881 return impl->cores[id]; 881 return *impl->cores[id];
882} 882}
883 883
884size_t KernelCore::CurrentPhysicalCoreIndex() const { 884size_t KernelCore::CurrentPhysicalCoreIndex() const {
@@ -890,11 +890,11 @@ size_t KernelCore::CurrentPhysicalCoreIndex() const {
890} 890}
891 891
892Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { 892Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
893 return impl->cores[CurrentPhysicalCoreIndex()]; 893 return *impl->cores[CurrentPhysicalCoreIndex()];
894} 894}
895 895
896const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { 896const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
897 return impl->cores[CurrentPhysicalCoreIndex()]; 897 return *impl->cores[CurrentPhysicalCoreIndex()];
898} 898}
899 899
900Kernel::KScheduler* KernelCore::CurrentScheduler() { 900Kernel::KScheduler* KernelCore::CurrentScheduler() {
@@ -906,15 +906,6 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() {
906 return impl->schedulers[core_id].get(); 906 return impl->schedulers[core_id].get();
907} 907}
908 908
909std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() {
910 return impl->interrupts;
911}
912
913const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts()
914 const {
915 return impl->interrupts;
916}
917
918Kernel::TimeManager& KernelCore::TimeManager() { 909Kernel::TimeManager& KernelCore::TimeManager() {
919 return impl->time_manager; 910 return impl->time_manager;
920} 911}
@@ -939,24 +930,18 @@ const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
939 return *impl->global_object_list_container; 930 return *impl->global_object_list_container;
940} 931}
941 932
942void KernelCore::InterruptAllPhysicalCores() {
943 for (auto& physical_core : impl->cores) {
944 physical_core.Interrupt();
945 }
946}
947
948void KernelCore::InvalidateAllInstructionCaches() { 933void KernelCore::InvalidateAllInstructionCaches() {
949 for (auto& physical_core : impl->cores) { 934 for (auto& physical_core : impl->cores) {
950 physical_core.ArmInterface().ClearInstructionCache(); 935 physical_core->ArmInterface().ClearInstructionCache();
951 } 936 }
952} 937}
953 938
954void KernelCore::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) { 939void KernelCore::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) {
955 for (auto& physical_core : impl->cores) { 940 for (auto& physical_core : impl->cores) {
956 if (!physical_core.IsInitialized()) { 941 if (!physical_core->IsInitialized()) {
957 continue; 942 continue;
958 } 943 }
959 physical_core.ArmInterface().InvalidateCacheRange(addr, size); 944 physical_core->ArmInterface().InvalidateCacheRange(addr, size);
960 } 945 }
961} 946}
962 947
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 6c7cf6af2..bcf016a97 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -9,14 +9,12 @@
9#include <string> 9#include <string>
10#include <unordered_map> 10#include <unordered_map>
11#include <vector> 11#include <vector>
12#include "core/arm/cpu_interrupt_handler.h"
13#include "core/hardware_properties.h" 12#include "core/hardware_properties.h"
14#include "core/hle/kernel/k_auto_object.h" 13#include "core/hle/kernel/k_auto_object.h"
15#include "core/hle/kernel/k_slab_heap.h" 14#include "core/hle/kernel/k_slab_heap.h"
16#include "core/hle/kernel/svc_common.h" 15#include "core/hle/kernel/svc_common.h"
17 16
18namespace Core { 17namespace Core {
19class CPUInterruptHandler;
20class ExclusiveMonitor; 18class ExclusiveMonitor;
21class System; 19class System;
22} // namespace Core 20} // namespace Core
@@ -183,12 +181,6 @@ public:
183 181
184 const KAutoObjectWithListContainer& ObjectListContainer() const; 182 const KAutoObjectWithListContainer& ObjectListContainer() const;
185 183
186 std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts();
187
188 const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts() const;
189
190 void InterruptAllPhysicalCores();
191
192 void InvalidateAllInstructionCaches(); 184 void InvalidateAllInstructionCaches();
193 185
194 void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size); 186 void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size);
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index 6e7dacf97..d4375962f 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -1,7 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/arm/cpu_interrupt_handler.h"
5#include "core/arm/dynarmic/arm_dynarmic_32.h" 4#include "core/arm/dynarmic/arm_dynarmic_32.h"
6#include "core/arm/dynarmic/arm_dynarmic_64.h" 5#include "core/arm/dynarmic/arm_dynarmic_64.h"
7#include "core/core.h" 6#include "core/core.h"
@@ -11,16 +10,14 @@
11 10
12namespace Kernel { 11namespace Kernel {
13 12
14PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, 13PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_)
15 Core::CPUInterrupts& interrupts_) 14 : core_index{core_index_}, system{system_}, scheduler{scheduler_} {
16 : core_index{core_index_}, system{system_}, scheduler{scheduler_},
17 interrupts{interrupts_}, guard{std::make_unique<std::mutex>()} {
18#ifdef ARCHITECTURE_x86_64 15#ifdef ARCHITECTURE_x86_64
19 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with 16 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with
20 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. 17 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
21 auto& kernel = system.Kernel(); 18 auto& kernel = system.Kernel();
22 arm_interface = std::make_unique<Core::ARM_Dynarmic_64>( 19 arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
23 system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); 20 system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
24#else 21#else
25#error Platform not supported yet. 22#error Platform not supported yet.
26#endif 23#endif
@@ -34,7 +31,7 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
34 if (!is_64_bit) { 31 if (!is_64_bit) {
35 // We already initialized a 64-bit core, replace with a 32-bit one. 32 // We already initialized a 64-bit core, replace with a 32-bit one.
36 arm_interface = std::make_unique<Core::ARM_Dynarmic_32>( 33 arm_interface = std::make_unique<Core::ARM_Dynarmic_32>(
37 system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); 34 system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
38 } 35 }
39#else 36#else
40#error Platform not supported yet. 37#error Platform not supported yet.
@@ -47,24 +44,26 @@ void PhysicalCore::Run() {
47} 44}
48 45
49void PhysicalCore::Idle() { 46void PhysicalCore::Idle() {
50 interrupts[core_index].AwaitInterrupt(); 47 std::unique_lock lk{guard};
48 on_interrupt.wait(lk, [this] { return is_interrupted; });
51} 49}
52 50
53bool PhysicalCore::IsInterrupted() const { 51bool PhysicalCore::IsInterrupted() const {
54 return interrupts[core_index].IsInterrupted(); 52 return is_interrupted;
55} 53}
56 54
57void PhysicalCore::Interrupt() { 55void PhysicalCore::Interrupt() {
58 guard->lock(); 56 std::unique_lock lk{guard};
59 interrupts[core_index].SetInterrupt(true); 57 is_interrupted = true;
60 arm_interface->SignalInterrupt(); 58 arm_interface->SignalInterrupt();
61 guard->unlock(); 59 on_interrupt.notify_all();
62} 60}
63 61
64void PhysicalCore::ClearInterrupt() { 62void PhysicalCore::ClearInterrupt() {
65 guard->lock(); 63 std::unique_lock lk{guard};
66 interrupts[core_index].SetInterrupt(false); 64 is_interrupted = false;
67 guard->unlock(); 65 arm_interface->ClearInterrupt();
66 on_interrupt.notify_all();
68} 67}
69 68
70} // namespace Kernel 69} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index 898d1e5db..2fc8d4be2 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -14,7 +14,6 @@ class KScheduler;
14} // namespace Kernel 14} // namespace Kernel
15 15
16namespace Core { 16namespace Core {
17class CPUInterruptHandler;
18class ExclusiveMonitor; 17class ExclusiveMonitor;
19class System; 18class System;
20} // namespace Core 19} // namespace Core
@@ -23,15 +22,11 @@ namespace Kernel {
23 22
24class PhysicalCore { 23class PhysicalCore {
25public: 24public:
26 PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, 25 PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_);
27 Core::CPUInterrupts& interrupts_);
28 ~PhysicalCore(); 26 ~PhysicalCore();
29 27
30 PhysicalCore(const PhysicalCore&) = delete; 28 YUZU_NON_COPYABLE(PhysicalCore);
31 PhysicalCore& operator=(const PhysicalCore&) = delete; 29 YUZU_NON_MOVEABLE(PhysicalCore);
32
33 PhysicalCore(PhysicalCore&&) = default;
34 PhysicalCore& operator=(PhysicalCore&&) = delete;
35 30
36 /// Initialize the core for the specified parameters. 31 /// Initialize the core for the specified parameters.
37 void Initialize(bool is_64_bit); 32 void Initialize(bool is_64_bit);
@@ -86,9 +81,11 @@ private:
86 const std::size_t core_index; 81 const std::size_t core_index;
87 Core::System& system; 82 Core::System& system;
88 Kernel::KScheduler& scheduler; 83 Kernel::KScheduler& scheduler;
89 Core::CPUInterrupts& interrupts; 84
90 std::unique_ptr<std::mutex> guard; 85 std::mutex guard;
86 std::condition_variable on_interrupt;
91 std::unique_ptr<Core::ARM_Interface> arm_interface; 87 std::unique_ptr<Core::ARM_Interface> arm_interface;
88 bool is_interrupted;
92}; 89};
93 90
94} // namespace Kernel 91} // namespace Kernel
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 3c28dee76..cb29004e8 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -163,28 +163,51 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
163 } 163 }
164 LOG_DEBUG(Service_HID, "Npad connected {}", npad_id); 164 LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
165 const auto controller_type = controller.device->GetNpadStyleIndex(); 165 const auto controller_type = controller.device->GetNpadStyleIndex();
166 const auto& body_colors = controller.device->GetColors();
167 const auto& battery_level = controller.device->GetBattery();
166 auto* shared_memory = controller.shared_memory; 168 auto* shared_memory = controller.shared_memory;
167 if (controller_type == Core::HID::NpadStyleIndex::None) { 169 if (controller_type == Core::HID::NpadStyleIndex::None) {
168 controller.styleset_changed_event->GetWritableEvent().Signal(); 170 controller.styleset_changed_event->GetWritableEvent().Signal();
169 return; 171 return;
170 } 172 }
173
174 // Reset memory values
171 shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; 175 shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None;
172 shared_memory->device_type.raw = 0; 176 shared_memory->device_type.raw = 0;
173 shared_memory->system_properties.raw = 0; 177 shared_memory->system_properties.raw = 0;
178 shared_memory->joycon_color.attribute = ColorAttribute::NoController;
179 shared_memory->joycon_color.attribute = ColorAttribute::NoController;
180 shared_memory->fullkey_color = {};
181 shared_memory->joycon_color.left = {};
182 shared_memory->joycon_color.right = {};
183 shared_memory->battery_level_dual = {};
184 shared_memory->battery_level_left = {};
185 shared_memory->battery_level_right = {};
186
174 switch (controller_type) { 187 switch (controller_type) {
175 case Core::HID::NpadStyleIndex::None: 188 case Core::HID::NpadStyleIndex::None:
176 ASSERT(false); 189 ASSERT(false);
177 break; 190 break;
178 case Core::HID::NpadStyleIndex::ProController: 191 case Core::HID::NpadStyleIndex::ProController:
192 shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
193 shared_memory->fullkey_color.fullkey = body_colors.fullkey;
194 shared_memory->battery_level_dual = battery_level.dual.battery_level;
179 shared_memory->style_tag.fullkey.Assign(1); 195 shared_memory->style_tag.fullkey.Assign(1);
180 shared_memory->device_type.fullkey.Assign(1); 196 shared_memory->device_type.fullkey.Assign(1);
181 shared_memory->system_properties.is_vertical.Assign(1); 197 shared_memory->system_properties.is_vertical.Assign(1);
182 shared_memory->system_properties.use_plus.Assign(1); 198 shared_memory->system_properties.use_plus.Assign(1);
183 shared_memory->system_properties.use_minus.Assign(1); 199 shared_memory->system_properties.use_minus.Assign(1);
200 shared_memory->system_properties.is_charging_joy_dual.Assign(
201 battery_level.dual.is_charging);
184 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController; 202 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController;
185 shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1); 203 shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
186 break; 204 break;
187 case Core::HID::NpadStyleIndex::Handheld: 205 case Core::HID::NpadStyleIndex::Handheld:
206 shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
207 shared_memory->joycon_color.attribute = ColorAttribute::Ok;
208 shared_memory->fullkey_color.fullkey = body_colors.fullkey;
209 shared_memory->joycon_color.left = body_colors.left;
210 shared_memory->joycon_color.right = body_colors.right;
188 shared_memory->style_tag.handheld.Assign(1); 211 shared_memory->style_tag.handheld.Assign(1);
189 shared_memory->device_type.handheld_left.Assign(1); 212 shared_memory->device_type.handheld_left.Assign(1);
190 shared_memory->device_type.handheld_right.Assign(1); 213 shared_memory->device_type.handheld_right.Assign(1);
@@ -192,47 +215,86 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
192 shared_memory->system_properties.use_plus.Assign(1); 215 shared_memory->system_properties.use_plus.Assign(1);
193 shared_memory->system_properties.use_minus.Assign(1); 216 shared_memory->system_properties.use_minus.Assign(1);
194 shared_memory->system_properties.use_directional_buttons.Assign(1); 217 shared_memory->system_properties.use_directional_buttons.Assign(1);
218 shared_memory->system_properties.is_charging_joy_dual.Assign(
219 battery_level.left.is_charging);
220 shared_memory->system_properties.is_charging_joy_left.Assign(
221 battery_level.left.is_charging);
222 shared_memory->system_properties.is_charging_joy_right.Assign(
223 battery_level.right.is_charging);
195 shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; 224 shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
196 shared_memory->applet_nfc_xcd.applet_footer.type = 225 shared_memory->applet_nfc_xcd.applet_footer.type =
197 AppletFooterUiType::HandheldJoyConLeftJoyConRight; 226 AppletFooterUiType::HandheldJoyConLeftJoyConRight;
198 shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1); 227 shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1);
199 break; 228 break;
200 case Core::HID::NpadStyleIndex::JoyconDual: 229 case Core::HID::NpadStyleIndex::JoyconDual:
230 shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
231 shared_memory->joycon_color.attribute = ColorAttribute::Ok;
201 shared_memory->style_tag.joycon_dual.Assign(1); 232 shared_memory->style_tag.joycon_dual.Assign(1);
202 if (controller.is_dual_left_connected) { 233 if (controller.is_dual_left_connected) {
234 shared_memory->joycon_color.left = body_colors.left;
235 shared_memory->battery_level_left = battery_level.left.battery_level;
203 shared_memory->device_type.joycon_left.Assign(1); 236 shared_memory->device_type.joycon_left.Assign(1);
204 shared_memory->system_properties.use_minus.Assign(1); 237 shared_memory->system_properties.use_minus.Assign(1);
238 shared_memory->system_properties.is_charging_joy_left.Assign(
239 battery_level.left.is_charging);
205 shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1); 240 shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1);
206 } 241 }
207 if (controller.is_dual_right_connected) { 242 if (controller.is_dual_right_connected) {
243 shared_memory->joycon_color.right = body_colors.right;
244 shared_memory->battery_level_right = battery_level.right.battery_level;
208 shared_memory->device_type.joycon_right.Assign(1); 245 shared_memory->device_type.joycon_right.Assign(1);
209 shared_memory->system_properties.use_plus.Assign(1); 246 shared_memory->system_properties.use_plus.Assign(1);
247 shared_memory->system_properties.is_charging_joy_right.Assign(
248 battery_level.right.is_charging);
210 shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1); 249 shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1);
211 } 250 }
212 shared_memory->system_properties.use_directional_buttons.Assign(1); 251 shared_memory->system_properties.use_directional_buttons.Assign(1);
213 shared_memory->system_properties.is_vertical.Assign(1); 252 shared_memory->system_properties.is_vertical.Assign(1);
214 shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; 253 shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
254
215 if (controller.is_dual_left_connected && controller.is_dual_right_connected) { 255 if (controller.is_dual_left_connected && controller.is_dual_right_connected) {
216 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDual; 256 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDual;
257 shared_memory->fullkey_color.fullkey = body_colors.left;
258 shared_memory->battery_level_dual = battery_level.left.battery_level;
259 shared_memory->system_properties.is_charging_joy_dual.Assign(
260 battery_level.left.is_charging);
217 } else if (controller.is_dual_left_connected) { 261 } else if (controller.is_dual_left_connected) {
218 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly; 262 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly;
263 shared_memory->fullkey_color.fullkey = body_colors.left;
264 shared_memory->battery_level_dual = battery_level.left.battery_level;
265 shared_memory->system_properties.is_charging_joy_dual.Assign(
266 battery_level.left.is_charging);
219 } else { 267 } else {
220 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualRightOnly; 268 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualRightOnly;
269 shared_memory->fullkey_color.fullkey = body_colors.right;
270 shared_memory->battery_level_dual = battery_level.right.battery_level;
271 shared_memory->system_properties.is_charging_joy_dual.Assign(
272 battery_level.right.is_charging);
221 } 273 }
222 break; 274 break;
223 case Core::HID::NpadStyleIndex::JoyconLeft: 275 case Core::HID::NpadStyleIndex::JoyconLeft:
276 shared_memory->joycon_color.attribute = ColorAttribute::Ok;
277 shared_memory->joycon_color.left = body_colors.left;
278 shared_memory->battery_level_dual = battery_level.left.battery_level;
224 shared_memory->style_tag.joycon_left.Assign(1); 279 shared_memory->style_tag.joycon_left.Assign(1);
225 shared_memory->device_type.joycon_left.Assign(1); 280 shared_memory->device_type.joycon_left.Assign(1);
226 shared_memory->system_properties.is_horizontal.Assign(1); 281 shared_memory->system_properties.is_horizontal.Assign(1);
227 shared_memory->system_properties.use_minus.Assign(1); 282 shared_memory->system_properties.use_minus.Assign(1);
283 shared_memory->system_properties.is_charging_joy_left.Assign(
284 battery_level.left.is_charging);
228 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal; 285 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
229 shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1); 286 shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
230 break; 287 break;
231 case Core::HID::NpadStyleIndex::JoyconRight: 288 case Core::HID::NpadStyleIndex::JoyconRight:
289 shared_memory->joycon_color.attribute = ColorAttribute::Ok;
290 shared_memory->joycon_color.right = body_colors.right;
291 shared_memory->battery_level_right = battery_level.right.battery_level;
232 shared_memory->style_tag.joycon_right.Assign(1); 292 shared_memory->style_tag.joycon_right.Assign(1);
233 shared_memory->device_type.joycon_right.Assign(1); 293 shared_memory->device_type.joycon_right.Assign(1);
234 shared_memory->system_properties.is_horizontal.Assign(1); 294 shared_memory->system_properties.is_horizontal.Assign(1);
235 shared_memory->system_properties.use_plus.Assign(1); 295 shared_memory->system_properties.use_plus.Assign(1);
296 shared_memory->system_properties.is_charging_joy_right.Assign(
297 battery_level.right.is_charging);
236 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal; 298 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
237 shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1); 299 shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1);
238 break; 300 break;
@@ -269,21 +331,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
269 break; 331 break;
270 } 332 }
271 333
272 const auto& body_colors = controller.device->GetColors();
273
274 shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
275 shared_memory->fullkey_color.fullkey = body_colors.fullkey;
276
277 shared_memory->joycon_color.attribute = ColorAttribute::Ok;
278 shared_memory->joycon_color.left = body_colors.left;
279 shared_memory->joycon_color.right = body_colors.right;
280
281 // TODO: Investigate when we should report all batery types
282 const auto& battery_level = controller.device->GetBattery();
283 shared_memory->battery_level_dual = battery_level.dual.battery_level;
284 shared_memory->battery_level_left = battery_level.left.battery_level;
285 shared_memory->battery_level_right = battery_level.right.battery_level;
286
287 controller.is_connected = true; 334 controller.is_connected = true;
288 controller.device->Connect(); 335 controller.device->Connect();
289 SignalStyleSetChangedEvent(npad_id); 336 SignalStyleSetChangedEvent(npad_id);
diff --git a/src/core/hle/service/ldn/errors.h b/src/core/hle/service/ldn/errors.h
deleted file mode 100644
index 972a74806..000000000
--- a/src/core/hle/service/ldn/errors.h
+++ /dev/null
@@ -1,12 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Service::LDN {
9
10constexpr Result ERROR_DISABLED{ErrorModule::LDN, 22};
11
12} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index 125d4dc4c..c11daff54 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -3,11 +3,15 @@
3 3
4#include <memory> 4#include <memory>
5 5
6#include "core/hle/ipc_helpers.h" 6#include "core/core.h"
7#include "core/hle/result.h"
8#include "core/hle/service/ldn/errors.h"
9#include "core/hle/service/ldn/ldn.h" 7#include "core/hle/service/ldn/ldn.h"
10#include "core/hle/service/sm/sm.h" 8#include "core/hle/service/ldn/ldn_results.h"
9#include "core/hle/service/ldn/ldn_types.h"
10#include "core/internal_network/network.h"
11#include "core/internal_network/network_interface.h"
12
13// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent
14#undef CreateEvent
11 15
12namespace Service::LDN { 16namespace Service::LDN {
13 17
@@ -100,74 +104,418 @@ class IUserLocalCommunicationService final
100 : public ServiceFramework<IUserLocalCommunicationService> { 104 : public ServiceFramework<IUserLocalCommunicationService> {
101public: 105public:
102 explicit IUserLocalCommunicationService(Core::System& system_) 106 explicit IUserLocalCommunicationService(Core::System& system_)
103 : ServiceFramework{system_, "IUserLocalCommunicationService"} { 107 : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew},
108 service_context{system, "IUserLocalCommunicationService"}, room_network{
109 system_.GetRoomNetwork()} {
104 // clang-format off 110 // clang-format off
105 static const FunctionInfo functions[] = { 111 static const FunctionInfo functions[] = {
106 {0, &IUserLocalCommunicationService::GetState, "GetState"}, 112 {0, &IUserLocalCommunicationService::GetState, "GetState"},
107 {1, nullptr, "GetNetworkInfo"}, 113 {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"},
108 {2, nullptr, "GetIpv4Address"}, 114 {2, nullptr, "GetIpv4Address"},
109 {3, nullptr, "GetDisconnectReason"}, 115 {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"},
110 {4, nullptr, "GetSecurityParameter"}, 116 {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"},
111 {5, nullptr, "GetNetworkConfig"}, 117 {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"},
112 {100, nullptr, "AttachStateChangeEvent"}, 118 {100, &IUserLocalCommunicationService::AttachStateChangeEvent, "AttachStateChangeEvent"},
113 {101, nullptr, "GetNetworkInfoLatestUpdate"}, 119 {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"},
114 {102, nullptr, "Scan"}, 120 {102, &IUserLocalCommunicationService::Scan, "Scan"},
115 {103, nullptr, "ScanPrivate"}, 121 {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"},
116 {104, nullptr, "SetWirelessControllerRestriction"}, 122 {104, nullptr, "SetWirelessControllerRestriction"},
117 {200, nullptr, "OpenAccessPoint"}, 123 {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"},
118 {201, nullptr, "CloseAccessPoint"}, 124 {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"},
119 {202, nullptr, "CreateNetwork"}, 125 {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"},
120 {203, nullptr, "CreateNetworkPrivate"}, 126 {203, &IUserLocalCommunicationService::CreateNetworkPrivate, "CreateNetworkPrivate"},
121 {204, nullptr, "DestroyNetwork"}, 127 {204, &IUserLocalCommunicationService::DestroyNetwork, "DestroyNetwork"},
122 {205, nullptr, "Reject"}, 128 {205, nullptr, "Reject"},
123 {206, nullptr, "SetAdvertiseData"}, 129 {206, &IUserLocalCommunicationService::SetAdvertiseData, "SetAdvertiseData"},
124 {207, nullptr, "SetStationAcceptPolicy"}, 130 {207, &IUserLocalCommunicationService::SetStationAcceptPolicy, "SetStationAcceptPolicy"},
125 {208, nullptr, "AddAcceptFilterEntry"}, 131 {208, &IUserLocalCommunicationService::AddAcceptFilterEntry, "AddAcceptFilterEntry"},
126 {209, nullptr, "ClearAcceptFilter"}, 132 {209, nullptr, "ClearAcceptFilter"},
127 {300, nullptr, "OpenStation"}, 133 {300, &IUserLocalCommunicationService::OpenStation, "OpenStation"},
128 {301, nullptr, "CloseStation"}, 134 {301, &IUserLocalCommunicationService::CloseStation, "CloseStation"},
129 {302, nullptr, "Connect"}, 135 {302, &IUserLocalCommunicationService::Connect, "Connect"},
130 {303, nullptr, "ConnectPrivate"}, 136 {303, nullptr, "ConnectPrivate"},
131 {304, nullptr, "Disconnect"}, 137 {304, &IUserLocalCommunicationService::Disconnect, "Disconnect"},
132 {400, nullptr, "Initialize"}, 138 {400, &IUserLocalCommunicationService::Initialize, "Initialize"},
133 {401, nullptr, "Finalize"}, 139 {401, &IUserLocalCommunicationService::Finalize, "Finalize"},
134 {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, // 7.0.0+ 140 {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"},
135 }; 141 };
136 // clang-format on 142 // clang-format on
137 143
138 RegisterHandlers(functions); 144 RegisterHandlers(functions);
145
146 state_change_event =
147 service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent");
148 }
149
150 ~IUserLocalCommunicationService() {
151 service_context.CloseEvent(state_change_event);
152 }
153
154 void OnEventFired() {
155 state_change_event->GetWritableEvent().Signal();
139 } 156 }
140 157
141 void GetState(Kernel::HLERequestContext& ctx) { 158 void GetState(Kernel::HLERequestContext& ctx) {
159 State state = State::Error;
160 LOG_WARNING(Service_LDN, "(STUBBED) called, state = {}", state);
161
162 IPC::ResponseBuilder rb{ctx, 3};
163 rb.Push(ResultSuccess);
164 rb.PushEnum(state);
165 }
166
167 void GetNetworkInfo(Kernel::HLERequestContext& ctx) {
168 const auto write_buffer_size = ctx.GetWriteBufferSize();
169
170 if (write_buffer_size != sizeof(NetworkInfo)) {
171 LOG_ERROR(Service_LDN, "Invalid buffer size {}", write_buffer_size);
172 IPC::ResponseBuilder rb{ctx, 2};
173 rb.Push(ResultBadInput);
174 return;
175 }
176
177 NetworkInfo network_info{};
178 const auto rc = ResultSuccess;
179 if (rc.IsError()) {
180 LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
181 IPC::ResponseBuilder rb{ctx, 2};
182 rb.Push(rc);
183 return;
184 }
185
186 LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}",
187 network_info.common.ssid.GetStringValue(), network_info.ldn.node_count);
188
189 ctx.WriteBuffer<NetworkInfo>(network_info);
190 IPC::ResponseBuilder rb{ctx, 2};
191 rb.Push(rc);
192 }
193
194 void GetDisconnectReason(Kernel::HLERequestContext& ctx) {
195 const auto disconnect_reason = DisconnectReason::None;
196
197 LOG_WARNING(Service_LDN, "(STUBBED) called, disconnect_reason={}", disconnect_reason);
198
199 IPC::ResponseBuilder rb{ctx, 3};
200 rb.Push(ResultSuccess);
201 rb.PushEnum(disconnect_reason);
202 }
203
204 void GetSecurityParameter(Kernel::HLERequestContext& ctx) {
205 SecurityParameter security_parameter{};
206 NetworkInfo info{};
207 const Result rc = ResultSuccess;
208
209 if (rc.IsError()) {
210 LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
211 IPC::ResponseBuilder rb{ctx, 2};
212 rb.Push(rc);
213 return;
214 }
215
216 security_parameter.session_id = info.network_id.session_id;
217 std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(),
218 sizeof(SecurityParameter::data));
219
142 LOG_WARNING(Service_LDN, "(STUBBED) called"); 220 LOG_WARNING(Service_LDN, "(STUBBED) called");
143 221
222 IPC::ResponseBuilder rb{ctx, 10};
223 rb.Push(rc);
224 rb.PushRaw<SecurityParameter>(security_parameter);
225 }
226
227 void GetNetworkConfig(Kernel::HLERequestContext& ctx) {
228 NetworkConfig config{};
229 NetworkInfo info{};
230 const Result rc = ResultSuccess;
231
232 if (rc.IsError()) {
233 LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw);
234 IPC::ResponseBuilder rb{ctx, 2};
235 rb.Push(rc);
236 return;
237 }
238
239 config.intent_id = info.network_id.intent_id;
240 config.channel = info.common.channel;
241 config.node_count_max = info.ldn.node_count_max;
242 config.local_communication_version = info.ldn.nodes[0].local_communication_version;
243
244 LOG_WARNING(Service_LDN,
245 "(STUBBED) called, intent_id={}/{}, channel={}, node_count_max={}, "
246 "local_communication_version={}",
247 config.intent_id.local_communication_id, config.intent_id.scene_id,
248 config.channel, config.node_count_max, config.local_communication_version);
249
250 IPC::ResponseBuilder rb{ctx, 10};
251 rb.Push(rc);
252 rb.PushRaw<NetworkConfig>(config);
253 }
254
255 void AttachStateChangeEvent(Kernel::HLERequestContext& ctx) {
256 LOG_INFO(Service_LDN, "called");
257
258 IPC::ResponseBuilder rb{ctx, 2, 1};
259 rb.Push(ResultSuccess);
260 rb.PushCopyObjects(state_change_event->GetReadableEvent());
261 }
262
263 void GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx) {
264 const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0);
265 const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate);
266
267 if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) {
268 LOG_ERROR(Service_LDN, "Invalid buffer size {}, {}", network_buffer_size,
269 node_buffer_count);
270 IPC::ResponseBuilder rb{ctx, 2};
271 rb.Push(ResultBadInput);
272 return;
273 }
274
275 NetworkInfo info;
276 std::vector<NodeLatestUpdate> latest_update(node_buffer_count);
277
278 const auto rc = ResultSuccess;
279 if (rc.IsError()) {
280 LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
281 IPC::ResponseBuilder rb{ctx, 2};
282 rb.Push(rc);
283 return;
284 }
285
286 LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}",
287 info.common.ssid.GetStringValue(), info.ldn.node_count);
288
289 ctx.WriteBuffer(info, 0);
290 ctx.WriteBuffer(latest_update, 1);
291
292 IPC::ResponseBuilder rb{ctx, 2};
293 rb.Push(ResultSuccess);
294 }
295
296 void Scan(Kernel::HLERequestContext& ctx) {
297 ScanImpl(ctx);
298 }
299
300 void ScanPrivate(Kernel::HLERequestContext& ctx) {
301 ScanImpl(ctx, true);
302 }
303
304 void ScanImpl(Kernel::HLERequestContext& ctx, bool is_private = false) {
305 IPC::RequestParser rp{ctx};
306 const auto channel{rp.PopEnum<WifiChannel>()};
307 const auto scan_filter{rp.PopRaw<ScanFilter>()};
308
309 const std::size_t network_info_size = ctx.GetWriteBufferSize() / sizeof(NetworkInfo);
310
311 if (network_info_size == 0) {
312 LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size);
313 IPC::ResponseBuilder rb{ctx, 2};
314 rb.Push(ResultBadInput);
315 return;
316 }
317
318 u16 count = 0;
319 std::vector<NetworkInfo> network_infos(network_info_size);
320
321 LOG_WARNING(Service_LDN,
322 "(STUBBED) called, channel={}, filter_scan_flag={}, filter_network_type={}",
323 channel, scan_filter.flag, scan_filter.network_type);
324
325 ctx.WriteBuffer(network_infos);
326
144 IPC::ResponseBuilder rb{ctx, 3}; 327 IPC::ResponseBuilder rb{ctx, 3};
328 rb.Push(ResultSuccess);
329 rb.Push<u32>(count);
330 }
331
332 void OpenAccessPoint(Kernel::HLERequestContext& ctx) {
333 LOG_WARNING(Service_LDN, "(STUBBED) called");
334
335 IPC::ResponseBuilder rb{ctx, 2};
336 rb.Push(ResultSuccess);
337 }
338
339 void CloseAccessPoint(Kernel::HLERequestContext& ctx) {
340 LOG_WARNING(Service_LDN, "(STUBBED) called");
341
342 IPC::ResponseBuilder rb{ctx, 2};
343 rb.Push(ResultSuccess);
344 }
345
346 void CreateNetwork(Kernel::HLERequestContext& ctx) {
347 IPC::RequestParser rp{ctx};
348 struct Parameters {
349 SecurityConfig security_config;
350 UserConfig user_config;
351 INSERT_PADDING_WORDS_NOINIT(1);
352 NetworkConfig network_config;
353 };
354 static_assert(sizeof(Parameters) == 0x98, "Parameters has incorrect size.");
355
356 const auto parameters{rp.PopRaw<Parameters>()};
357
358 LOG_WARNING(Service_LDN,
359 "(STUBBED) called, passphrase_size={}, security_mode={}, "
360 "local_communication_version={}",
361 parameters.security_config.passphrase_size,
362 parameters.security_config.security_mode,
363 parameters.network_config.local_communication_version);
364
365 IPC::ResponseBuilder rb{ctx, 2};
366 rb.Push(ResultSuccess);
367 }
368
369 void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) {
370 IPC::RequestParser rp{ctx};
371 struct Parameters {
372 SecurityConfig security_config;
373 SecurityParameter security_parameter;
374 UserConfig user_config;
375 NetworkConfig network_config;
376 };
377 static_assert(sizeof(Parameters) == 0xB8, "Parameters has incorrect size.");
378
379 const auto parameters{rp.PopRaw<Parameters>()};
380
381 LOG_WARNING(Service_LDN,
382 "(STUBBED) called, passphrase_size={}, security_mode={}, "
383 "local_communication_version={}",
384 parameters.security_config.passphrase_size,
385 parameters.security_config.security_mode,
386 parameters.network_config.local_communication_version);
387
388 IPC::ResponseBuilder rb{ctx, 2};
389 rb.Push(ResultSuccess);
390 }
391
392 void DestroyNetwork(Kernel::HLERequestContext& ctx) {
393 LOG_WARNING(Service_LDN, "(STUBBED) called");
394
395 IPC::ResponseBuilder rb{ctx, 2};
396 rb.Push(ResultSuccess);
397 }
398
399 void SetAdvertiseData(Kernel::HLERequestContext& ctx) {
400 std::vector<u8> read_buffer = ctx.ReadBuffer();
401
402 LOG_WARNING(Service_LDN, "(STUBBED) called, size {}", read_buffer.size());
403
404 IPC::ResponseBuilder rb{ctx, 2};
405 rb.Push(ResultSuccess);
406 }
407
408 void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) {
409 LOG_WARNING(Service_LDN, "(STUBBED) called");
410
411 IPC::ResponseBuilder rb{ctx, 2};
412 rb.Push(ResultSuccess);
413 }
145 414
146 // Indicate a network error, as we do not actually emulate LDN 415 void AddAcceptFilterEntry(Kernel::HLERequestContext& ctx) {
147 rb.Push(static_cast<u32>(State::Error)); 416 LOG_WARNING(Service_LDN, "(STUBBED) called");
148 417
418 IPC::ResponseBuilder rb{ctx, 2};
419 rb.Push(ResultSuccess);
420 }
421
422 void OpenStation(Kernel::HLERequestContext& ctx) {
423 LOG_WARNING(Service_LDN, "(STUBBED) called");
424
425 IPC::ResponseBuilder rb{ctx, 2};
426 rb.Push(ResultSuccess);
427 }
428
429 void CloseStation(Kernel::HLERequestContext& ctx) {
430 LOG_WARNING(Service_LDN, "(STUBBED) called");
431
432 IPC::ResponseBuilder rb{ctx, 2};
433 rb.Push(ResultSuccess);
434 }
435
436 void Connect(Kernel::HLERequestContext& ctx) {
437 IPC::RequestParser rp{ctx};
438 struct Parameters {
439 SecurityConfig security_config;
440 UserConfig user_config;
441 u32 local_communication_version;
442 u32 option;
443 };
444 static_assert(sizeof(Parameters) == 0x7C, "Parameters has incorrect size.");
445
446 const auto parameters{rp.PopRaw<Parameters>()};
447
448 LOG_WARNING(Service_LDN,
449 "(STUBBED) called, passphrase_size={}, security_mode={}, "
450 "local_communication_version={}",
451 parameters.security_config.passphrase_size,
452 parameters.security_config.security_mode,
453 parameters.local_communication_version);
454
455 const std::vector<u8> read_buffer = ctx.ReadBuffer();
456 NetworkInfo network_info{};
457
458 if (read_buffer.size() != sizeof(NetworkInfo)) {
459 LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!");
460 IPC::ResponseBuilder rb{ctx, 2};
461 rb.Push(ResultBadInput);
462 return;
463 }
464
465 std::memcpy(&network_info, read_buffer.data(), read_buffer.size());
466
467 IPC::ResponseBuilder rb{ctx, 2};
468 rb.Push(ResultSuccess);
469 }
470
471 void Disconnect(Kernel::HLERequestContext& ctx) {
472 LOG_WARNING(Service_LDN, "(STUBBED) called");
473
474 IPC::ResponseBuilder rb{ctx, 2};
475 rb.Push(ResultSuccess);
476 }
477 void Initialize(Kernel::HLERequestContext& ctx) {
478 LOG_WARNING(Service_LDN, "(STUBBED) called");
479
480 const auto rc = InitializeImpl(ctx);
481
482 IPC::ResponseBuilder rb{ctx, 2};
483 rb.Push(rc);
484 }
485
486 void Finalize(Kernel::HLERequestContext& ctx) {
487 LOG_WARNING(Service_LDN, "(STUBBED) called");
488
489 is_initialized = false;
490
491 IPC::ResponseBuilder rb{ctx, 2};
149 rb.Push(ResultSuccess); 492 rb.Push(ResultSuccess);
150 } 493 }
151 494
152 void Initialize2(Kernel::HLERequestContext& ctx) { 495 void Initialize2(Kernel::HLERequestContext& ctx) {
153 LOG_DEBUG(Service_LDN, "called"); 496 LOG_WARNING(Service_LDN, "(STUBBED) called");
154 497
155 is_initialized = true; 498 const auto rc = InitializeImpl(ctx);
156 499
157 IPC::ResponseBuilder rb{ctx, 2}; 500 IPC::ResponseBuilder rb{ctx, 2};
158 rb.Push(ERROR_DISABLED); 501 rb.Push(rc);
502 }
503
504 Result InitializeImpl(Kernel::HLERequestContext& ctx) {
505 const auto network_interface = Network::GetSelectedNetworkInterface();
506 if (!network_interface) {
507 LOG_ERROR(Service_LDN, "No network interface is set");
508 return ResultAirplaneModeEnabled;
509 }
510
511 is_initialized = true;
512 // TODO (flTobi): Change this to ResultSuccess when LDN is fully implemented
513 return ResultAirplaneModeEnabled;
159 } 514 }
160 515
161private: 516 KernelHelpers::ServiceContext service_context;
162 enum class State { 517 Kernel::KEvent* state_change_event;
163 None, 518 Network::RoomNetwork& room_network;
164 Initialized,
165 AccessPointOpened,
166 AccessPointCreated,
167 StationOpened,
168 StationConnected,
169 Error,
170 };
171 519
172 bool is_initialized{}; 520 bool is_initialized{};
173}; 521};
@@ -273,7 +621,7 @@ public:
273 LOG_WARNING(Service_LDN, "(STUBBED) called"); 621 LOG_WARNING(Service_LDN, "(STUBBED) called");
274 622
275 IPC::ResponseBuilder rb{ctx, 2}; 623 IPC::ResponseBuilder rb{ctx, 2};
276 rb.Push(ERROR_DISABLED); 624 rb.Push(ResultDisabled);
277 } 625 }
278}; 626};
279 627
diff --git a/src/core/hle/service/ldn/ldn.h b/src/core/hle/service/ldn/ldn.h
index a0031ac71..6afe2ea6f 100644
--- a/src/core/hle/service/ldn/ldn.h
+++ b/src/core/hle/service/ldn/ldn.h
@@ -3,6 +3,12 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/result.h"
9#include "core/hle/service/kernel_helpers.h"
10#include "core/hle/service/sm/sm.h"
11
6namespace Core { 12namespace Core {
7class System; 13class System;
8} 14}
diff --git a/src/core/hle/service/ldn/ldn_results.h b/src/core/hle/service/ldn/ldn_results.h
new file mode 100644
index 000000000..f340bda42
--- /dev/null
+++ b/src/core/hle/service/ldn/ldn_results.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Service::LDN {
9
10constexpr Result ResultAdvertiseDataTooLarge{ErrorModule::LDN, 10};
11constexpr Result ResultAuthenticationFailed{ErrorModule::LDN, 20};
12constexpr Result ResultDisabled{ErrorModule::LDN, 22};
13constexpr Result ResultAirplaneModeEnabled{ErrorModule::LDN, 23};
14constexpr Result ResultInvalidNodeCount{ErrorModule::LDN, 30};
15constexpr Result ResultConnectionFailed{ErrorModule::LDN, 31};
16constexpr Result ResultBadState{ErrorModule::LDN, 32};
17constexpr Result ResultNoIpAddress{ErrorModule::LDN, 33};
18constexpr Result ResultInvalidBufferCount{ErrorModule::LDN, 50};
19constexpr Result ResultAccessPointConnectionFailed{ErrorModule::LDN, 65};
20constexpr Result ResultAuthenticationTimeout{ErrorModule::LDN, 66};
21constexpr Result ResultMaximumNodeCount{ErrorModule::LDN, 67};
22constexpr Result ResultBadInput{ErrorModule::LDN, 96};
23constexpr Result ResultLocalCommunicationIdNotFound{ErrorModule::LDN, 97};
24constexpr Result ResultLocalCommunicationVersionTooLow{ErrorModule::LDN, 113};
25constexpr Result ResultLocalCommunicationVersionTooHigh{ErrorModule::LDN, 114};
26
27} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h
new file mode 100644
index 000000000..0c07a7397
--- /dev/null
+++ b/src/core/hle/service/ldn/ldn_types.h
@@ -0,0 +1,284 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <fmt/format.h>
7
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "network/network.h"
11
12namespace Service::LDN {
13
14constexpr size_t SsidLengthMax = 32;
15constexpr size_t AdvertiseDataSizeMax = 384;
16constexpr size_t UserNameBytesMax = 32;
17constexpr int NodeCountMax = 8;
18constexpr int StationCountMax = NodeCountMax - 1;
19constexpr size_t PassphraseLengthMax = 64;
20
21enum class SecurityMode : u16 {
22 All,
23 Retail,
24 Debug,
25};
26
27enum class NodeStateChange : u8 {
28 None,
29 Connect,
30 Disconnect,
31 DisconnectAndConnect,
32};
33
34enum class ScanFilterFlag : u32 {
35 None = 0,
36 LocalCommunicationId = 1 << 0,
37 SessionId = 1 << 1,
38 NetworkType = 1 << 2,
39 Ssid = 1 << 4,
40 SceneId = 1 << 5,
41 IntentId = LocalCommunicationId | SceneId,
42 NetworkId = IntentId | SessionId,
43};
44
45enum class NetworkType : u32 {
46 None,
47 General,
48 Ldn,
49 All,
50};
51
52enum class PackedNetworkType : u8 {
53 None,
54 General,
55 Ldn,
56 All,
57};
58
59enum class State : u32 {
60 None,
61 Initialized,
62 AccessPointOpened,
63 AccessPointCreated,
64 StationOpened,
65 StationConnected,
66 Error,
67};
68
69enum class DisconnectReason : s16 {
70 Unknown = -1,
71 None,
72 DisconnectedByUser,
73 DisconnectedBySystem,
74 DestroyedByUser,
75 DestroyedBySystem,
76 Rejected,
77 SignalLost,
78};
79
80enum class NetworkError {
81 Unknown = -1,
82 None = 0,
83 PortUnreachable,
84 TooManyPlayers,
85 VersionTooLow,
86 VersionTooHigh,
87 ConnectFailure,
88 ConnectNotFound,
89 ConnectTimeout,
90 ConnectRejected,
91 RejectFailed,
92};
93
94enum class AcceptPolicy : u8 {
95 AcceptAll,
96 RejectAll,
97 BlackList,
98 WhiteList,
99};
100
101enum class WifiChannel : s16 {
102 Default = 0,
103 wifi24_1 = 1,
104 wifi24_6 = 6,
105 wifi24_11 = 11,
106 wifi50_36 = 36,
107 wifi50_40 = 40,
108 wifi50_44 = 44,
109 wifi50_48 = 48,
110};
111
112enum class LinkLevel : s8 {
113 Bad,
114 Low,
115 Good,
116 Excelent,
117};
118
119struct NodeLatestUpdate {
120 NodeStateChange state_change;
121 INSERT_PADDING_BYTES(0x7); // Unknown
122};
123static_assert(sizeof(NodeLatestUpdate) == 0x8, "NodeLatestUpdate is an invalid size");
124
125struct SessionId {
126 u64 high;
127 u64 low;
128
129 bool operator==(const SessionId&) const = default;
130};
131static_assert(sizeof(SessionId) == 0x10, "SessionId is an invalid size");
132
133struct IntentId {
134 u64 local_communication_id;
135 INSERT_PADDING_BYTES(0x2); // Reserved
136 u16 scene_id;
137 INSERT_PADDING_BYTES(0x4); // Reserved
138};
139static_assert(sizeof(IntentId) == 0x10, "IntentId is an invalid size");
140
141struct NetworkId {
142 IntentId intent_id;
143 SessionId session_id;
144};
145static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size");
146
147struct Ssid {
148 u8 length;
149 std::array<char, SsidLengthMax + 1> raw;
150
151 std::string GetStringValue() const {
152 return std::string(raw.data(), length);
153 }
154};
155static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size");
156
157struct Ipv4Address {
158 union {
159 u32 raw{};
160 std::array<u8, 4> bytes;
161 };
162
163 std::string GetStringValue() const {
164 return fmt::format("{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]);
165 }
166};
167static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size");
168
169struct MacAddress {
170 std::array<u8, 6> raw{};
171
172 friend bool operator==(const MacAddress& lhs, const MacAddress& rhs) = default;
173};
174static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size");
175
176struct ScanFilter {
177 NetworkId network_id;
178 NetworkType network_type;
179 MacAddress mac_address;
180 Ssid ssid;
181 INSERT_PADDING_BYTES(0x10);
182 ScanFilterFlag flag;
183};
184static_assert(sizeof(ScanFilter) == 0x60, "ScanFilter is an invalid size");
185
186struct CommonNetworkInfo {
187 MacAddress bssid;
188 Ssid ssid;
189 WifiChannel channel;
190 LinkLevel link_level;
191 PackedNetworkType network_type;
192 INSERT_PADDING_BYTES(0x4);
193};
194static_assert(sizeof(CommonNetworkInfo) == 0x30, "CommonNetworkInfo is an invalid size");
195
196struct NodeInfo {
197 Ipv4Address ipv4_address;
198 MacAddress mac_address;
199 s8 node_id;
200 u8 is_connected;
201 std::array<u8, UserNameBytesMax + 1> user_name;
202 INSERT_PADDING_BYTES(0x1); // Reserved
203 s16 local_communication_version;
204 INSERT_PADDING_BYTES(0x10); // Reserved
205};
206static_assert(sizeof(NodeInfo) == 0x40, "NodeInfo is an invalid size");
207
208struct LdnNetworkInfo {
209 std::array<u8, 0x10> security_parameter;
210 SecurityMode security_mode;
211 AcceptPolicy station_accept_policy;
212 u8 has_action_frame;
213 INSERT_PADDING_BYTES(0x2); // Padding
214 u8 node_count_max;
215 u8 node_count;
216 std::array<NodeInfo, NodeCountMax> nodes;
217 INSERT_PADDING_BYTES(0x2); // Reserved
218 u16 advertise_data_size;
219 std::array<u8, AdvertiseDataSizeMax> advertise_data;
220 INSERT_PADDING_BYTES(0x8C); // Reserved
221 u64 random_authentication_id;
222};
223static_assert(sizeof(LdnNetworkInfo) == 0x430, "LdnNetworkInfo is an invalid size");
224
225struct NetworkInfo {
226 NetworkId network_id;
227 CommonNetworkInfo common;
228 LdnNetworkInfo ldn;
229};
230static_assert(sizeof(NetworkInfo) == 0x480, "NetworkInfo is an invalid size");
231
232struct SecurityConfig {
233 SecurityMode security_mode;
234 u16 passphrase_size;
235 std::array<u8, PassphraseLengthMax> passphrase;
236};
237static_assert(sizeof(SecurityConfig) == 0x44, "SecurityConfig is an invalid size");
238
239struct UserConfig {
240 std::array<u8, UserNameBytesMax + 1> user_name;
241 INSERT_PADDING_BYTES(0xF); // Reserved
242};
243static_assert(sizeof(UserConfig) == 0x30, "UserConfig is an invalid size");
244
245#pragma pack(push, 4)
246struct ConnectRequest {
247 SecurityConfig security_config;
248 UserConfig user_config;
249 u32 local_communication_version;
250 u32 option_unknown;
251 NetworkInfo network_info;
252};
253static_assert(sizeof(ConnectRequest) == 0x4FC, "ConnectRequest is an invalid size");
254#pragma pack(pop)
255
256struct SecurityParameter {
257 std::array<u8, 0x10> data; // Data, used with the same key derivation as SecurityConfig
258 SessionId session_id;
259};
260static_assert(sizeof(SecurityParameter) == 0x20, "SecurityParameter is an invalid size");
261
262struct NetworkConfig {
263 IntentId intent_id;
264 WifiChannel channel;
265 u8 node_count_max;
266 INSERT_PADDING_BYTES(0x1); // Reserved
267 u16 local_communication_version;
268 INSERT_PADDING_BYTES(0xA); // Reserved
269};
270static_assert(sizeof(NetworkConfig) == 0x20, "NetworkConfig is an invalid size");
271
272struct AddressEntry {
273 Ipv4Address ipv4_address;
274 MacAddress mac_address;
275 INSERT_PADDING_BYTES(0x2); // Reserved
276};
277static_assert(sizeof(AddressEntry) == 0xC, "AddressEntry is an invalid size");
278
279struct AddressList {
280 std::array<AddressEntry, 0x8> addresses;
281};
282static_assert(sizeof(AddressList) == 0x60, "AddressList is an invalid size");
283
284} // namespace Service::LDN
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index d4a49ea99..98dd9035a 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -1306,7 +1306,7 @@ void EmitContext::DefineInputs(const IR::Program& program) {
1306 subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR); 1306 subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR);
1307 subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR); 1307 subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR);
1308 } 1308 }
1309 if (info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || 1309 if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
1310 (profile.warp_size_potentially_larger_than_guest && 1310 (profile.warp_size_potentially_larger_than_guest &&
1311 (info.uses_subgroup_vote || info.uses_subgroup_mask))) { 1311 (info.uses_subgroup_vote || info.uses_subgroup_mask))) {
1312 subgroup_local_invocation_id = 1312 subgroup_local_invocation_id =
@@ -1411,7 +1411,8 @@ void EmitContext::DefineInputs(const IR::Program& program) {
1411void EmitContext::DefineOutputs(const IR::Program& program) { 1411void EmitContext::DefineOutputs(const IR::Program& program) {
1412 const Info& info{program.info}; 1412 const Info& info{program.info};
1413 const std::optional<u32> invocations{program.invocations}; 1413 const std::optional<u32> invocations{program.invocations};
1414 if (info.stores.AnyComponent(IR::Attribute::PositionX) || stage == Stage::VertexB) { 1414 if (runtime_info.convert_depth_mode || info.stores.AnyComponent(IR::Attribute::PositionX) ||
1415 stage == Stage::VertexB) {
1415 output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position); 1416 output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position);
1416 } 1417 }
1417 if (info.stores[IR::Attribute::PointSize] || runtime_info.fixed_state_point_size) { 1418 if (info.stores[IR::Attribute::PointSize] || runtime_info.fixed_state_point_size) {
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 4a1d96322..27e6ebf94 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -87,12 +87,8 @@ u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) {
87} 87}
88 88
89std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) { 89std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) {
90 // TODO(Rodrigo): Read this from HLE 90 return static_cast<std::size_t>(framebuffer.stride) *
91 constexpr u32 block_height_log2 = 4; 91 static_cast<std::size_t>(framebuffer.height) * GetBytesPerPixel(framebuffer);
92 const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer);
93 const u64 size_bytes{Tegra::Texture::CalculateSize(
94 true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
95 return size_bytes;
96} 92}
97 93
98VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { 94VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) {
@@ -173,10 +169,12 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
173 // TODO(Rodrigo): Read this from HLE 169 // TODO(Rodrigo): Read this from HLE
174 constexpr u32 block_height_log2 = 4; 170 constexpr u32 block_height_log2 = 4;
175 const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); 171 const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer);
176 const u64 size_bytes{GetSizeInBytes(framebuffer)}; 172 const u64 linear_size{GetSizeInBytes(framebuffer)};
177 173 const u64 tiled_size{Tegra::Texture::CalculateSize(true, bytes_per_pixel,
174 framebuffer.stride, framebuffer.height,
175 1, block_height_log2, 0)};
178 Tegra::Texture::UnswizzleTexture( 176 Tegra::Texture::UnswizzleTexture(
179 mapped_span.subspan(image_offset, size_bytes), std::span(host_ptr, size_bytes), 177 mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
180 bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); 178 bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
181 179
182 const VkBufferImageCopy copy{ 180 const VkBufferImageCopy copy{
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index fa8efd22e..a69ae7725 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -33,9 +33,10 @@ VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats)
33} 33}
34 34
35VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> modes) { 35VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> modes) {
36 // Mailbox doesn't lock the application like fifo (vsync), prefer it 36 // Mailbox (triple buffering) doesn't lock the application like fifo (vsync),
37 // prefer it if vsync option is not selected
37 const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR); 38 const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR);
38 if (found_mailbox != modes.end()) { 39 if (found_mailbox != modes.end() && !Settings::values.use_vsync.GetValue()) {
39 return VK_PRESENT_MODE_MAILBOX_KHR; 40 return VK_PRESENT_MODE_MAILBOX_KHR;
40 } 41 }
41 if (!Settings::values.use_speed_limit.GetValue()) { 42 if (!Settings::values.use_speed_limit.GetValue()) {
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index ba6d81420..16463a892 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1618,6 +1618,9 @@ ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParam
1618ImageView::~ImageView() = default; 1618ImageView::~ImageView() = default;
1619 1619
1620VkImageView ImageView::DepthView() { 1620VkImageView ImageView::DepthView() {
1621 if (!image_handle) {
1622 return VK_NULL_HANDLE;
1623 }
1621 if (depth_view) { 1624 if (depth_view) {
1622 return *depth_view; 1625 return *depth_view;
1623 } 1626 }
@@ -1627,6 +1630,9 @@ VkImageView ImageView::DepthView() {
1627} 1630}
1628 1631
1629VkImageView ImageView::StencilView() { 1632VkImageView ImageView::StencilView() {
1633 if (!image_handle) {
1634 return VK_NULL_HANDLE;
1635 }
1630 if (stencil_view) { 1636 if (stencil_view) {
1631 return *stencil_view; 1637 return *stencil_view;
1632 } 1638 }
@@ -1636,6 +1642,9 @@ VkImageView ImageView::StencilView() {
1636} 1642}
1637 1643
1638VkImageView ImageView::ColorView() { 1644VkImageView ImageView::ColorView() {
1645 if (!image_handle) {
1646 return VK_NULL_HANDLE;
1647 }
1639 if (color_view) { 1648 if (color_view) {
1640 return *color_view; 1649 return *color_view;
1641 } 1650 }
@@ -1645,6 +1654,9 @@ VkImageView ImageView::ColorView() {
1645 1654
1646VkImageView ImageView::StorageView(Shader::TextureType texture_type, 1655VkImageView ImageView::StorageView(Shader::TextureType texture_type,
1647 Shader::ImageFormat image_format) { 1656 Shader::ImageFormat image_format) {
1657 if (!image_handle) {
1658 return VK_NULL_HANDLE;
1659 }
1648 if (image_format == Shader::ImageFormat::Typeless) { 1660 if (image_format == Shader::ImageFormat::Typeless) {
1649 return Handle(texture_type); 1661 return Handle(texture_type);
1650 } 1662 }
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index f6b389ede..50007338f 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -221,6 +221,9 @@ if (ENABLE_QT_TRANSLATION)
221 # Update source TS file if enabled 221 # Update source TS file if enabled
222 if (GENERATE_QT_TRANSLATION) 222 if (GENERATE_QT_TRANSLATION)
223 get_target_property(SRCS yuzu SOURCES) 223 get_target_property(SRCS yuzu SOURCES)
224 # these calls to qt_create_translation also creates a rule to generate en.qm which conflicts with providing english plurals
225 # so we have to set a OUTPUT_LOCATION so that we don't have multiple rules to generate en.qm
226 set_source_files_properties(${YUZU_QT_LANGUAGES}/en.ts PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
224 qt_create_translation(QM_FILES 227 qt_create_translation(QM_FILES
225 ${SRCS} 228 ${SRCS}
226 ${UIS} 229 ${UIS}
@@ -229,7 +232,13 @@ if (ENABLE_QT_TRANSLATION)
229 -source-language en_US 232 -source-language en_US
230 -target-language en_US 233 -target-language en_US
231 ) 234 )
232 add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts) 235
236 # Generate plurals into dist/english_plurals/generated_en.ts so it can be used to revise dist/english_plurals/en.ts
237 set(GENERATED_PLURALS_FILE ${PROJECT_SOURCE_DIR}/dist/english_plurals/generated_en.ts)
238 set_source_files_properties(${GENERATED_PLURALS_FILE} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/plurals")
239 qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US)
240
241 add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts ${GENERATED_PLURALS_FILE})
233 endif() 242 endif()
234 243
235 # Find all TS files except en.ts 244 # Find all TS files except en.ts
@@ -239,6 +248,9 @@ if (ENABLE_QT_TRANSLATION)
239 # Compile TS files to QM files 248 # Compile TS files to QM files
240 qt_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) 249 qt_add_translation(LANGUAGES_QM ${LANGUAGES_TS})
241 250
251 # Compile english plurals TS file to en.qm
252 qt_add_translation(LANGUAGES_QM ${PROJECT_SOURCE_DIR}/dist/english_plurals/en.ts)
253
242 # Build a QRC file from the QM file list 254 # Build a QRC file from the QM file list
243 set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc) 255 set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc)
244 file(WRITE ${LANGUAGES_QRC} "<RCC><qresource prefix=\"languages\">\n") 256 file(WRITE ${LANGUAGES_QRC} "<RCC><qresource prefix=\"languages\">\n")
diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp
index 826c6c224..c8bcfb223 100644
--- a/src/yuzu/applets/qt_profile_select.cpp
+++ b/src/yuzu/applets/qt_profile_select.cpp
@@ -100,6 +100,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(Core::HID::HIDCore& hid_core,
100 } 100 }
101 QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); 101 QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
102 QCoreApplication::postEvent(tree_view, event); 102 QCoreApplication::postEvent(tree_view, event);
103 SelectUser(tree_view->currentIndex());
103 }); 104 });
104 105
105 const auto& profiles = profile_manager->GetAllUsers(); 106 const auto& profiles = profile_manager->GetAllUsers();
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index ef3bdfb1a..c262d0a2b 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -1089,8 +1089,8 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
1089 } 1089 }
1090 1090
1091 if (!unsupported_ext.empty()) { 1091 if (!unsupported_ext.empty()) {
1092 LOG_ERROR(Frontend, "GPU does not support all required extensions: {}", 1092 const std::string gl_renderer{reinterpret_cast<const char*>(glGetString(GL_RENDERER))};
1093 glGetString(GL_RENDERER)); 1093 LOG_ERROR(Frontend, "GPU does not support all required extensions: {}", gl_renderer);
1094 } 1094 }
1095 for (const QString& ext : unsupported_ext) { 1095 for (const QString& ext : unsupported_ext) {
1096 LOG_ERROR(Frontend, "Unsupported GL extension: {}", ext.toStdString()); 1096 LOG_ERROR(Frontend, "Unsupported GL extension: {}", ext.toStdString());
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index 96de0b3d1..d6d819364 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -75,7 +75,7 @@
75 <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string> 75 <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string>
76 </property> 76 </property>
77 <property name="text"> 77 <property name="text">
78 <string>Use VSync (OpenGL only)</string> 78 <string>Use VSync</string>
79 </property> 79 </property>
80 </widget> 80 </widget>
81 </item> 81 </item>
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index 2e98ede8e..48f71b53c 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -219,6 +219,7 @@ void ConfigureUi::InitializeLanguageComboBox() {
219 for (const auto& lang : languages) { 219 for (const auto& lang : languages) {
220 if (QString::fromLatin1(lang.id) == QStringLiteral("en")) { 220 if (QString::fromLatin1(lang.id) == QStringLiteral("en")) {
221 ui->language_combobox->addItem(lang.name, QStringLiteral("en")); 221 ui->language_combobox->addItem(lang.name, QStringLiteral("en"));
222 language_files.removeOne(QStringLiteral("en.qm"));
222 continue; 223 continue;
223 } 224 }
224 for (int i = 0; i < language_files.size(); ++i) { 225 for (int i = 0; i < language_files.size(); ++i) {
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 5111b0eba..44d7feddc 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1607,17 +1607,18 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
1607 return true; 1607 return true;
1608} 1608}
1609 1609
1610void GMainWindow::SelectAndSetCurrentUser() { 1610bool GMainWindow::SelectAndSetCurrentUser() {
1611 QtProfileSelectionDialog dialog(system->HIDCore(), this); 1611 QtProfileSelectionDialog dialog(system->HIDCore(), this);
1612 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | 1612 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
1613 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); 1613 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
1614 dialog.setWindowModality(Qt::WindowModal); 1614 dialog.setWindowModality(Qt::WindowModal);
1615 1615
1616 if (dialog.exec() == QDialog::Rejected) { 1616 if (dialog.exec() == QDialog::Rejected) {
1617 return; 1617 return false;
1618 } 1618 }
1619 1619
1620 Settings::values.current_user = dialog.GetIndex(); 1620 Settings::values.current_user = dialog.GetIndex();
1621 return true;
1621} 1622}
1622 1623
1623void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, 1624void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index,
@@ -1651,11 +1652,14 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1651 Settings::LogSettings(); 1652 Settings::LogSettings();
1652 1653
1653 if (UISettings::values.select_user_on_boot) { 1654 if (UISettings::values.select_user_on_boot) {
1654 SelectAndSetCurrentUser(); 1655 if (SelectAndSetCurrentUser() == false) {
1656 return;
1657 }
1655 } 1658 }
1656 1659
1657 if (!LoadROM(filename, program_id, program_index)) 1660 if (!LoadROM(filename, program_id, program_index)) {
1658 return; 1661 return;
1662 }
1659 1663
1660 system->SetShuttingDown(false); 1664 system->SetShuttingDown(false);
1661 1665
@@ -3353,7 +3357,8 @@ void GMainWindow::MigrateConfigFiles() {
3353 } 3357 }
3354 const auto origin = config_dir_fs_path / filename; 3358 const auto origin = config_dir_fs_path / filename;
3355 const auto destination = config_dir_fs_path / "custom" / filename; 3359 const auto destination = config_dir_fs_path / "custom" / filename;
3356 LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination); 3360 LOG_INFO(Frontend, "Migrating config file from {} to {}", origin.string(),
3361 destination.string());
3357 if (!Common::FS::RenameFile(origin, destination)) { 3362 if (!Common::FS::RenameFile(origin, destination)) {
3358 // Delete the old config file if one already exists in the new location. 3363 // Delete the old config file if one already exists in the new location.
3359 Common::FS::RemoveFile(origin); 3364 Common::FS::RemoveFile(origin);
@@ -4013,11 +4018,6 @@ void GMainWindow::UpdateUITheme() {
4013} 4018}
4014 4019
4015void GMainWindow::LoadTranslation() { 4020void GMainWindow::LoadTranslation() {
4016 // If the selected language is English, no need to install any translation
4017 if (UISettings::values.language == QStringLiteral("en")) {
4018 return;
4019 }
4020
4021 bool loaded; 4021 bool loaded;
4022 4022
4023 if (UISettings::values.language.isEmpty()) { 4023 if (UISettings::values.language.isEmpty()) {
@@ -4125,6 +4125,15 @@ int main(int argc, char* argv[]) {
4125 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 4125 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
4126 QApplication app(argc, argv); 4126 QApplication app(argc, argv);
4127 4127
4128 // Workaround for QTBUG-85409, for Suzhou numerals the number 1 is actually \u3021
4129 // so we can see if we get \u3008 instead
4130 // TL;DR all other number formats are consecutive in unicode code points
4131 // This bug is fixed in Qt6, specifically 6.0.0-alpha1
4132 const QLocale locale = QLocale::system();
4133 if (QStringLiteral("\u3008") == locale.toString(1)) {
4134 QLocale::setDefault(QLocale::system().name());
4135 }
4136
4128 // Qt changes the locale and causes issues in float conversion using std::to_string() when 4137 // Qt changes the locale and causes issues in float conversion using std::to_string() when
4129 // generating shaders 4138 // generating shaders
4130 setlocale(LC_ALL, "C"); 4139 setlocale(LC_ALL, "C");
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 0b25ebe6c..1ae2b93d9 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -218,7 +218,7 @@ private:
218 void SetDiscordEnabled(bool state); 218 void SetDiscordEnabled(bool state);
219 void LoadAmiibo(const QString& filename); 219 void LoadAmiibo(const QString& filename);
220 220
221 void SelectAndSetCurrentUser(); 221 bool SelectAndSetCurrentUser();
222 222
223 /** 223 /**
224 * Stores the filename in the recently loaded files list. 224 * Stores the filename in the recently loaded files list.
diff --git a/vcpkg.json b/vcpkg.json
index b247f7386..c4413e22a 100644
--- a/vcpkg.json
+++ b/vcpkg.json
@@ -1,7 +1,7 @@
1{ 1{
2 "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json", 2 "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
3 "name": "yuzu", 3 "name": "yuzu",
4 "builtin-baseline": "cef0b3ec767df6e83806899fe9525f6cf8d7bc91", 4 "builtin-baseline": "9b22b40c6c61bf0da2d46346dd44a11e90972cc9",
5 "version": "1.0", 5 "version": "1.0",
6 "dependencies": [ 6 "dependencies": [
7 "boost-algorithm", 7 "boost-algorithm",
@@ -37,6 +37,10 @@
37 { 37 {
38 "name": "catch2", 38 "name": "catch2",
39 "version": "2.13.9" 39 "version": "2.13.9"
40 },
41 {
42 "name": "fmt",
43 "version": "9.0.0"
40 } 44 }
41 ] 45 ]
42} 46}