From ae88ea79b24b658cf3f176370f209393d64f83bd Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 1 Jan 2024 12:38:20 -0500 Subject: [PATCH 01/33] vi: fix name of nvnflinger --- src/core/hle/service/vi/vi.cpp | 52 +++++++++++++++++----------------- src/core/hle/service/vi/vi.h | 2 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 9ab8788e3..29471abfe 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -343,8 +343,8 @@ private: class IManagerDisplayService final : public ServiceFramework { public: - explicit IManagerDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_) - : ServiceFramework{system_, "IManagerDisplayService"}, nv_flinger{nv_flinger_} { + explicit IManagerDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_) + : ServiceFramework{system_, "IManagerDisplayService"}, nvnflinger{nvnflinger_} { // clang-format off static const FunctionInfo functions[] = { {200, nullptr, "AllocateProcessHeapBlock"}, @@ -440,7 +440,7 @@ private: IPC::RequestParser rp{ctx}; const u64 display = rp.Pop(); - const Result rc = nv_flinger.CloseDisplay(display) ? ResultSuccess : ResultUnknown; + const Result rc = nvnflinger.CloseDisplay(display) ? ResultSuccess : ResultUnknown; IPC::ResponseBuilder rb{ctx, 2}; rb.Push(rc); @@ -457,7 +457,7 @@ private: "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}", unknown, display, aruid); - const auto layer_id = nv_flinger.CreateLayer(display); + const auto layer_id = nvnflinger.CreateLayer(display); if (!layer_id) { LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); IPC::ResponseBuilder rb{ctx, 2}; @@ -494,14 +494,14 @@ private: rb.Push(ResultSuccess); } - Nvnflinger::Nvnflinger& nv_flinger; + Nvnflinger::Nvnflinger& nvnflinger; }; class IApplicationDisplayService final : public ServiceFramework { public: - IApplicationDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, + IApplicationDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_, Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_) - : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_}, + : ServiceFramework{system_, "IApplicationDisplayService"}, nvnflinger{nvnflinger_}, hos_binder_driver_server{hos_binder_driver_server_} { static const FunctionInfo functions[] = { @@ -564,7 +564,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(system, nv_flinger); + rb.PushIpcInterface(system, nvnflinger); } void GetManagerDisplayService(HLERequestContext& ctx) { @@ -572,7 +572,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(system, nv_flinger); + rb.PushIpcInterface(system, nvnflinger); } void GetIndirectDisplayTransactionService(HLERequestContext& ctx) { @@ -607,7 +607,7 @@ private: ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); - const auto display_id = nv_flinger.OpenDisplay(name); + const auto display_id = nvnflinger.OpenDisplay(name); if (!display_id) { LOG_ERROR(Service_VI, "Display not found! display_name={}", name); IPC::ResponseBuilder rb{ctx, 2}; @@ -624,7 +624,7 @@ private: IPC::RequestParser rp{ctx}; const u64 display_id = rp.Pop(); - const Result rc = nv_flinger.CloseDisplay(display_id) ? ResultSuccess : ResultUnknown; + const Result rc = nvnflinger.CloseDisplay(display_id) ? ResultSuccess : ResultUnknown; IPC::ResponseBuilder rb{ctx, 2}; rb.Push(rc); @@ -703,7 +703,7 @@ private: LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid); - const auto display_id = nv_flinger.OpenDisplay(display_name); + const auto display_id = nvnflinger.OpenDisplay(display_name); if (!display_id) { LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -711,7 +711,7 @@ private: return; } - const auto buffer_queue_id = nv_flinger.FindBufferQueueId(*display_id, layer_id); + const auto buffer_queue_id = nvnflinger.FindBufferQueueId(*display_id, layer_id); if (!buffer_queue_id) { LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -719,7 +719,7 @@ private: return; } - nv_flinger.OpenLayer(layer_id); + nvnflinger.OpenLayer(layer_id); android::OutputParcel parcel; parcel.WriteInterface(NativeWindow{*buffer_queue_id}); @@ -737,7 +737,7 @@ private: LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id); - nv_flinger.CloseLayer(layer_id); + nvnflinger.CloseLayer(layer_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -753,7 +753,7 @@ private: // TODO(Subv): What's the difference between a Stray and a Managed layer? - const auto layer_id = nv_flinger.CreateLayer(display_id); + const auto layer_id = nvnflinger.CreateLayer(display_id); if (!layer_id) { LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -761,7 +761,7 @@ private: return; } - const auto buffer_queue_id = nv_flinger.FindBufferQueueId(display_id, *layer_id); + const auto buffer_queue_id = nvnflinger.FindBufferQueueId(display_id, *layer_id); if (!buffer_queue_id) { LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -785,7 +785,7 @@ private: const u64 layer_id = rp.Pop(); LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}", layer_id); - nv_flinger.DestroyLayer(layer_id); + nvnflinger.DestroyLayer(layer_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -798,7 +798,7 @@ private: LOG_DEBUG(Service_VI, "called. display_id={}", display_id); Kernel::KReadableEvent* vsync_event{}; - const auto result = nv_flinger.FindVsyncEvent(&vsync_event, display_id); + const auto result = nvnflinger.FindVsyncEvent(&vsync_event, display_id); if (result != ResultSuccess) { if (result == ResultNotFound) { LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); @@ -899,7 +899,7 @@ private: } } - Nvnflinger::Nvnflinger& nv_flinger; + Nvnflinger::Nvnflinger& nvnflinger; Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; }; @@ -916,7 +916,7 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) { } void detail::GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system, - Nvnflinger::Nvnflinger& nv_flinger, + Nvnflinger::Nvnflinger& nvnflinger, Nvnflinger::HosBinderDriverServer& hos_binder_driver_server, Permission permission) { IPC::RequestParser rp{ctx}; @@ -931,19 +931,19 @@ void detail::GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system, IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(system, nv_flinger, hos_binder_driver_server); + rb.PushIpcInterface(system, nvnflinger, hos_binder_driver_server); } -void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nv_flinger, +void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nvnflinger, Nvnflinger::HosBinderDriverServer& hos_binder_driver_server) { auto server_manager = std::make_unique(system); server_manager->RegisterNamedService( - "vi:m", std::make_shared(system, nv_flinger, hos_binder_driver_server)); + "vi:m", std::make_shared(system, nvnflinger, hos_binder_driver_server)); server_manager->RegisterNamedService( - "vi:s", std::make_shared(system, nv_flinger, hos_binder_driver_server)); + "vi:s", std::make_shared(system, nvnflinger, hos_binder_driver_server)); server_manager->RegisterNamedService( - "vi:u", std::make_shared(system, nv_flinger, hos_binder_driver_server)); + "vi:u", std::make_shared(system, nvnflinger, hos_binder_driver_server)); ServerManager::RunServer(std::move(server_manager)); } 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, Permission permission); } // namespace detail -void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nv_flinger, +void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nvnflinger, Nvnflinger::HosBinderDriverServer& hos_binder_driver_server); } // namespace Service::VI From 200b371d13bb5919ce68d72c760b86313bec3b0f Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 2 Jan 2024 18:23:57 -0500 Subject: [PATCH 02/33] server_manager: respond to session close correctly --- src/core/hle/service/server_manager.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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() { // Wait for a signal. s32 out_index{-1}; - R_TRY(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(), - num_objs, -1)); + R_TRY_CATCH(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, + wait_objs.data(), num_objs, -1)) { + R_CATCH(Kernel::ResultSessionClosed) { + // On session closed, index is updated and we don't want to return an error. + } + } + R_END_TRY_CATCH; ASSERT(out_index >= 0 && out_index < num_objs); // Set the output index. From ea710e652398f0646aeb1a1cdf78cee9691440e8 Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 6 Jan 2024 23:58:04 -0500 Subject: [PATCH 03/33] vi: connect vsync event handle lifetime to application display service interface --- src/core/hle/service/nvnflinger/nvnflinger.cpp | 3 ++- src/core/hle/service/vi/display/vi_display.cpp | 13 +------------ src/core/hle/service/vi/display/vi_display.h | 12 +----------- src/core/hle/service/vi/vi.cpp | 7 +++++++ 4 files changed, 11 insertions(+), 24 deletions(-) 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 return VI::ResultNotFound; } - return display->GetVSyncEvent(out_vsync_event); + *out_vsync_event = display->GetVSyncEvent(); + return ResultSuccess; } VI::Display* Nvnflinger::FindDisplay(u64 display_id) { 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 { return std::ranges::count_if(layers, [](auto& l) { return l->IsOpen(); }); } -Result Display::GetVSyncEvent(Kernel::KReadableEvent** out_vsync_event) { - if (got_vsync_event) { - return ResultPermissionDenied; - } - - got_vsync_event = true; - - *out_vsync_event = GetVSyncEventUnchecked(); - return ResultSuccess; -} - -Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() { +Kernel::KReadableEvent* Display::GetVSyncEvent() { return &vsync_event->GetReadableEvent(); } 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: std::size_t GetNumLayers() const; - /** - * Gets the internal vsync event. - * - * @returns The internal Vsync event if it has not yet been retrieved, - * VI::ResultPermissionDenied otherwise. - */ - [[nodiscard]] Result GetVSyncEvent(Kernel::KReadableEvent** out_vsync_event); - /// Gets the internal vsync event. - Kernel::KReadableEvent* GetVSyncEventUnchecked(); + Kernel::KReadableEvent* GetVSyncEvent(); /// Signals the internal vsync event. void SignalVSyncEvent(); @@ -104,7 +96,6 @@ public: /// Resets the display for a new connection. void Reset() { layers.clear(); - got_vsync_event = false; } /// Attempts to find a layer with the given ID. @@ -133,7 +124,6 @@ private: std::vector> layers; Kernel::KEvent* vsync_event{}; - bool got_vsync_event{false}; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 29471abfe..39d5be90d 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -808,6 +808,12 @@ private: rb.Push(result); return; } + if (vsync_event_fetched) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(VI::ResultPermissionDenied); + return; + } + vsync_event_fetched = true; IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); @@ -901,6 +907,7 @@ private: Nvnflinger::Nvnflinger& nvnflinger; Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; + bool vsync_event_fetched{false}; }; static bool IsValidServiceAccess(Permission permission, Policy policy) { From 4f83b00f6f98cff94ccdec7bbcd664e576bd878e Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 8 Jan 2024 09:34:16 -0500 Subject: [PATCH 04/33] general: fix trailing whitespace --- .ci/scripts/linux/exec.sh | 2 +- LICENSES/BSD-2-Clause.txt | 2 +- LICENSES/BSD-3-Clause.txt | 2 +- LICENSES/MPL-2.0.txt | 2 +- externals/ffmpeg/CMakeLists.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) 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 sudo chown -R 1027 ./ # The environment variables listed below: -# AZURECIREPO TITLEBARFORMATIDLE TITLEBARFORMATRUNNING DISPLAYVERSION +# AZURECIREPO TITLEBARFORMATIDLE TITLEBARFORMATRUNNING DISPLAYVERSION # are requested in src/common/CMakeLists.txt and appear to be provided somewhere in Azure DevOps docker 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/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 @@ -Copyright (c) +Copyright (c) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 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 @@ -Copyright (c) . +Copyright (c) . Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 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 means any form of the work other than Source Code Form. 1.7. "Larger Work" - means a work that combines Covered Software with other material, in + means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.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) --cross-prefix=${TOOLCHAIN}/bin/aarch64-linux-android- --sysroot=${SYSROOT} --target-os=android - --extra-ldflags="--ld-path=${TOOLCHAIN}/bin/ld.lld" + --extra-ldflags="--ld-path=${TOOLCHAIN}/bin/ld.lld" --extra-ldflags="-nostdlib" ) endif() From 30743eff5669a5557b1c00424f84865b8b3ff6ad Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 8 Jan 2024 09:15:19 -0500 Subject: [PATCH 05/33] ci: make verify format workflow output more helpful --- .ci/scripts/format/script.sh | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/.ci/scripts/format/script.sh b/.ci/scripts/format/script.sh index 25b0718f0..c9c5e4fac 100755 --- a/.ci/scripts/format/script.sh +++ b/.ci/scripts/format/script.sh @@ -3,38 +3,33 @@ # SPDX-FileCopyrightText: 2019 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later -if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop \ +shopt -s nullglob globstar + +if grep -nrI '\s$' src **/*.yml **/*.txt **/*.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop \ dist/*.svg dist/*.xml; then echo Trailing whitespace found, aborting exit 1 fi # Default clang-format points to default 3.5 version one -CLANG_FORMAT=${CLANG_FORMAT:-clang-format-15} -$CLANG_FORMAT --version - -if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then - # Get list of every file modified in this pull request - files_to_lint="$(git diff --name-only --diff-filter=ACMRTUXB $TRAVIS_COMMIT_RANGE | grep '^src/[^.]*[.]\(cpp\|h\)$' || true)" -else - # Check everything for branch pushes - files_to_lint="$(find src/ -name '*.cpp' -or -name '*.h')" -fi +CLANG_FORMAT="${CLANG_FORMAT:-clang-format-15}" +"$CLANG_FORMAT" --version # Turn off tracing for this because it's too verbose set +x -for f in $files_to_lint; do - d=$(diff -u "$f" <($CLANG_FORMAT "$f") || true) - if ! [ -z "$d" ]; then - echo "!!! $f not compliant to coding style, here is the fix:" - echo "$d" - fail=1 - fi +# Check everything for branch pushes +FILES_TO_LINT="$(find src/ -name '*.cpp' -or -name '*.h')" + +for f in $FILES_TO_LINT; do + echo "$f" + "$CLANG_FORMAT" -i "$f" done -set -x +DIFF=$(git diff) -if [ "$fail" = 1 ]; then +if [ ! -z "$DIFF" ]; then + echo "!!! Not compliant to coding style, here is the fix:" + echo "$DIFF" exit 1 fi From d99830b59c83bd029f16a93e73b872979fed2e6f Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Tue, 9 Jan 2024 17:29:38 -0500 Subject: [PATCH 06/33] externals: Update txdb_to_nx Includes a fix lat9nq/tzdb_to_nx@1e82342 that fixes a build issue on Mac OS. --- externals/nx_tzdb/tzdb_to_nx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/nx_tzdb/tzdb_to_nx b/externals/nx_tzdb/tzdb_to_nx index f6680093b..404d39004 160000 --- a/externals/nx_tzdb/tzdb_to_nx +++ b/externals/nx_tzdb/tzdb_to_nx @@ -1 +1 @@ -Subproject commit f6680093bca30265c161581fd813d4ddd33f1e3e +Subproject commit 404d39004570a26c734a9d1fa29ab4d63089c599 From e11a3414ae7114954345512e75583d0cf199c8c6 Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 10 Jan 2024 11:52:58 -0500 Subject: [PATCH 07/33] ci: fix format task --- .ci/scripts/format/script.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.ci/scripts/format/script.sh b/.ci/scripts/format/script.sh index c9c5e4fac..572fa9ffb 100755 --- a/.ci/scripts/format/script.sh +++ b/.ci/scripts/format/script.sh @@ -5,8 +5,7 @@ shopt -s nullglob globstar -if grep -nrI '\s$' src **/*.yml **/*.txt **/*.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop \ - dist/*.svg dist/*.xml; then +if git grep -nrI '\s$' src **/*.yml **/*.txt **/*.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop dist/*.svg dist/*.xml; then echo Trailing whitespace found, aborting exit 1 fi From 51ad2d10de80393371a0e1b92e107c90231d0f72 Mon Sep 17 00:00:00 2001 From: t895 Date: Mon, 8 Jan 2024 00:16:28 -0500 Subject: [PATCH 08/33] android: Create generic adapter and viewholder Eliminates repeated code associated with every async differ list --- .../yuzu_emu/adapters/AbstractDiffAdapter.kt | 33 +++++++++++++++++++ .../yuzu_emu/viewholder/AbstractViewHolder.kt | 18 ++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/viewholder/AbstractViewHolder.kt 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 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.adapters + +import android.annotation.SuppressLint +import androidx.recyclerview.widget.AsyncDifferConfig +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder +import androidx.recyclerview.widget.RecyclerView + +/** + * Generic adapter that implements an [AsyncDifferConfig] and covers some of the basic boilerplate + * code used in every [RecyclerView]. + * Type assigned to [Model] must inherit from [Object] in order to be compared properly. + */ +abstract class AbstractDiffAdapter> : + ListAdapter(AsyncDifferConfig.Builder(DiffCallback()).build()) { + override fun onBindViewHolder(holder: Holder, position: Int) = + holder.bind(currentList[position]) + + private class DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean { + return oldItem === newItem + } + + @SuppressLint("DiffUtilEquals") + override fun areContentsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean { + return oldItem == newItem + } + } +} 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 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.viewholder + +import androidx.recyclerview.widget.RecyclerView +import androidx.viewbinding.ViewBinding +import org.yuzu.yuzu_emu.adapters.AbstractDiffAdapter +import org.yuzu.yuzu_emu.adapters.AbstractListAdapter + +/** + * [RecyclerView.ViewHolder] meant to work together with a [AbstractDiffAdapter] or a + * [AbstractListAdapter] so we can run [bind] on each list item without needing a manual hookup. + */ +abstract class AbstractViewHolder(binding: ViewBinding) : + RecyclerView.ViewHolder(binding.root) { + abstract fun bind(model: Model) +} From 78c323c4eb1beb502f1c2b5367ecb8654181c1e0 Mon Sep 17 00:00:00 2001 From: t895 Date: Mon, 8 Jan 2024 00:17:01 -0500 Subject: [PATCH 09/33] android: Refactor async diff adapters to use AbstractDiffAdapter --- .../yuzu/yuzu_emu/adapters/AddonAdapter.kt | 36 +--- .../yuzu/yuzu_emu/adapters/FolderAdapter.kt | 36 +--- .../org/yuzu/yuzu_emu/adapters/GameAdapter.kt | 194 +++++++----------- 3 files changed, 93 insertions(+), 173 deletions(-) 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 import android.view.LayoutInflater import android.view.ViewGroup -import androidx.recyclerview.widget.AsyncDifferConfig -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding import org.yuzu.yuzu_emu.model.Addon +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder -class AddonAdapter : ListAdapter( - AsyncDifferConfig.Builder(DiffCallback()).build() -) { +class AddonAdapter : AbstractDiffAdapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddonViewHolder { ListItemAddonBinding.inflate(LayoutInflater.from(parent.context), parent, false) .also { return AddonViewHolder(it) } } - override fun getItemCount(): Int = currentList.size - - override fun onBindViewHolder(holder: AddonViewHolder, position: Int) = - holder.bind(currentList[position]) - inner class AddonViewHolder(val binding: ListItemAddonBinding) : - RecyclerView.ViewHolder(binding.root) { - fun bind(addon: Addon) { + AbstractViewHolder(binding) { + override fun bind(model: Addon) { binding.root.setOnClickListener { binding.addonSwitch.isChecked = !binding.addonSwitch.isChecked } - binding.title.text = addon.title - binding.version.text = addon.version + binding.title.text = model.title + binding.version.text = model.version binding.addonSwitch.setOnCheckedChangeListener { _, checked -> - addon.enabled = checked + model.enabled = checked } - binding.addonSwitch.isChecked = addon.enabled - } - } - - private class DiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: Addon, newItem: Addon): Boolean { - return oldItem == newItem - } - - override fun areContentsTheSame(oldItem: Addon, newItem: Addon): Boolean { - return oldItem == newItem + binding.addonSwitch.isChecked = model.enabled } } } 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 import android.view.LayoutInflater import android.view.ViewGroup import androidx.fragment.app.FragmentActivity -import androidx.recyclerview.widget.AsyncDifferConfig -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView import org.yuzu.yuzu_emu.databinding.CardFolderBinding import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment import org.yuzu.yuzu_emu.model.GameDir import org.yuzu.yuzu_emu.model.GamesViewModel +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) : - ListAdapter( - AsyncDifferConfig.Builder(DiffCallback()).build() - ) { + AbstractDiffAdapter() { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int @@ -29,18 +24,11 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie .also { return FolderViewHolder(it) } } - override fun onBindViewHolder(holder: FolderAdapter.FolderViewHolder, position: Int) = - holder.bind(currentList[position]) - inner class FolderViewHolder(val binding: CardFolderBinding) : - RecyclerView.ViewHolder(binding.root) { - private lateinit var gameDir: GameDir - - fun bind(gameDir: GameDir) { - this.gameDir = gameDir - + AbstractViewHolder(binding) { + override fun bind(model: GameDir) { binding.apply { - path.text = Uri.parse(gameDir.uriString).path + path.text = Uri.parse(model.uriString).path path.postDelayed( { path.isSelected = true @@ -50,7 +38,7 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie ) buttonEdit.setOnClickListener { - GameFolderPropertiesDialogFragment.newInstance(this@FolderViewHolder.gameDir) + GameFolderPropertiesDialogFragment.newInstance(model) .show( activity.supportFragmentManager, GameFolderPropertiesDialogFragment.TAG @@ -58,19 +46,9 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie } buttonDelete.setOnClickListener { - gamesViewModel.removeFolder(this@FolderViewHolder.gameDir) + gamesViewModel.removeFolder(model) } } } } - - private class DiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: GameDir, newItem: GameDir): Boolean { - return oldItem == newItem - } - - override fun areContentsTheSame(oldItem: GameDir, newItem: GameDir): Boolean { - return oldItem == newItem - } - } } 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 import android.net.Uri import android.text.TextUtils import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.Toast @@ -25,10 +24,6 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController import androidx.preference.PreferenceManager -import androidx.recyclerview.widget.AsyncDifferConfig -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -36,122 +31,26 @@ import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.activities.EmulationActivity -import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder import org.yuzu.yuzu_emu.databinding.CardGameBinding import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.utils.GameIconUtils +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class GameAdapter(private val activity: AppCompatActivity) : - ListAdapter(AsyncDifferConfig.Builder(DiffCallback()).build()), - View.OnClickListener, - View.OnLongClickListener { + AbstractDiffAdapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder { - // Create a new view. - val binding = CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false) - binding.cardGame.setOnClickListener(this) - binding.cardGame.setOnLongClickListener(this) - - // Use that view to create a ViewHolder. - return GameViewHolder(binding) - } - - override fun onBindViewHolder(holder: GameViewHolder, position: Int) = - holder.bind(currentList[position]) - - override fun getItemCount(): Int = currentList.size - - /** - * Launches the game that was clicked on. - * - * @param view The card representing the game the user wants to play. - */ - override fun onClick(view: View) { - val holder = view.tag as GameViewHolder - - val gameExists = DocumentFile.fromSingleUri( - YuzuApplication.appContext, - Uri.parse(holder.game.path) - )?.exists() == true - if (!gameExists) { - Toast.makeText( - YuzuApplication.appContext, - R.string.loader_error_file_not_found, - Toast.LENGTH_LONG - ).show() - - ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true) - return - } - - val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) - preferences.edit() - .putLong( - holder.game.keyLastPlayedTime, - System.currentTimeMillis() - ) - .apply() - - val openIntent = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply { - action = Intent.ACTION_VIEW - data = Uri.parse(holder.game.path) - } - - activity.lifecycleScope.launch { - withContext(Dispatchers.IO) { - val layerDrawable = ResourcesCompat.getDrawable( - YuzuApplication.appContext.resources, - R.drawable.shortcut, - null - ) as LayerDrawable - layerDrawable.setDrawableByLayerId( - R.id.shortcut_foreground, - GameIconUtils.getGameIcon(activity, holder.game) - .toDrawable(YuzuApplication.appContext.resources) - ) - val inset = YuzuApplication.appContext.resources - .getDimensionPixelSize(R.dimen.icon_inset) - layerDrawable.setLayerInset(1, inset, inset, inset, inset) - val shortcut = - ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path) - .setShortLabel(holder.game.title) - .setIcon( - IconCompat.createWithAdaptiveBitmap( - layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888) - ) - ) - .setIntent(openIntent) - .build() - ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) - } - } - - val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game, true) - view.findNavController().navigate(action) - } - - override fun onLongClick(view: View): Boolean { - val holder = view.tag as GameViewHolder - val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(holder.game) - view.findNavController().navigate(action) - return true + CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .also { return GameViewHolder(it) } } inner class GameViewHolder(val binding: CardGameBinding) : - RecyclerView.ViewHolder(binding.root) { - lateinit var game: Game - - init { - binding.cardGame.tag = this - } - - fun bind(game: Game) { - this.game = game - + AbstractViewHolder(binding) { + override fun bind(model: Game) { binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP - GameIconUtils.loadGameIcon(game, binding.imageGameScreen) + GameIconUtils.loadGameIcon(model, binding.imageGameScreen) - binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ") + binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ") binding.textGameTitle.postDelayed( { @@ -160,16 +59,79 @@ class GameAdapter(private val activity: AppCompatActivity) : }, 3000 ) - } - } - private class DiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean { - return oldItem == newItem + binding.cardGame.setOnClickListener { onClick(model) } + binding.cardGame.setOnLongClickListener { onLongClick(model) } } - override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean { - return oldItem == newItem + fun onClick(game: Game) { + val gameExists = DocumentFile.fromSingleUri( + YuzuApplication.appContext, + Uri.parse(game.path) + )?.exists() == true + if (!gameExists) { + Toast.makeText( + YuzuApplication.appContext, + R.string.loader_error_file_not_found, + Toast.LENGTH_LONG + ).show() + + ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true) + return + } + + val preferences = + PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) + preferences.edit() + .putLong( + game.keyLastPlayedTime, + System.currentTimeMillis() + ) + .apply() + + val openIntent = + Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply { + action = Intent.ACTION_VIEW + data = Uri.parse(game.path) + } + + activity.lifecycleScope.launch { + withContext(Dispatchers.IO) { + val layerDrawable = ResourcesCompat.getDrawable( + YuzuApplication.appContext.resources, + R.drawable.shortcut, + null + ) as LayerDrawable + layerDrawable.setDrawableByLayerId( + R.id.shortcut_foreground, + GameIconUtils.getGameIcon(activity, game) + .toDrawable(YuzuApplication.appContext.resources) + ) + val inset = YuzuApplication.appContext.resources + .getDimensionPixelSize(R.dimen.icon_inset) + layerDrawable.setLayerInset(1, inset, inset, inset, inset) + val shortcut = + ShortcutInfoCompat.Builder(YuzuApplication.appContext, game.path) + .setShortLabel(game.title) + .setIcon( + IconCompat.createWithAdaptiveBitmap( + layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888) + ) + ) + .setIntent(openIntent) + .build() + ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) + } + } + + val action = HomeNavigationDirections.actionGlobalEmulationActivity(game, true) + binding.root.findNavController().navigate(action) + } + + fun onLongClick(game: Game): Boolean { + val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(game) + binding.root.findNavController().navigate(action) + return true } } } From ad0066a6b651ab47b7b7e916a9f716ef77fe2e04 Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 12:50:32 -0500 Subject: [PATCH 10/33] android: Create generic list adapter for basic lists Simplifies basic setup for lists --- .../yuzu_emu/adapters/AbstractListAdapter.kt | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractListAdapter.kt 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 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.adapters + +import android.annotation.SuppressLint +import androidx.recyclerview.widget.RecyclerView +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder + +/** + * Generic list class meant to take care of basic lists + * @param currentList The list to show initially + */ +abstract class AbstractListAdapter>( + open var currentList: List +) : RecyclerView.Adapter() { + override fun onBindViewHolder(holder: Holder, position: Int) = + holder.bind(currentList[position]) + + override fun getItemCount(): Int = currentList.size + + /** + * Adds an item to [currentList] and notifies the underlying adapter of the change. If no parameter + * is passed in for position, [item] is added to the end of the list. Invokes [callback] last. + * @param item The item to add to the list + * @param position Index where [item] will be added + * @param callback Lambda that's called at the end of the list changes and has the added list + * position passed in as a parameter + */ + open fun addItem(item: Model, position: Int = -1, callback: ((position: Int) -> Unit)? = null) { + val newList = currentList.toMutableList() + val positionToUpdate: Int + if (position == -1) { + newList.add(item) + currentList = newList + positionToUpdate = currentList.size - 1 + } else { + newList.add(position, item) + currentList = newList + positionToUpdate = position + } + onItemAdded(positionToUpdate, callback) + } + + protected fun onItemAdded(position: Int, callback: ((Int) -> Unit)? = null) { + notifyItemInserted(position) + callback?.invoke(position) + } + + /** + * Replaces the [item] at [position] in the [currentList] and notifies the underlying adapter + * of the change. Invokes [callback] last. + * @param item New list item + * @param position Index where [item] will replace the existing list item + * @param callback Lambda that's called at the end of the list changes and has the changed list + * position passed in as a parameter + */ + fun changeItem(item: Model, position: Int, callback: ((position: Int) -> Unit)? = null) { + val newList = currentList.toMutableList() + newList[position] = item + currentList = newList + onItemChanged(position, callback) + } + + protected fun onItemChanged(position: Int, callback: ((Int) -> Unit)? = null) { + notifyItemChanged(position) + callback?.invoke(position) + } + + /** + * Removes the list item at [position] in [currentList] and notifies the underlying adapter + * of the change. Invokes [callback] last. + * @param position Index where the list item will be removed + * @param callback Lambda that's called at the end of the list changes and has the removed list + * position passed in as a parameter + */ + fun removeItem(position: Int, callback: ((position: Int) -> Unit)? = null) { + val newList = currentList.toMutableList() + newList.removeAt(position) + currentList = newList + onItemRemoved(position, callback) + } + + protected fun onItemRemoved(position: Int, callback: ((Int) -> Unit)? = null) { + notifyItemRemoved(position) + callback?.invoke(position) + } + + /** + * Replaces [currentList] with [newList] and notifies the underlying adapter of the change. + * @param newList The new list to replace [currentList] + */ + @SuppressLint("NotifyDataSetChanged") + open fun replaceList(newList: List) { + currentList = newList + notifyDataSetChanged() + } +} From 9130366a580aa1129a596bf6ee3ab4432d219b6d Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 12:51:14 -0500 Subject: [PATCH 11/33] android: Refactor recycler view adapters to use AbstractListAdapter --- .../yuzu/yuzu_emu/adapters/AppletAdapter.kt | 88 ++++++++----------- .../adapters/CabinetLauncherDialogAdapter.kt | 57 +++++------- .../adapters/GamePropertiesAdapter.kt | 28 ++---- .../yuzu_emu/adapters/HomeSettingAdapter.kt | 72 ++++++--------- .../yuzu_emu/adapters/InstallableAdapter.kt | 36 +++----- .../yuzu/yuzu_emu/adapters/LicenseAdapter.kt | 50 ++++------- .../yuzu/yuzu_emu/adapters/SetupAdapter.kt | 45 ++++------ 7 files changed, 140 insertions(+), 236 deletions(-) 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 @@ package org.yuzu.yuzu_emu.adapters import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.core.content.res.ResourcesCompat import androidx.fragment.app.FragmentActivity import androidx.navigation.findNavController -import androidx.recyclerview.widget.RecyclerView import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R @@ -19,72 +17,58 @@ import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding import org.yuzu.yuzu_emu.model.Applet import org.yuzu.yuzu_emu.model.AppletInfo import org.yuzu.yuzu_emu.model.Game +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder -class AppletAdapter(val activity: FragmentActivity, var applets: List) : - RecyclerView.Adapter(), - View.OnClickListener { - +class AppletAdapter(val activity: FragmentActivity, applets: List) : + AbstractListAdapter(applets) { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): AppletAdapter.AppletViewHolder { CardSimpleOutlinedBinding.inflate(LayoutInflater.from(parent.context), parent, false) - .apply { root.setOnClickListener(this@AppletAdapter) } .also { return AppletViewHolder(it) } } - override fun onBindViewHolder(holder: AppletViewHolder, position: Int) = - holder.bind(applets[position]) - - override fun getItemCount(): Int = applets.size - - override fun onClick(view: View) { - val applet = (view.tag as AppletViewHolder).applet - val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId) - if (appletPath.isEmpty()) { - Toast.makeText( - YuzuApplication.appContext, - R.string.applets_error_applet, - Toast.LENGTH_SHORT - ).show() - return - } - - if (applet.appletInfo == AppletInfo.Cabinet) { - view.findNavController() - .navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment) - return - } - - NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId) - val appletGame = Game( - title = YuzuApplication.appContext.getString(applet.titleId), - path = appletPath - ) - val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame) - view.findNavController().navigate(action) - } - inner class AppletViewHolder(val binding: CardSimpleOutlinedBinding) : - RecyclerView.ViewHolder(binding.root) { - lateinit var applet: Applet - - init { - itemView.tag = this - } - - fun bind(applet: Applet) { - this.applet = applet - - binding.title.setText(applet.titleId) - binding.description.setText(applet.descriptionId) + AbstractViewHolder(binding) { + override fun bind(model: Applet) { + binding.title.setText(model.titleId) + binding.description.setText(model.descriptionId) binding.icon.setImageDrawable( ResourcesCompat.getDrawable( binding.icon.context.resources, - applet.iconId, + model.iconId, binding.icon.context.theme ) ) + + binding.root.setOnClickListener { onClick(model) } + } + + fun onClick(applet: Applet) { + val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId) + if (appletPath.isEmpty()) { + Toast.makeText( + binding.root.context, + R.string.applets_error_applet, + Toast.LENGTH_SHORT + ).show() + return + } + + if (applet.appletInfo == AppletInfo.Cabinet) { + binding.root.findNavController() + .navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment) + return + } + + NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId) + val appletGame = Game( + title = YuzuApplication.appContext.getString(applet.titleId), + path = appletPath + ) + val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame) + binding.root.findNavController().navigate(action) } } } 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 @@ package org.yuzu.yuzu_emu.adapters import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.core.content.res.ResourcesCompat import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController -import androidx.recyclerview.widget.RecyclerView import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R @@ -19,54 +17,43 @@ import org.yuzu.yuzu_emu.model.CabinetMode import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter.CabinetModeViewHolder import org.yuzu.yuzu_emu.model.AppletInfo import org.yuzu.yuzu_emu.model.Game +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class CabinetLauncherDialogAdapter(val fragment: Fragment) : - RecyclerView.Adapter(), - View.OnClickListener { - private val cabinetModes = CabinetMode.values().copyOfRange(1, CabinetMode.values().size) + AbstractListAdapter( + CabinetMode.values().copyOfRange(1, CabinetMode.entries.size).toList() + ) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CabinetModeViewHolder { DialogListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) - .apply { root.setOnClickListener(this@CabinetLauncherDialogAdapter) } .also { return CabinetModeViewHolder(it) } } - override fun getItemCount(): Int = cabinetModes.size - - override fun onBindViewHolder(holder: CabinetModeViewHolder, position: Int) = - holder.bind(cabinetModes[position]) - - override fun onClick(view: View) { - val mode = (view.tag as CabinetModeViewHolder).cabinetMode - val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId) - NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId) - NativeLibrary.setCabinetMode(mode.id) - val appletGame = Game( - title = YuzuApplication.appContext.getString(R.string.cabinet_applet), - path = appletPath - ) - val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame) - fragment.findNavController().navigate(action) - } - inner class CabinetModeViewHolder(val binding: DialogListItemBinding) : - RecyclerView.ViewHolder(binding.root) { - lateinit var cabinetMode: CabinetMode - - init { - itemView.tag = this - } - - fun bind(cabinetMode: CabinetMode) { - this.cabinetMode = cabinetMode + AbstractViewHolder(binding) { + override fun bind(model: CabinetMode) { binding.icon.setImageDrawable( ResourcesCompat.getDrawable( binding.icon.context.resources, - cabinetMode.iconId, + model.iconId, binding.icon.context.theme ) ) - binding.title.setText(cabinetMode.titleId) + binding.title.setText(model.titleId) + + binding.root.setOnClickListener { onClick(model) } + } + + private fun onClick(mode: CabinetMode) { + val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId) + NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId) + NativeLibrary.setCabinetMode(mode.id) + val appletGame = Game( + title = YuzuApplication.appContext.getString(R.string.cabinet_applet), + path = appletPath + ) + val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame) + fragment.findNavController().navigate(action) } } } 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 import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import androidx.recyclerview.widget.RecyclerView import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding import org.yuzu.yuzu_emu.model.GameProperty import org.yuzu.yuzu_emu.model.InstallableProperty import org.yuzu.yuzu_emu.model.SubmenuProperty +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class GamePropertiesAdapter( private val viewLifecycle: LifecycleOwner, private var properties: List -) : - RecyclerView.Adapter() { +) : AbstractListAdapter>(properties) { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int - ): GamePropertyViewHolder { + ): AbstractViewHolder { val inflater = LayoutInflater.from(parent.context) return when (viewType) { PropertyType.Submenu.ordinal -> { @@ -51,11 +50,6 @@ class GamePropertiesAdapter( } } - override fun getItemCount(): Int = properties.size - - override fun onBindViewHolder(holder: GamePropertyViewHolder, position: Int) = - holder.bind(properties[position]) - override fun getItemViewType(position: Int): Int { return when (properties[position]) { is SubmenuProperty -> PropertyType.Submenu.ordinal @@ -63,14 +57,10 @@ class GamePropertiesAdapter( } } - sealed class GamePropertyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - abstract fun bind(property: GameProperty) - } - inner class SubmenuPropertyViewHolder(val binding: CardSimpleOutlinedBinding) : - GamePropertyViewHolder(binding.root) { - override fun bind(property: GameProperty) { - val submenuProperty = property as SubmenuProperty + AbstractViewHolder(binding) { + override fun bind(model: GameProperty) { + val submenuProperty = model as SubmenuProperty binding.root.setOnClickListener { submenuProperty.action.invoke() @@ -108,9 +98,9 @@ class GamePropertiesAdapter( } inner class InstallablePropertyViewHolder(val binding: CardInstallableIconBinding) : - GamePropertyViewHolder(binding.root) { - override fun bind(property: GameProperty) { - val installableProperty = property as InstallableProperty + AbstractViewHolder(binding) { + override fun bind(model: GameProperty) { + val installableProperty = model as InstallableProperty binding.title.setText(installableProperty.titleId) 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 import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import androidx.recyclerview.widget.RecyclerView import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.model.HomeSetting +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class HomeSettingAdapter( private val activity: AppCompatActivity, private val viewLifecycle: LifecycleOwner, - var options: List -) : - RecyclerView.Adapter(), - View.OnClickListener { + options: List +) : AbstractListAdapter(options) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOptionViewHolder { - val binding = - CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) - binding.root.setOnClickListener(this) - return HomeOptionViewHolder(binding) - } - - override fun getItemCount(): Int { - return options.size - } - - override fun onBindViewHolder(holder: HomeOptionViewHolder, position: Int) { - holder.bind(options[position]) - } - - override fun onClick(view: View) { - val holder = view.tag as HomeOptionViewHolder - if (holder.option.isEnabled.invoke()) { - holder.option.onClick.invoke() - } else { - MessageDialogFragment.newInstance( - activity, - titleId = holder.option.disabledTitleId, - descriptionId = holder.option.disabledMessageId - ).show(activity.supportFragmentManager, MessageDialogFragment.TAG) - } + CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .also { return HomeOptionViewHolder(it) } } inner class HomeOptionViewHolder(val binding: CardHomeOptionBinding) : - RecyclerView.ViewHolder(binding.root) { - lateinit var option: HomeSetting - - init { - itemView.tag = this - } - - fun bind(option: HomeSetting) { - this.option = option - binding.optionTitle.text = activity.resources.getString(option.titleId) - binding.optionDescription.text = activity.resources.getString(option.descriptionId) + AbstractViewHolder(binding) { + override fun bind(model: HomeSetting) { + binding.optionTitle.text = activity.resources.getString(model.titleId) + binding.optionDescription.text = activity.resources.getString(model.descriptionId) binding.optionIcon.setImageDrawable( ResourcesCompat.getDrawable( activity.resources, - option.iconId, + model.iconId, activity.theme ) ) - when (option.titleId) { + when (model.titleId) { R.string.get_early_access -> binding.optionLayout.background = ContextCompat.getDrawable( @@ -85,7 +53,7 @@ class HomeSettingAdapter( ) } - if (!option.isEnabled.invoke()) { + if (!model.isEnabled.invoke()) { binding.optionTitle.alpha = 0.5f binding.optionDescription.alpha = 0.5f binding.optionIcon.alpha = 0.5f @@ -93,7 +61,7 @@ class HomeSettingAdapter( viewLifecycle.lifecycleScope.launch { viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) { - option.details.collect { updateOptionDetails(it) } + model.details.collect { updateOptionDetails(it) } } } binding.optionDetail.postDelayed( @@ -103,6 +71,20 @@ class HomeSettingAdapter( }, 3000 ) + + binding.root.setOnClickListener { onClick(model) } + } + + private fun onClick(model: HomeSetting) { + if (model.isEnabled.invoke()) { + model.onClick.invoke() + } else { + MessageDialogFragment.newInstance( + activity, + titleId = model.disabledTitleId, + descriptionId = model.disabledMessageId + ).show(activity.supportFragmentManager, MessageDialogFragment.TAG) + } } 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 import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView import org.yuzu.yuzu_emu.databinding.CardInstallableBinding import org.yuzu.yuzu_emu.model.Installable +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder -class InstallableAdapter(private val installables: List) : - RecyclerView.Adapter() { +class InstallableAdapter(installables: List) : + AbstractListAdapter(installables) { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): InstallableAdapter.InstallableViewHolder { - val binding = - CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return InstallableViewHolder(binding) + CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .also { return InstallableViewHolder(it) } } - override fun getItemCount(): Int = installables.size - - override fun onBindViewHolder(holder: InstallableAdapter.InstallableViewHolder, position: Int) = - holder.bind(installables[position]) - inner class InstallableViewHolder(val binding: CardInstallableBinding) : - RecyclerView.ViewHolder(binding.root) { - lateinit var installable: Installable + AbstractViewHolder(binding) { + override fun bind(model: Installable) { + binding.title.setText(model.titleId) + binding.description.setText(model.descriptionId) - fun bind(installable: Installable) { - this.installable = installable - - binding.title.setText(installable.titleId) - binding.description.setText(installable.descriptionId) - - if (installable.install != null) { + if (model.install != null) { binding.buttonInstall.visibility = View.VISIBLE - binding.buttonInstall.setOnClickListener { installable.install.invoke() } + binding.buttonInstall.setOnClickListener { model.install.invoke() } } - if (installable.export != null) { + if (model.export != null) { binding.buttonExport.visibility = View.VISIBLE - binding.buttonExport.setOnClickListener { installable.export.invoke() } + binding.buttonExport.setOnClickListener { model.export.invoke() } } } } 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 import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity -import androidx.recyclerview.widget.RecyclerView -import androidx.recyclerview.widget.RecyclerView.ViewHolder -import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment import org.yuzu.yuzu_emu.model.License +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder -class LicenseAdapter(private val activity: AppCompatActivity, var licenses: List) : - RecyclerView.Adapter(), - View.OnClickListener { +class LicenseAdapter(private val activity: AppCompatActivity, licenses: List) : + AbstractListAdapter(licenses) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LicenseViewHolder { - val binding = - ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false) - binding.root.setOnClickListener(this) - return LicenseViewHolder(binding) + ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .also { return LicenseViewHolder(it) } } - override fun getItemCount(): Int = licenses.size + inner class LicenseViewHolder(val binding: ListItemSettingBinding) : + AbstractViewHolder(binding) { + override fun bind(model: License) { + binding.apply { + textSettingName.text = root.context.getString(model.titleId) + textSettingDescription.text = root.context.getString(model.descriptionId) + textSettingValue.visibility = View.GONE - override fun onBindViewHolder(holder: LicenseViewHolder, position: Int) { - holder.bind(licenses[position]) - } - - override fun onClick(view: View) { - val license = (view.tag as LicenseViewHolder).license - LicenseBottomSheetDialogFragment.newInstance(license) - .show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG) - } - - inner class LicenseViewHolder(val binding: ListItemSettingBinding) : ViewHolder(binding.root) { - lateinit var license: License - - init { - itemView.tag = this + root.setOnClickListener { onClick(model) } + } } - fun bind(license: License) { - this.license = license - - val context = YuzuApplication.appContext - binding.textSettingName.text = context.getString(license.titleId) - binding.textSettingDescription.text = context.getString(license.descriptionId) - binding.textSettingValue.visibility = View.GONE + private fun onClick(license: License) { + LicenseBottomSheetDialogFragment.newInstance(license) + .show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG) } } } 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 import androidx.appcompat.app.AppCompatActivity import androidx.core.content.res.ResourcesCompat import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.RecyclerView import com.google.android.material.button.MaterialButton import org.yuzu.yuzu_emu.databinding.PageSetupBinding import org.yuzu.yuzu_emu.model.HomeViewModel @@ -18,31 +17,19 @@ import org.yuzu.yuzu_emu.model.SetupCallback import org.yuzu.yuzu_emu.model.SetupPage import org.yuzu.yuzu_emu.model.StepState import org.yuzu.yuzu_emu.utils.ViewUtils +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder -class SetupAdapter(val activity: AppCompatActivity, val pages: List) : - RecyclerView.Adapter() { +class SetupAdapter(val activity: AppCompatActivity, pages: List) : + AbstractListAdapter(pages) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SetupPageViewHolder { - val binding = PageSetupBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return SetupPageViewHolder(binding) + PageSetupBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .also { return SetupPageViewHolder(it) } } - override fun getItemCount(): Int = pages.size - - override fun onBindViewHolder(holder: SetupPageViewHolder, position: Int) = - holder.bind(pages[position]) - inner class SetupPageViewHolder(val binding: PageSetupBinding) : - RecyclerView.ViewHolder(binding.root), SetupCallback { - lateinit var page: SetupPage - - init { - itemView.tag = this - } - - fun bind(page: SetupPage) { - this.page = page - - if (page.stepCompleted.invoke() == StepState.COMPLETE) { + AbstractViewHolder(binding), SetupCallback { + override fun bind(model: SetupPage) { + if (model.stepCompleted.invoke() == StepState.COMPLETE) { binding.buttonAction.visibility = View.INVISIBLE binding.textConfirmation.visibility = View.VISIBLE } @@ -50,31 +37,31 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List) binding.icon.setImageDrawable( ResourcesCompat.getDrawable( activity.resources, - page.iconId, + model.iconId, activity.theme ) ) - binding.textTitle.text = activity.resources.getString(page.titleId) + binding.textTitle.text = activity.resources.getString(model.titleId) binding.textDescription.text = - Html.fromHtml(activity.resources.getString(page.descriptionId), 0) + Html.fromHtml(activity.resources.getString(model.descriptionId), 0) binding.buttonAction.apply { - text = activity.resources.getString(page.buttonTextId) - if (page.buttonIconId != 0) { + text = activity.resources.getString(model.buttonTextId) + if (model.buttonIconId != 0) { icon = ResourcesCompat.getDrawable( activity.resources, - page.buttonIconId, + model.buttonIconId, activity.theme ) } iconGravity = - if (page.leftAlignedIcon) { + if (model.leftAlignedIcon) { MaterialButton.ICON_GRAVITY_START } else { MaterialButton.ICON_GRAVITY_END } setOnClickListener { - page.buttonAction.invoke(this@SetupPageViewHolder) + model.buttonAction.invoke(this@SetupPageViewHolder) } } } From b17db2b4627acc1f9d0e9862ccfa802cf959f1e1 Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 13:04:06 -0500 Subject: [PATCH 12/33] android: Create generic single selection list adapter --- .../adapters/AbstractSingleSelectionList.kt | 105 ++++++++++++++++++ .../org/yuzu/yuzu_emu/model/SelectableItem.kt | 9 ++ 2 files changed, 114 insertions(+) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt 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 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.adapters + +import org.yuzu.yuzu_emu.model.SelectableItem +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder + +/** + * Generic list class meant to take care of single selection UI updates + * @param currentList The list to show initially + * @param defaultSelection The default selection to use if no list items are selected by + * [SelectableItem.selected] or if the currently selected item is removed from the list + */ +abstract class AbstractSingleSelectionList< + Model : SelectableItem, + Holder : AbstractViewHolder + >( + final override var currentList: List, + private val defaultSelection: DefaultSelection = DefaultSelection.Start +) : AbstractListAdapter(currentList) { + var selectedItem = getDefaultSelection() + + init { + findSelectedItem() + } + + /** + * Changes the selection state of the [SelectableItem] that was selected and the previously selected + * item and notifies the underlying adapter of the change for those items. Invokes [callback] last. + * Does nothing if [position] is the same as the currently selected item. + * @param position Index of the item that was selected + * @param callback Lambda that's called at the end of the list changes and has the selected list + * position passed in as a parameter + */ + fun selectItem(position: Int, callback: ((position: Int) -> Unit)? = null) { + if (position == selectedItem) { + return + } + + val previouslySelectedItem = selectedItem + selectedItem = position + if (currentList.indices.contains(selectedItem)) { + currentList[selectedItem].onSelectionStateChanged(true) + } + if (currentList.indices.contains(previouslySelectedItem)) { + currentList[previouslySelectedItem].onSelectionStateChanged(false) + } + onItemChanged(previouslySelectedItem) + onItemChanged(selectedItem) + callback?.invoke(position) + } + + /** + * Removes a given item from the list and notifies the underlying adapter of the change. If the + * currently selected item was the item that was removed, the item at the position provided + * by [defaultSelection] will be made the new selection. Invokes [callback] last. + * @param position Index of the item that was removed + * @param callback Lambda that's called at the end of the list changes and has the removed and + * selected list positions passed in as parameters + */ + fun removeSelectableItem( + position: Int, + callback: ((removedPosition: Int, selectedPosition: Int) -> Unit)? + ) { + removeItem(position) + if (position == selectedItem) { + selectedItem = getDefaultSelection() + currentList[selectedItem].onSelectionStateChanged(true) + onItemChanged(selectedItem) + } else if (position < selectedItem) { + selectedItem-- + } + callback?.invoke(position, selectedItem) + } + + override fun addItem(item: Model, position: Int, callback: ((Int) -> Unit)?) { + super.addItem(item, position, callback) + if (position <= selectedItem && position != -1) { + selectedItem++ + } + } + + override fun replaceList(newList: List) { + super.replaceList(newList) + findSelectedItem() + } + + private fun findSelectedItem() { + for (i in currentList.indices) { + if (currentList[i].selected) { + selectedItem = i + break + } + } + } + + private fun getDefaultSelection(): Int = + when (defaultSelection) { + DefaultSelection.Start -> currentList.indices.first + DefaultSelection.End -> currentList.indices.last + } + + enum class DefaultSelection { Start, End } +} 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 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.model + +interface SelectableItem { + var selected: Boolean + fun onSelectionStateChanged(selected: Boolean) +} From 93239f191a17179599ccadea29b9f4adb5a4dc60 Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 13:05:50 -0500 Subject: [PATCH 13/33] android: Refactor DriverAdapter to use AbstractSingleSelectionList --- .../yuzu/yuzu_emu/adapters/DriverAdapter.kt | 96 +++++-------------- 1 file changed, 23 insertions(+), 73 deletions(-) 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..ca353cea7 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,34 @@ import android.text.TextUtils import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.recyclerview.widget.AsyncDifferConfig -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding +import org.yuzu.yuzu_emu.model.Driver import org.yuzu.yuzu_emu.model.DriverViewModel -import org.yuzu.yuzu_emu.utils.GpuDriverHelper -import org.yuzu.yuzu_emu.utils.GpuDriverMetadata +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class DriverAdapter(private val driverViewModel: DriverViewModel) : - ListAdapter, DriverAdapter.DriverViewHolder>( - AsyncDifferConfig.Builder(DiffCallback()).build() + AbstractSingleSelectionList( + driverViewModel.driverList.value ) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder { - val binding = - CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return DriverViewHolder(binding) - } - - override fun getItemCount(): Int = currentList.size - - override fun onBindViewHolder(holder: DriverViewHolder, position: Int) = - holder.bind(currentList[position]) - - private fun onSelectDriver(position: Int) { - driverViewModel.setSelectedDriverIndex(position) - notifyItemChanged(driverViewModel.previouslySelectedDriver) - notifyItemChanged(driverViewModel.selectedDriver) - } - - private fun onDeleteDriver(driverData: Pair, position: Int) { - if (driverViewModel.selectedDriver > position) { - driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1) - } - if (GpuDriverHelper.customDriverSettingData == driverData.second) { - driverViewModel.setSelectedDriverIndex(0) - } - driverViewModel.driversToDelete.add(driverData.first) - driverViewModel.removeDriver(driverData) - notifyItemRemoved(position) - notifyItemChanged(driverViewModel.selectedDriver) + CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .also { return DriverViewHolder(it) } } inner class DriverViewHolder(val binding: CardDriverOptionBinding) : - RecyclerView.ViewHolder(binding.root) { - private lateinit var driverData: Pair - - fun bind(driverData: Pair) { - this.driverData = driverData - val driver = driverData.second - + AbstractViewHolder(binding) { + override fun bind(model: Driver) { binding.apply { - radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition + radioButton.isChecked = model.selected root.setOnClickListener { - onSelectDriver(bindingAdapterPosition) + selectItem(bindingAdapterPosition) { driverViewModel.onDriverSelected(it) } } buttonDelete.setOnClickListener { - onDeleteDriver(driverData, bindingAdapterPosition) + removeSelectableItem( + bindingAdapterPosition + ) { removedPosition: Int, selectedPosition: Int -> + driverViewModel.onDriverRemoved(removedPosition, selectedPosition) + } } // Delay marquee by 3s @@ -80,38 +49,19 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) : }, 3000 ) - if (driver.name == null) { - title.setText(R.string.system_gpu_driver) - description.text = "" - version.text = "" - version.visibility = View.GONE - description.visibility = View.GONE - buttonDelete.visibility = View.GONE - } else { - title.text = driver.name - version.text = driver.version - description.text = driver.description + title.text = model.title + version.text = model.version + description.text = model.description + if (model.description.isNotEmpty()) { version.visibility = View.VISIBLE description.visibility = View.VISIBLE buttonDelete.visibility = View.VISIBLE + } else { + version.visibility = View.GONE + description.visibility = View.GONE + buttonDelete.visibility = View.GONE } } } } - - private class DiffCallback : DiffUtil.ItemCallback>() { - override fun areItemsTheSame( - oldItem: Pair, - newItem: Pair - ): Boolean { - return oldItem.first == newItem.first - } - - override fun areContentsTheSame( - oldItem: Pair, - newItem: Pair - ): Boolean { - return oldItem.second == newItem.second - } - } } From 6bfc3c530ce0c8ba3ac0a62609d1266aa8d67d35 Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 13:09:06 -0500 Subject: [PATCH 14/33] android: Rework driver fragment Applies settings upon selection and uses a new Driver model to represent the information in-view. Also switches from an async diff list to a plain one. --- .../fragments/DriverManagerFragment.kt | 39 ++---- .../java/org/yuzu/yuzu_emu/model/Driver.kt | 27 ++++ .../yuzu/yuzu_emu/model/DriverViewModel.kt | 129 ++++++------------ .../yuzu/yuzu_emu/utils/GpuDriverHelper.kt | 3 - 4 files changed, 86 insertions(+), 112 deletions(-) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Driver.kt 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..82c966954 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 @@ -13,16 +13,16 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.transition.MaterialSharedAxis -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.adapters.DriverAdapter import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding +import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.utils.FileUtil @@ -85,25 +85,6 @@ class DriverManagerFragment : Fragment() { adapter = DriverAdapter(driverViewModel) } - viewLifecycleOwner.lifecycleScope.apply { - launch { - driverViewModel.driverList.collectLatest { - (binding.listDrivers.adapter as DriverAdapter).submitList(it) - } - } - launch { - driverViewModel.newDriverInstalled.collect { - if (_binding != null && it) { - (binding.listDrivers.adapter as DriverAdapter).apply { - notifyItemChanged(driverViewModel.previouslySelectedDriver) - notifyItemChanged(driverViewModel.selectedDriver) - driverViewModel.setNewDriverInstalled(false) - } - } - } - } - } - setInsets() } @@ -177,12 +158,20 @@ class DriverManagerFragment : Fragment() { val driverData = GpuDriverHelper.getMetadataFromZip(driverFile) val driverInList = - driverViewModel.driverList.value.firstOrNull { it.second == driverData } + driverViewModel.driverData.firstOrNull { it.second == driverData } if (driverInList != null) { return@newInstance getString(R.string.driver_already_installed) } else { - driverViewModel.addDriver(Pair(driverPath, driverData)) - driverViewModel.setNewDriverInstalled(true) + driverViewModel.onDriverAdded(Pair(driverPath, driverData)) + withContext(Dispatchers.Main) { + if (_binding != null) { + val adapter = binding.listDrivers.adapter as DriverAdapter + adapter.addItem(driverData.toDriver()) + adapter.selectItem(adapter.currentList.indices.last) + binding.listDrivers + .smoothScrollToPosition(adapter.currentList.indices.last) + } + } } return@newInstance Any() }.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 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.model + +import org.yuzu.yuzu_emu.utils.GpuDriverMetadata + +data class Driver( + override var selected: Boolean, + val title: String, + val version: String = "", + val description: String = "" +) : SelectableItem { + override fun onSelectionStateChanged(selected: Boolean) { + this.selected = selected + } + + companion object { + fun GpuDriverMetadata.toDriver(selected: Boolean = false): Driver = + Driver( + selected, + this.name ?: "", + this.version ?: "", + this.description ?: "" + ) + } +} 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..a1fee48cc 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 @@ -17,11 +17,10 @@ import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile -import org.yuzu.yuzu_emu.utils.FileUtil +import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverMetadata import org.yuzu.yuzu_emu.utils.NativeConfig -import java.io.BufferedOutputStream import java.io.File class DriverViewModel : ViewModel() { @@ -38,97 +37,74 @@ class DriverViewModel : ViewModel() { !loading && ready && !deleting }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialValue = false) - private val _driverList = MutableStateFlow(GpuDriverHelper.getDrivers()) - val driverList: StateFlow>> get() = _driverList + var driverData = GpuDriverHelper.getDrivers() - var previouslySelectedDriver = 0 - var selectedDriver = -1 + private val _driverList = MutableStateFlow(emptyList()) + val driverList: StateFlow> get() = _driverList // Used for showing which driver is currently installed within the driver manager card private val _selectedDriverTitle = MutableStateFlow("") val selectedDriverTitle: StateFlow get() = _selectedDriverTitle - private val _newDriverInstalled = MutableStateFlow(false) - val newDriverInstalled: StateFlow get() = _newDriverInstalled - - val driversToDelete = mutableListOf() + private val driversToDelete = mutableListOf() init { - val currentDriverMetadata = GpuDriverHelper.installedCustomDriverData - findSelectedDriver(currentDriverMetadata) - - // If a user had installed a driver before the manager was implemented, this zips - // the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can - // be indexed and exported as expected. - if (selectedDriver == -1) { - val driverToSave = - File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip") - driverToSave.createNewFile() - FileUtil.zipFromInternalStorage( - File(GpuDriverHelper.driverInstallationPath!!), - GpuDriverHelper.driverInstallationPath!!, - BufferedOutputStream(driverToSave.outputStream()) - ) - _driverList.value.add(Pair(driverToSave.path, currentDriverMetadata)) - setSelectedDriverIndex(_driverList.value.size - 1) - } - - // If a user had installed a driver before the config was reworked to be multiplatform, - // we have save the path of the previously selected driver to the new setting. - if (StringSetting.DRIVER_PATH.getString(true).isEmpty() && selectedDriver > 0 && - StringSetting.DRIVER_PATH.global - ) { - StringSetting.DRIVER_PATH.setString(_driverList.value[selectedDriver].first) - NativeConfig.saveGlobalConfig() - } else { - findSelectedDriver(GpuDriverHelper.customDriverSettingData) - } + updateDriverList() updateDriverNameForGame(null) } - fun setSelectedDriverIndex(value: Int) { - if (selectedDriver != -1) { - previouslySelectedDriver = selectedDriver + fun reloadDriverData() { + _areDriversLoading.value = true + driverData = GpuDriverHelper.getDrivers() + updateDriverList() + _areDriversLoading.value = false + } + + private fun updateDriverList() { + val selectedDriver = GpuDriverHelper.customDriverSettingData + val newDriverList = mutableListOf( + Driver( + selectedDriver == GpuDriverMetadata(), + YuzuApplication.appContext.getString(R.string.system_gpu_driver) + ) + ) + driverData.forEach { + newDriverList.add(it.second.toDriver(it.second == selectedDriver)) } - selectedDriver = value - } - - fun setNewDriverInstalled(value: Boolean) { - _newDriverInstalled.value = value - } - - fun addDriver(driverData: Pair) { - val driverIndex = _driverList.value.indexOfFirst { it == driverData } - if (driverIndex == -1) { - _driverList.value.add(driverData) - setSelectedDriverIndex(_driverList.value.size - 1) - _selectedDriverTitle.value = driverData.second.name - ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver) - } else { - setSelectedDriverIndex(driverIndex) - } - } - - fun removeDriver(driverData: Pair) { - _driverList.value.remove(driverData) + _driverList.value = newDriverList } fun onOpenDriverManager(game: Game?) { if (game != null) { SettingsFile.loadCustomConfig(game) } + updateDriverList() + } - val driverPath = StringSetting.DRIVER_PATH.getString() - if (driverPath.isEmpty()) { - setSelectedDriverIndex(0) + fun onDriverSelected(position: Int) { + if (position == 0) { + StringSetting.DRIVER_PATH.setString("") } else { - findSelectedDriver(GpuDriverHelper.getMetadataFromZip(File(driverPath))) + StringSetting.DRIVER_PATH.setString(driverData[position - 1].first) } } + fun onDriverRemoved(removedPosition: Int, selectedPosition: Int) { + driversToDelete.add(driverData[removedPosition - 1].first) + driverData.removeAt(removedPosition - 1) + onDriverSelected(selectedPosition) + } + + fun onDriverAdded(driver: Pair) { + if (driversToDelete.contains(driver.first)) { + driversToDelete.remove(driver.first) + } + driverData.add(driver) + onDriverSelected(driverData.size) + } + fun onCloseDriverManager(game: Game?) { _isDeletingDrivers.value = true - StringSetting.DRIVER_PATH.setString(driverList.value[selectedDriver].first) updateDriverNameForGame(game) if (game == null) { NativeConfig.saveGlobalConfig() @@ -181,20 +157,6 @@ class DriverViewModel : ViewModel() { } } - private fun findSelectedDriver(currentDriverMetadata: GpuDriverMetadata) { - if (driverList.value.size == 1) { - setSelectedDriverIndex(0) - return - } - - driverList.value.forEachIndexed { i: Int, driver: Pair -> - if (driver.second == currentDriverMetadata) { - setSelectedDriverIndex(i) - return - } - } - } - fun updateDriverNameForGame(game: Game?) { if (!GpuDriverHelper.supportsCustomDriverLoading()) { return @@ -217,7 +179,6 @@ class DriverViewModel : ViewModel() { private fun setDriverReady() { _isDriverReady.value = true - _selectedDriverTitle.value = GpuDriverHelper.customDriverSettingData.name - ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver) + updateName() } } 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 { ?.sortedByDescending { it: Pair -> it.second.name } ?.distinct() ?.toMutableList() ?: mutableListOf() - - // TODO: Get system driver information - drivers.add(0, Pair("", GpuDriverMetadata())) return drivers } From 9e974d4c7e44b1067a5c71c56b172eb15c75e43f Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 13:09:21 -0500 Subject: [PATCH 15/33] android: Reload driver data on importing user data --- .../src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 3 +++ 1 file changed, 3 insertions(+) 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 import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.model.AddonViewModel +import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.TaskState @@ -58,6 +59,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { private val gamesViewModel: GamesViewModel by viewModels() private val taskViewModel: TaskViewModel by viewModels() private val addonViewModel: AddonViewModel by viewModels() + private val driverViewModel: DriverViewModel by viewModels() override var themeId: Int = 0 @@ -689,6 +691,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { NativeLibrary.initializeSystem(true) NativeConfig.initializeGlobalConfig() gamesViewModel.reloadGames(false) + driverViewModel.reloadDriverData() return@newInstance getString(R.string.user_data_import_success) }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) From dac8c4ce4d41d12b812caa59bc7f040220caf652 Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 15:34:14 -0500 Subject: [PATCH 16/33] android: Add button to use global driver value --- .../yuzu/yuzu_emu/adapters/DriverAdapter.kt | 7 +++- .../fragments/DriverManagerFragment.kt | 39 +++++++++++++++++++ .../yuzu/yuzu_emu/model/DriverViewModel.kt | 10 ++++- .../src/main/res/menu/menu_driver_manager.xml | 11 ++++++ 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 src/android/app/src/main/res/menu/menu_driver_manager.xml 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 ca353cea7..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 @@ -8,6 +8,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding +import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.model.Driver import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder @@ -27,13 +28,17 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) : binding.apply { radioButton.isChecked = model.selected root.setOnClickListener { - selectItem(bindingAdapterPosition) { driverViewModel.onDriverSelected(it) } + selectItem(bindingAdapterPosition) { + driverViewModel.onDriverSelected(it) + driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global) + } } buttonDelete.setOnClickListener { removeSelectableItem( bindingAdapterPosition ) { removedPosition: Int, selectedPosition: Int -> driverViewModel.onDriverRemoved(removedPosition, selectedPosition) + driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global) } } 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 82c966954..c01fdff25 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 @@ package org.yuzu.yuzu_emu.fragments +import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -13,20 +14,26 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.transition.MaterialSharedAxis import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.adapters.DriverAdapter import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding +import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.GpuDriverHelper +import org.yuzu.yuzu_emu.utils.NativeConfig import java.io.File import java.io.IOException @@ -55,12 +62,43 @@ class DriverManagerFragment : Fragment() { return binding.root } + // This is using the correct scope, lint is just acting up + @SuppressLint("UnsafeRepeatOnLifecycleDetector") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) homeViewModel.setNavigationVisibility(visible = false, animated = true) homeViewModel.setStatusBarShadeVisibility(visible = false) driverViewModel.onOpenDriverManager(args.game) + if (NativeConfig.isPerGameConfigLoaded()) { + binding.toolbarDrivers.inflateMenu(R.menu.menu_driver_manager) + driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global) + binding.toolbarDrivers.setOnMenuItemClickListener { + when (it.itemId) { + R.id.menu_driver_clear -> { + StringSetting.DRIVER_PATH.global = true + driverViewModel.updateDriverList() + (binding.listDrivers.adapter as DriverAdapter) + .replaceList(driverViewModel.driverList.value) + driverViewModel.showClearButton(false) + true + } + + else -> false + } + } + + viewLifecycleOwner.lifecycleScope.apply { + launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + driverViewModel.showClearButton.collect { + binding.toolbarDrivers.menu + .findItem(R.id.menu_driver_clear).isVisible = it + } + } + } + } + } if (!driverViewModel.isInteractionAllowed.value) { DriversLoadingDialogFragment().show( @@ -168,6 +206,7 @@ class DriverManagerFragment : Fragment() { val adapter = binding.listDrivers.adapter as DriverAdapter adapter.addItem(driverData.toDriver()) adapter.selectItem(adapter.currentList.indices.last) + driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global) binding.listDrivers .smoothScrollToPosition(adapter.currentList.indices.last) } 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 a1fee48cc..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 import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -46,6 +47,9 @@ class DriverViewModel : ViewModel() { private val _selectedDriverTitle = MutableStateFlow("") val selectedDriverTitle: StateFlow get() = _selectedDriverTitle + private val _showClearButton = MutableStateFlow(false) + val showClearButton = _showClearButton.asStateFlow() + private val driversToDelete = mutableListOf() init { @@ -60,7 +64,7 @@ class DriverViewModel : ViewModel() { _areDriversLoading.value = false } - private fun updateDriverList() { + fun updateDriverList() { val selectedDriver = GpuDriverHelper.customDriverSettingData val newDriverList = mutableListOf( Driver( @@ -81,6 +85,10 @@ class DriverViewModel : ViewModel() { updateDriverList() } + fun showClearButton(value: Boolean) { + _showClearButton.value = value + } + fun onDriverSelected(position: Int) { if (position == 0) { StringSetting.DRIVER_PATH.setString("") 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 @@ + + + + + + From d3ba6b334bdec89834f511dff70217fc9adb943c Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 15:36:22 -0500 Subject: [PATCH 17/33] android: Fix added driver path While this didn't break anything, the extra separator was unnecessary --- .../java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c01fdff25..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 @@ -179,7 +179,7 @@ class DriverManagerFragment : Fragment() { false ) { val driverPath = - "${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}" + "${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(result)}" val driverFile = File(driverPath) // Ignore file exceptions when a user selects an invalid zip From aae9eea53208fc0924c90ebb1272fcfaa3f23e0c Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 8 Jan 2024 00:49:00 -0500 Subject: [PATCH 18/33] fsp-srv: use program registry for SetCurrentProcess --- src/android/app/src/main/jni/native.cpp | 6 +- src/core/CMakeLists.txt | 4 + src/core/core.cpp | 1 + src/core/file_sys/savedata_factory.cpp | 17 +- src/core/file_sys/savedata_factory.h | 10 +- src/core/hle/service/am/am.cpp | 7 +- .../hle/service/filesystem/filesystem.cpp | 235 ++++-------------- src/core/hle/service/filesystem/filesystem.h | 55 ++-- src/core/hle/service/filesystem/fsp_srv.cpp | 55 ++-- src/core/hle/service/filesystem/fsp_srv.h | 6 + .../service/filesystem/romfs_controller.cpp | 37 +++ .../hle/service/filesystem/romfs_controller.h | 31 +++ .../filesystem/save_data_controller.cpp | 99 ++++++++ .../service/filesystem/save_data_controller.h | 35 +++ .../loader/deconstructed_rom_directory.cpp | 14 -- src/core/loader/nca.cpp | 6 +- src/core/loader/nro.cpp | 6 +- src/core/loader/nsp.cpp | 3 +- src/core/loader/xci.cpp | 3 +- src/yuzu/main.cpp | 8 +- 20 files changed, 364 insertions(+), 274 deletions(-) create mode 100644 src/core/hle/service/filesystem/romfs_controller.cpp create mode 100644 src/core/hle/service/filesystem/romfs_controller.h create mode 100644 src/core/hle/service/filesystem/save_data_controller.cpp create mode 100644 src/core/hle/service/filesystem/save_data_controller.h diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 136c8dee6..8017eb58d 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -770,8 +770,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv* ASSERT(user_id); const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( - EmulationSession::GetInstance().System(), vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, - FileSys::SaveDataType::SaveData, 1, user_id->AsU128(), 0); + {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, 1, + user_id->AsU128(), 0); const auto full_path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); if (!Common::FS::CreateParentDirs(full_path)) { @@ -878,7 +878,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j FileSys::Mode::Read); const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( - system, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, + {}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, program_id, user_id->AsU128(), 0); return ToJString(env, user_save_data_path); } 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 hle/service/filesystem/fsp_pr.h hle/service/filesystem/fsp_srv.cpp hle/service/filesystem/fsp_srv.h + hle/service/filesystem/romfs_controller.cpp + hle/service/filesystem/romfs_controller.h + hle/service/filesystem/save_data_controller.cpp + hle/service/filesystem/save_data_controller.h hle/service/fgm/fgm.cpp hle/service/fgm/fgm.h hle/service/friend/friend.cpp 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 { kernel.ShutdownCores(); services.reset(); service_manager.reset(); + fs_controller.Reset(); cheat_engine.reset(); telemetry_session.reset(); time_manager.Shutdown(); 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 { static_cast(rank), index); } -SaveDataFactory::SaveDataFactory(Core::System& system_, VirtualDir save_directory_) - : dir{std::move(save_directory_)}, system{system_} { +SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_, + VirtualDir save_directory_) + : system{system_}, program_id{program_id_}, dir{std::move(save_directory_)} { // Delete all temporary storages // On hardware, it is expected that temporary storage be empty at first use. dir->DeleteSubdirectoryRecursive("temp"); @@ -110,7 +111,7 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut PrintSaveDataAttributeWarnings(meta); const auto save_directory = - GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); + GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); return dir->CreateDirectoryRelative(save_directory); } @@ -118,7 +119,7 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const { const auto save_directory = - GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); + GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); auto out = dir->GetDirectoryRelative(save_directory); @@ -147,14 +148,14 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) { } } -std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir, +std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir, SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id) { // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should // be interpreted as the title id of the current process. if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) { if (title_id == 0) { - title_id = system.GetApplicationProcessProgramID(); + title_id = program_id; } } @@ -201,7 +202,7 @@ std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future) SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const { const auto path = - GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); + GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName()); @@ -220,7 +221,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value) const { const auto path = - GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); + GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); 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() { return ".yuzu_save_size"; } +using ProgramId = u64; + /// File system interface to the SaveData archive class SaveDataFactory { public: - explicit SaveDataFactory(Core::System& system_, VirtualDir save_directory_); + explicit SaveDataFactory(Core::System& system_, ProgramId program_id_, + VirtualDir save_directory_); ~SaveDataFactory(); VirtualDir Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const; @@ -99,7 +102,7 @@ public: VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); - static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space, + static std::string GetFullPath(ProgramId program_id, VirtualDir dir, SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id); static std::string GetUserGameSaveDataRoot(u128 user_id, bool future); @@ -110,8 +113,9 @@ public: void SetAutoCreate(bool state); private: - VirtualDir dir; Core::System& system; + ProgramId program_id; + VirtualDir dir; bool auto_create{true}; }; 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 @@ #include "core/hle/service/caps/caps_su.h" #include "core/hle/service/caps/caps_types.h" #include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/save_data_controller.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" @@ -2178,7 +2179,7 @@ void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) { attribute.type = FileSys::SaveDataType::SaveData; FileSys::VirtualDir save_data{}; - const auto res = system.GetFileSystemController().CreateSaveData( + const auto res = system.GetFileSystemController().OpenSaveDataController()->CreateSaveData( &save_data, FileSys::SaveDataSpaceId::NandUser, attribute); IPC::ResponseBuilder rb{ctx, 4}; @@ -2353,7 +2354,7 @@ void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) { "new_journal={:016X}", static_cast(type), user_id[1], user_id[0], new_normal_size, new_journal_size); - system.GetFileSystemController().WriteSaveDataSize( + system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize( type, system.GetApplicationProcessProgramID(), user_id, {new_normal_size, new_journal_size}); @@ -2378,7 +2379,7 @@ void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1], user_id[0]); - const auto size = system.GetFileSystemController().ReadSaveDataSize( + const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize( type, system.GetApplicationProcessProgramID(), user_id); IPC::ResponseBuilder rb{ctx, 6}; 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 @@ #include "core/hle/service/filesystem/fsp_ldr.h" #include "core/hle/service/filesystem/fsp_pr.h" #include "core/hle/service/filesystem/fsp_srv.h" +#include "core/hle/service/filesystem/romfs_controller.h" +#include "core/hle/service/filesystem/save_data_controller.h" #include "core/hle/service/server_manager.h" #include "core/loader/loader.h" namespace Service::FileSystem { -// A default size for normal/journal save data size if application control metadata cannot be found. -// This should be large enough to satisfy even the most extreme requirements (~4.2GB) -constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000; - static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, std::string_view dir_name_) { std::string dir_name(Common::FS::SanitizePath(dir_name_)); @@ -297,145 +295,65 @@ FileSystemController::FileSystemController(Core::System& system_) : system{syste FileSystemController::~FileSystemController() = default; -Result FileSystemController::RegisterRomFS(std::unique_ptr&& factory) { - romfs_factory = std::move(factory); - LOG_DEBUG(Service_FS, "Registered RomFS"); +Result FileSystemController::RegisterProcess( + ProcessId process_id, ProgramId program_id, + std::shared_ptr&& romfs_factory) { + std::scoped_lock lk{registration_lock}; + + registrations.emplace(process_id, Registration{ + .program_id = program_id, + .romfs_factory = std::move(romfs_factory), + .save_data_factory = CreateSaveDataFactory(program_id), + }); + + LOG_DEBUG(Service_FS, "Registered for process {}", process_id); return ResultSuccess; } -Result FileSystemController::RegisterSaveData(std::unique_ptr&& factory) { - ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data"); - save_data_factory = std::move(factory); - LOG_DEBUG(Service_FS, "Registered save data"); +Result FileSystemController::OpenProcess( + ProgramId* out_program_id, std::shared_ptr* out_save_data_controller, + std::shared_ptr* out_romfs_controller, ProcessId process_id) { + std::scoped_lock lk{registration_lock}; + + const auto it = registrations.find(process_id); + if (it == registrations.end()) { + return FileSys::ERROR_ENTITY_NOT_FOUND; + } + + *out_program_id = it->second.program_id; + *out_save_data_controller = + std::make_shared(system, it->second.save_data_factory); + *out_romfs_controller = + std::make_shared(it->second.romfs_factory, it->second.program_id); return ResultSuccess; } -Result FileSystemController::RegisterSDMC(std::unique_ptr&& factory) { - ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC"); - sdmc_factory = std::move(factory); - LOG_DEBUG(Service_FS, "Registered SDMC"); - return ResultSuccess; -} - -Result FileSystemController::RegisterBIS(std::unique_ptr&& factory) { - ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS"); - bis_factory = std::move(factory); - LOG_DEBUG(Service_FS, "Registered BIS"); - return ResultSuccess; -} - -void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) { +void FileSystemController::SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw) { LOG_TRACE(Service_FS, "Setting packed update for romfs"); - if (romfs_factory == nullptr) + std::scoped_lock lk{registration_lock}; + const auto it = registrations.find(process_id); + if (it == registrations.end()) { return; + } - romfs_factory->SetPackedUpdate(std::move(update_raw)); + it->second.romfs_factory->SetPackedUpdate(std::move(update_raw)); } -FileSys::VirtualFile FileSystemController::OpenRomFSCurrentProcess() const { - LOG_TRACE(Service_FS, "Opening RomFS for current process"); - - if (romfs_factory == nullptr) { - return nullptr; - } - - return romfs_factory->OpenCurrentProcess(system.GetApplicationProcessProgramID()); +std::shared_ptr FileSystemController::OpenSaveDataController() { + return std::make_shared(system, CreateSaveDataFactory(ProgramId{})); } -FileSys::VirtualFile FileSystemController::OpenPatchedRomFS(u64 title_id, - FileSys::ContentRecordType type) const { - LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}", title_id); +std::shared_ptr FileSystemController::CreateSaveDataFactory( + ProgramId program_id) { + using YuzuPath = Common::FS::YuzuPath; + const auto rw_mode = FileSys::Mode::ReadWrite; - if (romfs_factory == nullptr) { - return nullptr; - } - - return romfs_factory->OpenPatchedRomFS(title_id, type); -} - -FileSys::VirtualFile FileSystemController::OpenPatchedRomFSWithProgramIndex( - u64 title_id, u8 program_index, FileSys::ContentRecordType type) const { - LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}, program_index={}", title_id, - program_index); - - if (romfs_factory == nullptr) { - return nullptr; - } - - return romfs_factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type); -} - -FileSys::VirtualFile FileSystemController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id, - FileSys::ContentRecordType type) const { - LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}", - title_id, storage_id, type); - - if (romfs_factory == nullptr) { - return nullptr; - } - - return romfs_factory->Open(title_id, storage_id, type); -} - -std::shared_ptr FileSystemController::OpenBaseNca( - u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const { - return romfs_factory->GetEntry(title_id, storage_id, type); -} - -Result FileSystemController::CreateSaveData(FileSys::VirtualDir* out_save_data, - FileSys::SaveDataSpaceId space, - const FileSys::SaveDataAttribute& save_struct) const { - LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space, - save_struct.DebugInfo()); - - if (save_data_factory == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; - } - - auto save_data = save_data_factory->Create(space, save_struct); - if (save_data == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; - } - - *out_save_data = save_data; - return ResultSuccess; -} - -Result FileSystemController::OpenSaveData(FileSys::VirtualDir* out_save_data, - FileSys::SaveDataSpaceId space, - const FileSys::SaveDataAttribute& attribute) const { - LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", space, - attribute.DebugInfo()); - - if (save_data_factory == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; - } - - auto save_data = save_data_factory->Open(space, attribute); - if (save_data == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; - } - - *out_save_data = save_data; - return ResultSuccess; -} - -Result FileSystemController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space, - FileSys::SaveDataSpaceId space) const { - LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", space); - - if (save_data_factory == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; - } - - auto save_data_space = save_data_factory->GetSaveDataSpaceDirectory(space); - if (save_data_space == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; - } - - *out_save_data_space = save_data_space; - return ResultSuccess; + auto vfs = system.GetFilesystem(); + const auto nand_directory = + vfs->OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode); + return std::make_shared(system, program_id, + std::move(nand_directory)); } Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const { @@ -540,48 +458,6 @@ u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const { return 0; } -FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type, - u64 title_id, u128 user_id) const { - if (save_data_factory == nullptr) { - return {0, 0}; - } - - const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id); - - if (value.normal == 0 && value.journal == 0) { - FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE}; - - FileSys::NACP nacp; - const auto res = system.GetAppLoader().ReadControlData(nacp); - - if (res != Loader::ResultStatus::Success) { - const FileSys::PatchManager pm{system.GetApplicationProcessProgramID(), - system.GetFileSystemController(), - system.GetContentProvider()}; - const auto metadata = pm.GetControlMetadata(); - const auto& nacp_unique = metadata.first; - - if (nacp_unique != nullptr) { - new_size = {nacp_unique->GetDefaultNormalSaveSize(), - nacp_unique->GetDefaultJournalSaveSize()}; - } - } else { - new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()}; - } - - WriteSaveDataSize(type, title_id, user_id, new_size); - return new_size; - } - - return value; -} - -void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, - FileSys::SaveDataSize new_value) const { - if (save_data_factory != nullptr) - save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value); -} - void FileSystemController::SetGameCard(FileSys::VirtualFile file) { gamecard = std::make_unique(file); const auto dir = gamecard->ConcatenatedPseudoDirectory(); @@ -801,14 +677,9 @@ FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const { return bis_factory->GetBCATDirectory(title_id); } -void FileSystemController::SetAutoSaveDataCreation(bool enable) { - save_data_factory->SetAutoCreate(enable); -} - void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { if (overwrite) { bis_factory = nullptr; - save_data_factory = nullptr; sdmc_factory = nullptr; } @@ -836,11 +707,6 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove bis_factory->GetUserNANDContents()); } - if (save_data_factory == nullptr) { - save_data_factory = - std::make_unique(system, std::move(nand_directory)); - } - if (sdmc_factory == nullptr) { sdmc_factory = std::make_unique(std::move(sd_directory), std::move(sd_load_directory)); @@ -849,12 +715,19 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove } } +void FileSystemController::Reset() { + std::scoped_lock lk{registration_lock}; + registrations.clear(); +} + void LoopProcess(Core::System& system) { auto server_manager = std::make_unique(system); + const auto FileSystemProxyFactory = [&] { return std::make_shared(system); }; + server_manager->RegisterNamedService("fsp-ldr", std::make_shared(system)); server_manager->RegisterNamedService("fsp:pr", std::make_shared(system)); - server_manager->RegisterNamedService("fsp-srv", std::make_shared(system)); + server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory)); ServerManager::RunServer(std::move(server_manager)); } 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; namespace FileSystem { +class RomFsController; +class SaveDataController; + enum class ContentStorageId : u32 { System, User, @@ -61,32 +64,24 @@ enum class OpenDirectoryMode : u64 { }; DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode); +using ProcessId = u64; +using ProgramId = u64; + class FileSystemController { public: explicit FileSystemController(Core::System& system_); ~FileSystemController(); - Result RegisterRomFS(std::unique_ptr&& factory); - Result RegisterSaveData(std::unique_ptr&& factory); - Result RegisterSDMC(std::unique_ptr&& factory); - Result RegisterBIS(std::unique_ptr&& factory); + Result RegisterProcess(ProcessId process_id, ProgramId program_id, + std::shared_ptr&& factory); + Result OpenProcess(ProgramId* out_program_id, + std::shared_ptr* out_save_data_controller, + std::shared_ptr* out_romfs_controller, + ProcessId process_id); + void SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw); - void SetPackedUpdate(FileSys::VirtualFile update_raw); - FileSys::VirtualFile OpenRomFSCurrentProcess() const; - FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type) const; - FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index, - FileSys::ContentRecordType type) const; - FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id, - FileSys::ContentRecordType type) const; - std::shared_ptr OpenBaseNca(u64 title_id, FileSys::StorageId storage_id, - FileSys::ContentRecordType type) const; + std::shared_ptr OpenSaveDataController(); - Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space, - const FileSys::SaveDataAttribute& save_struct) const; - Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space, - const FileSys::SaveDataAttribute& save_struct) const; - Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space, - FileSys::SaveDataSpaceId space) const; Result OpenSDMC(FileSys::VirtualDir* out_sdmc) const; Result OpenBISPartition(FileSys::VirtualDir* out_bis_partition, FileSys::BisPartitionId id) const; @@ -96,11 +91,6 @@ public: u64 GetFreeSpaceSize(FileSys::StorageId id) const; u64 GetTotalSpaceSize(FileSys::StorageId id) const; - FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, - u128 user_id) const; - void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, - FileSys::SaveDataSize new_value) const; - void SetGameCard(FileSys::VirtualFile file); FileSys::XCI* GetGameCard() const; @@ -133,15 +123,24 @@ public: FileSys::VirtualDir GetBCATDirectory(u64 title_id) const; - void SetAutoSaveDataCreation(bool enable); - // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function // above is called. void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); + void Reset(); + private: - std::unique_ptr romfs_factory; - std::unique_ptr save_data_factory; + std::shared_ptr CreateSaveDataFactory(ProgramId program_id); + + struct Registration { + ProgramId program_id; + std::shared_ptr romfs_factory; + std::shared_ptr save_data_factory; + }; + + std::mutex registration_lock; + std::map registrations; + std::unique_ptr sdmc_factory; std::unique_ptr bis_factory; 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 @@ #include "core/hle/result.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/fsp_srv.h" +#include "core/hle/service/filesystem/romfs_controller.h" +#include "core/hle/service/filesystem/save_data_controller.h" #include "core/hle/service/hle_ipc.h" #include "core/hle/service/ipc_helpers.h" #include "core/reporter.h" @@ -577,9 +579,11 @@ private: class ISaveDataInfoReader final : public ServiceFramework { public: - explicit ISaveDataInfoReader(Core::System& system_, FileSys::SaveDataSpaceId space, - FileSystemController& fsc_) - : ServiceFramework{system_, "ISaveDataInfoReader"}, fsc{fsc_} { + explicit ISaveDataInfoReader(Core::System& system_, + std::shared_ptr save_data_controller_, + FileSys::SaveDataSpaceId space) + : ServiceFramework{system_, "ISaveDataInfoReader"}, save_data_controller{ + save_data_controller_} { static const FunctionInfo functions[] = { {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, }; @@ -626,7 +630,7 @@ private: void FindAllSaves(FileSys::SaveDataSpaceId space) { FileSys::VirtualDir save_root{}; - const auto result = fsc.OpenSaveDataSpace(&save_root, space); + const auto result = save_data_controller->OpenSaveDataSpace(&save_root, space); if (result != ResultSuccess || save_root == nullptr) { LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", space); @@ -723,7 +727,8 @@ private: }; static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); - FileSystemController& fsc; + ProcessId process_id = 0; + std::shared_ptr save_data_controller; std::vector info; u64 next_entry_index = 0; }; @@ -863,21 +868,20 @@ FSP_SRV::FSP_SRV(Core::System& system_) if (Settings::values.enable_fs_access_log) { access_log_mode = AccessLogMode::SdCard; } - - // This should be true on creation - fsc.SetAutoSaveDataCreation(true); } FSP_SRV::~FSP_SRV() = default; void FSP_SRV::SetCurrentProcess(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - current_process_id = rp.Pop(); + current_process_id = ctx.GetPID(); LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id); + const auto res = + fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller, current_process_id); + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(res); } void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) { @@ -916,7 +920,8 @@ void FSP_SRV::CreateSaveDataFileSystem(HLERequestContext& ctx) { uid[1], uid[0]); FileSys::VirtualDir save_data_dir{}; - fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser, save_struct); + save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser, + save_struct); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -931,7 +936,8 @@ void FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx) LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo()); FileSys::VirtualDir save_data_dir{}; - fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem, save_struct); + save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem, + save_struct); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -950,7 +956,8 @@ void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) { LOG_INFO(Service_FS, "called."); FileSys::VirtualDir dir{}; - auto result = fsc.OpenSaveData(&dir, parameters.space_id, parameters.attribute); + auto result = + save_data_controller->OpenSaveData(&dir, parameters.space_id, parameters.attribute); if (result != ResultSuccess) { IPC::ResponseBuilder rb{ctx, 2, 0, 0}; rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); @@ -1001,7 +1008,7 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface( - std::make_shared(system, space, fsc)); + std::make_shared(system, save_data_controller, space)); } void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) { @@ -1009,8 +1016,8 @@ void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(system, FileSys::SaveDataSpaceId::TemporaryStorage, - fsc); + rb.PushIpcInterface(system, save_data_controller, + FileSys::SaveDataSpaceId::TemporaryStorage); } void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx) { @@ -1050,7 +1057,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "called"); if (!romfs) { - auto current_romfs = fsc.OpenRomFSCurrentProcess(); + auto current_romfs = romfs_controller->OpenRomFSCurrentProcess(); if (!current_romfs) { // TODO (bunnei): Find the right error code to use here LOG_CRITICAL(Service_FS, "no file system interface available!"); @@ -1078,7 +1085,7 @@ void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}", storage_id, unknown, title_id); - auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); + auto data = romfs_controller->OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); if (!data) { const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); @@ -1101,7 +1108,8 @@ void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) { const FileSys::PatchManager pm{title_id, fsc, content_provider}; - auto base = fsc.OpenBaseNca(title_id, storage_id, FileSys::ContentRecordType::Data); + auto base = + romfs_controller->OpenBaseNca(title_id, storage_id, FileSys::ContentRecordType::Data); auto storage = std::make_shared( system, pm.PatchRomFS(base.get(), std::move(data), FileSys::ContentRecordType::Data)); @@ -1129,9 +1137,8 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "called, program_index={}", program_index); - auto patched_romfs = - fsc.OpenPatchedRomFSWithProgramIndex(system.GetApplicationProcessProgramID(), program_index, - FileSys::ContentRecordType::Program); + auto patched_romfs = romfs_controller->OpenPatchedRomFSWithProgramIndex( + program_id, program_index, FileSys::ContentRecordType::Program); if (!patched_romfs) { // TODO: Find the right error code to use here @@ -1152,7 +1159,7 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) { void FSP_SRV::DisableAutoSaveDataCreation(HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "called"); - fsc.SetAutoSaveDataCreation(false); + save_data_controller->SetAutoCreate(false); IPC::ResponseBuilder rb{ctx, 2}; 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; namespace Service::FileSystem { +class RomFsController; +class SaveDataController; + enum class AccessLogVersion : u32 { V7_0_0 = 2, @@ -67,6 +70,9 @@ private: u64 current_process_id = 0; u32 access_log_program_index = 0; AccessLogMode access_log_mode = AccessLogMode::None; + u64 program_id = 0; + std::shared_ptr save_data_controller; + std::shared_ptr romfs_controller; }; } // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/filesystem/romfs_controller.h" + +namespace Service::FileSystem { + +RomFsController::RomFsController(std::shared_ptr factory_, u64 program_id_) + : factory{std::move(factory_)}, program_id{program_id_} {} +RomFsController::~RomFsController() = default; + +FileSys::VirtualFile RomFsController::OpenRomFSCurrentProcess() { + return factory->OpenCurrentProcess(program_id); +} + +FileSys::VirtualFile RomFsController::OpenPatchedRomFS(u64 title_id, + FileSys::ContentRecordType type) { + return factory->OpenPatchedRomFS(title_id, type); +} + +FileSys::VirtualFile RomFsController::OpenPatchedRomFSWithProgramIndex( + u64 title_id, u8 program_index, FileSys::ContentRecordType type) { + return factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type); +} + +FileSys::VirtualFile RomFsController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id, + FileSys::ContentRecordType type) { + return factory->Open(title_id, storage_id, type); +} + +std::shared_ptr RomFsController::OpenBaseNca(u64 title_id, + FileSys::StorageId storage_id, + FileSys::ContentRecordType type) { + return factory->GetEntry(title_id, storage_id, type); +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/romfs_factory.h" +#include "core/file_sys/vfs_types.h" + +namespace Service::FileSystem { + +class RomFsController { +public: + explicit RomFsController(std::shared_ptr factory_, u64 program_id_); + ~RomFsController(); + + FileSys::VirtualFile OpenRomFSCurrentProcess(); + FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type); + FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index, + FileSys::ContentRecordType type); + FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id, + FileSys::ContentRecordType type); + std::shared_ptr OpenBaseNca(u64 title_id, FileSys::StorageId storage_id, + FileSys::ContentRecordType type); + +private: + const std::shared_ptr factory; + const u64 program_id; +}; + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/errors.h" +#include "core/file_sys/patch_manager.h" +#include "core/hle/service/filesystem/save_data_controller.h" +#include "core/loader/loader.h" + +namespace Service::FileSystem { + +namespace { + +// A default size for normal/journal save data size if application control metadata cannot be found. +// This should be large enough to satisfy even the most extreme requirements (~4.2GB) +constexpr u64 SufficientSaveDataSize = 0xF0000000; + +FileSys::SaveDataSize GetDefaultSaveDataSize(Core::System& system, u64 program_id) { + const FileSys::PatchManager pm{program_id, system.GetFileSystemController(), + system.GetContentProvider()}; + const auto metadata = pm.GetControlMetadata(); + const auto& nacp = metadata.first; + + if (nacp != nullptr) { + return {nacp->GetDefaultNormalSaveSize(), nacp->GetDefaultJournalSaveSize()}; + } + + return {SufficientSaveDataSize, SufficientSaveDataSize}; +} + +} // namespace + +SaveDataController::SaveDataController(Core::System& system_, + std::shared_ptr factory_) + : system{system_}, factory{std::move(factory_)} {} +SaveDataController::~SaveDataController() = default; + +Result SaveDataController::CreateSaveData(FileSys::VirtualDir* out_save_data, + FileSys::SaveDataSpaceId space, + const FileSys::SaveDataAttribute& attribute) { + LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space, + attribute.DebugInfo()); + + auto save_data = factory->Create(space, attribute); + if (save_data == nullptr) { + return FileSys::ERROR_ENTITY_NOT_FOUND; + } + + *out_save_data = save_data; + return ResultSuccess; +} + +Result SaveDataController::OpenSaveData(FileSys::VirtualDir* out_save_data, + FileSys::SaveDataSpaceId space, + const FileSys::SaveDataAttribute& attribute) { + auto save_data = factory->Open(space, attribute); + if (save_data == nullptr) { + return FileSys::ERROR_ENTITY_NOT_FOUND; + } + + *out_save_data = save_data; + return ResultSuccess; +} + +Result SaveDataController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space, + FileSys::SaveDataSpaceId space) { + auto save_data_space = factory->GetSaveDataSpaceDirectory(space); + if (save_data_space == nullptr) { + return FileSys::ERROR_ENTITY_NOT_FOUND; + } + + *out_save_data_space = save_data_space; + return ResultSuccess; +} + +FileSys::SaveDataSize SaveDataController::ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, + u128 user_id) { + const auto value = factory->ReadSaveDataSize(type, title_id, user_id); + + if (value.normal == 0 && value.journal == 0) { + const auto size = GetDefaultSaveDataSize(system, title_id); + factory->WriteSaveDataSize(type, title_id, user_id, size); + return size; + } + + return value; +} + +void SaveDataController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, + FileSys::SaveDataSize new_value) { + factory->WriteSaveDataSize(type, title_id, user_id, new_value); +} + +void SaveDataController::SetAutoCreate(bool state) { + factory->SetAutoCreate(state); +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/savedata_factory.h" +#include "core/file_sys/vfs_types.h" + +namespace Service::FileSystem { + +class SaveDataController { +public: + explicit SaveDataController(Core::System& system, + std::shared_ptr factory_); + ~SaveDataController(); + + Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space, + const FileSys::SaveDataAttribute& attribute); + Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space, + const FileSys::SaveDataAttribute& attribute); + Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space, + FileSys::SaveDataSpaceId space); + + FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id); + void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, + FileSys::SaveDataSize new_value); + void SetAutoCreate(bool state); + +private: + Core::System& system; + const std::shared_ptr factory; +}; + +} // namespace Service::FileSystem diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index c9f8707b7..1e599e78b 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -216,20 +216,6 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect LOG_DEBUG(Loader, "loaded module {} @ {:#X}", module, load_addr); } - // Find the RomFS by searching for a ".romfs" file in this directory - const auto& files = dir->GetFiles(); - const auto romfs_iter = - std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& f) { - return f->GetName().find(".romfs") != std::string::npos; - }); - - // Register the RomFS if a ".romfs" file was found - if (romfs_iter != files.end() && *romfs_iter != nullptr) { - romfs = *romfs_iter; - system.GetFileSystemController().RegisterRomFS(std::make_unique( - *this, system.GetContentProvider(), system.GetFileSystemController())); - } - is_loaded = true; return {ResultStatus::Success, 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 return load_result; } - system.GetFileSystemController().RegisterRomFS(std::make_unique( - *this, system.GetContentProvider(), system.GetFileSystemController())); + system.GetFileSystemController().RegisterProcess( + process.GetProcessId(), nca->GetTitleId(), + std::make_shared(*this, system.GetContentProvider(), + system.GetFileSystemController())); is_loaded = true; return load_result; diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index e74697cda..83371fcbd 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -276,8 +276,10 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::S } if (romfs != nullptr) { - system.GetFileSystemController().RegisterRomFS(std::make_unique( - *this, system.GetContentProvider(), system.GetFileSystemController())); + system.GetFileSystemController().RegisterProcess( + process.GetProcessId(), {}, + std::make_unique(*this, system.GetContentProvider(), + system.GetFileSystemController())); } is_loaded = true; 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 FileSys::VirtualFile update_raw; if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { - system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); + system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(), + std::move(update_raw)); } 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 FileSys::VirtualFile update_raw; if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { - system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); + system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(), + std::move(update_raw)); } is_loaded = true; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4f4c75f5c..29f47bf48 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2292,14 +2292,14 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target ASSERT(user_id); const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( - *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, + {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, program_id, user_id->AsU128(), 0); path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); } else { // Device save data const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath( - *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, + {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, program_id, {}, 0); path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path); @@ -2662,8 +2662,8 @@ void GMainWindow::RemoveCacheStorage(u64 program_id) { vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read); const auto cache_storage_path = FileSys::SaveDataFactory::GetFullPath( - *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, - FileSys::SaveDataType::CacheStorage, 0 /* program_id */, {}, 0); + {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::CacheStorage, + 0 /* program_id */, {}, 0); const auto path = Common::FS::ConcatPathSafe(nand_dir, cache_storage_path); From 2a0d707ce1d4880dfcbd34c4d6572917a501f675 Mon Sep 17 00:00:00 2001 From: Liam Date: Thu, 11 Jan 2024 16:50:59 -0500 Subject: [PATCH 19/33] shader_recompiler: emulate 8-bit and 16-bit storage writes with cas loop --- .../backend/spirv/emit_spirv_memory.cpp | 40 ++++++++++++--- .../backend/spirv/spirv_emit_context.cpp | 51 +++++++++++++++++++ .../backend/spirv/spirv_emit_context.h | 3 ++ 3 files changed, 86 insertions(+), 8 deletions(-) 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& WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32, sizeof(u32), &StorageDefinitions::U32, index_offset); } + +void WriteStorageByCasLoop(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, + Id value, Id bit_offset, Id bit_count) { + const Id pointer{StoragePointer(ctx, binding, offset, ctx.storage_types.U32, sizeof(u32), + &StorageDefinitions::U32)}; + ctx.OpFunctionCall(ctx.TypeVoid(), ctx.write_storage_cas_loop_func, pointer, value, bit_offset, + bit_count); +} } // Anonymous namespace void EmitLoadGlobalU8(EmitContext&) { @@ -219,26 +227,42 @@ Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Valu void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value) { - WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U8, value), ctx.storage_types.U8, - sizeof(u8), &StorageDefinitions::U8); + if (ctx.profile.support_int8) { + WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U8, value), ctx.storage_types.U8, + sizeof(u8), &StorageDefinitions::U8); + } else { + WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset8(offset), ctx.Const(8u)); + } } void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value) { - WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S8, value), ctx.storage_types.S8, - sizeof(s8), &StorageDefinitions::S8); + if (ctx.profile.support_int8) { + WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S8, value), ctx.storage_types.S8, + sizeof(s8), &StorageDefinitions::S8); + } else { + WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset8(offset), ctx.Const(8u)); + } } void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value) { - WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U16, value), ctx.storage_types.U16, - sizeof(u16), &StorageDefinitions::U16); + if (ctx.profile.support_int16) { + WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U16, value), ctx.storage_types.U16, + sizeof(u16), &StorageDefinitions::U16); + } else { + WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset16(offset), ctx.Const(16u)); + } } void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value) { - WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S16, value), ctx.storage_types.S16, - sizeof(s16), &StorageDefinitions::S16); + if (ctx.profile.support_int16) { + WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S16, value), ctx.storage_types.S16, + sizeof(s16), &StorageDefinitions::S16); + } else { + WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset16(offset), ctx.Const(16u)); + } } void 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 DefineTextures(program.info, texture_binding, bindings.texture_scaling_index); DefineImages(program.info, image_binding, bindings.image_scaling_index); DefineAttributeMemAccess(program.info); + DefineWriteStorageCasLoopFunction(program.info); DefineGlobalMemoryFunctions(program.info); DefineRescalingInput(program.info); DefineRenderArea(program.info); @@ -877,6 +878,56 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) { } } +void EmitContext::DefineWriteStorageCasLoopFunction(const Info& info) { + if (profile.support_int8 && profile.support_int16) { + return; + } + if (!info.uses_int8 && !info.uses_int16) { + return; + } + + AddCapability(spv::Capability::VariablePointersStorageBuffer); + + const Id ptr_type{TypePointer(spv::StorageClass::StorageBuffer, U32[1])}; + const Id func_type{TypeFunction(void_id, ptr_type, U32[1], U32[1], U32[1])}; + const Id func{OpFunction(void_id, spv::FunctionControlMask::MaskNone, func_type)}; + const Id pointer{OpFunctionParameter(ptr_type)}; + const Id value{OpFunctionParameter(U32[1])}; + const Id bit_offset{OpFunctionParameter(U32[1])}; + const Id bit_count{OpFunctionParameter(U32[1])}; + + AddLabel(); + const Id scope_device{Const(1u)}; + const Id ordering_relaxed{u32_zero_value}; + const Id body_label{OpLabel()}; + const Id continue_label{OpLabel()}; + const Id endloop_label{OpLabel()}; + const Id beginloop_label{OpLabel()}; + OpBranch(beginloop_label); + + AddLabel(beginloop_label); + OpLoopMerge(endloop_label, continue_label, spv::LoopControlMask::MaskNone); + OpBranch(body_label); + + AddLabel(body_label); + const Id expected_value{OpLoad(U32[1], pointer)}; + const Id desired_value{OpBitFieldInsert(U32[1], expected_value, value, bit_offset, bit_count)}; + const Id actual_value{OpAtomicCompareExchange(U32[1], pointer, scope_device, ordering_relaxed, + ordering_relaxed, desired_value, expected_value)}; + const Id store_successful{OpIEqual(U1, expected_value, actual_value)}; + OpBranchConditional(store_successful, endloop_label, continue_label); + + AddLabel(endloop_label); + OpReturn(); + + AddLabel(continue_label); + OpBranch(beginloop_label); + + OpFunctionEnd(); + + write_storage_cas_loop_func = func; +} + void EmitContext::DefineGlobalMemoryFunctions(const Info& info) { if (!info.uses_global_memory || !profile.support_int64) { 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: Id f32x2_min_cas{}; Id f32x2_max_cas{}; + Id write_storage_cas_loop_func{}; + Id load_global_func_u32{}; Id load_global_func_u32x2{}; Id load_global_func_u32x4{}; @@ -372,6 +374,7 @@ private: void DefineTextures(const Info& info, u32& binding, u32& scaling_index); void DefineImages(const Info& info, u32& binding, u32& scaling_index); void DefineAttributeMemAccess(const Info& info); + void DefineWriteStorageCasLoopFunction(const Info& info); void DefineGlobalMemoryFunctions(const Info& info); void DefineRescalingInput(const Info& info); void DefineRescalingInputPushConstant(); From 84787a2adaa58794ff72627ec2231da863b104c1 Mon Sep 17 00:00:00 2001 From: Liam Date: Thu, 11 Jan 2024 18:57:07 -0500 Subject: [PATCH 20/33] ci: fix file mode check in format script --- .ci/scripts/format/script.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/scripts/format/script.sh b/.ci/scripts/format/script.sh index 572fa9ffb..c22398de0 100755 --- a/.ci/scripts/format/script.sh +++ b/.ci/scripts/format/script.sh @@ -25,7 +25,7 @@ for f in $FILES_TO_LINT; do "$CLANG_FORMAT" -i "$f" done -DIFF=$(git diff) +DIFF=$(git -c core.fileMode=false diff) if [ ! -z "$DIFF" ]; then echo "!!! Not compliant to coding style, here is the fix:" From b5dac5f525e8d5884506ebd98a530e237b518480 Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 7 Jan 2024 09:05:12 -0600 Subject: [PATCH 21/33] service: hid: Create abstracted pad structure --- src/android/app/src/main/jni/native.cpp | 4 +- src/core/frontend/applets/controller.cpp | 2 +- src/core/hle/service/hid/hid_server.cpp | 2 +- src/hid_core/CMakeLists.txt | 34 ++ src/hid_core/frontend/emulated_controller.cpp | 22 +- src/hid_core/hid_types.h | 3 +- src/hid_core/hid_util.h | 2 +- .../abstract_battery_handler.cpp | 197 +++++++++++ .../abstracted_pad/abstract_battery_handler.h | 49 +++ .../abstract_button_handler.cpp | 199 +++++++++++ .../abstracted_pad/abstract_button_handler.h | 75 ++++ .../abstract_ir_sensor_handler.cpp | 126 +++++++ .../abstract_ir_sensor_handler.h | 56 +++ .../abstracted_pad/abstract_led_handler.cpp | 123 +++++++ .../abstracted_pad/abstract_led_handler.h | 43 +++ .../abstracted_pad/abstract_mcu_handler.cpp | 108 ++++++ .../abstracted_pad/abstract_mcu_handler.h | 52 +++ .../abstracted_pad/abstract_nfc_handler.cpp | 140 ++++++++ .../abstracted_pad/abstract_nfc_handler.h | 57 ++++ .../resources/abstracted_pad/abstract_pad.cpp | 294 ++++++++++++++++ .../resources/abstracted_pad/abstract_pad.h | 123 +++++++ .../abstracted_pad/abstract_pad_holder.cpp | 99 ++++++ .../abstracted_pad/abstract_pad_holder.h | 47 +++ .../abstracted_pad/abstract_palma_handler.cpp | 47 +++ .../abstracted_pad/abstract_palma_handler.h | 37 ++ .../abstract_properties_handler.cpp | 322 ++++++++++++++++++ .../abstract_properties_handler.h | 86 +++++ .../abstract_sixaxis_handler.cpp | 154 +++++++++ .../abstracted_pad/abstract_sixaxis_handler.h | 61 ++++ .../abstract_vibration_handler.cpp | 73 ++++ .../abstract_vibration_handler.h | 51 +++ src/hid_core/resources/npad/npad.cpp | 8 +- src/hid_core/resources/npad/npad_data.cpp | 2 +- src/hid_core/resources/npad/npad_types.h | 99 ++++++ .../resources/npad/npad_vibration.cpp | 80 +++++ src/hid_core/resources/npad/npad_vibration.h | 34 ++ src/hid_core/resources/six_axis/six_axis.cpp | 6 +- .../vibration/gc_vibration_device.cpp | 106 ++++++ .../resources/vibration/gc_vibration_device.h | 31 ++ .../vibration/n64_vibration_device.cpp | 80 +++++ .../vibration/n64_vibration_device.h | 29 ++ .../resources/vibration/vibration_base.cpp | 30 ++ .../resources/vibration/vibration_base.h | 28 ++ .../resources/vibration/vibration_device.cpp | 84 +++++ .../resources/vibration/vibration_device.h | 35 ++ src/yuzu/applets/qt_controller.cpp | 8 +- src/yuzu/applets/qt_software_keyboard.cpp | 2 +- .../configuration/configure_input_player.cpp | 18 +- .../configure_input_player_widget.cpp | 2 +- src/yuzu/main.cpp | 2 +- src/yuzu/util/controller_navigation.cpp | 4 +- 51 files changed, 3333 insertions(+), 43 deletions(-) create mode 100644 src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_battery_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_button_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_led_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_pad.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_pad.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_pad_holder.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_pad_holder.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_palma_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_properties_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h create mode 100644 src/hid_core/resources/npad/npad_vibration.cpp create mode 100644 src/hid_core/resources/npad/npad_vibration.h create mode 100644 src/hid_core/resources/vibration/gc_vibration_device.cpp create mode 100644 src/hid_core/resources/vibration/gc_vibration_device.h create mode 100644 src/hid_core/resources/vibration/n64_vibration_device.cpp create mode 100644 src/hid_core/resources/vibration/n64_vibration_device.h create mode 100644 src/hid_core/resources/vibration/vibration_base.cpp create mode 100644 src/hid_core/resources/vibration/vibration_base.h create mode 100644 src/hid_core/resources/vibration/vibration_device.cpp create mode 100644 src/hid_core/resources/vibration/vibration_device.h diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 136c8dee6..e436622e0 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) { jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { - handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); - controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey); + controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey); handheld->Disconnect(); } } 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 // Connect controllers based on the following priority list from highest to lowest priority: // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld if (parameters.allow_pro_controller) { - controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey); controller->Connect(true); } else if (parameters.allow_dual_joycons) { controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual); 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) { bool check_device_index = false; switch (vibration_device_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::Handheld: case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::JoyconLeft: 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 irsensor/processor_base.h irsensor/tera_plugin_processor.cpp irsensor/tera_plugin_processor.h + resources/abstracted_pad/abstract_battery_handler.cpp + resources/abstracted_pad/abstract_battery_handler.h + resources/abstracted_pad/abstract_button_handler.cpp + resources/abstracted_pad/abstract_button_handler.h + resources/abstracted_pad/abstract_ir_sensor_handler.cpp + resources/abstracted_pad/abstract_ir_sensor_handler.h + resources/abstracted_pad/abstract_led_handler.cpp + resources/abstracted_pad/abstract_led_handler.h + resources/abstracted_pad/abstract_mcu_handler.cpp + resources/abstracted_pad/abstract_mcu_handler.h + resources/abstracted_pad/abstract_nfc_handler.cpp + resources/abstracted_pad/abstract_nfc_handler.h + resources/abstracted_pad/abstract_pad.cpp + resources/abstracted_pad/abstract_pad.h + resources/abstracted_pad/abstract_pad_holder.cpp + resources/abstracted_pad/abstract_pad_holder.h + resources/abstracted_pad/abstract_palma_handler.cpp + resources/abstracted_pad/abstract_palma_handler.h + resources/abstracted_pad/abstract_properties_handler.cpp + resources/abstracted_pad/abstract_properties_handler.h + resources/abstracted_pad/abstract_sixaxis_handler.cpp + resources/abstracted_pad/abstract_sixaxis_handler.h + resources/abstracted_pad/abstract_vibration_handler.cpp + resources/abstracted_pad/abstract_vibration_handler.h resources/debug_pad/debug_pad.cpp resources/debug_pad/debug_pad.h resources/debug_pad/debug_pad_types.h @@ -56,6 +80,8 @@ add_library(hid_core STATIC resources/npad/npad_resource.cpp resources/npad/npad_resource.h resources/npad/npad_types.h + resources/npad/npad_vibration.cpp + resources/npad/npad_vibration.h resources/palma/palma.cpp resources/palma/palma.h resources/six_axis/console_six_axis.cpp @@ -78,6 +104,14 @@ add_library(hid_core STATIC resources/touch_screen/touch_types.h resources/unique_pad/unique_pad.cpp resources/unique_pad/unique_pad.h + resources/vibration/gc_vibration_device.h + resources/vibration/gc_vibration_device.cpp + resources/vibration/n64_vibration_device.h + resources/vibration/n64_vibration_device.cpp + resources/vibration/vibration_base.h + resources/vibration/vibration_base.cpp + resources/vibration/vibration_device.h + resources/vibration/vibration_device.cpp resources/applet_resource.cpp resources/applet_resource.h resources/controller_base.cpp diff --git a/src/hid_core/frontend/emulated_controller.cpp b/src/hid_core/frontend/emulated_controller.cpp index 3d2d1e9f9..a6a96935d 100644 --- a/src/hid_core/frontend/emulated_controller.cpp +++ b/src/hid_core/frontend/emulated_controller.cpp @@ -27,7 +27,7 @@ EmulatedController::~EmulatedController() = default; NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) { switch (type) { case Settings::ControllerType::ProController: - return NpadStyleIndex::ProController; + return NpadStyleIndex::Fullkey; case Settings::ControllerType::DualJoyconDetached: return NpadStyleIndex::JoyconDual; case Settings::ControllerType::LeftJoycon: @@ -49,13 +49,13 @@ NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerTyp case Settings::ControllerType::SegaGenesis: return NpadStyleIndex::SegaGenesis; default: - return NpadStyleIndex::ProController; + return NpadStyleIndex::Fullkey; } } Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) { switch (type) { - case NpadStyleIndex::ProController: + case NpadStyleIndex::Fullkey: return Settings::ControllerType::ProController; case NpadStyleIndex::JoyconDual: return Settings::ControllerType::DualJoyconDetached; @@ -106,7 +106,7 @@ void EmulatedController::ReloadFromSettings() { SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); original_npad_type = npad_type; } else { - SetNpadStyleIndex(NpadStyleIndex::ProController); + SetNpadStyleIndex(NpadStyleIndex::Fullkey); original_npad_type = npad_type; } @@ -1073,7 +1073,7 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback .body = GetNpadColor(controller.color_values[index].body), .button = GetNpadColor(controller.color_values[index].buttons), }; - if (npad_type == NpadStyleIndex::ProController) { + if (npad_type == NpadStyleIndex::Fullkey) { controller.colors_state.left = { .body = GetNpadColor(controller.color_values[index].left_grip), .button = GetNpadColor(controller.color_values[index].buttons), @@ -1356,7 +1356,7 @@ bool EmulatedController::HasNfc() const { switch (npad_type) { case NpadStyleIndex::JoyconRight: case NpadStyleIndex::JoyconDual: - case NpadStyleIndex::ProController: + case NpadStyleIndex::Fullkey: case NpadStyleIndex::Handheld: break; default: @@ -1548,7 +1548,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) // Fallback Fullkey controllers to Pro controllers if (IsControllerFullkey() && supported_style_tag.fullkey) { LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); - SetNpadStyleIndex(NpadStyleIndex::ProController); + SetNpadStyleIndex(NpadStyleIndex::Fullkey); Connect(); return; } @@ -1556,13 +1556,13 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) // Fallback Dual joycon controllers to Pro controllers if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) { LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); - SetNpadStyleIndex(NpadStyleIndex::ProController); + SetNpadStyleIndex(NpadStyleIndex::Fullkey); Connect(); return; } // Fallback Pro controllers to Dual joycon - if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) { + if (npad_type == NpadStyleIndex::Fullkey && supported_style_tag.joycon_dual) { LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type); SetNpadStyleIndex(NpadStyleIndex::JoyconDual); Connect(); @@ -1577,7 +1577,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const { std::scoped_lock lock{mutex}; const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; switch (type) { - case NpadStyleIndex::ProController: + case NpadStyleIndex::Fullkey: case NpadStyleIndex::GameCube: case NpadStyleIndex::NES: case NpadStyleIndex::SNES: @@ -1593,7 +1593,7 @@ bool EmulatedController::IsControllerSupported(bool use_temporary_value) const { std::scoped_lock lock{mutex}; const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; switch (type) { - case NpadStyleIndex::ProController: + case NpadStyleIndex::Fullkey: return supported_style_tag.fullkey.As(); case NpadStyleIndex::Handheld: return supported_style_tag.handheld.As(); 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 { }; enum class NpadInterfaceType : u8 { + None = 0, Bluetooth = 1, Rail = 2, Usb = 3, @@ -229,7 +230,7 @@ enum class NpadInterfaceType : u8 { // This is nn::hid::NpadStyleIndex enum class NpadStyleIndex : u8 { None = 0, - ProController = 3, + Fullkey = 3, Handheld = 4, HandheldNES = 4, 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 constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle& handle) { switch (handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::Handheld: case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::JoyconLeft: 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/core_timing.h" +#include "hid_core/hid_result.h" +#include "hid_core/hid_util.h" +#include "hid_core/resources/abstracted_pad/abstract_battery_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/shared_memory_format.h" + +namespace Service::HID { + +NpadAbstractBatteryHandler::NpadAbstractBatteryHandler() {} + +NpadAbstractBatteryHandler::~NpadAbstractBatteryHandler() = default; + +void NpadAbstractBatteryHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractBatteryHandler::SetAppletResource(AppletResourceHolder* applet_resource) { + applet_resource_holder = applet_resource; +} + +void NpadAbstractBatteryHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +Result NpadAbstractBatteryHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractBatteryHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +Result NpadAbstractBatteryHandler::UpdateBatteryState(u64 aruid) { + const auto npad_index = NpadIdTypeToIndex(properties_handler->GetNpadId()); + AruidData* aruid_data = applet_resource_holder->applet_resource->GetAruidData(aruid); + if (aruid_data == nullptr) { + return ResultSuccess; + } + + auto& npad_internal_state = + aruid_data->shared_memory_format->npad.npad_entry[npad_index].internal_state; + auto& system_properties = npad_internal_state.system_properties; + + system_properties.is_charging_joy_dual.Assign(dual_battery.is_charging); + system_properties.is_powered_joy_dual.Assign(dual_battery.is_powered); + system_properties.is_charging_joy_left.Assign(left_battery.is_charging); + system_properties.is_powered_joy_left.Assign(left_battery.is_powered); + system_properties.is_charging_joy_right.Assign(right_battery.is_charging); + system_properties.is_powered_joy_right.Assign(right_battery.is_powered); + + npad_internal_state.battery_level_dual = dual_battery.battery_level; + npad_internal_state.battery_level_left = left_battery.battery_level; + npad_internal_state.battery_level_right = right_battery.battery_level; + + return ResultSuccess; +} + +void NpadAbstractBatteryHandler::UpdateBatteryState() { + if (ref_counter == 0) { + return; + } + has_new_battery_data = GetNewBatteryState(); +} + +bool NpadAbstractBatteryHandler::GetNewBatteryState() { + bool has_changed = false; + Core::HID::NpadPowerInfo new_dual_battery_state{}; + Core::HID::NpadPowerInfo new_left_battery_state{}; + Core::HID::NpadPowerInfo new_right_battery_state{}; + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + const auto power_info = abstract_pad->power_info; + if (power_info.battery_level > Core::HID::NpadBatteryLevel::Full) { + // Abort + continue; + } + + const auto style = abstract_pad->assignment_style; + + if (style.is_external_assigned || style.is_handheld_assigned) { + new_dual_battery_state = power_info; + } + if (style.is_external_left_assigned || style.is_handheld_left_assigned) { + new_left_battery_state = power_info; + } + if (style.is_external_right_assigned || style.is_handheld_right_assigned) { + new_right_battery_state = power_info; + } + + if (abstract_pad->internal_flags.is_battery_low_ovln_required) { + if (abstract_pad->interface_type == Core::HID::NpadInterfaceType::Rail) { + // TODO + } + abstract_pad->internal_flags.is_battery_low_ovln_required.Assign(false); + } + } + + if (dual_battery.battery_level != new_dual_battery_state.battery_level || + dual_battery.is_charging != new_dual_battery_state.is_charging || + dual_battery.is_powered != new_dual_battery_state.is_powered) { + has_changed = true; + dual_battery = new_dual_battery_state; + } + + if (left_battery.battery_level != new_left_battery_state.battery_level || + left_battery.is_charging != new_left_battery_state.is_charging || + left_battery.is_powered != new_left_battery_state.is_powered) { + has_changed = true; + left_battery = new_left_battery_state; + } + + if (right_battery.battery_level != new_right_battery_state.battery_level || + right_battery.is_charging != new_right_battery_state.is_charging || + right_battery.is_powered != new_right_battery_state.is_powered) { + has_changed = true; + right_battery = new_right_battery_state; + } + + return has_changed; +} + +void NpadAbstractBatteryHandler::UpdateCoreBatteryState() { + if (ref_counter == 0) { + return; + } + if (!has_new_battery_data) { + return; + } + + UpdateBatteryState(0); +} + +void NpadAbstractBatteryHandler::InitializeBatteryState(u64 aruid) { + UpdateBatteryState(aruid); +} + +bool NpadAbstractBatteryHandler::HasBattery() const { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + for (std::size_t i = 0; i < count; i++) { + const auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + return abstract_pad->disabled_feature_set.has_fullkey_battery || + abstract_pad->disabled_feature_set.has_left_right_joy_battery; + } + + return false; +} + +void NpadAbstractBatteryHandler::HasLeftRightBattery(bool& has_left, bool& has_right) const { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + has_left = false; + has_right = false; + + for (std::size_t i = 0; i < count; i++) { + const auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (!abstract_pad->disabled_feature_set.has_fullkey_battery && + !abstract_pad->disabled_feature_set.has_left_right_joy_battery) { + continue; + } + has_left = abstract_pad->assignment_style.is_external_left_assigned || + abstract_pad->assignment_style.is_handheld_left_assigned; + has_right = abstract_pad->assignment_style.is_external_right_assigned || + abstract_pad->assignment_style.is_handheld_right_assigned; + } +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Service::HID { +struct AppletResourceHolder; +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; + +/// Handles Npad request from HID interfaces +class NpadAbstractBatteryHandler final { +public: + explicit NpadAbstractBatteryHandler(); + ~NpadAbstractBatteryHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetAppletResource(AppletResourceHolder* applet_resource); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + Result UpdateBatteryState(u64 aruid); + void UpdateBatteryState(); + bool GetNewBatteryState(); + void UpdateCoreBatteryState(); + void InitializeBatteryState(u64 aruid); + + bool HasBattery() const; + void HasLeftRightBattery(bool& has_left, bool& has_right) const; + +private: + AppletResourceHolder* applet_resource_holder{nullptr}; + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + + s32 ref_counter{}; + Core::HID::NpadPowerInfo dual_battery{}; + Core::HID::NpadPowerInfo left_battery{}; + Core::HID::NpadPowerInfo right_battery{}; + bool has_new_battery_data{}; +}; + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/hid_util.h" +#include "hid_core/resources/abstracted_pad/abstract_button_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_resource.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/shared_memory_format.h" + +namespace Service::HID { + +NpadAbstractButtonHandler::NpadAbstractButtonHandler() {} + +NpadAbstractButtonHandler::~NpadAbstractButtonHandler() = default; + +void NpadAbstractButtonHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractButtonHandler::SetAppletResource(AppletResourceHolder* applet_resource) { + applet_resource_holder = applet_resource; +} + +void NpadAbstractButtonHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +Result NpadAbstractButtonHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractButtonHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +Result NpadAbstractButtonHandler::UpdateAllButtonWithHomeProtection(u64 aruid) { + const Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); + auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid); + + if (data == nullptr) { + return ResultSuccess; + } + + auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; + UpdateButtonLifo(npad_entry, aruid); + + bool is_home_button_protection_enabled{}; + const auto result = applet_resource_holder->shared_npad_resource->GetHomeProtectionEnabled( + is_home_button_protection_enabled, aruid, npad_id); + + if (result.IsError()) { + return ResultSuccess; + } + + npad_entry.internal_state.button_properties.is_home_button_protection_enabled.Assign( + is_home_button_protection_enabled); + + return ResultSuccess; +} + +void NpadAbstractButtonHandler::UpdateAllButtonLifo() { + Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); + for (std::size_t i = 0; i < AruidIndexMax; i++) { + auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); + auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; + UpdateButtonLifo(npad_entry, data->aruid); + } +} + +void NpadAbstractButtonHandler::UpdateCoreBatteryState() { + Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); + for (std::size_t i = 0; i < AruidIndexMax; i++) { + auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); + auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; + UpdateButtonLifo(npad_entry, data->aruid); + } +} + +void NpadAbstractButtonHandler::UpdateButtonState(u64 aruid) { + Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); + auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid); + if (data == nullptr) { + return; + } + auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; + UpdateButtonLifo(npad_entry, aruid); +} + +Result NpadAbstractButtonHandler::SetHomeProtection(bool is_enabled, u64 aruid) { + const Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); + auto result = applet_resource_holder->shared_npad_resource->SetHomeProtectionEnabled( + aruid, npad_id, is_enabled); + if (result.IsError()) { + return result; + } + + auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid); + if (data == nullptr) { + return ResultSuccess; + } + + bool is_home_protection_enabled{}; + result = applet_resource_holder->shared_npad_resource->GetHomeProtectionEnabled( + is_home_protection_enabled, aruid, npad_id); + if (result.IsError()) { + return ResultSuccess; + } + + auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; + npad_entry.internal_state.button_properties.is_home_button_protection_enabled.Assign( + is_home_protection_enabled); + return ResultSuccess; +} + +bool NpadAbstractButtonHandler::IsButtonPressedOnConsoleMode() { + return is_button_pressed_on_console_mode; +} + +void NpadAbstractButtonHandler::EnableCenterClamp() { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + abstract_pad->internal_flags.use_center_clamp.Assign(true); + } +} + +void NpadAbstractButtonHandler::UpdateButtonLifo(NpadSharedMemoryEntry& shared_memory, u64 aruid) { + auto* npad_resource = applet_resource_holder->shared_npad_resource; + Core::HID::NpadStyleTag style_tag = {properties_handler->GetStyleSet(aruid)}; + style_tag.system_ext.Assign(npad_resource->GetActiveData()->GetNpadSystemExtState()); + + UpdateNpadFullkeyLifo(style_tag, 0, aruid, shared_memory); + UpdateHandheldLifo(style_tag, 1, aruid, shared_memory); + UpdateJoyconDualLifo(style_tag, 2, aruid, shared_memory); + UpdateJoyconLeftLifo(style_tag, 3, aruid, shared_memory); + UpdateJoyconRightLifo(style_tag, 4, aruid, shared_memory); + UpdatePalmaLifo(style_tag, 5, aruid, shared_memory); + UpdateSystemExtLifo(style_tag, 6, aruid, shared_memory); +} + +void NpadAbstractButtonHandler::UpdateNpadFullkeyLifo(Core::HID::NpadStyleTag style_tag, + int style_index, u64 aruid, + NpadSharedMemoryEntry& shared_memory) { + // TODO +} + +void NpadAbstractButtonHandler::UpdateHandheldLifo(Core::HID::NpadStyleTag style_tag, + int style_index, u64 aruid, + NpadSharedMemoryEntry& shared_memory) { + // TODO +} + +void NpadAbstractButtonHandler::UpdateJoyconDualLifo(Core::HID::NpadStyleTag style_tag, + int style_index, u64 aruid, + NpadSharedMemoryEntry& shared_memory) { + // TODO +} + +void NpadAbstractButtonHandler::UpdateJoyconLeftLifo(Core::HID::NpadStyleTag style_tag, + int style_index, u64 aruid, + NpadSharedMemoryEntry& shared_memory) { + // TODO +} + +void NpadAbstractButtonHandler::UpdateJoyconRightLifo(Core::HID::NpadStyleTag style_tag, + int style_index, u64 aruid, + NpadSharedMemoryEntry& shared_memory) { + // TODO +} + +void NpadAbstractButtonHandler::UpdateSystemExtLifo(Core::HID::NpadStyleTag style_tag, + int style_index, u64 aruid, + NpadSharedMemoryEntry& shared_memory) { + // TODO +} + +void NpadAbstractButtonHandler::UpdatePalmaLifo(Core::HID::NpadStyleTag style_tag, int style_index, + u64 aruid, NpadSharedMemoryEntry& shared_memory) { + // TODO +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Service::HID { +struct NpadSharedMemoryEntry; + +struct AppletResourceHolder; +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; + +/// Handles Npad request from HID interfaces +class NpadAbstractButtonHandler final { +public: + explicit NpadAbstractButtonHandler(); + ~NpadAbstractButtonHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetAppletResource(AppletResourceHolder* applet_resource); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + Result UpdateAllButtonWithHomeProtection(u64 aruid); + + void UpdateAllButtonLifo(); + void UpdateCoreBatteryState(); + void UpdateButtonState(u64 aruid); + + Result SetHomeProtection(bool is_enabled, u64 aruid); + bool IsButtonPressedOnConsoleMode(); + void EnableCenterClamp(); + + void UpdateButtonLifo(NpadSharedMemoryEntry& shared_memory, u64 aruid); + + void UpdateNpadFullkeyLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid, + NpadSharedMemoryEntry& shared_memory); + void UpdateHandheldLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid, + NpadSharedMemoryEntry& shared_memory); + void UpdateJoyconDualLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid, + NpadSharedMemoryEntry& shared_memory); + void UpdateJoyconLeftLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid, + NpadSharedMemoryEntry& shared_memory); + void UpdateJoyconRightLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid, + NpadSharedMemoryEntry& shared_memory); + void UpdateSystemExtLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid, + NpadSharedMemoryEntry& shared_memory); + void UpdatePalmaLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid, + NpadSharedMemoryEntry& shared_memory); + +private: + struct GcTrigger { + float left; + float right; + }; + + AppletResourceHolder* applet_resource_holder{nullptr}; + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + + s32 ref_counter{}; + + bool is_button_pressed_on_console_mode{}; + + u64 gc_sampling_number{}; + GcTrigger gc_trigger_state{}; +}; + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "hid_core/hid_result.h" +#include "hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { + +NpadAbstractIrSensorHandler::NpadAbstractIrSensorHandler() {} + +NpadAbstractIrSensorHandler::~NpadAbstractIrSensorHandler() = default; + +void NpadAbstractIrSensorHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractIrSensorHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +Result NpadAbstractIrSensorHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractIrSensorHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +void NpadAbstractIrSensorHandler::UpdateIrSensorState() { + const auto previous_state = sensor_state; + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + if (count == 0) { + sensor_state = NpadIrSensorState::Disabled; + if (sensor_state == previous_state) { + return; + } + ir_sensor_event->Signal(); + return; + } + + bool is_found{}; + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (!abstract_pad->disabled_feature_set.has_bluetooth_address) { + continue; + } + is_found = true; + xcd_handle = abstract_pad->xcd_handle; + } + + if (is_found) { + if (sensor_state == NpadIrSensorState::Active) { + return; + } + sensor_state = NpadIrSensorState::Available; + if (sensor_state == previous_state) { + return; + } + ir_sensor_event->Signal(); + return; + } + + sensor_state = NpadIrSensorState::Unavailable; + if (sensor_state == previous_state) { + return; + } + + ir_sensor_event->Signal(); + return; +} + +Result NpadAbstractIrSensorHandler::ActivateIrSensor(bool is_enabled) { + if (sensor_state == NpadIrSensorState::Unavailable) { + return ResultIrSensorIsNotReady; + } + if (is_enabled && sensor_state == NpadIrSensorState::Available) { + sensor_state = NpadIrSensorState::Active; + } else { + if (is_enabled) { + return ResultSuccess; + } + if (sensor_state != NpadIrSensorState::Active) { + return ResultSuccess; + } + sensor_state = NpadIrSensorState::Available; + } + ir_sensor_event->Signal(); + return ResultSuccess; +} + +Result NpadAbstractIrSensorHandler::GetIrSensorEventHandle(Kernel::KReadableEvent** out_event) { + *out_event = &ir_sensor_event->GetReadableEvent(); + return ResultSuccess; +} + +Result NpadAbstractIrSensorHandler::GetXcdHandleForNpadWithIrSensor(u64& handle) const { + if (sensor_state < NpadIrSensorState::Available) { + return ResultIrSensorIsNotReady; + } + handle = xcd_handle; + return ResultSuccess; +} + +NpadIrSensorState NpadAbstractIrSensorHandler::GetSensorState() const { + return sensor_state; +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +enum class NpadIrSensorState : u32 { + Disabled, + Unavailable, + Available, + Active, +}; + +namespace Service::HID { +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; + +/// Handles Npad request from HID interfaces +class NpadAbstractIrSensorHandler final { +public: + explicit NpadAbstractIrSensorHandler(); + ~NpadAbstractIrSensorHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + void UpdateIrSensorState(); + Result ActivateIrSensor(bool param_2); + + Result GetIrSensorEventHandle(Kernel::KReadableEvent** out_event); + + Result GetXcdHandleForNpadWithIrSensor(u64& handle) const; + + NpadIrSensorState GetSensorState() const; + +private: + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + + s32 ref_counter{}; + Kernel::KEvent* ir_sensor_event{nullptr}; + u64 xcd_handle{}; + NpadIrSensorState sensor_state{}; +}; +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/core_timing.h" +#include "hid_core/hid_result.h" +#include "hid_core/hid_util.h" +#include "hid_core/resources/abstracted_pad/abstract_led_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { + +NpadAbstractLedHandler::NpadAbstractLedHandler() {} + +NpadAbstractLedHandler::~NpadAbstractLedHandler() = default; + +void NpadAbstractLedHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractLedHandler::SetAppletResource(AppletResourceHolder* applet_resource) { + applet_resource_holder = applet_resource; +} + +void NpadAbstractLedHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +Result NpadAbstractLedHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractLedHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +void NpadAbstractLedHandler::SetNpadLedHandlerLedPattern() { + const auto npad_id = properties_handler->GetNpadId(); + + switch (npad_id) { + case Core::HID::NpadIdType::Player1: + left_pattern = Core::HID::LedPattern{1, 0, 0, 0}; + break; + case Core::HID::NpadIdType::Player2: + left_pattern = Core::HID::LedPattern{1, 1, 0, 0}; + break; + case Core::HID::NpadIdType::Player3: + left_pattern = Core::HID::LedPattern{1, 1, 1, 0}; + break; + case Core::HID::NpadIdType::Player4: + left_pattern = Core::HID::LedPattern{1, 1, 1, 1}; + break; + case Core::HID::NpadIdType::Player5: + left_pattern = Core::HID::LedPattern{1, 0, 0, 1}; + break; + case Core::HID::NpadIdType::Player6: + left_pattern = Core::HID::LedPattern{1, 0, 1, 0}; + break; + case Core::HID::NpadIdType::Player7: + left_pattern = Core::HID::LedPattern{1, 0, 1, 1}; + break; + case Core::HID::NpadIdType::Player8: + left_pattern = Core::HID::LedPattern{0, 1, 1, 0}; + break; + case Core::HID::NpadIdType::Other: + case Core::HID::NpadIdType::Handheld: + left_pattern = Core::HID::LedPattern{0, 0, 0, 0}; + break; + default: + ASSERT_MSG(false, "Invalid npad id type"); + break; + } + + switch (npad_id) { + case Core::HID::NpadIdType::Player1: + right_pattern = Core::HID::LedPattern{0, 0, 0, 1}; + break; + case Core::HID::NpadIdType::Player2: + right_pattern = Core::HID::LedPattern{0, 1, 1, 1}; + break; + case Core::HID::NpadIdType::Player3: + right_pattern = Core::HID::LedPattern{0, 1, 1, 1}; + break; + case Core::HID::NpadIdType::Player4: + right_pattern = Core::HID::LedPattern{1, 1, 1, 1}; + break; + case Core::HID::NpadIdType::Player5: + right_pattern = Core::HID::LedPattern{1, 0, 0, 1}; + break; + case Core::HID::NpadIdType::Player6: + right_pattern = Core::HID::LedPattern{0, 1, 0, 1}; + break; + case Core::HID::NpadIdType::Player7: + right_pattern = Core::HID::LedPattern{1, 1, 0, 1}; + break; + case Core::HID::NpadIdType::Player8: + right_pattern = Core::HID::LedPattern{0, 1, 1, 0}; + break; + case Core::HID::NpadIdType::Other: + case Core::HID::NpadIdType::Handheld: + right_pattern = Core::HID::LedPattern{0, 0, 0, 0}; + break; + default: + ASSERT_MSG(false, "Invalid npad id type"); + break; + } +} + +void NpadAbstractLedHandler::SetLedBlinkingDevice(Core::HID::LedPattern pattern) { + led_blinking = pattern; +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Service::HID { +struct AppletResourceHolder; +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; + +/// Handles Npad request from HID interfaces +class NpadAbstractLedHandler final { +public: + explicit NpadAbstractLedHandler(); + ~NpadAbstractLedHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetAppletResource(AppletResourceHolder* applet_resource); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + void SetNpadLedHandlerLedPattern(); + + void SetLedBlinkingDevice(Core::HID::LedPattern pattern); + +private: + AppletResourceHolder* applet_resource_holder{nullptr}; + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + + s32 ref_counter{}; + Core::HID::LedPattern led_blinking{0, 0, 0, 0}; + Core::HID::LedPattern left_pattern{0, 0, 0, 0}; + Core::HID::LedPattern right_pattern{0, 0, 0, 0}; + u64 led_interval{}; +}; +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/abstracted_pad/abstract_mcu_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { + +NpadAbstractMcuHandler::NpadAbstractMcuHandler() {} + +NpadAbstractMcuHandler::~NpadAbstractMcuHandler() = default; + +void NpadAbstractMcuHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractMcuHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +Result NpadAbstractMcuHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractMcuHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +void NpadAbstractMcuHandler::UpdateMcuState() { + std::array abstract_pads{}; + const std::size_t count = properties_handler->GetAbstractedPads(abstract_pads); + + if (count == 0) { + mcu_holder = {}; + return; + } + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (!abstract_pad->disabled_feature_set.has_left_joy_rail_bus) { + if (!abstract_pad->disabled_feature_set.has_left_joy_six_axis_sensor && + !abstract_pad->disabled_feature_set.has_right_joy_six_axis_sensor) { + continue; + } + if (mcu_holder[1].state != NpadMcuState::Active) { + mcu_holder[1].state = NpadMcuState::Available; + } + mcu_holder[1].abstracted_pad = abstract_pad; + continue; + } + if (mcu_holder[0].state != NpadMcuState::Active) { + mcu_holder[0].state = NpadMcuState::Available; + } + mcu_holder[0].abstracted_pad = abstract_pad; + } +} + +Result NpadAbstractMcuHandler::GetAbstractedPad(IAbstractedPad** data, u32 mcu_index) { + if (mcu_holder[mcu_index].state == NpadMcuState::None || + mcu_holder[mcu_index].abstracted_pad == nullptr) { + return ResultMcuIsNotReady; + } + *data = mcu_holder[mcu_index].abstracted_pad; + return ResultSuccess; +} + +NpadMcuState NpadAbstractMcuHandler::GetMcuState(u32 mcu_index) { + return mcu_holder[mcu_index].state; +} + +Result NpadAbstractMcuHandler::SetMcuState(bool is_enabled, u32 mcu_index) { + NpadMcuState& state = mcu_holder[mcu_index].state; + + if (state == NpadMcuState::None) { + return ResultMcuIsNotReady; + } + + if ((is_enabled) && (state == NpadMcuState::Available)) { + state = NpadMcuState::Active; + return ResultSuccess; + } + + if (is_enabled) { + return ResultSuccess; + } + if (state != NpadMcuState::Active) { + return ResultSuccess; + } + + state = NpadMcuState::Available; + return ResultSuccess; +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Service::HID { +struct IAbstractedPad; +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; + +enum class NpadMcuState : u32 { + None, + Available, + Active, +}; + +struct NpadMcuHolder { + NpadMcuState state; + INSERT_PADDING_BYTES(0x4); + IAbstractedPad* abstracted_pad; +}; +static_assert(sizeof(NpadMcuHolder) == 0x10, "NpadMcuHolder is an invalid size"); + +/// Handles Npad request from HID interfaces +class NpadAbstractMcuHandler final { +public: + explicit NpadAbstractMcuHandler(); + ~NpadAbstractMcuHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + void UpdateMcuState(); + Result GetAbstractedPad(IAbstractedPad** data, u32 mcu_index); + NpadMcuState GetMcuState(u32 mcu_index); + Result SetMcuState(bool is_enabled, u32 mcu_index); + +private: + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + + s32 ref_counter{}; + std::array mcu_holder{}; +}; +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "hid_core/hid_result.h" +#include "hid_core/resources/abstracted_pad/abstract_nfc_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { + +NpadAbstractNfcHandler::NpadAbstractNfcHandler() {} + +NpadAbstractNfcHandler::~NpadAbstractNfcHandler() = default; + +void NpadAbstractNfcHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractNfcHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +Result NpadAbstractNfcHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractNfcHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +void NpadAbstractNfcHandler::UpdateNfcState() { + std::array abstract_pads{}; + const std::size_t count = properties_handler->GetAbstractedPads(abstract_pads); + + if (count == 0) { + if (sensor_state == NpadNfcState::Active) { + nfc_activate_event->Signal(); + } + if (sensor_state == NpadNfcState::Unavailable) { + return; + } + sensor_state = NpadNfcState::Unavailable; + input_event->Signal(); + return; + } + + bool is_found{}; + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (!abstract_pad->disabled_feature_set.has_nfc) { + continue; + } + is_found = true; + xcd_handle = 0; + } + + if (is_found) { + if (sensor_state == NpadNfcState::Active) { + return; + } + if (sensor_state == NpadNfcState::Available) { + return; + } + sensor_state = NpadNfcState::Available; + input_event->Signal(); + return; + } + + if (sensor_state == NpadNfcState::Active) { + nfc_activate_event->Signal(); + } + if (sensor_state == NpadNfcState::Unavailable) { + return; + } + sensor_state = NpadNfcState::Unavailable; + input_event->Signal(); + return; +} + +bool NpadAbstractNfcHandler::HasNfcSensor() { + return sensor_state != NpadNfcState::Unavailable; +} + +bool NpadAbstractNfcHandler::IsNfcActivated() { + return sensor_state == NpadNfcState::Active; +} + +Result NpadAbstractNfcHandler::GetAcquireNfcActivateEventHandle( + Kernel::KReadableEvent** out_event) { + *out_event = &nfc_activate_event->GetReadableEvent(); + return ResultSuccess; +} + +void NpadAbstractNfcHandler::SetInputEvent(Kernel::KEvent* event) { + input_event = event; +} + +Result NpadAbstractNfcHandler::ActivateNfc(bool is_enabled) { + if (sensor_state == NpadNfcState::Active) { + return ResultNfcIsNotReady; + } + + NpadNfcState new_state = NpadNfcState::Available; + if (is_enabled) { + new_state = NpadNfcState::Active; + } + if (sensor_state != new_state) { + sensor_state = new_state; + nfc_activate_event->Signal(); + } + return ResultSuccess; +} + +Result NpadAbstractNfcHandler::GetXcdHandleWithNfc(u64& out_xcd_handle) const { + if (sensor_state == NpadNfcState::Unavailable) { + return ResultNfcIsNotReady; + } + if (xcd_handle == 0) { + return ResultNfcXcdHandleIsNotInitialized; + } + + out_xcd_handle = xcd_handle; + return ResultSuccess; +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Kernel { +class KReadableEvent; +} + +enum class NpadNfcState : u32 { + Unavailable, + Available, + Active, +}; + +namespace Service::HID { +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; + +/// Handles Npad request from HID interfaces +class NpadAbstractNfcHandler final { +public: + explicit NpadAbstractNfcHandler(); + ~NpadAbstractNfcHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + void UpdateNfcState(); + bool HasNfcSensor(); + bool IsNfcActivated(); + + Result GetAcquireNfcActivateEventHandle(Kernel::KReadableEvent** out_event); + void SetInputEvent(Kernel::KEvent* event); + + Result ActivateNfc(bool is_enabled); + + Result GetXcdHandleWithNfc(u64& out_xcd_handle) const; + +private: + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + + s32 ref_counter{}; + Kernel::KEvent* nfc_activate_event{nullptr}; + Kernel::KEvent* input_event{nullptr}; + u64 xcd_handle{}; + NpadNfcState sensor_state{NpadNfcState::Unavailable}; +}; +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/abstracted_pad/abstract_pad.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { + +AbstractPad::AbstractPad() {} + +AbstractPad::~AbstractPad() = default; + +void AbstractPad::SetExternals(AppletResourceHolder* applet_resource, + CaptureButtonResource* capture_button_resource, + HomeButtonResource* home_button_resource, + SixAxisResource* sixaxis_resource, PalmaResource* palma_resource, + VibrationHandler* vibration) { + applet_resource_holder = applet_resource; + + properties_handler.SetAppletResource(applet_resource_holder); + properties_handler.SetAbstractPadHolder(&abstract_pad_holder); + + led_handler.SetAppletResource(applet_resource_holder); + led_handler.SetAbstractPadHolder(&abstract_pad_holder); + led_handler.SetPropertiesHandler(&properties_handler); + + ir_sensor_handler.SetAbstractPadHolder(&abstract_pad_holder); + ir_sensor_handler.SetPropertiesHandler(&properties_handler); + + nfc_handler.SetAbstractPadHolder(&abstract_pad_holder); + nfc_handler.SetPropertiesHandler(&properties_handler); + + mcu_handler.SetAbstractPadHolder(&abstract_pad_holder); + mcu_handler.SetPropertiesHandler(&properties_handler); + + std::array vibration_devices{&vibration_left, &vibration_right}; + vibration_handler.SetAppletResource(applet_resource_holder); + vibration_handler.SetAbstractPadHolder(&abstract_pad_holder); + vibration_handler.SetPropertiesHandler(&properties_handler); + vibration_handler.SetN64Vibration(&vibration_n64); + vibration_handler.SetVibration(vibration_devices); + vibration_handler.SetGcVibration(&vibration_gc); + + sixaxis_handler.SetAppletResource(applet_resource_holder); + sixaxis_handler.SetAbstractPadHolder(&abstract_pad_holder); + sixaxis_handler.SetPropertiesHandler(&properties_handler); + sixaxis_handler.SetSixaxisResource(sixaxis_resource); + + button_handler.SetAppletResource(applet_resource_holder); + button_handler.SetAbstractPadHolder(&abstract_pad_holder); + button_handler.SetPropertiesHandler(&properties_handler); + + battery_handler.SetAppletResource(applet_resource_holder); + battery_handler.SetAbstractPadHolder(&abstract_pad_holder); + battery_handler.SetPropertiesHandler(&properties_handler); + + palma_handler.SetAbstractPadHolder(&abstract_pad_holder); + palma_handler.SetPropertiesHandler(&properties_handler); + palma_handler.SetPalmaResource(palma_resource); +} + +void AbstractPad::SetNpadId(Core::HID::NpadIdType npad_id) { + properties_handler.SetNpadId(npad_id); +} + +Result AbstractPad::Activate() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + + if (ref_counter != 0) { + ref_counter++; + return ResultSuccess; + } + + std::size_t stage = 0; + Result result = ResultSuccess; + + if (result.IsSuccess()) { + stage++; + result = properties_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = led_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = ir_sensor_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = mcu_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = nfc_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = vibration_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = sixaxis_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = button_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = battery_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = palma_handler.IncrementRefCounter(); + } + + if (result.IsSuccess()) { + ref_counter++; + return result; + } + + if (stage > 9) { + battery_handler.DecrementRefCounter(); + } + if (stage > 8) { + button_handler.DecrementRefCounter(); + } + if (stage > 7) { + sixaxis_handler.DecrementRefCounter(); + } + if (stage > 6) { + vibration_handler.DecrementRefCounter(); + } + if (stage > 5) { + nfc_handler.DecrementRefCounter(); + } + if (stage > 4) { + mcu_handler.DecrementRefCounter(); + } + if (stage > 3) { + ir_sensor_handler.DecrementRefCounter(); + } + if (stage > 2) { + led_handler.DecrementRefCounter(); + } + if (stage > 1) { + properties_handler.DecrementRefCounter(); + } + return result; +} + +Result AbstractPad::Deactivate() { + if (ref_counter == 0) { + return ResultNpadResourceNotInitialized; + } + + ref_counter--; + battery_handler.DecrementRefCounter(); + button_handler.DecrementRefCounter(); + sixaxis_handler.DecrementRefCounter(); + vibration_handler.DecrementRefCounter(); + nfc_handler.DecrementRefCounter(); + ir_sensor_handler.DecrementRefCounter(); + mcu_handler.DecrementRefCounter(); + led_handler.DecrementRefCounter(); + properties_handler.DecrementRefCounter(); + palma_handler.DecrementRefCounter(); + + return ResultSuccess; +} + +Result AbstractPad::ActivateNpad(u64 aruid) { + Result result = ResultSuccess; + if (result.IsSuccess()) { + result = properties_handler.ActivateNpadUnknown0x88(aruid); + } + if (result.IsSuccess()) { + result = sixaxis_handler.UpdateSixAxisState2(aruid); + } + if (result.IsSuccess()) { + result = battery_handler.UpdateBatteryState(aruid); + } + return result; +} + +NpadAbstractedPadHolder* AbstractPad::GetAbstractedPadHolder() { + return &abstract_pad_holder; +} + +NpadAbstractPropertiesHandler* AbstractPad::GetAbstractPropertiesHandler() { + return &properties_handler; +} + +NpadAbstractLedHandler* AbstractPad::GetAbstractLedHandler() { + return &led_handler; +} + +NpadAbstractIrSensorHandler* AbstractPad::GetAbstractIrSensorHandler() { + return &ir_sensor_handler; +} + +NpadAbstractMcuHandler* AbstractPad::GetAbstractMcuHandler() { + return &mcu_handler; +} + +NpadAbstractNfcHandler* AbstractPad::GetAbstractNfcHandler() { + return &nfc_handler; +} + +NpadAbstractVibrationHandler* AbstractPad::GetAbstractVibrationHandler() { + return &vibration_handler; +} + +NpadAbstractSixAxisHandler* AbstractPad::GetAbstractSixAxisHandler() { + return &sixaxis_handler; +} + +NpadAbstractButtonHandler* AbstractPad::GetAbstractButtonHandler() { + return &button_handler; +} + +NpadAbstractBatteryHandler* AbstractPad::GetAbstractBatteryHandler() { + return &battery_handler; +} + +NpadN64VibrationDevice* AbstractPad::GetN64VibrationDevice() { + return &vibration_n64; +} + +NpadVibrationDevice* AbstractPad::GetVibrationDevice(Core::HID::DeviceIndex device_index) { + if (device_index == Core::HID::DeviceIndex::Right) { + return &vibration_right; + } + return &vibration_left; +} + +void AbstractPad::GetLeftRightVibrationDevice(std::vector list) { + list.emplace_back(&vibration_left); + list.emplace_back(&vibration_right); +} + +NpadGcVibrationDevice* AbstractPad::GetGCVibrationDevice() { + return &vibration_gc; +} + +Core::HID::NpadIdType AbstractPad::GetLastActiveNpad() { + return properties_handler.GetNpadId(); +} + +void AbstractPad::UpdateInterfaceType() { + if (interface_type != properties_handler.GetInterfaceType()) { + Update(); + } + battery_handler.UpdateBatteryState(); +} + +void AbstractPad::Update() { + properties_handler.UpdateDeviceType(); + led_handler.SetNpadLedHandlerLedPattern(); + vibration_handler.UpdateVibrationState(); + sixaxis_handler.UpdateSixAxisState(); + nfc_handler.UpdateNfcState(); + ir_sensor_handler.UpdateIrSensorState(); + mcu_handler.UpdateMcuState(); + palma_handler.UpdatePalmaState(); + battery_handler.UpdateBatteryState(); + button_handler.EnableCenterClamp(); + + interface_type = properties_handler.GetInterfaceType(); + + std::scoped_lock lock{*applet_resource_holder->shared_mutex}; + properties_handler.UpdateAllDeviceProperties(); + battery_handler.UpdateCoreBatteryState(); + button_handler.UpdateCoreBatteryState(); +} + +void AbstractPad::UpdatePadState() { + button_handler.UpdateAllButtonLifo(); + sixaxis_handler.UpdateSixAxisState(); + battery_handler.UpdateCoreBatteryState(); +} + +void AbstractPad::EnableAppletToGetInput(u64 aruid) { + button_handler.UpdateButtonState(aruid); + sixaxis_handler.UpdateSixAxisState(aruid); + battery_handler.UpdateBatteryState(aruid); +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_types.h" + +#include "hid_core/resources/abstracted_pad/abstract_battery_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_button_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_led_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_mcu_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_nfc_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_palma_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_vibration_handler.h" +#include "hid_core/resources/vibration/gc_vibration_device.h" +#include "hid_core/resources/vibration/n64_vibration_device.h" +#include "hid_core/resources/vibration/vibration_device.h" + +namespace Service::HID { +class AppletResource; +class SixAxisResource; +class PalmaResource; +class NPadResource; +class AbstractPad; +class NpadLastActiveHandler; +class NpadIrNfcHandler; +class UniquePads; +class NpadPalmaHandler; +class FirmwareResource; +class NpadVibration; +class NpadHighestBattery; +class NpadGcVibration; + +class CaptureButtonResource; +class HomeButtonResource; +class VibrationHandler; + +struct HandheldConfig; + +/// Handles Npad request from HID interfaces +class AbstractPad final { +public: + explicit AbstractPad(); + ~AbstractPad(); + + void SetExternals(AppletResourceHolder* applet_resource, + CaptureButtonResource* capture_button_resource, + HomeButtonResource* home_button_resource, SixAxisResource* sixaxis_resource, + PalmaResource* palma_resource, VibrationHandler* vibration); + void SetNpadId(Core::HID::NpadIdType npad_id); + + Result Activate(); + Result Deactivate(); + + Result ActivateNpad(u64 aruid); + + NpadAbstractedPadHolder* GetAbstractedPadHolder(); + NpadAbstractPropertiesHandler* GetAbstractPropertiesHandler(); + NpadAbstractLedHandler* GetAbstractLedHandler(); + NpadAbstractIrSensorHandler* GetAbstractIrSensorHandler(); + NpadAbstractMcuHandler* GetAbstractMcuHandler(); + NpadAbstractNfcHandler* GetAbstractNfcHandler(); + NpadAbstractVibrationHandler* GetAbstractVibrationHandler(); + NpadAbstractSixAxisHandler* GetAbstractSixAxisHandler(); + NpadAbstractButtonHandler* GetAbstractButtonHandler(); + NpadAbstractBatteryHandler* GetAbstractBatteryHandler(); + + NpadN64VibrationDevice* GetN64VibrationDevice(); + NpadVibrationDevice* GetVibrationDevice(Core::HID::DeviceIndex device_index); + void GetLeftRightVibrationDevice(std::vector list); + NpadGcVibrationDevice* GetGCVibrationDevice(); + + Core::HID::NpadIdType GetLastActiveNpad(); + void UpdateInterfaceType(); + void Update(); + + void UpdatePadState(); + void EnableAppletToGetInput(u64 aruid); + +private: + AppletResourceHolder* applet_resource_holder{nullptr}; + NpadAbstractedPadHolder abstract_pad_holder{}; + NpadAbstractPropertiesHandler properties_handler{}; + NpadAbstractLedHandler led_handler{}; + NpadAbstractIrSensorHandler ir_sensor_handler{}; + NpadAbstractNfcHandler nfc_handler{}; + NpadAbstractMcuHandler mcu_handler{}; + NpadAbstractVibrationHandler vibration_handler{}; + NpadAbstractSixAxisHandler sixaxis_handler{}; + NpadAbstractButtonHandler button_handler{}; + NpadAbstractBatteryHandler battery_handler{}; + NpadAbstractPalmaHandler palma_handler{}; + + NpadN64VibrationDevice vibration_n64{}; + NpadVibrationDevice vibration_left{}; + NpadVibrationDevice vibration_right{}; + NpadGcVibrationDevice vibration_gc{}; + + // SixAxisConfigHolder fullkey_config; + // SixAxisConfigHolder handheld_config; + // SixAxisConfigHolder dual_left_config; + // SixAxisConfigHolder dual_right_config; + // SixAxisConfigHolder left_config; + // SixAxisConfigHolder right_config; + + s32 ref_counter{}; + Core::HID::NpadInterfaceType interface_type{Core::HID::NpadInterfaceType::None}; +}; + +using FullAbstractPad = std::array; + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { + +Result NpadAbstractedPadHolder::RegisterAbstractPad(IAbstractedPad* abstracted_pad) { + if (list_size >= assignment_list.size()) { + return ResultNpadIsNotProController; + } + + for (std::size_t i = 0; i < list_size; i++) { + if (assignment_list[i].device_type == abstracted_pad->device_type) { + return ResultNpadIsNotProController; + } + } + + assignment_list[list_size] = { + .abstracted_pad = abstracted_pad, + .device_type = abstracted_pad->device_type, + .interface_type = abstracted_pad->interface_type, + .controller_id = abstracted_pad->controller_id, + }; + + list_size++; + return ResultSuccess; +} + +void NpadAbstractedPadHolder::RemoveAbstractPadByControllerId(u64 controller_id) { + if (list_size == 0) { + return; + } + if (controller_id == 0) { + return; + } + for (std::size_t i = 0; i < list_size; i++) { + if (assignment_list[i].controller_id != controller_id) { + continue; + } + for (std::size_t e = i + 1; e < list_size; e++) { + assignment_list[e - 1] = assignment_list[e]; + } + list_size--; + return; + } +} + +void NpadAbstractedPadHolder::DetachAbstractedPad() { + while (list_size > 0) { + for (std::size_t i = 1; i < list_size; i++) { + assignment_list[i - 1] = assignment_list[i]; + } + list_size--; + } +} + +u64 NpadAbstractedPadHolder::RemoveAbstractPadByAssignmentStyle( + Service::HID::AssignmentStyle assignment_style) { + for (std::size_t i = 0; i < list_size; i++) { + if ((assignment_style.raw & assignment_list[i].abstracted_pad->assignment_style.raw) == 0) { + continue; + } + for (std::size_t e = i + 1; e < list_size; e++) { + assignment_list[e - 1] = assignment_list[e]; + } + list_size--; + return list_size; + } + return list_size; +} + +u32 NpadAbstractedPadHolder::GetAbstractedPads(std::span list) const { + u32 num_elements = std::min(static_cast(list.size()), list_size); + for (std::size_t i = 0; i < num_elements; i++) { + list[i] = assignment_list[i].abstracted_pad; + } + return num_elements; +} + +void NpadAbstractedPadHolder::SetAssignmentMode(const NpadJoyAssignmentMode& mode) { + assignment_mode = mode; +} + +NpadJoyAssignmentMode NpadAbstractedPadHolder::GetAssignmentMode() const { + return assignment_mode; +} + +std::size_t NpadAbstractedPadHolder::GetStyleIndexList( + std::span list) const { + for (std::size_t i = 0; i < list_size; i++) { + list[i] = assignment_list[i].device_type; + } + return list_size; +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { +struct IAbstractedPad; + +struct AbstractAssignmentHolder { + IAbstractedPad* abstracted_pad; + Core::HID::NpadStyleIndex device_type; + Core::HID::NpadInterfaceType interface_type; + INSERT_PADDING_BYTES(0x6); + u64 controller_id; +}; +static_assert(sizeof(AbstractAssignmentHolder) == 0x18, + "AbstractAssignmentHolder is an invalid size"); + +/// This is nn::hid::server::NpadAbstractedPadHolder +class NpadAbstractedPadHolder final { +public: + Result RegisterAbstractPad(IAbstractedPad* abstracted_pad); + void RemoveAbstractPadByControllerId(u64 controller_id); + void DetachAbstractedPad(); + u64 RemoveAbstractPadByAssignmentStyle(Service::HID::AssignmentStyle assignment_style); + u32 GetAbstractedPads(std::span list) const; + + void SetAssignmentMode(const NpadJoyAssignmentMode& mode); + NpadJoyAssignmentMode GetAssignmentMode() const; + + std::size_t GetStyleIndexList(std::span list) const; + +private: + std::array assignment_list{}; + u32 list_size{}; + NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual}; +}; +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/abstracted_pad/abstract_palma_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" + +namespace Service::HID { + +NpadAbstractPalmaHandler::NpadAbstractPalmaHandler() {} + +NpadAbstractPalmaHandler::~NpadAbstractPalmaHandler() = default; + +void NpadAbstractPalmaHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractPalmaHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; + return; +} + +void NpadAbstractPalmaHandler::SetPalmaResource(PalmaResource* resource) { + palma_resource = resource; +} + +Result NpadAbstractPalmaHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractPalmaHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +void NpadAbstractPalmaHandler::UpdatePalmaState() { + // TODO +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Service::HID { +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; +class PalmaResource; + +class NpadAbstractPalmaHandler final { +public: + explicit NpadAbstractPalmaHandler(); + ~NpadAbstractPalmaHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + void SetPalmaResource(PalmaResource* resource); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + void UpdatePalmaState(); + +private: + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + PalmaResource* palma_resource{nullptr}; + + s32 ref_counter{}; +}; + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_util.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_resource.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/shared_memory_format.h" + +namespace Service::HID { + +NpadAbstractPropertiesHandler::NpadAbstractPropertiesHandler() {} + +NpadAbstractPropertiesHandler::~NpadAbstractPropertiesHandler() = default; + +void NpadAbstractPropertiesHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; + return; +} + +void NpadAbstractPropertiesHandler::SetAppletResource(AppletResourceHolder* applet_resource) { + applet_resource_holder = applet_resource; + return; +} + +void NpadAbstractPropertiesHandler::SetNpadId(Core::HID::NpadIdType npad_id) { + if (!IsNpadIdValid(npad_id)) { + ASSERT_MSG(false, "Invalid npad id"); + } + + npad_id_type = npad_id; +} + +Core::HID::NpadIdType NpadAbstractPropertiesHandler::GetNpadId() const { + return npad_id_type; +} + +Result NpadAbstractPropertiesHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + + if (ref_counter != 0) { + ref_counter++; + return ResultSuccess; + } + + const auto npad_index = NpadIdTypeToIndex(npad_id_type); + for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { + auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid_index); + auto& internal_state = + data->shared_memory_format->npad.npad_entry[npad_index].internal_state; + if (!data->flag.is_assigned) { + continue; + } + internal_state.fullkey_lifo.buffer_count = 0; + internal_state.handheld_lifo.buffer_count = 0; + internal_state.joy_dual_lifo.buffer_count = 0; + internal_state.joy_left_lifo.buffer_count = 0; + internal_state.joy_right_lifo.buffer_count = 0; + internal_state.palma_lifo.buffer_count = 0; + internal_state.system_ext_lifo.buffer_count = 0; + internal_state.gc_trigger_lifo.buffer_count = 0; + internal_state.sixaxis_fullkey_lifo.lifo.buffer_count = 0; + internal_state.sixaxis_handheld_lifo.lifo.buffer_count = 0; + internal_state.sixaxis_dual_left_lifo.lifo.buffer_count = 0; + internal_state.sixaxis_dual_right_lifo.lifo.buffer_count = 0; + internal_state.sixaxis_left_lifo.lifo.buffer_count = 0; + internal_state.sixaxis_right_lifo.lifo.buffer_count = 0; + + internal_state.style_tag = {Core::HID::NpadStyleSet::None}; + internal_state.assignment_mode = NpadJoyAssignmentMode::Dual; + internal_state.joycon_color = {}; + internal_state.fullkey_color = {}; + + internal_state.system_properties.raw = 0; + internal_state.button_properties.raw = 0; + internal_state.device_type.raw = 0; + + internal_state.battery_level_dual = Core::HID::NpadBatteryLevel::Empty; + internal_state.battery_level_left = Core::HID::NpadBatteryLevel::Empty; + internal_state.battery_level_right = Core::HID::NpadBatteryLevel::Empty; + + internal_state.applet_footer_type = AppletFooterUiType::None; + internal_state.applet_footer_attributes = {}; + internal_state.lark_type_l_and_main = {}; + internal_state.lark_type_r = {}; + + internal_state.sixaxis_fullkey_properties.is_newly_assigned.Assign(true); + internal_state.sixaxis_handheld_properties.is_newly_assigned.Assign(true); + internal_state.sixaxis_dual_left_properties.is_newly_assigned.Assign(true); + internal_state.sixaxis_dual_right_properties.is_newly_assigned.Assign(true); + internal_state.sixaxis_left_properties.is_newly_assigned.Assign(true); + internal_state.sixaxis_right_properties.is_newly_assigned.Assign(true); + } + + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractPropertiesHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +Result NpadAbstractPropertiesHandler::ActivateNpadUnknown0x88(u64 aruid) { + const auto npad_index = NpadIdTypeToIndex(npad_id_type); + for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { + auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid_index); + if (!data->flag.is_assigned || data->aruid != aruid) { + continue; + } + UpdateDeviceProperties(aruid, data->shared_memory_format->npad.npad_entry[npad_index]); + return ResultSuccess; + } + return ResultSuccess; +} + +void NpadAbstractPropertiesHandler::UpdateDeviceType() { + // TODO +} + +void NpadAbstractPropertiesHandler::UpdateDeviceColor() { + // TODO +} + +void NpadAbstractPropertiesHandler::UpdateFooterAttributes() { + // TODO +} + +void NpadAbstractPropertiesHandler::UpdateAllDeviceProperties() { + const auto npad_index = NpadIdTypeToIndex(npad_id_type); + for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { + auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid_index); + if (!data->flag.is_assigned) { + continue; + } + auto& npad_entry = data->shared_memory_format->npad.npad_entry[npad_index]; + UpdateDeviceProperties(data->aruid, npad_entry); + } +} + +Core::HID::NpadInterfaceType NpadAbstractPropertiesHandler::GetFullkeyInterfaceType() { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (abstract_pad->device_type != Core::HID::NpadStyleIndex::Fullkey) { + continue; + } + if (abstract_pad->interface_type >= Core::HID::NpadInterfaceType::Embedded) { + // Abort + continue; + } + return abstract_pad->interface_type; + } + + return Core::HID::NpadInterfaceType::None; +} + +Core::HID::NpadInterfaceType NpadAbstractPropertiesHandler::GetInterfaceType() { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (!abstract_pad->disabled_feature_set.has_identification_code) { + continue; + } + if (abstract_pad->interface_type >= Core::HID::NpadInterfaceType::Embedded) { + // Abort + continue; + } + return abstract_pad->interface_type; + } + return Core::HID::NpadInterfaceType::None; +} + +Core::HID::NpadStyleSet NpadAbstractPropertiesHandler::GetStyleSet(u64 aruid) { + // TODO + return Core::HID::NpadStyleSet::None; +} + +std::size_t NpadAbstractPropertiesHandler::GetAbstractedPadsWithStyleTag( + std::span list, Core::HID::NpadStyleTag style) { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + if (count == 0) { + return count; + } + + bool is_supported_style_set{}; + const auto result = applet_resource_holder->shared_npad_resource->IsSupportedNpadStyleSet( + is_supported_style_set, applet_resource_holder->applet_resource->GetActiveAruid()); + + if (!is_supported_style_set || result.IsError()) { + for (std::size_t i = 0; i < count; i++) { + // TODO + } + return count; + } + + std::size_t filtered_count{}; + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + const bool is_enabled = true; + if (is_enabled) { + list[filtered_count] = abstract_pad; + filtered_count++; + } + } + + return filtered_count; +} + +std::size_t NpadAbstractPropertiesHandler::GetAbstractedPads(std::span list) { + Core::HID::NpadStyleTag style{ + GetStyleSet(applet_resource_holder->applet_resource->GetActiveAruid())}; + return GetAbstractedPadsWithStyleTag(list, style); +} + +AppletFooterUiType NpadAbstractPropertiesHandler::GetAppletFooterUiType() { + return applet_ui_type.footer; +} + +AppletDetailedUiType NpadAbstractPropertiesHandler::GetAppletDetailedUiType() { + return applet_ui_type; +} + +void NpadAbstractPropertiesHandler::UpdateDeviceProperties(u64 aruid, + NpadSharedMemoryEntry& internal_state) { + // TODO +} + +Core::HID::NpadInterfaceType NpadAbstractPropertiesHandler::GetNpadInterfaceType() { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (abstract_pad->interface_type >= Core::HID::NpadInterfaceType::Embedded) { + // Abort + continue; + } + return abstract_pad->interface_type; + } + + return Core::HID::NpadInterfaceType::None; +} + +Result NpadAbstractPropertiesHandler::GetNpadFullKeyGripColor( + Core::HID::NpadColor& main_color, Core::HID::NpadColor& sub_color) const { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + if (applet_ui_type.footer != AppletFooterUiType::SwitchProController) { + return ResultNpadIsNotProController; + } + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + return ResultSuccess; + } + + return ResultNpadIsNotProController; +} + +void NpadAbstractPropertiesHandler::GetNpadLeftRightInterfaceType( + Core::HID::NpadInterfaceType& out_left_interface, + Core::HID::NpadInterfaceType& out_right_interface) const { + out_left_interface = Core::HID::NpadInterfaceType::None; + out_right_interface = Core::HID::NpadInterfaceType::None; + + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (abstract_pad->assignment_style.is_external_left_assigned && + abstract_pad->assignment_style.is_handheld_left_assigned) { + if (abstract_pad->interface_type > Core::HID::NpadInterfaceType::Embedded) { + // Abort + continue; + } + out_left_interface = abstract_pad->interface_type; + continue; + } + if (abstract_pad->assignment_style.is_external_right_assigned && + abstract_pad->assignment_style.is_handheld_right_assigned) { + if (abstract_pad->interface_type > Core::HID::NpadInterfaceType::Embedded) { + // Abort + continue; + } + out_right_interface = abstract_pad->interface_type; + continue; + } + } +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { +struct NpadSharedMemoryEntry; + +struct AppletResourceHolder; +class NpadAbstractedPadHolder; + +struct ColorProperties { + ColorAttribute attribute; + Core::HID::NpadControllerColor color; + INSERT_PADDING_BYTES(0x4); +}; + +/// Handles Npad request from HID interfaces +class NpadAbstractPropertiesHandler final { +public: + explicit NpadAbstractPropertiesHandler(); + ~NpadAbstractPropertiesHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetAppletResource(AppletResourceHolder* applet_resource); + void SetNpadId(Core::HID::NpadIdType npad_id); + + Core::HID::NpadIdType GetNpadId() const; + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + Result ActivateNpadUnknown0x88(u64 aruid); + + void UpdateDeviceType(); + void UpdateDeviceColor(); + void UpdateFooterAttributes(); + void UpdateAllDeviceProperties(); + + Core::HID::NpadInterfaceType GetFullkeyInterfaceType(); + Core::HID::NpadInterfaceType GetInterfaceType(); + + Core::HID::NpadStyleSet GetStyleSet(u64 aruid); + std::size_t GetAbstractedPadsWithStyleTag(std::span list, + Core::HID::NpadStyleTag style); + std::size_t GetAbstractedPads(std::span list); + + AppletFooterUiType GetAppletFooterUiType(); + + AppletDetailedUiType GetAppletDetailedUiType(); + + void UpdateDeviceProperties(u64 aruid, NpadSharedMemoryEntry& internal_state); + + Core::HID::NpadInterfaceType GetNpadInterfaceType(); + + Result GetNpadFullKeyGripColor(Core::HID::NpadColor& main_color, + Core::HID::NpadColor& sub_color) const; + + void GetNpadLeftRightInterfaceType(Core::HID::NpadInterfaceType& param_2, + Core::HID::NpadInterfaceType& param_3) const; + +private: + AppletResourceHolder* applet_resource_holder{nullptr}; + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + Core::HID::NpadIdType npad_id_type{Core::HID::NpadIdType::Invalid}; + s32 ref_counter{}; + Core::HID::DeviceIndex device_type{}; + AppletDetailedUiType applet_ui_type{}; + AppletFooterUiAttributes applet_ui_attributes{}; + bool is_vertical{}; + bool is_horizontal{}; + bool use_plus{}; + bool use_minus{}; + bool has_directional_buttons{}; + ColorProperties fullkey_color{}; + ColorProperties left_color{}; + ColorProperties right_color{}; +}; +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/hid_util.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/shared_memory_format.h" + +namespace Service::HID { + +NpadAbstractSixAxisHandler::NpadAbstractSixAxisHandler() {} + +NpadAbstractSixAxisHandler::~NpadAbstractSixAxisHandler() = default; + +void NpadAbstractSixAxisHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractSixAxisHandler::SetAppletResource(AppletResourceHolder* applet_resource) { + applet_resource_holder = applet_resource; +} + +void NpadAbstractSixAxisHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +void NpadAbstractSixAxisHandler::SetSixaxisResource(SixAxisResource* resource) { + six_axis_resource = resource; +} + +Result NpadAbstractSixAxisHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractSixAxisHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +u64 NpadAbstractSixAxisHandler::IsFirmwareUpdateAvailable() { + // TODO + return false; +} + +Result NpadAbstractSixAxisHandler::UpdateSixAxisState() { + Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); + for (std::size_t i = 0; i < AruidIndexMax; i++) { + auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); + if (data->flag.is_assigned) { + continue; + } + auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; + UpdateSixaxisInternalState(npad_entry, data->aruid, + data->flag.enable_six_axis_sensor.As()); + } + return ResultSuccess; +} + +Result NpadAbstractSixAxisHandler::UpdateSixAxisState(u64 aruid) { + Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); + auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid); + if (data == nullptr) { + return ResultSuccess; + } + auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; + UpdateSixaxisInternalState(npad_entry, data->aruid, + data->flag.enable_six_axis_sensor.As()); + return ResultSuccess; +} + +Result NpadAbstractSixAxisHandler::UpdateSixAxisState2(u64 aruid) { + const auto npad_index = NpadIdTypeToIndex(properties_handler->GetNpadId()); + AruidData* aruid_data = applet_resource_holder->applet_resource->GetAruidData(aruid); + if (aruid_data == nullptr) { + return ResultSuccess; + } + auto& npad_internal_state = aruid_data->shared_memory_format->npad.npad_entry[npad_index]; + UpdateSixaxisInternalState(npad_internal_state, aruid, + aruid_data->flag.enable_six_axis_sensor.As()); + return ResultSuccess; +} + +void NpadAbstractSixAxisHandler::UpdateSixaxisInternalState(NpadSharedMemoryEntry& npad_entry, + u64 aruid, bool is_sensor_enabled) { + const Core::HID::NpadStyleTag style_tag{properties_handler->GetStyleSet(aruid)}; + + if (!style_tag.palma) { + UpdateSixaxisFullkeyLifo(style_tag, npad_entry.internal_state.sixaxis_fullkey_lifo, + is_sensor_enabled); + } else { + UpdateSixAxisPalmaLifo(style_tag, npad_entry.internal_state.sixaxis_fullkey_lifo, + is_sensor_enabled); + } + UpdateSixaxisHandheldLifo(style_tag, npad_entry.internal_state.sixaxis_handheld_lifo, + is_sensor_enabled); + UpdateSixaxisDualLifo(style_tag, npad_entry.internal_state.sixaxis_dual_left_lifo, + is_sensor_enabled); + UpdateSixaxisDualLifo(style_tag, npad_entry.internal_state.sixaxis_dual_right_lifo, + is_sensor_enabled); + UpdateSixaxisLeftLifo(style_tag, npad_entry.internal_state.sixaxis_left_lifo, + is_sensor_enabled); + UpdateSixaxisRightLifo(style_tag, npad_entry.internal_state.sixaxis_right_lifo, + is_sensor_enabled); + // TODO: Set sixaxis properties +} + +void NpadAbstractSixAxisHandler::UpdateSixaxisFullkeyLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, + bool is_sensor_enabled) { + // TODO +} + +void NpadAbstractSixAxisHandler::UpdateSixAxisPalmaLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, + bool is_sensor_enabled) { + // TODO +} + +void NpadAbstractSixAxisHandler::UpdateSixaxisHandheldLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, + bool is_sensor_enabled) { + // TODO +} + +void NpadAbstractSixAxisHandler::UpdateSixaxisDualLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, + bool is_sensor_enabled) { + // TODO +} + +void NpadAbstractSixAxisHandler::UpdateSixaxisLeftLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, + bool is_sensor_enabled) { + // TODO +} + +void NpadAbstractSixAxisHandler::UpdateSixaxisRightLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, + bool is_sensor_enabled) { + // TODO +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Service::HID { +class SixAxisResource; +struct AppletResourceHolder; +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; +struct NpadSixAxisSensorLifo; + +/// Handles Npad request from HID interfaces +class NpadAbstractSixAxisHandler final { +public: + explicit NpadAbstractSixAxisHandler(); + ~NpadAbstractSixAxisHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetAppletResource(AppletResourceHolder* applet_resource); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + void SetSixaxisResource(SixAxisResource* resource); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + u64 IsFirmwareUpdateAvailable(); + + Result UpdateSixAxisState(); + Result UpdateSixAxisState(u64 aruid); + Result UpdateSixAxisState2(u64 aruid); + +private: + void UpdateSixaxisInternalState(NpadSharedMemoryEntry& npad_entry, u64 aruid, + bool is_sensor_enabled); + void UpdateSixaxisFullkeyLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled); + void UpdateSixAxisPalmaLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled); + void UpdateSixaxisHandheldLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled); + void UpdateSixaxisDualLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled); + void UpdateSixaxisLeftLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled); + void UpdateSixaxisRightLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled); + + AppletResourceHolder* applet_resource_holder{nullptr}; + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + SixAxisResource* six_axis_resource{nullptr}; + + s32 ref_counter{}; +}; + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/hid_util.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_vibration_handler.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_vibration.h" +#include "hid_core/resources/vibration/gc_vibration_device.h" +#include "hid_core/resources/vibration/n64_vibration_device.h" +#include "hid_core/resources/vibration/vibration_device.h" + +namespace Service::HID { + +NpadAbstractVibrationHandler::NpadAbstractVibrationHandler() {} + +NpadAbstractVibrationHandler::~NpadAbstractVibrationHandler() = default; + +void NpadAbstractVibrationHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractVibrationHandler::SetAppletResource(AppletResourceHolder* applet_resource) { + applet_resource_holder = applet_resource; +} + +void NpadAbstractVibrationHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +void NpadAbstractVibrationHandler::SetN64Vibration(NpadN64VibrationDevice* n64_device) { + n64_vibration_device = n64_device; +} + +void NpadAbstractVibrationHandler::SetVibration(std::span device) { + for (std::size_t i = 0; i < device.size() && i < vibration_device.size(); i++) { + vibration_device[i] = device[i]; + } +} + +void NpadAbstractVibrationHandler::SetGcVibration(NpadGcVibrationDevice* gc_device) { + gc_vibration_device = gc_device; +} + +Result NpadAbstractVibrationHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractVibrationHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +void NpadAbstractVibrationHandler::UpdateVibrationState() { + const bool is_handheld_hid_enabled = + applet_resource_holder->handheld_config->is_handheld_hid_enabled; + const bool is_force_handheld_style_vibration = + applet_resource_holder->handheld_config->is_force_handheld_style_vibration; + + if (!is_handheld_hid_enabled && is_force_handheld_style_vibration) { + // TODO + } +} +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Service::HID { +struct AppletResourceHolder; +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; +class NpadGcVibrationDevice; +class NpadVibrationDevice; +class NpadN64VibrationDevice; +class NpadVibration; + +/// Keeps track of battery levels and updates npad battery shared memory values +class NpadAbstractVibrationHandler final { +public: + explicit NpadAbstractVibrationHandler(); + ~NpadAbstractVibrationHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetAppletResource(AppletResourceHolder* applet_resource); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + + void SetN64Vibration(NpadN64VibrationDevice* n64_device); + void SetVibration(std::span device); + void SetGcVibration(NpadGcVibrationDevice* gc_device); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + void UpdateVibrationState(); + +private: + AppletResourceHolder* applet_resource_holder{nullptr}; + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + + NpadN64VibrationDevice* n64_vibration_device{nullptr}; + std::array vibration_device{}; + NpadGcVibrationDevice* gc_vibration_device{nullptr}; + NpadVibration* vibration_handler{nullptr}; + s32 ref_counter{}; +}; +} // namespace Service::HID 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) { case Core::HID::NpadStyleIndex::None: ASSERT(false); break; - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: shared_memory->fullkey_color.attribute = ColorAttribute::Ok; shared_memory->fullkey_color.fullkey = body_colors.fullkey; shared_memory->battery_level_dual = battery_level.dual.battery_level; @@ -491,7 +491,7 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { case Core::HID::NpadStyleIndex::None: ASSERT(false); break; - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::NES: case Core::HID::NpadStyleIndex::SNES: case Core::HID::NpadStyleIndex::N64: @@ -1292,7 +1292,7 @@ Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) { auto& controller = GetControllerFromHandle(aruid, sixaxis_handle); switch (sixaxis_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::Pokeball: return controller.shared_memory->sixaxis_fullkey_properties; case Core::HID::NpadStyleIndex::Handheld: @@ -1315,7 +1315,7 @@ const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { const auto& controller = GetControllerFromHandle(aruid, sixaxis_handle); switch (sixaxis_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::Pokeball: return controller.shared_memory->sixaxis_fullkey_properties; 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 { bool NPadData::IsNpadStyleIndexSupported(Core::HID::NpadStyleIndex style_index) const { Core::HID::NpadStyleTag style = {supported_npad_style_set}; switch (style_index) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: return style.fullkey.As(); case Core::HID::NpadStyleIndex::Handheld: return style.handheld.As(); diff --git a/src/hid_core/resources/npad/npad_types.h b/src/hid_core/resources/npad/npad_types.h index a02f9cf16..074dd40cf 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 { U, }; +// nn::hidtypes::FeatureType +struct FeatureType { + union { + u64 raw{}; + BitField<0, 1, u64> has_left_analog_stick; + BitField<1, 1, u64> has_right_analog_stick; + BitField<2, 1, u64> has_left_joy_six_axis_sensor; + BitField<3, 1, u64> has_right_joy_six_axis_sensor; + BitField<4, 1, u64> has_fullkey_joy_six_axis_sensor; + BitField<5, 1, u64> has_left_lra_vibration_device; + BitField<6, 1, u64> has_right_lra_vibration_device; + BitField<7, 1, u64> has_gc_vibration_device; + BitField<8, 1, u64> has_erm_vibration_device; + BitField<9, 1, u64> has_left_joy_rail_bus; + BitField<10, 1, u64> has_right_joy_rail_bus; + BitField<11, 1, u64> has_internal_bus; + BitField<12, 1, u64> is_palma; + BitField<13, 1, u64> has_nfc; + BitField<14, 1, u64> has_ir_sensor; + BitField<15, 1, u64> is_analog_stick_calibration_supported; + BitField<16, 1, u64> is_six_axis_Sensor_user_calibration_supported; + BitField<17, 1, u64> has_left_right_joy_battery; + BitField<18, 1, u64> has_fullkey_battery; + BitField<19, 1, u64> is_disconnect_controller_if_battery_none; + BitField<20, 1, u64> has_controller_color; + BitField<21, 1, u64> has_grip_color; + BitField<22, 1, u64> has_identification_code; + BitField<23, 1, u64> has_bluetooth_address; + BitField<24, 1, u64> has_mcu; + BitField<25, 1, u64> has_notification_led; + BitField<26, 1, u64> has_directional_buttons; + BitField<27, 1, u64> has_indicator_led; + BitField<28, 1, u64> is_button_config_embedded_supported; + BitField<29, 1, u64> is_button_config_full_supported; + BitField<30, 1, u64> is_button_config_left_supported; + BitField<31, 1, u64> is_button_config_right_supported; + BitField<32, 1, u64> is_usb_hid_device; + BitField<33, 1, u64> is_kuina_device; + BitField<34, 1, u64> is_direct_usb_to_bt_switching_device; + BitField<35, 1, u64> is_normalize_analog_stick_with_inner_cross; + }; +}; +static_assert(sizeof(FeatureType) == 8, "FeatureType is an invalid size"); + +// This is nn::hid::AssignmentStyle +struct AssignmentStyle { + union { + u32 raw{}; + BitField<0, 1, u32> is_external_assigned; + BitField<1, 1, u32> is_external_left_assigned; + BitField<2, 1, u32> is_external_right_assigned; + BitField<3, 1, u32> is_handheld_assigned; + BitField<4, 1, u32> is_handheld_left_assigned; + BitField<5, 1, u32> is_handheld_right_assigned; + }; +}; +static_assert(sizeof(AssignmentStyle) == 4, "AssignmentStyle is an invalid size"); + +// This is nn::hid::server::IAbstractedPad::InternalFlags +struct InternalFlags { + union { + u32 raw{}; + BitField<0, 1, u32> is_bound; + BitField<1, 1, u32> is_connected; + BitField<2, 1, u32> is_battery_low_ovln_required; + BitField<3, 1, u32> is_battery_low_ovln_delay_required; + BitField<4, 1, u32> is_sample_recieved; + BitField<5, 1, u32> is_virtual_input; + BitField<6, 1, u32> is_wired; + BitField<8, 1, u32> use_center_clamp; + BitField<9, 1, u32> has_virtual_six_axis_sensor_acceleration; + BitField<10, 1, u32> has_virtual_six_axis_sensor_angle; + BitField<11, 1, u32> is_debug_pad; + }; +}; +static_assert(sizeof(InternalFlags) == 4, "InternalFlags is an invalid size"); + +/// This is nn::hid::server::IAbstractedPad +struct IAbstractedPad { + InternalFlags internal_flags; + u64 controller_id; + u32 controller_number; + u64 low_battery_display_delay_time; + u64 low_battery_display_delay_interval; + FeatureType feature_set; + FeatureType disabled_feature_set; + AssignmentStyle assignment_style; + Core::HID::NpadStyleIndex device_type; + Core::HID::NpadInterfaceType interface_type; + Core::HID::NpadPowerInfo power_info; + u32 pad_state; + u32 button_mask; + u32 system_button_mask; + u8 indicator; + std::vector virtual_six_axis_sensor_acceleration; + std::vector virtual_six_axis_sensor_angle; + u64 xcd_handle; + u64 color; +}; } // 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..3bdd55dec --- /dev/null +++ b/src/hid_core/resources/npad/npad_vibration.cpp @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/npad/npad_vibration.h" + +namespace Service::HID { + +NpadVibration::NpadVibration() {} + +NpadVibration::~NpadVibration() = default; + +Result NpadVibration::Activate() { + std::scoped_lock lock{mutex}; + + const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume(); + // if (master_volume < 0.0f || master_volume > 1.0f) { + // return ResultVibrationStrenghtOutOfRange; + // } + + volume = master_volume; + return ResultSuccess; +} + +Result NpadVibration::Deactivate() { + return ResultSuccess; +} + +Result NpadVibration::SetVibrationMasterVolume(f32 master_volume) { + std::scoped_lock lock{mutex}; + + if (master_volume < 0.0f && master_volume > 1.0f) { + return ResultVibrationStrenghtOutOfRange; + } + + volume = master_volume; + // nn::settings::system::SetVibrationMasterVolume(master_volume); + + return ResultSuccess; +} + +Result NpadVibration::GetVibrationVolume(f32& out_volume) const { + std::scoped_lock lock{mutex}; + out_volume = volume; + return ResultSuccess; +} + +Result NpadVibration::GetVibrationMasterVolume(f32& out_volume) const { + std::scoped_lock lock{mutex}; + + const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume(); + // if (master_volume < 0.0f || master_volume > 1.0f) { + // return ResultVibrationStrenghtOutOfRange; + // } + + out_volume = master_volume; + return ResultSuccess; +} + +Result NpadVibration::BeginPermitVibrationSession(u64 aruid) { + std::scoped_lock lock{mutex}; + session_aruid = aruid; + volume = 1.0; + return ResultSuccess; +} + +Result NpadVibration::EndPermitVibrationSession() { + std::scoped_lock lock{mutex}; + + const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume(); + // if (master_volume < 0.0f || master_volume > 1.0f) { + // return ResultVibrationStrenghtOutOfRange; + // } + + volume = master_volume; + session_aruid = 0; + return ResultSuccess; +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" +#include "core/hle/result.h" + +namespace Service::HID { + +class NpadVibration final { +public: + explicit NpadVibration(); + ~NpadVibration(); + + Result Activate(); + Result Deactivate(); + + Result SetVibrationMasterVolume(f32 master_volume); + Result GetVibrationVolume(f32& out_volume) const; + Result GetVibrationMasterVolume(f32& out_volume) const; + + Result BeginPermitVibrationSession(u64 aruid); + Result EndPermitVibrationSession(); + +private: + f32 volume{}; + u64 session_aruid{}; + mutable std::mutex mutex; +}; + +} // 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..da12d2d5a 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) { case Core::HID::NpadStyleIndex::None: ASSERT(false); break; - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: set_motion_state(sixaxis_fullkey_state, motion_state[0]); break; case Core::HID::NpadStyleIndex::Handheld: @@ -345,7 +345,7 @@ SixAxis::SixaxisParameters& SixAxis::GetSixaxisState( const Core::HID::SixAxisSensorHandle& sixaxis_handle) { auto& controller = GetControllerFromHandle(sixaxis_handle); switch (sixaxis_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::Pokeball: return controller.sixaxis_fullkey; case Core::HID::NpadStyleIndex::Handheld: @@ -368,7 +368,7 @@ const SixAxis::SixaxisParameters& SixAxis::GetSixaxisState( const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { const auto& controller = GetControllerFromHandle(sixaxis_handle); switch (sixaxis_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::Pokeball: return controller.sixaxis_fullkey; case Core::HID::NpadStyleIndex::Handheld: 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/npad/npad_vibration.h" +#include "hid_core/resources/vibration/gc_vibration_device.h" + +namespace Service::HID { + +NpadGcVibrationDevice::NpadGcVibrationDevice() {} + +Result NpadGcVibrationDevice::IncrementRefCounter() { + if (ref_counter == 0 && is_mounted) { + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsSuccess()) { + // TODO: SendVibrationGcErmCommand + } + } + ref_counter++; + return ResultSuccess; +} + +Result NpadGcVibrationDevice::DecrementRefCounter() { + if (ref_counter == 1 && !is_mounted) { + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsSuccess()) { + // TODO: SendVibrationGcErmCommand + } + } + + if (ref_counter > 0) { + ref_counter--; + } + + return ResultSuccess; +} + +Result NpadGcVibrationDevice::SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command) { + if (!is_mounted) { + return ResultSuccess; + } + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsError()) { + return result; + } + if (volume == 0.0) { + command = Core::HID::VibrationGcErmCommand::Stop; + } else { + if (command > Core::HID::VibrationGcErmCommand::StopHard) { + // Abort + return ResultSuccess; + } + } + // TODO: SendVibrationGcErmCommand + return ResultSuccess; +} + +Result NpadGcVibrationDevice::GetActualVibrationGcErmCommand( + Core::HID::VibrationGcErmCommand& out_command) { + if (!is_mounted) { + out_command = Core::HID::VibrationGcErmCommand::Stop; + return ResultSuccess; + } + + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsError()) { + return result; + } + if (volume == 0.0f) { + out_command = Core::HID::VibrationGcErmCommand::Stop; + return ResultSuccess; + } + + // TODO: GetActualVibrationGcErmCommand + return ResultSuccess; +} + +Result NpadGcVibrationDevice::SendVibrationNotificationPattern( + Core::HID::VibrationGcErmCommand command) { + if (!is_mounted) { + return ResultSuccess; + } + + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsError()) { + return result; + } + if (volume <= 0.0f) { + command = Core::HID::VibrationGcErmCommand::Stop; + } + if (command > Core::HID::VibrationGcErmCommand::StopHard) { + // Abort + return ResultSuccess; + } + + // TODO: SendVibrationNotificationPattern + return ResultSuccess; +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/vibration/vibration_base.h" + +namespace Service::HID { +class NpadVibration; + +/// Handles Npad request from HID interfaces +class NpadGcVibrationDevice final : public NpadVibrationBase { +public: + explicit NpadGcVibrationDevice(); + + Result IncrementRefCounter() override; + Result DecrementRefCounter() override; + + Result SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command); + + Result GetActualVibrationGcErmCommand(Core::HID::VibrationGcErmCommand& out_command); + Result SendVibrationNotificationPattern(Core::HID::VibrationGcErmCommand command); +}; +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/npad/npad_vibration.h" +#include "hid_core/resources/vibration/n64_vibration_device.h" + +namespace Service::HID { + +NpadN64VibrationDevice::NpadN64VibrationDevice() {} + +Result NpadN64VibrationDevice::IncrementRefCounter() { + if (ref_counter == 0 && is_mounted) { + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsSuccess()) { + // TODO: SendVibrationInBool + } + } + + ref_counter++; + return ResultSuccess; +} + +Result NpadN64VibrationDevice::DecrementRefCounter() { + if (ref_counter == 1) { + if (!is_mounted) { + ref_counter = 0; + if (is_mounted != false) { + // TODO: SendVibrationInBool + } + return ResultSuccess; + } + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsSuccess()) { + // TODO + } + } + + if (ref_counter > 0) { + ref_counter--; + } + + return ResultSuccess; +} + +Result NpadN64VibrationDevice::SendValueInBool(bool is_vibrating) { + if (ref_counter < 1) { + return ResultVibrationNotInitialized; + } + if (is_mounted) { + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsError()) { + return result; + } + // TODO: SendVibrationInBool + } + return ResultSuccess; +} + +Result NpadN64VibrationDevice::SendVibrationNotificationPattern([[maybe_unused]] u32 pattern) { + if (!is_mounted) { + return ResultSuccess; + } + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsError()) { + return result; + } + if (volume <= 0.0) { + pattern = 0; + } + // TODO: SendVibrationNotificationPattern + return ResultSuccess; +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/vibration/vibration_base.h" + +namespace Service::HID { +class NpadVibration; + +/// Handles Npad request from HID interfaces +class NpadN64VibrationDevice final : public NpadVibrationBase { +public: + explicit NpadN64VibrationDevice(); + + Result IncrementRefCounter() override; + Result DecrementRefCounter() override; + + Result SendValueInBool(bool is_vibrating); + Result SendVibrationNotificationPattern(u32 pattern); +}; +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/npad/npad_vibration.h" +#include "hid_core/resources/vibration/vibration_base.h" + +namespace Service::HID { + +NpadVibrationBase::NpadVibrationBase() {} + +Result NpadVibrationBase::IncrementRefCounter() { + ref_counter++; + return ResultSuccess; +} + +Result NpadVibrationBase::DecrementRefCounter() { + if (ref_counter > 0) { + ref_counter--; + } + + return ResultSuccess; +} + +bool NpadVibrationBase::IsVibrationMounted() const { + return is_mounted; +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" + +namespace Service::HID { +class NpadVibration; + +/// Handles Npad request from HID interfaces +class NpadVibrationBase { +public: + explicit NpadVibrationBase(); + + virtual Result IncrementRefCounter(); + virtual Result DecrementRefCounter(); + + bool IsVibrationMounted() const; + +protected: + u64 xcd_handle{}; + s32 ref_counter{}; + bool is_mounted{}; + NpadVibration* vibration_handler{nullptr}; +}; +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/npad/npad_vibration.h" +#include "hid_core/resources/vibration/vibration_device.h" + +namespace Service::HID { + +NpadVibrationDevice::NpadVibrationDevice() {} + +Result NpadVibrationDevice::IncrementRefCounter() { + ref_counter++; + return ResultSuccess; +} + +Result NpadVibrationDevice::DecrementRefCounter() { + if (ref_counter > 0) { + ref_counter--; + } + + return ResultSuccess; +} + +Result NpadVibrationDevice::SendVibrationValue(const Core::HID::VibrationValue& value) { + if (ref_counter == 0) { + return ResultVibrationNotInitialized; + } + if (!is_mounted) { + return ResultSuccess; + } + + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsError()) { + return result; + } + if (volume <= 0.0f) { + // TODO: SendVibrationValue + return ResultSuccess; + } + + Core::HID::VibrationValue vibration_value = value; + vibration_value.high_amplitude *= volume; + vibration_value.low_amplitude *= volume; + + // TODO: SendVibrationValue + return ResultSuccess; +} + +Result NpadVibrationDevice::SendVibrationNotificationPattern([[maybe_unused]] u32 pattern) { + if (!is_mounted) { + return ResultSuccess; + } + + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsError()) { + return result; + } + if (volume <= 0.0) { + pattern = 0; + } + + // return xcd_handle->SendVibrationNotificationPattern(pattern); + return ResultSuccess; +} + +Result NpadVibrationDevice::GetActualVibrationValue(Core::HID::VibrationValue& out_value) { + if (ref_counter < 1) { + return ResultVibrationNotInitialized; + } + + out_value = Core::HID::DEFAULT_VIBRATION_VALUE; + if (!is_mounted) { + return ResultSuccess; + } + + // TODO: SendVibrationValue + return ResultSuccess; +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/vibration/vibration_base.h" + +namespace Service::HID { +class NpadVibration; + +/// Handles Npad request from HID interfaces +class NpadVibrationDevice final : public NpadVibrationBase { +public: + explicit NpadVibrationDevice(); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + Result SendVibrationValue(const Core::HID::VibrationValue& value); + Result SendVibrationNotificationPattern(u32 pattern); + + Result GetActualVibrationValue(Core::HID::VibrationValue& out_value); + +private: + u32 device_index{}; +}; + +} // namespace Service::HID 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, bool IsControllerCompatible(Core::HID::NpadStyleIndex controller_type, Core::Frontend::ControllerParameters parameters) { switch (controller_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: return parameters.allow_pro_controller; case Core::HID::NpadStyleIndex::JoyconDual: return parameters.allow_dual_joycons; @@ -462,7 +462,7 @@ void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index }; if (npad_style_set.fullkey == 1) { - add_item(Core::HID::NpadStyleIndex::ProController, tr("Pro Controller")); + add_item(Core::HID::NpadStyleIndex::Fullkey, tr("Pro Controller")); } if (npad_style_set.joycon_dual == 1) { @@ -519,7 +519,7 @@ Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex [index](const auto& pair) { return pair.first == index; }); if (it == pairs.end()) { - return Core::HID::NpadStyleIndex::ProController; + return Core::HID::NpadStyleIndex::Fullkey; } return it->second; @@ -549,7 +549,7 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) const QString stylesheet = [this, player_index] { switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), player_index)) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::GameCube: return QStringLiteral("image: url(:/controller/applet_pro_controller%0); "); 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() { }(); switch (controller_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::GameCube: ui->icon_controller->setStyleSheet( QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index f3552191a..5dac9f1e7 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -1094,7 +1094,7 @@ void ConfigureInputPlayer::SetConnectableControllers() { }; if (npad_style_set.fullkey == 1) { - add_item(Core::HID::NpadStyleIndex::ProController, tr("Pro Controller")); + add_item(Core::HID::NpadStyleIndex::Fullkey, tr("Pro Controller")); } if (npad_style_set.joycon_dual == 1) { @@ -1149,7 +1149,7 @@ Core::HID::NpadStyleIndex ConfigureInputPlayer::GetControllerTypeFromIndex(int i [index](const auto& pair) { return pair.first == index; }); if (it == index_controller_type_pairs.end()) { - return Core::HID::NpadStyleIndex::ProController; + return Core::HID::NpadStyleIndex::Fullkey; } return it->second; @@ -1178,7 +1178,7 @@ void ConfigureInputPlayer::UpdateInputDevices() { void ConfigureInputPlayer::UpdateControllerAvailableButtons() { auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); if (debug) { - layout = Core::HID::NpadStyleIndex::ProController; + layout = Core::HID::NpadStyleIndex::Fullkey; } // List of all the widgets that will be hidden by any of the following layouts that need @@ -1206,7 +1206,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { std::vector layout_hidden; switch (layout) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::Handheld: layout_hidden = { ui->buttonShoulderButtonsSLSRLeft, @@ -1254,7 +1254,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { void ConfigureInputPlayer::UpdateControllerEnabledButtons() { auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); if (debug) { - layout = Core::HID::NpadStyleIndex::ProController; + layout = Core::HID::NpadStyleIndex::Fullkey; } // List of all the widgets that will be disabled by any of the following layouts that need @@ -1271,7 +1271,7 @@ void ConfigureInputPlayer::UpdateControllerEnabledButtons() { std::vector layout_disable; switch (layout) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::Handheld: case Core::HID::NpadStyleIndex::JoyconLeft: @@ -1304,7 +1304,7 @@ void ConfigureInputPlayer::UpdateMotionButtons() { // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller. switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::JoyconLeft: case Core::HID::NpadStyleIndex::Handheld: // Show "Motion 1" and hide "Motion 2". @@ -1333,11 +1333,11 @@ void ConfigureInputPlayer::UpdateMotionButtons() { void ConfigureInputPlayer::UpdateControllerButtonNames() { auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); if (debug) { - layout = Core::HID::NpadStyleIndex::ProController; + layout = Core::HID::NpadStyleIndex::Fullkey; } switch (layout) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::Handheld: 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..8f91f5e92 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) { case Core::HID::NpadStyleIndex::GameCube: DrawGCController(p, center); break; - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: default: DrawProController(p, center); break; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4f4c75f5c..fd5342537 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -3988,7 +3988,7 @@ void GMainWindow::OnToggleDockedMode() { tr("Handheld controller can't be used on docked mode. Pro " "controller will be selected.")); handheld->Disconnect(); - player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey); player_1->Connect(); controller_dialog->refreshConfiguration(); } 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() { } switch (controller_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::Handheld: case Core::HID::NpadStyleIndex::GameCube: @@ -116,7 +116,7 @@ void ControllerNavigation::ControllerUpdateStick() { } switch (controller_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::Handheld: case Core::HID::NpadStyleIndex::GameCube: From d940974789b1b8ff473440883d8c506a275b9b3b Mon Sep 17 00:00:00 2001 From: Liam Date: Fri, 12 Jan 2024 09:35:08 -0500 Subject: [PATCH 22/33] audio: fetch process object from handle table --- src/audio_core/device/device_session.cpp | 14 +++++++-- src/audio_core/device/device_session.h | 12 +++++--- src/audio_core/in/audio_in_system.cpp | 2 +- src/audio_core/in/audio_in_system.h | 13 +++++---- src/audio_core/out/audio_out_system.cpp | 4 +-- src/audio_core/out/audio_out_system.h | 13 +++++---- src/core/hle/service/audio/audin_u.cpp | 36 +++++++++++++++++++----- src/core/hle/service/audio/audout_u.cpp | 26 +++++++++++++---- 8 files changed, 85 insertions(+), 35 deletions(-) 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 @@ #include "core/core_timing.h" #include "core/memory.h" +#include "core/hle/kernel/k_process.h" + namespace AudioCore { using namespace std::literals; @@ -25,7 +27,7 @@ DeviceSession::~DeviceSession() { } Result DeviceSession::Initialize(std::string_view name_, SampleFormat sample_format_, - u16 channel_count_, size_t session_id_, u32 handle_, + u16 channel_count_, size_t session_id_, Kernel::KProcess* handle_, u64 applet_resource_user_id_, Sink::StreamType type_) { if (stream) { Finalize(); @@ -36,6 +38,7 @@ Result DeviceSession::Initialize(std::string_view name_, SampleFormat sample_for channel_count = channel_count_; session_id = session_id_; handle = handle_; + handle->Open(); applet_resource_user_id = applet_resource_user_id_; if (type == Sink::StreamType::In) { @@ -54,6 +57,11 @@ void DeviceSession::Finalize() { sink->CloseStream(stream); stream = nullptr; } + + if (handle) { + handle->Close(); + handle = nullptr; + } } void DeviceSession::Start() { @@ -91,7 +99,7 @@ void DeviceSession::AppendBuffers(std::span buffers) { stream->AppendBuffer(new_buffer, tmp_samples); } else { Core::Memory::CpuGuestMemory samples( - system.ApplicationMemory(), buffer.samples, buffer.size / sizeof(s16)); + handle->GetMemory(), buffer.samples, buffer.size / sizeof(s16)); stream->AppendBuffer(new_buffer, samples); } } @@ -100,7 +108,7 @@ void DeviceSession::AppendBuffers(std::span buffers) { void DeviceSession::ReleaseBuffer(const AudioBuffer& buffer) const { if (type == Sink::StreamType::In) { auto samples{stream->ReleaseBuffer(buffer.size / sizeof(s16))}; - system.ApplicationMemory().WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size); + handle->GetMemory().WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size); } } 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; } // namespace Timing } // namespace Core +namespace Kernel { +class KProcess; +} // namespace Kernel + namespace AudioCore { namespace Sink { @@ -44,13 +48,13 @@ public: * @param sample_format - Sample format for this device's output. * @param channel_count - Number of channels for this device (2 or 6). * @param session_id - This session's id. - * @param handle - Handle for this device session (unused). + * @param handle - Process handle for this device session. * @param applet_resource_user_id - Applet resource user id for this device session (unused). * @param type - Type of this stream (Render, In, Out). * @return Result code for this call. */ Result Initialize(std::string_view name, SampleFormat sample_format, u16 channel_count, - size_t session_id, u32 handle, u64 applet_resource_user_id, + size_t session_id, Kernel::KProcess* handle, u64 applet_resource_user_id, Sink::StreamType type); /** @@ -137,8 +141,8 @@ private: u16 channel_count{}; /// Session id of this device session size_t session_id{}; - /// Handle of this device session - u32 handle{}; + /// Process handle of device memory owner + Kernel::KProcess* handle{}; /// Applet resource user id of this device session u64 applet_resource_user_id{}; /// 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, } Result System::Initialize(std::string device_name, const AudioInParameter& in_params, - const u32 handle_, const u64 applet_resource_user_id_) { + Kernel::KProcess* handle_, const u64 applet_resource_user_id_) { auto result{IsConfigValid(device_name, in_params)}; if (result.IsError()) { 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; namespace Kernel { class KEvent; -} +class KProcess; +} // namespace Kernel namespace AudioCore::AudioIn { @@ -93,12 +94,12 @@ public: * * @param device_name - The name of the requested input device. * @param in_params - Input parameters, see AudioInParameter. - * @param handle - Unused. + * @param handle - Process handle. * @param applet_resource_user_id - Unused. * @return Result code. */ - Result Initialize(std::string device_name, const AudioInParameter& in_params, u32 handle, - u64 applet_resource_user_id); + Result Initialize(std::string device_name, const AudioInParameter& in_params, + Kernel::KProcess* handle, u64 applet_resource_user_id); /** * Start this system. @@ -244,8 +245,8 @@ public: private: /// Core system Core::System& system; - /// (Unused) - u32 handle{}; + /// Process handle + Kernel::KProcess* handle{}; /// (Unused) u64 applet_resource_user_id{}; /// 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, return Service::Audio::ResultInvalidChannelCount; } -Result System::Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle_, - u64 applet_resource_user_id_) { +Result System::Initialize(std::string device_name, const AudioOutParameter& in_params, + Kernel::KProcess* handle_, u64 applet_resource_user_id_) { auto result = IsConfigValid(device_name, in_params); if (result.IsError()) { 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; namespace Kernel { class KEvent; -} +class KProcess; +} // namespace Kernel namespace AudioCore::AudioOut { @@ -84,12 +85,12 @@ public: * * @param device_name - The name of the requested output device. * @param in_params - Input parameters, see AudioOutParameter. - * @param handle - Unused. + * @param handle - Process handle. * @param applet_resource_user_id - Unused. * @return Result code. */ - Result Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle, - u64 applet_resource_user_id); + Result Initialize(std::string device_name, const AudioOutParameter& in_params, + Kernel::KProcess* handle, u64 applet_resource_user_id); /** * Start this system. @@ -228,8 +229,8 @@ public: private: /// Core system Core::System& system; - /// (Unused) - u32 handle{}; + /// Process handle + Kernel::KProcess* handle{}; /// (Unused) u64 applet_resource_user_id{}; /// Buffer event, signalled when a buffer is ready 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; class IAudioIn final : public ServiceFramework { public: explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, - const std::string& device_name, const AudioInParameter& in_params, u32 handle, - u64 applet_resource_user_id) + const std::string& device_name, const AudioInParameter& in_params, + Kernel::KProcess* handle, u64 applet_resource_user_id) : ServiceFramework{system_, "IAudioIn"}, service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, - impl{std::make_shared(system_, manager, event, session_id)} { + process{handle}, impl{std::make_shared(system_, manager, event, session_id)} { // clang-format off static const FunctionInfo functions[] = { {0, &IAudioIn::GetAudioInState, "GetAudioInState"}, @@ -45,6 +45,8 @@ public: RegisterHandlers(functions); + process->Open(); + if (impl->GetSystem() .Initialize(device_name, in_params, handle, applet_resource_user_id) .IsError()) { @@ -55,6 +57,7 @@ public: ~IAudioIn() override { impl->Free(); service_context.CloseEvent(event); + process->Close(); } [[nodiscard]] std::shared_ptr GetImpl() { @@ -196,6 +199,7 @@ private: KernelHelpers::ServiceContext service_context; Kernel::KEvent* event; + Kernel::KProcess* process; std::shared_ptr impl; Common::ScratchBuffer released_buffer; }; @@ -267,6 +271,14 @@ void AudInU::OpenAudioIn(HLERequestContext& ctx) { auto device_name = Common::StringFromBuffer(device_name_data); auto handle{ctx.GetCopyHandle(0)}; + auto process{ctx.GetObjectFromHandle(handle)}; + if (process.IsNull()) { + LOG_ERROR(Service_Audio, "Failed to get process handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + std::scoped_lock l{impl->mutex}; auto link{impl->LinkToManager()}; if (link.IsError()) { @@ -287,8 +299,9 @@ void AudInU::OpenAudioIn(HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, impl->num_free_sessions); - auto audio_in = std::make_shared(system, *impl, new_session_id, device_name, - in_params, handle, applet_resource_user_id); + auto audio_in = + std::make_shared(system, *impl, new_session_id, device_name, in_params, + process.GetPointerUnsafe(), applet_resource_user_id); impl->sessions[new_session_id] = audio_in->GetImpl(); impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; @@ -318,6 +331,14 @@ void AudInU::OpenAudioInProtocolSpecified(HLERequestContext& ctx) { auto device_name = Common::StringFromBuffer(device_name_data); auto handle{ctx.GetCopyHandle(0)}; + auto process{ctx.GetObjectFromHandle(handle)}; + if (process.IsNull()) { + LOG_ERROR(Service_Audio, "Failed to get process handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + std::scoped_lock l{impl->mutex}; auto link{impl->LinkToManager()}; if (link.IsError()) { @@ -338,8 +359,9 @@ void AudInU::OpenAudioInProtocolSpecified(HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, impl->num_free_sessions); - auto audio_in = std::make_shared(system, *impl, new_session_id, device_name, - in_params, handle, applet_resource_user_id); + auto audio_in = + std::make_shared(system, *impl, new_session_id, device_name, in_params, + process.GetPointerUnsafe(), applet_resource_user_id); impl->sessions[new_session_id] = audio_in->GetImpl(); impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; 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 { public: explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, size_t session_id, const std::string& device_name, - const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) + const AudioOutParameter& in_params, Kernel::KProcess* handle, + u64 applet_resource_user_id) : ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"}, - event{service_context.CreateEvent("AudioOutEvent")}, + event{service_context.CreateEvent("AudioOutEvent")}, process{handle}, impl{std::make_shared(system_, manager, event, session_id)} { // clang-format off @@ -50,11 +51,14 @@ public: }; // clang-format on RegisterHandlers(functions); + + process->Open(); } ~IAudioOut() override { impl->Free(); service_context.CloseEvent(event); + process->Close(); } [[nodiscard]] std::shared_ptr GetImpl() { @@ -206,6 +210,7 @@ private: KernelHelpers::ServiceContext service_context; Kernel::KEvent* event; + Kernel::KProcess* process; std::shared_ptr impl; Common::ScratchBuffer released_buffer; }; @@ -257,6 +262,14 @@ void AudOutU::OpenAudioOut(HLERequestContext& ctx) { auto device_name = Common::StringFromBuffer(device_name_data); auto handle{ctx.GetCopyHandle(0)}; + auto process{ctx.GetObjectFromHandle(handle)}; + if (process.IsNull()) { + LOG_ERROR(Service_Audio, "Failed to get process handle"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + auto link{impl->LinkToManager()}; if (link.IsError()) { LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager"); @@ -276,10 +289,11 @@ void AudOutU::OpenAudioOut(HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id, impl->num_free_sessions); - auto audio_out = std::make_shared(system, *impl, new_session_id, device_name, - in_params, handle, applet_resource_user_id); - result = audio_out->GetImpl()->GetSystem().Initialize(device_name, in_params, handle, - applet_resource_user_id); + auto audio_out = + std::make_shared(system, *impl, new_session_id, device_name, in_params, + process.GetPointerUnsafe(), applet_resource_user_id); + result = audio_out->GetImpl()->GetSystem().Initialize( + device_name, in_params, process.GetPointerUnsafe(), applet_resource_user_id); if (result.IsError()) { LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!"); IPC::ResponseBuilder rb{ctx, 2}; From f2fed21c1139c8d5c030bc5caee5c612dfe7979f Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 7 Jan 2024 13:59:48 -0500 Subject: [PATCH 23/33] kernel: fix page leak on process termination --- src/common/page_table.cpp | 34 +++------- src/core/hle/kernel/k_page_table_base.cpp | 75 ++++++++++++++++++++++- src/core/hle/kernel/k_page_table_base.h | 1 + src/core/hle/kernel/k_process.cpp | 6 ++ 4 files changed, 91 insertions(+), 25 deletions(-) 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 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/page_table.h" +#include "common/scope_exit.h" namespace Common { @@ -11,29 +12,10 @@ PageTable::~PageTable() noexcept = default; bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context, Common::ProcessAddress address) const { - // Setup invalid defaults. - out_entry->phys_addr = 0; - out_entry->block_size = page_size; - out_context->next_page = 0; + out_context->next_offset = GetInteger(address); + out_context->next_page = address / page_size; - // Validate that we can read the actual entry. - const auto page = address / page_size; - if (page >= backing_addr.size()) { - return false; - } - - // Validate that the entry is mapped. - const auto phys_addr = backing_addr[page]; - if (phys_addr == 0) { - return false; - } - - // Populate the results. - out_entry->phys_addr = phys_addr + GetInteger(address); - out_context->next_page = page + 1; - out_context->next_offset = GetInteger(address) + page_size; - - return true; + return this->ContinueTraversal(out_entry, out_context); } bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const { @@ -41,6 +23,12 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c out_entry->phys_addr = 0; out_entry->block_size = page_size; + // Regardless of whether the page was mapped, advance on exit. + SCOPE_EXIT({ + context->next_page += 1; + context->next_offset += page_size; + }); + // Validate that we can read the actual entry. const auto page = context->next_page; if (page >= backing_addr.size()) { @@ -55,8 +43,6 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c // Populate the results. out_entry->phys_addr = phys_addr + context->next_offset; - context->next_page = page + 1; - context->next_offset += page_size; return true; } diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp index 73fbda331..f01eaa164 100644 --- a/src/core/hle/kernel/k_page_table_base.cpp +++ b/src/core/hle/kernel/k_page_table_base.cpp @@ -431,9 +431,82 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool m_memory_block_slab_manager)); } +Result KPageTableBase::FinalizeProcess() { + // Only process tables should be finalized. + ASSERT(!this->IsKernel()); + + // HLE processes don't have memory mapped. + R_SUCCEED_IF(m_impl == nullptr); + + // NOTE: Here Nintendo calls an unknown OnFinalize function. + // this->OnFinalize(); + + // NOTE: Here Nintendo calls a second unknown OnFinalize function. + // this->OnFinalize2(); + + // Get implementation objects. + auto& impl = this->GetImpl(); + auto& mm = m_kernel.MemoryManager(); + + // Traverse, freeing all pages. + { + // Get the address space size. + const size_t as_size = this->GetAddressSpaceSize(); + + // Begin the traversal. + TraversalContext context; + TraversalEntry cur_entry = { + .phys_addr = 0, + .block_size = 0, + }; + + bool cur_valid = false; + TraversalEntry next_entry; + bool next_valid; + size_t tot_size = 0; + + next_valid = impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), + this->GetAddressSpaceStart()); + + // Iterate over entries. + while (true) { + if ((!next_valid && !cur_valid) || + (next_valid && cur_valid && + next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) { + cur_entry.block_size += next_entry.block_size; + } else { + if (cur_valid && IsHeapPhysicalAddressForFinalize(cur_entry.phys_addr)) { + mm.Close(cur_entry.phys_addr, cur_entry.block_size / PageSize); + } + + // Update tracking variables. + tot_size += cur_entry.block_size; + cur_entry = next_entry; + cur_valid = next_valid; + } + + if (cur_entry.block_size + tot_size >= as_size) { + break; + } + + next_valid = + impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); + } + + // Handle the last block. + if (cur_valid && IsHeapPhysicalAddressForFinalize(cur_entry.phys_addr)) { + mm.Close(cur_entry.phys_addr, cur_entry.block_size / PageSize); + } + } + + R_SUCCEED(); +} + void KPageTableBase::Finalize() { + this->FinalizeProcess(); + auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) { - if (Settings::IsFastmemEnabled()) { + if (m_impl->fastmem_arena) { m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false); } }; 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: KResourceLimit* resource_limit, Core::Memory::Memory& memory, KProcessAddress aslr_space_start); + Result FinalizeProcess(); void Finalize(); bool IsKernel() const { diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 068e71dff..ae332a550 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -171,6 +171,12 @@ void KProcess::Finalize() { m_resource_limit->Close(); } + // Clear expensive resources, as the destructor is not called for guest objects. + for (auto& interface : m_arm_interfaces) { + interface.reset(); + } + m_exclusive_monitor.reset(); + // Perform inherited finalization. KSynchronizationObject::Finalize(); } From f90a022d3a20c86399f49a8154847b73bc1b8fd3 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 2 Jan 2024 17:12:16 -0500 Subject: [PATCH 24/33] kernel: fix debugger and process list lifetime --- src/core/debugger/debugger.cpp | 39 +++++++++--- src/core/debugger/gdbstub.cpp | 66 ++++++++++--------- src/core/debugger/gdbstub.h | 15 ++++- src/core/hle/kernel/kernel.cpp | 31 ++++++++- src/core/hle/kernel/kernel.h | 6 +- src/core/hle/kernel/svc/svc_process.cpp | 8 ++- src/core/hle/service/glue/arp.cpp | 7 +- src/core/hle/service/hid/hid.cpp | 10 ++- src/core/hle/service/pm/pm.cpp | 85 +++++++++++-------------- 9 files changed, 160 insertions(+), 107 deletions(-) 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: } Kernel::KThread* GetActiveThread() override { - return state->active_thread; + return state->active_thread.GetPointerUnsafe(); } private: @@ -147,11 +147,14 @@ private: std::scoped_lock lk{connection_lock}; + // Find the process we are going to debug. + SetDebugProcess(); + // Ensure everything is stopped. PauseEmulation(); // Set up the new frontend. - frontend = std::make_unique(*this, system); + frontend = std::make_unique(*this, system, debug_process.GetPointerUnsafe()); // Set the new state. This will tear down any existing state. state = ConnectionState{ @@ -194,15 +197,20 @@ private: UpdateActiveThread(); if (state->info.type == SignalType::Watchpoint) { - frontend->Watchpoint(state->active_thread, *state->info.watchpoint); + frontend->Watchpoint(std::addressof(*state->active_thread), + *state->info.watchpoint); } else { - frontend->Stopped(state->active_thread); + frontend->Stopped(std::addressof(*state->active_thread)); } break; case SignalType::ShuttingDown: frontend->ShuttingDown(); + // Release members. + state->active_thread.Reset(nullptr); + debug_process.Reset(nullptr); + // Wait for emulation to shut down gracefully now. state->signal_pipe.close(); state->client_socket.shutdown(boost::asio::socket_base::shutdown_both); @@ -222,7 +230,7 @@ private: stopped = true; PauseEmulation(); UpdateActiveThread(); - frontend->Stopped(state->active_thread); + frontend->Stopped(state->active_thread.GetPointerUnsafe()); break; } case DebuggerAction::Continue: @@ -232,7 +240,7 @@ private: MarkResumed([&] { state->active_thread->SetStepState(Kernel::StepState::StepPending); state->active_thread->Resume(Kernel::SuspendType::Debug); - ResumeEmulation(state->active_thread); + ResumeEmulation(state->active_thread.GetPointerUnsafe()); }); break; case DebuggerAction::StepThreadLocked: { @@ -255,6 +263,7 @@ private: } void PauseEmulation() { + Kernel::KScopedLightLock ll{debug_process->GetListLock()}; Kernel::KScopedSchedulerLock sl{system.Kernel()}; // Put all threads to sleep on next scheduler round. @@ -264,6 +273,9 @@ private: } void ResumeEmulation(Kernel::KThread* except = nullptr) { + Kernel::KScopedLightLock ll{debug_process->GetListLock()}; + Kernel::KScopedSchedulerLock sl{system.Kernel()}; + // Wake up all threads. for (auto& thread : ThreadList()) { if (std::addressof(thread) == except) { @@ -277,15 +289,16 @@ private: template void MarkResumed(Callback&& cb) { - Kernel::KScopedSchedulerLock sl{system.Kernel()}; stopped = false; cb(); } void UpdateActiveThread() { + Kernel::KScopedLightLock ll{debug_process->GetListLock()}; + auto& threads{ThreadList()}; for (auto& thread : threads) { - if (std::addressof(thread) == state->active_thread) { + if (std::addressof(thread) == state->active_thread.GetPointerUnsafe()) { // Thread is still alive, no need to update. return; } @@ -293,12 +306,18 @@ private: state->active_thread = std::addressof(threads.front()); } +private: + void SetDebugProcess() { + debug_process = std::move(system.Kernel().GetProcessList().back()); + } + Kernel::KProcess::ThreadList& ThreadList() { - return system.ApplicationProcess()->GetThreadList(); + return debug_process->GetThreadList(); } private: System& system; + Kernel::KScopedAutoObject debug_process; std::unique_ptr frontend; boost::asio::io_context io_context; @@ -310,7 +329,7 @@ private: boost::process::async_pipe signal_pipe; SignalInfo info; - Kernel::KThread* active_thread; + Kernel::KScopedAutoObject active_thread; std::array client_data; bool pipe_data; }; diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 4051ed4af..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) { return escaped; } -GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_) - : DebuggerFrontend(backend_), system{system_} { - if (system.ApplicationProcess()->Is64Bit()) { +GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_, Kernel::KProcess* debug_process_) + : DebuggerFrontend(backend_), system{system_}, debug_process{debug_process_} { + if (GetProcess()->Is64Bit()) { arch = std::make_unique(); } else { arch = std::make_unique(); @@ -276,7 +276,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector(strtoll(command.data() + sep, nullptr, 16))}; std::vector mem(size); - if (system.ApplicationMemory().ReadBlock(addr, mem.data(), size)) { + if (GetMemory().ReadBlock(addr, mem.data(), size)) { // Restore any bytes belonging to replaced instructions. auto it = replaced_instructions.lower_bound(addr); for (; it != replaced_instructions.end() && it->first < addr + size; it++) { @@ -310,8 +310,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector(strtoll(command.data() + addr_sep, nullptr, 16))}; const size_t size{static_cast(strtoll(command.data() + size_sep, nullptr, 16))}; - if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) { + if (!GetMemory().IsValidVirtualAddressRange(addr, size)) { SendReply(GDB_STUB_REPLY_ERR); return; } @@ -362,22 +362,20 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) { switch (type) { case BreakpointType::Software: - replaced_instructions[addr] = system.ApplicationMemory().Read32(addr); - system.ApplicationMemory().Write32(addr, arch->BreakpointInstruction()); - Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32)); + replaced_instructions[addr] = GetMemory().Read32(addr); + GetMemory().Write32(addr, arch->BreakpointInstruction()); + Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32)); success = true; break; case BreakpointType::WriteWatch: - success = system.ApplicationProcess()->InsertWatchpoint(addr, size, - Kernel::DebugWatchpointType::Write); + success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write); break; case BreakpointType::ReadWatch: - success = system.ApplicationProcess()->InsertWatchpoint(addr, size, - Kernel::DebugWatchpointType::Read); + success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read); break; case BreakpointType::AccessWatch: - success = system.ApplicationProcess()->InsertWatchpoint( - addr, size, Kernel::DebugWatchpointType::ReadOrWrite); + success = + GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite); break; case BreakpointType::Hardware: default: @@ -400,7 +398,7 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) { const size_t addr{static_cast(strtoll(command.data() + addr_sep, nullptr, 16))}; const size_t size{static_cast(strtoll(command.data() + size_sep, nullptr, 16))}; - if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) { + if (!GetMemory().IsValidVirtualAddressRange(addr, size)) { SendReply(GDB_STUB_REPLY_ERR); return; } @@ -411,24 +409,22 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) { case BreakpointType::Software: { const auto orig_insn{replaced_instructions.find(addr)}; if (orig_insn != replaced_instructions.end()) { - system.ApplicationMemory().Write32(addr, orig_insn->second); - Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32)); + GetMemory().Write32(addr, orig_insn->second); + Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32)); replaced_instructions.erase(addr); success = true; } break; } case BreakpointType::WriteWatch: - success = system.ApplicationProcess()->RemoveWatchpoint(addr, size, - Kernel::DebugWatchpointType::Write); + success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write); break; case BreakpointType::ReadWatch: - success = system.ApplicationProcess()->RemoveWatchpoint(addr, size, - Kernel::DebugWatchpointType::Read); + success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read); break; case BreakpointType::AccessWatch: - success = system.ApplicationProcess()->RemoveWatchpoint( - addr, size, Kernel::DebugWatchpointType::ReadOrWrite); + success = + GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite); break; case BreakpointType::Hardware: default: @@ -466,10 +462,10 @@ void GDBStub::HandleQuery(std::string_view command) { const auto target_xml{arch->GetTargetXML()}; SendReply(PaginateBuffer(target_xml, command.substr(30))); } else if (command.starts_with("Offsets")) { - const auto main_offset = Core::FindMainModuleEntrypoint(system.ApplicationProcess()); + const auto main_offset = Core::FindMainModuleEntrypoint(GetProcess()); SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset))); } else if (command.starts_with("Xfer:libraries:read::")) { - auto modules = Core::FindModules(system.ApplicationProcess()); + auto modules = Core::FindModules(GetProcess()); std::string buffer; buffer += R"()"; @@ -483,7 +479,7 @@ void GDBStub::HandleQuery(std::string_view command) { SendReply(PaginateBuffer(buffer, command.substr(21))); } else if (command.starts_with("fThreadInfo")) { // beginning of list - const auto& threads = system.ApplicationProcess()->GetThreadList(); + const auto& threads = GetProcess()->GetThreadList(); std::vector thread_ids; for (const auto& thread : threads) { thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId())); @@ -497,7 +493,7 @@ void GDBStub::HandleQuery(std::string_view command) { buffer += R"()"; buffer += ""; - const auto& threads = system.ApplicationProcess()->GetThreadList(); + const auto& threads = GetProcess()->GetThreadList(); for (const auto& thread : threads) { auto thread_name{Core::GetThreadName(&thread)}; if (!thread_name) { @@ -613,7 +609,7 @@ void GDBStub::HandleRcmd(const std::vector& command) { std::string_view command_str{reinterpret_cast(&command[0]), command.size()}; std::string reply; - auto* process = system.ApplicationProcess(); + auto* process = GetProcess(); auto& page_table = process->GetPageTable(); const char* commands = "Commands:\n" @@ -714,7 +710,7 @@ void GDBStub::HandleRcmd(const std::vector& command) { } Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { - auto& threads{system.ApplicationProcess()->GetThreadList()}; + auto& threads{GetProcess()->GetThreadList()}; for (auto& thread : threads) { if (thread.GetThreadId() == thread_id) { return std::addressof(thread); @@ -783,4 +779,12 @@ void GDBStub::SendStatus(char status) { backend.WriteToClient(buf); } +Kernel::KProcess* GDBStub::GetProcess() { + return debug_process; +} + +Core::Memory::Memory& GDBStub::GetMemory() { + return GetProcess()->GetMemory(); +} + } // 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 @@ #include "core/debugger/debugger_interface.h" #include "core/debugger/gdbstub_arch.h" +namespace Kernel { +class KProcess; +} + +namespace Core::Memory { +class Memory; +} + namespace Core { class System; class GDBStub : public DebuggerFrontend { public: - explicit GDBStub(DebuggerBackend& backend, Core::System& system); + explicit GDBStub(DebuggerBackend& backend, Core::System& system, + Kernel::KProcess* debug_process); ~GDBStub() override; void Connected() override; @@ -42,8 +51,12 @@ private: void SendReply(std::string_view data); void SendStatus(char status); + Kernel::KProcess* GetProcess(); + Core::Memory::Memory& GetMemory(); + private: Core::System& system; + Kernel::KProcess* debug_process; std::unique_ptr arch; std::vector current_command; std::map replaced_instructions; 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 { old_process->Close(); } - process_list.clear(); + { + std::scoped_lock lk{process_list_lock}; + for (auto* const process : process_list) { + process->Terminate(); + process->Close(); + } + process_list.clear(); + } next_object_id = 0; next_kernel_process_id = KProcess::InitialProcessIdMin; @@ -770,6 +777,7 @@ struct KernelCore::Impl { std::atomic next_thread_id{1}; // Lists all processes that exist in the current session. + std::mutex process_list_lock; std::vector process_list; std::atomic application_process{}; std::unique_ptr global_scheduler_context; @@ -869,9 +877,19 @@ KResourceLimit* KernelCore::GetSystemResourceLimit() { } void KernelCore::AppendNewProcess(KProcess* process) { + process->Open(); + + std::scoped_lock lk{impl->process_list_lock}; impl->process_list.push_back(process); } +void KernelCore::RemoveProcess(KProcess* process) { + std::scoped_lock lk{impl->process_list_lock}; + if (std::erase(impl->process_list, process)) { + process->Close(); + } +} + void KernelCore::MakeApplicationProcess(KProcess* process) { impl->MakeApplicationProcess(process); } @@ -884,8 +902,15 @@ const KProcess* KernelCore::ApplicationProcess() const { return impl->application_process; } -const std::vector& KernelCore::GetProcessList() const { - return impl->process_list; +std::list> KernelCore::GetProcessList() { + std::list> processes; + std::scoped_lock lk{impl->process_list_lock}; + + for (auto* const process : impl->process_list) { + processes.emplace_back(process); + } + + return processes; } Kernel::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 @@ #include #include +#include #include #include #include @@ -116,8 +117,9 @@ public: /// Retrieves a shared pointer to the system resource limit instance. KResourceLimit* GetSystemResourceLimit(); - /// Adds the given shared pointer to an internal list of active processes. + /// Adds/removes the given pointer to an internal list of active processes. void AppendNewProcess(KProcess* process); + void RemoveProcess(KProcess* process); /// Makes the given process the new application process. void MakeApplicationProcess(KProcess* process); @@ -129,7 +131,7 @@ public: const KProcess* ApplicationProcess() const; /// Retrieves the list of processes. - const std::vector& GetProcessList() const; + std::list> GetProcessList(); /// Gets the sole instance of the global scheduler 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 } auto& memory = GetCurrentMemory(kernel); - const auto& process_list = kernel.GetProcessList(); + auto process_list = kernel.GetProcessList(); + auto it = process_list.begin(); + const auto num_processes = process_list.size(); const auto copy_amount = std::min(static_cast(out_process_ids_size), num_processes); - for (std::size_t i = 0; i < copy_amount; ++i) { - memory.Write64(out_process_ids, process_list[i]->GetProcessId()); + for (std::size_t i = 0; i < copy_amount && it != process_list.end(); ++i, ++it) { + memory.Write64(out_process_ids, (*it)->GetProcessId()); out_process_ids += sizeof(u64); } 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 @@ namespace Service::Glue { namespace { -std::optional GetTitleIDForProcessID(const Core::System& system, u64 process_id) { - const auto& list = system.Kernel().GetProcessList(); - const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) { +std::optional GetTitleIDForProcessID(Core::System& system, u64 process_id) { + auto list = system.Kernel().GetProcessList(); + + const auto iter = std::find_if(list.begin(), list.end(), [&process_id](auto& process) { return process->GetProcessId() == process_id; }); diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index fc03a0a5f..4ce0a9834 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -22,12 +22,10 @@ void LoopProcess(Core::System& system) { std::shared_ptr firmware_settings = std::make_shared(); - // TODO: Remove this hack until this service is emulated properly. - const auto process_list = system.Kernel().GetProcessList(); - if (!process_list.empty()) { - resource_manager->Initialize(); - resource_manager->RegisterAppletResourceUserId(process_list[0]->GetId(), true); - } + // TODO: Remove this hack when am is emulated properly. + resource_manager->Initialize(); + resource_manager->RegisterAppletResourceUserId(system.ApplicationProcess()->GetProcessId(), + true); server_manager->RegisterNamedService( "hid", std::make_shared(system, resource_manager, firmware_settings)); 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}; constexpr u64 NO_PROCESS_FOUND_PID{0}; -std::optional SearchProcessList( - const std::vector& process_list, - std::function predicate) { +using ProcessList = std::list>; + +template +Kernel::KScopedAutoObject SearchProcessList(ProcessList& process_list, + F&& predicate) { const auto iter = std::find_if(process_list.begin(), process_list.end(), predicate); if (iter == process_list.end()) { - return std::nullopt; + return nullptr; } - return *iter; + return iter->GetPointerUnsafe(); } -void GetApplicationPidGeneric(HLERequestContext& ctx, - const std::vector& process_list) { - const auto process = SearchProcessList(process_list, [](const auto& proc) { - return proc->GetProcessId() == Kernel::KProcess::ProcessIdMin; - }); +void GetApplicationPidGeneric(HLERequestContext& ctx, ProcessList& process_list) { + auto process = SearchProcessList(process_list, [](auto& p) { return p->IsApplication(); }); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); - rb.Push(process.has_value() ? (*process)->GetProcessId() : NO_PROCESS_FOUND_PID); + rb.Push(process.IsNull() ? NO_PROCESS_FOUND_PID : process->GetProcessId()); } } // Anonymous namespace @@ -80,8 +79,7 @@ private: class DebugMonitor final : public ServiceFramework { public: - explicit DebugMonitor(Core::System& system_) - : ServiceFramework{system_, "pm:dmnt"}, kernel{system_.Kernel()} { + explicit DebugMonitor(Core::System& system_) : ServiceFramework{system_, "pm:dmnt"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetJitDebugProcessIdList"}, @@ -106,12 +104,11 @@ private: LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id); - const auto process = - SearchProcessList(kernel.GetProcessList(), [program_id](const auto& proc) { - return proc->GetProgramId() == program_id; - }); + auto list = kernel.GetProcessList(); + auto process = SearchProcessList( + list, [program_id](auto& p) { return p->GetProgramId() == program_id; }); - if (!process.has_value()) { + if (process.IsNull()) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultProcessNotFound); return; @@ -119,12 +116,13 @@ private: IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); - rb.Push((*process)->GetProcessId()); + rb.Push(process->GetProcessId()); } void GetApplicationProcessId(HLERequestContext& ctx) { LOG_DEBUG(Service_PM, "called"); - GetApplicationPidGeneric(ctx, kernel.GetProcessList()); + auto list = kernel.GetProcessList(); + GetApplicationPidGeneric(ctx, list); } void AtmosphereGetProcessInfo(HLERequestContext& ctx) { @@ -135,11 +133,10 @@ private: LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid); - const auto process = SearchProcessList(kernel.GetProcessList(), [pid](const auto& proc) { - return proc->GetProcessId() == pid; - }); + auto list = kernel.GetProcessList(); + auto process = SearchProcessList(list, [pid](auto& p) { return p->GetProcessId() == pid; }); - if (!process.has_value()) { + if (process.IsNull()) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultProcessNotFound); return; @@ -159,7 +156,7 @@ private: OverrideStatus override_status{}; ProgramLocation program_location{ - .program_id = (*process)->GetProgramId(), + .program_id = process->GetProgramId(), .storage_id = 0, }; @@ -169,14 +166,11 @@ private: rb.PushRaw(program_location); rb.PushRaw(override_status); } - - const Kernel::KernelCore& kernel; }; class Info final : public ServiceFramework { public: - explicit Info(Core::System& system_, const std::vector& process_list_) - : ServiceFramework{system_, "pm:info"}, process_list{process_list_} { + explicit Info(Core::System& system_) : ServiceFramework{system_, "pm:info"} { static const FunctionInfo functions[] = { {0, &Info::GetProgramId, "GetProgramId"}, {65000, &Info::AtmosphereGetProcessId, "AtmosphereGetProcessId"}, @@ -193,11 +187,11 @@ private: LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id); - const auto process = SearchProcessList(process_list, [process_id](const auto& proc) { - return proc->GetProcessId() == process_id; - }); + auto list = kernel.GetProcessList(); + auto process = SearchProcessList( + list, [process_id](auto& p) { return p->GetProcessId() == process_id; }); - if (!process.has_value()) { + if (process.IsNull()) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultProcessNotFound); return; @@ -205,7 +199,7 @@ private: IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); - rb.Push((*process)->GetProgramId()); + rb.Push(process->GetProgramId()); } void AtmosphereGetProcessId(HLERequestContext& ctx) { @@ -214,11 +208,11 @@ private: LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id); - const auto process = SearchProcessList(process_list, [program_id](const auto& proc) { - return proc->GetProgramId() == program_id; - }); + auto list = system.Kernel().GetProcessList(); + auto process = SearchProcessList( + list, [program_id](auto& p) { return p->GetProgramId() == program_id; }); - if (!process.has_value()) { + if (process.IsNull()) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultProcessNotFound); return; @@ -226,16 +220,13 @@ private: IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); - rb.Push((*process)->GetProcessId()); + rb.Push(process->GetProcessId()); } - - const std::vector& process_list; }; class Shell final : public ServiceFramework { public: - explicit Shell(Core::System& system_) - : ServiceFramework{system_, "pm:shell"}, kernel{system_.Kernel()} { + explicit Shell(Core::System& system_) : ServiceFramework{system_, "pm:shell"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "LaunchProgram"}, @@ -257,10 +248,9 @@ public: private: void GetApplicationProcessIdForShell(HLERequestContext& ctx) { LOG_DEBUG(Service_PM, "called"); - GetApplicationPidGeneric(ctx, kernel.GetProcessList()); + auto list = kernel.GetProcessList(); + GetApplicationPidGeneric(ctx, list); } - - const Kernel::KernelCore& kernel; }; void LoopProcess(Core::System& system) { @@ -268,8 +258,7 @@ void LoopProcess(Core::System& system) { server_manager->RegisterNamedService("pm:bm", std::make_shared(system)); server_manager->RegisterNamedService("pm:dmnt", std::make_shared(system)); - server_manager->RegisterNamedService( - "pm:info", std::make_shared(system, system.Kernel().GetProcessList())); + server_manager->RegisterNamedService("pm:info", std::make_shared(system)); server_manager->RegisterNamedService("pm:shell", std::make_shared(system)); ServerManager::RunServer(std::move(server_manager)); } From 2f0b57ca13fb91730d7e210f6f4504357ef6cd0a Mon Sep 17 00:00:00 2001 From: Liam Date: Fri, 12 Jan 2024 19:19:07 -0500 Subject: [PATCH 25/33] kernel: optimize page free on shutdown --- .../hle/kernel/k_memory_block_manager.cpp | 4 +- src/core/hle/kernel/k_memory_block_manager.h | 4 +- src/core/hle/kernel/k_page_table_base.cpp | 73 ++++--------------- 3 files changed, 18 insertions(+), 63 deletions(-) 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, } void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager, - HostUnmapCallback&& host_unmap_callback) { + BlockCallback&& block_callback) { // Erase every block until we have none left. auto it = m_memory_block_tree.begin(); while (it != m_memory_block_tree.end()) { KMemoryBlock* block = std::addressof(*it); it = m_memory_block_tree.erase(it); + block_callback(block->GetAddress(), block->GetSize()); slab_manager->Free(block); - host_unmap_callback(block->GetAddress(), block->GetSize()); } 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: public: KMemoryBlockManager(); - using HostUnmapCallback = std::function; + using BlockCallback = std::function; Result Initialize(KProcessAddress st, KProcessAddress nd, KMemoryBlockSlabManager* slab_manager); - void Finalize(KMemoryBlockSlabManager* slab_manager, HostUnmapCallback&& host_unmap_callback); + void Finalize(KMemoryBlockSlabManager* slab_manager, BlockCallback&& block_callback); iterator end() { 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 f01eaa164..3f0a39d33 100644 --- a/src/core/hle/kernel/k_page_table_base.cpp +++ b/src/core/hle/kernel/k_page_table_base.cpp @@ -435,69 +435,14 @@ Result KPageTableBase::FinalizeProcess() { // Only process tables should be finalized. ASSERT(!this->IsKernel()); - // HLE processes don't have memory mapped. - R_SUCCEED_IF(m_impl == nullptr); - // NOTE: Here Nintendo calls an unknown OnFinalize function. // this->OnFinalize(); // NOTE: Here Nintendo calls a second unknown OnFinalize function. // this->OnFinalize2(); - // Get implementation objects. - auto& impl = this->GetImpl(); - auto& mm = m_kernel.MemoryManager(); - - // Traverse, freeing all pages. - { - // Get the address space size. - const size_t as_size = this->GetAddressSpaceSize(); - - // Begin the traversal. - TraversalContext context; - TraversalEntry cur_entry = { - .phys_addr = 0, - .block_size = 0, - }; - - bool cur_valid = false; - TraversalEntry next_entry; - bool next_valid; - size_t tot_size = 0; - - next_valid = impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), - this->GetAddressSpaceStart()); - - // Iterate over entries. - while (true) { - if ((!next_valid && !cur_valid) || - (next_valid && cur_valid && - next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) { - cur_entry.block_size += next_entry.block_size; - } else { - if (cur_valid && IsHeapPhysicalAddressForFinalize(cur_entry.phys_addr)) { - mm.Close(cur_entry.phys_addr, cur_entry.block_size / PageSize); - } - - // Update tracking variables. - tot_size += cur_entry.block_size; - cur_entry = next_entry; - cur_valid = next_valid; - } - - if (cur_entry.block_size + tot_size >= as_size) { - break; - } - - next_valid = - impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); - } - - // Handle the last block. - if (cur_valid && IsHeapPhysicalAddressForFinalize(cur_entry.phys_addr)) { - mm.Close(cur_entry.phys_addr, cur_entry.block_size / PageSize); - } - } + // NOTE: Here Nintendo does a page table walk to discover heap pages to free. + // We will use the block manager finalization below to free them. R_SUCCEED(); } @@ -505,14 +450,24 @@ Result KPageTableBase::FinalizeProcess() { void KPageTableBase::Finalize() { this->FinalizeProcess(); - auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) { + auto BlockCallback = [&](KProcessAddress addr, u64 size) { if (m_impl->fastmem_arena) { m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false); } + + // Get physical pages. + KPageGroup pg(m_kernel, m_block_info_manager); + this->MakePageGroup(pg, addr, size / PageSize); + + // Free the pages. + pg.CloseAndReset(); }; // Finalize memory blocks. - m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(HostUnmapCallback)); + { + KScopedLightLock lk(m_general_lock); + m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(BlockCallback)); + } // Free any unsafe mapped memory. if (m_mapped_unsafe_physical_memory) { From 76880b84f9923a3fbdb53edb049d635d33de5e76 Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 13 Jan 2024 13:45:05 -0500 Subject: [PATCH 26/33] loader: fix homebrew nro registration --- src/core/loader/nro.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 83371fcbd..f8225d697 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -275,12 +275,12 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::S return {ResultStatus::ErrorLoadingNRO, {}}; } - if (romfs != nullptr) { - system.GetFileSystemController().RegisterProcess( - process.GetProcessId(), {}, - std::make_unique(*this, system.GetContentProvider(), - system.GetFileSystemController())); - } + u64 program_id{}; + ReadProgramId(program_id); + system.GetFileSystemController().RegisterProcess( + process.GetProcessId(), program_id, + std::make_unique(*this, system.GetContentProvider(), + system.GetFileSystemController())); is_loaded = true; return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority, From bee22540a1d8a7b3ebd9ff4c244bf257b5e9f8b7 Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 13 Jan 2024 14:19:44 -0600 Subject: [PATCH 27/33] service: acc: Only save profiles when profiles have changed --- src/core/hle/service/acc/profile_manager.cpp | 19 ++++++++++++++++--- src/core/hle/service/acc/profile_manager.h | 1 + .../configure_profile_manager.cpp | 4 ++++ 3 files changed, 21 insertions(+), 3 deletions(-) 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() { OpenUser(*GetUser(current)); } -ProfileManager::~ProfileManager() { - WriteUserSaveFile(); -} +ProfileManager::~ProfileManager() = default; /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the /// internal management of the users profiles @@ -113,6 +111,8 @@ Result ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& username) return ERROR_USER_ALREADY_EXISTS; } + is_save_needed = true; + return AddUser({ .user_uuid = uuid, .username = username, @@ -326,6 +326,9 @@ bool ProfileManager::RemoveUser(UUID uuid) { profiles[*index] = ProfileInfo{}; std::stable_partition(profiles.begin(), profiles.end(), [](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); }); + + is_save_needed = true; + return true; } @@ -340,6 +343,8 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { profile.username = profile_new.username; profile.creation_time = profile_new.timestamp; + is_save_needed = true; + return true; } @@ -348,6 +353,7 @@ bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& const auto index = GetUserIndex(uuid); if (index.has_value() && SetProfileBase(uuid, profile_new)) { profiles[*index].data = data_new; + is_save_needed = true; return true; } @@ -391,6 +397,10 @@ void ProfileManager::ParseUserSaveFile() { } void ProfileManager::WriteUserSaveFile() { + if (!is_save_needed) { + return; + } + ProfileDataRaw raw{}; for (std::size_t i = 0; i < MAX_USERS; ++i) { @@ -423,7 +433,10 @@ void ProfileManager::WriteUserSaveFile() { if (!save.IsOpen() || !save.SetSize(sizeof(ProfileDataRaw)) || !save.WriteObject(raw)) { LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " "made in current session will be saved."); + return; } + + is_save_needed = false; } }; // 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: std::optional AddToProfiles(const ProfileInfo& profile); bool RemoveProfileAtIndex(std::size_t index); + bool is_save_needed{}; std::array profiles{}; std::array stored_opened_profiles{}; std::size_t user_count{}; 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() { const auto uuid = Common::UUID::MakeRandom(); profile_manager.CreateNewUser(uuid, username.toStdString()); + profile_manager.WriteUserSaveFile(); item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)}); } @@ -228,6 +229,7 @@ void ConfigureProfileManager::RenameUser() { std::copy(username_std.begin(), username_std.end(), profile.username.begin()); profile_manager.SetProfileBase(*uuid, profile); + profile_manager.WriteUserSaveFile(); item_model->setItem( user, 0, @@ -256,6 +258,8 @@ void ConfigureProfileManager::DeleteUser(const Common::UUID& uuid) { return; } + profile_manager.WriteUserSaveFile(); + item_model->removeRows(tree_view->currentIndex().row(), 1); tree_view->clearSelection(); From cdeaca73c460fa4a85d2f7d493828711f90e8747 Mon Sep 17 00:00:00 2001 From: t895 Date: Thu, 11 Jan 2024 21:53:11 -0500 Subject: [PATCH 28/33] android: Move ktlintCheck to yuzu-verify --- .ci/scripts/format/script.sh | 3 +++ .github/workflows/verify.yml | 8 +++++--- src/android/app/build.gradle.kts | 9 ++++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.ci/scripts/format/script.sh b/.ci/scripts/format/script.sh index c22398de0..f9c63dbfa 100755 --- a/.ci/scripts/format/script.sh +++ b/.ci/scripts/format/script.sh @@ -32,3 +32,6 @@ if [ ! -z "$DIFF" ]; then echo "$DIFF" exit 1 fi + +cd src/android +./gradlew ktlintCheck 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: format: name: 'verify format' runs-on: ubuntu-latest - container: - image: yuzuemu/build-environments:linux-clang-format - options: -u 1001 steps: - uses: actions/checkout@v3 with: submodules: false + - name: set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' - name: 'Verify Formatting' run: bash -ex ./.ci/scripts/format/script.sh build: diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index 53aafa08c..d62254dd3 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -188,8 +188,15 @@ tasks.create("ktlintReset") { delete(File(buildDir.path + File.separator + "intermediates/ktLint")) } +val showFormatHelp = { + logger.lifecycle( + "If this check fails, please try running \"gradlew ktlintFormat\" for automatic " + + "codestyle fixes" + ) +} +tasks.getByPath("ktlintKotlinScriptCheck").doFirst { showFormatHelp.invoke() } +tasks.getByPath("ktlintMainSourceSetCheck").doFirst { showFormatHelp.invoke() } tasks.getByPath("loadKtlintReporters").dependsOn("ktlintReset") -tasks.getByPath("preBuild").dependsOn("ktlintCheck") ktlint { version.set("0.47.1") From 15d8a405296a0c1902cab1320d0894feb3129a2d Mon Sep 17 00:00:00 2001 From: t895 Date: Sat, 13 Jan 2024 18:06:33 -0500 Subject: [PATCH 29/33] android: Clean up git commands in build.gradle --- src/android/app/build.gradle.kts | 78 ++++++++------------------------ 1 file changed, 20 insertions(+), 58 deletions(-) diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index 53aafa08c..7318338fe 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -228,71 +228,33 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") } -fun getGitVersion(): String { - var versionName = "0.0" - - try { - versionName = ProcessBuilder("git", "describe", "--always", "--long") +fun runGitCommand(command: List): String { + return try { + ProcessBuilder(command) .directory(project.rootDir) .redirectOutput(ProcessBuilder.Redirect.PIPE) .redirectError(ProcessBuilder.Redirect.PIPE) .start().inputStream.bufferedReader().use { it.readText() } .trim() + } catch (e: Exception) { + logger.error("Cannot find git") + "" + } +} + +fun getGitVersion(): String { + val versionName = if (System.getenv("GITHUB_ACTIONS") != null) { + val gitTag = System.getenv("GIT_TAG_NAME") ?: "" + gitTag + } else { + runGitCommand(listOf("git", "describe", "--always", "--long")) .replace(Regex("(-0)?-[^-]+$"), "") - } catch (e: Exception) { - logger.error("Cannot find git, defaulting to dummy version number") } - - if (System.getenv("GITHUB_ACTIONS") != null) { - val gitTag = System.getenv("GIT_TAG_NAME") - versionName = gitTag ?: versionName - } - - return versionName + return versionName.ifEmpty { "0.0" } } -fun getGitHash(): String { - try { - val processBuilder = ProcessBuilder("git", "rev-parse", "--short", "HEAD") - processBuilder.directory(project.rootDir) - val process = processBuilder.start() - val inputStream = process.inputStream - val errorStream = process.errorStream - process.waitFor() +fun getGitHash(): String = + runGitCommand(listOf("git", "rev-parse", "--short", "HEAD")).ifEmpty { "dummy-hash" } - return if (process.exitValue() == 0) { - inputStream.bufferedReader() - .use { it.readText().trim() } // return the value of gitHash - } else { - val errorMessage = errorStream.bufferedReader().use { it.readText().trim() } - logger.error("Error running git command: $errorMessage") - "dummy-hash" // return a dummy hash value in case of an error - } - } catch (e: Exception) { - logger.error("$e: Cannot find git, defaulting to dummy build hash") - return "dummy-hash" // return a dummy hash value in case of an error - } -} - -fun getBranch(): String { - try { - val processBuilder = ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD") - processBuilder.directory(project.rootDir) - val process = processBuilder.start() - val inputStream = process.inputStream - val errorStream = process.errorStream - process.waitFor() - - return if (process.exitValue() == 0) { - inputStream.bufferedReader() - .use { it.readText().trim() } // return the value of gitHash - } else { - val errorMessage = errorStream.bufferedReader().use { it.readText().trim() } - logger.error("Error running git command: $errorMessage") - "dummy-hash" // return a dummy hash value in case of an error - } - } catch (e: Exception) { - logger.error("$e: Cannot find git, defaulting to dummy build hash") - return "dummy-hash" // return a dummy hash value in case of an error - } -} +fun getBranch(): String = + runGitCommand(listOf("git", "rev-parse", "--abbrev-ref", "HEAD")).ifEmpty { "dummy-hash" } From 7b3941e5d47915590f477b7900fcbd1759168295 Mon Sep 17 00:00:00 2001 From: t895 Date: Sat, 13 Jan 2024 18:12:19 -0500 Subject: [PATCH 30/33] android: Show version name instead of git hash in the about fragment --- .../main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt | 4 ++-- src/android/app/src/main/res/layout-w600dp/fragment_about.xml | 4 ++-- src/android/app/src/main/res/layout/fragment_about.xml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) 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() { binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment) } - binding.textBuildHash.text = BuildConfig.GIT_HASH - binding.buttonBuildHash.setOnClickListener { + binding.textVersionName.text = BuildConfig.VERSION_NAME + binding.textVersionName.setOnClickListener { val clipBoard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText(getString(R.string.build), BuildConfig.GIT_HASH) 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 @@ android:layout_marginHorizontal="20dp" /> Date: Wed, 3 Jan 2024 23:37:41 +0200 Subject: [PATCH 31/33] core: Support multiple modules per patcher --- src/core/arm/nce/patcher.cpp | 83 ++++++++++++------- src/core/arm/nce/patcher.h | 27 +++--- src/core/hle/kernel/k_process.cpp | 4 +- .../loader/deconstructed_rom_directory.cpp | 75 +++++++++++++---- src/core/loader/nso.cpp | 41 ++++----- src/core/loader/nso.h | 3 +- 6 files changed, 154 insertions(+), 79 deletions(-) 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; constexpr size_t MaxRelativeBranch = 128_MiB; constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32); -Patcher::Patcher() : c(m_patch_instructions) {} - -Patcher::~Patcher() = default; - -void Patcher::PatchText(const Kernel::PhysicalMemory& program_image, - const Kernel::CodeSet::Segment& code) { - // Branch to the first instruction of the module. - this->BranchToModule(0); +Patcher::Patcher() : c(m_patch_instructions) { + // The first word of the patch section is always a branch to the first instruction of the + // module. + c.dw(0); // Write save context helper function. c.l(m_save_context); @@ -38,6 +34,25 @@ void Patcher::PatchText(const Kernel::PhysicalMemory& program_image, // Write load context helper function. c.l(m_load_context); WriteLoadContext(); +} + +Patcher::~Patcher() = default; + +bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image, + const Kernel::CodeSet::Segment& code) { + // If we have patched modules but cannot reach the new module, then it needs its own patcher. + const size_t image_size = program_image.size(); + if (total_program_size + image_size > MaxRelativeBranch && total_program_size > 0) { + return false; + } + + // Add a new module patch to our list + modules.emplace_back(); + curr_patch = &modules.back(); + + // The first word of the patch section is always a branch to the first instruction of the + // module. + curr_patch->m_branch_to_module_relocations.push_back({0, 0}); // Retrieve text segment data. const auto text = std::span{program_image}.subspan(code.offset, code.size); @@ -94,16 +109,17 @@ void Patcher::PatchText(const Kernel::PhysicalMemory& program_image, } if (auto exclusive = Exclusive{inst}; exclusive.Verify()) { - m_exclusives.push_back(i); + curr_patch->m_exclusives.push_back(i); } } // Determine patching mode for the final relocation step - const size_t image_size = program_image.size(); + total_program_size += image_size; this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData; + return true; } -void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, +bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines) { @@ -120,7 +136,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, if (mode == PatchMode::PreText) { rc.B(rel.patch_offset - patch_size - rel.module_offset); } else { - rc.B(image_size - rel.module_offset + rel.patch_offset); + rc.B(total_program_size - rel.module_offset + rel.patch_offset); } }; @@ -129,7 +145,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, if (mode == PatchMode::PreText) { rc.B(patch_size - rel.patch_offset + rel.module_offset); } else { - rc.B(rel.module_offset - image_size - rel.patch_offset); + rc.B(rel.module_offset - total_program_size - rel.patch_offset); } }; @@ -137,7 +153,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, if (mode == PatchMode::PreText) { return GetInteger(load_base) + patch_offset; } else { - return GetInteger(load_base) + image_size + patch_offset; + return GetInteger(load_base) + total_program_size + patch_offset; } }; @@ -150,39 +166,50 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, }; // We are now ready to relocate! - for (const Relocation& rel : m_branch_to_patch_relocations) { + auto& patch = modules[m_relocate_module_index++]; + for (const Relocation& rel : patch.m_branch_to_patch_relocations) { ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel); } - for (const Relocation& rel : m_branch_to_module_relocations) { + for (const Relocation& rel : patch.m_branch_to_module_relocations) { ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32), rel); } // Rewrite PC constants and record post trampolines - for (const Relocation& rel : m_write_module_pc_relocations) { + for (const Relocation& rel : patch.m_write_module_pc_relocations) { oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)}; rc.dx(RebasePc(rel.module_offset)); } - for (const Trampoline& rel : m_trampolines) { + for (const Trampoline& rel : patch.m_trampolines) { out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)}); } // Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not. // Convert to ordered to preserve this assumption. - for (const ModuleTextAddress i : m_exclusives) { + for (const ModuleTextAddress i : patch.m_exclusives) { auto exclusive = Exclusive{text_words[i]}; text_words[i] = exclusive.AsOrdered(); } - // Copy to program image - if (this->mode == PatchMode::PreText) { - std::memcpy(program_image.data(), m_patch_instructions.data(), - m_patch_instructions.size() * sizeof(u32)); - } else { - program_image.resize(image_size + patch_size); - std::memcpy(program_image.data() + image_size, m_patch_instructions.data(), - m_patch_instructions.size() * sizeof(u32)); + // Remove the patched module size from the total. This is done so total_program_size + // always represents the distance from the currently patched module to the patch section. + total_program_size -= image_size; + + // Only copy to the program image of the last module + if (m_relocate_module_index == modules.size()) { + if (this->mode == PatchMode::PreText) { + ASSERT(image_size == total_program_size); + std::memcpy(program_image.data(), m_patch_instructions.data(), + m_patch_instructions.size() * sizeof(u32)); + } else { + program_image.resize(image_size + patch_size); + std::memcpy(program_image.data() + image_size, m_patch_instructions.data(), + m_patch_instructions.size() * sizeof(u32)); + } + return true; } + + return false; } size_t Patcher::GetSectionSize() const noexcept { @@ -322,7 +349,7 @@ void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) { // Write the post-SVC trampoline address, which will jump back to the guest after restoring its // state. - m_trampolines.push_back({c.offset(), module_dest}); + curr_patch->m_trampolines.push_back({c.offset(), module_dest}); // Host called this location. Save the return address so we can // 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: explicit Patcher(); ~Patcher(); - void PatchText(const Kernel::PhysicalMemory& program_image, + bool PatchText(const Kernel::PhysicalMemory& program_image, const Kernel::CodeSet::Segment& code); - void RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, + bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines); size_t GetSectionSize() const noexcept; @@ -61,16 +61,16 @@ private: private: void BranchToPatch(uintptr_t module_dest) { - m_branch_to_patch_relocations.push_back({c.offset(), module_dest}); + curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), module_dest}); } void BranchToModule(uintptr_t module_dest) { - m_branch_to_module_relocations.push_back({c.offset(), module_dest}); + curr_patch->m_branch_to_module_relocations.push_back({c.offset(), module_dest}); c.dw(0); } void WriteModulePc(uintptr_t module_dest) { - m_write_module_pc_relocations.push_back({c.offset(), module_dest}); + curr_patch->m_write_module_pc_relocations.push_back({c.offset(), module_dest}); c.dx(0); } @@ -84,15 +84,22 @@ private: uintptr_t module_offset; ///< Offset in bytes from the start of the text section. }; + struct ModulePatch { + std::vector m_trampolines; + std::vector m_branch_to_patch_relocations{}; + std::vector m_branch_to_module_relocations{}; + std::vector m_write_module_pc_relocations{}; + std::vector m_exclusives{}; + }; + oaknut::VectorCodeGenerator c; - std::vector m_trampolines; - std::vector m_branch_to_patch_relocations{}; - std::vector m_branch_to_module_relocations{}; - std::vector m_write_module_pc_relocations{}; - std::vector m_exclusives{}; oaknut::Label m_save_context{}; oaknut::Label m_load_context{}; PatchMode mode{PatchMode::None}; + size_t total_program_size{}; + size_t m_relocate_module_index{}; + std::vector modules; + ModulePatch* curr_patch; }; } // namespace Core::NCE diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 068e71dff..8839ddbc2 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -1233,10 +1233,10 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) { ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); #ifdef HAS_NCE - if (this->IsApplication() && Settings::IsNceEnabled()) { + const auto& patch = code_set.PatchSegment(); + if (this->IsApplication() && Settings::IsNceEnabled() && patch.size != 0) { auto& buffer = m_kernel.System().DeviceMemory().buffer; const auto& code = code_set.CodeSegment(); - const auto& patch = code_set.PatchSegment(); buffer.Protect(GetInteger(base_addr + code.addr), code.size, Common::MemoryPermission::Read | Common::MemoryPermission::Execute); buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index c9f8707b7..b2173f697 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -19,8 +19,54 @@ #include "core/arm/nce/patcher.h" #endif +#ifndef HAS_NCE +namespace Core::NCE { +class Patcher {}; +} // namespace Core::NCE +#endif + namespace Loader { +struct PatchCollection { + explicit PatchCollection(bool is_application_) : is_application{is_application_} { + module_patcher_indices.fill(-1); + patchers.emplace_back(); + } + + std::vector* GetPatchers() { + if (is_application && Settings::IsNceEnabled()) { + return &patchers; + } + return nullptr; + } + + size_t GetTotalPatchSize() const { + size_t total_size{}; +#ifdef HAS_NCE + for (auto& patcher : patchers) { + total_size += patcher.GetSectionSize(); + } +#endif + return total_size; + } + + void SaveIndex(size_t module) { + module_patcher_indices[module] = static_cast(patchers.size() - 1); + } + + s32 GetIndex(size_t module) const { + return module_patcher_indices[module]; + } + + s32 GetLastIndex() const { + return static_cast(patchers.size()) - 1; + } + + bool is_application; + std::vector patchers; + std::array module_patcher_indices{}; +}; + AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, bool override_update_) : AppLoader(std::move(file_)), override_update(override_update_), is_hbl(false) { @@ -142,18 +188,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect std::size_t code_size{}; // Define an nce patch context for each potential module. -#ifdef HAS_NCE - std::array module_patchers; -#endif - - const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* { -#ifdef HAS_NCE - if (is_application && Settings::IsNceEnabled()) { - return &module_patchers[i]; - } -#endif - return nullptr; - }; + PatchCollection patch_ctx{is_application}; // Use the NSO module loader to figure out the code layout for (size_t i = 0; i < static_modules.size(); i++) { @@ -164,13 +199,14 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect } const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; - const auto tentative_next_load_addr = - AppLoader_NSO::LoadModule(process, system, *module_file, code_size, - should_pass_arguments, false, {}, GetPatcher(i)); + const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( + process, system, *module_file, code_size, should_pass_arguments, false, {}, + patch_ctx.GetPatchers(), patch_ctx.GetLastIndex()); if (!tentative_next_load_addr) { return {ResultStatus::ErrorLoadingNSO, {}}; } + patch_ctx.SaveIndex(i); code_size = *tentative_next_load_addr; } @@ -184,6 +220,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect return 0; }(); + // Add patch size to the total module size + code_size += patch_ctx.GetTotalPatchSize(); + // Setup the process code layout if (process.LoadFromMetadata(metadata, code_size, fastmem_base, is_hbl).IsError()) { return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; @@ -204,9 +243,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect const VAddr load_addr{next_load_addr}; const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; - const auto tentative_next_load_addr = - AppLoader_NSO::LoadModule(process, system, *module_file, load_addr, - should_pass_arguments, true, pm, GetPatcher(i)); + const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( + process, system, *module_file, load_addr, should_pass_arguments, true, pm, + patch_ctx.GetPatchers(), patch_ctx.GetIndex(i)); if (!tentative_next_load_addr) { return {ResultStatus::ErrorLoadingNSO, {}}; } 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 AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: const FileSys::VfsFile& nso_file, VAddr load_base, bool should_pass_arguments, bool load_into_process, std::optional pm, - Core::NCE::Patcher* patch) { + std::vector* patches, + s32 patch_index) { if (nso_file.GetSize() < sizeof(NSOHeader)) { return std::nullopt; } @@ -94,8 +95,11 @@ std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: // Allocate some space at the beginning if we are patching in PreText mode. const size_t module_start = [&]() -> size_t { #ifdef HAS_NCE - if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::PreText) { - return patch->GetSectionSize(); + if (patches && load_into_process) { + auto* patch = &patches->operator[](patch_index); + if (patch->GetPatchMode() == Core::NCE::PatchMode::PreText) { + return patch->GetSectionSize(); + } } #endif return 0; @@ -160,27 +164,24 @@ std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: #ifdef HAS_NCE // If we are computing the process code layout and using nce backend, patch. const auto& code = codeset.CodeSegment(); - if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::None) { + auto* patch = patches ? &patches->operator[](patch_index) : nullptr; + if (patch && !load_into_process) { // Patch SVCs and MRS calls in the guest code - patch->PatchText(program_image, code); - - // Add patch section size to the module size. - image_size += static_cast(patch->GetSectionSize()); + while (!patch->PatchText(program_image, code)) { + patch = &patches->emplace_back(); + } } else if (patch) { // Relocate code patch and copy to the program_image. - patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers()); - - // Update patch section. - auto& patch_segment = codeset.PatchSegment(); - patch_segment.addr = - patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size; - patch_segment.size = static_cast(patch->GetSectionSize()); - - // Add patch section size to the module size. In PreText mode image_size - // already contains the patch segment as part of module_start. - if (patch->GetPatchMode() == Core::NCE::PatchMode::PostData) { - image_size += patch_segment.size; + if (patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers())) { + // Update patch section. + auto& patch_segment = codeset.PatchSegment(); + patch_segment.addr = + patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size; + patch_segment.size = static_cast(patch->GetSectionSize()); } + + // Refresh image_size to take account the patch section if it was added by RelocateAndCopy + image_size = static_cast(program_image.size()); } #endif 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: const FileSys::VfsFile& nso_file, VAddr load_base, bool should_pass_arguments, bool load_into_process, std::optional pm = {}, - Core::NCE::Patcher* patch = nullptr); + std::vector* patches = nullptr, + s32 patch_index = -1); LoadResult Load(Kernel::KProcess& process, Core::System& system) override; From 954eb402375313093fbde8f31929ffc66ecccc75 Mon Sep 17 00:00:00 2001 From: t895 Date: Mon, 15 Jan 2024 10:30:57 -0500 Subject: [PATCH 32/33] ci: Remove format step from mainline builds --- .ci/yuzu-mainline-step2.yml | 10 ---------- 1 file changed, 10 deletions(-) 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: DisplayVersion: $[counter(variables['DisplayPrefix'], 1)] stages: -- stage: format - displayName: 'format' - jobs: - - job: format - displayName: 'clang' - pool: - vmImage: ubuntu-latest - steps: - - template: ./templates/format-check.yml - stage: build - dependsOn: format displayName: 'build' jobs: - job: build From 8876a152279a5e6fc61460bb444174082f705a1c Mon Sep 17 00:00:00 2001 From: t895 Date: Mon, 15 Jan 2024 12:41:49 -0500 Subject: [PATCH 33/33] android: Fix overlay toggle ordering --- src/android/app/src/main/res/values/arrays.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 0363ff3b6..78e855bde 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -228,10 +228,10 @@ R ZL ZR - @string/gamepad_left_stick - @string/gamepad_right_stick L3 R3 + @string/gamepad_left_stick + @string/gamepad_right_stick @string/gamepad_d_pad