summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/scripts/common/post-upload.sh4
-rw-r--r--.ci/scripts/windows/upload.ps110
-rw-r--r--.gitignore2
-rw-r--r--.reuse/dep562
-rw-r--r--CMakeLists.txt9
-rw-r--r--LICENSES/CC-BY-ND-3.0.txt87
-rw-r--r--LICENSES/CC-BY-SA-3.0.txt359
-rw-r--r--dist/english_plurals/README.md19
-rw-r--r--dist/english_plurals/en.ts67
-rw-r--r--dist/qt_themes/colorful/icons/16x16/connected.pngbin362 -> 575 bytes
-rw-r--r--dist/qt_themes/colorful/icons/16x16/connected_notification.pngbin607 -> 760 bytes
-rw-r--r--dist/qt_themes/colorful/icons/16x16/disconnected.pngbin784 -> 648 bytes
-rw-r--r--dist/qt_themes/colorful/icons/48x48/list-add.pngbin0 -> 204 bytes
-rw-r--r--dist/qt_themes/colorful/icons/48x48/plus.pngbin496 -> 0 bytes
-rw-r--r--dist/qt_themes/colorful/icons/48x48/sd_card.pngbin680 -> 981 bytes
-rw-r--r--dist/qt_themes/colorful/icons/48x48/star.pngbin1248 -> 1108 bytes
-rw-r--r--dist/qt_themes/colorful/style.qrc2
-rw-r--r--dist/qt_themes/colorful_dark/icons/16x16/refresh.pngbin362 -> 0 bytes
-rw-r--r--dist/qt_themes/colorful_dark/icons/index.theme2
-rw-r--r--dist/qt_themes/colorful_dark/style.qrc2
-rw-r--r--dist/qt_themes/colorful_midnight_blue/icons/16x16/lock.pngbin401 -> 0 bytes
-rw-r--r--dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.pngbin362 -> 0 bytes
-rw-r--r--dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.pngbin362 -> 0 bytes
-rw-r--r--dist/qt_themes/colorful_midnight_blue/style.qrc6
-rw-r--r--dist/qt_themes/default/default.qrc2
-rw-r--r--dist/qt_themes/default/icons/16x16/checked.pngbin657 -> 414 bytes
-rw-r--r--dist/qt_themes/default/icons/16x16/connected.pngbin269 -> 575 bytes
-rw-r--r--dist/qt_themes/default/icons/16x16/connected_notification.pngbin517 -> 760 bytes
-rw-r--r--dist/qt_themes/default/icons/16x16/disconnected.pngbin306 -> 648 bytes
-rw-r--r--dist/qt_themes/default/icons/16x16/failed.pngbin524 -> 431 bytes
-rw-r--r--dist/qt_themes/default/icons/16x16/lock.pngbin279 -> 318 bytes
-rw-r--r--dist/qt_themes/default/icons/16x16/refresh.pngbin349 -> 0 bytes
-rw-r--r--dist/qt_themes/default/icons/256x256/plus_folder.pngbin3135 -> 3521 bytes
-rw-r--r--dist/qt_themes/default/icons/48x48/bad_folder.pngbin1088 -> 1007 bytes
-rw-r--r--dist/qt_themes/default/icons/48x48/chip.pngbin15070 -> 511 bytes
-rw-r--r--dist/qt_themes/default/icons/48x48/folder.pngbin410 -> 535 bytes
-rw-r--r--dist/qt_themes/default/icons/48x48/list-add.pngbin0 -> 204 bytes
-rw-r--r--dist/qt_themes/default/icons/48x48/no_avatar.pngbin588 -> 678 bytes
-rw-r--r--dist/qt_themes/default/icons/48x48/plus.pngbin316 -> 0 bytes
-rw-r--r--dist/qt_themes/default/icons/48x48/sd_card.pngbin614 -> 561 bytes
-rw-r--r--dist/qt_themes/default/icons/48x48/star.pngbin686 -> 1029 bytes
-rw-r--r--dist/qt_themes/default_dark/icons/index.theme8
-rw-r--r--dist/qt_themes/default_dark/style.qrc25
-rw-r--r--dist/qt_themes/default_dark/style.qss687
-rw-r--r--dist/qt_themes/qdarkstyle/icons/16x16/connected.pngbin397 -> 575 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/16x16/connected_notification.pngbin526 -> 760 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/16x16/disconnected.pngbin444 -> 648 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/16x16/lock.pngbin304 -> 343 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/16x16/refresh.pngbin362 -> 0 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/256x256/plus_folder.pngbin3438 -> 3931 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/48x48/bad_folder.pngbin1098 -> 1061 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/48x48/chip.pngbin15120 -> 551 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/48x48/folder.pngbin542 -> 594 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/48x48/list-add.pngbin0 -> 204 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/48x48/no_avatar.pngbin708 -> 763 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/48x48/plus.pngbin339 -> 0 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/48x48/sd_card.pngbin676 -> 587 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/48x48/star.pngbin725 -> 1055 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/style.qrc2
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/lock.pngbin304 -> 0 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.pngbin362 -> 0 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.pngbin362 -> 0 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/256x256/plus_folder.pngbin3438 -> 0 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/bad_folder.pngbin1098 -> 0 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/chip.pngbin15120 -> 0 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/folder.pngbin542 -> 0 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/plus.pngbin339 -> 0 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/sd_card.pngbin676 -> 0 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/star.pngbin725 -> 0 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/style.qrc19
-rw-r--r--externals/CMakeLists.txt2
-rw-r--r--externals/find-modules/FindCatch2.cmake51
-rw-r--r--externals/find-modules/FindOpus.cmake19
-rw-r--r--externals/find-modules/Findfmt.cmake71
-rw-r--r--externals/find-modules/Findlz4.cmake59
-rw-r--r--externals/find-modules/Findnlohmann_json.cmake51
-rw-r--r--externals/find-modules/Findopus.cmake44
-rw-r--r--externals/find-modules/Findzstd.cmake60
-rw-r--r--externals/opus/CMakeLists.txt2
-rw-r--r--src/CMakeLists.txt1
-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/uint128.h1
-rw-r--r--src/core/CMakeLists.txt7
-rw-r--r--src/core/announce_multiplayer_session.cpp6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp19
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp3
-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/file_sys/ips_layer.cpp7
-rw-r--r--src/core/file_sys/patch_manager.cpp14
-rw-r--r--src/core/hid/emulated_controller.cpp65
-rw-r--r--src/core/hid/emulated_controller.h8
-rw-r--r--src/core/hid/hid_types.h12
-rw-r--r--src/core/hle/service/audio/audren_u.cpp27
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp77
-rw-r--r--src/core/hle/service/hid/hid.cpp6
-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/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/kip.cpp2
-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/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/renderer_opengl.cpp19
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp18
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp5
-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/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/bootmanager.cpp20
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_audio.ui4
-rw-r--r--src/yuzu/configuration/configure_camera.cpp20
-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/main.cpp109
-rw-r--r--src/yuzu/main.h5
-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
177 files changed, 4247 insertions, 1296 deletions
diff --git a/.ci/scripts/common/post-upload.sh b/.ci/scripts/common/post-upload.sh
index 7f910b2b3..0930b7a7b 100644
--- a/.ci/scripts/common/post-upload.sh
+++ b/.ci/scripts/common/post-upload.sh
@@ -8,8 +8,10 @@ cp LICENSE.txt "$DIR_NAME"
8cp README.md "$DIR_NAME" 8cp README.md "$DIR_NAME"
9 9
10if [[ -z "${NO_SOURCE_PACK}" ]]; then 10if [[ -z "${NO_SOURCE_PACK}" ]]; then
11 tar -cJvf "${REV_NAME}-source.tar.xz" src externals CMakeLists.txt README.md LICENSE.txt 11 git clone --depth 1 file://$(readlink -e .) ${REV_NAME}-source
12 tar -cJvf "${REV_NAME}-source.tar.xz" ${REV_NAME}-source
12 cp -v "${REV_NAME}-source.tar.xz" "$DIR_NAME" 13 cp -v "${REV_NAME}-source.tar.xz" "$DIR_NAME"
14 cp -v "${REV_NAME}-source.tar.xz" "${ARTIFACTS_DIR}/"
13fi 15fi
14 16
15tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$DIR_NAME" 17tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$DIR_NAME"
diff --git a/.ci/scripts/windows/upload.ps1 b/.ci/scripts/windows/upload.ps1
index f2368be6f..d463281de 100644
--- a/.ci/scripts/windows/upload.ps1
+++ b/.ci/scripts/windows/upload.ps1
@@ -42,14 +42,10 @@ mkdir $RELEASE_DIST
42mkdir $MSVC_SOURCE 42mkdir $MSVC_SOURCE
43mkdir "artifacts" 43mkdir "artifacts"
44 44
45$CURRENT_DIR = Convert-Path .
46
45# Build a tar.xz for the source of the release 47# Build a tar.xz for the source of the release
46Copy-Item .\LICENSE.txt -Destination $MSVC_SOURCE 48git clone --depth 1 file://$CURRENT_DIR $MSVC_SOURCE
47Copy-Item .\README.md -Destination $MSVC_SOURCE
48Copy-Item .\CMakeLists.txt -Destination $MSVC_SOURCE
49Copy-Item .\src -Recurse -Destination $MSVC_SOURCE
50Copy-Item .\externals -Recurse -Destination $MSVC_SOURCE
51Copy-Item .\dist -Recurse -Destination $MSVC_SOURCE
52Copy-Item .\CMakeModules -Recurse -Destination $MSVC_SOURCE
537z a -r -ttar $MSVC_SOURCE_TAR $MSVC_SOURCE 497z a -r -ttar $MSVC_SOURCE_TAR $MSVC_SOURCE
547z a -r -txz $MSVC_SOURCE_TARXZ $MSVC_SOURCE_TAR 507z a -r -txz $MSVC_SOURCE_TARXZ $MSVC_SOURCE_TAR
55 51
diff --git a/.gitignore b/.gitignore
index 6207765d8..cdf37962a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,7 +7,7 @@ doc-build/
7 7
8# Generated source files 8# Generated source files
9src/common/scm_rev.cpp 9src/common/scm_rev.cpp
10.travis.descriptor.json 10dist/english_plurals/generated_en.ts
11 11
12# Project/editor files 12# Project/editor files
13*.swp 13*.swp
diff --git a/.reuse/dep5 b/.reuse/dep5
index e2ee4f456..fe4fa2f07 100644
--- a/.reuse/dep5
+++ b/.reuse/dep5
@@ -2,7 +2,8 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
2Comment: It is best to use this file to record copyright information about 2Comment: It is best to use this file to record copyright information about
3 generated, binary and third party files 3 generated, binary and third party files
4 4
5Files: dist/icons/controller/*.png 5Files: dist/english_plurals/*
6 dist/icons/controller/*.png
6 dist/icons/overlay/*.png 7 dist/icons/overlay/*.png
7 dist/languages/* 8 dist/languages/*
8 dist/qt_themes/*/icons/index.theme 9 dist/qt_themes/*/icons/index.theme
@@ -30,32 +31,57 @@ Copyright: 2013 Colin Duquesnoy
30 2019 Daniel Cosmo Pizetta 31 2019 Daniel Cosmo Pizetta
31License: CC-BY-4.0 32License: CC-BY-4.0
32 33
34Files: dist/qt_themes/default/icons/256x256/plus_folder.png
35 dist/qt_themes/default/icons/48x48/bad_folder.png
36 dist/qt_themes/default/icons/48x48/chip.png
37 dist/qt_themes/default/icons/48x48/folder.png
38 dist/qt_themes/default/icons/48x48/star.png
39 dist/qt_themes/qdarkstyle/icons/256x256/plus_folder.png
40 dist/qt_themes/qdarkstyle/icons/48x48/bad_folder.png
41 dist/qt_themes/qdarkstyle/icons/48x48/chip.png
42 dist/qt_themes/qdarkstyle/icons/48x48/folder.png
43 dist/qt_themes/qdarkstyle/icons/48x48/star.png
44Copyright: Refactoring UI Inc.
45License: MIT
46Comment: https://github.com/tailwindlabs/heroicons
47
48Files: dist/qt_themes/colorful/icons/16x16/lock.png
49 dist/qt_themes/colorful/icons/256x256/plus_folder.png
50 dist/qt_themes/colorful/icons/48x48/bad_folder.png
51 dist/qt_themes/colorful/icons/48x48/chip.png
52 dist/qt_themes/colorful/icons/48x48/folder.png
53 dist/qt_themes/colorful_dark/icons/16x16/lock.png
54Copyright: Icons8
55License: MIT
56Comment: https://github.com/icons8/flat-color-icons
57
33Files: dist/qt_themes/*/icons/16x16/connected.png 58Files: dist/qt_themes/*/icons/16x16/connected.png
34 dist/qt_themes/*/icons/16x16/connected_notification.png 59 dist/qt_themes/*/icons/16x16/connected_notification.png
35 dist/qt_themes/*/icons/16x16/disconnected.png 60 dist/qt_themes/*/icons/16x16/disconnected.png
36 dist/qt_themes/*/icons/16x16/lock.png 61Copyright: GNOME Project
37 dist/qt_themes/*/icons/48x48/bad_folder.png 62License: CC-BY-SA-3.0
38 dist/qt_themes/*/icons/48x48/chip.png 63Comment: connected_notification and disconnected are modified
39 dist/qt_themes/*/icons/48x48/folder.png 64
40 dist/qt_themes/*/icons/48x48/no_avatar.png 65Files: dist/qt_themes/*/icons/48x48/no_avatar.png
41 dist/qt_themes/*/icons/48x48/sd_card.png 66Copyright: Ionic (http://ionic.io/)
42 dist/qt_themes/*/icons/48x48/star.png 67License: MIT
43 dist/qt_themes/*/icons/256x256/plus_folder.png 68
44 dist/qt_themes/colorful/icons/48x48/plus.png 69
70Files: dist/qt_themes/*/icons/48x48/sd_card.png
71 dist/qt_themes/colorful/icons/48x48/star.png
45 dist/qt_themes/default/icons/16x16/checked.png 72 dist/qt_themes/default/icons/16x16/checked.png
46 dist/qt_themes/default/icons/16x16/failed.png 73 dist/qt_themes/default/icons/16x16/failed.png
47Copyright: https://icons8.com 74Copyright: SVG Repo
48License: CC-BY-ND-3.0 75License: CC0-1.0
49 76
50Files: dist/qt_themes/*/icons/16x16/refresh.png 77Files: dist/qt_themes/*/icons/16x16/view-refresh.png
51 dist/qt_themes/*/icons/16x16/view-refresh.png 78 dist/qt_themes/default/icons/16x16/lock.png
79 dist/qt_themes/qdarkstyle/icons/16x16/lock.png
52Copyright: Google, Inc. 80Copyright: Google, Inc.
53License: Apache-2.0 81License: Apache-2.0
54 82
55Files: dist/qt_themes/default/icons/48x48/plus.png 83Files: dist/qt_themes/*/icons/48x48/list-add.png
56 dist/qt_themes/qdarkstyle/icons/48x48/plus.png 84Copyright: Docteh
57 dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/plus.png
58Copyright: BreadFish64
59License: CC0-1.0 85License: CC0-1.0
60 86
61Files: externals/getopt/getopt.c 87Files: externals/getopt/getopt.c
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5f508d61a..2ab0ea589 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -40,6 +40,8 @@ option(YUZU_TESTS "Compile tests" ON)
40 40
41option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}") 41option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}")
42 42
43option(YUZU_CHECK_SUBMODULES "Check if submodules are present" ON)
44
43if (YUZU_USE_BUNDLED_VCPKG) 45if (YUZU_USE_BUNDLED_VCPKG)
44 if (YUZU_TESTS) 46 if (YUZU_TESTS)
45 list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests") 47 list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests")
@@ -81,12 +83,17 @@ function(check_submodules_present)
81 endforeach() 83 endforeach()
82endfunction() 84endfunction()
83 85
84if(EXISTS ${PROJECT_SOURCE_DIR}/.gitmodules) 86if(EXISTS ${PROJECT_SOURCE_DIR}/.gitmodules AND YUZU_CHECK_SUBMODULES)
85 check_submodules_present() 87 check_submodules_present()
86endif() 88endif()
87configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc 89configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
88 ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc 90 ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
89 COPYONLY) 91 COPYONLY)
92if (EXISTS ${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.json)
93 configure_file("${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.json"
94 "${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json"
95 COPYONLY)
96endif()
90if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) 97if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
91 message(STATUS "Downloading compatibility list for yuzu...") 98 message(STATUS "Downloading compatibility list for yuzu...")
92 file(DOWNLOAD 99 file(DOWNLOAD
diff --git a/LICENSES/CC-BY-ND-3.0.txt b/LICENSES/CC-BY-ND-3.0.txt
deleted file mode 100644
index d9265b9f1..000000000
--- a/LICENSES/CC-BY-ND-3.0.txt
+++ /dev/null
@@ -1,87 +0,0 @@
1Creative Commons Attribution-NoDerivs 3.0 Unported
2
3 CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
4
5License
6
7THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
8
9BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
10
111. Definitions
12
13 a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
14
15 b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
16
17 c. "Distribute" means to make available to the public the original and copies of the Work through sale or other transfer of ownership.
18
19 d. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
20
21 e. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
22
23 f. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
24
25 g. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
26
27 h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
28
29 i. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
30
312. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
32
333. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
34
35 a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; and,
36
37 b. to Distribute and Publicly Perform the Work including as incorporated in Collections.
38
39 c. For the avoidance of doubt:
40
41 i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
42
43 ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
44
45 iii. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
46
47The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Adaptations. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
48
494. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
50
51 a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested.
52
53 b. If You Distribute, or Publicly Perform the Work or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. The credit required by this Section 4(b) may be implemented in any reasonable manner; provided, however, that in the case of a Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
54
55 c. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation.
56
575. Representations, Warranties and Disclaimer
58
59UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
60
616. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
62
637. Termination
64
65 a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
66
67 b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
68
698. Miscellaneous
70
71 a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
72
73 b. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
74
75 c. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
76
77 d. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
78
79 e. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
80
81Creative Commons Notice
82
83Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
84
85Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
86
87Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/LICENSES/CC-BY-SA-3.0.txt b/LICENSES/CC-BY-SA-3.0.txt
new file mode 100644
index 000000000..604209a80
--- /dev/null
+++ b/LICENSES/CC-BY-SA-3.0.txt
@@ -0,0 +1,359 @@
1Creative Commons Legal Code
2
3Attribution-ShareAlike 3.0 Unported
4
5 CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
7 ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
10 DAMAGES RESULTING FROM ITS USE.
11
12License
13
14THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
15COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
16COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
17AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
18
19BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
20TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
21BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
22CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
23CONDITIONS.
24
251. Definitions
26
27 a. "Adaptation" means a work based upon the Work, or upon the Work and
28 other pre-existing works, such as a translation, adaptation,
29 derivative work, arrangement of music or other alterations of a
30 literary or artistic work, or phonogram or performance and includes
31 cinematographic adaptations or any other form in which the Work may be
32 recast, transformed, or adapted including in any form recognizably
33 derived from the original, except that a work that constitutes a
34 Collection will not be considered an Adaptation for the purpose of
35 this License. For the avoidance of doubt, where the Work is a musical
36 work, performance or phonogram, the synchronization of the Work in
37 timed-relation with a moving image ("synching") will be considered an
38 Adaptation for the purpose of this License.
39 b. "Collection" means a collection of literary or artistic works, such as
40 encyclopedias and anthologies, or performances, phonograms or
41 broadcasts, or other works or subject matter other than works listed
42 in Section 1(f) below, which, by reason of the selection and
43 arrangement of their contents, constitute intellectual creations, in
44 which the Work is included in its entirety in unmodified form along
45 with one or more other contributions, each constituting separate and
46 independent works in themselves, which together are assembled into a
47 collective whole. A work that constitutes a Collection will not be
48 considered an Adaptation (as defined below) for the purposes of this
49 License.
50 c. "Creative Commons Compatible License" means a license that is listed
51 at https://creativecommons.org/compatiblelicenses that has been
52 approved by Creative Commons as being essentially equivalent to this
53 License, including, at a minimum, because that license: (i) contains
54 terms that have the same purpose, meaning and effect as the License
55 Elements of this License; and, (ii) explicitly permits the relicensing
56 of adaptations of works made available under that license under this
57 License or a Creative Commons jurisdiction license with the same
58 License Elements as this License.
59 d. "Distribute" means to make available to the public the original and
60 copies of the Work or Adaptation, as appropriate, through sale or
61 other transfer of ownership.
62 e. "License Elements" means the following high-level license attributes
63 as selected by Licensor and indicated in the title of this License:
64 Attribution, ShareAlike.
65 f. "Licensor" means the individual, individuals, entity or entities that
66 offer(s) the Work under the terms of this License.
67 g. "Original Author" means, in the case of a literary or artistic work,
68 the individual, individuals, entity or entities who created the Work
69 or if no individual or entity can be identified, the publisher; and in
70 addition (i) in the case of a performance the actors, singers,
71 musicians, dancers, and other persons who act, sing, deliver, declaim,
72 play in, interpret or otherwise perform literary or artistic works or
73 expressions of folklore; (ii) in the case of a phonogram the producer
74 being the person or legal entity who first fixes the sounds of a
75 performance or other sounds; and, (iii) in the case of broadcasts, the
76 organization that transmits the broadcast.
77 h. "Work" means the literary and/or artistic work offered under the terms
78 of this License including without limitation any production in the
79 literary, scientific and artistic domain, whatever may be the mode or
80 form of its expression including digital form, such as a book,
81 pamphlet and other writing; a lecture, address, sermon or other work
82 of the same nature; a dramatic or dramatico-musical work; a
83 choreographic work or entertainment in dumb show; a musical
84 composition with or without words; a cinematographic work to which are
85 assimilated works expressed by a process analogous to cinematography;
86 a work of drawing, painting, architecture, sculpture, engraving or
87 lithography; a photographic work to which are assimilated works
88 expressed by a process analogous to photography; a work of applied
89 art; an illustration, map, plan, sketch or three-dimensional work
90 relative to geography, topography, architecture or science; a
91 performance; a broadcast; a phonogram; a compilation of data to the
92 extent it is protected as a copyrightable work; or a work performed by
93 a variety or circus performer to the extent it is not otherwise
94 considered a literary or artistic work.
95 i. "You" means an individual or entity exercising rights under this
96 License who has not previously violated the terms of this License with
97 respect to the Work, or who has received express permission from the
98 Licensor to exercise rights under this License despite a previous
99 violation.
100 j. "Publicly Perform" means to perform public recitations of the Work and
101 to communicate to the public those public recitations, by any means or
102 process, including by wire or wireless means or public digital
103 performances; to make available to the public Works in such a way that
104 members of the public may access these Works from a place and at a
105 place individually chosen by them; to perform the Work to the public
106 by any means or process and the communication to the public of the
107 performances of the Work, including by public digital performance; to
108 broadcast and rebroadcast the Work by any means including signs,
109 sounds or images.
110 k. "Reproduce" means to make copies of the Work by any means including
111 without limitation by sound or visual recordings and the right of
112 fixation and reproducing fixations of the Work, including storage of a
113 protected performance or phonogram in digital form or other electronic
114 medium.
115
1162. Fair Dealing Rights. Nothing in this License is intended to reduce,
117limit, or restrict any uses free from copyright or rights arising from
118limitations or exceptions that are provided for in connection with the
119copyright protection under copyright law or other applicable laws.
120
1213. License Grant. Subject to the terms and conditions of this License,
122Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
123perpetual (for the duration of the applicable copyright) license to
124exercise the rights in the Work as stated below:
125
126 a. to Reproduce the Work, to incorporate the Work into one or more
127 Collections, and to Reproduce the Work as incorporated in the
128 Collections;
129 b. to create and Reproduce Adaptations provided that any such Adaptation,
130 including any translation in any medium, takes reasonable steps to
131 clearly label, demarcate or otherwise identify that changes were made
132 to the original Work. For example, a translation could be marked "The
133 original work was translated from English to Spanish," or a
134 modification could indicate "The original work has been modified.";
135 c. to Distribute and Publicly Perform the Work including as incorporated
136 in Collections; and,
137 d. to Distribute and Publicly Perform Adaptations.
138 e. For the avoidance of doubt:
139
140 i. Non-waivable Compulsory License Schemes. In those jurisdictions in
141 which the right to collect royalties through any statutory or
142 compulsory licensing scheme cannot be waived, the Licensor
143 reserves the exclusive right to collect such royalties for any
144 exercise by You of the rights granted under this License;
145 ii. Waivable Compulsory License Schemes. In those jurisdictions in
146 which the right to collect royalties through any statutory or
147 compulsory licensing scheme can be waived, the Licensor waives the
148 exclusive right to collect such royalties for any exercise by You
149 of the rights granted under this License; and,
150 iii. Voluntary License Schemes. The Licensor waives the right to
151 collect royalties, whether individually or, in the event that the
152 Licensor is a member of a collecting society that administers
153 voluntary licensing schemes, via that society, from any exercise
154 by You of the rights granted under this License.
155
156The above rights may be exercised in all media and formats whether now
157known or hereafter devised. The above rights include the right to make
158such modifications as are technically necessary to exercise the rights in
159other media and formats. Subject to Section 8(f), all rights not expressly
160granted by Licensor are hereby reserved.
161
1624. Restrictions. The license granted in Section 3 above is expressly made
163subject to and limited by the following restrictions:
164
165 a. You may Distribute or Publicly Perform the Work only under the terms
166 of this License. You must include a copy of, or the Uniform Resource
167 Identifier (URI) for, this License with every copy of the Work You
168 Distribute or Publicly Perform. You may not offer or impose any terms
169 on the Work that restrict the terms of this License or the ability of
170 the recipient of the Work to exercise the rights granted to that
171 recipient under the terms of the License. You may not sublicense the
172 Work. You must keep intact all notices that refer to this License and
173 to the disclaimer of warranties with every copy of the Work You
174 Distribute or Publicly Perform. When You Distribute or Publicly
175 Perform the Work, You may not impose any effective technological
176 measures on the Work that restrict the ability of a recipient of the
177 Work from You to exercise the rights granted to that recipient under
178 the terms of the License. This Section 4(a) applies to the Work as
179 incorporated in a Collection, but this does not require the Collection
180 apart from the Work itself to be made subject to the terms of this
181 License. If You create a Collection, upon notice from any Licensor You
182 must, to the extent practicable, remove from the Collection any credit
183 as required by Section 4(c), as requested. If You create an
184 Adaptation, upon notice from any Licensor You must, to the extent
185 practicable, remove from the Adaptation any credit as required by
186 Section 4(c), as requested.
187 b. You may Distribute or Publicly Perform an Adaptation only under the
188 terms of: (i) this License; (ii) a later version of this License with
189 the same License Elements as this License; (iii) a Creative Commons
190 jurisdiction license (either this or a later license version) that
191 contains the same License Elements as this License (e.g.,
192 Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible
193 License. If you license the Adaptation under one of the licenses
194 mentioned in (iv), you must comply with the terms of that license. If
195 you license the Adaptation under the terms of any of the licenses
196 mentioned in (i), (ii) or (iii) (the "Applicable License"), you must
197 comply with the terms of the Applicable License generally and the
198 following provisions: (I) You must include a copy of, or the URI for,
199 the Applicable License with every copy of each Adaptation You
200 Distribute or Publicly Perform; (II) You may not offer or impose any
201 terms on the Adaptation that restrict the terms of the Applicable
202 License or the ability of the recipient of the Adaptation to exercise
203 the rights granted to that recipient under the terms of the Applicable
204 License; (III) You must keep intact all notices that refer to the
205 Applicable License and to the disclaimer of warranties with every copy
206 of the Work as included in the Adaptation You Distribute or Publicly
207 Perform; (IV) when You Distribute or Publicly Perform the Adaptation,
208 You may not impose any effective technological measures on the
209 Adaptation that restrict the ability of a recipient of the Adaptation
210 from You to exercise the rights granted to that recipient under the
211 terms of the Applicable License. This Section 4(b) applies to the
212 Adaptation as incorporated in a Collection, but this does not require
213 the Collection apart from the Adaptation itself to be made subject to
214 the terms of the Applicable License.
215 c. If You Distribute, or Publicly Perform the Work or any Adaptations or
216 Collections, You must, unless a request has been made pursuant to
217 Section 4(a), keep intact all copyright notices for the Work and
218 provide, reasonable to the medium or means You are utilizing: (i) the
219 name of the Original Author (or pseudonym, if applicable) if supplied,
220 and/or if the Original Author and/or Licensor designate another party
221 or parties (e.g., a sponsor institute, publishing entity, journal) for
222 attribution ("Attribution Parties") in Licensor's copyright notice,
223 terms of service or by other reasonable means, the name of such party
224 or parties; (ii) the title of the Work if supplied; (iii) to the
225 extent reasonably practicable, the URI, if any, that Licensor
226 specifies to be associated with the Work, unless such URI does not
227 refer to the copyright notice or licensing information for the Work;
228 and (iv) , consistent with Ssection 3(b), in the case of an
229 Adaptation, a credit identifying the use of the Work in the Adaptation
230 (e.g., "French translation of the Work by Original Author," or
231 "Screenplay based on original Work by Original Author"). The credit
232 required by this Section 4(c) may be implemented in any reasonable
233 manner; provided, however, that in the case of a Adaptation or
234 Collection, at a minimum such credit will appear, if a credit for all
235 contributing authors of the Adaptation or Collection appears, then as
236 part of these credits and in a manner at least as prominent as the
237 credits for the other contributing authors. For the avoidance of
238 doubt, You may only use the credit required by this Section for the
239 purpose of attribution in the manner set out above and, by exercising
240 Your rights under this License, You may not implicitly or explicitly
241 assert or imply any connection with, sponsorship or endorsement by the
242 Original Author, Licensor and/or Attribution Parties, as appropriate,
243 of You or Your use of the Work, without the separate, express prior
244 written permission of the Original Author, Licensor and/or Attribution
245 Parties.
246 d. Except as otherwise agreed in writing by the Licensor or as may be
247 otherwise permitted by applicable law, if You Reproduce, Distribute or
248 Publicly Perform the Work either by itself or as part of any
249 Adaptations or Collections, You must not distort, mutilate, modify or
250 take other derogatory action in relation to the Work which would be
251 prejudicial to the Original Author's honor or reputation. Licensor
252 agrees that in those jurisdictions (e.g. Japan), in which any exercise
253 of the right granted in Section 3(b) of this License (the right to
254 make Adaptations) would be deemed to be a distortion, mutilation,
255 modification or other derogatory action prejudicial to the Original
256 Author's honor and reputation, the Licensor will waive or not assert,
257 as appropriate, this Section, to the fullest extent permitted by the
258 applicable national law, to enable You to reasonably exercise Your
259 right under Section 3(b) of this License (right to make Adaptations)
260 but not otherwise.
261
2625. Representations, Warranties and Disclaimer
263
264UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
265OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
266KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
267INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
268FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
269LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
270WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
271OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
272
2736. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
274LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
275ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
276ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
277BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
278
2797. Termination
280
281 a. This License and the rights granted hereunder will terminate
282 automatically upon any breach by You of the terms of this License.
283 Individuals or entities who have received Adaptations or Collections
284 from You under this License, however, will not have their licenses
285 terminated provided such individuals or entities remain in full
286 compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
287 survive any termination of this License.
288 b. Subject to the above terms and conditions, the license granted here is
289 perpetual (for the duration of the applicable copyright in the Work).
290 Notwithstanding the above, Licensor reserves the right to release the
291 Work under different license terms or to stop distributing the Work at
292 any time; provided, however that any such election will not serve to
293 withdraw this License (or any other license that has been, or is
294 required to be, granted under the terms of this License), and this
295 License will continue in full force and effect unless terminated as
296 stated above.
297
2988. Miscellaneous
299
300 a. Each time You Distribute or Publicly Perform the Work or a Collection,
301 the Licensor offers to the recipient a license to the Work on the same
302 terms and conditions as the license granted to You under this License.
303 b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
304 offers to the recipient a license to the original Work on the same
305 terms and conditions as the license granted to You under this License.
306 c. If any provision of this License is invalid or unenforceable under
307 applicable law, it shall not affect the validity or enforceability of
308 the remainder of the terms of this License, and without further action
309 by the parties to this agreement, such provision shall be reformed to
310 the minimum extent necessary to make such provision valid and
311 enforceable.
312 d. No term or provision of this License shall be deemed waived and no
313 breach consented to unless such waiver or consent shall be in writing
314 and signed by the party to be charged with such waiver or consent.
315 e. This License constitutes the entire agreement between the parties with
316 respect to the Work licensed here. There are no understandings,
317 agreements or representations with respect to the Work not specified
318 here. Licensor shall not be bound by any additional provisions that
319 may appear in any communication from You. This License may not be
320 modified without the mutual written agreement of the Licensor and You.
321 f. The rights granted under, and the subject matter referenced, in this
322 License were drafted utilizing the terminology of the Berne Convention
323 for the Protection of Literary and Artistic Works (as amended on
324 September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
325 Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
326 and the Universal Copyright Convention (as revised on July 24, 1971).
327 These rights and subject matter take effect in the relevant
328 jurisdiction in which the License terms are sought to be enforced
329 according to the corresponding provisions of the implementation of
330 those treaty provisions in the applicable national law. If the
331 standard suite of rights granted under applicable copyright law
332 includes additional rights not granted under this License, such
333 additional rights are deemed to be included in the License; this
334 License is not intended to restrict the license of any rights under
335 applicable law.
336
337
338Creative Commons Notice
339
340 Creative Commons is not a party to this License, and makes no warranty
341 whatsoever in connection with the Work. Creative Commons will not be
342 liable to You or any party on any legal theory for any damages
343 whatsoever, including without limitation any general, special,
344 incidental or consequential damages arising in connection to this
345 license. Notwithstanding the foregoing two (2) sentences, if Creative
346 Commons has expressly identified itself as the Licensor hereunder, it
347 shall have all rights and obligations of Licensor.
348
349 Except for the limited purpose of indicating to the public that the
350 Work is licensed under the CCPL, Creative Commons does not authorize
351 the use by either party of the trademark "Creative Commons" or any
352 related trademark or logo of Creative Commons without the prior
353 written consent of Creative Commons. Any permitted use will be in
354 compliance with Creative Commons' then-current trademark usage
355 guidelines, as may be published on its website or otherwise made
356 available upon request from time to time. For the avoidance of doubt,
357 this trademark restriction does not form part of the License.
358
359 Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/dist/english_plurals/README.md b/dist/english_plurals/README.md
new file mode 100644
index 000000000..a4954f6a6
--- /dev/null
+++ b/dist/english_plurals/README.md
@@ -0,0 +1,19 @@
1# English Plurals
2
3Qt has "Translation Rules for Plurals", small example
4
5 // Take a source line like
6 tr("Building: %n shader(s)", "", i)
7
8 // i = 1:
9 Building: 1 shader
10 // i = 2:
11 Building: 2 shaders
12
13For yuzu the source language used is English, for all other languages handling of plurals is handled by Qt and the translation collaboration site. Handling plurals in the source language (English) requires special consideration.
14
15With CMake flag GENERATE_QT_TRANSLATION a generated_en.ts file is created from the source. It ignored by git (`.gitignore` in the project root). It is placed in this directory so that the relative refrences with the source code is correct.
16
17Having the plurals look nice isn't critical, and automation to use translation collaboration sites may require specifing the project language as "Pirate English", so this has been done manually.
18
19The en.ts in this directory is taken from a build, edited in Qt Linguist and then committed. As the code is in XML, using the tool is not strictly required.
diff --git a/dist/english_plurals/en.ts b/dist/english_plurals/en.ts
new file mode 100644
index 000000000..172cd4bba
--- /dev/null
+++ b/dist/english_plurals/en.ts
@@ -0,0 +1,67 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!DOCTYPE TS>
3<TS version="2.1" language="en_US" sourcelanguage="en_US">
4<context>
5 <name>GMainWindow</name>
6 <message numerus="yes">
7 <location filename="../../src/yuzu/main.cpp" line="2322"/>
8 <source>%n file(s) remaining</source>
9 <translation>
10 <numerusform>%n file remaining</numerusform>
11 <numerusform>%n files remaining</numerusform>
12 </translation>
13 </message>
14 <message numerus="yes">
15 <location filename="../../src/yuzu/main.cpp" line="2377"/>
16 <source>%n file(s) were newly installed
17</source>
18 <translation>
19 <numerusform>%n file was newly installed
20</numerusform>
21 <numerusform>%n files were newly installed
22</numerusform>
23 </translation>
24 </message>
25 <message numerus="yes">
26 <location filename="../../src/yuzu/main.cpp" line="2380"/>
27 <source>%n file(s) were overwritten
28</source>
29 <translation>
30 <numerusform>%n file was overwritten
31</numerusform>
32 <numerusform>%n were overwritten
33</numerusform>
34 </translation>
35 </message>
36 <message numerus="yes">
37 <location filename="../../src/yuzu/main.cpp" line="2382"/>
38 <source>%n file(s) failed to install
39</source>
40 <translation>
41 <numerusform>%n file failed to install
42</numerusform>
43 <numerusform>%n files failed to install
44</numerusform>
45 </translation>
46 </message>
47 <message numerus="yes">
48 <location filename="../../src/yuzu/main.cpp" line="3264"/>
49 <source>Building: %n shader(s)</source>
50 <translation>
51 <numerusform>Building: %n shader</numerusform>
52 <numerusform>Building: %n shaders</numerusform>
53 </translation>
54 </message>
55</context>
56<context>
57 <name>GameListSearchField</name>
58 <message numerus="yes">
59 <location filename="../../src/yuzu/game_list.cpp" line="87"/>
60 <source>%1 of %n result(s)</source>
61 <translation>
62 <numerusform>%1 of %n result</numerusform>
63 <numerusform>%1 of %n results</numerusform>
64 </translation>
65 </message>
66</context>
67</TS>
diff --git a/dist/qt_themes/colorful/icons/16x16/connected.png b/dist/qt_themes/colorful/icons/16x16/connected.png
index d6052f1a0..0afc18cb7 100644
--- a/dist/qt_themes/colorful/icons/16x16/connected.png
+++ b/dist/qt_themes/colorful/icons/16x16/connected.png
Binary files differ
diff --git a/dist/qt_themes/colorful/icons/16x16/connected_notification.png b/dist/qt_themes/colorful/icons/16x16/connected_notification.png
index 0dfe032d5..72466e098 100644
--- a/dist/qt_themes/colorful/icons/16x16/connected_notification.png
+++ b/dist/qt_themes/colorful/icons/16x16/connected_notification.png
Binary files differ
diff --git a/dist/qt_themes/colorful/icons/16x16/disconnected.png b/dist/qt_themes/colorful/icons/16x16/disconnected.png
index bacee3aeb..7258a8cfe 100644
--- a/dist/qt_themes/colorful/icons/16x16/disconnected.png
+++ b/dist/qt_themes/colorful/icons/16x16/disconnected.png
Binary files differ
diff --git a/dist/qt_themes/colorful/icons/48x48/list-add.png b/dist/qt_themes/colorful/icons/48x48/list-add.png
new file mode 100644
index 000000000..74e4882aa
--- /dev/null
+++ b/dist/qt_themes/colorful/icons/48x48/list-add.png
Binary files differ
diff --git a/dist/qt_themes/colorful/icons/48x48/plus.png b/dist/qt_themes/colorful/icons/48x48/plus.png
deleted file mode 100644
index bc2c47c91..000000000
--- a/dist/qt_themes/colorful/icons/48x48/plus.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/colorful/icons/48x48/sd_card.png b/dist/qt_themes/colorful/icons/48x48/sd_card.png
index 29be71a0d..47e491d32 100644
--- a/dist/qt_themes/colorful/icons/48x48/sd_card.png
+++ b/dist/qt_themes/colorful/icons/48x48/sd_card.png
Binary files differ
diff --git a/dist/qt_themes/colorful/icons/48x48/star.png b/dist/qt_themes/colorful/icons/48x48/star.png
index 43b5d52ed..19d55a0a8 100644
--- a/dist/qt_themes/colorful/icons/48x48/star.png
+++ b/dist/qt_themes/colorful/icons/48x48/star.png
Binary files differ
diff --git a/dist/qt_themes/colorful/style.qrc b/dist/qt_themes/colorful/style.qrc
index 4b3f28d89..507e0e58b 100644
--- a/dist/qt_themes/colorful/style.qrc
+++ b/dist/qt_themes/colorful/style.qrc
@@ -13,7 +13,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
13 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> 13 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
14 <file alias="48x48/chip.png">icons/48x48/chip.png</file> 14 <file alias="48x48/chip.png">icons/48x48/chip.png</file>
15 <file alias="48x48/folder.png">icons/48x48/folder.png</file> 15 <file alias="48x48/folder.png">icons/48x48/folder.png</file>
16 <file alias="48x48/plus.png">icons/48x48/plus.png</file> 16 <file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
17 <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file> 17 <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
18 <file alias="48x48/star.png">icons/48x48/star.png</file> 18 <file alias="48x48/star.png">icons/48x48/star.png</file>
19 <file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file> 19 <file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
diff --git a/dist/qt_themes/colorful_dark/icons/16x16/refresh.png b/dist/qt_themes/colorful_dark/icons/16x16/refresh.png
deleted file mode 100644
index d4afd76f9..000000000
--- a/dist/qt_themes/colorful_dark/icons/16x16/refresh.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/colorful_dark/icons/index.theme b/dist/qt_themes/colorful_dark/icons/index.theme
index 19dc0369a..b37a06df7 100644
--- a/dist/qt_themes/colorful_dark/icons/index.theme
+++ b/dist/qt_themes/colorful_dark/icons/index.theme
@@ -3,6 +3,6 @@ Name=colorful_dark
3Comment=Colorful theme (Dark style) 3Comment=Colorful theme (Dark style)
4Inherits=colorful 4Inherits=colorful
5Directories=16x16 5Directories=16x16
6 6
7[16x16] 7[16x16]
8Size=16 8Size=16
diff --git a/dist/qt_themes/colorful_dark/style.qrc b/dist/qt_themes/colorful_dark/style.qrc
index 50e78c37b..9853fd438 100644
--- a/dist/qt_themes/colorful_dark/style.qrc
+++ b/dist/qt_themes/colorful_dark/style.qrc
@@ -15,7 +15,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
15 <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file> 15 <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
16 <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file> 16 <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
17 <file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file> 17 <file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
18 <file alias="48x48/plus.png">../colorful/icons/48x48/plus.png</file> 18 <file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
19 <file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file> 19 <file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
20 <file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file> 20 <file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file>
21 </qresource> 21 </qresource>
diff --git a/dist/qt_themes/colorful_midnight_blue/icons/16x16/lock.png b/dist/qt_themes/colorful_midnight_blue/icons/16x16/lock.png
deleted file mode 100644
index 32c505848..000000000
--- a/dist/qt_themes/colorful_midnight_blue/icons/16x16/lock.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.png b/dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.png
deleted file mode 100644
index d4afd76f9..000000000
--- a/dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.png b/dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.png
deleted file mode 100644
index d4afd76f9..000000000
--- a/dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/colorful_midnight_blue/style.qrc b/dist/qt_themes/colorful_midnight_blue/style.qrc
index ac8cb0d49..b9821c672 100644
--- a/dist/qt_themes/colorful_midnight_blue/style.qrc
+++ b/dist/qt_themes/colorful_midnight_blue/style.qrc
@@ -6,12 +6,12 @@ SPDX-License-Identifier: GPL-2.0-or-later
6<RCC> 6<RCC>
7 <qresource prefix="icons/colorful_midnight_blue"> 7 <qresource prefix="icons/colorful_midnight_blue">
8 <file alias="index.theme">icons/index.theme</file> 8 <file alias="index.theme">icons/index.theme</file>
9 <file alias="16x16/lock.png">icons/16x16/lock.png</file> 9 <file alias="16x16/lock.png">../colorful_dark/icons/16x16/lock.png</file>
10 <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file> 10 <file alias="16x16/view-refresh.png">../qdarkstyle/icons/16x16/view-refresh.png</file>
11 <file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file> 11 <file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
12 <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file> 12 <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
13 <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file> 13 <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
14 <file alias="48x48/plus.png">../colorful/icons/48x48/plus.png</file> 14 <file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
15 <file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file> 15 <file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
16 <file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file> 16 <file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file>
17 </qresource> 17 </qresource>
diff --git a/dist/qt_themes/default/default.qrc b/dist/qt_themes/default/default.qrc
index ef080c221..a07f2a9c1 100644
--- a/dist/qt_themes/default/default.qrc
+++ b/dist/qt_themes/default/default.qrc
@@ -17,7 +17,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
17 <file alias="48x48/chip.png">icons/48x48/chip.png</file> 17 <file alias="48x48/chip.png">icons/48x48/chip.png</file>
18 <file alias="48x48/folder.png">icons/48x48/folder.png</file> 18 <file alias="48x48/folder.png">icons/48x48/folder.png</file>
19 <file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file> 19 <file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
20 <file alias="48x48/plus.png">icons/48x48/plus.png</file> 20 <file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
21 <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file> 21 <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
22 <file alias="48x48/star.png">icons/48x48/star.png</file> 22 <file alias="48x48/star.png">icons/48x48/star.png</file>
23 <file alias="256x256/yuzu.png">icons/256x256/yuzu.png</file> 23 <file alias="256x256/yuzu.png">icons/256x256/yuzu.png</file>
diff --git a/dist/qt_themes/default/icons/16x16/checked.png b/dist/qt_themes/default/icons/16x16/checked.png
index 3e017b715..b9e64e9e0 100644
--- a/dist/qt_themes/default/icons/16x16/checked.png
+++ b/dist/qt_themes/default/icons/16x16/checked.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/16x16/connected.png b/dist/qt_themes/default/icons/16x16/connected.png
index afa797394..0afc18cb7 100644
--- a/dist/qt_themes/default/icons/16x16/connected.png
+++ b/dist/qt_themes/default/icons/16x16/connected.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/16x16/connected_notification.png b/dist/qt_themes/default/icons/16x16/connected_notification.png
index e64901378..72466e098 100644
--- a/dist/qt_themes/default/icons/16x16/connected_notification.png
+++ b/dist/qt_themes/default/icons/16x16/connected_notification.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/16x16/disconnected.png b/dist/qt_themes/default/icons/16x16/disconnected.png
index 835b1f0d6..7258a8cfe 100644
--- a/dist/qt_themes/default/icons/16x16/disconnected.png
+++ b/dist/qt_themes/default/icons/16x16/disconnected.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/16x16/failed.png b/dist/qt_themes/default/icons/16x16/failed.png
index 7c4047dd0..a1872835d 100644
--- a/dist/qt_themes/default/icons/16x16/failed.png
+++ b/dist/qt_themes/default/icons/16x16/failed.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/16x16/lock.png b/dist/qt_themes/default/icons/16x16/lock.png
index 496b58078..69d399050 100644
--- a/dist/qt_themes/default/icons/16x16/lock.png
+++ b/dist/qt_themes/default/icons/16x16/lock.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/16x16/refresh.png b/dist/qt_themes/default/icons/16x16/refresh.png
deleted file mode 100644
index 69f9474ac..000000000
--- a/dist/qt_themes/default/icons/16x16/refresh.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/default/icons/256x256/plus_folder.png b/dist/qt_themes/default/icons/256x256/plus_folder.png
index ae4afccc7..3a49669a3 100644
--- a/dist/qt_themes/default/icons/256x256/plus_folder.png
+++ b/dist/qt_themes/default/icons/256x256/plus_folder.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/48x48/bad_folder.png b/dist/qt_themes/default/icons/48x48/bad_folder.png
index 2527c1318..364ec646f 100644
--- a/dist/qt_themes/default/icons/48x48/bad_folder.png
+++ b/dist/qt_themes/default/icons/48x48/bad_folder.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/48x48/chip.png b/dist/qt_themes/default/icons/48x48/chip.png
index 3efdf301e..1b573d51a 100644
--- a/dist/qt_themes/default/icons/48x48/chip.png
+++ b/dist/qt_themes/default/icons/48x48/chip.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/48x48/folder.png b/dist/qt_themes/default/icons/48x48/folder.png
index 2e67d8b38..507337fae 100644
--- a/dist/qt_themes/default/icons/48x48/folder.png
+++ b/dist/qt_themes/default/icons/48x48/folder.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/48x48/list-add.png b/dist/qt_themes/default/icons/48x48/list-add.png
new file mode 100644
index 000000000..fd8a06132
--- /dev/null
+++ b/dist/qt_themes/default/icons/48x48/list-add.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/48x48/no_avatar.png b/dist/qt_themes/default/icons/48x48/no_avatar.png
index d4bf82026..76f812349 100644
--- a/dist/qt_themes/default/icons/48x48/no_avatar.png
+++ b/dist/qt_themes/default/icons/48x48/no_avatar.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/48x48/plus.png b/dist/qt_themes/default/icons/48x48/plus.png
deleted file mode 100644
index dbc74687b..000000000
--- a/dist/qt_themes/default/icons/48x48/plus.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/default/icons/48x48/sd_card.png b/dist/qt_themes/default/icons/48x48/sd_card.png
index edacaeeb5..60dfba269 100644
--- a/dist/qt_themes/default/icons/48x48/sd_card.png
+++ b/dist/qt_themes/default/icons/48x48/sd_card.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/48x48/star.png b/dist/qt_themes/default/icons/48x48/star.png
index 740f7f3e7..c2b78f0c3 100644
--- a/dist/qt_themes/default/icons/48x48/star.png
+++ b/dist/qt_themes/default/icons/48x48/star.png
Binary files differ
diff --git a/dist/qt_themes/default_dark/icons/index.theme b/dist/qt_themes/default_dark/icons/index.theme
new file mode 100644
index 000000000..60a072d1d
--- /dev/null
+++ b/dist/qt_themes/default_dark/icons/index.theme
@@ -0,0 +1,8 @@
1[Icon Theme]
2Name=default_dark
3Comment=Colorful theme (Dark style)
4Inherits=colorful
5Directories=16x16
6
7[16x16]
8Size=16
diff --git a/dist/qt_themes/default_dark/style.qrc b/dist/qt_themes/default_dark/style.qrc
new file mode 100644
index 000000000..7de4737c2
--- /dev/null
+++ b/dist/qt_themes/default_dark/style.qrc
@@ -0,0 +1,25 @@
1<!--
2SPDX-FileCopyrightText: 2022 yuzu Emulator Project
3SPDX-License-Identifier: GPL-2.0-or-later
4-->
5<RCC>
6 <qresource prefix="icons/default_dark">
7 <file alias="16x16/connected.png">../colorful/icons/16x16/connected.png</file>
8 <file alias="16x16/connected_notification.png">../colorful/icons/16x16/connected_notification.png</file>
9 <file alias="16x16/disconnected.png">../colorful/icons/16x16/disconnected.png</file>
10 <file alias="index.theme">icons/index.theme</file>
11 <file alias="16x16/lock.png">../colorful_dark/icons/16x16/lock.png</file>
12 <file alias="16x16/view-refresh.png">../colorful_dark/icons/16x16/view-refresh.png</file>
13 <file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
14 <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
15 <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
16 <file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
17 <file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
18 <file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
19 <file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file>
20 </qresource>
21
22 <qresource prefix="default_dark">
23 <file>style.qss</file>
24 </qresource>
25</RCC>
diff --git a/dist/qt_themes/default_dark/style.qss b/dist/qt_themes/default_dark/style.qss
new file mode 100644
index 000000000..ca6daa2d5
--- /dev/null
+++ b/dist/qt_themes/default_dark/style.qss
@@ -0,0 +1,687 @@
1/*
2* SPDX-FileCopyrightText: 2018 yuzu Emulator Project
3* SPDX-License-Identifier: GPL-2.0-or-later
4*/
5QAbstractSpinBox {
6 min-height: 19px;
7}
8
9QPushButton#TogglableStatusBarButton {
10 color: #959595;
11 border: 1px solid transparent;
12 background-color: transparent;
13 padding: 0px 3px 0px 3px;
14 text-align: center;
15}
16
17QPushButton#TogglableStatusBarButton:checked {
18 color: palette(text);
19}
20
21QPushButton#TogglableStatusBarButton:hover {
22 border: 1px solid #76797C;
23}
24
25QPushButton#RendererStatusBarButton {
26 color: #656565;
27 border: 1px solid transparent;
28 background-color: transparent;
29 padding: 0px 3px 0px 3px;
30 text-align: center;
31}
32
33QPushButton#RendererStatusBarButton:hover {
34 border: 1px solid #76797C;
35}
36
37QPushButton#RendererStatusBarButton:checked {
38 color: #e85c00;
39}
40
41QPushButton#RendererStatusBarButton:!checked {
42 color: #00ccdd;
43}
44
45QPushButton#GPUStatusBarButton {
46 color: #656565;
47 border: 1px solid transparent;
48 background-color: transparent;
49 padding: 0px 3px 0px 3px;
50 text-align: center;
51}
52
53QPushButton#GPUStatusBarButton:hover {
54 border: 1px solid #76797C;
55}
56
57QPushButton#GPUStatusBarButton:checked {
58 color: #ff8040;
59}
60
61QPushButton#GPUStatusBarButton:!checked {
62 color: #40dd40;
63}
64
65QPushButton#DockingStatusBarButton {
66 min-width: 0px;
67 color: palette(text);
68 border: 1px solid transparent;
69 background-color: transparent;
70 padding: 0px 3px 0px 3px;
71 text-align: center;
72}
73
74QPushButton#DockingStatusBarButton:hover {
75 border: 1px solid #76797C;
76}
77
78QPushButton#buttonRefreshDevices {
79 min-width: 21px;
80 min-height: 21px;
81 max-width: 21px;
82 max-height: 21px;
83}
84
85QWidget#bottomPerGameInput,
86QWidget#topControllerApplet,
87QWidget#bottomControllerApplet,
88QGroupBox#groupPlayer1Connected:checked,
89QGroupBox#groupPlayer2Connected:checked,
90QGroupBox#groupPlayer3Connected:checked,
91QGroupBox#groupPlayer4Connected:checked,
92QGroupBox#groupPlayer5Connected:checked,
93QGroupBox#groupPlayer6Connected:checked,
94QGroupBox#groupPlayer7Connected:checked,
95QGroupBox#groupPlayer8Connected:checked {
96 background-color: #f5f5f5;
97}
98
99QWidget#topControllerApplet {
100 border-bottom: 1px solid #828790
101}
102
103QWidget#bottomPerGameInput,
104QWidget#bottomControllerApplet {
105 border-top: 1px solid #828790
106}
107
108QWidget#topPerGameInput,
109QWidget#middleControllerApplet {
110 background-color: #fff;
111}
112
113QWidget#topPerGameInput QComboBox,
114QWidget#middleControllerApplet QComboBox {
115 width: 120px;
116}
117
118QWidget#connectedControllers {
119 background: transparent;
120}
121
122QWidget#playersSupported,
123QWidget#controllersSupported,
124QWidget#controllerSupported1,
125QWidget#controllerSupported2,
126QWidget#controllerSupported3,
127QWidget#controllerSupported4,
128QWidget#controllerSupported5,
129QWidget#controllerSupported6 {
130 border: none;
131 background: transparent;
132}
133
134QGroupBox#groupPlayer1Connected,
135QGroupBox#groupPlayer2Connected,
136QGroupBox#groupPlayer3Connected,
137QGroupBox#groupPlayer4Connected,
138QGroupBox#groupPlayer5Connected,
139QGroupBox#groupPlayer6Connected,
140QGroupBox#groupPlayer7Connected,
141QGroupBox#groupPlayer8Connected {
142 border: 1px solid #828790;
143 border-radius: 3px;
144 padding: 0px;
145 min-height: 98px;
146 max-height: 98px;
147}
148
149QGroupBox#groupPlayer1Connected:unchecked,
150QGroupBox#groupPlayer2Connected:unchecked,
151QGroupBox#groupPlayer3Connected:unchecked,
152QGroupBox#groupPlayer4Connected:unchecked,
153QGroupBox#groupPlayer5Connected:unchecked,
154QGroupBox#groupPlayer6Connected:unchecked,
155QGroupBox#groupPlayer7Connected:unchecked,
156QGroupBox#groupPlayer8Connected:unchecked {
157 border: 1px solid #d9d9d9;
158}
159
160QGroupBox#groupPlayer1Connected::title,
161QGroupBox#groupPlayer2Connected::title,
162QGroupBox#groupPlayer3Connected::title,
163QGroupBox#groupPlayer4Connected::title,
164QGroupBox#groupPlayer5Connected::title,
165QGroupBox#groupPlayer6Connected::title,
166QGroupBox#groupPlayer7Connected::title,
167QGroupBox#groupPlayer8Connected::title {
168 subcontrol-origin: margin;
169 subcontrol-position: top left;
170 padding-left: 0px;
171 padding-right: 0px;
172 padding-top: 1px;
173 margin-left: 0px;
174 margin-right: -4px;
175 margin-bottom: 4px;
176}
177
178QCheckBox#checkboxPlayer1Connected,
179QCheckBox#checkboxPlayer2Connected,
180QCheckBox#checkboxPlayer3Connected,
181QCheckBox#checkboxPlayer4Connected,
182QCheckBox#checkboxPlayer5Connected,
183QCheckBox#checkboxPlayer6Connected,
184QCheckBox#checkboxPlayer7Connected,
185QCheckBox#checkboxPlayer8Connected {
186 spacing: 0px;
187}
188
189QWidget#Player1LEDs QCheckBox,
190QWidget#Player2LEDs QCheckBox,
191QWidget#Player3LEDs QCheckBox,
192QWidget#Player4LEDs QCheckBox,
193QWidget#Player5LEDs QCheckBox,
194QWidget#Player6LEDs QCheckBox,
195QWidget#Player7LEDs QCheckBox,
196QWidget#Player8LEDs QCheckBox {
197 spacing: 0px;
198}
199
200QWidget#Player1LEDs QCheckBox::indicator,
201QWidget#Player2LEDs QCheckBox::indicator,
202QWidget#Player3LEDs QCheckBox::indicator,
203QWidget#Player4LEDs QCheckBox::indicator,
204QWidget#Player5LEDs QCheckBox::indicator,
205QWidget#Player6LEDs QCheckBox::indicator,
206QWidget#Player7LEDs QCheckBox::indicator,
207QWidget#Player8LEDs QCheckBox::indicator {
208 width: 6px;
209 height: 6px;
210 margin-left: 0px;
211}
212
213QWidget#bottomPerGameInput QCheckBox#checkboxPlayer1Connected::indicator,
214QWidget#bottomPerGameInput QCheckBox#checkboxPlayer2Connected::indicator,
215QWidget#bottomPerGameInput QCheckBox#checkboxPlayer3Connected::indicator,
216QWidget#bottomPerGameInput QCheckBox#checkboxPlayer4Connected::indicator,
217QWidget#bottomPerGameInput QCheckBox#checkboxPlayer5Connected::indicator,
218QWidget#bottomPerGameInput QCheckBox#checkboxPlayer6Connected::indicator,
219QWidget#bottomPerGameInput QCheckBox#checkboxPlayer7Connected::indicator,
220QWidget#bottomPerGameInput QCheckBox#checkboxPlayer8Connected::indicator {
221 width: 12px;
222 height: 12px;
223}
224
225QCheckBox#checkboxPlayer1Connected::indicator,
226QCheckBox#checkboxPlayer2Connected::indicator,
227QCheckBox#checkboxPlayer3Connected::indicator,
228QCheckBox#checkboxPlayer4Connected::indicator,
229QCheckBox#checkboxPlayer5Connected::indicator,
230QCheckBox#checkboxPlayer6Connected::indicator,
231QCheckBox#checkboxPlayer7Connected::indicator,
232QCheckBox#checkboxPlayer8Connected::indicator {
233 width: 14px;
234 height: 14px;
235}
236
237QGroupBox#groupPlayer1Connected::indicator,
238QGroupBox#groupPlayer2Connected::indicator,
239QGroupBox#groupPlayer3Connected::indicator,
240QGroupBox#groupPlayer4Connected::indicator,
241QGroupBox#groupPlayer5Connected::indicator,
242QGroupBox#groupPlayer6Connected::indicator,
243QGroupBox#groupPlayer7Connected::indicator,
244QGroupBox#groupPlayer8Connected::indicator {
245 width: 16px;
246 height: 16px;
247}
248
249QWidget#Player1LEDs QCheckBox::indicator:checked,
250QWidget#Player2LEDs QCheckBox::indicator:checked,
251QWidget#Player3LEDs QCheckBox::indicator:checked,
252QWidget#Player4LEDs QCheckBox::indicator:checked,
253QWidget#Player5LEDs QCheckBox::indicator:checked,
254QWidget#Player6LEDs QCheckBox::indicator:checked,
255QWidget#Player7LEDs QCheckBox::indicator:checked,
256QWidget#Player8LEDs QCheckBox::indicator:checked,
257QGroupBox#groupPlayer1Connected::indicator:checked,
258QGroupBox#groupPlayer2Connected::indicator:checked,
259QGroupBox#groupPlayer3Connected::indicator:checked,
260QGroupBox#groupPlayer4Connected::indicator:checked,
261QGroupBox#groupPlayer5Connected::indicator:checked,
262QGroupBox#groupPlayer6Connected::indicator:checked,
263QGroupBox#groupPlayer7Connected::indicator:checked,
264QGroupBox#groupPlayer8Connected::indicator:checked,
265QCheckBox#checkboxPlayer1Connected::indicator:checked,
266QCheckBox#checkboxPlayer2Connected::indicator:checked,
267QCheckBox#checkboxPlayer3Connected::indicator:checked,
268QCheckBox#checkboxPlayer4Connected::indicator:checked,
269QCheckBox#checkboxPlayer5Connected::indicator:checked,
270QCheckBox#checkboxPlayer6Connected::indicator:checked,
271QCheckBox#checkboxPlayer7Connected::indicator:checked,
272QCheckBox#checkboxPlayer8Connected::indicator:checked,
273QGroupBox#groupConnectedController::indicator:checked {
274 border-radius: 2px;
275 border: 1px solid #929192;
276 background: #39ff14;
277 image: none;
278}
279
280QWidget#Player1LEDs QCheckBox::indicator:unchecked,
281QWidget#Player2LEDs QCheckBox::indicator:unchecked,
282QWidget#Player3LEDs QCheckBox::indicator:unchecked,
283QWidget#Player4LEDs QCheckBox::indicator:unchecked,
284QWidget#Player5LEDs QCheckBox::indicator:unchecked,
285QWidget#Player6LEDs QCheckBox::indicator:unchecked,
286QWidget#Player7LEDs QCheckBox::indicator:unchecked,
287QWidget#Player8LEDs QCheckBox::indicator:unchecked,
288QGroupBox#groupPlayer1Connected::indicator:unchecked,
289QGroupBox#groupPlayer2Connected::indicator:unchecked,
290QGroupBox#groupPlayer3Connected::indicator:unchecked,
291QGroupBox#groupPlayer4Connected::indicator:unchecked,
292QGroupBox#groupPlayer5Connected::indicator:unchecked,
293QGroupBox#groupPlayer6Connected::indicator:unchecked,
294QGroupBox#groupPlayer7Connected::indicator:unchecked,
295QGroupBox#groupPlayer8Connected::indicator:unchecked,
296QCheckBox#checkboxPlayer1Connected::indicator:unchecked,
297QCheckBox#checkboxPlayer2Connected::indicator:unchecked,
298QCheckBox#checkboxPlayer3Connected::indicator:unchecked,
299QCheckBox#checkboxPlayer4Connected::indicator:unchecked,
300QCheckBox#checkboxPlayer5Connected::indicator:unchecked,
301QCheckBox#checkboxPlayer6Connected::indicator:unchecked,
302QCheckBox#checkboxPlayer7Connected::indicator:unchecked,
303QCheckBox#checkboxPlayer8Connected::indicator:unchecked,
304QGroupBox#groupConnectedController::indicator:unchecked {
305 border-radius: 2px;
306 border: 1px solid #929192;
307 background: transparent;
308 image: none;
309}
310
311QWidget#controllerPlayer1,
312QWidget#controllerPlayer2,
313QWidget#controllerPlayer3,
314QWidget#controllerPlayer4,
315QWidget#controllerPlayer5,
316QWidget#controllerPlayer6,
317QWidget#controllerPlayer7,
318QWidget#controllerPlayer8 {
319 background: transparent;
320}
321
322QDialog#QtSoftwareKeyboardDialog,
323QStackedWidget#topOSK {
324 background: rgba(51, 51, 51, .9);
325}
326
327
328QDialog#OverlayDialog,
329QStackedWidget#stackedDialog {
330 background: rgba(51, 51, 51, .7);
331}
332
333QWidget#boxOSK,
334QWidget#lineOSK,
335QWidget#richDialog,
336QWidget#lineDialog {
337 background: transparent;
338}
339
340QStackedWidget#bottomOSK,
341QWidget#contentDialog,
342QWidget#contentRichDialog {
343 background: rgba(240, 240, 240, 1);
344}
345
346QWidget#contentDialog,
347QWidget#contentRichDialog {
348 margin: 5px;
349 border-radius: 6px;
350}
351
352QWidget#buttonsDialog,
353QWidget#buttonsRichDialog {
354 margin: 5px;
355 border-top: 2px solid rgba(44, 44, 44, 1);
356}
357
358QWidget#legendOSKnum {
359 border-top: 1px solid rgba(44, 44, 44, 1);
360}
361
362QStackedWidget#stackedDialog QTextBrowser QScrollBar::vertical {
363 background: #cdcdcd;
364 width: 15px;
365 margin: 15px 3px 15px 3px;
366 border: 1px transparent;
367 border-radius: 4px;
368}
369
370QStackedWidget#stackedDialog QTextBrowser QScrollBar::horizoncal {
371 background: #cdcdcd;
372 height: 15px;
373 margin: 3px 15px 3px 15px;
374 border: 1px transparent;
375 border-radius: 4px;
376}
377
378QStackedWidget#stackedDialog QTextBrowser QScrollBar::handle {
379 background: #fff;
380 border-radius: 4px;
381 min-height: 5px;
382 min-width: 5px;
383}
384
385QStackedWidget#stackedDialog QTextBrowser QScrollBar::add-line,
386QStackedWidget#stackedDialog QTextBrowser QScrollBar::sub-line,
387QStackedWidget#stackedDialog QTextBrowser QScrollBar::add-page,
388QStackedWidget#stackedDialog QTextBrowser QScrollBar::sub-page {
389 background: none;
390}
391
392QWidget#inputOSK {
393 border-bottom: 3px solid rgba(255, 255, 255, .9);
394}
395
396QWidget#inputOSK QLineEdit {
397 background: transparent;
398 border: none;
399 color: #ccc;
400}
401
402QWidget#inputBoxOSK {
403 border: 2px solid rgba(255, 255, 255, .9);
404}
405
406QWidget#inputBoxOSK QTextEdit {
407 background: transparent;
408 border: none;
409 color: #ccc;
410}
411
412QWidget#richDialog QTextBrowser {
413 background: transparent;
414 border: none;
415 padding: 35px 65px;
416}
417
418
419QWidget#lineOSK QLabel#label_header {
420 color: #f0f0f0;
421}
422
423QWidget#lineOSK QLabel#label_sub,
424QWidget#lineOSK QLabel#label_characters,
425QWidget#boxOSK QLabel#label_characters_box {
426 color: #ccc;
427}
428
429QWidget#contentDialog QLabel#label_title,
430QWidget#contentRichDialog QLabel#label_title_rich {
431 color: #888;
432}
433
434QWidget#contentDialog QLabel#label_dialog {
435 padding: 20px 65px;
436}
437
438QWidget#contentDialog QLabel#label_title,
439QWidget#contentRichDialog QLabel#label_title_rich {
440 padding: 0px 65px;
441}
442
443QDialog#OverlayDialog QPushButton {
444 color: rgba(49, 79, 239, 1);
445 background: transparent;
446 border: none;
447 padding: 0px;
448 min-width: 0px;
449}
450
451QDialog#OverlayDialog QPushButton:focus,
452QDialog#OverlayDialog QPushButton:hover {
453 color: rgba(49, 79, 239, 1);
454 background: rgba(255, 255, 255, 1);
455 border: 5px solid rgba(148, 250, 202, 1);
456 border-radius: 6px;
457 outline: none;
458}
459
460QDialog#OverlayDialog QPushButton:pressed {
461 color: rgba(240, 240, 240, 1);
462 background: rgba(150, 150, 150, 1);
463 border: 5px solid rgba(148, 250, 202, 1);
464 border-radius: 6px;
465 outline: none;
466}
467
468QDialog#QtSoftwareKeyboardDialog QPushButton {
469 background: rgba(232, 232, 232, 1);
470 border: 2px solid rgba(240, 240, 240, 1);
471}
472
473QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift,
474QDialog#QtSoftwareKeyboardDialog QPushButton#button_return,
475QDialog#QtSoftwareKeyboardDialog QPushButton#button_space,
476QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift,
477QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift,
478QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift {
479 background: rgba(218, 218, 218, 1);
480 border: 2px solid rgba(240, 240, 240, 1);
481}
482
483QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace,
484QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift,
485QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num {
486 color: rgba(240, 240, 240, 1);
487 background: rgba(44, 44, 44, 1);
488 border: 2px solid rgba(240, 240, 240, 1);
489}
490
491QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok,
492QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift,
493QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num {
494 color: rgba(240, 240, 240, 1);
495 background: rgba(49, 79, 239, 1);
496 border: 2px solid rgba(240, 240, 240, 1);
497}
498
499QDialog#QtSoftwareKeyboardDialog QPushButton:focus,
500QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:focus,
501QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:focus,
502QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:focus,
503QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:focus,
504QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:focus,
505QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:focus,
506QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:focus,
507QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:focus,
508QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:focus,
509QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:focus,
510QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:focus,
511QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:focus,
512
513QDialog#QtSoftwareKeyboardDialog QPushButton:hover,
514QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:hover,
515QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:hover,
516QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:hover,
517QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:hover,
518QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:hover,
519QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:hover,
520QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:hover,
521QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:hover,
522QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:hover,
523QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:hover,
524QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:hover,
525QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:hover {
526 color: rgba(0, 0, 0, 1);
527 background: rgba(255, 255, 255, 1);
528 border: 5px solid rgba(148, 250, 202, 1);
529 border-radius: 6px;
530 outline: none;
531}
532
533QDialog#QtSoftwareKeyboardDialog QPushButton:pressed,
534QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:pressed,
535QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:pressed,
536QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:pressed,
537QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:pressed,
538QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:pressed,
539QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:pressed,
540QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:pressed,
541QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:pressed,
542QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:pressed,
543QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:pressed,
544QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:pressed,
545QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:pressed {
546 color: rgba(240, 240, 240, 1);
547 background: rgba(150, 150, 150, 1);
548 border: 5px solid rgba(148, 250, 202, 1);
549 border-radius: 6px;
550}
551
552QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace,
553QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift,
554QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num {
555 image: url(:/overlay/osk_button_B.png);
556 image-position: right;
557 qproperty-icon: url(:/overlay/osk_button_backspace.png);
558 qproperty-iconSize: 36px;
559}
560
561QDialog#QtSoftwareKeyboardDialog QPushButton#button_space,
562QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift {
563 image: url(:/overlay/osk_button_Y.png);
564 image-position: right;
565}
566
567QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok,
568QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift,
569QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num {
570 image: url(:/overlay/osk_button_plus.png);
571 image-position: right;
572}
573
574QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift {
575 image: url(:/overlay/osk_button_shift_lock_off.png);
576 image-position: left;
577 qproperty-icon: url(:/overlay/osk_button_shift.png);
578 qproperty-iconSize: 36px;
579}
580
581QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift {
582 image: url(:/overlay/osk_button_shift_lock_off.png);
583 image-position: left;
584 qproperty-icon: url(:/overlay/osk_button_shift_on.png);
585 qproperty-iconSize: 36px;
586}
587
588QDialog#QtSoftwareKeyboardDialog QPushButton#button_left_bracket,
589QDialog#QtSoftwareKeyboardDialog QPushButton#button_right_bracket,
590QDialog#QtSoftwareKeyboardDialog QPushButton#button_left_parenthesis,
591QDialog#QtSoftwareKeyboardDialog QPushButton#button_right_parenthesis {
592 padding-bottom: 7px;
593}
594
595QDialog#QtSoftwareKeyboardDialog QWidget#titleOSK QLabel {
596 background: transparent;
597 color: #ccc;
598}
599
600QDialog#QtSoftwareKeyboardDialog QWidget#button_L,
601QDialog#QtSoftwareKeyboardDialog QWidget#button_L_shift,
602QDialog#QtSoftwareKeyboardDialog QWidget#button_L_num {
603 image: url(:/overlay/button_L.png);
604}
605
606QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left,
607QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left_shift,
608QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left_num {
609 image: url(:/overlay/arrow_left.png);
610}
611
612QDialog#QtSoftwareKeyboardDialog QWidget#button_R,
613QDialog#QtSoftwareKeyboardDialog QWidget#button_R_shift,
614QDialog#QtSoftwareKeyboardDialog QWidget#button_R_num {
615 image: url(:/overlay/button_R.png);
616}
617
618QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right,
619QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right_shift,
620QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right_num {
621 image: url(:/overlay/arrow_right.png);
622}
623
624QDialog#QtSoftwareKeyboardDialog QWidget#button_press_stick,
625QDialog#QtSoftwareKeyboardDialog QWidget#button_press_stick_shift {
626 image: url(:/overlay/button_press_stick.png);
627}
628
629QDialog#QtSoftwareKeyboardDialog QWidget#button_X,
630QDialog#QtSoftwareKeyboardDialog QWidget#button_X_shift,
631QDialog#QtSoftwareKeyboardDialog QWidget#button_X_num {
632 image: url(:/overlay/button_X.png);
633}
634
635QDialog#QtSoftwareKeyboardDialog QWidget#button_A,
636QDialog#QtSoftwareKeyboardDialog QWidget#button_A_shift,
637QDialog#QtSoftwareKeyboardDialog QWidget#button_A_num {
638 image: url(:/overlay/button_A.png);
639}
640
641QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:disabled,
642QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:disabled,
643QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:disabled,
644QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:disabled,
645QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:disabled,
646QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled,
647QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:disabled,
648QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:disabled,
649QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:disabled,
650QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:disabled {
651 color: rgba(164, 164, 164, 1);
652 background-color: rgba(218, 218, 218, 1);
653}
654
655QDialog#QtSoftwareKeyboardDialog QPushButton#button_at:disabled,
656QDialog#QtSoftwareKeyboardDialog QPushButton#button_slash:disabled,
657QDialog#QtSoftwareKeyboardDialog QPushButton#button_percent:disabled,
658QDialog#QtSoftwareKeyboardDialog QPushButton#button_1:disabled,
659QDialog#QtSoftwareKeyboardDialog QPushButton#button_2:disabled,
660QDialog#QtSoftwareKeyboardDialog QPushButton#button_3:disabled,
661QDialog#QtSoftwareKeyboardDialog QPushButton#button_4:disabled,
662QDialog#QtSoftwareKeyboardDialog QPushButton#button_5:disabled,
663QDialog#QtSoftwareKeyboardDialog QPushButton#button_6:disabled,
664QDialog#QtSoftwareKeyboardDialog QPushButton#button_7:disabled,
665QDialog#QtSoftwareKeyboardDialog QPushButton#button_8:disabled,
666QDialog#QtSoftwareKeyboardDialog QPushButton#button_9:disabled,
667QDialog#QtSoftwareKeyboardDialog QPushButton#button_0:disabled,
668QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:disabled {
669 color: rgba(164, 164, 164, 1);
670}
671
672QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:disabled,
673QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:disabled,
674QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:disabled {
675 image: url(:/overlay/osk_button_plus_disabled.png);
676}
677
678QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:disabled,
679QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:disabled,
680QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:disabled {
681 image: url(:/overlay/osk_button_B_disabled.png);
682}
683
684QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:disabled,
685QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled {
686 image: url(:/overlay/osk_button_Y_disabled.png);
687}
diff --git a/dist/qt_themes/qdarkstyle/icons/16x16/connected.png b/dist/qt_themes/qdarkstyle/icons/16x16/connected.png
index 90feb372a..0afc18cb7 100644
--- a/dist/qt_themes/qdarkstyle/icons/16x16/connected.png
+++ b/dist/qt_themes/qdarkstyle/icons/16x16/connected.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/16x16/connected_notification.png b/dist/qt_themes/qdarkstyle/icons/16x16/connected_notification.png
index 7cd8b9d29..72466e098 100644
--- a/dist/qt_themes/qdarkstyle/icons/16x16/connected_notification.png
+++ b/dist/qt_themes/qdarkstyle/icons/16x16/connected_notification.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/16x16/disconnected.png b/dist/qt_themes/qdarkstyle/icons/16x16/disconnected.png
index fc5f23894..7258a8cfe 100644
--- a/dist/qt_themes/qdarkstyle/icons/16x16/disconnected.png
+++ b/dist/qt_themes/qdarkstyle/icons/16x16/disconnected.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/16x16/lock.png b/dist/qt_themes/qdarkstyle/icons/16x16/lock.png
index c750a39e8..7e63927b2 100644
--- a/dist/qt_themes/qdarkstyle/icons/16x16/lock.png
+++ b/dist/qt_themes/qdarkstyle/icons/16x16/lock.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/16x16/refresh.png b/dist/qt_themes/qdarkstyle/icons/16x16/refresh.png
deleted file mode 100644
index d4afd76f9..000000000
--- a/dist/qt_themes/qdarkstyle/icons/16x16/refresh.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/256x256/plus_folder.png b/dist/qt_themes/qdarkstyle/icons/256x256/plus_folder.png
index 303f9a321..002101114 100644
--- a/dist/qt_themes/qdarkstyle/icons/256x256/plus_folder.png
+++ b/dist/qt_themes/qdarkstyle/icons/256x256/plus_folder.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/bad_folder.png b/dist/qt_themes/qdarkstyle/icons/48x48/bad_folder.png
index 4a9709623..245f96c7b 100644
--- a/dist/qt_themes/qdarkstyle/icons/48x48/bad_folder.png
+++ b/dist/qt_themes/qdarkstyle/icons/48x48/bad_folder.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/chip.png b/dist/qt_themes/qdarkstyle/icons/48x48/chip.png
index 973fabd05..db0cadac1 100644
--- a/dist/qt_themes/qdarkstyle/icons/48x48/chip.png
+++ b/dist/qt_themes/qdarkstyle/icons/48x48/chip.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/folder.png b/dist/qt_themes/qdarkstyle/icons/48x48/folder.png
index 0f1e987d6..11a76b5c1 100644
--- a/dist/qt_themes/qdarkstyle/icons/48x48/folder.png
+++ b/dist/qt_themes/qdarkstyle/icons/48x48/folder.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/list-add.png b/dist/qt_themes/qdarkstyle/icons/48x48/list-add.png
new file mode 100644
index 000000000..8fbe78011
--- /dev/null
+++ b/dist/qt_themes/qdarkstyle/icons/48x48/list-add.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/no_avatar.png b/dist/qt_themes/qdarkstyle/icons/48x48/no_avatar.png
index 43e0dd267..a7a48d33c 100644
--- a/dist/qt_themes/qdarkstyle/icons/48x48/no_avatar.png
+++ b/dist/qt_themes/qdarkstyle/icons/48x48/no_avatar.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/plus.png b/dist/qt_themes/qdarkstyle/icons/48x48/plus.png
deleted file mode 100644
index 16cc8b4f4..000000000
--- a/dist/qt_themes/qdarkstyle/icons/48x48/plus.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/sd_card.png b/dist/qt_themes/qdarkstyle/icons/48x48/sd_card.png
index 0291c6542..87ae5186d 100644
--- a/dist/qt_themes/qdarkstyle/icons/48x48/sd_card.png
+++ b/dist/qt_themes/qdarkstyle/icons/48x48/sd_card.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/star.png b/dist/qt_themes/qdarkstyle/icons/48x48/star.png
index 90d423a1d..546779e2a 100644
--- a/dist/qt_themes/qdarkstyle/icons/48x48/star.png
+++ b/dist/qt_themes/qdarkstyle/icons/48x48/star.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/style.qrc b/dist/qt_themes/qdarkstyle/style.qrc
index f770e09fd..a89fb26c6 100644
--- a/dist/qt_themes/qdarkstyle/style.qrc
+++ b/dist/qt_themes/qdarkstyle/style.qrc
@@ -10,7 +10,7 @@
10 <file alias="48x48/chip.png">icons/48x48/chip.png</file> 10 <file alias="48x48/chip.png">icons/48x48/chip.png</file>
11 <file alias="48x48/folder.png">icons/48x48/folder.png</file> 11 <file alias="48x48/folder.png">icons/48x48/folder.png</file>
12 <file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file> 12 <file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
13 <file alias="48x48/plus.png">icons/48x48/plus.png</file> 13 <file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
14 <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file> 14 <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
15 <file alias="48x48/star.png">icons/48x48/star.png</file> 15 <file alias="48x48/star.png">icons/48x48/star.png</file>
16 <file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file> 16 <file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/lock.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/lock.png
deleted file mode 100644
index c750a39e8..000000000
--- a/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/lock.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.png
deleted file mode 100644
index d4afd76f9..000000000
--- a/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.png
deleted file mode 100644
index d4afd76f9..000000000
--- a/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/256x256/plus_folder.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/256x256/plus_folder.png
deleted file mode 100644
index 303f9a321..000000000
--- a/dist/qt_themes/qdarkstyle_midnight_blue/icons/256x256/plus_folder.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/bad_folder.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/bad_folder.png
deleted file mode 100644
index 4a9709623..000000000
--- a/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/bad_folder.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/chip.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/chip.png
deleted file mode 100644
index 973fabd05..000000000
--- a/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/chip.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/folder.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/folder.png
deleted file mode 100644
index 0f1e987d6..000000000
--- a/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/folder.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/plus.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/plus.png
deleted file mode 100644
index 16cc8b4f4..000000000
--- a/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/plus.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/sd_card.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/sd_card.png
deleted file mode 100644
index 0291c6542..000000000
--- a/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/sd_card.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/star.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/star.png
deleted file mode 100644
index 90d423a1d..000000000
--- a/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/star.png
+++ /dev/null
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc b/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
index 142dd3288..dc3d7fecb 100644
--- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
@@ -1,15 +1,16 @@
1<RCC> 1<RCC>
2 <qresource prefix="icons/qdarkstyle_midnight_blue"> 2 <qresource prefix="icons/qdarkstyle_midnight_blue">
3 <file alias="index.theme">icons/index.theme</file> 3 <file alias="index.theme">icons/index.theme</file>
4 <file alias="16x16/lock.png">icons/16x16/lock.png</file> 4 <file alias="16x16/lock.png">../qdarkstyle/icons/16x16/lock.png</file>
5 <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file> 5 <file alias="16x16/view-refresh.png">../qdarkstyle/icons/16x16/view-refresh.png</file>
6 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> 6 <file alias="48x48/bad_folder.png">../qdarkstyle/icons/48x48/bad_folder.png</file>
7 <file alias="48x48/chip.png">icons/48x48/chip.png</file> 7 <file alias="48x48/chip.png">../qdarkstyle/icons/48x48/chip.png</file>
8 <file alias="48x48/folder.png">icons/48x48/folder.png</file> 8 <file alias="48x48/folder.png">../qdarkstyle/icons/48x48/folder.png</file>
9 <file alias="48x48/plus.png">icons/48x48/plus.png</file> 9 <file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
10 <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file> 10 <file alias="48x48/list-add.png">../qdarkstyle/icons/48x48/list-add.png</file>
11 <file alias="48x48/star.png">icons/48x48/star.png</file> 11 <file alias="48x48/sd_card.png">../qdarkstyle/icons/48x48/sd_card.png</file>
12 <file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file> 12 <file alias="48x48/star.png">../qdarkstyle/icons/48x48/star.png</file>
13 <file alias="256x256/plus_folder.png">../qdarkstyle/icons/256x256/plus_folder.png</file>
13 </qresource> 14 </qresource>
14 <qresource prefix="qss_icons"> 15 <qresource prefix="qss_icons">
15 <file>rc/arrow_down.png</file> 16 <file>rc/arrow_down.png</file>
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 6d04ace1d..eea70fc27 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -128,7 +128,7 @@ endif()
128if (YUZU_USE_BUNDLED_OPUS) 128if (YUZU_USE_BUNDLED_OPUS)
129 add_subdirectory(opus EXCLUDE_FROM_ALL) 129 add_subdirectory(opus EXCLUDE_FROM_ALL)
130else() 130else()
131 find_package(opus 1.3 REQUIRED) 131 find_package(Opus 1.3 REQUIRED)
132endif() 132endif()
133 133
134# FFMpeg 134# FFMpeg
diff --git a/externals/find-modules/FindCatch2.cmake b/externals/find-modules/FindCatch2.cmake
deleted file mode 100644
index bded15951..000000000
--- a/externals/find-modules/FindCatch2.cmake
+++ /dev/null
@@ -1,51 +0,0 @@
1# SPDX-FileCopyrightText: 2020 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4find_package(PkgConfig QUIET)
5pkg_check_modules(PC_Catch2 QUIET Catch2)
6
7find_path(Catch2_INCLUDE_DIR
8 NAMES catch.hpp
9 PATHS ${PC_Catch2_INCLUDE_DIRS} ${CONAN_CATCH2_ROOT}
10 PATH_SUFFIXES catch2
11)
12
13if(Catch2_INCLUDE_DIR)
14 file(STRINGS "${Catch2_INCLUDE_DIR}/catch.hpp" _Catch2_version_lines
15 REGEX "#define[ \t]+CATCH_VERSION_(MAJOR|MINOR|PATCH)")
16 string(REGEX REPLACE ".*CATCH_VERSION_MAJOR +\([0-9]+\).*" "\\1" _Catch2_version_major "${_Catch2_version_lines}")
17 string(REGEX REPLACE ".*CATCH_VERSION_MINOR +\([0-9]+\).*" "\\1" _Catch2_version_minor "${_Catch2_version_lines}")
18 string(REGEX REPLACE ".*CATCH_VERSION_PATCH +\([0-9]+\).*" "\\1" _Catch2_version_patch "${_Catch2_version_lines}")
19 set(Catch2_VERSION "${_Catch2_version_major}.${_Catch2_version_minor}.${_Catch2_version_patch}")
20 unset(_Catch2_version_major)
21 unset(_Catch2_version_minor)
22 unset(_Catch2_version_patch)
23 unset(_Catch2_version_lines)
24endif()
25
26include(FindPackageHandleStandardArgs)
27find_package_handle_standard_args(Catch2
28 FOUND_VAR Catch2_FOUND
29 REQUIRED_VARS
30 Catch2_INCLUDE_DIR
31 Catch2_VERSION
32 VERSION_VAR Catch2_VERSION
33)
34
35if(Catch2_FOUND)
36 set(Catch2_INCLUDE_DIRS ${Catch2_INCLUDE_DIR})
37 set(Catch2_DEFINITIONS ${PC_Catch2_CFLAGS_OTHER})
38endif()
39
40if(Catch2_FOUND AND NOT TARGET Catch2::Catch2)
41 add_library(Catch2::Catch2 UNKNOWN IMPORTED)
42 set_target_properties(Catch2::Catch2 PROPERTIES
43 IMPORTED_LOCATION "${Catch2_LIBRARY}"
44 INTERFACE_COMPILE_OPTIONS "${PC_Catch2_CFLAGS_OTHER}"
45 INTERFACE_INCLUDE_DIRECTORIES "${Catch2_INCLUDE_DIR}"
46 )
47endif()
48
49mark_as_advanced(
50 Catch2_INCLUDE_DIR
51)
diff --git a/externals/find-modules/FindOpus.cmake b/externals/find-modules/FindOpus.cmake
new file mode 100644
index 000000000..b68a6046b
--- /dev/null
+++ b/externals/find-modules/FindOpus.cmake
@@ -0,0 +1,19 @@
1# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4find_package(PkgConfig)
5
6if (PKG_CONFIG_FOUND)
7 pkg_search_module(opus IMPORTED_TARGET GLOBAL opus)
8 if (opus_FOUND)
9 add_library(Opus::opus ALIAS PkgConfig::opus)
10 endif()
11endif()
12
13include(FindPackageHandleStandardArgs)
14find_package_handle_standard_args(Opus
15 REQUIRED_VARS
16 opus_LINK_LIBRARIES
17 opus_FOUND
18 VERSION_VAR opus_VERSION
19)
diff --git a/externals/find-modules/Findfmt.cmake b/externals/find-modules/Findfmt.cmake
deleted file mode 100644
index d11e98a69..000000000
--- a/externals/find-modules/Findfmt.cmake
+++ /dev/null
@@ -1,71 +0,0 @@
1# SPDX-FileCopyrightText: 2020 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4find_package(PkgConfig QUIET)
5pkg_check_modules(PC_fmt QUIET fmt)
6
7find_path(fmt_INCLUDE_DIR
8 NAMES format.h
9 PATHS ${PC_fmt_INCLUDE_DIRS} ${CONAN_INCLUDE_DIRS_fmt}
10 PATH_SUFFIXES fmt
11)
12
13find_library(fmt_LIBRARY
14 NAMES fmt
15 PATHS ${PC_fmt_LIBRARY_DIRS} ${CONAN_LIB_DIRS_fmt}
16)
17
18if(fmt_INCLUDE_DIR)
19 set(_fmt_version_file "${fmt_INCLUDE_DIR}/core.h")
20 if(NOT EXISTS "${_fmt_version_file}")
21 set(_fmt_version_file "${fmt_INCLUDE_DIR}/format.h")
22 endif()
23 if(EXISTS "${_fmt_version_file}")
24 # parse "#define FMT_VERSION 60200" to 6.2.0
25 file(STRINGS "${_fmt_version_file}" fmt_VERSION_LINE
26 REGEX "^#define[ \t]+FMT_VERSION[ \t]+[0-9]+$")
27 string(REGEX REPLACE "^#define[ \t]+FMT_VERSION[ \t]+([0-9]+)$"
28 "\\1" fmt_VERSION "${fmt_VERSION_LINE}")
29 foreach(ver "fmt_VERSION_PATCH" "fmt_VERSION_MINOR" "fmt_VERSION_MAJOR")
30 math(EXPR ${ver} "${fmt_VERSION} % 100")
31 math(EXPR fmt_VERSION "(${fmt_VERSION} - ${${ver}}) / 100")
32 endforeach()
33 set(fmt_VERSION
34 "${fmt_VERSION_MAJOR}.${fmt_VERSION_MINOR}.${fmt_VERSION_PATCH}")
35 endif()
36 unset(_fmt_version_file)
37 unset(fmt_VERSION_LINE)
38 unset(fmt_VERSION_MAJOR)
39 unset(fmt_VERSION_MINOR)
40 unset(fmt_VERSION_PATCH)
41endif()
42
43include(FindPackageHandleStandardArgs)
44find_package_handle_standard_args(fmt
45 FOUND_VAR fmt_FOUND
46 REQUIRED_VARS
47 fmt_LIBRARY
48 fmt_INCLUDE_DIR
49 fmt_VERSION
50 VERSION_VAR fmt_VERSION
51)
52
53if(fmt_FOUND)
54 set(fmt_LIBRARIES ${fmt_LIBRARY})
55 set(fmt_INCLUDE_DIRS ${fmt_INCLUDE_DIR})
56 set(fmt_DEFINITIONS ${PC_fmt_CFLAGS_OTHER})
57endif()
58
59if(fmt_FOUND AND NOT TARGET fmt::fmt)
60 add_library(fmt::fmt UNKNOWN IMPORTED)
61 set_target_properties(fmt::fmt PROPERTIES
62 IMPORTED_LOCATION "${fmt_LIBRARY}"
63 INTERFACE_COMPILE_OPTIONS "${PC_fmt_CFLAGS_OTHER}"
64 INTERFACE_INCLUDE_DIRECTORIES "${fmt_INCLUDE_DIR}"
65 )
66endif()
67
68mark_as_advanced(
69 fmt_INCLUDE_DIR
70 fmt_LIBRARY
71)
diff --git a/externals/find-modules/Findlz4.cmake b/externals/find-modules/Findlz4.cmake
index 56dcca8f6..13ca5de66 100644
--- a/externals/find-modules/Findlz4.cmake
+++ b/externals/find-modules/Findlz4.cmake
@@ -1,56 +1,19 @@
1# SPDX-FileCopyrightText: 2020 yuzu Emulator Project 1# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later 2# SPDX-License-Identifier: GPL-2.0-or-later
3 3
4find_package(PkgConfig QUIET) 4find_package(PkgConfig)
5pkg_check_modules(PC_lz4 QUIET lz4)
6 5
7find_path(lz4_INCLUDE_DIR 6if (PKG_CONFIG_FOUND)
8 NAMES lz4.h 7 pkg_search_module(liblz4 IMPORTED_TARGET GLOBAL liblz4)
9 PATHS ${PC_lz4_INCLUDE_DIRS} 8 if (liblz4_FOUND)
10) 9 add_library(lz4::lz4 ALIAS PkgConfig::liblz4)
11find_library(lz4_LIBRARY 10 endif()
12 NAMES lz4
13 PATHS ${PC_lz4_LIBRARY_DIRS}
14)
15
16if(lz4_INCLUDE_DIR)
17 file(STRINGS "${lz4_INCLUDE_DIR}/lz4.h" _lz4_version_lines
18 REGEX "#define[ \t]+LZ4_VERSION_(MAJOR|MINOR|RELEASE)")
19 string(REGEX REPLACE ".*LZ4_VERSION_MAJOR *\([0-9]*\).*" "\\1" _lz4_version_major "${_lz4_version_lines}")
20 string(REGEX REPLACE ".*LZ4_VERSION_MINOR *\([0-9]*\).*" "\\1" _lz4_version_minor "${_lz4_version_lines}")
21 string(REGEX REPLACE ".*LZ4_VERSION_RELEASE *\([0-9]*\).*" "\\1" _lz4_version_release "${_lz4_version_lines}")
22 set(lz4_VERSION "${_lz4_version_major}.${_lz4_version_minor}.${_lz4_version_release}")
23 unset(_lz4_version_major)
24 unset(_lz4_version_minor)
25 unset(_lz4_version_release)
26 unset(_lz4_version_lines)
27endif() 11endif()
28 12
29include(FindPackageHandleStandardArgs) 13include(FindPackageHandleStandardArgs)
30find_package_handle_standard_args(lz4 14find_package_handle_standard_args(lz4
31 FOUND_VAR lz4_FOUND 15 REQUIRED_VARS
32 REQUIRED_VARS 16 liblz4_LINK_LIBRARIES
33 lz4_LIBRARY 17 liblz4_FOUND
34 lz4_INCLUDE_DIR 18 VERSION_VAR liblz4_VERSION
35 VERSION_VAR lz4_VERSION
36)
37
38if(lz4_FOUND)
39 set(lz4_LIBRARIES ${lz4_LIBRARY})
40 set(lz4_INCLUDE_DIRS ${lz4_INCLUDE_DIR})
41 set(lz4_DEFINITIONS ${PC_lz4_CFLAGS_OTHER})
42endif()
43
44if(lz4_FOUND AND NOT TARGET lz4::lz4)
45 add_library(lz4::lz4 UNKNOWN IMPORTED)
46 set_target_properties(lz4::lz4 PROPERTIES
47 IMPORTED_LOCATION "${lz4_LIBRARY}"
48 INTERFACE_COMPILE_OPTIONS "${PC_lz4_CFLAGS_OTHER}"
49 INTERFACE_INCLUDE_DIRECTORIES "${lz4_INCLUDE_DIR}"
50 )
51endif()
52
53mark_as_advanced(
54 lz4_INCLUDE_DIR
55 lz4_LIBRARY
56) 19)
diff --git a/externals/find-modules/Findnlohmann_json.cmake b/externals/find-modules/Findnlohmann_json.cmake
deleted file mode 100644
index 8a3958cf1..000000000
--- a/externals/find-modules/Findnlohmann_json.cmake
+++ /dev/null
@@ -1,51 +0,0 @@
1# SPDX-FileCopyrightText: 2020 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4find_package(PkgConfig QUIET)
5pkg_check_modules(PC_nlohmann_json QUIET nlohmann_json)
6
7find_path(nlohmann_json_INCLUDE_DIR
8 NAMES json.hpp
9 PATHS ${PC_nlohmann_json_INCLUDE_DIRS}
10 PATH_SUFFIXES nlohmann
11)
12
13if(nlohmann_json_INCLUDE_DIR)
14 file(STRINGS "${nlohmann_json_INCLUDE_DIR}/json.hpp" _nlohmann_json_version_lines
15 REGEX "#define[ \t]+NLOHMANN_JSON_VERSION_(MAJOR|MINOR|PATCH)")
16 string(REGEX REPLACE ".*NLOHMANN_JSON_VERSION_MAJOR +\([0-9]+\).*" "\\1" _nlohmann_json_version_major "${_nlohmann_json_version_lines}")
17 string(REGEX REPLACE ".*NLOHMANN_JSON_VERSION_MINOR +\([0-9]+\).*" "\\1" _nlohmann_json_version_minor "${_nlohmann_json_version_lines}")
18 string(REGEX REPLACE ".*NLOHMANN_JSON_VERSION_PATCH +\([0-9]+\).*" "\\1" _nlohmann_json_version_patch "${_nlohmann_json_version_lines}")
19 set(nlohmann_json_VERSION "${_nlohmann_json_version_major}.${_nlohmann_json_version_minor}.${_nlohmann_json_version_patch}")
20 unset(_nlohmann_json_version_major)
21 unset(_nlohmann_json_version_minor)
22 unset(_nlohmann_json_version_patch)
23 unset(_nlohmann_json_version_lines)
24endif()
25
26include(FindPackageHandleStandardArgs)
27find_package_handle_standard_args(nlohmann_json
28 FOUND_VAR nlohmann_json_FOUND
29 REQUIRED_VARS
30 nlohmann_json_INCLUDE_DIR
31 nlohmann_json_VERSION
32 VERSION_VAR nlohmann_json_VERSION
33)
34
35if(nlohmann_json_FOUND)
36 set(nlohmann_json_INCLUDE_DIRS ${nlohmann_json_INCLUDE_DIR})
37 set(nlohmann_json_DEFINITIONS ${PC_nlohmann_json_CFLAGS_OTHER})
38endif()
39
40if(nlohmann_json_FOUND AND NOT TARGET nlohmann_json::nlohmann_json)
41 add_library(nlohmann_json::nlohmann_json UNKNOWN IMPORTED)
42 set_target_properties(nlohmann_json::nlohmann_json PROPERTIES
43 IMPORTED_LOCATION "${nlohmann_json_LIBRARY}"
44 INTERFACE_COMPILE_OPTIONS "${PC_nlohmann_json_CFLAGS_OTHER}"
45 INTERFACE_INCLUDE_DIRECTORIES "${nlohmann_json_INCLUDE_DIR}"
46 )
47endif()
48
49mark_as_advanced(
50 nlohmann_json_INCLUDE_DIR
51)
diff --git a/externals/find-modules/Findopus.cmake b/externals/find-modules/Findopus.cmake
deleted file mode 100644
index ec7b4f61f..000000000
--- a/externals/find-modules/Findopus.cmake
+++ /dev/null
@@ -1,44 +0,0 @@
1# SPDX-FileCopyrightText: 2020 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4find_package(PkgConfig QUIET)
5pkg_check_modules(PC_opus QUIET opus)
6
7find_path(opus_INCLUDE_DIR
8 NAMES opus.h
9 PATHS ${PC_opus_INCLUDE_DIRS}
10 PATH_SUFFIXES opus
11)
12find_library(opus_LIBRARY
13 NAMES opus
14 PATHS ${PC_opus_LIBRARY_DIRS}
15)
16
17include(FindPackageHandleStandardArgs)
18find_package_handle_standard_args(opus
19 FOUND_VAR opus_FOUND
20 REQUIRED_VARS
21 opus_LIBRARY
22 opus_INCLUDE_DIR
23 VERSION_VAR opus_VERSION
24)
25
26if(opus_FOUND)
27 set(Opus_LIBRARIES ${opus_LIBRARY})
28 set(Opus_INCLUDE_DIRS ${opus_INCLUDE_DIR})
29 set(Opus_DEFINITIONS ${PC_opus_CFLAGS_OTHER})
30endif()
31
32if(opus_FOUND AND NOT TARGET Opus::Opus)
33 add_library(Opus::Opus UNKNOWN IMPORTED GLOBAL)
34 set_target_properties(Opus::Opus PROPERTIES
35 IMPORTED_LOCATION "${opus_LIBRARY}"
36 INTERFACE_COMPILE_OPTIONS "${PC_opus_CFLAGS_OTHER}"
37 INTERFACE_INCLUDE_DIRECTORIES "${opus_INCLUDE_DIR}"
38 )
39endif()
40
41mark_as_advanced(
42 opus_INCLUDE_DIR
43 opus_LIBRARY
44)
diff --git a/externals/find-modules/Findzstd.cmake b/externals/find-modules/Findzstd.cmake
index f0c56f499..f4031eb70 100644
--- a/externals/find-modules/Findzstd.cmake
+++ b/externals/find-modules/Findzstd.cmake
@@ -1,57 +1,19 @@
1# SPDX-FileCopyrightText: 2020 yuzu Emulator Project 1# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later 2# SPDX-License-Identifier: GPL-2.0-or-later
3 3
4find_package(PkgConfig QUIET) 4find_package(PkgConfig)
5pkg_check_modules(PC_zstd QUIET libzstd)
6 5
7find_path(zstd_INCLUDE_DIR 6if (PKG_CONFIG_FOUND)
8 NAMES zstd.h 7 pkg_search_module(libzstd IMPORTED_TARGET GLOBAL libzstd)
9 PATHS ${PC_zstd_INCLUDE_DIRS} 8 if (libzstd_FOUND)
10) 9 add_library(zstd::zstd ALIAS PkgConfig::libzstd)
11find_library(zstd_LIBRARY 10 endif()
12 NAMES zstd
13 PATHS ${PC_zstd_LIBRARY_DIRS}
14)
15
16if(zstd_INCLUDE_DIR)
17 file(STRINGS "${zstd_INCLUDE_DIR}/zstd.h" _zstd_version_lines
18 REGEX "#define[ \t]+ZSTD_VERSION_(MAJOR|MINOR|RELEASE)")
19 string(REGEX REPLACE ".*ZSTD_VERSION_MAJOR *\([0-9]*\).*" "\\1" _zstd_version_major "${_zstd_version_lines}")
20 string(REGEX REPLACE ".*ZSTD_VERSION_MINOR *\([0-9]*\).*" "\\1" _zstd_version_minor "${_zstd_version_lines}")
21 string(REGEX REPLACE ".*ZSTD_VERSION_RELEASE *\([0-9]*\).*" "\\1" _zstd_version_release "${_zstd_version_lines}")
22 set(zstd_VERSION "${_zstd_version_major}.${_zstd_version_minor}.${_zstd_version_release}")
23 unset(_zstd_version_major)
24 unset(_zstd_version_minor)
25 unset(_zstd_version_release)
26 unset(_zstd_version_lines)
27endif() 11endif()
28 12
29include(FindPackageHandleStandardArgs) 13include(FindPackageHandleStandardArgs)
30find_package_handle_standard_args(zstd 14find_package_handle_standard_args(zstd
31 FOUND_VAR zstd_FOUND 15 REQUIRED_VARS
32 REQUIRED_VARS 16 libzstd_LINK_LIBRARIES
33 zstd_LIBRARY 17 libzstd_FOUND
34 zstd_INCLUDE_DIR 18 VERSION_VAR libzstd_VERSION
35 zstd_VERSION
36 VERSION_VAR zstd_VERSION
37)
38
39if(zstd_FOUND)
40 set(zstd_LIBRARIES ${zstd_LIBRARY})
41 set(zstd_INCLUDE_DIRS ${zstd_INCLUDE_DIR})
42 set(zstd_DEFINITIONS ${PC_zstd_CFLAGS_OTHER})
43endif()
44
45if(zstd_FOUND AND NOT TARGET zstd::zstd)
46 add_library(zstd::zstd UNKNOWN IMPORTED)
47 set_target_properties(zstd::zstd PROPERTIES
48 IMPORTED_LOCATION "${zstd_LIBRARY}"
49 INTERFACE_COMPILE_OPTIONS "${PC_zstd_CFLAGS_OTHER}"
50 INTERFACE_INCLUDE_DIRECTORIES "${zstd_INCLUDE_DIR}"
51 )
52endif()
53
54mark_as_advanced(
55 zstd_INCLUDE_DIR
56 zstd_LIBRARY
57) 19)
diff --git a/externals/opus/CMakeLists.txt b/externals/opus/CMakeLists.txt
index a92ffbd69..410ff7c08 100644
--- a/externals/opus/CMakeLists.txt
+++ b/externals/opus/CMakeLists.txt
@@ -256,4 +256,4 @@ PRIVATE
256 opus/src 256 opus/src
257) 257)
258 258
259add_library(Opus::Opus ALIAS opus) 259add_library(Opus::opus ALIAS opus)
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/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/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 052357be4..8db9a3c65 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -502,9 +502,10 @@ add_library(core STATIC
502 hle/service/jit/jit.h 502 hle/service/jit/jit.h
503 hle/service/lbl/lbl.cpp 503 hle/service/lbl/lbl.cpp
504 hle/service/lbl/lbl.h 504 hle/service/lbl/lbl.h
505 hle/service/ldn/errors.h 505 hle/service/ldn/ldn_results.h
506 hle/service/ldn/ldn.cpp 506 hle/service/ldn/ldn.cpp
507 hle/service/ldn/ldn.h 507 hle/service/ldn/ldn.h
508 hle/service/ldn/ldn_types.h
508 hle/service/ldr/ldr.cpp 509 hle/service/ldr/ldr.cpp
509 hle/service/ldr/ldr.h 510 hle/service/ldr/ldr.h
510 hle/service/lm/lm.cpp 511 hle/service/lm/lm.cpp
@@ -723,6 +724,8 @@ add_library(core STATIC
723 internal_network/network_interface.cpp 724 internal_network/network_interface.cpp
724 internal_network/network_interface.h 725 internal_network/network_interface.h
725 internal_network/sockets.h 726 internal_network/sockets.h
727 internal_network/socket_proxy.cpp
728 internal_network/socket_proxy.h
726 loader/deconstructed_rom_directory.cpp 729 loader/deconstructed_rom_directory.cpp
727 loader/deconstructed_rom_directory.h 730 loader/deconstructed_rom_directory.h
728 loader/kip.cpp 731 loader/kip.cpp
@@ -783,7 +786,7 @@ endif()
783create_target_directory_groups(core) 786create_target_directory_groups(core)
784 787
785target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) 788target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
786target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus) 789target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus)
787if (MINGW) 790if (MINGW)
788 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) 791 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
789endif() 792endif()
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/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 1638bc41d..d1e70f19d 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -190,19 +190,21 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
190 config.callbacks = cb.get(); 190 config.callbacks = cb.get();
191 config.coprocessors[15] = cp15; 191 config.coprocessors[15] = cp15;
192 config.define_unpredictable_behaviour = true; 192 config.define_unpredictable_behaviour = true;
193 static constexpr std::size_t PAGE_BITS = 12; 193 static constexpr std::size_t YUZU_PAGEBITS = 12;
194 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);
195 if (page_table) { 195 if (page_table) {
196 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>*>(
197 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
198 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;
199 } 207 }
200 config.absolute_offset_page_table = true;
201 config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
202 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
203 config.only_detect_misalignment_via_page_table_on_page_boundary = true;
204 config.fastmem_exclusive_access = true;
205 config.recompile_on_exclusive_fastmem_failure = true;
206 208
207 // Multi-process state 209 // Multi-process state
208 config.processor_id = core_index; 210 config.processor_id = core_index;
@@ -254,6 +256,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
254 } 256 }
255 if (!Settings::values.cpuopt_fastmem) { 257 if (!Settings::values.cpuopt_fastmem) {
256 config.fastmem_pointer = nullptr; 258 config.fastmem_pointer = nullptr;
259 config.fastmem_exclusive_access = false;
257 } 260 }
258 if (!Settings::values.cpuopt_fastmem_exclusives) { 261 if (!Settings::values.cpuopt_fastmem_exclusives) {
259 config.fastmem_exclusive_access = false; 262 config.fastmem_exclusive_access = false;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 921a5a734..1d46f6d40 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -250,7 +250,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
250 config.fastmem_address_space_bits = address_space_bits; 250 config.fastmem_address_space_bits = address_space_bits;
251 config.silently_mirror_fastmem = false; 251 config.silently_mirror_fastmem = false;
252 252
253 config.fastmem_exclusive_access = true; 253 config.fastmem_exclusive_access = config.fastmem_pointer != nullptr;
254 config.recompile_on_exclusive_fastmem_failure = true; 254 config.recompile_on_exclusive_fastmem_failure = true;
255 } 255 }
256 256
@@ -314,6 +314,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
314 } 314 }
315 if (!Settings::values.cpuopt_fastmem) { 315 if (!Settings::values.cpuopt_fastmem) {
316 config.fastmem_pointer = nullptr; 316 config.fastmem_pointer = nullptr;
317 config.fastmem_exclusive_access = false;
317 } 318 }
318 if (!Settings::values.cpuopt_fastmem_exclusives) { 319 if (!Settings::values.cpuopt_fastmem_exclusives) {
319 config.fastmem_exclusive_access = false; 320 config.fastmem_exclusive_access = false;
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/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index 4b35ca82f..5aab428bb 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..4c80e13a9 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..f9f902c2d 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,23 +85,26 @@ 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));
104 original_npad_type = npad_type;
102 } else { 105 } else {
103 SetNpadStyleIndex(NpadStyleIndex::ProController); 106 SetNpadStyleIndex(NpadStyleIndex::ProController);
107 original_npad_type = npad_type;
104 } 108 }
105 109
106 if (player.connected) { 110 if (player.connected) {
@@ -352,6 +356,7 @@ void EmulatedController::DisableConfiguration() {
352 Disconnect(); 356 Disconnect();
353 } 357 }
354 SetNpadStyleIndex(tmp_npad_type); 358 SetNpadStyleIndex(tmp_npad_type);
359 original_npad_type = tmp_npad_type;
355 } 360 }
356 361
357 // Apply temporary connected status to the real controller 362 // Apply temporary connected status to the real controller
@@ -949,6 +954,9 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
949 // Send a slight vibration to test for rumble support 954 // Send a slight vibration to test for rumble support
950 output_devices[device_index]->SetVibration(test_vibration); 955 output_devices[device_index]->SetVibration(test_vibration);
951 956
957 // Wait for about 15ms to ensure the controller is ready for the stop command
958 std::this_thread::sleep_for(std::chrono::milliseconds(15));
959
952 // Stop any vibration and return the result 960 // Stop any vibration and return the result
953 return output_devices[device_index]->SetVibration(zero_vibration) == 961 return output_devices[device_index]->SetVibration(zero_vibration) ==
954 Common::Input::VibrationError::None; 962 Common::Input::VibrationError::None;
@@ -999,13 +1007,27 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
999 if (!is_connected) { 1007 if (!is_connected) {
1000 return; 1008 return;
1001 } 1009 }
1010
1011 // Attempt to reconnect with the original type
1012 if (npad_type != original_npad_type) {
1013 Disconnect();
1014 const auto current_npad_type = npad_type;
1015 SetNpadStyleIndex(original_npad_type);
1016 if (IsControllerSupported()) {
1017 Connect();
1018 return;
1019 }
1020 SetNpadStyleIndex(current_npad_type);
1021 Connect();
1022 }
1023
1002 if (IsControllerSupported()) { 1024 if (IsControllerSupported()) {
1003 return; 1025 return;
1004 } 1026 }
1005 1027
1006 Disconnect(); 1028 Disconnect();
1007 1029
1008 // Fallback fullkey controllers to Pro controllers 1030 // Fallback Fullkey controllers to Pro controllers
1009 if (IsControllerFullkey() && supported_style_tag.fullkey) { 1031 if (IsControllerFullkey() && supported_style_tag.fullkey) {
1010 LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); 1032 LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
1011 SetNpadStyleIndex(NpadStyleIndex::ProController); 1033 SetNpadStyleIndex(NpadStyleIndex::ProController);
@@ -1013,6 +1035,22 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
1013 return; 1035 return;
1014 } 1036 }
1015 1037
1038 // Fallback Dual joycon controllers to Pro controllers
1039 if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) {
1040 LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
1041 SetNpadStyleIndex(NpadStyleIndex::ProController);
1042 Connect();
1043 return;
1044 }
1045
1046 // Fallback Pro controllers to Dual joycon
1047 if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) {
1048 LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type);
1049 SetNpadStyleIndex(NpadStyleIndex::JoyconDual);
1050 Connect();
1051 return;
1052 }
1053
1016 LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller", 1054 LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
1017 npad_type); 1055 npad_type);
1018} 1056}
@@ -1310,6 +1348,15 @@ const CameraState& EmulatedController::GetCamera() const {
1310 return controller.camera_state; 1348 return controller.camera_state;
1311} 1349}
1312 1350
1351NpadColor EmulatedController::GetNpadColor(u32 color) {
1352 return {
1353 .r = static_cast<u8>((color >> 16) & 0xFF),
1354 .g = static_cast<u8>((color >> 8) & 0xFF),
1355 .b = static_cast<u8>(color & 0xFF),
1356 .a = 0xff,
1357 };
1358}
1359
1313void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { 1360void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
1314 std::scoped_lock lock{callback_mutex}; 1361 std::scoped_lock lock{callback_mutex};
1315 for (const auto& poller_pair : callback_list) { 1362 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..c3aa8f9d3 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
@@ -433,6 +440,7 @@ private:
433 440
434 const NpadIdType npad_id_type; 441 const NpadIdType npad_id_type;
435 NpadStyleIndex npad_type{NpadStyleIndex::None}; 442 NpadStyleIndex npad_type{NpadStyleIndex::None};
443 NpadStyleIndex original_npad_type{NpadStyleIndex::None};
436 NpadStyleTag supported_style_tag{NpadStyleSet::All}; 444 NpadStyleTag supported_style_tag{NpadStyleSet::All};
437 bool is_connected{false}; 445 bool is_connected{false};
438 bool is_configuring{false}; 446 bool is_configuring{false};
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/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 381a66ba5..bc69117c6 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -50,7 +50,7 @@ public:
50 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, 50 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
51 {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, 51 {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
52 {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, 52 {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
53 {10, nullptr, "RequestUpdateAuto"}, 53 {10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"},
54 {11, nullptr, "ExecuteAudioRendererRendering"}, 54 {11, nullptr, "ExecuteAudioRendererRendering"},
55 }; 55 };
56 // clang-format on 56 // clang-format on
@@ -113,15 +113,30 @@ private:
113 113
114 // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for 114 // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for
115 // checking size 0. Performance size is 0 for most games. 115 // checking size 0. Performance size is 0 for most games.
116 const auto buffers{ctx.BufferDescriptorB()}; 116
117 std::vector<u8> output(buffers[0].Size(), 0); 117 std::vector<u8> output{};
118 std::vector<u8> performance(buffers[1].Size(), 0); 118 std::vector<u8> performance{};
119 auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0};
120 if (is_buffer_b) {
121 const auto buffersB{ctx.BufferDescriptorB()};
122 output.resize(buffersB[0].Size(), 0);
123 performance.resize(buffersB[1].Size(), 0);
124 } else {
125 const auto buffersC{ctx.BufferDescriptorC()};
126 output.resize(buffersC[0].Size(), 0);
127 performance.resize(buffersC[1].Size(), 0);
128 }
119 129
120 auto result = impl->RequestUpdate(input, performance, output); 130 auto result = impl->RequestUpdate(input, performance, output);
121 131
122 if (result.IsSuccess()) { 132 if (result.IsSuccess()) {
123 ctx.WriteBufferB(output.data(), output.size(), 0); 133 if (is_buffer_b) {
124 ctx.WriteBufferB(performance.data(), performance.size(), 1); 134 ctx.WriteBufferB(output.data(), output.size(), 0);
135 ctx.WriteBufferB(performance.data(), performance.size(), 1);
136 } else {
137 ctx.WriteBufferC(output.data(), output.size(), 0);
138 ctx.WriteBufferC(performance.data(), performance.size(), 1);
139 }
125 } else { 140 } else {
126 LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); 141 LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description);
127 } 142 }
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index fae6e5aff..e23eae36a 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -246,7 +246,8 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec
246 entries.reserve(entries.size() + new_data.size()); 246 entries.reserve(entries.size() + new_data.size());
247 247
248 for (const auto& new_entry : new_data) { 248 for (const auto& new_entry : new_data) {
249 entries.emplace_back(new_entry->GetName(), type, new_entry->GetSize()); 249 entries.emplace_back(new_entry->GetName(), type,
250 type == FileSys::EntryType::Directory ? 0 : new_entry->GetSize());
250 } 251 }
251} 252}
252 253
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/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 5ecbddf94..7909141c0 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -2146,12 +2146,18 @@ public:
2146 {324, nullptr, "GetUniquePadButtonSet"}, 2146 {324, nullptr, "GetUniquePadButtonSet"},
2147 {325, nullptr, "GetUniquePadColor"}, 2147 {325, nullptr, "GetUniquePadColor"},
2148 {326, nullptr, "GetUniquePadAppletDetailedUiType"}, 2148 {326, nullptr, "GetUniquePadAppletDetailedUiType"},
2149 {327, nullptr, "GetAbstractedPadIdDataFromNpad"},
2150 {328, nullptr, "AttachAbstractedPadToNpad"},
2151 {329, nullptr, "DetachAbstractedPadAll"},
2152 {330, nullptr, "CheckAbstractedPadConnection"},
2149 {500, nullptr, "SetAppletResourceUserId"}, 2153 {500, nullptr, "SetAppletResourceUserId"},
2150 {501, nullptr, "RegisterAppletResourceUserId"}, 2154 {501, nullptr, "RegisterAppletResourceUserId"},
2151 {502, nullptr, "UnregisterAppletResourceUserId"}, 2155 {502, nullptr, "UnregisterAppletResourceUserId"},
2152 {503, nullptr, "EnableAppletToGetInput"}, 2156 {503, nullptr, "EnableAppletToGetInput"},
2153 {504, nullptr, "SetAruidValidForVibration"}, 2157 {504, nullptr, "SetAruidValidForVibration"},
2154 {505, nullptr, "EnableAppletToGetSixAxisSensor"}, 2158 {505, nullptr, "EnableAppletToGetSixAxisSensor"},
2159 {506, nullptr, "EnableAppletToGetPadInput"},
2160 {507, nullptr, "EnableAppletToGetTouchScreen"},
2155 {510, nullptr, "SetVibrationMasterVolume"}, 2161 {510, nullptr, "SetVibrationMasterVolume"},
2156 {511, nullptr, "GetVibrationMasterVolume"}, 2162 {511, nullptr, "GetVibrationMasterVolume"},
2157 {512, nullptr, "BeginPermitVibrationSession"}, 2163 {512, nullptr, "BeginPermitVibrationSession"},
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/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/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/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/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/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 7d1431b6d..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
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 27e6ebf94..444c29f68 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -1402,12 +1402,15 @@ void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig&
1402 break; 1402 break;
1403 } 1403 }
1404 1404
1405 UNIMPLEMENTED_IF(framebuffer_crop_rect.top != 0);
1406 UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0); 1405 UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0);
1407 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 }
1408 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);
1409 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);
1410
1411 // 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
1412 // (e.g. handheld mode) on a 1920x1080 framebuffer. 1415 // (e.g. handheld mode) on a 1920x1080 framebuffer.
1413 if (!fsr) { 1416 if (!fsr) {
@@ -1426,10 +1429,13 @@ void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig&
1426 const auto y = static_cast<f32>(screen.top); 1429 const auto y = static_cast<f32>(screen.top);
1427 const auto w = static_cast<f32>(screen.GetWidth()); 1430 const auto w = static_cast<f32>(screen.GetWidth());
1428 const auto h = static_cast<f32>(screen.GetHeight()); 1431 const auto h = static_cast<f32>(screen.GetHeight());
1429 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);
1430 data.vertices[1] = ScreenRectVertex(x + w, y, texcoords.bottom * scale_u, left * scale_v); 1433 data.vertices[1] =
1431 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);
1432 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);
1433} 1439}
1434 1440
1435void BlitScreen::CreateFSR() { 1441void BlitScreen::CreateFSR() {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 16e46d3e5..7e40c2df1 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -69,10 +69,17 @@ VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t in
69 const float width = conv(src.scale_x * 2.0f); 69 const float width = conv(src.scale_x * 2.0f);
70 float y = conv(src.translate_y - src.scale_y); 70 float y = conv(src.translate_y - src.scale_y);
71 float height = conv(src.scale_y * 2.0f); 71 float height = conv(src.scale_y * 2.0f);
72 if (regs.screen_y_control.y_negate) { 72 bool y_negate = regs.screen_y_control.y_negate;
73
74 if (!device.IsNvViewportSwizzleSupported()) {
75 y_negate = y_negate != (src.swizzle.y == Maxwell::ViewportSwizzle::NegativeY);
76 }
77
78 if (y_negate) {
73 y += height; 79 y += height;
74 height = -height; 80 height = -height;
75 } 81 }
82
76 const float reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1.0f : 0.0f; 83 const float reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1.0f : 0.0f;
77 VkViewport viewport{ 84 VkViewport viewport{
78 .x = x, 85 .x = x,
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/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/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/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index c262d0a2b..d3fbdb09d 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -815,6 +815,12 @@ void GRenderWindow::InitializeCamera() {
815 if (Settings::values.ir_sensor_device.GetValue() == cameraInfo.deviceName().toStdString() || 815 if (Settings::values.ir_sensor_device.GetValue() == cameraInfo.deviceName().toStdString() ||
816 Settings::values.ir_sensor_device.GetValue() == "Auto") { 816 Settings::values.ir_sensor_device.GetValue() == "Auto") {
817 camera = std::make_unique<QCamera>(cameraInfo); 817 camera = std::make_unique<QCamera>(cameraInfo);
818 if (!camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureViewfinder) &&
819 !camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureStillImage)) {
820 LOG_ERROR(Frontend,
821 "Camera doesn't support CaptureViewfinder or CaptureStillImage");
822 continue;
823 }
818 camera_found = true; 824 camera_found = true;
819 break; 825 break;
820 } 826 }
@@ -825,10 +831,22 @@ void GRenderWindow::InitializeCamera() {
825 } 831 }
826 832
827 camera_capture = std::make_unique<QCameraImageCapture>(camera.get()); 833 camera_capture = std::make_unique<QCameraImageCapture>(camera.get());
834
835 if (!camera_capture->isCaptureDestinationSupported(
836 QCameraImageCapture::CaptureDestination::CaptureToBuffer)) {
837 LOG_ERROR(Frontend, "Camera doesn't support saving to buffer");
838 return;
839 }
840
841 camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer);
828 connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, 842 connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this,
829 &GRenderWindow::OnCameraCapture); 843 &GRenderWindow::OnCameraCapture);
830 camera->unload(); 844 camera->unload();
831 camera->setCaptureMode(QCamera::CaptureViewfinder); 845 if (camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureViewfinder)) {
846 camera->setCaptureMode(QCamera::CaptureViewfinder);
847 } else if (camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureStillImage)) {
848 camera->setCaptureMode(QCamera::CaptureStillImage);
849 }
832 camera->load(); 850 camera->load();
833 camera->start(); 851 camera->start();
834 852
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_camera.cpp b/src/yuzu/configuration/configure_camera.cpp
index 73cdcf3f2..2a61de2a1 100644
--- a/src/yuzu/configuration/configure_camera.cpp
+++ b/src/yuzu/configuration/configure_camera.cpp
@@ -42,6 +42,12 @@ void ConfigureCamera::PreviewCamera() {
42 LOG_INFO(Frontend, "Selected Camera {} {}", cameraInfo.description().toStdString(), 42 LOG_INFO(Frontend, "Selected Camera {} {}", cameraInfo.description().toStdString(),
43 cameraInfo.deviceName().toStdString()); 43 cameraInfo.deviceName().toStdString());
44 camera = std::make_unique<QCamera>(cameraInfo); 44 camera = std::make_unique<QCamera>(cameraInfo);
45 if (!camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureViewfinder) &&
46 !camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureStillImage)) {
47 LOG_ERROR(Frontend,
48 "Camera doesn't support CaptureViewfinder or CaptureStillImage");
49 continue;
50 }
45 camera_found = true; 51 camera_found = true;
46 break; 52 break;
47 } 53 }
@@ -57,10 +63,22 @@ void ConfigureCamera::PreviewCamera() {
57 } 63 }
58 64
59 camera_capture = std::make_unique<QCameraImageCapture>(camera.get()); 65 camera_capture = std::make_unique<QCameraImageCapture>(camera.get());
66
67 if (!camera_capture->isCaptureDestinationSupported(
68 QCameraImageCapture::CaptureDestination::CaptureToBuffer)) {
69 LOG_ERROR(Frontend, "Camera doesn't support saving to buffer");
70 return;
71 }
72
73 camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer);
60 connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, 74 connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this,
61 &ConfigureCamera::DisplayCapturedFrame); 75 &ConfigureCamera::DisplayCapturedFrame);
62 camera->unload(); 76 camera->unload();
63 camera->setCaptureMode(QCamera::CaptureViewfinder); 77 if (camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureViewfinder)) {
78 camera->setCaptureMode(QCamera::CaptureViewfinder);
79 } else if (camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureStillImage)) {
80 camera->setCaptureMode(QCamera::CaptureStillImage);
81 }
64 camera->load(); 82 camera->load();
65 camera->start(); 83 camera->start();
66 84
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/main.cpp b/src/yuzu/main.cpp
index 653280642..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());
@@ -3814,6 +3829,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
3814 3829
3815 render_window->close(); 3830 render_window->close();
3816 multiplayer_state->Close(); 3831 multiplayer_state->Close();
3832 system->GetRoomNetwork().Shutdown();
3817 3833
3818 QWidget::closeEvent(event); 3834 QWidget::closeEvent(event);
3819} 3835}
@@ -3935,8 +3951,21 @@ void GMainWindow::filterBarSetChecked(bool state) {
3935 emit(OnToggleFilterBar()); 3951 emit(OnToggleFilterBar());
3936} 3952}
3937 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
3938void GMainWindow::UpdateUITheme() { 3966void GMainWindow::UpdateUITheme() {
3939 const QString default_theme = QStringLiteral("default"); 3967 const QString default_theme =
3968 QString::fromUtf8(UISettings::themes[static_cast<size_t>(Config::default_theme)].second);
3940 QString current_theme = UISettings::values.theme; 3969 QString current_theme = UISettings::values.theme;
3941 QStringList theme_paths(default_theme_paths); 3970 QStringList theme_paths(default_theme_paths);
3942 3971
@@ -3944,6 +3973,23 @@ void GMainWindow::UpdateUITheme() {
3944 current_theme = default_theme; 3973 current_theme = default_theme;
3945 } 3974 }
3946 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
3947 if (current_theme != default_theme) { 3993 if (current_theme != default_theme) {
3948 QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)}; 3994 QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)};
3949 QFile f(theme_uri); 3995 QFile f(theme_uri);
@@ -3966,25 +4012,9 @@ void GMainWindow::UpdateUITheme() {
3966 qApp->setStyleSheet({}); 4012 qApp->setStyleSheet({});
3967 setStyleSheet({}); 4013 setStyleSheet({});
3968 } 4014 }
3969
3970 QPalette new_pal(qApp->palette());
3971 if (UISettings::IsDarkTheme()) {
3972 new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255));
3973 } else {
3974 new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255));
3975 }
3976 qApp->setPalette(new_pal);
3977
3978 QIcon::setThemeName(current_theme);
3979 QIcon::setThemeSearchPaths(theme_paths);
3980} 4015}
3981 4016
3982void GMainWindow::LoadTranslation() { 4017void GMainWindow::LoadTranslation() {
3983 // If the selected language is English, no need to install any translation
3984 if (UISettings::values.language == QStringLiteral("en")) {
3985 return;
3986 }
3987
3988 bool loaded; 4018 bool loaded;
3989 4019
3990 if (UISettings::values.language.isEmpty()) { 4020 if (UISettings::values.language.isEmpty()) {
@@ -4027,6 +4057,26 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
4027 discord_rpc->Update(); 4057 discord_rpc->Update();
4028} 4058}
4029 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
4030#ifdef main 4080#ifdef main
4031#undef main 4081#undef main
4032#endif 4082#endif
@@ -4072,6 +4122,15 @@ int main(int argc, char* argv[]) {
4072 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 4122 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
4073 QApplication app(argc, argv); 4123 QApplication app(argc, argv);
4074 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
4075 // 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
4076 // generating shaders 4135 // generating shaders
4077 setlocale(LC_ALL, "C"); 4136 setlocale(LC_ALL, "C");
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index e13b38b24..1ae2b93d9 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -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;