summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/audio_core/renderer/effect/effect_info_base.h8
-rw-r--r--src/audio_core/sink/cubeb_sink.cpp3
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/announce_multiplayer_room.h10
-rw-r--r--src/common/microprofile.h9
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings.h2
-rw-r--r--src/common/socket_types.h51
-rw-r--r--src/common/threadsafe_queue.h2
-rw-r--r--src/common/uint128.h1
-rw-r--r--src/core/CMakeLists.txt9
-rw-r--r--src/core/announce_multiplayer_session.cpp6
-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.cpp51
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h5
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp34
-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/file_sys/ips_layer.cpp7
-rw-r--r--src/core/file_sys/patch_manager.cpp14
-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/core/hle/service/nifm/nifm.cpp342
-rw-r--r--src/core/hle/service/nifm/nifm.h27
-rw-r--r--src/core/hle/service/pcv/pcv.cpp93
-rw-r--r--src/core/hle/service/pcv/pcv.h91
-rw-r--r--src/core/hle/service/service.cpp7
-rw-r--r--src/core/hle/service/sockets/bsd.cpp40
-rw-r--r--src/core/hle/service/sockets/bsd.h13
-rw-r--r--src/core/hle/service/sockets/sockets.h6
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp2
-rw-r--r--src/core/internal_network/network.cpp62
-rw-r--r--src/core/internal_network/network.h43
-rw-r--r--src/core/internal_network/socket_proxy.cpp284
-rw-r--r--src/core/internal_network/socket_proxy.h97
-rw-r--r--src/core/internal_network/sockets.h132
-rw-r--r--src/core/loader/elf.cpp263
-rw-r--r--src/core/loader/elf.h36
-rw-r--r--src/core/loader/kip.cpp2
-rw-r--r--src/core/loader/loader.cpp11
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/nro.cpp2
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/core/memory.cpp81
-rw-r--r--src/core/memory.h6
-rw-r--r--src/dedicated_room/CMakeLists.txt27
-rw-r--r--src/dedicated_room/yuzu_room.cpp375
-rw-r--r--src/dedicated_room/yuzu_room.rc20
-rw-r--r--src/network/room.cpp170
-rw-r--r--src/network/room.h14
-rw-r--r--src/network/room_member.cpp123
-rw-r--r--src/network/room_member.h70
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp5
-rw-r--r--src/tests/video_core/buffer_base.cpp7
-rw-r--r--src/video_core/buffer_cache/buffer_base.h2
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h49
-rw-r--r--src/video_core/memory_manager.cpp4
-rw-r--r--src/video_core/query_cache.h12
-rw-r--r--src/video_core/rasterizer_accelerated.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp1
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp19
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp371
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp34
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp2
-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/video_core/shader_cache.cpp12
-rw-r--r--src/video_core/shader_cache.h4
-rw-r--r--src/video_core/texture_cache/texture_cache.h12
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h10
-rw-r--r--src/video_core/textures/decoders.cpp60
-rw-r--r--src/video_core/textures/decoders.h3
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp234
-rw-r--r--src/web_service/verify_user_jwt.cpp4
-rw-r--r--src/yuzu/CMakeLists.txt14
-rw-r--r--src/yuzu/aboutdialog.ui3
-rw-r--r--src/yuzu/applets/qt_profile_select.cpp1
-rw-r--r--src/yuzu/bootmanager.cpp4
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_audio.ui4
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui2
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp2
-rw-r--r--src/yuzu/configuration/configure_ui.cpp1
-rw-r--r--src/yuzu/game_list.cpp40
-rw-r--r--src/yuzu/game_list.h3
-rw-r--r--src/yuzu/game_list_p.h5
-rw-r--r--src/yuzu/loading_screen.cpp4
-rw-r--r--src/yuzu/main.cpp124
-rw-r--r--src/yuzu/main.h7
-rw-r--r--src/yuzu/multiplayer/chat_room.cpp14
-rw-r--r--src/yuzu/multiplayer/client_room.cpp1
-rw-r--r--src/yuzu/multiplayer/direct_connect.cpp10
-rw-r--r--src/yuzu/multiplayer/direct_connect.ui2
-rw-r--r--src/yuzu/multiplayer/host_room.cpp4
-rw-r--r--src/yuzu/multiplayer/lobby.cpp2
-rw-r--r--src/yuzu/multiplayer/message.cpp7
-rw-r--r--src/yuzu/multiplayer/message.h3
-rw-r--r--src/yuzu/multiplayer/state.cpp15
-rw-r--r--src/yuzu/multiplayer/validation.h2
-rw-r--r--src/yuzu/uisettings.h5
-rw-r--r--src/yuzu_cmd/yuzu.cpp10
121 files changed, 3335 insertions, 1610 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index fc177fa52..54de1dc94 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -162,6 +162,7 @@ add_subdirectory(video_core)
162add_subdirectory(network) 162add_subdirectory(network)
163add_subdirectory(input_common) 163add_subdirectory(input_common)
164add_subdirectory(shader_recompiler) 164add_subdirectory(shader_recompiler)
165add_subdirectory(dedicated_room)
165 166
166if (YUZU_TESTS) 167if (YUZU_TESTS)
167 add_subdirectory(tests) 168 add_subdirectory(tests)
diff --git a/src/audio_core/renderer/effect/effect_info_base.h b/src/audio_core/renderer/effect/effect_info_base.h
index 43d0589cc..8c9583878 100644
--- a/src/audio_core/renderer/effect/effect_info_base.h
+++ b/src/audio_core/renderer/effect/effect_info_base.h
@@ -419,13 +419,13 @@ protected:
419 /// Workbuffers assigned to this effect 419 /// Workbuffers assigned to this effect
420 std::array<AddressInfo, 2> workbuffers{AddressInfo(CpuAddr(0), 0), AddressInfo(CpuAddr(0), 0)}; 420 std::array<AddressInfo, 2> workbuffers{AddressInfo(CpuAddr(0), 0), AddressInfo(CpuAddr(0), 0)};
421 /// Aux/Capture buffer info for reading 421 /// Aux/Capture buffer info for reading
422 CpuAddr send_buffer_info; 422 CpuAddr send_buffer_info{};
423 /// Aux/Capture buffer for reading 423 /// Aux/Capture buffer for reading
424 CpuAddr send_buffer; 424 CpuAddr send_buffer{};
425 /// Aux/Capture buffer info for writing 425 /// Aux/Capture buffer info for writing
426 CpuAddr return_buffer_info; 426 CpuAddr return_buffer_info{};
427 /// Aux/Capture buffer for writing 427 /// Aux/Capture buffer for writing
428 CpuAddr return_buffer; 428 CpuAddr return_buffer{};
429 /// Parameters of this effect 429 /// Parameters of this effect
430 std::array<u8, sizeof(InParameterVersion2)> parameter{}; 430 std::array<u8, sizeof(InParameterVersion2)> parameter{};
431 /// State of this effect used by the AudioRenderer across calls 431 /// State of this effect used by the AudioRenderer across calls
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp
index a4e28de6d..90d049e8e 100644
--- a/src/audio_core/sink/cubeb_sink.cpp
+++ b/src/audio_core/sink/cubeb_sink.cpp
@@ -185,6 +185,9 @@ public:
185 constexpr s32 max{std::numeric_limits<s16>::max()}; 185 constexpr s32 max{std::numeric_limits<s16>::max()};
186 186
187 auto yuzu_volume{Settings::Volume()}; 187 auto yuzu_volume{Settings::Volume()};
188 if (yuzu_volume > 1.0f) {
189 yuzu_volume = 0.6f + 20 * std::log10(yuzu_volume);
190 }
188 auto volume{system_volume * device_volume * yuzu_volume}; 191 auto volume{system_volume * device_volume * yuzu_volume};
189 192
190 if (system_channels == 6 && device_channels == 2) { 193 if (system_channels == 6 && device_channels == 2) {
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index a6dc31b53..635fb85c8 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -124,6 +124,7 @@ add_library(common STATIC
124 settings.h 124 settings.h
125 settings_input.cpp 125 settings_input.cpp
126 settings_input.h 126 settings_input.h
127 socket_types.h
127 spin_lock.cpp 128 spin_lock.cpp
128 spin_lock.h 129 spin_lock.h
129 stream.cpp 130 stream.cpp
diff --git a/src/common/announce_multiplayer_room.h b/src/common/announce_multiplayer_room.h
index 0ad9da2be..cb004e0eb 100644
--- a/src/common/announce_multiplayer_room.h
+++ b/src/common/announce_multiplayer_room.h
@@ -8,12 +8,11 @@
8#include <string> 8#include <string>
9#include <vector> 9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/socket_types.h"
11#include "web_service/web_result.h" 12#include "web_service/web_result.h"
12 13
13namespace AnnounceMultiplayerRoom { 14namespace AnnounceMultiplayerRoom {
14 15
15using MacAddress = std::array<u8, 6>;
16
17struct GameInfo { 16struct GameInfo {
18 std::string name{""}; 17 std::string name{""};
19 u64 id{0}; 18 u64 id{0};
@@ -24,7 +23,7 @@ struct Member {
24 std::string nickname; 23 std::string nickname;
25 std::string display_name; 24 std::string display_name;
26 std::string avatar_url; 25 std::string avatar_url;
27 MacAddress mac_address; 26 Network::IPv4Address fake_ip;
28 GameInfo game; 27 GameInfo game;
29}; 28};
30 29
@@ -75,10 +74,7 @@ public:
75 const bool has_password, const GameInfo& preferred_game) = 0; 74 const bool has_password, const GameInfo& preferred_game) = 0;
76 /** 75 /**
77 * Adds a player information to the data that gets announced 76 * Adds a player information to the data that gets announced
78 * @param nickname The nickname of the player 77 * @param member The player to add
79 * @param mac_address The MAC Address of the player
80 * @param game_id The title id of the game the player plays
81 * @param game_name The name of the game the player plays
82 */ 78 */
83 virtual void AddPlayer(const Member& member) = 0; 79 virtual void AddPlayer(const Member& member) = 0;
84 80
diff --git a/src/common/microprofile.h b/src/common/microprofile.h
index 91d14d5e1..56ef0a2dc 100644
--- a/src/common/microprofile.h
+++ b/src/common/microprofile.h
@@ -22,12 +22,3 @@ typedef void* HANDLE;
22#include <microprofile.h> 22#include <microprofile.h>
23 23
24#define MP_RGB(r, g, b) ((r) << 16 | (g) << 8 | (b) << 0) 24#define MP_RGB(r, g, b) ((r) << 16 | (g) << 8 | (b) << 0)
25
26// On OS X, some Mach header included by MicroProfile defines these as macros, conflicting with
27// identifiers we use.
28#ifdef PAGE_SIZE
29#undef PAGE_SIZE
30#endif
31#ifdef PAGE_MASK
32#undef PAGE_MASK
33#endif
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 1c7b6dfae..7282a45d3 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -105,7 +105,7 @@ float Volume() {
105 if (values.audio_muted) { 105 if (values.audio_muted) {
106 return 0.0f; 106 return 0.0f;
107 } 107 }
108 return values.volume.GetValue() / 100.0f; 108 return values.volume.GetValue() / static_cast<f32>(values.volume.GetDefault());
109} 109}
110 110
111void UpdateRescalingInfo() { 111void UpdateRescalingInfo() {
diff --git a/src/common/settings.h b/src/common/settings.h
index 1079cf8cb..14ed9b237 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -374,7 +374,7 @@ struct Values {
374 Setting<std::string> audio_output_device_id{"auto", "output_device"}; 374 Setting<std::string> audio_output_device_id{"auto", "output_device"};
375 Setting<std::string> audio_input_device_id{"auto", "input_device"}; 375 Setting<std::string> audio_input_device_id{"auto", "input_device"};
376 Setting<bool> audio_muted{false, "audio_muted"}; 376 Setting<bool> audio_muted{false, "audio_muted"};
377 SwitchableSetting<u8, true> volume{100, 0, 100, "volume"}; 377 SwitchableSetting<u8, true> volume{100, 0, 200, "volume"};
378 Setting<bool> dump_audio_commands{false, "dump_audio_commands"}; 378 Setting<bool> dump_audio_commands{false, "dump_audio_commands"};
379 379
380 // Core 380 // Core
diff --git a/src/common/socket_types.h b/src/common/socket_types.h
new file mode 100644
index 000000000..0a801a443
--- /dev/null
+++ b/src/common/socket_types.h
@@ -0,0 +1,51 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Network {
9
10/// Address families
11enum class Domain : u8 {
12 INET, ///< Address family for IPv4
13};
14
15/// Socket types
16enum class Type {
17 STREAM,
18 DGRAM,
19 RAW,
20 SEQPACKET,
21};
22
23/// Protocol values for sockets
24enum class Protocol : u8 {
25 ICMP,
26 TCP,
27 UDP,
28};
29
30/// Shutdown mode
31enum class ShutdownHow {
32 RD,
33 WR,
34 RDWR,
35};
36
37/// Array of IPv4 address
38using IPv4Address = std::array<u8, 4>;
39
40/// Cross-platform sockaddr structure
41struct SockAddrIn {
42 Domain family;
43 IPv4Address ip;
44 u16 portno;
45};
46
47constexpr u32 FLAG_MSG_PEEK = 0x2;
48constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
49constexpr u32 FLAG_O_NONBLOCK = 0x800;
50
51} // namespace Network
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index f7ae9d8c2..053798e79 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -39,7 +39,7 @@ public:
39 template <typename Arg> 39 template <typename Arg>
40 void Push(Arg&& t) { 40 void Push(Arg&& t) {
41 // create the element, add it to the queue 41 // create the element, add it to the queue
42 write_ptr->current = std::forward<Arg>(t); 42 write_ptr->current = std::move(t);
43 // set the next pointer to a new element ptr 43 // set the next pointer to a new element ptr
44 // then advance the write pointer 44 // then advance the write pointer
45 ElementPtr* new_ptr = new ElementPtr(); 45 ElementPtr* new_ptr = new ElementPtr();
diff --git a/src/common/uint128.h b/src/common/uint128.h
index f890ffec2..f450a6db9 100644
--- a/src/common/uint128.h
+++ b/src/common/uint128.h
@@ -12,7 +12,6 @@
12#pragma intrinsic(_udiv128) 12#pragma intrinsic(_udiv128)
13#else 13#else
14#include <cstring> 14#include <cstring>
15#include <x86intrin.h>
16#endif 15#endif
17 16
18#include "common/common_types.h" 17#include "common/common_types.h"
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 0d3286d58..8db9a3c65 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
@@ -725,10 +724,10 @@ add_library(core STATIC
725 internal_network/network_interface.cpp 724 internal_network/network_interface.cpp
726 internal_network/network_interface.h 725 internal_network/network_interface.h
727 internal_network/sockets.h 726 internal_network/sockets.h
727 internal_network/socket_proxy.cpp
728 internal_network/socket_proxy.h
728 loader/deconstructed_rom_directory.cpp 729 loader/deconstructed_rom_directory.cpp
729 loader/deconstructed_rom_directory.h 730 loader/deconstructed_rom_directory.h
730 loader/elf.cpp
731 loader/elf.h
732 loader/kip.cpp 731 loader/kip.cpp
733 loader/kip.h 732 loader/kip.h
734 loader/loader.cpp 733 loader/loader.cpp
diff --git a/src/core/announce_multiplayer_session.cpp b/src/core/announce_multiplayer_session.cpp
index d73a488cf..6737ce85a 100644
--- a/src/core/announce_multiplayer_session.cpp
+++ b/src/core/announce_multiplayer_session.cpp
@@ -31,7 +31,7 @@ AnnounceMultiplayerSession::AnnounceMultiplayerSession(Network::RoomNetwork& roo
31} 31}
32 32
33WebService::WebResult AnnounceMultiplayerSession::Register() { 33WebService::WebResult AnnounceMultiplayerSession::Register() {
34 std::shared_ptr<Network::Room> room = room_network.GetRoom().lock(); 34 auto room = room_network.GetRoom().lock();
35 if (!room) { 35 if (!room) {
36 return WebService::WebResult{WebService::WebResult::Code::LibError, 36 return WebService::WebResult{WebService::WebResult::Code::LibError,
37 "Network is not initialized", ""}; 37 "Network is not initialized", ""};
@@ -102,7 +102,7 @@ void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr<Network::Room
102void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { 102void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() {
103 // Invokes all current bound error callbacks. 103 // Invokes all current bound error callbacks.
104 const auto ErrorCallback = [this](WebService::WebResult result) { 104 const auto ErrorCallback = [this](WebService::WebResult result) {
105 std::lock_guard<std::mutex> lock(callback_mutex); 105 std::lock_guard lock(callback_mutex);
106 for (auto callback : error_callbacks) { 106 for (auto callback : error_callbacks) {
107 (*callback)(result); 107 (*callback)(result);
108 } 108 }
@@ -120,7 +120,7 @@ void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() {
120 std::future<WebService::WebResult> future; 120 std::future<WebService::WebResult> future;
121 while (!shutdown_event.WaitUntil(update_time)) { 121 while (!shutdown_event.WaitUntil(update_time)) {
122 update_time += announce_time_interval; 122 update_time += announce_time_interval;
123 std::shared_ptr<Network::Room> room = room_network.GetRoom().lock(); 123 auto room = room_network.GetRoom().lock();
124 if (!room) { 124 if (!room) {
125 break; 125 break;
126 } 126 }
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..d1e70f19d 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 {
@@ -180,19 +190,21 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
180 config.callbacks = cb.get(); 190 config.callbacks = cb.get();
181 config.coprocessors[15] = cp15; 191 config.coprocessors[15] = cp15;
182 config.define_unpredictable_behaviour = true; 192 config.define_unpredictable_behaviour = true;
183 static constexpr std::size_t PAGE_BITS = 12; 193 static constexpr std::size_t YUZU_PAGEBITS = 12;
184 static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS); 194 static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - YUZU_PAGEBITS);
185 if (page_table) { 195 if (page_table) {
186 config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>( 196 config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
187 page_table->pointers.data()); 197 page_table->pointers.data());
198 config.absolute_offset_page_table = true;
199 config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
200 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
201 config.only_detect_misalignment_via_page_table_on_page_boundary = true;
202
188 config.fastmem_pointer = page_table->fastmem_arena; 203 config.fastmem_pointer = page_table->fastmem_arena;
204
205 config.fastmem_exclusive_access = config.fastmem_pointer != nullptr;
206 config.recompile_on_exclusive_fastmem_failure = true;
189 } 207 }
190 config.absolute_offset_page_table = true;
191 config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
192 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
193 config.only_detect_misalignment_via_page_table_on_page_boundary = true;
194 config.fastmem_exclusive_access = true;
195 config.recompile_on_exclusive_fastmem_failure = true;
196 208
197 // Multi-process state 209 // Multi-process state
198 config.processor_id = core_index; 210 config.processor_id = core_index;
@@ -200,7 +212,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
200 212
201 // Timing 213 // Timing
202 config.wall_clock_cntpct = uses_wall_clock; 214 config.wall_clock_cntpct = uses_wall_clock;
203 config.enable_cycle_counting = !uses_wall_clock; 215 config.enable_cycle_counting = true;
204 216
205 // Code cache size 217 // Code cache size
206 config.code_cache_size = 512_MiB; 218 config.code_cache_size = 512_MiB;
@@ -244,6 +256,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
244 } 256 }
245 if (!Settings::values.cpuopt_fastmem) { 257 if (!Settings::values.cpuopt_fastmem) {
246 config.fastmem_pointer = nullptr; 258 config.fastmem_pointer = nullptr;
259 config.fastmem_exclusive_access = false;
247 } 260 }
248 if (!Settings::values.cpuopt_fastmem_exclusives) { 261 if (!Settings::values.cpuopt_fastmem_exclusives) {
249 config.fastmem_exclusive_access = false; 262 config.fastmem_exclusive_access = false;
@@ -311,11 +324,9 @@ void ARM_Dynarmic_32::RewindBreakpointInstruction() {
311 LoadContext(breakpoint_context); 324 LoadContext(breakpoint_context);
312} 325}
313 326
314ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, 327ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, bool uses_wall_clock_,
315 bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, 328 ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_)
316 std::size_t core_index_) 329 : 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_}, 330 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_},
320 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, 331 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
321 null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {} 332 null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {}
@@ -394,6 +405,10 @@ void ARM_Dynarmic_32::SignalInterrupt() {
394 jit.load()->HaltExecution(break_loop); 405 jit.load()->HaltExecution(break_loop);
395} 406}
396 407
408void ARM_Dynarmic_32::ClearInterrupt() {
409 jit.load()->ClearHalt(break_loop);
410}
411
397void ARM_Dynarmic_32::ClearInstructionCache() { 412void ARM_Dynarmic_32::ClearInstructionCache() {
398 jit.load()->ClearCache(); 413 jit.load()->ClearCache();
399} 414}
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..1d46f6d40 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,
@@ -240,7 +250,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
240 config.fastmem_address_space_bits = address_space_bits; 250 config.fastmem_address_space_bits = address_space_bits;
241 config.silently_mirror_fastmem = false; 251 config.silently_mirror_fastmem = false;
242 252
243 config.fastmem_exclusive_access = true; 253 config.fastmem_exclusive_access = config.fastmem_pointer != nullptr;
244 config.recompile_on_exclusive_fastmem_failure = true; 254 config.recompile_on_exclusive_fastmem_failure = true;
245 } 255 }
246 256
@@ -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;
@@ -304,6 +314,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
304 } 314 }
305 if (!Settings::values.cpuopt_fastmem) { 315 if (!Settings::values.cpuopt_fastmem) {
306 config.fastmem_pointer = nullptr; 316 config.fastmem_pointer = nullptr;
317 config.fastmem_exclusive_access = false;
307 } 318 }
308 if (!Settings::values.cpuopt_fastmem_exclusives) { 319 if (!Settings::values.cpuopt_fastmem_exclusives) {
309 config.fastmem_exclusive_access = false; 320 config.fastmem_exclusive_access = false;
@@ -371,10 +382,9 @@ void ARM_Dynarmic_64::RewindBreakpointInstruction() {
371 LoadContext(breakpoint_context); 382 LoadContext(breakpoint_context);
372} 383}
373 384
374ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, 385ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, bool uses_wall_clock_,
375 bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, 386 ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_)
376 std::size_t core_index_) 387 : 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_}, 388 cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_},
379 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, 389 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
380 null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {} 390 null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {}
@@ -461,6 +471,10 @@ void ARM_Dynarmic_64::SignalInterrupt() {
461 jit.load()->HaltExecution(break_loop); 471 jit.load()->HaltExecution(break_loop);
462} 472}
463 473
474void ARM_Dynarmic_64::ClearInterrupt() {
475 jit.load()->ClearHalt(break_loop);
476}
477
464void ARM_Dynarmic_64::ClearInstructionCache() { 478void ARM_Dynarmic_64::ClearInstructionCache() {
465 jit.load()->ClearCache(); 479 jit.load()->ClearCache();
466} 480}
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/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index 4b35ca82f..c1a484497 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -217,9 +217,7 @@ void IPSwitchCompiler::Parse() {
217 break; 217 break;
218 } else if (StartsWith(line, "@nsobid-")) { 218 } else if (StartsWith(line, "@nsobid-")) {
219 // NSO Build ID Specifier 219 // NSO Build ID Specifier
220 auto raw_build_id = line.substr(8); 220 const auto raw_build_id = fmt::format("{:0>64}", line.substr(8));
221 if (raw_build_id.size() != 0x40)
222 raw_build_id.resize(0x40, '0');
223 nso_build_id = Common::HexStringToArray<0x20>(raw_build_id); 221 nso_build_id = Common::HexStringToArray<0x20>(raw_build_id);
224 } else if (StartsWith(line, "#")) { 222 } else if (StartsWith(line, "#")) {
225 // Mandatory Comment 223 // Mandatory Comment
@@ -287,7 +285,8 @@ void IPSwitchCompiler::Parse() {
287 std::copy(value.begin(), value.end(), std::back_inserter(replace)); 285 std::copy(value.begin(), value.end(), std::back_inserter(replace));
288 } else { 286 } else {
289 // hex replacement 287 // hex replacement
290 const auto value = patch_line.substr(9); 288 const auto value =
289 patch_line.substr(9, patch_line.find_first_of(" /\r\n", 9) - 9);
291 replace = Common::HexStringToVector(value, is_little_endian); 290 replace = Common::HexStringToVector(value, is_little_endian);
292 } 291 }
293 292
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index bd525b26c..41348ab26 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -191,6 +191,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
191std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, 191std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
192 const std::string& build_id) const { 192 const std::string& build_id) const {
193 const auto& disabled = Settings::values.disabled_addons[title_id]; 193 const auto& disabled = Settings::values.disabled_addons[title_id];
194 const auto nso_build_id = fmt::format("{:0>64}", build_id);
194 195
195 std::vector<VirtualFile> out; 196 std::vector<VirtualFile> out;
196 out.reserve(patch_dirs.size()); 197 out.reserve(patch_dirs.size());
@@ -203,21 +204,18 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
203 for (const auto& file : exefs_dir->GetFiles()) { 204 for (const auto& file : exefs_dir->GetFiles()) {
204 if (file->GetExtension() == "ips") { 205 if (file->GetExtension() == "ips") {
205 auto name = file->GetName(); 206 auto name = file->GetName();
206 const auto p1 = name.substr(0, name.find('.'));
207 const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1);
208 207
209 if (build_id == this_build_id) 208 const auto this_build_id =
209 fmt::format("{:0>64}", name.substr(0, name.find('.')));
210 if (nso_build_id == this_build_id)
210 out.push_back(file); 211 out.push_back(file);
211 } else if (file->GetExtension() == "pchtxt") { 212 } else if (file->GetExtension() == "pchtxt") {
212 IPSwitchCompiler compiler{file}; 213 IPSwitchCompiler compiler{file};
213 if (!compiler.IsValid()) 214 if (!compiler.IsValid())
214 continue; 215 continue;
215 216
216 auto this_build_id = Common::HexToString(compiler.GetBuildID()); 217 const auto this_build_id = Common::HexToString(compiler.GetBuildID());
217 this_build_id = 218 if (nso_build_id == this_build_id)
218 this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1);
219
220 if (build_id == this_build_id)
221 out.push_back(file); 219 out.push_back(file);
222 } 220 }
223 } 221 }
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/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 2889973e4..e3ef06481 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -6,7 +6,6 @@
6#include "core/hle/kernel/k_event.h" 6#include "core/hle/kernel/k_event.h"
7#include "core/hle/service/kernel_helpers.h" 7#include "core/hle/service/kernel_helpers.h"
8#include "core/hle/service/nifm/nifm.h" 8#include "core/hle/service/nifm/nifm.h"
9#include "core/hle/service/service.h"
10 9
11namespace { 10namespace {
12 11
@@ -271,213 +270,228 @@ public:
271 } 270 }
272}; 271};
273 272
274class IGeneralService final : public ServiceFramework<IGeneralService> { 273void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) {
275public: 274 static constexpr u32 client_id = 1;
276 explicit IGeneralService(Core::System& system_); 275 LOG_WARNING(Service_NIFM, "(STUBBED) called");
277 276
278private: 277 IPC::ResponseBuilder rb{ctx, 4};
279 void GetClientId(Kernel::HLERequestContext& ctx) { 278 rb.Push(ResultSuccess);
280 static constexpr u32 client_id = 1; 279 rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
281 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 280}
282 281
283 IPC::ResponseBuilder rb{ctx, 4}; 282void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) {
284 rb.Push(ResultSuccess); 283 LOG_DEBUG(Service_NIFM, "called");
285 rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
286 }
287 284
288 void CreateScanRequest(Kernel::HLERequestContext& ctx) { 285 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
289 LOG_DEBUG(Service_NIFM, "called");
290 286
291 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 287 rb.Push(ResultSuccess);
288 rb.PushIpcInterface<IScanRequest>(system);
289}
292 290
293 rb.Push(ResultSuccess); 291void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) {
294 rb.PushIpcInterface<IScanRequest>(system); 292 LOG_DEBUG(Service_NIFM, "called");
295 }
296 293
297 void CreateRequest(Kernel::HLERequestContext& ctx) { 294 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
298 LOG_DEBUG(Service_NIFM, "called");
299 295
300 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 296 rb.Push(ResultSuccess);
297 rb.PushIpcInterface<IRequest>(system);
298}
301 299
302 rb.Push(ResultSuccess); 300void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
303 rb.PushIpcInterface<IRequest>(system); 301 LOG_WARNING(Service_NIFM, "(STUBBED) called");
304 }
305 302
306 void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { 303 const auto net_iface = Network::GetSelectedNetworkInterface();
307 LOG_WARNING(Service_NIFM, "(STUBBED) called");
308 304
309 const auto net_iface = Network::GetSelectedNetworkInterface(); 305 SfNetworkProfileData network_profile_data = [&net_iface] {
310 306 if (!net_iface) {
311 const SfNetworkProfileData network_profile_data = [&net_iface] { 307 return SfNetworkProfileData{};
312 if (!net_iface) { 308 }
313 return SfNetworkProfileData{}; 309
314 } 310 return SfNetworkProfileData{
315 311 .ip_setting_data{
316 return SfNetworkProfileData{ 312 .ip_address_setting{
317 .ip_setting_data{ 313 .is_automatic{true},
318 .ip_address_setting{ 314 .current_address{Network::TranslateIPv4(net_iface->ip_address)},
319 .is_automatic{true}, 315 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
320 .current_address{Network::TranslateIPv4(net_iface->ip_address)}, 316 .gateway{Network::TranslateIPv4(net_iface->gateway)},
321 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
322 .gateway{Network::TranslateIPv4(net_iface->gateway)},
323 },
324 .dns_setting{
325 .is_automatic{true},
326 .primary_dns{1, 1, 1, 1},
327 .secondary_dns{1, 0, 0, 1},
328 },
329 .proxy_setting{
330 .enabled{false},
331 .port{},
332 .proxy_server{},
333 .automatic_auth_enabled{},
334 .user{},
335 .password{},
336 },
337 .mtu{1500},
338 }, 317 },
339 .uuid{0xdeadbeef, 0xdeadbeef}, 318 .dns_setting{
340 .network_name{"yuzu Network"}, 319 .is_automatic{true},
341 .wireless_setting_data{ 320 .primary_dns{1, 1, 1, 1},
342 .ssid_length{12}, 321 .secondary_dns{1, 0, 0, 1},
343 .ssid{"yuzu Network"},
344 .passphrase{"yuzupassword"},
345 }, 322 },
346 }; 323 .proxy_setting{
347 }(); 324 .enabled{false},
348 325 .port{},
349 ctx.WriteBuffer(network_profile_data); 326 .proxy_server{},
327 .automatic_auth_enabled{},
328 .user{},
329 .password{},
330 },
331 .mtu{1500},
332 },
333 .uuid{0xdeadbeef, 0xdeadbeef},
334 .network_name{"yuzu Network"},
335 .wireless_setting_data{
336 .ssid_length{12},
337 .ssid{"yuzu Network"},
338 .passphrase{"yuzupassword"},
339 },
340 };
341 }();
350 342
351 IPC::ResponseBuilder rb{ctx, 2}; 343 // When we're connected to a room, spoof the hosts IP address
352 rb.Push(ResultSuccess); 344 if (auto room_member = network.GetRoomMember().lock()) {
345 if (room_member->IsConnected()) {
346 network_profile_data.ip_setting_data.ip_address_setting.current_address =
347 room_member->GetFakeIpAddress();
348 }
353 } 349 }
354 350
355 void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { 351 ctx.WriteBuffer(network_profile_data);
356 LOG_WARNING(Service_NIFM, "(STUBBED) called");
357 352
358 IPC::ResponseBuilder rb{ctx, 2}; 353 IPC::ResponseBuilder rb{ctx, 2};
359 rb.Push(ResultSuccess); 354 rb.Push(ResultSuccess);
360 } 355}
361 356
362 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { 357void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
363 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 358 LOG_WARNING(Service_NIFM, "(STUBBED) called");
364 359
365 auto ipv4 = Network::GetHostIPv4Address(); 360 IPC::ResponseBuilder rb{ctx, 2};
366 if (!ipv4) { 361 rb.Push(ResultSuccess);
367 LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); 362}
368 ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
369 }
370 363
371 IPC::ResponseBuilder rb{ctx, 3}; 364void IGeneralService::GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
372 rb.Push(ResultSuccess); 365 LOG_WARNING(Service_NIFM, "(STUBBED) called");
373 rb.PushRaw(*ipv4); 366
367 auto ipv4 = Network::GetHostIPv4Address();
368 if (!ipv4) {
369 LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
370 ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
374 } 371 }
375 372
376 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { 373 // When we're connected to a room, spoof the hosts IP address
377 LOG_DEBUG(Service_NIFM, "called"); 374 if (auto room_member = network.GetRoomMember().lock()) {
375 if (room_member->IsConnected()) {
376 ipv4 = room_member->GetFakeIpAddress();
377 }
378 }
378 379
379 ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, 380 IPC::ResponseBuilder rb{ctx, 3};
380 "SfNetworkProfileData is not the correct size"); 381 rb.Push(ResultSuccess);
381 u128 uuid{}; 382 rb.PushRaw(*ipv4);
382 auto buffer = ctx.ReadBuffer(); 383}
383 std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
384 384
385 IPC::ResponseBuilder rb{ctx, 6, 0, 1}; 385void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
386 LOG_DEBUG(Service_NIFM, "called");
386 387
387 rb.Push(ResultSuccess); 388 ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "SfNetworkProfileData is not the correct size");
388 rb.PushIpcInterface<INetworkProfile>(system); 389 u128 uuid{};
389 rb.PushRaw<u128>(uuid); 390 auto buffer = ctx.ReadBuffer();
390 } 391 std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
391 392
392 void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { 393 IPC::ResponseBuilder rb{ctx, 6, 0, 1};
393 LOG_WARNING(Service_NIFM, "(STUBBED) called");
394 394
395 struct IpConfigInfo { 395 rb.Push(ResultSuccess);
396 IpAddressSetting ip_address_setting{}; 396 rb.PushIpcInterface<INetworkProfile>(system);
397 DnsSetting dns_setting{}; 397 rb.PushRaw<u128>(uuid);
398 }; 398}
399 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
400 "IpConfigInfo has incorrect size.");
401 399
402 const auto net_iface = Network::GetSelectedNetworkInterface(); 400void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
401 LOG_WARNING(Service_NIFM, "(STUBBED) called");
403 402
404 const IpConfigInfo ip_config_info = [&net_iface] { 403 struct IpConfigInfo {
405 if (!net_iface) { 404 IpAddressSetting ip_address_setting{};
406 return IpConfigInfo{}; 405 DnsSetting dns_setting{};
407 } 406 };
407 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
408 "IpConfigInfo has incorrect size.");
408 409
409 return IpConfigInfo{ 410 const auto net_iface = Network::GetSelectedNetworkInterface();
410 .ip_address_setting{
411 .is_automatic{true},
412 .current_address{Network::TranslateIPv4(net_iface->ip_address)},
413 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
414 .gateway{Network::TranslateIPv4(net_iface->gateway)},
415 },
416 .dns_setting{
417 .is_automatic{true},
418 .primary_dns{1, 1, 1, 1},
419 .secondary_dns{1, 0, 0, 1},
420 },
421 };
422 }();
423 411
424 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; 412 IpConfigInfo ip_config_info = [&net_iface] {
425 rb.Push(ResultSuccess); 413 if (!net_iface) {
426 rb.PushRaw<IpConfigInfo>(ip_config_info); 414 return IpConfigInfo{};
427 } 415 }
428 416
429 void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { 417 return IpConfigInfo{
430 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 418 .ip_address_setting{
419 .is_automatic{true},
420 .current_address{Network::TranslateIPv4(net_iface->ip_address)},
421 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
422 .gateway{Network::TranslateIPv4(net_iface->gateway)},
423 },
424 .dns_setting{
425 .is_automatic{true},
426 .primary_dns{1, 1, 1, 1},
427 .secondary_dns{1, 0, 0, 1},
428 },
429 };
430 }();
431 431
432 IPC::ResponseBuilder rb{ctx, 3}; 432 // When we're connected to a room, spoof the hosts IP address
433 rb.Push(ResultSuccess); 433 if (auto room_member = network.GetRoomMember().lock()) {
434 rb.Push<u8>(0); 434 if (room_member->IsConnected()) {
435 ip_config_info.ip_address_setting.current_address = room_member->GetFakeIpAddress();
436 }
435 } 437 }
436 438
437 void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) { 439 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
438 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 440 rb.Push(ResultSuccess);
441 rb.PushRaw<IpConfigInfo>(ip_config_info);
442}
439 443
440 struct Output { 444void IGeneralService::IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
441 InternetConnectionType type{InternetConnectionType::WiFi}; 445 LOG_WARNING(Service_NIFM, "(STUBBED) called");
442 u8 wifi_strength{3};
443 InternetConnectionStatus state{InternetConnectionStatus::Connected};
444 };
445 static_assert(sizeof(Output) == 0x3, "Output has incorrect size.");
446 446
447 constexpr Output out{}; 447 IPC::ResponseBuilder rb{ctx, 3};
448 rb.Push(ResultSuccess);
449 rb.Push<u8>(1);
450}
448 451
449 IPC::ResponseBuilder rb{ctx, 3}; 452void IGeneralService::GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) {
450 rb.Push(ResultSuccess); 453 LOG_WARNING(Service_NIFM, "(STUBBED) called");
451 rb.PushRaw(out);
452 }
453 454
454 void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { 455 struct Output {
455 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 456 InternetConnectionType type{InternetConnectionType::WiFi};
457 u8 wifi_strength{3};
458 InternetConnectionStatus state{InternetConnectionStatus::Connected};
459 };
460 static_assert(sizeof(Output) == 0x3, "Output has incorrect size.");
456 461
457 IPC::ResponseBuilder rb{ctx, 3}; 462 constexpr Output out{};
458 rb.Push(ResultSuccess); 463
459 if (Network::GetHostIPv4Address().has_value()) { 464 IPC::ResponseBuilder rb{ctx, 3};
460 rb.Push<u8>(1); 465 rb.Push(ResultSuccess);
461 } else { 466 rb.PushRaw(out);
462 rb.Push<u8>(0); 467}
463 } 468
469void IGeneralService::IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
470 LOG_WARNING(Service_NIFM, "(STUBBED) called");
471
472 IPC::ResponseBuilder rb{ctx, 3};
473 rb.Push(ResultSuccess);
474 if (Network::GetHostIPv4Address().has_value()) {
475 rb.Push<u8>(1);
476 } else {
477 rb.Push<u8>(0);
464 } 478 }
479}
465 480
466 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { 481void IGeneralService::IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
467 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 482 LOG_ERROR(Service_NIFM, "(STUBBED) called");
468 483
469 IPC::ResponseBuilder rb{ctx, 3}; 484 IPC::ResponseBuilder rb{ctx, 3};
470 rb.Push(ResultSuccess); 485 rb.Push(ResultSuccess);
471 if (Network::GetHostIPv4Address().has_value()) { 486 if (Network::GetHostIPv4Address().has_value()) {
472 rb.Push<u8>(1); 487 rb.Push<u8>(1);
473 } else { 488 } else {
474 rb.Push<u8>(0); 489 rb.Push<u8>(0);
475 }
476 } 490 }
477}; 491}
478 492
479IGeneralService::IGeneralService(Core::System& system_) 493IGeneralService::IGeneralService(Core::System& system_)
480 : ServiceFramework{system_, "IGeneralService"} { 494 : ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} {
481 // clang-format off 495 // clang-format off
482 static const FunctionInfo functions[] = { 496 static const FunctionInfo functions[] = {
483 {1, &IGeneralService::GetClientId, "GetClientId"}, 497 {1, &IGeneralService::GetClientId, "GetClientId"},
@@ -528,6 +542,8 @@ IGeneralService::IGeneralService(Core::System& system_)
528 RegisterHandlers(functions); 542 RegisterHandlers(functions);
529} 543}
530 544
545IGeneralService::~IGeneralService() = default;
546
531class NetworkInterface final : public ServiceFramework<NetworkInterface> { 547class NetworkInterface final : public ServiceFramework<NetworkInterface> {
532public: 548public:
533 explicit NetworkInterface(const char* name, Core::System& system_) 549 explicit NetworkInterface(const char* name, Core::System& system_)
diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h
index 5f62d0014..48161be28 100644
--- a/src/core/hle/service/nifm/nifm.h
+++ b/src/core/hle/service/nifm/nifm.h
@@ -3,6 +3,11 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/service.h"
7#include "network/network.h"
8#include "network/room.h"
9#include "network/room_member.h"
10
6namespace Core { 11namespace Core {
7class System; 12class System;
8} 13}
@@ -16,4 +21,26 @@ namespace Service::NIFM {
16/// Registers all NIFM services with the specified service manager. 21/// Registers all NIFM services with the specified service manager.
17void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); 22void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
18 23
24class IGeneralService final : public ServiceFramework<IGeneralService> {
25public:
26 explicit IGeneralService(Core::System& system_);
27 ~IGeneralService() override;
28
29private:
30 void GetClientId(Kernel::HLERequestContext& ctx);
31 void CreateScanRequest(Kernel::HLERequestContext& ctx);
32 void CreateRequest(Kernel::HLERequestContext& ctx);
33 void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx);
34 void RemoveNetworkProfile(Kernel::HLERequestContext& ctx);
35 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx);
36 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx);
37 void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx);
38 void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx);
39 void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx);
40 void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx);
41 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx);
42
43 Network::RoomNetwork& network;
44};
45
19} // namespace Service::NIFM 46} // namespace Service::NIFM
diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp
index 0989474be..f7a497a14 100644
--- a/src/core/hle/service/pcv/pcv.cpp
+++ b/src/core/hle/service/pcv/pcv.cpp
@@ -3,6 +3,7 @@
3 3
4#include <memory> 4#include <memory>
5 5
6#include "core/hle/ipc_helpers.h"
6#include "core/hle/service/pcv/pcv.h" 7#include "core/hle/service/pcv/pcv.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8#include "core/hle/service/sm/sm.h" 9#include "core/hle/service/sm/sm.h"
@@ -77,10 +78,102 @@ public:
77 } 78 }
78}; 79};
79 80
81class IClkrstSession final : public ServiceFramework<IClkrstSession> {
82public:
83 explicit IClkrstSession(Core::System& system_, DeviceCode deivce_code_)
84 : ServiceFramework{system_, "IClkrstSession"}, deivce_code(deivce_code_) {
85 // clang-format off
86 static const FunctionInfo functions[] = {
87 {0, nullptr, "SetClockEnabled"},
88 {1, nullptr, "SetClockDisabled"},
89 {2, nullptr, "SetResetAsserted"},
90 {3, nullptr, "SetResetDeasserted"},
91 {4, nullptr, "SetPowerEnabled"},
92 {5, nullptr, "SetPowerDisabled"},
93 {6, nullptr, "GetState"},
94 {7, &IClkrstSession::SetClockRate, "SetClockRate"},
95 {8, &IClkrstSession::GetClockRate, "GetClockRate"},
96 {9, nullptr, "SetMinVClockRate"},
97 {10, nullptr, "GetPossibleClockRates"},
98 {11, nullptr, "GetDvfsTable"},
99 };
100 // clang-format on
101 RegisterHandlers(functions);
102 }
103
104private:
105 void SetClockRate(Kernel::HLERequestContext& ctx) {
106 IPC::RequestParser rp{ctx};
107 clock_rate = rp.Pop<u32>();
108 LOG_DEBUG(Service_PCV, "(STUBBED) called, clock_rate={}", clock_rate);
109
110 IPC::ResponseBuilder rb{ctx, 2};
111 rb.Push(ResultSuccess);
112 }
113
114 void GetClockRate(Kernel::HLERequestContext& ctx) {
115 LOG_DEBUG(Service_PCV, "(STUBBED) called");
116
117 IPC::ResponseBuilder rb{ctx, 3};
118 rb.Push(ResultSuccess);
119 rb.Push<u32>(clock_rate);
120 }
121
122 DeviceCode deivce_code;
123 u32 clock_rate{};
124};
125
126class CLKRST final : public ServiceFramework<CLKRST> {
127public:
128 explicit CLKRST(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
129 // clang-format off
130 static const FunctionInfo functions[] = {
131 {0, &CLKRST::OpenSession, "OpenSession"},
132 {1, nullptr, "GetTemperatureThresholds"},
133 {2, nullptr, "SetTemperature"},
134 {3, nullptr, "GetModuleStateTable"},
135 {4, nullptr, "GetModuleStateTableEvent"},
136 {5, nullptr, "GetModuleStateTableMaxCount"},
137 };
138 // clang-format on
139
140 RegisterHandlers(functions);
141 }
142
143private:
144 void OpenSession(Kernel::HLERequestContext& ctx) {
145 IPC::RequestParser rp{ctx};
146 const auto device_code = static_cast<DeviceCode>(rp.Pop<u32>());
147 const auto unkonwn_input = rp.Pop<u32>();
148
149 LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unkonwn_input);
150
151 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
152 rb.Push(ResultSuccess);
153 rb.PushIpcInterface<IClkrstSession>(system, device_code);
154 }
155};
156
157class CLKRST_A final : public ServiceFramework<CLKRST_A> {
158public:
159 explicit CLKRST_A(Core::System& system_) : ServiceFramework{system_, "clkrst:a"} {
160 // clang-format off
161 static const FunctionInfo functions[] = {
162 {0, nullptr, "ReleaseControl"},
163 };
164 // clang-format on
165
166 RegisterHandlers(functions);
167 }
168};
169
80void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { 170void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
81 std::make_shared<PCV>(system)->InstallAsService(sm); 171 std::make_shared<PCV>(system)->InstallAsService(sm);
82 std::make_shared<PCV_ARB>(system)->InstallAsService(sm); 172 std::make_shared<PCV_ARB>(system)->InstallAsService(sm);
83 std::make_shared<PCV_IMM>(system)->InstallAsService(sm); 173 std::make_shared<PCV_IMM>(system)->InstallAsService(sm);
174 std::make_shared<CLKRST>(system, "clkrst")->InstallAsService(sm);
175 std::make_shared<CLKRST>(system, "clkrst:i")->InstallAsService(sm);
176 std::make_shared<CLKRST_A>(system)->InstallAsService(sm);
84} 177}
85 178
86} // namespace Service::PCV 179} // namespace Service::PCV
diff --git a/src/core/hle/service/pcv/pcv.h b/src/core/hle/service/pcv/pcv.h
index a42e6f8f6..6b26b6fa7 100644
--- a/src/core/hle/service/pcv/pcv.h
+++ b/src/core/hle/service/pcv/pcv.h
@@ -13,6 +13,97 @@ class ServiceManager;
13 13
14namespace Service::PCV { 14namespace Service::PCV {
15 15
16enum class DeviceCode : u32 {
17 Cpu = 0x40000001,
18 Gpu = 0x40000002,
19 I2s1 = 0x40000003,
20 I2s2 = 0x40000004,
21 I2s3 = 0x40000005,
22 Pwm = 0x40000006,
23 I2c1 = 0x02000001,
24 I2c2 = 0x02000002,
25 I2c3 = 0x02000003,
26 I2c4 = 0x02000004,
27 I2c5 = 0x02000005,
28 I2c6 = 0x02000006,
29 Spi1 = 0x07000000,
30 Spi2 = 0x07000001,
31 Spi3 = 0x07000002,
32 Spi4 = 0x07000003,
33 Disp1 = 0x40000011,
34 Disp2 = 0x40000012,
35 Isp = 0x40000013,
36 Vi = 0x40000014,
37 Sdmmc1 = 0x40000015,
38 Sdmmc2 = 0x40000016,
39 Sdmmc3 = 0x40000017,
40 Sdmmc4 = 0x40000018,
41 Owr = 0x40000019,
42 Csite = 0x4000001A,
43 Tsec = 0x4000001B,
44 Mselect = 0x4000001C,
45 Hda2codec2x = 0x4000001D,
46 Actmon = 0x4000001E,
47 I2cSlow = 0x4000001F,
48 Sor1 = 0x40000020,
49 Sata = 0x40000021,
50 Hda = 0x40000022,
51 XusbCoreHostSrc = 0x40000023,
52 XusbFalconSrc = 0x40000024,
53 XusbFsSrc = 0x40000025,
54 XusbCoreDevSrc = 0x40000026,
55 XusbSsSrc = 0x40000027,
56 UartA = 0x03000001,
57 UartB = 0x35000405,
58 UartC = 0x3500040F,
59 UartD = 0x37000001,
60 Host1x = 0x4000002C,
61 Entropy = 0x4000002D,
62 SocTherm = 0x4000002E,
63 Vic = 0x4000002F,
64 Nvenc = 0x40000030,
65 Nvjpg = 0x40000031,
66 Nvdec = 0x40000032,
67 Qspi = 0x40000033,
68 ViI2c = 0x40000034,
69 Tsecb = 0x40000035,
70 Ape = 0x40000036,
71 AudioDsp = 0x40000037,
72 AudioUart = 0x40000038,
73 Emc = 0x40000039,
74 Plle = 0x4000003A,
75 PlleHwSeq = 0x4000003B,
76 Dsi = 0x4000003C,
77 Maud = 0x4000003D,
78 Dpaux1 = 0x4000003E,
79 MipiCal = 0x4000003F,
80 UartFstMipiCal = 0x40000040,
81 Osc = 0x40000041,
82 SysBus = 0x40000042,
83 SorSafe = 0x40000043,
84 XusbSs = 0x40000044,
85 XusbHost = 0x40000045,
86 XusbDevice = 0x40000046,
87 Extperiph1 = 0x40000047,
88 Ahub = 0x40000048,
89 Hda2hdmicodec = 0x40000049,
90 Gpuaux = 0x4000004A,
91 UsbD = 0x4000004B,
92 Usb2 = 0x4000004C,
93 Pcie = 0x4000004D,
94 Afi = 0x4000004E,
95 PciExClk = 0x4000004F,
96 PExUsbPhy = 0x40000050,
97 XUsbPadCtl = 0x40000051,
98 Apbdma = 0x40000052,
99 Usb2TrkClk = 0x40000053,
100 XUsbIoPll = 0x40000054,
101 XUsbIoPllHwSeq = 0x40000055,
102 Cec = 0x40000056,
103 Extperiph2 = 0x40000057,
104 OscClk = 0x40000080
105};
106
16void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); 107void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
17 108
18} // namespace Service::PCV 109} // namespace Service::PCV
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index c64291e7f..dadaf897f 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -194,13 +194,16 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
194 Kernel::HLERequestContext& ctx) { 194 Kernel::HLERequestContext& ctx) {
195 const auto guard = LockService(); 195 const auto guard = LockService();
196 196
197 Result result = ResultSuccess;
198
197 switch (ctx.GetCommandType()) { 199 switch (ctx.GetCommandType()) {
198 case IPC::CommandType::Close: 200 case IPC::CommandType::Close:
199 case IPC::CommandType::TIPC_Close: { 201 case IPC::CommandType::TIPC_Close: {
200 session.Close(); 202 session.Close();
201 IPC::ResponseBuilder rb{ctx, 2}; 203 IPC::ResponseBuilder rb{ctx, 2};
202 rb.Push(ResultSuccess); 204 rb.Push(ResultSuccess);
203 return IPC::ERR_REMOTE_PROCESS_DEAD; 205 result = IPC::ERR_REMOTE_PROCESS_DEAD;
206 break;
204 } 207 }
205 case IPC::CommandType::ControlWithContext: 208 case IPC::CommandType::ControlWithContext:
206 case IPC::CommandType::Control: { 209 case IPC::CommandType::Control: {
@@ -227,7 +230,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
227 ctx.WriteToOutgoingCommandBuffer(ctx.GetThread()); 230 ctx.WriteToOutgoingCommandBuffer(ctx.GetThread());
228 } 231 }
229 232
230 return ResultSuccess; 233 return result;
231} 234}
232 235
233/// Initialize Services 236/// Initialize Services
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index c7194731e..e08c3cb67 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -9,12 +9,16 @@
9#include <fmt/format.h> 9#include <fmt/format.h>
10 10
11#include "common/microprofile.h" 11#include "common/microprofile.h"
12#include "common/socket_types.h"
13#include "core/core.h"
12#include "core/hle/ipc_helpers.h" 14#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/k_thread.h" 15#include "core/hle/kernel/k_thread.h"
14#include "core/hle/service/sockets/bsd.h" 16#include "core/hle/service/sockets/bsd.h"
15#include "core/hle/service/sockets/sockets_translate.h" 17#include "core/hle/service/sockets/sockets_translate.h"
16#include "core/internal_network/network.h" 18#include "core/internal_network/network.h"
19#include "core/internal_network/socket_proxy.h"
17#include "core/internal_network/sockets.h" 20#include "core/internal_network/sockets.h"
21#include "network/network.h"
18 22
19namespace Service::Sockets { 23namespace Service::Sockets {
20 24
@@ -472,7 +476,13 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
472 476
473 LOG_INFO(Service, "New socket fd={}", fd); 477 LOG_INFO(Service, "New socket fd={}", fd);
474 478
475 descriptor.socket = std::make_unique<Network::Socket>(); 479 auto room_member = room_network.GetRoomMember().lock();
480 if (room_member && room_member->IsConnected()) {
481 descriptor.socket = std::make_unique<Network::ProxySocket>(room_network);
482 } else {
483 descriptor.socket = std::make_unique<Network::Socket>();
484 }
485
476 descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol)); 486 descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol));
477 descriptor.is_connection_based = IsConnectionBased(type); 487 descriptor.is_connection_based = IsConnectionBased(type);
478 488
@@ -648,7 +658,7 @@ std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
648 ASSERT(arg == 0); 658 ASSERT(arg == 0);
649 return {descriptor.flags, Errno::SUCCESS}; 659 return {descriptor.flags, Errno::SUCCESS};
650 case FcntlCmd::SETFL: { 660 case FcntlCmd::SETFL: {
651 const bool enable = (arg & FLAG_O_NONBLOCK) != 0; 661 const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0;
652 const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable)); 662 const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
653 if (bsd_errno != Errno::SUCCESS) { 663 if (bsd_errno != Errno::SUCCESS) {
654 return {-1, bsd_errno}; 664 return {-1, bsd_errno};
@@ -669,7 +679,7 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
669 return Errno::BADF; 679 return Errno::BADF;
670 } 680 }
671 681
672 Network::Socket* const socket = file_descriptors[fd]->socket.get(); 682 Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
673 683
674 if (optname == OptName::LINGER) { 684 if (optname == OptName::LINGER) {
675 ASSERT(optlen == sizeof(Linger)); 685 ASSERT(optlen == sizeof(Linger));
@@ -724,6 +734,8 @@ std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message)
724 FileDescriptor& descriptor = *file_descriptors[fd]; 734 FileDescriptor& descriptor = *file_descriptors[fd];
725 735
726 // Apply flags 736 // Apply flags
737 using Network::FLAG_MSG_DONTWAIT;
738 using Network::FLAG_O_NONBLOCK;
727 if ((flags & FLAG_MSG_DONTWAIT) != 0) { 739 if ((flags & FLAG_MSG_DONTWAIT) != 0) {
728 flags &= ~FLAG_MSG_DONTWAIT; 740 flags &= ~FLAG_MSG_DONTWAIT;
729 if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { 741 if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
@@ -759,6 +771,8 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
759 } 771 }
760 772
761 // Apply flags 773 // Apply flags
774 using Network::FLAG_MSG_DONTWAIT;
775 using Network::FLAG_O_NONBLOCK;
762 if ((flags & FLAG_MSG_DONTWAIT) != 0) { 776 if ((flags & FLAG_MSG_DONTWAIT) != 0) {
763 flags &= ~FLAG_MSG_DONTWAIT; 777 flags &= ~FLAG_MSG_DONTWAIT;
764 if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { 778 if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
@@ -857,8 +871,19 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
857 rb.PushEnum(bsd_errno); 871 rb.PushEnum(bsd_errno);
858} 872}
859 873
874void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
875 for (auto& optional_descriptor : file_descriptors) {
876 if (!optional_descriptor.has_value()) {
877 continue;
878 }
879 FileDescriptor& descriptor = *optional_descriptor;
880 descriptor.socket.get()->HandleProxyPacket(packet);
881 }
882}
883
860BSD::BSD(Core::System& system_, const char* name) 884BSD::BSD(Core::System& system_, const char* name)
861 : ServiceFramework{system_, name, ServiceThreadType::CreateNew} { 885 : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, room_network{
886 system_.GetRoomNetwork()} {
862 // clang-format off 887 // clang-format off
863 static const FunctionInfo functions[] = { 888 static const FunctionInfo functions[] = {
864 {0, &BSD::RegisterClient, "RegisterClient"}, 889 {0, &BSD::RegisterClient, "RegisterClient"},
@@ -899,6 +924,13 @@ BSD::BSD(Core::System& system_, const char* name)
899 // clang-format on 924 // clang-format on
900 925
901 RegisterHandlers(functions); 926 RegisterHandlers(functions);
927
928 if (auto room_member = room_network.GetRoomMember().lock()) {
929 proxy_packet_received = room_member->BindOnProxyPacketReceived(
930 [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); });
931 } else {
932 LOG_ERROR(Service, "Network isn't initalized");
933 }
902} 934}
903 935
904BSD::~BSD() = default; 936BSD::~BSD() = default;
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 9ea36428d..81e855e0f 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -7,14 +7,17 @@
7#include <vector> 7#include <vector>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/socket_types.h"
10#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
11#include "core/hle/service/sockets/sockets.h" 12#include "core/hle/service/sockets/sockets.h"
13#include "network/network.h"
12 14
13namespace Core { 15namespace Core {
14class System; 16class System;
15} 17}
16 18
17namespace Network { 19namespace Network {
20class SocketBase;
18class Socket; 21class Socket;
19} // namespace Network 22} // namespace Network
20 23
@@ -30,7 +33,7 @@ private:
30 static constexpr size_t MAX_FD = 128; 33 static constexpr size_t MAX_FD = 128;
31 34
32 struct FileDescriptor { 35 struct FileDescriptor {
33 std::unique_ptr<Network::Socket> socket; 36 std::unique_ptr<Network::SocketBase> socket;
34 s32 flags = 0; 37 s32 flags = 0;
35 bool is_connection_based = false; 38 bool is_connection_based = false;
36 }; 39 };
@@ -165,6 +168,14 @@ private:
165 void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept; 168 void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;
166 169
167 std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors; 170 std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
171
172 Network::RoomNetwork& room_network;
173
174 /// Callback to parse and handle a received wifi packet.
175 void OnProxyPacketReceived(const Network::ProxyPacket& packet);
176
177 // Callback identifier for the OnProxyPacketReceived event.
178 Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received;
168}; 179};
169 180
170class BSDCFG final : public ServiceFramework<BSDCFG> { 181class BSDCFG final : public ServiceFramework<BSDCFG> {
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h
index b735b00fc..31b7dad33 100644
--- a/src/core/hle/service/sockets/sockets.h
+++ b/src/core/hle/service/sockets/sockets.h
@@ -22,7 +22,9 @@ enum class Errno : u32 {
22 AGAIN = 11, 22 AGAIN = 11,
23 INVAL = 22, 23 INVAL = 22,
24 MFILE = 24, 24 MFILE = 24,
25 MSGSIZE = 90,
25 NOTCONN = 107, 26 NOTCONN = 107,
27 TIMEDOUT = 110,
26}; 28};
27 29
28enum class Domain : u32 { 30enum class Domain : u32 {
@@ -96,10 +98,6 @@ struct Linger {
96 u32 linger; 98 u32 linger;
97}; 99};
98 100
99constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
100
101constexpr u32 FLAG_O_NONBLOCK = 0x800;
102
103/// Registers all Sockets services with the specified service manager. 101/// Registers all Sockets services with the specified service manager.
104void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); 102void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
105 103
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp
index 2db10ec81..023aa0486 100644
--- a/src/core/hle/service/sockets/sockets_translate.cpp
+++ b/src/core/hle/service/sockets/sockets_translate.cpp
@@ -25,6 +25,8 @@ Errno Translate(Network::Errno value) {
25 return Errno::MFILE; 25 return Errno::MFILE;
26 case Network::Errno::NOTCONN: 26 case Network::Errno::NOTCONN:
27 return Errno::NOTCONN; 27 return Errno::NOTCONN;
28 case Network::Errno::TIMEDOUT:
29 return Errno::TIMEDOUT;
28 default: 30 default:
29 UNIMPLEMENTED_MSG("Unimplemented errno={}", value); 31 UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
30 return Errno::SUCCESS; 32 return Errno::SUCCESS;
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp
index 36c43cc8f..cdf38a2a4 100644
--- a/src/core/internal_network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -32,6 +32,7 @@
32#include "core/internal_network/network.h" 32#include "core/internal_network/network.h"
33#include "core/internal_network/network_interface.h" 33#include "core/internal_network/network_interface.h"
34#include "core/internal_network/sockets.h" 34#include "core/internal_network/sockets.h"
35#include "network/network.h"
35 36
36namespace Network { 37namespace Network {
37 38
@@ -114,7 +115,10 @@ Errno TranslateNativeError(int e) {
114 return Errno::NETDOWN; 115 return Errno::NETDOWN;
115 case WSAENETUNREACH: 116 case WSAENETUNREACH:
116 return Errno::NETUNREACH; 117 return Errno::NETUNREACH;
118 case WSAEMSGSIZE:
119 return Errno::MSGSIZE;
117 default: 120 default:
121 UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
118 return Errno::OTHER; 122 return Errno::OTHER;
119 } 123 }
120} 124}
@@ -125,7 +129,6 @@ using SOCKET = int;
125using WSAPOLLFD = pollfd; 129using WSAPOLLFD = pollfd;
126using ULONG = u64; 130using ULONG = u64;
127 131
128constexpr SOCKET INVALID_SOCKET = -1;
129constexpr SOCKET SOCKET_ERROR = -1; 132constexpr SOCKET SOCKET_ERROR = -1;
130 133
131constexpr int SD_RECEIVE = SHUT_RD; 134constexpr int SD_RECEIVE = SHUT_RD;
@@ -206,7 +209,10 @@ Errno TranslateNativeError(int e) {
206 return Errno::NETDOWN; 209 return Errno::NETDOWN;
207 case ENETUNREACH: 210 case ENETUNREACH:
208 return Errno::NETUNREACH; 211 return Errno::NETUNREACH;
212 case EMSGSIZE:
213 return Errno::MSGSIZE;
209 default: 214 default:
215 UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
210 return Errno::OTHER; 216 return Errno::OTHER;
211 } 217 }
212} 218}
@@ -329,16 +335,6 @@ PollEvents TranslatePollRevents(short revents) {
329 return result; 335 return result;
330} 336}
331 337
332template <typename T>
333Errno SetSockOpt(SOCKET fd, int option, T value) {
334 const int result =
335 setsockopt(fd, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value));
336 if (result != SOCKET_ERROR) {
337 return Errno::SUCCESS;
338 }
339 return GetAndLogLastError();
340}
341
342} // Anonymous namespace 338} // Anonymous namespace
343 339
344NetworkInstance::NetworkInstance() { 340NetworkInstance::NetworkInstance() {
@@ -350,26 +346,16 @@ NetworkInstance::~NetworkInstance() {
350} 346}
351 347
352std::optional<IPv4Address> GetHostIPv4Address() { 348std::optional<IPv4Address> GetHostIPv4Address() {
353 const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); 349 const auto network_interface = Network::GetSelectedNetworkInterface();
354 const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); 350 if (!network_interface.has_value()) {
355 if (network_interfaces.size() == 0) { 351 LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface");
356 LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
357 return {}; 352 return {};
358 } 353 }
359 354
360 const auto res = 355 std::array<char, 16> ip_addr = {};
361 std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { 356 ASSERT(inet_ntop(AF_INET, &network_interface->ip_address, ip_addr.data(), sizeof(ip_addr)) !=
362 return iface.name == selected_network_interface; 357 nullptr);
363 }); 358 return TranslateIPv4(network_interface->ip_address);
364
365 if (res != network_interfaces.end()) {
366 char ip_addr[16] = {};
367 ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr);
368 return TranslateIPv4(res->ip_address);
369 } else {
370 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
371 return {};
372 }
373} 359}
374 360
375std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { 361std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
@@ -412,7 +398,19 @@ Socket::~Socket() {
412 fd = INVALID_SOCKET; 398 fd = INVALID_SOCKET;
413} 399}
414 400
415Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {} 401Socket::Socket(Socket&& rhs) noexcept {
402 fd = std::exchange(rhs.fd, INVALID_SOCKET);
403}
404
405template <typename T>
406Errno Socket::SetSockOpt(SOCKET fd_, int option, T value) {
407 const int result =
408 setsockopt(fd_, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value));
409 if (result != SOCKET_ERROR) {
410 return Errno::SUCCESS;
411 }
412 return GetAndLogLastError();
413}
416 414
417Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { 415Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
418 fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); 416 fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol));
@@ -423,7 +421,7 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
423 return GetAndLogLastError(); 421 return GetAndLogLastError();
424} 422}
425 423
426std::pair<Socket::AcceptResult, Errno> Socket::Accept() { 424std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() {
427 sockaddr addr; 425 sockaddr addr;
428 socklen_t addrlen = sizeof(addr); 426 socklen_t addrlen = sizeof(addr);
429 const SOCKET new_socket = accept(fd, &addr, &addrlen); 427 const SOCKET new_socket = accept(fd, &addr, &addrlen);
@@ -634,4 +632,8 @@ bool Socket::IsOpened() const {
634 return fd != INVALID_SOCKET; 632 return fd != INVALID_SOCKET;
635} 633}
636 634
635void Socket::HandleProxyPacket(const ProxyPacket& packet) {
636 LOG_WARNING(Network, "ProxyPacket received, but not in Proxy mode!");
637}
638
637} // namespace Network 639} // namespace Network
diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h
index 10e5ef10d..36994c22e 100644
--- a/src/core/internal_network/network.h
+++ b/src/core/internal_network/network.h
@@ -8,6 +8,7 @@
8 8
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/socket_types.h"
11 12
12#ifdef _WIN32 13#ifdef _WIN32
13#include <winsock2.h> 14#include <winsock2.h>
@@ -17,6 +18,7 @@
17 18
18namespace Network { 19namespace Network {
19 20
21class SocketBase;
20class Socket; 22class Socket;
21 23
22/// Error code for network functions 24/// Error code for network functions
@@ -31,46 +33,11 @@ enum class Errno {
31 HOSTUNREACH, 33 HOSTUNREACH,
32 NETDOWN, 34 NETDOWN,
33 NETUNREACH, 35 NETUNREACH,
36 TIMEDOUT,
37 MSGSIZE,
34 OTHER, 38 OTHER,
35}; 39};
36 40
37/// Address families
38enum class Domain {
39 INET, ///< Address family for IPv4
40};
41
42/// Socket types
43enum class Type {
44 STREAM,
45 DGRAM,
46 RAW,
47 SEQPACKET,
48};
49
50/// Protocol values for sockets
51enum class Protocol {
52 ICMP,
53 TCP,
54 UDP,
55};
56
57/// Shutdown mode
58enum class ShutdownHow {
59 RD,
60 WR,
61 RDWR,
62};
63
64/// Array of IPv4 address
65using IPv4Address = std::array<u8, 4>;
66
67/// Cross-platform sockaddr structure
68struct SockAddrIn {
69 Domain family;
70 IPv4Address ip;
71 u16 portno;
72};
73
74/// Cross-platform poll fd structure 41/// Cross-platform poll fd structure
75 42
76enum class PollEvents : u16 { 43enum class PollEvents : u16 {
@@ -86,7 +53,7 @@ enum class PollEvents : u16 {
86DECLARE_ENUM_FLAG_OPERATORS(PollEvents); 53DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
87 54
88struct PollFD { 55struct PollFD {
89 Socket* socket; 56 SocketBase* socket;
90 PollEvents events; 57 PollEvents events;
91 PollEvents revents; 58 PollEvents revents;
92}; 59};
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp
new file mode 100644
index 000000000..49d067f4c
--- /dev/null
+++ b/src/core/internal_network/socket_proxy.cpp
@@ -0,0 +1,284 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5#include <thread>
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/internal_network/network.h"
10#include "core/internal_network/network_interface.h"
11#include "core/internal_network/socket_proxy.h"
12
13namespace Network {
14
15ProxySocket::ProxySocket(RoomNetwork& room_network_) noexcept : room_network{room_network_} {}
16
17ProxySocket::~ProxySocket() {
18 if (fd == INVALID_SOCKET) {
19 return;
20 }
21 fd = INVALID_SOCKET;
22}
23
24void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
25 if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno ||
26 closed) {
27 return;
28 }
29 std::lock_guard guard(packets_mutex);
30 received_packets.push(packet);
31}
32
33template <typename T>
34Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) {
35 LOG_DEBUG(Network, "(STUBBED) called");
36 return Errno::SUCCESS;
37}
38
39Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
40 protocol = socket_protocol;
41 SetSockOpt(fd, SO_TYPE, type);
42
43 return Errno::SUCCESS;
44}
45
46std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
47 LOG_WARNING(Network, "(STUBBED) called");
48 return {AcceptResult{}, Errno::SUCCESS};
49}
50
51Errno ProxySocket::Connect(SockAddrIn addr_in) {
52 LOG_WARNING(Network, "(STUBBED) called");
53 return Errno::SUCCESS;
54}
55
56std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() {
57 LOG_WARNING(Network, "(STUBBED) called");
58 return {SockAddrIn{}, Errno::SUCCESS};
59}
60
61std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() {
62 LOG_WARNING(Network, "(STUBBED) called");
63 return {SockAddrIn{}, Errno::SUCCESS};
64}
65
66Errno ProxySocket::Bind(SockAddrIn addr) {
67 if (is_bound) {
68 LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
69 return Errno::SUCCESS;
70 }
71 local_endpoint = addr;
72 is_bound = true;
73
74 return Errno::SUCCESS;
75}
76
77Errno ProxySocket::Listen(s32 backlog) {
78 LOG_WARNING(Network, "(STUBBED) called");
79 return Errno::SUCCESS;
80}
81
82Errno ProxySocket::Shutdown(ShutdownHow how) {
83 LOG_WARNING(Network, "(STUBBED) called");
84 return Errno::SUCCESS;
85}
86
87std::pair<s32, Errno> ProxySocket::Recv(int flags, std::vector<u8>& message) {
88 LOG_WARNING(Network, "(STUBBED) called");
89 ASSERT(flags == 0);
90 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
91
92 return {static_cast<s32>(0), Errno::SUCCESS};
93}
94
95std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) {
96 ASSERT(flags == 0);
97 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
98
99 // TODO (flTobi): Verify the timeout behavior and break when connection is lost
100 const auto timestamp = std::chrono::steady_clock::now();
101 // When receive_timeout is set to zero, the socket is supposed to wait indefinitely until a
102 // packet arrives. In order to prevent lost packets from hanging the emulation thread, we set
103 // the timeout to 5s instead
104 const auto timeout = receive_timeout == 0 ? 5000 : receive_timeout;
105 while (true) {
106 {
107 std::lock_guard guard(packets_mutex);
108 if (received_packets.size() > 0) {
109 return ReceivePacket(flags, message, addr, message.size());
110 }
111 }
112
113 if (!blocking) {
114 return {-1, Errno::AGAIN};
115 }
116
117 std::this_thread::yield();
118
119 const auto time_diff = std::chrono::steady_clock::now() - timestamp;
120 const auto time_diff_ms =
121 std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
122
123 if (time_diff_ms > timeout) {
124 return {-1, Errno::TIMEDOUT};
125 }
126 }
127}
128
129std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& message,
130 SockAddrIn* addr, std::size_t max_length) {
131 ProxyPacket& packet = received_packets.front();
132 if (addr) {
133 addr->family = Domain::INET;
134 addr->ip = packet.local_endpoint.ip; // The senders ip address
135 addr->portno = packet.local_endpoint.portno; // The senders port number
136 }
137
138 bool peek = (flags & FLAG_MSG_PEEK) != 0;
139 std::size_t read_bytes;
140 if (packet.data.size() > max_length) {
141 read_bytes = max_length;
142 message.clear();
143 std::copy(packet.data.begin(), packet.data.begin() + read_bytes,
144 std::back_inserter(message));
145 message.resize(max_length);
146
147 if (protocol == Protocol::UDP) {
148 if (!peek) {
149 received_packets.pop();
150 }
151 return {-1, Errno::MSGSIZE};
152 } else if (protocol == Protocol::TCP) {
153 std::vector<u8> numArray(packet.data.size() - max_length);
154 std::copy(packet.data.begin() + max_length, packet.data.end(),
155 std::back_inserter(numArray));
156 packet.data = numArray;
157 }
158 } else {
159 read_bytes = packet.data.size();
160 message.clear();
161 std::copy(packet.data.begin(), packet.data.end(), std::back_inserter(message));
162 message.resize(max_length);
163 if (!peek) {
164 received_packets.pop();
165 }
166 }
167
168 return {static_cast<u32>(read_bytes), Errno::SUCCESS};
169}
170
171std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flags) {
172 LOG_WARNING(Network, "(STUBBED) called");
173 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
174 ASSERT(flags == 0);
175
176 return {static_cast<s32>(0), Errno::SUCCESS};
177}
178
179void ProxySocket::SendPacket(ProxyPacket& packet) {
180 if (auto room_member = room_network.GetRoomMember().lock()) {
181 if (room_member->IsConnected()) {
182 room_member->SendProxyPacket(packet);
183 }
184 }
185}
186
187std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, const std::vector<u8>& message,
188 const SockAddrIn* addr) {
189 ASSERT(flags == 0);
190
191 if (!is_bound) {
192 LOG_ERROR(Network, "ProxySocket is not bound!");
193 return {static_cast<s32>(message.size()), Errno::SUCCESS};
194 }
195
196 if (auto room_member = room_network.GetRoomMember().lock()) {
197 if (!room_member->IsConnected()) {
198 return {static_cast<s32>(message.size()), Errno::SUCCESS};
199 }
200 }
201
202 ProxyPacket packet;
203 packet.local_endpoint = local_endpoint;
204 packet.remote_endpoint = *addr;
205 packet.protocol = protocol;
206 packet.broadcast = broadcast;
207
208 auto& ip = local_endpoint.ip;
209 auto ipv4 = Network::GetHostIPv4Address();
210 // If the ip is all zeroes (INADDR_ANY) or if it matches the hosts ip address,
211 // replace it with a "fake" routing address
212 if (std::all_of(ip.begin(), ip.end(), [](u8 i) { return i == 0; }) || (ipv4 && ipv4 == ip)) {
213 if (auto room_member = room_network.GetRoomMember().lock()) {
214 packet.local_endpoint.ip = room_member->GetFakeIpAddress();
215 }
216 }
217
218 packet.data.clear();
219 std::copy(message.begin(), message.end(), std::back_inserter(packet.data));
220
221 SendPacket(packet);
222
223 return {static_cast<s32>(message.size()), Errno::SUCCESS};
224}
225
226Errno ProxySocket::Close() {
227 fd = INVALID_SOCKET;
228 closed = true;
229
230 return Errno::SUCCESS;
231}
232
233Errno ProxySocket::SetLinger(bool enable, u32 linger) {
234 struct Linger {
235 u16 linger_enable;
236 u16 linger_time;
237 } values;
238 values.linger_enable = enable ? 1 : 0;
239 values.linger_time = static_cast<u16>(linger);
240
241 return SetSockOpt(fd, SO_LINGER, values);
242}
243
244Errno ProxySocket::SetReuseAddr(bool enable) {
245 return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
246}
247
248Errno ProxySocket::SetBroadcast(bool enable) {
249 broadcast = enable;
250 return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
251}
252
253Errno ProxySocket::SetSndBuf(u32 value) {
254 return SetSockOpt(fd, SO_SNDBUF, value);
255}
256
257Errno ProxySocket::SetKeepAlive(bool enable) {
258 return Errno::SUCCESS;
259}
260
261Errno ProxySocket::SetRcvBuf(u32 value) {
262 return SetSockOpt(fd, SO_RCVBUF, value);
263}
264
265Errno ProxySocket::SetSndTimeo(u32 value) {
266 send_timeout = value;
267 return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value));
268}
269
270Errno ProxySocket::SetRcvTimeo(u32 value) {
271 receive_timeout = value;
272 return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value));
273}
274
275Errno ProxySocket::SetNonBlock(bool enable) {
276 blocking = !enable;
277 return Errno::SUCCESS;
278}
279
280bool ProxySocket::IsOpened() const {
281 return fd != INVALID_SOCKET;
282}
283
284} // namespace Network
diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h
new file mode 100644
index 000000000..f12b5f567
--- /dev/null
+++ b/src/core/internal_network/socket_proxy.h
@@ -0,0 +1,97 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7#include <vector>
8#include <queue>
9
10#include "common/common_funcs.h"
11#include "core/internal_network/sockets.h"
12#include "network/network.h"
13
14namespace Network {
15
16class ProxySocket : public SocketBase {
17public:
18 YUZU_NON_COPYABLE(ProxySocket);
19 YUZU_NON_MOVEABLE(ProxySocket);
20
21 explicit ProxySocket(RoomNetwork& room_network_) noexcept;
22 ~ProxySocket() override;
23
24 void HandleProxyPacket(const ProxyPacket& packet) override;
25
26 Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
27
28 Errno Close() override;
29
30 std::pair<AcceptResult, Errno> Accept() override;
31
32 Errno Connect(SockAddrIn addr_in) override;
33
34 std::pair<SockAddrIn, Errno> GetPeerName() override;
35
36 std::pair<SockAddrIn, Errno> GetSockName() override;
37
38 Errno Bind(SockAddrIn addr) override;
39
40 Errno Listen(s32 backlog) override;
41
42 Errno Shutdown(ShutdownHow how) override;
43
44 std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override;
45
46 std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override;
47
48 std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr,
49 std::size_t max_length);
50
51 std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override;
52
53 void SendPacket(ProxyPacket& packet);
54
55 std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
56 const SockAddrIn* addr) override;
57
58 Errno SetLinger(bool enable, u32 linger) override;
59
60 Errno SetReuseAddr(bool enable) override;
61
62 Errno SetBroadcast(bool enable) override;
63
64 Errno SetKeepAlive(bool enable) override;
65
66 Errno SetSndBuf(u32 value) override;
67
68 Errno SetRcvBuf(u32 value) override;
69
70 Errno SetSndTimeo(u32 value) override;
71
72 Errno SetRcvTimeo(u32 value) override;
73
74 Errno SetNonBlock(bool enable) override;
75
76 template <typename T>
77 Errno SetSockOpt(SOCKET fd, int option, T value);
78
79 bool IsOpened() const override;
80
81private:
82 bool broadcast = false;
83 bool closed = false;
84 u32 send_timeout = 0;
85 u32 receive_timeout = 0;
86 bool is_bound = false;
87 SockAddrIn local_endpoint{};
88 bool blocking = true;
89 std::queue<ProxyPacket> received_packets;
90 Protocol protocol;
91
92 std::mutex packets_mutex;
93
94 RoomNetwork& room_network;
95};
96
97} // namespace Network
diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h
index 77e27e928..a70429b19 100644
--- a/src/core/internal_network/sockets.h
+++ b/src/core/internal_network/sockets.h
@@ -14,20 +14,88 @@
14 14
15#include "common/common_types.h" 15#include "common/common_types.h"
16#include "core/internal_network/network.h" 16#include "core/internal_network/network.h"
17#include "network/network.h"
17 18
18// TODO: C++20 Replace std::vector usages with std::span 19// TODO: C++20 Replace std::vector usages with std::span
19 20
20namespace Network { 21namespace Network {
21 22
22class Socket { 23class SocketBase {
23public: 24public:
25#ifdef YUZU_UNIX
26 using SOCKET = int;
27 static constexpr SOCKET INVALID_SOCKET = -1;
28 static constexpr SOCKET SOCKET_ERROR = -1;
29#endif
30
24 struct AcceptResult { 31 struct AcceptResult {
25 std::unique_ptr<Socket> socket; 32 std::unique_ptr<SocketBase> socket;
26 SockAddrIn sockaddr_in; 33 SockAddrIn sockaddr_in;
27 }; 34 };
35 virtual ~SocketBase() = default;
36
37 virtual SocketBase& operator=(const SocketBase&) = delete;
38
39 // Avoid closing sockets implicitly
40 virtual SocketBase& operator=(SocketBase&&) noexcept = delete;
41
42 virtual Errno Initialize(Domain domain, Type type, Protocol protocol) = 0;
43
44 virtual Errno Close() = 0;
45
46 virtual std::pair<AcceptResult, Errno> Accept() = 0;
47
48 virtual Errno Connect(SockAddrIn addr_in) = 0;
49
50 virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0;
51
52 virtual std::pair<SockAddrIn, Errno> GetSockName() = 0;
53
54 virtual Errno Bind(SockAddrIn addr) = 0;
55
56 virtual Errno Listen(s32 backlog) = 0;
57
58 virtual Errno Shutdown(ShutdownHow how) = 0;
59
60 virtual std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) = 0;
61
62 virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message,
63 SockAddrIn* addr) = 0;
64
65 virtual std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) = 0;
66
67 virtual std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
68 const SockAddrIn* addr) = 0;
69
70 virtual Errno SetLinger(bool enable, u32 linger) = 0;
28 71
29 explicit Socket() = default; 72 virtual Errno SetReuseAddr(bool enable) = 0;
30 ~Socket(); 73
74 virtual Errno SetKeepAlive(bool enable) = 0;
75
76 virtual Errno SetBroadcast(bool enable) = 0;
77
78 virtual Errno SetSndBuf(u32 value) = 0;
79
80 virtual Errno SetRcvBuf(u32 value) = 0;
81
82 virtual Errno SetSndTimeo(u32 value) = 0;
83
84 virtual Errno SetRcvTimeo(u32 value) = 0;
85
86 virtual Errno SetNonBlock(bool enable) = 0;
87
88 virtual bool IsOpened() const = 0;
89
90 virtual void HandleProxyPacket(const ProxyPacket& packet) = 0;
91
92 SOCKET fd = INVALID_SOCKET;
93};
94
95class Socket : public SocketBase {
96public:
97 Socket() = default;
98 ~Socket() override;
31 99
32 Socket(const Socket&) = delete; 100 Socket(const Socket&) = delete;
33 Socket& operator=(const Socket&) = delete; 101 Socket& operator=(const Socket&) = delete;
@@ -37,57 +105,57 @@ public:
37 // Avoid closing sockets implicitly 105 // Avoid closing sockets implicitly
38 Socket& operator=(Socket&&) noexcept = delete; 106 Socket& operator=(Socket&&) noexcept = delete;
39 107
40 Errno Initialize(Domain domain, Type type, Protocol protocol); 108 Errno Initialize(Domain domain, Type type, Protocol protocol) override;
41 109
42 Errno Close(); 110 Errno Close() override;
43 111
44 std::pair<AcceptResult, Errno> Accept(); 112 std::pair<AcceptResult, Errno> Accept() override;
45 113
46 Errno Connect(SockAddrIn addr_in); 114 Errno Connect(SockAddrIn addr_in) override;
47 115
48 std::pair<SockAddrIn, Errno> GetPeerName(); 116 std::pair<SockAddrIn, Errno> GetPeerName() override;
49 117
50 std::pair<SockAddrIn, Errno> GetSockName(); 118 std::pair<SockAddrIn, Errno> GetSockName() override;
51 119
52 Errno Bind(SockAddrIn addr); 120 Errno Bind(SockAddrIn addr) override;
53 121
54 Errno Listen(s32 backlog); 122 Errno Listen(s32 backlog) override;
55 123
56 Errno Shutdown(ShutdownHow how); 124 Errno Shutdown(ShutdownHow how) override;
57 125
58 std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message); 126 std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override;
59 127
60 std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr); 128 std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override;
61 129
62 std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags); 130 std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override;
63 131
64 std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, const SockAddrIn* addr); 132 std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
133 const SockAddrIn* addr) override;
65 134
66 Errno SetLinger(bool enable, u32 linger); 135 Errno SetLinger(bool enable, u32 linger) override;
67 136
68 Errno SetReuseAddr(bool enable); 137 Errno SetReuseAddr(bool enable) override;
69 138
70 Errno SetKeepAlive(bool enable); 139 Errno SetKeepAlive(bool enable) override;
71 140
72 Errno SetBroadcast(bool enable); 141 Errno SetBroadcast(bool enable) override;
73 142
74 Errno SetSndBuf(u32 value); 143 Errno SetSndBuf(u32 value) override;
75 144
76 Errno SetRcvBuf(u32 value); 145 Errno SetRcvBuf(u32 value) override;
77 146
78 Errno SetSndTimeo(u32 value); 147 Errno SetSndTimeo(u32 value) override;
79 148
80 Errno SetRcvTimeo(u32 value); 149 Errno SetRcvTimeo(u32 value) override;
81 150
82 Errno SetNonBlock(bool enable); 151 Errno SetNonBlock(bool enable) override;
83 152
84 bool IsOpened() const; 153 template <typename T>
154 Errno SetSockOpt(SOCKET fd, int option, T value);
85 155
86#if defined(_WIN32) 156 bool IsOpened() const override;
87 SOCKET fd = INVALID_SOCKET; 157
88#elif YUZU_UNIX 158 void HandleProxyPacket(const ProxyPacket& packet) override;
89 int fd = -1;
90#endif
91}; 159};
92 160
93std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout); 161std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
deleted file mode 100644
index dfb10c34f..000000000
--- a/src/core/loader/elf.cpp
+++ /dev/null
@@ -1,263 +0,0 @@
1// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
2// SPDX-FileCopyrightText: 2014 Citra Emulator Project
3// SPDX-License-Identifier: GPL-2.0-or-later
4
5#include <cstring>
6#include <memory>
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9#include "common/elf.h"
10#include "common/logging/log.h"
11#include "core/hle/kernel/code_set.h"
12#include "core/hle/kernel/k_page_table.h"
13#include "core/hle/kernel/k_process.h"
14#include "core/loader/elf.h"
15#include "core/memory.h"
16
17using namespace Common::ELF;
18
19////////////////////////////////////////////////////////////////////////////////////////////////////
20// ElfReader class
21
22typedef int SectionID;
23
24class ElfReader {
25private:
26 char* base;
27 u32* base32;
28
29 Elf32_Ehdr* header;
30 Elf32_Phdr* segments;
31 Elf32_Shdr* sections;
32
33 u32* sectionAddrs;
34 bool relocate;
35 VAddr entryPoint;
36
37public:
38 explicit ElfReader(void* ptr);
39
40 u32 Read32(int off) const {
41 return base32[off >> 2];
42 }
43
44 // Quick accessors
45 u16 GetType() const {
46 return header->e_type;
47 }
48 u16 GetMachine() const {
49 return header->e_machine;
50 }
51 VAddr GetEntryPoint() const {
52 return entryPoint;
53 }
54 u32 GetFlags() const {
55 return (u32)(header->e_flags);
56 }
57 Kernel::CodeSet LoadInto(VAddr vaddr);
58
59 int GetNumSegments() const {
60 return (int)(header->e_phnum);
61 }
62 int GetNumSections() const {
63 return (int)(header->e_shnum);
64 }
65 const u8* GetPtr(int offset) const {
66 return (u8*)base + offset;
67 }
68 const char* GetSectionName(int section) const;
69 const u8* GetSectionDataPtr(int section) const {
70 if (section < 0 || section >= header->e_shnum)
71 return nullptr;
72 if (sections[section].sh_type != ElfShtNobits)
73 return GetPtr(sections[section].sh_offset);
74 else
75 return nullptr;
76 }
77 bool IsCodeSection(int section) const {
78 return sections[section].sh_type == ElfShtProgBits;
79 }
80 const u8* GetSegmentPtr(int segment) {
81 return GetPtr(segments[segment].p_offset);
82 }
83 u32 GetSectionAddr(SectionID section) const {
84 return sectionAddrs[section];
85 }
86 unsigned int GetSectionSize(SectionID section) const {
87 return sections[section].sh_size;
88 }
89 SectionID GetSectionByName(const char* name, int firstSection = 0) const; //-1 for not found
90
91 bool DidRelocate() const {
92 return relocate;
93 }
94};
95
96ElfReader::ElfReader(void* ptr) {
97 base = (char*)ptr;
98 base32 = (u32*)ptr;
99 header = (Elf32_Ehdr*)ptr;
100
101 segments = (Elf32_Phdr*)(base + header->e_phoff);
102 sections = (Elf32_Shdr*)(base + header->e_shoff);
103
104 entryPoint = header->e_entry;
105}
106
107const char* ElfReader::GetSectionName(int section) const {
108 if (sections[section].sh_type == ElfShtNull)
109 return nullptr;
110
111 int name_offset = sections[section].sh_name;
112 const char* ptr = reinterpret_cast<const char*>(GetSectionDataPtr(header->e_shstrndx));
113
114 if (ptr)
115 return ptr + name_offset;
116
117 return nullptr;
118}
119
120Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
121 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
122
123 // Should we relocate?
124 relocate = (header->e_type != ElfTypeExec);
125
126 if (relocate) {
127 LOG_DEBUG(Loader, "Relocatable module");
128 entryPoint += vaddr;
129 } else {
130 LOG_DEBUG(Loader, "Prerelocated executable");
131 }
132 LOG_DEBUG(Loader, "{} segments:", header->e_phnum);
133
134 // First pass : Get the bits into RAM
135 const VAddr base_addr = relocate ? vaddr : 0;
136
137 u64 total_image_size = 0;
138 for (unsigned int i = 0; i < header->e_phnum; ++i) {
139 const Elf32_Phdr* p = &segments[i];
140 if (p->p_type == ElfPtLoad) {
141 total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF;
142 }
143 }
144
145 Kernel::PhysicalMemory program_image(total_image_size);
146 std::size_t current_image_position = 0;
147
148 Kernel::CodeSet codeset;
149
150 for (unsigned int i = 0; i < header->e_phnum; ++i) {
151 const Elf32_Phdr* p = &segments[i];
152 LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type,
153 p->p_vaddr, p->p_filesz, p->p_memsz);
154
155 if (p->p_type == ElfPtLoad) {
156 Kernel::CodeSet::Segment* codeset_segment;
157 u32 permission_flags = p->p_flags & (ElfPfRead | ElfPfWrite | ElfPfExec);
158 if (permission_flags == (ElfPfRead | ElfPfExec)) {
159 codeset_segment = &codeset.CodeSegment();
160 } else if (permission_flags == (ElfPfRead)) {
161 codeset_segment = &codeset.RODataSegment();
162 } else if (permission_flags == (ElfPfRead | ElfPfWrite)) {
163 codeset_segment = &codeset.DataSegment();
164 } else {
165 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
166 p->p_flags);
167 continue;
168 }
169
170 if (codeset_segment->size != 0) {
171 LOG_ERROR(Loader,
172 "ELF has more than one segment of the same type. Skipping extra "
173 "segment (id {})",
174 i);
175 continue;
176 }
177
178 const VAddr segment_addr = base_addr + p->p_vaddr;
179 const u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF;
180
181 codeset_segment->offset = current_image_position;
182 codeset_segment->addr = segment_addr;
183 codeset_segment->size = aligned_size;
184
185 std::memcpy(program_image.data() + current_image_position, GetSegmentPtr(i),
186 p->p_filesz);
187 current_image_position += aligned_size;
188 }
189 }
190
191 codeset.entrypoint = base_addr + header->e_entry;
192 codeset.memory = std::move(program_image);
193
194 LOG_DEBUG(Loader, "Done loading.");
195
196 return codeset;
197}
198
199SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const {
200 for (int i = firstSection; i < header->e_shnum; i++) {
201 const char* secname = GetSectionName(i);
202
203 if (secname != nullptr && strcmp(name, secname) == 0)
204 return i;
205 }
206 return -1;
207}
208
209////////////////////////////////////////////////////////////////////////////////////////////////////
210// Loader namespace
211
212namespace Loader {
213
214AppLoader_ELF::AppLoader_ELF(FileSys::VirtualFile file_) : AppLoader(std::move(file_)) {}
215
216FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& elf_file) {
217 static constexpr u16 ELF_MACHINE_ARM{0x28};
218
219 u32 magic = 0;
220 if (4 != elf_file->ReadObject(&magic)) {
221 return FileType::Error;
222 }
223
224 u16 machine = 0;
225 if (2 != elf_file->ReadObject(&machine, 18)) {
226 return FileType::Error;
227 }
228
229 if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine) {
230 return FileType::ELF;
231 }
232
233 return FileType::Error;
234}
235
236AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::KProcess& process,
237 [[maybe_unused]] Core::System& system) {
238 if (is_loaded) {
239 return {ResultStatus::ErrorAlreadyLoaded, {}};
240 }
241
242 std::vector<u8> buffer = file->ReadAllBytes();
243 if (buffer.size() != file->GetSize()) {
244 return {ResultStatus::ErrorIncorrectELFFileSize, {}};
245 }
246
247 const VAddr base_address = process.PageTable().GetCodeRegionStart();
248 ElfReader elf_reader(&buffer[0]);
249 Kernel::CodeSet codeset = elf_reader.LoadInto(base_address);
250 const VAddr entry_point = codeset.entrypoint;
251
252 // Setup the process code layout
253 if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), buffer.size()).IsError()) {
254 return {ResultStatus::ErrorNotInitialized, {}};
255 }
256
257 process.LoadModule(std::move(codeset), entry_point);
258
259 is_loaded = true;
260 return {ResultStatus::Success, LoadParameters{48, Core::Memory::DEFAULT_STACK_SIZE}};
261}
262
263} // namespace Loader
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
deleted file mode 100644
index acd33dc3d..000000000
--- a/src/core/loader/elf.h
+++ /dev/null
@@ -1,36 +0,0 @@
1// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
2// SPDX-FileCopyrightText: 2014 Citra Emulator Project
3// SPDX-License-Identifier: GPL-2.0-or-later
4
5#pragma once
6
7#include "core/loader/loader.h"
8
9namespace Core {
10class System;
11}
12
13namespace Loader {
14
15/// Loads an ELF/AXF file
16class AppLoader_ELF final : public AppLoader {
17public:
18 explicit AppLoader_ELF(FileSys::VirtualFile file);
19
20 /**
21 * Identifies whether or not the given file is an ELF file.
22 *
23 * @param elf_file The file to identify.
24 *
25 * @return FileType::ELF, or FileType::Error if the file is not an ELF file.
26 */
27 static FileType IdentifyType(const FileSys::VirtualFile& elf_file);
28
29 FileType GetFileType() const override {
30 return IdentifyType(file);
31 }
32
33 LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
34};
35
36} // namespace Loader
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index 9af46a0f7..d8a1bf82a 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -14,7 +14,7 @@ namespace Loader {
14 14
15namespace { 15namespace {
16constexpr u32 PageAlignSize(u32 size) { 16constexpr u32 PageAlignSize(u32 size) {
17 return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK); 17 return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
18} 18}
19} // Anonymous namespace 19} // Anonymous namespace
20 20
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 994ee891f..104d16efa 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -12,7 +12,6 @@
12#include "core/core.h" 12#include "core/core.h"
13#include "core/hle/kernel/k_process.h" 13#include "core/hle/kernel/k_process.h"
14#include "core/loader/deconstructed_rom_directory.h" 14#include "core/loader/deconstructed_rom_directory.h"
15#include "core/loader/elf.h"
16#include "core/loader/kip.h" 15#include "core/loader/kip.h"
17#include "core/loader/nax.h" 16#include "core/loader/nax.h"
18#include "core/loader/nca.h" 17#include "core/loader/nca.h"
@@ -39,8 +38,6 @@ std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) {
39FileType IdentifyFile(FileSys::VirtualFile file) { 38FileType IdentifyFile(FileSys::VirtualFile file) {
40 if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) { 39 if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) {
41 return *romdir_type; 40 return *romdir_type;
42 } else if (const auto elf_type = IdentifyFileLoader<AppLoader_ELF>(file)) {
43 return *elf_type;
44 } else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) { 41 } else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) {
45 return *nso_type; 42 return *nso_type;
46 } else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) { 43 } else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) {
@@ -69,8 +66,6 @@ FileType GuessFromFilename(const std::string& name) {
69 const std::string extension = 66 const std::string extension =
70 Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name))); 67 Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
71 68
72 if (extension == "elf")
73 return FileType::ELF;
74 if (extension == "nro") 69 if (extension == "nro")
75 return FileType::NRO; 70 return FileType::NRO;
76 if (extension == "nso") 71 if (extension == "nso")
@@ -89,8 +84,6 @@ FileType GuessFromFilename(const std::string& name) {
89 84
90std::string GetFileTypeString(FileType type) { 85std::string GetFileTypeString(FileType type) {
91 switch (type) { 86 switch (type) {
92 case FileType::ELF:
93 return "ELF";
94 case FileType::NRO: 87 case FileType::NRO:
95 return "NRO"; 88 return "NRO";
96 case FileType::NSO: 89 case FileType::NSO:
@@ -208,10 +201,6 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
208 FileType type, u64 program_id, 201 FileType type, u64 program_id,
209 std::size_t program_index) { 202 std::size_t program_index) {
210 switch (type) { 203 switch (type) {
211 // Standard ELF file format.
212 case FileType::ELF:
213 return std::make_unique<AppLoader_ELF>(std::move(file));
214
215 // NX NSO file format. 204 // NX NSO file format.
216 case FileType::NSO: 205 case FileType::NSO:
217 return std::make_unique<AppLoader_NSO>(std::move(file)); 206 return std::make_unique<AppLoader_NSO>(std::move(file));
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 7bf4faaf1..7b43f70ed 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -34,7 +34,6 @@ namespace Loader {
34enum class FileType { 34enum class FileType {
35 Error, 35 Error,
36 Unknown, 36 Unknown,
37 ELF,
38 NSO, 37 NSO,
39 NRO, 38 NRO,
40 NCA, 39 NCA,
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 1b0bb0876..73d04d7ee 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -125,7 +125,7 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) {
125} 125}
126 126
127static constexpr u32 PageAlignSize(u32 size) { 127static constexpr u32 PageAlignSize(u32 size) {
128 return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK); 128 return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
129} 129}
130 130
131static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) { 131static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) {
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 8dd956fc6..4c3b3c655 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -45,7 +45,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
45} 45}
46 46
47constexpr u32 PageAlignSize(u32 size) { 47constexpr u32 PageAlignSize(u32 size) {
48 return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK); 48 return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
49} 49}
50} // Anonymous namespace 50} // Anonymous namespace
51 51
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 1b44280b5..34ad7cadd 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -36,10 +36,11 @@ struct Memory::Impl {
36 } 36 }
37 37
38 void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { 38 void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {
39 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); 39 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
40 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); 40 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", base);
41 ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", target); 41 ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", target);
42 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory); 42 MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target,
43 Common::PageType::Memory);
43 44
44 if (Settings::IsFastmemEnabled()) { 45 if (Settings::IsFastmemEnabled()) {
45 system.DeviceMemory().buffer.Map(base, target - DramMemoryMap::Base, size); 46 system.DeviceMemory().buffer.Map(base, target - DramMemoryMap::Base, size);
@@ -47,9 +48,10 @@ struct Memory::Impl {
47 } 48 }
48 49
49 void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { 50 void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
50 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); 51 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
51 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); 52 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", base);
52 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped); 53 MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0,
54 Common::PageType::Unmapped);
53 55
54 if (Settings::IsFastmemEnabled()) { 56 if (Settings::IsFastmemEnabled()) {
55 system.DeviceMemory().buffer.Unmap(base, size); 57 system.DeviceMemory().buffer.Unmap(base, size);
@@ -57,7 +59,7 @@ struct Memory::Impl {
57 } 59 }
58 60
59 [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const { 61 [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const {
60 const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]}; 62 const PAddr paddr{current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]};
61 63
62 if (!paddr) { 64 if (!paddr) {
63 return {}; 65 return {};
@@ -67,7 +69,7 @@ struct Memory::Impl {
67 } 69 }
68 70
69 [[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const { 71 [[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const {
70 const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]}; 72 const PAddr paddr{current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]};
71 73
72 if (paddr == 0) { 74 if (paddr == 0) {
73 return {}; 75 return {};
@@ -176,13 +178,14 @@ struct Memory::Impl {
176 auto on_unmapped, auto on_memory, auto on_rasterizer, auto increment) { 178 auto on_unmapped, auto on_memory, auto on_rasterizer, auto increment) {
177 const auto& page_table = process.PageTable().PageTableImpl(); 179 const auto& page_table = process.PageTable().PageTableImpl();
178 std::size_t remaining_size = size; 180 std::size_t remaining_size = size;
179 std::size_t page_index = addr >> PAGE_BITS; 181 std::size_t page_index = addr >> YUZU_PAGEBITS;
180 std::size_t page_offset = addr & PAGE_MASK; 182 std::size_t page_offset = addr & YUZU_PAGEMASK;
181 183
182 while (remaining_size) { 184 while (remaining_size) {
183 const std::size_t copy_amount = 185 const std::size_t copy_amount =
184 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); 186 std::min(static_cast<std::size_t>(YUZU_PAGESIZE) - page_offset, remaining_size);
185 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 187 const auto current_vaddr =
188 static_cast<VAddr>((page_index << YUZU_PAGEBITS) + page_offset);
186 189
187 const auto [pointer, type] = page_table.pointers[page_index].PointerType(); 190 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
188 switch (type) { 191 switch (type) {
@@ -192,7 +195,7 @@ struct Memory::Impl {
192 } 195 }
193 case Common::PageType::Memory: { 196 case Common::PageType::Memory: {
194 DEBUG_ASSERT(pointer); 197 DEBUG_ASSERT(pointer);
195 u8* mem_ptr = pointer + page_offset + (page_index << PAGE_BITS); 198 u8* mem_ptr = pointer + page_offset + (page_index << YUZU_PAGEBITS);
196 on_memory(copy_amount, mem_ptr); 199 on_memory(copy_amount, mem_ptr);
197 break; 200 break;
198 } 201 }
@@ -339,10 +342,10 @@ struct Memory::Impl {
339 // Iterate over a contiguous CPU address space, marking/unmarking the region. 342 // Iterate over a contiguous CPU address space, marking/unmarking the region.
340 // The region is at a granularity of CPU pages. 343 // The region is at a granularity of CPU pages.
341 344
342 const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; 345 const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
343 for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { 346 for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
344 const Common::PageType page_type{ 347 const Common::PageType page_type{
345 current_page_table->pointers[vaddr >> PAGE_BITS].Type()}; 348 current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type()};
346 if (debug) { 349 if (debug) {
347 // Switch page type to debug if now debug 350 // Switch page type to debug if now debug
348 switch (page_type) { 351 switch (page_type) {
@@ -354,7 +357,7 @@ struct Memory::Impl {
354 // Page is already marked. 357 // Page is already marked.
355 break; 358 break;
356 case Common::PageType::Memory: 359 case Common::PageType::Memory:
357 current_page_table->pointers[vaddr >> PAGE_BITS].Store( 360 current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
358 nullptr, Common::PageType::DebugMemory); 361 nullptr, Common::PageType::DebugMemory);
359 break; 362 break;
360 default: 363 default:
@@ -371,9 +374,9 @@ struct Memory::Impl {
371 // Don't mess with already non-debug or rasterizer memory. 374 // Don't mess with already non-debug or rasterizer memory.
372 break; 375 break;
373 case Common::PageType::DebugMemory: { 376 case Common::PageType::DebugMemory: {
374 u8* const pointer{GetPointerFromDebugMemory(vaddr & ~PAGE_MASK)}; 377 u8* const pointer{GetPointerFromDebugMemory(vaddr & ~YUZU_PAGEMASK)};
375 current_page_table->pointers[vaddr >> PAGE_BITS].Store( 378 current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
376 pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory); 379 pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
377 break; 380 break;
378 } 381 }
379 default: 382 default:
@@ -398,10 +401,10 @@ struct Memory::Impl {
398 // granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size 401 // granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size
399 // is different). This assumes the specified GPU address region is contiguous as well. 402 // is different). This assumes the specified GPU address region is contiguous as well.
400 403
401 const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; 404 const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
402 for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { 405 for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
403 const Common::PageType page_type{ 406 const Common::PageType page_type{
404 current_page_table->pointers[vaddr >> PAGE_BITS].Type()}; 407 current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type()};
405 if (cached) { 408 if (cached) {
406 // Switch page type to cached if now cached 409 // Switch page type to cached if now cached
407 switch (page_type) { 410 switch (page_type) {
@@ -411,7 +414,7 @@ struct Memory::Impl {
411 break; 414 break;
412 case Common::PageType::DebugMemory: 415 case Common::PageType::DebugMemory:
413 case Common::PageType::Memory: 416 case Common::PageType::Memory:
414 current_page_table->pointers[vaddr >> PAGE_BITS].Store( 417 current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
415 nullptr, Common::PageType::RasterizerCachedMemory); 418 nullptr, Common::PageType::RasterizerCachedMemory);
416 break; 419 break;
417 case Common::PageType::RasterizerCachedMemory: 420 case Common::PageType::RasterizerCachedMemory:
@@ -434,16 +437,16 @@ struct Memory::Impl {
434 // that this area is already unmarked as cached. 437 // that this area is already unmarked as cached.
435 break; 438 break;
436 case Common::PageType::RasterizerCachedMemory: { 439 case Common::PageType::RasterizerCachedMemory: {
437 u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)}; 440 u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~YUZU_PAGEMASK)};
438 if (pointer == nullptr) { 441 if (pointer == nullptr) {
439 // It's possible that this function has been called while updating the 442 // It's possible that this function has been called while updating the
440 // pagetable after unmapping a VMA. In that case the underlying VMA will no 443 // pagetable after unmapping a VMA. In that case the underlying VMA will no
441 // longer exist, and we should just leave the pagetable entry blank. 444 // longer exist, and we should just leave the pagetable entry blank.
442 current_page_table->pointers[vaddr >> PAGE_BITS].Store( 445 current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
443 nullptr, Common::PageType::Unmapped); 446 nullptr, Common::PageType::Unmapped);
444 } else { 447 } else {
445 current_page_table->pointers[vaddr >> PAGE_BITS].Store( 448 current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
446 pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory); 449 pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
447 } 450 }
448 break; 451 break;
449 } 452 }
@@ -465,8 +468,8 @@ struct Memory::Impl {
465 */ 468 */
466 void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target, 469 void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target,
467 Common::PageType type) { 470 Common::PageType type) {
468 LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * PAGE_SIZE, 471 LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * YUZU_PAGESIZE,
469 (base + size) * PAGE_SIZE); 472 (base + size) * YUZU_PAGESIZE);
470 473
471 // During boot, current_page_table might not be set yet, in which case we need not flush 474 // During boot, current_page_table might not be set yet, in which case we need not flush
472 if (system.IsPoweredOn()) { 475 if (system.IsPoweredOn()) {
@@ -474,7 +477,7 @@ struct Memory::Impl {
474 for (u64 i = 0; i < size; i++) { 477 for (u64 i = 0; i < size; i++) {
475 const auto page = base + i; 478 const auto page = base + i;
476 if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) { 479 if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) {
477 gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE); 480 gpu.FlushAndInvalidateRegion(page << YUZU_PAGEBITS, YUZU_PAGESIZE);
478 } 481 }
479 } 482 }
480 } 483 }
@@ -485,7 +488,7 @@ struct Memory::Impl {
485 488
486 if (!target) { 489 if (!target) {
487 ASSERT_MSG(type != Common::PageType::Memory, 490 ASSERT_MSG(type != Common::PageType::Memory,
488 "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE); 491 "Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE);
489 492
490 while (base != end) { 493 while (base != end) {
491 page_table.pointers[base].Store(nullptr, type); 494 page_table.pointers[base].Store(nullptr, type);
@@ -496,14 +499,14 @@ struct Memory::Impl {
496 } else { 499 } else {
497 while (base != end) { 500 while (base != end) {
498 page_table.pointers[base].Store( 501 page_table.pointers[base].Store(
499 system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS), type); 502 system.DeviceMemory().GetPointer(target) - (base << YUZU_PAGEBITS), type);
500 page_table.backing_addr[base] = target - (base << PAGE_BITS); 503 page_table.backing_addr[base] = target - (base << YUZU_PAGEBITS);
501 504
502 ASSERT_MSG(page_table.pointers[base].Pointer(), 505 ASSERT_MSG(page_table.pointers[base].Pointer(),
503 "memory mapping base yield a nullptr within the table"); 506 "memory mapping base yield a nullptr within the table");
504 507
505 base += 1; 508 base += 1;
506 target += PAGE_SIZE; 509 target += YUZU_PAGESIZE;
507 } 510 }
508 } 511 }
509 } 512 }
@@ -518,7 +521,7 @@ struct Memory::Impl {
518 } 521 }
519 522
520 // Avoid adding any extra logic to this fast-path block 523 // Avoid adding any extra logic to this fast-path block
521 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); 524 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Raw();
522 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { 525 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
523 return &pointer[vaddr]; 526 return &pointer[vaddr];
524 } 527 }
@@ -657,7 +660,7 @@ void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
657bool Memory::IsValidVirtualAddress(const VAddr vaddr) const { 660bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
658 const Kernel::KProcess& process = *system.CurrentProcess(); 661 const Kernel::KProcess& process = *system.CurrentProcess();
659 const auto& page_table = process.PageTable().PageTableImpl(); 662 const auto& page_table = process.PageTable().PageTableImpl();
660 const size_t page = vaddr >> PAGE_BITS; 663 const size_t page = vaddr >> YUZU_PAGEBITS;
661 if (page >= page_table.pointers.size()) { 664 if (page >= page_table.pointers.size()) {
662 return false; 665 return false;
663 } 666 }
@@ -668,9 +671,9 @@ bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
668 671
669bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const { 672bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const {
670 VAddr end = base + size; 673 VAddr end = base + size;
671 VAddr page = Common::AlignDown(base, PAGE_SIZE); 674 VAddr page = Common::AlignDown(base, YUZU_PAGESIZE);
672 675
673 for (; page < end; page += PAGE_SIZE) { 676 for (; page < end; page += YUZU_PAGESIZE) {
674 if (!IsValidVirtualAddress(page)) { 677 if (!IsValidVirtualAddress(page)) {
675 return false; 678 return false;
676 } 679 }
diff --git a/src/core/memory.h b/src/core/memory.h
index 2a21fbcfd..a11ff8766 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -27,9 +27,9 @@ namespace Core::Memory {
27 * Page size used by the ARM architecture. This is the smallest granularity with which memory can 27 * Page size used by the ARM architecture. This is the smallest granularity with which memory can
28 * be mapped. 28 * be mapped.
29 */ 29 */
30constexpr std::size_t PAGE_BITS = 12; 30constexpr std::size_t YUZU_PAGEBITS = 12;
31constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS; 31constexpr u64 YUZU_PAGESIZE = 1ULL << YUZU_PAGEBITS;
32constexpr u64 PAGE_MASK = PAGE_SIZE - 1; 32constexpr u64 YUZU_PAGEMASK = YUZU_PAGESIZE - 1;
33 33
34/// Virtual user-space memory regions 34/// Virtual user-space memory regions
35enum : VAddr { 35enum : VAddr {
diff --git a/src/dedicated_room/CMakeLists.txt b/src/dedicated_room/CMakeLists.txt
new file mode 100644
index 000000000..b674b915b
--- /dev/null
+++ b/src/dedicated_room/CMakeLists.txt
@@ -0,0 +1,27 @@
1# SPDX-FileCopyrightText: 2017 Citra Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
5
6add_executable(yuzu-room
7 yuzu_room.cpp
8 yuzu_room.rc
9)
10
11create_target_directory_groups(yuzu-room)
12
13target_link_libraries(yuzu-room PRIVATE common core network)
14if (ENABLE_WEB_SERVICE)
15 target_compile_definitions(yuzu-room PRIVATE -DENABLE_WEB_SERVICE)
16 target_link_libraries(yuzu-room PRIVATE web_service)
17endif()
18
19target_link_libraries(yuzu-room PRIVATE mbedtls)
20if (MSVC)
21 target_link_libraries(yuzu-room PRIVATE getopt)
22endif()
23target_link_libraries(yuzu-room PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
24
25if(UNIX AND NOT APPLE)
26 install(TARGETS yuzu-room RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
27endif()
diff --git a/src/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp
new file mode 100644
index 000000000..482e772fb
--- /dev/null
+++ b/src/dedicated_room/yuzu_room.cpp
@@ -0,0 +1,375 @@
1// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5#include <fstream>
6#include <iostream>
7#include <memory>
8#include <regex>
9#include <string>
10#include <thread>
11
12#ifdef _WIN32
13// windows.h needs to be included before shellapi.h
14#include <windows.h>
15
16#include <shellapi.h>
17#endif
18
19#include <mbedtls/base64.h>
20#include "common/common_types.h"
21#include "common/detached_tasks.h"
22#include "common/fs/file.h"
23#include "common/fs/fs.h"
24#include "common/fs/path_util.h"
25#include "common/logging/backend.h"
26#include "common/logging/log.h"
27#include "common/scm_rev.h"
28#include "common/settings.h"
29#include "common/string_util.h"
30#include "core/announce_multiplayer_session.h"
31#include "core/core.h"
32#include "network/network.h"
33#include "network/room.h"
34#include "network/verify_user.h"
35
36#ifdef ENABLE_WEB_SERVICE
37#include "web_service/verify_user_jwt.h"
38#endif
39
40#undef _UNICODE
41#include <getopt.h>
42#ifndef _MSC_VER
43#include <unistd.h>
44#endif
45
46static void PrintHelp(const char* argv0) {
47 LOG_INFO(Network,
48 "Usage: {}"
49 " [options] <filename>\n"
50 "--room-name The name of the room\n"
51 "--room-description The room description\n"
52 "--port The port used for the room\n"
53 "--max_members The maximum number of players for this room\n"
54 "--password The password for the room\n"
55 "--preferred-game The preferred game for this room\n"
56 "--preferred-game-id The preferred game-id for this room\n"
57 "--username The username used for announce\n"
58 "--token The token used for announce\n"
59 "--web-api-url yuzu Web API url\n"
60 "--ban-list-file The file for storing the room ban list\n"
61 "--log-file The file for storing the room log\n"
62 "--enable-yuzu-mods Allow yuzu Community Moderators to moderate on your room\n"
63 "-h, --help Display this help and exit\n"
64 "-v, --version Output version information and exit\n",
65 argv0);
66}
67
68static void PrintVersion() {
69 LOG_INFO(Network, "yuzu dedicated room {} {} Libnetwork: {}", Common::g_scm_branch,
70 Common::g_scm_desc, Network::network_version);
71}
72
73/// The magic text at the beginning of a yuzu-room ban list file.
74static constexpr char BanListMagic[] = "YuzuRoom-BanList-1";
75
76static constexpr char token_delimiter{':'};
77
78static std::string UsernameFromDisplayToken(const std::string& display_token) {
79 std::size_t outlen;
80
81 std::array<unsigned char, 512> output{};
82 mbedtls_base64_decode(output.data(), output.size(), &outlen,
83 reinterpret_cast<const unsigned char*>(display_token.c_str()),
84 display_token.length());
85 std::string decoded_display_token(reinterpret_cast<char*>(&output), outlen);
86 return decoded_display_token.substr(0, decoded_display_token.find(token_delimiter));
87}
88
89static std::string TokenFromDisplayToken(const std::string& display_token) {
90 std::size_t outlen;
91
92 std::array<unsigned char, 512> output{};
93 mbedtls_base64_decode(output.data(), output.size(), &outlen,
94 reinterpret_cast<const unsigned char*>(display_token.c_str()),
95 display_token.length());
96 std::string decoded_display_token(reinterpret_cast<char*>(&output), outlen);
97 return decoded_display_token.substr(decoded_display_token.find(token_delimiter) + 1);
98}
99
100static Network::Room::BanList LoadBanList(const std::string& path) {
101 std::ifstream file;
102 Common::FS::OpenFileStream(file, path, std::ios_base::in);
103 if (!file || file.eof()) {
104 LOG_ERROR(Network, "Could not open ban list!");
105 return {};
106 }
107 std::string magic;
108 std::getline(file, magic);
109 if (magic != BanListMagic) {
110 LOG_ERROR(Network, "Ban list is not valid!");
111 return {};
112 }
113
114 // false = username ban list, true = ip ban list
115 bool ban_list_type = false;
116 Network::Room::UsernameBanList username_ban_list;
117 Network::Room::IPBanList ip_ban_list;
118 while (!file.eof()) {
119 std::string line;
120 std::getline(file, line);
121 line.erase(std::remove(line.begin(), line.end(), '\0'), line.end());
122 line = Common::StripSpaces(line);
123 if (line.empty()) {
124 // An empty line marks start of the IP ban list
125 ban_list_type = true;
126 continue;
127 }
128 if (ban_list_type) {
129 ip_ban_list.emplace_back(line);
130 } else {
131 username_ban_list.emplace_back(line);
132 }
133 }
134
135 return {username_ban_list, ip_ban_list};
136}
137
138static void SaveBanList(const Network::Room::BanList& ban_list, const std::string& path) {
139 std::ofstream file;
140 Common::FS::OpenFileStream(file, path, std::ios_base::out);
141 if (!file) {
142 LOG_ERROR(Network, "Could not save ban list!");
143 return;
144 }
145
146 file << BanListMagic << "\n";
147
148 // Username ban list
149 for (const auto& username : ban_list.first) {
150 file << username << "\n";
151 }
152 file << "\n";
153
154 // IP ban list
155 for (const auto& ip : ban_list.second) {
156 file << ip << "\n";
157 }
158}
159
160static void InitializeLogging(const std::string& log_file) {
161 Common::Log::Initialize();
162 Common::Log::SetColorConsoleBackendEnabled(true);
163 Common::Log::Start();
164}
165
166/// Application entry point
167int main(int argc, char** argv) {
168 Common::DetachedTasks detached_tasks;
169 int option_index = 0;
170 char* endarg;
171
172 std::string room_name;
173 std::string room_description;
174 std::string password;
175 std::string preferred_game;
176 std::string username;
177 std::string token;
178 std::string web_api_url;
179 std::string ban_list_file;
180 std::string log_file = "yuzu-room.log";
181 u64 preferred_game_id = 0;
182 u32 port = Network::DefaultRoomPort;
183 u32 max_members = 16;
184 bool enable_yuzu_mods = false;
185
186 static struct option long_options[] = {
187 {"room-name", required_argument, 0, 'n'},
188 {"room-description", required_argument, 0, 'd'},
189 {"port", required_argument, 0, 'p'},
190 {"max_members", required_argument, 0, 'm'},
191 {"password", required_argument, 0, 'w'},
192 {"preferred-game", required_argument, 0, 'g'},
193 {"preferred-game-id", required_argument, 0, 'i'},
194 {"username", optional_argument, 0, 'u'},
195 {"token", required_argument, 0, 't'},
196 {"web-api-url", required_argument, 0, 'a'},
197 {"ban-list-file", required_argument, 0, 'b'},
198 {"log-file", required_argument, 0, 'l'},
199 {"enable-yuzu-mods", no_argument, 0, 'e'},
200 {"help", no_argument, 0, 'h'},
201 {"version", no_argument, 0, 'v'},
202 {0, 0, 0, 0},
203 };
204
205 InitializeLogging(log_file);
206
207 while (optind < argc) {
208 int arg = getopt_long(argc, argv, "n:d:p:m:w:g:u:t:a:i:l:hv", long_options, &option_index);
209 if (arg != -1) {
210 switch (static_cast<char>(arg)) {
211 case 'n':
212 room_name.assign(optarg);
213 break;
214 case 'd':
215 room_description.assign(optarg);
216 break;
217 case 'p':
218 port = strtoul(optarg, &endarg, 0);
219 break;
220 case 'm':
221 max_members = strtoul(optarg, &endarg, 0);
222 break;
223 case 'w':
224 password.assign(optarg);
225 break;
226 case 'g':
227 preferred_game.assign(optarg);
228 break;
229 case 'i':
230 preferred_game_id = strtoull(optarg, &endarg, 16);
231 break;
232 case 'u':
233 username.assign(optarg);
234 break;
235 case 't':
236 token.assign(optarg);
237 break;
238 case 'a':
239 web_api_url.assign(optarg);
240 break;
241 case 'b':
242 ban_list_file.assign(optarg);
243 break;
244 case 'l':
245 log_file.assign(optarg);
246 break;
247 case 'e':
248 enable_yuzu_mods = true;
249 break;
250 case 'h':
251 PrintHelp(argv[0]);
252 return 0;
253 case 'v':
254 PrintVersion();
255 return 0;
256 }
257 }
258 }
259
260 if (room_name.empty()) {
261 LOG_ERROR(Network, "Room name is empty!");
262 PrintHelp(argv[0]);
263 return -1;
264 }
265 if (preferred_game.empty()) {
266 LOG_ERROR(Network, "Preferred game is empty!");
267 PrintHelp(argv[0]);
268 return -1;
269 }
270 if (preferred_game_id == 0) {
271 LOG_ERROR(Network,
272 "preferred-game-id not set!\nThis should get set to allow users to find your "
273 "room.\nSet with --preferred-game-id id");
274 }
275 if (max_members > Network::MaxConcurrentConnections || max_members < 2) {
276 LOG_ERROR(Network, "max_members needs to be in the range 2 - {}!",
277 Network::MaxConcurrentConnections);
278 PrintHelp(argv[0]);
279 return -1;
280 }
281 if (port > UINT16_MAX) {
282 LOG_ERROR(Network, "Port needs to be in the range 0 - 65535!");
283 PrintHelp(argv[0]);
284 return -1;
285 }
286 if (ban_list_file.empty()) {
287 LOG_ERROR(Network, "Ban list file not set!\nThis should get set to load and save room ban "
288 "list.\nSet with --ban-list-file <file>");
289 }
290 bool announce = true;
291 if (token.empty() && announce) {
292 announce = false;
293 LOG_INFO(Network, "Token is empty: Hosting a private room");
294 }
295 if (web_api_url.empty() && announce) {
296 announce = false;
297 LOG_INFO(Network, "Endpoint url is empty: Hosting a private room");
298 }
299 if (announce) {
300 if (username.empty()) {
301 LOG_INFO(Network, "Hosting a public room");
302 Settings::values.web_api_url = web_api_url;
303 Settings::values.yuzu_username = UsernameFromDisplayToken(token);
304 username = Settings::values.yuzu_username.GetValue();
305 Settings::values.yuzu_token = TokenFromDisplayToken(token);
306 } else {
307 LOG_INFO(Network, "Hosting a public room");
308 Settings::values.web_api_url = web_api_url;
309 Settings::values.yuzu_username = username;
310 Settings::values.yuzu_token = token;
311 }
312 }
313 if (!announce && enable_yuzu_mods) {
314 enable_yuzu_mods = false;
315 LOG_INFO(Network, "Can not enable yuzu Moderators for private rooms");
316 }
317
318 // Load the ban list
319 Network::Room::BanList ban_list;
320 if (!ban_list_file.empty()) {
321 ban_list = LoadBanList(ban_list_file);
322 }
323
324 std::unique_ptr<Network::VerifyUser::Backend> verify_backend;
325 if (announce) {
326#ifdef ENABLE_WEB_SERVICE
327 verify_backend =
328 std::make_unique<WebService::VerifyUserJWT>(Settings::values.web_api_url.GetValue());
329#else
330 LOG_INFO(Network,
331 "yuzu Web Services is not available with this build: validation is disabled.");
332 verify_backend = std::make_unique<Network::VerifyUser::NullBackend>();
333#endif
334 } else {
335 verify_backend = std::make_unique<Network::VerifyUser::NullBackend>();
336 }
337
338 Network::RoomNetwork network{};
339 network.Init();
340 if (auto room = network.GetRoom().lock()) {
341 AnnounceMultiplayerRoom::GameInfo preferred_game_info{.name = preferred_game,
342 .id = preferred_game_id};
343 if (!room->Create(room_name, room_description, "", port, password, max_members, username,
344 preferred_game_info, std::move(verify_backend), ban_list,
345 enable_yuzu_mods)) {
346 LOG_INFO(Network, "Failed to create room: ");
347 return -1;
348 }
349 LOG_INFO(Network, "Room is open. Close with Q+Enter...");
350 auto announce_session = std::make_unique<Core::AnnounceMultiplayerSession>(network);
351 if (announce) {
352 announce_session->Start();
353 }
354 while (room->GetState() == Network::Room::State::Open) {
355 std::string in;
356 std::cin >> in;
357 if (in.size() > 0) {
358 break;
359 }
360 std::this_thread::sleep_for(std::chrono::milliseconds(100));
361 }
362 if (announce) {
363 announce_session->Stop();
364 }
365 announce_session.reset();
366 // Save the ban list
367 if (!ban_list_file.empty()) {
368 SaveBanList(room->GetBanList(), ban_list_file);
369 }
370 room->Destroy();
371 }
372 network.Shutdown();
373 detached_tasks.WaitForAllTasks();
374 return 0;
375}
diff --git a/src/dedicated_room/yuzu_room.rc b/src/dedicated_room/yuzu_room.rc
new file mode 100644
index 000000000..a08957684
--- /dev/null
+++ b/src/dedicated_room/yuzu_room.rc
@@ -0,0 +1,20 @@
1// SPDX-FileCopyrightText: 2017 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "winresrc.h"
5/////////////////////////////////////////////////////////////////////////////
6//
7// Icon
8//
9
10// Icon with lowest ID value placed first to ensure application icon
11// remains consistent on all systems.
12YUZU_ICON ICON "../../dist/yuzu.ico"
13
14
15/////////////////////////////////////////////////////////////////////////////
16//
17// RT_MANIFEST
18//
19
200 RT_MANIFEST "../../dist/yuzu.manifest"
diff --git a/src/network/room.cpp b/src/network/room.cpp
index 3fc3a0383..b06797bf1 100644
--- a/src/network/room.cpp
+++ b/src/network/room.cpp
@@ -20,9 +20,7 @@ namespace Network {
20 20
21class Room::RoomImpl { 21class Room::RoomImpl {
22public: 22public:
23 // This MAC address is used to generate a 'Nintendo' like Mac address. 23 std::mt19937 random_gen; ///< Random number generator. Used for GenerateFakeIPAddress
24 const MacAddress NintendoOUI;
25 std::mt19937 random_gen; ///< Random number generator. Used for GenerateMacAddress
26 24
27 ENetHost* server = nullptr; ///< Network interface. 25 ENetHost* server = nullptr; ///< Network interface.
28 26
@@ -35,10 +33,9 @@ public:
35 std::string password; ///< The password required to connect to this room. 33 std::string password; ///< The password required to connect to this room.
36 34
37 struct Member { 35 struct Member {
38 std::string nickname; ///< The nickname of the member. 36 std::string nickname; ///< The nickname of the member.
39 std::string console_id_hash; ///< A hash of the console ID of the member. 37 GameInfo game_info; ///< The current game of the member
40 GameInfo game_info; ///< The current game of the member 38 IPv4Address fake_ip; ///< The assigned fake ip address of the member.
41 MacAddress mac_address; ///< The assigned mac address of the member.
42 /// Data of the user, often including authenticated forum username. 39 /// Data of the user, often including authenticated forum username.
43 VerifyUser::UserData user_data; 40 VerifyUser::UserData user_data;
44 ENetPeer* peer; ///< The remote peer. 41 ENetPeer* peer; ///< The remote peer.
@@ -51,8 +48,7 @@ public:
51 IPBanList ip_ban_list; ///< List of banned IP addresses 48 IPBanList ip_ban_list; ///< List of banned IP addresses
52 mutable std::mutex ban_list_mutex; ///< Mutex for the ban lists 49 mutable std::mutex ban_list_mutex; ///< Mutex for the ban lists
53 50
54 RoomImpl() 51 RoomImpl() : random_gen(std::random_device()()) {}
55 : NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00}, random_gen(std::random_device()()) {}
56 52
57 /// Thread that receives and dispatches network packets 53 /// Thread that receives and dispatches network packets
58 std::unique_ptr<std::thread> room_thread; 54 std::unique_ptr<std::thread> room_thread;
@@ -101,16 +97,10 @@ public:
101 bool IsValidNickname(const std::string& nickname) const; 97 bool IsValidNickname(const std::string& nickname) const;
102 98
103 /** 99 /**
104 * Returns whether the MAC address is valid, ie. isn't already taken by someone else in the 100 * Returns whether the fake ip address is valid, ie. isn't already taken by someone else in the
105 * room. 101 * room.
106 */ 102 */
107 bool IsValidMacAddress(const MacAddress& address) const; 103 bool IsValidFakeIPAddress(const IPv4Address& address) const;
108
109 /**
110 * Returns whether the console ID (hash) is valid, ie. isn't already taken by someone else in
111 * the room.
112 */
113 bool IsValidConsoleId(const std::string& console_id_hash) const;
114 104
115 /** 105 /**
116 * Returns whether a user has mod permissions. 106 * Returns whether a user has mod permissions.
@@ -128,15 +118,9 @@ public:
128 void SendNameCollision(ENetPeer* client); 118 void SendNameCollision(ENetPeer* client);
129 119
130 /** 120 /**
131 * Sends a ID_ROOM_MAC_COLLISION message telling the client that the MAC is invalid. 121 * Sends a ID_ROOM_IP_COLLISION message telling the client that the IP is invalid.
132 */ 122 */
133 void SendMacCollision(ENetPeer* client); 123 void SendIPCollision(ENetPeer* client);
134
135 /**
136 * Sends a IdConsoleIdCollison message telling the client that another member with the same
137 * console ID exists.
138 */
139 void SendConsoleIdCollision(ENetPeer* client);
140 124
141 /** 125 /**
142 * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the version is invalid. 126 * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the version is invalid.
@@ -152,13 +136,13 @@ public:
152 * Notifies the member that its connection attempt was successful, 136 * Notifies the member that its connection attempt was successful,
153 * and it is now part of the room. 137 * and it is now part of the room.
154 */ 138 */
155 void SendJoinSuccess(ENetPeer* client, MacAddress mac_address); 139 void SendJoinSuccess(ENetPeer* client, IPv4Address fake_ip);
156 140
157 /** 141 /**
158 * Notifies the member that its connection attempt was successful, 142 * Notifies the member that its connection attempt was successful,
159 * and it is now part of the room, and it has been granted mod permissions. 143 * and it is now part of the room, and it has been granted mod permissions.
160 */ 144 */
161 void SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address); 145 void SendJoinSuccessAsMod(ENetPeer* client, IPv4Address fake_ip);
162 146
163 /** 147 /**
164 * Sends a IdHostKicked message telling the client that they have been kicked. 148 * Sends a IdHostKicked message telling the client that they have been kicked.
@@ -210,7 +194,7 @@ public:
210 * <u32> num_members: the number of currently joined clients 194 * <u32> num_members: the number of currently joined clients
211 * This is followed by the following three values for each member: 195 * This is followed by the following three values for each member:
212 * <String> nickname of that member 196 * <String> nickname of that member
213 * <MacAddress> mac_address of that member 197 * <IPv4Address> fake_ip of that member
214 * <String> game_name of that member 198 * <String> game_name of that member
215 */ 199 */
216 void BroadcastRoomInformation(); 200 void BroadcastRoomInformation();
@@ -219,13 +203,13 @@ public:
219 * Generates a free MAC address to assign to a new client. 203 * Generates a free MAC address to assign to a new client.
220 * The first 3 bytes are the NintendoOUI 0x00, 0x1F, 0x32 204 * The first 3 bytes are the NintendoOUI 0x00, 0x1F, 0x32
221 */ 205 */
222 MacAddress GenerateMacAddress(); 206 IPv4Address GenerateFakeIPAddress();
223 207
224 /** 208 /**
225 * Broadcasts this packet to all members except the sender. 209 * Broadcasts this packet to all members except the sender.
226 * @param event The ENet event containing the data 210 * @param event The ENet event containing the data
227 */ 211 */
228 void HandleWifiPacket(const ENetEvent* event); 212 void HandleProxyPacket(const ENetEvent* event);
229 213
230 /** 214 /**
231 * Extracts a chat entry from a received ENet packet and adds it to the chat queue. 215 * Extracts a chat entry from a received ENet packet and adds it to the chat queue.
@@ -250,7 +234,7 @@ public:
250void Room::RoomImpl::ServerLoop() { 234void Room::RoomImpl::ServerLoop() {
251 while (state != State::Closed) { 235 while (state != State::Closed) {
252 ENetEvent event; 236 ENetEvent event;
253 if (enet_host_service(server, &event, 16) > 0) { 237 if (enet_host_service(server, &event, 50) > 0) {
254 switch (event.type) { 238 switch (event.type) {
255 case ENET_EVENT_TYPE_RECEIVE: 239 case ENET_EVENT_TYPE_RECEIVE:
256 switch (event.packet->data[0]) { 240 switch (event.packet->data[0]) {
@@ -260,8 +244,8 @@ void Room::RoomImpl::ServerLoop() {
260 case IdSetGameInfo: 244 case IdSetGameInfo:
261 HandleGameNamePacket(&event); 245 HandleGameNamePacket(&event);
262 break; 246 break;
263 case IdWifiPacket: 247 case IdProxyPacket:
264 HandleWifiPacket(&event); 248 HandleProxyPacket(&event);
265 break; 249 break;
266 case IdChatMessage: 250 case IdChatMessage:
267 HandleChatPacket(&event); 251 HandleChatPacket(&event);
@@ -313,11 +297,8 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
313 std::string nickname; 297 std::string nickname;
314 packet.Read(nickname); 298 packet.Read(nickname);
315 299
316 std::string console_id_hash; 300 IPv4Address preferred_fake_ip;
317 packet.Read(console_id_hash); 301 packet.Read(preferred_fake_ip);
318
319 MacAddress preferred_mac;
320 packet.Read(preferred_mac);
321 302
322 u32 client_version; 303 u32 client_version;
323 packet.Read(client_version); 304 packet.Read(client_version);
@@ -338,20 +319,15 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
338 return; 319 return;
339 } 320 }
340 321
341 if (preferred_mac != NoPreferredMac) { 322 if (preferred_fake_ip != NoPreferredIP) {
342 // Verify if the preferred mac is available 323 // Verify if the preferred fake ip is available
343 if (!IsValidMacAddress(preferred_mac)) { 324 if (!IsValidFakeIPAddress(preferred_fake_ip)) {
344 SendMacCollision(event->peer); 325 SendIPCollision(event->peer);
345 return; 326 return;
346 } 327 }
347 } else { 328 } else {
348 // Assign a MAC address of this client automatically 329 // Assign a fake ip address of this client automatically
349 preferred_mac = GenerateMacAddress(); 330 preferred_fake_ip = GenerateFakeIPAddress();
350 }
351
352 if (!IsValidConsoleId(console_id_hash)) {
353 SendConsoleIdCollision(event->peer);
354 return;
355 } 331 }
356 332
357 if (client_version != network_version) { 333 if (client_version != network_version) {
@@ -361,8 +337,7 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
361 337
362 // At this point the client is ready to be added to the room. 338 // At this point the client is ready to be added to the room.
363 Member member{}; 339 Member member{};
364 member.mac_address = preferred_mac; 340 member.fake_ip = preferred_fake_ip;
365 member.console_id_hash = console_id_hash;
366 member.nickname = nickname; 341 member.nickname = nickname;
367 member.peer = event->peer; 342 member.peer = event->peer;
368 343
@@ -408,9 +383,9 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
408 // Notify everyone that the room information has changed. 383 // Notify everyone that the room information has changed.
409 BroadcastRoomInformation(); 384 BroadcastRoomInformation();
410 if (HasModPermission(event->peer)) { 385 if (HasModPermission(event->peer)) {
411 SendJoinSuccessAsMod(event->peer, preferred_mac); 386 SendJoinSuccessAsMod(event->peer, preferred_fake_ip);
412 } else { 387 } else {
413 SendJoinSuccess(event->peer, preferred_mac); 388 SendJoinSuccess(event->peer, preferred_fake_ip);
414 } 389 }
415} 390}
416 391
@@ -575,19 +550,11 @@ bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const {
575 [&nickname](const auto& member) { return member.nickname != nickname; }); 550 [&nickname](const auto& member) { return member.nickname != nickname; });
576} 551}
577 552
578bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const { 553bool Room::RoomImpl::IsValidFakeIPAddress(const IPv4Address& address) const {
579 // A MAC address is valid if it is not already taken by anybody else in the room. 554 // An IP address is valid if it is not already taken by anybody else in the room.
580 std::lock_guard lock(member_mutex); 555 std::lock_guard lock(member_mutex);
581 return std::all_of(members.begin(), members.end(), 556 return std::all_of(members.begin(), members.end(),
582 [&address](const auto& member) { return member.mac_address != address; }); 557 [&address](const auto& member) { return member.fake_ip != address; });
583}
584
585bool Room::RoomImpl::IsValidConsoleId(const std::string& console_id_hash) const {
586 // A Console ID is valid if it is not already taken by anybody else in the room.
587 std::lock_guard lock(member_mutex);
588 return std::all_of(members.begin(), members.end(), [&console_id_hash](const auto& member) {
589 return member.console_id_hash != console_id_hash;
590 });
591} 558}
592 559
593bool Room::RoomImpl::HasModPermission(const ENetPeer* client) const { 560bool Room::RoomImpl::HasModPermission(const ENetPeer* client) const {
@@ -621,19 +588,9 @@ void Room::RoomImpl::SendNameCollision(ENetPeer* client) {
621 enet_host_flush(server); 588 enet_host_flush(server);
622} 589}
623 590
624void Room::RoomImpl::SendMacCollision(ENetPeer* client) { 591void Room::RoomImpl::SendIPCollision(ENetPeer* client) {
625 Packet packet; 592 Packet packet;
626 packet.Write(static_cast<u8>(IdMacCollision)); 593 packet.Write(static_cast<u8>(IdIpCollision));
627
628 ENetPacket* enet_packet =
629 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
630 enet_peer_send(client, 0, enet_packet);
631 enet_host_flush(server);
632}
633
634void Room::RoomImpl::SendConsoleIdCollision(ENetPeer* client) {
635 Packet packet;
636 packet.Write(static_cast<u8>(IdConsoleIdCollision));
637 594
638 ENetPacket* enet_packet = 595 ENetPacket* enet_packet =
639 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); 596 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
@@ -672,20 +629,20 @@ void Room::RoomImpl::SendVersionMismatch(ENetPeer* client) {
672 enet_host_flush(server); 629 enet_host_flush(server);
673} 630}
674 631
675void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, MacAddress mac_address) { 632void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, IPv4Address fake_ip) {
676 Packet packet; 633 Packet packet;
677 packet.Write(static_cast<u8>(IdJoinSuccess)); 634 packet.Write(static_cast<u8>(IdJoinSuccess));
678 packet.Write(mac_address); 635 packet.Write(fake_ip);
679 ENetPacket* enet_packet = 636 ENetPacket* enet_packet =
680 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); 637 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
681 enet_peer_send(client, 0, enet_packet); 638 enet_peer_send(client, 0, enet_packet);
682 enet_host_flush(server); 639 enet_host_flush(server);
683} 640}
684 641
685void Room::RoomImpl::SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address) { 642void Room::RoomImpl::SendJoinSuccessAsMod(ENetPeer* client, IPv4Address fake_ip) {
686 Packet packet; 643 Packet packet;
687 packet.Write(static_cast<u8>(IdJoinSuccessAsMod)); 644 packet.Write(static_cast<u8>(IdJoinSuccessAsMod));
688 packet.Write(mac_address); 645 packet.Write(fake_ip);
689 ENetPacket* enet_packet = 646 ENetPacket* enet_packet =
690 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); 647 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
691 enet_peer_send(client, 0, enet_packet); 648 enet_peer_send(client, 0, enet_packet);
@@ -818,7 +775,7 @@ void Room::RoomImpl::BroadcastRoomInformation() {
818 std::lock_guard lock(member_mutex); 775 std::lock_guard lock(member_mutex);
819 for (const auto& member : members) { 776 for (const auto& member : members) {
820 packet.Write(member.nickname); 777 packet.Write(member.nickname);
821 packet.Write(member.mac_address); 778 packet.Write(member.fake_ip);
822 packet.Write(member.game_info.name); 779 packet.Write(member.game_info.name);
823 packet.Write(member.game_info.id); 780 packet.Write(member.game_info.id);
824 packet.Write(member.user_data.username); 781 packet.Write(member.user_data.username);
@@ -833,34 +790,43 @@ void Room::RoomImpl::BroadcastRoomInformation() {
833 enet_host_flush(server); 790 enet_host_flush(server);
834} 791}
835 792
836MacAddress Room::RoomImpl::GenerateMacAddress() { 793IPv4Address Room::RoomImpl::GenerateFakeIPAddress() {
837 MacAddress result_mac = 794 IPv4Address result_ip{192, 168, 0, 0};
838 NintendoOUI; // The first three bytes of each MAC address will be the NintendoOUI 795 std::uniform_int_distribution<> dis(0x01, 0xFE); // Random byte between 1 and 0xFE
839 std::uniform_int_distribution<> dis(0x00, 0xFF); // Random byte between 0 and 0xFF
840 do { 796 do {
841 for (std::size_t i = 3; i < result_mac.size(); ++i) { 797 for (std::size_t i = 2; i < result_ip.size(); ++i) {
842 result_mac[i] = dis(random_gen); 798 result_ip[i] = dis(random_gen);
843 } 799 }
844 } while (!IsValidMacAddress(result_mac)); 800 } while (!IsValidFakeIPAddress(result_ip));
845 return result_mac; 801
802 return result_ip;
846} 803}
847 804
848void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) { 805void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) {
849 Packet in_packet; 806 Packet in_packet;
850 in_packet.Append(event->packet->data, event->packet->dataLength); 807 in_packet.Append(event->packet->data, event->packet->dataLength);
851 in_packet.IgnoreBytes(sizeof(u8)); // Message type 808 in_packet.IgnoreBytes(sizeof(u8)); // Message type
852 in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Type 809
853 in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Channel 810 in_packet.IgnoreBytes(sizeof(u8)); // Domain
854 in_packet.IgnoreBytes(sizeof(MacAddress)); // WifiPacket Transmitter Address 811 in_packet.IgnoreBytes(sizeof(IPv4Address)); // IP
855 MacAddress destination_address; 812 in_packet.IgnoreBytes(sizeof(u16)); // Port
856 in_packet.Read(destination_address); 813
814 in_packet.IgnoreBytes(sizeof(u8)); // Domain
815 IPv4Address remote_ip;
816 in_packet.Read(remote_ip); // IP
817 in_packet.IgnoreBytes(sizeof(u16)); // Port
818
819 in_packet.IgnoreBytes(sizeof(u8)); // Protocol
820 bool broadcast;
821 in_packet.Read(broadcast); // Broadcast
857 822
858 Packet out_packet; 823 Packet out_packet;
859 out_packet.Append(event->packet->data, event->packet->dataLength); 824 out_packet.Append(event->packet->data, event->packet->dataLength);
860 ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(), 825 ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
861 ENET_PACKET_FLAG_RELIABLE); 826 ENET_PACKET_FLAG_RELIABLE);
862 827
863 if (destination_address == BroadcastMac) { // Send the data to everyone except the sender 828 const auto& destination_address = remote_ip;
829 if (broadcast) { // Send the data to everyone except the sender
864 std::lock_guard lock(member_mutex); 830 std::lock_guard lock(member_mutex);
865 bool sent_packet = false; 831 bool sent_packet = false;
866 for (const auto& member : members) { 832 for (const auto& member : members) {
@@ -877,16 +843,16 @@ void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) {
877 std::lock_guard lock(member_mutex); 843 std::lock_guard lock(member_mutex);
878 auto member = std::find_if(members.begin(), members.end(), 844 auto member = std::find_if(members.begin(), members.end(),
879 [destination_address](const Member& member_entry) -> bool { 845 [destination_address](const Member& member_entry) -> bool {
880 return member_entry.mac_address == destination_address; 846 return member_entry.fake_ip == destination_address;
881 }); 847 });
882 if (member != members.end()) { 848 if (member != members.end()) {
883 enet_peer_send(member->peer, 0, enet_packet); 849 enet_peer_send(member->peer, 0, enet_packet);
884 } else { 850 } else {
885 LOG_ERROR(Network, 851 LOG_ERROR(Network,
886 "Attempting to send to unknown MAC address: " 852 "Attempting to send to unknown IP address: "
887 "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", 853 "{}.{}.{}.{}",
888 destination_address[0], destination_address[1], destination_address[2], 854 destination_address[0], destination_address[1], destination_address[2],
889 destination_address[3], destination_address[4], destination_address[5]); 855 destination_address[3]);
890 enet_packet_destroy(enet_packet); 856 enet_packet_destroy(enet_packet);
891 } 857 }
892 } 858 }
@@ -1073,7 +1039,7 @@ std::vector<Member> Room::GetRoomMemberList() const {
1073 member.username = member_impl.user_data.username; 1039 member.username = member_impl.user_data.username;
1074 member.display_name = member_impl.user_data.display_name; 1040 member.display_name = member_impl.user_data.display_name;
1075 member.avatar_url = member_impl.user_data.avatar_url; 1041 member.avatar_url = member_impl.user_data.avatar_url;
1076 member.mac_address = member_impl.mac_address; 1042 member.fake_ip = member_impl.fake_ip;
1077 member.game = member_impl.game_info; 1043 member.game = member_impl.game_info;
1078 member_list.push_back(member); 1044 member_list.push_back(member);
1079 } 1045 }
diff --git a/src/network/room.h b/src/network/room.h
index 6f7e3b5b5..c2a4b1a70 100644
--- a/src/network/room.h
+++ b/src/network/room.h
@@ -9,12 +9,12 @@
9#include <vector> 9#include <vector>
10#include "common/announce_multiplayer_room.h" 10#include "common/announce_multiplayer_room.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/socket_types.h"
12#include "network/verify_user.h" 13#include "network/verify_user.h"
13 14
14namespace Network { 15namespace Network {
15 16
16using AnnounceMultiplayerRoom::GameInfo; 17using AnnounceMultiplayerRoom::GameInfo;
17using AnnounceMultiplayerRoom::MacAddress;
18using AnnounceMultiplayerRoom::Member; 18using AnnounceMultiplayerRoom::Member;
19using AnnounceMultiplayerRoom::RoomInformation; 19using AnnounceMultiplayerRoom::RoomInformation;
20 20
@@ -29,12 +29,9 @@ static constexpr u32 MaxConcurrentConnections = 254;
29 29
30constexpr std::size_t NumChannels = 1; // Number of channels used for the connection 30constexpr std::size_t NumChannels = 1; // Number of channels used for the connection
31 31
32/// A special MAC address that tells the room we're joining to assign us a MAC address 32/// A special IP address that tells the room we're joining to assign us a IP address
33/// automatically. 33/// automatically.
34constexpr MacAddress NoPreferredMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 34constexpr IPv4Address NoPreferredIP = {0xFF, 0xFF, 0xFF, 0xFF};
35
36// 802.11 broadcast MAC address
37constexpr MacAddress BroadcastMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
38 35
39// The different types of messages that can be sent. The first byte of each packet defines the type 36// The different types of messages that can be sent. The first byte of each packet defines the type
40enum RoomMessageTypes : u8 { 37enum RoomMessageTypes : u8 {
@@ -42,15 +39,14 @@ enum RoomMessageTypes : u8 {
42 IdJoinSuccess, 39 IdJoinSuccess,
43 IdRoomInformation, 40 IdRoomInformation,
44 IdSetGameInfo, 41 IdSetGameInfo,
45 IdWifiPacket, 42 IdProxyPacket,
46 IdChatMessage, 43 IdChatMessage,
47 IdNameCollision, 44 IdNameCollision,
48 IdMacCollision, 45 IdIpCollision,
49 IdVersionMismatch, 46 IdVersionMismatch,
50 IdWrongPassword, 47 IdWrongPassword,
51 IdCloseRoom, 48 IdCloseRoom,
52 IdRoomIsFull, 49 IdRoomIsFull,
53 IdConsoleIdCollision,
54 IdStatusMessage, 50 IdStatusMessage,
55 IdHostKicked, 51 IdHostKicked,
56 IdHostBanned, 52 IdHostBanned,
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp
index e4f823e98..9f08bf611 100644
--- a/src/network/room_member.cpp
+++ b/src/network/room_member.cpp
@@ -7,6 +7,7 @@
7#include <set> 7#include <set>
8#include <thread> 8#include <thread>
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/socket_types.h"
10#include "enet/enet.h" 11#include "enet/enet.h"
11#include "network/packet.h" 12#include "network/packet.h"
12#include "network/room_member.h" 13#include "network/room_member.h"
@@ -38,7 +39,7 @@ public:
38 std::string username; ///< The username of this member. 39 std::string username; ///< The username of this member.
39 mutable std::mutex username_mutex; ///< Mutex for locking username. 40 mutable std::mutex username_mutex; ///< Mutex for locking username.
40 41
41 MacAddress mac_address; ///< The mac_address of this member. 42 IPv4Address fake_ip; ///< The fake ip of this member.
42 43
43 std::mutex network_mutex; ///< Mutex that controls access to the `client` variable. 44 std::mutex network_mutex; ///< Mutex that controls access to the `client` variable.
44 /// Thread that receives and dispatches network packets 45 /// Thread that receives and dispatches network packets
@@ -56,7 +57,7 @@ public:
56 CallbackSet<T>& Get(); 57 CallbackSet<T>& Get();
57 58
58 private: 59 private:
59 CallbackSet<WifiPacket> callback_set_wifi_packet; 60 CallbackSet<ProxyPacket> callback_set_proxy_packet;
60 CallbackSet<ChatEntry> callback_set_chat_messages; 61 CallbackSet<ChatEntry> callback_set_chat_messages;
61 CallbackSet<StatusMessageEntry> callback_set_status_messages; 62 CallbackSet<StatusMessageEntry> callback_set_status_messages;
62 CallbackSet<RoomInformation> callback_set_room_information; 63 CallbackSet<RoomInformation> callback_set_room_information;
@@ -78,15 +79,15 @@ public:
78 79
79 /** 80 /**
80 * Sends a request to the server, asking for permission to join a room with the specified 81 * Sends a request to the server, asking for permission to join a room with the specified
81 * nickname and preferred mac. 82 * nickname and preferred fake ip.
82 * @params nickname The desired nickname. 83 * @params nickname The desired nickname.
83 * @params console_id_hash A hash of the Console ID. 84 * @params preferred_fake_ip The preferred IP address to use in the room, the NoPreferredIP
84 * @params preferred_mac The preferred MAC address to use in the room, the NoPreferredMac tells 85 * tells
85 * @params password The password for the room 86 * @params password The password for the room
86 * the server to assign one for us. 87 * the server to assign one for us.
87 */ 88 */
88 void SendJoinRequest(const std::string& nickname_, const std::string& console_id_hash, 89 void SendJoinRequest(const std::string& nickname_,
89 const MacAddress& preferred_mac = NoPreferredMac, 90 const IPv4Address& preferred_fake_ip = NoPreferredIP,
90 const std::string& password = "", const std::string& token = ""); 91 const std::string& password = "", const std::string& token = "");
91 92
92 /** 93 /**
@@ -101,10 +102,10 @@ public:
101 void HandleRoomInformationPacket(const ENetEvent* event); 102 void HandleRoomInformationPacket(const ENetEvent* event);
102 103
103 /** 104 /**
104 * Extracts a WifiPacket from a received ENet packet. 105 * Extracts a ProxyPacket from a received ENet packet.
105 * @param event The ENet event that was received. 106 * @param event The ENet event that was received.
106 */ 107 */
107 void HandleWifiPackets(const ENetEvent* event); 108 void HandleProxyPackets(const ENetEvent* event);
108 109
109 /** 110 /**
110 * Extracts a chat entry from a received ENet packet and adds it to the chat queue. 111 * Extracts a chat entry from a received ENet packet and adds it to the chat queue.
@@ -158,12 +159,12 @@ void RoomMember::RoomMemberImpl::MemberLoop() {
158 while (IsConnected()) { 159 while (IsConnected()) {
159 std::lock_guard lock(network_mutex); 160 std::lock_guard lock(network_mutex);
160 ENetEvent event; 161 ENetEvent event;
161 if (enet_host_service(client, &event, 16) > 0) { 162 if (enet_host_service(client, &event, 100) > 0) {
162 switch (event.type) { 163 switch (event.type) {
163 case ENET_EVENT_TYPE_RECEIVE: 164 case ENET_EVENT_TYPE_RECEIVE:
164 switch (event.packet->data[0]) { 165 switch (event.packet->data[0]) {
165 case IdWifiPacket: 166 case IdProxyPacket:
166 HandleWifiPackets(&event); 167 HandleProxyPackets(&event);
167 break; 168 break;
168 case IdChatMessage: 169 case IdChatMessage:
169 HandleChatPacket(&event); 170 HandleChatPacket(&event);
@@ -198,13 +199,9 @@ void RoomMember::RoomMemberImpl::MemberLoop() {
198 SetState(State::Idle); 199 SetState(State::Idle);
199 SetError(Error::NameCollision); 200 SetError(Error::NameCollision);
200 break; 201 break;
201 case IdMacCollision: 202 case IdIpCollision:
202 SetState(State::Idle); 203 SetState(State::Idle);
203 SetError(Error::MacCollision); 204 SetError(Error::IpCollision);
204 break;
205 case IdConsoleIdCollision:
206 SetState(State::Idle);
207 SetError(Error::ConsoleIdCollision);
208 break; 205 break;
209 case IdVersionMismatch: 206 case IdVersionMismatch:
210 SetState(State::Idle); 207 SetState(State::Idle);
@@ -275,15 +272,13 @@ void RoomMember::RoomMemberImpl::Send(Packet&& packet) {
275} 272}
276 273
277void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname_, 274void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname_,
278 const std::string& console_id_hash, 275 const IPv4Address& preferred_fake_ip,
279 const MacAddress& preferred_mac,
280 const std::string& password, 276 const std::string& password,
281 const std::string& token) { 277 const std::string& token) {
282 Packet packet; 278 Packet packet;
283 packet.Write(static_cast<u8>(IdJoinRequest)); 279 packet.Write(static_cast<u8>(IdJoinRequest));
284 packet.Write(nickname_); 280 packet.Write(nickname_);
285 packet.Write(console_id_hash); 281 packet.Write(preferred_fake_ip);
286 packet.Write(preferred_mac);
287 packet.Write(network_version); 282 packet.Write(network_version);
288 packet.Write(password); 283 packet.Write(password);
289 packet.Write(token); 284 packet.Write(token);
@@ -317,7 +312,7 @@ void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* ev
317 312
318 for (auto& member : member_information) { 313 for (auto& member : member_information) {
319 packet.Read(member.nickname); 314 packet.Read(member.nickname);
320 packet.Read(member.mac_address); 315 packet.Read(member.fake_ip);
321 packet.Read(member.game_info.name); 316 packet.Read(member.game_info.name);
322 packet.Read(member.game_info.id); 317 packet.Read(member.game_info.id);
323 packet.Read(member.username); 318 packet.Read(member.username);
@@ -342,29 +337,38 @@ void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) {
342 packet.IgnoreBytes(sizeof(u8)); // Ignore the message type 337 packet.IgnoreBytes(sizeof(u8)); // Ignore the message type
343 338
344 // Parse the MAC Address from the packet 339 // Parse the MAC Address from the packet
345 packet.Read(mac_address); 340 packet.Read(fake_ip);
346} 341}
347 342
348void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) { 343void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) {
349 WifiPacket wifi_packet{}; 344 ProxyPacket proxy_packet{};
350 Packet packet; 345 Packet packet;
351 packet.Append(event->packet->data, event->packet->dataLength); 346 packet.Append(event->packet->data, event->packet->dataLength);
352 347
353 // Ignore the first byte, which is the message id. 348 // Ignore the first byte, which is the message id.
354 packet.IgnoreBytes(sizeof(u8)); // Ignore the message type 349 packet.IgnoreBytes(sizeof(u8)); // Ignore the message type
355 350
356 // Parse the WifiPacket from the packet 351 // Parse the ProxyPacket from the packet
357 u8 frame_type; 352 u8 local_family;
358 packet.Read(frame_type); 353 packet.Read(local_family);
359 WifiPacket::PacketType type = static_cast<WifiPacket::PacketType>(frame_type); 354 proxy_packet.local_endpoint.family = static_cast<Domain>(local_family);
355 packet.Read(proxy_packet.local_endpoint.ip);
356 packet.Read(proxy_packet.local_endpoint.portno);
357
358 u8 remote_family;
359 packet.Read(remote_family);
360 proxy_packet.remote_endpoint.family = static_cast<Domain>(remote_family);
361 packet.Read(proxy_packet.remote_endpoint.ip);
362 packet.Read(proxy_packet.remote_endpoint.portno);
363
364 u8 protocol_type;
365 packet.Read(protocol_type);
366 proxy_packet.protocol = static_cast<Protocol>(protocol_type);
360 367
361 wifi_packet.type = type; 368 packet.Read(proxy_packet.broadcast);
362 packet.Read(wifi_packet.channel); 369 packet.Read(proxy_packet.data);
363 packet.Read(wifi_packet.transmitter_address);
364 packet.Read(wifi_packet.destination_address);
365 packet.Read(wifi_packet.data);
366 370
367 Invoke<WifiPacket>(wifi_packet); 371 Invoke<ProxyPacket>(proxy_packet);
368} 372}
369 373
370void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { 374void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {
@@ -440,8 +444,8 @@ void RoomMember::RoomMemberImpl::Disconnect() {
440} 444}
441 445
442template <> 446template <>
443RoomMember::RoomMemberImpl::CallbackSet<WifiPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() { 447RoomMember::RoomMemberImpl::CallbackSet<ProxyPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() {
444 return callback_set_wifi_packet; 448 return callback_set_proxy_packet;
445} 449}
446 450
447template <> 451template <>
@@ -525,19 +529,18 @@ const std::string& RoomMember::GetUsername() const {
525 return room_member_impl->username; 529 return room_member_impl->username;
526} 530}
527 531
528const MacAddress& RoomMember::GetMacAddress() const { 532const IPv4Address& RoomMember::GetFakeIpAddress() const {
529 ASSERT_MSG(IsConnected(), "Tried to get MAC address while not connected"); 533 ASSERT_MSG(IsConnected(), "Tried to get fake ip address while not connected");
530 return room_member_impl->mac_address; 534 return room_member_impl->fake_ip;
531} 535}
532 536
533RoomInformation RoomMember::GetRoomInformation() const { 537RoomInformation RoomMember::GetRoomInformation() const {
534 return room_member_impl->room_information; 538 return room_member_impl->room_information;
535} 539}
536 540
537void RoomMember::Join(const std::string& nick, const std::string& console_id_hash, 541void RoomMember::Join(const std::string& nick, const char* server_addr, u16 server_port,
538 const char* server_addr, u16 server_port, u16 client_port, 542 u16 client_port, const IPv4Address& preferred_fake_ip,
539 const MacAddress& preferred_mac, const std::string& password, 543 const std::string& password, const std::string& token) {
540 const std::string& token) {
541 // If the member is connected, kill the connection first 544 // If the member is connected, kill the connection first
542 if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) { 545 if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) {
543 Leave(); 546 Leave();
@@ -571,7 +574,7 @@ void RoomMember::Join(const std::string& nick, const std::string& console_id_has
571 if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { 574 if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
572 room_member_impl->nickname = nick; 575 room_member_impl->nickname = nick;
573 room_member_impl->StartLoop(); 576 room_member_impl->StartLoop();
574 room_member_impl->SendJoinRequest(nick, console_id_hash, preferred_mac, password, token); 577 room_member_impl->SendJoinRequest(nick, preferred_fake_ip, password, token);
575 SendGameInfo(room_member_impl->current_game_info); 578 SendGameInfo(room_member_impl->current_game_info);
576 } else { 579 } else {
577 enet_peer_disconnect(room_member_impl->server, 0); 580 enet_peer_disconnect(room_member_impl->server, 0);
@@ -584,14 +587,22 @@ bool RoomMember::IsConnected() const {
584 return room_member_impl->IsConnected(); 587 return room_member_impl->IsConnected();
585} 588}
586 589
587void RoomMember::SendWifiPacket(const WifiPacket& wifi_packet) { 590void RoomMember::SendProxyPacket(const ProxyPacket& proxy_packet) {
588 Packet packet; 591 Packet packet;
589 packet.Write(static_cast<u8>(IdWifiPacket)); 592 packet.Write(static_cast<u8>(IdProxyPacket));
590 packet.Write(static_cast<u8>(wifi_packet.type)); 593
591 packet.Write(wifi_packet.channel); 594 packet.Write(static_cast<u8>(proxy_packet.local_endpoint.family));
592 packet.Write(wifi_packet.transmitter_address); 595 packet.Write(proxy_packet.local_endpoint.ip);
593 packet.Write(wifi_packet.destination_address); 596 packet.Write(proxy_packet.local_endpoint.portno);
594 packet.Write(wifi_packet.data); 597
598 packet.Write(static_cast<u8>(proxy_packet.remote_endpoint.family));
599 packet.Write(proxy_packet.remote_endpoint.ip);
600 packet.Write(proxy_packet.remote_endpoint.portno);
601
602 packet.Write(static_cast<u8>(proxy_packet.protocol));
603 packet.Write(proxy_packet.broadcast);
604 packet.Write(proxy_packet.data);
605
595 room_member_impl->Send(std::move(packet)); 606 room_member_impl->Send(std::move(packet));
596} 607}
597 608
@@ -645,8 +656,8 @@ RoomMember::CallbackHandle<RoomMember::Error> RoomMember::BindOnError(
645 return room_member_impl->Bind(callback); 656 return room_member_impl->Bind(callback);
646} 657}
647 658
648RoomMember::CallbackHandle<WifiPacket> RoomMember::BindOnWifiPacketReceived( 659RoomMember::CallbackHandle<ProxyPacket> RoomMember::BindOnProxyPacketReceived(
649 std::function<void(const WifiPacket&)> callback) { 660 std::function<void(const ProxyPacket&)> callback) {
650 return room_member_impl->Bind(callback); 661 return room_member_impl->Bind(callback);
651} 662}
652 663
@@ -685,7 +696,7 @@ void RoomMember::Leave() {
685 room_member_impl->client = nullptr; 696 room_member_impl->client = nullptr;
686} 697}
687 698
688template void RoomMember::Unbind(CallbackHandle<WifiPacket>); 699template void RoomMember::Unbind(CallbackHandle<ProxyPacket>);
689template void RoomMember::Unbind(CallbackHandle<RoomMember::State>); 700template void RoomMember::Unbind(CallbackHandle<RoomMember::State>);
690template void RoomMember::Unbind(CallbackHandle<RoomMember::Error>); 701template void RoomMember::Unbind(CallbackHandle<RoomMember::Error>);
691template void RoomMember::Unbind(CallbackHandle<RoomInformation>); 702template void RoomMember::Unbind(CallbackHandle<RoomInformation>);
diff --git a/src/network/room_member.h b/src/network/room_member.h
index bbb7d13d4..4252b7146 100644
--- a/src/network/room_member.h
+++ b/src/network/room_member.h
@@ -9,6 +9,7 @@
9#include <vector> 9#include <vector>
10#include "common/announce_multiplayer_room.h" 10#include "common/announce_multiplayer_room.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/socket_types.h"
12#include "network/room.h" 13#include "network/room.h"
13 14
14namespace Network { 15namespace Network {
@@ -17,22 +18,12 @@ using AnnounceMultiplayerRoom::GameInfo;
17using AnnounceMultiplayerRoom::RoomInformation; 18using AnnounceMultiplayerRoom::RoomInformation;
18 19
19/// Information about the received WiFi packets. 20/// Information about the received WiFi packets.
20/// Acts as our own 802.11 header. 21struct ProxyPacket {
21struct WifiPacket { 22 SockAddrIn local_endpoint;
22 enum class PacketType : u8 { 23 SockAddrIn remote_endpoint;
23 Beacon, 24 Protocol protocol;
24 Data, 25 bool broadcast;
25 Authentication, 26 std::vector<u8> data;
26 AssociationResponse,
27 Deauthentication,
28 NodeMap
29 };
30 PacketType type; ///< The type of 802.11 frame.
31 std::vector<u8> data; ///< Raw 802.11 frame data, starting at the management frame header
32 /// for management frames.
33 MacAddress transmitter_address; ///< Mac address of the transmitter.
34 MacAddress destination_address; ///< Mac address of the receiver.
35 u8 channel; ///< WiFi channel where this frame was transmitted.
36}; 27};
37 28
38/// Represents a chat message. 29/// Represents a chat message.
@@ -72,15 +63,14 @@ public:
72 HostKicked, ///< Kicked by the host 63 HostKicked, ///< Kicked by the host
73 64
74 // Reasons why connection was rejected 65 // Reasons why connection was rejected
75 UnknownError, ///< Some error [permissions to network device missing or something] 66 UnknownError, ///< Some error [permissions to network device missing or something]
76 NameCollision, ///< Somebody is already using this name 67 NameCollision, ///< Somebody is already using this name
77 MacCollision, ///< Somebody is already using that mac-address 68 IpCollision, ///< Somebody is already using that fake-ip-address
78 ConsoleIdCollision, ///< Somebody in the room has the same Console ID 69 WrongVersion, ///< The room version is not the same as for this RoomMember
79 WrongVersion, ///< The room version is not the same as for this RoomMember 70 WrongPassword, ///< The password doesn't match the one from the Room
80 WrongPassword, ///< The password doesn't match the one from the Room 71 CouldNotConnect, ///< The room is not responding to a connection attempt
81 CouldNotConnect, ///< The room is not responding to a connection attempt 72 RoomIsFull, ///< Room is already at the maximum number of players
82 RoomIsFull, ///< Room is already at the maximum number of players 73 HostBanned, ///< The user is banned by the host
83 HostBanned, ///< The user is banned by the host
84 74
85 // Reasons why moderation request failed 75 // Reasons why moderation request failed
86 PermissionDenied, ///< The user does not have mod permissions 76 PermissionDenied, ///< The user does not have mod permissions
@@ -92,9 +82,9 @@ public:
92 std::string username; ///< The web services username of the member. Can be empty. 82 std::string username; ///< The web services username of the member. Can be empty.
93 std::string display_name; ///< The web services display name of the member. Can be empty. 83 std::string display_name; ///< The web services display name of the member. Can be empty.
94 std::string avatar_url; ///< Url to the member's avatar. Can be empty. 84 std::string avatar_url; ///< Url to the member's avatar. Can be empty.
95 GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're 85 GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're
96 /// not playing anything. 86 /// not playing anything.
97 MacAddress mac_address; ///< MAC address associated with this member. 87 IPv4Address fake_ip; ///< Fake Ip address associated with this member.
98 }; 88 };
99 using MemberList = std::vector<MemberInformation>; 89 using MemberList = std::vector<MemberInformation>;
100 90
@@ -135,7 +125,7 @@ public:
135 /** 125 /**
136 * Returns the MAC address of the RoomMember. 126 * Returns the MAC address of the RoomMember.
137 */ 127 */
138 const MacAddress& GetMacAddress() const; 128 const IPv4Address& GetFakeIpAddress() const;
139 129
140 /** 130 /**
141 * Returns information about the room we're currently connected to. 131 * Returns information about the room we're currently connected to.
@@ -149,19 +139,17 @@ public:
149 139
150 /** 140 /**
151 * Attempts to join a room at the specified address and port, using the specified nickname. 141 * Attempts to join a room at the specified address and port, using the specified nickname.
152 * A console ID hash is passed in to check console ID conflicts.
153 * This may fail if the username or console ID is already taken.
154 */ 142 */
155 void Join(const std::string& nickname, const std::string& console_id_hash, 143 void Join(const std::string& nickname, const char* server_addr = "127.0.0.1",
156 const char* server_addr = "127.0.0.1", u16 server_port = DefaultRoomPort, 144 u16 server_port = DefaultRoomPort, u16 client_port = 0,
157 u16 client_port = 0, const MacAddress& preferred_mac = NoPreferredMac, 145 const IPv4Address& preferred_fake_ip = NoPreferredIP,
158 const std::string& password = "", const std::string& token = ""); 146 const std::string& password = "", const std::string& token = "");
159 147
160 /** 148 /**
161 * Sends a WiFi packet to the room. 149 * Sends a WiFi packet to the room.
162 * @param packet The WiFi packet to send. 150 * @param packet The WiFi packet to send.
163 */ 151 */
164 void SendWifiPacket(const WifiPacket& packet); 152 void SendProxyPacket(const ProxyPacket& packet);
165 153
166 /** 154 /**
167 * Sends a chat message to the room. 155 * Sends a chat message to the room.
@@ -207,14 +195,14 @@ public:
207 CallbackHandle<Error> BindOnError(std::function<void(const Error&)> callback); 195 CallbackHandle<Error> BindOnError(std::function<void(const Error&)> callback);
208 196
209 /** 197 /**
210 * Binds a function to an event that will be triggered every time a WifiPacket is received. 198 * Binds a function to an event that will be triggered every time a ProxyPacket is received.
211 * The function wil be called everytime the event is triggered. 199 * The function wil be called everytime the event is triggered.
212 * The callback function must not bind or unbind a function. Doing so will cause a deadlock 200 * The callback function must not bind or unbind a function. Doing so will cause a deadlock
213 * @param callback The function to call 201 * @param callback The function to call
214 * @return A handle used for removing the function from the registered list 202 * @return A handle used for removing the function from the registered list
215 */ 203 */
216 CallbackHandle<WifiPacket> BindOnWifiPacketReceived( 204 CallbackHandle<ProxyPacket> BindOnProxyPacketReceived(
217 std::function<void(const WifiPacket&)> callback); 205 std::function<void(const ProxyPacket&)> callback);
218 206
219 /** 207 /**
220 * Binds a function to an event that will be triggered every time the RoomInformation changes. 208 * Binds a function to an event that will be triggered every time the RoomInformation changes.
@@ -292,10 +280,8 @@ inline const char* GetErrorStr(const RoomMember::Error& e) {
292 return "UnknownError"; 280 return "UnknownError";
293 case RoomMember::Error::NameCollision: 281 case RoomMember::Error::NameCollision:
294 return "NameCollision"; 282 return "NameCollision";
295 case RoomMember::Error::MacCollision: 283 case RoomMember::Error::IpCollision:
296 return "MaxCollision"; 284 return "IpCollision";
297 case RoomMember::Error::ConsoleIdCollision:
298 return "ConsoleIdCollision";
299 case RoomMember::Error::WrongVersion: 285 case RoomMember::Error::WrongVersion:
300 return "WrongVersion"; 286 return "WrongVersion";
301 case RoomMember::Error::WrongPassword: 287 case RoomMember::Error::WrongPassword:
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/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp
index a1be8dcf1..71121e42a 100644
--- a/src/tests/video_core/buffer_base.cpp
+++ b/src/tests/video_core/buffer_base.cpp
@@ -22,8 +22,9 @@ constexpr VAddr c = 0x1328914000;
22class RasterizerInterface { 22class RasterizerInterface {
23public: 23public:
24 void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { 24 void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
25 const u64 page_start{addr >> Core::Memory::PAGE_BITS}; 25 const u64 page_start{addr >> Core::Memory::YUZU_PAGEBITS};
26 const u64 page_end{(addr + size + Core::Memory::PAGE_SIZE - 1) >> Core::Memory::PAGE_BITS}; 26 const u64 page_end{(addr + size + Core::Memory::YUZU_PAGESIZE - 1) >>
27 Core::Memory::YUZU_PAGEBITS};
27 for (u64 page = page_start; page < page_end; ++page) { 28 for (u64 page = page_start; page < page_end; ++page) {
28 int& value = page_table[page]; 29 int& value = page_table[page];
29 value += delta; 30 value += delta;
@@ -37,7 +38,7 @@ public:
37 } 38 }
38 39
39 [[nodiscard]] int Count(VAddr addr) const noexcept { 40 [[nodiscard]] int Count(VAddr addr) const noexcept {
40 const auto it = page_table.find(addr >> Core::Memory::PAGE_BITS); 41 const auto it = page_table.find(addr >> Core::Memory::YUZU_PAGEBITS);
41 return it == page_table.end() ? 0 : it->second; 42 return it == page_table.end() ? 0 : it->second;
42 } 43 }
43 44
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index 3e20608ca..0b2bc67b1 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -36,7 +36,7 @@ struct NullBufferParams {};
36template <class RasterizerInterface> 36template <class RasterizerInterface>
37class BufferBase { 37class BufferBase {
38 static constexpr u64 PAGES_PER_WORD = 64; 38 static constexpr u64 PAGES_PER_WORD = 64;
39 static constexpr u64 BYTES_PER_PAGE = Core::Memory::PAGE_SIZE; 39 static constexpr u64 BYTES_PER_PAGE = Core::Memory::YUZU_PAGESIZE;
40 static constexpr u64 BYTES_PER_WORD = PAGES_PER_WORD * BYTES_PER_PAGE; 40 static constexpr u64 BYTES_PER_WORD = PAGES_PER_WORD * BYTES_PER_PAGE;
41 41
42 /// Vector tracking modified pages tightly packed with small vector optimization 42 /// Vector tracking modified pages tightly packed with small vector optimization
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index b74ad7900..f015dae56 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -60,8 +60,8 @@ class BufferCache {
60 60
61 // Page size for caching purposes. 61 // Page size for caching purposes.
62 // This is unrelated to the CPU page size and it can be changed as it seems optimal. 62 // This is unrelated to the CPU page size and it can be changed as it seems optimal.
63 static constexpr u32 PAGE_BITS = 16; 63 static constexpr u32 YUZU_PAGEBITS = 16;
64 static constexpr u64 PAGE_SIZE = u64{1} << PAGE_BITS; 64 static constexpr u64 YUZU_PAGESIZE = u64{1} << YUZU_PAGEBITS;
65 65
66 static constexpr bool IS_OPENGL = P::IS_OPENGL; 66 static constexpr bool IS_OPENGL = P::IS_OPENGL;
67 static constexpr bool HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS = 67 static constexpr bool HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS =
@@ -216,8 +216,8 @@ private:
216 216
217 template <typename Func> 217 template <typename Func>
218 void ForEachBufferInRange(VAddr cpu_addr, u64 size, Func&& func) { 218 void ForEachBufferInRange(VAddr cpu_addr, u64 size, Func&& func) {
219 const u64 page_end = Common::DivCeil(cpu_addr + size, PAGE_SIZE); 219 const u64 page_end = Common::DivCeil(cpu_addr + size, YUZU_PAGESIZE);
220 for (u64 page = cpu_addr >> PAGE_BITS; page < page_end;) { 220 for (u64 page = cpu_addr >> YUZU_PAGEBITS; page < page_end;) {
221 const BufferId buffer_id = page_table[page]; 221 const BufferId buffer_id = page_table[page];
222 if (!buffer_id) { 222 if (!buffer_id) {
223 ++page; 223 ++page;
@@ -227,7 +227,7 @@ private:
227 func(buffer_id, buffer); 227 func(buffer_id, buffer);
228 228
229 const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes(); 229 const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes();
230 page = Common::DivCeil(end_addr, PAGE_SIZE); 230 page = Common::DivCeil(end_addr, YUZU_PAGESIZE);
231 } 231 }
232 } 232 }
233 233
@@ -262,8 +262,8 @@ private:
262 } 262 }
263 263
264 static bool IsRangeGranular(VAddr cpu_addr, size_t size) { 264 static bool IsRangeGranular(VAddr cpu_addr, size_t size) {
265 return (cpu_addr & ~Core::Memory::PAGE_MASK) == 265 return (cpu_addr & ~Core::Memory::YUZU_PAGEMASK) ==
266 ((cpu_addr + size) & ~Core::Memory::PAGE_MASK); 266 ((cpu_addr + size) & ~Core::Memory::YUZU_PAGEMASK);
267 } 267 }
268 268
269 void RunGarbageCollector(); 269 void RunGarbageCollector();
@@ -439,7 +439,7 @@ private:
439 u64 minimum_memory = 0; 439 u64 minimum_memory = 0;
440 u64 critical_memory = 0; 440 u64 critical_memory = 0;
441 441
442 std::array<BufferId, ((1ULL << 39) >> PAGE_BITS)> page_table; 442 std::array<BufferId, ((1ULL << 39) >> YUZU_PAGEBITS)> page_table;
443}; 443};
444 444
445template <class P> 445template <class P>
@@ -926,8 +926,8 @@ void BufferCache<P>::PopAsyncFlushes() {}
926 926
927template <class P> 927template <class P>
928bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { 928bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
929 const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); 929 const u64 page_end = Common::DivCeil(addr + size, YUZU_PAGESIZE);
930 for (u64 page = addr >> PAGE_BITS; page < page_end;) { 930 for (u64 page = addr >> YUZU_PAGEBITS; page < page_end;) {
931 const BufferId image_id = page_table[page]; 931 const BufferId image_id = page_table[page];
932 if (!image_id) { 932 if (!image_id) {
933 ++page; 933 ++page;
@@ -938,7 +938,7 @@ bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
938 return true; 938 return true;
939 } 939 }
940 const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes(); 940 const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes();
941 page = Common::DivCeil(end_addr, PAGE_SIZE); 941 page = Common::DivCeil(end_addr, YUZU_PAGESIZE);
942 } 942 }
943 return false; 943 return false;
944} 944}
@@ -946,8 +946,8 @@ bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
946template <class P> 946template <class P>
947bool BufferCache<P>::IsRegionRegistered(VAddr addr, size_t size) { 947bool BufferCache<P>::IsRegionRegistered(VAddr addr, size_t size) {
948 const VAddr end_addr = addr + size; 948 const VAddr end_addr = addr + size;
949 const u64 page_end = Common::DivCeil(end_addr, PAGE_SIZE); 949 const u64 page_end = Common::DivCeil(end_addr, YUZU_PAGESIZE);
950 for (u64 page = addr >> PAGE_BITS; page < page_end;) { 950 for (u64 page = addr >> YUZU_PAGEBITS; page < page_end;) {
951 const BufferId buffer_id = page_table[page]; 951 const BufferId buffer_id = page_table[page];
952 if (!buffer_id) { 952 if (!buffer_id) {
953 ++page; 953 ++page;
@@ -959,15 +959,15 @@ bool BufferCache<P>::IsRegionRegistered(VAddr addr, size_t size) {
959 if (buf_start_addr < end_addr && addr < buf_end_addr) { 959 if (buf_start_addr < end_addr && addr < buf_end_addr) {
960 return true; 960 return true;
961 } 961 }
962 page = Common::DivCeil(end_addr, PAGE_SIZE); 962 page = Common::DivCeil(end_addr, YUZU_PAGESIZE);
963 } 963 }
964 return false; 964 return false;
965} 965}
966 966
967template <class P> 967template <class P>
968bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) { 968bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) {
969 const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); 969 const u64 page_end = Common::DivCeil(addr + size, YUZU_PAGESIZE);
970 for (u64 page = addr >> PAGE_BITS; page < page_end;) { 970 for (u64 page = addr >> YUZU_PAGEBITS; page < page_end;) {
971 const BufferId image_id = page_table[page]; 971 const BufferId image_id = page_table[page];
972 if (!image_id) { 972 if (!image_id) {
973 ++page; 973 ++page;
@@ -978,7 +978,7 @@ bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) {
978 return true; 978 return true;
979 } 979 }
980 const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes(); 980 const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes();
981 page = Common::DivCeil(end_addr, PAGE_SIZE); 981 page = Common::DivCeil(end_addr, YUZU_PAGESIZE);
982 } 982 }
983 return false; 983 return false;
984} 984}
@@ -1472,7 +1472,7 @@ BufferId BufferCache<P>::FindBuffer(VAddr cpu_addr, u32 size) {
1472 if (cpu_addr == 0) { 1472 if (cpu_addr == 0) {
1473 return NULL_BUFFER_ID; 1473 return NULL_BUFFER_ID;
1474 } 1474 }
1475 const u64 page = cpu_addr >> PAGE_BITS; 1475 const u64 page = cpu_addr >> YUZU_PAGEBITS;
1476 const BufferId buffer_id = page_table[page]; 1476 const BufferId buffer_id = page_table[page];
1477 if (!buffer_id) { 1477 if (!buffer_id) {
1478 return CreateBuffer(cpu_addr, size); 1478 return CreateBuffer(cpu_addr, size);
@@ -1493,8 +1493,9 @@ typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu
1493 VAddr end = cpu_addr + wanted_size; 1493 VAddr end = cpu_addr + wanted_size;
1494 int stream_score = 0; 1494 int stream_score = 0;
1495 bool has_stream_leap = false; 1495 bool has_stream_leap = false;
1496 for (; cpu_addr >> PAGE_BITS < Common::DivCeil(end, PAGE_SIZE); cpu_addr += PAGE_SIZE) { 1496 for (; cpu_addr >> YUZU_PAGEBITS < Common::DivCeil(end, YUZU_PAGESIZE);
1497 const BufferId overlap_id = page_table[cpu_addr >> PAGE_BITS]; 1497 cpu_addr += YUZU_PAGESIZE) {
1498 const BufferId overlap_id = page_table[cpu_addr >> YUZU_PAGEBITS];
1498 if (!overlap_id) { 1499 if (!overlap_id) {
1499 continue; 1500 continue;
1500 } 1501 }
@@ -1520,11 +1521,11 @@ typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu
1520 // as a stream buffer. Increase the size to skip constantly recreating buffers. 1521 // as a stream buffer. Increase the size to skip constantly recreating buffers.
1521 has_stream_leap = true; 1522 has_stream_leap = true;
1522 if (expands_right) { 1523 if (expands_right) {
1523 begin -= PAGE_SIZE * 256; 1524 begin -= YUZU_PAGESIZE * 256;
1524 cpu_addr = begin; 1525 cpu_addr = begin;
1525 } 1526 }
1526 if (expands_left) { 1527 if (expands_left) {
1527 end += PAGE_SIZE * 256; 1528 end += YUZU_PAGESIZE * 256;
1528 } 1529 }
1529 } 1530 }
1530 } 1531 }
@@ -1598,8 +1599,8 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
1598 } 1599 }
1599 const VAddr cpu_addr_begin = buffer.CpuAddr(); 1600 const VAddr cpu_addr_begin = buffer.CpuAddr();
1600 const VAddr cpu_addr_end = cpu_addr_begin + size; 1601 const VAddr cpu_addr_end = cpu_addr_begin + size;
1601 const u64 page_begin = cpu_addr_begin / PAGE_SIZE; 1602 const u64 page_begin = cpu_addr_begin / YUZU_PAGESIZE;
1602 const u64 page_end = Common::DivCeil(cpu_addr_end, PAGE_SIZE); 1603 const u64 page_end = Common::DivCeil(cpu_addr_end, YUZU_PAGESIZE);
1603 for (u64 page = page_begin; page != page_end; ++page) { 1604 for (u64 page = page_begin; page != page_end; ++page) {
1604 if constexpr (insert) { 1605 if constexpr (insert) {
1605 page_table[page] = buffer_id; 1606 page_table[page] = buffer_id;
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index d373be0ba..bf9eb735d 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -369,8 +369,8 @@ bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
369 if (!cpu_addr) { 369 if (!cpu_addr) {
370 return false; 370 return false;
371 } 371 }
372 const std::size_t page{(*cpu_addr & Core::Memory::PAGE_MASK) + size}; 372 const std::size_t page{(*cpu_addr & Core::Memory::YUZU_PAGEMASK) + size};
373 return page <= Core::Memory::PAGE_SIZE; 373 return page <= Core::Memory::YUZU_PAGESIZE;
374} 374}
375 375
376bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const { 376bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const {
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h
index fcce87acb..889b606b3 100644
--- a/src/video_core/query_cache.h
+++ b/src/video_core/query_cache.h
@@ -214,8 +214,8 @@ private:
214 return cache_begin < addr_end && addr_begin < cache_end; 214 return cache_begin < addr_end && addr_begin < cache_end;
215 }; 215 };
216 216
217 const u64 page_end = addr_end >> PAGE_BITS; 217 const u64 page_end = addr_end >> YUZU_PAGEBITS;
218 for (u64 page = addr_begin >> PAGE_BITS; page <= page_end; ++page) { 218 for (u64 page = addr_begin >> YUZU_PAGEBITS; page <= page_end; ++page) {
219 const auto& it = cached_queries.find(page); 219 const auto& it = cached_queries.find(page);
220 if (it == std::end(cached_queries)) { 220 if (it == std::end(cached_queries)) {
221 continue; 221 continue;
@@ -235,14 +235,14 @@ private:
235 /// Registers the passed parameters as cached and returns a pointer to the stored cached query. 235 /// Registers the passed parameters as cached and returns a pointer to the stored cached query.
236 CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) { 236 CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) {
237 rasterizer.UpdatePagesCachedCount(cpu_addr, CachedQuery::SizeInBytes(timestamp), 1); 237 rasterizer.UpdatePagesCachedCount(cpu_addr, CachedQuery::SizeInBytes(timestamp), 1);
238 const u64 page = static_cast<u64>(cpu_addr) >> PAGE_BITS; 238 const u64 page = static_cast<u64>(cpu_addr) >> YUZU_PAGEBITS;
239 return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr, 239 return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr,
240 host_ptr); 240 host_ptr);
241 } 241 }
242 242
243 /// Tries to a get a cached query. Returns nullptr on failure. 243 /// Tries to a get a cached query. Returns nullptr on failure.
244 CachedQuery* TryGet(VAddr addr) { 244 CachedQuery* TryGet(VAddr addr) {
245 const u64 page = static_cast<u64>(addr) >> PAGE_BITS; 245 const u64 page = static_cast<u64>(addr) >> YUZU_PAGEBITS;
246 const auto it = cached_queries.find(page); 246 const auto it = cached_queries.find(page);
247 if (it == std::end(cached_queries)) { 247 if (it == std::end(cached_queries)) {
248 return nullptr; 248 return nullptr;
@@ -260,8 +260,8 @@ private:
260 uncommitted_flushes->push_back(addr); 260 uncommitted_flushes->push_back(addr);
261 } 261 }
262 262
263 static constexpr std::uintptr_t PAGE_SIZE = 4096; 263 static constexpr std::uintptr_t YUZU_PAGESIZE = 4096;
264 static constexpr unsigned PAGE_BITS = 12; 264 static constexpr unsigned YUZU_PAGEBITS = 12;
265 265
266 VideoCore::RasterizerInterface& rasterizer; 266 VideoCore::RasterizerInterface& rasterizer;
267 Tegra::Engines::Maxwell3D& maxwell3d; 267 Tegra::Engines::Maxwell3D& maxwell3d;
diff --git a/src/video_core/rasterizer_accelerated.cpp b/src/video_core/rasterizer_accelerated.cpp
index 87a29e144..4a197d65d 100644
--- a/src/video_core/rasterizer_accelerated.cpp
+++ b/src/video_core/rasterizer_accelerated.cpp
@@ -24,8 +24,8 @@ void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int del
24 u64 cache_bytes = 0; 24 u64 cache_bytes = 0;
25 25
26 std::atomic_thread_fence(std::memory_order_acquire); 26 std::atomic_thread_fence(std::memory_order_acquire);
27 const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); 27 const u64 page_end = Common::DivCeil(addr + size, YUZU_PAGESIZE);
28 for (u64 page = addr >> PAGE_BITS; page != page_end; ++page) { 28 for (u64 page = addr >> YUZU_PAGEBITS; page != page_end; ++page) {
29 std::atomic_uint16_t& count = cached_pages.at(page >> 2).Count(page); 29 std::atomic_uint16_t& count = cached_pages.at(page >> 2).Count(page);
30 30
31 if (delta > 0) { 31 if (delta > 0) {
@@ -44,26 +44,27 @@ void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int del
44 if (uncache_bytes == 0) { 44 if (uncache_bytes == 0) {
45 uncache_begin = page; 45 uncache_begin = page;
46 } 46 }
47 uncache_bytes += PAGE_SIZE; 47 uncache_bytes += YUZU_PAGESIZE;
48 } else if (uncache_bytes > 0) { 48 } else if (uncache_bytes > 0) {
49 cpu_memory.RasterizerMarkRegionCached(uncache_begin << PAGE_BITS, uncache_bytes, false); 49 cpu_memory.RasterizerMarkRegionCached(uncache_begin << YUZU_PAGEBITS, uncache_bytes,
50 false);
50 uncache_bytes = 0; 51 uncache_bytes = 0;
51 } 52 }
52 if (count.load(std::memory_order::relaxed) == 1 && delta > 0) { 53 if (count.load(std::memory_order::relaxed) == 1 && delta > 0) {
53 if (cache_bytes == 0) { 54 if (cache_bytes == 0) {
54 cache_begin = page; 55 cache_begin = page;
55 } 56 }
56 cache_bytes += PAGE_SIZE; 57 cache_bytes += YUZU_PAGESIZE;
57 } else if (cache_bytes > 0) { 58 } else if (cache_bytes > 0) {
58 cpu_memory.RasterizerMarkRegionCached(cache_begin << PAGE_BITS, cache_bytes, true); 59 cpu_memory.RasterizerMarkRegionCached(cache_begin << YUZU_PAGEBITS, cache_bytes, true);
59 cache_bytes = 0; 60 cache_bytes = 0;
60 } 61 }
61 } 62 }
62 if (uncache_bytes > 0) { 63 if (uncache_bytes > 0) {
63 cpu_memory.RasterizerMarkRegionCached(uncache_begin << PAGE_BITS, uncache_bytes, false); 64 cpu_memory.RasterizerMarkRegionCached(uncache_begin << YUZU_PAGEBITS, uncache_bytes, false);
64 } 65 }
65 if (cache_bytes > 0) { 66 if (cache_bytes > 0) {
66 cpu_memory.RasterizerMarkRegionCached(cache_begin << PAGE_BITS, cache_bytes, true); 67 cpu_memory.RasterizerMarkRegionCached(cache_begin << YUZU_PAGEBITS, cache_bytes, true);
67 } 68 }
68} 69}
69 70
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 07d4b7cf0..1ad56d9e7 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -299,7 +299,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
299 state.has_loaded = true; 299 state.has_loaded = true;
300 lock.unlock(); 300 lock.unlock();
301 301
302 workers->WaitForRequests(); 302 workers->WaitForRequests(stop_loading);
303 if (!use_asynchronous_shaders) { 303 if (!use_asynchronous_shaders) {
304 workers.reset(); 304 workers.reset();
305 } 305 }
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index f83ad0a5b..a0d9d10ef 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -17,6 +17,7 @@ static OGLProgram LinkSeparableProgram(GLuint shader) {
17 glProgramParameteri(program.handle, GL_PROGRAM_SEPARABLE, GL_TRUE); 17 glProgramParameteri(program.handle, GL_PROGRAM_SEPARABLE, GL_TRUE);
18 glAttachShader(program.handle, shader); 18 glAttachShader(program.handle, shader);
19 glLinkProgram(program.handle); 19 glLinkProgram(program.handle);
20 glDetachShader(program.handle, shader);
20 if (!Settings::values.renderer_debug) { 21 if (!Settings::values.renderer_debug) {
21 return program; 22 return program;
22 } 23 }
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 01028cee0..34f3f7a67 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -478,13 +478,16 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
478 } 478 }
479 } 479 }
480 480
481 ASSERT_MSG(framebuffer_crop_rect.top == 0, "Unimplemented");
482 ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented"); 481 ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented");
483 482
483 f32 left_start{};
484 if (framebuffer_crop_rect.Top() > 0) {
485 left_start = static_cast<f32>(framebuffer_crop_rect.Top()) /
486 static_cast<f32>(framebuffer_crop_rect.Bottom());
487 }
484 f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width); 488 f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width);
485 f32 scale_v = 489 f32 scale_v =
486 static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height); 490 static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height);
487
488 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering 491 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
489 // (e.g. handheld mode) on a 1920x1080 framebuffer. 492 // (e.g. handheld mode) on a 1920x1080 framebuffer.
490 if (framebuffer_crop_rect.GetWidth() > 0) { 493 if (framebuffer_crop_rect.GetWidth() > 0) {
@@ -503,10 +506,14 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
503 506
504 const auto& screen = layout.screen; 507 const auto& screen = layout.screen;
505 const std::array vertices = { 508 const std::array vertices = {
506 ScreenRectVertex(screen.left, screen.top, texcoords.top * scale_u, left * scale_v), 509 ScreenRectVertex(screen.left, screen.top, texcoords.top * scale_u,
507 ScreenRectVertex(screen.right, screen.top, texcoords.bottom * scale_u, left * scale_v), 510 left_start + left * scale_v),
508 ScreenRectVertex(screen.left, screen.bottom, texcoords.top * scale_u, right * scale_v), 511 ScreenRectVertex(screen.right, screen.top, texcoords.bottom * scale_u,
509 ScreenRectVertex(screen.right, screen.bottom, texcoords.bottom * scale_u, right * scale_v), 512 left_start + left * scale_v),
513 ScreenRectVertex(screen.left, screen.bottom, texcoords.top * scale_u,
514 left_start + right * scale_v),
515 ScreenRectVertex(screen.right, screen.bottom, texcoords.bottom * scale_u,
516 left_start + right * scale_v),
510 }; 517 };
511 glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices)); 518 glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
512 519
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 689164a6a..bdb71dc53 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -172,7 +172,7 @@ struct FormatTuple {
172 {VK_FORMAT_R8G8_SINT, Attachable | Storage}, // R8G8_SINT 172 {VK_FORMAT_R8G8_SINT, Attachable | Storage}, // R8G8_SINT
173 {VK_FORMAT_R8G8_UINT, Attachable | Storage}, // R8G8_UINT 173 {VK_FORMAT_R8G8_UINT, Attachable | Storage}, // R8G8_UINT
174 {VK_FORMAT_R32G32_UINT, Attachable | Storage}, // R32G32_UINT 174 {VK_FORMAT_R32G32_UINT, Attachable | Storage}, // R32G32_UINT
175 {VK_FORMAT_UNDEFINED}, // R16G16B16X16_FLOAT 175 {VK_FORMAT_R16G16B16A16_SFLOAT, Attachable | Storage}, // R16G16B16X16_FLOAT
176 {VK_FORMAT_R32_UINT, Attachable | Storage}, // R32_UINT 176 {VK_FORMAT_R32_UINT, Attachable | Storage}, // R32_UINT
177 {VK_FORMAT_R32_SINT, Attachable | Storage}, // R32_SINT 177 {VK_FORMAT_R32_SINT, Attachable | Storage}, // R32_SINT
178 {VK_FORMAT_ASTC_8x8_UNORM_BLOCK}, // ASTC_2D_8X8_UNORM 178 {VK_FORMAT_ASTC_8x8_UNORM_BLOCK}, // ASTC_2D_8X8_UNORM
@@ -317,195 +317,204 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device,
317 } 317 }
318} 318}
319 319
320VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { 320VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
321 switch (type) { 321 Maxwell::VertexAttribute::Size size) {
322 case Maxwell::VertexAttribute::Type::UnsignedNorm: 322 const VkFormat format{([&]() {
323 switch (size) { 323 switch (type) {
324 case Maxwell::VertexAttribute::Size::Size_8: 324 case Maxwell::VertexAttribute::Type::UnsignedNorm:
325 return VK_FORMAT_R8_UNORM; 325 switch (size) {
326 case Maxwell::VertexAttribute::Size::Size_8_8: 326 case Maxwell::VertexAttribute::Size::Size_8:
327 return VK_FORMAT_R8G8_UNORM; 327 return VK_FORMAT_R8_UNORM;
328 case Maxwell::VertexAttribute::Size::Size_8_8_8: 328 case Maxwell::VertexAttribute::Size::Size_8_8:
329 return VK_FORMAT_R8G8B8_UNORM; 329 return VK_FORMAT_R8G8_UNORM;
330 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 330 case Maxwell::VertexAttribute::Size::Size_8_8_8:
331 return VK_FORMAT_R8G8B8A8_UNORM; 331 return VK_FORMAT_R8G8B8_UNORM;
332 case Maxwell::VertexAttribute::Size::Size_16: 332 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
333 return VK_FORMAT_R16_UNORM; 333 return VK_FORMAT_R8G8B8A8_UNORM;
334 case Maxwell::VertexAttribute::Size::Size_16_16: 334 case Maxwell::VertexAttribute::Size::Size_16:
335 return VK_FORMAT_R16G16_UNORM; 335 return VK_FORMAT_R16_UNORM;
336 case Maxwell::VertexAttribute::Size::Size_16_16_16: 336 case Maxwell::VertexAttribute::Size::Size_16_16:
337 return VK_FORMAT_R16G16B16_UNORM; 337 return VK_FORMAT_R16G16_UNORM;
338 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 338 case Maxwell::VertexAttribute::Size::Size_16_16_16:
339 return VK_FORMAT_R16G16B16A16_UNORM; 339 return VK_FORMAT_R16G16B16_UNORM;
340 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 340 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
341 return VK_FORMAT_A2B10G10R10_UNORM_PACK32; 341 return VK_FORMAT_R16G16B16A16_UNORM;
342 default: 342 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
343 return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
344 default:
345 break;
346 }
343 break; 347 break;
344 } 348 case Maxwell::VertexAttribute::Type::SignedNorm:
345 break; 349 switch (size) {
346 case Maxwell::VertexAttribute::Type::SignedNorm: 350 case Maxwell::VertexAttribute::Size::Size_8:
347 switch (size) { 351 return VK_FORMAT_R8_SNORM;
348 case Maxwell::VertexAttribute::Size::Size_8: 352 case Maxwell::VertexAttribute::Size::Size_8_8:
349 return VK_FORMAT_R8_SNORM; 353 return VK_FORMAT_R8G8_SNORM;
350 case Maxwell::VertexAttribute::Size::Size_8_8: 354 case Maxwell::VertexAttribute::Size::Size_8_8_8:
351 return VK_FORMAT_R8G8_SNORM; 355 return VK_FORMAT_R8G8B8_SNORM;
352 case Maxwell::VertexAttribute::Size::Size_8_8_8: 356 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
353 return VK_FORMAT_R8G8B8_SNORM; 357 return VK_FORMAT_R8G8B8A8_SNORM;
354 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 358 case Maxwell::VertexAttribute::Size::Size_16:
355 return VK_FORMAT_R8G8B8A8_SNORM; 359 return VK_FORMAT_R16_SNORM;
356 case Maxwell::VertexAttribute::Size::Size_16: 360 case Maxwell::VertexAttribute::Size::Size_16_16:
357 return VK_FORMAT_R16_SNORM; 361 return VK_FORMAT_R16G16_SNORM;
358 case Maxwell::VertexAttribute::Size::Size_16_16: 362 case Maxwell::VertexAttribute::Size::Size_16_16_16:
359 return VK_FORMAT_R16G16_SNORM; 363 return VK_FORMAT_R16G16B16_SNORM;
360 case Maxwell::VertexAttribute::Size::Size_16_16_16: 364 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
361 return VK_FORMAT_R16G16B16_SNORM; 365 return VK_FORMAT_R16G16B16A16_SNORM;
362 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 366 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
363 return VK_FORMAT_R16G16B16A16_SNORM; 367 return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
364 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 368 default:
365 return VK_FORMAT_A2B10G10R10_SNORM_PACK32; 369 break;
366 default: 370 }
367 break; 371 break;
368 } 372 case Maxwell::VertexAttribute::Type::UnsignedScaled:
369 break; 373 switch (size) {
370 case Maxwell::VertexAttribute::Type::UnsignedScaled: 374 case Maxwell::VertexAttribute::Size::Size_8:
371 switch (size) { 375 return VK_FORMAT_R8_USCALED;
372 case Maxwell::VertexAttribute::Size::Size_8: 376 case Maxwell::VertexAttribute::Size::Size_8_8:
373 return VK_FORMAT_R8_USCALED; 377 return VK_FORMAT_R8G8_USCALED;
374 case Maxwell::VertexAttribute::Size::Size_8_8: 378 case Maxwell::VertexAttribute::Size::Size_8_8_8:
375 return VK_FORMAT_R8G8_USCALED; 379 return VK_FORMAT_R8G8B8_USCALED;
376 case Maxwell::VertexAttribute::Size::Size_8_8_8: 380 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
377 return VK_FORMAT_R8G8B8_USCALED; 381 return VK_FORMAT_R8G8B8A8_USCALED;
378 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 382 case Maxwell::VertexAttribute::Size::Size_16:
379 return VK_FORMAT_R8G8B8A8_USCALED; 383 return VK_FORMAT_R16_USCALED;
380 case Maxwell::VertexAttribute::Size::Size_16: 384 case Maxwell::VertexAttribute::Size::Size_16_16:
381 return VK_FORMAT_R16_USCALED; 385 return VK_FORMAT_R16G16_USCALED;
382 case Maxwell::VertexAttribute::Size::Size_16_16: 386 case Maxwell::VertexAttribute::Size::Size_16_16_16:
383 return VK_FORMAT_R16G16_USCALED; 387 return VK_FORMAT_R16G16B16_USCALED;
384 case Maxwell::VertexAttribute::Size::Size_16_16_16: 388 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
385 return VK_FORMAT_R16G16B16_USCALED; 389 return VK_FORMAT_R16G16B16A16_USCALED;
386 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 390 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
387 return VK_FORMAT_R16G16B16A16_USCALED; 391 return VK_FORMAT_A2B10G10R10_USCALED_PACK32;
388 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 392 default:
389 return VK_FORMAT_A2B10G10R10_USCALED_PACK32; 393 break;
390 default: 394 }
391 break; 395 break;
392 } 396 case Maxwell::VertexAttribute::Type::SignedScaled:
393 break; 397 switch (size) {
394 case Maxwell::VertexAttribute::Type::SignedScaled: 398 case Maxwell::VertexAttribute::Size::Size_8:
395 switch (size) { 399 return VK_FORMAT_R8_SSCALED;
396 case Maxwell::VertexAttribute::Size::Size_8: 400 case Maxwell::VertexAttribute::Size::Size_8_8:
397 return VK_FORMAT_R8_SSCALED; 401 return VK_FORMAT_R8G8_SSCALED;
398 case Maxwell::VertexAttribute::Size::Size_8_8: 402 case Maxwell::VertexAttribute::Size::Size_8_8_8:
399 return VK_FORMAT_R8G8_SSCALED; 403 return VK_FORMAT_R8G8B8_SSCALED;
400 case Maxwell::VertexAttribute::Size::Size_8_8_8: 404 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
401 return VK_FORMAT_R8G8B8_SSCALED; 405 return VK_FORMAT_R8G8B8A8_SSCALED;
402 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 406 case Maxwell::VertexAttribute::Size::Size_16:
403 return VK_FORMAT_R8G8B8A8_SSCALED; 407 return VK_FORMAT_R16_SSCALED;
404 case Maxwell::VertexAttribute::Size::Size_16: 408 case Maxwell::VertexAttribute::Size::Size_16_16:
405 return VK_FORMAT_R16_SSCALED; 409 return VK_FORMAT_R16G16_SSCALED;
406 case Maxwell::VertexAttribute::Size::Size_16_16: 410 case Maxwell::VertexAttribute::Size::Size_16_16_16:
407 return VK_FORMAT_R16G16_SSCALED; 411 return VK_FORMAT_R16G16B16_SSCALED;
408 case Maxwell::VertexAttribute::Size::Size_16_16_16: 412 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
409 return VK_FORMAT_R16G16B16_SSCALED; 413 return VK_FORMAT_R16G16B16A16_SSCALED;
410 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 414 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
411 return VK_FORMAT_R16G16B16A16_SSCALED; 415 return VK_FORMAT_A2B10G10R10_SSCALED_PACK32;
412 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 416 default:
413 return VK_FORMAT_A2B10G10R10_SSCALED_PACK32; 417 break;
414 default: 418 }
415 break; 419 break;
416 } 420 case Maxwell::VertexAttribute::Type::UnsignedInt:
417 break; 421 switch (size) {
418 case Maxwell::VertexAttribute::Type::UnsignedInt: 422 case Maxwell::VertexAttribute::Size::Size_8:
419 switch (size) { 423 return VK_FORMAT_R8_UINT;
420 case Maxwell::VertexAttribute::Size::Size_8: 424 case Maxwell::VertexAttribute::Size::Size_8_8:
421 return VK_FORMAT_R8_UINT; 425 return VK_FORMAT_R8G8_UINT;
422 case Maxwell::VertexAttribute::Size::Size_8_8: 426 case Maxwell::VertexAttribute::Size::Size_8_8_8:
423 return VK_FORMAT_R8G8_UINT; 427 return VK_FORMAT_R8G8B8_UINT;
424 case Maxwell::VertexAttribute::Size::Size_8_8_8: 428 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
425 return VK_FORMAT_R8G8B8_UINT; 429 return VK_FORMAT_R8G8B8A8_UINT;
426 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 430 case Maxwell::VertexAttribute::Size::Size_16:
427 return VK_FORMAT_R8G8B8A8_UINT; 431 return VK_FORMAT_R16_UINT;
428 case Maxwell::VertexAttribute::Size::Size_16: 432 case Maxwell::VertexAttribute::Size::Size_16_16:
429 return VK_FORMAT_R16_UINT; 433 return VK_FORMAT_R16G16_UINT;
430 case Maxwell::VertexAttribute::Size::Size_16_16: 434 case Maxwell::VertexAttribute::Size::Size_16_16_16:
431 return VK_FORMAT_R16G16_UINT; 435 return VK_FORMAT_R16G16B16_UINT;
432 case Maxwell::VertexAttribute::Size::Size_16_16_16: 436 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
433 return VK_FORMAT_R16G16B16_UINT; 437 return VK_FORMAT_R16G16B16A16_UINT;
434 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 438 case Maxwell::VertexAttribute::Size::Size_32:
435 return VK_FORMAT_R16G16B16A16_UINT; 439 return VK_FORMAT_R32_UINT;
436 case Maxwell::VertexAttribute::Size::Size_32: 440 case Maxwell::VertexAttribute::Size::Size_32_32:
437 return VK_FORMAT_R32_UINT; 441 return VK_FORMAT_R32G32_UINT;
438 case Maxwell::VertexAttribute::Size::Size_32_32: 442 case Maxwell::VertexAttribute::Size::Size_32_32_32:
439 return VK_FORMAT_R32G32_UINT; 443 return VK_FORMAT_R32G32B32_UINT;
440 case Maxwell::VertexAttribute::Size::Size_32_32_32: 444 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
441 return VK_FORMAT_R32G32B32_UINT; 445 return VK_FORMAT_R32G32B32A32_UINT;
442 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 446 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
443 return VK_FORMAT_R32G32B32A32_UINT; 447 return VK_FORMAT_A2B10G10R10_UINT_PACK32;
444 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 448 default:
445 return VK_FORMAT_A2B10G10R10_UINT_PACK32; 449 break;
446 default: 450 }
447 break; 451 break;
448 } 452 case Maxwell::VertexAttribute::Type::SignedInt:
449 break; 453 switch (size) {
450 case Maxwell::VertexAttribute::Type::SignedInt: 454 case Maxwell::VertexAttribute::Size::Size_8:
451 switch (size) { 455 return VK_FORMAT_R8_SINT;
452 case Maxwell::VertexAttribute::Size::Size_8: 456 case Maxwell::VertexAttribute::Size::Size_8_8:
453 return VK_FORMAT_R8_SINT; 457 return VK_FORMAT_R8G8_SINT;
454 case Maxwell::VertexAttribute::Size::Size_8_8: 458 case Maxwell::VertexAttribute::Size::Size_8_8_8:
455 return VK_FORMAT_R8G8_SINT; 459 return VK_FORMAT_R8G8B8_SINT;
456 case Maxwell::VertexAttribute::Size::Size_8_8_8: 460 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
457 return VK_FORMAT_R8G8B8_SINT; 461 return VK_FORMAT_R8G8B8A8_SINT;
458 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 462 case Maxwell::VertexAttribute::Size::Size_16:
459 return VK_FORMAT_R8G8B8A8_SINT; 463 return VK_FORMAT_R16_SINT;
460 case Maxwell::VertexAttribute::Size::Size_16: 464 case Maxwell::VertexAttribute::Size::Size_16_16:
461 return VK_FORMAT_R16_SINT; 465 return VK_FORMAT_R16G16_SINT;
462 case Maxwell::VertexAttribute::Size::Size_16_16: 466 case Maxwell::VertexAttribute::Size::Size_16_16_16:
463 return VK_FORMAT_R16G16_SINT; 467 return VK_FORMAT_R16G16B16_SINT;
464 case Maxwell::VertexAttribute::Size::Size_16_16_16: 468 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
465 return VK_FORMAT_R16G16B16_SINT; 469 return VK_FORMAT_R16G16B16A16_SINT;
466 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 470 case Maxwell::VertexAttribute::Size::Size_32:
467 return VK_FORMAT_R16G16B16A16_SINT; 471 return VK_FORMAT_R32_SINT;
468 case Maxwell::VertexAttribute::Size::Size_32: 472 case Maxwell::VertexAttribute::Size::Size_32_32:
469 return VK_FORMAT_R32_SINT; 473 return VK_FORMAT_R32G32_SINT;
470 case Maxwell::VertexAttribute::Size::Size_32_32: 474 case Maxwell::VertexAttribute::Size::Size_32_32_32:
471 return VK_FORMAT_R32G32_SINT; 475 return VK_FORMAT_R32G32B32_SINT;
472 case Maxwell::VertexAttribute::Size::Size_32_32_32: 476 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
473 return VK_FORMAT_R32G32B32_SINT; 477 return VK_FORMAT_R32G32B32A32_SINT;
474 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 478 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
475 return VK_FORMAT_R32G32B32A32_SINT; 479 return VK_FORMAT_A2B10G10R10_SINT_PACK32;
476 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 480 default:
477 return VK_FORMAT_A2B10G10R10_SINT_PACK32; 481 break;
478 default: 482 }
479 break; 483 break;
480 } 484 case Maxwell::VertexAttribute::Type::Float:
481 break; 485 switch (size) {
482 case Maxwell::VertexAttribute::Type::Float: 486 case Maxwell::VertexAttribute::Size::Size_16:
483 switch (size) { 487 return VK_FORMAT_R16_SFLOAT;
484 case Maxwell::VertexAttribute::Size::Size_16: 488 case Maxwell::VertexAttribute::Size::Size_16_16:
485 return VK_FORMAT_R16_SFLOAT; 489 return VK_FORMAT_R16G16_SFLOAT;
486 case Maxwell::VertexAttribute::Size::Size_16_16: 490 case Maxwell::VertexAttribute::Size::Size_16_16_16:
487 return VK_FORMAT_R16G16_SFLOAT; 491 return VK_FORMAT_R16G16B16_SFLOAT;
488 case Maxwell::VertexAttribute::Size::Size_16_16_16: 492 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
489 return VK_FORMAT_R16G16B16_SFLOAT; 493 return VK_FORMAT_R16G16B16A16_SFLOAT;
490 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 494 case Maxwell::VertexAttribute::Size::Size_32:
491 return VK_FORMAT_R16G16B16A16_SFLOAT; 495 return VK_FORMAT_R32_SFLOAT;
492 case Maxwell::VertexAttribute::Size::Size_32: 496 case Maxwell::VertexAttribute::Size::Size_32_32:
493 return VK_FORMAT_R32_SFLOAT; 497 return VK_FORMAT_R32G32_SFLOAT;
494 case Maxwell::VertexAttribute::Size::Size_32_32: 498 case Maxwell::VertexAttribute::Size::Size_32_32_32:
495 return VK_FORMAT_R32G32_SFLOAT; 499 return VK_FORMAT_R32G32B32_SFLOAT;
496 case Maxwell::VertexAttribute::Size::Size_32_32_32: 500 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
497 return VK_FORMAT_R32G32B32_SFLOAT; 501 return VK_FORMAT_R32G32B32A32_SFLOAT;
498 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 502 case Maxwell::VertexAttribute::Size::Size_11_11_10:
499 return VK_FORMAT_R32G32B32A32_SFLOAT; 503 return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
500 case Maxwell::VertexAttribute::Size::Size_11_11_10: 504 default:
501 return VK_FORMAT_B10G11R11_UFLOAT_PACK32; 505 break;
502 default: 506 }
503 break; 507 break;
504 } 508 }
505 break; 509 return VK_FORMAT_UNDEFINED;
510 })()};
511
512 if (format == VK_FORMAT_UNDEFINED) {
513 UNIMPLEMENTED_MSG("Unimplemented vertex format of type={} and size={}", type, size);
506 } 514 }
507 UNIMPLEMENTED_MSG("Unimplemented vertex format of type={} and size={}", type, size); 515
508 return {}; 516 return device.GetSupportedFormat(format, VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT,
517 FormatType::Buffer);
509} 518}
510 519
511VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison) { 520VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison) {
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.h b/src/video_core/renderer_vulkan/maxwell_to_vk.h
index 9edd6af6a..356d46292 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.h
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.h
@@ -48,7 +48,8 @@ VkShaderStageFlagBits ShaderStage(Shader::Stage stage);
48 48
49VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology); 49VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology);
50 50
51VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size); 51VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
52 Maxwell::VertexAttribute::Size size);
52 53
53VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison); 54VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison);
54 55
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 4a1d96322..444c29f68 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{
@@ -1404,12 +1402,15 @@ void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig&
1404 break; 1402 break;
1405 } 1403 }
1406 1404
1407 UNIMPLEMENTED_IF(framebuffer_crop_rect.top != 0);
1408 UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0); 1405 UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0);
1409 1406
1407 f32 left_start{};
1408 if (framebuffer_crop_rect.Top() > 0) {
1409 left_start = static_cast<f32>(framebuffer_crop_rect.Top()) /
1410 static_cast<f32>(framebuffer_crop_rect.Bottom());
1411 }
1410 f32 scale_u = static_cast<f32>(framebuffer.width) / static_cast<f32>(screen_info.width); 1412 f32 scale_u = static_cast<f32>(framebuffer.width) / static_cast<f32>(screen_info.width);
1411 f32 scale_v = static_cast<f32>(framebuffer.height) / static_cast<f32>(screen_info.height); 1413 f32 scale_v = static_cast<f32>(framebuffer.height) / static_cast<f32>(screen_info.height);
1412
1413 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering 1414 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
1414 // (e.g. handheld mode) on a 1920x1080 framebuffer. 1415 // (e.g. handheld mode) on a 1920x1080 framebuffer.
1415 if (!fsr) { 1416 if (!fsr) {
@@ -1428,10 +1429,13 @@ void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig&
1428 const auto y = static_cast<f32>(screen.top); 1429 const auto y = static_cast<f32>(screen.top);
1429 const auto w = static_cast<f32>(screen.GetWidth()); 1430 const auto w = static_cast<f32>(screen.GetWidth());
1430 const auto h = static_cast<f32>(screen.GetHeight()); 1431 const auto h = static_cast<f32>(screen.GetHeight());
1431 data.vertices[0] = ScreenRectVertex(x, y, texcoords.top * scale_u, left * scale_v); 1432 data.vertices[0] = ScreenRectVertex(x, y, texcoords.top * scale_u, left_start + left * scale_v);
1432 data.vertices[1] = ScreenRectVertex(x + w, y, texcoords.bottom * scale_u, left * scale_v); 1433 data.vertices[1] =
1433 data.vertices[2] = ScreenRectVertex(x, y + h, texcoords.top * scale_u, right * scale_v); 1434 ScreenRectVertex(x + w, y, texcoords.bottom * scale_u, left_start + left * scale_v);
1434 data.vertices[3] = ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v); 1435 data.vertices[2] =
1436 ScreenRectVertex(x, y + h, texcoords.top * scale_u, left_start + right * scale_v);
1437 data.vertices[3] =
1438 ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, left_start + right * scale_v);
1435} 1439}
1436 1440
1437void BlitScreen::CreateFSR() { 1441void BlitScreen::CreateFSR() {
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 682f05335..5aca8f038 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -559,7 +559,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
559 vertex_attributes.push_back({ 559 vertex_attributes.push_back({
560 .location = static_cast<u32>(index), 560 .location = static_cast<u32>(index),
561 .binding = attribute.buffer, 561 .binding = attribute.buffer,
562 .format = MaxwellToVK::VertexFormat(attribute.Type(), attribute.Size()), 562 .format = MaxwellToVK::VertexFormat(device, attribute.Type(), attribute.Size()),
563 .offset = attribute.offset, 563 .offset = attribute.offset,
564 }); 564 });
565 } 565 }
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 09e035799..43cc94fab 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -452,7 +452,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
452 state.has_loaded = true; 452 state.has_loaded = true;
453 lock.unlock(); 453 lock.unlock();
454 454
455 workers.WaitForRequests(); 455 workers.WaitForRequests(stop_loading);
456 456
457 if (state.statistics) { 457 if (state.statistics) {
458 state.statistics->Report(); 458 state.statistics->Report();
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 10f9fe7fe..16e46d3e5 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -939,7 +939,7 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
939 .pNext = nullptr, 939 .pNext = nullptr,
940 .location = static_cast<u32>(index), 940 .location = static_cast<u32>(index),
941 .binding = binding, 941 .binding = binding,
942 .format = MaxwellToVK::VertexFormat(attribute.type, attribute.size), 942 .format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size),
943 .offset = attribute.offset, 943 .offset = attribute.offset,
944 }); 944 });
945 } 945 }
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/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp
index 4b1101f7c..164e4ee0e 100644
--- a/src/video_core/shader_cache.cpp
+++ b/src/video_core/shader_cache.cpp
@@ -123,8 +123,8 @@ void ShaderCache::Register(std::unique_ptr<ShaderInfo> data, VAddr addr, size_t
123 const VAddr addr_end = addr + size; 123 const VAddr addr_end = addr + size;
124 Entry* const entry = NewEntry(addr, addr_end, data.get()); 124 Entry* const entry = NewEntry(addr, addr_end, data.get());
125 125
126 const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS; 126 const u64 page_end = (addr_end + YUZU_PAGESIZE - 1) >> YUZU_PAGEBITS;
127 for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) { 127 for (u64 page = addr >> YUZU_PAGEBITS; page < page_end; ++page) {
128 invalidation_cache[page].push_back(entry); 128 invalidation_cache[page].push_back(entry);
129 } 129 }
130 130
@@ -135,8 +135,8 @@ void ShaderCache::Register(std::unique_ptr<ShaderInfo> data, VAddr addr, size_t
135 135
136void ShaderCache::InvalidatePagesInRegion(VAddr addr, size_t size) { 136void ShaderCache::InvalidatePagesInRegion(VAddr addr, size_t size) {
137 const VAddr addr_end = addr + size; 137 const VAddr addr_end = addr + size;
138 const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS; 138 const u64 page_end = (addr_end + YUZU_PAGESIZE - 1) >> YUZU_PAGEBITS;
139 for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) { 139 for (u64 page = addr >> YUZU_PAGEBITS; page < page_end; ++page) {
140 auto it = invalidation_cache.find(page); 140 auto it = invalidation_cache.find(page);
141 if (it == invalidation_cache.end()) { 141 if (it == invalidation_cache.end()) {
142 continue; 142 continue;
@@ -189,8 +189,8 @@ void ShaderCache::InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr
189} 189}
190 190
191void ShaderCache::RemoveEntryFromInvalidationCache(const Entry* entry) { 191void ShaderCache::RemoveEntryFromInvalidationCache(const Entry* entry) {
192 const u64 page_end = (entry->addr_end + PAGE_SIZE - 1) >> PAGE_BITS; 192 const u64 page_end = (entry->addr_end + YUZU_PAGESIZE - 1) >> YUZU_PAGEBITS;
193 for (u64 page = entry->addr_start >> PAGE_BITS; page < page_end; ++page) { 193 for (u64 page = entry->addr_start >> YUZU_PAGEBITS; page < page_end; ++page) {
194 const auto entries_it = invalidation_cache.find(page); 194 const auto entries_it = invalidation_cache.find(page);
195 ASSERT(entries_it != invalidation_cache.end()); 195 ASSERT(entries_it != invalidation_cache.end());
196 std::vector<Entry*>& entries = entries_it->second; 196 std::vector<Entry*>& entries = entries_it->second;
diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h
index 1109cfe83..f67cea8c4 100644
--- a/src/video_core/shader_cache.h
+++ b/src/video_core/shader_cache.h
@@ -29,8 +29,8 @@ struct ShaderInfo {
29}; 29};
30 30
31class ShaderCache { 31class ShaderCache {
32 static constexpr u64 PAGE_BITS = 14; 32 static constexpr u64 YUZU_PAGEBITS = 14;
33 static constexpr u64 PAGE_SIZE = u64(1) << PAGE_BITS; 33 static constexpr u64 YUZU_PAGESIZE = u64(1) << YUZU_PAGEBITS;
34 34
35 static constexpr size_t NUM_PROGRAMS = 6; 35 static constexpr size_t NUM_PROGRAMS = 6;
36 36
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index cf3ca06a6..1dbe01bc0 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -589,7 +589,7 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
589template <class P> 589template <class P>
590typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_addr) { 590typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_addr) {
591 // TODO: Properly implement this 591 // TODO: Properly implement this
592 const auto it = page_table.find(cpu_addr >> PAGE_BITS); 592 const auto it = page_table.find(cpu_addr >> YUZU_PAGEBITS);
593 if (it == page_table.end()) { 593 if (it == page_table.end()) {
594 return nullptr; 594 return nullptr;
595 } 595 }
@@ -1485,14 +1485,14 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1485 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>>& selected_page_table) { 1485 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>>& selected_page_table) {
1486 const auto page_it = selected_page_table.find(page); 1486 const auto page_it = selected_page_table.find(page);
1487 if (page_it == selected_page_table.end()) { 1487 if (page_it == selected_page_table.end()) {
1488 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << PAGE_BITS); 1488 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS);
1489 return; 1489 return;
1490 } 1490 }
1491 std::vector<ImageId>& image_ids = page_it->second; 1491 std::vector<ImageId>& image_ids = page_it->second;
1492 const auto vector_it = std::ranges::find(image_ids, image_id); 1492 const auto vector_it = std::ranges::find(image_ids, image_id);
1493 if (vector_it == image_ids.end()) { 1493 if (vector_it == image_ids.end()) {
1494 ASSERT_MSG(false, "Unregistering unregistered image in page=0x{:x}", 1494 ASSERT_MSG(false, "Unregistering unregistered image in page=0x{:x}",
1495 page << PAGE_BITS); 1495 page << YUZU_PAGEBITS);
1496 return; 1496 return;
1497 } 1497 }
1498 image_ids.erase(vector_it); 1498 image_ids.erase(vector_it);
@@ -1504,14 +1504,14 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1504 ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) { 1504 ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) {
1505 const auto page_it = page_table.find(page); 1505 const auto page_it = page_table.find(page);
1506 if (page_it == page_table.end()) { 1506 if (page_it == page_table.end()) {
1507 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << PAGE_BITS); 1507 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS);
1508 return; 1508 return;
1509 } 1509 }
1510 std::vector<ImageMapId>& image_map_ids = page_it->second; 1510 std::vector<ImageMapId>& image_map_ids = page_it->second;
1511 const auto vector_it = std::ranges::find(image_map_ids, map_id); 1511 const auto vector_it = std::ranges::find(image_map_ids, map_id);
1512 if (vector_it == image_map_ids.end()) { 1512 if (vector_it == image_map_ids.end()) {
1513 ASSERT_MSG(false, "Unregistering unregistered image in page=0x{:x}", 1513 ASSERT_MSG(false, "Unregistering unregistered image in page=0x{:x}",
1514 page << PAGE_BITS); 1514 page << YUZU_PAGEBITS);
1515 return; 1515 return;
1516 } 1516 }
1517 image_map_ids.erase(vector_it); 1517 image_map_ids.erase(vector_it);
@@ -1532,7 +1532,7 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1532 ForEachCPUPage(cpu_addr, size, [this, image_id](u64 page) { 1532 ForEachCPUPage(cpu_addr, size, [this, image_id](u64 page) {
1533 const auto page_it = page_table.find(page); 1533 const auto page_it = page_table.find(page);
1534 if (page_it == page_table.end()) { 1534 if (page_it == page_table.end()) {
1535 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << PAGE_BITS); 1535 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS);
1536 return; 1536 return;
1537 } 1537 }
1538 std::vector<ImageMapId>& image_map_ids = page_it->second; 1538 std::vector<ImageMapId>& image_map_ids = page_it->second;
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index e2f8f84c9..7e6c6cef2 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -47,7 +47,7 @@ struct ImageViewInOut {
47template <class P> 47template <class P>
48class TextureCache { 48class TextureCache {
49 /// Address shift for caching images into a hash table 49 /// Address shift for caching images into a hash table
50 static constexpr u64 PAGE_BITS = 20; 50 static constexpr u64 YUZU_PAGEBITS = 20;
51 51
52 /// Enables debugging features to the texture cache 52 /// Enables debugging features to the texture cache
53 static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION; 53 static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION;
@@ -178,8 +178,8 @@ private:
178 template <typename Func> 178 template <typename Func>
179 static void ForEachCPUPage(VAddr addr, size_t size, Func&& func) { 179 static void ForEachCPUPage(VAddr addr, size_t size, Func&& func) {
180 static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>; 180 static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>;
181 const u64 page_end = (addr + size - 1) >> PAGE_BITS; 181 const u64 page_end = (addr + size - 1) >> YUZU_PAGEBITS;
182 for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) { 182 for (u64 page = addr >> YUZU_PAGEBITS; page <= page_end; ++page) {
183 if constexpr (RETURNS_BOOL) { 183 if constexpr (RETURNS_BOOL) {
184 if (func(page)) { 184 if (func(page)) {
185 break; 185 break;
@@ -193,8 +193,8 @@ private:
193 template <typename Func> 193 template <typename Func>
194 static void ForEachGPUPage(GPUVAddr addr, size_t size, Func&& func) { 194 static void ForEachGPUPage(GPUVAddr addr, size_t size, Func&& func) {
195 static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>; 195 static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>;
196 const u64 page_end = (addr + size - 1) >> PAGE_BITS; 196 const u64 page_end = (addr + size - 1) >> YUZU_PAGEBITS;
197 for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) { 197 for (u64 page = addr >> YUZU_PAGEBITS; page <= page_end; ++page) {
198 if constexpr (RETURNS_BOOL) { 198 if constexpr (RETURNS_BOOL) {
199 if (func(page)) { 199 if (func(page)) {
200 break; 200 break;
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 9b6b8527b..913f8ebcb 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -15,6 +15,24 @@
15 15
16namespace Tegra::Texture { 16namespace Tegra::Texture {
17namespace { 17namespace {
18template <u32 mask>
19constexpr u32 pdep(u32 value) {
20 u32 result = 0;
21 u32 m = mask;
22 for (u32 bit = 1; m; bit += bit) {
23 if (value & bit)
24 result |= m & -m;
25 m &= m - 1;
26 }
27 return result;
28}
29
30template <u32 mask, u32 incr_amount>
31void incrpdep(u32& value) {
32 constexpr u32 swizzled_incr = pdep<mask>(incr_amount);
33 value = ((value | ~mask) + swizzled_incr) & mask;
34}
35
18template <bool TO_LINEAR, u32 BYTES_PER_PIXEL> 36template <bool TO_LINEAR, u32 BYTES_PER_PIXEL>
19void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height, u32 depth, 37void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height, u32 depth,
20 u32 block_height, u32 block_depth, u32 stride_alignment) { 38 u32 block_height, u32 block_depth, u32 stride_alignment) {
@@ -44,18 +62,20 @@ void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32
44 ((z & block_depth_mask) << (GOB_SIZE_SHIFT + block_height)); 62 ((z & block_depth_mask) << (GOB_SIZE_SHIFT + block_height));
45 for (u32 line = 0; line < height; ++line) { 63 for (u32 line = 0; line < height; ++line) {
46 const u32 y = line + origin_y; 64 const u32 y = line + origin_y;
47 const auto& table = SWIZZLE_TABLE[y % GOB_SIZE_Y]; 65 const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(y);
48 66
49 const u32 block_y = y >> GOB_SIZE_Y_SHIFT; 67 const u32 block_y = y >> GOB_SIZE_Y_SHIFT;
50 const u32 offset_y = (block_y >> block_height) * block_size + 68 const u32 offset_y = (block_y >> block_height) * block_size +
51 ((block_y & block_height_mask) << GOB_SIZE_SHIFT); 69 ((block_y & block_height_mask) << GOB_SIZE_SHIFT);
52 70
53 for (u32 column = 0; column < width; ++column) { 71 u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL);
72 for (u32 column = 0; column < width;
73 ++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) {
54 const u32 x = (column + origin_x) * BYTES_PER_PIXEL; 74 const u32 x = (column + origin_x) * BYTES_PER_PIXEL;
55 const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift; 75 const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift;
56 76
57 const u32 base_swizzled_offset = offset_z + offset_y + offset_x; 77 const u32 base_swizzled_offset = offset_z + offset_y + offset_x;
58 const u32 swizzled_offset = base_swizzled_offset + table[x % GOB_SIZE_X]; 78 const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y);
59 79
60 const u32 unswizzled_offset = 80 const u32 unswizzled_offset =
61 slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL; 81 slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
@@ -103,12 +123,15 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32
103 const u32 gob_address_y = 123 const u32 gob_address_y =
104 (dst_y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs + 124 (dst_y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs +
105 ((dst_y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE; 125 ((dst_y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE;
106 const auto& table = SWIZZLE_TABLE[dst_y % GOB_SIZE_Y]; 126
107 for (u32 x = 0; x < subrect_width; ++x) { 127 const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(dst_y);
128 u32 swizzled_x = pdep<SWIZZLE_X_BITS>(offset_x * BYTES_PER_PIXEL);
129 for (u32 x = 0; x < subrect_width;
130 ++x, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) {
108 const u32 dst_x = x + offset_x; 131 const u32 dst_x = x + offset_x;
109 const u32 gob_address = 132 const u32 gob_address =
110 gob_address_y + (dst_x * BYTES_PER_PIXEL / GOB_SIZE_X) * GOB_SIZE * block_height; 133 gob_address_y + (dst_x * BYTES_PER_PIXEL / GOB_SIZE_X) * GOB_SIZE * block_height;
111 const u32 swizzled_offset = gob_address + table[(dst_x * BYTES_PER_PIXEL) % GOB_SIZE_X]; 134 const u32 swizzled_offset = gob_address + (swizzled_x | swizzled_y);
112 const u32 unswizzled_offset = line * source_pitch + x * BYTES_PER_PIXEL; 135 const u32 unswizzled_offset = line * source_pitch + x * BYTES_PER_PIXEL;
113 136
114 const u8* const source_line = unswizzled_data + unswizzled_offset; 137 const u8* const source_line = unswizzled_data + unswizzled_offset;
@@ -130,16 +153,19 @@ void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width,
130 153
131 for (u32 line = 0; line < line_count; ++line) { 154 for (u32 line = 0; line < line_count; ++line) {
132 const u32 src_y = line + origin_y; 155 const u32 src_y = line + origin_y;
133 const auto& table = SWIZZLE_TABLE[src_y % GOB_SIZE_Y]; 156 const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(src_y);
134 157
135 const u32 block_y = src_y >> GOB_SIZE_Y_SHIFT; 158 const u32 block_y = src_y >> GOB_SIZE_Y_SHIFT;
136 const u32 src_offset_y = (block_y >> block_height) * block_size + 159 const u32 src_offset_y = (block_y >> block_height) * block_size +
137 ((block_y & block_height_mask) << GOB_SIZE_SHIFT); 160 ((block_y & block_height_mask) << GOB_SIZE_SHIFT);
138 for (u32 column = 0; column < line_length_in; ++column) { 161
162 u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL);
163 for (u32 column = 0; column < line_length_in;
164 ++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) {
139 const u32 src_x = (column + origin_x) * BYTES_PER_PIXEL; 165 const u32 src_x = (column + origin_x) * BYTES_PER_PIXEL;
140 const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift; 166 const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift;
141 167
142 const u32 swizzled_offset = src_offset_y + src_offset_x + table[src_x % GOB_SIZE_X]; 168 const u32 swizzled_offset = src_offset_y + src_offset_x + (swizzled_x | swizzled_y);
143 const u32 unswizzled_offset = line * pitch + column * BYTES_PER_PIXEL; 169 const u32 unswizzled_offset = line * pitch + column * BYTES_PER_PIXEL;
144 170
145 std::memcpy(output + unswizzled_offset, input + swizzled_offset, BYTES_PER_PIXEL); 171 std::memcpy(output + unswizzled_offset, input + swizzled_offset, BYTES_PER_PIXEL);
@@ -162,13 +188,15 @@ void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 widt
162 const u32 x_shift = static_cast<u32>(GOB_SIZE_SHIFT) + block_height + block_depth; 188 const u32 x_shift = static_cast<u32>(GOB_SIZE_SHIFT) + block_height + block_depth;
163 189
164 for (u32 line = 0; line < line_count; ++line) { 190 for (u32 line = 0; line < line_count; ++line) {
165 const auto& table = SWIZZLE_TABLE[line % GOB_SIZE_Y]; 191 const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(line);
166 const u32 block_y = line / GOB_SIZE_Y; 192 const u32 block_y = line / GOB_SIZE_Y;
167 const u32 dst_offset_y = 193 const u32 dst_offset_y =
168 (block_y >> block_height) * block_size + (block_y & block_height_mask) * GOB_SIZE; 194 (block_y >> block_height) * block_size + (block_y & block_height_mask) * GOB_SIZE;
169 for (u32 x = 0; x < line_length_in; ++x) { 195
196 u32 swizzled_x = 0;
197 for (u32 x = 0; x < line_length_in; ++x, incrpdep<SWIZZLE_X_BITS, 1>(swizzled_x)) {
170 const u32 dst_offset = 198 const u32 dst_offset =
171 ((x / GOB_SIZE_X) << x_shift) + dst_offset_y + table[x % GOB_SIZE_X]; 199 ((x / GOB_SIZE_X) << x_shift) + dst_offset_y + (swizzled_x | swizzled_y);
172 const u32 src_offset = x * BYTES_PER_PIXEL + line * pitch; 200 const u32 src_offset = x * BYTES_PER_PIXEL + line * pitch;
173 std::memcpy(output + dst_offset, input + src_offset, BYTES_PER_PIXEL); 201 std::memcpy(output + dst_offset, input + src_offset, BYTES_PER_PIXEL);
174 } 202 }
@@ -267,11 +295,13 @@ void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32
267 const std::size_t gob_address_y = 295 const std::size_t gob_address_y =
268 (y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs + 296 (y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs +
269 ((y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE; 297 ((y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE;
270 const auto& table = SWIZZLE_TABLE[y % GOB_SIZE_Y]; 298 const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(static_cast<u32>(y));
271 for (std::size_t x = dst_x; x < width && count < copy_size; ++x) { 299 u32 swizzled_x = pdep<SWIZZLE_X_BITS>(dst_x);
300 for (std::size_t x = dst_x; x < width && count < copy_size;
301 ++x, incrpdep<SWIZZLE_X_BITS, 1>(swizzled_x)) {
272 const std::size_t gob_address = 302 const std::size_t gob_address =
273 gob_address_y + (x / GOB_SIZE_X) * GOB_SIZE * block_height; 303 gob_address_y + (x / GOB_SIZE_X) * GOB_SIZE * block_height;
274 const std::size_t swizzled_offset = gob_address + table[x % GOB_SIZE_X]; 304 const std::size_t swizzled_offset = gob_address + (swizzled_x | swizzled_y);
275 const u8* source_line = source_data + count; 305 const u8* source_line = source_data + count;
276 u8* dest_addr = swizzle_data + swizzled_offset; 306 u8* dest_addr = swizzle_data + swizzled_offset;
277 count++; 307 count++;
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 59dfd1621..31a11708f 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -20,6 +20,9 @@ constexpr u32 GOB_SIZE_Y_SHIFT = 3;
20constexpr u32 GOB_SIZE_Z_SHIFT = 0; 20constexpr u32 GOB_SIZE_Z_SHIFT = 0;
21constexpr u32 GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT; 21constexpr u32 GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT;
22 22
23constexpr u32 SWIZZLE_X_BITS = 0b100101111;
24constexpr u32 SWIZZLE_Y_BITS = 0b011010000;
25
23using SwizzleTable = std::array<std::array<u32, GOB_SIZE_X>, GOB_SIZE_Y>; 26using SwizzleTable = std::array<std::array<u32, GOB_SIZE_X>, GOB_SIZE_Y>;
24 27
25/** 28/**
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 743ac09f6..ddecfca13 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -50,6 +50,21 @@ constexpr std::array R4G4_UNORM_PACK8{
50 VK_FORMAT_UNDEFINED, 50 VK_FORMAT_UNDEFINED,
51}; 51};
52 52
53constexpr std::array R16G16B16_SFLOAT{
54 VK_FORMAT_R16G16B16A16_SFLOAT,
55 VK_FORMAT_UNDEFINED,
56};
57
58constexpr std::array R16G16B16_SSCALED{
59 VK_FORMAT_R16G16B16A16_SSCALED,
60 VK_FORMAT_UNDEFINED,
61};
62
63constexpr std::array R8G8B8_SSCALED{
64 VK_FORMAT_R8G8B8A8_SSCALED,
65 VK_FORMAT_UNDEFINED,
66};
67
53} // namespace Alternatives 68} // namespace Alternatives
54 69
55enum class NvidiaArchitecture { 70enum class NvidiaArchitecture {
@@ -102,6 +117,12 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
102 return Alternatives::B5G6R5_UNORM_PACK16.data(); 117 return Alternatives::B5G6R5_UNORM_PACK16.data();
103 case VK_FORMAT_R4G4_UNORM_PACK8: 118 case VK_FORMAT_R4G4_UNORM_PACK8:
104 return Alternatives::R4G4_UNORM_PACK8.data(); 119 return Alternatives::R4G4_UNORM_PACK8.data();
120 case VK_FORMAT_R16G16B16_SFLOAT:
121 return Alternatives::R16G16B16_SFLOAT.data();
122 case VK_FORMAT_R16G16B16_SSCALED:
123 return Alternatives::R16G16B16_SSCALED.data();
124 case VK_FORMAT_R8G8B8_SSCALED:
125 return Alternatives::R8G8B8_SSCALED.data();
105 default: 126 default:
106 return nullptr; 127 return nullptr;
107 } 128 }
@@ -122,109 +143,142 @@ VkFormatFeatureFlags GetFormatFeatures(VkFormatProperties properties, FormatType
122 143
123std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::PhysicalDevice physical) { 144std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::PhysicalDevice physical) {
124 static constexpr std::array formats{ 145 static constexpr std::array formats{
125 VK_FORMAT_A8B8G8R8_UNORM_PACK32, 146 VK_FORMAT_A1R5G5B5_UNORM_PACK16,
126 VK_FORMAT_A8B8G8R8_UINT_PACK32, 147 VK_FORMAT_A2B10G10R10_SINT_PACK32,
127 VK_FORMAT_A8B8G8R8_SNORM_PACK32, 148 VK_FORMAT_A2B10G10R10_SNORM_PACK32,
149 VK_FORMAT_A2B10G10R10_SSCALED_PACK32,
150 VK_FORMAT_A2B10G10R10_UINT_PACK32,
151 VK_FORMAT_A2B10G10R10_UNORM_PACK32,
152 VK_FORMAT_A2B10G10R10_USCALED_PACK32,
128 VK_FORMAT_A8B8G8R8_SINT_PACK32, 153 VK_FORMAT_A8B8G8R8_SINT_PACK32,
154 VK_FORMAT_A8B8G8R8_SNORM_PACK32,
129 VK_FORMAT_A8B8G8R8_SRGB_PACK32, 155 VK_FORMAT_A8B8G8R8_SRGB_PACK32,
130 VK_FORMAT_R5G6B5_UNORM_PACK16, 156 VK_FORMAT_A8B8G8R8_UINT_PACK32,
131 VK_FORMAT_B5G6R5_UNORM_PACK16, 157 VK_FORMAT_A8B8G8R8_UNORM_PACK32,
132 VK_FORMAT_R5G5B5A1_UNORM_PACK16, 158 VK_FORMAT_ASTC_10x10_SRGB_BLOCK,
159 VK_FORMAT_ASTC_10x10_UNORM_BLOCK,
160 VK_FORMAT_ASTC_10x5_SRGB_BLOCK,
161 VK_FORMAT_ASTC_10x5_UNORM_BLOCK,
162 VK_FORMAT_ASTC_10x6_SRGB_BLOCK,
163 VK_FORMAT_ASTC_10x6_UNORM_BLOCK,
164 VK_FORMAT_ASTC_10x8_SRGB_BLOCK,
165 VK_FORMAT_ASTC_10x8_UNORM_BLOCK,
166 VK_FORMAT_ASTC_12x10_SRGB_BLOCK,
167 VK_FORMAT_ASTC_12x10_UNORM_BLOCK,
168 VK_FORMAT_ASTC_12x12_SRGB_BLOCK,
169 VK_FORMAT_ASTC_12x12_UNORM_BLOCK,
170 VK_FORMAT_ASTC_4x4_SRGB_BLOCK,
171 VK_FORMAT_ASTC_4x4_UNORM_BLOCK,
172 VK_FORMAT_ASTC_5x4_SRGB_BLOCK,
173 VK_FORMAT_ASTC_5x4_UNORM_BLOCK,
174 VK_FORMAT_ASTC_5x5_SRGB_BLOCK,
175 VK_FORMAT_ASTC_5x5_UNORM_BLOCK,
176 VK_FORMAT_ASTC_6x5_SRGB_BLOCK,
177 VK_FORMAT_ASTC_6x5_UNORM_BLOCK,
178 VK_FORMAT_ASTC_6x6_SRGB_BLOCK,
179 VK_FORMAT_ASTC_6x6_UNORM_BLOCK,
180 VK_FORMAT_ASTC_8x5_SRGB_BLOCK,
181 VK_FORMAT_ASTC_8x5_UNORM_BLOCK,
182 VK_FORMAT_ASTC_8x6_SRGB_BLOCK,
183 VK_FORMAT_ASTC_8x6_UNORM_BLOCK,
184 VK_FORMAT_ASTC_8x8_SRGB_BLOCK,
185 VK_FORMAT_ASTC_8x8_UNORM_BLOCK,
186 VK_FORMAT_B10G11R11_UFLOAT_PACK32,
187 VK_FORMAT_B4G4R4A4_UNORM_PACK16,
133 VK_FORMAT_B5G5R5A1_UNORM_PACK16, 188 VK_FORMAT_B5G5R5A1_UNORM_PACK16,
134 VK_FORMAT_A2B10G10R10_UNORM_PACK32, 189 VK_FORMAT_B5G6R5_UNORM_PACK16,
135 VK_FORMAT_A2B10G10R10_UINT_PACK32, 190 VK_FORMAT_B8G8R8A8_SRGB,
136 VK_FORMAT_A1R5G5B5_UNORM_PACK16, 191 VK_FORMAT_B8G8R8A8_UNORM,
137 VK_FORMAT_R32G32B32A32_SFLOAT, 192 VK_FORMAT_BC1_RGBA_SRGB_BLOCK,
138 VK_FORMAT_R32G32B32A32_SINT, 193 VK_FORMAT_BC1_RGBA_UNORM_BLOCK,
139 VK_FORMAT_R32G32B32A32_UINT, 194 VK_FORMAT_BC2_SRGB_BLOCK,
140 VK_FORMAT_R32G32_SFLOAT, 195 VK_FORMAT_BC2_UNORM_BLOCK,
141 VK_FORMAT_R32G32_SINT, 196 VK_FORMAT_BC3_SRGB_BLOCK,
142 VK_FORMAT_R32G32_UINT, 197 VK_FORMAT_BC3_UNORM_BLOCK,
198 VK_FORMAT_BC4_SNORM_BLOCK,
199 VK_FORMAT_BC4_UNORM_BLOCK,
200 VK_FORMAT_BC5_SNORM_BLOCK,
201 VK_FORMAT_BC5_UNORM_BLOCK,
202 VK_FORMAT_BC6H_SFLOAT_BLOCK,
203 VK_FORMAT_BC6H_UFLOAT_BLOCK,
204 VK_FORMAT_BC7_SRGB_BLOCK,
205 VK_FORMAT_BC7_UNORM_BLOCK,
206 VK_FORMAT_D16_UNORM,
207 VK_FORMAT_D16_UNORM_S8_UINT,
208 VK_FORMAT_D24_UNORM_S8_UINT,
209 VK_FORMAT_D32_SFLOAT,
210 VK_FORMAT_D32_SFLOAT_S8_UINT,
211 VK_FORMAT_E5B9G9R9_UFLOAT_PACK32,
212 VK_FORMAT_R16G16B16A16_SFLOAT,
143 VK_FORMAT_R16G16B16A16_SINT, 213 VK_FORMAT_R16G16B16A16_SINT,
144 VK_FORMAT_R16G16B16A16_UINT,
145 VK_FORMAT_R16G16B16A16_SNORM, 214 VK_FORMAT_R16G16B16A16_SNORM,
215 VK_FORMAT_R16G16B16A16_SSCALED,
216 VK_FORMAT_R16G16B16A16_UINT,
146 VK_FORMAT_R16G16B16A16_UNORM, 217 VK_FORMAT_R16G16B16A16_UNORM,
147 VK_FORMAT_R16G16_UNORM, 218 VK_FORMAT_R16G16B16A16_USCALED,
148 VK_FORMAT_R16G16_SNORM, 219 VK_FORMAT_R16G16B16_SFLOAT,
220 VK_FORMAT_R16G16B16_SINT,
221 VK_FORMAT_R16G16B16_SNORM,
222 VK_FORMAT_R16G16B16_SSCALED,
223 VK_FORMAT_R16G16B16_UINT,
224 VK_FORMAT_R16G16B16_UNORM,
225 VK_FORMAT_R16G16B16_USCALED,
149 VK_FORMAT_R16G16_SFLOAT, 226 VK_FORMAT_R16G16_SFLOAT,
150 VK_FORMAT_R16G16_UINT,
151 VK_FORMAT_R16G16_SINT, 227 VK_FORMAT_R16G16_SINT,
152 VK_FORMAT_R16_UNORM, 228 VK_FORMAT_R16G16_SNORM,
229 VK_FORMAT_R16G16_SSCALED,
230 VK_FORMAT_R16G16_UINT,
231 VK_FORMAT_R16G16_UNORM,
232 VK_FORMAT_R16G16_USCALED,
233 VK_FORMAT_R16_SFLOAT,
234 VK_FORMAT_R16_SINT,
153 VK_FORMAT_R16_SNORM, 235 VK_FORMAT_R16_SNORM,
236 VK_FORMAT_R16_SSCALED,
154 VK_FORMAT_R16_UINT, 237 VK_FORMAT_R16_UINT,
238 VK_FORMAT_R16_UNORM,
239 VK_FORMAT_R16_USCALED,
240 VK_FORMAT_R32G32B32A32_SFLOAT,
241 VK_FORMAT_R32G32B32A32_SINT,
242 VK_FORMAT_R32G32B32A32_UINT,
243 VK_FORMAT_R32G32B32_SFLOAT,
244 VK_FORMAT_R32G32B32_SINT,
245 VK_FORMAT_R32G32B32_UINT,
246 VK_FORMAT_R32G32_SFLOAT,
247 VK_FORMAT_R32G32_SINT,
248 VK_FORMAT_R32G32_UINT,
249 VK_FORMAT_R32_SFLOAT,
250 VK_FORMAT_R32_SINT,
251 VK_FORMAT_R32_UINT,
252 VK_FORMAT_R4G4B4A4_UNORM_PACK16,
253 VK_FORMAT_R4G4_UNORM_PACK8,
254 VK_FORMAT_R5G5B5A1_UNORM_PACK16,
255 VK_FORMAT_R5G6B5_UNORM_PACK16,
256 VK_FORMAT_R8G8B8A8_SINT,
257 VK_FORMAT_R8G8B8A8_SNORM,
155 VK_FORMAT_R8G8B8A8_SRGB, 258 VK_FORMAT_R8G8B8A8_SRGB,
156 VK_FORMAT_R8G8_UNORM, 259 VK_FORMAT_R8G8B8A8_SSCALED,
157 VK_FORMAT_R8G8_SNORM, 260 VK_FORMAT_R8G8B8A8_UINT,
261 VK_FORMAT_R8G8B8A8_UNORM,
262 VK_FORMAT_R8G8B8A8_USCALED,
263 VK_FORMAT_R8G8B8_SINT,
264 VK_FORMAT_R8G8B8_SNORM,
265 VK_FORMAT_R8G8B8_SSCALED,
266 VK_FORMAT_R8G8B8_UINT,
267 VK_FORMAT_R8G8B8_UNORM,
268 VK_FORMAT_R8G8B8_USCALED,
158 VK_FORMAT_R8G8_SINT, 269 VK_FORMAT_R8G8_SINT,
270 VK_FORMAT_R8G8_SNORM,
271 VK_FORMAT_R8G8_SSCALED,
159 VK_FORMAT_R8G8_UINT, 272 VK_FORMAT_R8G8_UINT,
160 VK_FORMAT_R8_UNORM, 273 VK_FORMAT_R8G8_UNORM,
161 VK_FORMAT_R8_SNORM, 274 VK_FORMAT_R8G8_USCALED,
162 VK_FORMAT_R8_SINT, 275 VK_FORMAT_R8_SINT,
276 VK_FORMAT_R8_SNORM,
277 VK_FORMAT_R8_SSCALED,
163 VK_FORMAT_R8_UINT, 278 VK_FORMAT_R8_UINT,
164 VK_FORMAT_B10G11R11_UFLOAT_PACK32, 279 VK_FORMAT_R8_UNORM,
165 VK_FORMAT_R32_SFLOAT, 280 VK_FORMAT_R8_USCALED,
166 VK_FORMAT_R32_UINT,
167 VK_FORMAT_R32_SINT,
168 VK_FORMAT_R16_SFLOAT,
169 VK_FORMAT_R16G16B16A16_SFLOAT,
170 VK_FORMAT_B8G8R8A8_UNORM,
171 VK_FORMAT_B8G8R8A8_SRGB,
172 VK_FORMAT_R4G4_UNORM_PACK8,
173 VK_FORMAT_R4G4B4A4_UNORM_PACK16,
174 VK_FORMAT_B4G4R4A4_UNORM_PACK16,
175 VK_FORMAT_D32_SFLOAT,
176 VK_FORMAT_D16_UNORM,
177 VK_FORMAT_S8_UINT, 281 VK_FORMAT_S8_UINT,
178 VK_FORMAT_D16_UNORM_S8_UINT,
179 VK_FORMAT_D24_UNORM_S8_UINT,
180 VK_FORMAT_D32_SFLOAT_S8_UINT,
181 VK_FORMAT_BC1_RGBA_UNORM_BLOCK,
182 VK_FORMAT_BC2_UNORM_BLOCK,
183 VK_FORMAT_BC3_UNORM_BLOCK,
184 VK_FORMAT_BC4_UNORM_BLOCK,
185 VK_FORMAT_BC4_SNORM_BLOCK,
186 VK_FORMAT_BC5_UNORM_BLOCK,
187 VK_FORMAT_BC5_SNORM_BLOCK,
188 VK_FORMAT_BC7_UNORM_BLOCK,
189 VK_FORMAT_BC6H_UFLOAT_BLOCK,
190 VK_FORMAT_BC6H_SFLOAT_BLOCK,
191 VK_FORMAT_BC1_RGBA_SRGB_BLOCK,
192 VK_FORMAT_BC2_SRGB_BLOCK,
193 VK_FORMAT_BC3_SRGB_BLOCK,
194 VK_FORMAT_BC7_SRGB_BLOCK,
195 VK_FORMAT_ASTC_4x4_UNORM_BLOCK,
196 VK_FORMAT_ASTC_4x4_SRGB_BLOCK,
197 VK_FORMAT_ASTC_5x4_UNORM_BLOCK,
198 VK_FORMAT_ASTC_5x4_SRGB_BLOCK,
199 VK_FORMAT_ASTC_5x5_UNORM_BLOCK,
200 VK_FORMAT_ASTC_5x5_SRGB_BLOCK,
201 VK_FORMAT_ASTC_6x5_UNORM_BLOCK,
202 VK_FORMAT_ASTC_6x5_SRGB_BLOCK,
203 VK_FORMAT_ASTC_6x6_UNORM_BLOCK,
204 VK_FORMAT_ASTC_6x6_SRGB_BLOCK,
205 VK_FORMAT_ASTC_8x5_UNORM_BLOCK,
206 VK_FORMAT_ASTC_8x5_SRGB_BLOCK,
207 VK_FORMAT_ASTC_8x6_UNORM_BLOCK,
208 VK_FORMAT_ASTC_8x6_SRGB_BLOCK,
209 VK_FORMAT_ASTC_8x8_UNORM_BLOCK,
210 VK_FORMAT_ASTC_8x8_SRGB_BLOCK,
211 VK_FORMAT_ASTC_10x5_UNORM_BLOCK,
212 VK_FORMAT_ASTC_10x5_SRGB_BLOCK,
213 VK_FORMAT_ASTC_10x6_UNORM_BLOCK,
214 VK_FORMAT_ASTC_10x6_SRGB_BLOCK,
215 VK_FORMAT_ASTC_10x8_UNORM_BLOCK,
216 VK_FORMAT_ASTC_10x8_SRGB_BLOCK,
217 VK_FORMAT_ASTC_10x10_UNORM_BLOCK,
218 VK_FORMAT_ASTC_10x10_SRGB_BLOCK,
219 VK_FORMAT_ASTC_12x10_UNORM_BLOCK,
220 VK_FORMAT_ASTC_12x10_SRGB_BLOCK,
221 VK_FORMAT_ASTC_12x12_UNORM_BLOCK,
222 VK_FORMAT_ASTC_12x12_SRGB_BLOCK,
223 VK_FORMAT_ASTC_8x6_UNORM_BLOCK,
224 VK_FORMAT_ASTC_8x6_SRGB_BLOCK,
225 VK_FORMAT_ASTC_6x5_UNORM_BLOCK,
226 VK_FORMAT_ASTC_6x5_SRGB_BLOCK,
227 VK_FORMAT_E5B9G9R9_UFLOAT_PACK32,
228 }; 282 };
229 std::unordered_map<VkFormat, VkFormatProperties> format_properties; 283 std::unordered_map<VkFormat, VkFormatProperties> format_properties;
230 for (const auto format : formats) { 284 for (const auto format : formats) {
@@ -739,9 +793,9 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
739 if (!IsFormatSupported(alternative, wanted_usage, format_type)) { 793 if (!IsFormatSupported(alternative, wanted_usage, format_type)) {
740 continue; 794 continue;
741 } 795 }
742 LOG_WARNING(Render_Vulkan, 796 LOG_DEBUG(Render_Vulkan,
743 "Emulating format={} with alternative format={} with usage={} and type={}", 797 "Emulating format={} with alternative format={} with usage={} and type={}",
744 wanted_format, alternative, wanted_usage, format_type); 798 wanted_format, alternative, wanted_usage, format_type);
745 return alternative; 799 return alternative;
746 } 800 }
747 801
diff --git a/src/web_service/verify_user_jwt.cpp b/src/web_service/verify_user_jwt.cpp
index 3bff46f0a..129eb1968 100644
--- a/src/web_service/verify_user_jwt.cpp
+++ b/src/web_service/verify_user_jwt.cpp
@@ -39,8 +39,10 @@ Network::VerifyUser::UserData VerifyUserJWT::LoadUserData(const std::string& ver
39 const std::string audience = fmt::format("external-{}", verify_uid); 39 const std::string audience = fmt::format("external-{}", verify_uid);
40 using namespace jwt::params; 40 using namespace jwt::params;
41 std::error_code error; 41 std::error_code error;
42
43 // We use the Citra backend so the issuer is citra-core
42 auto decoded = 44 auto decoded =
43 jwt::decode(token, algorithms({"rs256"}), error, secret(pub_key), issuer("yuzu-core"), 45 jwt::decode(token, algorithms({"rs256"}), error, secret(pub_key), issuer("citra-core"),
44 aud(audience), validate_iat(true), validate_jti(true)); 46 aud(audience), validate_iat(true), validate_jti(true));
45 if (error) { 47 if (error) {
46 LOG_INFO(WebService, "Verification failed: category={}, code={}, message={}", 48 LOG_INFO(WebService, "Verification failed: category={}, code={}, message={}",
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/aboutdialog.ui b/src/yuzu/aboutdialog.ui
index c4ffb293e..aea82809d 100644
--- a/src/yuzu/aboutdialog.ui
+++ b/src/yuzu/aboutdialog.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>616</width> 9 <width>616</width>
10 <height>261</height> 10 <height>294</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -165,6 +165,7 @@ p, li { white-space: pre-wrap; }
165 </widget> 165 </widget>
166 <resources> 166 <resources>
167 <include location="../../dist/qt_themes_default/default/default.qrc"/> 167 <include location="../../dist/qt_themes_default/default/default.qrc"/>
168 <include location="../../dist/qt_themes/default/default.qrc"/>
168 </resources> 169 </resources>
169 <connections> 170 <connections>
170 <connection> 171 <connection>
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/config.cpp b/src/yuzu/configuration/config.cpp
index 58f1239bf..da6e5aa88 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -73,7 +73,7 @@ const std::array<int, 2> Config::default_ringcon_analogs{{
73const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ 73const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
74 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, 74 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}},
75 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, 75 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}},
76 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, 76 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}},
77 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, 77 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}},
78 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}}, 78 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}},
79 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, 79 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}},
diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui
index a5bcee415..6034d8581 100644
--- a/src/yuzu/configuration/configure_audio.ui
+++ b/src/yuzu/configuration/configure_audio.ui
@@ -120,10 +120,10 @@
120 </sizepolicy> 120 </sizepolicy>
121 </property> 121 </property>
122 <property name="maximum"> 122 <property name="maximum">
123 <number>100</number> 123 <number>200</number>
124 </property> 124 </property>
125 <property name="pageStep"> 125 <property name="pageStep">
126 <number>10</number> 126 <number>5</number>
127 </property> 127 </property>
128 <property name="orientation"> 128 <property name="orientation">
129 <enum>Qt::Horizontal</enum> 129 <enum>Qt::Horizontal</enum>
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_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 00bee85b2..109689c88 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -1475,7 +1475,7 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
1475 1475
1476void ConfigureInputPlayer::CreateProfile() { 1476void ConfigureInputPlayer::CreateProfile() {
1477 const auto profile_name = 1477 const auto profile_name =
1478 LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 20, 1478 LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 30,
1479 LimitableInputDialog::InputLimiter::Filesystem); 1479 LimitableInputDialog::InputLimiter::Filesystem);
1480 1480
1481 if (profile_name.isEmpty()) { 1481 if (profile_name.isEmpty()) {
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/game_list.cpp b/src/yuzu/game_list.cpp
index 041e6ac11..b127badc2 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -126,10 +126,8 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
126 layout_filter = new QHBoxLayout; 126 layout_filter = new QHBoxLayout;
127 layout_filter->setContentsMargins(8, 8, 8, 8); 127 layout_filter->setContentsMargins(8, 8, 8, 8);
128 label_filter = new QLabel; 128 label_filter = new QLabel;
129 label_filter->setText(tr("Filter:"));
130 edit_filter = new QLineEdit; 129 edit_filter = new QLineEdit;
131 edit_filter->clear(); 130 edit_filter->clear();
132 edit_filter->setPlaceholderText(tr("Enter pattern to filter"));
133 edit_filter->installEventFilter(key_release_eater); 131 edit_filter->installEventFilter(key_release_eater);
134 edit_filter->setClearButtonEnabled(true); 132 edit_filter->setClearButtonEnabled(true);
135 connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::OnTextChanged); 133 connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::OnTextChanged);
@@ -149,6 +147,7 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
149 layout_filter->addWidget(label_filter_result); 147 layout_filter->addWidget(label_filter_result);
150 layout_filter->addWidget(button_filter_close); 148 layout_filter->addWidget(button_filter_close);
151 setLayout(layout_filter); 149 setLayout(layout_filter);
150 RetranslateUI();
152} 151}
153 152
154/** 153/**
@@ -286,7 +285,7 @@ void GameList::OnUpdateThemedIcons() {
286 } 285 }
287 case GameListItemType::AddDir: 286 case GameListItemType::AddDir:
288 child->setData( 287 child->setData(
289 QIcon::fromTheme(QStringLiteral("plus")) 288 QIcon::fromTheme(QStringLiteral("list-add"))
290 .pixmap(icon_size) 289 .pixmap(icon_size)
291 .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), 290 .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
292 Qt::DecorationRole); 291 Qt::DecorationRole);
@@ -333,13 +332,9 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
333 tree_view->setStyleSheet(QStringLiteral("QTreeView{ border: none; }")); 332 tree_view->setStyleSheet(QStringLiteral("QTreeView{ border: none; }"));
334 333
335 item_model->insertColumns(0, COLUMN_COUNT); 334 item_model->insertColumns(0, COLUMN_COUNT);
336 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); 335 RetranslateUI();
337 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
338 336
339 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
340 tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); 337 tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons);
341 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
342 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
343 item_model->setSortRole(GameListItemPath::SortRole); 338 item_model->setSortRole(GameListItemPath::SortRole);
344 339
345 connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons); 340 connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons);
@@ -753,6 +748,35 @@ void GameList::LoadCompatibilityList() {
753 } 748 }
754} 749}
755 750
751void GameList::changeEvent(QEvent* event) {
752 if (event->type() == QEvent::LanguageChange) {
753 RetranslateUI();
754 }
755
756 QWidget::changeEvent(event);
757}
758
759void GameList::RetranslateUI() {
760 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
761 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
762 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
763 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
764 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
765}
766
767void GameListSearchField::changeEvent(QEvent* event) {
768 if (event->type() == QEvent::LanguageChange) {
769 RetranslateUI();
770 }
771
772 QWidget::changeEvent(event);
773}
774
775void GameListSearchField::RetranslateUI() {
776 label_filter->setText(tr("Filter:"));
777 edit_filter->setPlaceholderText(tr("Enter pattern to filter"));
778}
779
756QStandardItemModel* GameList::GetModel() const { 780QStandardItemModel* GameList::GetModel() const {
757 return item_model; 781 return item_model;
758} 782}
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index f783283c9..cdf085019 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -140,6 +140,9 @@ private:
140 void AddPermDirPopup(QMenu& context_menu, QModelIndex selected); 140 void AddPermDirPopup(QMenu& context_menu, QModelIndex selected);
141 void AddFavoritesPopup(QMenu& context_menu); 141 void AddFavoritesPopup(QMenu& context_menu);
142 142
143 void changeEvent(QEvent*) override;
144 void RetranslateUI();
145
143 std::shared_ptr<FileSys::VfsFilesystem> vfs; 146 std::shared_ptr<FileSys::VfsFilesystem> vfs;
144 FileSys::ManualContentProvider* provider; 147 FileSys::ManualContentProvider* provider;
145 GameListSearchField* search_field; 148 GameListSearchField* search_field;
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index e7667cf60..6198d1e4e 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -294,7 +294,7 @@ public:
294 294
295 const int icon_size = UISettings::values.folder_icon_size.GetValue(); 295 const int icon_size = UISettings::values.folder_icon_size.GetValue();
296 296
297 setData(QIcon::fromTheme(QStringLiteral("plus")) 297 setData(QIcon::fromTheme(QStringLiteral("list-add"))
298 .pixmap(icon_size) 298 .pixmap(icon_size)
299 .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), 299 .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
300 Qt::DecorationRole); 300 Qt::DecorationRole);
@@ -353,6 +353,9 @@ public:
353 void setFocus(); 353 void setFocus();
354 354
355private: 355private:
356 void changeEvent(QEvent*) override;
357 void RetranslateUI();
358
356 class KeyReleaseEater : public QObject { 359 class KeyReleaseEater : public QObject {
357 public: 360 public:
358 explicit KeyReleaseEater(GameList* gamelist_, QObject* parent = nullptr); 361 explicit KeyReleaseEater(GameList* gamelist_, QObject* parent = nullptr);
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp
index e273744fd..e263a07a7 100644
--- a/src/yuzu/loading_screen.cpp
+++ b/src/yuzu/loading_screen.cpp
@@ -147,6 +147,10 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size
147 ui->progress_bar->setMaximum(static_cast<int>(total)); 147 ui->progress_bar->setMaximum(static_cast<int>(total));
148 previous_total = total; 148 previous_total = total;
149 } 149 }
150 // Reset the progress bar ranges if compilation is done
151 if (stage == VideoCore::LoadCallbackStage::Complete) {
152 ui->progress_bar->setRange(0, 0);
153 }
150 154
151 QString estimate; 155 QString estimate;
152 // If theres a drastic slowdown in the rate, then display an estimate 156 // If theres a drastic slowdown in the rate, then display an estimate
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index f8c234082..e103df977 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -257,6 +257,18 @@ static QString PrettyProductName() {
257 return QSysInfo::prettyProductName(); 257 return QSysInfo::prettyProductName();
258} 258}
259 259
260bool GMainWindow::CheckDarkMode() {
261#ifdef __linux__
262 const QPalette test_palette(qApp->palette());
263 const QColor text_color = test_palette.color(QPalette::Active, QPalette::Text);
264 const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window);
265 return (text_color.value() > window_color.value());
266#else
267 // TODO: Windows
268 return false;
269#endif // __linux__
270}
271
260GMainWindow::GMainWindow(bool has_broken_vulkan) 272GMainWindow::GMainWindow(bool has_broken_vulkan)
261 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, 273 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
262 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, 274 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
@@ -274,6 +286,13 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
274 ui->setupUi(this); 286 ui->setupUi(this);
275 statusBar()->hide(); 287 statusBar()->hide();
276 288
289 // Check dark mode before a theme is loaded
290 os_dark_mode = CheckDarkMode();
291 startup_icon_theme = QIcon::themeName();
292 // fallback can only be set once, colorful theme icons are okay on both light/dark
293 QIcon::setFallbackThemeName(QStringLiteral("colorful"));
294 QIcon::setFallbackSearchPaths(QStringList(QStringLiteral(":/icons")));
295
277 default_theme_paths = QIcon::themeSearchPaths(); 296 default_theme_paths = QIcon::themeSearchPaths();
278 UpdateUITheme(); 297 UpdateUITheme();
279 298
@@ -473,8 +492,6 @@ GMainWindow::~GMainWindow() {
473 delete render_window; 492 delete render_window;
474 } 493 }
475 494
476 system->GetRoomNetwork().Shutdown();
477
478#ifdef __linux__ 495#ifdef __linux__
479 ::close(sig_interrupt_fds[0]); 496 ::close(sig_interrupt_fds[0]);
480 ::close(sig_interrupt_fds[1]); 497 ::close(sig_interrupt_fds[1]);
@@ -1075,7 +1092,7 @@ void GMainWindow::InitializeHotkeys() {
1075 connect_shortcut(QStringLiteral("Audio Mute/Unmute"), 1092 connect_shortcut(QStringLiteral("Audio Mute/Unmute"),
1076 [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); 1093 [] { Settings::values.audio_muted = !Settings::values.audio_muted; });
1077 connect_shortcut(QStringLiteral("Audio Volume Down"), [] { 1094 connect_shortcut(QStringLiteral("Audio Volume Down"), [] {
1078 const auto current_volume = static_cast<int>(Settings::values.volume.GetValue()); 1095 const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue());
1079 int step = 5; 1096 int step = 5;
1080 if (current_volume <= 30) { 1097 if (current_volume <= 30) {
1081 step = 2; 1098 step = 2;
@@ -1083,11 +1100,10 @@ void GMainWindow::InitializeHotkeys() {
1083 if (current_volume <= 6) { 1100 if (current_volume <= 6) {
1084 step = 1; 1101 step = 1;
1085 } 1102 }
1086 const auto new_volume = std::max(current_volume - step, 0); 1103 Settings::values.volume.SetValue(std::max(current_volume - step, 0));
1087 Settings::values.volume.SetValue(static_cast<u8>(new_volume));
1088 }); 1104 });
1089 connect_shortcut(QStringLiteral("Audio Volume Up"), [] { 1105 connect_shortcut(QStringLiteral("Audio Volume Up"), [] {
1090 const auto current_volume = static_cast<int>(Settings::values.volume.GetValue()); 1106 const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue());
1091 int step = 5; 1107 int step = 5;
1092 if (current_volume < 30) { 1108 if (current_volume < 30) {
1093 step = 2; 1109 step = 2;
@@ -1095,8 +1111,7 @@ void GMainWindow::InitializeHotkeys() {
1095 if (current_volume < 6) { 1111 if (current_volume < 6) {
1096 step = 1; 1112 step = 1;
1097 } 1113 }
1098 const auto new_volume = std::min(current_volume + step, 100); 1114 Settings::values.volume.SetValue(current_volume + step);
1099 Settings::values.volume.SetValue(static_cast<u8>(new_volume));
1100 }); 1115 });
1101 connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] { 1116 connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] {
1102 Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue()); 1117 Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue());
@@ -1588,17 +1603,18 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
1588 return true; 1603 return true;
1589} 1604}
1590 1605
1591void GMainWindow::SelectAndSetCurrentUser() { 1606bool GMainWindow::SelectAndSetCurrentUser() {
1592 QtProfileSelectionDialog dialog(system->HIDCore(), this); 1607 QtProfileSelectionDialog dialog(system->HIDCore(), this);
1593 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | 1608 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
1594 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); 1609 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
1595 dialog.setWindowModality(Qt::WindowModal); 1610 dialog.setWindowModality(Qt::WindowModal);
1596 1611
1597 if (dialog.exec() == QDialog::Rejected) { 1612 if (dialog.exec() == QDialog::Rejected) {
1598 return; 1613 return false;
1599 } 1614 }
1600 1615
1601 Settings::values.current_user = dialog.GetIndex(); 1616 Settings::values.current_user = dialog.GetIndex();
1617 return true;
1602} 1618}
1603 1619
1604void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, 1620void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index,
@@ -1632,11 +1648,14 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1632 Settings::LogSettings(); 1648 Settings::LogSettings();
1633 1649
1634 if (UISettings::values.select_user_on_boot) { 1650 if (UISettings::values.select_user_on_boot) {
1635 SelectAndSetCurrentUser(); 1651 if (SelectAndSetCurrentUser() == false) {
1652 return;
1653 }
1636 } 1654 }
1637 1655
1638 if (!LoadROM(filename, program_id, program_index)) 1656 if (!LoadROM(filename, program_id, program_index)) {
1639 return; 1657 return;
1658 }
1640 1659
1641 system->SetShuttingDown(false); 1660 system->SetShuttingDown(false);
1642 1661
@@ -3334,7 +3353,8 @@ void GMainWindow::MigrateConfigFiles() {
3334 } 3353 }
3335 const auto origin = config_dir_fs_path / filename; 3354 const auto origin = config_dir_fs_path / filename;
3336 const auto destination = config_dir_fs_path / "custom" / filename; 3355 const auto destination = config_dir_fs_path / "custom" / filename;
3337 LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination); 3356 LOG_INFO(Frontend, "Migrating config file from {} to {}", origin.string(),
3357 destination.string());
3338 if (!Common::FS::RenameFile(origin, destination)) { 3358 if (!Common::FS::RenameFile(origin, destination)) {
3339 // Delete the old config file if one already exists in the new location. 3359 // Delete the old config file if one already exists in the new location.
3340 Common::FS::RemoveFile(origin); 3360 Common::FS::RemoveFile(origin);
@@ -3809,6 +3829,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
3809 3829
3810 render_window->close(); 3830 render_window->close();
3811 multiplayer_state->Close(); 3831 multiplayer_state->Close();
3832 system->GetRoomNetwork().Shutdown();
3812 3833
3813 QWidget::closeEvent(event); 3834 QWidget::closeEvent(event);
3814} 3835}
@@ -3930,8 +3951,21 @@ void GMainWindow::filterBarSetChecked(bool state) {
3930 emit(OnToggleFilterBar()); 3951 emit(OnToggleFilterBar());
3931} 3952}
3932 3953
3954static void AdjustLinkColor() {
3955 QPalette new_pal(qApp->palette());
3956 if (UISettings::IsDarkTheme()) {
3957 new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255));
3958 } else {
3959 new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255));
3960 }
3961 if (qApp->palette().color(QPalette::Link) != new_pal.color(QPalette::Link)) {
3962 qApp->setPalette(new_pal);
3963 }
3964}
3965
3933void GMainWindow::UpdateUITheme() { 3966void GMainWindow::UpdateUITheme() {
3934 const QString default_theme = QStringLiteral("default"); 3967 const QString default_theme =
3968 QString::fromUtf8(UISettings::themes[static_cast<size_t>(Config::default_theme)].second);
3935 QString current_theme = UISettings::values.theme; 3969 QString current_theme = UISettings::values.theme;
3936 QStringList theme_paths(default_theme_paths); 3970 QStringList theme_paths(default_theme_paths);
3937 3971
@@ -3939,6 +3973,23 @@ void GMainWindow::UpdateUITheme() {
3939 current_theme = default_theme; 3973 current_theme = default_theme;
3940 } 3974 }
3941 3975
3976#ifdef _WIN32
3977 QIcon::setThemeName(current_theme);
3978 AdjustLinkColor();
3979#else
3980 if (current_theme == QStringLiteral("default") || current_theme == QStringLiteral("colorful")) {
3981 QIcon::setThemeName(current_theme == QStringLiteral("colorful") ? current_theme
3982 : startup_icon_theme);
3983 QIcon::setThemeSearchPaths(theme_paths);
3984 if (CheckDarkMode()) {
3985 current_theme = QStringLiteral("default_dark");
3986 }
3987 } else {
3988 QIcon::setThemeName(current_theme);
3989 QIcon::setThemeSearchPaths(QStringList(QStringLiteral(":/icons")));
3990 AdjustLinkColor();
3991 }
3992#endif
3942 if (current_theme != default_theme) { 3993 if (current_theme != default_theme) {
3943 QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)}; 3994 QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)};
3944 QFile f(theme_uri); 3995 QFile f(theme_uri);
@@ -3961,25 +4012,9 @@ void GMainWindow::UpdateUITheme() {
3961 qApp->setStyleSheet({}); 4012 qApp->setStyleSheet({});
3962 setStyleSheet({}); 4013 setStyleSheet({});
3963 } 4014 }
3964
3965 QPalette new_pal(qApp->palette());
3966 if (UISettings::IsDarkTheme()) {
3967 new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255));
3968 } else {
3969 new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255));
3970 }
3971 qApp->setPalette(new_pal);
3972
3973 QIcon::setThemeName(current_theme);
3974 QIcon::setThemeSearchPaths(theme_paths);
3975} 4015}
3976 4016
3977void GMainWindow::LoadTranslation() { 4017void GMainWindow::LoadTranslation() {
3978 // If the selected language is English, no need to install any translation
3979 if (UISettings::values.language == QStringLiteral("en")) {
3980 return;
3981 }
3982
3983 bool loaded; 4018 bool loaded;
3984 4019
3985 if (UISettings::values.language.isEmpty()) { 4020 if (UISettings::values.language.isEmpty()) {
@@ -4022,6 +4057,26 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
4022 discord_rpc->Update(); 4057 discord_rpc->Update();
4023} 4058}
4024 4059
4060void GMainWindow::changeEvent(QEvent* event) {
4061#ifdef __linux__
4062 // PaletteChange event appears to only reach so far into the GUI, explicitly asking to
4063 // UpdateUITheme is a decent work around
4064 if (event->type() == QEvent::PaletteChange) {
4065 const QPalette test_palette(qApp->palette());
4066 const QString current_theme = UISettings::values.theme;
4067 // Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too
4068 static QColor last_window_color;
4069 const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window);
4070 if (last_window_color != window_color && (current_theme == QStringLiteral("default") ||
4071 current_theme == QStringLiteral("colorful"))) {
4072 UpdateUITheme();
4073 }
4074 last_window_color = window_color;
4075 }
4076#endif // __linux__
4077 QWidget::changeEvent(event);
4078}
4079
4025#ifdef main 4080#ifdef main
4026#undef main 4081#undef main
4027#endif 4082#endif
@@ -4067,6 +4122,15 @@ int main(int argc, char* argv[]) {
4067 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 4122 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
4068 QApplication app(argc, argv); 4123 QApplication app(argc, argv);
4069 4124
4125 // Workaround for QTBUG-85409, for Suzhou numerals the number 1 is actually \u3021
4126 // so we can see if we get \u3008 instead
4127 // TL;DR all other number formats are consecutive in unicode code points
4128 // This bug is fixed in Qt6, specifically 6.0.0-alpha1
4129 const QLocale locale = QLocale::system();
4130 if (QStringLiteral("\u3008") == locale.toString(1)) {
4131 QLocale::setDefault(QLocale::system().name());
4132 }
4133
4070 // Qt changes the locale and causes issues in float conversion using std::to_string() when 4134 // Qt changes the locale and causes issues in float conversion using std::to_string() when
4071 // generating shaders 4135 // generating shaders
4072 setlocale(LC_ALL, "C"); 4136 setlocale(LC_ALL, "C");
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 23b67a14e..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.
@@ -251,6 +251,7 @@ private:
251 bool ConfirmForceLockedExit(); 251 bool ConfirmForceLockedExit();
252 void RequestGameExit(); 252 void RequestGameExit();
253 void RequestGameResume(); 253 void RequestGameResume();
254 void changeEvent(QEvent* event) override;
254 void closeEvent(QCloseEvent* event) override; 255 void closeEvent(QCloseEvent* event) override;
255 256
256#ifdef __linux__ 257#ifdef __linux__
@@ -347,6 +348,7 @@ private:
347 void OpenURL(const QUrl& url); 348 void OpenURL(const QUrl& url);
348 void LoadTranslation(); 349 void LoadTranslation();
349 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); 350 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
351 bool CheckDarkMode();
350 352
351 QString GetTasStateDescription() const; 353 QString GetTasStateDescription() const;
352 354
@@ -392,6 +394,9 @@ private:
392 QTimer mouse_hide_timer; 394 QTimer mouse_hide_timer;
393 QTimer mouse_center_timer; 395 QTimer mouse_center_timer;
394 396
397 QString startup_icon_theme;
398 bool os_dark_mode = false;
399
395 // FS 400 // FS
396 std::shared_ptr<FileSys::VfsFilesystem> vfs; 401 std::shared_ptr<FileSys::VfsFilesystem> vfs;
397 std::unique_ptr<FileSys::ManualContentProvider> provider; 402 std::unique_ptr<FileSys::ManualContentProvider> provider;
diff --git a/src/yuzu/multiplayer/chat_room.cpp b/src/yuzu/multiplayer/chat_room.cpp
index 5837b36ab..1968a3c75 100644
--- a/src/yuzu/multiplayer/chat_room.cpp
+++ b/src/yuzu/multiplayer/chat_room.cpp
@@ -316,21 +316,19 @@ void ChatRoom::OnStatusMessageReceive(const Network::StatusMessageEntry& status_
316} 316}
317 317
318void ChatRoom::OnSendChat() { 318void ChatRoom::OnSendChat() {
319 if (auto room = room_network->GetRoomMember().lock()) { 319 if (auto room_member = room_network->GetRoomMember().lock()) {
320 if (room->GetState() != Network::RoomMember::State::Joined && 320 if (!room_member->IsConnected()) {
321 room->GetState() != Network::RoomMember::State::Moderator) {
322
323 return; 321 return;
324 } 322 }
325 auto message = ui->chat_message->text().toStdString(); 323 auto message = ui->chat_message->text().toStdString();
326 if (!ValidateMessage(message)) { 324 if (!ValidateMessage(message)) {
327 return; 325 return;
328 } 326 }
329 auto nick = room->GetNickname(); 327 auto nick = room_member->GetNickname();
330 auto username = room->GetUsername(); 328 auto username = room_member->GetUsername();
331 Network::ChatEntry chat{nick, username, message}; 329 Network::ChatEntry chat{nick, username, message};
332 330
333 auto members = room->GetMemberInformation(); 331 auto members = room_member->GetMemberInformation();
334 auto it = std::find_if(members.begin(), members.end(), 332 auto it = std::find_if(members.begin(), members.end(),
335 [&chat](const Network::RoomMember::MemberInformation& member) { 333 [&chat](const Network::RoomMember::MemberInformation& member) {
336 return member.nickname == chat.nickname && 334 return member.nickname == chat.nickname &&
@@ -341,7 +339,7 @@ void ChatRoom::OnSendChat() {
341 } 339 }
342 auto player = std::distance(members.begin(), it); 340 auto player = std::distance(members.begin(), it);
343 ChatMessage m(chat, *room_network); 341 ChatMessage m(chat, *room_network);
344 room->SendChatMessage(message); 342 room_member->SendChatMessage(message);
345 AppendChatMessage(m.GetPlayerChatMessage(player)); 343 AppendChatMessage(m.GetPlayerChatMessage(player));
346 ui->chat_message->clear(); 344 ui->chat_message->clear();
347 } 345 }
diff --git a/src/yuzu/multiplayer/client_room.cpp b/src/yuzu/multiplayer/client_room.cpp
index a9859ed70..86baafbf0 100644
--- a/src/yuzu/multiplayer/client_room.cpp
+++ b/src/yuzu/multiplayer/client_room.cpp
@@ -74,7 +74,6 @@ void ClientRoomWindow::OnRoomUpdate(const Network::RoomInformation& info) {
74void ClientRoomWindow::OnStateChange(const Network::RoomMember::State& state) { 74void ClientRoomWindow::OnStateChange(const Network::RoomMember::State& state) {
75 if (state == Network::RoomMember::State::Joined || 75 if (state == Network::RoomMember::State::Joined ||
76 state == Network::RoomMember::State::Moderator) { 76 state == Network::RoomMember::State::Moderator) {
77
78 ui->chat->Clear(); 77 ui->chat->Clear();
79 ui->chat->AppendStatusMessage(tr("Connected")); 78 ui->chat->AppendStatusMessage(tr("Connected"));
80 SetModPerms(state == Network::RoomMember::State::Moderator); 79 SetModPerms(state == Network::RoomMember::State::Moderator);
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp
index 9000c4531..4c0ea0a6b 100644
--- a/src/yuzu/multiplayer/direct_connect.cpp
+++ b/src/yuzu/multiplayer/direct_connect.cpp
@@ -97,9 +97,9 @@ void DirectConnectWindow::Connect() {
97 QFuture<void> f = QtConcurrent::run([&] { 97 QFuture<void> f = QtConcurrent::run([&] {
98 if (auto room_member = room_network.GetRoomMember().lock()) { 98 if (auto room_member = room_network.GetRoomMember().lock()) {
99 auto port = UISettings::values.multiplayer_port.GetValue(); 99 auto port = UISettings::values.multiplayer_port.GetValue();
100 room_member->Join(ui->nickname->text().toStdString(), "", 100 room_member->Join(ui->nickname->text().toStdString(),
101 ui->ip->text().toStdString().c_str(), port, 0, 101 ui->ip->text().toStdString().c_str(), port, 0, Network::NoPreferredIP,
102 Network::NoPreferredMac, ui->password->text().toStdString().c_str()); 102 ui->password->text().toStdString().c_str());
103 } 103 }
104 }); 104 });
105 watcher->setFuture(f); 105 watcher->setFuture(f);
@@ -121,9 +121,7 @@ void DirectConnectWindow::OnConnection() {
121 EndConnecting(); 121 EndConnecting();
122 122
123 if (auto room_member = room_network.GetRoomMember().lock()) { 123 if (auto room_member = room_network.GetRoomMember().lock()) {
124 if (room_member->GetState() == Network::RoomMember::State::Joined || 124 if (room_member->IsConnected()) {
125 room_member->GetState() == Network::RoomMember::State::Moderator) {
126
127 close(); 125 close();
128 } 126 }
129 } 127 }
diff --git a/src/yuzu/multiplayer/direct_connect.ui b/src/yuzu/multiplayer/direct_connect.ui
index 681b6bf69..57d6ec25a 100644
--- a/src/yuzu/multiplayer/direct_connect.ui
+++ b/src/yuzu/multiplayer/direct_connect.ui
@@ -83,7 +83,7 @@
83 <number>5</number> 83 <number>5</number>
84 </property> 84 </property>
85 <property name="placeholderText"> 85 <property name="placeholderText">
86 <string>24872</string> 86 <string notr="true" extracomment="placeholder string that tells user default port">24872</string>
87 </property> 87 </property>
88 </widget> 88 </widget>
89 </item> 89 </item>
diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp
index cb9464b2b..d70a9a3c8 100644
--- a/src/yuzu/multiplayer/host_room.cpp
+++ b/src/yuzu/multiplayer/host_room.cpp
@@ -201,8 +201,8 @@ void HostRoomWindow::Host() {
201 } 201 }
202#endif 202#endif
203 // TODO: Check what to do with this 203 // TODO: Check what to do with this
204 member->Join(ui->username->text().toStdString(), "", "127.0.0.1", port, 0, 204 member->Join(ui->username->text().toStdString(), "127.0.0.1", port, 0,
205 Network::NoPreferredMac, password, token); 205 Network::NoPreferredIP, password, token);
206 206
207 // Store settings 207 // Store settings
208 UISettings::values.multiplayer_room_nickname = ui->username->text(); 208 UISettings::values.multiplayer_room_nickname = ui->username->text();
diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp
index 23c2f21ab..1cc518279 100644
--- a/src/yuzu/multiplayer/lobby.cpp
+++ b/src/yuzu/multiplayer/lobby.cpp
@@ -169,7 +169,7 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
169 } 169 }
170#endif 170#endif
171 if (auto room_member = room_network.GetRoomMember().lock()) { 171 if (auto room_member = room_network.GetRoomMember().lock()) {
172 room_member->Join(nickname, "", ip.c_str(), port, 0, Network::NoPreferredMac, password, 172 room_member->Join(nickname, ip.c_str(), port, 0, Network::NoPreferredIP, password,
173 token); 173 token);
174 } 174 }
175 }); 175 });
diff --git a/src/yuzu/multiplayer/message.cpp b/src/yuzu/multiplayer/message.cpp
index 76ec276ad..94d7a38b8 100644
--- a/src/yuzu/multiplayer/message.cpp
+++ b/src/yuzu/multiplayer/message.cpp
@@ -43,11 +43,8 @@ const ConnectionError ErrorManager::LOST_CONNECTION(
43 QT_TR_NOOP("Connection to room lost. Try to reconnect.")); 43 QT_TR_NOOP("Connection to room lost. Try to reconnect."));
44const ConnectionError ErrorManager::HOST_KICKED( 44const ConnectionError ErrorManager::HOST_KICKED(
45 QT_TR_NOOP("You have been kicked by the room host.")); 45 QT_TR_NOOP("You have been kicked by the room host."));
46const ConnectionError ErrorManager::MAC_COLLISION( 46const ConnectionError ErrorManager::IP_COLLISION(
47 QT_TR_NOOP("MAC address is already in use. Please choose another.")); 47 QT_TR_NOOP("IP address is already in use. Please choose another."));
48const ConnectionError ErrorManager::CONSOLE_ID_COLLISION(QT_TR_NOOP(
49 "Your Console ID conflicted with someone else's in the room.\n\nPlease go to Emulation "
50 "> Configure > System to regenerate your Console ID."));
51const ConnectionError ErrorManager::PERMISSION_DENIED( 48const ConnectionError ErrorManager::PERMISSION_DENIED(
52 QT_TR_NOOP("You do not have enough permission to perform this action.")); 49 QT_TR_NOOP("You do not have enough permission to perform this action."));
53const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP( 50const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP(
diff --git a/src/yuzu/multiplayer/message.h b/src/yuzu/multiplayer/message.h
index eb5c8d1be..812495c72 100644
--- a/src/yuzu/multiplayer/message.h
+++ b/src/yuzu/multiplayer/message.h
@@ -40,8 +40,7 @@ public:
40 static const ConnectionError GENERIC_ERROR; 40 static const ConnectionError GENERIC_ERROR;
41 static const ConnectionError LOST_CONNECTION; 41 static const ConnectionError LOST_CONNECTION;
42 static const ConnectionError HOST_KICKED; 42 static const ConnectionError HOST_KICKED;
43 static const ConnectionError MAC_COLLISION; 43 static const ConnectionError IP_COLLISION;
44 static const ConnectionError CONSOLE_ID_COLLISION;
45 static const ConnectionError PERMISSION_DENIED; 44 static const ConnectionError PERMISSION_DENIED;
46 static const ConnectionError NO_SUCH_USER; 45 static const ConnectionError NO_SUCH_USER;
47 /** 46 /**
diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp
index 4149b5232..dba76b22b 100644
--- a/src/yuzu/multiplayer/state.cpp
+++ b/src/yuzu/multiplayer/state.cpp
@@ -59,7 +59,9 @@ MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_lis
59 }); 59 });
60} 60}
61 61
62MultiplayerState::~MultiplayerState() { 62MultiplayerState::~MultiplayerState() = default;
63
64void MultiplayerState::Close() {
63 if (state_callback_handle) { 65 if (state_callback_handle) {
64 if (auto member = room_network.GetRoomMember().lock()) { 66 if (auto member = room_network.GetRoomMember().lock()) {
65 member->Unbind(state_callback_handle); 67 member->Unbind(state_callback_handle);
@@ -71,9 +73,6 @@ MultiplayerState::~MultiplayerState() {
71 member->Unbind(error_callback_handle); 73 member->Unbind(error_callback_handle);
72 } 74 }
73 } 75 }
74}
75
76void MultiplayerState::Close() {
77 if (host_room) { 76 if (host_room) {
78 host_room->close(); 77 host_room->close();
79 } 78 }
@@ -95,7 +94,6 @@ void MultiplayerState::retranslateUi() {
95 status_text->setText(tr("Not Connected. Click here to find a room!")); 94 status_text->setText(tr("Not Connected. Click here to find a room!"));
96 } else if (current_state == Network::RoomMember::State::Joined || 95 } else if (current_state == Network::RoomMember::State::Joined ||
97 current_state == Network::RoomMember::State::Moderator) { 96 current_state == Network::RoomMember::State::Moderator) {
98
99 status_text->setText(tr("Connected")); 97 status_text->setText(tr("Connected"));
100 } else { 98 } else {
101 status_text->setText(tr("Not Connected")); 99 status_text->setText(tr("Not Connected"));
@@ -151,11 +149,8 @@ void MultiplayerState::OnNetworkError(const Network::RoomMember::Error& error) {
151 NetworkMessage::ErrorManager::ShowError( 149 NetworkMessage::ErrorManager::ShowError(
152 NetworkMessage::ErrorManager::USERNAME_NOT_VALID_SERVER); 150 NetworkMessage::ErrorManager::USERNAME_NOT_VALID_SERVER);
153 break; 151 break;
154 case Network::RoomMember::Error::MacCollision: 152 case Network::RoomMember::Error::IpCollision:
155 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::MAC_COLLISION); 153 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::IP_COLLISION);
156 break;
157 case Network::RoomMember::Error::ConsoleIdCollision:
158 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::CONSOLE_ID_COLLISION);
159 break; 154 break;
160 case Network::RoomMember::Error::RoomIsFull: 155 case Network::RoomMember::Error::RoomIsFull:
161 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::ROOM_IS_FULL); 156 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::ROOM_IS_FULL);
diff --git a/src/yuzu/multiplayer/validation.h b/src/yuzu/multiplayer/validation.h
index 7d48e589d..dabf860be 100644
--- a/src/yuzu/multiplayer/validation.h
+++ b/src/yuzu/multiplayer/validation.h
@@ -10,7 +10,7 @@
10class Validation { 10class Validation {
11public: 11public:
12 Validation() 12 Validation()
13 : room_name(room_name_regex), nickname(nickname_regex), ip(ip_regex), port(0, 65535) {} 13 : room_name(room_name_regex), nickname(nickname_regex), ip(ip_regex), port(0, UINT16_MAX) {}
14 14
15 ~Validation() = default; 15 ~Validation() = default;
16 16
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 25d1bf1e6..e12d414d9 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -104,11 +104,12 @@ struct Values {
104 // multiplayer settings 104 // multiplayer settings
105 Settings::Setting<QString> multiplayer_nickname{QStringLiteral("yuzu"), "nickname"}; 105 Settings::Setting<QString> multiplayer_nickname{QStringLiteral("yuzu"), "nickname"};
106 Settings::Setting<QString> multiplayer_ip{{}, "ip"}; 106 Settings::Setting<QString> multiplayer_ip{{}, "ip"};
107 Settings::SwitchableSetting<uint, true> multiplayer_port{24872, 0, 65535, "port"}; 107 Settings::SwitchableSetting<uint, true> multiplayer_port{24872, 0, UINT16_MAX, "port"};
108 Settings::Setting<QString> multiplayer_room_nickname{{}, "room_nickname"}; 108 Settings::Setting<QString> multiplayer_room_nickname{{}, "room_nickname"};
109 Settings::Setting<QString> multiplayer_room_name{{}, "room_name"}; 109 Settings::Setting<QString> multiplayer_room_name{{}, "room_name"};
110 Settings::SwitchableSetting<uint, true> multiplayer_max_player{8, 0, 8, "max_player"}; 110 Settings::SwitchableSetting<uint, true> multiplayer_max_player{8, 0, 8, "max_player"};
111 Settings::SwitchableSetting<uint, true> multiplayer_room_port{24872, 0, 65535, "room_port"}; 111 Settings::SwitchableSetting<uint, true> multiplayer_room_port{24872, 0, UINT16_MAX,
112 "room_port"};
112 Settings::SwitchableSetting<uint, true> multiplayer_host_type{0, 0, 1, "host_type"}; 113 Settings::SwitchableSetting<uint, true> multiplayer_host_type{0, 0, 1, "host_type"};
113 Settings::Setting<qulonglong> multiplayer_game_id{{}, "game_id"}; 114 Settings::Setting<qulonglong> multiplayer_game_id{{}, "game_id"};
114 Settings::Setting<QString> multiplayer_room_description{{}, "room_description"}; 115 Settings::Setting<QString> multiplayer_room_description{{}, "room_description"};
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 003890c07..3a0f33cba 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -108,15 +108,11 @@ static void OnNetworkError(const Network::RoomMember::Error& error) {
108 "You tried to use the same nickname as another user that is connected to the Room"); 108 "You tried to use the same nickname as another user that is connected to the Room");
109 exit(1); 109 exit(1);
110 break; 110 break;
111 case Network::RoomMember::Error::MacCollision: 111 case Network::RoomMember::Error::IpCollision:
112 LOG_ERROR(Network, "You tried to use the same MAC-Address as another user that is " 112 LOG_ERROR(Network, "You tried to use the same fake IP-Address as another user that is "
113 "connected to the Room"); 113 "connected to the Room");
114 exit(1); 114 exit(1);
115 break; 115 break;
116 case Network::RoomMember::Error::ConsoleIdCollision:
117 LOG_ERROR(Network, "Your Console ID conflicted with someone else in the Room");
118 exit(1);
119 break;
120 case Network::RoomMember::Error::WrongPassword: 116 case Network::RoomMember::Error::WrongPassword:
121 LOG_ERROR(Network, "Room replied with: Wrong password"); 117 LOG_ERROR(Network, "Room replied with: Wrong password");
122 exit(1); 118 exit(1);
@@ -365,7 +361,7 @@ int main(int argc, char** argv) {
365 member->BindOnError(OnNetworkError); 361 member->BindOnError(OnNetworkError);
366 LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, 362 LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port,
367 nickname); 363 nickname);
368 member->Join(nickname, "", address.c_str(), port, 0, Network::NoPreferredMac, password); 364 member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredIP, password);
369 } else { 365 } else {
370 LOG_ERROR(Network, "Could not access RoomMember"); 366 LOG_ERROR(Network, "Could not access RoomMember");
371 return 0; 367 return 0;