From 80bafc8fe876371c90d64fb9b7288e03d2ba8da8 Mon Sep 17 00:00:00 2001 From: wildcard Date: Sun, 8 Mar 2026 20:48:19 +0100 Subject: [PATCH 01/11] [vulkan] Fix incorrect offset application for Array2D textures (#3696) Earlier we were taking the coords and adding them to coords instead of actually taking offsets and adding it to coord Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3696 Reviewed-by: MaranBr Reviewed-by: crueter Co-authored-by: wildcard Co-committed-by: wildcard --- src/shader_recompiler/backend/spirv/emit_spirv_image.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 4bff810547..c4c898bec9 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -343,8 +343,8 @@ void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, I break; } case TextureType::ColorArray2D: - offset = ctx.OpCompositeConstruct(ctx.U32[3], ctx.OpCompositeExtract(ctx.U32[1], coords, 0), - ctx.OpCompositeExtract(ctx.U32[1], coords, 1), + offset = ctx.OpCompositeConstruct(ctx.U32[3], ctx.OpCompositeExtract(ctx.U32[1], offset, 0), + ctx.OpCompositeExtract(ctx.U32[1], offset, 1), ctx.u32_zero_value); [[fallthrough]]; case TextureType::Color3D: { From 0a169dec4dc8438aac40206bfa62cf151d34afd5 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sun, 8 Mar 2026 20:48:47 +0100 Subject: [PATCH 02/11] [qt] fix crash when having an invalid graphics backend (#3693) closely related to #3692, however the crash may also occur when sharing configs between macOS and a system that had OpenGL Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3693 Reviewed-by: DraVee Reviewed-by: crueter Co-authored-by: lizzie Co-committed-by: lizzie --- src/yuzu/configuration/configure_graphics.cpp | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 8663594678..b0c63aff4f 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2016 Citra Emulator Project @@ -416,11 +416,9 @@ const QString ConfigureGraphics::TranslateVSyncMode(VkPresentModeKHR mode, } int ConfigureGraphics::FindIndex(u32 enumeration, int value) const { - for (u32 i = 0; i < combobox_translations.at(enumeration).size(); i++) { - if (combobox_translations.at(enumeration)[i].first == static_cast(value)) { + for (u32 i = 0; enumeration < combobox_translations.size() && i < combobox_translations.at(enumeration).size(); i++) + if (combobox_translations.at(enumeration)[i].first == u32(value)) return i; - } - } return -1; } @@ -433,13 +431,11 @@ void ConfigureGraphics::ApplyConfiguration() { UpdateVsyncSetting(); Settings::values.vulkan_device.SetGlobal(true); - if (Settings::IsConfiguringGlobal() || - (!Settings::IsConfiguringGlobal() && api_restore_global_button->isEnabled())) { - auto backend = static_cast( - combobox_translations - .at(Settings::EnumMetadata< - Settings::RendererBackend>::Index())[api_combobox->currentIndex()] - .first); + auto const index = Settings::EnumMetadata::Index(); + if (Settings::IsConfiguringGlobal() || (!Settings::IsConfiguringGlobal() && api_restore_global_button->isEnabled())) { + auto backend = index >= combobox_translations.size() || size_t(api_combobox->currentIndex()) >= combobox_translations.at(index).size() + ? Settings::values.renderer_backend.GetValue() + : Settings::RendererBackend(combobox_translations.at(index)[api_combobox->currentIndex()].first); switch (backend) { case Settings::RendererBackend::Vulkan: Settings::values.vulkan_device.SetGlobal(Settings::IsConfiguringGlobal()); @@ -507,13 +503,12 @@ void ConfigureGraphics::RetrieveVulkanDevices() { Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { const auto selected_backend = [&]() { - if (!Settings::IsConfiguringGlobal() && !api_restore_global_button->isEnabled()) { + auto const index = Settings::EnumMetadata::Index(); + if (!Settings::IsConfiguringGlobal() && !api_restore_global_button->isEnabled()) return Settings::values.renderer_backend.GetValue(true); - } - return Settings::RendererBackend( - combobox_translations.at(Settings::EnumMetadata::Index()) - .at(api_combobox->currentIndex()) - .first); + return index >= combobox_translations.size() || size_t(api_combobox->currentIndex()) >= combobox_translations.at(index).size() + ? Settings::values.renderer_backend.GetValue() + : Settings::RendererBackend(combobox_translations.at(index).at(api_combobox->currentIndex()).first); }(); if (selected_backend == Settings::RendererBackend::Vulkan && UISettings::values.has_broken_vulkan) From 1864160326c4d1f7484c41aa824e33ac7e28e1c1 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sun, 8 Mar 2026 20:49:17 +0100 Subject: [PATCH 03/11] [qt] remove 'None' interface since net code already uses first avail interface anyways, disable changing ifaces at runtime (#3683) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3683 Reviewed-by: DraVee Reviewed-by: crueter Co-authored-by: lizzie Co-committed-by: lizzie --- src/yuzu/configuration/configure_network.cpp | 21 ++++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/yuzu/configuration/configure_network.cpp b/src/yuzu/configuration/configure_network.cpp index 7df5e51a7f..62a097117e 100644 --- a/src/yuzu/configuration/configure_network.cpp +++ b/src/yuzu/configuration/configure_network.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -12,14 +12,13 @@ #include "yuzu/configuration/configure_network.h" ConfigureNetwork::ConfigureNetwork(const Core::System& system_, QWidget* parent) - : QWidget(parent), ui(std::make_unique()), system{system_} { + : QWidget(parent) + , ui(std::make_unique()) + , system{system_} +{ ui->setupUi(this); - - ui->network_interface->addItem(tr("None")); - for (const auto& iface : Network::GetAvailableNetworkInterfaces()) { + for (const auto& iface : Network::GetAvailableNetworkInterfaces()) ui->network_interface->addItem(QString::fromStdString(iface.name)); - } - this->SetConfiguration(); } @@ -44,13 +43,9 @@ void ConfigureNetwork::RetranslateUI() { void ConfigureNetwork::SetConfiguration() { const bool runtime_lock = !system.IsPoweredOn(); - - const std::string& network_interface = Settings::values.network_interface.GetValue(); - const bool& airplane_mode = Settings::values.airplane_mode.GetValue(); - + auto const network_interface = Settings::values.network_interface.GetValue(); + auto const airplane_mode = Settings::values.airplane_mode.GetValue(); ui->network_interface->setCurrentText(QString::fromStdString(network_interface)); ui->network_interface->setEnabled(runtime_lock); - ui->airplane_mode->setChecked(airplane_mode); - ui->network_interface->setEnabled(true); } From 38aa2bc5e1620539d4804528a37f72dc30376ead Mon Sep 17 00:00:00 2001 From: lizzie Date: Sun, 8 Mar 2026 20:50:29 +0100 Subject: [PATCH 04/11] [hle/services] use ankerl:: for Service's function handlers map, use const char* instead of std::string{} (#3671) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3671 Reviewed-by: DraVee Reviewed-by: crueter Co-authored-by: lizzie Co-committed-by: lizzie --- src/core/hle/service/service.cpp | 16 +++++++++------- src/core/hle/service/service.h | 28 +++++++++++----------------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index b972b83e1a..8b71a97320 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project @@ -29,10 +29,13 @@ namespace Service { return function_string; } -ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_, - u32 max_sessions_, InvokerFn* handler_invoker_) - : SessionRequestHandler(system_.Kernel(), service_name_), system{system_}, - service_name{service_name_}, handler_invoker{handler_invoker_}, max_sessions{max_sessions_} {} +ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_, u32 max_sessions_, InvokerFn* handler_invoker_) + : SessionRequestHandler(system_.Kernel(), service_name_) + , system{system_} + , service_name{service_name_} + , handler_invoker{handler_invoker_} + , max_sessions{max_sessions_} +{} ServiceFrameworkBase::~ServiceFrameworkBase() { // Wait for other threads to release access before destroying @@ -50,8 +53,7 @@ void ServiceFrameworkBase::RegisterHandlersBaseTipc(const FunctionInfoBase* func // Usually this array is sorted by id already, so hint to insert at the end handlers_tipc.reserve(handlers_tipc.size() + n); for (std::size_t i = 0; i < n; ++i) - handlers_tipc.emplace_hint(handlers_tipc.cend(), functions[i].expected_header, - functions[i]); + handlers_tipc.emplace_hint(handlers_tipc.cend(), functions[i].expected_header, functions[i]); } void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx, diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index dbc870a662..41429958ad 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project @@ -8,8 +8,7 @@ #include #include -#include -#include +#include #include "common/common_types.h" #include "core/hle/service/hle_ipc.h" @@ -78,13 +77,6 @@ protected: [[nodiscard]] virtual std::unique_lock LockService() noexcept { return std::unique_lock{lock_service}; } - - /// System context that the service operates under. - Core::System& system; - - /// Identifier string used to connect to the service. - std::string service_name; - private: template friend class ServiceFramework; @@ -106,16 +98,19 @@ private: void RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n); void ReportUnimplementedFunction(HLERequestContext& ctx, const FunctionInfoBase* info); - boost::container::flat_map handlers; - boost::container::flat_map handlers_tipc; +protected: + ankerl::unordered_dense::map handlers; + ankerl::unordered_dense::map handlers_tipc; /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. std::mutex lock_service; + /// System context that the service operates under. + Core::System& system; + /// Identifier string used to connect to the service. + const char* service_name; /// Function used to safely up-cast pointers to the derived class before invoking a handler. InvokerFn* handler_invoker; - /// Maximum number of concurrent sessions that this service can handle. u32 max_sessions; - /// Flag to store if a port was already create/installed to detect multiple install attempts, /// which is not supported. bool service_registered = false; @@ -159,8 +154,7 @@ protected: * @param max_sessions_ Maximum number of sessions that can be connected to this service at the * same time. */ - explicit ServiceFramework(Core::System& system_, const char* service_name_, - u32 max_sessions_ = ServerSessionCountMax) + explicit ServiceFramework(Core::System& system_, const char* service_name_, u32 max_sessions_ = ServerSessionCountMax) : ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {} /// Registers handlers in the service. @@ -219,7 +213,7 @@ private: static void Invoker(ServiceFrameworkBase* object, HandlerFnP member, HLERequestContext& ctx) { // Cast back up to our original types and call the member function - (static_cast(object)->*static_cast>(member))(ctx); + (static_cast(object)->*HandlerFnP(member))(ctx); } }; From c7b23f4a1aec49b445bf88196d7206c93b1a8759 Mon Sep 17 00:00:00 2001 From: crueter Date: Sun, 8 Mar 2026 20:53:37 +0100 Subject: [PATCH 05/11] [time] fix: guard timezone out buffer logging (#3668) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original text, per the emailed patch: --------------- Hello, I am submitting a small fix to prevent an unintended abort when _GLIBCXX_ASSERTIONS is enabled, caused by out-of-bounds access in debug logging. Background / Issue In the server-side implementations of ITimeZoneService::ToPosixTime and ToPosixTimeWithMyRule, the SCOPE_EXIT debug logging previously accessed out_times[0] and out_times[1] unconditionally. However, out_times is an IPC-provided output buffer (OutArray, which inherits from std::span). Its length depends on the caller-provided buffer capacity. During debugging, I encountered a case where out_times.size() == 1. Under _GLIBCXX_ASSERTIONS, accessing out_times[1] triggers a std::span::operator[] assertion failure (std::__glibcxx_assert_fail) and aborts the process, causing the service thread to crash. This results in an unintended crash caused solely by debug logging. Change Description In the SCOPE_EXIT logging blocks of both ToPosixTime and ToPosixTimeWithMyRule, I added bounds checks before accessing out_times[0] and out_times[1]: Access out_times[0] only if out_times.size() > 0 Access out_times[1] only if out_times.size() > 1 Print 0 when the corresponding element is unavailable This change only affects debug log output. It does not modify IPC semantics or the time conversion logic itself. Reproduction Context (for reference) I encountered this issue while running 13 Sentinels: Aegis Rim (title ID: 01008D7016438000). During the “Load Game” flow, ToPosixTimeWithMyRule is invoked with an out_times buffer of length 1, which previously led to the out-of-bounds access in the logging code. Thank you for your time and review. Best regards, darkpaper Environment: Arch Linux / KDE / X11 This email and the accompanying patch were prepared with assistance from an LLM. Authored-by: darkpaper Signed-off-by: crueter Co-authored-by: darkpaper Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3668 --- src/core/hle/service/glue/time/time_zone.cpp | 12 +++++++++--- src/core/hle/service/psc/time/time_zone_service.cpp | 9 +++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/core/hle/service/glue/time/time_zone.cpp b/src/core/hle/service/glue/time/time_zone.cpp index b2e8159653..c358cfbf28 100644 --- a/src/core/hle/service/glue/time/time_zone.cpp +++ b/src/core/hle/service/glue/time/time_zone.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -104,7 +107,8 @@ Result TimeZoneService::LoadLocationNameList( OutArray out_names, u32 index) { SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. index={} out_count={} out_names[0]={} out_names[1]={}", - index, *out_count, out_names[0], out_names[1]); + index, *out_count, out_names.size() > 0 ? out_names[0] : Service::PSC::Time::LocationName{}, + out_names.size() > 1 ? out_names[1] : Service::PSC::Time::LocationName{}); }; std::scoped_lock l{m_mutex}; @@ -208,7 +212,8 @@ Result TimeZoneService::ToPosixTime(Out out_count, SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}", - calendar_time, *out_count, out_times[0], out_times[1]); + calendar_time, *out_count, out_times.size() > 0 ? out_times[0] : s64{0}, + out_times.size() > 1 ? out_times[1] : s64{0}); }; R_RETURN(m_wrapped_service->ToPosixTime(out_count, out_times, calendar_time, rule)); @@ -220,7 +225,8 @@ Result TimeZoneService::ToPosixTimeWithMyRule( SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}", - calendar_time, *out_count, out_times[0], out_times[1]); + calendar_time, *out_count, out_times.size() > 0 ? out_times[0] : s64{0}, + out_times.size() > 1 ? out_times[1] : s64{0}); }; R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, calendar_time)); diff --git a/src/core/hle/service/psc/time/time_zone_service.cpp b/src/core/hle/service/psc/time/time_zone_service.cpp index 9e0674f275..d49c0056ec 100644 --- a/src/core/hle/service/psc/time/time_zone_service.cpp +++ b/src/core/hle/service/psc/time/time_zone_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -148,7 +151,8 @@ Result TimeZoneService::ToPosixTime(Out out_count, SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ", - calendar_time, *out_count, out_times[0], out_times[1]); + calendar_time, *out_count, out_times.size() > 0 ? out_times[0] : s64{0}, + out_times.size() > 1 ? out_times[1] : s64{0}); }; R_RETURN( @@ -161,7 +165,8 @@ Result TimeZoneService::ToPosixTimeWithMyRule(Out out_count, SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ", - calendar_time, *out_count, out_times[0], out_times[1]); + calendar_time, *out_count, out_times.size() > 0 ? out_times[0] : s64{0}, + out_times.size() > 1 ? out_times[1] : s64{0}); }; R_RETURN( From 3d1a67af18128831539c24f804b112f75b0194fd Mon Sep 17 00:00:00 2001 From: crueter Date: Sun, 8 Mar 2026 22:33:51 +0100 Subject: [PATCH 06/11] [externals] Update dependencies (#3664) * zlib: 1.3.1.2 -> 1.3.2 * vulkan-validation-layers: 1.4.335.0 -> 1.4.341.0 * sirit: 1.0.3 -> 1.0.4 * httplib: 0.35.0 -> 0.37.0 * xbyak: 7.33.3 -> 7.35.2 * catch2: 3.12.0 -> 3.13.0 * vulkan-headers: 1.4.342 -> 1.4.345 * vulkan-utility-libraries: 1.4.342 -> 1.4.345 Also fixed a build error with newer xbyak. Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3664 --- .patch/httplib/0002-fix-zstd.patch | 89 +++++++++++++++++++ cpmfile.json | 8 +- externals/cpmfile.json | 25 +++--- src/CMakeLists.txt | 1 + src/core/CMakeLists.txt | 2 +- .../src/dynarmic/backend/x64/a32_emit_x64.cpp | 2 +- .../backend/x64/emit_x64_data_processing.cpp | 2 +- .../backend/x64/emit_x64_saturation.cpp | 4 +- tools/cpm/package/update.sh | 29 +++++- tools/cpm/package/util/fix-hash.sh | 8 +- 10 files changed, 145 insertions(+), 25 deletions(-) create mode 100644 .patch/httplib/0002-fix-zstd.patch diff --git a/.patch/httplib/0002-fix-zstd.patch b/.patch/httplib/0002-fix-zstd.patch new file mode 100644 index 0000000000..f54485ea53 --- /dev/null +++ b/.patch/httplib/0002-fix-zstd.patch @@ -0,0 +1,89 @@ +From 509be32bbfa6eb95014860f7c9ea6d45c8ddaa56 Mon Sep 17 00:00:00 2001 +From: crueter +Date: Sun, 8 Mar 2026 15:11:12 -0400 +Subject: [PATCH] [cmake] Simplify zstd find logic, and support pre-existing + zstd target + +Some deduplication work on the zstd required/if-available logic. Also +adds support for pre-existing `zstd::libzstd` which is useful for +projects that bundle their own zstd in a way that doesn't get caught by +`CONFIG` + +Signed-off-by: crueter +--- + CMakeLists.txt | 46 ++++++++++++++++++++++++++-------------------- + 1 file changed, 26 insertions(+), 20 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 1874e36be0..8d31198006 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -241,28 +241,34 @@ endif() + # NOTE: + # zstd < 1.5.6 does not provide the CMake imported target `zstd::libzstd`. + # Older versions must be consumed via their pkg-config file. +-if(HTTPLIB_REQUIRE_ZSTD) +- find_package(zstd 1.5.6 CONFIG) +- if(NOT zstd_FOUND) +- find_package(PkgConfig REQUIRED) +- pkg_check_modules(zstd REQUIRED IMPORTED_TARGET libzstd) +- add_library(zstd::libzstd ALIAS PkgConfig::zstd) +- endif() +- set(HTTPLIB_IS_USING_ZSTD TRUE) +-elseif(HTTPLIB_USE_ZSTD_IF_AVAILABLE) +- find_package(zstd 1.5.6 CONFIG QUIET) +- if(NOT zstd_FOUND) +- find_package(PkgConfig QUIET) +- if(PKG_CONFIG_FOUND) +- pkg_check_modules(zstd QUIET IMPORTED_TARGET libzstd) +- +- if(TARGET PkgConfig::zstd) ++if (HTTPLIB_REQUIRE_ZSTD) ++ set(HTTPLIB_ZSTD_REQUESTED ON) ++ set(HTTPLIB_ZSTD_REQUIRED REQUIRED) ++elseif (HTTPLIB_USE_ZSTD_IF_AVAILABLE) ++ set(HTTPLIB_ZSTD_REQUESTED ON) ++ set(HTTPLIB_ZSTD_REQUIRED QUIET) ++endif() ++ ++if (HTTPLIB_ZSTD_REQUESTED) ++ if (TARGET zstd::libzstd) ++ set(HTTPLIB_IS_USING_ZSTD TRUE) ++ else() ++ find_package(zstd 1.5.6 CONFIG QUIET) ++ ++ if (NOT zstd_FOUND) ++ find_package(PkgConfig ${HTTPLIB_ZSTD_REQUIRED}) ++ pkg_check_modules(zstd ${HTTPLIB_ZSTD_REQUIRED} IMPORTED_TARGET libzstd) ++ ++ if (TARGET PkgConfig::zstd) + add_library(zstd::libzstd ALIAS PkgConfig::zstd) + endif() + endif() ++ ++ # This will always be true if zstd is required. ++ # If zstd *isn't* found when zstd is set to required, ++ # CMake will error out earlier in this block. ++ set(HTTPLIB_IS_USING_ZSTD ${zstd_FOUND}) + endif() +- # Both find_package and PkgConf set a XXX_FOUND var +- set(HTTPLIB_IS_USING_ZSTD ${zstd_FOUND}) + endif() + + # Used for default, common dirs that the end-user can change (if needed) +@@ -317,13 +323,13 @@ if(HTTPLIB_COMPILE) + $ + $ + ) +- ++ + # Add C++20 module support if requested + # Include from separate file to prevent parse errors on older CMake versions + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.28") + include(cmake/modules.cmake) + endif() +- ++ + set_target_properties(${PROJECT_NAME} + PROPERTIES + VERSION ${${PROJECT_NAME}_VERSION} diff --git a/cpmfile.json b/cpmfile.json index 774f160360..c938e67e88 100644 --- a/cpmfile.json +++ b/cpmfile.json @@ -46,9 +46,9 @@ "package": "ZLIB", "repo": "madler/zlib", "tag": "v%VERSION%", - "hash": "06eaa3a1eaaeb31f461a2283b03a91ed8eb2406e62cd97ea1c69836324909edeecd93edd03ff0bf593d9dde223e3376149134c5b1fe2e8688c258cadf8cd60ff", + "hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4", "version": "1.2", - "git_version": "1.3.1.2", + "git_version": "1.3.2", "options": [ "ZLIB_BUILD_SHARED OFF", "ZLIB_INSTALL OFF" @@ -98,9 +98,9 @@ "package": "VVL", "repo": "KhronosGroup/Vulkan-ValidationLayers", "tag": "vulkan-sdk-%VERSION%", - "git_version": "1.4.335.0", + "git_version": "1.4.341.0", "artifact": "android-binaries-%VERSION%.zip", - "hash": "48167c4a17736301bd08f9290f41830443e1f18cce8ad867fc6f289b49e18b40e93c9850b377951af82f51b5b6d7313aa6a884fc5df79f5ce3df82696c1c1244" + "hash": "8812ae84cbe49e6a3418ade9c458d3be6d74a3dffd319d4502007b564d580998056e8190414368ec11b27bc83993c7a0dad713c31bcc3d9553b51243efee3753" }, "quazip": { "package": "QuaZip-Qt6", diff --git a/externals/cpmfile.json b/externals/cpmfile.json index 9644647638..de024fd40e 100644 --- a/externals/cpmfile.json +++ b/externals/cpmfile.json @@ -9,7 +9,7 @@ }, "sirit": { "repo": "eden-emulator/sirit", - "git_version": "1.0.3", + "git_version": "1.0.4", "tag": "v%VERSION%", "artifact": "sirit-source-%VERSION%.tar.zst", "hash_suffix": "sha512sum", @@ -28,11 +28,12 @@ "httplib": { "repo": "yhirose/cpp-httplib", "tag": "v%VERSION%", - "hash": "a229e24cca4afe78e5c0aa2e482f15108ac34101fd8dbd927365f15e8c37dec4de38c5277d635017d692a5b320e1b929f8bfcc076f52b8e4dcdab8fe53bfdf2e", - "git_version": "0.30.1", + "hash": "5efa8140aadffe105dcf39935b732476e95755f6c7473ada3d0b64df2bc02c557633ae3948a25b45e1cf67e89a3ff6329fb30362e4ac033b9a1d1e453aa2eded", + "git_version": "0.37.0", "find_args": "MODULE GLOBAL", "patches": [ - "0001-mingw.patch" + "0001-mingw.patch", + "0002-fix-zstd.patch" ], "options": [ "HTTPLIB_REQUIRE_OPENSSL ON" @@ -55,8 +56,8 @@ "package": "xbyak", "repo": "herumi/xbyak", "tag": "v%VERSION%", - "hash": "ac333d7bea1d61865bebebb116201a58db431946aa2f11aa042ef5795c390ff30af4d6c90ed3b3d24443a1d430703b08f14fc13b2fa405c155a241456ed78a47", - "git_version": "7.33.2" + "hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a", + "git_version": "7.35.2" }, "oaknut": { "repo": "eden-emulator/oaknut", @@ -146,9 +147,9 @@ "package": "Catch2", "repo": "catchorg/Catch2", "tag": "v%VERSION%", - "hash": "acb3f463a7404d6a3bce52e474075cdadf9bb241d93feaf147c182d756e5a2f8bd412f4658ca186d15ab8fed36fc587d79ec311f55642d8e4ded16df9e213656", + "hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35", "version": "3.0.1", - "git_version": "3.12.0", + "git_version": "3.13.0", "patches": [ "0001-solaris-isnan-fix.patch" ] @@ -256,15 +257,15 @@ "repo": "KhronosGroup/Vulkan-Headers", "package": "VulkanHeaders", "version": "1.4.317", - "hash": "26e0ad8fa34ab65a91ca62ddc54cc4410d209a94f64f2817dcdb8061dc621539a4262eab6387e9b9aa421db3dbf2cf8e2a4b041b696d0d03746bae1f25191272", - "git_version": "1.4.342", + "hash": "d2846ea228415772645eea4b52a9efd33e6a563043dd3de059e798be6391a8f0ca089f455ae420ff22574939ed0f48ed7c6ff3d5a9987d5231dbf3b3f89b484b", + "git_version": "1.4.345", "tag": "v%VERSION%" }, "vulkan-utility-libraries": { "repo": "KhronosGroup/Vulkan-Utility-Libraries", "package": "VulkanUtilityLibraries", - "hash": "8147370f964fd82c315d6bb89adeda30186098427bf3efaa641d36282d42a263f31e96e4586bfd7ae0410ff015379c19aa4512ba160630444d3d8553afd1ec14", - "git_version": "1.4.342", + "hash": "114f6b237a6dcba923ccc576befb5dea3f1c9b3a30de7dc741f234a831d1c2d52d8a224afb37dd57dffca67ac0df461eaaab6a5ab5e503b393f91c166680c3e1", + "git_version": "1.4.345", "tag": "v%VERSION%" }, "frozen": { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 21affffb6b..dfb311b327 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,6 @@ # SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later + # SPDX-FileCopyrightText: 2018 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 08d6e73688..7566372b51 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1220,7 +1220,7 @@ target_link_libraries(core PRIVATE RenderDoc::API ZLIB::ZLIB) -target_link_libraries(core PRIVATE httplib::httplib) +target_link_libraries(core PUBLIC httplib::httplib zstd::zstd) if (ENABLE_WEB_SERVICE) target_compile_definitions(core PUBLIC ENABLE_WEB_SERVICE) diff --git a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp index 38cec7c750..f037919eb0 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp @@ -687,7 +687,7 @@ void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto& arg = args[0]; - const u32 upper_without_t = (ctx.EndLocation().SetSingleStepping(false).UniqueHash() >> 32) & 0xFFFFFFFE; + const u64 upper_without_t = (ctx.EndLocation().SetSingleStepping(false).UniqueHash() >> 32) & 0xFFFFFFFE; // Pseudocode: // if (new_pc & 1) { diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_data_processing.cpp b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_data_processing.cpp index 108a341a71..f2af4e5b80 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_data_processing.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_data_processing.cpp @@ -947,7 +947,7 @@ static void EmitAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, size_t const Xbyak::Reg8 overflow = overflow_inst ? ctx.reg_alloc.ScratchGpr(code).cvt8() : Xbyak::Reg8{-1}; if (args[1].IsImmediate() && args[1].GetType() == IR::Type::U32) { - const u32 op_arg = args[1].GetImmediateU32(); + const u64 op_arg = args[1].GetImmediateU64(); if (carry_in.IsImmediate()) { if (carry_in.GetImmediateU1()) { // In range for a valid LEA materialisation diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_saturation.cpp b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_saturation.cpp index 2ba67f5a13..85ee3584eb 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_saturation.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_saturation.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later /* This file is part of the dynarmic project. @@ -135,7 +135,7 @@ void EmitX64::EmitSignedSaturation(EmitContext& ctx, IR::Inst* inst) { const u32 mask = (1u << N) - 1; const u32 positive_saturated_value = (1u << (N - 1)) - 1; - const u32 negative_saturated_value = 1u << (N - 1); + const u64 negative_saturated_value = 1u << (N - 1); const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr(code).cvt32(); const Xbyak::Reg32 reg_a = ctx.reg_alloc.UseGpr(code, args[0]).cvt32(); diff --git a/tools/cpm/package/update.sh b/tools/cpm/package/update.sh index be6a0fbbef..92f4f423a9 100755 --- a/tools/cpm/package/update.sh +++ b/tools/cpm/package/update.sh @@ -19,7 +19,8 @@ Check a specific package or packages for updates. Options: -n, --dry-run Do not update the package if it has an update available - -a, --all Operate on all packages in this project. + -a, --all Operate on all packages in this project. + -c, --commit Automatically generate a commit message EOF @@ -38,6 +39,7 @@ while :; do case "$char" in a) ALL=1 ;; n) UPDATE=false ;; + c) COMMIT=true ;; h) usage ;; *) die "Invalid option -$char" ;; esac @@ -46,6 +48,7 @@ while :; do --dry-run) UPDATE=false ;; --all) ALL=1 ;; --help) usage ;; + --commit) COMMIT=true ;; "$0") break ;; "") break ;; *) packages="$packages $1" ;; @@ -56,6 +59,7 @@ done [ "$ALL" != 1 ] || packages="${LIBS:-$packages}" : "${UPDATE:=true}" +: "${COMMIT:=false}" [ -n "$packages" ] || usage for pkg in $packages; do @@ -93,6 +97,9 @@ for pkg in $packages; do filter_out yotta # mbedtls + # ???????????????????????????????? + filter_out vksc + # ignore betas/alphas (remove if needed) filter_out alpha filter_out beta @@ -119,19 +126,35 @@ for pkg in $packages; do else NEW_GIT_VERSION=$(echo "$LATEST" | sed "s/$VERSION_PREFIX//g") fi + else + NEW_GIT_VERSION="$LATEST" fi + _commit="$_commit +* $PACKAGE: $GIT_VERSION -> $NEW_GIT_VERSION" + echo "-- * Version $LATEST available, current is $TAG" if [ "$UPDATE" = "true" ]; then if [ "$HAS_REPLACE" = "true" ]; then NEW_JSON=$(echo "$JSON" | jq ".git_version = \"$NEW_GIT_VERSION\"") else - NEW_JSON=$(echo "$JSON" | jq ".tag = \"$LATEST\"") + NEW_JSON=$(echo "$JSON" | jq ".tag = \"$NEW_GIT_VERSION\"") fi "$SCRIPTS"/util/replace.sh "$PACKAGE" "$NEW_JSON" - QUIET=true "$SCRIPTS"/util/fix-hash.sh + echo "-- * -- Updating hash" + + export UPDATE + QUIET=true "$SCRIPTS"/util/fix-hash.sh "$PACKAGE" fi done + +if [ "$UPDATE" = "true" ] && [ "$COMMIT" = "true" ] && [ -n "$_commit" ]; then + for file in $CPMFILES; do + git add "$file" + done + git commit -m "Update dependencies +$_commit" +fi diff --git a/tools/cpm/package/util/fix-hash.sh b/tools/cpm/package/util/fix-hash.sh index 07b9d266dd..3a69f0aaa7 100755 --- a/tools/cpm/package/util/fix-hash.sh +++ b/tools/cpm/package/util/fix-hash.sh @@ -3,9 +3,15 @@ # SPDX-FileCopyrightText: Copyright 2026 crueter # SPDX-License-Identifier: LGPL-3.0-or-later +# shellcheck disable=SC1091 + : "${PACKAGE:=$1}" -# shellcheck disable=SC1091 +# re-read json files +# shellcheck disable=SC2016 +PACKAGES=$(echo "$CPMFILES" | xargs jq -s 'reduce .[] as $item ({}; . * $item)') +export PACKAGES + . "$SCRIPTS"/vars.sh [ "$CI" = null ] || exit 0 From 361e6209b2a7cbe2aec8119539f93b40f8a06213 Mon Sep 17 00:00:00 2001 From: smiRaphi Date: Sun, 8 Mar 2026 22:37:20 +0100 Subject: [PATCH 07/11] [settings] Add back & properly implement use_dev_keys (#3631) Was removed recently but also wasn't really working before, this adds it to the debug UI (under the kiosk option) and also makes it properly reload the keys on launch & setting change. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3631 Reviewed-by: crueter Reviewed-by: CamilleLaVey Co-authored-by: smiRaphi Co-committed-by: smiRaphi --- src/common/settings.h | 1 + src/core/crypto/key_manager.cpp | 16 +++++++++++++--- src/core/crypto/key_manager.h | 3 ++- src/yuzu/configuration/configure_debug.cpp | 4 ++++ src/yuzu/configuration/configure_debug.ui | 7 +++++++ src/yuzu/main_window.cpp | 5 +++++ 6 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/common/settings.h b/src/common/settings.h index ac04d26fc5..8cd55bcdd3 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -777,6 +777,7 @@ struct Values { Setting reporting_services{ linkage, false, "reporting_services", Category::Debugging, Specialization::Default, false}; Setting quest_flag{linkage, false, "quest_flag", Category::Debugging}; + Setting use_dev_keys{linkage, false, "use_dev_keys", Category::Debugging}; Setting disable_macro_jit{linkage, false, "disable_macro_jit", Category::DebuggingGraphics}; Setting disable_macro_hle{linkage, false, "disable_macro_hle", diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 9a1b9d7a50..fc0ee064cc 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -21,6 +21,7 @@ #include "common/fs/path_util.h" #include "common/hex_util.h" #include "common/logging/log.h" +#include "common/settings.h" #include "common/string_util.h" #include "core/crypto/aes_util.h" #include "core/crypto/key_manager.h" @@ -642,8 +643,15 @@ void KeyManager::ReloadKeys() { const auto keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir); if (!Common::FS::CreateDir(keys_dir)) LOG_ERROR(Core, "Failed to create the keys directory."); - LoadFromFile(keys_dir / "prod.keys_autogenerated", false); - LoadFromFile(keys_dir / "prod.keys", false); + if (Settings::values.use_dev_keys.GetValue()) { + dev_mode = true; + LoadFromFile(keys_dir / "dev.keys_autogenerated", false); + LoadFromFile(keys_dir / "dev.keys", false); + } else { + dev_mode = false; + LoadFromFile(keys_dir / "prod.keys_autogenerated", false); + LoadFromFile(keys_dir / "prod.keys", false); + } LoadFromFile(keys_dir / "title.keys_autogenerated", true); LoadFromFile(keys_dir / "title.keys", true); LoadFromFile(keys_dir / "console.keys_autogenerated", false); @@ -838,7 +846,7 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname, std::string filename = "title.keys_autogenerated"; if (category == KeyCategory::Standard) { - filename = "prod.keys_autogenerated"; + filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated"; } else if (category == KeyCategory::Console) { filename = "console.keys_autogenerated"; } @@ -936,6 +944,8 @@ bool KeyManager::KeyFileExists(bool title) { const auto keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir); if (title) return Common::FS::Exists(keys_dir / "title.keys"); + if (Settings::values.use_dev_keys.GetValue()) + return Common::FS::Exists(keys_dir / "dev.keys"); return Common::FS::Exists(keys_dir / "prod.keys"); } diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index 4d51b42677..7ef8375690 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project @@ -314,6 +314,7 @@ private: std::array eticket_extended_kek{}; RSAKeyPair<2048> eticket_rsa_keypair{}; + bool dev_mode; void LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys); template diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index bc835d35a2..e0bdbaeaa1 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -12,6 +12,7 @@ #include "common/logging/filter.h" #include "common/settings.h" #include "core/core.h" +#include "core/crypto/key_manager.h" #include "ui_configure_debug.h" #include "yuzu/configuration/configure_debug.h" #include "yuzu/debugger/console.h" @@ -45,6 +46,7 @@ void ConfigureDebug::SetConfiguration() { ui->reporting_services->setChecked(Settings::values.reporting_services.GetValue()); ui->dump_audio_commands->setChecked(Settings::values.dump_audio_commands.GetValue()); ui->quest_flag->setChecked(Settings::values.quest_flag.GetValue()); + ui->use_dev_keys->setChecked(Settings::values.use_dev_keys.GetValue()); ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts.GetValue()); ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue()); ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue()); @@ -105,6 +107,7 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.reporting_services = ui->reporting_services->isChecked(); Settings::values.dump_audio_commands = ui->dump_audio_commands->isChecked(); Settings::values.quest_flag = ui->quest_flag->isChecked(); + Settings::values.use_dev_keys = ui->use_dev_keys->isChecked(); Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); Settings::values.use_auto_stub = ui->use_auto_stub->isChecked(); Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked(); @@ -126,6 +129,7 @@ void ConfigureDebug::ApplyConfiguration() { Common::Log::Filter filter; filter.ParseFilterString(Settings::values.log_filter.GetValue()); Common::Log::SetGlobalFilter(filter); + Core::Crypto::KeyManager::Instance().ReloadKeys(); } void ConfigureDebug::changeEvent(QEvent* event) { diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 1b1423b62c..f76f3b012d 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -435,6 +435,13 @@ + + + + Use dev.keys + + + diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index 688078385a..399b3bd976 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -139,6 +139,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/perf_stats.h" +#include "core/crypto/key_manager.h" + // Input // #include "hid_core/hid_core.h" #include "hid_core/frontend/emulated_controller.h" @@ -564,6 +566,9 @@ MainWindow::MainWindow(bool has_broken_vulkan) // Check for orphaned profiles and reset profile data if necessary QtCommon::Content::FixProfiles(); + if (Settings::values.use_dev_keys.GetValue()) { + Core::Crypto::KeyManager::Instance().ReloadKeys(); + } game_list->LoadCompatibilityList(); game_list->PopulateAsync(UISettings::values.game_dirs); From f8ea09fa0f06572b32df1ead2fb38a64098e312e Mon Sep 17 00:00:00 2001 From: MaranBr Date: Sun, 8 Mar 2026 22:45:35 +0100 Subject: [PATCH 08/11] [video_core] Simplify TextureCache GC and remove redundant code (#3652) This enhances the garbage collection in TextureCache to make it more responsive and reliable during long gameplay sessions. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3652 Co-authored-by: MaranBr Co-committed-by: MaranBr --- src/video_core/texture_cache/texture_cache.h | 141 ++---------------- .../texture_cache/texture_cache_base.h | 1 - 2 files changed, 12 insertions(+), 130 deletions(-) diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 71210ffe6e..e32f21d2ce 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -70,14 +70,10 @@ TextureCache

::TextureCache(Runtime& runtime_, Tegra::MaxwellDeviceMemoryManag (std::max)((std::min)(device_local_memory - min_vacancy_critical, min_spacing_critical), DEFAULT_CRITICAL_MEMORY)); minimum_memory = static_cast((device_local_memory - mem_threshold) / 2); - - lowmemorydevice = false; } else { expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB; critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB; minimum_memory = 0; - - lowmemorydevice = true; } const bool gpu_unswizzle_enabled = Settings::values.gpu_unswizzle_enabled.GetValue(); @@ -122,102 +118,46 @@ void TextureCache

