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/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/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/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/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/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}; 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, }; 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( 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); } }; 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. 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/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: { 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; 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/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) 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); } 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); 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