summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.ci/scripts/format/script.sh39
-rw-r--r--.ci/scripts/linux/exec.sh2
-rw-r--r--.ci/yuzu-mainline-step2.yml10
-rw-r--r--.github/workflows/verify.yml8
-rw-r--r--LICENSES/BSD-2-Clause.txt2
-rw-r--r--LICENSES/BSD-3-Clause.txt2
-rw-r--r--LICENSES/MPL-2.0.txt2
-rw-r--r--externals/ffmpeg/CMakeLists.txt2
m---------externals/nx_tzdb/tzdb_to_nx0
-rw-r--r--src/android/app/build.gradle.kts83
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt33
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractListAdapter.kt98
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt105
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt36
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt88
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt57
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt101
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt36
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt192
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt28
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt72
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt36
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt50
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt45
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt76
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Driver.kt27
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt129
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/viewholder/AbstractViewHolder.kt18
-rw-r--r--src/android/app/src/main/jni/native.cpp10
-rw-r--r--src/android/app/src/main/jni/native_config.cpp2
-rw-r--r--src/android/app/src/main/res/layout-w600dp/fragment_about.xml4
-rw-r--r--src/android/app/src/main/res/layout/fragment_about.xml4
-rw-r--r--src/android/app/src/main/res/menu/menu_driver_manager.xml11
-rw-r--r--src/android/app/src/main/res/values/arrays.xml6
-rw-r--r--src/android/app/src/main/res/values/strings.xml2
-rw-r--r--src/audio_core/adsp/adsp.cpp2
-rw-r--r--src/audio_core/device/device_session.cpp14
-rw-r--r--src/audio_core/device/device_session.h12
-rw-r--r--src/audio_core/in/audio_in_system.cpp2
-rw-r--r--src/audio_core/in/audio_in_system.h13
-rw-r--r--src/audio_core/out/audio_out_system.cpp4
-rw-r--r--src/audio_core/out/audio_out_system.h13
-rw-r--r--src/audio_core/renderer/command/command_generator.cpp12
-rw-r--r--src/audio_core/renderer/mix/mix_info.cpp2
-rw-r--r--src/audio_core/renderer/splitter/splitter_context.cpp2
-rw-r--r--src/audio_core/renderer/splitter/splitter_context.h2
-rw-r--r--src/common/page_table.cpp34
-rw-r--r--src/common/settings_common.cpp2
-rw-r--r--src/common/settings_common.h2
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/nce/patcher.cpp83
-rw-r--r--src/core/arm/nce/patcher.h27
-rw-r--r--src/core/core.cpp1
-rw-r--r--src/core/cpu_manager.h2
-rw-r--r--src/core/debugger/debugger.cpp39
-rw-r--r--src/core/debugger/gdbstub.cpp110
-rw-r--r--src/core/debugger/gdbstub.h15
-rw-r--r--src/core/file_sys/savedata_factory.cpp17
-rw-r--r--src/core/file_sys/savedata_factory.h10
-rw-r--r--src/core/frontend/applets/controller.cpp2
-rw-r--r--src/core/hle/kernel/k_memory_block.h14
-rw-r--r--src/core/hle/kernel/k_memory_block_manager.cpp4
-rw-r--r--src/core/hle/kernel/k_memory_block_manager.h4
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp46
-rw-r--r--src/core/hle/kernel/k_page_table_base.h1
-rw-r--r--src/core/hle/kernel/k_process.cpp10
-rw-r--r--src/core/hle/kernel/k_transfer_memory.cpp8
-rw-r--r--src/core/hle/kernel/kernel.cpp31
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/kernel/svc/svc_process.cpp8
-rw-r--r--src/core/hle/kernel/svc/svc_transfer_memory.cpp4
-rw-r--r--src/core/hle/kernel/svc_types.h4
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp19
-rw-r--r--src/core/hle/service/acc/profile_manager.h1
-rw-r--r--src/core/hle/service/am/am.cpp7
-rw-r--r--src/core/hle/service/am/applets/applet_profile_select.h2
-rw-r--r--src/core/hle/service/audio/audin_u.cpp36
-rw-r--r--src/core/hle/service/audio/audout_u.cpp26
-rw-r--r--src/core/hle/service/caps/caps_manager.cpp10
-rw-r--r--src/core/hle/service/caps/caps_manager.h4
-rw-r--r--src/core/hle/service/caps/caps_result.h2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp227
-rw-r--r--src/core/hle/service/filesystem/filesystem.h59
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp55
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h6
-rw-r--r--src/core/hle/service/filesystem/romfs_controller.cpp37
-rw-r--r--src/core/hle/service/filesystem/romfs_controller.h31
-rw-r--r--src/core/hle/service/filesystem/save_data_controller.cpp99
-rw-r--r--src/core/hle/service/filesystem/save_data_controller.h35
-rw-r--r--src/core/hle/service/friend/friend.cpp2
-rw-r--r--src/core/hle/service/glue/arp.cpp7
-rw-r--r--src/core/hle/service/hid/hid.cpp18
-rw-r--r--src/core/hle/service/hid/hid_server.cpp2
-rw-r--r--src/core/hle/service/hid/hidbus.cpp8
-rw-r--r--src/core/hle/service/hid/hidbus.h2
-rw-r--r--src/core/hle/service/hle_ipc.cpp20
-rw-r--r--src/core/hle/service/hle_ipc.h18
-rw-r--r--src/core/hle/service/nfc/common/amiibo_crypto.cpp10
-rw-r--r--src/core/hle/service/nfc/common/device.cpp6
-rw-r--r--src/core/hle/service/nfc/common/device.h2
-rw-r--r--src/core/hle/service/nfp/nfp_types.h14
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp2
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h2
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp3
-rw-r--r--src/core/hle/service/pcv/pcv.cpp10
-rw-r--r--src/core/hle/service/pm/pm.cpp85
-rw-r--r--src/core/hle/service/server_manager.cpp9
-rw-r--r--src/core/hle/service/set/system_settings.cpp2
-rw-r--r--src/core/hle/service/set/system_settings.h4
-rw-r--r--src/core/hle/service/set/system_settings_server.cpp10
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp13
-rw-r--r--src/core/hle/service/vi/display/vi_display.h12
-rw-r--r--src/core/hle/service/vi/vi.cpp59
-rw-r--r--src/core/hle/service/vi/vi.h2
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp89
-rw-r--r--src/core/loader/nca.cpp6
-rw-r--r--src/core/loader/nro.cpp10
-rw-r--r--src/core/loader/nso.cpp41
-rw-r--r--src/core/loader/nso.h3
-rw-r--r--src/core/loader/nsp.cpp3
-rw-r--r--src/core/loader/xci.cpp3
-rw-r--r--src/hid_core/CMakeLists.txt34
-rw-r--r--src/hid_core/frontend/emulated_controller.cpp54
-rw-r--r--src/hid_core/frontend/emulated_controller.h2
-rw-r--r--src/hid_core/frontend/motion_input.h4
-rw-r--r--src/hid_core/hid_result.h2
-rw-r--r--src/hid_core/hid_types.h3
-rw-r--r--src/hid_core/hid_util.h2
-rw-r--r--src/hid_core/irsensor/irs_types.h4
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp197
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_battery_handler.h49
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp199
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_button_handler.h75
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp126
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h56
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp123
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_led_handler.h43
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp108
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h52
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp140
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h57
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_pad.cpp294
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_pad.h123
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_pad_holder.cpp99
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_pad_holder.h47
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp47
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_palma_handler.h37
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp322
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_properties_handler.h86
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp154
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h61
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp73
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h51
-rw-r--r--src/hid_core/resources/hid_firmware_settings.cpp4
-rw-r--r--src/hid_core/resources/hid_firmware_settings.h2
-rw-r--r--src/hid_core/resources/npad/npad.cpp8
-rw-r--r--src/hid_core/resources/npad/npad_data.cpp2
-rw-r--r--src/hid_core/resources/npad/npad_types.h99
-rw-r--r--src/hid_core/resources/npad/npad_vibration.cpp80
-rw-r--r--src/hid_core/resources/npad/npad_vibration.h34
-rw-r--r--src/hid_core/resources/six_axis/six_axis.cpp10
-rw-r--r--src/hid_core/resources/six_axis/six_axis.h2
-rw-r--r--src/hid_core/resources/vibration/gc_vibration_device.cpp106
-rw-r--r--src/hid_core/resources/vibration/gc_vibration_device.h31
-rw-r--r--src/hid_core/resources/vibration/n64_vibration_device.cpp80
-rw-r--r--src/hid_core/resources/vibration/n64_vibration_device.h29
-rw-r--r--src/hid_core/resources/vibration/vibration_base.cpp30
-rw-r--r--src/hid_core/resources/vibration/vibration_base.h28
-rw-r--r--src/hid_core/resources/vibration/vibration_device.cpp84
-rw-r--r--src/hid_core/resources/vibration/vibration_device.h35
-rw-r--r--src/input_common/drivers/gc_adapter.cpp4
-rw-r--r--src/input_common/helpers/joycon_protocol/irs.cpp4
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h6
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp8
-rw-r--r--src/input_common/helpers/joycon_protocol/rumble.cpp12
-rw-r--r--src/input_common/helpers/udp_protocol.h2
-rw-r--r--src/network/room_member.cpp2
-rw-r--r--src/network/room_member.h2
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp40
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp51
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h2
-rw-r--r--src/yuzu/applets/qt_amiibo_settings.cpp4
-rw-r--r--src/yuzu/applets/qt_amiibo_settings.h2
-rw-r--r--src/yuzu/applets/qt_controller.cpp8
-rw-r--r--src/yuzu/applets/qt_software_keyboard.cpp2
-rw-r--r--src/yuzu/bootmanager.h2
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp18
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp26
-rw-r--r--src/yuzu/configuration/configure_profile_manager.cpp4
-rw-r--r--src/yuzu/configuration/configure_system.cpp2
-rw-r--r--src/yuzu/configuration/shared_widget.cpp4
-rw-r--r--src/yuzu/main.cpp10
-rw-r--r--src/yuzu/multiplayer/chat_room.cpp2
-rw-r--r--src/yuzu/util/controller_navigation.cpp4
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
203 files changed, 5229 insertions, 1527 deletions
diff --git a/.ci/scripts/format/script.sh b/.ci/scripts/format/script.sh
index 25b0718f0..f9c63dbfa 100755
--- a/.ci/scripts/format/script.sh
+++ b/.ci/scripts/format/script.sh
@@ -3,38 +3,35 @@
3# SPDX-FileCopyrightText: 2019 yuzu Emulator Project 3# SPDX-FileCopyrightText: 2019 yuzu Emulator Project
4# SPDX-License-Identifier: GPL-2.0-or-later 4# SPDX-License-Identifier: GPL-2.0-or-later
5 5
6if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop \ 6shopt -s nullglob globstar
7 dist/*.svg dist/*.xml; then 7
8if git grep -nrI '\s$' src **/*.yml **/*.txt **/*.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop dist/*.svg dist/*.xml; then
8 echo Trailing whitespace found, aborting 9 echo Trailing whitespace found, aborting
9 exit 1 10 exit 1
10fi 11fi
11 12
12# Default clang-format points to default 3.5 version one 13# Default clang-format points to default 3.5 version one
13CLANG_FORMAT=${CLANG_FORMAT:-clang-format-15} 14CLANG_FORMAT="${CLANG_FORMAT:-clang-format-15}"
14$CLANG_FORMAT --version 15"$CLANG_FORMAT" --version
15
16if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then
17 # Get list of every file modified in this pull request
18 files_to_lint="$(git diff --name-only --diff-filter=ACMRTUXB $TRAVIS_COMMIT_RANGE | grep '^src/[^.]*[.]\(cpp\|h\)$' || true)"
19else
20 # Check everything for branch pushes
21 files_to_lint="$(find src/ -name '*.cpp' -or -name '*.h')"
22fi
23 16
24# Turn off tracing for this because it's too verbose 17# Turn off tracing for this because it's too verbose
25set +x 18set +x
26 19
27for f in $files_to_lint; do 20# Check everything for branch pushes
28 d=$(diff -u "$f" <($CLANG_FORMAT "$f") || true) 21FILES_TO_LINT="$(find src/ -name '*.cpp' -or -name '*.h')"
29 if ! [ -z "$d" ]; then 22
30 echo "!!! $f not compliant to coding style, here is the fix:" 23for f in $FILES_TO_LINT; do
31 echo "$d" 24 echo "$f"
32 fail=1 25 "$CLANG_FORMAT" -i "$f"
33 fi
34done 26done
35 27
36set -x 28DIFF=$(git -c core.fileMode=false diff)
37 29
38if [ "$fail" = 1 ]; then 30if [ ! -z "$DIFF" ]; then
31 echo "!!! Not compliant to coding style, here is the fix:"
32 echo "$DIFF"
39 exit 1 33 exit 1
40fi 34fi
35
36cd src/android
37./gradlew ktlintCheck
diff --git a/.ci/scripts/linux/exec.sh b/.ci/scripts/linux/exec.sh
index fa3d78cc2..04e2486a1 100644
--- a/.ci/scripts/linux/exec.sh
+++ b/.ci/scripts/linux/exec.sh
@@ -9,7 +9,7 @@ chmod a+x ./.ci/scripts/linux/docker.sh
9sudo chown -R 1027 ./ 9sudo chown -R 1027 ./
10 10
11# The environment variables listed below: 11# The environment variables listed below:
12# AZURECIREPO TITLEBARFORMATIDLE TITLEBARFORMATRUNNING DISPLAYVERSION 12# AZURECIREPO TITLEBARFORMATIDLE TITLEBARFORMATRUNNING DISPLAYVERSION
13# are requested in src/common/CMakeLists.txt and appear to be provided somewhere in Azure DevOps 13# are requested in src/common/CMakeLists.txt and appear to be provided somewhere in Azure DevOps
14 14
15docker run -e AZURECIREPO -e TITLEBARFORMATIDLE -e TITLEBARFORMATRUNNING -e DISPLAYVERSION -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v "$(pwd):/yuzu" -w /yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/linux/docker.sh "$1" 15docker run -e AZURECIREPO -e TITLEBARFORMATIDLE -e TITLEBARFORMATRUNNING -e DISPLAYVERSION -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v "$(pwd):/yuzu" -w /yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/linux/docker.sh "$1"
diff --git a/.ci/yuzu-mainline-step2.yml b/.ci/yuzu-mainline-step2.yml
index b294827f4..8bb0572f5 100644
--- a/.ci/yuzu-mainline-step2.yml
+++ b/.ci/yuzu-mainline-step2.yml
@@ -8,17 +8,7 @@ variables:
8 DisplayVersion: $[counter(variables['DisplayPrefix'], 1)] 8 DisplayVersion: $[counter(variables['DisplayPrefix'], 1)]
9 9
10stages: 10stages:
11- stage: format
12 displayName: 'format'
13 jobs:
14 - job: format
15 displayName: 'clang'
16 pool:
17 vmImage: ubuntu-latest
18 steps:
19 - template: ./templates/format-check.yml
20- stage: build 11- stage: build
21 dependsOn: format
22 displayName: 'build' 12 displayName: 'build'
23 jobs: 13 jobs:
24 - job: build 14 - job: build
diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml
index c073f3f3f..62eb69aeb 100644
--- a/.github/workflows/verify.yml
+++ b/.github/workflows/verify.yml
@@ -13,13 +13,15 @@ jobs:
13 format: 13 format:
14 name: 'verify format' 14 name: 'verify format'
15 runs-on: ubuntu-latest 15 runs-on: ubuntu-latest
16 container:
17 image: yuzuemu/build-environments:linux-clang-format
18 options: -u 1001
19 steps: 16 steps:
20 - uses: actions/checkout@v3 17 - uses: actions/checkout@v3
21 with: 18 with:
22 submodules: false 19 submodules: false
20 - name: set up JDK 17
21 uses: actions/setup-java@v3
22 with:
23 java-version: '17'
24 distribution: 'temurin'
23 - name: 'Verify Formatting' 25 - name: 'Verify Formatting'
24 run: bash -ex ./.ci/scripts/format/script.sh 26 run: bash -ex ./.ci/scripts/format/script.sh
25 build: 27 build:
diff --git a/LICENSES/BSD-2-Clause.txt b/LICENSES/BSD-2-Clause.txt
index 5f662b354..eb3c575b8 100644
--- a/LICENSES/BSD-2-Clause.txt
+++ b/LICENSES/BSD-2-Clause.txt
@@ -1,4 +1,4 @@
1Copyright (c) <year> <owner> 1Copyright (c) <year> <owner>
2 2
3Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 3Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4 4
diff --git a/LICENSES/BSD-3-Clause.txt b/LICENSES/BSD-3-Clause.txt
index ea890afbc..086d3992c 100644
--- a/LICENSES/BSD-3-Clause.txt
+++ b/LICENSES/BSD-3-Clause.txt
@@ -1,4 +1,4 @@
1Copyright (c) <year> <owner>. 1Copyright (c) <year> <owner>.
2 2
3Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 3Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4 4
diff --git a/LICENSES/MPL-2.0.txt b/LICENSES/MPL-2.0.txt
index 14e2f777f..a612ad981 100644
--- a/LICENSES/MPL-2.0.txt
+++ b/LICENSES/MPL-2.0.txt
@@ -35,7 +35,7 @@ Mozilla Public License Version 2.0
35 means any form of the work other than Source Code Form. 35 means any form of the work other than Source Code Form.
36 36
371.7. "Larger Work" 371.7. "Larger Work"
38 means a work that combines Covered Software with other material, in 38 means a work that combines Covered Software with other material, in
39 a separate file or files, that is not Covered Software. 39 a separate file or files, that is not Covered Software.
40 40
411.8. "License" 411.8. "License"
diff --git a/externals/ffmpeg/CMakeLists.txt b/externals/ffmpeg/CMakeLists.txt
index f2886eb6c..543585d4f 100644
--- a/externals/ffmpeg/CMakeLists.txt
+++ b/externals/ffmpeg/CMakeLists.txt
@@ -138,7 +138,7 @@ if (NOT WIN32 AND NOT ANDROID)
138 --cross-prefix=${TOOLCHAIN}/bin/aarch64-linux-android- 138 --cross-prefix=${TOOLCHAIN}/bin/aarch64-linux-android-
139 --sysroot=${SYSROOT} 139 --sysroot=${SYSROOT}
140 --target-os=android 140 --target-os=android
141 --extra-ldflags="--ld-path=${TOOLCHAIN}/bin/ld.lld" 141 --extra-ldflags="--ld-path=${TOOLCHAIN}/bin/ld.lld"
142 --extra-ldflags="-nostdlib" 142 --extra-ldflags="-nostdlib"
143 ) 143 )
144 endif() 144 endif()
diff --git a/externals/nx_tzdb/tzdb_to_nx b/externals/nx_tzdb/tzdb_to_nx
Subproject f6680093bca30265c161581fd813d4ddd33f1e3 Subproject 404d39004570a26c734a9d1fa29ab4d63089c59
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index 53aafa08c..06e59d1ac 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -188,8 +188,15 @@ tasks.create<Delete>("ktlintReset") {
188 delete(File(buildDir.path + File.separator + "intermediates/ktLint")) 188 delete(File(buildDir.path + File.separator + "intermediates/ktLint"))
189} 189}
190 190
191val showFormatHelp = {
192 logger.lifecycle(
193 "If this check fails, please try running \"gradlew ktlintFormat\" for automatic " +
194 "codestyle fixes"
195 )
196}
197tasks.getByPath("ktlintKotlinScriptCheck").doFirst { showFormatHelp.invoke() }
198tasks.getByPath("ktlintMainSourceSetCheck").doFirst { showFormatHelp.invoke() }
191tasks.getByPath("loadKtlintReporters").dependsOn("ktlintReset") 199tasks.getByPath("loadKtlintReporters").dependsOn("ktlintReset")
192tasks.getByPath("preBuild").dependsOn("ktlintCheck")
193 200
194ktlint { 201ktlint {
195 version.set("0.47.1") 202 version.set("0.47.1")
@@ -228,71 +235,33 @@ dependencies {
228 implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") 235 implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
229} 236}
230 237
231fun getGitVersion(): String { 238fun runGitCommand(command: List<String>): String {
232 var versionName = "0.0" 239 return try {
233 240 ProcessBuilder(command)
234 try {
235 versionName = ProcessBuilder("git", "describe", "--always", "--long")
236 .directory(project.rootDir) 241 .directory(project.rootDir)
237 .redirectOutput(ProcessBuilder.Redirect.PIPE) 242 .redirectOutput(ProcessBuilder.Redirect.PIPE)
238 .redirectError(ProcessBuilder.Redirect.PIPE) 243 .redirectError(ProcessBuilder.Redirect.PIPE)
239 .start().inputStream.bufferedReader().use { it.readText() } 244 .start().inputStream.bufferedReader().use { it.readText() }
240 .trim() 245 .trim()
241 .replace(Regex("(-0)?-[^-]+$"), "")
242 } catch (e: Exception) { 246 } catch (e: Exception) {
243 logger.error("Cannot find git, defaulting to dummy version number") 247 logger.error("Cannot find git")
248 ""
244 } 249 }
245
246 if (System.getenv("GITHUB_ACTIONS") != null) {
247 val gitTag = System.getenv("GIT_TAG_NAME")
248 versionName = gitTag ?: versionName
249 }
250
251 return versionName
252} 250}
253 251
254fun getGitHash(): String { 252fun getGitVersion(): String {
255 try { 253 val versionName = if (System.getenv("GITHUB_ACTIONS") != null) {
256 val processBuilder = ProcessBuilder("git", "rev-parse", "--short", "HEAD") 254 val gitTag = System.getenv("GIT_TAG_NAME") ?: ""
257 processBuilder.directory(project.rootDir) 255 gitTag
258 val process = processBuilder.start() 256 } else {
259 val inputStream = process.inputStream 257 runGitCommand(listOf("git", "describe", "--always", "--long"))
260 val errorStream = process.errorStream 258 .replace(Regex("(-0)?-[^-]+$"), "")
261 process.waitFor()
262
263 return if (process.exitValue() == 0) {
264 inputStream.bufferedReader()
265 .use { it.readText().trim() } // return the value of gitHash
266 } else {
267 val errorMessage = errorStream.bufferedReader().use { it.readText().trim() }
268 logger.error("Error running git command: $errorMessage")
269 "dummy-hash" // return a dummy hash value in case of an error
270 }
271 } catch (e: Exception) {
272 logger.error("$e: Cannot find git, defaulting to dummy build hash")
273 return "dummy-hash" // return a dummy hash value in case of an error
274 } 259 }
260 return versionName.ifEmpty { "0.0" }
275} 261}
276 262
277fun getBranch(): String { 263fun getGitHash(): String =
278 try { 264 runGitCommand(listOf("git", "rev-parse", "--short", "HEAD")).ifEmpty { "dummy-hash" }
279 val processBuilder = ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD") 265
280 processBuilder.directory(project.rootDir) 266fun getBranch(): String =
281 val process = processBuilder.start() 267 runGitCommand(listOf("git", "rev-parse", "--abbrev-ref", "HEAD")).ifEmpty { "dummy-hash" }
282 val inputStream = process.inputStream
283 val errorStream = process.errorStream
284 process.waitFor()
285
286 return if (process.exitValue() == 0) {
287 inputStream.bufferedReader()
288 .use { it.readText().trim() } // return the value of gitHash
289 } else {
290 val errorMessage = errorStream.bufferedReader().use { it.readText().trim() }
291 logger.error("Error running git command: $errorMessage")
292 "dummy-hash" // return a dummy hash value in case of an error
293 }
294 } catch (e: Exception) {
295 logger.error("$e: Cannot find git, defaulting to dummy build hash")
296 return "dummy-hash" // return a dummy hash value in case of an error
297 }
298}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt
new file mode 100644
index 000000000..f006f9e3d
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.adapters
5
6import android.annotation.SuppressLint
7import androidx.recyclerview.widget.AsyncDifferConfig
8import androidx.recyclerview.widget.DiffUtil
9import androidx.recyclerview.widget.ListAdapter
10import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
11import androidx.recyclerview.widget.RecyclerView
12
13/**
14 * Generic adapter that implements an [AsyncDifferConfig] and covers some of the basic boilerplate
15 * code used in every [RecyclerView].
16 * Type assigned to [Model] must inherit from [Object] in order to be compared properly.
17 */
18abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>> :
19 ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>()).build()) {
20 override fun onBindViewHolder(holder: Holder, position: Int) =
21 holder.bind(currentList[position])
22
23 private class DiffCallback<Model> : DiffUtil.ItemCallback<Model>() {
24 override fun areItemsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
25 return oldItem === newItem
26 }
27
28 @SuppressLint("DiffUtilEquals")
29 override fun areContentsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
30 return oldItem == newItem
31 }
32 }
33}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractListAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractListAdapter.kt
new file mode 100644
index 000000000..3dfee3d0c
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractListAdapter.kt
@@ -0,0 +1,98 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.adapters
5
6import android.annotation.SuppressLint
7import androidx.recyclerview.widget.RecyclerView
8import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
9
10/**
11 * Generic list class meant to take care of basic lists
12 * @param currentList The list to show initially
13 */
14abstract class AbstractListAdapter<Model : Any, Holder : AbstractViewHolder<Model>>(
15 open var currentList: List<Model>
16) : RecyclerView.Adapter<Holder>() {
17 override fun onBindViewHolder(holder: Holder, position: Int) =
18 holder.bind(currentList[position])
19
20 override fun getItemCount(): Int = currentList.size
21
22 /**
23 * Adds an item to [currentList] and notifies the underlying adapter of the change. If no parameter
24 * is passed in for position, [item] is added to the end of the list. Invokes [callback] last.
25 * @param item The item to add to the list
26 * @param position Index where [item] will be added
27 * @param callback Lambda that's called at the end of the list changes and has the added list
28 * position passed in as a parameter
29 */
30 open fun addItem(item: Model, position: Int = -1, callback: ((position: Int) -> Unit)? = null) {
31 val newList = currentList.toMutableList()
32 val positionToUpdate: Int
33 if (position == -1) {
34 newList.add(item)
35 currentList = newList
36 positionToUpdate = currentList.size - 1
37 } else {
38 newList.add(position, item)
39 currentList = newList
40 positionToUpdate = position
41 }
42 onItemAdded(positionToUpdate, callback)
43 }
44
45 protected fun onItemAdded(position: Int, callback: ((Int) -> Unit)? = null) {
46 notifyItemInserted(position)
47 callback?.invoke(position)
48 }
49
50 /**
51 * Replaces the [item] at [position] in the [currentList] and notifies the underlying adapter
52 * of the change. Invokes [callback] last.
53 * @param item New list item
54 * @param position Index where [item] will replace the existing list item
55 * @param callback Lambda that's called at the end of the list changes and has the changed list
56 * position passed in as a parameter
57 */
58 fun changeItem(item: Model, position: Int, callback: ((position: Int) -> Unit)? = null) {
59 val newList = currentList.toMutableList()
60 newList[position] = item
61 currentList = newList
62 onItemChanged(position, callback)
63 }
64
65 protected fun onItemChanged(position: Int, callback: ((Int) -> Unit)? = null) {
66 notifyItemChanged(position)
67 callback?.invoke(position)
68 }
69
70 /**
71 * Removes the list item at [position] in [currentList] and notifies the underlying adapter
72 * of the change. Invokes [callback] last.
73 * @param position Index where the list item will be removed
74 * @param callback Lambda that's called at the end of the list changes and has the removed list
75 * position passed in as a parameter
76 */
77 fun removeItem(position: Int, callback: ((position: Int) -> Unit)? = null) {
78 val newList = currentList.toMutableList()
79 newList.removeAt(position)
80 currentList = newList
81 onItemRemoved(position, callback)
82 }
83
84 protected fun onItemRemoved(position: Int, callback: ((Int) -> Unit)? = null) {
85 notifyItemRemoved(position)
86 callback?.invoke(position)
87 }
88
89 /**
90 * Replaces [currentList] with [newList] and notifies the underlying adapter of the change.
91 * @param newList The new list to replace [currentList]
92 */
93 @SuppressLint("NotifyDataSetChanged")
94 open fun replaceList(newList: List<Model>) {
95 currentList = newList
96 notifyDataSetChanged()
97 }
98}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt
new file mode 100644
index 000000000..52163f9d7
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt
@@ -0,0 +1,105 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.adapters
5
6import org.yuzu.yuzu_emu.model.SelectableItem
7import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
8
9/**
10 * Generic list class meant to take care of single selection UI updates
11 * @param currentList The list to show initially
12 * @param defaultSelection The default selection to use if no list items are selected by
13 * [SelectableItem.selected] or if the currently selected item is removed from the list
14 */
15abstract class AbstractSingleSelectionList<
16 Model : SelectableItem,
17 Holder : AbstractViewHolder<Model>
18 >(
19 final override var currentList: List<Model>,
20 private val defaultSelection: DefaultSelection = DefaultSelection.Start
21) : AbstractListAdapter<Model, Holder>(currentList) {
22 var selectedItem = getDefaultSelection()
23
24 init {
25 findSelectedItem()
26 }
27
28 /**
29 * Changes the selection state of the [SelectableItem] that was selected and the previously selected
30 * item and notifies the underlying adapter of the change for those items. Invokes [callback] last.
31 * Does nothing if [position] is the same as the currently selected item.
32 * @param position Index of the item that was selected
33 * @param callback Lambda that's called at the end of the list changes and has the selected list
34 * position passed in as a parameter
35 */
36 fun selectItem(position: Int, callback: ((position: Int) -> Unit)? = null) {
37 if (position == selectedItem) {
38 return
39 }
40
41 val previouslySelectedItem = selectedItem
42 selectedItem = position
43 if (currentList.indices.contains(selectedItem)) {
44 currentList[selectedItem].onSelectionStateChanged(true)
45 }
46 if (currentList.indices.contains(previouslySelectedItem)) {
47 currentList[previouslySelectedItem].onSelectionStateChanged(false)
48 }
49 onItemChanged(previouslySelectedItem)
50 onItemChanged(selectedItem)
51 callback?.invoke(position)
52 }
53
54 /**
55 * Removes a given item from the list and notifies the underlying adapter of the change. If the
56 * currently selected item was the item that was removed, the item at the position provided
57 * by [defaultSelection] will be made the new selection. Invokes [callback] last.
58 * @param position Index of the item that was removed
59 * @param callback Lambda that's called at the end of the list changes and has the removed and
60 * selected list positions passed in as parameters
61 */
62 fun removeSelectableItem(
63 position: Int,
64 callback: ((removedPosition: Int, selectedPosition: Int) -> Unit)?
65 ) {
66 removeItem(position)
67 if (position == selectedItem) {
68 selectedItem = getDefaultSelection()
69 currentList[selectedItem].onSelectionStateChanged(true)
70 onItemChanged(selectedItem)
71 } else if (position < selectedItem) {
72 selectedItem--
73 }
74 callback?.invoke(position, selectedItem)
75 }
76
77 override fun addItem(item: Model, position: Int, callback: ((Int) -> Unit)?) {
78 super.addItem(item, position, callback)
79 if (position <= selectedItem && position != -1) {
80 selectedItem++
81 }
82 }
83
84 override fun replaceList(newList: List<Model>) {
85 super.replaceList(newList)
86 findSelectedItem()
87 }
88
89 private fun findSelectedItem() {
90 for (i in currentList.indices) {
91 if (currentList[i].selected) {
92 selectedItem = i
93 break
94 }
95 }
96 }
97
98 private fun getDefaultSelection(): Int =
99 when (defaultSelection) {
100 DefaultSelection.Start -> currentList.indices.first
101 DefaultSelection.End -> currentList.indices.last
102 }
103
104 enum class DefaultSelection { Start, End }
105}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt
index 15c7ca3c9..94c151325 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt
@@ -5,48 +5,28 @@ package org.yuzu.yuzu_emu.adapters
5 5
6import android.view.LayoutInflater 6import android.view.LayoutInflater
7import android.view.ViewGroup 7import android.view.ViewGroup
8import androidx.recyclerview.widget.AsyncDifferConfig
9import androidx.recyclerview.widget.DiffUtil
10import androidx.recyclerview.widget.ListAdapter
11import androidx.recyclerview.widget.RecyclerView
12import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding 8import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding
13import org.yuzu.yuzu_emu.model.Addon 9import org.yuzu.yuzu_emu.model.Addon
10import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
14 11
15class AddonAdapter : ListAdapter<Addon, AddonAdapter.AddonViewHolder>( 12class AddonAdapter : AbstractDiffAdapter<Addon, AddonAdapter.AddonViewHolder>() {
16 AsyncDifferConfig.Builder(DiffCallback()).build()
17) {
18 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddonViewHolder { 13 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddonViewHolder {
19 ListItemAddonBinding.inflate(LayoutInflater.from(parent.context), parent, false) 14 ListItemAddonBinding.inflate(LayoutInflater.from(parent.context), parent, false)
20 .also { return AddonViewHolder(it) } 15 .also { return AddonViewHolder(it) }
21 } 16 }
22 17
23 override fun getItemCount(): Int = currentList.size
24
25 override fun onBindViewHolder(holder: AddonViewHolder, position: Int) =
26 holder.bind(currentList[position])
27
28 inner class AddonViewHolder(val binding: ListItemAddonBinding) : 18 inner class AddonViewHolder(val binding: ListItemAddonBinding) :
29 RecyclerView.ViewHolder(binding.root) { 19 AbstractViewHolder<Addon>(binding) {
30 fun bind(addon: Addon) { 20 override fun bind(model: Addon) {
31 binding.root.setOnClickListener { 21 binding.root.setOnClickListener {
32 binding.addonSwitch.isChecked = !binding.addonSwitch.isChecked 22 binding.addonSwitch.isChecked = !binding.addonSwitch.isChecked
33 } 23 }
34 binding.title.text = addon.title 24 binding.title.text = model.title
35 binding.version.text = addon.version 25 binding.version.text = model.version
36 binding.addonSwitch.setOnCheckedChangeListener { _, checked -> 26 binding.addonSwitch.setOnCheckedChangeListener { _, checked ->
37 addon.enabled = checked 27 model.enabled = checked
38 } 28 }
39 binding.addonSwitch.isChecked = addon.enabled 29 binding.addonSwitch.isChecked = model.enabled
40 }
41 }
42
43 private class DiffCallback : DiffUtil.ItemCallback<Addon>() {
44 override fun areItemsTheSame(oldItem: Addon, newItem: Addon): Boolean {
45 return oldItem == newItem
46 }
47
48 override fun areContentsTheSame(oldItem: Addon, newItem: Addon): Boolean {
49 return oldItem == newItem
50 } 30 }
51 } 31 }
52} 32}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt
index 4a05c5be9..41d7f72b8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt
@@ -4,13 +4,11 @@
4package org.yuzu.yuzu_emu.adapters 4package org.yuzu.yuzu_emu.adapters
5 5
6import android.view.LayoutInflater 6import android.view.LayoutInflater
7import android.view.View
8import android.view.ViewGroup 7import android.view.ViewGroup
9import android.widget.Toast 8import android.widget.Toast
10import androidx.core.content.res.ResourcesCompat 9import androidx.core.content.res.ResourcesCompat
11import androidx.fragment.app.FragmentActivity 10import androidx.fragment.app.FragmentActivity
12import androidx.navigation.findNavController 11import androidx.navigation.findNavController
13import androidx.recyclerview.widget.RecyclerView
14import org.yuzu.yuzu_emu.HomeNavigationDirections 12import org.yuzu.yuzu_emu.HomeNavigationDirections
15import org.yuzu.yuzu_emu.NativeLibrary 13import org.yuzu.yuzu_emu.NativeLibrary
16import org.yuzu.yuzu_emu.R 14import org.yuzu.yuzu_emu.R
@@ -19,72 +17,58 @@ import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
19import org.yuzu.yuzu_emu.model.Applet 17import org.yuzu.yuzu_emu.model.Applet
20import org.yuzu.yuzu_emu.model.AppletInfo 18import org.yuzu.yuzu_emu.model.AppletInfo
21import org.yuzu.yuzu_emu.model.Game 19import org.yuzu.yuzu_emu.model.Game
20import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
22 21
23class AppletAdapter(val activity: FragmentActivity, var applets: List<Applet>) : 22class AppletAdapter(val activity: FragmentActivity, applets: List<Applet>) :
24 RecyclerView.Adapter<AppletAdapter.AppletViewHolder>(), 23 AbstractListAdapter<Applet, AppletAdapter.AppletViewHolder>(applets) {
25 View.OnClickListener {
26
27 override fun onCreateViewHolder( 24 override fun onCreateViewHolder(
28 parent: ViewGroup, 25 parent: ViewGroup,
29 viewType: Int 26 viewType: Int
30 ): AppletAdapter.AppletViewHolder { 27 ): AppletAdapter.AppletViewHolder {
31 CardSimpleOutlinedBinding.inflate(LayoutInflater.from(parent.context), parent, false) 28 CardSimpleOutlinedBinding.inflate(LayoutInflater.from(parent.context), parent, false)
32 .apply { root.setOnClickListener(this@AppletAdapter) }
33 .also { return AppletViewHolder(it) } 29 .also { return AppletViewHolder(it) }
34 } 30 }
35 31
36 override fun onBindViewHolder(holder: AppletViewHolder, position: Int) =
37 holder.bind(applets[position])
38
39 override fun getItemCount(): Int = applets.size
40
41 override fun onClick(view: View) {
42 val applet = (view.tag as AppletViewHolder).applet
43 val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId)
44 if (appletPath.isEmpty()) {
45 Toast.makeText(
46 YuzuApplication.appContext,
47 R.string.applets_error_applet,
48 Toast.LENGTH_SHORT
49 ).show()
50 return
51 }
52
53 if (applet.appletInfo == AppletInfo.Cabinet) {
54 view.findNavController()
55 .navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment)
56 return
57 }
58
59 NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId)
60 val appletGame = Game(
61 title = YuzuApplication.appContext.getString(applet.titleId),
62 path = appletPath
63 )
64 val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
65 view.findNavController().navigate(action)
66 }
67
68 inner class AppletViewHolder(val binding: CardSimpleOutlinedBinding) : 32 inner class AppletViewHolder(val binding: CardSimpleOutlinedBinding) :
69 RecyclerView.ViewHolder(binding.root) { 33 AbstractViewHolder<Applet>(binding) {
70 lateinit var applet: Applet 34 override fun bind(model: Applet) {
71 35 binding.title.setText(model.titleId)
72 init { 36 binding.description.setText(model.descriptionId)
73 itemView.tag = this
74 }
75
76 fun bind(applet: Applet) {
77 this.applet = applet
78
79 binding.title.setText(applet.titleId)
80 binding.description.setText(applet.descriptionId)
81 binding.icon.setImageDrawable( 37 binding.icon.setImageDrawable(
82 ResourcesCompat.getDrawable( 38 ResourcesCompat.getDrawable(
83 binding.icon.context.resources, 39 binding.icon.context.resources,
84 applet.iconId, 40 model.iconId,
85 binding.icon.context.theme 41 binding.icon.context.theme
86 ) 42 )
87 ) 43 )
44
45 binding.root.setOnClickListener { onClick(model) }
46 }
47
48 fun onClick(applet: Applet) {
49 val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId)
50 if (appletPath.isEmpty()) {
51 Toast.makeText(
52 binding.root.context,
53 R.string.applets_error_applet,
54 Toast.LENGTH_SHORT
55 ).show()
56 return
57 }
58
59 if (applet.appletInfo == AppletInfo.Cabinet) {
60 binding.root.findNavController()
61 .navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment)
62 return
63 }
64
65 NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId)
66 val appletGame = Game(
67 title = YuzuApplication.appContext.getString(applet.titleId),
68 path = appletPath
69 )
70 val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
71 binding.root.findNavController().navigate(action)
88 } 72 }
89 } 73 }
90} 74}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt
index e7b7c0f2f..a56137148 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt
@@ -4,12 +4,10 @@
4package org.yuzu.yuzu_emu.adapters 4package org.yuzu.yuzu_emu.adapters
5 5
6import android.view.LayoutInflater 6import android.view.LayoutInflater
7import android.view.View
8import android.view.ViewGroup 7import android.view.ViewGroup
9import androidx.core.content.res.ResourcesCompat 8import androidx.core.content.res.ResourcesCompat
10import androidx.fragment.app.Fragment 9import androidx.fragment.app.Fragment
11import androidx.navigation.fragment.findNavController 10import androidx.navigation.fragment.findNavController
12import androidx.recyclerview.widget.RecyclerView
13import org.yuzu.yuzu_emu.HomeNavigationDirections 11import org.yuzu.yuzu_emu.HomeNavigationDirections
14import org.yuzu.yuzu_emu.NativeLibrary 12import org.yuzu.yuzu_emu.NativeLibrary
15import org.yuzu.yuzu_emu.R 13import org.yuzu.yuzu_emu.R
@@ -19,54 +17,43 @@ import org.yuzu.yuzu_emu.model.CabinetMode
19import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter.CabinetModeViewHolder 17import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter.CabinetModeViewHolder
20import org.yuzu.yuzu_emu.model.AppletInfo 18import org.yuzu.yuzu_emu.model.AppletInfo
21import org.yuzu.yuzu_emu.model.Game 19import org.yuzu.yuzu_emu.model.Game
20import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
22 21
23class CabinetLauncherDialogAdapter(val fragment: Fragment) : 22class CabinetLauncherDialogAdapter(val fragment: Fragment) :
24 RecyclerView.Adapter<CabinetModeViewHolder>(), 23 AbstractListAdapter<CabinetMode, CabinetModeViewHolder>(
25 View.OnClickListener { 24 CabinetMode.values().copyOfRange(1, CabinetMode.entries.size).toList()
26 private val cabinetModes = CabinetMode.values().copyOfRange(1, CabinetMode.values().size) 25 ) {
27 26
28 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CabinetModeViewHolder { 27 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CabinetModeViewHolder {
29 DialogListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) 28 DialogListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
30 .apply { root.setOnClickListener(this@CabinetLauncherDialogAdapter) }
31 .also { return CabinetModeViewHolder(it) } 29 .also { return CabinetModeViewHolder(it) }
32 } 30 }
33 31
34 override fun getItemCount(): Int = cabinetModes.size
35
36 override fun onBindViewHolder(holder: CabinetModeViewHolder, position: Int) =
37 holder.bind(cabinetModes[position])
38
39 override fun onClick(view: View) {
40 val mode = (view.tag as CabinetModeViewHolder).cabinetMode
41 val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId)
42 NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId)
43 NativeLibrary.setCabinetMode(mode.id)
44 val appletGame = Game(
45 title = YuzuApplication.appContext.getString(R.string.cabinet_applet),
46 path = appletPath
47 )
48 val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
49 fragment.findNavController().navigate(action)
50 }
51
52 inner class CabinetModeViewHolder(val binding: DialogListItemBinding) : 32 inner class CabinetModeViewHolder(val binding: DialogListItemBinding) :
53 RecyclerView.ViewHolder(binding.root) { 33 AbstractViewHolder<CabinetMode>(binding) {
54 lateinit var cabinetMode: CabinetMode 34 override fun bind(model: CabinetMode) {
55
56 init {
57 itemView.tag = this
58 }
59
60 fun bind(cabinetMode: CabinetMode) {
61 this.cabinetMode = cabinetMode
62 binding.icon.setImageDrawable( 35 binding.icon.setImageDrawable(
63 ResourcesCompat.getDrawable( 36 ResourcesCompat.getDrawable(
64 binding.icon.context.resources, 37 binding.icon.context.resources,
65 cabinetMode.iconId, 38 model.iconId,
66 binding.icon.context.theme 39 binding.icon.context.theme
67 ) 40 )
68 ) 41 )
69 binding.title.setText(cabinetMode.titleId) 42 binding.title.setText(model.titleId)
43
44 binding.root.setOnClickListener { onClick(model) }
45 }
46
47 private fun onClick(mode: CabinetMode) {
48 val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId)
49 NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId)
50 NativeLibrary.setCabinetMode(mode.id)
51 val appletGame = Game(
52 title = YuzuApplication.appContext.getString(R.string.cabinet_applet),
53 path = appletPath
54 )
55 val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
56 fragment.findNavController().navigate(action)
70 } 57 }
71 } 58 }
72} 59}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
index d290a656c..d6f17cf29 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
@@ -7,65 +7,39 @@ import android.text.TextUtils
7import android.view.LayoutInflater 7import android.view.LayoutInflater
8import android.view.View 8import android.view.View
9import android.view.ViewGroup 9import android.view.ViewGroup
10import androidx.recyclerview.widget.AsyncDifferConfig
11import androidx.recyclerview.widget.DiffUtil
12import androidx.recyclerview.widget.ListAdapter
13import androidx.recyclerview.widget.RecyclerView
14import org.yuzu.yuzu_emu.R
15import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding 10import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
11import org.yuzu.yuzu_emu.features.settings.model.StringSetting
12import org.yuzu.yuzu_emu.model.Driver
16import org.yuzu.yuzu_emu.model.DriverViewModel 13import org.yuzu.yuzu_emu.model.DriverViewModel
17import org.yuzu.yuzu_emu.utils.GpuDriverHelper 14import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
18import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
19 15
20class DriverAdapter(private val driverViewModel: DriverViewModel) : 16class DriverAdapter(private val driverViewModel: DriverViewModel) :
21 ListAdapter<Pair<String, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>( 17 AbstractSingleSelectionList<Driver, DriverAdapter.DriverViewHolder>(
22 AsyncDifferConfig.Builder(DiffCallback()).build() 18 driverViewModel.driverList.value
23 ) { 19 ) {
24 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder { 20 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder {
25 val binding = 21 CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
26 CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) 22 .also { return DriverViewHolder(it) }
27 return DriverViewHolder(binding)
28 }
29
30 override fun getItemCount(): Int = currentList.size
31
32 override fun onBindViewHolder(holder: DriverViewHolder, position: Int) =
33 holder.bind(currentList[position])
34
35 private fun onSelectDriver(position: Int) {
36 driverViewModel.setSelectedDriverIndex(position)
37 notifyItemChanged(driverViewModel.previouslySelectedDriver)
38 notifyItemChanged(driverViewModel.selectedDriver)
39 }
40
41 private fun onDeleteDriver(driverData: Pair<String, GpuDriverMetadata>, position: Int) {
42 if (driverViewModel.selectedDriver > position) {
43 driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1)
44 }
45 if (GpuDriverHelper.customDriverSettingData == driverData.second) {
46 driverViewModel.setSelectedDriverIndex(0)
47 }
48 driverViewModel.driversToDelete.add(driverData.first)
49 driverViewModel.removeDriver(driverData)
50 notifyItemRemoved(position)
51 notifyItemChanged(driverViewModel.selectedDriver)
52 } 23 }
53 24
54 inner class DriverViewHolder(val binding: CardDriverOptionBinding) : 25 inner class DriverViewHolder(val binding: CardDriverOptionBinding) :
55 RecyclerView.ViewHolder(binding.root) { 26 AbstractViewHolder<Driver>(binding) {
56 private lateinit var driverData: Pair<String, GpuDriverMetadata> 27 override fun bind(model: Driver) {
57
58 fun bind(driverData: Pair<String, GpuDriverMetadata>) {
59 this.driverData = driverData
60 val driver = driverData.second
61
62 binding.apply { 28 binding.apply {
63 radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition 29 radioButton.isChecked = model.selected
64 root.setOnClickListener { 30 root.setOnClickListener {
65 onSelectDriver(bindingAdapterPosition) 31 selectItem(bindingAdapterPosition) {
32 driverViewModel.onDriverSelected(it)
33 driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
34 }
66 } 35 }
67 buttonDelete.setOnClickListener { 36 buttonDelete.setOnClickListener {
68 onDeleteDriver(driverData, bindingAdapterPosition) 37 removeSelectableItem(
38 bindingAdapterPosition
39 ) { removedPosition: Int, selectedPosition: Int ->
40 driverViewModel.onDriverRemoved(removedPosition, selectedPosition)
41 driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
42 }
69 } 43 }
70 44
71 // Delay marquee by 3s 45 // Delay marquee by 3s
@@ -80,38 +54,19 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) :
80 }, 54 },
81 3000 55 3000
82 ) 56 )
83 if (driver.name == null) { 57 title.text = model.title
84 title.setText(R.string.system_gpu_driver) 58 version.text = model.version
85 description.text = "" 59 description.text = model.description
86 version.text = "" 60 if (model.description.isNotEmpty()) {
87 version.visibility = View.GONE
88 description.visibility = View.GONE
89 buttonDelete.visibility = View.GONE
90 } else {
91 title.text = driver.name
92 version.text = driver.version
93 description.text = driver.description
94 version.visibility = View.VISIBLE 61 version.visibility = View.VISIBLE
95 description.visibility = View.VISIBLE 62 description.visibility = View.VISIBLE
96 buttonDelete.visibility = View.VISIBLE 63 buttonDelete.visibility = View.VISIBLE
64 } else {
65 version.visibility = View.GONE
66 description.visibility = View.GONE
67 buttonDelete.visibility = View.GONE
97 } 68 }
98 } 69 }
99 } 70 }
100 } 71 }
101
102 private class DiffCallback : DiffUtil.ItemCallback<Pair<String, GpuDriverMetadata>>() {
103 override fun areItemsTheSame(
104 oldItem: Pair<String, GpuDriverMetadata>,
105 newItem: Pair<String, GpuDriverMetadata>
106 ): Boolean {
107 return oldItem.first == newItem.first
108 }
109
110 override fun areContentsTheSame(
111 oldItem: Pair<String, GpuDriverMetadata>,
112 newItem: Pair<String, GpuDriverMetadata>
113 ): Boolean {
114 return oldItem.second == newItem.second
115 }
116 }
117} 72}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
index ab657a7b9..3d8f0bda8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
@@ -8,19 +8,14 @@ import android.text.TextUtils
8import android.view.LayoutInflater 8import android.view.LayoutInflater
9import android.view.ViewGroup 9import android.view.ViewGroup
10import androidx.fragment.app.FragmentActivity 10import androidx.fragment.app.FragmentActivity
11import androidx.recyclerview.widget.AsyncDifferConfig
12import androidx.recyclerview.widget.DiffUtil
13import androidx.recyclerview.widget.ListAdapter
14import androidx.recyclerview.widget.RecyclerView
15import org.yuzu.yuzu_emu.databinding.CardFolderBinding 11import org.yuzu.yuzu_emu.databinding.CardFolderBinding
16import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment 12import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment
17import org.yuzu.yuzu_emu.model.GameDir 13import org.yuzu.yuzu_emu.model.GameDir
18import org.yuzu.yuzu_emu.model.GamesViewModel 14import org.yuzu.yuzu_emu.model.GamesViewModel
15import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
19 16
20class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) : 17class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) :
21 ListAdapter<GameDir, FolderAdapter.FolderViewHolder>( 18 AbstractDiffAdapter<GameDir, FolderAdapter.FolderViewHolder>() {
22 AsyncDifferConfig.Builder(DiffCallback()).build()
23 ) {
24 override fun onCreateViewHolder( 19 override fun onCreateViewHolder(
25 parent: ViewGroup, 20 parent: ViewGroup,
26 viewType: Int 21 viewType: Int
@@ -29,18 +24,11 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
29 .also { return FolderViewHolder(it) } 24 .also { return FolderViewHolder(it) }
30 } 25 }
31 26
32 override fun onBindViewHolder(holder: FolderAdapter.FolderViewHolder, position: Int) =
33 holder.bind(currentList[position])
34
35 inner class FolderViewHolder(val binding: CardFolderBinding) : 27 inner class FolderViewHolder(val binding: CardFolderBinding) :
36 RecyclerView.ViewHolder(binding.root) { 28 AbstractViewHolder<GameDir>(binding) {
37 private lateinit var gameDir: GameDir 29 override fun bind(model: GameDir) {
38
39 fun bind(gameDir: GameDir) {
40 this.gameDir = gameDir
41
42 binding.apply { 30 binding.apply {
43 path.text = Uri.parse(gameDir.uriString).path 31 path.text = Uri.parse(model.uriString).path
44 path.postDelayed( 32 path.postDelayed(
45 { 33 {
46 path.isSelected = true 34 path.isSelected = true
@@ -50,7 +38,7 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
50 ) 38 )
51 39
52 buttonEdit.setOnClickListener { 40 buttonEdit.setOnClickListener {
53 GameFolderPropertiesDialogFragment.newInstance(this@FolderViewHolder.gameDir) 41 GameFolderPropertiesDialogFragment.newInstance(model)
54 .show( 42 .show(
55 activity.supportFragmentManager, 43 activity.supportFragmentManager,
56 GameFolderPropertiesDialogFragment.TAG 44 GameFolderPropertiesDialogFragment.TAG
@@ -58,19 +46,9 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
58 } 46 }
59 47
60 buttonDelete.setOnClickListener { 48 buttonDelete.setOnClickListener {
61 gamesViewModel.removeFolder(this@FolderViewHolder.gameDir) 49 gamesViewModel.removeFolder(model)
62 } 50 }
63 } 51 }
64 } 52 }
65 } 53 }
66
67 private class DiffCallback : DiffUtil.ItemCallback<GameDir>() {
68 override fun areItemsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
69 return oldItem == newItem
70 }
71
72 override fun areContentsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
73 return oldItem == newItem
74 }
75 }
76} 54}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index a578f0de8..e26c2e0ab 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -9,7 +9,6 @@ import android.graphics.drawable.LayerDrawable
9import android.net.Uri 9import android.net.Uri
10import android.text.TextUtils 10import android.text.TextUtils
11import android.view.LayoutInflater 11import android.view.LayoutInflater
12import android.view.View
13import android.view.ViewGroup 12import android.view.ViewGroup
14import android.widget.ImageView 13import android.widget.ImageView
15import android.widget.Toast 14import android.widget.Toast
@@ -25,10 +24,6 @@ import androidx.lifecycle.ViewModelProvider
25import androidx.lifecycle.lifecycleScope 24import androidx.lifecycle.lifecycleScope
26import androidx.navigation.findNavController 25import androidx.navigation.findNavController
27import androidx.preference.PreferenceManager 26import androidx.preference.PreferenceManager
28import androidx.recyclerview.widget.AsyncDifferConfig
29import androidx.recyclerview.widget.DiffUtil
30import androidx.recyclerview.widget.ListAdapter
31import androidx.recyclerview.widget.RecyclerView
32import kotlinx.coroutines.Dispatchers 27import kotlinx.coroutines.Dispatchers
33import kotlinx.coroutines.launch 28import kotlinx.coroutines.launch
34import kotlinx.coroutines.withContext 29import kotlinx.coroutines.withContext
@@ -36,122 +31,26 @@ import org.yuzu.yuzu_emu.HomeNavigationDirections
36import org.yuzu.yuzu_emu.R 31import org.yuzu.yuzu_emu.R
37import org.yuzu.yuzu_emu.YuzuApplication 32import org.yuzu.yuzu_emu.YuzuApplication
38import org.yuzu.yuzu_emu.activities.EmulationActivity 33import org.yuzu.yuzu_emu.activities.EmulationActivity
39import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
40import org.yuzu.yuzu_emu.databinding.CardGameBinding 34import org.yuzu.yuzu_emu.databinding.CardGameBinding
41import org.yuzu.yuzu_emu.model.Game 35import org.yuzu.yuzu_emu.model.Game
42import org.yuzu.yuzu_emu.model.GamesViewModel 36import org.yuzu.yuzu_emu.model.GamesViewModel
43import org.yuzu.yuzu_emu.utils.GameIconUtils 37import org.yuzu.yuzu_emu.utils.GameIconUtils
38import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
44 39
45class GameAdapter(private val activity: AppCompatActivity) : 40class GameAdapter(private val activity: AppCompatActivity) :
46 ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()), 41 AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>() {
47 View.OnClickListener,
48 View.OnLongClickListener {
49 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder { 42 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
50 // Create a new view. 43 CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
51 val binding = CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false) 44 .also { return GameViewHolder(it) }
52 binding.cardGame.setOnClickListener(this)
53 binding.cardGame.setOnLongClickListener(this)
54
55 // Use that view to create a ViewHolder.
56 return GameViewHolder(binding)
57 }
58
59 override fun onBindViewHolder(holder: GameViewHolder, position: Int) =
60 holder.bind(currentList[position])
61
62 override fun getItemCount(): Int = currentList.size
63
64 /**
65 * Launches the game that was clicked on.
66 *
67 * @param view The card representing the game the user wants to play.
68 */
69 override fun onClick(view: View) {
70 val holder = view.tag as GameViewHolder
71
72 val gameExists = DocumentFile.fromSingleUri(
73 YuzuApplication.appContext,
74 Uri.parse(holder.game.path)
75 )?.exists() == true
76 if (!gameExists) {
77 Toast.makeText(
78 YuzuApplication.appContext,
79 R.string.loader_error_file_not_found,
80 Toast.LENGTH_LONG
81 ).show()
82
83 ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
84 return
85 }
86
87 val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
88 preferences.edit()
89 .putLong(
90 holder.game.keyLastPlayedTime,
91 System.currentTimeMillis()
92 )
93 .apply()
94
95 val openIntent = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
96 action = Intent.ACTION_VIEW
97 data = Uri.parse(holder.game.path)
98 }
99
100 activity.lifecycleScope.launch {
101 withContext(Dispatchers.IO) {
102 val layerDrawable = ResourcesCompat.getDrawable(
103 YuzuApplication.appContext.resources,
104 R.drawable.shortcut,
105 null
106 ) as LayerDrawable
107 layerDrawable.setDrawableByLayerId(
108 R.id.shortcut_foreground,
109 GameIconUtils.getGameIcon(activity, holder.game)
110 .toDrawable(YuzuApplication.appContext.resources)
111 )
112 val inset = YuzuApplication.appContext.resources
113 .getDimensionPixelSize(R.dimen.icon_inset)
114 layerDrawable.setLayerInset(1, inset, inset, inset, inset)
115 val shortcut =
116 ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
117 .setShortLabel(holder.game.title)
118 .setIcon(
119 IconCompat.createWithAdaptiveBitmap(
120 layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
121 )
122 )
123 .setIntent(openIntent)
124 .build()
125 ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
126 }
127 }
128
129 val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game, true)
130 view.findNavController().navigate(action)
131 }
132
133 override fun onLongClick(view: View): Boolean {
134 val holder = view.tag as GameViewHolder
135 val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(holder.game)
136 view.findNavController().navigate(action)
137 return true
138 } 45 }
139 46
140 inner class GameViewHolder(val binding: CardGameBinding) : 47 inner class GameViewHolder(val binding: CardGameBinding) :
141 RecyclerView.ViewHolder(binding.root) { 48 AbstractViewHolder<Game>(binding) {
142 lateinit var game: Game 49 override fun bind(model: Game) {
143
144 init {
145 binding.cardGame.tag = this
146 }
147
148 fun bind(game: Game) {
149 this.game = game
150
151 binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP 50 binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
152 GameIconUtils.loadGameIcon(game, binding.imageGameScreen) 51 GameIconUtils.loadGameIcon(model, binding.imageGameScreen)
153 52
154 binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ") 53 binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ")
155 54
156 binding.textGameTitle.postDelayed( 55 binding.textGameTitle.postDelayed(
157 { 56 {
@@ -160,16 +59,79 @@ class GameAdapter(private val activity: AppCompatActivity) :
160 }, 59 },
161 3000 60 3000
162 ) 61 )
62
63 binding.cardGame.setOnClickListener { onClick(model) }
64 binding.cardGame.setOnLongClickListener { onLongClick(model) }
163 } 65 }
164 }
165 66
166 private class DiffCallback : DiffUtil.ItemCallback<Game>() { 67 fun onClick(game: Game) {
167 override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean { 68 val gameExists = DocumentFile.fromSingleUri(
168 return oldItem == newItem 69 YuzuApplication.appContext,
70 Uri.parse(game.path)
71 )?.exists() == true
72 if (!gameExists) {
73 Toast.makeText(
74 YuzuApplication.appContext,
75 R.string.loader_error_file_not_found,
76 Toast.LENGTH_LONG
77 ).show()
78
79 ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
80 return
81 }
82
83 val preferences =
84 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
85 preferences.edit()
86 .putLong(
87 game.keyLastPlayedTime,
88 System.currentTimeMillis()
89 )
90 .apply()
91
92 val openIntent =
93 Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
94 action = Intent.ACTION_VIEW
95 data = Uri.parse(game.path)
96 }
97
98 activity.lifecycleScope.launch {
99 withContext(Dispatchers.IO) {
100 val layerDrawable = ResourcesCompat.getDrawable(
101 YuzuApplication.appContext.resources,
102 R.drawable.shortcut,
103 null
104 ) as LayerDrawable
105 layerDrawable.setDrawableByLayerId(
106 R.id.shortcut_foreground,
107 GameIconUtils.getGameIcon(activity, game)
108 .toDrawable(YuzuApplication.appContext.resources)
109 )
110 val inset = YuzuApplication.appContext.resources
111 .getDimensionPixelSize(R.dimen.icon_inset)
112 layerDrawable.setLayerInset(1, inset, inset, inset, inset)
113 val shortcut =
114 ShortcutInfoCompat.Builder(YuzuApplication.appContext, game.path)
115 .setShortLabel(game.title)
116 .setIcon(
117 IconCompat.createWithAdaptiveBitmap(
118 layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
119 )
120 )
121 .setIntent(openIntent)
122 .build()
123 ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
124 }
125 }
126
127 val action = HomeNavigationDirections.actionGlobalEmulationActivity(game, true)
128 binding.root.findNavController().navigate(action)
169 } 129 }
170 130
171 override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean { 131 fun onLongClick(game: Game): Boolean {
172 return oldItem == newItem 132 val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(game)
133 binding.root.findNavController().navigate(action)
134 return true
173 } 135 }
174 } 136 }
175} 137}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
index 95841d786..0046d5314 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
@@ -12,23 +12,22 @@ import androidx.lifecycle.Lifecycle
12import androidx.lifecycle.LifecycleOwner 12import androidx.lifecycle.LifecycleOwner
13import androidx.lifecycle.lifecycleScope 13import androidx.lifecycle.lifecycleScope
14import androidx.lifecycle.repeatOnLifecycle 14import androidx.lifecycle.repeatOnLifecycle
15import androidx.recyclerview.widget.RecyclerView
16import kotlinx.coroutines.launch 15import kotlinx.coroutines.launch
17import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding 16import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding
18import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding 17import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
19import org.yuzu.yuzu_emu.model.GameProperty 18import org.yuzu.yuzu_emu.model.GameProperty
20import org.yuzu.yuzu_emu.model.InstallableProperty 19import org.yuzu.yuzu_emu.model.InstallableProperty
21import org.yuzu.yuzu_emu.model.SubmenuProperty 20import org.yuzu.yuzu_emu.model.SubmenuProperty
21import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
22 22
23class GamePropertiesAdapter( 23class GamePropertiesAdapter(
24 private val viewLifecycle: LifecycleOwner, 24 private val viewLifecycle: LifecycleOwner,
25 private var properties: List<GameProperty> 25 private var properties: List<GameProperty>
26) : 26) : AbstractListAdapter<GameProperty, AbstractViewHolder<GameProperty>>(properties) {
27 RecyclerView.Adapter<GamePropertiesAdapter.GamePropertyViewHolder>() {
28 override fun onCreateViewHolder( 27 override fun onCreateViewHolder(
29 parent: ViewGroup, 28 parent: ViewGroup,
30 viewType: Int 29 viewType: Int
31 ): GamePropertyViewHolder { 30 ): AbstractViewHolder<GameProperty> {
32 val inflater = LayoutInflater.from(parent.context) 31 val inflater = LayoutInflater.from(parent.context)
33 return when (viewType) { 32 return when (viewType) {
34 PropertyType.Submenu.ordinal -> { 33 PropertyType.Submenu.ordinal -> {
@@ -51,11 +50,6 @@ class GamePropertiesAdapter(
51 } 50 }
52 } 51 }
53 52
54 override fun getItemCount(): Int = properties.size
55
56 override fun onBindViewHolder(holder: GamePropertyViewHolder, position: Int) =
57 holder.bind(properties[position])
58
59 override fun getItemViewType(position: Int): Int { 53 override fun getItemViewType(position: Int): Int {
60 return when (properties[position]) { 54 return when (properties[position]) {
61 is SubmenuProperty -> PropertyType.Submenu.ordinal 55 is SubmenuProperty -> PropertyType.Submenu.ordinal
@@ -63,14 +57,10 @@ class GamePropertiesAdapter(
63 } 57 }
64 } 58 }
65 59
66 sealed class GamePropertyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
67 abstract fun bind(property: GameProperty)
68 }
69
70 inner class SubmenuPropertyViewHolder(val binding: CardSimpleOutlinedBinding) : 60 inner class SubmenuPropertyViewHolder(val binding: CardSimpleOutlinedBinding) :
71 GamePropertyViewHolder(binding.root) { 61 AbstractViewHolder<GameProperty>(binding) {
72 override fun bind(property: GameProperty) { 62 override fun bind(model: GameProperty) {
73 val submenuProperty = property as SubmenuProperty 63 val submenuProperty = model as SubmenuProperty
74 64
75 binding.root.setOnClickListener { 65 binding.root.setOnClickListener {
76 submenuProperty.action.invoke() 66 submenuProperty.action.invoke()
@@ -108,9 +98,9 @@ class GamePropertiesAdapter(
108 } 98 }
109 99
110 inner class InstallablePropertyViewHolder(val binding: CardInstallableIconBinding) : 100 inner class InstallablePropertyViewHolder(val binding: CardInstallableIconBinding) :
111 GamePropertyViewHolder(binding.root) { 101 AbstractViewHolder<GameProperty>(binding) {
112 override fun bind(property: GameProperty) { 102 override fun bind(model: GameProperty) {
113 val installableProperty = property as InstallableProperty 103 val installableProperty = model as InstallableProperty
114 104
115 binding.title.setText(installableProperty.titleId) 105 binding.title.setText(installableProperty.titleId)
116 binding.description.setText(installableProperty.descriptionId) 106 binding.description.setText(installableProperty.descriptionId)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
index 58ce343f4..b512845d5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
@@ -14,69 +14,37 @@ import androidx.lifecycle.Lifecycle
14import androidx.lifecycle.LifecycleOwner 14import androidx.lifecycle.LifecycleOwner
15import androidx.lifecycle.lifecycleScope 15import androidx.lifecycle.lifecycleScope
16import androidx.lifecycle.repeatOnLifecycle 16import androidx.lifecycle.repeatOnLifecycle
17import androidx.recyclerview.widget.RecyclerView
18import kotlinx.coroutines.launch 17import kotlinx.coroutines.launch
19import org.yuzu.yuzu_emu.R 18import org.yuzu.yuzu_emu.R
20import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding 19import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
21import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 20import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
22import org.yuzu.yuzu_emu.model.HomeSetting 21import org.yuzu.yuzu_emu.model.HomeSetting
22import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
23 23
24class HomeSettingAdapter( 24class HomeSettingAdapter(
25 private val activity: AppCompatActivity, 25 private val activity: AppCompatActivity,
26 private val viewLifecycle: LifecycleOwner, 26 private val viewLifecycle: LifecycleOwner,
27 var options: List<HomeSetting> 27 options: List<HomeSetting>
28) : 28) : AbstractListAdapter<HomeSetting, HomeSettingAdapter.HomeOptionViewHolder>(options) {
29 RecyclerView.Adapter<HomeSettingAdapter.HomeOptionViewHolder>(),
30 View.OnClickListener {
31 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOptionViewHolder { 29 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOptionViewHolder {
32 val binding = 30 CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
33 CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) 31 .also { return HomeOptionViewHolder(it) }
34 binding.root.setOnClickListener(this)
35 return HomeOptionViewHolder(binding)
36 }
37
38 override fun getItemCount(): Int {
39 return options.size
40 }
41
42 override fun onBindViewHolder(holder: HomeOptionViewHolder, position: Int) {
43 holder.bind(options[position])
44 }
45
46 override fun onClick(view: View) {
47 val holder = view.tag as HomeOptionViewHolder
48 if (holder.option.isEnabled.invoke()) {
49 holder.option.onClick.invoke()
50 } else {
51 MessageDialogFragment.newInstance(
52 activity,
53 titleId = holder.option.disabledTitleId,
54 descriptionId = holder.option.disabledMessageId
55 ).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
56 }
57 } 32 }
58 33
59 inner class HomeOptionViewHolder(val binding: CardHomeOptionBinding) : 34 inner class HomeOptionViewHolder(val binding: CardHomeOptionBinding) :
60 RecyclerView.ViewHolder(binding.root) { 35 AbstractViewHolder<HomeSetting>(binding) {
61 lateinit var option: HomeSetting 36 override fun bind(model: HomeSetting) {
62 37 binding.optionTitle.text = activity.resources.getString(model.titleId)
63 init { 38 binding.optionDescription.text = activity.resources.getString(model.descriptionId)
64 itemView.tag = this
65 }
66
67 fun bind(option: HomeSetting) {
68 this.option = option
69 binding.optionTitle.text = activity.resources.getString(option.titleId)
70 binding.optionDescription.text = activity.resources.getString(option.descriptionId)
71 binding.optionIcon.setImageDrawable( 39 binding.optionIcon.setImageDrawable(
72 ResourcesCompat.getDrawable( 40 ResourcesCompat.getDrawable(
73 activity.resources, 41 activity.resources,
74 option.iconId, 42 model.iconId,
75 activity.theme 43 activity.theme
76 ) 44 )
77 ) 45 )
78 46
79 when (option.titleId) { 47 when (model.titleId) {
80 R.string.get_early_access -> 48 R.string.get_early_access ->
81 binding.optionLayout.background = 49 binding.optionLayout.background =
82 ContextCompat.getDrawable( 50 ContextCompat.getDrawable(
@@ -85,7 +53,7 @@ class HomeSettingAdapter(
85 ) 53 )
86 } 54 }
87 55
88 if (!option.isEnabled.invoke()) { 56 if (!model.isEnabled.invoke()) {
89 binding.optionTitle.alpha = 0.5f 57 binding.optionTitle.alpha = 0.5f
90 binding.optionDescription.alpha = 0.5f 58 binding.optionDescription.alpha = 0.5f
91 binding.optionIcon.alpha = 0.5f 59 binding.optionIcon.alpha = 0.5f
@@ -93,7 +61,7 @@ class HomeSettingAdapter(
93 61
94 viewLifecycle.lifecycleScope.launch { 62 viewLifecycle.lifecycleScope.launch {
95 viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) { 63 viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
96 option.details.collect { updateOptionDetails(it) } 64 model.details.collect { updateOptionDetails(it) }
97 } 65 }
98 } 66 }
99 binding.optionDetail.postDelayed( 67 binding.optionDetail.postDelayed(
@@ -103,6 +71,20 @@ class HomeSettingAdapter(
103 }, 71 },
104 3000 72 3000
105 ) 73 )
74
75 binding.root.setOnClickListener { onClick(model) }
76 }
77
78 private fun onClick(model: HomeSetting) {
79 if (model.isEnabled.invoke()) {
80 model.onClick.invoke()
81 } else {
82 MessageDialogFragment.newInstance(
83 activity,
84 titleId = model.disabledTitleId,
85 descriptionId = model.disabledMessageId
86 ).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
87 }
106 } 88 }
107 89
108 private fun updateOptionDetails(detailString: String) { 90 private fun updateOptionDetails(detailString: String) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt
index e960fbaab..4218c4e52 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt
@@ -6,43 +6,33 @@ package org.yuzu.yuzu_emu.adapters
6import android.view.LayoutInflater 6import android.view.LayoutInflater
7import android.view.View 7import android.view.View
8import android.view.ViewGroup 8import android.view.ViewGroup
9import androidx.recyclerview.widget.RecyclerView
10import org.yuzu.yuzu_emu.databinding.CardInstallableBinding 9import org.yuzu.yuzu_emu.databinding.CardInstallableBinding
11import org.yuzu.yuzu_emu.model.Installable 10import org.yuzu.yuzu_emu.model.Installable
11import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
12 12
13class InstallableAdapter(private val installables: List<Installable>) : 13class InstallableAdapter(installables: List<Installable>) :
14 RecyclerView.Adapter<InstallableAdapter.InstallableViewHolder>() { 14 AbstractListAdapter<Installable, InstallableAdapter.InstallableViewHolder>(installables) {
15 override fun onCreateViewHolder( 15 override fun onCreateViewHolder(
16 parent: ViewGroup, 16 parent: ViewGroup,
17 viewType: Int 17 viewType: Int
18 ): InstallableAdapter.InstallableViewHolder { 18 ): InstallableAdapter.InstallableViewHolder {
19 val binding = 19 CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false)
20 CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false) 20 .also { return InstallableViewHolder(it) }
21 return InstallableViewHolder(binding)
22 } 21 }
23 22
24 override fun getItemCount(): Int = installables.size
25
26 override fun onBindViewHolder(holder: InstallableAdapter.InstallableViewHolder, position: Int) =
27 holder.bind(installables[position])
28
29 inner class InstallableViewHolder(val binding: CardInstallableBinding) : 23 inner class InstallableViewHolder(val binding: CardInstallableBinding) :
30 RecyclerView.ViewHolder(binding.root) { 24 AbstractViewHolder<Installable>(binding) {
31 lateinit var installable: Installable 25 override fun bind(model: Installable) {
32 26 binding.title.setText(model.titleId)
33 fun bind(installable: Installable) { 27 binding.description.setText(model.descriptionId)
34 this.installable = installable
35
36 binding.title.setText(installable.titleId)
37 binding.description.setText(installable.descriptionId)
38 28
39 if (installable.install != null) { 29 if (model.install != null) {
40 binding.buttonInstall.visibility = View.VISIBLE 30 binding.buttonInstall.visibility = View.VISIBLE
41 binding.buttonInstall.setOnClickListener { installable.install.invoke() } 31 binding.buttonInstall.setOnClickListener { model.install.invoke() }
42 } 32 }
43 if (installable.export != null) { 33 if (model.export != null) {
44 binding.buttonExport.visibility = View.VISIBLE 34 binding.buttonExport.visibility = View.VISIBLE
45 binding.buttonExport.setOnClickListener { installable.export.invoke() } 35 binding.buttonExport.setOnClickListener { model.export.invoke() }
46 } 36 }
47 } 37 }
48 } 38 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
index bc6ff1364..38bb1f96f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
@@ -7,49 +7,33 @@ import android.view.LayoutInflater
7import android.view.View 7import android.view.View
8import android.view.ViewGroup 8import android.view.ViewGroup
9import androidx.appcompat.app.AppCompatActivity 9import androidx.appcompat.app.AppCompatActivity
10import androidx.recyclerview.widget.RecyclerView
11import androidx.recyclerview.widget.RecyclerView.ViewHolder
12import org.yuzu.yuzu_emu.YuzuApplication
13import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 10import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
14import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment 11import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment
15import org.yuzu.yuzu_emu.model.License 12import org.yuzu.yuzu_emu.model.License
13import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
16 14
17class LicenseAdapter(private val activity: AppCompatActivity, var licenses: List<License>) : 15class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<License>) :
18 RecyclerView.Adapter<LicenseAdapter.LicenseViewHolder>(), 16 AbstractListAdapter<License, LicenseAdapter.LicenseViewHolder>(licenses) {
19 View.OnClickListener {
20 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LicenseViewHolder { 17 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LicenseViewHolder {
21 val binding = 18 ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false)
22 ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false) 19 .also { return LicenseViewHolder(it) }
23 binding.root.setOnClickListener(this)
24 return LicenseViewHolder(binding)
25 } 20 }
26 21
27 override fun getItemCount(): Int = licenses.size 22 inner class LicenseViewHolder(val binding: ListItemSettingBinding) :
23 AbstractViewHolder<License>(binding) {
24 override fun bind(model: License) {
25 binding.apply {
26 textSettingName.text = root.context.getString(model.titleId)
27 textSettingDescription.text = root.context.getString(model.descriptionId)
28 textSettingValue.visibility = View.GONE
28 29
29 override fun onBindViewHolder(holder: LicenseViewHolder, position: Int) { 30 root.setOnClickListener { onClick(model) }
30 holder.bind(licenses[position]) 31 }
31 }
32
33 override fun onClick(view: View) {
34 val license = (view.tag as LicenseViewHolder).license
35 LicenseBottomSheetDialogFragment.newInstance(license)
36 .show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG)
37 }
38
39 inner class LicenseViewHolder(val binding: ListItemSettingBinding) : ViewHolder(binding.root) {
40 lateinit var license: License
41
42 init {
43 itemView.tag = this
44 } 32 }
45 33
46 fun bind(license: License) { 34 private fun onClick(license: License) {
47 this.license = license 35 LicenseBottomSheetDialogFragment.newInstance(license)
48 36 .show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG)
49 val context = YuzuApplication.appContext
50 binding.textSettingName.text = context.getString(license.titleId)
51 binding.textSettingDescription.text = context.getString(license.descriptionId)
52 binding.textSettingValue.visibility = View.GONE
53 } 37 }
54 } 38 }
55} 39}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
index 6b46d359e..02118e1a8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
@@ -10,7 +10,6 @@ import android.view.ViewGroup
10import androidx.appcompat.app.AppCompatActivity 10import androidx.appcompat.app.AppCompatActivity
11import androidx.core.content.res.ResourcesCompat 11import androidx.core.content.res.ResourcesCompat
12import androidx.lifecycle.ViewModelProvider 12import androidx.lifecycle.ViewModelProvider
13import androidx.recyclerview.widget.RecyclerView
14import com.google.android.material.button.MaterialButton 13import com.google.android.material.button.MaterialButton
15import org.yuzu.yuzu_emu.databinding.PageSetupBinding 14import org.yuzu.yuzu_emu.databinding.PageSetupBinding
16import org.yuzu.yuzu_emu.model.HomeViewModel 15import org.yuzu.yuzu_emu.model.HomeViewModel
@@ -18,31 +17,19 @@ import org.yuzu.yuzu_emu.model.SetupCallback
18import org.yuzu.yuzu_emu.model.SetupPage 17import org.yuzu.yuzu_emu.model.SetupPage
19import org.yuzu.yuzu_emu.model.StepState 18import org.yuzu.yuzu_emu.model.StepState
20import org.yuzu.yuzu_emu.utils.ViewUtils 19import org.yuzu.yuzu_emu.utils.ViewUtils
20import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
21 21
22class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) : 22class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
23 RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() { 23 AbstractListAdapter<SetupPage, SetupAdapter.SetupPageViewHolder>(pages) {
24 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SetupPageViewHolder { 24 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SetupPageViewHolder {
25 val binding = PageSetupBinding.inflate(LayoutInflater.from(parent.context), parent, false) 25 PageSetupBinding.inflate(LayoutInflater.from(parent.context), parent, false)
26 return SetupPageViewHolder(binding) 26 .also { return SetupPageViewHolder(it) }
27 } 27 }
28 28
29 override fun getItemCount(): Int = pages.size
30
31 override fun onBindViewHolder(holder: SetupPageViewHolder, position: Int) =
32 holder.bind(pages[position])
33
34 inner class SetupPageViewHolder(val binding: PageSetupBinding) : 29 inner class SetupPageViewHolder(val binding: PageSetupBinding) :
35 RecyclerView.ViewHolder(binding.root), SetupCallback { 30 AbstractViewHolder<SetupPage>(binding), SetupCallback {
36 lateinit var page: SetupPage 31 override fun bind(model: SetupPage) {
37 32 if (model.stepCompleted.invoke() == StepState.COMPLETE) {
38 init {
39 itemView.tag = this
40 }
41
42 fun bind(page: SetupPage) {
43 this.page = page
44
45 if (page.stepCompleted.invoke() == StepState.COMPLETE) {
46 binding.buttonAction.visibility = View.INVISIBLE 33 binding.buttonAction.visibility = View.INVISIBLE
47 binding.textConfirmation.visibility = View.VISIBLE 34 binding.textConfirmation.visibility = View.VISIBLE
48 } 35 }
@@ -50,31 +37,31 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
50 binding.icon.setImageDrawable( 37 binding.icon.setImageDrawable(
51 ResourcesCompat.getDrawable( 38 ResourcesCompat.getDrawable(
52 activity.resources, 39 activity.resources,
53 page.iconId, 40 model.iconId,
54 activity.theme 41 activity.theme
55 ) 42 )
56 ) 43 )
57 binding.textTitle.text = activity.resources.getString(page.titleId) 44 binding.textTitle.text = activity.resources.getString(model.titleId)
58 binding.textDescription.text = 45 binding.textDescription.text =
59 Html.fromHtml(activity.resources.getString(page.descriptionId), 0) 46 Html.fromHtml(activity.resources.getString(model.descriptionId), 0)
60 47
61 binding.buttonAction.apply { 48 binding.buttonAction.apply {
62 text = activity.resources.getString(page.buttonTextId) 49 text = activity.resources.getString(model.buttonTextId)
63 if (page.buttonIconId != 0) { 50 if (model.buttonIconId != 0) {
64 icon = ResourcesCompat.getDrawable( 51 icon = ResourcesCompat.getDrawable(
65 activity.resources, 52 activity.resources,
66 page.buttonIconId, 53 model.buttonIconId,
67 activity.theme 54 activity.theme
68 ) 55 )
69 } 56 }
70 iconGravity = 57 iconGravity =
71 if (page.leftAlignedIcon) { 58 if (model.leftAlignedIcon) {
72 MaterialButton.ICON_GRAVITY_START 59 MaterialButton.ICON_GRAVITY_START
73 } else { 60 } else {
74 MaterialButton.ICON_GRAVITY_END 61 MaterialButton.ICON_GRAVITY_END
75 } 62 }
76 setOnClickListener { 63 setOnClickListener {
77 page.buttonAction.invoke(this@SetupPageViewHolder) 64 model.buttonAction.invoke(this@SetupPageViewHolder)
78 } 65 }
79 } 66 }
80 } 67 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
index a1620fbb7..5b5f800c1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
@@ -76,8 +76,8 @@ class AboutFragment : Fragment() {
76 binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment) 76 binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
77 } 77 }
78 78
79 binding.textBuildHash.text = BuildConfig.GIT_HASH 79 binding.textVersionName.text = BuildConfig.VERSION_NAME
80 binding.buttonBuildHash.setOnClickListener { 80 binding.textVersionName.setOnClickListener {
81 val clipBoard = 81 val clipBoard =
82 requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager 82 requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
83 val clip = ClipData.newPlainText(getString(R.string.build), BuildConfig.GIT_HASH) 83 val clip = ClipData.newPlainText(getString(R.string.build), BuildConfig.GIT_HASH)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
index cc71254dc..9dabb9c41 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
@@ -3,6 +3,7 @@
3 3
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.annotation.SuppressLint
6import android.os.Bundle 7import android.os.Bundle
7import android.view.LayoutInflater 8import android.view.LayoutInflater
8import android.view.View 9import android.view.View
@@ -13,20 +14,26 @@ import androidx.core.view.WindowInsetsCompat
13import androidx.core.view.updatePadding 14import androidx.core.view.updatePadding
14import androidx.fragment.app.Fragment 15import androidx.fragment.app.Fragment
15import androidx.fragment.app.activityViewModels 16import androidx.fragment.app.activityViewModels
17import androidx.lifecycle.Lifecycle
16import androidx.lifecycle.lifecycleScope 18import androidx.lifecycle.lifecycleScope
19import androidx.lifecycle.repeatOnLifecycle
17import androidx.navigation.findNavController 20import androidx.navigation.findNavController
18import androidx.navigation.fragment.navArgs 21import androidx.navigation.fragment.navArgs
19import androidx.recyclerview.widget.GridLayoutManager 22import androidx.recyclerview.widget.GridLayoutManager
20import com.google.android.material.transition.MaterialSharedAxis 23import com.google.android.material.transition.MaterialSharedAxis
21import kotlinx.coroutines.flow.collectLatest 24import kotlinx.coroutines.Dispatchers
22import kotlinx.coroutines.launch 25import kotlinx.coroutines.launch
26import kotlinx.coroutines.withContext
23import org.yuzu.yuzu_emu.R 27import org.yuzu.yuzu_emu.R
24import org.yuzu.yuzu_emu.adapters.DriverAdapter 28import org.yuzu.yuzu_emu.adapters.DriverAdapter
25import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding 29import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
30import org.yuzu.yuzu_emu.features.settings.model.StringSetting
31import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
26import org.yuzu.yuzu_emu.model.DriverViewModel 32import org.yuzu.yuzu_emu.model.DriverViewModel
27import org.yuzu.yuzu_emu.model.HomeViewModel 33import org.yuzu.yuzu_emu.model.HomeViewModel
28import org.yuzu.yuzu_emu.utils.FileUtil 34import org.yuzu.yuzu_emu.utils.FileUtil
29import org.yuzu.yuzu_emu.utils.GpuDriverHelper 35import org.yuzu.yuzu_emu.utils.GpuDriverHelper
36import org.yuzu.yuzu_emu.utils.NativeConfig
30import java.io.File 37import java.io.File
31import java.io.IOException 38import java.io.IOException
32 39
@@ -55,12 +62,43 @@ class DriverManagerFragment : Fragment() {
55 return binding.root 62 return binding.root
56 } 63 }
57 64
65 // This is using the correct scope, lint is just acting up
66 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
58 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 67 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
59 super.onViewCreated(view, savedInstanceState) 68 super.onViewCreated(view, savedInstanceState)
60 homeViewModel.setNavigationVisibility(visible = false, animated = true) 69 homeViewModel.setNavigationVisibility(visible = false, animated = true)
61 homeViewModel.setStatusBarShadeVisibility(visible = false) 70 homeViewModel.setStatusBarShadeVisibility(visible = false)
62 71
63 driverViewModel.onOpenDriverManager(args.game) 72 driverViewModel.onOpenDriverManager(args.game)
73 if (NativeConfig.isPerGameConfigLoaded()) {
74 binding.toolbarDrivers.inflateMenu(R.menu.menu_driver_manager)
75 driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
76 binding.toolbarDrivers.setOnMenuItemClickListener {
77 when (it.itemId) {
78 R.id.menu_driver_clear -> {
79 StringSetting.DRIVER_PATH.global = true
80 driverViewModel.updateDriverList()
81 (binding.listDrivers.adapter as DriverAdapter)
82 .replaceList(driverViewModel.driverList.value)
83 driverViewModel.showClearButton(false)
84 true
85 }
86
87 else -> false
88 }
89 }
90
91 viewLifecycleOwner.lifecycleScope.apply {
92 launch {
93 repeatOnLifecycle(Lifecycle.State.STARTED) {
94 driverViewModel.showClearButton.collect {
95 binding.toolbarDrivers.menu
96 .findItem(R.id.menu_driver_clear).isVisible = it
97 }
98 }
99 }
100 }
101 }
64 102
65 if (!driverViewModel.isInteractionAllowed.value) { 103 if (!driverViewModel.isInteractionAllowed.value) {
66 DriversLoadingDialogFragment().show( 104 DriversLoadingDialogFragment().show(
@@ -85,25 +123,6 @@ class DriverManagerFragment : Fragment() {
85 adapter = DriverAdapter(driverViewModel) 123 adapter = DriverAdapter(driverViewModel)
86 } 124 }
87 125
88 viewLifecycleOwner.lifecycleScope.apply {
89 launch {
90 driverViewModel.driverList.collectLatest {
91 (binding.listDrivers.adapter as DriverAdapter).submitList(it)
92 }
93 }
94 launch {
95 driverViewModel.newDriverInstalled.collect {
96 if (_binding != null && it) {
97 (binding.listDrivers.adapter as DriverAdapter).apply {
98 notifyItemChanged(driverViewModel.previouslySelectedDriver)
99 notifyItemChanged(driverViewModel.selectedDriver)
100 driverViewModel.setNewDriverInstalled(false)
101 }
102 }
103 }
104 }
105 }
106
107 setInsets() 126 setInsets()
108 } 127 }
109 128
@@ -160,7 +179,7 @@ class DriverManagerFragment : Fragment() {
160 false 179 false
161 ) { 180 ) {
162 val driverPath = 181 val driverPath =
163 "${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}" 182 "${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(result)}"
164 val driverFile = File(driverPath) 183 val driverFile = File(driverPath)
165 184
166 // Ignore file exceptions when a user selects an invalid zip 185 // Ignore file exceptions when a user selects an invalid zip
@@ -177,12 +196,21 @@ class DriverManagerFragment : Fragment() {
177 196
178 val driverData = GpuDriverHelper.getMetadataFromZip(driverFile) 197 val driverData = GpuDriverHelper.getMetadataFromZip(driverFile)
179 val driverInList = 198 val driverInList =
180 driverViewModel.driverList.value.firstOrNull { it.second == driverData } 199 driverViewModel.driverData.firstOrNull { it.second == driverData }
181 if (driverInList != null) { 200 if (driverInList != null) {
182 return@newInstance getString(R.string.driver_already_installed) 201 return@newInstance getString(R.string.driver_already_installed)
183 } else { 202 } else {
184 driverViewModel.addDriver(Pair(driverPath, driverData)) 203 driverViewModel.onDriverAdded(Pair(driverPath, driverData))
185 driverViewModel.setNewDriverInstalled(true) 204 withContext(Dispatchers.Main) {
205 if (_binding != null) {
206 val adapter = binding.listDrivers.adapter as DriverAdapter
207 adapter.addItem(driverData.toDriver())
208 adapter.selectItem(adapter.currentList.indices.last)
209 driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
210 binding.listDrivers
211 .smoothScrollToPosition(adapter.currentList.indices.last)
212 }
213 }
186 } 214 }
187 return@newInstance Any() 215 return@newInstance Any()
188 }.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG) 216 }.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Driver.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Driver.kt
new file mode 100644
index 000000000..de342212a
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Driver.kt
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.model
5
6import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
7
8data class Driver(
9 override var selected: Boolean,
10 val title: String,
11 val version: String = "",
12 val description: String = ""
13) : SelectableItem {
14 override fun onSelectionStateChanged(selected: Boolean) {
15 this.selected = selected
16 }
17
18 companion object {
19 fun GpuDriverMetadata.toDriver(selected: Boolean = false): Driver =
20 Driver(
21 selected,
22 this.name ?: "",
23 this.version ?: "",
24 this.description ?: ""
25 )
26 }
27}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt
index 76accf8f3..15ae3a42b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt
@@ -9,6 +9,7 @@ import kotlinx.coroutines.Dispatchers
9import kotlinx.coroutines.flow.MutableStateFlow 9import kotlinx.coroutines.flow.MutableStateFlow
10import kotlinx.coroutines.flow.SharingStarted 10import kotlinx.coroutines.flow.SharingStarted
11import kotlinx.coroutines.flow.StateFlow 11import kotlinx.coroutines.flow.StateFlow
12import kotlinx.coroutines.flow.asStateFlow
12import kotlinx.coroutines.flow.combine 13import kotlinx.coroutines.flow.combine
13import kotlinx.coroutines.flow.stateIn 14import kotlinx.coroutines.flow.stateIn
14import kotlinx.coroutines.launch 15import kotlinx.coroutines.launch
@@ -17,11 +18,10 @@ import org.yuzu.yuzu_emu.R
17import org.yuzu.yuzu_emu.YuzuApplication 18import org.yuzu.yuzu_emu.YuzuApplication
18import org.yuzu.yuzu_emu.features.settings.model.StringSetting 19import org.yuzu.yuzu_emu.features.settings.model.StringSetting
19import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 20import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
20import org.yuzu.yuzu_emu.utils.FileUtil 21import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
21import org.yuzu.yuzu_emu.utils.GpuDriverHelper 22import org.yuzu.yuzu_emu.utils.GpuDriverHelper
22import org.yuzu.yuzu_emu.utils.GpuDriverMetadata 23import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
23import org.yuzu.yuzu_emu.utils.NativeConfig 24import org.yuzu.yuzu_emu.utils.NativeConfig
24import java.io.BufferedOutputStream
25import java.io.File 25import java.io.File
26 26
27class DriverViewModel : ViewModel() { 27class DriverViewModel : ViewModel() {
@@ -38,97 +38,81 @@ class DriverViewModel : ViewModel() {
38 !loading && ready && !deleting 38 !loading && ready && !deleting
39 }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialValue = false) 39 }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialValue = false)
40 40
41 private val _driverList = MutableStateFlow(GpuDriverHelper.getDrivers()) 41 var driverData = GpuDriverHelper.getDrivers()
42 val driverList: StateFlow<MutableList<Pair<String, GpuDriverMetadata>>> get() = _driverList
43 42
44 var previouslySelectedDriver = 0 43 private val _driverList = MutableStateFlow(emptyList<Driver>())
45 var selectedDriver = -1 44 val driverList: StateFlow<List<Driver>> get() = _driverList
46 45
47 // Used for showing which driver is currently installed within the driver manager card 46 // Used for showing which driver is currently installed within the driver manager card
48 private val _selectedDriverTitle = MutableStateFlow("") 47 private val _selectedDriverTitle = MutableStateFlow("")
49 val selectedDriverTitle: StateFlow<String> get() = _selectedDriverTitle 48 val selectedDriverTitle: StateFlow<String> get() = _selectedDriverTitle
50 49
51 private val _newDriverInstalled = MutableStateFlow(false) 50 private val _showClearButton = MutableStateFlow(false)
52 val newDriverInstalled: StateFlow<Boolean> get() = _newDriverInstalled 51 val showClearButton = _showClearButton.asStateFlow()
53 52
54 val driversToDelete = mutableListOf<String>() 53 private val driversToDelete = mutableListOf<String>()
55 54
56 init { 55 init {
57 val currentDriverMetadata = GpuDriverHelper.installedCustomDriverData 56 updateDriverList()
58 findSelectedDriver(currentDriverMetadata) 57 updateDriverNameForGame(null)
59 58 }
60 // If a user had installed a driver before the manager was implemented, this zips
61 // the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can
62 // be indexed and exported as expected.
63 if (selectedDriver == -1) {
64 val driverToSave =
65 File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip")
66 driverToSave.createNewFile()
67 FileUtil.zipFromInternalStorage(
68 File(GpuDriverHelper.driverInstallationPath!!),
69 GpuDriverHelper.driverInstallationPath!!,
70 BufferedOutputStream(driverToSave.outputStream())
71 )
72 _driverList.value.add(Pair(driverToSave.path, currentDriverMetadata))
73 setSelectedDriverIndex(_driverList.value.size - 1)
74 }
75 59
76 // If a user had installed a driver before the config was reworked to be multiplatform, 60 fun reloadDriverData() {
77 // we have save the path of the previously selected driver to the new setting. 61 _areDriversLoading.value = true
78 if (StringSetting.DRIVER_PATH.getString(true).isEmpty() && selectedDriver > 0 && 62 driverData = GpuDriverHelper.getDrivers()
79 StringSetting.DRIVER_PATH.global 63 updateDriverList()
80 ) { 64 _areDriversLoading.value = false
81 StringSetting.DRIVER_PATH.setString(_driverList.value[selectedDriver].first) 65 }
82 NativeConfig.saveGlobalConfig() 66
83 } else { 67 fun updateDriverList() {
84 findSelectedDriver(GpuDriverHelper.customDriverSettingData) 68 val selectedDriver = GpuDriverHelper.customDriverSettingData
69 val newDriverList = mutableListOf(
70 Driver(
71 selectedDriver == GpuDriverMetadata(),
72 YuzuApplication.appContext.getString(R.string.system_gpu_driver)
73 )
74 )
75 driverData.forEach {
76 newDriverList.add(it.second.toDriver(it.second == selectedDriver))
85 } 77 }
86 updateDriverNameForGame(null) 78 _driverList.value = newDriverList
87 } 79 }
88 80
89 fun setSelectedDriverIndex(value: Int) { 81 fun onOpenDriverManager(game: Game?) {
90 if (selectedDriver != -1) { 82 if (game != null) {
91 previouslySelectedDriver = selectedDriver 83 SettingsFile.loadCustomConfig(game)
92 } 84 }
93 selectedDriver = value 85 updateDriverList()
94 } 86 }
95 87
96 fun setNewDriverInstalled(value: Boolean) { 88 fun showClearButton(value: Boolean) {
97 _newDriverInstalled.value = value 89 _showClearButton.value = value
98 } 90 }
99 91
100 fun addDriver(driverData: Pair<String, GpuDriverMetadata>) { 92 fun onDriverSelected(position: Int) {
101 val driverIndex = _driverList.value.indexOfFirst { it == driverData } 93 if (position == 0) {
102 if (driverIndex == -1) { 94 StringSetting.DRIVER_PATH.setString("")
103 _driverList.value.add(driverData)
104 setSelectedDriverIndex(_driverList.value.size - 1)
105 _selectedDriverTitle.value = driverData.second.name
106 ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
107 } else { 95 } else {
108 setSelectedDriverIndex(driverIndex) 96 StringSetting.DRIVER_PATH.setString(driverData[position - 1].first)
109 } 97 }
110 } 98 }
111 99
112 fun removeDriver(driverData: Pair<String, GpuDriverMetadata>) { 100 fun onDriverRemoved(removedPosition: Int, selectedPosition: Int) {
113 _driverList.value.remove(driverData) 101 driversToDelete.add(driverData[removedPosition - 1].first)
102 driverData.removeAt(removedPosition - 1)
103 onDriverSelected(selectedPosition)
114 } 104 }
115 105
116 fun onOpenDriverManager(game: Game?) { 106 fun onDriverAdded(driver: Pair<String, GpuDriverMetadata>) {
117 if (game != null) { 107 if (driversToDelete.contains(driver.first)) {
118 SettingsFile.loadCustomConfig(game) 108 driversToDelete.remove(driver.first)
119 }
120
121 val driverPath = StringSetting.DRIVER_PATH.getString()
122 if (driverPath.isEmpty()) {
123 setSelectedDriverIndex(0)
124 } else {
125 findSelectedDriver(GpuDriverHelper.getMetadataFromZip(File(driverPath)))
126 } 109 }
110 driverData.add(driver)
111 onDriverSelected(driverData.size)
127 } 112 }
128 113
129 fun onCloseDriverManager(game: Game?) { 114 fun onCloseDriverManager(game: Game?) {
130 _isDeletingDrivers.value = true 115 _isDeletingDrivers.value = true
131 StringSetting.DRIVER_PATH.setString(driverList.value[selectedDriver].first)
132 updateDriverNameForGame(game) 116 updateDriverNameForGame(game)
133 if (game == null) { 117 if (game == null) {
134 NativeConfig.saveGlobalConfig() 118 NativeConfig.saveGlobalConfig()
@@ -181,20 +165,6 @@ class DriverViewModel : ViewModel() {
181 } 165 }
182 } 166 }
183 167
184 private fun findSelectedDriver(currentDriverMetadata: GpuDriverMetadata) {
185 if (driverList.value.size == 1) {
186 setSelectedDriverIndex(0)
187 return
188 }
189
190 driverList.value.forEachIndexed { i: Int, driver: Pair<String, GpuDriverMetadata> ->
191 if (driver.second == currentDriverMetadata) {
192 setSelectedDriverIndex(i)
193 return
194 }
195 }
196 }
197
198 fun updateDriverNameForGame(game: Game?) { 168 fun updateDriverNameForGame(game: Game?) {
199 if (!GpuDriverHelper.supportsCustomDriverLoading()) { 169 if (!GpuDriverHelper.supportsCustomDriverLoading()) {
200 return 170 return
@@ -217,7 +187,6 @@ class DriverViewModel : ViewModel() {
217 187
218 private fun setDriverReady() { 188 private fun setDriverReady() {
219 _isDriverReady.value = true 189 _isDriverReady.value = true
220 _selectedDriverTitle.value = GpuDriverHelper.customDriverSettingData.name 190 updateName()
221 ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
222 } 191 }
223} 192}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt
new file mode 100644
index 000000000..11c22d323
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt
@@ -0,0 +1,9 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.model
5
6interface SelectableItem {
7 var selected: Boolean
8 fun onSelectionStateChanged(selected: Boolean)
9}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index 622ae996e..644289e25 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -41,6 +41,7 @@ import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
41import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment 41import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
42import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 42import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
43import org.yuzu.yuzu_emu.model.AddonViewModel 43import org.yuzu.yuzu_emu.model.AddonViewModel
44import org.yuzu.yuzu_emu.model.DriverViewModel
44import org.yuzu.yuzu_emu.model.GamesViewModel 45import org.yuzu.yuzu_emu.model.GamesViewModel
45import org.yuzu.yuzu_emu.model.HomeViewModel 46import org.yuzu.yuzu_emu.model.HomeViewModel
46import org.yuzu.yuzu_emu.model.TaskState 47import org.yuzu.yuzu_emu.model.TaskState
@@ -58,6 +59,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
58 private val gamesViewModel: GamesViewModel by viewModels() 59 private val gamesViewModel: GamesViewModel by viewModels()
59 private val taskViewModel: TaskViewModel by viewModels() 60 private val taskViewModel: TaskViewModel by viewModels()
60 private val addonViewModel: AddonViewModel by viewModels() 61 private val addonViewModel: AddonViewModel by viewModels()
62 private val driverViewModel: DriverViewModel by viewModels()
61 63
62 override var themeId: Int = 0 64 override var themeId: Int = 0
63 65
@@ -689,6 +691,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
689 NativeLibrary.initializeSystem(true) 691 NativeLibrary.initializeSystem(true)
690 NativeConfig.initializeGlobalConfig() 692 NativeConfig.initializeGlobalConfig()
691 gamesViewModel.reloadGames(false) 693 gamesViewModel.reloadGames(false)
694 driverViewModel.reloadDriverData()
692 695
693 return@newInstance getString(R.string.user_data_import_success) 696 return@newInstance getString(R.string.user_data_import_success)
694 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) 697 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
index 132f002fb..b54a19c65 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
@@ -104,7 +104,7 @@ object FileUtil {
104 104
105 /** 105 /**
106 * Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow 106 * Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
107 * This function will be faster than DoucmentFile.listFiles 107 * This function will be faster than DocumentFile.listFiles
108 * @param uri Directory uri. 108 * @param uri Directory uri.
109 * @return CheapDocument lists. 109 * @return CheapDocument lists.
110 */ 110 */
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
index 685272288..a8f9dcc34 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
@@ -62,9 +62,6 @@ object GpuDriverHelper {
62 ?.sortedByDescending { it: Pair<String, GpuDriverMetadata> -> it.second.name } 62 ?.sortedByDescending { it: Pair<String, GpuDriverMetadata> -> it.second.name }
63 ?.distinct() 63 ?.distinct()
64 ?.toMutableList() ?: mutableListOf() 64 ?.toMutableList() ?: mutableListOf()
65
66 // TODO: Get system driver information
67 drivers.add(0, Pair("", GpuDriverMetadata()))
68 return drivers 65 return drivers
69 } 66 }
70 67
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/viewholder/AbstractViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/viewholder/AbstractViewHolder.kt
new file mode 100644
index 000000000..7101ad434
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/viewholder/AbstractViewHolder.kt
@@ -0,0 +1,18 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.viewholder
5
6import androidx.recyclerview.widget.RecyclerView
7import androidx.viewbinding.ViewBinding
8import org.yuzu.yuzu_emu.adapters.AbstractDiffAdapter
9import org.yuzu.yuzu_emu.adapters.AbstractListAdapter
10
11/**
12 * [RecyclerView.ViewHolder] meant to work together with a [AbstractDiffAdapter] or a
13 * [AbstractListAdapter] so we can run [bind] on each list item without needing a manual hookup.
14 */
15abstract class AbstractViewHolder<Model>(binding: ViewBinding) :
16 RecyclerView.ViewHolder(binding.root) {
17 abstract fun bind(model: Model)
18}
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 136c8dee6..ed3b1353a 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -410,8 +410,8 @@ void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) {
410 jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); 410 jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
411 411
412 if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { 412 if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
413 handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); 413 handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
414 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); 414 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
415 handheld->Disconnect(); 415 handheld->Disconnect();
416 } 416 }
417 } 417 }
@@ -770,8 +770,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
770 ASSERT(user_id); 770 ASSERT(user_id);
771 771
772 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( 772 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
773 EmulationSession::GetInstance().System(), vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, 773 {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, 1,
774 FileSys::SaveDataType::SaveData, 1, user_id->AsU128(), 0); 774 user_id->AsU128(), 0);
775 775
776 const auto full_path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); 776 const auto full_path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
777 if (!Common::FS::CreateParentDirs(full_path)) { 777 if (!Common::FS::CreateParentDirs(full_path)) {
@@ -878,7 +878,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
878 FileSys::Mode::Read); 878 FileSys::Mode::Read);
879 879
880 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( 880 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
881 system, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, 881 {}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
882 program_id, user_id->AsU128(), 0); 882 program_id, user_id->AsU128(), 0);
883 return ToJString(env, user_save_data_path); 883 return ToJString(env, user_save_data_path);
884} 884}
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
index 535902483..c6c3343dc 100644
--- a/src/android/app/src/main/jni/native_config.cpp
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -205,7 +205,7 @@ jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEn
205 jstring jkey) { 205 jstring jkey) {
206 auto setting = getSetting<std::string>(env, jkey); 206 auto setting = getSetting<std::string>(env, jkey);
207 if (setting != nullptr) { 207 if (setting != nullptr) {
208 return setting->RuntimeModfiable(); 208 return setting->RuntimeModifiable();
209 } 209 }
210 return true; 210 return true;
211} 211}
diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_about.xml b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml
index a26ffbc73..655e49219 100644
--- a/src/android/app/src/main/res/layout-w600dp/fragment_about.xml
+++ b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml
@@ -147,7 +147,7 @@
147 android:layout_marginHorizontal="20dp" /> 147 android:layout_marginHorizontal="20dp" />
148 148
149 <LinearLayout 149 <LinearLayout
150 android:id="@+id/button_build_hash" 150 android:id="@+id/button_version_name"
151 android:layout_width="match_parent" 151 android:layout_width="match_parent"
152 android:layout_height="wrap_content" 152 android:layout_height="wrap_content"
153 android:background="?attr/selectableItemBackground" 153 android:background="?attr/selectableItemBackground"
@@ -164,7 +164,7 @@
164 android:textAlignment="viewStart" /> 164 android:textAlignment="viewStart" />
165 165
166 <com.google.android.material.textview.MaterialTextView 166 <com.google.android.material.textview.MaterialTextView
167 android:id="@+id/text_build_hash" 167 android:id="@+id/text_version_name"
168 style="@style/TextAppearance.Material3.BodyMedium" 168 style="@style/TextAppearance.Material3.BodyMedium"
169 android:layout_width="match_parent" 169 android:layout_width="match_parent"
170 android:layout_height="wrap_content" 170 android:layout_height="wrap_content"
diff --git a/src/android/app/src/main/res/layout/fragment_about.xml b/src/android/app/src/main/res/layout/fragment_about.xml
index a24f5230e..38090fa50 100644
--- a/src/android/app/src/main/res/layout/fragment_about.xml
+++ b/src/android/app/src/main/res/layout/fragment_about.xml
@@ -148,7 +148,7 @@
148 android:layout_marginHorizontal="20dp" /> 148 android:layout_marginHorizontal="20dp" />
149 149
150 <LinearLayout 150 <LinearLayout
151 android:id="@+id/button_build_hash" 151 android:id="@+id/button_version_name"
152 android:layout_width="match_parent" 152 android:layout_width="match_parent"
153 android:layout_height="wrap_content" 153 android:layout_height="wrap_content"
154 android:paddingVertical="16dp" 154 android:paddingVertical="16dp"
@@ -165,7 +165,7 @@
165 android:text="@string/build" /> 165 android:text="@string/build" />
166 166
167 <com.google.android.material.textview.MaterialTextView 167 <com.google.android.material.textview.MaterialTextView
168 android:id="@+id/text_build_hash" 168 android:id="@+id/text_version_name"
169 style="@style/TextAppearance.Material3.BodyMedium" 169 style="@style/TextAppearance.Material3.BodyMedium"
170 android:layout_width="match_parent" 170 android:layout_width="match_parent"
171 android:layout_height="wrap_content" 171 android:layout_height="wrap_content"
diff --git a/src/android/app/src/main/res/menu/menu_driver_manager.xml b/src/android/app/src/main/res/menu/menu_driver_manager.xml
new file mode 100644
index 000000000..dee5d57b6
--- /dev/null
+++ b/src/android/app/src/main/res/menu/menu_driver_manager.xml
@@ -0,0 +1,11 @@
1<?xml version="1.0" encoding="utf-8"?>
2<menu xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto">
4
5 <item
6 android:id="@+id/menu_driver_clear"
7 android:icon="@drawable/ic_clear"
8 android:title="@string/clear"
9 app:showAsAction="always" />
10
11</menu>
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index 0363ff3b6..4701913eb 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -29,7 +29,7 @@
29 <item>@string/language_dutch</item> 29 <item>@string/language_dutch</item>
30 <item>@string/language_english</item> 30 <item>@string/language_english</item>
31 <item>@string/language_french</item> 31 <item>@string/language_french</item>
32 <item>@string/langauge_german</item> 32 <item>@string/language_german</item>
33 <item>@string/language_italian</item> 33 <item>@string/language_italian</item>
34 <item>@string/language_japanese</item> 34 <item>@string/language_japanese</item>
35 <item>@string/language_korean</item> 35 <item>@string/language_korean</item>
@@ -228,10 +228,10 @@
228 <item>R</item> 228 <item>R</item>
229 <item>ZL</item> 229 <item>ZL</item>
230 <item>ZR</item> 230 <item>ZR</item>
231 <item>@string/gamepad_left_stick</item>
232 <item>@string/gamepad_right_stick</item>
233 <item>L3</item> 231 <item>L3</item>
234 <item>R3</item> 232 <item>R3</item>
233 <item>@string/gamepad_left_stick</item>
234 <item>@string/gamepad_right_stick</item>
235 <item>@string/gamepad_d_pad</item> 235 <item>@string/gamepad_d_pad</item>
236 </string-array> 236 </string-array>
237 237
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 3bb92ad67..547752bda 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -410,7 +410,7 @@
410 <string name="language_japanese" translatable="false">日本語</string> 410 <string name="language_japanese" translatable="false">日本語</string>
411 <string name="language_english" translatable="false">English</string> 411 <string name="language_english" translatable="false">English</string>
412 <string name="language_french" translatable="false">Français</string> 412 <string name="language_french" translatable="false">Français</string>
413 <string name="langauge_german" translatable="false">Deutsch</string> 413 <string name="language_german" translatable="false">Deutsch</string>
414 <string name="language_italian" translatable="false">Italiano</string> 414 <string name="language_italian" translatable="false">Italiano</string>
415 <string name="language_spanish" translatable="false">Español</string> 415 <string name="language_spanish" translatable="false">Español</string>
416 <string name="language_chinese" translatable="false">简体中文</string> 416 <string name="language_chinese" translatable="false">简体中文</string>
diff --git a/src/audio_core/adsp/adsp.cpp b/src/audio_core/adsp/adsp.cpp
index 6c53c98fd..48f0a63d4 100644
--- a/src/audio_core/adsp/adsp.cpp
+++ b/src/audio_core/adsp/adsp.cpp
@@ -11,7 +11,7 @@ ADSP::ADSP(Core::System& system, Sink::Sink& sink) {
11 opus_decoder = std::make_unique<OpusDecoder::OpusDecoder>(system); 11 opus_decoder = std::make_unique<OpusDecoder::OpusDecoder>(system);
12 opus_decoder->Send(Direction::DSP, OpusDecoder::Message::Start); 12 opus_decoder->Send(Direction::DSP, OpusDecoder::Message::Start);
13 if (opus_decoder->Receive(Direction::Host) != OpusDecoder::Message::StartOK) { 13 if (opus_decoder->Receive(Direction::Host) != OpusDecoder::Message::StartOK) {
14 LOG_ERROR(Service_Audio, "OpusDeocder failed to initialize."); 14 LOG_ERROR(Service_Audio, "OpusDecoder failed to initialize.");
15 return; 15 return;
16 } 16 }
17} 17}
diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp
index ee42ae529..3c214ec00 100644
--- a/src/audio_core/device/device_session.cpp
+++ b/src/audio_core/device/device_session.cpp
@@ -10,6 +10,8 @@
10#include "core/core_timing.h" 10#include "core/core_timing.h"
11#include "core/memory.h" 11#include "core/memory.h"
12 12
13#include "core/hle/kernel/k_process.h"
14
13namespace AudioCore { 15namespace AudioCore {
14 16
15using namespace std::literals; 17using namespace std::literals;
@@ -25,7 +27,7 @@ DeviceSession::~DeviceSession() {
25} 27}
26 28
27Result DeviceSession::Initialize(std::string_view name_, SampleFormat sample_format_, 29Result DeviceSession::Initialize(std::string_view name_, SampleFormat sample_format_,
28 u16 channel_count_, size_t session_id_, u32 handle_, 30 u16 channel_count_, size_t session_id_, Kernel::KProcess* handle_,
29 u64 applet_resource_user_id_, Sink::StreamType type_) { 31 u64 applet_resource_user_id_, Sink::StreamType type_) {
30 if (stream) { 32 if (stream) {
31 Finalize(); 33 Finalize();
@@ -36,6 +38,7 @@ Result DeviceSession::Initialize(std::string_view name_, SampleFormat sample_for
36 channel_count = channel_count_; 38 channel_count = channel_count_;
37 session_id = session_id_; 39 session_id = session_id_;
38 handle = handle_; 40 handle = handle_;
41 handle->Open();
39 applet_resource_user_id = applet_resource_user_id_; 42 applet_resource_user_id = applet_resource_user_id_;
40 43
41 if (type == Sink::StreamType::In) { 44 if (type == Sink::StreamType::In) {
@@ -54,6 +57,11 @@ void DeviceSession::Finalize() {
54 sink->CloseStream(stream); 57 sink->CloseStream(stream);
55 stream = nullptr; 58 stream = nullptr;
56 } 59 }
60
61 if (handle) {
62 handle->Close();
63 handle = nullptr;
64 }
57} 65}
58 66
59void DeviceSession::Start() { 67void DeviceSession::Start() {
@@ -91,7 +99,7 @@ void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) {
91 stream->AppendBuffer(new_buffer, tmp_samples); 99 stream->AppendBuffer(new_buffer, tmp_samples);
92 } else { 100 } else {
93 Core::Memory::CpuGuestMemory<s16, Core::Memory::GuestMemoryFlags::UnsafeRead> samples( 101 Core::Memory::CpuGuestMemory<s16, Core::Memory::GuestMemoryFlags::UnsafeRead> samples(
94 system.ApplicationMemory(), buffer.samples, buffer.size / sizeof(s16)); 102 handle->GetMemory(), buffer.samples, buffer.size / sizeof(s16));
95 stream->AppendBuffer(new_buffer, samples); 103 stream->AppendBuffer(new_buffer, samples);
96 } 104 }
97 } 105 }
@@ -100,7 +108,7 @@ void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) {
100void DeviceSession::ReleaseBuffer(const AudioBuffer& buffer) const { 108void DeviceSession::ReleaseBuffer(const AudioBuffer& buffer) const {
101 if (type == Sink::StreamType::In) { 109 if (type == Sink::StreamType::In) {
102 auto samples{stream->ReleaseBuffer(buffer.size / sizeof(s16))}; 110 auto samples{stream->ReleaseBuffer(buffer.size / sizeof(s16))};
103 system.ApplicationMemory().WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size); 111 handle->GetMemory().WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size);
104 } 112 }
105} 113}
106 114
diff --git a/src/audio_core/device/device_session.h b/src/audio_core/device/device_session.h
index 7d52f362d..f3fae2617 100644
--- a/src/audio_core/device/device_session.h
+++ b/src/audio_core/device/device_session.h
@@ -20,6 +20,10 @@ struct EventType;
20} // namespace Timing 20} // namespace Timing
21} // namespace Core 21} // namespace Core
22 22
23namespace Kernel {
24class KProcess;
25} // namespace Kernel
26
23namespace AudioCore { 27namespace AudioCore {
24 28
25namespace Sink { 29namespace Sink {
@@ -44,13 +48,13 @@ public:
44 * @param sample_format - Sample format for this device's output. 48 * @param sample_format - Sample format for this device's output.
45 * @param channel_count - Number of channels for this device (2 or 6). 49 * @param channel_count - Number of channels for this device (2 or 6).
46 * @param session_id - This session's id. 50 * @param session_id - This session's id.
47 * @param handle - Handle for this device session (unused). 51 * @param handle - Process handle for this device session.
48 * @param applet_resource_user_id - Applet resource user id for this device session (unused). 52 * @param applet_resource_user_id - Applet resource user id for this device session (unused).
49 * @param type - Type of this stream (Render, In, Out). 53 * @param type - Type of this stream (Render, In, Out).
50 * @return Result code for this call. 54 * @return Result code for this call.
51 */ 55 */
52 Result Initialize(std::string_view name, SampleFormat sample_format, u16 channel_count, 56 Result Initialize(std::string_view name, SampleFormat sample_format, u16 channel_count,
53 size_t session_id, u32 handle, u64 applet_resource_user_id, 57 size_t session_id, Kernel::KProcess* handle, u64 applet_resource_user_id,
54 Sink::StreamType type); 58 Sink::StreamType type);
55 59
56 /** 60 /**
@@ -137,8 +141,8 @@ private:
137 u16 channel_count{}; 141 u16 channel_count{};
138 /// Session id of this device session 142 /// Session id of this device session
139 size_t session_id{}; 143 size_t session_id{};
140 /// Handle of this device session 144 /// Process handle of device memory owner
141 u32 handle{}; 145 Kernel::KProcess* handle{};
142 /// Applet resource user id of this device session 146 /// Applet resource user id of this device session
143 u64 applet_resource_user_id{}; 147 u64 applet_resource_user_id{};
144 /// Total number of samples played by this device session 148 /// Total number of samples played by this device session
diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp
index 579129121..b2dd3ef9f 100644
--- a/src/audio_core/in/audio_in_system.cpp
+++ b/src/audio_core/in/audio_in_system.cpp
@@ -57,7 +57,7 @@ Result System::IsConfigValid(const std::string_view device_name,
57} 57}
58 58
59Result System::Initialize(std::string device_name, const AudioInParameter& in_params, 59Result System::Initialize(std::string device_name, const AudioInParameter& in_params,
60 const u32 handle_, const u64 applet_resource_user_id_) { 60 Kernel::KProcess* handle_, const u64 applet_resource_user_id_) {
61 auto result{IsConfigValid(device_name, in_params)}; 61 auto result{IsConfigValid(device_name, in_params)};
62 if (result.IsError()) { 62 if (result.IsError()) {
63 return result; 63 return result;
diff --git a/src/audio_core/in/audio_in_system.h b/src/audio_core/in/audio_in_system.h
index 1c5154638..ee048190c 100644
--- a/src/audio_core/in/audio_in_system.h
+++ b/src/audio_core/in/audio_in_system.h
@@ -19,7 +19,8 @@ class System;
19 19
20namespace Kernel { 20namespace Kernel {
21class KEvent; 21class KEvent;
22} 22class KProcess;
23} // namespace Kernel
23 24
24namespace AudioCore::AudioIn { 25namespace AudioCore::AudioIn {
25 26
@@ -93,12 +94,12 @@ public:
93 * 94 *
94 * @param device_name - The name of the requested input device. 95 * @param device_name - The name of the requested input device.
95 * @param in_params - Input parameters, see AudioInParameter. 96 * @param in_params - Input parameters, see AudioInParameter.
96 * @param handle - Unused. 97 * @param handle - Process handle.
97 * @param applet_resource_user_id - Unused. 98 * @param applet_resource_user_id - Unused.
98 * @return Result code. 99 * @return Result code.
99 */ 100 */
100 Result Initialize(std::string device_name, const AudioInParameter& in_params, u32 handle, 101 Result Initialize(std::string device_name, const AudioInParameter& in_params,
101 u64 applet_resource_user_id); 102 Kernel::KProcess* handle, u64 applet_resource_user_id);
102 103
103 /** 104 /**
104 * Start this system. 105 * Start this system.
@@ -244,8 +245,8 @@ public:
244private: 245private:
245 /// Core system 246 /// Core system
246 Core::System& system; 247 Core::System& system;
247 /// (Unused) 248 /// Process handle
248 u32 handle{}; 249 Kernel::KProcess* handle{};
249 /// (Unused) 250 /// (Unused)
250 u64 applet_resource_user_id{}; 251 u64 applet_resource_user_id{};
251 /// Buffer event, signalled when a buffer is ready 252 /// Buffer event, signalled when a buffer is ready
diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp
index 0adf64bd3..7b3ff4e88 100644
--- a/src/audio_core/out/audio_out_system.cpp
+++ b/src/audio_core/out/audio_out_system.cpp
@@ -48,8 +48,8 @@ Result System::IsConfigValid(std::string_view device_name,
48 return Service::Audio::ResultInvalidChannelCount; 48 return Service::Audio::ResultInvalidChannelCount;
49} 49}
50 50
51Result System::Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle_, 51Result System::Initialize(std::string device_name, const AudioOutParameter& in_params,
52 u64 applet_resource_user_id_) { 52 Kernel::KProcess* handle_, u64 applet_resource_user_id_) {
53 auto result = IsConfigValid(device_name, in_params); 53 auto result = IsConfigValid(device_name, in_params);
54 if (result.IsError()) { 54 if (result.IsError()) {
55 return result; 55 return result;
diff --git a/src/audio_core/out/audio_out_system.h b/src/audio_core/out/audio_out_system.h
index b95cb91be..82aada185 100644
--- a/src/audio_core/out/audio_out_system.h
+++ b/src/audio_core/out/audio_out_system.h
@@ -19,7 +19,8 @@ class System;
19 19
20namespace Kernel { 20namespace Kernel {
21class KEvent; 21class KEvent;
22} 22class KProcess;
23} // namespace Kernel
23 24
24namespace AudioCore::AudioOut { 25namespace AudioCore::AudioOut {
25 26
@@ -84,12 +85,12 @@ public:
84 * 85 *
85 * @param device_name - The name of the requested output device. 86 * @param device_name - The name of the requested output device.
86 * @param in_params - Input parameters, see AudioOutParameter. 87 * @param in_params - Input parameters, see AudioOutParameter.
87 * @param handle - Unused. 88 * @param handle - Process handle.
88 * @param applet_resource_user_id - Unused. 89 * @param applet_resource_user_id - Unused.
89 * @return Result code. 90 * @return Result code.
90 */ 91 */
91 Result Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle, 92 Result Initialize(std::string device_name, const AudioOutParameter& in_params,
92 u64 applet_resource_user_id); 93 Kernel::KProcess* handle, u64 applet_resource_user_id);
93 94
94 /** 95 /**
95 * Start this system. 96 * Start this system.
@@ -228,8 +229,8 @@ public:
228private: 229private:
229 /// Core system 230 /// Core system
230 Core::System& system; 231 Core::System& system;
231 /// (Unused) 232 /// Process handle
232 u32 handle{}; 233 Kernel::KProcess* handle{};
233 /// (Unused) 234 /// (Unused)
234 u64 applet_resource_user_id{}; 235 u64 applet_resource_user_id{};
235 /// Buffer event, signalled when a buffer is ready 236 /// Buffer event, signalled when a buffer is ready
diff --git a/src/audio_core/renderer/command/command_generator.cpp b/src/audio_core/renderer/command/command_generator.cpp
index ccb186209..f97db5899 100644
--- a/src/audio_core/renderer/command/command_generator.cpp
+++ b/src/audio_core/renderer/command/command_generator.cpp
@@ -41,7 +41,7 @@ void CommandGenerator::GenerateDataSourceCommand(VoiceInfo& voice_info,
41 const VoiceState& voice_state, const s8 channel) { 41 const VoiceState& voice_state, const s8 channel) {
42 if (voice_info.mix_id == UnusedMixId) { 42 if (voice_info.mix_id == UnusedMixId) {
43 if (voice_info.splitter_id != UnusedSplitterId) { 43 if (voice_info.splitter_id != UnusedSplitterId) {
44 auto destination{splitter_context.GetDesintationData(voice_info.splitter_id, 0)}; 44 auto destination{splitter_context.GetDestinationData(voice_info.splitter_id, 0)};
45 u32 dest_id{0}; 45 u32 dest_id{0};
46 while (destination != nullptr) { 46 while (destination != nullptr) {
47 if (destination->IsConfigured()) { 47 if (destination->IsConfigured()) {
@@ -55,7 +55,7 @@ void CommandGenerator::GenerateDataSourceCommand(VoiceInfo& voice_info,
55 } 55 }
56 } 56 }
57 dest_id++; 57 dest_id++;
58 destination = splitter_context.GetDesintationData(voice_info.splitter_id, dest_id); 58 destination = splitter_context.GetDestinationData(voice_info.splitter_id, dest_id);
59 } 59 }
60 } 60 }
61 } else { 61 } else {
@@ -234,7 +234,7 @@ void CommandGenerator::GenerateVoiceCommand(VoiceInfo& voice_info) {
234 if (voice_info.mix_id == UnusedMixId) { 234 if (voice_info.mix_id == UnusedMixId) {
235 if (voice_info.splitter_id != UnusedSplitterId) { 235 if (voice_info.splitter_id != UnusedSplitterId) {
236 auto i{channel}; 236 auto i{channel};
237 auto destination{splitter_context.GetDesintationData(voice_info.splitter_id, i)}; 237 auto destination{splitter_context.GetDestinationData(voice_info.splitter_id, i)};
238 while (destination != nullptr) { 238 while (destination != nullptr) {
239 if (destination->IsConfigured()) { 239 if (destination->IsConfigured()) {
240 const auto mix_id{destination->GetMixId()}; 240 const auto mix_id{destination->GetMixId()};
@@ -249,7 +249,7 @@ void CommandGenerator::GenerateVoiceCommand(VoiceInfo& voice_info) {
249 } 249 }
250 } 250 }
251 i += voice_info.channel_count; 251 i += voice_info.channel_count;
252 destination = splitter_context.GetDesintationData(voice_info.splitter_id, i); 252 destination = splitter_context.GetDestinationData(voice_info.splitter_id, i);
253 } 253 }
254 } 254 }
255 } else { 255 } else {
@@ -591,7 +591,7 @@ void CommandGenerator::GenerateMixCommands(MixInfo& mix_info) {
591 if (mix_info.dst_splitter_id != UnusedSplitterId) { 591 if (mix_info.dst_splitter_id != UnusedSplitterId) {
592 s16 dest_id{0}; 592 s16 dest_id{0};
593 auto destination{ 593 auto destination{
594 splitter_context.GetDesintationData(mix_info.dst_splitter_id, dest_id)}; 594 splitter_context.GetDestinationData(mix_info.dst_splitter_id, dest_id)};
595 while (destination != nullptr) { 595 while (destination != nullptr) {
596 if (destination->IsConfigured()) { 596 if (destination->IsConfigured()) {
597 auto splitter_mix_id{destination->GetMixId()}; 597 auto splitter_mix_id{destination->GetMixId()};
@@ -612,7 +612,7 @@ void CommandGenerator::GenerateMixCommands(MixInfo& mix_info) {
612 } 612 }
613 dest_id++; 613 dest_id++;
614 destination = 614 destination =
615 splitter_context.GetDesintationData(mix_info.dst_splitter_id, dest_id); 615 splitter_context.GetDestinationData(mix_info.dst_splitter_id, dest_id);
616 } 616 }
617 } 617 }
618 } else { 618 } else {
diff --git a/src/audio_core/renderer/mix/mix_info.cpp b/src/audio_core/renderer/mix/mix_info.cpp
index 5e44bde18..68bbe0aed 100644
--- a/src/audio_core/renderer/mix/mix_info.cpp
+++ b/src/audio_core/renderer/mix/mix_info.cpp
@@ -93,7 +93,7 @@ bool MixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const InParameter& in_pa
93 93
94 for (u32 i = 0; i < destination_count; i++) { 94 for (u32 i = 0; i < destination_count; i++) {
95 auto destination{ 95 auto destination{
96 splitter_context.GetDesintationData(in_params.dest_splitter_id, i)}; 96 splitter_context.GetDestinationData(in_params.dest_splitter_id, i)};
97 97
98 if (destination) { 98 if (destination) {
99 const auto destination_id{destination->GetMixId()}; 99 const auto destination_id{destination->GetMixId()};
diff --git a/src/audio_core/renderer/splitter/splitter_context.cpp b/src/audio_core/renderer/splitter/splitter_context.cpp
index 686150ea6..d0f3b60c2 100644
--- a/src/audio_core/renderer/splitter/splitter_context.cpp
+++ b/src/audio_core/renderer/splitter/splitter_context.cpp
@@ -9,7 +9,7 @@
9 9
10namespace AudioCore::Renderer { 10namespace AudioCore::Renderer {
11 11
12SplitterDestinationData* SplitterContext::GetDesintationData(const s32 splitter_id, 12SplitterDestinationData* SplitterContext::GetDestinationData(const s32 splitter_id,
13 const s32 destination_id) { 13 const s32 destination_id) {
14 return splitter_infos[splitter_id].GetData(destination_id); 14 return splitter_infos[splitter_id].GetData(destination_id);
15} 15}
diff --git a/src/audio_core/renderer/splitter/splitter_context.h b/src/audio_core/renderer/splitter/splitter_context.h
index 556e6dcc3..1c0b84671 100644
--- a/src/audio_core/renderer/splitter/splitter_context.h
+++ b/src/audio_core/renderer/splitter/splitter_context.h
@@ -42,7 +42,7 @@ public:
42 * @param destination_id - Destination index within the splitter. 42 * @param destination_id - Destination index within the splitter.
43 * @return Pointer to the found destination. May be nullptr. 43 * @return Pointer to the found destination. May be nullptr.
44 */ 44 */
45 SplitterDestinationData* GetDesintationData(s32 splitter_id, s32 destination_id); 45 SplitterDestinationData* GetDestinationData(s32 splitter_id, s32 destination_id);
46 46
47 /** 47 /**
48 * Get a splitter from the given index. 48 * Get a splitter from the given index.
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index 166dc3dce..85dc18c11 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/page_table.h" 4#include "common/page_table.h"
5#include "common/scope_exit.h"
5 6
6namespace Common { 7namespace Common {
7 8
@@ -11,29 +12,10 @@ PageTable::~PageTable() noexcept = default;
11 12
12bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context, 13bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
13 Common::ProcessAddress address) const { 14 Common::ProcessAddress address) const {
14 // Setup invalid defaults. 15 out_context->next_offset = GetInteger(address);
15 out_entry->phys_addr = 0; 16 out_context->next_page = address / page_size;
16 out_entry->block_size = page_size;
17 out_context->next_page = 0;
18
19 // Validate that we can read the actual entry.
20 const auto page = address / page_size;
21 if (page >= backing_addr.size()) {
22 return false;
23 }
24
25 // Validate that the entry is mapped.
26 const auto phys_addr = backing_addr[page];
27 if (phys_addr == 0) {
28 return false;
29 }
30 17
31 // Populate the results. 18 return this->ContinueTraversal(out_entry, out_context);
32 out_entry->phys_addr = phys_addr + GetInteger(address);
33 out_context->next_page = page + 1;
34 out_context->next_offset = GetInteger(address) + page_size;
35
36 return true;
37} 19}
38 20
39bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const { 21bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
@@ -41,6 +23,12 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c
41 out_entry->phys_addr = 0; 23 out_entry->phys_addr = 0;
42 out_entry->block_size = page_size; 24 out_entry->block_size = page_size;
43 25
26 // Regardless of whether the page was mapped, advance on exit.
27 SCOPE_EXIT({
28 context->next_page += 1;
29 context->next_offset += page_size;
30 });
31
44 // Validate that we can read the actual entry. 32 // Validate that we can read the actual entry.
45 const auto page = context->next_page; 33 const auto page = context->next_page;
46 if (page >= backing_addr.size()) { 34 if (page >= backing_addr.size()) {
@@ -55,8 +43,6 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c
55 43
56 // Populate the results. 44 // Populate the results.
57 out_entry->phys_addr = phys_addr + context->next_offset; 45 out_entry->phys_addr = phys_addr + context->next_offset;
58 context->next_page = page + 1;
59 context->next_offset += page_size;
60 46
61 return true; 47 return true;
62} 48}
diff --git a/src/common/settings_common.cpp b/src/common/settings_common.cpp
index 5960b78aa..b90e3509c 100644
--- a/src/common/settings_common.cpp
+++ b/src/common/settings_common.cpp
@@ -35,7 +35,7 @@ bool BasicSetting::Save() const {
35 return save; 35 return save;
36} 36}
37 37
38bool BasicSetting::RuntimeModfiable() const { 38bool BasicSetting::RuntimeModifiable() const {
39 return runtime_modifiable; 39 return runtime_modifiable;
40} 40}
41 41
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index 1a290ad77..987489e8a 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -186,7 +186,7 @@ public:
186 /** 186 /**
187 * @returns true if the current setting can be changed while the guest is running. 187 * @returns true if the current setting can be changed while the guest is running.
188 */ 188 */
189 [[nodiscard]] bool RuntimeModfiable() const; 189 [[nodiscard]] bool RuntimeModifiable() const;
190 190
191 /** 191 /**
192 * @returns A unique number corresponding to the setting. 192 * @returns A unique number corresponding to the setting.
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 753f55ebe..293d9647b 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -490,6 +490,10 @@ add_library(core STATIC
490 hle/service/filesystem/fsp_pr.h 490 hle/service/filesystem/fsp_pr.h
491 hle/service/filesystem/fsp_srv.cpp 491 hle/service/filesystem/fsp_srv.cpp
492 hle/service/filesystem/fsp_srv.h 492 hle/service/filesystem/fsp_srv.h
493 hle/service/filesystem/romfs_controller.cpp
494 hle/service/filesystem/romfs_controller.h
495 hle/service/filesystem/save_data_controller.cpp
496 hle/service/filesystem/save_data_controller.h
493 hle/service/fgm/fgm.cpp 497 hle/service/fgm/fgm.cpp
494 hle/service/fgm/fgm.h 498 hle/service/fgm/fgm.h
495 hle/service/friend/friend.cpp 499 hle/service/friend/friend.cpp
diff --git a/src/core/arm/nce/patcher.cpp b/src/core/arm/nce/patcher.cpp
index 47a7a8880..c7285e3a0 100644
--- a/src/core/arm/nce/patcher.cpp
+++ b/src/core/arm/nce/patcher.cpp
@@ -22,14 +22,10 @@ using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
22constexpr size_t MaxRelativeBranch = 128_MiB; 22constexpr size_t MaxRelativeBranch = 128_MiB;
23constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32); 23constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32);
24 24
25Patcher::Patcher() : c(m_patch_instructions) {} 25Patcher::Patcher() : c(m_patch_instructions) {
26 26 // The first word of the patch section is always a branch to the first instruction of the
27Patcher::~Patcher() = default; 27 // module.
28 28 c.dw(0);
29void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
30 const Kernel::CodeSet::Segment& code) {
31 // Branch to the first instruction of the module.
32 this->BranchToModule(0);
33 29
34 // Write save context helper function. 30 // Write save context helper function.
35 c.l(m_save_context); 31 c.l(m_save_context);
@@ -38,6 +34,25 @@ void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
38 // Write load context helper function. 34 // Write load context helper function.
39 c.l(m_load_context); 35 c.l(m_load_context);
40 WriteLoadContext(); 36 WriteLoadContext();
37}
38
39Patcher::~Patcher() = default;
40
41bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
42 const Kernel::CodeSet::Segment& code) {
43 // If we have patched modules but cannot reach the new module, then it needs its own patcher.
44 const size_t image_size = program_image.size();
45 if (total_program_size + image_size > MaxRelativeBranch && total_program_size > 0) {
46 return false;
47 }
48
49 // Add a new module patch to our list
50 modules.emplace_back();
51 curr_patch = &modules.back();
52
53 // The first word of the patch section is always a branch to the first instruction of the
54 // module.
55 curr_patch->m_branch_to_module_relocations.push_back({0, 0});
41 56
42 // Retrieve text segment data. 57 // Retrieve text segment data.
43 const auto text = std::span{program_image}.subspan(code.offset, code.size); 58 const auto text = std::span{program_image}.subspan(code.offset, code.size);
@@ -94,16 +109,17 @@ void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
94 } 109 }
95 110
96 if (auto exclusive = Exclusive{inst}; exclusive.Verify()) { 111 if (auto exclusive = Exclusive{inst}; exclusive.Verify()) {
97 m_exclusives.push_back(i); 112 curr_patch->m_exclusives.push_back(i);
98 } 113 }
99 } 114 }
100 115
101 // Determine patching mode for the final relocation step 116 // Determine patching mode for the final relocation step
102 const size_t image_size = program_image.size(); 117 total_program_size += image_size;
103 this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData; 118 this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData;
119 return true;
104} 120}
105 121
106void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, 122bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
107 const Kernel::CodeSet::Segment& code, 123 const Kernel::CodeSet::Segment& code,
108 Kernel::PhysicalMemory& program_image, 124 Kernel::PhysicalMemory& program_image,
109 EntryTrampolines* out_trampolines) { 125 EntryTrampolines* out_trampolines) {
@@ -120,7 +136,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
120 if (mode == PatchMode::PreText) { 136 if (mode == PatchMode::PreText) {
121 rc.B(rel.patch_offset - patch_size - rel.module_offset); 137 rc.B(rel.patch_offset - patch_size - rel.module_offset);
122 } else { 138 } else {
123 rc.B(image_size - rel.module_offset + rel.patch_offset); 139 rc.B(total_program_size - rel.module_offset + rel.patch_offset);
124 } 140 }
125 }; 141 };
126 142
@@ -129,7 +145,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
129 if (mode == PatchMode::PreText) { 145 if (mode == PatchMode::PreText) {
130 rc.B(patch_size - rel.patch_offset + rel.module_offset); 146 rc.B(patch_size - rel.patch_offset + rel.module_offset);
131 } else { 147 } else {
132 rc.B(rel.module_offset - image_size - rel.patch_offset); 148 rc.B(rel.module_offset - total_program_size - rel.patch_offset);
133 } 149 }
134 }; 150 };
135 151
@@ -137,7 +153,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
137 if (mode == PatchMode::PreText) { 153 if (mode == PatchMode::PreText) {
138 return GetInteger(load_base) + patch_offset; 154 return GetInteger(load_base) + patch_offset;
139 } else { 155 } else {
140 return GetInteger(load_base) + image_size + patch_offset; 156 return GetInteger(load_base) + total_program_size + patch_offset;
141 } 157 }
142 }; 158 };
143 159
@@ -150,39 +166,50 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
150 }; 166 };
151 167
152 // We are now ready to relocate! 168 // We are now ready to relocate!
153 for (const Relocation& rel : m_branch_to_patch_relocations) { 169 auto& patch = modules[m_relocate_module_index++];
170 for (const Relocation& rel : patch.m_branch_to_patch_relocations) {
154 ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel); 171 ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel);
155 } 172 }
156 for (const Relocation& rel : m_branch_to_module_relocations) { 173 for (const Relocation& rel : patch.m_branch_to_module_relocations) {
157 ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32), 174 ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32),
158 rel); 175 rel);
159 } 176 }
160 177
161 // Rewrite PC constants and record post trampolines 178 // Rewrite PC constants and record post trampolines
162 for (const Relocation& rel : m_write_module_pc_relocations) { 179 for (const Relocation& rel : patch.m_write_module_pc_relocations) {
163 oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)}; 180 oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)};
164 rc.dx(RebasePc(rel.module_offset)); 181 rc.dx(RebasePc(rel.module_offset));
165 } 182 }
166 for (const Trampoline& rel : m_trampolines) { 183 for (const Trampoline& rel : patch.m_trampolines) {
167 out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)}); 184 out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)});
168 } 185 }
169 186
170 // Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not. 187 // Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not.
171 // Convert to ordered to preserve this assumption. 188 // Convert to ordered to preserve this assumption.
172 for (const ModuleTextAddress i : m_exclusives) { 189 for (const ModuleTextAddress i : patch.m_exclusives) {
173 auto exclusive = Exclusive{text_words[i]}; 190 auto exclusive = Exclusive{text_words[i]};
174 text_words[i] = exclusive.AsOrdered(); 191 text_words[i] = exclusive.AsOrdered();
175 } 192 }
176 193
177 // Copy to program image 194 // Remove the patched module size from the total. This is done so total_program_size
178 if (this->mode == PatchMode::PreText) { 195 // always represents the distance from the currently patched module to the patch section.
179 std::memcpy(program_image.data(), m_patch_instructions.data(), 196 total_program_size -= image_size;
180 m_patch_instructions.size() * sizeof(u32)); 197
181 } else { 198 // Only copy to the program image of the last module
182 program_image.resize(image_size + patch_size); 199 if (m_relocate_module_index == modules.size()) {
183 std::memcpy(program_image.data() + image_size, m_patch_instructions.data(), 200 if (this->mode == PatchMode::PreText) {
184 m_patch_instructions.size() * sizeof(u32)); 201 ASSERT(image_size == total_program_size);
202 std::memcpy(program_image.data(), m_patch_instructions.data(),
203 m_patch_instructions.size() * sizeof(u32));
204 } else {
205 program_image.resize(image_size + patch_size);
206 std::memcpy(program_image.data() + image_size, m_patch_instructions.data(),
207 m_patch_instructions.size() * sizeof(u32));
208 }
209 return true;
185 } 210 }
211
212 return false;
186} 213}
187 214
188size_t Patcher::GetSectionSize() const noexcept { 215size_t Patcher::GetSectionSize() const noexcept {
@@ -322,7 +349,7 @@ void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) {
322 349
323 // Write the post-SVC trampoline address, which will jump back to the guest after restoring its 350 // Write the post-SVC trampoline address, which will jump back to the guest after restoring its
324 // state. 351 // state.
325 m_trampolines.push_back({c.offset(), module_dest}); 352 curr_patch->m_trampolines.push_back({c.offset(), module_dest});
326 353
327 // Host called this location. Save the return address so we can 354 // Host called this location. Save the return address so we can
328 // unwind the stack properly when jumping back. 355 // unwind the stack properly when jumping back.
diff --git a/src/core/arm/nce/patcher.h b/src/core/arm/nce/patcher.h
index c6d1608c1..a44f385e2 100644
--- a/src/core/arm/nce/patcher.h
+++ b/src/core/arm/nce/patcher.h
@@ -31,9 +31,9 @@ public:
31 explicit Patcher(); 31 explicit Patcher();
32 ~Patcher(); 32 ~Patcher();
33 33
34 void PatchText(const Kernel::PhysicalMemory& program_image, 34 bool PatchText(const Kernel::PhysicalMemory& program_image,
35 const Kernel::CodeSet::Segment& code); 35 const Kernel::CodeSet::Segment& code);
36 void RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, 36 bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code,
37 Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines); 37 Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines);
38 size_t GetSectionSize() const noexcept; 38 size_t GetSectionSize() const noexcept;
39 39
@@ -61,16 +61,16 @@ private:
61 61
62private: 62private:
63 void BranchToPatch(uintptr_t module_dest) { 63 void BranchToPatch(uintptr_t module_dest) {
64 m_branch_to_patch_relocations.push_back({c.offset(), module_dest}); 64 curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), module_dest});
65 } 65 }
66 66
67 void BranchToModule(uintptr_t module_dest) { 67 void BranchToModule(uintptr_t module_dest) {
68 m_branch_to_module_relocations.push_back({c.offset(), module_dest}); 68 curr_patch->m_branch_to_module_relocations.push_back({c.offset(), module_dest});
69 c.dw(0); 69 c.dw(0);
70 } 70 }
71 71
72 void WriteModulePc(uintptr_t module_dest) { 72 void WriteModulePc(uintptr_t module_dest) {
73 m_write_module_pc_relocations.push_back({c.offset(), module_dest}); 73 curr_patch->m_write_module_pc_relocations.push_back({c.offset(), module_dest});
74 c.dx(0); 74 c.dx(0);
75 } 75 }
76 76
@@ -84,15 +84,22 @@ private:
84 uintptr_t module_offset; ///< Offset in bytes from the start of the text section. 84 uintptr_t module_offset; ///< Offset in bytes from the start of the text section.
85 }; 85 };
86 86
87 struct ModulePatch {
88 std::vector<Trampoline> m_trampolines;
89 std::vector<Relocation> m_branch_to_patch_relocations{};
90 std::vector<Relocation> m_branch_to_module_relocations{};
91 std::vector<Relocation> m_write_module_pc_relocations{};
92 std::vector<ModuleTextAddress> m_exclusives{};
93 };
94
87 oaknut::VectorCodeGenerator c; 95 oaknut::VectorCodeGenerator c;
88 std::vector<Trampoline> m_trampolines;
89 std::vector<Relocation> m_branch_to_patch_relocations{};
90 std::vector<Relocation> m_branch_to_module_relocations{};
91 std::vector<Relocation> m_write_module_pc_relocations{};
92 std::vector<ModuleTextAddress> m_exclusives{};
93 oaknut::Label m_save_context{}; 96 oaknut::Label m_save_context{};
94 oaknut::Label m_load_context{}; 97 oaknut::Label m_load_context{};
95 PatchMode mode{PatchMode::None}; 98 PatchMode mode{PatchMode::None};
99 size_t total_program_size{};
100 size_t m_relocate_module_index{};
101 std::vector<ModulePatch> modules;
102 ModulePatch* curr_patch;
96}; 103};
97 104
98} // namespace Core::NCE 105} // namespace Core::NCE
diff --git a/src/core/core.cpp b/src/core/core.cpp
index c063f7719..461eea9c8 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -413,6 +413,7 @@ struct System::Impl {
413 kernel.ShutdownCores(); 413 kernel.ShutdownCores();
414 services.reset(); 414 services.reset();
415 service_manager.reset(); 415 service_manager.reset();
416 fs_controller.Reset();
416 cheat_engine.reset(); 417 cheat_engine.reset();
417 telemetry_session.reset(); 418 telemetry_session.reset();
418 time_manager.Shutdown(); 419 time_manager.Shutdown();
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index 0deea9c58..a249dc5f7 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -64,7 +64,7 @@ public:
64 return [this] { ShutdownThreadFunction(); }; 64 return [this] { ShutdownThreadFunction(); };
65 } 65 }
66 66
67 void PreemptSingleCore(bool from_running_enviroment = true); 67 void PreemptSingleCore(bool from_running_environment = true);
68 68
69 std::size_t CurrentCore() const { 69 std::size_t CurrentCore() const {
70 return current_core.load(); 70 return current_core.load();
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index 0e270eb50..e86aae846 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -114,7 +114,7 @@ public:
114 } 114 }
115 115
116 Kernel::KThread* GetActiveThread() override { 116 Kernel::KThread* GetActiveThread() override {
117 return state->active_thread; 117 return state->active_thread.GetPointerUnsafe();
118 } 118 }
119 119
120private: 120private:
@@ -147,11 +147,14 @@ private:
147 147
148 std::scoped_lock lk{connection_lock}; 148 std::scoped_lock lk{connection_lock};
149 149
150 // Find the process we are going to debug.
151 SetDebugProcess();
152
150 // Ensure everything is stopped. 153 // Ensure everything is stopped.
151 PauseEmulation(); 154 PauseEmulation();
152 155
153 // Set up the new frontend. 156 // Set up the new frontend.
154 frontend = std::make_unique<GDBStub>(*this, system); 157 frontend = std::make_unique<GDBStub>(*this, system, debug_process.GetPointerUnsafe());
155 158
156 // Set the new state. This will tear down any existing state. 159 // Set the new state. This will tear down any existing state.
157 state = ConnectionState{ 160 state = ConnectionState{
@@ -194,15 +197,20 @@ private:
194 UpdateActiveThread(); 197 UpdateActiveThread();
195 198
196 if (state->info.type == SignalType::Watchpoint) { 199 if (state->info.type == SignalType::Watchpoint) {
197 frontend->Watchpoint(state->active_thread, *state->info.watchpoint); 200 frontend->Watchpoint(std::addressof(*state->active_thread),
201 *state->info.watchpoint);
198 } else { 202 } else {
199 frontend->Stopped(state->active_thread); 203 frontend->Stopped(std::addressof(*state->active_thread));
200 } 204 }
201 205
202 break; 206 break;
203 case SignalType::ShuttingDown: 207 case SignalType::ShuttingDown:
204 frontend->ShuttingDown(); 208 frontend->ShuttingDown();
205 209
210 // Release members.
211 state->active_thread.Reset(nullptr);
212 debug_process.Reset(nullptr);
213
206 // Wait for emulation to shut down gracefully now. 214 // Wait for emulation to shut down gracefully now.
207 state->signal_pipe.close(); 215 state->signal_pipe.close();
208 state->client_socket.shutdown(boost::asio::socket_base::shutdown_both); 216 state->client_socket.shutdown(boost::asio::socket_base::shutdown_both);
@@ -222,7 +230,7 @@ private:
222 stopped = true; 230 stopped = true;
223 PauseEmulation(); 231 PauseEmulation();
224 UpdateActiveThread(); 232 UpdateActiveThread();
225 frontend->Stopped(state->active_thread); 233 frontend->Stopped(state->active_thread.GetPointerUnsafe());
226 break; 234 break;
227 } 235 }
228 case DebuggerAction::Continue: 236 case DebuggerAction::Continue:
@@ -232,7 +240,7 @@ private:
232 MarkResumed([&] { 240 MarkResumed([&] {
233 state->active_thread->SetStepState(Kernel::StepState::StepPending); 241 state->active_thread->SetStepState(Kernel::StepState::StepPending);
234 state->active_thread->Resume(Kernel::SuspendType::Debug); 242 state->active_thread->Resume(Kernel::SuspendType::Debug);
235 ResumeEmulation(state->active_thread); 243 ResumeEmulation(state->active_thread.GetPointerUnsafe());
236 }); 244 });
237 break; 245 break;
238 case DebuggerAction::StepThreadLocked: { 246 case DebuggerAction::StepThreadLocked: {
@@ -255,6 +263,7 @@ private:
255 } 263 }
256 264
257 void PauseEmulation() { 265 void PauseEmulation() {
266 Kernel::KScopedLightLock ll{debug_process->GetListLock()};
258 Kernel::KScopedSchedulerLock sl{system.Kernel()}; 267 Kernel::KScopedSchedulerLock sl{system.Kernel()};
259 268
260 // Put all threads to sleep on next scheduler round. 269 // Put all threads to sleep on next scheduler round.
@@ -264,6 +273,9 @@ private:
264 } 273 }
265 274
266 void ResumeEmulation(Kernel::KThread* except = nullptr) { 275 void ResumeEmulation(Kernel::KThread* except = nullptr) {
276 Kernel::KScopedLightLock ll{debug_process->GetListLock()};
277 Kernel::KScopedSchedulerLock sl{system.Kernel()};
278
267 // Wake up all threads. 279 // Wake up all threads.
268 for (auto& thread : ThreadList()) { 280 for (auto& thread : ThreadList()) {
269 if (std::addressof(thread) == except) { 281 if (std::addressof(thread) == except) {
@@ -277,15 +289,16 @@ private:
277 289
278 template <typename Callback> 290 template <typename Callback>
279 void MarkResumed(Callback&& cb) { 291 void MarkResumed(Callback&& cb) {
280 Kernel::KScopedSchedulerLock sl{system.Kernel()};
281 stopped = false; 292 stopped = false;
282 cb(); 293 cb();
283 } 294 }
284 295
285 void UpdateActiveThread() { 296 void UpdateActiveThread() {
297 Kernel::KScopedLightLock ll{debug_process->GetListLock()};
298
286 auto& threads{ThreadList()}; 299 auto& threads{ThreadList()};
287 for (auto& thread : threads) { 300 for (auto& thread : threads) {
288 if (std::addressof(thread) == state->active_thread) { 301 if (std::addressof(thread) == state->active_thread.GetPointerUnsafe()) {
289 // Thread is still alive, no need to update. 302 // Thread is still alive, no need to update.
290 return; 303 return;
291 } 304 }
@@ -293,12 +306,18 @@ private:
293 state->active_thread = std::addressof(threads.front()); 306 state->active_thread = std::addressof(threads.front());
294 } 307 }
295 308
309private:
310 void SetDebugProcess() {
311 debug_process = std::move(system.Kernel().GetProcessList().back());
312 }
313
296 Kernel::KProcess::ThreadList& ThreadList() { 314 Kernel::KProcess::ThreadList& ThreadList() {
297 return system.ApplicationProcess()->GetThreadList(); 315 return debug_process->GetThreadList();
298 } 316 }
299 317
300private: 318private:
301 System& system; 319 System& system;
320 Kernel::KScopedAutoObject<Kernel::KProcess> debug_process;
302 std::unique_ptr<DebuggerFrontend> frontend; 321 std::unique_ptr<DebuggerFrontend> frontend;
303 322
304 boost::asio::io_context io_context; 323 boost::asio::io_context io_context;
@@ -310,7 +329,7 @@ private:
310 boost::process::async_pipe signal_pipe; 329 boost::process::async_pipe signal_pipe;
311 330
312 SignalInfo info; 331 SignalInfo info;
313 Kernel::KThread* active_thread; 332 Kernel::KScopedAutoObject<Kernel::KThread> active_thread;
314 std::array<u8, 4096> client_data; 333 std::array<u8, 4096> client_data;
315 bool pipe_data; 334 bool pipe_data;
316 }; 335 };
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 66e46c4ba..80091cc7e 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -108,9 +108,9 @@ static std::string EscapeXML(std::string_view data) {
108 return escaped; 108 return escaped;
109} 109}
110 110
111GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_) 111GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_, Kernel::KProcess* debug_process_)
112 : DebuggerFrontend(backend_), system{system_} { 112 : DebuggerFrontend(backend_), system{system_}, debug_process{debug_process_} {
113 if (system.ApplicationProcess()->Is64Bit()) { 113 if (GetProcess()->Is64Bit()) {
114 arch = std::make_unique<GDBStubA64>(); 114 arch = std::make_unique<GDBStubA64>();
115 } else { 115 } else {
116 arch = std::make_unique<GDBStubA32>(); 116 arch = std::make_unique<GDBStubA32>();
@@ -276,7 +276,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
276 const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))}; 276 const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
277 277
278 std::vector<u8> mem(size); 278 std::vector<u8> mem(size);
279 if (system.ApplicationMemory().ReadBlock(addr, mem.data(), size)) { 279 if (GetMemory().ReadBlock(addr, mem.data(), size)) {
280 // Restore any bytes belonging to replaced instructions. 280 // Restore any bytes belonging to replaced instructions.
281 auto it = replaced_instructions.lower_bound(addr); 281 auto it = replaced_instructions.lower_bound(addr);
282 for (; it != replaced_instructions.end() && it->first < addr + size; it++) { 282 for (; it != replaced_instructions.end() && it->first < addr + size; it++) {
@@ -310,8 +310,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
310 const auto mem_substr{std::string_view(command).substr(mem_sep)}; 310 const auto mem_substr{std::string_view(command).substr(mem_sep)};
311 const auto mem{Common::HexStringToVector(mem_substr, false)}; 311 const auto mem{Common::HexStringToVector(mem_substr, false)};
312 312
313 if (system.ApplicationMemory().WriteBlock(addr, mem.data(), size)) { 313 if (GetMemory().WriteBlock(addr, mem.data(), size)) {
314 Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, size); 314 Core::InvalidateInstructionCacheRange(GetProcess(), addr, size);
315 SendReply(GDB_STUB_REPLY_OK); 315 SendReply(GDB_STUB_REPLY_OK);
316 } else { 316 } else {
317 SendReply(GDB_STUB_REPLY_ERR); 317 SendReply(GDB_STUB_REPLY_ERR);
@@ -353,7 +353,7 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
353 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; 353 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
354 const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))}; 354 const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
355 355
356 if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) { 356 if (!GetMemory().IsValidVirtualAddressRange(addr, size)) {
357 SendReply(GDB_STUB_REPLY_ERR); 357 SendReply(GDB_STUB_REPLY_ERR);
358 return; 358 return;
359 } 359 }
@@ -362,22 +362,20 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
362 362
363 switch (type) { 363 switch (type) {
364 case BreakpointType::Software: 364 case BreakpointType::Software:
365 replaced_instructions[addr] = system.ApplicationMemory().Read32(addr); 365 replaced_instructions[addr] = GetMemory().Read32(addr);
366 system.ApplicationMemory().Write32(addr, arch->BreakpointInstruction()); 366 GetMemory().Write32(addr, arch->BreakpointInstruction());
367 Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32)); 367 Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32));
368 success = true; 368 success = true;
369 break; 369 break;
370 case BreakpointType::WriteWatch: 370 case BreakpointType::WriteWatch:
371 success = system.ApplicationProcess()->InsertWatchpoint(addr, size, 371 success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
372 Kernel::DebugWatchpointType::Write);
373 break; 372 break;
374 case BreakpointType::ReadWatch: 373 case BreakpointType::ReadWatch:
375 success = system.ApplicationProcess()->InsertWatchpoint(addr, size, 374 success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
376 Kernel::DebugWatchpointType::Read);
377 break; 375 break;
378 case BreakpointType::AccessWatch: 376 case BreakpointType::AccessWatch:
379 success = system.ApplicationProcess()->InsertWatchpoint( 377 success =
380 addr, size, Kernel::DebugWatchpointType::ReadOrWrite); 378 GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
381 break; 379 break;
382 case BreakpointType::Hardware: 380 case BreakpointType::Hardware:
383 default: 381 default:
@@ -400,7 +398,7 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
400 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; 398 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
401 const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))}; 399 const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
402 400
403 if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) { 401 if (!GetMemory().IsValidVirtualAddressRange(addr, size)) {
404 SendReply(GDB_STUB_REPLY_ERR); 402 SendReply(GDB_STUB_REPLY_ERR);
405 return; 403 return;
406 } 404 }
@@ -411,24 +409,22 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
411 case BreakpointType::Software: { 409 case BreakpointType::Software: {
412 const auto orig_insn{replaced_instructions.find(addr)}; 410 const auto orig_insn{replaced_instructions.find(addr)};
413 if (orig_insn != replaced_instructions.end()) { 411 if (orig_insn != replaced_instructions.end()) {
414 system.ApplicationMemory().Write32(addr, orig_insn->second); 412 GetMemory().Write32(addr, orig_insn->second);
415 Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32)); 413 Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32));
416 replaced_instructions.erase(addr); 414 replaced_instructions.erase(addr);
417 success = true; 415 success = true;
418 } 416 }
419 break; 417 break;
420 } 418 }
421 case BreakpointType::WriteWatch: 419 case BreakpointType::WriteWatch:
422 success = system.ApplicationProcess()->RemoveWatchpoint(addr, size, 420 success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
423 Kernel::DebugWatchpointType::Write);
424 break; 421 break;
425 case BreakpointType::ReadWatch: 422 case BreakpointType::ReadWatch:
426 success = system.ApplicationProcess()->RemoveWatchpoint(addr, size, 423 success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
427 Kernel::DebugWatchpointType::Read);
428 break; 424 break;
429 case BreakpointType::AccessWatch: 425 case BreakpointType::AccessWatch:
430 success = system.ApplicationProcess()->RemoveWatchpoint( 426 success =
431 addr, size, Kernel::DebugWatchpointType::ReadOrWrite); 427 GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
432 break; 428 break;
433 case BreakpointType::Hardware: 429 case BreakpointType::Hardware:
434 default: 430 default:
@@ -466,10 +462,10 @@ void GDBStub::HandleQuery(std::string_view command) {
466 const auto target_xml{arch->GetTargetXML()}; 462 const auto target_xml{arch->GetTargetXML()};
467 SendReply(PaginateBuffer(target_xml, command.substr(30))); 463 SendReply(PaginateBuffer(target_xml, command.substr(30)));
468 } else if (command.starts_with("Offsets")) { 464 } else if (command.starts_with("Offsets")) {
469 const auto main_offset = Core::FindMainModuleEntrypoint(system.ApplicationProcess()); 465 const auto main_offset = Core::FindMainModuleEntrypoint(GetProcess());
470 SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset))); 466 SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset)));
471 } else if (command.starts_with("Xfer:libraries:read::")) { 467 } else if (command.starts_with("Xfer:libraries:read::")) {
472 auto modules = Core::FindModules(system.ApplicationProcess()); 468 auto modules = Core::FindModules(GetProcess());
473 469
474 std::string buffer; 470 std::string buffer;
475 buffer += R"(<?xml version="1.0"?>)"; 471 buffer += R"(<?xml version="1.0"?>)";
@@ -483,7 +479,7 @@ void GDBStub::HandleQuery(std::string_view command) {
483 SendReply(PaginateBuffer(buffer, command.substr(21))); 479 SendReply(PaginateBuffer(buffer, command.substr(21)));
484 } else if (command.starts_with("fThreadInfo")) { 480 } else if (command.starts_with("fThreadInfo")) {
485 // beginning of list 481 // beginning of list
486 const auto& threads = system.ApplicationProcess()->GetThreadList(); 482 const auto& threads = GetProcess()->GetThreadList();
487 std::vector<std::string> thread_ids; 483 std::vector<std::string> thread_ids;
488 for (const auto& thread : threads) { 484 for (const auto& thread : threads) {
489 thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId())); 485 thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId()));
@@ -497,7 +493,7 @@ void GDBStub::HandleQuery(std::string_view command) {
497 buffer += R"(<?xml version="1.0"?>)"; 493 buffer += R"(<?xml version="1.0"?>)";
498 buffer += "<threads>"; 494 buffer += "<threads>";
499 495
500 const auto& threads = system.ApplicationProcess()->GetThreadList(); 496 const auto& threads = GetProcess()->GetThreadList();
501 for (const auto& thread : threads) { 497 for (const auto& thread : threads) {
502 auto thread_name{Core::GetThreadName(&thread)}; 498 auto thread_name{Core::GetThreadName(&thread)};
503 if (!thread_name) { 499 if (!thread_name) {
@@ -559,28 +555,28 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>&
559} 555}
560 556
561constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{ 557constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{
562 {"----- Free -----", Kernel::Svc::MemoryState::Free}, 558 {"----- Free ------", Kernel::Svc::MemoryState::Free},
563 {"Io ", Kernel::Svc::MemoryState::Io}, 559 {"Io ", Kernel::Svc::MemoryState::Io},
564 {"Static ", Kernel::Svc::MemoryState::Static}, 560 {"Static ", Kernel::Svc::MemoryState::Static},
565 {"Code ", Kernel::Svc::MemoryState::Code}, 561 {"Code ", Kernel::Svc::MemoryState::Code},
566 {"CodeData ", Kernel::Svc::MemoryState::CodeData}, 562 {"CodeData ", Kernel::Svc::MemoryState::CodeData},
567 {"Normal ", Kernel::Svc::MemoryState::Normal}, 563 {"Normal ", Kernel::Svc::MemoryState::Normal},
568 {"Shared ", Kernel::Svc::MemoryState::Shared}, 564 {"Shared ", Kernel::Svc::MemoryState::Shared},
569 {"AliasCode ", Kernel::Svc::MemoryState::AliasCode}, 565 {"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
570 {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData}, 566 {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
571 {"Ipc ", Kernel::Svc::MemoryState::Ipc}, 567 {"Ipc ", Kernel::Svc::MemoryState::Ipc},
572 {"Stack ", Kernel::Svc::MemoryState::Stack}, 568 {"Stack ", Kernel::Svc::MemoryState::Stack},
573 {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal}, 569 {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
574 {"Transfered ", Kernel::Svc::MemoryState::Transfered}, 570 {"Transferred ", Kernel::Svc::MemoryState::Transferred},
575 {"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered}, 571 {"SharedTransferred", Kernel::Svc::MemoryState::SharedTransferred},
576 {"SharedCode ", Kernel::Svc::MemoryState::SharedCode}, 572 {"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
577 {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible}, 573 {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
578 {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc}, 574 {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
579 {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc}, 575 {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
580 {"Kernel ", Kernel::Svc::MemoryState::Kernel}, 576 {"Kernel ", Kernel::Svc::MemoryState::Kernel},
581 {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode}, 577 {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
582 {"CodeOut ", Kernel::Svc::MemoryState::CodeOut}, 578 {"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
583 {"Coverage ", Kernel::Svc::MemoryState::Coverage}, 579 {"Coverage ", Kernel::Svc::MemoryState::Coverage},
584}}; 580}};
585 581
586static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) { 582static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
@@ -613,7 +609,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
613 std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; 609 std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
614 std::string reply; 610 std::string reply;
615 611
616 auto* process = system.ApplicationProcess(); 612 auto* process = GetProcess();
617 auto& page_table = process->GetPageTable(); 613 auto& page_table = process->GetPageTable();
618 614
619 const char* commands = "Commands:\n" 615 const char* commands = "Commands:\n"
@@ -714,7 +710,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
714} 710}
715 711
716Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { 712Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
717 auto& threads{system.ApplicationProcess()->GetThreadList()}; 713 auto& threads{GetProcess()->GetThreadList()};
718 for (auto& thread : threads) { 714 for (auto& thread : threads) {
719 if (thread.GetThreadId() == thread_id) { 715 if (thread.GetThreadId() == thread_id) {
720 return std::addressof(thread); 716 return std::addressof(thread);
@@ -783,4 +779,12 @@ void GDBStub::SendStatus(char status) {
783 backend.WriteToClient(buf); 779 backend.WriteToClient(buf);
784} 780}
785 781
782Kernel::KProcess* GDBStub::GetProcess() {
783 return debug_process;
784}
785
786Core::Memory::Memory& GDBStub::GetMemory() {
787 return GetProcess()->GetMemory();
788}
789
786} // namespace Core 790} // namespace Core
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
index 368197920..232dcf49f 100644
--- a/src/core/debugger/gdbstub.h
+++ b/src/core/debugger/gdbstub.h
@@ -12,13 +12,22 @@
12#include "core/debugger/debugger_interface.h" 12#include "core/debugger/debugger_interface.h"
13#include "core/debugger/gdbstub_arch.h" 13#include "core/debugger/gdbstub_arch.h"
14 14
15namespace Kernel {
16class KProcess;
17}
18
19namespace Core::Memory {
20class Memory;
21}
22
15namespace Core { 23namespace Core {
16 24
17class System; 25class System;
18 26
19class GDBStub : public DebuggerFrontend { 27class GDBStub : public DebuggerFrontend {
20public: 28public:
21 explicit GDBStub(DebuggerBackend& backend, Core::System& system); 29 explicit GDBStub(DebuggerBackend& backend, Core::System& system,
30 Kernel::KProcess* debug_process);
22 ~GDBStub() override; 31 ~GDBStub() override;
23 32
24 void Connected() override; 33 void Connected() override;
@@ -42,8 +51,12 @@ private:
42 void SendReply(std::string_view data); 51 void SendReply(std::string_view data);
43 void SendStatus(char status); 52 void SendStatus(char status);
44 53
54 Kernel::KProcess* GetProcess();
55 Core::Memory::Memory& GetMemory();
56
45private: 57private:
46 Core::System& system; 58 Core::System& system;
59 Kernel::KProcess* debug_process;
47 std::unique_ptr<GDBStubArch> arch; 60 std::unique_ptr<GDBStubArch> arch;
48 std::vector<char> current_command; 61 std::vector<char> current_command;
49 std::map<VAddr, u32> replaced_instructions; 62 std::map<VAddr, u32> replaced_instructions;
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 12b3bd797..23196cd5f 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -97,8 +97,9 @@ std::string SaveDataAttribute::DebugInfo() const {
97 static_cast<u8>(rank), index); 97 static_cast<u8>(rank), index);
98} 98}
99 99
100SaveDataFactory::SaveDataFactory(Core::System& system_, VirtualDir save_directory_) 100SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_,
101 : dir{std::move(save_directory_)}, system{system_} { 101 VirtualDir save_directory_)
102 : system{system_}, program_id{program_id_}, dir{std::move(save_directory_)} {
102 // Delete all temporary storages 103 // Delete all temporary storages
103 // On hardware, it is expected that temporary storage be empty at first use. 104 // On hardware, it is expected that temporary storage be empty at first use.
104 dir->DeleteSubdirectoryRecursive("temp"); 105 dir->DeleteSubdirectoryRecursive("temp");
@@ -110,7 +111,7 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut
110 PrintSaveDataAttributeWarnings(meta); 111 PrintSaveDataAttributeWarnings(meta);
111 112
112 const auto save_directory = 113 const auto save_directory =
113 GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); 114 GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
114 115
115 return dir->CreateDirectoryRelative(save_directory); 116 return dir->CreateDirectoryRelative(save_directory);
116} 117}
@@ -118,7 +119,7 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut
118VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const { 119VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
119 120
120 const auto save_directory = 121 const auto save_directory =
121 GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); 122 GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
122 123
123 auto out = dir->GetDirectoryRelative(save_directory); 124 auto out = dir->GetDirectoryRelative(save_directory);
124 125
@@ -147,14 +148,14 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
147 } 148 }
148} 149}
149 150
150std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir, 151std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir,
151 SaveDataSpaceId space, SaveDataType type, u64 title_id, 152 SaveDataSpaceId space, SaveDataType type, u64 title_id,
152 u128 user_id, u64 save_id) { 153 u128 user_id, u64 save_id) {
153 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should 154 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
154 // be interpreted as the title id of the current process. 155 // be interpreted as the title id of the current process.
155 if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) { 156 if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) {
156 if (title_id == 0) { 157 if (title_id == 0) {
157 title_id = system.GetApplicationProcessProgramID(); 158 title_id = program_id;
158 } 159 }
159 } 160 }
160 161
@@ -201,7 +202,7 @@ std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future)
201SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, 202SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
202 u128 user_id) const { 203 u128 user_id) const {
203 const auto path = 204 const auto path =
204 GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); 205 GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
205 const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); 206 const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
206 207
207 const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName()); 208 const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName());
@@ -220,7 +221,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
220void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, 221void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
221 SaveDataSize new_value) const { 222 SaveDataSize new_value) const {
222 const auto path = 223 const auto path =
223 GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); 224 GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
224 const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); 225 const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
225 226
226 const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName()); 227 const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName());
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index fd4887e99..30d96928e 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -87,10 +87,13 @@ constexpr const char* GetSaveDataSizeFileName() {
87 return ".yuzu_save_size"; 87 return ".yuzu_save_size";
88} 88}
89 89
90using ProgramId = u64;
91
90/// File system interface to the SaveData archive 92/// File system interface to the SaveData archive
91class SaveDataFactory { 93class SaveDataFactory {
92public: 94public:
93 explicit SaveDataFactory(Core::System& system_, VirtualDir save_directory_); 95 explicit SaveDataFactory(Core::System& system_, ProgramId program_id_,
96 VirtualDir save_directory_);
94 ~SaveDataFactory(); 97 ~SaveDataFactory();
95 98
96 VirtualDir Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const; 99 VirtualDir Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
@@ -99,7 +102,7 @@ public:
99 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; 102 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
100 103
101 static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); 104 static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
102 static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space, 105 static std::string GetFullPath(ProgramId program_id, VirtualDir dir, SaveDataSpaceId space,
103 SaveDataType type, u64 title_id, u128 user_id, u64 save_id); 106 SaveDataType type, u64 title_id, u128 user_id, u64 save_id);
104 static std::string GetUserGameSaveDataRoot(u128 user_id, bool future); 107 static std::string GetUserGameSaveDataRoot(u128 user_id, bool future);
105 108
@@ -110,8 +113,9 @@ public:
110 void SetAutoCreate(bool state); 113 void SetAutoCreate(bool state);
111 114
112private: 115private:
113 VirtualDir dir;
114 Core::System& system; 116 Core::System& system;
117 ProgramId program_id;
118 VirtualDir dir;
115 bool auto_create{true}; 119 bool auto_create{true};
116}; 120};
117 121
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
index 34fe23b6a..e04d884ba 100644
--- a/src/core/frontend/applets/controller.cpp
+++ b/src/core/frontend/applets/controller.cpp
@@ -47,7 +47,7 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
47 // Connect controllers based on the following priority list from highest to lowest priority: 47 // Connect controllers based on the following priority list from highest to lowest priority:
48 // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld 48 // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
49 if (parameters.allow_pro_controller) { 49 if (parameters.allow_pro_controller) {
50 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); 50 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
51 controller->Connect(true); 51 controller->Connect(true);
52 } else if (parameters.allow_dual_joycons) { 52 } else if (parameters.allow_dual_joycons) {
53 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual); 53 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index ef3f61321..d2b7e9a66 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -81,12 +81,12 @@ enum class KMemoryState : u32 {
81 81
82 ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagLinearMapped, 82 ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagLinearMapped,
83 83
84 Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc | 84 Transferred = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
85 FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | 85 FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
86 FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, 86 FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
87 87
88 SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc | 88 SharedTransferred = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
89 FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, 89 FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
90 90
91 SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped | 91 SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
92 FlagReferenceCounted | FlagLinearMapped | FlagCanUseNonSecureIpc | 92 FlagReferenceCounted | FlagLinearMapped | FlagCanUseNonSecureIpc |
@@ -130,8 +130,8 @@ static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x0FFFBD09);
130static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A); 130static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
131static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B); 131static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
132static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400000C); 132static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400000C);
133static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D); 133static_assert(static_cast<u32>(KMemoryState::Transferred) == 0x055C3C0D);
134static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E); 134static_assert(static_cast<u32>(KMemoryState::SharedTransferred) == 0x045C380E);
135static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F); 135static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
136static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010); 136static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
137static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811); 137static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);
diff --git a/src/core/hle/kernel/k_memory_block_manager.cpp b/src/core/hle/kernel/k_memory_block_manager.cpp
index 58a1e7216..f08a6e448 100644
--- a/src/core/hle/kernel/k_memory_block_manager.cpp
+++ b/src/core/hle/kernel/k_memory_block_manager.cpp
@@ -28,14 +28,14 @@ Result KMemoryBlockManager::Initialize(KProcessAddress st, KProcessAddress nd,
28} 28}
29 29
30void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager, 30void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager,
31 HostUnmapCallback&& host_unmap_callback) { 31 BlockCallback&& block_callback) {
32 // Erase every block until we have none left. 32 // Erase every block until we have none left.
33 auto it = m_memory_block_tree.begin(); 33 auto it = m_memory_block_tree.begin();
34 while (it != m_memory_block_tree.end()) { 34 while (it != m_memory_block_tree.end()) {
35 KMemoryBlock* block = std::addressof(*it); 35 KMemoryBlock* block = std::addressof(*it);
36 it = m_memory_block_tree.erase(it); 36 it = m_memory_block_tree.erase(it);
37 block_callback(block->GetAddress(), block->GetSize());
37 slab_manager->Free(block); 38 slab_manager->Free(block);
38 host_unmap_callback(block->GetAddress(), block->GetSize());
39 } 39 }
40 40
41 ASSERT(m_memory_block_tree.empty()); 41 ASSERT(m_memory_block_tree.empty());
diff --git a/src/core/hle/kernel/k_memory_block_manager.h b/src/core/hle/kernel/k_memory_block_manager.h
index cb7b6f430..377628504 100644
--- a/src/core/hle/kernel/k_memory_block_manager.h
+++ b/src/core/hle/kernel/k_memory_block_manager.h
@@ -85,11 +85,11 @@ public:
85public: 85public:
86 KMemoryBlockManager(); 86 KMemoryBlockManager();
87 87
88 using HostUnmapCallback = std::function<void(Common::ProcessAddress, u64)>; 88 using BlockCallback = std::function<void(Common::ProcessAddress, u64)>;
89 89
90 Result Initialize(KProcessAddress st, KProcessAddress nd, 90 Result Initialize(KProcessAddress st, KProcessAddress nd,
91 KMemoryBlockSlabManager* slab_manager); 91 KMemoryBlockSlabManager* slab_manager);
92 void Finalize(KMemoryBlockSlabManager* slab_manager, HostUnmapCallback&& host_unmap_callback); 92 void Finalize(KMemoryBlockSlabManager* slab_manager, BlockCallback&& block_callback);
93 93
94 iterator end() { 94 iterator end() {
95 return m_memory_block_tree.end(); 95 return m_memory_block_tree.end();
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
index 8c1549559..3f0a39d33 100644
--- a/src/core/hle/kernel/k_page_table_base.cpp
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -431,15 +431,43 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
431 m_memory_block_slab_manager)); 431 m_memory_block_slab_manager));
432} 432}
433 433
434Result KPageTableBase::FinalizeProcess() {
435 // Only process tables should be finalized.
436 ASSERT(!this->IsKernel());
437
438 // NOTE: Here Nintendo calls an unknown OnFinalize function.
439 // this->OnFinalize();
440
441 // NOTE: Here Nintendo calls a second unknown OnFinalize function.
442 // this->OnFinalize2();
443
444 // NOTE: Here Nintendo does a page table walk to discover heap pages to free.
445 // We will use the block manager finalization below to free them.
446
447 R_SUCCEED();
448}
449
434void KPageTableBase::Finalize() { 450void KPageTableBase::Finalize() {
435 auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) { 451 this->FinalizeProcess();
436 if (Settings::IsFastmemEnabled()) { 452
453 auto BlockCallback = [&](KProcessAddress addr, u64 size) {
454 if (m_impl->fastmem_arena) {
437 m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false); 455 m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false);
438 } 456 }
457
458 // Get physical pages.
459 KPageGroup pg(m_kernel, m_block_info_manager);
460 this->MakePageGroup(pg, addr, size / PageSize);
461
462 // Free the pages.
463 pg.CloseAndReset();
439 }; 464 };
440 465
441 // Finalize memory blocks. 466 // Finalize memory blocks.
442 m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(HostUnmapCallback)); 467 {
468 KScopedLightLock lk(m_general_lock);
469 m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(BlockCallback));
470 }
443 471
444 // Free any unsafe mapped memory. 472 // Free any unsafe mapped memory.
445 if (m_mapped_unsafe_physical_memory) { 473 if (m_mapped_unsafe_physical_memory) {
@@ -486,8 +514,8 @@ KProcessAddress KPageTableBase::GetRegionAddress(Svc::MemoryState state) const {
486 case Svc::MemoryState::Shared: 514 case Svc::MemoryState::Shared:
487 case Svc::MemoryState::AliasCode: 515 case Svc::MemoryState::AliasCode:
488 case Svc::MemoryState::AliasCodeData: 516 case Svc::MemoryState::AliasCodeData:
489 case Svc::MemoryState::Transfered: 517 case Svc::MemoryState::Transferred:
490 case Svc::MemoryState::SharedTransfered: 518 case Svc::MemoryState::SharedTransferred:
491 case Svc::MemoryState::SharedCode: 519 case Svc::MemoryState::SharedCode:
492 case Svc::MemoryState::GeneratedCode: 520 case Svc::MemoryState::GeneratedCode:
493 case Svc::MemoryState::CodeOut: 521 case Svc::MemoryState::CodeOut:
@@ -522,8 +550,8 @@ size_t KPageTableBase::GetRegionSize(Svc::MemoryState state) const {
522 case Svc::MemoryState::Shared: 550 case Svc::MemoryState::Shared:
523 case Svc::MemoryState::AliasCode: 551 case Svc::MemoryState::AliasCode:
524 case Svc::MemoryState::AliasCodeData: 552 case Svc::MemoryState::AliasCodeData:
525 case Svc::MemoryState::Transfered: 553 case Svc::MemoryState::Transferred:
526 case Svc::MemoryState::SharedTransfered: 554 case Svc::MemoryState::SharedTransferred:
527 case Svc::MemoryState::SharedCode: 555 case Svc::MemoryState::SharedCode:
528 case Svc::MemoryState::GeneratedCode: 556 case Svc::MemoryState::GeneratedCode:
529 case Svc::MemoryState::CodeOut: 557 case Svc::MemoryState::CodeOut:
@@ -564,8 +592,8 @@ bool KPageTableBase::CanContain(KProcessAddress addr, size_t size, Svc::MemorySt
564 case Svc::MemoryState::AliasCodeData: 592 case Svc::MemoryState::AliasCodeData:
565 case Svc::MemoryState::Stack: 593 case Svc::MemoryState::Stack:
566 case Svc::MemoryState::ThreadLocal: 594 case Svc::MemoryState::ThreadLocal:
567 case Svc::MemoryState::Transfered: 595 case Svc::MemoryState::Transferred:
568 case Svc::MemoryState::SharedTransfered: 596 case Svc::MemoryState::SharedTransferred:
569 case Svc::MemoryState::SharedCode: 597 case Svc::MemoryState::SharedCode:
570 case Svc::MemoryState::GeneratedCode: 598 case Svc::MemoryState::GeneratedCode:
571 case Svc::MemoryState::CodeOut: 599 case Svc::MemoryState::CodeOut:
diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h
index 077cafc96..748419f86 100644
--- a/src/core/hle/kernel/k_page_table_base.h
+++ b/src/core/hle/kernel/k_page_table_base.h
@@ -241,6 +241,7 @@ public:
241 KResourceLimit* resource_limit, Core::Memory::Memory& memory, 241 KResourceLimit* resource_limit, Core::Memory::Memory& memory,
242 KProcessAddress aslr_space_start); 242 KProcessAddress aslr_space_start);
243 243
244 Result FinalizeProcess();
244 void Finalize(); 245 void Finalize();
245 246
246 bool IsKernel() const { 247 bool IsKernel() const {
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 068e71dff..53735a225 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -171,6 +171,12 @@ void KProcess::Finalize() {
171 m_resource_limit->Close(); 171 m_resource_limit->Close();
172 } 172 }
173 173
174 // Clear expensive resources, as the destructor is not called for guest objects.
175 for (auto& interface : m_arm_interfaces) {
176 interface.reset();
177 }
178 m_exclusive_monitor.reset();
179
174 // Perform inherited finalization. 180 // Perform inherited finalization.
175 KSynchronizationObject::Finalize(); 181 KSynchronizationObject::Finalize();
176} 182}
@@ -1233,10 +1239,10 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
1233 ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); 1239 ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
1234 1240
1235#ifdef HAS_NCE 1241#ifdef HAS_NCE
1236 if (this->IsApplication() && Settings::IsNceEnabled()) { 1242 const auto& patch = code_set.PatchSegment();
1243 if (this->IsApplication() && Settings::IsNceEnabled() && patch.size != 0) {
1237 auto& buffer = m_kernel.System().DeviceMemory().buffer; 1244 auto& buffer = m_kernel.System().DeviceMemory().buffer;
1238 const auto& code = code_set.CodeSegment(); 1245 const auto& code = code_set.CodeSegment();
1239 const auto& patch = code_set.PatchSegment();
1240 buffer.Protect(GetInteger(base_addr + code.addr), code.size, 1246 buffer.Protect(GetInteger(base_addr + code.addr), code.size,
1241 Common::MemoryPermission::Read | Common::MemoryPermission::Execute); 1247 Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
1242 buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, 1248 buffer.Protect(GetInteger(base_addr + patch.addr), patch.size,
diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp
index 0e2e11743..cbb1b02bb 100644
--- a/src/core/hle/kernel/k_transfer_memory.cpp
+++ b/src/core/hle/kernel/k_transfer_memory.cpp
@@ -76,8 +76,8 @@ Result KTransferMemory::Map(KProcessAddress address, size_t size, Svc::MemoryPer
76 76
77 // Map the memory. 77 // Map the memory.
78 const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None) 78 const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None)
79 ? KMemoryState::Transfered 79 ? KMemoryState::Transferred
80 : KMemoryState::SharedTransfered; 80 : KMemoryState::SharedTransferred;
81 R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup( 81 R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup(
82 address, *m_page_group, state, KMemoryPermission::UserReadWrite)); 82 address, *m_page_group, state, KMemoryPermission::UserReadWrite));
83 83
@@ -96,8 +96,8 @@ Result KTransferMemory::Unmap(KProcessAddress address, size_t size) {
96 96
97 // Unmap the memory. 97 // Unmap the memory.
98 const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None) 98 const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None)
99 ? KMemoryState::Transfered 99 ? KMemoryState::Transferred
100 : KMemoryState::SharedTransfered; 100 : KMemoryState::SharedTransferred;
101 R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, state)); 101 R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, state));
102 102
103 // Mark ourselves as unmapped. 103 // Mark ourselves as unmapped.
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 1030f0c12..f3683cdcc 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -112,7 +112,14 @@ struct KernelCore::Impl {
112 old_process->Close(); 112 old_process->Close();
113 } 113 }
114 114
115 process_list.clear(); 115 {
116 std::scoped_lock lk{process_list_lock};
117 for (auto* const process : process_list) {
118 process->Terminate();
119 process->Close();
120 }
121 process_list.clear();
122 }
116 123
117 next_object_id = 0; 124 next_object_id = 0;
118 next_kernel_process_id = KProcess::InitialProcessIdMin; 125 next_kernel_process_id = KProcess::InitialProcessIdMin;
@@ -770,6 +777,7 @@ struct KernelCore::Impl {
770 std::atomic<u64> next_thread_id{1}; 777 std::atomic<u64> next_thread_id{1};
771 778
772 // Lists all processes that exist in the current session. 779 // Lists all processes that exist in the current session.
780 std::mutex process_list_lock;
773 std::vector<KProcess*> process_list; 781 std::vector<KProcess*> process_list;
774 std::atomic<KProcess*> application_process{}; 782 std::atomic<KProcess*> application_process{};
775 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; 783 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
@@ -869,9 +877,19 @@ KResourceLimit* KernelCore::GetSystemResourceLimit() {
869} 877}
870 878
871void KernelCore::AppendNewProcess(KProcess* process) { 879void KernelCore::AppendNewProcess(KProcess* process) {
880 process->Open();
881
882 std::scoped_lock lk{impl->process_list_lock};
872 impl->process_list.push_back(process); 883 impl->process_list.push_back(process);
873} 884}
874 885
886void KernelCore::RemoveProcess(KProcess* process) {
887 std::scoped_lock lk{impl->process_list_lock};
888 if (std::erase(impl->process_list, process)) {
889 process->Close();
890 }
891}
892
875void KernelCore::MakeApplicationProcess(KProcess* process) { 893void KernelCore::MakeApplicationProcess(KProcess* process) {
876 impl->MakeApplicationProcess(process); 894 impl->MakeApplicationProcess(process);
877} 895}
@@ -884,8 +902,15 @@ const KProcess* KernelCore::ApplicationProcess() const {
884 return impl->application_process; 902 return impl->application_process;
885} 903}
886 904
887const std::vector<KProcess*>& KernelCore::GetProcessList() const { 905std::list<KScopedAutoObject<KProcess>> KernelCore::GetProcessList() {
888 return impl->process_list; 906 std::list<KScopedAutoObject<KProcess>> processes;
907 std::scoped_lock lk{impl->process_list_lock};
908
909 for (auto* const process : impl->process_list) {
910 processes.emplace_back(process);
911 }
912
913 return processes;
889} 914}
890 915
891Kernel::GlobalSchedulerContext& KernelCore::GlobalSchedulerContext() { 916Kernel::GlobalSchedulerContext& KernelCore::GlobalSchedulerContext() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 5d4102145..8ea5bed1c 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7#include <functional> 7#include <functional>
8#include <list>
8#include <memory> 9#include <memory>
9#include <string> 10#include <string>
10#include <unordered_map> 11#include <unordered_map>
@@ -116,8 +117,9 @@ public:
116 /// Retrieves a shared pointer to the system resource limit instance. 117 /// Retrieves a shared pointer to the system resource limit instance.
117 KResourceLimit* GetSystemResourceLimit(); 118 KResourceLimit* GetSystemResourceLimit();
118 119
119 /// Adds the given shared pointer to an internal list of active processes. 120 /// Adds/removes the given pointer to an internal list of active processes.
120 void AppendNewProcess(KProcess* process); 121 void AppendNewProcess(KProcess* process);
122 void RemoveProcess(KProcess* process);
121 123
122 /// Makes the given process the new application process. 124 /// Makes the given process the new application process.
123 void MakeApplicationProcess(KProcess* process); 125 void MakeApplicationProcess(KProcess* process);
@@ -129,7 +131,7 @@ public:
129 const KProcess* ApplicationProcess() const; 131 const KProcess* ApplicationProcess() const;
130 132
131 /// Retrieves the list of processes. 133 /// Retrieves the list of processes.
132 const std::vector<KProcess*>& GetProcessList() const; 134 std::list<KScopedAutoObject<KProcess>> GetProcessList();
133 135
134 /// Gets the sole instance of the global scheduler 136 /// Gets the sole instance of the global scheduler
135 Kernel::GlobalSchedulerContext& GlobalSchedulerContext(); 137 Kernel::GlobalSchedulerContext& GlobalSchedulerContext();
diff --git a/src/core/hle/kernel/svc/svc_process.cpp b/src/core/hle/kernel/svc/svc_process.cpp
index caa8bee9a..5c3e8829f 100644
--- a/src/core/hle/kernel/svc/svc_process.cpp
+++ b/src/core/hle/kernel/svc/svc_process.cpp
@@ -74,13 +74,15 @@ Result GetProcessList(Core::System& system, s32* out_num_processes, u64 out_proc
74 } 74 }
75 75
76 auto& memory = GetCurrentMemory(kernel); 76 auto& memory = GetCurrentMemory(kernel);
77 const auto& process_list = kernel.GetProcessList(); 77 auto process_list = kernel.GetProcessList();
78 auto it = process_list.begin();
79
78 const auto num_processes = process_list.size(); 80 const auto num_processes = process_list.size();
79 const auto copy_amount = 81 const auto copy_amount =
80 std::min(static_cast<std::size_t>(out_process_ids_size), num_processes); 82 std::min(static_cast<std::size_t>(out_process_ids_size), num_processes);
81 83
82 for (std::size_t i = 0; i < copy_amount; ++i) { 84 for (std::size_t i = 0; i < copy_amount && it != process_list.end(); ++i, ++it) {
83 memory.Write64(out_process_ids, process_list[i]->GetProcessId()); 85 memory.Write64(out_process_ids, (*it)->GetProcessId());
84 out_process_ids += sizeof(u64); 86 out_process_ids += sizeof(u64);
85 } 87 }
86 88
diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
index 1f97121b3..671bca23f 100644
--- a/src/core/hle/kernel/svc/svc_transfer_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
@@ -90,7 +90,7 @@ Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t add
90 // Verify that the mapping is in range. 90 // Verify that the mapping is in range.
91 R_UNLESS(GetCurrentProcess(system.Kernel()) 91 R_UNLESS(GetCurrentProcess(system.Kernel())
92 .GetPageTable() 92 .GetPageTable()
93 .CanContain(address, size, KMemoryState::Transfered), 93 .CanContain(address, size, KMemoryState::Transferred),
94 ResultInvalidMemoryRegion); 94 ResultInvalidMemoryRegion);
95 95
96 // Map the transfer memory. 96 // Map the transfer memory.
@@ -117,7 +117,7 @@ Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t a
117 // Verify that the mapping is in range. 117 // Verify that the mapping is in range.
118 R_UNLESS(GetCurrentProcess(system.Kernel()) 118 R_UNLESS(GetCurrentProcess(system.Kernel())
119 .GetPageTable() 119 .GetPageTable()
120 .CanContain(address, size, KMemoryState::Transfered), 120 .CanContain(address, size, KMemoryState::Transferred),
121 ResultInvalidMemoryRegion); 121 ResultInvalidMemoryRegion);
122 122
123 // Unmap the transfer memory. 123 // Unmap the transfer memory.
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 50de02e36..ab432ea78 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -27,8 +27,8 @@ enum class MemoryState : u32 {
27 Ipc = 0x0A, 27 Ipc = 0x0A,
28 Stack = 0x0B, 28 Stack = 0x0B,
29 ThreadLocal = 0x0C, 29 ThreadLocal = 0x0C,
30 Transfered = 0x0D, 30 Transferred = 0x0D,
31 SharedTransfered = 0x0E, 31 SharedTransferred = 0x0E,
32 SharedCode = 0x0F, 32 SharedCode = 0x0F,
33 Inaccessible = 0x10, 33 Inaccessible = 0x10,
34 NonSecureIpc = 0x11, 34 NonSecureIpc = 0x11,
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 5542d6cbc..683f44e27 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -61,9 +61,7 @@ ProfileManager::ProfileManager() {
61 OpenUser(*GetUser(current)); 61 OpenUser(*GetUser(current));
62} 62}
63 63
64ProfileManager::~ProfileManager() { 64ProfileManager::~ProfileManager() = default;
65 WriteUserSaveFile();
66}
67 65
68/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the 66/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
69/// internal management of the users profiles 67/// internal management of the users profiles
@@ -113,6 +111,8 @@ Result ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& username)
113 return ERROR_USER_ALREADY_EXISTS; 111 return ERROR_USER_ALREADY_EXISTS;
114 } 112 }
115 113
114 is_save_needed = true;
115
116 return AddUser({ 116 return AddUser({
117 .user_uuid = uuid, 117 .user_uuid = uuid,
118 .username = username, 118 .username = username,
@@ -326,6 +326,9 @@ bool ProfileManager::RemoveUser(UUID uuid) {
326 profiles[*index] = ProfileInfo{}; 326 profiles[*index] = ProfileInfo{};
327 std::stable_partition(profiles.begin(), profiles.end(), 327 std::stable_partition(profiles.begin(), profiles.end(),
328 [](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); }); 328 [](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); });
329
330 is_save_needed = true;
331
329 return true; 332 return true;
330} 333}
331 334
@@ -340,6 +343,8 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
340 profile.username = profile_new.username; 343 profile.username = profile_new.username;
341 profile.creation_time = profile_new.timestamp; 344 profile.creation_time = profile_new.timestamp;
342 345
346 is_save_needed = true;
347
343 return true; 348 return true;
344} 349}
345 350
@@ -348,6 +353,7 @@ bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase&
348 const auto index = GetUserIndex(uuid); 353 const auto index = GetUserIndex(uuid);
349 if (index.has_value() && SetProfileBase(uuid, profile_new)) { 354 if (index.has_value() && SetProfileBase(uuid, profile_new)) {
350 profiles[*index].data = data_new; 355 profiles[*index].data = data_new;
356 is_save_needed = true;
351 return true; 357 return true;
352 } 358 }
353 359
@@ -391,6 +397,10 @@ void ProfileManager::ParseUserSaveFile() {
391} 397}
392 398
393void ProfileManager::WriteUserSaveFile() { 399void ProfileManager::WriteUserSaveFile() {
400 if (!is_save_needed) {
401 return;
402 }
403
394 ProfileDataRaw raw{}; 404 ProfileDataRaw raw{};
395 405
396 for (std::size_t i = 0; i < MAX_USERS; ++i) { 406 for (std::size_t i = 0; i < MAX_USERS; ++i) {
@@ -423,7 +433,10 @@ void ProfileManager::WriteUserSaveFile() {
423 if (!save.IsOpen() || !save.SetSize(sizeof(ProfileDataRaw)) || !save.WriteObject(raw)) { 433 if (!save.IsOpen() || !save.SetSize(sizeof(ProfileDataRaw)) || !save.WriteObject(raw)) {
424 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " 434 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
425 "made in current session will be saved."); 435 "made in current session will be saved.");
436 return;
426 } 437 }
438
439 is_save_needed = false;
427} 440}
428 441
429}; // namespace Service::Account 442}; // namespace Service::Account
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 900e32200..e21863e64 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -103,6 +103,7 @@ private:
103 std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); 103 std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
104 bool RemoveProfileAtIndex(std::size_t index); 104 bool RemoveProfileAtIndex(std::size_t index);
105 105
106 bool is_save_needed{};
106 std::array<ProfileInfo, MAX_USERS> profiles{}; 107 std::array<ProfileInfo, MAX_USERS> profiles{};
107 std::array<ProfileInfo, MAX_USERS> stored_opened_profiles{}; 108 std::array<ProfileInfo, MAX_USERS> stored_opened_profiles{};
108 std::size_t user_count{}; 109 std::size_t user_count{};
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 9e05bdafa..a768bdc54 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -36,6 +36,7 @@
36#include "core/hle/service/caps/caps_su.h" 36#include "core/hle/service/caps/caps_su.h"
37#include "core/hle/service/caps/caps_types.h" 37#include "core/hle/service/caps/caps_types.h"
38#include "core/hle/service/filesystem/filesystem.h" 38#include "core/hle/service/filesystem/filesystem.h"
39#include "core/hle/service/filesystem/save_data_controller.h"
39#include "core/hle/service/ipc_helpers.h" 40#include "core/hle/service/ipc_helpers.h"
40#include "core/hle/service/ns/ns.h" 41#include "core/hle/service/ns/ns.h"
41#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" 42#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
@@ -2178,7 +2179,7 @@ void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) {
2178 attribute.type = FileSys::SaveDataType::SaveData; 2179 attribute.type = FileSys::SaveDataType::SaveData;
2179 2180
2180 FileSys::VirtualDir save_data{}; 2181 FileSys::VirtualDir save_data{};
2181 const auto res = system.GetFileSystemController().CreateSaveData( 2182 const auto res = system.GetFileSystemController().OpenSaveDataController()->CreateSaveData(
2182 &save_data, FileSys::SaveDataSpaceId::NandUser, attribute); 2183 &save_data, FileSys::SaveDataSpaceId::NandUser, attribute);
2183 2184
2184 IPC::ResponseBuilder rb{ctx, 4}; 2185 IPC::ResponseBuilder rb{ctx, 4};
@@ -2353,7 +2354,7 @@ void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) {
2353 "new_journal={:016X}", 2354 "new_journal={:016X}",
2354 static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size); 2355 static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
2355 2356
2356 system.GetFileSystemController().WriteSaveDataSize( 2357 system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
2357 type, system.GetApplicationProcessProgramID(), user_id, 2358 type, system.GetApplicationProcessProgramID(), user_id,
2358 {new_normal_size, new_journal_size}); 2359 {new_normal_size, new_journal_size});
2359 2360
@@ -2378,7 +2379,7 @@ void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) {
2378 LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1], 2379 LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1],
2379 user_id[0]); 2380 user_id[0]);
2380 2381
2381 const auto size = system.GetFileSystemController().ReadSaveDataSize( 2382 const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
2382 type, system.GetApplicationProcessProgramID(), user_id); 2383 type, system.GetApplicationProcessProgramID(), user_id);
2383 2384
2384 IPC::ResponseBuilder rb{ctx, 6}; 2385 IPC::ResponseBuilder rb{ctx, 6};
diff --git a/src/core/hle/service/am/applets/applet_profile_select.h b/src/core/hle/service/am/applets/applet_profile_select.h
index 369f9250f..673eed516 100644
--- a/src/core/hle/service/am/applets/applet_profile_select.h
+++ b/src/core/hle/service/am/applets/applet_profile_select.h
@@ -76,7 +76,7 @@ struct UiSettingsDisplayOptions {
76 bool is_system_or_launcher; 76 bool is_system_or_launcher;
77 bool is_registration_permitted; 77 bool is_registration_permitted;
78 bool show_skip_button; 78 bool show_skip_button;
79 bool aditional_select; 79 bool additional_select;
80 bool show_user_selector; 80 bool show_user_selector;
81 bool is_unqualified_user_selectable; 81 bool is_unqualified_user_selectable;
82}; 82};
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 56fee4591..de2aa6906 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -18,11 +18,11 @@ using namespace AudioCore::AudioIn;
18class IAudioIn final : public ServiceFramework<IAudioIn> { 18class IAudioIn final : public ServiceFramework<IAudioIn> {
19public: 19public:
20 explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, 20 explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
21 const std::string& device_name, const AudioInParameter& in_params, u32 handle, 21 const std::string& device_name, const AudioInParameter& in_params,
22 u64 applet_resource_user_id) 22 Kernel::KProcess* handle, u64 applet_resource_user_id)
23 : ServiceFramework{system_, "IAudioIn"}, 23 : ServiceFramework{system_, "IAudioIn"},
24 service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, 24 service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")},
25 impl{std::make_shared<In>(system_, manager, event, session_id)} { 25 process{handle}, impl{std::make_shared<In>(system_, manager, event, session_id)} {
26 // clang-format off 26 // clang-format off
27 static const FunctionInfo functions[] = { 27 static const FunctionInfo functions[] = {
28 {0, &IAudioIn::GetAudioInState, "GetAudioInState"}, 28 {0, &IAudioIn::GetAudioInState, "GetAudioInState"},
@@ -45,6 +45,8 @@ public:
45 45
46 RegisterHandlers(functions); 46 RegisterHandlers(functions);
47 47
48 process->Open();
49
48 if (impl->GetSystem() 50 if (impl->GetSystem()
49 .Initialize(device_name, in_params, handle, applet_resource_user_id) 51 .Initialize(device_name, in_params, handle, applet_resource_user_id)
50 .IsError()) { 52 .IsError()) {
@@ -55,6 +57,7 @@ public:
55 ~IAudioIn() override { 57 ~IAudioIn() override {
56 impl->Free(); 58 impl->Free();
57 service_context.CloseEvent(event); 59 service_context.CloseEvent(event);
60 process->Close();
58 } 61 }
59 62
60 [[nodiscard]] std::shared_ptr<In> GetImpl() { 63 [[nodiscard]] std::shared_ptr<In> GetImpl() {
@@ -196,6 +199,7 @@ private:
196 199
197 KernelHelpers::ServiceContext service_context; 200 KernelHelpers::ServiceContext service_context;
198 Kernel::KEvent* event; 201 Kernel::KEvent* event;
202 Kernel::KProcess* process;
199 std::shared_ptr<AudioCore::AudioIn::In> impl; 203 std::shared_ptr<AudioCore::AudioIn::In> impl;
200 Common::ScratchBuffer<u64> released_buffer; 204 Common::ScratchBuffer<u64> released_buffer;
201}; 205};
@@ -267,6 +271,14 @@ void AudInU::OpenAudioIn(HLERequestContext& ctx) {
267 auto device_name = Common::StringFromBuffer(device_name_data); 271 auto device_name = Common::StringFromBuffer(device_name_data);
268 auto handle{ctx.GetCopyHandle(0)}; 272 auto handle{ctx.GetCopyHandle(0)};
269 273
274 auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(handle)};
275 if (process.IsNull()) {
276 LOG_ERROR(Service_Audio, "Failed to get process handle");
277 IPC::ResponseBuilder rb{ctx, 2};
278 rb.Push(ResultUnknown);
279 return;
280 }
281
270 std::scoped_lock l{impl->mutex}; 282 std::scoped_lock l{impl->mutex};
271 auto link{impl->LinkToManager()}; 283 auto link{impl->LinkToManager()};
272 if (link.IsError()) { 284 if (link.IsError()) {
@@ -287,8 +299,9 @@ void AudInU::OpenAudioIn(HLERequestContext& ctx) {
287 LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, 299 LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
288 impl->num_free_sessions); 300 impl->num_free_sessions);
289 301
290 auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, 302 auto audio_in =
291 in_params, handle, applet_resource_user_id); 303 std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, in_params,
304 process.GetPointerUnsafe(), applet_resource_user_id);
292 impl->sessions[new_session_id] = audio_in->GetImpl(); 305 impl->sessions[new_session_id] = audio_in->GetImpl();
293 impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; 306 impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
294 307
@@ -318,6 +331,14 @@ void AudInU::OpenAudioInProtocolSpecified(HLERequestContext& ctx) {
318 auto device_name = Common::StringFromBuffer(device_name_data); 331 auto device_name = Common::StringFromBuffer(device_name_data);
319 auto handle{ctx.GetCopyHandle(0)}; 332 auto handle{ctx.GetCopyHandle(0)};
320 333
334 auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(handle)};
335 if (process.IsNull()) {
336 LOG_ERROR(Service_Audio, "Failed to get process handle");
337 IPC::ResponseBuilder rb{ctx, 2};
338 rb.Push(ResultUnknown);
339 return;
340 }
341
321 std::scoped_lock l{impl->mutex}; 342 std::scoped_lock l{impl->mutex};
322 auto link{impl->LinkToManager()}; 343 auto link{impl->LinkToManager()};
323 if (link.IsError()) { 344 if (link.IsError()) {
@@ -338,8 +359,9 @@ void AudInU::OpenAudioInProtocolSpecified(HLERequestContext& ctx) {
338 LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, 359 LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
339 impl->num_free_sessions); 360 impl->num_free_sessions);
340 361
341 auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, 362 auto audio_in =
342 in_params, handle, applet_resource_user_id); 363 std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, in_params,
364 process.GetPointerUnsafe(), applet_resource_user_id);
343 impl->sessions[new_session_id] = audio_in->GetImpl(); 365 impl->sessions[new_session_id] = audio_in->GetImpl();
344 impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; 366 impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
345 367
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index ca683d72c..8cc7b69f4 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -26,9 +26,10 @@ class IAudioOut final : public ServiceFramework<IAudioOut> {
26public: 26public:
27 explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, 27 explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
28 size_t session_id, const std::string& device_name, 28 size_t session_id, const std::string& device_name,
29 const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) 29 const AudioOutParameter& in_params, Kernel::KProcess* handle,
30 u64 applet_resource_user_id)
30 : ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"}, 31 : ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"},
31 event{service_context.CreateEvent("AudioOutEvent")}, 32 event{service_context.CreateEvent("AudioOutEvent")}, process{handle},
32 impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { 33 impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} {
33 34
34 // clang-format off 35 // clang-format off
@@ -50,11 +51,14 @@ public:
50 }; 51 };
51 // clang-format on 52 // clang-format on
52 RegisterHandlers(functions); 53 RegisterHandlers(functions);
54
55 process->Open();
53 } 56 }
54 57
55 ~IAudioOut() override { 58 ~IAudioOut() override {
56 impl->Free(); 59 impl->Free();
57 service_context.CloseEvent(event); 60 service_context.CloseEvent(event);
61 process->Close();
58 } 62 }
59 63
60 [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() { 64 [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() {
@@ -206,6 +210,7 @@ private:
206 210
207 KernelHelpers::ServiceContext service_context; 211 KernelHelpers::ServiceContext service_context;
208 Kernel::KEvent* event; 212 Kernel::KEvent* event;
213 Kernel::KProcess* process;
209 std::shared_ptr<AudioCore::AudioOut::Out> impl; 214 std::shared_ptr<AudioCore::AudioOut::Out> impl;
210 Common::ScratchBuffer<u64> released_buffer; 215 Common::ScratchBuffer<u64> released_buffer;
211}; 216};
@@ -257,6 +262,14 @@ void AudOutU::OpenAudioOut(HLERequestContext& ctx) {
257 auto device_name = Common::StringFromBuffer(device_name_data); 262 auto device_name = Common::StringFromBuffer(device_name_data);
258 auto handle{ctx.GetCopyHandle(0)}; 263 auto handle{ctx.GetCopyHandle(0)};
259 264
265 auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(handle)};
266 if (process.IsNull()) {
267 LOG_ERROR(Service_Audio, "Failed to get process handle");
268 IPC::ResponseBuilder rb{ctx, 2};
269 rb.Push(ResultUnknown);
270 return;
271 }
272
260 auto link{impl->LinkToManager()}; 273 auto link{impl->LinkToManager()};
261 if (link.IsError()) { 274 if (link.IsError()) {
262 LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager"); 275 LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager");
@@ -276,10 +289,11 @@ void AudOutU::OpenAudioOut(HLERequestContext& ctx) {
276 LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id, 289 LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
277 impl->num_free_sessions); 290 impl->num_free_sessions);
278 291
279 auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name, 292 auto audio_out =
280 in_params, handle, applet_resource_user_id); 293 std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name, in_params,
281 result = audio_out->GetImpl()->GetSystem().Initialize(device_name, in_params, handle, 294 process.GetPointerUnsafe(), applet_resource_user_id);
282 applet_resource_user_id); 295 result = audio_out->GetImpl()->GetSystem().Initialize(
296 device_name, in_params, process.GetPointerUnsafe(), applet_resource_user_id);
283 if (result.IsError()) { 297 if (result.IsError()) {
284 LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!"); 298 LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!");
285 IPC::ResponseBuilder rb{ctx, 2}; 299 IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp
index 96b225d5f..261fc204c 100644
--- a/src/core/hle/service/caps/caps_manager.cpp
+++ b/src/core/hle/service/caps/caps_manager.cpp
@@ -85,7 +85,7 @@ Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, Albu
85} 85}
86 86
87Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, 87Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries,
88 ContentType contex_type, s64 start_posix_time, 88 ContentType content_type, s64 start_posix_time,
89 s64 end_posix_time, u64 aruid) const { 89 s64 end_posix_time, u64 aruid) const {
90 if (!is_mounted) { 90 if (!is_mounted) {
91 return ResultIsNotMounted; 91 return ResultIsNotMounted;
@@ -94,7 +94,7 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& ou
94 std::vector<ApplicationAlbumEntry> album_entries; 94 std::vector<ApplicationAlbumEntry> album_entries;
95 const auto start_date = ConvertToAlbumDateTime(start_posix_time); 95 const auto start_date = ConvertToAlbumDateTime(start_posix_time);
96 const auto end_date = ConvertToAlbumDateTime(end_posix_time); 96 const auto end_date = ConvertToAlbumDateTime(end_posix_time);
97 const auto result = GetAlbumFileList(album_entries, contex_type, start_date, end_date, aruid); 97 const auto result = GetAlbumFileList(album_entries, content_type, start_date, end_date, aruid);
98 98
99 if (result.IsError()) { 99 if (result.IsError()) {
100 return result; 100 return result;
@@ -113,14 +113,14 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& ou
113} 113}
114 114
115Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, 115Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries,
116 ContentType contex_type, AlbumFileDateTime start_date, 116 ContentType content_type, AlbumFileDateTime start_date,
117 AlbumFileDateTime end_date, u64 aruid) const { 117 AlbumFileDateTime end_date, u64 aruid) const {
118 if (!is_mounted) { 118 if (!is_mounted) {
119 return ResultIsNotMounted; 119 return ResultIsNotMounted;
120 } 120 }
121 121
122 for (auto& [file_id, path] : album_files) { 122 for (auto& [file_id, path] : album_files) {
123 if (file_id.type != contex_type) { 123 if (file_id.type != content_type) {
124 continue; 124 continue;
125 } 125 }
126 if (file_id.date > start_date) { 126 if (file_id.date > start_date) {
@@ -139,7 +139,7 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_en
139 .hash{}, 139 .hash{},
140 .datetime = file_id.date, 140 .datetime = file_id.date,
141 .storage = file_id.storage, 141 .storage = file_id.storage,
142 .content = contex_type, 142 .content = content_type,
143 .unknown = 1, 143 .unknown = 1,
144 }; 144 };
145 out_entries.push_back(entry); 145 out_entries.push_back(entry);
diff --git a/src/core/hle/service/caps/caps_manager.h b/src/core/hle/service/caps/caps_manager.h
index e20c70c7b..6fd34f589 100644
--- a/src/core/hle/service/caps/caps_manager.h
+++ b/src/core/hle/service/caps/caps_manager.h
@@ -45,10 +45,10 @@ public:
45 Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage, 45 Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage,
46 u8 flags) const; 46 u8 flags) const;
47 Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, 47 Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries,
48 ContentType contex_type, s64 start_posix_time, s64 end_posix_time, 48 ContentType content_type, s64 start_posix_time, s64 end_posix_time,
49 u64 aruid) const; 49 u64 aruid) const;
50 Result GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, 50 Result GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries,
51 ContentType contex_type, AlbumFileDateTime start_date, 51 ContentType content_type, AlbumFileDateTime start_date,
52 AlbumFileDateTime end_date, u64 aruid) const; 52 AlbumFileDateTime end_date, u64 aruid) const;
53 Result GetAutoSavingStorage(bool& out_is_autosaving) const; 53 Result GetAutoSavingStorage(bool& out_is_autosaving) const;
54 Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output, 54 Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output,
diff --git a/src/core/hle/service/caps/caps_result.h b/src/core/hle/service/caps/caps_result.h
index c65e5fb9a..179ae4840 100644
--- a/src/core/hle/service/caps/caps_result.h
+++ b/src/core/hle/service/caps/caps_result.h
@@ -12,7 +12,7 @@ constexpr Result ResultUnknown5(ErrorModule::Capture, 5);
12constexpr Result ResultUnknown6(ErrorModule::Capture, 6); 12constexpr Result ResultUnknown6(ErrorModule::Capture, 6);
13constexpr Result ResultUnknown7(ErrorModule::Capture, 7); 13constexpr Result ResultUnknown7(ErrorModule::Capture, 7);
14constexpr Result ResultOutOfRange(ErrorModule::Capture, 8); 14constexpr Result ResultOutOfRange(ErrorModule::Capture, 8);
15constexpr Result ResulInvalidTimestamp(ErrorModule::Capture, 12); 15constexpr Result ResultInvalidTimestamp(ErrorModule::Capture, 12);
16constexpr Result ResultInvalidStorage(ErrorModule::Capture, 13); 16constexpr Result ResultInvalidStorage(ErrorModule::Capture, 13);
17constexpr Result ResultInvalidFileContents(ErrorModule::Capture, 14); 17constexpr Result ResultInvalidFileContents(ErrorModule::Capture, 14);
18constexpr Result ResultIsNotMounted(ErrorModule::Capture, 21); 18constexpr Result ResultIsNotMounted(ErrorModule::Capture, 21);
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 780f8c74d..ca6d8d607 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -24,15 +24,13 @@
24#include "core/hle/service/filesystem/fsp_ldr.h" 24#include "core/hle/service/filesystem/fsp_ldr.h"
25#include "core/hle/service/filesystem/fsp_pr.h" 25#include "core/hle/service/filesystem/fsp_pr.h"
26#include "core/hle/service/filesystem/fsp_srv.h" 26#include "core/hle/service/filesystem/fsp_srv.h"
27#include "core/hle/service/filesystem/romfs_controller.h"
28#include "core/hle/service/filesystem/save_data_controller.h"
27#include "core/hle/service/server_manager.h" 29#include "core/hle/service/server_manager.h"
28#include "core/loader/loader.h" 30#include "core/loader/loader.h"
29 31
30namespace Service::FileSystem { 32namespace Service::FileSystem {
31 33
32// A default size for normal/journal save data size if application control metadata cannot be found.
33// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
34constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
35
36static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, 34static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
37 std::string_view dir_name_) { 35 std::string_view dir_name_) {
38 std::string dir_name(Common::FS::SanitizePath(dir_name_)); 36 std::string dir_name(Common::FS::SanitizePath(dir_name_));
@@ -297,145 +295,65 @@ FileSystemController::FileSystemController(Core::System& system_) : system{syste
297 295
298FileSystemController::~FileSystemController() = default; 296FileSystemController::~FileSystemController() = default;
299 297
300Result FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { 298Result FileSystemController::RegisterProcess(
301 romfs_factory = std::move(factory); 299 ProcessId process_id, ProgramId program_id,
302 LOG_DEBUG(Service_FS, "Registered RomFS"); 300 std::shared_ptr<FileSys::RomFSFactory>&& romfs_factory) {
303 return ResultSuccess; 301 std::scoped_lock lk{registration_lock};
304}
305
306Result FileSystemController::RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
307 ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data");
308 save_data_factory = std::move(factory);
309 LOG_DEBUG(Service_FS, "Registered save data");
310 return ResultSuccess;
311}
312 302
313Result FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { 303 registrations.emplace(process_id, Registration{
314 ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC"); 304 .program_id = program_id,
315 sdmc_factory = std::move(factory); 305 .romfs_factory = std::move(romfs_factory),
316 LOG_DEBUG(Service_FS, "Registered SDMC"); 306 .save_data_factory = CreateSaveDataFactory(program_id),
317 return ResultSuccess; 307 });
318}
319 308
320Result FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { 309 LOG_DEBUG(Service_FS, "Registered for process {}", process_id);
321 ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS");
322 bis_factory = std::move(factory);
323 LOG_DEBUG(Service_FS, "Registered BIS");
324 return ResultSuccess; 310 return ResultSuccess;
325} 311}
326 312
327void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) { 313Result FileSystemController::OpenProcess(
328 LOG_TRACE(Service_FS, "Setting packed update for romfs"); 314 ProgramId* out_program_id, std::shared_ptr<SaveDataController>* out_save_data_controller,
329 315 std::shared_ptr<RomFsController>* out_romfs_controller, ProcessId process_id) {
330 if (romfs_factory == nullptr) 316 std::scoped_lock lk{registration_lock};
331 return;
332
333 romfs_factory->SetPackedUpdate(std::move(update_raw));
334}
335
336FileSys::VirtualFile FileSystemController::OpenRomFSCurrentProcess() const {
337 LOG_TRACE(Service_FS, "Opening RomFS for current process");
338
339 if (romfs_factory == nullptr) {
340 return nullptr;
341 }
342
343 return romfs_factory->OpenCurrentProcess(system.GetApplicationProcessProgramID());
344}
345
346FileSys::VirtualFile FileSystemController::OpenPatchedRomFS(u64 title_id,
347 FileSys::ContentRecordType type) const {
348 LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}", title_id);
349
350 if (romfs_factory == nullptr) {
351 return nullptr;
352 }
353
354 return romfs_factory->OpenPatchedRomFS(title_id, type);
355}
356
357FileSys::VirtualFile FileSystemController::OpenPatchedRomFSWithProgramIndex(
358 u64 title_id, u8 program_index, FileSys::ContentRecordType type) const {
359 LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}, program_index={}", title_id,
360 program_index);
361
362 if (romfs_factory == nullptr) {
363 return nullptr;
364 }
365
366 return romfs_factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type);
367}
368
369FileSys::VirtualFile FileSystemController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
370 FileSys::ContentRecordType type) const {
371 LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}",
372 title_id, storage_id, type);
373
374 if (romfs_factory == nullptr) {
375 return nullptr;
376 }
377
378 return romfs_factory->Open(title_id, storage_id, type);
379}
380
381std::shared_ptr<FileSys::NCA> FileSystemController::OpenBaseNca(
382 u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const {
383 return romfs_factory->GetEntry(title_id, storage_id, type);
384}
385
386Result FileSystemController::CreateSaveData(FileSys::VirtualDir* out_save_data,
387 FileSys::SaveDataSpaceId space,
388 const FileSys::SaveDataAttribute& save_struct) const {
389 LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space,
390 save_struct.DebugInfo());
391 317
392 if (save_data_factory == nullptr) { 318 const auto it = registrations.find(process_id);
319 if (it == registrations.end()) {
393 return FileSys::ERROR_ENTITY_NOT_FOUND; 320 return FileSys::ERROR_ENTITY_NOT_FOUND;
394 } 321 }
395 322
396 auto save_data = save_data_factory->Create(space, save_struct); 323 *out_program_id = it->second.program_id;
397 if (save_data == nullptr) { 324 *out_save_data_controller =
398 return FileSys::ERROR_ENTITY_NOT_FOUND; 325 std::make_shared<SaveDataController>(system, it->second.save_data_factory);
399 } 326 *out_romfs_controller =
400 327 std::make_shared<RomFsController>(it->second.romfs_factory, it->second.program_id);
401 *out_save_data = save_data;
402 return ResultSuccess; 328 return ResultSuccess;
403} 329}
404 330
405Result FileSystemController::OpenSaveData(FileSys::VirtualDir* out_save_data, 331void FileSystemController::SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw) {
406 FileSys::SaveDataSpaceId space, 332 LOG_TRACE(Service_FS, "Setting packed update for romfs");
407 const FileSys::SaveDataAttribute& attribute) const {
408 LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", space,
409 attribute.DebugInfo());
410
411 if (save_data_factory == nullptr) {
412 return FileSys::ERROR_ENTITY_NOT_FOUND;
413 }
414 333
415 auto save_data = save_data_factory->Open(space, attribute); 334 std::scoped_lock lk{registration_lock};
416 if (save_data == nullptr) { 335 const auto it = registrations.find(process_id);
417 return FileSys::ERROR_ENTITY_NOT_FOUND; 336 if (it == registrations.end()) {
337 return;
418 } 338 }
419 339
420 *out_save_data = save_data; 340 it->second.romfs_factory->SetPackedUpdate(std::move(update_raw));
421 return ResultSuccess;
422} 341}
423 342
424Result FileSystemController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space, 343std::shared_ptr<SaveDataController> FileSystemController::OpenSaveDataController() {
425 FileSys::SaveDataSpaceId space) const { 344 return std::make_shared<SaveDataController>(system, CreateSaveDataFactory(ProgramId{}));
426 LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", space); 345}
427
428 if (save_data_factory == nullptr) {
429 return FileSys::ERROR_ENTITY_NOT_FOUND;
430 }
431 346
432 auto save_data_space = save_data_factory->GetSaveDataSpaceDirectory(space); 347std::shared_ptr<FileSys::SaveDataFactory> FileSystemController::CreateSaveDataFactory(
433 if (save_data_space == nullptr) { 348 ProgramId program_id) {
434 return FileSys::ERROR_ENTITY_NOT_FOUND; 349 using YuzuPath = Common::FS::YuzuPath;
435 } 350 const auto rw_mode = FileSys::Mode::ReadWrite;
436 351
437 *out_save_data_space = save_data_space; 352 auto vfs = system.GetFilesystem();
438 return ResultSuccess; 353 const auto nand_directory =
354 vfs->OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode);
355 return std::make_shared<FileSys::SaveDataFactory>(system, program_id,
356 std::move(nand_directory));
439} 357}
440 358
441Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const { 359Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const {
@@ -540,48 +458,6 @@ u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const {
540 return 0; 458 return 0;
541} 459}
542 460
543FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type,
544 u64 title_id, u128 user_id) const {
545 if (save_data_factory == nullptr) {
546 return {0, 0};
547 }
548
549 const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id);
550
551 if (value.normal == 0 && value.journal == 0) {
552 FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE};
553
554 FileSys::NACP nacp;
555 const auto res = system.GetAppLoader().ReadControlData(nacp);
556
557 if (res != Loader::ResultStatus::Success) {
558 const FileSys::PatchManager pm{system.GetApplicationProcessProgramID(),
559 system.GetFileSystemController(),
560 system.GetContentProvider()};
561 const auto metadata = pm.GetControlMetadata();
562 const auto& nacp_unique = metadata.first;
563
564 if (nacp_unique != nullptr) {
565 new_size = {nacp_unique->GetDefaultNormalSaveSize(),
566 nacp_unique->GetDefaultJournalSaveSize()};
567 }
568 } else {
569 new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()};
570 }
571
572 WriteSaveDataSize(type, title_id, user_id, new_size);
573 return new_size;
574 }
575
576 return value;
577}
578
579void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
580 FileSys::SaveDataSize new_value) const {
581 if (save_data_factory != nullptr)
582 save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
583}
584
585void FileSystemController::SetGameCard(FileSys::VirtualFile file) { 461void FileSystemController::SetGameCard(FileSys::VirtualFile file) {
586 gamecard = std::make_unique<FileSys::XCI>(file); 462 gamecard = std::make_unique<FileSys::XCI>(file);
587 const auto dir = gamecard->ConcatenatedPseudoDirectory(); 463 const auto dir = gamecard->ConcatenatedPseudoDirectory();
@@ -801,14 +677,9 @@ FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const {
801 return bis_factory->GetBCATDirectory(title_id); 677 return bis_factory->GetBCATDirectory(title_id);
802} 678}
803 679
804void FileSystemController::SetAutoSaveDataCreation(bool enable) {
805 save_data_factory->SetAutoCreate(enable);
806}
807
808void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { 680void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
809 if (overwrite) { 681 if (overwrite) {
810 bis_factory = nullptr; 682 bis_factory = nullptr;
811 save_data_factory = nullptr;
812 sdmc_factory = nullptr; 683 sdmc_factory = nullptr;
813 } 684 }
814 685
@@ -836,11 +707,6 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
836 bis_factory->GetUserNANDContents()); 707 bis_factory->GetUserNANDContents());
837 } 708 }
838 709
839 if (save_data_factory == nullptr) {
840 save_data_factory =
841 std::make_unique<FileSys::SaveDataFactory>(system, std::move(nand_directory));
842 }
843
844 if (sdmc_factory == nullptr) { 710 if (sdmc_factory == nullptr) {
845 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory), 711 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory),
846 std::move(sd_load_directory)); 712 std::move(sd_load_directory));
@@ -849,12 +715,19 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
849 } 715 }
850} 716}
851 717
718void FileSystemController::Reset() {
719 std::scoped_lock lk{registration_lock};
720 registrations.clear();
721}
722
852void LoopProcess(Core::System& system) { 723void LoopProcess(Core::System& system) {
853 auto server_manager = std::make_unique<ServerManager>(system); 724 auto server_manager = std::make_unique<ServerManager>(system);
854 725
726 const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); };
727
855 server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system)); 728 server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system));
856 server_manager->RegisterNamedService("fsp:pr", std::make_shared<FSP_PR>(system)); 729 server_manager->RegisterNamedService("fsp:pr", std::make_shared<FSP_PR>(system));
857 server_manager->RegisterNamedService("fsp-srv", std::make_shared<FSP_SRV>(system)); 730 server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory));
858 ServerManager::RunServer(std::move(server_manager)); 731 ServerManager::RunServer(std::move(server_manager));
859} 732}
860 733
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 276d264e1..48f37d289 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -43,6 +43,9 @@ class ServiceManager;
43 43
44namespace FileSystem { 44namespace FileSystem {
45 45
46class RomFsController;
47class SaveDataController;
48
46enum class ContentStorageId : u32 { 49enum class ContentStorageId : u32 {
47 System, 50 System,
48 User, 51 User,
@@ -61,32 +64,24 @@ enum class OpenDirectoryMode : u64 {
61}; 64};
62DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode); 65DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode);
63 66
67using ProcessId = u64;
68using ProgramId = u64;
69
64class FileSystemController { 70class FileSystemController {
65public: 71public:
66 explicit FileSystemController(Core::System& system_); 72 explicit FileSystemController(Core::System& system_);
67 ~FileSystemController(); 73 ~FileSystemController();
68 74
69 Result RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); 75 Result RegisterProcess(ProcessId process_id, ProgramId program_id,
70 Result RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); 76 std::shared_ptr<FileSys::RomFSFactory>&& factory);
71 Result RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); 77 Result OpenProcess(ProgramId* out_program_id,
72 Result RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); 78 std::shared_ptr<SaveDataController>* out_save_data_controller,
73 79 std::shared_ptr<RomFsController>* out_romfs_controller,
74 void SetPackedUpdate(FileSys::VirtualFile update_raw); 80 ProcessId process_id);
75 FileSys::VirtualFile OpenRomFSCurrentProcess() const; 81 void SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw);
76 FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type) const; 82
77 FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index, 83 std::shared_ptr<SaveDataController> OpenSaveDataController();
78 FileSys::ContentRecordType type) const; 84
79 FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
80 FileSys::ContentRecordType type) const;
81 std::shared_ptr<FileSys::NCA> OpenBaseNca(u64 title_id, FileSys::StorageId storage_id,
82 FileSys::ContentRecordType type) const;
83
84 Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
85 const FileSys::SaveDataAttribute& save_struct) const;
86 Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
87 const FileSys::SaveDataAttribute& save_struct) const;
88 Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
89 FileSys::SaveDataSpaceId space) const;
90 Result OpenSDMC(FileSys::VirtualDir* out_sdmc) const; 85 Result OpenSDMC(FileSys::VirtualDir* out_sdmc) const;
91 Result OpenBISPartition(FileSys::VirtualDir* out_bis_partition, 86 Result OpenBISPartition(FileSys::VirtualDir* out_bis_partition,
92 FileSys::BisPartitionId id) const; 87 FileSys::BisPartitionId id) const;
@@ -96,11 +91,6 @@ public:
96 u64 GetFreeSpaceSize(FileSys::StorageId id) const; 91 u64 GetFreeSpaceSize(FileSys::StorageId id) const;
97 u64 GetTotalSpaceSize(FileSys::StorageId id) const; 92 u64 GetTotalSpaceSize(FileSys::StorageId id) const;
98 93
99 FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
100 u128 user_id) const;
101 void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
102 FileSys::SaveDataSize new_value) const;
103
104 void SetGameCard(FileSys::VirtualFile file); 94 void SetGameCard(FileSys::VirtualFile file);
105 FileSys::XCI* GetGameCard() const; 95 FileSys::XCI* GetGameCard() const;
106 96
@@ -133,15 +123,24 @@ public:
133 123
134 FileSys::VirtualDir GetBCATDirectory(u64 title_id) const; 124 FileSys::VirtualDir GetBCATDirectory(u64 title_id) const;
135 125
136 void SetAutoSaveDataCreation(bool enable);
137
138 // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function 126 // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
139 // above is called. 127 // above is called.
140 void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); 128 void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
141 129
130 void Reset();
131
142private: 132private:
143 std::unique_ptr<FileSys::RomFSFactory> romfs_factory; 133 std::shared_ptr<FileSys::SaveDataFactory> CreateSaveDataFactory(ProgramId program_id);
144 std::unique_ptr<FileSys::SaveDataFactory> save_data_factory; 134
135 struct Registration {
136 ProgramId program_id;
137 std::shared_ptr<FileSys::RomFSFactory> romfs_factory;
138 std::shared_ptr<FileSys::SaveDataFactory> save_data_factory;
139 };
140
141 std::mutex registration_lock;
142 std::map<ProcessId, Registration> registrations;
143
145 std::unique_ptr<FileSys::SDMCFactory> sdmc_factory; 144 std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
146 std::unique_ptr<FileSys::BISFactory> bis_factory; 145 std::unique_ptr<FileSys::BISFactory> bis_factory;
147 146
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 82ecc1b90..a2397bec4 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -27,6 +27,8 @@
27#include "core/hle/result.h" 27#include "core/hle/result.h"
28#include "core/hle/service/filesystem/filesystem.h" 28#include "core/hle/service/filesystem/filesystem.h"
29#include "core/hle/service/filesystem/fsp_srv.h" 29#include "core/hle/service/filesystem/fsp_srv.h"
30#include "core/hle/service/filesystem/romfs_controller.h"
31#include "core/hle/service/filesystem/save_data_controller.h"
30#include "core/hle/service/hle_ipc.h" 32#include "core/hle/service/hle_ipc.h"
31#include "core/hle/service/ipc_helpers.h" 33#include "core/hle/service/ipc_helpers.h"
32#include "core/reporter.h" 34#include "core/reporter.h"
@@ -577,9 +579,11 @@ private:
577 579
578class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { 580class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
579public: 581public:
580 explicit ISaveDataInfoReader(Core::System& system_, FileSys::SaveDataSpaceId space, 582 explicit ISaveDataInfoReader(Core::System& system_,
581 FileSystemController& fsc_) 583 std::shared_ptr<SaveDataController> save_data_controller_,
582 : ServiceFramework{system_, "ISaveDataInfoReader"}, fsc{fsc_} { 584 FileSys::SaveDataSpaceId space)
585 : ServiceFramework{system_, "ISaveDataInfoReader"}, save_data_controller{
586 save_data_controller_} {
583 static const FunctionInfo functions[] = { 587 static const FunctionInfo functions[] = {
584 {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, 588 {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
585 }; 589 };
@@ -626,7 +630,7 @@ private:
626 630
627 void FindAllSaves(FileSys::SaveDataSpaceId space) { 631 void FindAllSaves(FileSys::SaveDataSpaceId space) {
628 FileSys::VirtualDir save_root{}; 632 FileSys::VirtualDir save_root{};
629 const auto result = fsc.OpenSaveDataSpace(&save_root, space); 633 const auto result = save_data_controller->OpenSaveDataSpace(&save_root, space);
630 634
631 if (result != ResultSuccess || save_root == nullptr) { 635 if (result != ResultSuccess || save_root == nullptr) {
632 LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", space); 636 LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", space);
@@ -723,7 +727,8 @@ private:
723 }; 727 };
724 static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); 728 static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
725 729
726 FileSystemController& fsc; 730 ProcessId process_id = 0;
731 std::shared_ptr<SaveDataController> save_data_controller;
727 std::vector<SaveDataInfo> info; 732 std::vector<SaveDataInfo> info;
728 u64 next_entry_index = 0; 733 u64 next_entry_index = 0;
729}; 734};
@@ -863,21 +868,20 @@ FSP_SRV::FSP_SRV(Core::System& system_)
863 if (Settings::values.enable_fs_access_log) { 868 if (Settings::values.enable_fs_access_log) {
864 access_log_mode = AccessLogMode::SdCard; 869 access_log_mode = AccessLogMode::SdCard;
865 } 870 }
866
867 // This should be true on creation
868 fsc.SetAutoSaveDataCreation(true);
869} 871}
870 872
871FSP_SRV::~FSP_SRV() = default; 873FSP_SRV::~FSP_SRV() = default;
872 874
873void FSP_SRV::SetCurrentProcess(HLERequestContext& ctx) { 875void FSP_SRV::SetCurrentProcess(HLERequestContext& ctx) {
874 IPC::RequestParser rp{ctx}; 876 current_process_id = ctx.GetPID();
875 current_process_id = rp.Pop<u64>();
876 877
877 LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id); 878 LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
878 879
880 const auto res =
881 fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller, current_process_id);
882
879 IPC::ResponseBuilder rb{ctx, 2}; 883 IPC::ResponseBuilder rb{ctx, 2};
880 rb.Push(ResultSuccess); 884 rb.Push(res);
881} 885}
882 886
883void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) { 887void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) {
@@ -916,7 +920,8 @@ void FSP_SRV::CreateSaveDataFileSystem(HLERequestContext& ctx) {
916 uid[1], uid[0]); 920 uid[1], uid[0]);
917 921
918 FileSys::VirtualDir save_data_dir{}; 922 FileSys::VirtualDir save_data_dir{};
919 fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser, save_struct); 923 save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser,
924 save_struct);
920 925
921 IPC::ResponseBuilder rb{ctx, 2}; 926 IPC::ResponseBuilder rb{ctx, 2};
922 rb.Push(ResultSuccess); 927 rb.Push(ResultSuccess);
@@ -931,7 +936,8 @@ void FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx)
931 LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo()); 936 LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo());
932 937
933 FileSys::VirtualDir save_data_dir{}; 938 FileSys::VirtualDir save_data_dir{};
934 fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem, save_struct); 939 save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem,
940 save_struct);
935 941
936 IPC::ResponseBuilder rb{ctx, 2}; 942 IPC::ResponseBuilder rb{ctx, 2};
937 rb.Push(ResultSuccess); 943 rb.Push(ResultSuccess);
@@ -950,7 +956,8 @@ void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) {
950 LOG_INFO(Service_FS, "called."); 956 LOG_INFO(Service_FS, "called.");
951 957
952 FileSys::VirtualDir dir{}; 958 FileSys::VirtualDir dir{};
953 auto result = fsc.OpenSaveData(&dir, parameters.space_id, parameters.attribute); 959 auto result =
960 save_data_controller->OpenSaveData(&dir, parameters.space_id, parameters.attribute);
954 if (result != ResultSuccess) { 961 if (result != ResultSuccess) {
955 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 962 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
956 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); 963 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
@@ -1001,7 +1008,7 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx) {
1001 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1008 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1002 rb.Push(ResultSuccess); 1009 rb.Push(ResultSuccess);
1003 rb.PushIpcInterface<ISaveDataInfoReader>( 1010 rb.PushIpcInterface<ISaveDataInfoReader>(
1004 std::make_shared<ISaveDataInfoReader>(system, space, fsc)); 1011 std::make_shared<ISaveDataInfoReader>(system, save_data_controller, space));
1005} 1012}
1006 1013
1007void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) { 1014void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) {
@@ -1009,8 +1016,8 @@ void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) {
1009 1016
1010 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1017 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1011 rb.Push(ResultSuccess); 1018 rb.Push(ResultSuccess);
1012 rb.PushIpcInterface<ISaveDataInfoReader>(system, FileSys::SaveDataSpaceId::TemporaryStorage, 1019 rb.PushIpcInterface<ISaveDataInfoReader>(system, save_data_controller,
1013 fsc); 1020 FileSys::SaveDataSpaceId::TemporaryStorage);
1014} 1021}
1015 1022
1016void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx) { 1023void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx) {
@@ -1050,7 +1057,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(HLERequestContext& ctx) {
1050 LOG_DEBUG(Service_FS, "called"); 1057 LOG_DEBUG(Service_FS, "called");
1051 1058
1052 if (!romfs) { 1059 if (!romfs) {
1053 auto current_romfs = fsc.OpenRomFSCurrentProcess(); 1060 auto current_romfs = romfs_controller->OpenRomFSCurrentProcess();
1054 if (!current_romfs) { 1061 if (!current_romfs) {
1055 // TODO (bunnei): Find the right error code to use here 1062 // TODO (bunnei): Find the right error code to use here
1056 LOG_CRITICAL(Service_FS, "no file system interface available!"); 1063 LOG_CRITICAL(Service_FS, "no file system interface available!");
@@ -1078,7 +1085,7 @@ void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) {
1078 LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}", 1085 LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}",
1079 storage_id, unknown, title_id); 1086 storage_id, unknown, title_id);
1080 1087
1081 auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); 1088 auto data = romfs_controller->OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
1082 1089
1083 if (!data) { 1090 if (!data) {
1084 const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); 1091 const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
@@ -1101,7 +1108,8 @@ void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) {
1101 1108
1102 const FileSys::PatchManager pm{title_id, fsc, content_provider}; 1109 const FileSys::PatchManager pm{title_id, fsc, content_provider};
1103 1110
1104 auto base = fsc.OpenBaseNca(title_id, storage_id, FileSys::ContentRecordType::Data); 1111 auto base =
1112 romfs_controller->OpenBaseNca(title_id, storage_id, FileSys::ContentRecordType::Data);
1105 auto storage = std::make_shared<IStorage>( 1113 auto storage = std::make_shared<IStorage>(
1106 system, pm.PatchRomFS(base.get(), std::move(data), FileSys::ContentRecordType::Data)); 1114 system, pm.PatchRomFS(base.get(), std::move(data), FileSys::ContentRecordType::Data));
1107 1115
@@ -1129,9 +1137,8 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) {
1129 1137
1130 LOG_DEBUG(Service_FS, "called, program_index={}", program_index); 1138 LOG_DEBUG(Service_FS, "called, program_index={}", program_index);
1131 1139
1132 auto patched_romfs = 1140 auto patched_romfs = romfs_controller->OpenPatchedRomFSWithProgramIndex(
1133 fsc.OpenPatchedRomFSWithProgramIndex(system.GetApplicationProcessProgramID(), program_index, 1141 program_id, program_index, FileSys::ContentRecordType::Program);
1134 FileSys::ContentRecordType::Program);
1135 1142
1136 if (!patched_romfs) { 1143 if (!patched_romfs) {
1137 // TODO: Find the right error code to use here 1144 // TODO: Find the right error code to use here
@@ -1152,7 +1159,7 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) {
1152void FSP_SRV::DisableAutoSaveDataCreation(HLERequestContext& ctx) { 1159void FSP_SRV::DisableAutoSaveDataCreation(HLERequestContext& ctx) {
1153 LOG_DEBUG(Service_FS, "called"); 1160 LOG_DEBUG(Service_FS, "called");
1154 1161
1155 fsc.SetAutoSaveDataCreation(false); 1162 save_data_controller->SetAutoCreate(false);
1156 1163
1157 IPC::ResponseBuilder rb{ctx, 2}; 1164 IPC::ResponseBuilder rb{ctx, 2};
1158 rb.Push(ResultSuccess); 1165 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 280bc9867..26980af99 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -17,6 +17,9 @@ class FileSystemBackend;
17 17
18namespace Service::FileSystem { 18namespace Service::FileSystem {
19 19
20class RomFsController;
21class SaveDataController;
22
20enum class AccessLogVersion : u32 { 23enum class AccessLogVersion : u32 {
21 V7_0_0 = 2, 24 V7_0_0 = 2,
22 25
@@ -67,6 +70,9 @@ private:
67 u64 current_process_id = 0; 70 u64 current_process_id = 0;
68 u32 access_log_program_index = 0; 71 u32 access_log_program_index = 0;
69 AccessLogMode access_log_mode = AccessLogMode::None; 72 AccessLogMode access_log_mode = AccessLogMode::None;
73 u64 program_id = 0;
74 std::shared_ptr<SaveDataController> save_data_controller;
75 std::shared_ptr<RomFsController> romfs_controller;
70}; 76};
71 77
72} // namespace Service::FileSystem 78} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/romfs_controller.cpp b/src/core/hle/service/filesystem/romfs_controller.cpp
new file mode 100644
index 000000000..19c9cec72
--- /dev/null
+++ b/src/core/hle/service/filesystem/romfs_controller.cpp
@@ -0,0 +1,37 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/filesystem/romfs_controller.h"
5
6namespace Service::FileSystem {
7
8RomFsController::RomFsController(std::shared_ptr<FileSys::RomFSFactory> factory_, u64 program_id_)
9 : factory{std::move(factory_)}, program_id{program_id_} {}
10RomFsController::~RomFsController() = default;
11
12FileSys::VirtualFile RomFsController::OpenRomFSCurrentProcess() {
13 return factory->OpenCurrentProcess(program_id);
14}
15
16FileSys::VirtualFile RomFsController::OpenPatchedRomFS(u64 title_id,
17 FileSys::ContentRecordType type) {
18 return factory->OpenPatchedRomFS(title_id, type);
19}
20
21FileSys::VirtualFile RomFsController::OpenPatchedRomFSWithProgramIndex(
22 u64 title_id, u8 program_index, FileSys::ContentRecordType type) {
23 return factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type);
24}
25
26FileSys::VirtualFile RomFsController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
27 FileSys::ContentRecordType type) {
28 return factory->Open(title_id, storage_id, type);
29}
30
31std::shared_ptr<FileSys::NCA> RomFsController::OpenBaseNca(u64 title_id,
32 FileSys::StorageId storage_id,
33 FileSys::ContentRecordType type) {
34 return factory->GetEntry(title_id, storage_id, type);
35}
36
37} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/romfs_controller.h b/src/core/hle/service/filesystem/romfs_controller.h
new file mode 100644
index 000000000..9a478f71d
--- /dev/null
+++ b/src/core/hle/service/filesystem/romfs_controller.h
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/file_sys/nca_metadata.h"
7#include "core/file_sys/romfs_factory.h"
8#include "core/file_sys/vfs_types.h"
9
10namespace Service::FileSystem {
11
12class RomFsController {
13public:
14 explicit RomFsController(std::shared_ptr<FileSys::RomFSFactory> factory_, u64 program_id_);
15 ~RomFsController();
16
17 FileSys::VirtualFile OpenRomFSCurrentProcess();
18 FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type);
19 FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index,
20 FileSys::ContentRecordType type);
21 FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
22 FileSys::ContentRecordType type);
23 std::shared_ptr<FileSys::NCA> OpenBaseNca(u64 title_id, FileSys::StorageId storage_id,
24 FileSys::ContentRecordType type);
25
26private:
27 const std::shared_ptr<FileSys::RomFSFactory> factory;
28 const u64 program_id;
29};
30
31} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/save_data_controller.cpp b/src/core/hle/service/filesystem/save_data_controller.cpp
new file mode 100644
index 000000000..d19b3ea1e
--- /dev/null
+++ b/src/core/hle/service/filesystem/save_data_controller.cpp
@@ -0,0 +1,99 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/file_sys/control_metadata.h"
6#include "core/file_sys/errors.h"
7#include "core/file_sys/patch_manager.h"
8#include "core/hle/service/filesystem/save_data_controller.h"
9#include "core/loader/loader.h"
10
11namespace Service::FileSystem {
12
13namespace {
14
15// A default size for normal/journal save data size if application control metadata cannot be found.
16// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
17constexpr u64 SufficientSaveDataSize = 0xF0000000;
18
19FileSys::SaveDataSize GetDefaultSaveDataSize(Core::System& system, u64 program_id) {
20 const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
21 system.GetContentProvider()};
22 const auto metadata = pm.GetControlMetadata();
23 const auto& nacp = metadata.first;
24
25 if (nacp != nullptr) {
26 return {nacp->GetDefaultNormalSaveSize(), nacp->GetDefaultJournalSaveSize()};
27 }
28
29 return {SufficientSaveDataSize, SufficientSaveDataSize};
30}
31
32} // namespace
33
34SaveDataController::SaveDataController(Core::System& system_,
35 std::shared_ptr<FileSys::SaveDataFactory> factory_)
36 : system{system_}, factory{std::move(factory_)} {}
37SaveDataController::~SaveDataController() = default;
38
39Result SaveDataController::CreateSaveData(FileSys::VirtualDir* out_save_data,
40 FileSys::SaveDataSpaceId space,
41 const FileSys::SaveDataAttribute& attribute) {
42 LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space,
43 attribute.DebugInfo());
44
45 auto save_data = factory->Create(space, attribute);
46 if (save_data == nullptr) {
47 return FileSys::ERROR_ENTITY_NOT_FOUND;
48 }
49
50 *out_save_data = save_data;
51 return ResultSuccess;
52}
53
54Result SaveDataController::OpenSaveData(FileSys::VirtualDir* out_save_data,
55 FileSys::SaveDataSpaceId space,
56 const FileSys::SaveDataAttribute& attribute) {
57 auto save_data = factory->Open(space, attribute);
58 if (save_data == nullptr) {
59 return FileSys::ERROR_ENTITY_NOT_FOUND;
60 }
61
62 *out_save_data = save_data;
63 return ResultSuccess;
64}
65
66Result SaveDataController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
67 FileSys::SaveDataSpaceId space) {
68 auto save_data_space = factory->GetSaveDataSpaceDirectory(space);
69 if (save_data_space == nullptr) {
70 return FileSys::ERROR_ENTITY_NOT_FOUND;
71 }
72
73 *out_save_data_space = save_data_space;
74 return ResultSuccess;
75}
76
77FileSys::SaveDataSize SaveDataController::ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
78 u128 user_id) {
79 const auto value = factory->ReadSaveDataSize(type, title_id, user_id);
80
81 if (value.normal == 0 && value.journal == 0) {
82 const auto size = GetDefaultSaveDataSize(system, title_id);
83 factory->WriteSaveDataSize(type, title_id, user_id, size);
84 return size;
85 }
86
87 return value;
88}
89
90void SaveDataController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
91 FileSys::SaveDataSize new_value) {
92 factory->WriteSaveDataSize(type, title_id, user_id, new_value);
93}
94
95void SaveDataController::SetAutoCreate(bool state) {
96 factory->SetAutoCreate(state);
97}
98
99} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/save_data_controller.h b/src/core/hle/service/filesystem/save_data_controller.h
new file mode 100644
index 000000000..863188e4c
--- /dev/null
+++ b/src/core/hle/service/filesystem/save_data_controller.h
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/file_sys/nca_metadata.h"
7#include "core/file_sys/savedata_factory.h"
8#include "core/file_sys/vfs_types.h"
9
10namespace Service::FileSystem {
11
12class SaveDataController {
13public:
14 explicit SaveDataController(Core::System& system,
15 std::shared_ptr<FileSys::SaveDataFactory> factory_);
16 ~SaveDataController();
17
18 Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
19 const FileSys::SaveDataAttribute& attribute);
20 Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
21 const FileSys::SaveDataAttribute& attribute);
22 Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
23 FileSys::SaveDataSpaceId space);
24
25 FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
26 void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
27 FileSys::SaveDataSize new_value);
28 void SetAutoCreate(bool state);
29
30private:
31 Core::System& system;
32 const std::shared_ptr<FileSys::SaveDataFactory> factory;
33};
34
35} // namespace Service::FileSystem
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 0507b14e7..aeb849efa 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -131,7 +131,7 @@ private:
131 u8 is_favorite; 131 u8 is_favorite;
132 u8 same_app; 132 u8 same_app;
133 u8 same_app_played; 133 u8 same_app_played;
134 u8 arbitary_app_played; 134 u8 arbitrary_app_played;
135 u64 group_id; 135 u64 group_id;
136 }; 136 };
137 static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size"); 137 static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size");
diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp
index 6f1151b03..1254b6d49 100644
--- a/src/core/hle/service/glue/arp.cpp
+++ b/src/core/hle/service/glue/arp.cpp
@@ -15,9 +15,10 @@
15namespace Service::Glue { 15namespace Service::Glue {
16 16
17namespace { 17namespace {
18std::optional<u64> GetTitleIDForProcessID(const Core::System& system, u64 process_id) { 18std::optional<u64> GetTitleIDForProcessID(Core::System& system, u64 process_id) {
19 const auto& list = system.Kernel().GetProcessList(); 19 auto list = system.Kernel().GetProcessList();
20 const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) { 20
21 const auto iter = std::find_if(list.begin(), list.end(), [&process_id](auto& process) {
21 return process->GetProcessId() == process_id; 22 return process->GetProcessId() == process_id;
22 }); 23 });
23 24
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index fc8a3ab66..4ce0a9834 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -18,23 +18,21 @@ namespace Service::HID {
18 18
19void LoopProcess(Core::System& system) { 19void LoopProcess(Core::System& system) {
20 auto server_manager = std::make_unique<ServerManager>(system); 20 auto server_manager = std::make_unique<ServerManager>(system);
21 std::shared_ptr<ResourceManager> resouce_manager = std::make_shared<ResourceManager>(system); 21 std::shared_ptr<ResourceManager> resource_manager = std::make_shared<ResourceManager>(system);
22 std::shared_ptr<HidFirmwareSettings> firmware_settings = 22 std::shared_ptr<HidFirmwareSettings> firmware_settings =
23 std::make_shared<HidFirmwareSettings>(); 23 std::make_shared<HidFirmwareSettings>();
24 24
25 // TODO: Remove this hack until this service is emulated properly. 25 // TODO: Remove this hack when am is emulated properly.
26 const auto process_list = system.Kernel().GetProcessList(); 26 resource_manager->Initialize();
27 if (!process_list.empty()) { 27 resource_manager->RegisterAppletResourceUserId(system.ApplicationProcess()->GetProcessId(),
28 resouce_manager->Initialize(); 28 true);
29 resouce_manager->RegisterAppletResourceUserId(process_list[0]->GetId(), true);
30 }
31 29
32 server_manager->RegisterNamedService( 30 server_manager->RegisterNamedService(
33 "hid", std::make_shared<IHidServer>(system, resouce_manager, firmware_settings)); 31 "hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings));
34 server_manager->RegisterNamedService( 32 server_manager->RegisterNamedService(
35 "hid:dbg", std::make_shared<IHidDebugServer>(system, resouce_manager)); 33 "hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager));
36 server_manager->RegisterNamedService( 34 server_manager->RegisterNamedService(
37 "hid:sys", std::make_shared<IHidSystemServer>(system, resouce_manager)); 35 "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager));
38 36
39 server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system)); 37 server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system));
40 38
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
index 74898888a..1951da33b 100644
--- a/src/core/hle/service/hid/hid_server.cpp
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -1498,7 +1498,7 @@ void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) {
1498 bool check_device_index = false; 1498 bool check_device_index = false;
1499 1499
1500 switch (vibration_device_handle.npad_type) { 1500 switch (vibration_device_handle.npad_type) {
1501 case Core::HID::NpadStyleIndex::ProController: 1501 case Core::HID::NpadStyleIndex::Fullkey:
1502 case Core::HID::NpadStyleIndex::Handheld: 1502 case Core::HID::NpadStyleIndex::Handheld:
1503 case Core::HID::NpadStyleIndex::JoyconDual: 1503 case Core::HID::NpadStyleIndex::JoyconDual:
1504 case Core::HID::NpadStyleIndex::JoyconLeft: 1504 case Core::HID::NpadStyleIndex::JoyconLeft:
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
index 46f503d38..c903ee8b8 100644
--- a/src/core/hle/service/hid/hidbus.cpp
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -67,7 +67,7 @@ HidBus::~HidBus() {
67void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) { 67void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) {
68 if (is_hidbus_enabled) { 68 if (is_hidbus_enabled) {
69 for (std::size_t i = 0; i < devices.size(); ++i) { 69 for (std::size_t i = 0; i < devices.size(); ++i) {
70 if (!devices[i].is_device_initializated) { 70 if (!devices[i].is_device_initialized) {
71 continue; 71 continue;
72 } 72 }
73 auto& device = devices[i].device; 73 auto& device = devices[i].device;
@@ -213,7 +213,7 @@ void HidBus::Initialize(HLERequestContext& ctx) {
213 213
214 if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) { 214 if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) {
215 MakeDevice<RingController>(bus_handle_); 215 MakeDevice<RingController>(bus_handle_);
216 devices[device_index.value()].is_device_initializated = true; 216 devices[device_index.value()].is_device_initialized = true;
217 devices[device_index.value()].device->ActivateDevice(); 217 devices[device_index.value()].device->ActivateDevice();
218 cur_entry.is_in_focus = true; 218 cur_entry.is_in_focus = true;
219 cur_entry.is_connected = true; 219 cur_entry.is_connected = true;
@@ -222,7 +222,7 @@ void HidBus::Initialize(HLERequestContext& ctx) {
222 cur_entry.is_polling_mode = false; 222 cur_entry.is_polling_mode = false;
223 } else { 223 } else {
224 MakeDevice<HidbusStubbed>(bus_handle_); 224 MakeDevice<HidbusStubbed>(bus_handle_);
225 devices[device_index.value()].is_device_initializated = true; 225 devices[device_index.value()].is_device_initialized = true;
226 cur_entry.is_in_focus = true; 226 cur_entry.is_in_focus = true;
227 cur_entry.is_connected = false; 227 cur_entry.is_connected = false;
228 cur_entry.is_connected_result = ResultSuccess; 228 cur_entry.is_connected_result = ResultSuccess;
@@ -261,7 +261,7 @@ void HidBus::Finalize(HLERequestContext& ctx) {
261 const auto entry_index = devices[device_index.value()].handle.internal_index; 261 const auto entry_index = devices[device_index.value()].handle.internal_index;
262 auto& cur_entry = hidbus_status.entries[entry_index]; 262 auto& cur_entry = hidbus_status.entries[entry_index];
263 auto& device = devices[device_index.value()].device; 263 auto& device = devices[device_index.value()].device;
264 devices[device_index.value()].is_device_initializated = false; 264 devices[device_index.value()].is_device_initialized = false;
265 device->DeactivateDevice(); 265 device->DeactivateDevice();
266 266
267 cur_entry.is_in_focus = true; 267 cur_entry.is_in_focus = true;
diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h
index 05f62f634..03d9f6863 100644
--- a/src/core/hle/service/hid/hidbus.h
+++ b/src/core/hle/service/hid/hidbus.h
@@ -89,7 +89,7 @@ private:
89 static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size"); 89 static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size");
90 90
91 struct HidbusDevice { 91 struct HidbusDevice {
92 bool is_device_initializated{}; 92 bool is_device_initialized{};
93 BusHandle handle{}; 93 BusHandle handle{};
94 std::unique_ptr<HidbusBase> device{nullptr}; 94 std::unique_ptr<HidbusBase> device{nullptr};
95 }; 95 };
diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp
index 39df77e43..3f38ceb03 100644
--- a/src/core/hle/service/hle_ipc.cpp
+++ b/src/core/hle/service/hle_ipc.cpp
@@ -181,22 +181,22 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
181 } 181 }
182 } 182 }
183 183
184 buffer_x_desciptors.reserve(command_header->num_buf_x_descriptors); 184 buffer_x_descriptors.reserve(command_header->num_buf_x_descriptors);
185 buffer_a_desciptors.reserve(command_header->num_buf_a_descriptors); 185 buffer_a_descriptors.reserve(command_header->num_buf_a_descriptors);
186 buffer_b_desciptors.reserve(command_header->num_buf_b_descriptors); 186 buffer_b_descriptors.reserve(command_header->num_buf_b_descriptors);
187 buffer_w_desciptors.reserve(command_header->num_buf_w_descriptors); 187 buffer_w_descriptors.reserve(command_header->num_buf_w_descriptors);
188 188
189 for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) { 189 for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) {
190 buffer_x_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>()); 190 buffer_x_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>());
191 } 191 }
192 for (u32 i = 0; i < command_header->num_buf_a_descriptors; ++i) { 192 for (u32 i = 0; i < command_header->num_buf_a_descriptors; ++i) {
193 buffer_a_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>()); 193 buffer_a_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
194 } 194 }
195 for (u32 i = 0; i < command_header->num_buf_b_descriptors; ++i) { 195 for (u32 i = 0; i < command_header->num_buf_b_descriptors; ++i) {
196 buffer_b_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>()); 196 buffer_b_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
197 } 197 }
198 for (u32 i = 0; i < command_header->num_buf_w_descriptors; ++i) { 198 for (u32 i = 0; i < command_header->num_buf_w_descriptors; ++i) {
199 buffer_w_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>()); 199 buffer_w_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
200 } 200 }
201 201
202 const auto buffer_c_offset = rp.GetCurrentOffset() + command_header->data_size; 202 const auto buffer_c_offset = rp.GetCurrentOffset() + command_header->data_size;
@@ -246,7 +246,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
246 IPC::CommandHeader::BufferDescriptorCFlag::InlineDescriptor) { 246 IPC::CommandHeader::BufferDescriptorCFlag::InlineDescriptor) {
247 if (command_header->buf_c_descriptor_flags == 247 if (command_header->buf_c_descriptor_flags ==
248 IPC::CommandHeader::BufferDescriptorCFlag::OneDescriptor) { 248 IPC::CommandHeader::BufferDescriptorCFlag::OneDescriptor) {
249 buffer_c_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>()); 249 buffer_c_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>());
250 } else { 250 } else {
251 u32 num_buf_c_descriptors = 251 u32 num_buf_c_descriptors =
252 static_cast<u32>(command_header->buf_c_descriptor_flags.Value()) - 2; 252 static_cast<u32>(command_header->buf_c_descriptor_flags.Value()) - 2;
@@ -256,7 +256,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
256 ASSERT(num_buf_c_descriptors < 14); 256 ASSERT(num_buf_c_descriptors < 14);
257 257
258 for (u32 i = 0; i < num_buf_c_descriptors; ++i) { 258 for (u32 i = 0; i < num_buf_c_descriptors; ++i) {
259 buffer_c_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>()); 259 buffer_c_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>());
260 } 260 }
261 } 261 }
262 } 262 }
diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h
index 40d86943e..440737db5 100644
--- a/src/core/hle/service/hle_ipc.h
+++ b/src/core/hle/service/hle_ipc.h
@@ -232,19 +232,19 @@ public:
232 } 232 }
233 233
234 [[nodiscard]] const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const { 234 [[nodiscard]] const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
235 return buffer_x_desciptors; 235 return buffer_x_descriptors;
236 } 236 }
237 237
238 [[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const { 238 [[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
239 return buffer_a_desciptors; 239 return buffer_a_descriptors;
240 } 240 }
241 241
242 [[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const { 242 [[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
243 return buffer_b_desciptors; 243 return buffer_b_descriptors;
244 } 244 }
245 245
246 [[nodiscard]] const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const { 246 [[nodiscard]] const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
247 return buffer_c_desciptors; 247 return buffer_c_descriptors;
248 } 248 }
249 249
250 [[nodiscard]] const IPC::DomainMessageHeader& GetDomainMessageHeader() const { 250 [[nodiscard]] const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
@@ -406,11 +406,11 @@ private:
406 std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header; 406 std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header;
407 std::optional<IPC::DataPayloadHeader> data_payload_header; 407 std::optional<IPC::DataPayloadHeader> data_payload_header;
408 std::optional<IPC::DomainMessageHeader> domain_message_header; 408 std::optional<IPC::DomainMessageHeader> domain_message_header;
409 std::vector<IPC::BufferDescriptorX> buffer_x_desciptors; 409 std::vector<IPC::BufferDescriptorX> buffer_x_descriptors;
410 std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors; 410 std::vector<IPC::BufferDescriptorABW> buffer_a_descriptors;
411 std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors; 411 std::vector<IPC::BufferDescriptorABW> buffer_b_descriptors;
412 std::vector<IPC::BufferDescriptorABW> buffer_w_desciptors; 412 std::vector<IPC::BufferDescriptorABW> buffer_w_descriptors;
413 std::vector<IPC::BufferDescriptorC> buffer_c_desciptors; 413 std::vector<IPC::BufferDescriptorC> buffer_c_descriptors;
414 414
415 u32_le command{}; 415 u32_le command{};
416 u64 pid{}; 416 u64 pid{};
diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.cpp b/src/core/hle/service/nfc/common/amiibo_crypto.cpp
index 9556e9193..4274a92c9 100644
--- a/src/core/hle/service/nfc/common/amiibo_crypto.cpp
+++ b/src/core/hle/service/nfc/common/amiibo_crypto.cpp
@@ -19,7 +19,7 @@ namespace Service::NFP::AmiiboCrypto {
19bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { 19bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
20 const auto& amiibo_data = ntag_file.user_memory; 20 const auto& amiibo_data = ntag_file.user_memory;
21 LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock); 21 LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock);
22 LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container); 22 LOG_DEBUG(Service_NFP, "compatibility_container=0x{0:x}", ntag_file.compatibility_container);
23 LOG_DEBUG(Service_NFP, "write_count={}", static_cast<u16>(amiibo_data.write_counter)); 23 LOG_DEBUG(Service_NFP, "write_count={}", static_cast<u16>(amiibo_data.write_counter));
24 24
25 LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); 25 LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id);
@@ -49,7 +49,7 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
49 if (ntag_file.static_lock != 0xE00F) { 49 if (ntag_file.static_lock != 0xE00F) {
50 return false; 50 return false;
51 } 51 }
52 if (ntag_file.compability_container != 0xEEFF10F1U) { 52 if (ntag_file.compatibility_container != 0xEEFF10F1U) {
53 return false; 53 return false;
54 } 54 }
55 if (amiibo_data.model_info.tag_type != NFC::PackedTagType::Type2) { 55 if (amiibo_data.model_info.tag_type != NFC::PackedTagType::Type2) {
@@ -78,7 +78,7 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
78 encoded_data.uid_crc_check2 = nfc_data.uuid_crc_check2; 78 encoded_data.uid_crc_check2 = nfc_data.uuid_crc_check2;
79 encoded_data.internal_number = nfc_data.internal_number; 79 encoded_data.internal_number = nfc_data.internal_number;
80 encoded_data.static_lock = nfc_data.static_lock; 80 encoded_data.static_lock = nfc_data.static_lock;
81 encoded_data.compability_container = nfc_data.compability_container; 81 encoded_data.compatibility_container = nfc_data.compatibility_container;
82 encoded_data.hmac_data = nfc_data.user_memory.hmac_data; 82 encoded_data.hmac_data = nfc_data.user_memory.hmac_data;
83 encoded_data.constant_value = nfc_data.user_memory.constant_value; 83 encoded_data.constant_value = nfc_data.user_memory.constant_value;
84 encoded_data.write_counter = nfc_data.user_memory.write_counter; 84 encoded_data.write_counter = nfc_data.user_memory.write_counter;
@@ -112,7 +112,7 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
112 nfc_data.uuid_crc_check2 = encoded_data.uid_crc_check2; 112 nfc_data.uuid_crc_check2 = encoded_data.uid_crc_check2;
113 nfc_data.internal_number = encoded_data.internal_number; 113 nfc_data.internal_number = encoded_data.internal_number;
114 nfc_data.static_lock = encoded_data.static_lock; 114 nfc_data.static_lock = encoded_data.static_lock;
115 nfc_data.compability_container = encoded_data.compability_container; 115 nfc_data.compatibility_container = encoded_data.compatibility_container;
116 nfc_data.user_memory.hmac_data = encoded_data.hmac_data; 116 nfc_data.user_memory.hmac_data = encoded_data.hmac_data;
117 nfc_data.user_memory.constant_value = encoded_data.constant_value; 117 nfc_data.user_memory.constant_value = encoded_data.constant_value;
118 nfc_data.user_memory.write_counter = encoded_data.write_counter; 118 nfc_data.user_memory.write_counter = encoded_data.write_counter;
@@ -257,7 +257,7 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou
257 out_data.uid_crc_check2 = in_data.uid_crc_check2; 257 out_data.uid_crc_check2 = in_data.uid_crc_check2;
258 out_data.internal_number = in_data.internal_number; 258 out_data.internal_number = in_data.internal_number;
259 out_data.static_lock = in_data.static_lock; 259 out_data.static_lock = in_data.static_lock;
260 out_data.compability_container = in_data.compability_container; 260 out_data.compatibility_container = in_data.compatibility_container;
261 261
262 out_data.constant_value = in_data.constant_value; 262 out_data.constant_value = in_data.constant_value;
263 out_data.write_counter = in_data.write_counter; 263 out_data.write_counter = in_data.write_counter;
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index b37fb6da3..31cc87acc 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -75,7 +75,7 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
75 return; 75 return;
76 } 76 }
77 77
78 if (!is_initalized) { 78 if (!is_initialized) {
79 return; 79 return;
80 } 80 }
81 81
@@ -207,7 +207,7 @@ void NfcDevice::Initialize() {
207 return; 207 return;
208 } 208 }
209 209
210 is_initalized = npad_device->AddNfcHandle(); 210 is_initialized = npad_device->AddNfcHandle();
211} 211}
212 212
213void NfcDevice::Finalize() { 213void NfcDevice::Finalize() {
@@ -226,7 +226,7 @@ void NfcDevice::Finalize() {
226 } 226 }
227 227
228 device_state = DeviceState::Unavailable; 228 device_state = DeviceState::Unavailable;
229 is_initalized = false; 229 is_initialized = false;
230} 230}
231 231
232Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) { 232Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) {
diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h
index d8efe25ec..15f9b25da 100644
--- a/src/core/hle/service/nfc/common/device.h
+++ b/src/core/hle/service/nfc/common/device.h
@@ -126,7 +126,7 @@ private:
126 Kernel::KEvent* deactivate_event = nullptr; 126 Kernel::KEvent* deactivate_event = nullptr;
127 Kernel::KEvent* availability_change_event = nullptr; 127 Kernel::KEvent* availability_change_event = nullptr;
128 128
129 bool is_initalized{}; 129 bool is_initialized{};
130 NfcProtocol allowed_protocols{}; 130 NfcProtocol allowed_protocols{};
131 DeviceState device_state{DeviceState::Unavailable}; 131 DeviceState device_state{DeviceState::Unavailable};
132 132
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
index f96d21220..2505eb551 100644
--- a/src/core/hle/service/nfp/nfp_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -243,12 +243,12 @@ static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid si
243struct NTAG215File { 243struct NTAG215File {
244 u8 uid_crc_check2; 244 u8 uid_crc_check2;
245 u8 internal_number; 245 u8 internal_number;
246 u16 static_lock; // Set defined pages as read only 246 u16 static_lock; // Set defined pages as read only
247 u32 compability_container; // Defines available memory 247 u32 compatibility_container; // Defines available memory
248 HashData hmac_data; // Hash 248 HashData hmac_data; // Hash
249 u8 constant_value; // Must be A5 249 u8 constant_value; // Must be A5
250 u16_be write_counter; // Number of times the amiibo has been written? 250 u16_be write_counter; // Number of times the amiibo has been written?
251 u8 amiibo_version; // Amiibo file version 251 u8 amiibo_version; // Amiibo file version
252 AmiiboSettings settings; 252 AmiiboSettings settings;
253 Service::Mii::Ver3StoreData owner_mii; // Mii data 253 Service::Mii::Ver3StoreData owner_mii; // Mii data
254 u64_be application_id; // Game id 254 u64_be application_id; // Game id
@@ -278,7 +278,7 @@ struct EncryptedNTAG215File {
278 u8 uuid_crc_check2; 278 u8 uuid_crc_check2;
279 u8 internal_number; 279 u8 internal_number;
280 u16 static_lock; // Set defined pages as read only 280 u16 static_lock; // Set defined pages as read only
281 u32 compability_container; // Defines available memory 281 u32 compatibility_container; // Defines available memory
282 EncryptedAmiiboFile user_memory; // Writable data 282 EncryptedAmiiboFile user_memory; // Writable data
283 u32 dynamic_lock; // Dynamic lock 283 u32 dynamic_lock; // Dynamic lock
284 u32 CFG0; // Defines memory protected by password 284 u32 CFG0; // Defines memory protected by password
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index 932997e75..79a21683d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -90,7 +90,7 @@ private:
90 u64_le align; 90 u64_le align;
91 }; 91 };
92 }; 92 };
93 static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size"); 93 static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitializeEx is incorrect size");
94 94
95 struct IoctlFreeSpace { 95 struct IoctlFreeSpace {
96 u64_le offset{}; 96 u64_le offset{};
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 61a2df121..3e0c96456 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -15,7 +15,7 @@ namespace Service::Nvidia::Devices {
15nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_) 15nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_)
16 : nvdevice{system_}, events_interface{events_interface_} { 16 : nvdevice{system_}, events_interface{events_interface_} {
17 error_notifier_event = events_interface.CreateEvent("CtrlGpuErrorNotifier"); 17 error_notifier_event = events_interface.CreateEvent("CtrlGpuErrorNotifier");
18 unknown_event = events_interface.CreateEvent("CtrlGpuUknownEvent"); 18 unknown_event = events_interface.CreateEvent("CtrlGpuUnknownEvent");
19} 19}
20nvhost_ctrl_gpu::~nvhost_ctrl_gpu() { 20nvhost_ctrl_gpu::~nvhost_ctrl_gpu() {
21 events_interface.FreeEvent(error_notifier_event); 21 events_interface.FreeEvent(error_notifier_event);
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 0e2f47075..38f35e79f 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -51,7 +51,7 @@ enum class NvResult : u32 {
51 DispNoDisplaysAttached = 0x20003, 51 DispNoDisplaysAttached = 0x20003,
52 DispModeNotSupported = 0x20004, 52 DispModeNotSupported = 0x20004,
53 DispNotFound = 0x20005, 53 DispNotFound = 0x20005,
54 DispAttachDissallowed = 0x20006, 54 DispAttachDisallowed = 0x20006,
55 DispTypeNotSupported = 0x20007, 55 DispTypeNotSupported = 0x20007,
56 DispAuthenticationFailed = 0x20008, 56 DispAuthenticationFailed = 0x20008,
57 DispNotAttached = 0x20009, 57 DispNotAttached = 0x20009,
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index aa8aaa2d9..0469110e8 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -223,7 +223,8 @@ Result Nvnflinger::FindVsyncEvent(Kernel::KReadableEvent** out_vsync_event, u64
223 return VI::ResultNotFound; 223 return VI::ResultNotFound;
224 } 224 }
225 225
226 return display->GetVSyncEvent(out_vsync_event); 226 *out_vsync_event = display->GetVSyncEvent();
227 return ResultSuccess;
227} 228}
228 229
229VI::Display* Nvnflinger::FindDisplay(u64 display_id) { 230VI::Display* Nvnflinger::FindDisplay(u64 display_id) {
diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp
index c13ffa6f6..3d0f2aeb7 100644
--- a/src/core/hle/service/pcv/pcv.cpp
+++ b/src/core/hle/service/pcv/pcv.cpp
@@ -54,8 +54,8 @@ public:
54 54
55class IClkrstSession final : public ServiceFramework<IClkrstSession> { 55class IClkrstSession final : public ServiceFramework<IClkrstSession> {
56public: 56public:
57 explicit IClkrstSession(Core::System& system_, DeviceCode deivce_code_) 57 explicit IClkrstSession(Core::System& system_, DeviceCode device_code_)
58 : ServiceFramework{system_, "IClkrstSession"}, deivce_code(deivce_code_) { 58 : ServiceFramework{system_, "IClkrstSession"}, device_code(device_code_) {
59 // clang-format off 59 // clang-format off
60 static const FunctionInfo functions[] = { 60 static const FunctionInfo functions[] = {
61 {0, nullptr, "SetClockEnabled"}, 61 {0, nullptr, "SetClockEnabled"},
@@ -93,7 +93,7 @@ private:
93 rb.Push<u32>(clock_rate); 93 rb.Push<u32>(clock_rate);
94 } 94 }
95 95
96 DeviceCode deivce_code; 96 DeviceCode device_code;
97 u32 clock_rate{}; 97 u32 clock_rate{};
98}; 98};
99 99
@@ -118,9 +118,9 @@ private:
118 void OpenSession(HLERequestContext& ctx) { 118 void OpenSession(HLERequestContext& ctx) {
119 IPC::RequestParser rp{ctx}; 119 IPC::RequestParser rp{ctx};
120 const auto device_code = static_cast<DeviceCode>(rp.Pop<u32>()); 120 const auto device_code = static_cast<DeviceCode>(rp.Pop<u32>());
121 const auto unkonwn_input = rp.Pop<u32>(); 121 const auto unknown_input = rp.Pop<u32>();
122 122
123 LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unkonwn_input); 123 LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unknown_input);
124 124
125 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 125 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
126 rb.Push(ResultSuccess); 126 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index d92499f05..b52468e41 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -22,27 +22,26 @@ constexpr Result ResultProcessNotFound{ErrorModule::PM, 1};
22 22
23constexpr u64 NO_PROCESS_FOUND_PID{0}; 23constexpr u64 NO_PROCESS_FOUND_PID{0};
24 24
25std::optional<Kernel::KProcess*> SearchProcessList( 25using ProcessList = std::list<Kernel::KScopedAutoObject<Kernel::KProcess>>;
26 const std::vector<Kernel::KProcess*>& process_list, 26
27 std::function<bool(Kernel::KProcess*)> predicate) { 27template <typename F>
28Kernel::KScopedAutoObject<Kernel::KProcess> SearchProcessList(ProcessList& process_list,
29 F&& predicate) {
28 const auto iter = std::find_if(process_list.begin(), process_list.end(), predicate); 30 const auto iter = std::find_if(process_list.begin(), process_list.end(), predicate);
29 31
30 if (iter == process_list.end()) { 32 if (iter == process_list.end()) {
31 return std::nullopt; 33 return nullptr;
32 } 34 }
33 35
34 return *iter; 36 return iter->GetPointerUnsafe();
35} 37}
36 38
37void GetApplicationPidGeneric(HLERequestContext& ctx, 39void GetApplicationPidGeneric(HLERequestContext& ctx, ProcessList& process_list) {
38 const std::vector<Kernel::KProcess*>& process_list) { 40 auto process = SearchProcessList(process_list, [](auto& p) { return p->IsApplication(); });
39 const auto process = SearchProcessList(process_list, [](const auto& proc) {
40 return proc->GetProcessId() == Kernel::KProcess::ProcessIdMin;
41 });
42 41
43 IPC::ResponseBuilder rb{ctx, 4}; 42 IPC::ResponseBuilder rb{ctx, 4};
44 rb.Push(ResultSuccess); 43 rb.Push(ResultSuccess);
45 rb.Push(process.has_value() ? (*process)->GetProcessId() : NO_PROCESS_FOUND_PID); 44 rb.Push(process.IsNull() ? NO_PROCESS_FOUND_PID : process->GetProcessId());
46} 45}
47 46
48} // Anonymous namespace 47} // Anonymous namespace
@@ -80,8 +79,7 @@ private:
80 79
81class DebugMonitor final : public ServiceFramework<DebugMonitor> { 80class DebugMonitor final : public ServiceFramework<DebugMonitor> {
82public: 81public:
83 explicit DebugMonitor(Core::System& system_) 82 explicit DebugMonitor(Core::System& system_) : ServiceFramework{system_, "pm:dmnt"} {
84 : ServiceFramework{system_, "pm:dmnt"}, kernel{system_.Kernel()} {
85 // clang-format off 83 // clang-format off
86 static const FunctionInfo functions[] = { 84 static const FunctionInfo functions[] = {
87 {0, nullptr, "GetJitDebugProcessIdList"}, 85 {0, nullptr, "GetJitDebugProcessIdList"},
@@ -106,12 +104,11 @@ private:
106 104
107 LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id); 105 LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
108 106
109 const auto process = 107 auto list = kernel.GetProcessList();
110 SearchProcessList(kernel.GetProcessList(), [program_id](const auto& proc) { 108 auto process = SearchProcessList(
111 return proc->GetProgramId() == program_id; 109 list, [program_id](auto& p) { return p->GetProgramId() == program_id; });
112 });
113 110
114 if (!process.has_value()) { 111 if (process.IsNull()) {
115 IPC::ResponseBuilder rb{ctx, 2}; 112 IPC::ResponseBuilder rb{ctx, 2};
116 rb.Push(ResultProcessNotFound); 113 rb.Push(ResultProcessNotFound);
117 return; 114 return;
@@ -119,12 +116,13 @@ private:
119 116
120 IPC::ResponseBuilder rb{ctx, 4}; 117 IPC::ResponseBuilder rb{ctx, 4};
121 rb.Push(ResultSuccess); 118 rb.Push(ResultSuccess);
122 rb.Push((*process)->GetProcessId()); 119 rb.Push(process->GetProcessId());
123 } 120 }
124 121
125 void GetApplicationProcessId(HLERequestContext& ctx) { 122 void GetApplicationProcessId(HLERequestContext& ctx) {
126 LOG_DEBUG(Service_PM, "called"); 123 LOG_DEBUG(Service_PM, "called");
127 GetApplicationPidGeneric(ctx, kernel.GetProcessList()); 124 auto list = kernel.GetProcessList();
125 GetApplicationPidGeneric(ctx, list);
128 } 126 }
129 127
130 void AtmosphereGetProcessInfo(HLERequestContext& ctx) { 128 void AtmosphereGetProcessInfo(HLERequestContext& ctx) {
@@ -135,11 +133,10 @@ private:
135 133
136 LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid); 134 LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid);
137 135
138 const auto process = SearchProcessList(kernel.GetProcessList(), [pid](const auto& proc) { 136 auto list = kernel.GetProcessList();
139 return proc->GetProcessId() == pid; 137 auto process = SearchProcessList(list, [pid](auto& p) { return p->GetProcessId() == pid; });
140 });
141 138
142 if (!process.has_value()) { 139 if (process.IsNull()) {
143 IPC::ResponseBuilder rb{ctx, 2}; 140 IPC::ResponseBuilder rb{ctx, 2};
144 rb.Push(ResultProcessNotFound); 141 rb.Push(ResultProcessNotFound);
145 return; 142 return;
@@ -159,7 +156,7 @@ private:
159 156
160 OverrideStatus override_status{}; 157 OverrideStatus override_status{};
161 ProgramLocation program_location{ 158 ProgramLocation program_location{
162 .program_id = (*process)->GetProgramId(), 159 .program_id = process->GetProgramId(),
163 .storage_id = 0, 160 .storage_id = 0,
164 }; 161 };
165 162
@@ -169,14 +166,11 @@ private:
169 rb.PushRaw(program_location); 166 rb.PushRaw(program_location);
170 rb.PushRaw(override_status); 167 rb.PushRaw(override_status);
171 } 168 }
172
173 const Kernel::KernelCore& kernel;
174}; 169};
175 170
176class Info final : public ServiceFramework<Info> { 171class Info final : public ServiceFramework<Info> {
177public: 172public:
178 explicit Info(Core::System& system_, const std::vector<Kernel::KProcess*>& process_list_) 173 explicit Info(Core::System& system_) : ServiceFramework{system_, "pm:info"} {
179 : ServiceFramework{system_, "pm:info"}, process_list{process_list_} {
180 static const FunctionInfo functions[] = { 174 static const FunctionInfo functions[] = {
181 {0, &Info::GetProgramId, "GetProgramId"}, 175 {0, &Info::GetProgramId, "GetProgramId"},
182 {65000, &Info::AtmosphereGetProcessId, "AtmosphereGetProcessId"}, 176 {65000, &Info::AtmosphereGetProcessId, "AtmosphereGetProcessId"},
@@ -193,11 +187,11 @@ private:
193 187
194 LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id); 188 LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id);
195 189
196 const auto process = SearchProcessList(process_list, [process_id](const auto& proc) { 190 auto list = kernel.GetProcessList();
197 return proc->GetProcessId() == process_id; 191 auto process = SearchProcessList(
198 }); 192 list, [process_id](auto& p) { return p->GetProcessId() == process_id; });
199 193
200 if (!process.has_value()) { 194 if (process.IsNull()) {
201 IPC::ResponseBuilder rb{ctx, 2}; 195 IPC::ResponseBuilder rb{ctx, 2};
202 rb.Push(ResultProcessNotFound); 196 rb.Push(ResultProcessNotFound);
203 return; 197 return;
@@ -205,7 +199,7 @@ private:
205 199
206 IPC::ResponseBuilder rb{ctx, 4}; 200 IPC::ResponseBuilder rb{ctx, 4};
207 rb.Push(ResultSuccess); 201 rb.Push(ResultSuccess);
208 rb.Push((*process)->GetProgramId()); 202 rb.Push(process->GetProgramId());
209 } 203 }
210 204
211 void AtmosphereGetProcessId(HLERequestContext& ctx) { 205 void AtmosphereGetProcessId(HLERequestContext& ctx) {
@@ -214,11 +208,11 @@ private:
214 208
215 LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id); 209 LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
216 210
217 const auto process = SearchProcessList(process_list, [program_id](const auto& proc) { 211 auto list = system.Kernel().GetProcessList();
218 return proc->GetProgramId() == program_id; 212 auto process = SearchProcessList(
219 }); 213 list, [program_id](auto& p) { return p->GetProgramId() == program_id; });
220 214
221 if (!process.has_value()) { 215 if (process.IsNull()) {
222 IPC::ResponseBuilder rb{ctx, 2}; 216 IPC::ResponseBuilder rb{ctx, 2};
223 rb.Push(ResultProcessNotFound); 217 rb.Push(ResultProcessNotFound);
224 return; 218 return;
@@ -226,16 +220,13 @@ private:
226 220
227 IPC::ResponseBuilder rb{ctx, 4}; 221 IPC::ResponseBuilder rb{ctx, 4};
228 rb.Push(ResultSuccess); 222 rb.Push(ResultSuccess);
229 rb.Push((*process)->GetProcessId()); 223 rb.Push(process->GetProcessId());
230 } 224 }
231
232 const std::vector<Kernel::KProcess*>& process_list;
233}; 225};
234 226
235class Shell final : public ServiceFramework<Shell> { 227class Shell final : public ServiceFramework<Shell> {
236public: 228public:
237 explicit Shell(Core::System& system_) 229 explicit Shell(Core::System& system_) : ServiceFramework{system_, "pm:shell"} {
238 : ServiceFramework{system_, "pm:shell"}, kernel{system_.Kernel()} {
239 // clang-format off 230 // clang-format off
240 static const FunctionInfo functions[] = { 231 static const FunctionInfo functions[] = {
241 {0, nullptr, "LaunchProgram"}, 232 {0, nullptr, "LaunchProgram"},
@@ -257,10 +248,9 @@ public:
257private: 248private:
258 void GetApplicationProcessIdForShell(HLERequestContext& ctx) { 249 void GetApplicationProcessIdForShell(HLERequestContext& ctx) {
259 LOG_DEBUG(Service_PM, "called"); 250 LOG_DEBUG(Service_PM, "called");
260 GetApplicationPidGeneric(ctx, kernel.GetProcessList()); 251 auto list = kernel.GetProcessList();
252 GetApplicationPidGeneric(ctx, list);
261 } 253 }
262
263 const Kernel::KernelCore& kernel;
264}; 254};
265 255
266void LoopProcess(Core::System& system) { 256void LoopProcess(Core::System& system) {
@@ -268,8 +258,7 @@ void LoopProcess(Core::System& system) {
268 258
269 server_manager->RegisterNamedService("pm:bm", std::make_shared<BootMode>(system)); 259 server_manager->RegisterNamedService("pm:bm", std::make_shared<BootMode>(system));
270 server_manager->RegisterNamedService("pm:dmnt", std::make_shared<DebugMonitor>(system)); 260 server_manager->RegisterNamedService("pm:dmnt", std::make_shared<DebugMonitor>(system));
271 server_manager->RegisterNamedService( 261 server_manager->RegisterNamedService("pm:info", std::make_shared<Info>(system));
272 "pm:info", std::make_shared<Info>(system, system.Kernel().GetProcessList()));
273 server_manager->RegisterNamedService("pm:shell", std::make_shared<Shell>(system)); 262 server_manager->RegisterNamedService("pm:shell", std::make_shared<Shell>(system));
274 ServerManager::RunServer(std::move(server_manager)); 263 ServerManager::RunServer(std::move(server_manager));
275} 264}
diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp
index 15edb23e0..8ef49387d 100644
--- a/src/core/hle/service/server_manager.cpp
+++ b/src/core/hle/service/server_manager.cpp
@@ -256,8 +256,13 @@ Result ServerManager::WaitAndProcessImpl() {
256 256
257 // Wait for a signal. 257 // Wait for a signal.
258 s32 out_index{-1}; 258 s32 out_index{-1};
259 R_TRY(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(), 259 R_TRY_CATCH(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index,
260 num_objs, -1)); 260 wait_objs.data(), num_objs, -1)) {
261 R_CATCH(Kernel::ResultSessionClosed) {
262 // On session closed, index is updated and we don't want to return an error.
263 }
264 }
265 R_END_TRY_CATCH;
261 ASSERT(out_index >= 0 && out_index < num_objs); 266 ASSERT(out_index >= 0 && out_index < num_objs);
262 267
263 // Set the output index. 268 // Set the output index.
diff --git a/src/core/hle/service/set/system_settings.cpp b/src/core/hle/service/set/system_settings.cpp
index 2723417ad..5977429b2 100644
--- a/src/core/hle/service/set/system_settings.cpp
+++ b/src/core/hle/service/set/system_settings.cpp
@@ -28,7 +28,7 @@ SystemSettings DefaultSystemSettings() {
28 .cmu_mode = CmuMode::None, 28 .cmu_mode = CmuMode::None,
29 .tv_underscan = {}, 29 .tv_underscan = {},
30 .tv_gama = 1.0f, 30 .tv_gama = 1.0f,
31 .constrast_ratio = 0.5f, 31 .contrast_ratio = 0.5f,
32 }; 32 };
33 33
34 settings.initial_launch_settings_packed = { 34 settings.initial_launch_settings_packed = {
diff --git a/src/core/hle/service/set/system_settings.h b/src/core/hle/service/set/system_settings.h
index ded2906ad..6ec9e71e7 100644
--- a/src/core/hle/service/set/system_settings.h
+++ b/src/core/hle/service/set/system_settings.h
@@ -208,7 +208,7 @@ struct TvSettings {
208 CmuMode cmu_mode; 208 CmuMode cmu_mode;
209 u32 tv_underscan; 209 u32 tv_underscan;
210 f32 tv_gama; 210 f32 tv_gama;
211 f32 constrast_ratio; 211 f32 contrast_ratio;
212}; 212};
213static_assert(sizeof(TvSettings) == 0x20, "TvSettings is an invalid size"); 213static_assert(sizeof(TvSettings) == 0x20, "TvSettings is an invalid size");
214 214
@@ -341,7 +341,7 @@ struct SystemSettings {
341 std::array<u8, 0x3C> reserved_09934; 341 std::array<u8, 0x3C> reserved_09934;
342 342
343 // nn::settings::system::ErrorReportSharePermission 343 // nn::settings::system::ErrorReportSharePermission
344 ErrorReportSharePermission error_report_share_permssion; 344 ErrorReportSharePermission error_report_share_permission;
345 345
346 std::array<u8, 0x3C> reserved_09974; 346 std::array<u8, 0x3C> reserved_09974;
347 347
diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp
index f7ad6193e..af9348522 100644
--- a/src/core/hle/service/set/system_settings_server.cpp
+++ b/src/core/hle/service/set/system_settings_server.cpp
@@ -721,10 +721,10 @@ void ISystemSettingsServer::SetTvSettings(HLERequestContext& ctx) {
721 SetSaveNeeded(); 721 SetSaveNeeded();
722 722
723 LOG_INFO(Service_SET, 723 LOG_INFO(Service_SET,
724 "called, flags={}, cmu_mode={}, constrast_ratio={}, hdmi_content_type={}, " 724 "called, flags={}, cmu_mode={}, contrast_ratio={}, hdmi_content_type={}, "
725 "rgb_range={}, tv_gama={}, tv_resolution={}, tv_underscan={}", 725 "rgb_range={}, tv_gama={}, tv_resolution={}, tv_underscan={}",
726 m_system_settings.tv_settings.flags.raw, m_system_settings.tv_settings.cmu_mode, 726 m_system_settings.tv_settings.flags.raw, m_system_settings.tv_settings.cmu_mode,
727 m_system_settings.tv_settings.constrast_ratio, 727 m_system_settings.tv_settings.contrast_ratio,
728 m_system_settings.tv_settings.hdmi_content_type, 728 m_system_settings.tv_settings.hdmi_content_type,
729 m_system_settings.tv_settings.rgb_range, m_system_settings.tv_settings.tv_gama, 729 m_system_settings.tv_settings.rgb_range, m_system_settings.tv_settings.tv_gama,
730 m_system_settings.tv_settings.tv_resolution, 730 m_system_settings.tv_settings.tv_resolution,
@@ -870,10 +870,10 @@ void ISystemSettingsServer::GetInitialLaunchSettings(HLERequestContext& ctx) {
870 870
871void ISystemSettingsServer::SetInitialLaunchSettings(HLERequestContext& ctx) { 871void ISystemSettingsServer::SetInitialLaunchSettings(HLERequestContext& ctx) {
872 IPC::RequestParser rp{ctx}; 872 IPC::RequestParser rp{ctx};
873 auto inital_launch_settings = rp.PopRaw<InitialLaunchSettings>(); 873 auto initial_launch_settings = rp.PopRaw<InitialLaunchSettings>();
874 874
875 m_system_settings.initial_launch_settings_packed.flags = inital_launch_settings.flags; 875 m_system_settings.initial_launch_settings_packed.flags = initial_launch_settings.flags;
876 m_system_settings.initial_launch_settings_packed.timestamp = inital_launch_settings.timestamp; 876 m_system_settings.initial_launch_settings_packed.timestamp = initial_launch_settings.timestamp;
877 SetSaveNeeded(); 877 SetSaveNeeded();
878 878
879 LOG_INFO(Service_SET, "called, flags={}, timestamp={}", 879 LOG_INFO(Service_SET, "called, flags={}, timestamp={}",
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index 71ce9be50..e2d9cd98a 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -71,18 +71,7 @@ size_t Display::GetNumLayers() const {
71 return std::ranges::count_if(layers, [](auto& l) { return l->IsOpen(); }); 71 return std::ranges::count_if(layers, [](auto& l) { return l->IsOpen(); });
72} 72}
73 73
74Result Display::GetVSyncEvent(Kernel::KReadableEvent** out_vsync_event) { 74Kernel::KReadableEvent* Display::GetVSyncEvent() {
75 if (got_vsync_event) {
76 return ResultPermissionDenied;
77 }
78
79 got_vsync_event = true;
80
81 *out_vsync_event = GetVSyncEventUnchecked();
82 return ResultSuccess;
83}
84
85Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() {
86 return &vsync_event->GetReadableEvent(); 75 return &vsync_event->GetReadableEvent();
87} 76}
88 77
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 1d9360b96..7e68ee79b 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -74,16 +74,8 @@ public:
74 74
75 std::size_t GetNumLayers() const; 75 std::size_t GetNumLayers() const;
76 76
77 /**
78 * Gets the internal vsync event.
79 *
80 * @returns The internal Vsync event if it has not yet been retrieved,
81 * VI::ResultPermissionDenied otherwise.
82 */
83 [[nodiscard]] Result GetVSyncEvent(Kernel::KReadableEvent** out_vsync_event);
84
85 /// Gets the internal vsync event. 77 /// Gets the internal vsync event.
86 Kernel::KReadableEvent* GetVSyncEventUnchecked(); 78 Kernel::KReadableEvent* GetVSyncEvent();
87 79
88 /// Signals the internal vsync event. 80 /// Signals the internal vsync event.
89 void SignalVSyncEvent(); 81 void SignalVSyncEvent();
@@ -104,7 +96,6 @@ public:
104 /// Resets the display for a new connection. 96 /// Resets the display for a new connection.
105 void Reset() { 97 void Reset() {
106 layers.clear(); 98 layers.clear();
107 got_vsync_event = false;
108 } 99 }
109 100
110 /// Attempts to find a layer with the given ID. 101 /// Attempts to find a layer with the given ID.
@@ -133,7 +124,6 @@ private:
133 124
134 std::vector<std::unique_ptr<Layer>> layers; 125 std::vector<std::unique_ptr<Layer>> layers;
135 Kernel::KEvent* vsync_event{}; 126 Kernel::KEvent* vsync_event{};
136 bool got_vsync_event{false};
137}; 127};
138 128
139} // namespace Service::VI 129} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 9ab8788e3..39d5be90d 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -343,8 +343,8 @@ private:
343 343
344class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { 344class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
345public: 345public:
346 explicit IManagerDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_) 346 explicit IManagerDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_)
347 : ServiceFramework{system_, "IManagerDisplayService"}, nv_flinger{nv_flinger_} { 347 : ServiceFramework{system_, "IManagerDisplayService"}, nvnflinger{nvnflinger_} {
348 // clang-format off 348 // clang-format off
349 static const FunctionInfo functions[] = { 349 static const FunctionInfo functions[] = {
350 {200, nullptr, "AllocateProcessHeapBlock"}, 350 {200, nullptr, "AllocateProcessHeapBlock"},
@@ -440,7 +440,7 @@ private:
440 IPC::RequestParser rp{ctx}; 440 IPC::RequestParser rp{ctx};
441 const u64 display = rp.Pop<u64>(); 441 const u64 display = rp.Pop<u64>();
442 442
443 const Result rc = nv_flinger.CloseDisplay(display) ? ResultSuccess : ResultUnknown; 443 const Result rc = nvnflinger.CloseDisplay(display) ? ResultSuccess : ResultUnknown;
444 444
445 IPC::ResponseBuilder rb{ctx, 2}; 445 IPC::ResponseBuilder rb{ctx, 2};
446 rb.Push(rc); 446 rb.Push(rc);
@@ -457,7 +457,7 @@ private:
457 "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}", 457 "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}",
458 unknown, display, aruid); 458 unknown, display, aruid);
459 459
460 const auto layer_id = nv_flinger.CreateLayer(display); 460 const auto layer_id = nvnflinger.CreateLayer(display);
461 if (!layer_id) { 461 if (!layer_id) {
462 LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); 462 LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display);
463 IPC::ResponseBuilder rb{ctx, 2}; 463 IPC::ResponseBuilder rb{ctx, 2};
@@ -494,14 +494,14 @@ private:
494 rb.Push(ResultSuccess); 494 rb.Push(ResultSuccess);
495 } 495 }
496 496
497 Nvnflinger::Nvnflinger& nv_flinger; 497 Nvnflinger::Nvnflinger& nvnflinger;
498}; 498};
499 499
500class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { 500class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
501public: 501public:
502 IApplicationDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, 502 IApplicationDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_,
503 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_) 503 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_)
504 : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_}, 504 : ServiceFramework{system_, "IApplicationDisplayService"}, nvnflinger{nvnflinger_},
505 hos_binder_driver_server{hos_binder_driver_server_} { 505 hos_binder_driver_server{hos_binder_driver_server_} {
506 506
507 static const FunctionInfo functions[] = { 507 static const FunctionInfo functions[] = {
@@ -564,7 +564,7 @@ private:
564 564
565 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 565 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
566 rb.Push(ResultSuccess); 566 rb.Push(ResultSuccess);
567 rb.PushIpcInterface<ISystemDisplayService>(system, nv_flinger); 567 rb.PushIpcInterface<ISystemDisplayService>(system, nvnflinger);
568 } 568 }
569 569
570 void GetManagerDisplayService(HLERequestContext& ctx) { 570 void GetManagerDisplayService(HLERequestContext& ctx) {
@@ -572,7 +572,7 @@ private:
572 572
573 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 573 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
574 rb.Push(ResultSuccess); 574 rb.Push(ResultSuccess);
575 rb.PushIpcInterface<IManagerDisplayService>(system, nv_flinger); 575 rb.PushIpcInterface<IManagerDisplayService>(system, nvnflinger);
576 } 576 }
577 577
578 void GetIndirectDisplayTransactionService(HLERequestContext& ctx) { 578 void GetIndirectDisplayTransactionService(HLERequestContext& ctx) {
@@ -607,7 +607,7 @@ private:
607 607
608 ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); 608 ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
609 609
610 const auto display_id = nv_flinger.OpenDisplay(name); 610 const auto display_id = nvnflinger.OpenDisplay(name);
611 if (!display_id) { 611 if (!display_id) {
612 LOG_ERROR(Service_VI, "Display not found! display_name={}", name); 612 LOG_ERROR(Service_VI, "Display not found! display_name={}", name);
613 IPC::ResponseBuilder rb{ctx, 2}; 613 IPC::ResponseBuilder rb{ctx, 2};
@@ -624,7 +624,7 @@ private:
624 IPC::RequestParser rp{ctx}; 624 IPC::RequestParser rp{ctx};
625 const u64 display_id = rp.Pop<u64>(); 625 const u64 display_id = rp.Pop<u64>();
626 626
627 const Result rc = nv_flinger.CloseDisplay(display_id) ? ResultSuccess : ResultUnknown; 627 const Result rc = nvnflinger.CloseDisplay(display_id) ? ResultSuccess : ResultUnknown;
628 628
629 IPC::ResponseBuilder rb{ctx, 2}; 629 IPC::ResponseBuilder rb{ctx, 2};
630 rb.Push(rc); 630 rb.Push(rc);
@@ -703,7 +703,7 @@ private:
703 703
704 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid); 704 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid);
705 705
706 const auto display_id = nv_flinger.OpenDisplay(display_name); 706 const auto display_id = nvnflinger.OpenDisplay(display_name);
707 if (!display_id) { 707 if (!display_id) {
708 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); 708 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id);
709 IPC::ResponseBuilder rb{ctx, 2}; 709 IPC::ResponseBuilder rb{ctx, 2};
@@ -711,7 +711,7 @@ private:
711 return; 711 return;
712 } 712 }
713 713
714 const auto buffer_queue_id = nv_flinger.FindBufferQueueId(*display_id, layer_id); 714 const auto buffer_queue_id = nvnflinger.FindBufferQueueId(*display_id, layer_id);
715 if (!buffer_queue_id) { 715 if (!buffer_queue_id) {
716 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); 716 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id);
717 IPC::ResponseBuilder rb{ctx, 2}; 717 IPC::ResponseBuilder rb{ctx, 2};
@@ -719,7 +719,7 @@ private:
719 return; 719 return;
720 } 720 }
721 721
722 nv_flinger.OpenLayer(layer_id); 722 nvnflinger.OpenLayer(layer_id);
723 723
724 android::OutputParcel parcel; 724 android::OutputParcel parcel;
725 parcel.WriteInterface(NativeWindow{*buffer_queue_id}); 725 parcel.WriteInterface(NativeWindow{*buffer_queue_id});
@@ -737,7 +737,7 @@ private:
737 737
738 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id); 738 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id);
739 739
740 nv_flinger.CloseLayer(layer_id); 740 nvnflinger.CloseLayer(layer_id);
741 741
742 IPC::ResponseBuilder rb{ctx, 2}; 742 IPC::ResponseBuilder rb{ctx, 2};
743 rb.Push(ResultSuccess); 743 rb.Push(ResultSuccess);
@@ -753,7 +753,7 @@ private:
753 753
754 // TODO(Subv): What's the difference between a Stray and a Managed layer? 754 // TODO(Subv): What's the difference between a Stray and a Managed layer?
755 755
756 const auto layer_id = nv_flinger.CreateLayer(display_id); 756 const auto layer_id = nvnflinger.CreateLayer(display_id);
757 if (!layer_id) { 757 if (!layer_id) {
758 LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id); 758 LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id);
759 IPC::ResponseBuilder rb{ctx, 2}; 759 IPC::ResponseBuilder rb{ctx, 2};
@@ -761,7 +761,7 @@ private:
761 return; 761 return;
762 } 762 }
763 763
764 const auto buffer_queue_id = nv_flinger.FindBufferQueueId(display_id, *layer_id); 764 const auto buffer_queue_id = nvnflinger.FindBufferQueueId(display_id, *layer_id);
765 if (!buffer_queue_id) { 765 if (!buffer_queue_id) {
766 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); 766 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id);
767 IPC::ResponseBuilder rb{ctx, 2}; 767 IPC::ResponseBuilder rb{ctx, 2};
@@ -785,7 +785,7 @@ private:
785 const u64 layer_id = rp.Pop<u64>(); 785 const u64 layer_id = rp.Pop<u64>();
786 786
787 LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}", layer_id); 787 LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}", layer_id);
788 nv_flinger.DestroyLayer(layer_id); 788 nvnflinger.DestroyLayer(layer_id);
789 789
790 IPC::ResponseBuilder rb{ctx, 2}; 790 IPC::ResponseBuilder rb{ctx, 2};
791 rb.Push(ResultSuccess); 791 rb.Push(ResultSuccess);
@@ -798,7 +798,7 @@ private:
798 LOG_DEBUG(Service_VI, "called. display_id={}", display_id); 798 LOG_DEBUG(Service_VI, "called. display_id={}", display_id);
799 799
800 Kernel::KReadableEvent* vsync_event{}; 800 Kernel::KReadableEvent* vsync_event{};
801 const auto result = nv_flinger.FindVsyncEvent(&vsync_event, display_id); 801 const auto result = nvnflinger.FindVsyncEvent(&vsync_event, display_id);
802 if (result != ResultSuccess) { 802 if (result != ResultSuccess) {
803 if (result == ResultNotFound) { 803 if (result == ResultNotFound) {
804 LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); 804 LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id);
@@ -808,6 +808,12 @@ private:
808 rb.Push(result); 808 rb.Push(result);
809 return; 809 return;
810 } 810 }
811 if (vsync_event_fetched) {
812 IPC::ResponseBuilder rb{ctx, 2};
813 rb.Push(VI::ResultPermissionDenied);
814 return;
815 }
816 vsync_event_fetched = true;
811 817
812 IPC::ResponseBuilder rb{ctx, 2, 1}; 818 IPC::ResponseBuilder rb{ctx, 2, 1};
813 rb.Push(ResultSuccess); 819 rb.Push(ResultSuccess);
@@ -899,8 +905,9 @@ private:
899 } 905 }
900 } 906 }
901 907
902 Nvnflinger::Nvnflinger& nv_flinger; 908 Nvnflinger::Nvnflinger& nvnflinger;
903 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; 909 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server;
910 bool vsync_event_fetched{false};
904}; 911};
905 912
906static bool IsValidServiceAccess(Permission permission, Policy policy) { 913static bool IsValidServiceAccess(Permission permission, Policy policy) {
@@ -916,7 +923,7 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) {
916} 923}
917 924
918void detail::GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system, 925void detail::GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system,
919 Nvnflinger::Nvnflinger& nv_flinger, 926 Nvnflinger::Nvnflinger& nvnflinger,
920 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server, 927 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server,
921 Permission permission) { 928 Permission permission) {
922 IPC::RequestParser rp{ctx}; 929 IPC::RequestParser rp{ctx};
@@ -931,19 +938,19 @@ void detail::GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system,
931 938
932 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 939 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
933 rb.Push(ResultSuccess); 940 rb.Push(ResultSuccess);
934 rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger, hos_binder_driver_server); 941 rb.PushIpcInterface<IApplicationDisplayService>(system, nvnflinger, hos_binder_driver_server);
935} 942}
936 943
937void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nv_flinger, 944void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nvnflinger,
938 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server) { 945 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server) {
939 auto server_manager = std::make_unique<ServerManager>(system); 946 auto server_manager = std::make_unique<ServerManager>(system);
940 947
941 server_manager->RegisterNamedService( 948 server_manager->RegisterNamedService(
942 "vi:m", std::make_shared<VI_M>(system, nv_flinger, hos_binder_driver_server)); 949 "vi:m", std::make_shared<VI_M>(system, nvnflinger, hos_binder_driver_server));
943 server_manager->RegisterNamedService( 950 server_manager->RegisterNamedService(
944 "vi:s", std::make_shared<VI_S>(system, nv_flinger, hos_binder_driver_server)); 951 "vi:s", std::make_shared<VI_S>(system, nvnflinger, hos_binder_driver_server));
945 server_manager->RegisterNamedService( 952 server_manager->RegisterNamedService(
946 "vi:u", std::make_shared<VI_U>(system, nv_flinger, hos_binder_driver_server)); 953 "vi:u", std::make_shared<VI_U>(system, nvnflinger, hos_binder_driver_server));
947 ServerManager::RunServer(std::move(server_manager)); 954 ServerManager::RunServer(std::move(server_manager));
948} 955}
949 956
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index a35b62f97..ee4bcbcfa 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -48,7 +48,7 @@ void GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system,
48 Permission permission); 48 Permission permission);
49} // namespace detail 49} // namespace detail
50 50
51void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nv_flinger, 51void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nvnflinger,
52 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server); 52 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server);
53 53
54} // namespace Service::VI 54} // namespace Service::VI
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index c9f8707b7..9b75c660c 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -19,8 +19,54 @@
19#include "core/arm/nce/patcher.h" 19#include "core/arm/nce/patcher.h"
20#endif 20#endif
21 21
22#ifndef HAS_NCE
23namespace Core::NCE {
24class Patcher {};
25} // namespace Core::NCE
26#endif
27
22namespace Loader { 28namespace Loader {
23 29
30struct PatchCollection {
31 explicit PatchCollection(bool is_application_) : is_application{is_application_} {
32 module_patcher_indices.fill(-1);
33 patchers.emplace_back();
34 }
35
36 std::vector<Core::NCE::Patcher>* GetPatchers() {
37 if (is_application && Settings::IsNceEnabled()) {
38 return &patchers;
39 }
40 return nullptr;
41 }
42
43 size_t GetTotalPatchSize() const {
44 size_t total_size{};
45#ifdef HAS_NCE
46 for (auto& patcher : patchers) {
47 total_size += patcher.GetSectionSize();
48 }
49#endif
50 return total_size;
51 }
52
53 void SaveIndex(size_t module) {
54 module_patcher_indices[module] = static_cast<s32>(patchers.size() - 1);
55 }
56
57 s32 GetIndex(size_t module) const {
58 return module_patcher_indices[module];
59 }
60
61 s32 GetLastIndex() const {
62 return static_cast<s32>(patchers.size()) - 1;
63 }
64
65 bool is_application;
66 std::vector<Core::NCE::Patcher> patchers;
67 std::array<s32, 13> module_patcher_indices{};
68};
69
24AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, 70AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_,
25 bool override_update_) 71 bool override_update_)
26 : AppLoader(std::move(file_)), override_update(override_update_), is_hbl(false) { 72 : AppLoader(std::move(file_)), override_update(override_update_), is_hbl(false) {
@@ -142,18 +188,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
142 std::size_t code_size{}; 188 std::size_t code_size{};
143 189
144 // Define an nce patch context for each potential module. 190 // Define an nce patch context for each potential module.
145#ifdef HAS_NCE 191 PatchCollection patch_ctx{is_application};
146 std::array<Core::NCE::Patcher, 13> module_patchers;
147#endif
148
149 const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* {
150#ifdef HAS_NCE
151 if (is_application && Settings::IsNceEnabled()) {
152 return &module_patchers[i];
153 }
154#endif
155 return nullptr;
156 };
157 192
158 // Use the NSO module loader to figure out the code layout 193 // Use the NSO module loader to figure out the code layout
159 for (size_t i = 0; i < static_modules.size(); i++) { 194 for (size_t i = 0; i < static_modules.size(); i++) {
@@ -164,13 +199,14 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
164 } 199 }
165 200
166 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; 201 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
167 const auto tentative_next_load_addr = 202 const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
168 AppLoader_NSO::LoadModule(process, system, *module_file, code_size, 203 process, system, *module_file, code_size, should_pass_arguments, false, {},
169 should_pass_arguments, false, {}, GetPatcher(i)); 204 patch_ctx.GetPatchers(), patch_ctx.GetLastIndex());
170 if (!tentative_next_load_addr) { 205 if (!tentative_next_load_addr) {
171 return {ResultStatus::ErrorLoadingNSO, {}}; 206 return {ResultStatus::ErrorLoadingNSO, {}};
172 } 207 }
173 208
209 patch_ctx.SaveIndex(i);
174 code_size = *tentative_next_load_addr; 210 code_size = *tentative_next_load_addr;
175 } 211 }
176 212
@@ -184,6 +220,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
184 return 0; 220 return 0;
185 }(); 221 }();
186 222
223 // Add patch size to the total module size
224 code_size += patch_ctx.GetTotalPatchSize();
225
187 // Setup the process code layout 226 // Setup the process code layout
188 if (process.LoadFromMetadata(metadata, code_size, fastmem_base, is_hbl).IsError()) { 227 if (process.LoadFromMetadata(metadata, code_size, fastmem_base, is_hbl).IsError()) {
189 return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; 228 return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
@@ -204,9 +243,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
204 243
205 const VAddr load_addr{next_load_addr}; 244 const VAddr load_addr{next_load_addr};
206 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; 245 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
207 const auto tentative_next_load_addr = 246 const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
208 AppLoader_NSO::LoadModule(process, system, *module_file, load_addr, 247 process, system, *module_file, load_addr, should_pass_arguments, true, pm,
209 should_pass_arguments, true, pm, GetPatcher(i)); 248 patch_ctx.GetPatchers(), patch_ctx.GetIndex(i));
210 if (!tentative_next_load_addr) { 249 if (!tentative_next_load_addr) {
211 return {ResultStatus::ErrorLoadingNSO, {}}; 250 return {ResultStatus::ErrorLoadingNSO, {}};
212 } 251 }
@@ -216,20 +255,6 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
216 LOG_DEBUG(Loader, "loaded module {} @ {:#X}", module, load_addr); 255 LOG_DEBUG(Loader, "loaded module {} @ {:#X}", module, load_addr);
217 } 256 }
218 257
219 // Find the RomFS by searching for a ".romfs" file in this directory
220 const auto& files = dir->GetFiles();
221 const auto romfs_iter =
222 std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& f) {
223 return f->GetName().find(".romfs") != std::string::npos;
224 });
225
226 // Register the RomFS if a ".romfs" file was found
227 if (romfs_iter != files.end() && *romfs_iter != nullptr) {
228 romfs = *romfs_iter;
229 system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
230 *this, system.GetContentProvider(), system.GetFileSystemController()));
231 }
232
233 is_loaded = true; 258 is_loaded = true;
234 return {ResultStatus::Success, 259 return {ResultStatus::Success,
235 LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}}; 260 LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}};
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 814407535..2a32b1276 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -74,8 +74,10 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::S
74 return load_result; 74 return load_result;
75 } 75 }
76 76
77 system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( 77 system.GetFileSystemController().RegisterProcess(
78 *this, system.GetContentProvider(), system.GetFileSystemController())); 78 process.GetProcessId(), nca->GetTitleId(),
79 std::make_shared<FileSys::RomFSFactory>(*this, system.GetContentProvider(),
80 system.GetFileSystemController()));
79 81
80 is_loaded = true; 82 is_loaded = true;
81 return load_result; 83 return load_result;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index e74697cda..f8225d697 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -275,10 +275,12 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::S
275 return {ResultStatus::ErrorLoadingNRO, {}}; 275 return {ResultStatus::ErrorLoadingNRO, {}};
276 } 276 }
277 277
278 if (romfs != nullptr) { 278 u64 program_id{};
279 system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( 279 ReadProgramId(program_id);
280 *this, system.GetContentProvider(), system.GetFileSystemController())); 280 system.GetFileSystemController().RegisterProcess(
281 } 281 process.GetProcessId(), program_id,
282 std::make_unique<FileSys::RomFSFactory>(*this, system.GetContentProvider(),
283 system.GetFileSystemController()));
282 284
283 is_loaded = true; 285 is_loaded = true;
284 return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority, 286 return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority,
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index b053a0d14..583b7e927 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -77,7 +77,8 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
77 const FileSys::VfsFile& nso_file, VAddr load_base, 77 const FileSys::VfsFile& nso_file, VAddr load_base,
78 bool should_pass_arguments, bool load_into_process, 78 bool should_pass_arguments, bool load_into_process,
79 std::optional<FileSys::PatchManager> pm, 79 std::optional<FileSys::PatchManager> pm,
80 Core::NCE::Patcher* patch) { 80 std::vector<Core::NCE::Patcher>* patches,
81 s32 patch_index) {
81 if (nso_file.GetSize() < sizeof(NSOHeader)) { 82 if (nso_file.GetSize() < sizeof(NSOHeader)) {
82 return std::nullopt; 83 return std::nullopt;
83 } 84 }
@@ -94,8 +95,11 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
94 // Allocate some space at the beginning if we are patching in PreText mode. 95 // Allocate some space at the beginning if we are patching in PreText mode.
95 const size_t module_start = [&]() -> size_t { 96 const size_t module_start = [&]() -> size_t {
96#ifdef HAS_NCE 97#ifdef HAS_NCE
97 if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::PreText) { 98 if (patches && load_into_process) {
98 return patch->GetSectionSize(); 99 auto* patch = &patches->operator[](patch_index);
100 if (patch->GetPatchMode() == Core::NCE::PatchMode::PreText) {
101 return patch->GetSectionSize();
102 }
99 } 103 }
100#endif 104#endif
101 return 0; 105 return 0;
@@ -160,27 +164,24 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
160#ifdef HAS_NCE 164#ifdef HAS_NCE
161 // If we are computing the process code layout and using nce backend, patch. 165 // If we are computing the process code layout and using nce backend, patch.
162 const auto& code = codeset.CodeSegment(); 166 const auto& code = codeset.CodeSegment();
163 if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::None) { 167 auto* patch = patches ? &patches->operator[](patch_index) : nullptr;
168 if (patch && !load_into_process) {
164 // Patch SVCs and MRS calls in the guest code 169 // Patch SVCs and MRS calls in the guest code
165 patch->PatchText(program_image, code); 170 while (!patch->PatchText(program_image, code)) {
166 171 patch = &patches->emplace_back();
167 // Add patch section size to the module size. 172 }
168 image_size += static_cast<u32>(patch->GetSectionSize());
169 } else if (patch) { 173 } else if (patch) {
170 // Relocate code patch and copy to the program_image. 174 // Relocate code patch and copy to the program_image.
171 patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers()); 175 if (patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers())) {
172 176 // Update patch section.
173 // Update patch section. 177 auto& patch_segment = codeset.PatchSegment();
174 auto& patch_segment = codeset.PatchSegment(); 178 patch_segment.addr =
175 patch_segment.addr = 179 patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size;
176 patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size; 180 patch_segment.size = static_cast<u32>(patch->GetSectionSize());
177 patch_segment.size = static_cast<u32>(patch->GetSectionSize());
178
179 // Add patch section size to the module size. In PreText mode image_size
180 // already contains the patch segment as part of module_start.
181 if (patch->GetPatchMode() == Core::NCE::PatchMode::PostData) {
182 image_size += patch_segment.size;
183 } 181 }
182
183 // Refresh image_size to take account the patch section if it was added by RelocateAndCopy
184 image_size = static_cast<u32>(program_image.size());
184 } 185 }
185#endif 186#endif
186 187
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 29b86ed4c..6356697e3 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -93,7 +93,8 @@ public:
93 const FileSys::VfsFile& nso_file, VAddr load_base, 93 const FileSys::VfsFile& nso_file, VAddr load_base,
94 bool should_pass_arguments, bool load_into_process, 94 bool should_pass_arguments, bool load_into_process,
95 std::optional<FileSys::PatchManager> pm = {}, 95 std::optional<FileSys::PatchManager> pm = {},
96 Core::NCE::Patcher* patch = nullptr); 96 std::vector<Core::NCE::Patcher>* patches = nullptr,
97 s32 patch_index = -1);
97 98
98 LoadResult Load(Kernel::KProcess& process, Core::System& system) override; 99 LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
99 100
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index f4ab75b77..28116ff3a 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -111,7 +111,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S
111 111
112 FileSys::VirtualFile update_raw; 112 FileSys::VirtualFile update_raw;
113 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { 113 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
114 system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); 114 system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(),
115 std::move(update_raw));
115 } 116 }
116 117
117 is_loaded = true; 118 is_loaded = true;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 12d72c380..e9abb199a 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -78,7 +78,8 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::KProcess& process, Core::S
78 78
79 FileSys::VirtualFile update_raw; 79 FileSys::VirtualFile update_raw;
80 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { 80 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
81 system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); 81 system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(),
82 std::move(update_raw));
82 } 83 }
83 84
84 is_loaded = true; 85 is_loaded = true;
diff --git a/src/hid_core/CMakeLists.txt b/src/hid_core/CMakeLists.txt
index cce4e6857..aa85502b5 100644
--- a/src/hid_core/CMakeLists.txt
+++ b/src/hid_core/CMakeLists.txt
@@ -36,6 +36,30 @@ add_library(hid_core STATIC
36 irsensor/processor_base.h 36 irsensor/processor_base.h
37 irsensor/tera_plugin_processor.cpp 37 irsensor/tera_plugin_processor.cpp
38 irsensor/tera_plugin_processor.h 38 irsensor/tera_plugin_processor.h
39 resources/abstracted_pad/abstract_battery_handler.cpp
40 resources/abstracted_pad/abstract_battery_handler.h
41 resources/abstracted_pad/abstract_button_handler.cpp
42 resources/abstracted_pad/abstract_button_handler.h
43 resources/abstracted_pad/abstract_ir_sensor_handler.cpp
44 resources/abstracted_pad/abstract_ir_sensor_handler.h
45 resources/abstracted_pad/abstract_led_handler.cpp
46 resources/abstracted_pad/abstract_led_handler.h
47 resources/abstracted_pad/abstract_mcu_handler.cpp
48 resources/abstracted_pad/abstract_mcu_handler.h
49 resources/abstracted_pad/abstract_nfc_handler.cpp
50 resources/abstracted_pad/abstract_nfc_handler.h
51 resources/abstracted_pad/abstract_pad.cpp
52 resources/abstracted_pad/abstract_pad.h
53 resources/abstracted_pad/abstract_pad_holder.cpp
54 resources/abstracted_pad/abstract_pad_holder.h
55 resources/abstracted_pad/abstract_palma_handler.cpp
56 resources/abstracted_pad/abstract_palma_handler.h
57 resources/abstracted_pad/abstract_properties_handler.cpp
58 resources/abstracted_pad/abstract_properties_handler.h
59 resources/abstracted_pad/abstract_sixaxis_handler.cpp
60 resources/abstracted_pad/abstract_sixaxis_handler.h
61 resources/abstracted_pad/abstract_vibration_handler.cpp
62 resources/abstracted_pad/abstract_vibration_handler.h
39 resources/debug_pad/debug_pad.cpp 63 resources/debug_pad/debug_pad.cpp
40 resources/debug_pad/debug_pad.h 64 resources/debug_pad/debug_pad.h
41 resources/debug_pad/debug_pad_types.h 65 resources/debug_pad/debug_pad_types.h
@@ -56,6 +80,8 @@ add_library(hid_core STATIC
56 resources/npad/npad_resource.cpp 80 resources/npad/npad_resource.cpp
57 resources/npad/npad_resource.h 81 resources/npad/npad_resource.h
58 resources/npad/npad_types.h 82 resources/npad/npad_types.h
83 resources/npad/npad_vibration.cpp
84 resources/npad/npad_vibration.h
59 resources/palma/palma.cpp 85 resources/palma/palma.cpp
60 resources/palma/palma.h 86 resources/palma/palma.h
61 resources/six_axis/console_six_axis.cpp 87 resources/six_axis/console_six_axis.cpp
@@ -78,6 +104,14 @@ add_library(hid_core STATIC
78 resources/touch_screen/touch_types.h 104 resources/touch_screen/touch_types.h
79 resources/unique_pad/unique_pad.cpp 105 resources/unique_pad/unique_pad.cpp
80 resources/unique_pad/unique_pad.h 106 resources/unique_pad/unique_pad.h
107 resources/vibration/gc_vibration_device.h
108 resources/vibration/gc_vibration_device.cpp
109 resources/vibration/n64_vibration_device.h
110 resources/vibration/n64_vibration_device.cpp
111 resources/vibration/vibration_base.h
112 resources/vibration/vibration_base.cpp
113 resources/vibration/vibration_device.h
114 resources/vibration/vibration_device.cpp
81 resources/applet_resource.cpp 115 resources/applet_resource.cpp
82 resources/applet_resource.h 116 resources/applet_resource.h
83 resources/controller_base.cpp 117 resources/controller_base.cpp
diff --git a/src/hid_core/frontend/emulated_controller.cpp b/src/hid_core/frontend/emulated_controller.cpp
index 3d2d1e9f9..2ab93402d 100644
--- a/src/hid_core/frontend/emulated_controller.cpp
+++ b/src/hid_core/frontend/emulated_controller.cpp
@@ -27,7 +27,7 @@ EmulatedController::~EmulatedController() = default;
27NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) { 27NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) {
28 switch (type) { 28 switch (type) {
29 case Settings::ControllerType::ProController: 29 case Settings::ControllerType::ProController:
30 return NpadStyleIndex::ProController; 30 return NpadStyleIndex::Fullkey;
31 case Settings::ControllerType::DualJoyconDetached: 31 case Settings::ControllerType::DualJoyconDetached:
32 return NpadStyleIndex::JoyconDual; 32 return NpadStyleIndex::JoyconDual;
33 case Settings::ControllerType::LeftJoycon: 33 case Settings::ControllerType::LeftJoycon:
@@ -49,13 +49,13 @@ NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerTyp
49 case Settings::ControllerType::SegaGenesis: 49 case Settings::ControllerType::SegaGenesis:
50 return NpadStyleIndex::SegaGenesis; 50 return NpadStyleIndex::SegaGenesis;
51 default: 51 default:
52 return NpadStyleIndex::ProController; 52 return NpadStyleIndex::Fullkey;
53 } 53 }
54} 54}
55 55
56Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) { 56Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) {
57 switch (type) { 57 switch (type) {
58 case NpadStyleIndex::ProController: 58 case NpadStyleIndex::Fullkey:
59 return Settings::ControllerType::ProController; 59 return Settings::ControllerType::ProController;
60 case NpadStyleIndex::JoyconDual: 60 case NpadStyleIndex::JoyconDual:
61 return Settings::ControllerType::DualJoyconDetached; 61 return Settings::ControllerType::DualJoyconDetached;
@@ -106,7 +106,7 @@ void EmulatedController::ReloadFromSettings() {
106 SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); 106 SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
107 original_npad_type = npad_type; 107 original_npad_type = npad_type;
108 } else { 108 } else {
109 SetNpadStyleIndex(NpadStyleIndex::ProController); 109 SetNpadStyleIndex(NpadStyleIndex::Fullkey);
110 original_npad_type = npad_type; 110 original_npad_type = npad_type;
111 } 111 }
112 112
@@ -509,11 +509,11 @@ void EmulatedController::ReloadInput() {
509 }); 509 });
510 } 510 }
511 turbo_button_state = 0; 511 turbo_button_state = 0;
512 is_initalized = true; 512 is_initialized = true;
513} 513}
514 514
515void EmulatedController::UnloadInput() { 515void EmulatedController::UnloadInput() {
516 is_initalized = false; 516 is_initialized = false;
517 for (auto& button : button_devices) { 517 for (auto& button : button_devices) {
518 button.reset(); 518 button.reset();
519 } 519 }
@@ -1073,7 +1073,7 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback
1073 .body = GetNpadColor(controller.color_values[index].body), 1073 .body = GetNpadColor(controller.color_values[index].body),
1074 .button = GetNpadColor(controller.color_values[index].buttons), 1074 .button = GetNpadColor(controller.color_values[index].buttons),
1075 }; 1075 };
1076 if (npad_type == NpadStyleIndex::ProController) { 1076 if (npad_type == NpadStyleIndex::Fullkey) {
1077 controller.colors_state.left = { 1077 controller.colors_state.left = {
1078 .body = GetNpadColor(controller.color_values[index].left_grip), 1078 .body = GetNpadColor(controller.color_values[index].left_grip),
1079 .button = GetNpadColor(controller.color_values[index].buttons), 1079 .button = GetNpadColor(controller.color_values[index].buttons),
@@ -1209,7 +1209,7 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1209} 1209}
1210 1210
1211bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { 1211bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
1212 if (!is_initalized) { 1212 if (!is_initialized) {
1213 return false; 1213 return false;
1214 } 1214 }
1215 if (device_index >= output_devices.size()) { 1215 if (device_index >= output_devices.size()) {
@@ -1247,7 +1247,7 @@ bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
1247 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); 1247 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
1248 const auto& player = Settings::values.players.GetValue()[player_index]; 1248 const auto& player = Settings::values.players.GetValue()[player_index];
1249 1249
1250 if (!is_initalized) { 1250 if (!is_initialized) {
1251 return false; 1251 return false;
1252 } 1252 }
1253 1253
@@ -1270,7 +1270,7 @@ Common::Input::DriverResult EmulatedController::SetPollingMode(
1270 EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) { 1270 EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) {
1271 LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index); 1271 LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index);
1272 1272
1273 if (!is_initalized) { 1273 if (!is_initialized) {
1274 return Common::Input::DriverResult::InvalidHandle; 1274 return Common::Input::DriverResult::InvalidHandle;
1275 } 1275 }
1276 1276
@@ -1319,7 +1319,7 @@ bool EmulatedController::SetCameraFormat(
1319 Core::IrSensor::ImageTransferProcessorFormat camera_format) { 1319 Core::IrSensor::ImageTransferProcessorFormat camera_format) {
1320 LOG_INFO(Service_HID, "Set camera format {}", camera_format); 1320 LOG_INFO(Service_HID, "Set camera format {}", camera_format);
1321 1321
1322 if (!is_initalized) { 1322 if (!is_initialized) {
1323 return false; 1323 return false;
1324 } 1324 }
1325 1325
@@ -1347,7 +1347,7 @@ void EmulatedController::SetRingParam(Common::ParamPackage param) {
1347 1347
1348bool EmulatedController::HasNfc() const { 1348bool EmulatedController::HasNfc() const {
1349 1349
1350 if (!is_initalized) { 1350 if (!is_initialized) {
1351 return false; 1351 return false;
1352 } 1352 }
1353 1353
@@ -1356,7 +1356,7 @@ bool EmulatedController::HasNfc() const {
1356 switch (npad_type) { 1356 switch (npad_type) {
1357 case NpadStyleIndex::JoyconRight: 1357 case NpadStyleIndex::JoyconRight:
1358 case NpadStyleIndex::JoyconDual: 1358 case NpadStyleIndex::JoyconDual:
1359 case NpadStyleIndex::ProController: 1359 case NpadStyleIndex::Fullkey:
1360 case NpadStyleIndex::Handheld: 1360 case NpadStyleIndex::Handheld:
1361 break; 1361 break;
1362 default: 1362 default:
@@ -1388,7 +1388,7 @@ bool EmulatedController::RemoveNfcHandle() {
1388} 1388}
1389 1389
1390bool EmulatedController::StartNfcPolling() { 1390bool EmulatedController::StartNfcPolling() {
1391 if (!is_initalized) { 1391 if (!is_initialized) {
1392 return false; 1392 return false;
1393 } 1393 }
1394 1394
@@ -1403,7 +1403,7 @@ bool EmulatedController::StartNfcPolling() {
1403} 1403}
1404 1404
1405bool EmulatedController::StopNfcPolling() { 1405bool EmulatedController::StopNfcPolling() {
1406 if (!is_initalized) { 1406 if (!is_initialized) {
1407 return false; 1407 return false;
1408 } 1408 }
1409 1409
@@ -1418,7 +1418,7 @@ bool EmulatedController::StopNfcPolling() {
1418} 1418}
1419 1419
1420bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { 1420bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
1421 if (!is_initalized) { 1421 if (!is_initialized) {
1422 return false; 1422 return false;
1423 } 1423 }
1424 1424
@@ -1434,7 +1434,7 @@ bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
1434 1434
1435bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, 1435bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request,
1436 Common::Input::MifareRequest& out_data) { 1436 Common::Input::MifareRequest& out_data) {
1437 if (!is_initalized) { 1437 if (!is_initialized) {
1438 return false; 1438 return false;
1439 } 1439 }
1440 1440
@@ -1450,7 +1450,7 @@ bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& requ
1450} 1450}
1451 1451
1452bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) { 1452bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) {
1453 if (!is_initalized) { 1453 if (!is_initialized) {
1454 return false; 1454 return false;
1455 } 1455 }
1456 1456
@@ -1465,7 +1465,7 @@ bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& req
1465} 1465}
1466 1466
1467bool EmulatedController::WriteNfc(const std::vector<u8>& data) { 1467bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
1468 if (!is_initalized) { 1468 if (!is_initialized) {
1469 return false; 1469 return false;
1470 } 1470 }
1471 1471
@@ -1480,7 +1480,7 @@ bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
1480} 1480}
1481 1481
1482void EmulatedController::SetLedPattern() { 1482void EmulatedController::SetLedPattern() {
1483 if (!is_initalized) { 1483 if (!is_initialized) {
1484 return; 1484 return;
1485 } 1485 }
1486 1486
@@ -1508,8 +1508,8 @@ void EmulatedController::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode mode)
1508 motion.emulated.SetGyroThreshold(motion.emulated.ThresholdLoose); 1508 motion.emulated.SetGyroThreshold(motion.emulated.ThresholdLoose);
1509 break; 1509 break;
1510 case GyroscopeZeroDriftMode::Tight: 1510 case GyroscopeZeroDriftMode::Tight:
1511 motion_sensitivity = motion.emulated.IsAtRestThight; 1511 motion_sensitivity = motion.emulated.IsAtRestTight;
1512 motion.emulated.SetGyroThreshold(motion.emulated.ThresholdThight); 1512 motion.emulated.SetGyroThreshold(motion.emulated.ThresholdTight);
1513 break; 1513 break;
1514 case GyroscopeZeroDriftMode::Standard: 1514 case GyroscopeZeroDriftMode::Standard:
1515 default: 1515 default:
@@ -1548,7 +1548,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
1548 // Fallback Fullkey controllers to Pro controllers 1548 // Fallback Fullkey controllers to Pro controllers
1549 if (IsControllerFullkey() && supported_style_tag.fullkey) { 1549 if (IsControllerFullkey() && supported_style_tag.fullkey) {
1550 LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); 1550 LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
1551 SetNpadStyleIndex(NpadStyleIndex::ProController); 1551 SetNpadStyleIndex(NpadStyleIndex::Fullkey);
1552 Connect(); 1552 Connect();
1553 return; 1553 return;
1554 } 1554 }
@@ -1556,13 +1556,13 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
1556 // Fallback Dual joycon controllers to Pro controllers 1556 // Fallback Dual joycon controllers to Pro controllers
1557 if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) { 1557 if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) {
1558 LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); 1558 LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
1559 SetNpadStyleIndex(NpadStyleIndex::ProController); 1559 SetNpadStyleIndex(NpadStyleIndex::Fullkey);
1560 Connect(); 1560 Connect();
1561 return; 1561 return;
1562 } 1562 }
1563 1563
1564 // Fallback Pro controllers to Dual joycon 1564 // Fallback Pro controllers to Dual joycon
1565 if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) { 1565 if (npad_type == NpadStyleIndex::Fullkey && supported_style_tag.joycon_dual) {
1566 LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type); 1566 LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type);
1567 SetNpadStyleIndex(NpadStyleIndex::JoyconDual); 1567 SetNpadStyleIndex(NpadStyleIndex::JoyconDual);
1568 Connect(); 1568 Connect();
@@ -1577,7 +1577,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
1577 std::scoped_lock lock{mutex}; 1577 std::scoped_lock lock{mutex};
1578 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; 1578 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
1579 switch (type) { 1579 switch (type) {
1580 case NpadStyleIndex::ProController: 1580 case NpadStyleIndex::Fullkey:
1581 case NpadStyleIndex::GameCube: 1581 case NpadStyleIndex::GameCube:
1582 case NpadStyleIndex::NES: 1582 case NpadStyleIndex::NES:
1583 case NpadStyleIndex::SNES: 1583 case NpadStyleIndex::SNES:
@@ -1593,7 +1593,7 @@ bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
1593 std::scoped_lock lock{mutex}; 1593 std::scoped_lock lock{mutex};
1594 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; 1594 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
1595 switch (type) { 1595 switch (type) {
1596 case NpadStyleIndex::ProController: 1596 case NpadStyleIndex::Fullkey:
1597 return supported_style_tag.fullkey.As<bool>(); 1597 return supported_style_tag.fullkey.As<bool>();
1598 case NpadStyleIndex::Handheld: 1598 case NpadStyleIndex::Handheld:
1599 return supported_style_tag.handheld.As<bool>(); 1599 return supported_style_tag.handheld.As<bool>();
diff --git a/src/hid_core/frontend/emulated_controller.h b/src/hid_core/frontend/emulated_controller.h
index 94798164d..90e536e07 100644
--- a/src/hid_core/frontend/emulated_controller.h
+++ b/src/hid_core/frontend/emulated_controller.h
@@ -559,7 +559,7 @@ private:
559 NpadStyleTag supported_style_tag{NpadStyleSet::All}; 559 NpadStyleTag supported_style_tag{NpadStyleSet::All};
560 bool is_connected{false}; 560 bool is_connected{false};
561 bool is_configuring{false}; 561 bool is_configuring{false};
562 bool is_initalized{false}; 562 bool is_initialized{false};
563 bool system_buttons_enabled{true}; 563 bool system_buttons_enabled{true};
564 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; 564 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
565 u32 turbo_button_state{0}; 565 u32 turbo_button_state{0};
diff --git a/src/hid_core/frontend/motion_input.h b/src/hid_core/frontend/motion_input.h
index 11678983d..c902a3a6e 100644
--- a/src/hid_core/frontend/motion_input.h
+++ b/src/hid_core/frontend/motion_input.h
@@ -13,12 +13,12 @@ class MotionInput {
13public: 13public:
14 static constexpr float ThresholdLoose = 0.01f; 14 static constexpr float ThresholdLoose = 0.01f;
15 static constexpr float ThresholdStandard = 0.007f; 15 static constexpr float ThresholdStandard = 0.007f;
16 static constexpr float ThresholdThight = 0.002f; 16 static constexpr float ThresholdTight = 0.002f;
17 17
18 static constexpr float IsAtRestRelaxed = 0.05f; 18 static constexpr float IsAtRestRelaxed = 0.05f;
19 static constexpr float IsAtRestLoose = 0.02f; 19 static constexpr float IsAtRestLoose = 0.02f;
20 static constexpr float IsAtRestStandard = 0.01f; 20 static constexpr float IsAtRestStandard = 0.01f;
21 static constexpr float IsAtRestThight = 0.005f; 21 static constexpr float IsAtRestTight = 0.005f;
22 22
23 static constexpr float GyroMaxValue = 5.0f; 23 static constexpr float GyroMaxValue = 5.0f;
24 static constexpr float AccelMaxValue = 7.0f; 24 static constexpr float AccelMaxValue = 7.0f;
diff --git a/src/hid_core/hid_result.h b/src/hid_core/hid_result.h
index bb14aa61e..df9b28c9a 100644
--- a/src/hid_core/hid_result.h
+++ b/src/hid_core/hid_result.h
@@ -15,7 +15,7 @@ constexpr Result ResultVibrationNotInitialized{ErrorModule::HID, 121};
15constexpr Result ResultVibrationInvalidStyleIndex{ErrorModule::HID, 122}; 15constexpr Result ResultVibrationInvalidStyleIndex{ErrorModule::HID, 122};
16constexpr Result ResultVibrationInvalidNpadId{ErrorModule::HID, 123}; 16constexpr Result ResultVibrationInvalidNpadId{ErrorModule::HID, 123};
17constexpr Result ResultVibrationDeviceIndexOutOfRange{ErrorModule::HID, 124}; 17constexpr Result ResultVibrationDeviceIndexOutOfRange{ErrorModule::HID, 124};
18constexpr Result ResultVibrationStrenghtOutOfRange{ErrorModule::HID, 126}; 18constexpr Result ResultVibrationStrengthOutOfRange{ErrorModule::HID, 126};
19constexpr Result ResultVibrationArraySizeMismatch{ErrorModule::HID, 131}; 19constexpr Result ResultVibrationArraySizeMismatch{ErrorModule::HID, 131};
20 20
21constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423}; 21constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423};
diff --git a/src/hid_core/hid_types.h b/src/hid_core/hid_types.h
index a81ed6af0..2c3f02f34 100644
--- a/src/hid_core/hid_types.h
+++ b/src/hid_core/hid_types.h
@@ -220,6 +220,7 @@ enum class NpadIdType : u32 {
220}; 220};
221 221
222enum class NpadInterfaceType : u8 { 222enum class NpadInterfaceType : u8 {
223 None = 0,
223 Bluetooth = 1, 224 Bluetooth = 1,
224 Rail = 2, 225 Rail = 2,
225 Usb = 3, 226 Usb = 3,
@@ -229,7 +230,7 @@ enum class NpadInterfaceType : u8 {
229// This is nn::hid::NpadStyleIndex 230// This is nn::hid::NpadStyleIndex
230enum class NpadStyleIndex : u8 { 231enum class NpadStyleIndex : u8 {
231 None = 0, 232 None = 0,
232 ProController = 3, 233 Fullkey = 3,
233 Handheld = 4, 234 Handheld = 4,
234 HandheldNES = 4, 235 HandheldNES = 4,
235 JoyconDual = 5, 236 JoyconDual = 5,
diff --git a/src/hid_core/hid_util.h b/src/hid_core/hid_util.h
index 94ff2d23a..397a87472 100644
--- a/src/hid_core/hid_util.h
+++ b/src/hid_core/hid_util.h
@@ -42,7 +42,7 @@ constexpr Result IsSixaxisHandleValid(const Core::HID::SixAxisSensorHandle& hand
42 42
43constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle& handle) { 43constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle& handle) {
44 switch (handle.npad_type) { 44 switch (handle.npad_type) {
45 case Core::HID::NpadStyleIndex::ProController: 45 case Core::HID::NpadStyleIndex::Fullkey:
46 case Core::HID::NpadStyleIndex::Handheld: 46 case Core::HID::NpadStyleIndex::Handheld:
47 case Core::HID::NpadStyleIndex::JoyconDual: 47 case Core::HID::NpadStyleIndex::JoyconDual:
48 case Core::HID::NpadStyleIndex::JoyconLeft: 48 case Core::HID::NpadStyleIndex::JoyconLeft:
diff --git a/src/hid_core/irsensor/irs_types.h b/src/hid_core/irsensor/irs_types.h
index 017f38e6c..d7354de21 100644
--- a/src/hid_core/irsensor/irs_types.h
+++ b/src/hid_core/irsensor/irs_types.h
@@ -106,8 +106,8 @@ enum class HandAnalysisMode : u32 {
106 None, 106 None,
107 Silhouette, 107 Silhouette,
108 Image, 108 Image,
109 SilhoueteAndImage, 109 SilhouetteAndImage,
110 SilhuetteOnly, 110 SilhouetteOnly,
111}; 111};
112 112
113// This is nn::irsensor::IrSensorFunctionLevel 113// This is nn::irsensor::IrSensorFunctionLevel
diff --git a/src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp
new file mode 100644
index 000000000..62fbbb0a7
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp
@@ -0,0 +1,197 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/core_timing.h"
5#include "hid_core/hid_result.h"
6#include "hid_core/hid_util.h"
7#include "hid_core/resources/abstracted_pad/abstract_battery_handler.h"
8#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
9#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
10#include "hid_core/resources/applet_resource.h"
11#include "hid_core/resources/npad/npad_types.h"
12#include "hid_core/resources/shared_memory_format.h"
13
14namespace Service::HID {
15
16NpadAbstractBatteryHandler::NpadAbstractBatteryHandler() {}
17
18NpadAbstractBatteryHandler::~NpadAbstractBatteryHandler() = default;
19
20void NpadAbstractBatteryHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
21 abstract_pad_holder = holder;
22}
23
24void NpadAbstractBatteryHandler::SetAppletResource(AppletResourceHolder* applet_resource) {
25 applet_resource_holder = applet_resource;
26}
27
28void NpadAbstractBatteryHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
29 properties_handler = handler;
30}
31
32Result NpadAbstractBatteryHandler::IncrementRefCounter() {
33 if (ref_counter == std::numeric_limits<s32>::max() - 1) {
34 return ResultNpadHandlerOverflow;
35 }
36 ref_counter++;
37 return ResultSuccess;
38}
39
40Result NpadAbstractBatteryHandler::DecrementRefCounter() {
41 if (ref_counter == 0) {
42 return ResultNpadHandlerNotInitialized;
43 }
44 ref_counter--;
45 return ResultSuccess;
46}
47
48Result NpadAbstractBatteryHandler::UpdateBatteryState(u64 aruid) {
49 const auto npad_index = NpadIdTypeToIndex(properties_handler->GetNpadId());
50 AruidData* aruid_data = applet_resource_holder->applet_resource->GetAruidData(aruid);
51 if (aruid_data == nullptr) {
52 return ResultSuccess;
53 }
54
55 auto& npad_internal_state =
56 aruid_data->shared_memory_format->npad.npad_entry[npad_index].internal_state;
57 auto& system_properties = npad_internal_state.system_properties;
58
59 system_properties.is_charging_joy_dual.Assign(dual_battery.is_charging);
60 system_properties.is_powered_joy_dual.Assign(dual_battery.is_powered);
61 system_properties.is_charging_joy_left.Assign(left_battery.is_charging);
62 system_properties.is_powered_joy_left.Assign(left_battery.is_powered);
63 system_properties.is_charging_joy_right.Assign(right_battery.is_charging);
64 system_properties.is_powered_joy_right.Assign(right_battery.is_powered);
65
66 npad_internal_state.battery_level_dual = dual_battery.battery_level;
67 npad_internal_state.battery_level_left = left_battery.battery_level;
68 npad_internal_state.battery_level_right = right_battery.battery_level;
69
70 return ResultSuccess;
71}
72
73void NpadAbstractBatteryHandler::UpdateBatteryState() {
74 if (ref_counter == 0) {
75 return;
76 }
77 has_new_battery_data = GetNewBatteryState();
78}
79
80bool NpadAbstractBatteryHandler::GetNewBatteryState() {
81 bool has_changed = false;
82 Core::HID::NpadPowerInfo new_dual_battery_state{};
83 Core::HID::NpadPowerInfo new_left_battery_state{};
84 Core::HID::NpadPowerInfo new_right_battery_state{};
85 std::array<IAbstractedPad*, 5> abstract_pads{};
86 const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
87
88 for (std::size_t i = 0; i < count; i++) {
89 auto* abstract_pad = abstract_pads[i];
90 if (!abstract_pad->internal_flags.is_connected) {
91 continue;
92 }
93 const auto power_info = abstract_pad->power_info;
94 if (power_info.battery_level > Core::HID::NpadBatteryLevel::Full) {
95 // Abort
96 continue;
97 }
98
99 const auto style = abstract_pad->assignment_style;
100
101 if (style.is_external_assigned || style.is_handheld_assigned) {
102 new_dual_battery_state = power_info;
103 }
104 if (style.is_external_left_assigned || style.is_handheld_left_assigned) {
105 new_left_battery_state = power_info;
106 }
107 if (style.is_external_right_assigned || style.is_handheld_right_assigned) {
108 new_right_battery_state = power_info;
109 }
110
111 if (abstract_pad->internal_flags.is_battery_low_ovln_required) {
112 if (abstract_pad->interface_type == Core::HID::NpadInterfaceType::Rail) {
113 // TODO
114 }
115 abstract_pad->internal_flags.is_battery_low_ovln_required.Assign(false);
116 }
117 }
118
119 if (dual_battery.battery_level != new_dual_battery_state.battery_level ||
120 dual_battery.is_charging != new_dual_battery_state.is_charging ||
121 dual_battery.is_powered != new_dual_battery_state.is_powered) {
122 has_changed = true;
123 dual_battery = new_dual_battery_state;
124 }
125
126 if (left_battery.battery_level != new_left_battery_state.battery_level ||
127 left_battery.is_charging != new_left_battery_state.is_charging ||
128 left_battery.is_powered != new_left_battery_state.is_powered) {
129 has_changed = true;
130 left_battery = new_left_battery_state;
131 }
132
133 if (right_battery.battery_level != new_right_battery_state.battery_level ||
134 right_battery.is_charging != new_right_battery_state.is_charging ||
135 right_battery.is_powered != new_right_battery_state.is_powered) {
136 has_changed = true;
137 right_battery = new_right_battery_state;
138 }
139
140 return has_changed;
141}
142
143void NpadAbstractBatteryHandler::UpdateCoreBatteryState() {
144 if (ref_counter == 0) {
145 return;
146 }
147 if (!has_new_battery_data) {
148 return;
149 }
150
151 UpdateBatteryState(0);
152}
153
154void NpadAbstractBatteryHandler::InitializeBatteryState(u64 aruid) {
155 UpdateBatteryState(aruid);
156}
157
158bool NpadAbstractBatteryHandler::HasBattery() const {
159 std::array<IAbstractedPad*, 5> abstract_pads{};
160 const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
161
162 for (std::size_t i = 0; i < count; i++) {
163 const auto* abstract_pad = abstract_pads[i];
164 if (!abstract_pad->internal_flags.is_connected) {
165 continue;
166 }
167 return abstract_pad->disabled_feature_set.has_fullkey_battery ||
168 abstract_pad->disabled_feature_set.has_left_right_joy_battery;
169 }
170
171 return false;
172}
173
174void NpadAbstractBatteryHandler::HasLeftRightBattery(bool& has_left, bool& has_right) const {
175 std::array<IAbstractedPad*, 5> abstract_pads{};
176 const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
177
178 has_left = false;
179 has_right = false;
180
181 for (std::size_t i = 0; i < count; i++) {
182 const auto* abstract_pad = abstract_pads[i];
183 if (!abstract_pad->internal_flags.is_connected) {
184 continue;
185 }
186 if (!abstract_pad->disabled_feature_set.has_fullkey_battery &&
187 !abstract_pad->disabled_feature_set.has_left_right_joy_battery) {
188 continue;
189 }
190 has_left = abstract_pad->assignment_style.is_external_left_assigned ||
191 abstract_pad->assignment_style.is_handheld_left_assigned;
192 has_right = abstract_pad->assignment_style.is_external_right_assigned ||
193 abstract_pad->assignment_style.is_handheld_right_assigned;
194 }
195}
196
197} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_battery_handler.h b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.h
new file mode 100644
index 000000000..85ac5eb72
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.h
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/result.h"
8#include "hid_core/hid_types.h"
9
10namespace Service::HID {
11struct AppletResourceHolder;
12class NpadAbstractedPadHolder;
13class NpadAbstractPropertiesHandler;
14
15/// Handles Npad request from HID interfaces
16class NpadAbstractBatteryHandler final {
17public:
18 explicit NpadAbstractBatteryHandler();
19 ~NpadAbstractBatteryHandler();
20
21 void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
22 void SetAppletResource(AppletResourceHolder* applet_resource);
23 void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
24
25 Result IncrementRefCounter();
26 Result DecrementRefCounter();
27
28 Result UpdateBatteryState(u64 aruid);
29 void UpdateBatteryState();
30 bool GetNewBatteryState();
31 void UpdateCoreBatteryState();
32 void InitializeBatteryState(u64 aruid);
33
34 bool HasBattery() const;
35 void HasLeftRightBattery(bool& has_left, bool& has_right) const;
36
37private:
38 AppletResourceHolder* applet_resource_holder{nullptr};
39 NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
40 NpadAbstractPropertiesHandler* properties_handler{nullptr};
41
42 s32 ref_counter{};
43 Core::HID::NpadPowerInfo dual_battery{};
44 Core::HID::NpadPowerInfo left_battery{};
45 Core::HID::NpadPowerInfo right_battery{};
46 bool has_new_battery_data{};
47};
48
49} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp
new file mode 100644
index 000000000..587169433
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp
@@ -0,0 +1,199 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/hid_result.h"
5#include "hid_core/hid_util.h"
6#include "hid_core/resources/abstracted_pad/abstract_button_handler.h"
7#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
8#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
9#include "hid_core/resources/applet_resource.h"
10#include "hid_core/resources/npad/npad_resource.h"
11#include "hid_core/resources/npad/npad_types.h"
12#include "hid_core/resources/shared_memory_format.h"
13
14namespace Service::HID {
15
16NpadAbstractButtonHandler::NpadAbstractButtonHandler() {}
17
18NpadAbstractButtonHandler::~NpadAbstractButtonHandler() = default;
19
20void NpadAbstractButtonHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
21 abstract_pad_holder = holder;
22}
23
24void NpadAbstractButtonHandler::SetAppletResource(AppletResourceHolder* applet_resource) {
25 applet_resource_holder = applet_resource;
26}
27
28void NpadAbstractButtonHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
29 properties_handler = handler;
30}
31
32Result NpadAbstractButtonHandler::IncrementRefCounter() {
33 if (ref_counter == std::numeric_limits<s32>::max() - 1) {
34 return ResultNpadHandlerOverflow;
35 }
36 ref_counter++;
37 return ResultSuccess;
38}
39
40Result NpadAbstractButtonHandler::DecrementRefCounter() {
41 if (ref_counter == 0) {
42 return ResultNpadHandlerNotInitialized;
43 }
44 ref_counter--;
45 return ResultSuccess;
46}
47
48Result NpadAbstractButtonHandler::UpdateAllButtonWithHomeProtection(u64 aruid) {
49 const Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
50 auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid);
51
52 if (data == nullptr) {
53 return ResultSuccess;
54 }
55
56 auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
57 UpdateButtonLifo(npad_entry, aruid);
58
59 bool is_home_button_protection_enabled{};
60 const auto result = applet_resource_holder->shared_npad_resource->GetHomeProtectionEnabled(
61 is_home_button_protection_enabled, aruid, npad_id);
62
63 if (result.IsError()) {
64 return ResultSuccess;
65 }
66
67 npad_entry.internal_state.button_properties.is_home_button_protection_enabled.Assign(
68 is_home_button_protection_enabled);
69
70 return ResultSuccess;
71}
72
73void NpadAbstractButtonHandler::UpdateAllButtonLifo() {
74 Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
75 for (std::size_t i = 0; i < AruidIndexMax; i++) {
76 auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
77 auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
78 UpdateButtonLifo(npad_entry, data->aruid);
79 }
80}
81
82void NpadAbstractButtonHandler::UpdateCoreBatteryState() {
83 Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
84 for (std::size_t i = 0; i < AruidIndexMax; i++) {
85 auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
86 auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
87 UpdateButtonLifo(npad_entry, data->aruid);
88 }
89}
90
91void NpadAbstractButtonHandler::UpdateButtonState(u64 aruid) {
92 Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
93 auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid);
94 if (data == nullptr) {
95 return;
96 }
97 auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
98 UpdateButtonLifo(npad_entry, aruid);
99}
100
101Result NpadAbstractButtonHandler::SetHomeProtection(bool is_enabled, u64 aruid) {
102 const Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
103 auto result = applet_resource_holder->shared_npad_resource->SetHomeProtectionEnabled(
104 aruid, npad_id, is_enabled);
105 if (result.IsError()) {
106 return result;
107 }
108
109 auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid);
110 if (data == nullptr) {
111 return ResultSuccess;
112 }
113
114 bool is_home_protection_enabled{};
115 result = applet_resource_holder->shared_npad_resource->GetHomeProtectionEnabled(
116 is_home_protection_enabled, aruid, npad_id);
117 if (result.IsError()) {
118 return ResultSuccess;
119 }
120
121 auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
122 npad_entry.internal_state.button_properties.is_home_button_protection_enabled.Assign(
123 is_home_protection_enabled);
124 return ResultSuccess;
125}
126
127bool NpadAbstractButtonHandler::IsButtonPressedOnConsoleMode() {
128 return is_button_pressed_on_console_mode;
129}
130
131void NpadAbstractButtonHandler::EnableCenterClamp() {
132 std::array<IAbstractedPad*, 5> abstract_pads{};
133 const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
134
135 for (std::size_t i = 0; i < count; i++) {
136 auto* abstract_pad = abstract_pads[i];
137 if (!abstract_pad->internal_flags.is_connected) {
138 continue;
139 }
140 abstract_pad->internal_flags.use_center_clamp.Assign(true);
141 }
142}
143
144void NpadAbstractButtonHandler::UpdateButtonLifo(NpadSharedMemoryEntry& shared_memory, u64 aruid) {
145 auto* npad_resource = applet_resource_holder->shared_npad_resource;
146 Core::HID::NpadStyleTag style_tag = {properties_handler->GetStyleSet(aruid)};
147 style_tag.system_ext.Assign(npad_resource->GetActiveData()->GetNpadSystemExtState());
148
149 UpdateNpadFullkeyLifo(style_tag, 0, aruid, shared_memory);
150 UpdateHandheldLifo(style_tag, 1, aruid, shared_memory);
151 UpdateJoyconDualLifo(style_tag, 2, aruid, shared_memory);
152 UpdateJoyconLeftLifo(style_tag, 3, aruid, shared_memory);
153 UpdateJoyconRightLifo(style_tag, 4, aruid, shared_memory);
154 UpdatePalmaLifo(style_tag, 5, aruid, shared_memory);
155 UpdateSystemExtLifo(style_tag, 6, aruid, shared_memory);
156}
157
158void NpadAbstractButtonHandler::UpdateNpadFullkeyLifo(Core::HID::NpadStyleTag style_tag,
159 int style_index, u64 aruid,
160 NpadSharedMemoryEntry& shared_memory) {
161 // TODO
162}
163
164void NpadAbstractButtonHandler::UpdateHandheldLifo(Core::HID::NpadStyleTag style_tag,
165 int style_index, u64 aruid,
166 NpadSharedMemoryEntry& shared_memory) {
167 // TODO
168}
169
170void NpadAbstractButtonHandler::UpdateJoyconDualLifo(Core::HID::NpadStyleTag style_tag,
171 int style_index, u64 aruid,
172 NpadSharedMemoryEntry& shared_memory) {
173 // TODO
174}
175
176void NpadAbstractButtonHandler::UpdateJoyconLeftLifo(Core::HID::NpadStyleTag style_tag,
177 int style_index, u64 aruid,
178 NpadSharedMemoryEntry& shared_memory) {
179 // TODO
180}
181
182void NpadAbstractButtonHandler::UpdateJoyconRightLifo(Core::HID::NpadStyleTag style_tag,
183 int style_index, u64 aruid,
184 NpadSharedMemoryEntry& shared_memory) {
185 // TODO
186}
187
188void NpadAbstractButtonHandler::UpdateSystemExtLifo(Core::HID::NpadStyleTag style_tag,
189 int style_index, u64 aruid,
190 NpadSharedMemoryEntry& shared_memory) {
191 // TODO
192}
193
194void NpadAbstractButtonHandler::UpdatePalmaLifo(Core::HID::NpadStyleTag style_tag, int style_index,
195 u64 aruid, NpadSharedMemoryEntry& shared_memory) {
196 // TODO
197}
198
199} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_button_handler.h b/src/hid_core/resources/abstracted_pad/abstract_button_handler.h
new file mode 100644
index 000000000..01eafe96d
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_button_handler.h
@@ -0,0 +1,75 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/result.h"
8#include "hid_core/hid_types.h"
9
10namespace Service::HID {
11struct NpadSharedMemoryEntry;
12
13struct AppletResourceHolder;
14class NpadAbstractedPadHolder;
15class NpadAbstractPropertiesHandler;
16
17/// Handles Npad request from HID interfaces
18class NpadAbstractButtonHandler final {
19public:
20 explicit NpadAbstractButtonHandler();
21 ~NpadAbstractButtonHandler();
22
23 void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
24 void SetAppletResource(AppletResourceHolder* applet_resource);
25 void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
26
27 Result IncrementRefCounter();
28 Result DecrementRefCounter();
29
30 Result UpdateAllButtonWithHomeProtection(u64 aruid);
31
32 void UpdateAllButtonLifo();
33 void UpdateCoreBatteryState();
34 void UpdateButtonState(u64 aruid);
35
36 Result SetHomeProtection(bool is_enabled, u64 aruid);
37 bool IsButtonPressedOnConsoleMode();
38 void EnableCenterClamp();
39
40 void UpdateButtonLifo(NpadSharedMemoryEntry& shared_memory, u64 aruid);
41
42 void UpdateNpadFullkeyLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid,
43 NpadSharedMemoryEntry& shared_memory);
44 void UpdateHandheldLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid,
45 NpadSharedMemoryEntry& shared_memory);
46 void UpdateJoyconDualLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid,
47 NpadSharedMemoryEntry& shared_memory);
48 void UpdateJoyconLeftLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid,
49 NpadSharedMemoryEntry& shared_memory);
50 void UpdateJoyconRightLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid,
51 NpadSharedMemoryEntry& shared_memory);
52 void UpdateSystemExtLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid,
53 NpadSharedMemoryEntry& shared_memory);
54 void UpdatePalmaLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid,
55 NpadSharedMemoryEntry& shared_memory);
56
57private:
58 struct GcTrigger {
59 float left;
60 float right;
61 };
62
63 AppletResourceHolder* applet_resource_holder{nullptr};
64 NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
65 NpadAbstractPropertiesHandler* properties_handler{nullptr};
66
67 s32 ref_counter{};
68
69 bool is_button_pressed_on_console_mode{};
70
71 u64 gc_sampling_number{};
72 GcTrigger gc_trigger_state{};
73};
74
75} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp
new file mode 100644
index 000000000..d4e4181bf
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp
@@ -0,0 +1,126 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/kernel/k_event.h"
5#include "core/hle/kernel/k_readable_event.h"
6#include "hid_core/hid_result.h"
7#include "hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h"
8#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
9#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
10#include "hid_core/resources/npad/npad_types.h"
11
12namespace Service::HID {
13
14NpadAbstractIrSensorHandler::NpadAbstractIrSensorHandler() {}
15
16NpadAbstractIrSensorHandler::~NpadAbstractIrSensorHandler() = default;
17
18void NpadAbstractIrSensorHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
19 abstract_pad_holder = holder;
20}
21
22void NpadAbstractIrSensorHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
23 properties_handler = handler;
24}
25
26Result NpadAbstractIrSensorHandler::IncrementRefCounter() {
27 if (ref_counter == std::numeric_limits<s32>::max() - 1) {
28 return ResultNpadHandlerOverflow;
29 }
30 ref_counter++;
31 return ResultSuccess;
32}
33
34Result NpadAbstractIrSensorHandler::DecrementRefCounter() {
35 if (ref_counter == 0) {
36 return ResultNpadHandlerNotInitialized;
37 }
38 ref_counter--;
39 return ResultSuccess;
40}
41
42void NpadAbstractIrSensorHandler::UpdateIrSensorState() {
43 const auto previous_state = sensor_state;
44 std::array<IAbstractedPad*, 5> abstract_pads{};
45 const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
46
47 if (count == 0) {
48 sensor_state = NpadIrSensorState::Disabled;
49 if (sensor_state == previous_state) {
50 return;
51 }
52 ir_sensor_event->Signal();
53 return;
54 }
55
56 bool is_found{};
57 for (std::size_t i = 0; i < count; i++) {
58 auto* abstract_pad = abstract_pads[i];
59 if (!abstract_pad->internal_flags.is_connected) {
60 continue;
61 }
62 if (!abstract_pad->disabled_feature_set.has_bluetooth_address) {
63 continue;
64 }
65 is_found = true;
66 xcd_handle = abstract_pad->xcd_handle;
67 }
68
69 if (is_found) {
70 if (sensor_state == NpadIrSensorState::Active) {
71 return;
72 }
73 sensor_state = NpadIrSensorState::Available;
74 if (sensor_state == previous_state) {
75 return;
76 }
77 ir_sensor_event->Signal();
78 return;
79 }
80
81 sensor_state = NpadIrSensorState::Unavailable;
82 if (sensor_state == previous_state) {
83 return;
84 }
85
86 ir_sensor_event->Signal();
87 return;
88}
89
90Result NpadAbstractIrSensorHandler::ActivateIrSensor(bool is_enabled) {
91 if (sensor_state == NpadIrSensorState::Unavailable) {
92 return ResultIrSensorIsNotReady;
93 }
94 if (is_enabled && sensor_state == NpadIrSensorState::Available) {
95 sensor_state = NpadIrSensorState::Active;
96 } else {
97 if (is_enabled) {
98 return ResultSuccess;
99 }
100 if (sensor_state != NpadIrSensorState::Active) {
101 return ResultSuccess;
102 }
103 sensor_state = NpadIrSensorState::Available;
104 }
105 ir_sensor_event->Signal();
106 return ResultSuccess;
107}
108
109Result NpadAbstractIrSensorHandler::GetIrSensorEventHandle(Kernel::KReadableEvent** out_event) {
110 *out_event = &ir_sensor_event->GetReadableEvent();
111 return ResultSuccess;
112}
113
114Result NpadAbstractIrSensorHandler::GetXcdHandleForNpadWithIrSensor(u64& handle) const {
115 if (sensor_state < NpadIrSensorState::Available) {
116 return ResultIrSensorIsNotReady;
117 }
118 handle = xcd_handle;
119 return ResultSuccess;
120}
121
122NpadIrSensorState NpadAbstractIrSensorHandler::GetSensorState() const {
123 return sensor_state;
124}
125
126} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h
new file mode 100644
index 000000000..fe8e005af
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h
@@ -0,0 +1,56 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/result.h"
8#include "hid_core/hid_types.h"
9
10namespace Kernel {
11class KEvent;
12class KReadableEvent;
13} // namespace Kernel
14
15enum class NpadIrSensorState : u32 {
16 Disabled,
17 Unavailable,
18 Available,
19 Active,
20};
21
22namespace Service::HID {
23class NpadAbstractedPadHolder;
24class NpadAbstractPropertiesHandler;
25
26/// Handles Npad request from HID interfaces
27class NpadAbstractIrSensorHandler final {
28public:
29 explicit NpadAbstractIrSensorHandler();
30 ~NpadAbstractIrSensorHandler();
31
32 void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
33 void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
34
35 Result IncrementRefCounter();
36 Result DecrementRefCounter();
37
38 void UpdateIrSensorState();
39 Result ActivateIrSensor(bool param_2);
40
41 Result GetIrSensorEventHandle(Kernel::KReadableEvent** out_event);
42
43 Result GetXcdHandleForNpadWithIrSensor(u64& handle) const;
44
45 NpadIrSensorState GetSensorState() const;
46
47private:
48 NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
49 NpadAbstractPropertiesHandler* properties_handler{nullptr};
50
51 s32 ref_counter{};
52 Kernel::KEvent* ir_sensor_event{nullptr};
53 u64 xcd_handle{};
54 NpadIrSensorState sensor_state{};
55};
56} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp
new file mode 100644
index 000000000..0b2bfe88d
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp
@@ -0,0 +1,123 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/core_timing.h"
5#include "hid_core/hid_result.h"
6#include "hid_core/hid_util.h"
7#include "hid_core/resources/abstracted_pad/abstract_led_handler.h"
8#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
9#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
10#include "hid_core/resources/applet_resource.h"
11#include "hid_core/resources/npad/npad_types.h"
12
13namespace Service::HID {
14
15NpadAbstractLedHandler::NpadAbstractLedHandler() {}
16
17NpadAbstractLedHandler::~NpadAbstractLedHandler() = default;
18
19void NpadAbstractLedHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
20 abstract_pad_holder = holder;
21}
22
23void NpadAbstractLedHandler::SetAppletResource(AppletResourceHolder* applet_resource) {
24 applet_resource_holder = applet_resource;
25}
26
27void NpadAbstractLedHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
28 properties_handler = handler;
29}
30
31Result NpadAbstractLedHandler::IncrementRefCounter() {
32 if (ref_counter == std::numeric_limits<s32>::max() - 1) {
33 return ResultNpadHandlerOverflow;
34 }
35 ref_counter++;
36 return ResultSuccess;
37}
38
39Result NpadAbstractLedHandler::DecrementRefCounter() {
40 if (ref_counter == 0) {
41 return ResultNpadHandlerNotInitialized;
42 }
43 ref_counter--;
44 return ResultSuccess;
45}
46
47void NpadAbstractLedHandler::SetNpadLedHandlerLedPattern() {
48 const auto npad_id = properties_handler->GetNpadId();
49
50 switch (npad_id) {
51 case Core::HID::NpadIdType::Player1:
52 left_pattern = Core::HID::LedPattern{1, 0, 0, 0};
53 break;
54 case Core::HID::NpadIdType::Player2:
55 left_pattern = Core::HID::LedPattern{1, 1, 0, 0};
56 break;
57 case Core::HID::NpadIdType::Player3:
58 left_pattern = Core::HID::LedPattern{1, 1, 1, 0};
59 break;
60 case Core::HID::NpadIdType::Player4:
61 left_pattern = Core::HID::LedPattern{1, 1, 1, 1};
62 break;
63 case Core::HID::NpadIdType::Player5:
64 left_pattern = Core::HID::LedPattern{1, 0, 0, 1};
65 break;
66 case Core::HID::NpadIdType::Player6:
67 left_pattern = Core::HID::LedPattern{1, 0, 1, 0};
68 break;
69 case Core::HID::NpadIdType::Player7:
70 left_pattern = Core::HID::LedPattern{1, 0, 1, 1};
71 break;
72 case Core::HID::NpadIdType::Player8:
73 left_pattern = Core::HID::LedPattern{0, 1, 1, 0};
74 break;
75 case Core::HID::NpadIdType::Other:
76 case Core::HID::NpadIdType::Handheld:
77 left_pattern = Core::HID::LedPattern{0, 0, 0, 0};
78 break;
79 default:
80 ASSERT_MSG(false, "Invalid npad id type");
81 break;
82 }
83
84 switch (npad_id) {
85 case Core::HID::NpadIdType::Player1:
86 right_pattern = Core::HID::LedPattern{0, 0, 0, 1};
87 break;
88 case Core::HID::NpadIdType::Player2:
89 right_pattern = Core::HID::LedPattern{0, 1, 1, 1};
90 break;
91 case Core::HID::NpadIdType::Player3:
92 right_pattern = Core::HID::LedPattern{0, 1, 1, 1};
93 break;
94 case Core::HID::NpadIdType::Player4:
95 right_pattern = Core::HID::LedPattern{1, 1, 1, 1};
96 break;
97 case Core::HID::NpadIdType::Player5:
98 right_pattern = Core::HID::LedPattern{1, 0, 0, 1};
99 break;
100 case Core::HID::NpadIdType::Player6:
101 right_pattern = Core::HID::LedPattern{0, 1, 0, 1};
102 break;
103 case Core::HID::NpadIdType::Player7:
104 right_pattern = Core::HID::LedPattern{1, 1, 0, 1};
105 break;
106 case Core::HID::NpadIdType::Player8:
107 right_pattern = Core::HID::LedPattern{0, 1, 1, 0};
108 break;
109 case Core::HID::NpadIdType::Other:
110 case Core::HID::NpadIdType::Handheld:
111 right_pattern = Core::HID::LedPattern{0, 0, 0, 0};
112 break;
113 default:
114 ASSERT_MSG(false, "Invalid npad id type");
115 break;
116 }
117}
118
119void NpadAbstractLedHandler::SetLedBlinkingDevice(Core::HID::LedPattern pattern) {
120 led_blinking = pattern;
121}
122
123} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_led_handler.h b/src/hid_core/resources/abstracted_pad/abstract_led_handler.h
new file mode 100644
index 000000000..09528129b
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_led_handler.h
@@ -0,0 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/result.h"
8#include "hid_core/hid_types.h"
9
10namespace Service::HID {
11struct AppletResourceHolder;
12class NpadAbstractedPadHolder;
13class NpadAbstractPropertiesHandler;
14
15/// Handles Npad request from HID interfaces
16class NpadAbstractLedHandler final {
17public:
18 explicit NpadAbstractLedHandler();
19 ~NpadAbstractLedHandler();
20
21 void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
22 void SetAppletResource(AppletResourceHolder* applet_resource);
23 void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
24
25 Result IncrementRefCounter();
26 Result DecrementRefCounter();
27
28 void SetNpadLedHandlerLedPattern();
29
30 void SetLedBlinkingDevice(Core::HID::LedPattern pattern);
31
32private:
33 AppletResourceHolder* applet_resource_holder{nullptr};
34 NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
35 NpadAbstractPropertiesHandler* properties_handler{nullptr};
36
37 s32 ref_counter{};
38 Core::HID::LedPattern led_blinking{0, 0, 0, 0};
39 Core::HID::LedPattern left_pattern{0, 0, 0, 0};
40 Core::HID::LedPattern right_pattern{0, 0, 0, 0};
41 u64 led_interval{};
42};
43} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp
new file mode 100644
index 000000000..6f35bd95c
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp
@@ -0,0 +1,108 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/hid_result.h"
5#include "hid_core/resources/abstracted_pad/abstract_mcu_handler.h"
6#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
7#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
8#include "hid_core/resources/npad/npad_types.h"
9
10namespace Service::HID {
11
12NpadAbstractMcuHandler::NpadAbstractMcuHandler() {}
13
14NpadAbstractMcuHandler::~NpadAbstractMcuHandler() = default;
15
16void NpadAbstractMcuHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
17 abstract_pad_holder = holder;
18}
19
20void NpadAbstractMcuHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
21 properties_handler = handler;
22}
23
24Result NpadAbstractMcuHandler::IncrementRefCounter() {
25 if (ref_counter == std::numeric_limits<s32>::max() - 1) {
26 return ResultNpadHandlerOverflow;
27 }
28 ref_counter++;
29 return ResultSuccess;
30}
31
32Result NpadAbstractMcuHandler::DecrementRefCounter() {
33 if (ref_counter == 0) {
34 return ResultNpadHandlerNotInitialized;
35 }
36 ref_counter--;
37 return ResultSuccess;
38}
39
40void NpadAbstractMcuHandler::UpdateMcuState() {
41 std::array<IAbstractedPad*, 5> abstract_pads{};
42 const std::size_t count = properties_handler->GetAbstractedPads(abstract_pads);
43
44 if (count == 0) {
45 mcu_holder = {};
46 return;
47 }
48
49 for (std::size_t i = 0; i < count; i++) {
50 auto* abstract_pad = abstract_pads[i];
51 if (!abstract_pad->internal_flags.is_connected) {
52 continue;
53 }
54 if (!abstract_pad->disabled_feature_set.has_left_joy_rail_bus) {
55 if (!abstract_pad->disabled_feature_set.has_left_joy_six_axis_sensor &&
56 !abstract_pad->disabled_feature_set.has_right_joy_six_axis_sensor) {
57 continue;
58 }
59 if (mcu_holder[1].state != NpadMcuState::Active) {
60 mcu_holder[1].state = NpadMcuState::Available;
61 }
62 mcu_holder[1].abstracted_pad = abstract_pad;
63 continue;
64 }
65 if (mcu_holder[0].state != NpadMcuState::Active) {
66 mcu_holder[0].state = NpadMcuState::Available;
67 }
68 mcu_holder[0].abstracted_pad = abstract_pad;
69 }
70}
71
72Result NpadAbstractMcuHandler::GetAbstractedPad(IAbstractedPad** data, u32 mcu_index) {
73 if (mcu_holder[mcu_index].state == NpadMcuState::None ||
74 mcu_holder[mcu_index].abstracted_pad == nullptr) {
75 return ResultMcuIsNotReady;
76 }
77 *data = mcu_holder[mcu_index].abstracted_pad;
78 return ResultSuccess;
79}
80
81NpadMcuState NpadAbstractMcuHandler::GetMcuState(u32 mcu_index) {
82 return mcu_holder[mcu_index].state;
83}
84
85Result NpadAbstractMcuHandler::SetMcuState(bool is_enabled, u32 mcu_index) {
86 NpadMcuState& state = mcu_holder[mcu_index].state;
87
88 if (state == NpadMcuState::None) {
89 return ResultMcuIsNotReady;
90 }
91
92 if ((is_enabled) && (state == NpadMcuState::Available)) {
93 state = NpadMcuState::Active;
94 return ResultSuccess;
95 }
96
97 if (is_enabled) {
98 return ResultSuccess;
99 }
100 if (state != NpadMcuState::Active) {
101 return ResultSuccess;
102 }
103
104 state = NpadMcuState::Available;
105 return ResultSuccess;
106}
107
108} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h
new file mode 100644
index 000000000..9902dd03a
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h
@@ -0,0 +1,52 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/result.h"
8#include "hid_core/hid_types.h"
9
10namespace Service::HID {
11struct IAbstractedPad;
12class NpadAbstractedPadHolder;
13class NpadAbstractPropertiesHandler;
14
15enum class NpadMcuState : u32 {
16 None,
17 Available,
18 Active,
19};
20
21struct NpadMcuHolder {
22 NpadMcuState state;
23 INSERT_PADDING_BYTES(0x4);
24 IAbstractedPad* abstracted_pad;
25};
26static_assert(sizeof(NpadMcuHolder) == 0x10, "NpadMcuHolder is an invalid size");
27
28/// Handles Npad request from HID interfaces
29class NpadAbstractMcuHandler final {
30public:
31 explicit NpadAbstractMcuHandler();
32 ~NpadAbstractMcuHandler();
33
34 void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
35 void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
36
37 Result IncrementRefCounter();
38 Result DecrementRefCounter();
39
40 void UpdateMcuState();
41 Result GetAbstractedPad(IAbstractedPad** data, u32 mcu_index);
42 NpadMcuState GetMcuState(u32 mcu_index);
43 Result SetMcuState(bool is_enabled, u32 mcu_index);
44
45private:
46 NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
47 NpadAbstractPropertiesHandler* properties_handler{nullptr};
48
49 s32 ref_counter{};
50 std::array<NpadMcuHolder, 2> mcu_holder{};
51};
52} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp
new file mode 100644
index 000000000..bd9b79333
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp
@@ -0,0 +1,140 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/kernel/k_event.h"
5#include "core/hle/kernel/k_readable_event.h"
6#include "hid_core/hid_result.h"
7#include "hid_core/resources/abstracted_pad/abstract_nfc_handler.h"
8#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
9#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
10#include "hid_core/resources/npad/npad_types.h"
11
12namespace Service::HID {
13
14NpadAbstractNfcHandler::NpadAbstractNfcHandler() {}
15
16NpadAbstractNfcHandler::~NpadAbstractNfcHandler() = default;
17
18void NpadAbstractNfcHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
19 abstract_pad_holder = holder;
20}
21
22void NpadAbstractNfcHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
23 properties_handler = handler;
24}
25
26Result NpadAbstractNfcHandler::IncrementRefCounter() {
27 if (ref_counter == std::numeric_limits<s32>::max() - 1) {
28 return ResultNpadHandlerOverflow;
29 }
30 ref_counter++;
31 return ResultSuccess;
32}
33
34Result NpadAbstractNfcHandler::DecrementRefCounter() {
35 if (ref_counter == 0) {
36 return ResultNpadHandlerNotInitialized;
37 }
38 ref_counter--;
39 return ResultSuccess;
40}
41
42void NpadAbstractNfcHandler::UpdateNfcState() {
43 std::array<IAbstractedPad*, 5> abstract_pads{};
44 const std::size_t count = properties_handler->GetAbstractedPads(abstract_pads);
45
46 if (count == 0) {
47 if (sensor_state == NpadNfcState::Active) {
48 nfc_activate_event->Signal();
49 }
50 if (sensor_state == NpadNfcState::Unavailable) {
51 return;
52 }
53 sensor_state = NpadNfcState::Unavailable;
54 input_event->Signal();
55 return;
56 }
57
58 bool is_found{};
59 for (std::size_t i = 0; i < count; i++) {
60 auto* abstract_pad = abstract_pads[i];
61 if (!abstract_pad->internal_flags.is_connected) {
62 continue;
63 }
64 if (!abstract_pad->disabled_feature_set.has_nfc) {
65 continue;
66 }
67 is_found = true;
68 xcd_handle = 0;
69 }
70
71 if (is_found) {
72 if (sensor_state == NpadNfcState::Active) {
73 return;
74 }
75 if (sensor_state == NpadNfcState::Available) {
76 return;
77 }
78 sensor_state = NpadNfcState::Available;
79 input_event->Signal();
80 return;
81 }
82
83 if (sensor_state == NpadNfcState::Active) {
84 nfc_activate_event->Signal();
85 }
86 if (sensor_state == NpadNfcState::Unavailable) {
87 return;
88 }
89 sensor_state = NpadNfcState::Unavailable;
90 input_event->Signal();
91 return;
92}
93
94bool NpadAbstractNfcHandler::HasNfcSensor() {
95 return sensor_state != NpadNfcState::Unavailable;
96}
97
98bool NpadAbstractNfcHandler::IsNfcActivated() {
99 return sensor_state == NpadNfcState::Active;
100}
101
102Result NpadAbstractNfcHandler::GetAcquireNfcActivateEventHandle(
103 Kernel::KReadableEvent** out_event) {
104 *out_event = &nfc_activate_event->GetReadableEvent();
105 return ResultSuccess;
106}
107
108void NpadAbstractNfcHandler::SetInputEvent(Kernel::KEvent* event) {
109 input_event = event;
110}
111
112Result NpadAbstractNfcHandler::ActivateNfc(bool is_enabled) {
113 if (sensor_state == NpadNfcState::Active) {
114 return ResultNfcIsNotReady;
115 }
116
117 NpadNfcState new_state = NpadNfcState::Available;
118 if (is_enabled) {
119 new_state = NpadNfcState::Active;
120 }
121 if (sensor_state != new_state) {
122 sensor_state = new_state;
123 nfc_activate_event->Signal();
124 }
125 return ResultSuccess;
126}
127
128Result NpadAbstractNfcHandler::GetXcdHandleWithNfc(u64& out_xcd_handle) const {
129 if (sensor_state == NpadNfcState::Unavailable) {
130 return ResultNfcIsNotReady;
131 }
132 if (xcd_handle == 0) {
133 return ResultNfcXcdHandleIsNotInitialized;
134 }
135
136 out_xcd_handle = xcd_handle;
137 return ResultSuccess;
138}
139
140} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h
new file mode 100644
index 000000000..0702722a6
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h
@@ -0,0 +1,57 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/result.h"
8#include "hid_core/hid_types.h"
9
10namespace Kernel {
11class KReadableEvent;
12}
13
14enum class NpadNfcState : u32 {
15 Unavailable,
16 Available,
17 Active,
18};
19
20namespace Service::HID {
21class NpadAbstractedPadHolder;
22class NpadAbstractPropertiesHandler;
23
24/// Handles Npad request from HID interfaces
25class NpadAbstractNfcHandler final {
26public:
27 explicit NpadAbstractNfcHandler();
28 ~NpadAbstractNfcHandler();
29
30 void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
31 void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
32
33 Result IncrementRefCounter();
34 Result DecrementRefCounter();
35
36 void UpdateNfcState();
37 bool HasNfcSensor();
38 bool IsNfcActivated();
39
40 Result GetAcquireNfcActivateEventHandle(Kernel::KReadableEvent** out_event);
41 void SetInputEvent(Kernel::KEvent* event);
42
43 Result ActivateNfc(bool is_enabled);
44
45 Result GetXcdHandleWithNfc(u64& out_xcd_handle) const;
46
47private:
48 NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
49 NpadAbstractPropertiesHandler* properties_handler{nullptr};
50
51 s32 ref_counter{};
52 Kernel::KEvent* nfc_activate_event{nullptr};
53 Kernel::KEvent* input_event{nullptr};
54 u64 xcd_handle{};
55 NpadNfcState sensor_state{NpadNfcState::Unavailable};
56};
57} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad.cpp b/src/hid_core/resources/abstracted_pad/abstract_pad.cpp
new file mode 100644
index 000000000..2c7691d7c
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_pad.cpp
@@ -0,0 +1,294 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/hid_result.h"
5#include "hid_core/resources/abstracted_pad/abstract_pad.h"
6#include "hid_core/resources/applet_resource.h"
7#include "hid_core/resources/npad/npad_types.h"
8
9namespace Service::HID {
10
11AbstractPad::AbstractPad() {}
12
13AbstractPad::~AbstractPad() = default;
14
15void AbstractPad::SetExternals(AppletResourceHolder* applet_resource,
16 CaptureButtonResource* capture_button_resource,
17 HomeButtonResource* home_button_resource,
18 SixAxisResource* sixaxis_resource, PalmaResource* palma_resource,
19 VibrationHandler* vibration) {
20 applet_resource_holder = applet_resource;
21
22 properties_handler.SetAppletResource(applet_resource_holder);
23 properties_handler.SetAbstractPadHolder(&abstract_pad_holder);
24
25 led_handler.SetAppletResource(applet_resource_holder);
26 led_handler.SetAbstractPadHolder(&abstract_pad_holder);
27 led_handler.SetPropertiesHandler(&properties_handler);
28
29 ir_sensor_handler.SetAbstractPadHolder(&abstract_pad_holder);
30 ir_sensor_handler.SetPropertiesHandler(&properties_handler);
31
32 nfc_handler.SetAbstractPadHolder(&abstract_pad_holder);
33 nfc_handler.SetPropertiesHandler(&properties_handler);
34
35 mcu_handler.SetAbstractPadHolder(&abstract_pad_holder);
36 mcu_handler.SetPropertiesHandler(&properties_handler);
37
38 std::array<NpadVibrationDevice*, 2> vibration_devices{&vibration_left, &vibration_right};
39 vibration_handler.SetAppletResource(applet_resource_holder);
40 vibration_handler.SetAbstractPadHolder(&abstract_pad_holder);
41 vibration_handler.SetPropertiesHandler(&properties_handler);
42 vibration_handler.SetN64Vibration(&vibration_n64);
43 vibration_handler.SetVibration(vibration_devices);
44 vibration_handler.SetGcVibration(&vibration_gc);
45
46 sixaxis_handler.SetAppletResource(applet_resource_holder);
47 sixaxis_handler.SetAbstractPadHolder(&abstract_pad_holder);
48 sixaxis_handler.SetPropertiesHandler(&properties_handler);
49 sixaxis_handler.SetSixaxisResource(sixaxis_resource);
50
51 button_handler.SetAppletResource(applet_resource_holder);
52 button_handler.SetAbstractPadHolder(&abstract_pad_holder);
53 button_handler.SetPropertiesHandler(&properties_handler);
54
55 battery_handler.SetAppletResource(applet_resource_holder);
56 battery_handler.SetAbstractPadHolder(&abstract_pad_holder);
57 battery_handler.SetPropertiesHandler(&properties_handler);
58
59 palma_handler.SetAbstractPadHolder(&abstract_pad_holder);
60 palma_handler.SetPropertiesHandler(&properties_handler);
61 palma_handler.SetPalmaResource(palma_resource);
62}
63
64void AbstractPad::SetNpadId(Core::HID::NpadIdType npad_id) {
65 properties_handler.SetNpadId(npad_id);
66}
67
68Result AbstractPad::Activate() {
69 if (ref_counter == std::numeric_limits<s32>::max() - 1) {
70 return ResultNpadHandlerOverflow;
71 }
72
73 if (ref_counter != 0) {
74 ref_counter++;
75 return ResultSuccess;
76 }
77
78 std::size_t stage = 0;
79 Result result = ResultSuccess;
80
81 if (result.IsSuccess()) {
82 stage++;
83 result = properties_handler.IncrementRefCounter();
84 }
85 if (result.IsSuccess()) {
86 stage++;
87 result = led_handler.IncrementRefCounter();
88 }
89 if (result.IsSuccess()) {
90 stage++;
91 result = ir_sensor_handler.IncrementRefCounter();
92 }
93 if (result.IsSuccess()) {
94 stage++;
95 result = mcu_handler.IncrementRefCounter();
96 }
97 if (result.IsSuccess()) {
98 stage++;
99 result = nfc_handler.IncrementRefCounter();
100 }
101 if (result.IsSuccess()) {
102 stage++;
103 result = vibration_handler.IncrementRefCounter();
104 }
105 if (result.IsSuccess()) {
106 stage++;
107 result = sixaxis_handler.IncrementRefCounter();
108 }
109 if (result.IsSuccess()) {
110 stage++;
111 result = button_handler.IncrementRefCounter();
112 }
113 if (result.IsSuccess()) {
114 stage++;
115 result = battery_handler.IncrementRefCounter();
116 }
117 if (result.IsSuccess()) {
118 stage++;
119 result = palma_handler.IncrementRefCounter();
120 }
121
122 if (result.IsSuccess()) {
123 ref_counter++;
124 return result;
125 }
126
127 if (stage > 9) {
128 battery_handler.DecrementRefCounter();
129 }
130 if (stage > 8) {
131 button_handler.DecrementRefCounter();
132 }
133 if (stage > 7) {
134 sixaxis_handler.DecrementRefCounter();
135 }
136 if (stage > 6) {
137 vibration_handler.DecrementRefCounter();
138 }
139 if (stage > 5) {
140 nfc_handler.DecrementRefCounter();
141 }
142 if (stage > 4) {
143 mcu_handler.DecrementRefCounter();
144 }
145 if (stage > 3) {
146 ir_sensor_handler.DecrementRefCounter();
147 }
148 if (stage > 2) {
149 led_handler.DecrementRefCounter();
150 }
151 if (stage > 1) {
152 properties_handler.DecrementRefCounter();
153 }
154 return result;
155}
156
157Result AbstractPad::Deactivate() {
158 if (ref_counter == 0) {
159 return ResultNpadResourceNotInitialized;
160 }
161
162 ref_counter--;
163 battery_handler.DecrementRefCounter();
164 button_handler.DecrementRefCounter();
165 sixaxis_handler.DecrementRefCounter();
166 vibration_handler.DecrementRefCounter();
167 nfc_handler.DecrementRefCounter();
168 ir_sensor_handler.DecrementRefCounter();
169 mcu_handler.DecrementRefCounter();
170 led_handler.DecrementRefCounter();
171 properties_handler.DecrementRefCounter();
172 palma_handler.DecrementRefCounter();
173
174 return ResultSuccess;
175}
176
177Result AbstractPad::ActivateNpad(u64 aruid) {
178 Result result = ResultSuccess;
179 if (result.IsSuccess()) {
180 result = properties_handler.ActivateNpadUnknown0x88(aruid);
181 }
182 if (result.IsSuccess()) {
183 result = sixaxis_handler.UpdateSixAxisState2(aruid);
184 }
185 if (result.IsSuccess()) {
186 result = battery_handler.UpdateBatteryState(aruid);
187 }
188 return result;
189}
190
191NpadAbstractedPadHolder* AbstractPad::GetAbstractedPadHolder() {
192 return &abstract_pad_holder;
193}
194
195NpadAbstractPropertiesHandler* AbstractPad::GetAbstractPropertiesHandler() {
196 return &properties_handler;
197}
198
199NpadAbstractLedHandler* AbstractPad::GetAbstractLedHandler() {
200 return &led_handler;
201}
202
203NpadAbstractIrSensorHandler* AbstractPad::GetAbstractIrSensorHandler() {
204 return &ir_sensor_handler;
205}
206
207NpadAbstractMcuHandler* AbstractPad::GetAbstractMcuHandler() {
208 return &mcu_handler;
209}
210
211NpadAbstractNfcHandler* AbstractPad::GetAbstractNfcHandler() {
212 return &nfc_handler;
213}
214
215NpadAbstractVibrationHandler* AbstractPad::GetAbstractVibrationHandler() {
216 return &vibration_handler;
217}
218
219NpadAbstractSixAxisHandler* AbstractPad::GetAbstractSixAxisHandler() {
220 return &sixaxis_handler;
221}
222
223NpadAbstractButtonHandler* AbstractPad::GetAbstractButtonHandler() {
224 return &button_handler;
225}
226
227NpadAbstractBatteryHandler* AbstractPad::GetAbstractBatteryHandler() {
228 return &battery_handler;
229}
230
231NpadN64VibrationDevice* AbstractPad::GetN64VibrationDevice() {
232 return &vibration_n64;
233}
234
235NpadVibrationDevice* AbstractPad::GetVibrationDevice(Core::HID::DeviceIndex device_index) {
236 if (device_index == Core::HID::DeviceIndex::Right) {
237 return &vibration_right;
238 }
239 return &vibration_left;
240}
241
242void AbstractPad::GetLeftRightVibrationDevice(std::vector<NpadVibrationDevice*> list) {
243 list.emplace_back(&vibration_left);
244 list.emplace_back(&vibration_right);
245}
246
247NpadGcVibrationDevice* AbstractPad::GetGCVibrationDevice() {
248 return &vibration_gc;
249}
250
251Core::HID::NpadIdType AbstractPad::GetLastActiveNpad() {
252 return properties_handler.GetNpadId();
253}
254
255void AbstractPad::UpdateInterfaceType() {
256 if (interface_type != properties_handler.GetInterfaceType()) {
257 Update();
258 }
259 battery_handler.UpdateBatteryState();
260}
261
262void AbstractPad::Update() {
263 properties_handler.UpdateDeviceType();
264 led_handler.SetNpadLedHandlerLedPattern();
265 vibration_handler.UpdateVibrationState();
266 sixaxis_handler.UpdateSixAxisState();
267 nfc_handler.UpdateNfcState();
268 ir_sensor_handler.UpdateIrSensorState();
269 mcu_handler.UpdateMcuState();
270 palma_handler.UpdatePalmaState();
271 battery_handler.UpdateBatteryState();
272 button_handler.EnableCenterClamp();
273
274 interface_type = properties_handler.GetInterfaceType();
275
276 std::scoped_lock lock{*applet_resource_holder->shared_mutex};
277 properties_handler.UpdateAllDeviceProperties();
278 battery_handler.UpdateCoreBatteryState();
279 button_handler.UpdateCoreBatteryState();
280}
281
282void AbstractPad::UpdatePadState() {
283 button_handler.UpdateAllButtonLifo();
284 sixaxis_handler.UpdateSixAxisState();
285 battery_handler.UpdateCoreBatteryState();
286}
287
288void AbstractPad::EnableAppletToGetInput(u64 aruid) {
289 button_handler.UpdateButtonState(aruid);
290 sixaxis_handler.UpdateSixAxisState(aruid);
291 battery_handler.UpdateBatteryState(aruid);
292}
293
294} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad.h b/src/hid_core/resources/abstracted_pad/abstract_pad.h
new file mode 100644
index 000000000..cbdf84af7
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_pad.h
@@ -0,0 +1,123 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <mutex>
8
9#include "common/common_types.h"
10#include "core/hle/result.h"
11#include "hid_core/hid_types.h"
12#include "hid_core/resources/applet_resource.h"
13#include "hid_core/resources/npad/npad_types.h"
14
15#include "hid_core/resources/abstracted_pad/abstract_battery_handler.h"
16#include "hid_core/resources/abstracted_pad/abstract_button_handler.h"
17#include "hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h"
18#include "hid_core/resources/abstracted_pad/abstract_led_handler.h"
19#include "hid_core/resources/abstracted_pad/abstract_mcu_handler.h"
20#include "hid_core/resources/abstracted_pad/abstract_nfc_handler.h"
21#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
22#include "hid_core/resources/abstracted_pad/abstract_palma_handler.h"
23#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
24#include "hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h"
25#include "hid_core/resources/abstracted_pad/abstract_vibration_handler.h"
26#include "hid_core/resources/vibration/gc_vibration_device.h"
27#include "hid_core/resources/vibration/n64_vibration_device.h"
28#include "hid_core/resources/vibration/vibration_device.h"
29
30namespace Service::HID {
31class AppletResource;
32class SixAxisResource;
33class PalmaResource;
34class NPadResource;
35class AbstractPad;
36class NpadLastActiveHandler;
37class NpadIrNfcHandler;
38class UniquePads;
39class NpadPalmaHandler;
40class FirmwareResource;
41class NpadVibration;
42class NpadHighestBattery;
43class NpadGcVibration;
44
45class CaptureButtonResource;
46class HomeButtonResource;
47class VibrationHandler;
48
49struct HandheldConfig;
50
51/// Handles Npad request from HID interfaces
52class AbstractPad final {
53public:
54 explicit AbstractPad();
55 ~AbstractPad();
56
57 void SetExternals(AppletResourceHolder* applet_resource,
58 CaptureButtonResource* capture_button_resource,
59 HomeButtonResource* home_button_resource, SixAxisResource* sixaxis_resource,
60 PalmaResource* palma_resource, VibrationHandler* vibration);
61 void SetNpadId(Core::HID::NpadIdType npad_id);
62
63 Result Activate();
64 Result Deactivate();
65
66 Result ActivateNpad(u64 aruid);
67
68 NpadAbstractedPadHolder* GetAbstractedPadHolder();
69 NpadAbstractPropertiesHandler* GetAbstractPropertiesHandler();
70 NpadAbstractLedHandler* GetAbstractLedHandler();
71 NpadAbstractIrSensorHandler* GetAbstractIrSensorHandler();
72 NpadAbstractMcuHandler* GetAbstractMcuHandler();
73 NpadAbstractNfcHandler* GetAbstractNfcHandler();
74 NpadAbstractVibrationHandler* GetAbstractVibrationHandler();
75 NpadAbstractSixAxisHandler* GetAbstractSixAxisHandler();
76 NpadAbstractButtonHandler* GetAbstractButtonHandler();
77 NpadAbstractBatteryHandler* GetAbstractBatteryHandler();
78
79 NpadN64VibrationDevice* GetN64VibrationDevice();
80 NpadVibrationDevice* GetVibrationDevice(Core::HID::DeviceIndex device_index);
81 void GetLeftRightVibrationDevice(std::vector<NpadVibrationDevice*> list);
82 NpadGcVibrationDevice* GetGCVibrationDevice();
83
84 Core::HID::NpadIdType GetLastActiveNpad();
85 void UpdateInterfaceType();
86 void Update();
87
88 void UpdatePadState();
89 void EnableAppletToGetInput(u64 aruid);
90
91private:
92 AppletResourceHolder* applet_resource_holder{nullptr};
93 NpadAbstractedPadHolder abstract_pad_holder{};
94 NpadAbstractPropertiesHandler properties_handler{};
95 NpadAbstractLedHandler led_handler{};
96 NpadAbstractIrSensorHandler ir_sensor_handler{};
97 NpadAbstractNfcHandler nfc_handler{};
98 NpadAbstractMcuHandler mcu_handler{};
99 NpadAbstractVibrationHandler vibration_handler{};
100 NpadAbstractSixAxisHandler sixaxis_handler{};
101 NpadAbstractButtonHandler button_handler{};
102 NpadAbstractBatteryHandler battery_handler{};
103 NpadAbstractPalmaHandler palma_handler{};
104
105 NpadN64VibrationDevice vibration_n64{};
106 NpadVibrationDevice vibration_left{};
107 NpadVibrationDevice vibration_right{};
108 NpadGcVibrationDevice vibration_gc{};
109
110 // SixAxisConfigHolder fullkey_config;
111 // SixAxisConfigHolder handheld_config;
112 // SixAxisConfigHolder dual_left_config;
113 // SixAxisConfigHolder dual_right_config;
114 // SixAxisConfigHolder left_config;
115 // SixAxisConfigHolder right_config;
116
117 s32 ref_counter{};
118 Core::HID::NpadInterfaceType interface_type{Core::HID::NpadInterfaceType::None};
119};
120
121using FullAbstractPad = std::array<AbstractPad, MaxSupportedNpadIdTypes>;
122
123} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad_holder.cpp b/src/hid_core/resources/abstracted_pad/abstract_pad_holder.cpp
new file mode 100644
index 000000000..8334dc34f
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_pad_holder.cpp
@@ -0,0 +1,99 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/hid_result.h"
5#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
6#include "hid_core/resources/npad/npad_types.h"
7
8namespace Service::HID {
9
10Result NpadAbstractedPadHolder::RegisterAbstractPad(IAbstractedPad* abstracted_pad) {
11 if (list_size >= assignment_list.size()) {
12 return ResultNpadIsNotProController;
13 }
14
15 for (std::size_t i = 0; i < list_size; i++) {
16 if (assignment_list[i].device_type == abstracted_pad->device_type) {
17 return ResultNpadIsNotProController;
18 }
19 }
20
21 assignment_list[list_size] = {
22 .abstracted_pad = abstracted_pad,
23 .device_type = abstracted_pad->device_type,
24 .interface_type = abstracted_pad->interface_type,
25 .controller_id = abstracted_pad->controller_id,
26 };
27
28 list_size++;
29 return ResultSuccess;
30}
31
32void NpadAbstractedPadHolder::RemoveAbstractPadByControllerId(u64 controller_id) {
33 if (list_size == 0) {
34 return;
35 }
36 if (controller_id == 0) {
37 return;
38 }
39 for (std::size_t i = 0; i < list_size; i++) {
40 if (assignment_list[i].controller_id != controller_id) {
41 continue;
42 }
43 for (std::size_t e = i + 1; e < list_size; e++) {
44 assignment_list[e - 1] = assignment_list[e];
45 }
46 list_size--;
47 return;
48 }
49}
50
51void NpadAbstractedPadHolder::DetachAbstractedPad() {
52 while (list_size > 0) {
53 for (std::size_t i = 1; i < list_size; i++) {
54 assignment_list[i - 1] = assignment_list[i];
55 }
56 list_size--;
57 }
58}
59
60u64 NpadAbstractedPadHolder::RemoveAbstractPadByAssignmentStyle(
61 Service::HID::AssignmentStyle assignment_style) {
62 for (std::size_t i = 0; i < list_size; i++) {
63 if ((assignment_style.raw & assignment_list[i].abstracted_pad->assignment_style.raw) == 0) {
64 continue;
65 }
66 for (std::size_t e = i + 1; e < list_size; e++) {
67 assignment_list[e - 1] = assignment_list[e];
68 }
69 list_size--;
70 return list_size;
71 }
72 return list_size;
73}
74
75u32 NpadAbstractedPadHolder::GetAbstractedPads(std::span<IAbstractedPad*> list) const {
76 u32 num_elements = std::min(static_cast<u32>(list.size()), list_size);
77 for (std::size_t i = 0; i < num_elements; i++) {
78 list[i] = assignment_list[i].abstracted_pad;
79 }
80 return num_elements;
81}
82
83void NpadAbstractedPadHolder::SetAssignmentMode(const NpadJoyAssignmentMode& mode) {
84 assignment_mode = mode;
85}
86
87NpadJoyAssignmentMode NpadAbstractedPadHolder::GetAssignmentMode() const {
88 return assignment_mode;
89}
90
91std::size_t NpadAbstractedPadHolder::GetStyleIndexList(
92 std::span<Core::HID::NpadStyleIndex> list) const {
93 for (std::size_t i = 0; i < list_size; i++) {
94 list[i] = assignment_list[i].device_type;
95 }
96 return list_size;
97}
98
99} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad_holder.h b/src/hid_core/resources/abstracted_pad/abstract_pad_holder.h
new file mode 100644
index 000000000..fb7f472e8
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_pad_holder.h
@@ -0,0 +1,47 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <mutex>
8#include <span>
9
10#include "common/common_types.h"
11#include "core/hle/result.h"
12#include "hid_core/hid_types.h"
13#include "hid_core/resources/npad/npad_types.h"
14
15namespace Service::HID {
16struct IAbstractedPad;
17
18struct AbstractAssignmentHolder {
19 IAbstractedPad* abstracted_pad;
20 Core::HID::NpadStyleIndex device_type;
21 Core::HID::NpadInterfaceType interface_type;
22 INSERT_PADDING_BYTES(0x6);
23 u64 controller_id;
24};
25static_assert(sizeof(AbstractAssignmentHolder) == 0x18,
26 "AbstractAssignmentHolder is an invalid size");
27
28/// This is nn::hid::server::NpadAbstractedPadHolder
29class NpadAbstractedPadHolder final {
30public:
31 Result RegisterAbstractPad(IAbstractedPad* abstracted_pad);
32 void RemoveAbstractPadByControllerId(u64 controller_id);
33 void DetachAbstractedPad();
34 u64 RemoveAbstractPadByAssignmentStyle(Service::HID::AssignmentStyle assignment_style);
35 u32 GetAbstractedPads(std::span<IAbstractedPad*> list) const;
36
37 void SetAssignmentMode(const NpadJoyAssignmentMode& mode);
38 NpadJoyAssignmentMode GetAssignmentMode() const;
39
40 std::size_t GetStyleIndexList(std::span<Core::HID::NpadStyleIndex> list) const;
41
42private:
43 std::array<AbstractAssignmentHolder, 5> assignment_list{};
44 u32 list_size{};
45 NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
46};
47} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp
new file mode 100644
index 000000000..04d276d61
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp
@@ -0,0 +1,47 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/hid_result.h"
5#include "hid_core/resources/abstracted_pad/abstract_palma_handler.h"
6#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
7
8namespace Service::HID {
9
10NpadAbstractPalmaHandler::NpadAbstractPalmaHandler() {}
11
12NpadAbstractPalmaHandler::~NpadAbstractPalmaHandler() = default;
13
14void NpadAbstractPalmaHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
15 abstract_pad_holder = holder;
16}
17
18void NpadAbstractPalmaHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
19 properties_handler = handler;
20 return;
21}
22
23void NpadAbstractPalmaHandler::SetPalmaResource(PalmaResource* resource) {
24 palma_resource = resource;
25}
26
27Result NpadAbstractPalmaHandler::IncrementRefCounter() {
28 if (ref_counter == std::numeric_limits<s32>::max() - 1) {
29 return ResultNpadHandlerOverflow;
30 }
31 ref_counter++;
32 return ResultSuccess;
33}
34
35Result NpadAbstractPalmaHandler::DecrementRefCounter() {
36 if (ref_counter == 0) {
37 return ResultNpadHandlerNotInitialized;
38 }
39 ref_counter--;
40 return ResultSuccess;
41}
42
43void NpadAbstractPalmaHandler::UpdatePalmaState() {
44 // TODO
45}
46
47} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_palma_handler.h b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.h
new file mode 100644
index 000000000..fbd2e67e5
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.h
@@ -0,0 +1,37 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/result.h"
8#include "hid_core/hid_types.h"
9
10namespace Service::HID {
11class NpadAbstractedPadHolder;
12class NpadAbstractPropertiesHandler;
13class PalmaResource;
14
15class NpadAbstractPalmaHandler final {
16public:
17 explicit NpadAbstractPalmaHandler();
18 ~NpadAbstractPalmaHandler();
19
20 void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
21 void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
22 void SetPalmaResource(PalmaResource* resource);
23
24 Result IncrementRefCounter();
25 Result DecrementRefCounter();
26
27 void UpdatePalmaState();
28
29private:
30 NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
31 NpadAbstractPropertiesHandler* properties_handler{nullptr};
32 PalmaResource* palma_resource{nullptr};
33
34 s32 ref_counter{};
35};
36
37} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp
new file mode 100644
index 000000000..4897a2784
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp
@@ -0,0 +1,322 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/hid_util.h"
5#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
6#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
7#include "hid_core/resources/applet_resource.h"
8#include "hid_core/resources/npad/npad_resource.h"
9#include "hid_core/resources/npad/npad_types.h"
10#include "hid_core/resources/shared_memory_format.h"
11
12namespace Service::HID {
13
14NpadAbstractPropertiesHandler::NpadAbstractPropertiesHandler() {}
15
16NpadAbstractPropertiesHandler::~NpadAbstractPropertiesHandler() = default;
17
18void NpadAbstractPropertiesHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
19 abstract_pad_holder = holder;
20 return;
21}
22
23void NpadAbstractPropertiesHandler::SetAppletResource(AppletResourceHolder* applet_resource) {
24 applet_resource_holder = applet_resource;
25 return;
26}
27
28void NpadAbstractPropertiesHandler::SetNpadId(Core::HID::NpadIdType npad_id) {
29 if (!IsNpadIdValid(npad_id)) {
30 ASSERT_MSG(false, "Invalid npad id");
31 }
32
33 npad_id_type = npad_id;
34}
35
36Core::HID::NpadIdType NpadAbstractPropertiesHandler::GetNpadId() const {
37 return npad_id_type;
38}
39
40Result NpadAbstractPropertiesHandler::IncrementRefCounter() {
41 if (ref_counter == std::numeric_limits<s32>::max() - 1) {
42 return ResultNpadHandlerOverflow;
43 }
44
45 if (ref_counter != 0) {
46 ref_counter++;
47 return ResultSuccess;
48 }
49
50 const auto npad_index = NpadIdTypeToIndex(npad_id_type);
51 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
52 auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid_index);
53 auto& internal_state =
54 data->shared_memory_format->npad.npad_entry[npad_index].internal_state;
55 if (!data->flag.is_assigned) {
56 continue;
57 }
58 internal_state.fullkey_lifo.buffer_count = 0;
59 internal_state.handheld_lifo.buffer_count = 0;
60 internal_state.joy_dual_lifo.buffer_count = 0;
61 internal_state.joy_left_lifo.buffer_count = 0;
62 internal_state.joy_right_lifo.buffer_count = 0;
63 internal_state.palma_lifo.buffer_count = 0;
64 internal_state.system_ext_lifo.buffer_count = 0;
65 internal_state.gc_trigger_lifo.buffer_count = 0;
66 internal_state.sixaxis_fullkey_lifo.lifo.buffer_count = 0;
67 internal_state.sixaxis_handheld_lifo.lifo.buffer_count = 0;
68 internal_state.sixaxis_dual_left_lifo.lifo.buffer_count = 0;
69 internal_state.sixaxis_dual_right_lifo.lifo.buffer_count = 0;
70 internal_state.sixaxis_left_lifo.lifo.buffer_count = 0;
71 internal_state.sixaxis_right_lifo.lifo.buffer_count = 0;
72
73 internal_state.style_tag = {Core::HID::NpadStyleSet::None};
74 internal_state.assignment_mode = NpadJoyAssignmentMode::Dual;
75 internal_state.joycon_color = {};
76 internal_state.fullkey_color = {};
77
78 internal_state.system_properties.raw = 0;
79 internal_state.button_properties.raw = 0;
80 internal_state.device_type.raw = 0;
81
82 internal_state.battery_level_dual = Core::HID::NpadBatteryLevel::Empty;
83 internal_state.battery_level_left = Core::HID::NpadBatteryLevel::Empty;
84 internal_state.battery_level_right = Core::HID::NpadBatteryLevel::Empty;
85
86 internal_state.applet_footer_type = AppletFooterUiType::None;
87 internal_state.applet_footer_attributes = {};
88 internal_state.lark_type_l_and_main = {};
89 internal_state.lark_type_r = {};
90
91 internal_state.sixaxis_fullkey_properties.is_newly_assigned.Assign(true);
92 internal_state.sixaxis_handheld_properties.is_newly_assigned.Assign(true);
93 internal_state.sixaxis_dual_left_properties.is_newly_assigned.Assign(true);
94 internal_state.sixaxis_dual_right_properties.is_newly_assigned.Assign(true);
95 internal_state.sixaxis_left_properties.is_newly_assigned.Assign(true);
96 internal_state.sixaxis_right_properties.is_newly_assigned.Assign(true);
97 }
98
99 ref_counter++;
100 return ResultSuccess;
101}
102
103Result NpadAbstractPropertiesHandler::DecrementRefCounter() {
104 if (ref_counter == 0) {
105 return ResultNpadHandlerNotInitialized;
106 }
107 ref_counter--;
108 return ResultSuccess;
109}
110
111Result NpadAbstractPropertiesHandler::ActivateNpadUnknown0x88(u64 aruid) {
112 const auto npad_index = NpadIdTypeToIndex(npad_id_type);
113 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
114 auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid_index);
115 if (!data->flag.is_assigned || data->aruid != aruid) {
116 continue;
117 }
118 UpdateDeviceProperties(aruid, data->shared_memory_format->npad.npad_entry[npad_index]);
119 return ResultSuccess;
120 }
121 return ResultSuccess;
122}
123
124void NpadAbstractPropertiesHandler::UpdateDeviceType() {
125 // TODO
126}
127
128void NpadAbstractPropertiesHandler::UpdateDeviceColor() {
129 // TODO
130}
131
132void NpadAbstractPropertiesHandler::UpdateFooterAttributes() {
133 // TODO
134}
135
136void NpadAbstractPropertiesHandler::UpdateAllDeviceProperties() {
137 const auto npad_index = NpadIdTypeToIndex(npad_id_type);
138 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
139 auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid_index);
140 if (!data->flag.is_assigned) {
141 continue;
142 }
143 auto& npad_entry = data->shared_memory_format->npad.npad_entry[npad_index];
144 UpdateDeviceProperties(data->aruid, npad_entry);
145 }
146}
147
148Core::HID::NpadInterfaceType NpadAbstractPropertiesHandler::GetFullkeyInterfaceType() {
149 std::array<IAbstractedPad*, 5> abstract_pads{};
150 const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
151
152 for (std::size_t i = 0; i < count; i++) {
153 auto* abstract_pad = abstract_pads[i];
154 if (!abstract_pad->internal_flags.is_connected) {
155 continue;
156 }
157 if (abstract_pad->device_type != Core::HID::NpadStyleIndex::Fullkey) {
158 continue;
159 }
160 if (abstract_pad->interface_type >= Core::HID::NpadInterfaceType::Embedded) {
161 // Abort
162 continue;
163 }
164 return abstract_pad->interface_type;
165 }
166
167 return Core::HID::NpadInterfaceType::None;
168}
169
170Core::HID::NpadInterfaceType NpadAbstractPropertiesHandler::GetInterfaceType() {
171 std::array<IAbstractedPad*, 5> abstract_pads{};
172 const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
173
174 for (std::size_t i = 0; i < count; i++) {
175 auto* abstract_pad = abstract_pads[i];
176 if (!abstract_pad->internal_flags.is_connected) {
177 continue;
178 }
179 if (!abstract_pad->disabled_feature_set.has_identification_code) {
180 continue;
181 }
182 if (abstract_pad->interface_type >= Core::HID::NpadInterfaceType::Embedded) {
183 // Abort
184 continue;
185 }
186 return abstract_pad->interface_type;
187 }
188 return Core::HID::NpadInterfaceType::None;
189}
190
191Core::HID::NpadStyleSet NpadAbstractPropertiesHandler::GetStyleSet(u64 aruid) {
192 // TODO
193 return Core::HID::NpadStyleSet::None;
194}
195
196std::size_t NpadAbstractPropertiesHandler::GetAbstractedPadsWithStyleTag(
197 std::span<IAbstractedPad*> list, Core::HID::NpadStyleTag style) {
198 std::array<IAbstractedPad*, 5> abstract_pads{};
199 const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
200
201 if (count == 0) {
202 return count;
203 }
204
205 bool is_supported_style_set{};
206 const auto result = applet_resource_holder->shared_npad_resource->IsSupportedNpadStyleSet(
207 is_supported_style_set, applet_resource_holder->applet_resource->GetActiveAruid());
208
209 if (!is_supported_style_set || result.IsError()) {
210 for (std::size_t i = 0; i < count; i++) {
211 // TODO
212 }
213 return count;
214 }
215
216 std::size_t filtered_count{};
217 for (std::size_t i = 0; i < count; i++) {
218 auto* abstract_pad = abstract_pads[i];
219 const bool is_enabled = true;
220 if (is_enabled) {
221 list[filtered_count] = abstract_pad;
222 filtered_count++;
223 }
224 }
225
226 return filtered_count;
227}
228
229std::size_t NpadAbstractPropertiesHandler::GetAbstractedPads(std::span<IAbstractedPad*> list) {
230 Core::HID::NpadStyleTag style{
231 GetStyleSet(applet_resource_holder->applet_resource->GetActiveAruid())};
232 return GetAbstractedPadsWithStyleTag(list, style);
233}
234
235AppletFooterUiType NpadAbstractPropertiesHandler::GetAppletFooterUiType() {
236 return applet_ui_type.footer;
237}
238
239AppletDetailedUiType NpadAbstractPropertiesHandler::GetAppletDetailedUiType() {
240 return applet_ui_type;
241}
242
243void NpadAbstractPropertiesHandler::UpdateDeviceProperties(u64 aruid,
244 NpadSharedMemoryEntry& internal_state) {
245 // TODO
246}
247
248Core::HID::NpadInterfaceType NpadAbstractPropertiesHandler::GetNpadInterfaceType() {
249 std::array<IAbstractedPad*, 5> abstract_pads{};
250 const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
251
252 for (std::size_t i = 0; i < count; i++) {
253 auto* abstract_pad = abstract_pads[i];
254 if (!abstract_pad->internal_flags.is_connected) {
255 continue;
256 }
257 if (abstract_pad->interface_type >= Core::HID::NpadInterfaceType::Embedded) {
258 // Abort
259 continue;
260 }
261 return abstract_pad->interface_type;
262 }
263
264 return Core::HID::NpadInterfaceType::None;
265}
266
267Result NpadAbstractPropertiesHandler::GetNpadFullKeyGripColor(
268 Core::HID::NpadColor& main_color, Core::HID::NpadColor& sub_color) const {
269 std::array<IAbstractedPad*, 5> abstract_pads{};
270 const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
271
272 if (applet_ui_type.footer != AppletFooterUiType::SwitchProController) {
273 return ResultNpadIsNotProController;
274 }
275
276 for (std::size_t i = 0; i < count; i++) {
277 auto* abstract_pad = abstract_pads[i];
278 if (!abstract_pad->internal_flags.is_connected) {
279 continue;
280 }
281 return ResultSuccess;
282 }
283
284 return ResultNpadIsNotProController;
285}
286
287void NpadAbstractPropertiesHandler::GetNpadLeftRightInterfaceType(
288 Core::HID::NpadInterfaceType& out_left_interface,
289 Core::HID::NpadInterfaceType& out_right_interface) const {
290 out_left_interface = Core::HID::NpadInterfaceType::None;
291 out_right_interface = Core::HID::NpadInterfaceType::None;
292
293 std::array<IAbstractedPad*, 5> abstract_pads{};
294 const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
295
296 for (std::size_t i = 0; i < count; i++) {
297 auto* abstract_pad = abstract_pads[i];
298 if (!abstract_pad->internal_flags.is_connected) {
299 continue;
300 }
301 if (abstract_pad->assignment_style.is_external_left_assigned &&
302 abstract_pad->assignment_style.is_handheld_left_assigned) {
303 if (abstract_pad->interface_type > Core::HID::NpadInterfaceType::Embedded) {
304 // Abort
305 continue;
306 }
307 out_left_interface = abstract_pad->interface_type;
308 continue;
309 }
310 if (abstract_pad->assignment_style.is_external_right_assigned &&
311 abstract_pad->assignment_style.is_handheld_right_assigned) {
312 if (abstract_pad->interface_type > Core::HID::NpadInterfaceType::Embedded) {
313 // Abort
314 continue;
315 }
316 out_right_interface = abstract_pad->interface_type;
317 continue;
318 }
319 }
320}
321
322} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_properties_handler.h b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.h
new file mode 100644
index 000000000..fa6827899
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.h
@@ -0,0 +1,86 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <span>
7
8#include "common/common_types.h"
9#include "core/hle/result.h"
10#include "hid_core/hid_types.h"
11#include "hid_core/resources/npad/npad_types.h"
12
13namespace Service::HID {
14struct NpadSharedMemoryEntry;
15
16struct AppletResourceHolder;
17class NpadAbstractedPadHolder;
18
19struct ColorProperties {
20 ColorAttribute attribute;
21 Core::HID::NpadControllerColor color;
22 INSERT_PADDING_BYTES(0x4);
23};
24
25/// Handles Npad request from HID interfaces
26class NpadAbstractPropertiesHandler final {
27public:
28 explicit NpadAbstractPropertiesHandler();
29 ~NpadAbstractPropertiesHandler();
30
31 void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
32 void SetAppletResource(AppletResourceHolder* applet_resource);
33 void SetNpadId(Core::HID::NpadIdType npad_id);
34
35 Core::HID::NpadIdType GetNpadId() const;
36
37 Result IncrementRefCounter();
38 Result DecrementRefCounter();
39
40 Result ActivateNpadUnknown0x88(u64 aruid);
41
42 void UpdateDeviceType();
43 void UpdateDeviceColor();
44 void UpdateFooterAttributes();
45 void UpdateAllDeviceProperties();
46
47 Core::HID::NpadInterfaceType GetFullkeyInterfaceType();
48 Core::HID::NpadInterfaceType GetInterfaceType();
49
50 Core::HID::NpadStyleSet GetStyleSet(u64 aruid);
51 std::size_t GetAbstractedPadsWithStyleTag(std::span<IAbstractedPad*> list,
52 Core::HID::NpadStyleTag style);
53 std::size_t GetAbstractedPads(std::span<IAbstractedPad*> list);
54
55 AppletFooterUiType GetAppletFooterUiType();
56
57 AppletDetailedUiType GetAppletDetailedUiType();
58
59 void UpdateDeviceProperties(u64 aruid, NpadSharedMemoryEntry& internal_state);
60
61 Core::HID::NpadInterfaceType GetNpadInterfaceType();
62
63 Result GetNpadFullKeyGripColor(Core::HID::NpadColor& main_color,
64 Core::HID::NpadColor& sub_color) const;
65
66 void GetNpadLeftRightInterfaceType(Core::HID::NpadInterfaceType& param_2,
67 Core::HID::NpadInterfaceType& param_3) const;
68
69private:
70 AppletResourceHolder* applet_resource_holder{nullptr};
71 NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
72 Core::HID::NpadIdType npad_id_type{Core::HID::NpadIdType::Invalid};
73 s32 ref_counter{};
74 Core::HID::DeviceIndex device_type{};
75 AppletDetailedUiType applet_ui_type{};
76 AppletFooterUiAttributes applet_ui_attributes{};
77 bool is_vertical{};
78 bool is_horizontal{};
79 bool use_plus{};
80 bool use_minus{};
81 bool has_directional_buttons{};
82 ColorProperties fullkey_color{};
83 ColorProperties left_color{};
84 ColorProperties right_color{};
85};
86} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp
new file mode 100644
index 000000000..6d759298e
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp
@@ -0,0 +1,154 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/hid_result.h"
5#include "hid_core/hid_util.h"
6#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
7#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
8#include "hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h"
9#include "hid_core/resources/applet_resource.h"
10#include "hid_core/resources/npad/npad_types.h"
11#include "hid_core/resources/shared_memory_format.h"
12
13namespace Service::HID {
14
15NpadAbstractSixAxisHandler::NpadAbstractSixAxisHandler() {}
16
17NpadAbstractSixAxisHandler::~NpadAbstractSixAxisHandler() = default;
18
19void NpadAbstractSixAxisHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
20 abstract_pad_holder = holder;
21}
22
23void NpadAbstractSixAxisHandler::SetAppletResource(AppletResourceHolder* applet_resource) {
24 applet_resource_holder = applet_resource;
25}
26
27void NpadAbstractSixAxisHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
28 properties_handler = handler;
29}
30
31void NpadAbstractSixAxisHandler::SetSixaxisResource(SixAxisResource* resource) {
32 six_axis_resource = resource;
33}
34
35Result NpadAbstractSixAxisHandler::IncrementRefCounter() {
36 if (ref_counter == std::numeric_limits<s32>::max() - 1) {
37 return ResultNpadHandlerOverflow;
38 }
39 ref_counter++;
40 return ResultSuccess;
41}
42
43Result NpadAbstractSixAxisHandler::DecrementRefCounter() {
44 if (ref_counter == 0) {
45 return ResultNpadHandlerNotInitialized;
46 }
47 ref_counter--;
48 return ResultSuccess;
49}
50
51u64 NpadAbstractSixAxisHandler::IsFirmwareUpdateAvailable() {
52 // TODO
53 return false;
54}
55
56Result NpadAbstractSixAxisHandler::UpdateSixAxisState() {
57 Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
58 for (std::size_t i = 0; i < AruidIndexMax; i++) {
59 auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
60 if (data->flag.is_assigned) {
61 continue;
62 }
63 auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
64 UpdateSixaxisInternalState(npad_entry, data->aruid,
65 data->flag.enable_six_axis_sensor.As<bool>());
66 }
67 return ResultSuccess;
68}
69
70Result NpadAbstractSixAxisHandler::UpdateSixAxisState(u64 aruid) {
71 Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
72 auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid);
73 if (data == nullptr) {
74 return ResultSuccess;
75 }
76 auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
77 UpdateSixaxisInternalState(npad_entry, data->aruid,
78 data->flag.enable_six_axis_sensor.As<bool>());
79 return ResultSuccess;
80}
81
82Result NpadAbstractSixAxisHandler::UpdateSixAxisState2(u64 aruid) {
83 const auto npad_index = NpadIdTypeToIndex(properties_handler->GetNpadId());
84 AruidData* aruid_data = applet_resource_holder->applet_resource->GetAruidData(aruid);
85 if (aruid_data == nullptr) {
86 return ResultSuccess;
87 }
88 auto& npad_internal_state = aruid_data->shared_memory_format->npad.npad_entry[npad_index];
89 UpdateSixaxisInternalState(npad_internal_state, aruid,
90 aruid_data->flag.enable_six_axis_sensor.As<bool>());
91 return ResultSuccess;
92}
93
94void NpadAbstractSixAxisHandler::UpdateSixaxisInternalState(NpadSharedMemoryEntry& npad_entry,
95 u64 aruid, bool is_sensor_enabled) {
96 const Core::HID::NpadStyleTag style_tag{properties_handler->GetStyleSet(aruid)};
97
98 if (!style_tag.palma) {
99 UpdateSixaxisFullkeyLifo(style_tag, npad_entry.internal_state.sixaxis_fullkey_lifo,
100 is_sensor_enabled);
101 } else {
102 UpdateSixAxisPalmaLifo(style_tag, npad_entry.internal_state.sixaxis_fullkey_lifo,
103 is_sensor_enabled);
104 }
105 UpdateSixaxisHandheldLifo(style_tag, npad_entry.internal_state.sixaxis_handheld_lifo,
106 is_sensor_enabled);
107 UpdateSixaxisDualLifo(style_tag, npad_entry.internal_state.sixaxis_dual_left_lifo,
108 is_sensor_enabled);
109 UpdateSixaxisDualLifo(style_tag, npad_entry.internal_state.sixaxis_dual_right_lifo,
110 is_sensor_enabled);
111 UpdateSixaxisLeftLifo(style_tag, npad_entry.internal_state.sixaxis_left_lifo,
112 is_sensor_enabled);
113 UpdateSixaxisRightLifo(style_tag, npad_entry.internal_state.sixaxis_right_lifo,
114 is_sensor_enabled);
115 // TODO: Set sixaxis properties
116}
117
118void NpadAbstractSixAxisHandler::UpdateSixaxisFullkeyLifo(Core::HID::NpadStyleTag style_tag,
119 NpadSixAxisSensorLifo& sensor_lifo,
120 bool is_sensor_enabled) {
121 // TODO
122}
123
124void NpadAbstractSixAxisHandler::UpdateSixAxisPalmaLifo(Core::HID::NpadStyleTag style_tag,
125 NpadSixAxisSensorLifo& sensor_lifo,
126 bool is_sensor_enabled) {
127 // TODO
128}
129
130void NpadAbstractSixAxisHandler::UpdateSixaxisHandheldLifo(Core::HID::NpadStyleTag style_tag,
131 NpadSixAxisSensorLifo& sensor_lifo,
132 bool is_sensor_enabled) {
133 // TODO
134}
135
136void NpadAbstractSixAxisHandler::UpdateSixaxisDualLifo(Core::HID::NpadStyleTag style_tag,
137 NpadSixAxisSensorLifo& sensor_lifo,
138 bool is_sensor_enabled) {
139 // TODO
140}
141
142void NpadAbstractSixAxisHandler::UpdateSixaxisLeftLifo(Core::HID::NpadStyleTag style_tag,
143 NpadSixAxisSensorLifo& sensor_lifo,
144 bool is_sensor_enabled) {
145 // TODO
146}
147
148void NpadAbstractSixAxisHandler::UpdateSixaxisRightLifo(Core::HID::NpadStyleTag style_tag,
149 NpadSixAxisSensorLifo& sensor_lifo,
150 bool is_sensor_enabled) {
151 // TODO
152}
153
154} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h
new file mode 100644
index 000000000..9c20459e9
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h
@@ -0,0 +1,61 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/result.h"
8#include "hid_core/hid_types.h"
9
10namespace Service::HID {
11class SixAxisResource;
12struct AppletResourceHolder;
13class NpadAbstractedPadHolder;
14class NpadAbstractPropertiesHandler;
15struct NpadSixAxisSensorLifo;
16
17/// Handles Npad request from HID interfaces
18class NpadAbstractSixAxisHandler final {
19public:
20 explicit NpadAbstractSixAxisHandler();
21 ~NpadAbstractSixAxisHandler();
22
23 void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
24 void SetAppletResource(AppletResourceHolder* applet_resource);
25 void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
26 void SetSixaxisResource(SixAxisResource* resource);
27
28 Result IncrementRefCounter();
29 Result DecrementRefCounter();
30
31 u64 IsFirmwareUpdateAvailable();
32
33 Result UpdateSixAxisState();
34 Result UpdateSixAxisState(u64 aruid);
35 Result UpdateSixAxisState2(u64 aruid);
36
37private:
38 void UpdateSixaxisInternalState(NpadSharedMemoryEntry& npad_entry, u64 aruid,
39 bool is_sensor_enabled);
40 void UpdateSixaxisFullkeyLifo(Core::HID::NpadStyleTag style_tag,
41 NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled);
42 void UpdateSixAxisPalmaLifo(Core::HID::NpadStyleTag style_tag,
43 NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled);
44 void UpdateSixaxisHandheldLifo(Core::HID::NpadStyleTag style_tag,
45 NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled);
46 void UpdateSixaxisDualLifo(Core::HID::NpadStyleTag style_tag,
47 NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled);
48 void UpdateSixaxisLeftLifo(Core::HID::NpadStyleTag style_tag,
49 NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled);
50 void UpdateSixaxisRightLifo(Core::HID::NpadStyleTag style_tag,
51 NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled);
52
53 AppletResourceHolder* applet_resource_holder{nullptr};
54 NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
55 NpadAbstractPropertiesHandler* properties_handler{nullptr};
56 SixAxisResource* six_axis_resource{nullptr};
57
58 s32 ref_counter{};
59};
60
61} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp
new file mode 100644
index 000000000..a00d6c9de
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp
@@ -0,0 +1,73 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/hid_result.h"
5#include "hid_core/hid_util.h"
6#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
7#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
8#include "hid_core/resources/abstracted_pad/abstract_vibration_handler.h"
9#include "hid_core/resources/applet_resource.h"
10#include "hid_core/resources/npad/npad_vibration.h"
11#include "hid_core/resources/vibration/gc_vibration_device.h"
12#include "hid_core/resources/vibration/n64_vibration_device.h"
13#include "hid_core/resources/vibration/vibration_device.h"
14
15namespace Service::HID {
16
17NpadAbstractVibrationHandler::NpadAbstractVibrationHandler() {}
18
19NpadAbstractVibrationHandler::~NpadAbstractVibrationHandler() = default;
20
21void NpadAbstractVibrationHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
22 abstract_pad_holder = holder;
23}
24
25void NpadAbstractVibrationHandler::SetAppletResource(AppletResourceHolder* applet_resource) {
26 applet_resource_holder = applet_resource;
27}
28
29void NpadAbstractVibrationHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
30 properties_handler = handler;
31}
32
33void NpadAbstractVibrationHandler::SetN64Vibration(NpadN64VibrationDevice* n64_device) {
34 n64_vibration_device = n64_device;
35}
36
37void NpadAbstractVibrationHandler::SetVibration(std::span<NpadVibrationDevice*> device) {
38 for (std::size_t i = 0; i < device.size() && i < vibration_device.size(); i++) {
39 vibration_device[i] = device[i];
40 }
41}
42
43void NpadAbstractVibrationHandler::SetGcVibration(NpadGcVibrationDevice* gc_device) {
44 gc_vibration_device = gc_device;
45}
46
47Result NpadAbstractVibrationHandler::IncrementRefCounter() {
48 if (ref_counter == std::numeric_limits<s32>::max() - 1) {
49 return ResultNpadHandlerOverflow;
50 }
51 ref_counter++;
52 return ResultSuccess;
53}
54
55Result NpadAbstractVibrationHandler::DecrementRefCounter() {
56 if (ref_counter == 0) {
57 return ResultNpadHandlerNotInitialized;
58 }
59 ref_counter--;
60 return ResultSuccess;
61}
62
63void NpadAbstractVibrationHandler::UpdateVibrationState() {
64 const bool is_handheld_hid_enabled =
65 applet_resource_holder->handheld_config->is_handheld_hid_enabled;
66 const bool is_force_handheld_style_vibration =
67 applet_resource_holder->handheld_config->is_force_handheld_style_vibration;
68
69 if (!is_handheld_hid_enabled && is_force_handheld_style_vibration) {
70 // TODO
71 }
72}
73} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h
new file mode 100644
index 000000000..aeb07ce86
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h
@@ -0,0 +1,51 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <span>
7
8#include "common/common_types.h"
9#include "core/hle/result.h"
10#include "hid_core/hid_types.h"
11
12namespace Service::HID {
13struct AppletResourceHolder;
14class NpadAbstractedPadHolder;
15class NpadAbstractPropertiesHandler;
16class NpadGcVibrationDevice;
17class NpadVibrationDevice;
18class NpadN64VibrationDevice;
19class NpadVibration;
20
21/// Keeps track of battery levels and updates npad battery shared memory values
22class NpadAbstractVibrationHandler final {
23public:
24 explicit NpadAbstractVibrationHandler();
25 ~NpadAbstractVibrationHandler();
26
27 void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
28 void SetAppletResource(AppletResourceHolder* applet_resource);
29 void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
30
31 void SetN64Vibration(NpadN64VibrationDevice* n64_device);
32 void SetVibration(std::span<NpadVibrationDevice*> device);
33 void SetGcVibration(NpadGcVibrationDevice* gc_device);
34
35 Result IncrementRefCounter();
36 Result DecrementRefCounter();
37
38 void UpdateVibrationState();
39
40private:
41 AppletResourceHolder* applet_resource_holder{nullptr};
42 NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
43 NpadAbstractPropertiesHandler* properties_handler{nullptr};
44
45 NpadN64VibrationDevice* n64_vibration_device{nullptr};
46 std::array<NpadVibrationDevice*, 2> vibration_device{};
47 NpadGcVibrationDevice* gc_vibration_device{nullptr};
48 NpadVibration* vibration_handler{nullptr};
49 s32 ref_counter{};
50};
51} // namespace Service::HID
diff --git a/src/hid_core/resources/hid_firmware_settings.cpp b/src/hid_core/resources/hid_firmware_settings.cpp
index e76b3a016..9fa0db17e 100644
--- a/src/hid_core/resources/hid_firmware_settings.cpp
+++ b/src/hid_core/resources/hid_firmware_settings.cpp
@@ -14,7 +14,7 @@ void HidFirmwareSettings::Reload() {
14} 14}
15 15
16void HidFirmwareSettings::LoadSettings(bool reload_config) { 16void HidFirmwareSettings::LoadSettings(bool reload_config) {
17 if (is_initalized && !reload_config) { 17 if (is_initialized && !reload_config) {
18 return; 18 return;
19 } 19 }
20 20
@@ -33,7 +33,7 @@ void HidFirmwareSettings::LoadSettings(bool reload_config) {
33 is_handheld_forced = true; 33 is_handheld_forced = true;
34 features_per_id_disabled = {}; 34 features_per_id_disabled = {};
35 is_touch_firmware_auto_update_disabled = false; 35 is_touch_firmware_auto_update_disabled = false;
36 is_initalized = true; 36 is_initialized = true;
37} 37}
38 38
39bool HidFirmwareSettings::IsDebugPadEnabled() { 39bool HidFirmwareSettings::IsDebugPadEnabled() {
diff --git a/src/hid_core/resources/hid_firmware_settings.h b/src/hid_core/resources/hid_firmware_settings.h
index 6c10c440b..00201fd94 100644
--- a/src/hid_core/resources/hid_firmware_settings.h
+++ b/src/hid_core/resources/hid_firmware_settings.h
@@ -33,7 +33,7 @@ public:
33 FeaturesPerId FeaturesDisabledPerId(); 33 FeaturesPerId FeaturesDisabledPerId();
34 34
35private: 35private:
36 bool is_initalized{}; 36 bool is_initialized{};
37 37
38 // Debug settings 38 // Debug settings
39 bool is_debug_pad_enabled{}; 39 bool is_debug_pad_enabled{};
diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp
index 1f8a0f8ab..97537a2e2 100644
--- a/src/hid_core/resources/npad/npad.cpp
+++ b/src/hid_core/resources/npad/npad.cpp
@@ -193,7 +193,7 @@ void NPad::InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id) {
193 case Core::HID::NpadStyleIndex::None: 193 case Core::HID::NpadStyleIndex::None:
194 ASSERT(false); 194 ASSERT(false);
195 break; 195 break;
196 case Core::HID::NpadStyleIndex::ProController: 196 case Core::HID::NpadStyleIndex::Fullkey:
197 shared_memory->fullkey_color.attribute = ColorAttribute::Ok; 197 shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
198 shared_memory->fullkey_color.fullkey = body_colors.fullkey; 198 shared_memory->fullkey_color.fullkey = body_colors.fullkey;
199 shared_memory->battery_level_dual = battery_level.dual.battery_level; 199 shared_memory->battery_level_dual = battery_level.dual.battery_level;
@@ -491,7 +491,7 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
491 case Core::HID::NpadStyleIndex::None: 491 case Core::HID::NpadStyleIndex::None:
492 ASSERT(false); 492 ASSERT(false);
493 break; 493 break;
494 case Core::HID::NpadStyleIndex::ProController: 494 case Core::HID::NpadStyleIndex::Fullkey:
495 case Core::HID::NpadStyleIndex::NES: 495 case Core::HID::NpadStyleIndex::NES:
496 case Core::HID::NpadStyleIndex::SNES: 496 case Core::HID::NpadStyleIndex::SNES:
497 case Core::HID::NpadStyleIndex::N64: 497 case Core::HID::NpadStyleIndex::N64:
@@ -1292,7 +1292,7 @@ Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
1292 u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 1292 u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1293 auto& controller = GetControllerFromHandle(aruid, sixaxis_handle); 1293 auto& controller = GetControllerFromHandle(aruid, sixaxis_handle);
1294 switch (sixaxis_handle.npad_type) { 1294 switch (sixaxis_handle.npad_type) {
1295 case Core::HID::NpadStyleIndex::ProController: 1295 case Core::HID::NpadStyleIndex::Fullkey:
1296 case Core::HID::NpadStyleIndex::Pokeball: 1296 case Core::HID::NpadStyleIndex::Pokeball:
1297 return controller.shared_memory->sixaxis_fullkey_properties; 1297 return controller.shared_memory->sixaxis_fullkey_properties;
1298 case Core::HID::NpadStyleIndex::Handheld: 1298 case Core::HID::NpadStyleIndex::Handheld:
@@ -1315,7 +1315,7 @@ const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
1315 u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { 1315 u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
1316 const auto& controller = GetControllerFromHandle(aruid, sixaxis_handle); 1316 const auto& controller = GetControllerFromHandle(aruid, sixaxis_handle);
1317 switch (sixaxis_handle.npad_type) { 1317 switch (sixaxis_handle.npad_type) {
1318 case Core::HID::NpadStyleIndex::ProController: 1318 case Core::HID::NpadStyleIndex::Fullkey:
1319 case Core::HID::NpadStyleIndex::Pokeball: 1319 case Core::HID::NpadStyleIndex::Pokeball:
1320 return controller.shared_memory->sixaxis_fullkey_properties; 1320 return controller.shared_memory->sixaxis_fullkey_properties;
1321 case Core::HID::NpadStyleIndex::Handheld: 1321 case Core::HID::NpadStyleIndex::Handheld:
diff --git a/src/hid_core/resources/npad/npad_data.cpp b/src/hid_core/resources/npad/npad_data.cpp
index c7e9760cb..29ad5cb08 100644
--- a/src/hid_core/resources/npad/npad_data.cpp
+++ b/src/hid_core/resources/npad/npad_data.cpp
@@ -151,7 +151,7 @@ Core::HID::NpadStyleSet NPadData::GetSupportedNpadStyleSet() const {
151bool NPadData::IsNpadStyleIndexSupported(Core::HID::NpadStyleIndex style_index) const { 151bool NPadData::IsNpadStyleIndexSupported(Core::HID::NpadStyleIndex style_index) const {
152 Core::HID::NpadStyleTag style = {supported_npad_style_set}; 152 Core::HID::NpadStyleTag style = {supported_npad_style_set};
153 switch (style_index) { 153 switch (style_index) {
154 case Core::HID::NpadStyleIndex::ProController: 154 case Core::HID::NpadStyleIndex::Fullkey:
155 return style.fullkey.As<bool>(); 155 return style.fullkey.As<bool>();
156 case Core::HID::NpadStyleIndex::Handheld: 156 case Core::HID::NpadStyleIndex::Handheld:
157 return style.handheld.As<bool>(); 157 return style.handheld.As<bool>();
diff --git a/src/hid_core/resources/npad/npad_types.h b/src/hid_core/resources/npad/npad_types.h
index a02f9cf16..fd86c8e40 100644
--- a/src/hid_core/resources/npad/npad_types.h
+++ b/src/hid_core/resources/npad/npad_types.h
@@ -252,4 +252,103 @@ enum class NpadLagerType : u32 {
252 U, 252 U,
253}; 253};
254 254
255// nn::hidtypes::FeatureType
256struct FeatureType {
257 union {
258 u64 raw{};
259 BitField<0, 1, u64> has_left_analog_stick;
260 BitField<1, 1, u64> has_right_analog_stick;
261 BitField<2, 1, u64> has_left_joy_six_axis_sensor;
262 BitField<3, 1, u64> has_right_joy_six_axis_sensor;
263 BitField<4, 1, u64> has_fullkey_joy_six_axis_sensor;
264 BitField<5, 1, u64> has_left_lra_vibration_device;
265 BitField<6, 1, u64> has_right_lra_vibration_device;
266 BitField<7, 1, u64> has_gc_vibration_device;
267 BitField<8, 1, u64> has_erm_vibration_device;
268 BitField<9, 1, u64> has_left_joy_rail_bus;
269 BitField<10, 1, u64> has_right_joy_rail_bus;
270 BitField<11, 1, u64> has_internal_bus;
271 BitField<12, 1, u64> is_palma;
272 BitField<13, 1, u64> has_nfc;
273 BitField<14, 1, u64> has_ir_sensor;
274 BitField<15, 1, u64> is_analog_stick_calibration_supported;
275 BitField<16, 1, u64> is_six_axis_Sensor_user_calibration_supported;
276 BitField<17, 1, u64> has_left_right_joy_battery;
277 BitField<18, 1, u64> has_fullkey_battery;
278 BitField<19, 1, u64> is_disconnect_controller_if_battery_none;
279 BitField<20, 1, u64> has_controller_color;
280 BitField<21, 1, u64> has_grip_color;
281 BitField<22, 1, u64> has_identification_code;
282 BitField<23, 1, u64> has_bluetooth_address;
283 BitField<24, 1, u64> has_mcu;
284 BitField<25, 1, u64> has_notification_led;
285 BitField<26, 1, u64> has_directional_buttons;
286 BitField<27, 1, u64> has_indicator_led;
287 BitField<28, 1, u64> is_button_config_embedded_supported;
288 BitField<29, 1, u64> is_button_config_full_supported;
289 BitField<30, 1, u64> is_button_config_left_supported;
290 BitField<31, 1, u64> is_button_config_right_supported;
291 BitField<32, 1, u64> is_usb_hid_device;
292 BitField<33, 1, u64> is_kuina_device;
293 BitField<34, 1, u64> is_direct_usb_to_bt_switching_device;
294 BitField<35, 1, u64> is_normalize_analog_stick_with_inner_cross;
295 };
296};
297static_assert(sizeof(FeatureType) == 8, "FeatureType is an invalid size");
298
299// This is nn::hid::AssignmentStyle
300struct AssignmentStyle {
301 union {
302 u32 raw{};
303 BitField<0, 1, u32> is_external_assigned;
304 BitField<1, 1, u32> is_external_left_assigned;
305 BitField<2, 1, u32> is_external_right_assigned;
306 BitField<3, 1, u32> is_handheld_assigned;
307 BitField<4, 1, u32> is_handheld_left_assigned;
308 BitField<5, 1, u32> is_handheld_right_assigned;
309 };
310};
311static_assert(sizeof(AssignmentStyle) == 4, "AssignmentStyle is an invalid size");
312
313// This is nn::hid::server::IAbstractedPad::InternalFlags
314struct InternalFlags {
315 union {
316 u32 raw{};
317 BitField<0, 1, u32> is_bound;
318 BitField<1, 1, u32> is_connected;
319 BitField<2, 1, u32> is_battery_low_ovln_required;
320 BitField<3, 1, u32> is_battery_low_ovln_delay_required;
321 BitField<4, 1, u32> is_sample_received;
322 BitField<5, 1, u32> is_virtual_input;
323 BitField<6, 1, u32> is_wired;
324 BitField<8, 1, u32> use_center_clamp;
325 BitField<9, 1, u32> has_virtual_six_axis_sensor_acceleration;
326 BitField<10, 1, u32> has_virtual_six_axis_sensor_angle;
327 BitField<11, 1, u32> is_debug_pad;
328 };
329};
330static_assert(sizeof(InternalFlags) == 4, "InternalFlags is an invalid size");
331
332/// This is nn::hid::server::IAbstractedPad
333struct IAbstractedPad {
334 InternalFlags internal_flags;
335 u64 controller_id;
336 u32 controller_number;
337 u64 low_battery_display_delay_time;
338 u64 low_battery_display_delay_interval;
339 FeatureType feature_set;
340 FeatureType disabled_feature_set;
341 AssignmentStyle assignment_style;
342 Core::HID::NpadStyleIndex device_type;
343 Core::HID::NpadInterfaceType interface_type;
344 Core::HID::NpadPowerInfo power_info;
345 u32 pad_state;
346 u32 button_mask;
347 u32 system_button_mask;
348 u8 indicator;
349 std::vector<f32> virtual_six_axis_sensor_acceleration;
350 std::vector<f32> virtual_six_axis_sensor_angle;
351 u64 xcd_handle;
352 u64 color;
353};
255} // namespace Service::HID 354} // namespace Service::HID
diff --git a/src/hid_core/resources/npad/npad_vibration.cpp b/src/hid_core/resources/npad/npad_vibration.cpp
new file mode 100644
index 000000000..7056e8eab
--- /dev/null
+++ b/src/hid_core/resources/npad/npad_vibration.cpp
@@ -0,0 +1,80 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/hid_result.h"
5#include "hid_core/resources/npad/npad_vibration.h"
6
7namespace Service::HID {
8
9NpadVibration::NpadVibration() {}
10
11NpadVibration::~NpadVibration() = default;
12
13Result NpadVibration::Activate() {
14 std::scoped_lock lock{mutex};
15
16 const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume();
17 // if (master_volume < 0.0f || master_volume > 1.0f) {
18 // return ResultVibrationStrengthOutOfRange;
19 // }
20
21 volume = master_volume;
22 return ResultSuccess;
23}
24
25Result NpadVibration::Deactivate() {
26 return ResultSuccess;
27}
28
29Result NpadVibration::SetVibrationMasterVolume(f32 master_volume) {
30 std::scoped_lock lock{mutex};
31
32 if (master_volume < 0.0f && master_volume > 1.0f) {
33 return ResultVibrationStrengthOutOfRange;
34 }
35
36 volume = master_volume;
37 // nn::settings::system::SetVibrationMasterVolume(master_volume);
38
39 return ResultSuccess;
40}
41
42Result NpadVibration::GetVibrationVolume(f32& out_volume) const {
43 std::scoped_lock lock{mutex};
44 out_volume = volume;
45 return ResultSuccess;
46}
47
48Result NpadVibration::GetVibrationMasterVolume(f32& out_volume) const {
49 std::scoped_lock lock{mutex};
50
51 const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume();
52 // if (master_volume < 0.0f || master_volume > 1.0f) {
53 // return ResultVibrationStrengthOutOfRange;
54 // }
55
56 out_volume = master_volume;
57 return ResultSuccess;
58}
59
60Result NpadVibration::BeginPermitVibrationSession(u64 aruid) {
61 std::scoped_lock lock{mutex};
62 session_aruid = aruid;
63 volume = 1.0;
64 return ResultSuccess;
65}
66
67Result NpadVibration::EndPermitVibrationSession() {
68 std::scoped_lock lock{mutex};
69
70 const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume();
71 // if (master_volume < 0.0f || master_volume > 1.0f) {
72 // return ResultVibrationStrengthOutOfRange;
73 // }
74
75 volume = master_volume;
76 session_aruid = 0;
77 return ResultSuccess;
78}
79
80} // namespace Service::HID
diff --git a/src/hid_core/resources/npad/npad_vibration.h b/src/hid_core/resources/npad/npad_vibration.h
new file mode 100644
index 000000000..0748aeffc
--- /dev/null
+++ b/src/hid_core/resources/npad/npad_vibration.h
@@ -0,0 +1,34 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <mutex>
7
8#include "common/common_types.h"
9#include "core/hle/result.h"
10
11namespace Service::HID {
12
13class NpadVibration final {
14public:
15 explicit NpadVibration();
16 ~NpadVibration();
17
18 Result Activate();
19 Result Deactivate();
20
21 Result SetVibrationMasterVolume(f32 master_volume);
22 Result GetVibrationVolume(f32& out_volume) const;
23 Result GetVibrationMasterVolume(f32& out_volume) const;
24
25 Result BeginPermitVibrationSession(u64 aruid);
26 Result EndPermitVibrationSession();
27
28private:
29 f32 volume{};
30 u64 session_aruid{};
31 mutable std::mutex mutex;
32};
33
34} // namespace Service::HID
diff --git a/src/hid_core/resources/six_axis/six_axis.cpp b/src/hid_core/resources/six_axis/six_axis.cpp
index 8a9677c50..abb6fd152 100644
--- a/src/hid_core/resources/six_axis/six_axis.cpp
+++ b/src/hid_core/resources/six_axis/six_axis.cpp
@@ -114,7 +114,7 @@ void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
114 case Core::HID::NpadStyleIndex::None: 114 case Core::HID::NpadStyleIndex::None:
115 ASSERT(false); 115 ASSERT(false);
116 break; 116 break;
117 case Core::HID::NpadStyleIndex::ProController: 117 case Core::HID::NpadStyleIndex::Fullkey:
118 set_motion_state(sixaxis_fullkey_state, motion_state[0]); 118 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
119 break; 119 break;
120 case Core::HID::NpadStyleIndex::Handheld: 120 case Core::HID::NpadStyleIndex::Handheld:
@@ -249,7 +249,7 @@ Result SixAxis::EnableSixAxisSensorUnalteredPassthrough(
249 } 249 }
250 250
251 auto& sixaxis = GetSixaxisState(sixaxis_handle); 251 auto& sixaxis = GetSixaxisState(sixaxis_handle);
252 sixaxis.unaltered_passtrough = is_enabled; 252 sixaxis.unaltered_passthrough = is_enabled;
253 return ResultSuccess; 253 return ResultSuccess;
254} 254}
255 255
@@ -262,7 +262,7 @@ Result SixAxis::IsSixAxisSensorUnalteredPassthroughEnabled(
262 } 262 }
263 263
264 const auto& sixaxis = GetSixaxisState(sixaxis_handle); 264 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
265 is_enabled = sixaxis.unaltered_passtrough; 265 is_enabled = sixaxis.unaltered_passthrough;
266 return ResultSuccess; 266 return ResultSuccess;
267} 267}
268 268
@@ -345,7 +345,7 @@ SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
345 const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 345 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
346 auto& controller = GetControllerFromHandle(sixaxis_handle); 346 auto& controller = GetControllerFromHandle(sixaxis_handle);
347 switch (sixaxis_handle.npad_type) { 347 switch (sixaxis_handle.npad_type) {
348 case Core::HID::NpadStyleIndex::ProController: 348 case Core::HID::NpadStyleIndex::Fullkey:
349 case Core::HID::NpadStyleIndex::Pokeball: 349 case Core::HID::NpadStyleIndex::Pokeball:
350 return controller.sixaxis_fullkey; 350 return controller.sixaxis_fullkey;
351 case Core::HID::NpadStyleIndex::Handheld: 351 case Core::HID::NpadStyleIndex::Handheld:
@@ -368,7 +368,7 @@ const SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
368 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { 368 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
369 const auto& controller = GetControllerFromHandle(sixaxis_handle); 369 const auto& controller = GetControllerFromHandle(sixaxis_handle);
370 switch (sixaxis_handle.npad_type) { 370 switch (sixaxis_handle.npad_type) {
371 case Core::HID::NpadStyleIndex::ProController: 371 case Core::HID::NpadStyleIndex::Fullkey:
372 case Core::HID::NpadStyleIndex::Pokeball: 372 case Core::HID::NpadStyleIndex::Pokeball:
373 return controller.sixaxis_fullkey; 373 return controller.sixaxis_fullkey;
374 case Core::HID::NpadStyleIndex::Handheld: 374 case Core::HID::NpadStyleIndex::Handheld:
diff --git a/src/hid_core/resources/six_axis/six_axis.h b/src/hid_core/resources/six_axis/six_axis.h
index 1054e1b27..b4b00a441 100644
--- a/src/hid_core/resources/six_axis/six_axis.h
+++ b/src/hid_core/resources/six_axis/six_axis.h
@@ -62,7 +62,7 @@ private:
62 62
63 struct SixaxisParameters { 63 struct SixaxisParameters {
64 bool is_fusion_enabled{true}; 64 bool is_fusion_enabled{true};
65 bool unaltered_passtrough{false}; 65 bool unaltered_passthrough{false};
66 Core::HID::SixAxisSensorFusionParameters fusion{}; 66 Core::HID::SixAxisSensorFusionParameters fusion{};
67 Core::HID::SixAxisSensorCalibrationParameter calibration{}; 67 Core::HID::SixAxisSensorCalibrationParameter calibration{};
68 Core::HID::SixAxisSensorIcInformation ic_information{}; 68 Core::HID::SixAxisSensorIcInformation ic_information{};
diff --git a/src/hid_core/resources/vibration/gc_vibration_device.cpp b/src/hid_core/resources/vibration/gc_vibration_device.cpp
new file mode 100644
index 000000000..f01f81b9a
--- /dev/null
+++ b/src/hid_core/resources/vibration/gc_vibration_device.cpp
@@ -0,0 +1,106 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/hid_result.h"
5#include "hid_core/resources/npad/npad_types.h"
6#include "hid_core/resources/npad/npad_vibration.h"
7#include "hid_core/resources/vibration/gc_vibration_device.h"
8
9namespace Service::HID {
10
11NpadGcVibrationDevice::NpadGcVibrationDevice() {}
12
13Result NpadGcVibrationDevice::IncrementRefCounter() {
14 if (ref_counter == 0 && is_mounted) {
15 f32 volume = 1.0f;
16 const auto result = vibration_handler->GetVibrationVolume(volume);
17 if (result.IsSuccess()) {
18 // TODO: SendVibrationGcErmCommand
19 }
20 }
21 ref_counter++;
22 return ResultSuccess;
23}
24
25Result NpadGcVibrationDevice::DecrementRefCounter() {
26 if (ref_counter == 1 && !is_mounted) {
27 f32 volume = 1.0f;
28 const auto result = vibration_handler->GetVibrationVolume(volume);
29 if (result.IsSuccess()) {
30 // TODO: SendVibrationGcErmCommand
31 }
32 }
33
34 if (ref_counter > 0) {
35 ref_counter--;
36 }
37
38 return ResultSuccess;
39}
40
41Result NpadGcVibrationDevice::SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command) {
42 if (!is_mounted) {
43 return ResultSuccess;
44 }
45 f32 volume = 1.0f;
46 const auto result = vibration_handler->GetVibrationVolume(volume);
47 if (result.IsError()) {
48 return result;
49 }
50 if (volume == 0.0) {
51 command = Core::HID::VibrationGcErmCommand::Stop;
52 } else {
53 if (command > Core::HID::VibrationGcErmCommand::StopHard) {
54 // Abort
55 return ResultSuccess;
56 }
57 }
58 // TODO: SendVibrationGcErmCommand
59 return ResultSuccess;
60}
61
62Result NpadGcVibrationDevice::GetActualVibrationGcErmCommand(
63 Core::HID::VibrationGcErmCommand& out_command) {
64 if (!is_mounted) {
65 out_command = Core::HID::VibrationGcErmCommand::Stop;
66 return ResultSuccess;
67 }
68
69 f32 volume = 1.0f;
70 const auto result = vibration_handler->GetVibrationVolume(volume);
71 if (result.IsError()) {
72 return result;
73 }
74 if (volume == 0.0f) {
75 out_command = Core::HID::VibrationGcErmCommand::Stop;
76 return ResultSuccess;
77 }
78
79 // TODO: GetActualVibrationGcErmCommand
80 return ResultSuccess;
81}
82
83Result NpadGcVibrationDevice::SendVibrationNotificationPattern(
84 Core::HID::VibrationGcErmCommand command) {
85 if (!is_mounted) {
86 return ResultSuccess;
87 }
88
89 f32 volume = 1.0f;
90 const auto result = vibration_handler->GetVibrationVolume(volume);
91 if (result.IsError()) {
92 return result;
93 }
94 if (volume <= 0.0f) {
95 command = Core::HID::VibrationGcErmCommand::Stop;
96 }
97 if (command > Core::HID::VibrationGcErmCommand::StopHard) {
98 // Abort
99 return ResultSuccess;
100 }
101
102 // TODO: SendVibrationNotificationPattern
103 return ResultSuccess;
104}
105
106} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/gc_vibration_device.h b/src/hid_core/resources/vibration/gc_vibration_device.h
new file mode 100644
index 000000000..87abca57d
--- /dev/null
+++ b/src/hid_core/resources/vibration/gc_vibration_device.h
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <mutex>
8
9#include "common/common_types.h"
10#include "core/hle/result.h"
11#include "hid_core/hid_types.h"
12#include "hid_core/resources/npad/npad_types.h"
13#include "hid_core/resources/vibration/vibration_base.h"
14
15namespace Service::HID {
16class NpadVibration;
17
18/// Handles Npad request from HID interfaces
19class NpadGcVibrationDevice final : public NpadVibrationBase {
20public:
21 explicit NpadGcVibrationDevice();
22
23 Result IncrementRefCounter() override;
24 Result DecrementRefCounter() override;
25
26 Result SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command);
27
28 Result GetActualVibrationGcErmCommand(Core::HID::VibrationGcErmCommand& out_command);
29 Result SendVibrationNotificationPattern(Core::HID::VibrationGcErmCommand command);
30};
31} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/n64_vibration_device.cpp b/src/hid_core/resources/vibration/n64_vibration_device.cpp
new file mode 100644
index 000000000..639f87abf
--- /dev/null
+++ b/src/hid_core/resources/vibration/n64_vibration_device.cpp
@@ -0,0 +1,80 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/hid_result.h"
5#include "hid_core/resources/npad/npad_types.h"
6#include "hid_core/resources/npad/npad_vibration.h"
7#include "hid_core/resources/vibration/n64_vibration_device.h"
8
9namespace Service::HID {
10
11NpadN64VibrationDevice::NpadN64VibrationDevice() {}
12
13Result NpadN64VibrationDevice::IncrementRefCounter() {
14 if (ref_counter == 0 && is_mounted) {
15 f32 volume = 1.0f;
16 const auto result = vibration_handler->GetVibrationVolume(volume);
17 if (result.IsSuccess()) {
18 // TODO: SendVibrationInBool
19 }
20 }
21
22 ref_counter++;
23 return ResultSuccess;
24}
25
26Result NpadN64VibrationDevice::DecrementRefCounter() {
27 if (ref_counter == 1) {
28 if (!is_mounted) {
29 ref_counter = 0;
30 if (is_mounted != false) {
31 // TODO: SendVibrationInBool
32 }
33 return ResultSuccess;
34 }
35 f32 volume = 1.0f;
36 const auto result = vibration_handler->GetVibrationVolume(volume);
37 if (result.IsSuccess()) {
38 // TODO
39 }
40 }
41
42 if (ref_counter > 0) {
43 ref_counter--;
44 }
45
46 return ResultSuccess;
47}
48
49Result NpadN64VibrationDevice::SendValueInBool(bool is_vibrating) {
50 if (ref_counter < 1) {
51 return ResultVibrationNotInitialized;
52 }
53 if (is_mounted) {
54 f32 volume = 1.0f;
55 const auto result = vibration_handler->GetVibrationVolume(volume);
56 if (result.IsError()) {
57 return result;
58 }
59 // TODO: SendVibrationInBool
60 }
61 return ResultSuccess;
62}
63
64Result NpadN64VibrationDevice::SendVibrationNotificationPattern([[maybe_unused]] u32 pattern) {
65 if (!is_mounted) {
66 return ResultSuccess;
67 }
68 f32 volume = 1.0f;
69 const auto result = vibration_handler->GetVibrationVolume(volume);
70 if (result.IsError()) {
71 return result;
72 }
73 if (volume <= 0.0) {
74 pattern = 0;
75 }
76 // TODO: SendVibrationNotificationPattern
77 return ResultSuccess;
78}
79
80} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/n64_vibration_device.h b/src/hid_core/resources/vibration/n64_vibration_device.h
new file mode 100644
index 000000000..54e6efc1a
--- /dev/null
+++ b/src/hid_core/resources/vibration/n64_vibration_device.h
@@ -0,0 +1,29 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <mutex>
8
9#include "common/common_types.h"
10#include "core/hle/result.h"
11#include "hid_core/hid_types.h"
12#include "hid_core/resources/npad/npad_types.h"
13#include "hid_core/resources/vibration/vibration_base.h"
14
15namespace Service::HID {
16class NpadVibration;
17
18/// Handles Npad request from HID interfaces
19class NpadN64VibrationDevice final : public NpadVibrationBase {
20public:
21 explicit NpadN64VibrationDevice();
22
23 Result IncrementRefCounter() override;
24 Result DecrementRefCounter() override;
25
26 Result SendValueInBool(bool is_vibrating);
27 Result SendVibrationNotificationPattern(u32 pattern);
28};
29} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/vibration_base.cpp b/src/hid_core/resources/vibration/vibration_base.cpp
new file mode 100644
index 000000000..350f349c2
--- /dev/null
+++ b/src/hid_core/resources/vibration/vibration_base.cpp
@@ -0,0 +1,30 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/hid_result.h"
5#include "hid_core/resources/npad/npad_types.h"
6#include "hid_core/resources/npad/npad_vibration.h"
7#include "hid_core/resources/vibration/vibration_base.h"
8
9namespace Service::HID {
10
11NpadVibrationBase::NpadVibrationBase() {}
12
13Result NpadVibrationBase::IncrementRefCounter() {
14 ref_counter++;
15 return ResultSuccess;
16}
17
18Result NpadVibrationBase::DecrementRefCounter() {
19 if (ref_counter > 0) {
20 ref_counter--;
21 }
22
23 return ResultSuccess;
24}
25
26bool NpadVibrationBase::IsVibrationMounted() const {
27 return is_mounted;
28}
29
30} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/vibration_base.h b/src/hid_core/resources/vibration/vibration_base.h
new file mode 100644
index 000000000..c6c5fc4d9
--- /dev/null
+++ b/src/hid_core/resources/vibration/vibration_base.h
@@ -0,0 +1,28 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/result.h"
8
9namespace Service::HID {
10class NpadVibration;
11
12/// Handles Npad request from HID interfaces
13class NpadVibrationBase {
14public:
15 explicit NpadVibrationBase();
16
17 virtual Result IncrementRefCounter();
18 virtual Result DecrementRefCounter();
19
20 bool IsVibrationMounted() const;
21
22protected:
23 u64 xcd_handle{};
24 s32 ref_counter{};
25 bool is_mounted{};
26 NpadVibration* vibration_handler{nullptr};
27};
28} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/vibration_device.cpp b/src/hid_core/resources/vibration/vibration_device.cpp
new file mode 100644
index 000000000..888c3a7ed
--- /dev/null
+++ b/src/hid_core/resources/vibration/vibration_device.cpp
@@ -0,0 +1,84 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/hid_result.h"
5#include "hid_core/resources/npad/npad_types.h"
6#include "hid_core/resources/npad/npad_vibration.h"
7#include "hid_core/resources/vibration/vibration_device.h"
8
9namespace Service::HID {
10
11NpadVibrationDevice::NpadVibrationDevice() {}
12
13Result NpadVibrationDevice::IncrementRefCounter() {
14 ref_counter++;
15 return ResultSuccess;
16}
17
18Result NpadVibrationDevice::DecrementRefCounter() {
19 if (ref_counter > 0) {
20 ref_counter--;
21 }
22
23 return ResultSuccess;
24}
25
26Result NpadVibrationDevice::SendVibrationValue(const Core::HID::VibrationValue& value) {
27 if (ref_counter == 0) {
28 return ResultVibrationNotInitialized;
29 }
30 if (!is_mounted) {
31 return ResultSuccess;
32 }
33
34 f32 volume = 1.0f;
35 const auto result = vibration_handler->GetVibrationVolume(volume);
36 if (result.IsError()) {
37 return result;
38 }
39 if (volume <= 0.0f) {
40 // TODO: SendVibrationValue
41 return ResultSuccess;
42 }
43
44 Core::HID::VibrationValue vibration_value = value;
45 vibration_value.high_amplitude *= volume;
46 vibration_value.low_amplitude *= volume;
47
48 // TODO: SendVibrationValue
49 return ResultSuccess;
50}
51
52Result NpadVibrationDevice::SendVibrationNotificationPattern([[maybe_unused]] u32 pattern) {
53 if (!is_mounted) {
54 return ResultSuccess;
55 }
56
57 f32 volume = 1.0f;
58 const auto result = vibration_handler->GetVibrationVolume(volume);
59 if (result.IsError()) {
60 return result;
61 }
62 if (volume <= 0.0) {
63 pattern = 0;
64 }
65
66 // return xcd_handle->SendVibrationNotificationPattern(pattern);
67 return ResultSuccess;
68}
69
70Result NpadVibrationDevice::GetActualVibrationValue(Core::HID::VibrationValue& out_value) {
71 if (ref_counter < 1) {
72 return ResultVibrationNotInitialized;
73 }
74
75 out_value = Core::HID::DEFAULT_VIBRATION_VALUE;
76 if (!is_mounted) {
77 return ResultSuccess;
78 }
79
80 // TODO: SendVibrationValue
81 return ResultSuccess;
82}
83
84} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/vibration_device.h b/src/hid_core/resources/vibration/vibration_device.h
new file mode 100644
index 000000000..3574ad60b
--- /dev/null
+++ b/src/hid_core/resources/vibration/vibration_device.h
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <mutex>
8
9#include "common/common_types.h"
10#include "core/hle/result.h"
11#include "hid_core/hid_types.h"
12#include "hid_core/resources/npad/npad_types.h"
13#include "hid_core/resources/vibration/vibration_base.h"
14
15namespace Service::HID {
16class NpadVibration;
17
18/// Handles Npad request from HID interfaces
19class NpadVibrationDevice final : public NpadVibrationBase {
20public:
21 explicit NpadVibrationDevice();
22
23 Result IncrementRefCounter();
24 Result DecrementRefCounter();
25
26 Result SendVibrationValue(const Core::HID::VibrationValue& value);
27 Result SendVibrationNotificationPattern(u32 pattern);
28
29 Result GetActualVibrationValue(Core::HID::VibrationValue& out_value);
30
31private:
32 u32 device_index{};
33};
34
35} // namespace Service::HID
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index 1ff296af5..f1184a5fa 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -451,11 +451,11 @@ ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& p
451 std::tuple{Settings::NativeButton::ZL, PadButton::TriggerL, PadAxes::TriggerLeft}, 451 std::tuple{Settings::NativeButton::ZL, PadButton::TriggerL, PadAxes::TriggerLeft},
452 {Settings::NativeButton::ZR, PadButton::TriggerR, PadAxes::TriggerRight}, 452 {Settings::NativeButton::ZR, PadButton::TriggerR, PadAxes::TriggerRight},
453 }; 453 };
454 for (const auto& [switch_button, gcadapter_buton, gcadapter_axis] : switch_to_gcadapter_axis) { 454 for (const auto& [switch_button, gcadapter_button, gcadapter_axis] : switch_to_gcadapter_axis) {
455 Common::ParamPackage button_params{}; 455 Common::ParamPackage button_params{};
456 button_params.Set("engine", GetEngineName()); 456 button_params.Set("engine", GetEngineName());
457 button_params.Set("port", params.Get("port", 0)); 457 button_params.Set("port", params.Get("port", 0));
458 button_params.Set("button", static_cast<s32>(gcadapter_buton)); 458 button_params.Set("button", static_cast<s32>(gcadapter_button));
459 button_params.Set("axis", static_cast<s32>(gcadapter_axis)); 459 button_params.Set("axis", static_cast<s32>(gcadapter_axis));
460 button_params.Set("threshold", 0.5f); 460 button_params.Set("threshold", 0.5f);
461 button_params.Set("range", 1.9f); 461 button_params.Set("range", 1.9f);
diff --git a/src/input_common/helpers/joycon_protocol/irs.cpp b/src/input_common/helpers/joycon_protocol/irs.cpp
index 68b0589e3..5bf72114d 100644
--- a/src/input_common/helpers/joycon_protocol/irs.cpp
+++ b/src/input_common/helpers/joycon_protocol/irs.cpp
@@ -236,9 +236,9 @@ Common::Input::DriverResult IrsProtocol::WriteRegistersStep2() {
236 .number_of_registers = 0x8, 236 .number_of_registers = 0x8,
237 .registers = 237 .registers =
238 { 238 {
239 IrsRegister{IrRegistersAddress::LedIntensitiyMSB, 239 IrsRegister{IrRegistersAddress::LedIntensityMSB,
240 static_cast<u8>(led_intensity >> 8)}, 240 static_cast<u8>(led_intensity >> 8)},
241 {IrRegistersAddress::LedIntensitiyLSB, static_cast<u8>(led_intensity & 0xff)}, 241 {IrRegistersAddress::LedIntensityLSB, static_cast<u8>(led_intensity & 0xff)},
242 {IrRegistersAddress::ImageFlip, static_cast<u8>(image_flip)}, 242 {IrRegistersAddress::ImageFlip, static_cast<u8>(image_flip)},
243 {IrRegistersAddress::DenoiseSmoothing, static_cast<u8>((denoise >> 16) & 0xff)}, 243 {IrRegistersAddress::DenoiseSmoothing, static_cast<u8>((denoise >> 16) & 0xff)},
244 {IrRegistersAddress::DenoiseEdge, static_cast<u8>((denoise >> 8) & 0xff)}, 244 {IrRegistersAddress::DenoiseEdge, static_cast<u8>((denoise >> 8) & 0xff)},
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
index 77a43c67a..792f124e1 100644
--- a/src/input_common/helpers/joycon_protocol/joycon_types.h
+++ b/src/input_common/helpers/joycon_protocol/joycon_types.h
@@ -282,7 +282,7 @@ enum class NFCCommand : u8 {
282 CancelAll = 0x00, 282 CancelAll = 0x00,
283 StartPolling = 0x01, 283 StartPolling = 0x01,
284 StopPolling = 0x02, 284 StopPolling = 0x02,
285 StartWaitingRecieve = 0x04, 285 StartWaitingReceive = 0x04,
286 ReadNtag = 0x06, 286 ReadNtag = 0x06,
287 WriteNtag = 0x08, 287 WriteNtag = 0x08,
288 Mifare = 0x0F, 288 Mifare = 0x0F,
@@ -382,8 +382,8 @@ enum class IrRegistersAddress : u16 {
382 FinalizeConfig = 0x0700, 382 FinalizeConfig = 0x0700,
383 LedFilter = 0x0e00, 383 LedFilter = 0x0e00,
384 Leds = 0x1000, 384 Leds = 0x1000,
385 LedIntensitiyMSB = 0x1100, 385 LedIntensityMSB = 0x1100,
386 LedIntensitiyLSB = 0x1200, 386 LedIntensityLSB = 0x1200,
387 ImageFlip = 0x2d00, 387 ImageFlip = 0x2d00,
388 Resolution = 0x2e00, 388 Resolution = 0x2e00,
389 DigitalGainLSB = 0x2e01, 389 DigitalGainLSB = 0x2e01,
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp
index 09953394b..db83f9ef4 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.cpp
+++ b/src/input_common/helpers/joycon_protocol/nfc.cpp
@@ -519,13 +519,13 @@ Common::Input::DriverResult NfcProtocol::GetMifareData(
519 } 519 }
520 520
521 if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) { 521 if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) {
522 constexpr std::size_t DATA_LENGHT = 0x10 + 1; 522 constexpr std::size_t DATA_LENGTH = 0x10 + 1;
523 constexpr std::size_t DATA_START = 11; 523 constexpr std::size_t DATA_START = 11;
524 const u8 number_of_elements = output.mcu_data[10]; 524 const u8 number_of_elements = output.mcu_data[10];
525 for (std::size_t i = 0; i < number_of_elements; i++) { 525 for (std::size_t i = 0; i < number_of_elements; i++) {
526 out_data[i].sector = output.mcu_data[DATA_START + (i * DATA_LENGHT)]; 526 out_data[i].sector = output.mcu_data[DATA_START + (i * DATA_LENGTH)];
527 memcpy(out_data[i].data.data(), 527 memcpy(out_data[i].data.data(),
528 output.mcu_data.data() + DATA_START + 1 + (i * DATA_LENGHT), 528 output.mcu_data.data() + DATA_START + 1 + (i * DATA_LENGTH),
529 sizeof(MifareReadData::data)); 529 sizeof(MifareReadData::data));
530 } 530 }
531 package_index++; 531 package_index++;
@@ -659,7 +659,7 @@ Common::Input::DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandRespon
659Common::Input::DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, 659Common::Input::DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output,
660 u8 packet_id) { 660 u8 packet_id) {
661 NFCRequestState request{ 661 NFCRequestState request{
662 .command_argument = NFCCommand::StartWaitingRecieve, 662 .command_argument = NFCCommand::StartWaitingReceive,
663 .block_id = {}, 663 .block_id = {},
664 .packet_id = packet_id, 664 .packet_id = packet_id,
665 .packet_flag = MCUPacketFlag::LastCommandPacket, 665 .packet_flag = MCUPacketFlag::LastCommandPacket,
diff --git a/src/input_common/helpers/joycon_protocol/rumble.cpp b/src/input_common/helpers/joycon_protocol/rumble.cpp
index 7647f505e..9fd0b8470 100644
--- a/src/input_common/helpers/joycon_protocol/rumble.cpp
+++ b/src/input_common/helpers/joycon_protocol/rumble.cpp
@@ -67,7 +67,7 @@ u8 RumbleProtocol::EncodeHighAmplitude(f32 amplitude) const {
67 // More information about these values can be found here: 67 // More information about these values can be found here:
68 // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md 68 // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
69 69
70 static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{ 70 static constexpr std::array<std::pair<f32, int>, 101> high_frequency_amplitude{
71 std::pair<f32, int>{0.0f, 0x0}, 71 std::pair<f32, int>{0.0f, 0x0},
72 {0.01f, 0x2}, 72 {0.01f, 0x2},
73 {0.012f, 0x4}, 73 {0.012f, 0x4},
@@ -171,20 +171,20 @@ u8 RumbleProtocol::EncodeHighAmplitude(f32 amplitude) const {
171 {1.003f, 0xc8}, 171 {1.003f, 0xc8},
172 }; 172 };
173 173
174 for (const auto& [amplitude_value, code] : high_fequency_amplitude) { 174 for (const auto& [amplitude_value, code] : high_frequency_amplitude) {
175 if (amplitude <= amplitude_value) { 175 if (amplitude <= amplitude_value) {
176 return static_cast<u8>(code); 176 return static_cast<u8>(code);
177 } 177 }
178 } 178 }
179 179
180 return static_cast<u8>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second); 180 return static_cast<u8>(high_frequency_amplitude[high_frequency_amplitude.size() - 1].second);
181} 181}
182 182
183u16 RumbleProtocol::EncodeLowAmplitude(f32 amplitude) const { 183u16 RumbleProtocol::EncodeLowAmplitude(f32 amplitude) const {
184 // More information about these values can be found here: 184 // More information about these values can be found here:
185 // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md 185 // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
186 186
187 static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{ 187 static constexpr std::array<std::pair<f32, int>, 101> high_frequency_amplitude{
188 std::pair<f32, int>{0.0f, 0x0040}, 188 std::pair<f32, int>{0.0f, 0x0040},
189 {0.01f, 0x8040}, 189 {0.01f, 0x8040},
190 {0.012f, 0x0041}, 190 {0.012f, 0x0041},
@@ -288,13 +288,13 @@ u16 RumbleProtocol::EncodeLowAmplitude(f32 amplitude) const {
288 {1.003f, 0x0072}, 288 {1.003f, 0x0072},
289 }; 289 };
290 290
291 for (const auto& [amplitude_value, code] : high_fequency_amplitude) { 291 for (const auto& [amplitude_value, code] : high_frequency_amplitude) {
292 if (amplitude <= amplitude_value) { 292 if (amplitude <= amplitude_value) {
293 return static_cast<u16>(code); 293 return static_cast<u16>(code);
294 } 294 }
295 } 295 }
296 296
297 return static_cast<u16>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second); 297 return static_cast<u16>(high_frequency_amplitude[high_frequency_amplitude.size() - 1].second);
298} 298}
299 299
300} // namespace InputCommon::Joycon 300} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/udp_protocol.h b/src/input_common/helpers/udp_protocol.h
index d9643ffe0..dba9f87d9 100644
--- a/src/input_common/helpers/udp_protocol.h
+++ b/src/input_common/helpers/udp_protocol.h
@@ -78,7 +78,7 @@ namespace Request {
78enum RegisterFlags : u8 { 78enum RegisterFlags : u8 {
79 AllPads, 79 AllPads,
80 PadID, 80 PadID,
81 PadMACAdddress, 81 PadMACAddress,
82}; 82};
83 83
84struct Version {}; 84struct Version {};
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp
index b94cb24ad..a6845273c 100644
--- a/src/network/room_member.cpp
+++ b/src/network/room_member.cpp
@@ -724,7 +724,7 @@ RoomMember::CallbackHandle<RoomInformation> RoomMember::BindOnRoomInformationCha
724 return room_member_impl->Bind(callback); 724 return room_member_impl->Bind(callback);
725} 725}
726 726
727RoomMember::CallbackHandle<ChatEntry> RoomMember::BindOnChatMessageRecieved( 727RoomMember::CallbackHandle<ChatEntry> RoomMember::BindOnChatMessageReceived(
728 std::function<void(const ChatEntry&)> callback) { 728 std::function<void(const ChatEntry&)> callback) {
729 return room_member_impl->Bind(callback); 729 return room_member_impl->Bind(callback);
730} 730}
diff --git a/src/network/room_member.h b/src/network/room_member.h
index 33ac18e72..37e9ea16a 100644
--- a/src/network/room_member.h
+++ b/src/network/room_member.h
@@ -254,7 +254,7 @@ public:
254 * @param callback The function to call 254 * @param callback The function to call
255 * @return A handle used for removing the function from the registered list 255 * @return A handle used for removing the function from the registered list
256 */ 256 */
257 CallbackHandle<ChatEntry> BindOnChatMessageRecieved( 257 CallbackHandle<ChatEntry> BindOnChatMessageReceived(
258 std::function<void(const ChatEntry&)> callback); 258 std::function<void(const ChatEntry&)> callback);
259 259
260 /** 260 /**
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index b2ceeefc4..c5ac7b8f2 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -608,8 +608,8 @@ std::string EmitContext::DefineGlobalMemoryFunctions() {
608 const auto aligned_low_addr{fmt::format("{}&{}", addr_xy[0], ssbo_align_mask)}; 608 const auto aligned_low_addr{fmt::format("{}&{}", addr_xy[0], ssbo_align_mask)};
609 const auto aligned_addr{fmt::format("uvec2({},{})", aligned_low_addr, addr_xy[1])}; 609 const auto aligned_addr{fmt::format("uvec2({},{})", aligned_low_addr, addr_xy[1])};
610 const auto addr_pack{fmt::format("packUint2x32({})", aligned_addr)}; 610 const auto addr_pack{fmt::format("packUint2x32({})", aligned_addr)};
611 const auto addr_statment{fmt::format("uint64_t {}={};", ssbo_addr, addr_pack)}; 611 const auto addr_statement{fmt::format("uint64_t {}={};", ssbo_addr, addr_pack)};
612 func += addr_statment; 612 func += addr_statement;
613 613
614 const auto size_vec{fmt::format("uvec2({},{})", size_xy[0], size_xy[1])}; 614 const auto size_vec{fmt::format("uvec2({},{})", size_xy[0], size_xy[1])};
615 const auto comp_lhs{fmt::format("(addr>={})", ssbo_addr)}; 615 const auto comp_lhs{fmt::format("(addr>={})", ssbo_addr)};
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
index 8693801c7..bdcbccfde 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
@@ -65,6 +65,14 @@ void WriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value&
65 WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32, sizeof(u32), 65 WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32, sizeof(u32),
66 &StorageDefinitions::U32, index_offset); 66 &StorageDefinitions::U32, index_offset);
67} 67}
68
69void WriteStorageByCasLoop(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
70 Id value, Id bit_offset, Id bit_count) {
71 const Id pointer{StoragePointer(ctx, binding, offset, ctx.storage_types.U32, sizeof(u32),
72 &StorageDefinitions::U32)};
73 ctx.OpFunctionCall(ctx.TypeVoid(), ctx.write_storage_cas_loop_func, pointer, value, bit_offset,
74 bit_count);
75}
68} // Anonymous namespace 76} // Anonymous namespace
69 77
70void EmitLoadGlobalU8(EmitContext&) { 78void EmitLoadGlobalU8(EmitContext&) {
@@ -219,26 +227,42 @@ Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Valu
219 227
220void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 228void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
221 Id value) { 229 Id value) {
222 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U8, value), ctx.storage_types.U8, 230 if (ctx.profile.support_int8) {
223 sizeof(u8), &StorageDefinitions::U8); 231 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U8, value), ctx.storage_types.U8,
232 sizeof(u8), &StorageDefinitions::U8);
233 } else {
234 WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset8(offset), ctx.Const(8u));
235 }
224} 236}
225 237
226void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 238void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
227 Id value) { 239 Id value) {
228 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S8, value), ctx.storage_types.S8, 240 if (ctx.profile.support_int8) {
229 sizeof(s8), &StorageDefinitions::S8); 241 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S8, value), ctx.storage_types.S8,
242 sizeof(s8), &StorageDefinitions::S8);
243 } else {
244 WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset8(offset), ctx.Const(8u));
245 }
230} 246}
231 247
232void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 248void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
233 Id value) { 249 Id value) {
234 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U16, value), ctx.storage_types.U16, 250 if (ctx.profile.support_int16) {
235 sizeof(u16), &StorageDefinitions::U16); 251 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U16, value), ctx.storage_types.U16,
252 sizeof(u16), &StorageDefinitions::U16);
253 } else {
254 WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset16(offset), ctx.Const(16u));
255 }
236} 256}
237 257
238void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 258void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
239 Id value) { 259 Id value) {
240 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S16, value), ctx.storage_types.S16, 260 if (ctx.profile.support_int16) {
241 sizeof(s16), &StorageDefinitions::S16); 261 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S16, value), ctx.storage_types.S16,
262 sizeof(s16), &StorageDefinitions::S16);
263 } else {
264 WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset16(offset), ctx.Const(16u));
265 }
242} 266}
243 267
244void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 268void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 0442adc83..a27f2f73a 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -480,6 +480,7 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf
480 DefineTextures(program.info, texture_binding, bindings.texture_scaling_index); 480 DefineTextures(program.info, texture_binding, bindings.texture_scaling_index);
481 DefineImages(program.info, image_binding, bindings.image_scaling_index); 481 DefineImages(program.info, image_binding, bindings.image_scaling_index);
482 DefineAttributeMemAccess(program.info); 482 DefineAttributeMemAccess(program.info);
483 DefineWriteStorageCasLoopFunction(program.info);
483 DefineGlobalMemoryFunctions(program.info); 484 DefineGlobalMemoryFunctions(program.info);
484 DefineRescalingInput(program.info); 485 DefineRescalingInput(program.info);
485 DefineRenderArea(program.info); 486 DefineRenderArea(program.info);
@@ -877,6 +878,56 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
877 } 878 }
878} 879}
879 880
881void EmitContext::DefineWriteStorageCasLoopFunction(const Info& info) {
882 if (profile.support_int8 && profile.support_int16) {
883 return;
884 }
885 if (!info.uses_int8 && !info.uses_int16) {
886 return;
887 }
888
889 AddCapability(spv::Capability::VariablePointersStorageBuffer);
890
891 const Id ptr_type{TypePointer(spv::StorageClass::StorageBuffer, U32[1])};
892 const Id func_type{TypeFunction(void_id, ptr_type, U32[1], U32[1], U32[1])};
893 const Id func{OpFunction(void_id, spv::FunctionControlMask::MaskNone, func_type)};
894 const Id pointer{OpFunctionParameter(ptr_type)};
895 const Id value{OpFunctionParameter(U32[1])};
896 const Id bit_offset{OpFunctionParameter(U32[1])};
897 const Id bit_count{OpFunctionParameter(U32[1])};
898
899 AddLabel();
900 const Id scope_device{Const(1u)};
901 const Id ordering_relaxed{u32_zero_value};
902 const Id body_label{OpLabel()};
903 const Id continue_label{OpLabel()};
904 const Id endloop_label{OpLabel()};
905 const Id beginloop_label{OpLabel()};
906 OpBranch(beginloop_label);
907
908 AddLabel(beginloop_label);
909 OpLoopMerge(endloop_label, continue_label, spv::LoopControlMask::MaskNone);
910 OpBranch(body_label);
911
912 AddLabel(body_label);
913 const Id expected_value{OpLoad(U32[1], pointer)};
914 const Id desired_value{OpBitFieldInsert(U32[1], expected_value, value, bit_offset, bit_count)};
915 const Id actual_value{OpAtomicCompareExchange(U32[1], pointer, scope_device, ordering_relaxed,
916 ordering_relaxed, desired_value, expected_value)};
917 const Id store_successful{OpIEqual(U1, expected_value, actual_value)};
918 OpBranchConditional(store_successful, endloop_label, continue_label);
919
920 AddLabel(endloop_label);
921 OpReturn();
922
923 AddLabel(continue_label);
924 OpBranch(beginloop_label);
925
926 OpFunctionEnd();
927
928 write_storage_cas_loop_func = func;
929}
930
880void EmitContext::DefineGlobalMemoryFunctions(const Info& info) { 931void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
881 if (!info.uses_global_memory || !profile.support_int64) { 932 if (!info.uses_global_memory || !profile.support_int64) {
882 return; 933 return;
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index 56019ad89..40adcb6b6 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -325,6 +325,8 @@ public:
325 Id f32x2_min_cas{}; 325 Id f32x2_min_cas{};
326 Id f32x2_max_cas{}; 326 Id f32x2_max_cas{};
327 327
328 Id write_storage_cas_loop_func{};
329
328 Id load_global_func_u32{}; 330 Id load_global_func_u32{};
329 Id load_global_func_u32x2{}; 331 Id load_global_func_u32x2{};
330 Id load_global_func_u32x4{}; 332 Id load_global_func_u32x4{};
@@ -372,6 +374,7 @@ private:
372 void DefineTextures(const Info& info, u32& binding, u32& scaling_index); 374 void DefineTextures(const Info& info, u32& binding, u32& scaling_index);
373 void DefineImages(const Info& info, u32& binding, u32& scaling_index); 375 void DefineImages(const Info& info, u32& binding, u32& scaling_index);
374 void DefineAttributeMemAccess(const Info& info); 376 void DefineAttributeMemAccess(const Info& info);
377 void DefineWriteStorageCasLoopFunction(const Info& info);
375 void DefineGlobalMemoryFunctions(const Info& info); 378 void DefineGlobalMemoryFunctions(const Info& info);
376 void DefineRescalingInput(const Info& info); 379 void DefineRescalingInput(const Info& info);
377 void DefineRescalingInputPushConstant(); 380 void DefineRescalingInputPushConstant();
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index f8d8ca80a..51e7ab1e1 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -55,7 +55,7 @@ public:
55 /// Requests to begin a renderpass. 55 /// Requests to begin a renderpass.
56 void RequestRenderpass(const Framebuffer* framebuffer); 56 void RequestRenderpass(const Framebuffer* framebuffer);
57 57
58 /// Requests the current executino context to be able to execute operations only allowed outside 58 /// Requests the current execution context to be able to execute operations only allowed outside
59 /// of a renderpass. 59 /// of a renderpass.
60 void RequestOutsideRenderPassOperationContext(); 60 void RequestOutsideRenderPassOperationContext();
61 61
diff --git a/src/yuzu/applets/qt_amiibo_settings.cpp b/src/yuzu/applets/qt_amiibo_settings.cpp
index b457a736a..b91796dde 100644
--- a/src/yuzu/applets/qt_amiibo_settings.cpp
+++ b/src/yuzu/applets/qt_amiibo_settings.cpp
@@ -36,7 +36,7 @@ QtAmiiboSettingsDialog::QtAmiiboSettingsDialog(QWidget* parent,
36QtAmiiboSettingsDialog::~QtAmiiboSettingsDialog() = default; 36QtAmiiboSettingsDialog::~QtAmiiboSettingsDialog() = default;
37 37
38int QtAmiiboSettingsDialog::exec() { 38int QtAmiiboSettingsDialog::exec() {
39 if (!is_initalized) { 39 if (!is_initialized) {
40 return QDialog::Rejected; 40 return QDialog::Rejected;
41 } 41 }
42 return QDialog::exec(); 42 return QDialog::exec();
@@ -66,7 +66,7 @@ void QtAmiiboSettingsDialog::LoadInfo() {
66 QString::fromStdString(input_subsystem->GetVirtualAmiibo()->GetLastFilePath())); 66 QString::fromStdString(input_subsystem->GetVirtualAmiibo()->GetLastFilePath()));
67 67
68 SetSettingsDescription(); 68 SetSettingsDescription();
69 is_initalized = true; 69 is_initialized = true;
70} 70}
71 71
72void QtAmiiboSettingsDialog::LoadAmiiboInfo() { 72void QtAmiiboSettingsDialog::LoadAmiiboInfo() {
diff --git a/src/yuzu/applets/qt_amiibo_settings.h b/src/yuzu/applets/qt_amiibo_settings.h
index ee66a0255..3833cf6f2 100644
--- a/src/yuzu/applets/qt_amiibo_settings.h
+++ b/src/yuzu/applets/qt_amiibo_settings.h
@@ -58,7 +58,7 @@ private:
58 Core::Frontend::CabinetParameters parameters; 58 Core::Frontend::CabinetParameters parameters;
59 59
60 // If false amiibo settings failed to load 60 // If false amiibo settings failed to load
61 bool is_initalized{}; 61 bool is_initialized{};
62}; 62};
63 63
64class QtAmiiboSettings final : public QObject, public Core::Frontend::CabinetApplet { 64class QtAmiiboSettings final : public QObject, public Core::Frontend::CabinetApplet {
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 8b340ee6c..48ce860ad 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -41,7 +41,7 @@ void UpdateController(Core::HID::EmulatedController* controller,
41bool IsControllerCompatible(Core::HID::NpadStyleIndex controller_type, 41bool IsControllerCompatible(Core::HID::NpadStyleIndex controller_type,
42 Core::Frontend::ControllerParameters parameters) { 42 Core::Frontend::ControllerParameters parameters) {
43 switch (controller_type) { 43 switch (controller_type) {
44 case Core::HID::NpadStyleIndex::ProController: 44 case Core::HID::NpadStyleIndex::Fullkey:
45 return parameters.allow_pro_controller; 45 return parameters.allow_pro_controller;
46 case Core::HID::NpadStyleIndex::JoyconDual: 46 case Core::HID::NpadStyleIndex::JoyconDual:
47 return parameters.allow_dual_joycons; 47 return parameters.allow_dual_joycons;
@@ -462,7 +462,7 @@ void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index
462 }; 462 };
463 463
464 if (npad_style_set.fullkey == 1) { 464 if (npad_style_set.fullkey == 1) {
465 add_item(Core::HID::NpadStyleIndex::ProController, tr("Pro Controller")); 465 add_item(Core::HID::NpadStyleIndex::Fullkey, tr("Pro Controller"));
466 } 466 }
467 467
468 if (npad_style_set.joycon_dual == 1) { 468 if (npad_style_set.joycon_dual == 1) {
@@ -519,7 +519,7 @@ Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex
519 [index](const auto& pair) { return pair.first == index; }); 519 [index](const auto& pair) { return pair.first == index; });
520 520
521 if (it == pairs.end()) { 521 if (it == pairs.end()) {
522 return Core::HID::NpadStyleIndex::ProController; 522 return Core::HID::NpadStyleIndex::Fullkey;
523 } 523 }
524 524
525 return it->second; 525 return it->second;
@@ -549,7 +549,7 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
549 const QString stylesheet = [this, player_index] { 549 const QString stylesheet = [this, player_index] {
550 switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), 550 switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
551 player_index)) { 551 player_index)) {
552 case Core::HID::NpadStyleIndex::ProController: 552 case Core::HID::NpadStyleIndex::Fullkey:
553 case Core::HID::NpadStyleIndex::GameCube: 553 case Core::HID::NpadStyleIndex::GameCube:
554 return QStringLiteral("image: url(:/controller/applet_pro_controller%0); "); 554 return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
555 case Core::HID::NpadStyleIndex::JoyconDual: 555 case Core::HID::NpadStyleIndex::JoyconDual:
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp
index bbe17c35e..ac81ace9e 100644
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -832,7 +832,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
832 }(); 832 }();
833 833
834 switch (controller_type) { 834 switch (controller_type) {
835 case Core::HID::NpadStyleIndex::ProController: 835 case Core::HID::NpadStyleIndex::Fullkey:
836 case Core::HID::NpadStyleIndex::GameCube: 836 case Core::HID::NpadStyleIndex::GameCube:
837 ui->icon_controller->setStyleSheet( 837 ui->icon_controller->setStyleSheet(
838 QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); 838 QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 60edd464c..ae12b3481 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -170,7 +170,7 @@ public:
170 170
171 void resizeEvent(QResizeEvent* event) override; 171 void resizeEvent(QResizeEvent* event) override;
172 172
173 /// Converts a Qt keybard key into NativeKeyboard key 173 /// Converts a Qt keyboard key into NativeKeyboard key
174 static int QtKeyToSwitchKey(Qt::Key qt_keys); 174 static int QtKeyToSwitchKey(Qt::Key qt_keys);
175 175
176 /// Converts a Qt modifier keys into NativeKeyboard modifier keys 176 /// Converts a Qt modifier keys into NativeKeyboard modifier keys
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 9745757c7..400917f9d 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -1094,7 +1094,7 @@ void ConfigureInputPlayer::SetConnectableControllers() {
1094 }; 1094 };
1095 1095
1096 if (npad_style_set.fullkey == 1) { 1096 if (npad_style_set.fullkey == 1) {
1097 add_item(Core::HID::NpadStyleIndex::ProController, tr("Pro Controller")); 1097 add_item(Core::HID::NpadStyleIndex::Fullkey, tr("Pro Controller"));
1098 } 1098 }
1099 1099
1100 if (npad_style_set.joycon_dual == 1) { 1100 if (npad_style_set.joycon_dual == 1) {
@@ -1149,7 +1149,7 @@ Core::HID::NpadStyleIndex ConfigureInputPlayer::GetControllerTypeFromIndex(int i
1149 [index](const auto& pair) { return pair.first == index; }); 1149 [index](const auto& pair) { return pair.first == index; });
1150 1150
1151 if (it == index_controller_type_pairs.end()) { 1151 if (it == index_controller_type_pairs.end()) {
1152 return Core::HID::NpadStyleIndex::ProController; 1152 return Core::HID::NpadStyleIndex::Fullkey;
1153 } 1153 }
1154 1154
1155 return it->second; 1155 return it->second;
@@ -1178,7 +1178,7 @@ void ConfigureInputPlayer::UpdateInputDevices() {
1178void ConfigureInputPlayer::UpdateControllerAvailableButtons() { 1178void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1179 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); 1179 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
1180 if (debug) { 1180 if (debug) {
1181 layout = Core::HID::NpadStyleIndex::ProController; 1181 layout = Core::HID::NpadStyleIndex::Fullkey;
1182 } 1182 }
1183 1183
1184 // List of all the widgets that will be hidden by any of the following layouts that need 1184 // List of all the widgets that will be hidden by any of the following layouts that need
@@ -1206,7 +1206,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1206 1206
1207 std::vector<QWidget*> layout_hidden; 1207 std::vector<QWidget*> layout_hidden;
1208 switch (layout) { 1208 switch (layout) {
1209 case Core::HID::NpadStyleIndex::ProController: 1209 case Core::HID::NpadStyleIndex::Fullkey:
1210 case Core::HID::NpadStyleIndex::Handheld: 1210 case Core::HID::NpadStyleIndex::Handheld:
1211 layout_hidden = { 1211 layout_hidden = {
1212 ui->buttonShoulderButtonsSLSRLeft, 1212 ui->buttonShoulderButtonsSLSRLeft,
@@ -1254,7 +1254,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1254void ConfigureInputPlayer::UpdateControllerEnabledButtons() { 1254void ConfigureInputPlayer::UpdateControllerEnabledButtons() {
1255 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); 1255 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
1256 if (debug) { 1256 if (debug) {
1257 layout = Core::HID::NpadStyleIndex::ProController; 1257 layout = Core::HID::NpadStyleIndex::Fullkey;
1258 } 1258 }
1259 1259
1260 // List of all the widgets that will be disabled by any of the following layouts that need 1260 // List of all the widgets that will be disabled by any of the following layouts that need
@@ -1271,7 +1271,7 @@ void ConfigureInputPlayer::UpdateControllerEnabledButtons() {
1271 1271
1272 std::vector<QWidget*> layout_disable; 1272 std::vector<QWidget*> layout_disable;
1273 switch (layout) { 1273 switch (layout) {
1274 case Core::HID::NpadStyleIndex::ProController: 1274 case Core::HID::NpadStyleIndex::Fullkey:
1275 case Core::HID::NpadStyleIndex::JoyconDual: 1275 case Core::HID::NpadStyleIndex::JoyconDual:
1276 case Core::HID::NpadStyleIndex::Handheld: 1276 case Core::HID::NpadStyleIndex::Handheld:
1277 case Core::HID::NpadStyleIndex::JoyconLeft: 1277 case Core::HID::NpadStyleIndex::JoyconLeft:
@@ -1304,7 +1304,7 @@ void ConfigureInputPlayer::UpdateMotionButtons() {
1304 1304
1305 // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller. 1305 // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller.
1306 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { 1306 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
1307 case Core::HID::NpadStyleIndex::ProController: 1307 case Core::HID::NpadStyleIndex::Fullkey:
1308 case Core::HID::NpadStyleIndex::JoyconLeft: 1308 case Core::HID::NpadStyleIndex::JoyconLeft:
1309 case Core::HID::NpadStyleIndex::Handheld: 1309 case Core::HID::NpadStyleIndex::Handheld:
1310 // Show "Motion 1" and hide "Motion 2". 1310 // Show "Motion 1" and hide "Motion 2".
@@ -1333,11 +1333,11 @@ void ConfigureInputPlayer::UpdateMotionButtons() {
1333void ConfigureInputPlayer::UpdateControllerButtonNames() { 1333void ConfigureInputPlayer::UpdateControllerButtonNames() {
1334 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); 1334 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
1335 if (debug) { 1335 if (debug) {
1336 layout = Core::HID::NpadStyleIndex::ProController; 1336 layout = Core::HID::NpadStyleIndex::Fullkey;
1337 } 1337 }
1338 1338
1339 switch (layout) { 1339 switch (layout) {
1340 case Core::HID::NpadStyleIndex::ProController: 1340 case Core::HID::NpadStyleIndex::Fullkey:
1341 case Core::HID::NpadStyleIndex::JoyconDual: 1341 case Core::HID::NpadStyleIndex::JoyconDual:
1342 case Core::HID::NpadStyleIndex::Handheld: 1342 case Core::HID::NpadStyleIndex::Handheld:
1343 case Core::HID::NpadStyleIndex::JoyconLeft: 1343 case Core::HID::NpadStyleIndex::JoyconLeft:
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index 19fdca7d3..b3d9d8006 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -244,7 +244,7 @@ void PlayerControlPreview::paintEvent(QPaintEvent* event) {
244 case Core::HID::NpadStyleIndex::GameCube: 244 case Core::HID::NpadStyleIndex::GameCube:
245 DrawGCController(p, center); 245 DrawGCController(p, center);
246 break; 246 break;
247 case Core::HID::NpadStyleIndex::ProController: 247 case Core::HID::NpadStyleIndex::Fullkey:
248 default: 248 default:
249 DrawProController(p, center); 249 DrawProController(p, center);
250 break; 250 break;
@@ -845,12 +845,12 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
845 DrawSymbol(p, face_center + QPoint(-face_distance, 1), Symbol::Y, text_size); 845 DrawSymbol(p, face_center + QPoint(-face_distance, 1), Symbol::Y, text_size);
846 846
847 // D-pad buttons 847 // D-pad buttons
848 const QPointF dpad_postion = center + QPoint(-61, 0); 848 const QPointF dpad_position = center + QPoint(-61, 0);
849 DrawArrowButton(p, dpad_postion, Direction::Up, button_values[DUp]); 849 DrawArrowButton(p, dpad_position, Direction::Up, button_values[DUp]);
850 DrawArrowButton(p, dpad_postion, Direction::Left, button_values[DLeft]); 850 DrawArrowButton(p, dpad_position, Direction::Left, button_values[DLeft]);
851 DrawArrowButton(p, dpad_postion, Direction::Right, button_values[DRight]); 851 DrawArrowButton(p, dpad_position, Direction::Right, button_values[DRight]);
852 DrawArrowButton(p, dpad_postion, Direction::Down, button_values[DDown]); 852 DrawArrowButton(p, dpad_position, Direction::Down, button_values[DDown]);
853 DrawArrowButtonOutline(p, dpad_postion); 853 DrawArrowButtonOutline(p, dpad_position);
854 854
855 // ZL and ZR buttons 855 // ZL and ZR buttons
856 p.setPen(colors.outline); 856 p.setPen(colors.outline);
@@ -935,13 +935,13 @@ void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) {
935 DrawSymbol(p, center + QPoint(100, -83), Symbol::Y, text_size); 935 DrawSymbol(p, center + QPoint(100, -83), Symbol::Y, text_size);
936 936
937 // D-pad buttons 937 // D-pad buttons
938 const QPointF dpad_postion = center + QPoint(-61, 37); 938 const QPointF dpad_position = center + QPoint(-61, 37);
939 const float dpad_size = 0.8f; 939 const float dpad_size = 0.8f;
940 DrawArrowButton(p, dpad_postion, Direction::Up, button_values[DUp], dpad_size); 940 DrawArrowButton(p, dpad_position, Direction::Up, button_values[DUp], dpad_size);
941 DrawArrowButton(p, dpad_postion, Direction::Left, button_values[DLeft], dpad_size); 941 DrawArrowButton(p, dpad_position, Direction::Left, button_values[DLeft], dpad_size);
942 DrawArrowButton(p, dpad_postion, Direction::Right, button_values[DRight], dpad_size); 942 DrawArrowButton(p, dpad_position, Direction::Right, button_values[DRight], dpad_size);
943 DrawArrowButton(p, dpad_postion, Direction::Down, button_values[DDown], dpad_size); 943 DrawArrowButton(p, dpad_position, Direction::Down, button_values[DDown], dpad_size);
944 DrawArrowButtonOutline(p, dpad_postion, dpad_size); 944 DrawArrowButtonOutline(p, dpad_position, dpad_size);
945 945
946 // Minus and Plus buttons 946 // Minus and Plus buttons
947 p.setPen(colors.outline); 947 p.setPen(colors.outline);
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp
index fa5f383d6..12a04b9a0 100644
--- a/src/yuzu/configuration/configure_profile_manager.cpp
+++ b/src/yuzu/configuration/configure_profile_manager.cpp
@@ -205,6 +205,7 @@ void ConfigureProfileManager::AddUser() {
205 205
206 const auto uuid = Common::UUID::MakeRandom(); 206 const auto uuid = Common::UUID::MakeRandom();
207 profile_manager.CreateNewUser(uuid, username.toStdString()); 207 profile_manager.CreateNewUser(uuid, username.toStdString());
208 profile_manager.WriteUserSaveFile();
208 209
209 item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)}); 210 item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)});
210} 211}
@@ -228,6 +229,7 @@ void ConfigureProfileManager::RenameUser() {
228 std::copy(username_std.begin(), username_std.end(), profile.username.begin()); 229 std::copy(username_std.begin(), username_std.end(), profile.username.begin());
229 230
230 profile_manager.SetProfileBase(*uuid, profile); 231 profile_manager.SetProfileBase(*uuid, profile);
232 profile_manager.WriteUserSaveFile();
231 233
232 item_model->setItem( 234 item_model->setItem(
233 user, 0, 235 user, 0,
@@ -256,6 +258,8 @@ void ConfigureProfileManager::DeleteUser(const Common::UUID& uuid) {
256 return; 258 return;
257 } 259 }
258 260
261 profile_manager.WriteUserSaveFile();
262
259 item_model->removeRows(tree_view->currentIndex().row(), 1); 263 item_model->removeRows(tree_view->currentIndex().row(), 1);
260 tree_view->clearSelection(); 264 tree_view->clearSelection();
261 265
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 7cbf43775..b0b84f967 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -121,7 +121,7 @@ void ConfigureSystem::Setup(const ConfigurationShared::Builder& builder) {
121 } 121 }
122 122
123 if (setting->Id() == Settings::values.region_index.Id()) { 123 if (setting->Id() == Settings::values.region_index.Id()) {
124 // Keep track of the region_index (and langauge_index) combobox to validate the selected 124 // Keep track of the region_index (and language_index) combobox to validate the selected
125 // settings 125 // settings
126 combo_region = widget->combobox; 126 combo_region = widget->combobox;
127 } else if (setting->Id() == Settings::values.language_index.Id()) { 127 } else if (setting->Id() == Settings::values.language_index.Id()) {
diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp
index 941683a43..85f4f7655 100644
--- a/src/yuzu/configuration/shared_widget.cpp
+++ b/src/yuzu/configuration/shared_widget.cpp
@@ -750,12 +750,12 @@ Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translati
750 } 750 }
751 751
752 apply_funcs.push_back([load_func, setting_](bool powered_on) { 752 apply_funcs.push_back([load_func, setting_](bool powered_on) {
753 if (setting_->RuntimeModfiable() || !powered_on) { 753 if (setting_->RuntimeModifiable() || !powered_on) {
754 load_func(); 754 load_func();
755 } 755 }
756 }); 756 });
757 757
758 bool enable = runtime_lock || setting.RuntimeModfiable(); 758 bool enable = runtime_lock || setting.RuntimeModifiable();
759 if (setting.Switchable() && Settings::IsConfiguringGlobal() && !runtime_lock) { 759 if (setting.Switchable() && Settings::IsConfiguringGlobal() && !runtime_lock) {
760 enable &= setting.UsingGlobal(); 760 enable &= setting.UsingGlobal();
761 } 761 }
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 4f4c75f5c..33756febf 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2292,14 +2292,14 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
2292 ASSERT(user_id); 2292 ASSERT(user_id);
2293 2293
2294 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( 2294 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
2295 *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, 2295 {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser,
2296 FileSys::SaveDataType::SaveData, program_id, user_id->AsU128(), 0); 2296 FileSys::SaveDataType::SaveData, program_id, user_id->AsU128(), 0);
2297 2297
2298 path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); 2298 path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
2299 } else { 2299 } else {
2300 // Device save data 2300 // Device save data
2301 const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath( 2301 const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath(
2302 *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, 2302 {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser,
2303 FileSys::SaveDataType::SaveData, program_id, {}, 0); 2303 FileSys::SaveDataType::SaveData, program_id, {}, 0);
2304 2304
2305 path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path); 2305 path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path);
@@ -2662,8 +2662,8 @@ void GMainWindow::RemoveCacheStorage(u64 program_id) {
2662 vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read); 2662 vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
2663 2663
2664 const auto cache_storage_path = FileSys::SaveDataFactory::GetFullPath( 2664 const auto cache_storage_path = FileSys::SaveDataFactory::GetFullPath(
2665 *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, 2665 {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::CacheStorage,
2666 FileSys::SaveDataType::CacheStorage, 0 /* program_id */, {}, 0); 2666 0 /* program_id */, {}, 0);
2667 2667
2668 const auto path = Common::FS::ConcatPathSafe(nand_dir, cache_storage_path); 2668 const auto path = Common::FS::ConcatPathSafe(nand_dir, cache_storage_path);
2669 2669
@@ -3988,7 +3988,7 @@ void GMainWindow::OnToggleDockedMode() {
3988 tr("Handheld controller can't be used on docked mode. Pro " 3988 tr("Handheld controller can't be used on docked mode. Pro "
3989 "controller will be selected.")); 3989 "controller will be selected."));
3990 handheld->Disconnect(); 3990 handheld->Disconnect();
3991 player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); 3991 player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
3992 player_1->Connect(); 3992 player_1->Connect();
3993 controller_dialog->refreshConfiguration(); 3993 controller_dialog->refreshConfiguration();
3994 } 3994 }
diff --git a/src/yuzu/multiplayer/chat_room.cpp b/src/yuzu/multiplayer/chat_room.cpp
index dec9696c1..4463616b4 100644
--- a/src/yuzu/multiplayer/chat_room.cpp
+++ b/src/yuzu/multiplayer/chat_room.cpp
@@ -206,7 +206,7 @@ void ChatRoom::Initialize(Network::RoomNetwork* room_network_) {
206 room_network = room_network_; 206 room_network = room_network_;
207 // setup the callbacks for network updates 207 // setup the callbacks for network updates
208 if (auto member = room_network->GetRoomMember().lock()) { 208 if (auto member = room_network->GetRoomMember().lock()) {
209 member->BindOnChatMessageRecieved( 209 member->BindOnChatMessageReceived(
210 [this](const Network::ChatEntry& chat) { emit ChatReceived(chat); }); 210 [this](const Network::ChatEntry& chat) { emit ChatReceived(chat); });
211 member->BindOnStatusMessageReceived( 211 member->BindOnStatusMessageReceived(
212 [this](const Network::StatusMessageEntry& status_message) { 212 [this](const Network::StatusMessageEntry& status_message) {
diff --git a/src/yuzu/util/controller_navigation.cpp b/src/yuzu/util/controller_navigation.cpp
index 2690b075d..0dbfca243 100644
--- a/src/yuzu/util/controller_navigation.cpp
+++ b/src/yuzu/util/controller_navigation.cpp
@@ -66,7 +66,7 @@ void ControllerNavigation::ControllerUpdateButton() {
66 } 66 }
67 67
68 switch (controller_type) { 68 switch (controller_type) {
69 case Core::HID::NpadStyleIndex::ProController: 69 case Core::HID::NpadStyleIndex::Fullkey:
70 case Core::HID::NpadStyleIndex::JoyconDual: 70 case Core::HID::NpadStyleIndex::JoyconDual:
71 case Core::HID::NpadStyleIndex::Handheld: 71 case Core::HID::NpadStyleIndex::Handheld:
72 case Core::HID::NpadStyleIndex::GameCube: 72 case Core::HID::NpadStyleIndex::GameCube:
@@ -116,7 +116,7 @@ void ControllerNavigation::ControllerUpdateStick() {
116 } 116 }
117 117
118 switch (controller_type) { 118 switch (controller_type) {
119 case Core::HID::NpadStyleIndex::ProController: 119 case Core::HID::NpadStyleIndex::Fullkey:
120 case Core::HID::NpadStyleIndex::JoyconDual: 120 case Core::HID::NpadStyleIndex::JoyconDual:
121 case Core::HID::NpadStyleIndex::Handheld: 121 case Core::HID::NpadStyleIndex::Handheld:
122 case Core::HID::NpadStyleIndex::GameCube: 122 case Core::HID::NpadStyleIndex::GameCube:
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index a81635fa4..c3cacf852 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -401,7 +401,7 @@ int main(int argc, char** argv) {
401 401
402 if (use_multiplayer) { 402 if (use_multiplayer) {
403 if (auto member = system.GetRoomNetwork().GetRoomMember().lock()) { 403 if (auto member = system.GetRoomNetwork().GetRoomMember().lock()) {
404 member->BindOnChatMessageRecieved(OnMessageReceived); 404 member->BindOnChatMessageReceived(OnMessageReceived);
405 member->BindOnStatusMessageReceived(OnStatusMessageReceived); 405 member->BindOnStatusMessageReceived(OnStatusMessageReceived);
406 member->BindOnStateChanged(OnStateChanged); 406 member->BindOnStateChanged(OnStateChanged);
407 member->BindOnError(OnNetworkError); 407 member->BindOnError(OnNetworkError);