::RunGarbageCollector() { bool aggressive_mode = false; u64 ticks_to_destroy = 0; size_t num_iterations = 0; - const auto Configure = [&](bool allow_aggressive) { high_priority_mode = total_used_memory >= expected_memory; aggressive_mode = allow_aggressive && total_used_memory >= critical_memory; ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL; num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10); }; - - const auto Cleanup = [this, &num_iterations, &high_priority_mode, - &aggressive_mode](ImageId image_id) { + const auto Cleanup = [this, &num_iterations, &high_priority_mode, &aggressive_mode](ImageId image_id) { if (num_iterations == 0) { return true; } --num_iterations; auto& image = slot_images[image_id]; - - // Never delete recently allocated sparse textures (within 3 frames) - const bool is_recently_allocated = image.allocation_tick >= frame_tick - 3; - if (is_recently_allocated && image.info.is_sparse) { - return false; - } - if (True(image.flags & ImageFlagBits::IsDecoding)) { - // This image is still being decoded, deleting it will invalidate the slot - // used by the async decoder thread. return false; } - - // Prioritize large sparse textures for cleanup - const bool is_large_sparse = lowmemorydevice && - image.info.is_sparse && - image.guest_size_bytes >= 256_MiB; - - if (!aggressive_mode && !is_large_sparse && - True(image.flags & ImageFlagBits::CostlyLoad)) { - return false; - } - - const bool must_download = - image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap); - if (!high_priority_mode && !is_large_sparse && must_download) { - return false; - } - - if (must_download && !is_large_sparse) { + const bool must_download = image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap); + if (must_download && !image.info.is_sparse) { auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes); const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info)); image.DownloadMemory(map, copies); runtime.Finish(); - SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span, - swizzle_data_buffer); + SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span, swizzle_data_buffer); } - if (True(image.flags & ImageFlagBits::Tracked)) { UntrackImage(image, image_id); } UnregisterImage(image_id); - DeleteImage(image_id, image.scale_tick > frame_tick + 5); - - if (total_used_memory < critical_memory) { - if (aggressive_mode) { - // Sink the aggresiveness. - num_iterations >>= 2; - aggressive_mode = false; - return false; - } - if (high_priority_mode && total_used_memory < expected_memory) { - num_iterations >>= 1; - high_priority_mode = false; - } + DeleteImage(image_id, (frame_tick - image.scale_tick) > 5 || aggressive_mode); + if (aggressive_mode && total_used_memory < critical_memory) { + num_iterations >>= 2; + aggressive_mode = false; + } + if (high_priority_mode && total_used_memory < expected_memory) { + num_iterations >>= 1; + high_priority_mode = false; } return false; }; - - // Aggressively clear massive sparse textures - if (total_used_memory >= expected_memory) { - lru_cache.ForEachItemBelow(frame_tick, [&](ImageId image_id) { - auto& image = slot_images[image_id]; - // Only target sparse textures that are old enough - if (lowmemorydevice && - image.info.is_sparse && - image.guest_size_bytes >= 256_MiB && - image.allocation_tick < frame_tick - 3) { - LOG_DEBUG(HW_GPU, "GC targeting old sparse texture at 0x{:X} ({} MiB, age: {} frames)", - image.gpu_addr, image.guest_size_bytes / (1024 * 1024), - frame_tick - image.allocation_tick); - return Cleanup(image_id); - } - return false; - }); - } - Configure(false); lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup); - - // If pressure is still too high, prune aggressively. if (total_used_memory >= critical_memory) { Configure(true); lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup); @@ -1196,9 +1136,6 @@ void TextureCache

::RefreshContents(Image& image, ImageId image_id) { } image.flags &= ~ImageFlagBits::CpuModified; - if( lowmemorydevice && image.info.format == PixelFormat::BC1_RGBA_UNORM && MapSizeBytes(image) >= 256_MiB ) { - return; - } TrackImage(image, image_id); @@ -1619,39 +1556,6 @@ ImageId TextureCache

::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr, } } ASSERT_MSG(cpu_addr, "Tried to insert an image to an invalid gpu_addr=0x{:x}", gpu_addr); - - // For large sparse textures, aggressively clean up old allocations at same address - if (lowmemorydevice && info.is_sparse && CalculateGuestSizeInBytes(info) >= 256_MiB) { - const auto alloc_it = image_allocs_table.find(gpu_addr); - if (alloc_it != image_allocs_table.end()) { - const ImageAllocId alloc_id = alloc_it->second; - auto& alloc_images = slot_image_allocs[alloc_id].images; - - // Collect old images at this address that were created more than 2 frames ago - boost::container::small_vector to_delete; - for (ImageId old_image_id : alloc_images) { - Image& old_image = slot_images[old_image_id]; - if (old_image.info.is_sparse && - old_image.gpu_addr == gpu_addr && - old_image.allocation_tick < frame_tick - 2) { // Try not to delete fresh textures - to_delete.push_back(old_image_id); - } - } - - // Delete old images immediately - for (ImageId old_id : to_delete) { - Image& old_image = slot_images[old_id]; - LOG_DEBUG(HW_GPU, "Immediately deleting old sparse texture at 0x{:X} ({} MiB)", - gpu_addr, old_image.guest_size_bytes / (1024 * 1024)); - if (True(old_image.flags & ImageFlagBits::Tracked)) { - UntrackImage(old_image, old_id); - } - UnregisterImage(old_id); - DeleteImage(old_id, true); - } - } - } - const ImageId image_id = JoinImages(info, gpu_addr, *cpu_addr); const Image& image = slot_images[image_id]; // Using "image.gpu_addr" instead of "gpu_addr" is important because it might be different @@ -1667,27 +1571,6 @@ template ImageId TextureCache

::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DAddr cpu_addr) { ImageInfo new_info = info; const size_t size_bytes = CalculateGuestSizeInBytes(new_info); - - // Proactive cleanup for large sparse texture allocations - if (lowmemorydevice && new_info.is_sparse && size_bytes >= 256_MiB) { - const u64 estimated_alloc_size = size_bytes; - - if (total_used_memory + estimated_alloc_size >= critical_memory) { - LOG_DEBUG(HW_GPU, "Large sparse texture allocation ({} MiB) - running aggressive GC. " - "Current memory: {} MiB, Critical: {} MiB", - size_bytes / (1024 * 1024), - total_used_memory / (1024 * 1024), - critical_memory / (1024 * 1024)); - RunGarbageCollector(); - - // If still over threshold after GC, try one more aggressive pass - if (total_used_memory + estimated_alloc_size >= critical_memory) { - LOG_DEBUG(HW_GPU, "Still critically low on memory, running second GC pass"); - RunGarbageCollector(); - } - } - } - const bool broken_views = runtime.HasBrokenTextureViewFormats(); const bool native_bgr = runtime.HasNativeBgr(); join_overlap_ids.clear(); diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 4b4061f21d..47f52c5c99 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -478,7 +478,6 @@ private: u64 minimum_memory; u64 expected_memory; u64 critical_memory; - bool lowmemorydevice = false; size_t gpu_unswizzle_maxsize = 0; size_t swizzle_chunk_size = 0; u32 swizzle_slices_per_batch = 0; From 6693b99ae44209b8e10ecc8b09517ceae9ab0bc0 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sun, 8 Mar 2026 22:45:38 +0100 Subject: [PATCH 09/11] [core] coalesce tracking entries for GPU (#3677) I think I may have attempted this before, but I doubt it. Anyways this should reduce virtual buffers from 3 to just 1, also improved access times :) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3677 Reviewed-by: crueter Co-authored-by: lizzie Co-committed-by: lizzie --- src/core/device_memory_manager.h | 26 +++++----- src/core/device_memory_manager.inc | 79 ++++++++++++------------------ 2 files changed, 46 insertions(+), 59 deletions(-) diff --git a/src/core/device_memory_manager.h b/src/core/device_memory_manager.h index 9d86a17d12..3d97fdcc5c 100644 --- a/src/core/device_memory_manager.h +++ b/src/core/device_memory_manager.h @@ -76,16 +76,16 @@ public: template void ApplyOpOnPAddr(PAddr address, Common::ScratchBuffer& buffer, Func&& operation) { - DAddr subbits = static_cast(address & page_mask); + DAddr subbits = DAddr(address & page_mask); const u32 base = compressed_device_addr[(address >> page_bits)]; if ((base >> MULTI_FLAG_BITS) == 0) [[likely]] { - const DAddr d_address = (static_cast(base) << page_bits) + subbits; + const DAddr d_address = (DAddr(base) << page_bits) + subbits; operation(d_address); return; } InnerGatherDeviceAddresses(buffer, address); for (u32 value : buffer) { - operation((static_cast(value) << page_bits) + subbits); + operation((DAddr(value) << page_bits) + subbits); } } @@ -96,12 +96,12 @@ public: } PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const { - PAddr subbits = static_cast(address & page_mask); - auto paddr = compressed_physical_ptr[(address >> page_bits)]; + PAddr subbits = PAddr(address & page_mask); + auto paddr = tracked_entries[(address >> page_bits)].compressed_physical_ptr; if (paddr == 0) { return 0; } - return (static_cast(paddr - 1) << page_bits) + subbits; + return (PAddr(paddr - 1) << page_bits) + subbits; } template @@ -172,9 +172,14 @@ private: const uintptr_t physical_base; DeviceInterface* device_inter; - Common::VirtualBuffer compressed_physical_ptr; + + struct TrackedEntry { + VAddr cpu_backing_address; + u32 continuity_tracker; + u32 compressed_physical_ptr; + }; Common::VirtualBuffer compressed_device_addr; - Common::VirtualBuffer continuity_tracker; + Common::VirtualBuffer tracked_entries; // Process memory interfaces @@ -189,17 +194,16 @@ private: static constexpr size_t asid_start_bit = guest_max_as_bits; std::pair ExtractCPUBacking(size_t page_index) { - auto content = cpu_backing_address[page_index]; + auto content = tracked_entries[page_index].cpu_backing_address; const VAddr address = content & guest_mask; const Asid asid{static_cast(content >> asid_start_bit)}; return std::make_pair(asid, address); } void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) { - cpu_backing_address[page_index] = address | (asid.id << asid_start_bit); + tracked_entries[page_index].cpu_backing_address = address | (asid.id << asid_start_bit); } - Common::VirtualBuffer cpu_backing_address; std::array t_slot{}; u32 cache_cursor = 0; using CounterType = u8; diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc index 08fe799174..15e3a1ad52 100644 --- a/src/core/device_memory_manager.inc +++ b/src/core/device_memory_manager.inc @@ -166,29 +166,21 @@ struct DeviceMemoryManagerAllocator { template DeviceMemoryManager::DeviceMemoryManager(const DeviceMemory& device_memory_) - : physical_base{reinterpret_cast(device_memory_.buffer.BackingBasePointer())}, - device_inter{nullptr}, compressed_physical_ptr(device_as_size >> Memory::YUZU_PAGEBITS), - compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == - Settings::MemoryLayout::Memory_4Gb - ? physical_min_bits - : physical_max_bits) - - Memory::YUZU_PAGEBITS)), - continuity_tracker(device_as_size >> Memory::YUZU_PAGEBITS), - cpu_backing_address(device_as_size >> Memory::YUZU_PAGEBITS) { + : physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())} + , device_inter{nullptr} + , compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS)) + , tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS) +{ impl = std::make_unique>(); cached_pages = std::make_unique(); const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS; for (size_t i = 0; i < total_virtual; i++) { - compressed_physical_ptr[i] = 0; - continuity_tracker[i] = 1; - cpu_backing_address[i] = 0; + tracked_entries[i].compressed_physical_ptr = 0; + tracked_entries[i].continuity_tracker = 1; + tracked_entries[i].cpu_backing_address = 0; } - const size_t total_phys = 1ULL << ((Settings::values.memory_layout_mode.GetValue() == - Settings::MemoryLayout::Memory_4Gb - ? physical_min_bits - : physical_max_bits) - - Memory::YUZU_PAGEBITS); + const size_t total_phys = 1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS); for (size_t i = 0; i < total_phys; i++) { compressed_device_addr[i] = 0; } @@ -228,11 +220,11 @@ void DeviceMemoryManager::Map(DAddr address, VAddr virtual_address, size const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE; auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress)); if (ptr == nullptr) [[unlikely]] { - compressed_physical_ptr[start_page_d + i] = 0; + tracked_entries[start_page_d + i].compressed_physical_ptr = 0; continue; } auto phys_addr = static_cast(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U; - compressed_physical_ptr[start_page_d + i] = phys_addr; + tracked_entries[start_page_d + i].compressed_physical_ptr = phys_addr; InsertCPUBacking(start_page_d + i, new_vaddress, asid); const u32 base_dev = compressed_device_addr[phys_addr - 1U]; const u32 new_dev = static_cast(start_page_d + i); @@ -260,9 +252,9 @@ void DeviceMemoryManager::Unmap(DAddr address, size_t size) { device_inter->InvalidateRegion(address, size); std::scoped_lock lk(mapping_guard); for (size_t i = 0; i < num_pages; i++) { - auto phys_addr = compressed_physical_ptr[start_page_d + i]; - compressed_physical_ptr[start_page_d + i] = 0; - cpu_backing_address[start_page_d + i] = 0; + auto phys_addr = tracked_entries[start_page_d + i].compressed_physical_ptr; + tracked_entries[start_page_d + i].compressed_physical_ptr = 0; + tracked_entries[start_page_d + i].cpu_backing_address = 0; if (phys_addr != 0) [[likely]] { const u32 base_dev = compressed_device_addr[phys_addr - 1U]; if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] { @@ -300,14 +292,14 @@ void DeviceMemoryManager::TrackContinuityImpl(DAddr address, VAddr virtu page_count = 1; } last_ptr = new_ptr; - continuity_tracker[start_page_d + index] = static_cast(page_count); + tracked_entries[start_page_d + index].continuity_tracker = static_cast(page_count); } } template u8* DeviceMemoryManager::GetSpan(const DAddr src_addr, const std::size_t size) { size_t page_index = src_addr >> page_bits; size_t subbits = src_addr & page_mask; - if ((static_cast(continuity_tracker[page_index]) << page_bits) >= size + subbits) { + if ((static_cast(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) { return GetPointer(src_addr); } return nullptr; @@ -317,7 +309,7 @@ template const u8* DeviceMemoryManager::GetSpan(const DAddr src_addr, const std::size_t size) const { size_t page_index = src_addr >> page_bits; size_t subbits = src_addr & page_mask; - if ((static_cast(continuity_tracker[page_index]) << page_bits) >= size + subbits) { + if ((static_cast(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) { return GetPointer(src_addr); } return nullptr; @@ -342,12 +334,10 @@ template T* DeviceMemoryManager::GetPointer(DAddr address) { const size_t index = address >> Memory::YUZU_PAGEBITS; const size_t offset = address & Memory::YUZU_PAGEMASK; - auto phys_addr = compressed_physical_ptr[index]; - if (phys_addr == 0) [[unlikely]] { + auto phys_addr = tracked_entries[index].compressed_physical_ptr; + if (phys_addr == 0) [[unlikely]] return nullptr; - } - return GetPointerFromRaw((static_cast(phys_addr - 1) << Memory::YUZU_PAGEBITS) + - offset); + return GetPointerFromRaw((PAddr(phys_addr - 1) << Memory::YUZU_PAGEBITS) + offset); } template @@ -355,12 +345,10 @@ template const T* DeviceMemoryManager::GetPointer(DAddr address) const { const size_t index = address >> Memory::YUZU_PAGEBITS; const size_t offset = address & Memory::YUZU_PAGEMASK; - auto phys_addr = compressed_physical_ptr[index]; - if (phys_addr == 0) [[unlikely]] { + auto phys_addr = tracked_entries[index].compressed_physical_ptr; + if (phys_addr == 0) return nullptr; - } - return GetPointerFromRaw((static_cast(phys_addr - 1) << Memory::YUZU_PAGEBITS) + - offset); + return GetPointerFromRaw((PAddr(phys_addr - 1) << Memory::YUZU_PAGEBITS) + offset); } template @@ -386,18 +374,14 @@ T DeviceMemoryManager::Read(DAddr address) const { } template -void DeviceMemoryManager::WalkBlock(DAddr addr, std::size_t size, auto on_unmapped, - auto on_memory, auto increment) { +void DeviceMemoryManager::WalkBlock(DAddr addr, std::size_t size, auto on_unmapped, auto on_memory, auto increment) { std::size_t remaining_size = size; std::size_t page_index = addr >> Memory::YUZU_PAGEBITS; std::size_t page_offset = addr & Memory::YUZU_PAGEMASK; - while (remaining_size) { - const size_t next_pages = static_cast(continuity_tracker[page_index]); - const std::size_t copy_amount = - (std::min)((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size); - const auto current_vaddr = - static_cast((page_index << Memory::YUZU_PAGEBITS) + page_offset); + const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker); + const std::size_t copy_amount = (std::min)((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size); + const auto current_vaddr = u64((page_index << Memory::YUZU_PAGEBITS) + page_offset); SCOPE_EXIT{ page_index += next_pages; page_offset = 0; @@ -405,13 +389,12 @@ void DeviceMemoryManager::WalkBlock(DAddr addr, std::size_t size, auto o remaining_size -= copy_amount; }; - auto phys_addr = compressed_physical_ptr[page_index]; + auto phys_addr = tracked_entries[page_index].compressed_physical_ptr; if (phys_addr == 0) { on_unmapped(copy_amount, current_vaddr); continue; } - auto* mem_ptr = GetPointerFromRaw( - (static_cast(phys_addr - 1) << Memory::YUZU_PAGEBITS) + page_offset); + auto* mem_ptr = GetPointerFromRaw((PAddr(phys_addr - 1) << Memory::YUZU_PAGEBITS) + page_offset); on_memory(copy_amount, mem_ptr); } } @@ -430,7 +413,7 @@ void DeviceMemoryManager::ReadBlock(DAddr address, void* dest_pointer, s } const std::size_t page_index = address >> Memory::YUZU_PAGEBITS; - const auto phys_addr = compressed_physical_ptr[page_index]; + const auto phys_addr = tracked_entries[page_index].compressed_physical_ptr; if (phys_addr != 0) { auto* const mem_ptr = GetPointerFromRaw((PAddr(phys_addr - 1) << Memory::YUZU_PAGEBITS)); t_slot[cache_cursor % t_slot.size()] = TranslationEntry{.guest_page = guest_page, .host_ptr = mem_ptr}; @@ -488,7 +471,7 @@ void DeviceMemoryManager::ReadBlockUnsafe(DAddr address, void* dest_poin } const std::size_t page_index = address >> Memory::YUZU_PAGEBITS; - const auto phys_addr = compressed_physical_ptr[page_index]; + const auto phys_addr = tracked_entries[page_index].compressed_physical_ptr; if (phys_addr != 0) { auto* const mem_ptr = GetPointerFromRaw((PAddr(phys_addr - 1) << Memory::YUZU_PAGEBITS)); t_slot[cache_cursor % t_slot.size()] = TranslationEntry{.guest_page = guest_page, .host_ptr = mem_ptr}; From f5e2b1fb132f0a553cb37edec0ce58b4e4638878 Mon Sep 17 00:00:00 2001 From: crueter Date: Sun, 8 Mar 2026 23:22:51 +0100 Subject: [PATCH 10/11] [dynarmic] Remove incorrect LICENSE (#3698) Our dynarmic is GPLv3, not BSD. Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3698 --- src/dynarmic/LICENSE.txt | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 src/dynarmic/LICENSE.txt diff --git a/src/dynarmic/LICENSE.txt b/src/dynarmic/LICENSE.txt deleted file mode 100644 index a6bc0ba042..0000000000 --- a/src/dynarmic/LICENSE.txt +++ /dev/null @@ -1,12 +0,0 @@ -Copyright (C) 2017 merryhime - -Permission to use, copy, modify, and/or distribute this software for -any purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN -AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. From a1b50e9339fc4ad003ea55de6fdaabf92a12e163 Mon Sep 17 00:00:00 2001 From: xbzk Date: Mon, 9 Mar 2026 00:30:10 +0100 Subject: [PATCH 11/11] [android] patches bin button + version bug fixes (#3691) This fixed the delete button enabled for external content (which is auto handled and the proper way to get rid of them is either by removing its folder from ext content list, or removing the file itself) by streaming patch source thru jni. Along the way stumbled upon another bug: If you have an external content update installed (say latest version for example) and you NAND install a previous update (like in silksong's hard mode update), the newest update version string would leak to the previous one. Did videos for both. Fixed both. Seems good to go. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3691 Reviewed-by: crueter Reviewed-by: Lizzie Reviewed-by: DraVee Co-authored-by: xbzk Co-committed-by: xbzk --- .../yuzu/yuzu_emu/adapters/AddonAdapter.kt | 18 ++- .../java/org/yuzu/yuzu_emu/model/Patch.kt | 16 +- src/android/app/src/main/jni/native.cpp | 2 +- src/common/android/id_cache.cpp | 2 +- src/core/file_sys/patch_manager.cpp | 143 +++++++++++++----- src/core/file_sys/patch_manager.h | 1 + 6 files changed, 134 insertions(+), 48 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 371fef0025..cbca66e13a 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 @@ -40,11 +40,21 @@ class AddonAdapter(val addonViewModel: AddonViewModel) : } } - val deleteAction = { - addonViewModel.setAddonToDelete(model) + val canDelete = model.isRemovable + binding.deleteCard.isEnabled = canDelete + binding.buttonDelete.isEnabled = canDelete + binding.deleteCard.alpha = if (canDelete) 1f else 0.38f + + if (canDelete) { + val deleteAction = { + addonViewModel.setAddonToDelete(model) + } + binding.deleteCard.setOnClickListener { deleteAction() } + binding.buttonDelete.setOnClickListener { deleteAction() } + } else { + binding.deleteCard.setOnClickListener(null) + binding.buttonDelete.setOnClickListener(null) } - binding.deleteCard.setOnClickListener { deleteAction() } - binding.buttonDelete.setOnClickListener { deleteAction() } } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Patch.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Patch.kt index 8a367116c1..a3785dd3ac 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Patch.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Patch.kt @@ -16,5 +16,17 @@ data class Patch( val type: Int, val programId: String, val titleId: String, - val numericVersion: Long = 0 -) + val numericVersion: Long = 0, + val source: Int = 0 +) { + companion object { + const val SOURCE_UNKNOWN = 0 + const val SOURCE_NAND = 1 + const val SOURCE_SDMC = 2 + const val SOURCE_EXTERNAL = 3 + const val SOURCE_PACKED = 4 + } + + val isRemovable: Boolean + get() = source != SOURCE_EXTERNAL && source != SOURCE_PACKED +} diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 3f0029c78a..2108e05911 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -1407,7 +1407,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env Common::Android::ToJString(env, patch.version), static_cast(patch.type), Common::Android::ToJString(env, std::to_string(patch.program_id)), Common::Android::ToJString(env, std::to_string(patch.title_id)), - static_cast(patch.numeric_version)); + static_cast(patch.numeric_version), static_cast(patch.source)); env->SetObjectArrayElement(jpatchArray, i, jpatch); ++i; } diff --git a/src/common/android/id_cache.cpp b/src/common/android/id_cache.cpp index c7f5332a68..76af1e0fb2 100644 --- a/src/common/android/id_cache.cpp +++ b/src/common/android/id_cache.cpp @@ -516,7 +516,7 @@ namespace Common::Android { s_patch_class = reinterpret_cast(env->NewGlobalRef(patch_class)); s_patch_constructor = env->GetMethodID( patch_class, "", - "(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;J)V"); + "(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V"); s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z"); s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;"); s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;"); diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index e9c3bb75c2..627646ee84 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -123,6 +123,39 @@ bool IsVersionedExternalUpdateDisabled(const std::vector& disabled, return std::find(disabled.cbegin(), disabled.cend(), disabled_key) != disabled.cend() || std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend(); } + +std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64 update_tid) { + if (provider == nullptr) { + return {}; + } + + auto control_nca = provider->GetEntry(update_tid, ContentRecordType::Control); + if (control_nca == nullptr || + control_nca->GetStatus() != Loader::ResultStatus::Success) { + return {}; + } + + const auto romfs = control_nca->GetRomFS(); + if (romfs == nullptr) { + return {}; + } + + const auto extracted = ExtractRomFS(romfs); + if (extracted == nullptr) { + return {}; + } + + auto nacp_file = extracted->GetFile("control.nacp"); + if (nacp_file == nullptr) { + nacp_file = extracted->GetFile("Control.nacp"); + } + if (nacp_file == nullptr) { + return {}; + } + + NACP nacp{nacp_file}; + return nacp.GetVersionString(); +} } // Anonymous namespace PatchManager::PatchManager(u64 title_id_, @@ -771,6 +804,7 @@ std::vector PatchManager::GetPatches(VirtualFile update_raw) const { std::nullopt, std::nullopt, ContentRecordType::Program, update_tid); for (const auto& [slot, entry] : all_updates) { + (void)entry; if (slot == ContentProviderUnionSlot::External || slot == ContentProviderUnionSlot::FrontendManual) { continue; @@ -786,7 +820,7 @@ std::vector PatchManager::GetPatches(VirtualFile update_raw) const { source_suffix = " (NAND)"; break; case ContentProviderUnionSlot::SDMC: - source_type = PatchSource::NAND; + source_type = PatchSource::SDMC; source_suffix = " (SDMC)"; break; default: @@ -795,19 +829,16 @@ std::vector PatchManager::GetPatches(VirtualFile update_raw) const { std::string version_str; u32 numeric_ver = 0; - PatchManager update{update_tid, fs_controller, content_provider}; - const auto metadata = update.GetControlMetadata(); - const auto& nacp = metadata.first; + const auto* slot_provider = content_union->GetSlotProvider(slot); + version_str = GetUpdateVersionStringFromSlot(slot_provider, update_tid); - if (nacp != nullptr) { - version_str = nacp->GetVersionString(); - } - - const auto meta_ver = content_provider.GetEntryVersion(update_tid); - if (meta_ver.has_value()) { - numeric_ver = *meta_ver; - if (version_str.empty() && numeric_ver != 0) { - version_str = FormatTitleVersion(numeric_ver); + if (slot_provider != nullptr) { + const auto slot_ver = slot_provider->GetEntryVersion(update_tid); + if (slot_ver.has_value()) { + numeric_ver = *slot_ver; + if (version_str.empty() && numeric_ver != 0) { + version_str = FormatTitleVersion(numeric_ver); + } } } @@ -956,37 +987,60 @@ std::vector PatchManager::GetPatches(VirtualFile update_raw) const { } // DLC - const auto dlc_entries = - content_provider.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); - std::vector dlc_match; - dlc_match.reserve(dlc_entries.size()); - std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), - [this](const ContentProviderEntry& entry) { - const auto base_tid = GetBaseTitleID(entry.title_id); - const bool matches_base = base_tid == title_id; + bool has_external_dlc = false; + bool has_nand_dlc = false; + bool has_sdmc_dlc = false; + bool has_other_dlc = false; + const auto dlc_entries_with_origin = + content_union->ListEntriesFilterOrigin(std::nullopt, TitleType::AOC, ContentRecordType::Data); - if (!matches_base) { - LOG_DEBUG(Loader, "DLC {:016X} base {:016X} doesn't match title {:016X}", - entry.title_id, base_tid, title_id); - return false; - } + dlc_match.reserve(dlc_entries_with_origin.size()); + for (const auto& [slot, entry] : dlc_entries_with_origin) { + const auto base_tid = GetBaseTitleID(entry.title_id); + const bool matches_base = base_tid == title_id; + if (!matches_base) { + LOG_DEBUG(Loader, "DLC {:016X} base {:016X} doesn't match title {:016X}", + entry.title_id, base_tid, title_id); + continue; + } - auto nca = content_provider.GetEntry(entry); - if (!nca) { - LOG_DEBUG(Loader, "Failed to get NCA for DLC {:016X}", entry.title_id); - return false; - } + const auto* slot_provider = content_union->GetSlotProvider(slot); + if (slot_provider == nullptr) { + continue; + } - const auto status = nca->GetStatus(); - if (status != Loader::ResultStatus::Success) { - LOG_DEBUG(Loader, "DLC {:016X} NCA has status {}", - entry.title_id, static_cast(status)); - return false; - } + auto nca = slot_provider->GetEntry(entry); + if (!nca) { + LOG_DEBUG(Loader, "Failed to get NCA for DLC {:016X}", entry.title_id); + continue; + } - return true; - }); + const auto status = nca->GetStatus(); + if (status != Loader::ResultStatus::Success) { + LOG_DEBUG(Loader, "DLC {:016X} NCA has status {}", entry.title_id, + static_cast(status)); + continue; + } + + switch (slot) { + case ContentProviderUnionSlot::External: + case ContentProviderUnionSlot::FrontendManual: + has_external_dlc = true; + break; + case ContentProviderUnionSlot::UserNAND: + case ContentProviderUnionSlot::SysNAND: + has_nand_dlc = true; + break; + case ContentProviderUnionSlot::SDMC: + has_sdmc_dlc = true; + break; + default: + has_other_dlc = true; + break; + } + dlc_match.push_back(entry); + } if (!dlc_match.empty()) { // Ensure sorted so DLC IDs show in order. @@ -1000,13 +1054,22 @@ std::vector PatchManager::GetPatches(VirtualFile update_raw) const { const auto dlc_disabled = std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end(); + PatchSource dlc_source = PatchSource::Unknown; + if (has_external_dlc && !has_nand_dlc && !has_sdmc_dlc && !has_other_dlc) { + dlc_source = PatchSource::External; + } else if (has_nand_dlc && !has_external_dlc && !has_sdmc_dlc && !has_other_dlc) { + dlc_source = PatchSource::NAND; + } else if (has_sdmc_dlc && !has_external_dlc && !has_nand_dlc && !has_other_dlc) { + dlc_source = PatchSource::SDMC; + } + out.push_back({.enabled = !dlc_disabled, .name = "DLC", .version = std::move(list), .type = PatchType::DLC, .program_id = title_id, .title_id = dlc_match.back().title_id, - .source = PatchSource::Unknown}); + .source = dlc_source}); } return out; diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 2be963078d..755d924fd4 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -34,6 +34,7 @@ enum class PatchType { Update, DLC, Mod }; enum class PatchSource { Unknown, NAND, + SDMC, External, Packed, };