From 201f2817ca929d54460bfd5e18d3a23b0cad65c5 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Sun, 19 Jan 2025 18:55:27 -0300 Subject: [PATCH 01/68] Fix SurfaceFormat Format1_5_5_5 - Format5_5_5_1 (#2191) * Fix SurfaceFormat Format1_5_5_5 - again * Fix Format5_5_5_1 --- src/video_core/amdgpu/types.h | 11 +++++++++-- src/video_core/renderer_vulkan/liverpool_to_vk.cpp | 6 ++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/video_core/amdgpu/types.h b/src/video_core/amdgpu/types.h index 57f97418a..63e184cc5 100644 --- a/src/video_core/amdgpu/types.h +++ b/src/video_core/amdgpu/types.h @@ -283,8 +283,7 @@ inline CompMapping RemapSwizzle(const DataFormat format, const CompMapping swizz result.a = swizzle.a; return result; } - case DataFormat::Format10_10_10_2: - case DataFormat::Format5_5_5_1: { + case DataFormat::Format10_10_10_2: { CompMapping result; result.r = swizzle.a; result.g = swizzle.b; @@ -292,6 +291,14 @@ inline CompMapping RemapSwizzle(const DataFormat format, const CompMapping swizz result.a = swizzle.r; return result; } + case DataFormat::Format1_5_5_5: { + CompMapping result; + result.r = swizzle.b; + result.g = swizzle.g; + result.b = swizzle.r; + result.a = swizzle.a; + return result; + } default: return swizzle; } diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 4ac8d5cc8..35585edb7 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -612,8 +612,10 @@ std::span SurfaceFormats() { vk::Format::eB5G6R5UnormPack16), // 1_5_5_5 CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format1_5_5_5, AmdGpu::NumberFormat::Unorm, - vk::Format::eA1B5G5R5UnormPack16), - // 5_5_5_1 - Remapped to 1_5_5_5. + vk::Format::eA1R5G5B5UnormPack16), + // 5_5_5_1 + CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format5_5_5_1, AmdGpu::NumberFormat::Unorm, + vk::Format::eR5G5B5A1UnormPack16), // 4_4_4_4 CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format4_4_4_4, AmdGpu::NumberFormat::Unorm, vk::Format::eR4G4B4A4UnormPack16), From 80092b6367e1aa230fb6e72fb304a0828061d409 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Sun, 19 Jan 2025 20:09:10 -0300 Subject: [PATCH 02/68] Fix SurfaceFormat Format4_4_4_4 (#2193) * Fix SurfaceFormat Format4_4_4_4 Pac-Man 256 * add_extension --- src/video_core/renderer_vulkan/liverpool_to_vk.cpp | 2 +- src/video_core/renderer_vulkan/vk_instance.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 35585edb7..f2fbc6530 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -618,7 +618,7 @@ std::span SurfaceFormats() { vk::Format::eR5G5B5A1UnormPack16), // 4_4_4_4 CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format4_4_4_4, AmdGpu::NumberFormat::Unorm, - vk::Format::eR4G4B4A4UnormPack16), + vk::Format::eA4B4G4R4UnormPack16), // 8_24 // 24_8 // X24_8_32 diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 5efdf4127..d183d6b09 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -283,6 +283,7 @@ bool Instance::CreateDevice() { add_extension(VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME); add_extension(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); + add_extension(VK_EXT_4444_FORMATS_EXTENSION_NAME); #ifdef __APPLE__ // Required by Vulkan spec if supported. From d14e57f6a8bf327db8a032382fcaefa0b441fd1c Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 19 Jan 2025 18:45:34 -0800 Subject: [PATCH 03/68] hotfix: Move some command buffer references down. Prevents references becoming stale due to stream buffer flushes. --- src/video_core/buffer_cache/buffer_cache.cpp | 2 +- src/video_core/texture_cache/texture_cache.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 487544a21..11ad0e96f 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -210,7 +210,6 @@ void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bo return; } scheduler.EndRendering(); - const auto cmdbuf = scheduler.CommandBuffer(); const Buffer* buffer = [&] { if (is_gds) { return &gds_buffer; @@ -218,6 +217,7 @@ void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bo const BufferId buffer_id = FindBuffer(address, num_bytes); return &slot_buffers[buffer_id]; }(); + const auto cmdbuf = scheduler.CommandBuffer(); const vk::BufferMemoryBarrier2 pre_barrier = { .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, .srcAccessMask = vk::AccessFlagBits2::eMemoryRead, diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index a281b89c9..5947db864 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -545,12 +545,12 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule auto* sched_ptr = custom_scheduler ? custom_scheduler : &scheduler; sched_ptr->EndRendering(); - const auto cmdbuf = sched_ptr->CommandBuffer(); const VAddr image_addr = image.info.guest_address; const size_t image_size = image.info.guest_size; const auto [vk_buffer, buf_offset] = buffer_cache.ObtainViewBuffer(image_addr, image_size, is_gpu_dirty); + const auto cmdbuf = sched_ptr->CommandBuffer(); // The obtained buffer may be written by a shader so we need to emit a barrier to prevent RAW // hazard if (auto barrier = vk_buffer->GetBarrier(vk::AccessFlagBits2::eTransferRead, From 4fa501c8d525a6afa3ae36134b26e2dc8007a24e Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sun, 19 Jan 2025 21:12:42 -0600 Subject: [PATCH 04/68] Additional libSceNpManager functions and cleanup (#2195) * Error return on sceNpGetAccountIdA Confirmed through hardware testing, this returns ORBIS_NP_ERROR_SIGNED_OUT on a signed out console. Parameters are based on fpPS4 code. * Fix compile * Move errors to separate file * Cleanup function headers Swaps user_ids to use our OrbisUserServiceUserId type, and fixes parameter names to align with our coding standards. * Add proper parameter checks * Implement sceNpGetAccountId This function takes an online_id, uses an NpManager function to get the user_id, then uses that user_id to perform the same internal functions as sceNpGetAccountIdA. * Implement sceNpHasSignedUp * Fix sceNpGetAccountId Further hardware testing shows that these always write 0 to account_id when failing. * Update np_manager.cpp --- src/core/libraries/np_manager/np_manager.cpp | 42 ++++++++++++++----- src/core/libraries/np_manager/np_manager.h | 14 +++---- .../libraries/np_manager/np_manager_error.h | 9 ++++ 3 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 src/core/libraries/np_manager/np_manager_error.h diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index e26c5a830..a60dcd86f 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -5,6 +5,7 @@ #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/np_manager/np_manager.h" +#include "core/libraries/np_manager/np_manager_error.h" #include "core/tls.h" namespace Libraries::NpManager { @@ -935,14 +936,22 @@ int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetAccountId() { - LOG_ERROR(Lib_NpManager, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id) { + LOG_DEBUG(Lib_NpManager, "called"); + if (online_id == nullptr || account_id == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } + *account_id = 0; + return ORBIS_NP_ERROR_SIGNED_OUT; } -int PS4_SYSV_ABI sceNpGetAccountIdA() { - LOG_ERROR(Lib_NpManager, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id) { + LOG_DEBUG(Lib_NpManager, "user_id {}", user_id); + if (account_id == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } + *account_id = 0; + return ORBIS_NP_ERROR_SIGNED_OUT; } int PS4_SYSV_ABI sceNpGetAccountLanguage() { @@ -972,6 +981,9 @@ int PS4_SYSV_ABI sceNpGetGamePresenceStatusA() { int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id) { LOG_DEBUG(Lib_NpManager, "user_id {}", user_id); + if (np_id == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } return ORBIS_NP_ERROR_SIGNED_OUT; } @@ -980,8 +992,11 @@ int PS4_SYSV_ABI sceNpGetNpReachabilityState() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetOnlineId(s32 user_id, OrbisNpOnlineId* online_id) { +int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineId* online_id) { LOG_DEBUG(Lib_NpManager, "user_id {}", user_id); + if (online_id == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } return ORBIS_NP_ERROR_SIGNED_OUT; } @@ -995,7 +1010,10 @@ int PS4_SYSV_ABI sceNpGetParentalControlInfoA() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetState(s32 userId, OrbisNpState* state) { +int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* state) { + if (state == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } *state = OrbisNpState::SignedOut; LOG_DEBUG(Lib_NpManager, "Signed out"); return ORBIS_OK; @@ -1011,8 +1029,12 @@ int PS4_SYSV_ABI sceNpGetUserIdByOnlineId() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpHasSignedUp() { - LOG_ERROR(Lib_NpManager, "(STUBBED) called"); +int PS4_SYSV_ABI sceNpHasSignedUp(OrbisUserServiceUserId user_id, bool* has_signed_up) { + LOG_DEBUG(Lib_NpManager, "called"); + if (has_signed_up == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } + *has_signed_up = false; return ORBIS_OK; } diff --git a/src/core/libraries/np_manager/np_manager.h b/src/core/libraries/np_manager/np_manager.h index 6ba588e5e..02a1a32f6 100644 --- a/src/core/libraries/np_manager/np_manager.h +++ b/src/core/libraries/np_manager/np_manager.h @@ -11,8 +11,6 @@ class SymbolsResolver; namespace Libraries::NpManager { -constexpr int ORBIS_NP_ERROR_SIGNED_OUT = 0x80550006; - enum class OrbisNpState : u32 { Unknown = 0, SignedOut, SignedIn }; using OrbisNpStateCallbackForNpToolkit = PS4_SYSV_ABI void (*)(s32 userId, OrbisNpState state, @@ -220,22 +218,22 @@ int PS4_SYSV_ABI sceNpGetAccountCountry(); int PS4_SYSV_ABI sceNpGetAccountCountryA(); int PS4_SYSV_ABI sceNpGetAccountDateOfBirth(); int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA(); -int PS4_SYSV_ABI sceNpGetAccountId(); -int PS4_SYSV_ABI sceNpGetAccountIdA(); +int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id); +int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id); int PS4_SYSV_ABI sceNpGetAccountLanguage(); int PS4_SYSV_ABI sceNpGetAccountLanguage2(); int PS4_SYSV_ABI sceNpGetAccountLanguageA(); int PS4_SYSV_ABI sceNpGetGamePresenceStatus(); int PS4_SYSV_ABI sceNpGetGamePresenceStatusA(); -int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId userId, OrbisNpId* npId); +int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id); int PS4_SYSV_ABI sceNpGetNpReachabilityState(); -int PS4_SYSV_ABI sceNpGetOnlineId(s32 userId, OrbisNpOnlineId* onlineId); +int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineId* online_id); int PS4_SYSV_ABI sceNpGetParentalControlInfo(); int PS4_SYSV_ABI sceNpGetParentalControlInfoA(); -int PS4_SYSV_ABI sceNpGetState(s32 userId, OrbisNpState* state); +int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* state); int PS4_SYSV_ABI sceNpGetUserIdByAccountId(); int PS4_SYSV_ABI sceNpGetUserIdByOnlineId(); -int PS4_SYSV_ABI sceNpHasSignedUp(); +int PS4_SYSV_ABI sceNpHasSignedUp(OrbisUserServiceUserId user_id, bool* has_signed_up); int PS4_SYSV_ABI sceNpIdMapperAbortRequest(); int PS4_SYSV_ABI sceNpIdMapperAccountIdToNpId(); int PS4_SYSV_ABI sceNpIdMapperAccountIdToOnlineId(); diff --git a/src/core/libraries/np_manager/np_manager_error.h b/src/core/libraries/np_manager/np_manager_error.h new file mode 100644 index 000000000..4af0d08ef --- /dev/null +++ b/src/core/libraries/np_manager/np_manager_error.h @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +constexpr int ORBIS_NP_ERROR_INVALID_ARGUMENT = 0x80550003; +constexpr int ORBIS_NP_ERROR_SIGNED_OUT = 0x80550006; \ No newline at end of file From 0f93edb377f50dcb462427cdda0eb9939f37de31 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sun, 19 Jan 2025 21:20:51 -0600 Subject: [PATCH 05/68] Implement IMAGE_ATOMIC_SWAP (#2194) We already handle everything for this opcode in our IMAGE_ATOMIC function, so implementing this is fairly simple. Should improve Wipeout 3. --- src/shader_recompiler/frontend/format.cpp | 4 ++-- src/shader_recompiler/frontend/translate/vector_memory.cpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/shader_recompiler/frontend/format.cpp b/src/shader_recompiler/frontend/format.cpp index 9677be3e5..2fcac7c10 100644 --- a/src/shader_recompiler/frontend/format.cpp +++ b/src/shader_recompiler/frontend/format.cpp @@ -3420,8 +3420,8 @@ constexpr std::array InstructionFormatMIMG = {{ {InstClass::VectorMemImgUt, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, ScalarType::Uint32}, // 15 = IMAGE_ATOMIC_SWAP - {InstClass::VectorMemImgNoSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgNoSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Uint32}, // 16 = IMAGE_ATOMIC_CMPSWAP {InstClass::VectorMemImgNoSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, ScalarType::Undefined}, diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index 8fa0f3f87..685785af1 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -107,6 +107,8 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { return IMAGE_GET_RESINFO(inst); // Image atomic operations + case Opcode::IMAGE_ATOMIC_SWAP: + return IMAGE_ATOMIC(AtomicOp::Swap, inst); case Opcode::IMAGE_ATOMIC_ADD: return IMAGE_ATOMIC(AtomicOp::Add, inst); case Opcode::IMAGE_ATOMIC_SMIN: From e1132db197def956132f08870258a1f8c21e9c99 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 19 Jan 2025 23:33:37 -0800 Subject: [PATCH 06/68] texture_cache: Prevent unregistered images from being tracked. (#2196) --- src/video_core/texture_cache/texture_cache.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 5947db864..798ecf030 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -643,6 +643,9 @@ void TextureCache::UnregisterImage(ImageId image_id) { void TextureCache::TrackImage(ImageId image_id) { auto& image = slot_images[image_id]; + if (!(image.flags & ImageFlagBits::Registered)) { + return; + } const auto image_begin = image.info.guest_address; const auto image_end = image.info.guest_address + image.info.guest_size; if (image_begin == image.track_addr && image_end == image.track_addr_end) { @@ -666,6 +669,9 @@ void TextureCache::TrackImage(ImageId image_id) { void TextureCache::TrackImageHead(ImageId image_id) { auto& image = slot_images[image_id]; + if (!(image.flags & ImageFlagBits::Registered)) { + return; + } const auto image_begin = image.info.guest_address; if (image_begin == image.track_addr) { return; @@ -678,6 +684,9 @@ void TextureCache::TrackImageHead(ImageId image_id) { void TextureCache::TrackImageTail(ImageId image_id) { auto& image = slot_images[image_id]; + if (!(image.flags & ImageFlagBits::Registered)) { + return; + } const auto image_end = image.info.guest_address + image.info.guest_size; if (image_end == image.track_addr_end) { return; From a3967ccdb43ebe5c8005dd35e72825497abbbb3d Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Mon, 20 Jan 2025 04:48:32 -0800 Subject: [PATCH 07/68] externals: Update vulkan-headers (#2197) --- CMakeLists.txt | 2 +- externals/vulkan-headers | 2 +- src/video_core/renderer_vulkan/vk_platform.cpp | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e5c16bd1b..918a1acb7 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,7 +120,7 @@ find_package(SDL3 3.1.2 CONFIG) find_package(stb MODULE) find_package(toml11 4.2.0 CONFIG) find_package(tsl-robin-map 1.3.0 CONFIG) -find_package(VulkanHeaders 1.4.303 CONFIG) +find_package(VulkanHeaders 1.4.305 CONFIG) find_package(VulkanMemoryAllocator 3.1.0 CONFIG) find_package(xbyak 7.07 CONFIG) find_package(xxHash 0.8.2 MODULE) diff --git a/externals/vulkan-headers b/externals/vulkan-headers index 6a74a7d65..a03d2f6d5 160000 --- a/externals/vulkan-headers +++ b/externals/vulkan-headers @@ -1 +1 @@ -Subproject commit 6a74a7d65cafa19e38ec116651436cce6efd5b2e +Subproject commit a03d2f6d5753b365d704d58161825890baad0755 diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index fdd590e9d..07ebfbda6 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -28,19 +28,19 @@ static const char* const VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; static const char* const CRASH_DIAGNOSTIC_LAYER_NAME = "VK_LAYER_LUNARG_crash_diagnostic"; static VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsCallback( - VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT type, - const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data) { + vk::DebugUtilsMessageSeverityFlagBitsEXT severity, vk::DebugUtilsMessageTypeFlagsEXT type, + const vk::DebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data) { Common::Log::Level level{}; switch (severity) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + case vk::DebugUtilsMessageSeverityFlagBitsEXT::eError: level = Common::Log::Level::Error; break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + case vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning: level = Common::Log::Level::Info; break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + case vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo: + case vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose: level = Common::Log::Level::Debug; break; default: From 95a30b2b3e1aa4e20c3db632955cc67bbded0fb1 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Mon, 20 Jan 2025 13:38:09 -0800 Subject: [PATCH 08/68] texture_cache: Lock when updating image. (#2198) --- src/video_core/texture_cache/texture_cache.cpp | 6 ++---- src/video_core/texture_cache/texture_cache.h | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 798ecf030..04711539c 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -60,15 +60,13 @@ void TextureCache::MarkAsMaybeDirty(ImageId image_id, Image& image) { void TextureCache::InvalidateMemory(VAddr addr, size_t size) { std::scoped_lock lock{mutex}; - const auto end = addr + size; const auto pages_start = PageManager::GetPageAddr(addr); const auto pages_end = PageManager::GetNextPageAddr(addr + size - 1); ForEachImageInRegion(pages_start, pages_end - pages_start, [&](ImageId image_id, Image& image) { const auto image_begin = image.info.guest_address; const auto image_end = image.info.guest_address + image.info.guest_size; - if (image_begin < end && addr < image_end) { - // Start or end of the modified region is in the image, or the image is entirely within - // the modified region, so the image was definitely accessed by this page fault. + if (image.Overlaps(addr, size)) { + // Modified region overlaps image, so the image was definitely accessed by this fault. // Untrack the image, so that the range is unprotected and the guest can write freely. image.flags |= ImageFlagBits::CpuDirty; UntrackImage(image_id); diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 343a510e6..f262768ea 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -118,6 +118,7 @@ public: /// Updates image contents if it was modified by CPU. void UpdateImage(ImageId image_id, Vulkan::Scheduler* custom_scheduler = nullptr) { + std::scoped_lock lock{mutex}; Image& image = slot_images[image_id]; TrackImage(image_id); RefreshImage(image, custom_scheduler); From 3563b88d8c9b72474460233b13e4b28ac93e8bc1 Mon Sep 17 00:00:00 2001 From: polyproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Tue, 21 Jan 2025 19:28:39 +0100 Subject: [PATCH 09/68] hotfix: use logger device on stdin --- CMakeLists.txt | 20 +++++++++---------- src/core/devices/base_device.cpp | 4 ++-- src/core/devices/base_device.h | 4 ++-- .../{dev_console.cpp => console_device.cpp} | 13 +++++++++--- .../{dev_console.h => console_device.h} | 4 ++-- .../{deci_tty6.cpp => deci_tty6_device.cpp} | 14 ++++++++++--- .../{deci_tty6.h => deci_tty6_device.h} | 4 ++-- src/core/devices/ioccom.h | 4 ++-- src/core/devices/logger.cpp | 8 +++++--- src/core/devices/logger.h | 4 ++-- src/core/devices/nop_device.h | 17 +++++++++++++--- .../devices/{random.cpp => random_device.cpp} | 18 +++++++++++++---- .../devices/{random.h => random_device.h} | 4 ++-- .../{srandom.cpp => srandom_device.cpp} | 18 +++++++++++++---- .../devices/{srandom.h => srandom_device.h} | 4 ++-- .../{urandom.cpp => urandom_device.cpp} | 16 +++++++++++---- .../devices/{urandom.h => urandom_device.h} | 4 ++-- src/core/file_sys/fs.cpp | 2 +- src/core/libraries/kernel/file_system.cpp | 17 +++++----------- 19 files changed, 114 insertions(+), 65 deletions(-) rename src/core/devices/{dev_console.cpp => console_device.cpp} (92%) rename src/core/devices/{dev_console.h => console_device.h} (90%) rename src/core/devices/{deci_tty6.cpp => deci_tty6_device.cpp} (92%) rename src/core/devices/{deci_tty6.h => deci_tty6_device.h} (90%) rename src/core/devices/{random.cpp => random_device.cpp} (90%) rename src/core/devices/{random.h => random_device.h} (90%) rename src/core/devices/{srandom.cpp => srandom_device.cpp} (90%) rename src/core/devices/{srandom.h => srandom_device.h} (90%) rename src/core/devices/{urandom.cpp => urandom_device.cpp} (90%) rename src/core/devices/{urandom.h => urandom_device.h} (90%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 918a1acb7..131809c8e 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -557,16 +557,16 @@ set(CORE src/core/aerolib/stubs.cpp src/core/devices/logger.cpp src/core/devices/logger.h src/core/devices/nop_device.h - src/core/devices/dev_console.cpp - src/core/devices/dev_console.h - src/core/devices/deci_tty6.cpp - src/core/devices/deci_tty6.h - src/core/devices/random.cpp - src/core/devices/random.h - src/core/devices/urandom.cpp - src/core/devices/urandom.h - src/core/devices/srandom.cpp - src/core/devices/srandom.h + src/core/devices/console_device.cpp + src/core/devices/console_device.h + src/core/devices/deci_tty6_device.cpp + src/core/devices/deci_tty6_device.h + src/core/devices/random_device.cpp + src/core/devices/random_device.h + src/core/devices/urandom_device.cpp + src/core/devices/urandom_device.h + src/core/devices/srandom_device.cpp + src/core/devices/srandom_device.h src/core/file_format/pfs.h src/core/file_format/pkg.cpp src/core/file_format/pkg.h diff --git a/src/core/devices/base_device.cpp b/src/core/devices/base_device.cpp index 4f91c81c7..fc2a98a29 100644 --- a/src/core/devices/base_device.cpp +++ b/src/core/devices/base_device.cpp @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "base_device.h" diff --git a/src/core/devices/base_device.h b/src/core/devices/base_device.h index 351af82b4..36614b8f4 100644 --- a/src/core/devices/base_device.h +++ b/src/core/devices/base_device.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/devices/dev_console.cpp b/src/core/devices/console_device.cpp similarity index 92% rename from src/core/devices/dev_console.cpp rename to src/core/devices/console_device.cpp index 0ddcfd040..f109cadb9 100644 --- a/src/core/devices/dev_console.cpp +++ b/src/core/devices/console_device.cpp @@ -1,34 +1,41 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" -#include "dev_console.h" +#include "console_device.h" namespace Core::Devices { + std::shared_ptr ConsoleDevice::Create(u32 handle, const char*, int, u16) { return std::shared_ptr( reinterpret_cast(new ConsoleDevice(handle))); } + int ConsoleDevice::ioctl(u64 cmd, Common::VaCtx* args) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 ConsoleDevice::write(const void* buf, size_t nbytes) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t ConsoleDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t ConsoleDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 ConsoleDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 ConsoleDevice::lseek(s64 offset, int whence) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; diff --git a/src/core/devices/dev_console.h b/src/core/devices/console_device.h similarity index 90% rename from src/core/devices/dev_console.h rename to src/core/devices/console_device.h index f280200e2..d4b590ba0 100644 --- a/src/core/devices/dev_console.h +++ b/src/core/devices/console_device.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include diff --git a/src/core/devices/deci_tty6.cpp b/src/core/devices/deci_tty6_device.cpp similarity index 92% rename from src/core/devices/deci_tty6.cpp rename to src/core/devices/deci_tty6_device.cpp index 20423de61..e7a5fd4fc 100644 --- a/src/core/devices/deci_tty6.cpp +++ b/src/core/devices/deci_tty6_device.cpp @@ -1,34 +1,41 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" -#include "deci_tty6.h" +#include "deci_tty6_device.h" namespace Core::Devices { + std::shared_ptr DeciTty6Device::Create(u32 handle, const char*, int, u16) { return std::shared_ptr( reinterpret_cast(new DeciTty6Device(handle))); } + int DeciTty6Device::ioctl(u64 cmd, Common::VaCtx* args) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 DeciTty6Device::write(const void* buf, size_t nbytes) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t DeciTty6Device::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t DeciTty6Device::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 DeciTty6Device::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 DeciTty6Device::lseek(s64 offset, int whence) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; @@ -63,4 +70,5 @@ s64 DeciTty6Device::pwrite(const void* buf, size_t nbytes, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + } // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/deci_tty6.h b/src/core/devices/deci_tty6_device.h similarity index 90% rename from src/core/devices/deci_tty6.h rename to src/core/devices/deci_tty6_device.h index 71cbfba6b..b8bd48556 100644 --- a/src/core/devices/deci_tty6.h +++ b/src/core/devices/deci_tty6_device.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include diff --git a/src/core/devices/ioccom.h b/src/core/devices/ioccom.h index 671ee33d4..2ded90bd8 100644 --- a/src/core/devices/ioccom.h +++ b/src/core/devices/ioccom.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/devices/logger.cpp b/src/core/devices/logger.cpp index 6f104509c..8dcb24a3b 100644 --- a/src/core/devices/logger.cpp +++ b/src/core/devices/logger.cpp @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "core/libraries/kernel/file_system.h" @@ -17,10 +17,12 @@ s64 Logger::write(const void* buf, size_t nbytes) { } size_t Logger::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + size_t total_written = 0; for (int i = 0; i < iovcnt; i++) { log(static_cast(iov[i].iov_base), iov[i].iov_len); + total_written += iov[i].iov_len; } - return iovcnt; + return total_written; } s64 Logger::pwrite(const void* buf, size_t nbytes, u64 offset) { diff --git a/src/core/devices/logger.h b/src/core/devices/logger.h index bfb07f337..eef17bc4b 100644 --- a/src/core/devices/logger.h +++ b/src/core/devices/logger.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/devices/nop_device.h b/src/core/devices/nop_device.h index a75b92f1b..5518b1de1 100644 --- a/src/core/devices/nop_device.h +++ b/src/core/devices/nop_device.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "base_device.h" @@ -17,36 +17,47 @@ public: int ioctl(u64 cmd, Common::VaCtx* args) override { return 0; } + s64 write(const void* buf, size_t nbytes) override { return 0; } + size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override { return 0; } + size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override { - return 0; + return ORBIS_KERNEL_ERROR_EBADF; } + s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override { return 0; } + s64 lseek(s64 offset, int whence) override { return 0; } + s64 read(void* buf, size_t nbytes) override { return 0; } + int fstat(Libraries::Kernel::OrbisKernelStat* sb) override { return 0; } + s32 fsync() override { return 0; } + int ftruncate(s64 length) override { return 0; } + int getdents(void* buf, u32 nbytes, s64* basep) override { return 0; } + s64 pwrite(const void* buf, size_t nbytes, u64 offset) override { return 0; } diff --git a/src/core/devices/random.cpp b/src/core/devices/random_device.cpp similarity index 90% rename from src/core/devices/random.cpp rename to src/core/devices/random_device.cpp index 705449423..50934e3b8 100644 --- a/src/core/devices/random.cpp +++ b/src/core/devices/random_device.cpp @@ -1,43 +1,52 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + #include #include "common/logging/log.h" -#include "random.h" +#include "random_device.h" namespace Core::Devices { + std::shared_ptr RandomDevice::Create(u32 handle, const char*, int, u16) { std::srand(std::time(nullptr)); return std::shared_ptr( reinterpret_cast(new RandomDevice(handle))); } + int RandomDevice::ioctl(u64 cmd, Common::VaCtx* args) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 RandomDevice::write(const void* buf, size_t nbytes) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t RandomDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t RandomDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 RandomDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 RandomDevice::lseek(s64 offset, int whence) { return 0; } s64 RandomDevice::read(void* buf, size_t nbytes) { auto rbuf = static_cast(buf); - for (size_t i = 0; i < nbytes; i++) + for (size_t i = 0; i < nbytes; i++) { rbuf[i] = std::rand() & 0xFF; + } return nbytes; } @@ -65,4 +74,5 @@ s64 RandomDevice::pwrite(const void* buf, size_t nbytes, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + } // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/random.h b/src/core/devices/random_device.h similarity index 90% rename from src/core/devices/random.h rename to src/core/devices/random_device.h index 3bbed1ca2..a5c8e9845 100644 --- a/src/core/devices/random.h +++ b/src/core/devices/random_device.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include diff --git a/src/core/devices/srandom.cpp b/src/core/devices/srandom_device.cpp similarity index 90% rename from src/core/devices/srandom.cpp rename to src/core/devices/srandom_device.cpp index ff80adeaf..ab78ddbe2 100644 --- a/src/core/devices/srandom.cpp +++ b/src/core/devices/srandom_device.cpp @@ -1,35 +1,43 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + #include #include "common/logging/log.h" -#include "srandom.h" +#include "srandom_device.h" namespace Core::Devices { + std::shared_ptr SRandomDevice::Create(u32 handle, const char*, int, u16) { std::srand(std::time(nullptr)); return std::shared_ptr( reinterpret_cast(new SRandomDevice(handle))); } + int SRandomDevice::ioctl(u64 cmd, Common::VaCtx* args) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 SRandomDevice::write(const void* buf, size_t nbytes) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t SRandomDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t SRandomDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 SRandomDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 SRandomDevice::lseek(s64 offset, int whence) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; @@ -37,8 +45,9 @@ s64 SRandomDevice::lseek(s64 offset, int whence) { s64 SRandomDevice::read(void* buf, size_t nbytes) { auto rbuf = static_cast(buf); - for (size_t i = 0; i < nbytes; i++) + for (size_t i = 0; i < nbytes; i++) { rbuf[i] = std::rand(); + } return nbytes; } @@ -66,4 +75,5 @@ s64 SRandomDevice::pwrite(const void* buf, size_t nbytes, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + } // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/srandom.h b/src/core/devices/srandom_device.h similarity index 90% rename from src/core/devices/srandom.h rename to src/core/devices/srandom_device.h index 3a3b02571..cd32f7289 100644 --- a/src/core/devices/srandom.h +++ b/src/core/devices/srandom_device.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include diff --git a/src/core/devices/urandom.cpp b/src/core/devices/urandom_device.cpp similarity index 90% rename from src/core/devices/urandom.cpp rename to src/core/devices/urandom_device.cpp index 8917caea5..c001aab83 100644 --- a/src/core/devices/urandom.cpp +++ b/src/core/devices/urandom_device.cpp @@ -1,8 +1,9 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + #include #include "common/logging/log.h" -#include "urandom.h" +#include "urandom_device.h" namespace Core::Devices { @@ -16,22 +17,27 @@ int URandomDevice::ioctl(u64 cmd, Common::VaCtx* args) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 URandomDevice::write(const void* buf, size_t nbytes) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t URandomDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + size_t URandomDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 URandomDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + s64 URandomDevice::lseek(s64 offset, int whence) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; @@ -39,8 +45,9 @@ s64 URandomDevice::lseek(s64 offset, int whence) { s64 URandomDevice::read(void* buf, size_t nbytes) { auto rbuf = static_cast(buf); - for (size_t i = 0; i < nbytes; i++) + for (size_t i = 0; i < nbytes; i++) { rbuf[i] = std::rand() & 0xFF; + } return nbytes; } @@ -68,4 +75,5 @@ s64 URandomDevice::pwrite(const void* buf, size_t nbytes, u64 offset) { LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); return 0; } + } // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/urandom.h b/src/core/devices/urandom_device.h similarity index 90% rename from src/core/devices/urandom.h rename to src/core/devices/urandom_device.h index 9370017d5..b8a854cc0 100644 --- a/src/core/devices/urandom.h +++ b/src/core/devices/urandom_device.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 7d456780b..8a191e666 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -233,7 +233,7 @@ void HandleTable::CreateStdHandles() { std::shared_ptr{reinterpret_cast(device)}; }; // order matters - setup("/dev/stdin", new Devices::NopDevice(0)); // stdin + setup("/dev/stdin", new Devices::Logger("stdin", false)); // stdin setup("/dev/stdout", new Devices::Logger("stdout", false)); // stdout setup("/dev/stderr", new Devices::Logger("stderr", true)); // stderr } diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index ce91fe192..b0f7fdafe 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -8,13 +8,13 @@ #include "common/logging/log.h" #include "common/scope_exit.h" #include "common/singleton.h" -#include "core/devices/deci_tty6.h" -#include "core/devices/dev_console.h" +#include "core/devices/deci_tty6_device.h" +#include "core/devices/console_device.h" #include "core/devices/logger.h" #include "core/devices/nop_device.h" -#include "core/devices/random.h" -#include "core/devices/srandom.h" -#include "core/devices/urandom.h" +#include "core/devices/random_device.h" +#include "core/devices/srandom_device.h" +#include "core/devices/urandom_device.h" #include "core/file_sys/fs.h" #include "core/libraries/kernel/file_system.h" #include "core/libraries/kernel/orbis_error.h" @@ -270,13 +270,6 @@ size_t PS4_SYSV_ABI _readv(int d, const SceKernelIovec* iov, int iovcnt) { } size_t PS4_SYSV_ABI _writev(int fd, const SceKernelIovec* iov, int iovcn) { - if (fd == 1) { - size_t total_written = 0; - for (int i = 0; i < iovcn; i++) { - total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout); - } - return total_written; - } auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(fd); if (file == nullptr) { From 84a341dce570c4a070d07bc023a757652247fb6f Mon Sep 17 00:00:00 2001 From: polyproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Tue, 21 Jan 2025 19:30:34 +0100 Subject: [PATCH 10/68] remove BADF return --- src/core/devices/nop_device.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/devices/nop_device.h b/src/core/devices/nop_device.h index 5518b1de1..da9a3fc82 100644 --- a/src/core/devices/nop_device.h +++ b/src/core/devices/nop_device.h @@ -27,7 +27,7 @@ public: } size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override { - return ORBIS_KERNEL_ERROR_EBADF; + return 0; } s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override { From 41b39428335025e65f9e707ed8d5a9a1b09ba942 Mon Sep 17 00:00:00 2001 From: polyproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Tue, 21 Jan 2025 19:34:05 +0100 Subject: [PATCH 11/68] clang-format --- src/core/file_sys/fs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 8a191e666..ec940503f 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -233,7 +233,7 @@ void HandleTable::CreateStdHandles() { std::shared_ptr{reinterpret_cast(device)}; }; // order matters - setup("/dev/stdin", new Devices::Logger("stdin", false)); // stdin + setup("/dev/stdin", new Devices::Logger("stdin", false)); // stdin setup("/dev/stdout", new Devices::Logger("stdout", false)); // stdout setup("/dev/stderr", new Devices::Logger("stderr", true)); // stderr } From 5c62a00134523c4d0b2f06e9b6c28c596631e748 Mon Sep 17 00:00:00 2001 From: polyproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Tue, 21 Jan 2025 21:14:05 +0100 Subject: [PATCH 12/68] `clang-format` (again) --- src/core/libraries/kernel/file_system.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index b0f7fdafe..0150c11f5 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -8,8 +8,8 @@ #include "common/logging/log.h" #include "common/scope_exit.h" #include "common/singleton.h" -#include "core/devices/deci_tty6_device.h" #include "core/devices/console_device.h" +#include "core/devices/deci_tty6_device.h" #include "core/devices/logger.h" #include "core/devices/nop_device.h" #include "core/devices/random_device.h" From 2a4798cfa60f430840855a88339c1f4b364df9a4 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 22 Jan 2025 00:40:00 -0800 Subject: [PATCH 13/68] tile: Fix some tile thickness calculation errors. (#2203) * tile: Fix some tile thickness calculation errors. * tile: Do not pad mip height to tile height. --- src/video_core/texture_cache/image_info.cpp | 13 ++++++------- src/video_core/texture_cache/tile.h | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 8068aae2f..dd89be8aa 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -178,7 +178,6 @@ void ImageInfo::UpdateSize() { case AmdGpu::TilingMode::Display_Linear: { std::tie(mip_info.pitch, mip_info.size) = ImageSizeLinearAligned(mip_w, mip_h, bpp, num_samples); - mip_info.height = mip_h; break; } case AmdGpu::TilingMode::Texture_Volume: @@ -188,12 +187,7 @@ void ImageInfo::UpdateSize() { case AmdGpu::TilingMode::Display_MicroTiled: case AmdGpu::TilingMode::Texture_MicroTiled: { std::tie(mip_info.pitch, mip_info.size) = - ImageSizeMicroTiled(mip_w, mip_h, bpp, thickness, num_samples); - mip_info.height = std::max(mip_h, 8u); - if (props.is_block) { - mip_info.pitch = std::max(mip_info.pitch * 4, 32u); - mip_info.height = std::max(mip_info.height * 4, 32u); - } + ImageSizeMicroTiled(mip_w, mip_h, thickness, bpp, num_samples); break; } case AmdGpu::TilingMode::Display_MacroTiled: @@ -208,6 +202,11 @@ void ImageInfo::UpdateSize() { UNREACHABLE(); } } + mip_info.height = mip_h; + if (props.is_block) { + mip_info.pitch = std::max(mip_info.pitch * 4, 32u); + mip_info.height = std::max(mip_info.height * 4, 32u); + } mip_info.size *= mip_d; mip_info.offset = guest_size; mips_layout.emplace_back(mip_info); diff --git a/src/video_core/texture_cache/tile.h b/src/video_core/texture_cache/tile.h index c111e6aca..54938b801 100644 --- a/src/video_core/texture_cache/tile.h +++ b/src/video_core/texture_cache/tile.h @@ -313,8 +313,8 @@ constexpr std::pair ImageSizeMicroTiled(u32 pitch, u32 height, u32 const auto& [pitch_align, height_align] = micro_tile_extent; auto pitch_aligned = (pitch + pitch_align - 1) & ~(pitch_align - 1); const auto height_aligned = (height + height_align - 1) & ~(height_align - 1); - size_t log_sz = (pitch_aligned * height_aligned * bpp * num_samples * thickness + 7) / 8; - while (log_sz % 256) { + size_t log_sz = (pitch_aligned * height_aligned * bpp * num_samples + 7) / 8; + while ((log_sz * thickness) % 256) { pitch_aligned += pitch_align; log_sz = (pitch_aligned * height_aligned * bpp * num_samples + 7) / 8; } From 78ae9613c5f0e40f30b5a5cea83fa368530f1c24 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Wed, 22 Jan 2025 04:07:43 -0600 Subject: [PATCH 14/68] Fix for address_space initialization on Windows (#2202) Should fix some `Region coalescing failed: Attempt to access invalid address.` crashes. Co-authored-by: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> --- src/core/address_space.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp index 2b7331cbd..e9fb8cfbc 100644 --- a/src/core/address_space.cpp +++ b/src/core/address_space.cpp @@ -67,28 +67,25 @@ struct AddressSpace::Impl { static constexpr size_t ReductionOnFail = 1_GB; static constexpr size_t MaxReductions = 10; - size_t reduction = 0; size_t virtual_size = SystemManagedSize + SystemReservedSize + UserSize; for (u32 i = 0; i < MaxReductions; i++) { - virtual_base = static_cast(VirtualAlloc2(process, NULL, virtual_size - reduction, + virtual_base = static_cast(VirtualAlloc2(process, NULL, virtual_size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, ¶m, 1)); if (virtual_base) { break; } - reduction += ReductionOnFail; + virtual_size -= ReductionOnFail; } ASSERT_MSG(virtual_base, "Unable to reserve virtual address space: {}", Common::GetLastErrorMsg()); - // Take the reduction off of the system managed area, and leave the others unchanged. - reduction = size_t(virtual_base - SYSTEM_MANAGED_MIN); - system_managed_base = virtual_base; - system_managed_size = SystemManagedSize - reduction; system_reserved_base = reinterpret_cast(SYSTEM_RESERVED_MIN); system_reserved_size = SystemReservedSize; + system_managed_base = virtual_base; + system_managed_size = system_reserved_base - virtual_base; user_base = reinterpret_cast(USER_MIN); - user_size = UserSize; + user_size = virtual_base + virtual_size - user_base; LOG_INFO(Kernel_Vmm, "System managed virtual memory region: {} - {}", fmt::ptr(system_managed_base), @@ -101,10 +98,8 @@ struct AddressSpace::Impl { // Initializer placeholder tracker const uintptr_t system_managed_addr = reinterpret_cast(system_managed_base); - const uintptr_t system_reserved_addr = reinterpret_cast(system_reserved_base); - const uintptr_t user_addr = reinterpret_cast(user_base); regions.emplace(system_managed_addr, - MemoryRegion{system_managed_addr, virtual_size - reduction, false}); + MemoryRegion{system_managed_addr, virtual_size, false}); // Allocate backing file that represents the total physical memory. backing_handle = From adbff4056fe5a98026c2239ecd9511cd1be90e0f Mon Sep 17 00:00:00 2001 From: f3d209 <45915273+rootexpm@users.noreply.github.com> Date: Wed, 22 Jan 2025 10:10:35 +0000 Subject: [PATCH 15/68] Added ability to change save data path (#2199) * added ability to change save data path * get default save data path from fs * add copyright * formatting --- src/common/config.cpp | 15 +++ src/common/config.h | 2 + .../libraries/save_data/save_instance.cpp | 6 +- src/qt_gui/settings_dialog.cpp | 33 ++++- src/qt_gui/settings_dialog.ui | 121 +++++++++++------- src/qt_gui/translations/en.ts | 8 ++ 6 files changed, 132 insertions(+), 53 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index ed9af5a72..6e9db50ff 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -78,6 +78,7 @@ static std::string trophyKey; static bool load_game_size = true; std::vector settings_install_dirs = {}; std::filesystem::path settings_addon_install_dir = {}; +std::filesystem::path save_data_path = {}; u32 main_window_geometry_x = 400; u32 main_window_geometry_y = 400; u32 main_window_geometry_w = 1280; @@ -110,6 +111,13 @@ bool GetLoadGameSizeEnabled() { return load_game_size; } +std::filesystem::path GetSaveDataPath() { + if (save_data_path.empty()) { + return Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir); + } + return save_data_path; +} + void setLoadGameSizeEnabled(bool enable) { load_game_size = enable; } @@ -502,6 +510,10 @@ void setGameInstallDirs(const std::vector& settings_insta settings_install_dirs = settings_install_dirs_config; } +void setSaveDataPath(const std::filesystem::path& path) { + save_data_path = path; +} + u32 getMainWindowGeometryX() { return main_window_geometry_x; } @@ -690,6 +702,8 @@ void load(const std::filesystem::path& path) { addGameInstallDir(std::filesystem::path{dir}); } + save_data_path = toml::find_fs_path_or(gui, "saveDataPath", {}); + settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {}); main_window_geometry_x = toml::find_or(gui, "geometry_x", 0); main_window_geometry_y = toml::find_or(gui, "geometry_y", 0); @@ -784,6 +798,7 @@ void save(const std::filesystem::path& path) { install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data}); } data["GUI"]["installDirs"] = install_dirs; + data["GUI"]["saveDataPath"] = std::string{fmt::UTF(save_data_path.u8string()).data}; data["GUI"]["loadGameSizeEnabled"] = load_game_size; data["GUI"]["addonInstallDir"] = diff --git a/src/common/config.h b/src/common/config.h index cb56f99c7..564947f1e 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -18,6 +18,7 @@ void saveMainWindow(const std::filesystem::path& path); std::string getTrophyKey(); void setTrophyKey(std::string key); bool GetLoadGameSizeEnabled(); +std::filesystem::path GetSaveDataPath(); void setLoadGameSizeEnabled(bool enable); bool getIsFullscreen(); std::string getFullscreenMode(); @@ -82,6 +83,7 @@ void setUserName(const std::string& type); void setUpdateChannel(const std::string& type); void setSeparateUpdateEnabled(bool use); void setGameInstallDirs(const std::vector& settings_install_dirs_config); +void setSaveDataPath(const std::filesystem::path& path); void setCompatibilityEnabled(bool use); void setCheckCompatibilityOnStartup(bool use); diff --git a/src/core/libraries/save_data/save_instance.cpp b/src/core/libraries/save_data/save_instance.cpp index 99daf83cc..c2b7dca3c 100644 --- a/src/core/libraries/save_data/save_instance.cpp +++ b/src/core/libraries/save_data/save_instance.cpp @@ -47,15 +47,13 @@ namespace Libraries::SaveData { std::filesystem::path SaveInstance::MakeTitleSavePath(OrbisUserServiceUserId user_id, std::string_view game_serial) { - return Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) / std::to_string(user_id) / - game_serial; + return Config::GetSaveDataPath() / std::to_string(user_id) / game_serial; } std::filesystem::path SaveInstance::MakeDirSavePath(OrbisUserServiceUserId user_id, std::string_view game_serial, std::string_view dir_name) { - return Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) / std::to_string(user_id) / - game_serial / dir_name; + return Config::GetSaveDataPath() / std::to_string(user_id) / game_serial / dir_name; } uint64_t SaveInstance::GetMaxBlockFromSFO(const PSF& psf) { diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 175c8c51d..5695d6232 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -202,6 +202,21 @@ SettingsDialog::SettingsDialog(std::span physical_devices, delete selected_item; } }); + + connect(ui->browseButton, &QPushButton::clicked, this, [this]() { + const auto save_data_path = Config::GetSaveDataPath(); + QString initial_path; + Common::FS::PathToQString(initial_path, save_data_path); + + QString save_data_path_string = + QFileDialog::getExistingDirectory(this, tr("Directory to save data"), initial_path); + + auto file_path = Common::FS::PathFromQString(save_data_path_string); + if (!file_path.empty()) { + Config::setSaveDataPath(file_path); + ui->currentSaveDataPath->setText(save_data_path_string); + } + }); } // DEBUG TAB @@ -256,6 +271,10 @@ SettingsDialog::SettingsDialog(std::span physical_devices, ui->addFolderButton->installEventFilter(this); ui->removeFolderButton->installEventFilter(this); + ui->saveDataGroupBox->installEventFilter(this); + ui->currentSaveDataPath->installEventFilter(this); + ui->browseButton->installEventFilter(this); + // Debug ui->debugDump->installEventFilter(this); ui->vkValidationCheckBox->installEventFilter(this); @@ -286,6 +305,11 @@ void SettingsDialog::LoadValuesFromConfig() { const QVector languageIndexes = {21, 23, 14, 6, 18, 1, 12, 22, 2, 4, 25, 24, 29, 5, 0, 9, 15, 16, 17, 7, 26, 8, 11, 20, 3, 13, 27, 10, 19, 30, 28}; + const auto save_data_path = Config::GetSaveDataPath(); + QString save_data_path_string; + Common::FS::PathToQString(save_data_path_string, save_data_path); + ui->currentSaveDataPath->setText(save_data_path_string); + ui->consoleLanguageComboBox->setCurrentIndex( std::distance(languageIndexes.begin(), std::find(languageIndexes.begin(), languageIndexes.end(), @@ -497,6 +521,13 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("removeFolderButton"); } + // Save Data + if (elementName == "saveDataGroupBox" || elementName == "currentSaveDataPath") { + text = tr("saveDataBox"); + } else if (elementName == "browseButton") { + text = tr("browseButton"); + } + // Debug if (elementName == "debugDump") { text = tr("debugDump"); @@ -603,4 +634,4 @@ void SettingsDialog::ResetInstallFolders() { } Config::setGameInstallDirs(settings_install_dirs_config); } -} +} \ No newline at end of file diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index c084d4849..d72a56973 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -53,7 +53,7 @@ - 0 + 3 @@ -67,8 +67,8 @@ 0 0 - 946 - 611 + 771 + 606 @@ -644,8 +644,8 @@ 0 0 - 946 - 605 + 455 + 252 @@ -928,8 +928,8 @@ 0 0 - 946 - 605 + 579 + 194 @@ -1178,51 +1178,76 @@ Paths - - - 0 - 0 - 946 - 605 - - - + - - - - - Game Folders - - - - - - 0 - - + + + Game Folders + + + + + - - Add... - + + Add... + - - + + - - Remove - + + Remove + - - - - - - + + + + + Qt::Horizontal + + + + 40 + 20 + + + + - - - + + + + + + + + + + + Save Data Path + + + + + + + + true + + + + + + + Browse + + + + + + + @@ -1239,8 +1264,8 @@ 0 0 - 946 - 586 + 510 + 269 diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 9127df7e3..dc72d97c9 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -912,6 +912,14 @@ rdocCheckBox Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame. + + saveDataBox + Save Data Path:\nThe folder where game save data will be saved. + + + browseButton + Browse:\nBrowse for a folder to set as the save data path. + CheatsPatches From 2968cf5a99a1be2fe7f3f28dbc65fe16f488c8af Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Wed, 22 Jan 2025 09:06:27 -0600 Subject: [PATCH 16/68] sceKernelVirtualQuery Fixes (#2204) --- src/core/memory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 619941000..f90d4e6aa 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -529,8 +529,8 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags, info->is_flexible.Assign(vma.type == VMAType::Flexible); info->is_direct.Assign(vma.type == VMAType::Direct); info->is_stack.Assign(vma.type == VMAType::Stack); - info->is_pooled.Assign(vma.type == VMAType::PoolReserved); - info->is_committed.Assign(vma.type == VMAType::Pooled); + info->is_pooled.Assign(vma.type == VMAType::PoolReserved || vma.type == VMAType::Pooled); + info->is_committed.Assign(vma.IsMapped()); vma.name.copy(info->name.data(), std::min(info->name.size(), vma.name.size())); if (vma.type == VMAType::Direct) { const auto dmem_it = FindDmemArea(vma.phys_base); From b3bce086b30c148f25817f9df06abaedad244453 Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Wed, 22 Jan 2025 12:08:49 -0300 Subject: [PATCH 17/68] devtools: fix ReleaseKeyboard assert being triggered if many shader editor windows exist (#2205) --- src/core/devtools/widget/shader_list.cpp | 65 +++++++++++++++--------- src/core/devtools/widget/shader_list.h | 9 ++-- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/core/devtools/widget/shader_list.cpp b/src/core/devtools/widget/shader_list.cpp index 97d01896d..0285db5a5 100644 --- a/src/core/devtools/widget/shader_list.cpp +++ b/src/core/devtools/widget/shader_list.cpp @@ -24,16 +24,33 @@ using namespace ImGui; namespace Core::Devtools::Widget { -ShaderList::Selection::Selection(int index) : index(index) { - isa_editor.SetPalette(TextEditor::GetDarkPalette()); - isa_editor.SetReadOnly(true); - glsl_editor.SetPalette(TextEditor::GetDarkPalette()); - glsl_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL()); +ShaderList::Selection::Selection(int index) + : index(index), isa_editor(std::make_unique()), + glsl_editor(std::make_unique()) { + isa_editor->SetPalette(TextEditor::GetDarkPalette()); + isa_editor->SetReadOnly(true); + glsl_editor->SetPalette(TextEditor::GetDarkPalette()); + glsl_editor->SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL()); presenter->GetWindow().RequestKeyboard(); } ShaderList::Selection::~Selection() { - presenter->GetWindow().ReleaseKeyboard(); + if (index >= 0) { + presenter->GetWindow().ReleaseKeyboard(); + } +} + +ShaderList::Selection::Selection(Selection&& other) noexcept + : index{other.index}, isa_editor{std::move(other.isa_editor)}, + glsl_editor{std::move(other.glsl_editor)}, open{other.open}, showing_bin{other.showing_bin}, + patch_path{std::move(other.patch_path)}, patch_bin_path{std::move(other.patch_bin_path)} { + other.index = -1; +} + +ShaderList::Selection& ShaderList::Selection::operator=(Selection other) { + using std::swap; + swap(*this, other); + return *this; } void ShaderList::Selection::ReloadShader(DebugStateType::ShaderDump& value) { @@ -72,13 +89,13 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) { value.is_patched = !value.patch_spv.empty(); if (!value.is_patched) { // No patch - isa_editor.SetText(value.cache_isa_disasm); - glsl_editor.SetText(value.cache_spv_disasm); + isa_editor->SetText(value.cache_isa_disasm); + glsl_editor->SetText(value.cache_spv_disasm); } else { - isa_editor.SetText(value.cache_patch_disasm); - isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV()); - glsl_editor.SetText(value.patch_source); - glsl_editor.SetReadOnly(false); + isa_editor->SetText(value.cache_patch_disasm); + isa_editor->SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV()); + glsl_editor->SetText(value.patch_source); + glsl_editor->SetReadOnly(false); } } @@ -97,18 +114,18 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) { if (value.patch_source.empty()) { value.patch_source = value.cache_spv_disasm; } - isa_editor.SetText(value.cache_patch_disasm); - isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV()); - glsl_editor.SetText(value.patch_source); - glsl_editor.SetReadOnly(false); + isa_editor->SetText(value.cache_patch_disasm); + isa_editor->SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV()); + glsl_editor->SetText(value.patch_source); + glsl_editor->SetReadOnly(false); if (!value.patch_spv.empty()) { ReloadShader(value); } } else { - isa_editor.SetText(value.cache_isa_disasm); - isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition()); - glsl_editor.SetText(value.cache_spv_disasm); - glsl_editor.SetReadOnly(true); + isa_editor->SetText(value.cache_isa_disasm); + isa_editor->SetLanguageDefinition(TextEditor::LanguageDefinition()); + glsl_editor->SetText(value.cache_spv_disasm); + glsl_editor->SetReadOnly(true); ReloadShader(value); } } @@ -154,7 +171,7 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) { compile = true; } if (save) { - value.patch_source = glsl_editor.GetText(); + value.patch_source = glsl_editor->GetText(); std::ofstream file{patch_path, std::ios::binary | std::ios::trunc}; file << value.patch_source; std::string msg = "Patch saved to "; @@ -192,7 +209,7 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) { DebugState.ShowDebugMessage("Decompilation failed (Compile was ok):\n" + res); } else { - isa_editor.SetText(value.cache_patch_disasm); + isa_editor->SetText(value.cache_patch_disasm); ReloadShader(value); } } @@ -201,9 +218,9 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) { } if (showing_bin) { - isa_editor.Render(value.is_patched ? "SPIRV" : "ISA", GetContentRegionAvail()); + isa_editor->Render(value.is_patched ? "SPIRV" : "ISA", GetContentRegionAvail()); } else { - glsl_editor.Render("GLSL", GetContentRegionAvail()); + glsl_editor->Render("GLSL", GetContentRegionAvail()); } End(); diff --git a/src/core/devtools/widget/shader_list.h b/src/core/devtools/widget/shader_list.h index fbb8d2070..c882b0964 100644 --- a/src/core/devtools/widget/shader_list.h +++ b/src/core/devtools/widget/shader_list.h @@ -14,14 +14,17 @@ class ShaderList { struct Selection { explicit Selection(int index); ~Selection(); + Selection(const Selection& other) = delete; + Selection(Selection&& other) noexcept; + Selection& operator=(Selection other); void ReloadShader(DebugStateType::ShaderDump& value); bool DrawShader(DebugStateType::ShaderDump& value); - int index; - TextEditor isa_editor{}; - TextEditor glsl_editor{}; + int index{-1}; + std::unique_ptr isa_editor{}; + std::unique_ptr glsl_editor{}; bool open = true; bool showing_bin = false; From e584444aa38a6f1191f622ef1b8db7617c0549c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8A=E9=A4=85=E3=81=AECreeeper?= <56744841+creeper-0910@users.noreply.github.com> Date: Thu, 23 Jan 2025 06:52:27 +0900 Subject: [PATCH 18/68] Update Japanese translation (#2209) * Update Japanese translation * Update Japanese translation * Update Japanese translation --- src/qt_gui/translations/ja_JP.ts | 164 +++++++++++++++---------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index e07d4eb25..bb9488a6b 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -19,7 +19,7 @@ This software should not be used to play games you have not legally obtained. - このソフトウェアは、合法的に入手していないゲームをプレイするために使用するものではありません。 + 非正規、非合法のゲームをプレイするためにこのソフトウェアを使用しないでください。 @@ -33,7 +33,7 @@ GameInfoClass Loading game list, please wait :3 - ゲームリストを読み込み中です。お待ちください :3 + ゲームリストを読み込み中です。しばらくお待ちください :3 Cancel @@ -52,7 +52,7 @@ Select which directory you want to install to. - Select which directory you want to install to. + インストール先のディレクトリを選択してください。 @@ -75,7 +75,7 @@ The value for location to install games is not valid. - ゲームをインストールする場所が無効です。 + ゲームのインストール場所が無効です。 @@ -130,35 +130,35 @@ Delete... - Delete... + 削除... Delete Game - Delete Game + ゲームを削除 Delete Update - Delete Update + アップデートを削除 Delete DLC - Delete DLC + DLCを削除 Compatibility... - Compatibility... + 互換性... Update database - Update database + データベースを更新 View report - View report + レポートを表示 Submit a report - Submit a report + レポートを送信 Shortcut creation @@ -182,23 +182,23 @@ Game - Game + ゲーム requiresEnableSeparateUpdateFolder_MSG - This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + この機能を利用するには、 'アップデートフォルダの分離を有効化' を有効化する必要があります。 This game has no update to delete! - This game has no update to delete! + このゲームにはアップデートがないため削除することができません! Update - Update + アップデート This game has no DLC to delete! - This game has no DLC to delete! + このゲームにはDLCがないため削除することができません! DLC @@ -206,11 +206,11 @@ Delete %1 - Delete %1 + %1 を削除 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + %1 の %2 ディレクトリを本当に削除しますか? @@ -241,15 +241,15 @@ Install application from a .pkg file - .pkgファイルからアプリケーションをインストールする + .pkgファイルからアプリケーションをインストール Recent Games - 最近のゲーム + 最近プレイしたゲーム Open shadPS4 Folder - Open shadPS4 Folder + shadPS4フォルダを開く Exit @@ -273,7 +273,7 @@ Tiny - 極小 + 最小 Small @@ -297,7 +297,7 @@ Elf Viewer - Elfビュワー + Elfビューアー Game Install Directory @@ -397,11 +397,11 @@ You have downloaded cheats for all the games you have installed. - インストールしたすべてのゲームのチートをダウンロードしました。 + インストールされているすべてのゲームのチートをダウンロードしました。 Patches Downloaded Successfully! - パッチが正常にダウンロードされました! + パッチが正常にダウンロードされました! All Patches available for all games have been downloaded. @@ -425,11 +425,11 @@ Only one file can be selected! - 1つのファイルしか選択できません! + 1つのファイルしか選択できません! PKG Extraction - PKG抽出 + PKGの抽出 Patch detected! @@ -473,7 +473,7 @@ PKG is a patch, please install the game first! - PKGはパッチです。ゲームを先にインストールしてください! + PKGはパッチです。ゲームを先にインストールしてください! PKG ERROR @@ -526,11 +526,11 @@ Console Language - コンソール言語 + コンソールの言語 Emulator Language - エミュレーター言語 + エミュレーターの言語 Emulator @@ -542,7 +542,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + アップデートフォルダの分離を有効化 Show Game Size In List @@ -550,7 +550,7 @@ Show Splash - スプラッシュを表示する + スプラッシュ画面を表示する Is PS4 Pro @@ -566,11 +566,11 @@ Trophy Key - Trophy Key + トロフィーキー Trophy - Trophy + トロフィー Logger @@ -602,7 +602,7 @@ Hide Cursor Idle Timeout - カーソル非アクティブタイムアウト + カーソルを隠すまでの非アクティブ期間 s @@ -706,7 +706,7 @@ Disable Trophy Pop-ups - Disable Trophy Pop-ups + トロフィーのポップアップを無効化 Play title music @@ -714,19 +714,19 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + 起動時に互換性データベースを更新する Game Compatibility - Game Compatibility + ゲームの互換性 Display Compatibility Data - Display Compatibility Data + 互換性に関するデータを表示 Update Compatibility Database - Update Compatibility Database + 互換性データベースを更新 Volume @@ -734,7 +734,7 @@ Audio Backend - Audio Backend + オーディオ バックエンド Save @@ -754,15 +754,15 @@ Point your mouse at an option to display its description. - オプションにマウスをポイントすると、その説明が表示されます。 + 設定項目にマウスをホバーすると、説明が表示されます。 consoleLanguageGroupBox - コンソール言語:\nPS4ゲームが使用する言語を設定します。\nこれはゲームがサポートする言語に設定することをお勧めしますが、地域によって異なる場合があります。 + コンソールの言語:\nPS4ゲームが使用する言語を設定します。\nゲームでサポートされている言語に設定することをお勧めしますが、地域によって異なる場合があります。 emulatorLanguageGroupBox - エミュレーター言語:\nエミュレーターのユーザーインターフェースの言語を設定します。 + エミュレーターの言語:\nエミュレーターのユーザーインターフェースの言語を設定します。 fullscreenCheckBox @@ -770,7 +770,7 @@ separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + Enable Separate Update Folder:\nゲームのアップデートを別のフォルダにインストールすることで、管理が容易になります。 showSplashCheckBox @@ -778,7 +778,7 @@ ps4proCheckBox - PS4 Proです:\nエミュレーターがPS4 PROとして動作するようにし、これをサポートするゲームで特別な機能を有効にする場合があります。 + PS4 Pro モード:\nエミュレーターがPS4 PROとして動作するようになり、PS4 PROをサポートする一部のゲームで特別な機能が有効化される場合があります。 discordRPCCheckbox @@ -790,7 +790,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + トロフィーキー:\nトロフィーの復号に使用されるキーです。脱獄済みのコンソールから取得することができます。\n16進数のみを受け入れます。 logTypeGroupBox @@ -798,27 +798,27 @@ logFilter - ログフィルター:\n特定の情報のみを印刷するようにログをフィルタリングします。\n例: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" レベル: Trace, Debug, Info, Warning, Error, Critical - この順序で、特定のレベルはリスト内のすべての前のレベルをサイレンスし、その後のすべてのレベルをログに記録します。 + ログフィルター:\n特定の情報のみを印刷するようにログをフィルタリングします。\n例: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" \nレベル: Trace, Debug, Info, Warning, Error, Critical - レベルはこの並び通りに処理され、指定されたレベルより前のレベル ログを抑制し、それ以外のすべてのレベルをログに記録します。 updaterGroupBox - 更新:\nRelease: 非常に古いかもしれないが、より信頼性が高くテスト済みの公式バージョンを毎月リリースします。\nNightly: 最新の機能と修正がすべて含まれていますが、バグが含まれている可能性があり、安定性は低いです。 + 更新:\nRelease: 最新の機能を利用できない可能性がありますが、より信頼性が高くテストされた公式バージョンが毎月リリースされます。\nNightly: 最新の機能と修正がすべて含まれていますが、バグが含まれている可能性があり、安定性は低いです。 GUIgroupBox - タイトルミュージックを再生:\nゲームがそれをサポートしている場合、GUIでゲームを選択したときに特別な音楽を再生することを有効にします。 + タイトルミュージックを再生:\nゲームでサポートされている場合に、GUIでゲームを選択したときに特別な音楽を再生する機能を有効にします。 disableTrophycheckBox - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + トロフィーのポップアップを無効化:\nゲーム内でのトロフィー通知を無効化します。 トロフィーの進行状況は、トロフィービューアーを使用して確認できます。(メインウィンドウでゲームを右クリック) hideCursorGroupBox - カーソルを隠す:\nカーソルが消えるタイミングを選択してください:\n決して: いつでもマウスが見えます。\nアイドル: アイダルの後に消えるまでの時間を設定します。\n常に: マウスは決して見えません。 + カーソルを隠す:\nカーソルが消えるタイミングを選択してください:\n無効: 常にカーソルが表示されます。\n非アクティブ時: カーソルの非アクティブ期間が指定した時間を超えた場合にカーソルを隠します。\n常に: カーソルは常に隠れた状態になります。 idleTimeoutGroupBox - アイドル後にマウスが消えるまでの時間を設定します。 + カーソルが非アクティブになってから隠すまでの時間を設定します。 backButtonBehaviorGroupBox @@ -826,23 +826,23 @@ enableCompatibilityCheckBox - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + 互換性に関するデータを表示:\nゲームの互換性に関する情報を表として表示します。常に最新情報を取得したい場合、"起動時に互換性データベースを更新する" を有効化してください。 checkCompatibilityOnStartupCheckBox - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + 起動時に互換性データベースを更新する:\nshadPS4の起動時に自動で互換性データベースを更新します。 updateCompatibilityButton - Update Compatibility Database:\nImmediately update the compatibility database. + 互換性データベースを更新する:\n今すぐ互換性データベースを更新します。 Never - 決して + 無効 Idle - アイドル + 非アクティブ時 Always @@ -850,11 +850,11 @@ Touchpad Left - タッチパッド左 + 左タッチパッド Touchpad Right - タッチパッド右 + 右タッチパッド Touchpad Center @@ -866,15 +866,15 @@ graphicsAdapterGroupBox - グラフィックデバイス:\n複数のGPUシステムで、ドロップダウンリストからエミュレーターで使用するGPUを選択するか、\n「自動選択」を選択して自動的に決定します。 + グラフィックデバイス:\nシステムに複数のGPUが搭載されている場合、ドロップダウンリストからエミュレーターで使用するGPUを選択するか、\n「自動選択」を選択して自動的に決定します。 resolutionLayout - 幅/高さ:\n起動時にエミュレーターウィンドウのサイズを設定します。ゲーム中にサイズ変更できます。\nこれはゲーム内の解像度とは異なります。 + 幅/高さ:\n起動時にエミュレーターウィンドウのサイズを設定します。ゲーム中でもサイズを変更することができます。\nこれはゲーム内の解像度とは異なります。 heightDivider - Vblankディバイダー:\nエミュレーターが更新されるフレームレートにこの数を掛けます。これを変更すると、ゲームの速度が上がったり、想定外の変更がある場合、ゲームの重要な機能が壊れる可能性があります! + Vblankディバイダー:\nエミュレーターが更新されるフレームレートにこの数を掛けます。これを変更すると、ゲームの速度が上がったり、想定外の変更がある場合、ゲームの重要な機能が壊れる可能性があります! dumpShadersCheckBox @@ -917,11 +917,11 @@ CheatsPatches Cheats / Patches for - Cheats / Patches for + のチート/パッチ defaultTextEdit_MSG - チート/パッチは実験的です。\n使用には注意してください。\n\nリポジトリを選択し、ダウンロードボタンをクリックしてチートを個別にダウンロードします。\n「Patches」タブでは、すべてのパッチを一度にダウンロードし、使用したいものを選択して選択を保存できます。\n\nチート/パッチを開発していないため、\n問題があればチートの作者に報告してください。\n\n新しいチートを作成しましたか?\nhttps://github.com/shadps4-emu/ps4_cheats を訪問してください。 + チート/パッチは実験的です。\n使用には注意してください。\n\nリポジトリを選択し、ダウンロードボタンをクリックしてチートを個別にダウンロードします。\n「Patches」タブでは、すべてのパッチを一度にダウンロードし、使用したいものを選択して選択を保存できます。\n\nチート/パッチは開発を行っていないため、\n問題があればチートの作者に報告してください。\n\n新しいチートを作成しましたか?\nhttps://github.com/shadps4-emu/ps4_cheats を訪問してください。 No Image Available @@ -997,7 +997,7 @@ Unable to open files.json for reading. - files.jsonを読み込み用に開けません。 + files.jsonを読み取りのために開く事が出来ませんでした。 No patch file found for the current serial. @@ -1005,11 +1005,11 @@ Unable to open the file for reading. - ファイルを読み込み用に開けません。 + ファイルを読み取りのために開く事が出来ませんでした。 Unable to open the file for writing. - ファイルを記録用に開けません。 + ファイルをを書き込みのために開く事が出来ませんでした。 Failed to parse XML: @@ -1029,7 +1029,7 @@ The selected source is invalid. - 選択したソースは無効です。 + 選択されたソースは無効です。 File Exists @@ -1113,7 +1113,7 @@ Failed to open files.json for writing - files.jsonを記録用に開けません + files.jsonを読み取りのために開く事が出来ませんでした。 Author: @@ -1125,7 +1125,7 @@ Failed to open files.json for reading. - files.jsonを読み込み用に開けません。 + files.jsonを読み取りのために開く事が出来ませんでした。 Name: @@ -1180,43 +1180,43 @@ Never Played - Never Played + 未プレイ h - h + 時間 m - m + s - s + Compatibility is untested - Compatibility is untested + 互換性は未検証です Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + ゲームが正常に初期化されない/エミュレーターがクラッシュする Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + ゲームは起動しますが、空のスクリーンが表示されます Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + 正常にゲーム画面が表示されますが、メニューから先に進むことができません Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + ゲームを壊すような不具合や、プレイが不可能なほどのパフォーマンスの問題があります Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + パフォーマンスに問題はなく、大きな不具合なしでゲームをプレイすることができます From c015ac134462b3c425d69953e27da0e6c8ca9498 Mon Sep 17 00:00:00 2001 From: tomboylover93 <95257311+tomboylover93@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:53:27 -0800 Subject: [PATCH 19/68] Update Linux building documentation (#2211) * wip: redo build instructions guide for Linux * Add Linux screenshots directory to REUSE.toml * change links for Visual Studio Code images * change instructions for building from terminal reference: https://github.com/shadps4-emu/shadPS4/pull/2211#issuecomment-2608134455 --- REUSE.toml | 1 + documents/Screenshots/Linux/1.png | Bin 0 -> 11447 bytes documents/Screenshots/Linux/2.png | Bin 0 -> 20159 bytes documents/Screenshots/Linux/3.png | Bin 0 -> 25540 bytes documents/Screenshots/Linux/4.png | Bin 0 -> 16845 bytes documents/Screenshots/Linux/5.png | Bin 0 -> 26893 bytes documents/building-linux.md | 103 +++++++++++++++++++++++++----- 7 files changed, 89 insertions(+), 15 deletions(-) create mode 100644 documents/Screenshots/Linux/1.png create mode 100644 documents/Screenshots/Linux/2.png create mode 100644 documents/Screenshots/Linux/3.png create mode 100644 documents/Screenshots/Linux/4.png create mode 100644 documents/Screenshots/Linux/5.png diff --git a/REUSE.toml b/REUSE.toml index 55d76673d..a62974bcd 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -14,6 +14,7 @@ path = [ "documents/changelog.md", "documents/Quickstart/2.png", "documents/Screenshots/*", + "documents/Screenshots/Linux/*", "externals/MoltenVK/MoltenVK_icd.json", "scripts/ps4_names.txt", "src/images/about_icon.png", diff --git a/documents/Screenshots/Linux/1.png b/documents/Screenshots/Linux/1.png new file mode 100644 index 0000000000000000000000000000000000000000..3dd5ef92b39960dcf98cf22d47f3895d84884581 GIT binary patch literal 11447 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 OgJdKW#s7Xb^#4D*Qdz11 literal 0 HcmV?d00001 diff --git a/documents/Screenshots/Linux/2.png b/documents/Screenshots/Linux/2.png new file mode 100644 index 0000000000000000000000000000000000000000..fc5ff3b534773394c892a9d026a4d5e553ed2cfa GIT binary patch literal 20159 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

IFt#Ds2NY9n;YzAXc!7BTm6;cMp= zW??z(+?D4Cs|gWX=%HmiC|G#D?|vgNXaF1~(99)kf?FS=O3s`ssPnB%REGBWSDV?U z0OUpl@lRaN3(JkRMmt{D*T+KFBqbG5j35BNrKj>366)&moe9`ALVs}P9Og{I^3pjG zQG{jR*aRx48g@$$PgBO?sSA2;A%>EF#H6xcir3l`Gqhj9Z9JJ0@Z7Vwz*FC94@Wr{ z+THbJ)lI(ec6t9K?n2NW@ZbF4=W$-A0>cG@3Z$k`skYZqDxUfYZ=$@9N=!*g$}^UjCEF z8m6e|5ZSr&=EZg@`JxFuV3=`5Et0-pr9z4lj>(cI`OMi7Ienrh?qs3eEoaT^(fmR) zi|c}75)-p5=yOxQrrKbzjQevfO!icZZVzvkf9h-XYP3;`PTjFR>2@yn)h823ODHJB z?ogWt)uzsx_24{?f2%Vx7>SEDRiC2?8Q2{xN=cG9|joDDq*<#PdYQB$_y*5^Z z^1C{gC#SAiRzBed#|TVMV=(0G2OHyB(U212#1e~AM#FZ}zV3=>#<6w-!oE46%ZekJ zUUkdSH3>zkzSO{^xT=2%KiH~%us&X?x&yJHDo7*8T~XCi?{@6GU77pIJF>|$+FXas zO#?A~qbVZR0`V8_-|0EPwwYd;892OnrDl4c-|JHSHEz7yhX8k{-9EH4=@U6HV63zL z%-PW~@m@Cj4qR54z-861tlEI%q$99oU)1U}h&$|g(W!OOcBun&Ql`vUcphhn3*{0f zqX$*`HFuP1Esc9U^!uiB1F{7$)C&7Es3v0Pa)5ax++h12$Lnk@;a*>-hjr*zW zJ$ZC^r)>88-#jVPp()Xi8^V{6)Wm$qD-Mn82sY5c2TfZBb9`)w9FO-ZQ)S-ECKV0i zeTm~$4sWIo*{W-IHQ-YXTV+wMc0#m^=ggab3c!!=PwyL#nTRc`I#aEiR5*f)8qiee zaN%!JY`{a8j*o~n8;^`e%}(2;R`I>i4@ zdjtFv^O~D6ydQ2&;>X#@G$B|n(XVE7ooDkkBo*TWEYJU#o?tw=EQG`H^NJYxCtR`f zT-PW(d1|q+Pq|003tBiKKCkL-buY`uA`wNOk&U5+XgCmyzQxAKkhPxIK|v|#THmFP zYh?uNTaq)XwtAEo`wKzYH)qI}VIF;e?E&$^JP~xmg0iC7?JUgIVnT<|d$#L^%`|bh zyH)OH?r;#@edvj=y*7tERfXFg^~pUtgHSPkPNuJ%rr7BRU%D5jhs-p9%dz?1$Wj%C zwJkMy@@N{vqQs@nm7#uJEKjQvdem_CMc5soOW3==TLNu^ZBClN8o0Aw<9LAao?^7&2!pMoaL5}2j#<&4!NgG=K01V zo9(S9NR1Q)7hlFe%1!J?cEd+vfe*N_sHo`;Yl<$^!armj{Sj*lxC6sof*9n_V3T)d zjA);C%YQ^wa2p7UA`5sXl-D(GHU`}rhwz?!bc?RCdWPcaR2s$ax)XC}dxvp|Wc{** z95=c<+UDj4;rX8Q>GyD?`#hahW!R23G=L`Ec}%UFGkE7sv(99U>>!54po5EpkuEM( zkP;!;NKhvDTW*5HASWWUD*?fp9PXN`HsrZJo(B~-fZT+st19MTmTZi5YC_7;Nb*Nw zs}S=tDbb)7eCcmXRvUF_d{jkacmi(fpIae1wR+~47#L#C5j3GuyF2}1+*jSAFX!*& zo7IsQ7uIiYZ^;Y>1I@k$t(!XE$xfi*Z`rC!D$O}K`JlL5GFPII6Xm8&zdrTVbrF)1 zYP5t(WqIB}70G6N?Y3miQ!J4$n}2$JMaK4Un`+O4V6$np>qA2lP)ISXn9IG1skwKQ z_hHWhJCE-~d#6G`}iXQ|#Rn;n%8J?$oq(&uEBT z#~6TB;FShx6fd{=Yr7cLai8j4+~B*uEu0XU2W71{Oo^TPT5)^*T(uodH}=8%*y!4- zdeGZ`?0qtQWlFBdcsujeFVd7%pG1UqHXG7N8C~FFCDiMiaSKJ*Sd&Tl#!uwAY-aK}_`wtDR4!!n*zrCB- zuUa88v-y;_hs6C&@sf-GCfHPbE1SZ5E$*~`WZnaH3z+7gGS;MklSJpr9@b7pH=Z_# z(8$AQ%~ICUkr5k<^z!m@b&LIOH<`sJCn)$;LPA3J94#zx&InElqUV3O0LQXcy%BUS z>pW*0_l>pBq2t4zu~}ZEu}VH}L0RXxd~0|pC`0I7$zl!;FrROSM^Q}cHgu3`f&nxTNhEybqn#t9X6XXtaoufg zs0rInXi^*8dDm*R55Es8nH@hrP&z;IHswS_zOk^d7=TlQXe8FCgTKrJb<_2#2O^;? zTen_$nPhE??DUs6u>#FXZ{Br7`_tphX>OPebKL!gu*mpfF7@onOrcf)Fv@hGGT!N=F7;Gei`Ibg`B z$Cy&RO#A5M`B0bT1d#h~P1bmF{-)MYdw(#$bj1GNQTOrjVdp>aA2)#52(BIb7Jb;l zwqVA%1bXlzjr#zZG3mF7Png=T(`=`^BZZZf6;U`1(Y%;}0nwsgEF>%}^F~Xh6%}f% zP(br#L-xqTs6 zFgK?RG?wL-;9KKt&QxiC5f1?je@M;6>{1p?W1jYJT}R(WjBOzSm2>m+(<>{bmUyhH zQzu9CHv`yh@#DQR{K|5`n+hXOiEo%48p;4dXJ0tm-DN!8p^PWwX>l{;hP155U)yUj z2Ot5p)VJHg_>q8d{q$_38uh2o&g=728*sP3f7(0|!A&X0v{+wC3Kw2rev&lZ1}UDG za({)Ev>qH77^s9+|1p_ZK}#p;1&l8Y*E&)IVGL^2-6>wP(_J<=ggssx+MX)^QaiNd zMDbFJQY=*Fzw&oxTVf`;@Pni#n2i{(tghCmQxgxE%BW1WzFz#*w@or;=07Do#0IEx zUXQwN&--d_7t83vq|$#8m4|_ro5k^SfJ%e$kk`|EXM|c6)Gyh#SEY<^-Vb<5Diy?H zk>QX4koo!fIK29QzWSB#&0ToP{%*@3q1tOOYswUyPJ`$#w`6v%9$s@Xx0FTbLdeJ! z?$~kORd1jq3isku=pTtEF~W zX(CVnr{_`E`_$&cOcw6jT_3edMzXBv7TQVvx1+qMco}-vgKg77PE$BHpqdLi{Ux|0 zFBDoWwXUdC_>SK0G4(C<0Ucw4&+9}JB*q#1o4RvVEen%bNG%lj;kY6rRs=0Ml@YYx zLJE^fadB~WM~8L`5E;eKRUQ^mKx_?kPD zefA`?ElU}|yTOJyqmqcAJ8Sc8kxspuz}{6uU*B)Gb@w_fM3uD#mW?&dYEIzVyaBMx zY&jUI#sd!+jKX)pO7?ksva@bGXDzxHX3PCc3z9x>GScxst2LfX+OGS9odel z3e7Kv!-?zF&5uO#OxIqcgi~_9cR~;;rC6ZYAV8lN@(;KBUf5u!vfPTLLQ|!KeDe6o z>^ZP)^Kp8Z2Tt?r8%!<`i#oo<*FeR#`uP^!*vRVry1E$&*nSz7^I^})He$v)K0a1p zh4~9$0^`r#1!c7caPZzkCZSGx&w-q_QbUbpX1&S@S!FCb+ZINhsnW_yRaO|C97EnW zSOl813auShvuU)<4^h19MHyTGk@2~U+sjA6Y!&kbg$!jH-aB~8ZK0`@TVU+RmdkQF zf5aA(J=WiR7&pR4q4Irwt1(woOG`@-I`YAxsC&>jFXC#`RDhpAR#cYr`scg zsMrLjCVSMRgNY3P9{Cc>PRgLKi>BJ4k!x^&6=bAeiHyJvn1vzdn6fB6baffXy|Qo5 zQ~9OS6&bj@aT$4y1hQrRaw}O&XpIlt=4AFps+|unX-LoY!ON$hj}!ZxGUl^Bc zzc~xkX{KSur$DC1Jin$8D(mi%5JV-Kia4c4+3fGEA`up$fdzxy`Rd@eN9>rG-;>Ya z*G`j)Q(MoC7zyi)6jeTB?}ubh-rp-b%#a%kYfD)cFoSnfm|V7!Cfy#ZmUjPaZsI>f zm+D`D(YpAf@!Q%~&)Wa0t+h9r&5xN>t2w+3X6I>#GNeAZZVJwyWzwZ$cqsv6i~My2 z$L**~@G2 zUY+OAizXh&RH3o8f5A4rFdXjBplNZjqpOdnK}Z(eG(by)4XstmS?SWX&!R_7ZsNUV zj15nJQrF`6VTN7`T{61Cg&^RCmVitUG;gwwTLA5(>gv9&;S;hKH5E zO0#;zJL{{U5CNs$93p0KU2wL~r)U}bg@yvVO8{KNlB<;T62|PPo$xRuD|S~N4(7Kw z`Qe4pAF#3*53XX0<#Api2v!pP1zw55_AI0-LseR65vt+jR7r{zCE<5@Z-v2_PS!Lo zw4>-3X=cpG=?YKlJYMi$!b3YwhDD@ExnE9?DJ8M&v{}AH=MJgIg*%?W>A3G=tP7nF zi1vRO5*ErxbnVFe)oK9SMNG*9HO-183y?^@tZo&RGgjriCgfx`kDg?)zdQE~UDa~p zDhtuNr+2$PT)G{RrKIt1(oGva-v*E@3!a7$J>W1-?dO`Kp=8KRlnN8s%oE&e(l;b2 z-9-_du%vLel18RlP1@TY5nrT@91N<>W_(&WHw=k~HC~;^%RVSuXtZ#{XJm!9oU=nb zqA8jsLbP#bk*s!RAyFhGgb7Y9(O}5o=|6TVhWlfyLQ9kIP-O(z;+j(@r;czmSN(pu zh20b@Jcom>C^4jq613nP@W?vRKrQ=_Ou=HmmG73_b~367#dnQE*RdIruPwQ-GEXu#n=3eA z-xyQtRVBX$4|VFpFYCkGwXDMOS)mQXqffgn%4k--3AE=f#004Y^$}Q0}UyaD)k|k91lZI_Wm%) zb6Ala=!6~>HMOPi(u{cn@yft1rL7xQ{hT>VO-+rg6^IeOw6>82a|gMLQvDT!{X)Lt z?FypdJ13(cCwp~Ad?lXiwT(I6&gwZTGpzi=bHHtGXfOtZJaa+R0}vVr!{`eL6c^MV{kc)iAxdfdms|2PJooy`12x6 zb=jH9_q{$JlJS=Hy8*S~VU~Ff*wOII8u-o2c)M$JKL@*{JL3Ya2>pVPsmgH592Q zZL;OB0lWTbi8>{TOCI6vvcTjHBJmNxVC$=;qYWL_1=75?F&vapVEacAn&CMT zx~Od*TE9-Norc=Hy<4yq4x3{wGA|ICpLO7?E+#&hF1%W6Ha%+PAlVAiynnrss%+(} zH2`DTV%TT+MRrejL4mNI5`2)>m%&drhn!A&e2I~aL{kx_%pj+ene3o;* z+&EX5`YJB%2=6Dv`?Qr@Yc?b9J8b#Uly%cr1pdjx%N)HXXrw?W({sF6E>3Qt0yfas zMuFWhDn`Uek@9^P@gbgV#)!p$TSHi*Ea<6UAqhdHLj{N1Fnivzu&g3J)|@GoBL|HB zYLtZ#=_CxMgg*6{l}+(s0x>m64g>d+frJS8&QyB;jH0AqRDWPeUU(Ynw%DUIe?Is4 za__GBJqO7VtN1vCKyP{~Tbl+dOprkZThzH|ghn|Y+mzFy@X-?{fgDf`ev-WZ#C`U? z-WC*F&7A%DKvq)P|30>u+4_-B@%dsqt-DDk%5wun==5Y?=mZLcj7T4oLP6h z^X@R0ZdEDld(rS-N6_pQ?*hU-@~)+S`ql<~Y1 zcWd2<3EqmSrNl&~9)=P#Zm>CmW1Zkhl5aeI6-ksP1D}A431kA0;OFm(E2$SKgxx%st_?nj&!UtCq0&!-BlYJ(h=iEjYd5!g+j4&i93cB86ZArgM|4=qA zy_(z7Sk{<3m6AVW^SQAgbSzj^fWri93B&c3ANeP^XBI7QsZOn$3MEeE9(NFXuq=|4 z`JJ-LI-rDB$y&f^wcA_oNU))^&cEr?k5pxiJoCdG?RRkGfahRxnwrm&lqaBfZINvm zy!P_9&VwqeiX^!2zD?le=rqKy{kN#r7)F%@IYDoXlmh2{Jjp;vn$%OjrWq8M#g zjk$X#_^=^$lSgjiEm_G)Z!auXf8d?hZ8!7y&n{OJ!nN$7I=63#H&G*YzGzy)+o_vy z*QOMX5T#N^eNU-LkkLq)V?%59rqi~2{%Gyua~+v|A7pWP@Q{0e+BV+1$@8i^O6$^E zW+r6X<9##V#DfQPJy+>UOHSF(?TNDK4|Vae5Se}%OJkcnwvmX1gu33tw$IP`X~dew zkTzc5*cb^+>R7GUiY>ox?c-Kard47|G{v470n9&I;-D{;?bU?YaRZES9zm$IjbTcE z;+jj5Yx5+1I%%%Ivc`{R+~txQM)u{2bJnNkbPamIQmyl-&h_MgX?H?60>=5((qQbx zXvS4>p(xyw*5!%SVU@kh33=^EC16zMvTUbu3tA#t1khv?;lOA>61t4pq2XiF;1#OL zvvPw#F2;sQ?5~VcCo_dQMZ}`Dud!&*_VsQV{~wee%sDzHJvI)doY*vI7w$`zW(un7 zvz(bMSY7)#`@@|C0GAMcmHr0_O2fGH<2U*g&KMggqsm>FI|QQ(8b<9x-_ixsbUq;^ zAy%OAhbR4Dk_yQ(DcBZ1KU$6PJ^l_T3me+}_zE!*6dOY8p6cL%p}owse=SQG~O~+JQzBjJ1jV45PYILvM~V$c}lR zsh4Ft>pPskCk<}BQbR$h_@N1j+*TTqd?Gsx|~HH9&WclJi z1bH2-BSpu^IKY(gy)l5K0!$oCq>6B#HQ3}WpzH>!IxV4@&hTzt*K~szO_8+>M_tAQh#o+q*I^5%9W_tk3v%Hx8R$ zJAz*WY?bUy_*w~NL{&!176VGn(SfC3HOGN%S2XJ1SPuHLHGZnv+PGF$RI$?M? z3C8T4qM(^N^z5#}lWAR84(j7$7ulfXm;3%hYIw>PMNPH@>fxD7uprIzOztbCm8 zu!lV6Wvm`6)V0YD_gbhddB7XCr677S%yUSmCS>s9tCR_9zgQ$OK7VMnn{a6LV)ZR(Rh~SNsf_y87MII>|U4^cz>-t0J1hiM@TFR&=R7! zt2eei;p<2Ffy)8UKzjiF`5-n--F>`XrBkjz)~5FiE-Dqp(+WqhGlmu`qpXozNUZBNpKSA5dLOTq3MJ$I&BU;>B7t0Z#Bxix4eS|zQo zDOu~(3L7!Gx?ObropFhi6Xx6Pdbe)hTg!U2l*d>h)_AQr{B&70_}T}yF>3diWp_Cn z@vigmH#Zbzsu##HNK&@uPg3^vY(3L6suOE()4VQQ>4$y(3L9C`YI2?`f&KmRX!Y}j z2R2C3yF4XChvA1;dp64oroWSu6VP{1rjaKADh@)(Gcg)U@9!Rc@-5K&@4QQ}9isgx{Br%O( ziT0|s+-OgkQ(h#8;(mWODRf?N{fB7!t~u&iE{}R#_K8s=W%L-7ZXYe^!>uN8kDH$y2V1$4c7h-f0i%wHmWvAb__LFoo zUXJnpF($$7l_Hi?kJKwL8|CCx-a&!+xMFdrCZ0w&Y=Q?*xzK!NSOn-IUy69cX~(1F z^Y-UEl$$wU=Y8C#$2xe$-JRn_$KwX#q8yH*rsibOJ;IN)zd*4bpsj>b6S>43PTSoi z$n%RzE!t;9B^7I4O1Syr6sgC>`}8ajhVSj|6_=4AWoJ(Yg@BjWy?nUyqgNl61U3;9 z`)_Ui+~3gya!o9k-&FpKt$VP^7+Dah&>Ty%bizTT5bE-G7{fCyhU12=IpwrtuX>Q} zMfx5S4VF1$!X_+PqBmdiE-L-dl9;k@uV(IEt}|i4fCbh@>@LJVKk9l$!oRmhE4om=Os365;wB`LSGvtep2X60=5{IuR)1e^2iO=Jd<;lGWv z5Y|s4iCiibR6M)3wO)M3RON8GIhH{buz-v%pGfafH)fZLZaOloECu5V7iCq|2?+dn zhph~ubZ(B9z~a9OAb_zBr@d@n^ndfPudG3G!~CvtT4C8nZ^v}U%S_d9L9P&BTj4+Dj8RON7L&@EL{9MY;!UJ<->5wf0L}b^Ory zeQfBmRB_Z;5oV>3Fgtm1j@pW~Nd9dNhNxW6E}&vwVEGO~+i}YZWFfm+c(v@ud?PZI z;m=%=TmDbCVC$u77635b2^TtcLTuZhtxSY3RhI@^qCQ_-wt zA7Zr(f{&u#jy=zV{jdXCOGz>5EX=O=3S?)R18eFRECvq$r^RN z31Nj1aSfz6R32fV5KUC*E%7m(fdv$gwe%DB2x!~2K%77H!6vWxXmUgZXg;jl2qA`6 zk=SPN_dIRyc|D-J;yi$F62}^r@)*QU(}h+kGWBvjJ8A1Z$$O#loPC>`p8dS$cIt%P z?ERcYI?HLw;eNDzJy|<%>i2xPi3u$IVYIdao+p%ulA-Qt*$ChM6Z}`|wJq*f)@Jy-^@BHk2v zx2#ONu!G&laV!OR+nu(*>Yg6&2PfWo+A27=c5wYk2?27}f4kk5SQTHhn||>$e~;fy z*cTp4tI{HouDPOnkM(2I$=Qlfd&;7T8R_@4?YAzjW>KC`)q4d?*k);O!xElS1fvnt z5$GJ#VoJ*{+L^NKgwuVeNsk74l?}OPcR9MMW!j&$s|jol66q;FAa~zg`Csk;Xngv57BIqH9@oQa67FzfIENA56&mpsk_(ncnnF@p&G91)~lZ z@{q#GdVfH8{&GE`jp<3f}%{@1eElq z0(8Do_W`h8>Kpq^|-*zFmItEkYJK2IGmvhPj$_UIFZU-7uDJgJZOd2Zvz z-kG69{(8i1F9flCdaDk#r~jvi244jR|)f)`qA}1(BB>~I(S6&!ns-)(U;HZZWh;O`IL4v3Tx;rIgk$mxa-@oDc;r(eoch1~%=gf_B=X>VNBt=(0Wp0-p zpT30V{6R2J8{U&gW;%=%F?h?=2dC8f<>K zzc)+L;T0d4t-LIxi%s37C}qMR!?~k*Kr82?!vAT*VAcb>57IMJSwUj%nj7cd?0&oK_J5;%5o*1)sor?Ao4=$uWcNo65*ViTo8XEa&g16!g zMXfXHTXsnq%G}n@71#RWzML#Er zK>QWVoNNEwTjWE<3e<0Gq05)x$Nu~$8H`|zddfk2kX9aHFL&KDH(oj&0Mk2L!#%au z`&Wp`xP9$k{Yp7!?C0z?ApN9<@Y5L&NUW2b+$dTY>ek$uM|g$XxUDk7YQx7k_lUDn zC1na2s*Dqqz=uo~o+?1F4lZt*B@fGZ>dyr^@7r9QCU)Pb4wj3u3f+w()@!m5Z zqKhIQm_<^985OSmrc3=#5Pbkvl!Shd>UBbN%{IN`k>bs^i{Q5Za9c&B3OYv79n$@k zkRON@J8fDW=Stg6P|?nKT!}Pnt9f%lEW3?8>eD+^HJQ1%e0W*7vSBG;ZT+q>==L&tg^+*_%ZcuaWo6FBq%&#=Ye# zTp4 znr41KS0o}0i1?yqVsh{3xg{ZeYHsxSDf=5_ecU_GOQYvhmAuZ}L}MLxZ8%ZG*aC*I%iXbYXsuDNQE5$xHo5y)h4^5} z-qFz1k<)Ok;dMutOB!i%s*N=W-(h+3y1J`y)`5=m5ky?i@^KozLgu_x6?pJbwsNO` zKkau_goWnkcYMmDMk9MAi;qg#k2Ie35Za@(csqZ$U+;i_Gh;qu-URBJ7){9EG4faH zyY6rZ{p>`D=I?a`LpXA^qaaL0W!T9+BZ-im2jx$&CkJDn!&*qQb~$ry^499Iz!okF zEuXogzN}@XH&dX`%j*GG)8C4s+#UqvFLjBtvawiniQ7d`?|k7GyHNmQA<^V^8v9ne zA$O=lx9~cyF&G#{-?DgJat0F;y->ob7AG3%X8;hkmk_T+)4JeTEMD5RRvQYvWh<^d zR8wqeaW=z(_+N-ilrebUhs0sowr?EW>bG*gG2;f?H4(R$#H%#eP#RROM8`!W+2NY`jyVyV7@*_No} zY6tz@T7F_@(cX4tt`*GL7{TWCXL!)t@!`|{G{i>zVHoVhU%x66x&|rc8NzUdi%wp# zkk{I*dEYa~eMju1xzP((R`%W&1GfT1@bGWGJl?Yg2FA^L>Ye^((Gg&6&Nr zMTK6PYy+sbYUzISPfk8&JV#%U3!Kv3(J-LcyTB_p);)J-8+d+?w+*@%N5}TjKsgp`Etd2+sPJCoFHT`xjm^WZ}9C$1|tP?ZxBlhr; zUB;&}RoL*wtd5#ZU1yGs3#xT#xm{T~T%%mGre52*Mps?et}X}{w4cew+H!@KAtQ99_xSE_03%H6X4% zny2AmF3v|8=;djagfGnJq49}<-=p07oU<63gM1}Wp9w|1pS6yXLm*X5rH`GP_stA@ z0d5md)+#NJrqNznGVxSiv-1a3)BlbzU#0szvD-XQi#kI%_UFqo&MvM&rMnBm?>uFt zbH1h_gN4oci0^ZKZG+Em>_VN_uwjKk{8zA%&^0RA#r-k0gv#jRFG4E27H4E<3@SUp z>dF^}r0gcq)VibbJIlLUSgBY^ougqF zJ&SpLmKbaGhRD9o{ zzV&@t-93*fa$#X1k2i#8>%PuMtWj7jvSXu`4C&1;UWtVs7qp~-KgIiNcHwV-1+6$+ zip`Z2Rh9X$w_)J=>b_&xv2DX0HxeE`b&7rOeuD}X9wg5yM0lAk5b{49y}vAw*fAlg zznft)|BN;nwz><^%y}~+qT=XB#Zri^{`tk_TX#I7 zI(}im{G9yh2EX?kgJ1Io$m94Sk(Sz+Wv79i?lG@rt&~m1TU6xQ0(iog3#0#5<0vRC zgB!^Hi`Dv#%I12Wm+NQ)FE2&48t%?Eo&0(UbN8D5V7y9Ter7xy-5p~BzE8c z_chb8oadyuR2%hl$hS#zIvfo_=+4@`v-VfX7zl(sMULmqkGCb$H)#)BE?VMWuNtNc z=4@--y0BT;fDezIrCA7lS`e=7XQSx6y-lt%ZiQC1_%EI!y@5CMl3uo|YR)>U`lj@^#*#Q5 z+DIazsHo@mZ6AMVl~Ofo>DVHpBmBVzu{=h!!qmELLR3i${lM*9SIhN|Z_drgYR+xF z*lFcp=H4PRyb6=<9t)fMl5w`;Nmvp12@OKZ}fX5lUTjRw+>ir z^}6^{wE-NcA^_+hbi5JArujNk`l?50w_hN+aZ*wA!AtCjcyj^j&nI{_$axGbmbntB z_Br85(X6q;%2G}HR>`u;U}Bcd`Gqf>5IC5GP=0r$d&06mb)tj~k>1U;u=hdupT6{u z{!T}EZ~f>4AN8luG#)mM^!^*8)(EM5+u?QJ89~CNL+A$~?~f>-2e;MdxXB(OqqU?Z zc2t4_@{E(DzY zuTHoGW3u`xtcoc*W4st+$1`>zZvrnV`})f$0o*AiazE4`%ZoP6$y{89E89MltEA@R zfiu&xI%6ohmVQ5$5Y1-_rG$sAz9M0_kmDY%yb^fl&YYCIrHSSJE_2;SEM?GbNMg?- zy=46LpCPeeiCYZm$z#j@0du^w@NRm0QT*|kpA!nID+@G4PnCHWtOl64%jxeug*)P^ zcghM`Y5b%z-tUXH1E&SuhGW!K#Xp=88|tSuWE7PJ?9H|2Z9lR{@a%Fj^l9JZ)F%xF zjAP5#5j8j(v+0s#k*_2=_MIVQ#HL#JCLRo$^sOGUm-tp9WuwTGEMAVpc_-jll#@X&LiQOSiFg+=*}D1U;a1b_HjNFv3&g#f`SP1Uz+P)_ z;qQfFMS0ZvHzvaS&U1c2m-Lz32bcNgc(}NPRMr|QVSZAf#nDU4@l1M2MNF5N2AYLh zycNQbnGjt(gUvnXg!~p6sEt@$DV?0&jXhPz;{`s(-EwGek+?hC@)#7M%6+KHLz&zV z5C0_(BZ8~;=)9PQkRVl8Ebn<~Vc#2M_JiWw8|`b3kyOH4$C+R4}zoI6m@I6HT=#GnQTgZ9zTH;rp?0P2jf+j z%h#cJvJQ2Tu?Tpq~^uYxA5zm&`-l1g? z6PtmiFFAE}Xs^3^dOG2KCTjTCtEs|(y^-WvXJ>_ReZ}~m1G4=>UC8C$G48``}rDD_idnB&9XIKPLj%<9omwL zhG^JS!I+iNR4wqY==FX!|BEH;XSvOPPYH{W;`;4wmcv-U2B(%zE+6FI;KJ814`iVV z;Je$4bC#&zzgzAPE{Y#J*QI||t$g`sq-O~VEJOH*N{?6Eu0%-;4Er;KC9TkghUmmZ z4e>ko*62y1XGPKE=l_x5ns?q4|Is`c{nP((USS#jYat+GG*$dZdx9A4{~1V1FFu(4 zAH~E@toe_jO(lt%{vFf5lkfR|(MhCtH|LoL-R*DR-McyG)7KxwQd*bvABJCdvR64} ztaaIle;&IJS3P>{yX%W#uGznOurE(*Ltb2HUh6K*kksnqVEP+Pr$+qV8xOS+eSL zqu!%D71q?$Z>n27mLrKMu+#56N_SaeLsQC)Gg*}~ySOS{6pp>_4uA3bra(mNKK^H@ zk^byv^kj{*6bjV2289ZgJ)vxFrGS?bld}v_CfVwop@uhi1|5rKeR@~QJ>~>*%;4DT z{+Y0Wh5*T+!}LbWz$P_9li%J3P^m2Zuqe!Z;dp5^ef3^mUvk-Hs|}OhKgqGD23<{c zF^nN2OAp^@nc@N_p;xngHY+DnU%n{#gt~QaNga-AfA<71dH74iDbX6ftf+hm%@ABn zO2p1p!s*OO7TXJa-TB~5C>><--K_hy28R@9lCE8GC&UVJ&AgM5@J>V<-`2?o)H)?u z9+8_QLf+IV#P$f&8(A&J_F{XPi~OjDF8y!B0yh%$t#%u_$r zwB66Lr`|Ctyt1WKLd^=9xS(EI$2S8w&RdO*PNsYQ0hS>c2^}tg#?_7J7WSn17W0wL zUW)168J;DA)bHPb+Abkc+Ax7^7?ik=yxAY%40JXeOsI39MdSY3XY7 zHJcknXm%mHi*&@zYUrZewl-##NMwCds3kY0e+*51sW~Bu=Bpb|lxB@-K=C+P$Fl<` zVa>V4eRhh!^#M5eII2r7yinfupW;B5P%EGmG}{wpMqGttc3DsGMV=xpfy|Ed5N#`# z_#gOY_Uo^tuB5&JVS(SC+BQf2?%u|~68RPoO zgXIL9%@viStIDO>0N1GWl_h{79#RvIH%9`kD&;PjQ^alrU)UHqcb&yhpUb@e)64cm zXa)gDf_T|X&*32?{NqG?#7QAVQ)A(Fm5O>Vz-!o%~NynWqa%|&H&6oqXzu; zoJ#D6@mDW6Ef8o*c6<4Zp*Ar{RL5k^HmZ3O!W8=tDm1pnrShz-c`s$DEOo8=YxuQ$ zHy(s;VzQ*Q;*_R&@Dsti7#&@2*#z2Jdk&z*pw{fRHLYE50gy3rBrT0o=I<|G0<0ou zT!$QwW#oS&E%Rg<+#?lzO&X545v7`#rwi$8ysFDz-iWnJ3B4aRAXRQ?4H#oXe$(Os zlJ%99b|}9PjvVprO!oM;+HOtPi_mv&G9Y?4}ZQhhxls+NRT;OG{ z3{}UrIWfAmAJw{Pyir{EFh*EetEe!sgo1&_C3qo(vDa5azq8jh z%b{5nYEpae80V_tS+s$GG0M`INEQwAcXn=-#*FH*AWCcmXLH14 z6+XX~iLu3Ka&t6t0awfleioGPGPYXw(R6_x+Zz~-!m&Nc)op6r!6H;o ze90eW5@r?d>}a)|AY}A*!6s{X0-OGD>Xn}c_HgHYC-{V_ytJZj;7mo}&3gyJ1DufO z$)@GUF=p1oOH&FA4}lwmrPXk)4qtg(U&5w9Sj~nu$rD_yxVrTF$O5e@sn*eV>nCn; zr9ZFCcW19?jN+core!o9rOrzgt`D>1j37v*?hflNh-faNHnoBbqoznftc@q2TwbBR_$Sl1sf zMom(dfFR>0xp4Lny8>D9n&lAhMHfD({ki|V% z5WZcd4xMxuSY+jt4aUf)5t6SuUm9j#oQLGVHd?h4WUeVWWuvisIu5*Q>ZC0^UL8gf z&u11{_T6l>tljJC(+7tbog4odwWdt6m_b+YpYIV-4UkfBi;7{FHeOb|a0@Mq%82s% zUCouvE-ujR2(OQ>*ARWd2Uzv_kRK{Q?tHU^ z`=+2LJen#bXcfi;GL{}e-?Ga*18fxlIF>A#+Vv@Y<6EjvC?qT~+~g<#rs|sVx2Y7< zF@v7^ix_6qy;$DsLQ*6!fx|pWMM;Hr)R3x~ojVVUHXhRsUdC!Zf9&el8GT!m1caNa z{(djwuC471Y52KXg2%E| zKVB99nuwCj^d?6+tkX^=*`6odb<&B-*%nT_%~oWie$|6OO6i_?0#JgnJ!3grC|v$8AG zh2_-6Hv*Oar$rZimz&4MO|^I4KkqrXg{zQS{lB8!6M)W!i)Nlp4Deh!1L>2D%Ev0H HFG2qUd8Vx* literal 0 HcmV?d00001 diff --git a/documents/Screenshots/Linux/4.png b/documents/Screenshots/Linux/4.png new file mode 100644 index 0000000000000000000000000000000000000000..6e8a3b6c1e9a39d06bfc9ec0364db3b1f0b7a5bc GIT binary patch literal 16845 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 r -## Build shadPS4 for Linux +## Build shadPS4 for Linux -### Install the necessary tools to build shadPS4: +First and foremost, Clang 18 is the **recommended compiler** as it is used for official builds and CI. If you build with GCC, you might encounter issues — please report any you find. Additionally, if you choose to use GCC, please build shadPS4 with Clang at least once before creating an `[APP BUG]` issue or submitting a pull request. + +## Preparatory steps + +### Installing dependencies #### Debian & Ubuntu + ``` sudo apt install build-essential clang git cmake libasound2-dev libpulse-dev libopenal-dev libssl-dev zlib1g-dev libedit-dev libudev-dev libevdev-dev libsdl2-dev libjack-dev libsndio-dev qt6-base-dev qt6-tools-dev qt6-multimedia-dev libvulkan-dev vulkan-validationlayers ``` #### Fedora + ``` sudo dnf install clang git cmake libatomic alsa-lib-devel pipewire-jack-audio-connection-kit-devel openal-devel openssl-devel libevdev-devel libudev-devel libXext-devel qt6-qtbase-devel qt6-qtbase-private-devel qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel vulkan-devel vulkan-validation-layers ``` #### Arch Linux + ``` sudo pacman -S base-devel clang git cmake sndio jack2 openal qt6-base qt6-declarative qt6-multimedia sdl2 vulkan-validation-layers ``` +**Note**: The `shadps4-git` AUR package is not maintained by any of the developers, and it uses GCC as the compiler as opposed to Clang. Use at your own discretion. #### OpenSUSE + ``` sudo zypper install clang git cmake libasound2 libpulse-devel libsndio7 libjack-devel openal-soft-devel libopenssl-devel zlib-devel libedit-devel systemd-devel libevdev-devel qt6-base-devel qt6-multimedia-devel qt6-svg-devel qt6-linguist-devel qt6-gui-private-devel vulkan-devel vulkan-validationlayers ``` -### Cloning and compiling: -Clone the repository recursively: +#### Other Linux distributions + +You can try one of two methods: + +- Search the packages by name and install them with your package manager, or +- Install [distrobox](https://distrobox.it/), create a container using any of the distributions cited above as a base, for Arch Linux you'd do: + +``` +distrobox create --name archlinux --init --image archlinux:latest +``` + +and install the dependencies on that container as cited above. +This option is **highly recommended** for NixOS and distributions with immutable/atomic filesystems (example: Fedora Kinoite, SteamOS). +### Cloning + ``` git clone --recursive https://github.com/shadps4-emu/shadPS4.git cd shadPS4 ``` -Generate the build directory in the shadPS4 directory. To disable the QT GUI, remove the ```-DENABLE_QT_GUI=ON``` flag: +## Building + +There are 3 options you can choose from. Option 1 is **highly recommended**. + +#### Option 1: Terminal-only + +1. Generate the build directory in the shadPS4 directory. -**Note**: Clang is the compiler used for official builds and CI. If you build with GCC, you might encounter issues—please report any you find. If you choose to use GCC, we recommend building with Clang at least once before submitting a pull request. ``` cmake -S . -B build/ -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ ``` -Enter the directory: +To disable the Qt GUI, remove the `-DENABLE_QT_GUI=ON` flag. To change the build type (for debugging), add `-DCMAKE_BUILD_TYPE=Debug`. + +2. Use CMake to build the project: + ``` -cd build/ +cmake --build ./build --parallel$(nproc) ``` -Use make to build the project: +If your computer freezes during this step, this could be caused by excessive system resource usage. In that case, remove `--parallel$(nproc)`. + +Now run the emulator. If Qt was enabled at configure time: + ``` -cmake --build . --parallel$(nproc) +./build/shadps4 ``` -Now run the emulator. If QT is enabled: -``` -./shadps4 -``` Otherwise, specify the path to your PKG's boot file: + ``` -./shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin +./build/shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin ``` + +You can also specify the Game ID as an argument for which game to boot, as long as the folder containing the games is specified in config.toml (example: Bloodborne (US) is CUSA00900). +#### Option 2: Configuring with cmake-gui + +`cmake-gui` should be installed by default alongside `cmake`, if not search for the package in your package manager and install it. + +Open `cmake-gui` and specify the source code and build directories. If you cloned the source code to your Home directory, it would be `/home/user/shadPS4` and `/home/user/shadPS4/build`. + +Click on Configure, select "Unix Makefiles", select "Specify native compilers", click Next and choose `clang` and `clang++` as the C and CXX compilers. Usually they are located in `/bin/clang` and `/bin/clang++`. Click on Finish and let it configure the project. + +Now every option should be displayed in red. Change anything you want, then click on Generate to make the changes permanent, then open a terminal window and do steps 2 and 3 of Option 1. + +#### Option 3: Visual Studio Code + +This option is pretty convoluted and should only be used if you have VSCode as your default IDE, or just prefer building and debugging projects through it. This also assumes that you're using an Arch Linux environment, as the naming for some options might differ from other distros. + +[Download Visual Studio Code for your platform](https://code.visualstudio.com/download), or use [Code - OSS](https://github.com/microsoft/vscode) if you'd like. Code - OSS is available on most Linux distributions' package repositories (on Arch Linux it is simply named `code`). + +Once set up, go to Extensions and install "CMake Tools": + +![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/3.png) + +You can also install other CMake and Clang related extensions if you'd like, but this one is what enables you to configure and build CMake projects directly within VSCode. + +Go to Settings, filter by `@ext:ms-vscode.cmake-tools configure` and disable this option: + +![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/1.png) + +If you wish to build with the Qt GUI, add `-DENABLE_QT_GUI=ON` to the configure arguments: + +![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/2.png) + +On the CMake tab, change the options as you wish, but make sure that it looks similar to or exactly like this: + +![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/4.png) + +When hovering over Project Status > Configure, there should be an icon titled "Configure". Click on it and let it configure the project, then do the same for Project Status > Build. + +If you want to debug it, change the build type under Project Status > Configure to Debug (it should be the default) and compile it, then click on the icon in Project Status > Debug. If you simply want to launch the shadPS4 executable from within VSCode, click on the icon in Project Status > Launch. + +Don't forget to change the launch target for both options to the shadPS4 executable inside shadPS4/build: + +![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/5.png) \ No newline at end of file From d7c2cb17f3e1b339d78c5eb1bcb44e0305a85e89 Mon Sep 17 00:00:00 2001 From: panzone91 <150828896+panzone91@users.noreply.github.com> Date: Wed, 22 Jan 2025 21:53:54 +0000 Subject: [PATCH 20/68] update extension vector capacity (#2210) --- src/video_core/renderer_vulkan/vk_instance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index d183d6b09..a722b5322 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -232,7 +232,7 @@ bool Instance::CreateDevice() { return false; } - boost::container::static_vector enabled_extensions; + boost::container::static_vector enabled_extensions; const auto add_extension = [&](std::string_view extension) -> bool { const auto result = std::find_if(available_extensions.begin(), available_extensions.end(), From 1fcfb07421909ab779ee1a884b6a5ce0fb0fd4e3 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Wed, 22 Jan 2025 19:21:52 -0300 Subject: [PATCH 21/68] GUI: Settings improvements (#2213) --- src/common/config.cpp | 13 +- src/common/config.h | 2 + src/qt_gui/settings_dialog.cpp | 23 +- src/qt_gui/settings_dialog.ui | 1336 ++++++++++++++++++------------ src/qt_gui/translations/ar.ts | 18 +- src/qt_gui/translations/da_DK.ts | 18 +- src/qt_gui/translations/de.ts | 18 +- src/qt_gui/translations/el.ts | 18 +- src/qt_gui/translations/en.ts | 18 +- src/qt_gui/translations/es_ES.ts | 18 +- src/qt_gui/translations/fa_IR.ts | 18 +- src/qt_gui/translations/fi.ts | 18 +- src/qt_gui/translations/fr.ts | 18 +- src/qt_gui/translations/hu_HU.ts | 18 +- src/qt_gui/translations/id.ts | 18 +- src/qt_gui/translations/it.ts | 18 +- src/qt_gui/translations/ja_JP.ts | 18 +- src/qt_gui/translations/ko_KR.ts | 18 +- src/qt_gui/translations/lt_LT.ts | 18 +- src/qt_gui/translations/nb.ts | 18 +- src/qt_gui/translations/nl.ts | 18 +- src/qt_gui/translations/pl_PL.ts | 18 +- src/qt_gui/translations/pt_BR.ts | 24 +- src/qt_gui/translations/ro_RO.ts | 18 +- src/qt_gui/translations/ru_RU.ts | 18 +- src/qt_gui/translations/sq.ts | 18 +- src/qt_gui/translations/sv.ts | 18 +- src/qt_gui/translations/tr_TR.ts | 18 +- src/qt_gui/translations/uk_UA.ts | 18 +- src/qt_gui/translations/vi_VN.ts | 18 +- src/qt_gui/translations/zh_CN.ts | 18 +- src/qt_gui/translations/zh_TW.ts | 18 +- 32 files changed, 1339 insertions(+), 545 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 6e9db50ff..a57b6d35c 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -45,6 +45,7 @@ static std::string logFilter; static std::string logType = "async"; static std::string userName = "shadPS4"; static std::string updateChannel; +static std::string chooseHomeTab; static u16 deadZoneLeft = 2.0; static u16 deadZoneRight = 2.0; static std::string backButtonBehavior = "left"; @@ -194,6 +195,10 @@ std::string getUpdateChannel() { return updateChannel; } +std::string getChooseHomeTab() { + return chooseHomeTab; +} + std::string getBackButtonBehavior() { return backButtonBehavior; } @@ -399,6 +404,9 @@ void setUserName(const std::string& type) { void setUpdateChannel(const std::string& type) { updateChannel = type; } +void setChooseHomeTab(const std::string& type) { + chooseHomeTab = type; +} void setBackButtonBehavior(const std::string& type) { backButtonBehavior = type; @@ -637,6 +645,7 @@ void load(const std::filesystem::path& path) { compatibilityData = toml::find_or(general, "compatibilityEnabled", false); checkCompatibilityOnStartup = toml::find_or(general, "checkCompatibilityOnStartup", false); + chooseHomeTab = toml::find_or(general, "chooseHomeTab", "Release"); } if (data.contains("Input")) { @@ -760,6 +769,7 @@ void save(const std::filesystem::path& path) { data["General"]["logType"] = logType; data["General"]["userName"] = userName; data["General"]["updateChannel"] = updateChannel; + data["General"]["chooseHomeTab"] = chooseHomeTab; data["General"]["showSplash"] = isShowSplash; data["General"]["autoUpdate"] = isAutoUpdate; data["General"]["separateUpdateEnabled"] = separateupdatefolder; @@ -871,6 +881,7 @@ void setDefaultValues() { } else { updateChannel = "Nightly"; } + chooseHomeTab = "General"; cursorState = HideCursorState::Idle; cursorHideTimeout = 5; backButtonBehavior = "left"; @@ -898,4 +909,4 @@ void setDefaultValues() { checkCompatibilityOnStartup = false; } -} // namespace Config +} // namespace Config \ No newline at end of file diff --git a/src/common/config.h b/src/common/config.h index 564947f1e..15937606e 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -35,6 +35,7 @@ std::string getLogFilter(); std::string getLogType(); std::string getUserName(); std::string getUpdateChannel(); +std::string getChooseHomeTab(); u16 leftDeadZone(); u16 rightDeadZone(); @@ -81,6 +82,7 @@ void setLanguage(u32 language); void setNeoMode(bool enable); void setUserName(const std::string& type); void setUpdateChannel(const std::string& type); +void setChooseHomeTab(const std::string& type); void setSeparateUpdateEnabled(bool use); void setGameInstallDirs(const std::vector& settings_install_dirs_config); void setSaveDataPath(const std::filesystem::path& path); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 5695d6232..b41fde745 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -65,6 +65,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, : QDialog(parent), ui(new Ui::SettingsDialog) { ui->setupUi(this); ui->tabWidgetSettings->setUsesScrollButtons(false); + initialHeight = this->height(); const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); @@ -150,7 +151,6 @@ SettingsDialog::SettingsDialog(std::span physical_devices, }); #else ui->updaterGroupBox->setVisible(false); - ui->GUIgroupBox->setMaximumSize(265, 16777215); #endif connect(ui->updateCompatibilityButton, &QPushButton::clicked, this, [this, parent, m_compat_info]() { @@ -169,6 +169,11 @@ SettingsDialog::SettingsDialog(std::span physical_devices, }); } + // Gui TAB + { + connect(ui->chooseHomeTabComboBox, &QComboBox::currentTextChanged, this, + [](const QString& hometab) { Config::setChooseHomeTab(hometab.toStdString()); }); + } // Input TAB { connect(ui->hideCursorComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, @@ -246,7 +251,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, #ifdef ENABLE_UPDATER ui->updaterGroupBox->installEventFilter(this); #endif - ui->GUIgroupBox->installEventFilter(this); + ui->GUIMusicGroupBox->installEventFilter(this); ui->disableTrophycheckBox->installEventFilter(this); ui->enableCompatibilityCheckBox->installEventFilter(this); ui->checkCompatibilityOnStartupCheckBox->installEventFilter(this); @@ -373,6 +378,15 @@ void SettingsDialog::LoadValuesFromConfig() { ui->updateComboBox->setCurrentText(QString::fromStdString(updateChannel)); #endif + std::string chooseHomeTab = toml::find_or(data, "General", "chooseHomeTab", ""); + ui->chooseHomeTabComboBox->setCurrentText(QString::fromStdString(chooseHomeTab)); + QStringList tabNames = {tr("General"), tr("Gui"), tr("Graphics"), tr("User"), + tr("Input"), tr("Paths"), tr("Debug")}; + QString chooseHomeTabQString = QString::fromStdString(chooseHomeTab); + int indexTab = tabNames.indexOf(chooseHomeTabQString); + indexTab = (indexTab == -1) ? 0 : indexTab; + ui->tabWidgetSettings->setCurrentIndex(indexTab); + QString backButtonBehavior = QString::fromStdString( toml::find_or(data, "Input", "backButtonBehavior", "left")); int index = ui->backButtonBehaviorComboBox->findData(backButtonBehavior); @@ -476,8 +490,8 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { } else if (elementName == "updaterGroupBox") { text = tr("updaterGroupBox"); #endif - } else if (elementName == "GUIgroupBox") { - text = tr("GUIgroupBox"); + } else if (elementName == "GUIMusicGroupBox") { + text = tr("GUIMusicGroupBox"); } else if (elementName == "disableTrophycheckBox") { text = tr("disableTrophycheckBox"); } else if (elementName == "enableCompatibilityCheckBox") { @@ -592,6 +606,7 @@ void SettingsDialog::UpdateSettings() { Config::setRdocEnabled(ui->rdocCheckBox->isChecked()); Config::setAutoUpdate(ui->updateCheckBox->isChecked()); Config::setUpdateChannel(ui->updateComboBox->currentText().toStdString()); + Config::setChooseHomeTab(ui->chooseHomeTabComboBox->currentText().toStdString()); Config::setCompatibilityEnabled(ui->enableCompatibilityCheckBox->isChecked()); Config::setCheckCompatibilityOnStartup(ui->checkCompatibilityOnStartupCheckBox->isChecked()); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index d72a56973..5da6a8198 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -12,11 +12,11 @@ 0 0 970 - 820 + 600 - + 0 0 @@ -52,8 +52,14 @@ 0 + + + 0 + 0 + + - 3 + 0 @@ -67,8 +73,8 @@ 0 0 - 771 - 606 + 946 + 386 @@ -77,14 +83,56 @@ 0 - + + 6 + + + + 0 + + + 0 + + + 0 + + + + 0 + 0 + + Emulator - + + Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft + + + false + + + false + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + @@ -106,7 +154,7 @@ - + 0 0 @@ -149,215 +197,40 @@ - - - - 6 - - - 0 - - - - - - - Username - - - - - - - - - - - - - - + + + + 6 + + + 0 + - + - + 0 0 - + 0 0 - - GUI Settings - - - - 1 - - - 11 - - - - - Show Game Size In List - - - - - - - - 0 - 0 - - - - Play title music - - - - - - - 1 - - - 0 - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Volume - - - - - - - Set the volume of the background music. - - - 100 - - - 10 - - - 20 - - - 50 - - - Qt::Orientation::Horizontal - - - false - - - false - - - QSlider::TickPosition::NoTicks - - - 10 - - - - - - - - - 6 - - - 0 - - - 50 - - - - - - - Trophy - - - - - - Disable Trophy Pop-ups - - - - - - - Trophy Key - - - - - - - - 0 - 0 - - - - - 10 - false - - - - - - - - - - - - - - - - - - - - System + + 70 + @@ -387,6 +260,45 @@ + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + @@ -432,16 +344,16 @@ - 10 + 6 - 1 + 9 - 11 + 9 - 190 + 80 @@ -508,7 +420,7 @@ - + 0 0 @@ -554,8 +466,255 @@ - + + + + + + + + true + + + Gui + + + + + 0 + 0 + 946 + 386 + + + + + + + 0 + + + 0 + + + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + GUI Settings + + + + 9 + + + 9 + + + + + Default tab when opening settings + + + + + + + 0 + 0 + + + + + General + + + + + Gui + + + + + Graphics + + + + + User + + + + + Input + + + + + Paths + + + + + Debug + + + + + + + + + + + Show Game Size In List + + + + + + + + 0 + 0 + + + + + 11 + false + true + + + + false + + + + + + false + + + + + 10 + 80 + 416 + 29 + + + + + 0 + 0 + + + + Set the volume of the background music. + + + 100 + + + 10 + + + 20 + + + 50 + + + Qt::Orientation::Horizontal + + + false + + + false + + + QSlider::TickPosition::NoTicks + + + 10 + + + + + + 10 + 22 + 416 + 26 + + + + + 0 + 0 + + + + Play title music + + + + + + 10 + 54 + 416 + 20 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Volume + + + + + + + + + + + + 6 + + + 210 + @@ -570,6 +729,12 @@ 0 + + + 0 + 0 + + Game Compatibility @@ -578,10 +743,10 @@ 10 - 1 + 9 - 11 + 9 @@ -632,6 +797,394 @@ + + + true + + + Graphics + + + + + 0 + 0 + 946 + 386 + + + + + + + + + + + Graphics Device + + + + + + + + + Enable NULL GPU + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + 6 + + + 0 + + + + + + + Width + + + + + + true + + + QAbstractSpinBox::CorrectionMode::CorrectToNearestValue + + + false + + + 0 + + + 9999 + + + 1280 + + + + + + + + + + Height + + + + + + true + + + true + + + QAbstractSpinBox::CorrectionMode::CorrectToNearestValue + + + false + + + 0 + + + 9999 + + + 720 + + + + + + + + + + Vblank Divider + + + + + + true + + + true + + + QAbstractSpinBox::CorrectionMode::CorrectToNearestValue + + + false + + + 1 + + + 9999 + + + 1 + + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + 12 + + + 12 + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + true + + + User + + + + + 0 + 0 + 946 + 386 + + + + + + + 0 + + + 0 + + + + + + + Username + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 40 + + + + + + + + + + 6 + + + 0 + + + 50 + + + + + + + Trophy + + + + + + Disable Trophy Pop-ups + + + + + + + Trophy Key + + + + + + + + 0 + 0 + + + + + 10 + false + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::MinimumExpanding + + + + 0 + 0 + + + + + + + true @@ -644,8 +1197,8 @@ 0 0 - 455 - 252 + 946 + 386 @@ -916,260 +1469,6 @@ - - - true - - - Graphics - - - - - 0 - 0 - 579 - 194 - - - - - - - - - - - Graphics Device - - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - 6 - - - 0 - - - - - - - Width - - - - - - true - - - QAbstractSpinBox::CorrectionMode::CorrectToNearestValue - - - false - - - 0 - - - 9999 - - - 1280 - - - - - - - - - - Height - - - - - - true - - - true - - - QAbstractSpinBox::CorrectionMode::CorrectToNearestValue - - - false - - - 0 - - - 9999 - - - 720 - - - - - - - - - - - - - - 6 - - - 0 - - - - - - - Vblank Divider - - - - - - true - - - true - - - QAbstractSpinBox::CorrectionMode::CorrectToNearestValue - - - false - - - 1 - - - 9999 - - - 1 - - - - - - - - - - - - - - - - 12 - - - 12 - - - - - true - - - Advanced - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - - - - Enable Shaders Dumping - - - - - - - Enable NULL GPU - - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - true @@ -1178,6 +1477,14 @@ Paths + + + 0 + 0 + 946 + 386 + + @@ -1186,34 +1493,34 @@ - - - - - Add... - - - - - - - Remove - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - + + + + + Add... + + + + + + + Remove + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + @@ -1225,27 +1532,27 @@ - Save Data Path + Save Data Path - - - - - - true - - - - - - - Browse - - - - - + + + + + + true + + + + + + + Browse + + + + + @@ -1264,13 +1571,13 @@ 0 0 - 510 - 269 + 946 + 386 - + 0 @@ -1291,6 +1598,13 @@ Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + + + Enable Shaders Dumping + + + diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index 47bd673b2..f7b93028a 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -540,10 +540,18 @@ Enable Fullscreen تمكين ملء الشاشة + + Fullscreen Mode + وضع ملء الشاشة + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + علامة التبويب الافتراضية عند فتح الإعدادات + Show Game Size In List عرض حجم اللعبة في القائمة @@ -620,6 +628,14 @@ Graphics الرسومات + + Gui + واجهة + + + User + مستخدم + Graphics Device جهاز الرسومات @@ -805,7 +821,7 @@ تحديث: Release: إصدارات رسمية تصدر شهريًا، قد تكون قديمة بعض الشيء، لكنها أكثر استقرارًا واختبارًا. Nightly: إصدارات تطوير تحتوي على أحدث الميزات والإصلاحات، لكنها قد تحتوي على أخطاء وأقل استقرارًا. - GUIgroupBox + GUIMusicGroupBox تشغيل موسيقى العنوان:\nإذا كانت اللعبة تدعم ذلك، قم بتمكين تشغيل موسيقى خاصة عند اختيار اللعبة في واجهة المستخدم. diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 91a98abd4..61d2f68bf 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Fuldskærmstilstand + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Standardfaneblad ved åbning af indstillinger + Show Game Size In List Vis vis spilstørrelse i listen @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Interface + + + User + Bruger + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Opdatering:\nRelease: Officielle builds, der frigives månedligt, som kan være meget ældre, men mere stabile og testet.\nNightly: Udviklerbuilds med de nyeste funktioner og rettelser, men som kan indeholde fejl og være mindre stabile. - GUIgroupBox + GUIMusicGroupBox Titelsmusikafspilning:\nHvis spillet understøtter det, aktiver speciel musik, når spillet vælges i brugergrænsefladen. diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index b1e1d2664..9d99a7048 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -540,10 +540,18 @@ Enable Fullscreen Vollbild aktivieren + + Fullscreen Mode + Vollbildmodus + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Standardregisterkarte beim Öffnen der Einstellungen + Show Game Size In List Zeigen Sie die Spielgröße in der Liste @@ -620,6 +628,14 @@ Graphics Grafik + + Gui + Benutzeroberfläche + + + User + Benutzer + Graphics Device Grafikgerät @@ -805,7 +821,7 @@ Update:\nRelease: Offizielle Builds, die monatlich veröffentlicht werden, können viel älter sein, aber stabiler und getestet.\nNightly: Entwickler-Builds, die die neuesten Funktionen und Fehlerbehebungen enthalten, aber Fehler enthalten und weniger stabil sein können. - GUIgroupBox + GUIMusicGroupBox Wiedergabe der Titelmusik:\nWenn das Spiel dies unterstützt, wird beim Auswählen des Spiels in der Benutzeroberfläche spezielle Musik abgespielt. diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index ecda0ede0..66c3c07cd 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Λειτουργία Πλήρους Οθόνης + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Προεπιλεγμένη καρτέλα κατά την ανοίγμα των ρυθμίσεων + Show Game Size In List Εμφάνιση Μεγέθους Παιχνιδιού στη Λίστα @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Διεπαφή + + + User + Χρήστης + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Ενημερώσεις:\nRelease: Επίσημες εκδόσεις που κυκλοφορούν μηνιαίως, είναι παλαιότερες αλλά πιο σταθερές και δοκιμασμένες.\nNightly: Εκδόσεις προγραμματιστών με νέες δυνατότητες και διορθώσεις, αλλά μπορεί να περιέχουν σφάλματα και να είναι λιγότερο σταθερές. - GUIgroupBox + GUIMusicGroupBox Αναπαραγωγή Μουσικής Τίτλων:\nΕάν το παιχνίδι το υποστηρίζει, ενεργοποιεί ειδική μουσική κατά την επιλογή του παιχνιδιού από τη διεπαφή χρήστη. diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index dc72d97c9..8b6c3c69f 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Fullscreen Mode + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Default tab when opening settings + Show Game Size In List Show Game Size In List @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Gui + + + User + User + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable. - GUIgroupBox + GUIMusicGroupBox Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index a47f7c577..8f7a09b5d 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -540,10 +540,18 @@ Enable Fullscreen Habilitar pantalla completa + + Fullscreen Mode + Modo de Pantalla Completa + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Pestaña predeterminada al abrir la configuración + Show Game Size In List Mostrar Tamaño del Juego en la Lista @@ -620,6 +628,14 @@ Graphics Gráficos + + Gui + Interfaz + + + User + Usuario + Graphics Device Dispositivo gráfico @@ -805,7 +821,7 @@ Actualización:\nRelease: Versiones oficiales lanzadas cada mes que pueden estar muy desactualizadas, pero son más confiables y están probadas.\nNightly: Versiones de desarrollo que tienen todas las últimas funciones y correcciones, pero pueden contener errores y son menos estables. - GUIgroupBox + GUIMusicGroupBox Reproducir Música del Título:\nSi un juego lo admite, habilita la reproducción de música especial al seleccionar el juego en la interfaz gráfica. diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 976e7614e..043d782c2 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -540,10 +540,18 @@ Enable Fullscreen تمام صفحه + + Fullscreen Mode + حالت تمام صفحه + Enable Separate Update Folder فعال‌سازی پوشه جداگانه برای به‌روزرسانی + + Default tab when opening settings + زبان پیش‌فرض هنگام باز کردن تنظیمات + Show Game Size In List نمایش اندازه بازی در لیست @@ -620,6 +628,14 @@ Graphics گرافیک + + Gui + رابط کاربری + + + User + کاربر + Graphics Device کارت گرافیک مورداستفاده @@ -805,7 +821,7 @@ به‌روزرسانی:\nانتشار: نسخه‌های رسمی که هر ماه منتشر می‌شوند و ممکن است بسیار قدیمی باشند، اما پایدارتر و تست‌ شده‌تر هستند.\nشبانه: نسخه‌های توسعه‌ای که شامل جدیدترین ویژگی‌ها و اصلاحات هستند، اما ممکن است دارای اشکال باشند و کمتر پایدار باشند. - GUIgroupBox + GUIMusicGroupBox پخش موسیقی عنوان:\nIدر صورتی که بازی از آن پشتیبانی کند، پخش موسیقی ویژه هنگام انتخاب بازی در رابط کاربری را فعال می‌کند. diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index abc091b7e..86f6a6d3c 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -540,10 +540,18 @@ Enable Fullscreen Ota Käyttöön Koko Ruudun Tila + + Fullscreen Mode + Koko näytön tila + Enable Separate Update Folder Ota Käyttöön Erillinen Päivityshakemisto + + Default tab when opening settings + Oletusvälilehti avattaessa asetuksia + Show Game Size In List Näytä pelin koko luettelossa @@ -620,6 +628,14 @@ Graphics Grafiikka + + Gui + Rajapinta + + + User + Käyttäjä + Graphics Device Näytönohjain @@ -805,7 +821,7 @@ Päivitys:\nRelease: Viralliset versiot, jotka julkaistaan kuukausittain ja saattavat olla hyvin vanhoja, mutta ovat luotettavampia ja testatumpia.\nNightly: Kehitysversiot, joissa on kaikki uusimmat ominaisuudet ja korjaukset, mutta ne saattavat sisältää virheitä ja ovat vähemmän vakaita. - GUIgroupBox + GUIMusicGroupBox Soita Otsikkomusiikkia:\nJos peli tukee sitä, ota käyttöön erityisen musiikin soittaminen pelin valinnan yhteydessä käyttöliittymässä. diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index d2a1c5307..601ece8b4 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -540,10 +540,18 @@ Enable Fullscreen Plein écran + + Fullscreen Mode + Mode Plein Écran + Enable Separate Update Folder Dossier séparé pour les mises à jours + + Default tab when opening settings + Onglet par défaut lors de l'ouverture des paramètres + Show Game Size In List Afficher la taille du jeu dans la liste @@ -620,6 +628,14 @@ Graphics Graphismes + + Gui + Interface + + + User + Utilisateur + Graphics Device Carte graphique @@ -805,7 +821,7 @@ Mise à jour:\nRelease: versions officielles publiées chaque mois qui peuvent être très anciennes, mais plus fiables et testées.\nNightly: versions de développement avec toutes les dernières fonctionnalités et correctifs, mais pouvant avoir des bogues et être moins stables. - GUIgroupBox + GUIMusicGroupBox Jouer de la musique de titre:\nSi le jeu le prend en charge, cela active la musique spéciale lorsque vous sélectionnez le jeu dans l'interface utilisateur. diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index dff6a3a18..fe71e8120 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -540,10 +540,18 @@ Enable Fullscreen Teljes Képernyő Engedélyezése + + Fullscreen Mode + Teljes képernyős mód + Enable Separate Update Folder Külön Frissítési Mappa Engedélyezése + + Default tab when opening settings + Alapértelmezett fül a beállítások megnyitásakor + Show Game Size In List Játékméret megjelenítése a listában @@ -620,6 +628,14 @@ Graphics Grafika + + Gui + Felület + + + User + Felhasználó + Graphics Device Grafikai Eszköz @@ -805,7 +821,7 @@ Frissítés:\nRelease: Hivatalos verziók, amelyeket havonta adnak ki, és amelyek nagyon elavultak lehetnek, de megbízhatóbbak és teszteltek.\nNightly: Fejlesztési verziók, amelyek az összes legújabb funkciót és javítást tartalmazzák, de hibákat tartalmazhatnak és kevésbé stabilak. - GUIgroupBox + GUIMusicGroupBox Játék címzene lejátszása:\nHa a játék támogatja, engedélyezze egy speciális zene lejátszását, amikor a játékot kiválasztja a GUI-ban. diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index e6fb8b5aa..acfde43a7 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Mode Layar Penuh + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Tab default saat membuka pengaturan + Show Game Size In List Tampilkan Ukuran Game di Daftar @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Antarmuka + + + User + Pengguna + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Pembaruan:\nRelease: Versi resmi yang dirilis setiap bulan yang mungkin sangat ketinggalan zaman, tetapi lebih dapat diandalkan dan teruji.\nNightly: Versi pengembangan yang memiliki semua fitur dan perbaikan terbaru, tetapi mungkin mengandung bug dan kurang stabil. - GUIgroupBox + GUIMusicGroupBox Putar Musik Judul Permainan:\nJika permainan mendukungnya, aktifkan pemutaran musik khusus saat memilih permainan di GUI. diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 73dbdc603..93c6233a6 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -540,10 +540,18 @@ Enable Fullscreen Abilita Schermo Intero + + Fullscreen Mode + Modalità Schermo Intero + Enable Separate Update Folder Abilita Cartella Aggiornamenti Separata + + Default tab when opening settings + Scheda predefinita all'apertura delle impostazioni + Show Game Size In List Mostra la dimensione del gioco nell'elenco @@ -620,6 +628,14 @@ Graphics Grafica + + Gui + Interfaccia + + + User + Utente + Graphics Device Scheda Grafica @@ -805,7 +821,7 @@ Aggiornamento:\nRelease: Versioni ufficiali rilasciate ogni mese che potrebbero essere molto datate, ma sono più affidabili e testate.\nNightly: Versioni di sviluppo che hanno tutte le ultime funzionalità e correzioni, ma potrebbero contenere bug e sono meno stabili. - GUIgroupBox + GUIMusicGroupBox Riproduci Musica del Titolo:\nSe un gioco lo supporta, attiva la riproduzione di musica speciale quando selezioni il gioco nell'interfaccia grafica. diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index bb9488a6b..921af52bb 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -540,10 +540,18 @@ Enable Fullscreen フルスクリーンを有効にする + + Fullscreen Mode + 全画面モード + Enable Separate Update Folder アップデートフォルダの分離を有効化 + + Default tab when opening settings + 設定を開くときのデフォルトタブ + Show Game Size In List ゲームサイズをリストに表示 @@ -620,6 +628,14 @@ Graphics グラフィックス + + Gui + インターフェース + + + User + ユーザー + Graphics Device グラフィックスデバイス @@ -805,7 +821,7 @@ 更新:\nRelease: 最新の機能を利用できない可能性がありますが、より信頼性が高くテストされた公式バージョンが毎月リリースされます。\nNightly: 最新の機能と修正がすべて含まれていますが、バグが含まれている可能性があり、安定性は低いです。 - GUIgroupBox + GUIMusicGroupBox タイトルミュージックを再生:\nゲームでサポートされている場合に、GUIでゲームを選択したときに特別な音楽を再生する機能を有効にします。 diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 560b58340..2491959a6 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + 전체 화면 모드 + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + 설정 열기 시 기본 탭 + Show Game Size In List 게임 크기를 목록에 표시 @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + 인터페이스 + + + User + 사용자 + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable. - GUIgroupBox + GUIMusicGroupBox Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index e2ec1e5c3..23a52e948 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Viso ekranas + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Numatytoji kortelė atidarius nustatymus + Show Game Size In List Rodyti žaidimo dydį sąraše @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Interfeisa + + + User + Naudotojas + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Atnaujinti:\nRelease: Oficialios versijos, išleidžiamos kiekvieną mėnesį, kurios gali būti labai pasenusios, tačiau yra patikimos ir išbandytos.\nNightly: Vystymo versijos, kuriose yra visos naujausios funkcijos ir taisymai, tačiau gali turėti klaidų ir būti mažiau stabilios. - GUIgroupBox + GUIMusicGroupBox Groti antraščių muziką:\nJei žaidimas tai palaiko, įjungia specialios muzikos grojimą, kai pasirinkite žaidimą GUI. diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index b94d29b23..d6d4090df 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -540,10 +540,18 @@ Enable Fullscreen Aktiver fullskjerm + + Fullscreen Mode + Fullskjermmodus + Enable Separate Update Folder Aktiver seperat oppdateringsmappe + + Default tab when opening settings + Standardfanen når innstillingene åpnes + Show Game Size In List Vis spillstørrelse i listen @@ -620,6 +628,14 @@ Graphics Grafikk + + Gui + Grensesnitt + + + User + Bruker + Graphics Device Grafikkenhet @@ -805,7 +821,7 @@ Oppdatering:\nRelease: Offisielle versjoner utgitt hver måned som kan være veldig utdaterte, men er mer pålitelige og testet.\nNightly: Utviklingsversjoner som har alle de nyeste funksjonene og feilrettingene, men som kan inneholde feil og er mindre stabile. - GUIgroupBox + GUIMusicGroupBox Spille tittelmusikk:\nHvis et spill støtter det, så aktiveres det spesiell musikk når du velger spillet i menyen. diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index add27500f..1dabda8b4 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Volledig schermmodus + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Standaardtabblad bij het openen van instellingen + Show Game Size In List Toon grootte van het spel in de lijst @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Interface + + + User + Gebruiker + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Updateren:\nRelease: Officiële versies die elke maand worden uitgebracht, die zeer verouderd kunnen zijn, maar betrouwbaar en getest zijn.\nNightly: Ontwikkelingsversies die alle nieuwste functies en bugfixes bevatten, maar mogelijk bugs bevatten en minder stabiel zijn. - GUIgroupBox + GUIMusicGroupBox Speel titelsong:\nAls een game dit ondersteunt, wordt speciale muziek afgespeeld wanneer je het spel in de GUI selecteert. diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 3280beea7..528e88337 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -540,10 +540,18 @@ Enable Fullscreen Włącz pełny ekran + + Fullscreen Mode + Tryb Pełnoekranowy + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Domyślna zakładka podczas otwierania ustawień + Show Game Size In List Pokaż rozmiar gry na liście @@ -620,6 +628,14 @@ Graphics Grafika + + Gui + Interfejs + + + User + Użytkownik + Graphics Device Karta graficzna @@ -805,7 +821,7 @@ Aktualizator:\nRelease: Oficjalne wersje wydawane co miesiąc, które mogą być bardzo przestarzałe, ale są niezawodne i przetestowane.\nNightly: Wersje rozwojowe, które zawierają wszystkie najnowsze funkcje i poprawki błędów, ale mogą mieć błędy i być mniej stabilne. - GUIgroupBox + GUIMusicGroupBox Odtwórz muzykę tytułową:\nJeśli gra to obsługuje, aktywuje odtwarzanie specjalnej muzyki podczas wybierania gry w GUI. diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index b9d889519..fde1d3b63 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -540,10 +540,18 @@ Enable Fullscreen Ativar Tela Cheia + + Fullscreen Mode + Modo de Tela Cheia + Enable Separate Update Folder Habilitar pasta de atualização separada + + Default tab when opening settings + Aba padrão ao abrir as configurações + Show Game Size In List Mostrar Tamanho do Jogo na Lista @@ -620,6 +628,14 @@ Graphics Gráficos + + Gui + Interface + + + User + Usuário + Graphics Device Placa de Vídeo @@ -766,7 +782,7 @@ fullscreenCheckBox - Ativar Tela Cheia:\nMove automaticamente a janela do jogo para o modo tela cheia.\nIsso pode ser alterado pressionando a tecla F11. + Ativar Tela Cheia:\nAltera a janela do jogo para o modo tela cheia.\nIsso pode ser alterado pressionando a tecla F11. separateUpdatesCheckBox @@ -805,7 +821,11 @@ Atualizações:\nRelease: Versões oficiais que são lançadas todo mês e podem ser bastante antigas, mas são mais confiáveis e testadas.\nNightly: Versões de desenvolvimento que têm todos os novos recursos e correções, mas podem ter bugs e ser instáveis. - GUIgroupBox + chooseHomeTabGroupBox + do menu. + + + GUIMusicGroupBox Reproduzir música de abertura:\nSe o jogo suportar, ativa a reprodução de uma música especial ao selecionar o jogo na interface do menu. diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 00a9eb179..9791a7682 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Mod Ecran Complet + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Tab-ul implicit la deschiderea setărilor + Show Game Size In List Afișează dimensiunea jocului în listă @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Interfață + + + User + Utilizator + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Actualizare:\nRelease: Versiuni oficiale lansate în fiecare lună, care pot fi foarte învechite, dar sunt mai fiabile și testate.\nNightly: Versiuni de dezvoltare care conțin toate cele mai recente funcții și corecții, dar pot conține erori și sunt mai puțin stabile. - GUIgroupBox + GUIMusicGroupBox Redă muzica titlului:\nDacă un joc o suportă, activează redarea muzicii speciale când selectezi jocul în GUI. diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 4c90450dd..6423f5ba4 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -540,10 +540,18 @@ Enable Fullscreen Полноэкранный режим + + Fullscreen Mode + Режим Полного Экран + Enable Separate Update Folder Отдельная папка обновлений + + Default tab when opening settings + Вкладка по умолчанию при открытии настроек + Show Game Size In List Показать размер игры в списке @@ -620,6 +628,14 @@ Graphics Графика + + Gui + Интерфейс + + + User + Пользователь + Graphics Device Графическое устройство @@ -805,7 +821,7 @@ Обновление:\nRelease: Официальные версии, которые выпускаются каждый месяц и могут быть очень старыми, но они более надежные и проверенные.\nNightly: Версии разработки, которые содержат все последние функции и исправления, но могут содержать ошибки и менее стабильны. - GUIgroupBox + GUIMusicGroupBox Играть заглавную музыку:\nВключает воспроизведение специальной музыки при выборе игры в списке, если она это поддерживает. diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 768db1e75..2f88b4390 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -540,10 +540,18 @@ Enable Fullscreen Aktivizo Ekranin e plotë + + Fullscreen Mode + Modaliteti i Plotë + Enable Separate Update Folder Aktivizo dosjen e ndarë të përditësimit + + Default tab when opening settings + Skeda e parazgjedhur kur hapni cilësimet + Show Game Size In List Shfaq madhësinë e lojës në listë @@ -620,6 +628,14 @@ Graphics Grafika + + Gui + Ndërfaqe + + + User + Përdorues + Graphics Device Pajisja e Grafikës @@ -805,7 +821,7 @@ Përditësimi:\nRelease: Versionet zyrtare të lëshuara çdo muaj që mund të jenë shumë të vjetra, por janë më të besueshme dhe të provuara.\nNightly: Versionet e zhvillimit që kanë të gjitha veçoritë dhe rregullimet më të fundit, por mund të përmbajnë gabime dhe janë më pak të qëndrueshme. - GUIgroupBox + GUIMusicGroupBox Luaj muzikën e titullit:\nNëse një lojë e mbështet, aktivizohet luajtja e muzikës të veçantë kur të zgjidhësh lojën në GUI. diff --git a/src/qt_gui/translations/sv.ts b/src/qt_gui/translations/sv.ts index 3781ba45c..e17944f12 100644 --- a/src/qt_gui/translations/sv.ts +++ b/src/qt_gui/translations/sv.ts @@ -1032,10 +1032,18 @@ Enable Fullscreen Aktivera helskärm + + Fullscreen Mode + Helskärmsläge + Enable Separate Update Folder Aktivera separat uppdateringsmapp + + Default tab when opening settings + Standardflik när inställningar öppnas + Show Game Size In List Visa spelstorlek i listan @@ -1108,6 +1116,14 @@ Graphics Grafik + + Gui + Gränssnitt + + + User + Användare + Graphics Device Grafikenhet @@ -1285,7 +1301,7 @@ updaterGroupBox - GUIgroupBox + GUIMusicGroupBox Spela upp titelmusik:\nOm ett spel har stöd för det kan speciell musik spelas upp från spelet i gränssnittet diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 5e8499073..6436278de 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -540,10 +540,18 @@ Enable Fullscreen Tam Ekranı Etkinleştir + + Fullscreen Mode + Tam Ekran Modu + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Ayarlar açıldığında varsayılan sekme + Show Game Size In List Göster oyun boyutunu listede @@ -620,6 +628,14 @@ Graphics Grafikler + + Gui + Arayüz + + + User + Kullanıcı + Graphics Device Grafik Cihazı @@ -805,7 +821,7 @@ Güncelleme:\nRelease: Her ay yayınlanan resmi sürümler; çok eski olabilirler, ancak daha güvenilirdir ve test edilmiştir.\nNightly: Tüm en son özellikler ve düzeltmeler ile birlikte geliştirme sürümleri; hatalar içerebilir ve daha az kararlıdırlar. - GUIgroupBox + GUIMusicGroupBox Başlık Müziklerini Çal:\nEğer bir oyun bunu destekliyorsa, GUI'de oyunu seçtiğinizde özel müziklerin çalmasını etkinleştirir. diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index a1c7e97e0..720ad5b99 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -540,10 +540,18 @@ Enable Fullscreen Увімкнути повноекранний режим + + Fullscreen Mode + Режим Повноекранний + Enable Separate Update Folder Увімкнути окрему папку оновлень + + Default tab when opening settings + Вкладка за замовчуванням при відкритті налаштувань + Show Game Size In List Показати розмір гри в списку @@ -620,6 +628,14 @@ Graphics Графіка + + Gui + Інтерфейс + + + User + Користувач + Graphics Device Графічний пристрій @@ -805,7 +821,7 @@ Оновлення:\nRelease: Офіційні версії, які випускаються щомісяця і можуть бути дуже старими, але вони більш надійні та перевірені.\nNightly: Версії для розробників, які мають усі найновіші функції та виправлення, але можуть містити помилки та є менш стабільними. - GUIgroupBox + GUIMusicGroupBox Грати заголовну музику:\nВмикає відтворення спеціальної музики під час вибору гри в списку, якщо вона це підтримує. diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index a579a1983..a81630fad 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + Chế độ Toàn màn hình + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + Tab mặc định khi mở cài đặt + Show Game Size In List Hiển thị Kích thước Game trong Danh sách @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + Giao diện + + + User + Người dùng + Graphics Device Graphics Device @@ -805,7 +821,7 @@ Cập nhật:\nRelease: Các phiên bản chính thức được phát hành hàng tháng; có thể khá cũ nhưng đáng tin cậy hơn và đã được thử nghiệm.\nNightly: Các phiên bản phát triển có tất cả các tính năng và sửa lỗi mới nhất; có thể có lỗi và ít ổn định hơn. - GUIgroupBox + GUIMusicGroupBox Phát nhạc tiêu đề trò chơi:\nNếu một trò chơi hỗ trợ điều này, hãy kích hoạt phát nhạc đặc biệt khi bạn chọn trò chơi trong GUI. diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 5450f3dfd..b06b0a286 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -540,10 +540,18 @@ Enable Fullscreen 启用全屏 + + Fullscreen Mode + 全屏模式 + Enable Separate Update Folder 启用单独的更新目录 + + Default tab when opening settings + 打开设置时的默认选项卡 + Show Game Size In List 显示游戏大小在列表中 @@ -620,6 +628,14 @@ Graphics 图像 + + Gui + 界面 + + + User + 用户 + Graphics Device 图形设备 @@ -805,7 +821,7 @@ 更新:\nRelease:每月发布的官方版本可能非常过时,但更可靠且经过测试。\nNightly:包含所有最新功能和修复的开发版本,但可能包含错误且稳定性较低。 - GUIgroupBox + GUIMusicGroupBox 播放标题音乐:\n如果游戏支持,在图形界面选择游戏时播放特殊音乐。 diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 0ce0b4d69..9f6758797 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -540,10 +540,18 @@ Enable Fullscreen Enable Fullscreen + + Fullscreen Mode + 全螢幕模式 + Enable Separate Update Folder Enable Separate Update Folder + + Default tab when opening settings + 打開設置時的默認選項卡 + Show Game Size In List 顯示遊戲大小在列表中 @@ -620,6 +628,14 @@ Graphics Graphics + + Gui + 介面 + + + User + 使用者 + Graphics Device Graphics Device @@ -805,7 +821,7 @@ 更新:\nRelease: 每月發布的官方版本,可能非常舊,但更可靠且經過測試。\nNightly: 開發版本,擁有所有最新的功能和修復,但可能包含錯誤,穩定性較差。 - GUIgroupBox + GUIMusicGroupBox 播放標題音樂:\n如果遊戲支持,啟用在GUI中選擇遊戲時播放特殊音樂。 From 81e7e4b21646ad40edaae06f9b1e58eaea139b3c Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 23 Jan 2025 00:52:22 +0200 Subject: [PATCH 22/68] Update building-linux.md --- documents/building-linux.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documents/building-linux.md b/documents/building-linux.md index 2672c70f4..28b8c6056 100644 --- a/documents/building-linux.md +++ b/documents/building-linux.md @@ -99,7 +99,7 @@ Open `cmake-gui` and specify the source code and build directories. If you clone Click on Configure, select "Unix Makefiles", select "Specify native compilers", click Next and choose `clang` and `clang++` as the C and CXX compilers. Usually they are located in `/bin/clang` and `/bin/clang++`. Click on Finish and let it configure the project. -Now every option should be displayed in red. Change anything you want, then click on Generate to make the changes permanent, then open a terminal window and do steps 2 and 3 of Option 1. +Now every option should be displayed in red. Change anything you want, then click on Generate to make the changes permanent, then open a terminal window and do step 2 of Option 1. #### Option 3: Visual Studio Code @@ -131,4 +131,4 @@ If you want to debug it, change the build type under Project Status > Configure Don't forget to change the launch target for both options to the shadPS4 executable inside shadPS4/build: -![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/5.png) \ No newline at end of file +![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/5.png) From cc2e13873f9be4e2f95e4da7a71e7333502fa183 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Wed, 22 Jan 2025 22:21:41 -0300 Subject: [PATCH 23/68] Fix showing debug menu bar / Devtools (#2214) * Fix showing debug menu bar / Devtools * Fix --- src/core/debug_state.cpp | 2 ++ src/core/debug_state.h | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index 6508a9875..23ebcbb9b 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -17,6 +17,8 @@ using namespace DebugStateType; DebugStateImpl& DebugState = *Common::Singleton::Instance(); +bool DebugStateType::showing_debug_menu_bar = false; + static ThreadID ThisThreadID() { #ifdef _WIN32 return GetCurrentThreadId(); diff --git a/src/core/debug_state.h b/src/core/debug_state.h index a9e6f48b7..217efd1a9 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -35,6 +35,8 @@ class ShaderList; namespace DebugStateType { +extern bool showing_debug_menu_bar; + enum class QueueType { dcb = 0, ccb = 1, @@ -131,8 +133,6 @@ class DebugStateImpl { friend class Core::Devtools::Widget::FrameGraph; friend class Core::Devtools::Widget::ShaderList; - bool showing_debug_menu_bar = false; - std::queue debug_message_popup; std::mutex guest_threads_mutex{}; From cef92fbcaad34e0caa90bc5afbce5e5b30c43636 Mon Sep 17 00:00:00 2001 From: DemoJameson Date: Thu, 23 Jan 2025 19:28:44 +0800 Subject: [PATCH 24/68] Update Simplified Chinese translation (#2216) * Update Simplified Chinese translation * Fix incorrect translation * Fix new line --- src/qt_gui/translations/zh_CN.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index b06b0a286..16ae03f94 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -552,7 +552,7 @@ Default tab when opening settings 打开设置时的默认选项卡 - + Show Game Size In List 显示游戏大小在列表中 @@ -830,7 +830,7 @@ hideCursorGroupBox - 隐藏光标:\n选择光标何时消失:\n从不: 始终显示光标。\闲置: 光标在闲置若干秒后消失。\n始终: 始终显示光标。 + 隐藏光标:\n选择光标何时消失:\n从不: 从不隐藏光标。\n闲置:光标在闲置若干秒后消失。\n始终:始终隐藏光标。 idleTimeoutGroupBox @@ -928,6 +928,14 @@ rdocCheckBox 启用 RenderDoc 调试:\n启用后模拟器将提供与 Renderdoc 的兼容性,允许在渲染过程中捕获和分析当前渲染的帧。 + + saveDataBox + 存档数据路径:\n保存游戏存档数据的目录。 + + + browseButton + 浏览:\n选择一个目录保存游戏存档数据。 + CheatsPatches From fb738bc24739704abcc87d87f25dd6a241c9719d Mon Sep 17 00:00:00 2001 From: Angelo Rosa Date: Thu, 23 Jan 2025 05:54:42 -0800 Subject: [PATCH 25/68] Optimize workflows by caching `apt install` (#2212) * optimize apt caches * change on push syntax * removing comments and check time improvements on following pushes * add recursive submodule fetch * Revert "add recursive submodule fetch" This reverts commit b059bda3b88aa7d81e4bee3348c9104536b15fd2. * restore old push on everyhing rule * fix libfuse2 uncaching package * moving qt deps outside from caching directives --- .github/workflows/build.yml | 48 ++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3da7163dd..a49ffefe9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,20 +24,27 @@ jobs: runs-on: ubuntu-24.04 continue-on-error: true steps: + - uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Install run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main' - sudo apt update - sudo apt install clang-format-18 + + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: clang-format-18 + version: 1.0 + - name: Build env: COMMIT_RANGE: ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} run: ./.ci/clang-format.sh - + + get-info: runs-on: ubuntu-24.04 outputs: @@ -282,7 +289,13 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + run: | + sudo apt install -y libfuse2 + + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + version: 1.0 - name: Cache CMake Configuration uses: actions/cache@v4 @@ -338,7 +351,13 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev + run: | + sudo apt install -y libfuse2 qt6-base-dev qt6-tools-dev qt6-multimedia-dev + + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + version: 1.0 - name: Cache CMake Configuration uses: actions/cache@v4 @@ -385,7 +404,13 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + run: | + sudo apt install -y libfuse2 + + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + version: 1.0 - name: Cache CMake Configuration uses: actions/cache@v4 @@ -421,7 +446,13 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev + run: | + sudo apt install -y libfuse2 qt6-base-dev qt6-tools-dev qt6-multimedia-dev + + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev + version: 1.0 - name: Cache CMake Configuration uses: actions/cache@v4 @@ -443,7 +474,8 @@ jobs: key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} - name: Configure CMake - run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: | + cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) From 2e6c9b8f9866f48bd39dc35b25310e77e3fa89f6 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 23 Jan 2025 22:58:45 +0200 Subject: [PATCH 26/68] Revert "Optimize workflows by caching `apt install` (#2212)" (#2220) This reverts commit fb738bc24739704abcc87d87f25dd6a241c9719d. --- .github/workflows/build.yml | 48 +++++++------------------------------ 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a49ffefe9..3da7163dd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,27 +24,20 @@ jobs: runs-on: ubuntu-24.04 continue-on-error: true steps: - - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Install run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main' - - - uses: awalsh128/cache-apt-pkgs-action@latest - with: - packages: clang-format-18 - version: 1.0 - + sudo apt update + sudo apt install clang-format-18 - name: Build env: COMMIT_RANGE: ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} run: ./.ci/clang-format.sh - - + get-info: runs-on: ubuntu-24.04 outputs: @@ -289,13 +282,7 @@ jobs: submodules: recursive - name: Install dependencies - run: | - sudo apt install -y libfuse2 - - - uses: awalsh128/cache-apt-pkgs-action@latest - with: - packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - version: 1.0 + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -351,13 +338,7 @@ jobs: submodules: recursive - name: Install dependencies - run: | - sudo apt install -y libfuse2 qt6-base-dev qt6-tools-dev qt6-multimedia-dev - - - uses: awalsh128/cache-apt-pkgs-action@latest - with: - packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - version: 1.0 + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -404,13 +385,7 @@ jobs: submodules: recursive - name: Install dependencies - run: | - sudo apt install -y libfuse2 - - - uses: awalsh128/cache-apt-pkgs-action@latest - with: - packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - version: 1.0 + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -446,13 +421,7 @@ jobs: submodules: recursive - name: Install dependencies - run: | - sudo apt install -y libfuse2 qt6-base-dev qt6-tools-dev qt6-multimedia-dev - - - uses: awalsh128/cache-apt-pkgs-action@latest - with: - packages: libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - version: 1.0 + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -474,8 +443,7 @@ jobs: key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} - name: Configure CMake - run: | - cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) From cdca420a2e8579832c7ebffb7ec2ed8f9e74030c Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 23 Jan 2025 23:27:37 +0200 Subject: [PATCH 27/68] Update building-linux.md --- documents/building-linux.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/documents/building-linux.md b/documents/building-linux.md index 28b8c6056..eedcd6664 100644 --- a/documents/building-linux.md +++ b/documents/building-linux.md @@ -29,8 +29,7 @@ sudo dnf install clang git cmake libatomic alsa-lib-devel pipewire-jack-audio-co sudo pacman -S base-devel clang git cmake sndio jack2 openal qt6-base qt6-declarative qt6-multimedia sdl2 vulkan-validation-layers ``` -**Note**: The `shadps4-git` AUR package is not maintained by any of the developers, and it uses GCC as the compiler as opposed to Clang. Use at your own discretion. -#### OpenSUSE +**Note**: The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion.#### OpenSUSE ``` sudo zypper install clang git cmake libasound2 libpulse-devel libsndio7 libjack-devel openal-soft-devel libopenssl-devel zlib-devel libedit-devel systemd-devel libevdev-devel qt6-base-devel qt6-multimedia-devel qt6-svg-devel qt6-linguist-devel qt6-gui-private-devel vulkan-devel vulkan-validationlayers From 0ebe817a28afc14511578d183f8ef2afce3905aa Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 23 Jan 2025 23:46:15 +0200 Subject: [PATCH 28/68] Update building-linux.md --- documents/building-linux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documents/building-linux.md b/documents/building-linux.md index eedcd6664..2342e9afc 100644 --- a/documents/building-linux.md +++ b/documents/building-linux.md @@ -29,7 +29,7 @@ sudo dnf install clang git cmake libatomic alsa-lib-devel pipewire-jack-audio-co sudo pacman -S base-devel clang git cmake sndio jack2 openal qt6-base qt6-declarative qt6-multimedia sdl2 vulkan-validation-layers ``` -**Note**: The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion.#### OpenSUSE +**Note** : The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion.#### OpenSUSE ``` sudo zypper install clang git cmake libasound2 libpulse-devel libsndio7 libjack-devel openal-soft-devel libopenssl-devel zlib-devel libedit-devel systemd-devel libevdev-devel qt6-base-devel qt6-multimedia-devel qt6-svg-devel qt6-linguist-devel qt6-gui-private-devel vulkan-devel vulkan-validationlayers From cc4ddd28c3f3cfe978a0be75131a99ca4e86851b Mon Sep 17 00:00:00 2001 From: Log3rinioo <80830849+Log3rinioo@users.noreply.github.com> Date: Thu, 23 Jan 2025 22:56:06 +0100 Subject: [PATCH 29/68] Add missing Polish translations and fix typos (#2222) --- src/qt_gui/translations/pl_PL.ts | 86 ++++++++++++++++---------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 528e88337..85eb63bfb 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -52,7 +52,7 @@ Select which directory you want to install to. - Select which directory you want to install to. + Wybierz katalog, do którego chcesz zainstalować. @@ -130,35 +130,35 @@ Delete... - Delete... + Usuń... Delete Game - Delete Game + Usuń Grę Delete Update - Delete Update + Usuń Aktualizację Delete DLC - Delete DLC + Usuń DLC Compatibility... - Compatibility... + kompatybilność... Update database - Update database + Zaktualizuj bazę danych View report - View report + Wyświetl zgłoszenie Submit a report - Submit a report + Wyślij zgłoszenie Shortcut creation @@ -182,23 +182,23 @@ Game - Game + Gra requiresEnableSeparateUpdateFolder_MSG - This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + Ta funkcja wymaga do działania opcji „Włącz oddzielny folder aktualizacji”. Jeśli chcesz korzystać z tej funkcji, włącz ją. This game has no update to delete! - This game has no update to delete! + Ta gra nie ma aktualizacji do usunięcia! Update - Update + Aktualizacja This game has no DLC to delete! - This game has no DLC to delete! + Ta gra nie ma DLC do usunięcia! DLC @@ -206,11 +206,11 @@ Delete %1 - Delete %1 + Usuń %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + Czy na pewno chcesz usunąć katalog %1 z %2? @@ -249,7 +249,7 @@ Open shadPS4 Folder - Open shadPS4 Folder + Otwórz folder shadPS4 Exit @@ -546,7 +546,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + Włącz oddzielny folder aktualizacji Default tab when opening settings @@ -574,11 +574,11 @@ Trophy Key - Trophy Key + Klucz trofeów Trophy - Trophy + Trofeum Logger @@ -722,7 +722,7 @@ Disable Trophy Pop-ups - Disable Trophy Pop-ups + Wyłącz wyskakujące okienka trofeów Play title music @@ -730,19 +730,19 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + Aktualizuj bazę danych zgodności podczas uruchamiania Game Compatibility - Game Compatibility + Kompatybilność gier Display Compatibility Data - Display Compatibility Data + Wyświetl dane zgodności Update Compatibility Database - Update Compatibility Database + Aktualizuj bazę danych zgodności Volume @@ -750,7 +750,7 @@ Audio Backend - Audio Backend + Zaplecze audio Save @@ -786,7 +786,7 @@ separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + Włącz oddzielny folder aktualizacji:\nUmożliwia instalowanie aktualizacji gier w oddzielnym folderze w celu łatwego zarządzania. showSplashCheckBox @@ -798,7 +798,7 @@ discordRPCCheckbox - Włącz Discord Rich Presence:\nWyświetla ikonę emuladora i odpowiednie informacje na twoim profilu Discord. + Włącz Discord Rich Presence:\nWyświetla ikonę emulatora i odpowiednie informacje na twoim profilu Discord. userName @@ -806,7 +806,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + Klucz trofeów:\nKlucz używany do odszyfrowywania trofeów. Musi być uzyskany z konsoli po jailbreaku. Musi zawierać tylko znaki w kodzie szesnastkowym. logTypeGroupBox @@ -826,7 +826,7 @@ disableTrophycheckBox - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + Wyłącz wyskakujące okienka trofeów:\nWyłącz powiadomienia o trofeach w grze. Postępy w zdobywaniu trofeów można nadal śledzić za pomocą przeglądarki trofeów (kliknij prawym przyciskiem myszy grę w oknie głównym). hideCursorGroupBox @@ -842,15 +842,15 @@ enableCompatibilityCheckBox - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + Wyświetl dane zgodności:\nWyświetla informacje o kompatybilności gry w widoku tabeli. Włącz opcję „Aktualizuj zgodność przy uruchomieniu”, aby uzyskać aktualne informacje. checkCompatibilityOnStartupCheckBox - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + Aktualizuj zgodność przy uruchomieniu:\nAutomatycznie aktualizuj bazę danych kompatybilności podczas uruchamiania shadPS4. updateCompatibilityButton - Update Compatibility Database:\nImmediately update the compatibility database. + Zaktualizuj bazę danych zgodności:\nNatychmiast zaktualizuj bazę danych zgodności. Never @@ -933,7 +933,7 @@ CheatsPatches Cheats / Patches for - Cheats / Patches for + Kody / Łatki dla defaultTextEdit_MSG @@ -1145,7 +1145,7 @@ Failed to parse JSON: - Nie udało się przeanlizować JSON: + Nie udało się przeanalizować JSON: Can't apply cheats before the game is started @@ -1168,7 +1168,7 @@ Compatibility - Compatibility + Zgodność Region @@ -1196,7 +1196,7 @@ Never Played - Never Played + Nigdy nie grane h @@ -1212,27 +1212,27 @@ Compatibility is untested - Compatibility is untested + Kompatybilność nie została przetestowana Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + Gra nie inicjuje się poprawnie / zawiesza się emulator Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + Gra uruchamia się, ale wyświetla tylko pusty ekran Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + Gra wyświetla obraz, ale nie przechodzi do menu Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + Gra ma usterki przerywające rozgrywkę lub niegrywalną wydajność Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + Grę można ukończyć z grywalną wydajnością i bez większych usterek From e652369f22fc7cbdd456ce338dd1dae0974c69a4 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 23 Jan 2025 23:58:43 +0200 Subject: [PATCH 30/68] sdl3 update (#2221) --- externals/MoltenVK/MoltenVK | 2 +- externals/sdl3 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK index 9f0b616d9..2473ce6f0 160000 --- a/externals/MoltenVK/MoltenVK +++ b/externals/MoltenVK/MoltenVK @@ -1 +1 @@ -Subproject commit 9f0b616d9e2c39464d2a859b79dbc655c4a30e7e +Subproject commit 2473ce6f0ab7d5d8a49aa91b2e37f3447a939f18 diff --git a/externals/sdl3 b/externals/sdl3 index 22422f774..a336b62d8 160000 --- a/externals/sdl3 +++ b/externals/sdl3 @@ -1 +1 @@ -Subproject commit 22422f7748d5128135995ed34c8f8012861c7332 +Subproject commit a336b62d8b0b97b09214e053203e442e2b6e2be5 From a8a779c79b48f020a1432274114f1ddaff0b1dec Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Fri, 24 Jan 2025 05:11:48 -0300 Subject: [PATCH 31/68] Fix AutoUpdate Changelog (#2224) --- src/qt_gui/check_update.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp index e3e019144..0c1cce5da 100644 --- a/src/qt_gui/check_update.cpp +++ b/src/qt_gui/check_update.cpp @@ -146,14 +146,14 @@ void CheckUpdate::CheckForUpdates(const bool showMessage) { } QString currentRev = (updateChannel == "Nightly") - ? QString::fromStdString(Common::g_scm_rev).left(7) + ? QString::fromStdString(Common::g_scm_rev) : "v." + QString::fromStdString(Common::VERSION); QString currentDate = Common::g_scm_date; QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate); latestDate = dateTime.isValid() ? dateTime.toString("yyyy-MM-dd HH:mm:ss") : "Unknown date"; - if (latestRev == currentRev) { + if (latestRev == currentRev.left(7)) { if (showMessage) { QMessageBox::information(this, tr("Auto Updater"), tr("Your version is already up to date!")); @@ -190,7 +190,7 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate, QString("


" + tr("Update Channel") + ":
" + updateChannel + "
" + tr("Current Version") + ": %1 (%2)
" + tr("Latest Version") + ": %3 (%4)

" + tr("Do you want to update?") + "

") - .arg(currentRev, currentDate, latestRev, latestDate); + .arg(currentRev.left(7), currentDate, latestRev, latestDate); QLabel* updateLabel = new QLabel(updateText, this); layout->addWidget(updateLabel); From 91444a05453ba1468b29c575d844ae173cc7e738 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:21:32 -0800 Subject: [PATCH 32/68] liverpool: Fix tiled check for color buffer. (#2227) --- src/video_core/amdgpu/liverpool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index a29bde4ce..ec9c92ef1 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -904,7 +904,7 @@ struct Liverpool { } bool IsTiled() const { - return !info.linear_general; + return GetTilingMode() != TilingMode::Display_Linear; } [[nodiscard]] DataFormat GetDataFmt() const { From 74710116f6bdd805c39ffe7fff4be017df3ac910 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:21:56 -0800 Subject: [PATCH 33/68] renderer_vulkan: Remove dead code. (#2228) --- CMakeLists.txt | 2 - .../vk_descriptor_update_queue.cpp | 108 ------------------ .../vk_descriptor_update_queue.h | 51 --------- 3 files changed, 161 deletions(-) delete mode 100644 src/video_core/renderer_vulkan/vk_descriptor_update_queue.cpp delete mode 100644 src/video_core/renderer_vulkan/vk_descriptor_update_queue.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 131809c8e..3db142be7 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -751,8 +751,6 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp src/video_core/renderer_vulkan/vk_common.h src/video_core/renderer_vulkan/vk_compute_pipeline.cpp src/video_core/renderer_vulkan/vk_compute_pipeline.h - src/video_core/renderer_vulkan/vk_descriptor_update_queue.cpp - src/video_core/renderer_vulkan/vk_descriptor_update_queue.h src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp src/video_core/renderer_vulkan/vk_graphics_pipeline.h src/video_core/renderer_vulkan/vk_instance.cpp diff --git a/src/video_core/renderer_vulkan/vk_descriptor_update_queue.cpp b/src/video_core/renderer_vulkan/vk_descriptor_update_queue.cpp deleted file mode 100644 index 7699bea9d..000000000 --- a/src/video_core/renderer_vulkan/vk_descriptor_update_queue.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "video_core/renderer_vulkan/vk_descriptor_update_queue.h" -#include "video_core/renderer_vulkan/vk_instance.h" - -namespace Vulkan { - -DescriptorUpdateQueue::DescriptorUpdateQueue(const Instance& instance, u32 descriptor_write_max_) - : device{instance.GetDevice()}, descriptor_write_max{descriptor_write_max_} { - descriptor_infos = std::make_unique(descriptor_write_max); - descriptor_writes = std::make_unique(descriptor_write_max); -} - -void DescriptorUpdateQueue::Flush() { - if (descriptor_write_end == 0) { - return; - } - device.updateDescriptorSets({std::span(descriptor_writes.get(), descriptor_write_end)}, {}); - descriptor_write_end = 0; -} - -void DescriptorUpdateQueue::AddStorageImage(vk::DescriptorSet target, u8 binding, - vk::ImageView image_view, - vk::ImageLayout image_layout) { - if (descriptor_write_end >= descriptor_write_max) [[unlikely]] { - Flush(); - } - - auto& image_info = descriptor_infos[descriptor_write_end].image_info; - image_info.sampler = VK_NULL_HANDLE; - image_info.imageView = image_view; - image_info.imageLayout = image_layout; - - descriptor_writes[descriptor_write_end++] = vk::WriteDescriptorSet{ - .dstSet = target, - .dstBinding = binding, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eStorageImage, - .pImageInfo = &image_info, - }; -} - -void DescriptorUpdateQueue::AddImageSampler(vk::DescriptorSet target, u8 binding, u8 array_index, - vk::ImageView image_view, vk::Sampler sampler, - vk::ImageLayout image_layout) { - if (descriptor_write_end >= descriptor_write_max) [[unlikely]] { - Flush(); - } - - auto& image_info = descriptor_infos[descriptor_write_end].image_info; - image_info.sampler = sampler; - image_info.imageView = image_view; - image_info.imageLayout = image_layout; - - descriptor_writes[descriptor_write_end++] = vk::WriteDescriptorSet{ - .dstSet = target, - .dstBinding = binding, - .dstArrayElement = array_index, - .descriptorCount = 1, - .descriptorType = - sampler ? vk::DescriptorType::eCombinedImageSampler : vk::DescriptorType::eSampledImage, - .pImageInfo = &image_info, - }; -} - -void DescriptorUpdateQueue::AddBuffer(vk::DescriptorSet target, u8 binding, vk::Buffer buffer, - vk::DeviceSize offset, vk::DeviceSize size, - vk::DescriptorType type) { - if (descriptor_write_end >= descriptor_write_max) [[unlikely]] { - Flush(); - } - - auto& buffer_info = descriptor_infos[descriptor_write_end].buffer_info; - buffer_info.buffer = buffer; - buffer_info.offset = offset; - buffer_info.range = size; - - descriptor_writes[descriptor_write_end++] = vk::WriteDescriptorSet{ - .dstSet = target, - .dstBinding = binding, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = type, - .pBufferInfo = &buffer_info, - }; -} - -void DescriptorUpdateQueue::AddTexelBuffer(vk::DescriptorSet target, u8 binding, - vk::BufferView buffer_view) { - if (descriptor_write_end >= descriptor_write_max) [[unlikely]] { - Flush(); - } - - auto& buffer_info = descriptor_infos[descriptor_write_end].buffer_view; - buffer_info = buffer_view; - descriptor_writes[descriptor_write_end++] = vk::WriteDescriptorSet{ - .dstSet = target, - .dstBinding = binding, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eUniformTexelBuffer, - .pTexelBufferView = &buffer_info, - }; -} - -} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_descriptor_update_queue.h b/src/video_core/renderer_vulkan/vk_descriptor_update_queue.h deleted file mode 100644 index 9e864db6e..000000000 --- a/src/video_core/renderer_vulkan/vk_descriptor_update_queue.h +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#include "common/types.h" -#include "video_core/renderer_vulkan/vk_common.h" - -namespace Vulkan { - -class Instance; - -struct DescriptorInfoUnion { - DescriptorInfoUnion() {} - - union { - vk::DescriptorImageInfo image_info; - vk::DescriptorBufferInfo buffer_info; - vk::BufferView buffer_view; - }; -}; - -class DescriptorUpdateQueue { -public: - explicit DescriptorUpdateQueue(const Instance& instance, u32 descriptor_write_max = 2048); - ~DescriptorUpdateQueue() = default; - - void Flush(); - - void AddStorageImage(vk::DescriptorSet target, u8 binding, vk::ImageView image_view, - vk::ImageLayout image_layout = vk::ImageLayout::eGeneral); - - void AddImageSampler(vk::DescriptorSet target, u8 binding, u8 array_index, - vk::ImageView image_view, vk::Sampler sampler, - vk::ImageLayout imageLayout = vk::ImageLayout::eGeneral); - - void AddBuffer(vk::DescriptorSet target, u8 binding, vk::Buffer buffer, vk::DeviceSize offset, - vk::DeviceSize size = VK_WHOLE_SIZE, - vk::DescriptorType type = vk::DescriptorType::eUniformBufferDynamic); - - void AddTexelBuffer(vk::DescriptorSet target, u8 binding, vk::BufferView buffer_view); - -private: - const vk::Device device; - const u32 descriptor_write_max; - std::unique_ptr descriptor_infos; - std::unique_ptr descriptor_writes; - u32 descriptor_write_end = 0; -}; - -} // namespace Vulkan From d1b9a5adcceb0c61e03293db9840ca6844442398 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:23:18 -0800 Subject: [PATCH 34/68] texture_cache: Do not overwrite overlap hit with a miss. (#2217) --- src/video_core/texture_cache/texture_cache.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 04711539c..e995b10b2 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -345,8 +345,13 @@ ImageId TextureCache::FindImage(BaseDesc& desc, FindFlags flags) { view_slice = -1; const auto& merged_info = image_id ? slot_images[image_id].info : info; - std::tie(image_id, view_mip, view_slice) = + auto [overlap_image_id, overlap_view_mip, overlap_view_slice] = ResolveOverlap(merged_info, desc.type, cache_id, image_id); + if (overlap_image_id) { + image_id = overlap_image_id; + view_mip = overlap_view_mip; + view_slice = overlap_view_slice; + } } } From 481f420a892e02ac30bfe3e4966368900c69ead2 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 24 Jan 2025 10:28:14 +0200 Subject: [PATCH 35/68] Update building-linux.md --- documents/building-linux.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/documents/building-linux.md b/documents/building-linux.md index 2342e9afc..4aa66aac6 100644 --- a/documents/building-linux.md +++ b/documents/building-linux.md @@ -29,7 +29,9 @@ sudo dnf install clang git cmake libatomic alsa-lib-devel pipewire-jack-audio-co sudo pacman -S base-devel clang git cmake sndio jack2 openal qt6-base qt6-declarative qt6-multimedia sdl2 vulkan-validation-layers ``` -**Note** : The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion.#### OpenSUSE +**Note** : The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion. + +#### OpenSUSE ``` sudo zypper install clang git cmake libasound2 libpulse-devel libsndio7 libjack-devel openal-soft-devel libopenssl-devel zlib-devel libedit-devel systemd-devel libevdev-devel qt6-base-devel qt6-multimedia-devel qt6-svg-devel qt6-linguist-devel qt6-gui-private-devel vulkan-devel vulkan-validationlayers @@ -48,6 +50,7 @@ distrobox create --name archlinux --init --image archlinux:latest and install the dependencies on that container as cited above. This option is **highly recommended** for NixOS and distributions with immutable/atomic filesystems (example: Fedora Kinoite, SteamOS). + ### Cloning ``` From 0f69697acb7504e57abe2b15238b2c92414a0d8f Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:48:39 -0800 Subject: [PATCH 36/68] documents: Update CPU requirements. (#2229) --- documents/Quickstart/Quickstart.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/documents/Quickstart/Quickstart.md b/documents/Quickstart/Quickstart.md index b2931e51e..2f2751887 100644 --- a/documents/Quickstart/Quickstart.md +++ b/documents/Quickstart/Quickstart.md @@ -22,7 +22,10 @@ SPDX-License-Identifier: GPL-2.0-or-later - A processor with at least 4 cores and 6 threads - Above 2.5 GHz frequency -- required support AVX2 extension or Rosetta 2 on ARM +- A CPU supporting the following instruction sets: MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, F16C, CLMUL, AES, BMI1, MOVBE, XSAVE, ABM + - **Intel**: Haswell generation or newer + - **AMD**: Jaguar generation or newer + - **Apple**: Rosetta 2 on macOS 15 or newer ### GPU From 9dcf40e261c6775a997b92d26a4bd8113821c6ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Fri, 24 Jan 2025 11:07:36 +0000 Subject: [PATCH 37/68] Handle more 64bit shifts in Translator (#1825) --- .../frontend/translate/scalar_alu.cpp | 22 ++++++++++++++++++- .../frontend/translate/translate.h | 2 ++ .../frontend/translate/vector_alu.cpp | 4 ++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index 7f34126f5..b1b260fde 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -72,10 +72,14 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { return S_OR_B64(NegateMode::Result, true, inst); case Opcode::S_LSHL_B32: return S_LSHL_B32(inst); + case Opcode::S_LSHL_B64: + return S_LSHL_B64(inst); case Opcode::S_LSHR_B32: return S_LSHR_B32(inst); case Opcode::S_ASHR_I32: return S_ASHR_I32(inst); + case Opcode::S_ASHR_I64: + return S_ASHR_I64(inst); case Opcode::S_BFM_B32: return S_BFM_B32(inst); case Opcode::S_MUL_I32: @@ -420,6 +424,14 @@ void Translator::S_LSHL_B32(const GcnInst& inst) { ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); } +void Translator::S_LSHL_B64(const GcnInst& inst) { + const IR::U64 src0{GetSrc64(inst.src[0])}; + const IR::U64 src1{GetSrc64(inst.src[1])}; + const IR::U64 result = ir.ShiftLeftLogical(src0, ir.BitwiseAnd(src1, ir.Imm64(u64(0x3F)))); + SetDst64(inst.dst[0], result); + ir.SetScc(ir.INotEqual(result, ir.Imm64(u64(0)))); +} + void Translator::S_LSHR_B32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; @@ -431,11 +443,19 @@ void Translator::S_LSHR_B32(const GcnInst& inst) { void Translator::S_ASHR_I32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; - const IR::U32 result{ir.ShiftRightArithmetic(src0, src1)}; + const IR::U32 result{ir.ShiftRightArithmetic(src0, ir.BitwiseAnd(src1, ir.Imm32(0x1F)))}; SetDst(inst.dst[0], result); ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); } +void Translator::S_ASHR_I64(const GcnInst& inst) { + const IR::U64 src0{GetSrc64(inst.src[0])}; + const IR::U64 src1{GetSrc64(inst.src[1])}; + const IR::U64 result{ir.ShiftRightArithmetic(src0, ir.BitwiseAnd(src1, ir.Imm64(u64(0x3F))))}; + SetDst64(inst.dst[0], result); + ir.SetScc(ir.INotEqual(result, ir.Imm64(u64(0)))); +} + void Translator::S_BFM_B32(const GcnInst& inst) { const IR::U32 src0{ir.BitwiseAnd(GetSrc(inst.src[0]), ir.Imm32(0x1F))}; const IR::U32 src1{ir.BitwiseAnd(GetSrc(inst.src[1]), ir.Imm32(0x1F))}; diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index bef61f997..496455b50 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -90,8 +90,10 @@ public: void S_OR_B64(NegateMode negate, bool is_xor, const GcnInst& inst); void S_XOR_B32(const GcnInst& inst); void S_LSHL_B32(const GcnInst& inst); + void S_LSHL_B64(const GcnInst& inst); void S_LSHR_B32(const GcnInst& inst); void S_ASHR_I32(const GcnInst& inst); + void S_ASHR_I64(const GcnInst& inst); void S_BFM_B32(const GcnInst& inst); void S_MUL_I32(const GcnInst& inst); void S_BFE(const GcnInst& inst, bool is_signed); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index b2863f6a8..ac72293e4 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -1273,6 +1273,10 @@ void Translator::V_LSHL_B64(const GcnInst& inst) { ir.SetVectorReg(dst_reg + 1, ir.Imm32(static_cast(result >> 32))); return; } + + const IR::U64 result = ir.ShiftLeftLogical(src0, ir.BitwiseAnd(src1, ir.Imm64(u64(0x3F)))); + SetDst64(inst.dst[0], result); + return; } UNREACHABLE_MSG("Unimplemented V_LSHL_B64 arguments"); } From 4d12de8149cce521916f7752a0830430170144db Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 03:11:13 -0800 Subject: [PATCH 38/68] hotfix: 64-bit shift fixups --- .../backend/spirv/emit_spirv_instructions.h | 3 ++- .../backend/spirv/emit_spirv_integer.cpp | 6 ++++- .../frontend/translate/vector_alu.cpp | 23 +------------------ src/shader_recompiler/ir/ir_emitter.cpp | 14 +++++++++-- src/shader_recompiler/ir/ir_emitter.h | 2 +- src/shader_recompiler/ir/opcodes.inc | 3 ++- .../ir/passes/constant_propagation_pass.cpp | 5 +++- 7 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index f0bb9fd7e..4833dc9d0 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -348,7 +348,8 @@ Id EmitSLessThanEqual(EmitContext& ctx, Id lhs, Id rhs); Id EmitULessThanEqual(EmitContext& ctx, Id lhs, Id rhs); Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs); Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs); -Id EmitINotEqual(EmitContext& ctx, Id lhs, Id rhs); +Id EmitINotEqual32(EmitContext& ctx, Id lhs, Id rhs); +Id EmitINotEqual64(EmitContext& ctx, Id lhs, Id rhs); Id EmitSGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs); Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs); Id EmitLogicalOr(EmitContext& ctx, Id a, Id b); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp index 70411ecec..e2d702389 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp @@ -324,7 +324,11 @@ Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs) { return ctx.OpUGreaterThan(ctx.U1[1], lhs, rhs); } -Id EmitINotEqual(EmitContext& ctx, Id lhs, Id rhs) { +Id EmitINotEqual32(EmitContext& ctx, Id lhs, Id rhs) { + return ctx.OpINotEqual(ctx.U1[1], lhs, rhs); +} + +Id EmitINotEqual64(EmitContext& ctx, Id lhs, Id rhs) { return ctx.OpINotEqual(ctx.U1[1], lhs, rhs); } diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index ac72293e4..42dbcc513 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -1257,28 +1257,7 @@ void Translator::V_CVT_PK_U8_F32(const GcnInst& inst) { void Translator::V_LSHL_B64(const GcnInst& inst) { const IR::U64 src0{GetSrc64(inst.src[0])}; const IR::U64 src1{GetSrc64(inst.src[1])}; - const IR::VectorReg dst_reg{inst.dst[0].code}; - if (src0.IsImmediate()) { - if (src0.U64() == -1) { - // If src0 is a fixed -1, the result will always be -1. - ir.SetVectorReg(dst_reg, ir.Imm32(0xFFFFFFFF)); - ir.SetVectorReg(dst_reg + 1, ir.Imm32(0xFFFFFFFF)); - return; - } - if (src1.IsImmediate()) { - // If both src0 and src1 are immediates, we can calculate the result now. - // Note that according to the manual, only bits 4:0 are used from src1. - const u64 result = src0.U64() << (src1.U64() & 0x1F); - ir.SetVectorReg(dst_reg, ir.Imm32(static_cast(result))); - ir.SetVectorReg(dst_reg + 1, ir.Imm32(static_cast(result >> 32))); - return; - } - - const IR::U64 result = ir.ShiftLeftLogical(src0, ir.BitwiseAnd(src1, ir.Imm64(u64(0x3F)))); - SetDst64(inst.dst[0], result); - return; - } - UNREACHABLE_MSG("Unimplemented V_LSHL_B64 arguments"); + SetDst64(inst.dst[0], ir.ShiftLeftLogical(src0, ir.BitwiseAnd(src1, ir.Imm64(u64(0x3F))))); } void Translator::V_MUL_F64(const GcnInst& inst) { diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 5ac08e7dc..f0558665b 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -1461,8 +1461,18 @@ U1 IREmitter::IGreaterThan(const U32& lhs, const U32& rhs, bool is_signed) { return Inst(is_signed ? Opcode::SGreaterThan : Opcode::UGreaterThan, lhs, rhs); } -U1 IREmitter::INotEqual(const U32& lhs, const U32& rhs) { - return Inst(Opcode::INotEqual, lhs, rhs); +U1 IREmitter::INotEqual(const U32U64& lhs, const U32U64& rhs) { + if (lhs.Type() != rhs.Type()) { + UNREACHABLE_MSG("Mismatching types {} and {}", lhs.Type(), rhs.Type()); + } + switch (lhs.Type()) { + case Type::U32: + return Inst(Opcode::INotEqual32, lhs, rhs); + case Type::U64: + return Inst(Opcode::INotEqual64, lhs, rhs); + default: + ThrowInvalidType(lhs.Type()); + } } U1 IREmitter::IGreaterThanEqual(const U32& lhs, const U32& rhs, bool is_signed) { diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index d1dc44d74..5dd49ce7a 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -258,7 +258,7 @@ public: [[nodiscard]] U1 IEqual(const U32U64& lhs, const U32U64& rhs); [[nodiscard]] U1 ILessThanEqual(const U32& lhs, const U32& rhs, bool is_signed); [[nodiscard]] U1 IGreaterThan(const U32& lhs, const U32& rhs, bool is_signed); - [[nodiscard]] U1 INotEqual(const U32& lhs, const U32& rhs); + [[nodiscard]] U1 INotEqual(const U32U64& lhs, const U32U64& rhs); [[nodiscard]] U1 IGreaterThanEqual(const U32& lhs, const U32& rhs, bool is_signed); [[nodiscard]] U1 LogicalOr(const U1& a, const U1& b); diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index b45151dba..63a4e1e62 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -321,7 +321,8 @@ OPCODE(SLessThanEqual, U1, U32, OPCODE(ULessThanEqual, U1, U32, U32, ) OPCODE(SGreaterThan, U1, U32, U32, ) OPCODE(UGreaterThan, U1, U32, U32, ) -OPCODE(INotEqual, U1, U32, U32, ) +OPCODE(INotEqual32, U1, U32, U32, ) +OPCODE(INotEqual64, U1, U64, U64, ) OPCODE(SGreaterThanEqual, U1, U32, U32, ) OPCODE(UGreaterThanEqual, U1, U32, U32, ) diff --git a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp index 26d819d8e..12a1b56e9 100644 --- a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp @@ -403,9 +403,12 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) { case IR::Opcode::IEqual64: FoldWhenAllImmediates(inst, [](u64 a, u64 b) { return a == b; }); return; - case IR::Opcode::INotEqual: + case IR::Opcode::INotEqual32: FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a != b; }); return; + case IR::Opcode::INotEqual64: + FoldWhenAllImmediates(inst, [](u64 a, u64 b) { return a != b; }); + return; case IR::Opcode::BitwiseAnd32: FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a & b; }); return; From 4f426b723f2f015af022e2f9c48813448c264d7b Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 24 Jan 2025 14:30:55 +0100 Subject: [PATCH 39/68] Rebase of "Handle munmap over multiple VMAs" (#2233) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Unmap memory in chunks if spanning over multiple VMAs * clang * Merge fixups * Minor code style changes * Update function declarations --------- Co-authored-by: Marcin Mikołajczyk --- src/core/memory.cpp | 49 +++++++++++++++++++++++++++++---------------- src/core/memory.h | 4 +++- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index f90d4e6aa..a8dd72acc 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -389,32 +389,29 @@ s32 MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) { return UnmapMemoryImpl(virtual_addr, size); } -s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) { - const auto it = FindVMA(virtual_addr); - const auto& vma_base = it->second; - ASSERT_MSG(vma_base.Contains(virtual_addr, size), - "Existing mapping does not contain requested unmap range"); - - const auto type = vma_base.type; - if (type == VMAType::Free) { - return ORBIS_OK; - } - +u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma_base, u64 size) { const auto vma_base_addr = vma_base.base; const auto vma_base_size = vma_base.size; + const auto type = vma_base.type; const auto phys_base = vma_base.phys_base; const bool is_exec = vma_base.is_exec; const auto start_in_vma = virtual_addr - vma_base_addr; + const auto adjusted_size = + vma_base_size - start_in_vma < size ? vma_base_size - start_in_vma : size; const bool has_backing = type == VMAType::Direct || type == VMAType::File; + + if (type == VMAType::Free) { + return adjusted_size; + } if (type == VMAType::Direct || type == VMAType::Pooled) { - rasterizer->UnmapMemory(virtual_addr, size); + rasterizer->UnmapMemory(virtual_addr, adjusted_size); } if (type == VMAType::Flexible) { - flexible_usage -= size; + flexible_usage -= adjusted_size; } // Mark region as free and attempt to coalesce it with neighbours. - const auto new_it = CarveVMA(virtual_addr, size); + const auto new_it = CarveVMA(virtual_addr, adjusted_size); auto& vma = new_it->second; vma.type = VMAType::Free; vma.prot = MemoryProt::NoAccess; @@ -423,13 +420,25 @@ s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) { vma.name = ""; MergeAdjacent(vma_map, new_it); bool readonly_file = vma.prot == MemoryProt::CpuRead && type == VMAType::File; - if (type != VMAType::Reserved && type != VMAType::PoolReserved) { // Unmap the memory region. - impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, - is_exec, has_backing, readonly_file); + impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + adjusted_size, + phys_base, is_exec, has_backing, readonly_file); TRACK_FREE(virtual_addr, "VMEM"); } + return adjusted_size; +} + +s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, u64 size) { + u64 unmapped_bytes = 0; + do { + auto it = FindVMA(virtual_addr + unmapped_bytes); + auto& vma_base = it->second; + auto unmapped = + UnmapBytesFromEntry(virtual_addr + unmapped_bytes, vma_base, size - unmapped_bytes); + ASSERT_MSG(unmapped > 0, "Failed to unmap memory, progress is impossible"); + unmapped_bytes += unmapped; + } while (unmapped_bytes < size); return ORBIS_OK; } @@ -651,6 +660,12 @@ MemoryManager::VMAHandle MemoryManager::CarveVMA(VAddr virtual_addr, size_t size const VAddr start_in_vma = virtual_addr - vma.base; const VAddr end_in_vma = start_in_vma + size; + + if (start_in_vma == 0 && size == vma.size) { + // if requsting the whole VMA, return it + return vma_handle; + } + ASSERT_MSG(end_in_vma <= vma.size, "Mapping cannot fit inside free region"); if (end_in_vma != vma.size) { diff --git a/src/core/memory.h b/src/core/memory.h index 615ecc3eb..59e48b248 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -252,7 +252,9 @@ private: DMemHandle Split(DMemHandle dmem_handle, size_t offset_in_area); - s32 UnmapMemoryImpl(VAddr virtual_addr, size_t size); + u64 UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma_base, u64 size); + + s32 UnmapMemoryImpl(VAddr virtual_addr, u64 size); private: AddressSpace impl; From a4eba8e827c062b5e6874255ff810346943d0ea2 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 24 Jan 2025 19:22:06 +0200 Subject: [PATCH 40/68] stubbed webbrowserdialog,npparty (#2234) * stubbed webbrowserdialog,npparty * added poly's suggestions --- CMakeLists.txt | 4 + src/common/logging/filter.cpp | 2 + src/common/logging/types.h | 2 + src/core/libraries/libs.cpp | 4 + src/core/libraries/np_party/np_party.cpp | 195 ++++++++++++++++++ src/core/libraries/np_party/np_party.h | 44 ++++ .../web_browser_dialog/webbrowserdialog.cpp | 112 ++++++++++ .../web_browser_dialog/webbrowserdialog.h | 30 +++ 8 files changed, 393 insertions(+) create mode 100644 src/core/libraries/np_party/np_party.cpp create mode 100644 src/core/libraries/np_party/np_party.h create mode 100644 src/core/libraries/web_browser_dialog/webbrowserdialog.cpp create mode 100644 src/core/libraries/web_browser_dialog/webbrowserdialog.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3db142be7..4c9dad307 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -344,6 +344,8 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/razor_cpu/razor_cpu.h src/core/libraries/mouse/mouse.cpp src/core/libraries/mouse/mouse.h + src/core/libraries/web_browser_dialog/webbrowserdialog.cpp + src/core/libraries/web_browser_dialog/webbrowserdialog.h ) set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h @@ -434,6 +436,8 @@ set(NP_LIBS src/core/libraries/np_common/np_common.cpp src/core/libraries/np_trophy/np_trophy_error.h src/core/libraries/np_web_api/np_web_api.cpp src/core/libraries/np_web_api/np_web_api.h + src/core/libraries/np_party/np_party.cpp + src/core/libraries/np_party/np_party.h ) set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 168d03948..f1d3a9499 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -131,6 +131,8 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Videodec) \ SUB(Lib, RazorCpu) \ SUB(Lib, Mouse) \ + SUB(Lib, WebBrowserDialog) \ + SUB(Lib, NpParty) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 4ca88e1be..d5530312c 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -98,6 +98,8 @@ enum class Class : u8 { Lib_Videodec, ///< The LibSceVideodec implementation. Lib_RazorCpu, ///< The LibRazorCpu implementation. Lib_Mouse, ///< The LibSceMouse implementation + Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation + Lib_NpParty, ///< The LibSceNpParty implementation Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 6dc455028..e09de1cee 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -28,6 +28,7 @@ #include "core/libraries/network/ssl2.h" #include "core/libraries/np_common/np_common.h" #include "core/libraries/np_manager/np_manager.h" +#include "core/libraries/np_party/np_party.h" #include "core/libraries/np_score/np_score.h" #include "core/libraries/np_trophy/np_trophy.h" #include "core/libraries/np_web_api/np_web_api.h" @@ -52,6 +53,7 @@ #include "core/libraries/videodec/videodec.h" #include "core/libraries/videodec/videodec2.h" #include "core/libraries/videoout/video_out.h" +#include "core/libraries/web_browser_dialog/webbrowserdialog.h" #include "fiber/fiber.h" #include "jpeg/jpegenc.h" @@ -107,6 +109,8 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Fiber::RegisterlibSceFiber(sym); Libraries::JpegEnc::RegisterlibSceJpegEnc(sym); Libraries::Mouse::RegisterlibSceMouse(sym); + Libraries::WebBrowserDialog::RegisterlibSceWebBrowserDialog(sym); + Libraries::NpParty::RegisterlibSceNpParty(sym); } } // namespace Libraries diff --git a/src/core/libraries/np_party/np_party.cpp b/src/core/libraries/np_party/np_party.cpp new file mode 100644 index 000000000..8a66ccb22 --- /dev/null +++ b/src/core/libraries/np_party/np_party.cpp @@ -0,0 +1,195 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/np_party/np_party.h" + +namespace Libraries::NpParty { + +s32 PS4_SYSV_ABI sceNpPartyCheckCallback() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyCreate() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyCreateA() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetId() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetMemberInfo() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetMemberInfoA() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetMembers() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetMembersA() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetMemberSessionInfo() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetMemberVoiceInfo() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetState() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetStateAsUser() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetStateAsUserA() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyGetVoiceChatPriority() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyInitialize() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyJoin() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyLeave() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyRegisterHandler() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyRegisterHandlerA() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyRegisterPrivateHandler() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartySendBinaryMessage() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartySetVoiceChatPriority() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyShowInvitationList() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyShowInvitationListA() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyTerminate() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpPartyUnregisterPrivateHandler() { + LOG_ERROR(Lib_NpParty, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceNpParty(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("3e4k2mzLkmc", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyCheckCallback); + LIB_FUNCTION("nOZRy-slBoA", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyCreate); + LIB_FUNCTION("XQSUbbnpPBA", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyCreateA); + LIB_FUNCTION("DRA3ay-1DFQ", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyGetId); + LIB_FUNCTION("F1P+-wpxQow", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyGetMemberInfo); + LIB_FUNCTION("v2RYVGrJDkM", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyGetMemberInfoA); + LIB_FUNCTION("T2UOKf00ZN0", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyGetMembers); + LIB_FUNCTION("TaNw7W25QJw", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyGetMembersA); + LIB_FUNCTION("4gOMfNYzllw", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyGetMemberSessionInfo); + LIB_FUNCTION("EKi1jx59SP4", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyGetMemberVoiceInfo); + LIB_FUNCTION("aEzKdJzATZ0", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyGetState); + LIB_FUNCTION("o7grRhiGHYI", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyGetStateAsUser); + LIB_FUNCTION("EjyAI+QNgFw", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyGetStateAsUserA); + LIB_FUNCTION("-lc6XZnQXvM", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyGetVoiceChatPriority); + LIB_FUNCTION("lhYCTQmBkds", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyInitialize); + LIB_FUNCTION("RXNCDw2GDEg", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyJoin); + LIB_FUNCTION("J8jAi-tfJHc", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyLeave); + LIB_FUNCTION("kA88gbv71ao", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyRegisterHandler); + LIB_FUNCTION("+v4fVHMwFWc", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyRegisterHandlerA); + LIB_FUNCTION("zo4G5WWYpKg", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyRegisterPrivateHandler); + LIB_FUNCTION("U6VdUe-PNAY", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartySendBinaryMessage); + LIB_FUNCTION("nazKyHygHhY", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartySetVoiceChatPriority); + LIB_FUNCTION("-MFiL7hEnPE", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyShowInvitationList); + LIB_FUNCTION("yARHEYLajs0", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyShowInvitationListA); + LIB_FUNCTION("oLYkibiHqRA", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyTerminate); + LIB_FUNCTION("zQ7gIvt11Pc", "libSceNpParty", 1, "libSceNpParty", 1, 1, + sceNpPartyUnregisterPrivateHandler); + LIB_FUNCTION("nOZRy-slBoA", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1, sceNpPartyCreate); + LIB_FUNCTION("F1P+-wpxQow", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1, + sceNpPartyGetMemberInfo); + LIB_FUNCTION("T2UOKf00ZN0", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1, + sceNpPartyGetMembers); + LIB_FUNCTION("o7grRhiGHYI", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1, + sceNpPartyGetStateAsUser); + LIB_FUNCTION("kA88gbv71ao", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1, + sceNpPartyRegisterHandler); + LIB_FUNCTION("-MFiL7hEnPE", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1, + sceNpPartyShowInvitationList); +}; + +} // namespace Libraries::NpParty \ No newline at end of file diff --git a/src/core/libraries/np_party/np_party.h b/src/core/libraries/np_party/np_party.h new file mode 100644 index 000000000..d5f20e4f8 --- /dev/null +++ b/src/core/libraries/np_party/np_party.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::NpParty { + +s32 PS4_SYSV_ABI sceNpPartyCheckCallback(); +s32 PS4_SYSV_ABI sceNpPartyCreate(); +s32 PS4_SYSV_ABI sceNpPartyCreateA(); +s32 PS4_SYSV_ABI sceNpPartyGetId(); +s32 PS4_SYSV_ABI sceNpPartyGetMemberInfo(); +s32 PS4_SYSV_ABI sceNpPartyGetMemberInfoA(); +s32 PS4_SYSV_ABI sceNpPartyGetMembers(); +s32 PS4_SYSV_ABI sceNpPartyGetMembersA(); +s32 PS4_SYSV_ABI sceNpPartyGetMemberSessionInfo(); +s32 PS4_SYSV_ABI sceNpPartyGetMemberVoiceInfo(); +s32 PS4_SYSV_ABI sceNpPartyGetState(); +s32 PS4_SYSV_ABI sceNpPartyGetStateAsUser(); +s32 PS4_SYSV_ABI sceNpPartyGetStateAsUserA(); +s32 PS4_SYSV_ABI sceNpPartyGetVoiceChatPriority(); +s32 PS4_SYSV_ABI sceNpPartyInitialize(); +s32 PS4_SYSV_ABI sceNpPartyJoin(); +s32 PS4_SYSV_ABI sceNpPartyLeave(); +s32 PS4_SYSV_ABI sceNpPartyRegisterHandler(); +s32 PS4_SYSV_ABI sceNpPartyRegisterHandlerA(); +s32 PS4_SYSV_ABI sceNpPartyRegisterPrivateHandler(); +s32 PS4_SYSV_ABI sceNpPartySendBinaryMessage(); +s32 PS4_SYSV_ABI sceNpPartySetVoiceChatPriority(); +s32 PS4_SYSV_ABI sceNpPartyShowInvitationList(); +s32 PS4_SYSV_ABI sceNpPartyShowInvitationListA(); +s32 PS4_SYSV_ABI sceNpPartyTerminate(); +s32 PS4_SYSV_ABI sceNpPartyUnregisterPrivateHandler(); +s32 PS4_SYSV_ABI module_start(); +s32 PS4_SYSV_ABI module_stop(); + +void RegisterlibSceNpParty(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::NpParty \ No newline at end of file diff --git a/src/core/libraries/web_browser_dialog/webbrowserdialog.cpp b/src/core/libraries/web_browser_dialog/webbrowserdialog.cpp new file mode 100644 index 000000000..ee434f96a --- /dev/null +++ b/src/core/libraries/web_browser_dialog/webbrowserdialog.cpp @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/web_browser_dialog/webbrowserdialog.h" + +namespace Libraries::WebBrowserDialog { + +s32 PS4_SYSV_ABI sceWebBrowserDialogClose() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogGetEvent() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogGetResult() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogGetStatus() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogInitialize() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogNavigate() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogOpen() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogOpenForPredeterminedContent() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogResetCookie() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogSetCookie() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogSetZoom() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogTerminate() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceWebBrowserDialogUpdateStatus() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_F2BE042771625F8C() { + LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceWebBrowserDialog(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("PSK+Eik919Q", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogClose); + LIB_FUNCTION("Wit4LjeoeX4", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogGetEvent); + LIB_FUNCTION("vCaW0fgVQmc", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogGetResult); + LIB_FUNCTION("CFTG6a8TjOU", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogGetStatus); + LIB_FUNCTION("jqb7HntFQFc", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogInitialize); + LIB_FUNCTION("uYELOMVnmNQ", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogNavigate); + LIB_FUNCTION("FraP7debcdg", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogOpen); + LIB_FUNCTION("O7dIZQrwVFY", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogOpenForPredeterminedContent); + LIB_FUNCTION("Cya+jvTtPqg", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogResetCookie); + LIB_FUNCTION("TZnDVkP91Rg", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogSetCookie); + LIB_FUNCTION("RLhKBOoNyXY", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogSetZoom); + LIB_FUNCTION("ocHtyBwHfys", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogTerminate); + LIB_FUNCTION("h1dR-t5ISgg", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1, + sceWebBrowserDialogUpdateStatus); + LIB_FUNCTION("8r4EJ3FiX4w", "libSceWebBrowserDialogLimited", 1, "libSceWebBrowserDialog", 1, 1, + Func_F2BE042771625F8C); +}; + +} // namespace Libraries::WebBrowserDialog \ No newline at end of file diff --git a/src/core/libraries/web_browser_dialog/webbrowserdialog.h b/src/core/libraries/web_browser_dialog/webbrowserdialog.h new file mode 100644 index 000000000..aa118fe45 --- /dev/null +++ b/src/core/libraries/web_browser_dialog/webbrowserdialog.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::WebBrowserDialog { + +s32 PS4_SYSV_ABI sceWebBrowserDialogClose(); +s32 PS4_SYSV_ABI sceWebBrowserDialogGetEvent(); +s32 PS4_SYSV_ABI sceWebBrowserDialogGetResult(); +s32 PS4_SYSV_ABI sceWebBrowserDialogGetStatus(); +s32 PS4_SYSV_ABI sceWebBrowserDialogInitialize(); +s32 PS4_SYSV_ABI sceWebBrowserDialogNavigate(); +s32 PS4_SYSV_ABI sceWebBrowserDialogOpen(); +s32 PS4_SYSV_ABI sceWebBrowserDialogOpenForPredeterminedContent(); +s32 PS4_SYSV_ABI sceWebBrowserDialogResetCookie(); +s32 PS4_SYSV_ABI sceWebBrowserDialogSetCookie(); +s32 PS4_SYSV_ABI sceWebBrowserDialogSetZoom(); +s32 PS4_SYSV_ABI sceWebBrowserDialogTerminate(); +s32 PS4_SYSV_ABI sceWebBrowserDialogUpdateStatus(); +s32 PS4_SYSV_ABI Func_F2BE042771625F8C(); + +void RegisterlibSceWebBrowserDialog(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::WebBrowserDialog \ No newline at end of file From b3c573f7989ffe3cc0fe15ce69f620bcaab9c61b Mon Sep 17 00:00:00 2001 From: poly <47796739+polybiusproxy@users.noreply.github.com> Date: Fri, 24 Jan 2025 19:37:34 +0100 Subject: [PATCH 41/68] libraries/fiber: print fiber ctx size on stack overflow --- src/core/libraries/fiber/fiber.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp index b77b5b5b6..345f0834d 100644 --- a/src/core/libraries/fiber/fiber.cpp +++ b/src/core/libraries/fiber/fiber.cpp @@ -37,8 +37,9 @@ extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) { void PS4_SYSV_ABI _sceFiberCheckStackOverflow(OrbisFiberContext* ctx) { u64* stack_base = reinterpret_cast(ctx->current_fiber->addr_context); + u64 stack_size = ctx->current_fiber->size_context; if (stack_base && *stack_base != kFiberStackSignature) { - UNREACHABLE_MSG("Stack overflow detected in fiber."); + UNREACHABLE_MSG("Stack overflow detected in fiber with size = 0x{:x}", stack_size); } } From 56f4b8a2b860fa14421b0e6884532b441ea34a12 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 10:41:58 -0800 Subject: [PATCH 42/68] shader_recompiler: Implement shader export formats. (#2226) --- externals/sirit | 2 +- .../spirv/emit_spirv_bitwise_conversion.cpp | 44 +++++ .../backend/spirv/emit_spirv_instructions.h | 8 + .../frontend/translate/export.cpp | 152 ++++++++++++++---- .../frontend/translate/translate.h | 9 ++ .../frontend/translate/vector_alu.cpp | 33 ++-- src/shader_recompiler/ir/ir_emitter.cpp | 32 ++++ src/shader_recompiler/ir/ir_emitter.h | 8 + src/shader_recompiler/ir/opcodes.inc | 8 + .../ir/passes/constant_propagation_pass.cpp | 16 ++ src/shader_recompiler/runtime_info.h | 1 + src/video_core/amdgpu/liverpool.h | 4 + .../renderer_vulkan/vk_graphics_pipeline.h | 5 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 21 ++- 14 files changed, 286 insertions(+), 57 deletions(-) diff --git a/externals/sirit b/externals/sirit index 26ad5a9d0..d6f3c0d99 160000 --- a/externals/sirit +++ b/externals/sirit @@ -1 +1 @@ -Subproject commit 26ad5a9d0fe13260b0d7d6c64419d01a196b2e32 +Subproject commit d6f3c0d99862ab2ff8f95e9ac221560f1f97e29a diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp index 02ac74e19..539c6cb81 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp @@ -58,4 +58,48 @@ Id EmitUnpackHalf2x16(EmitContext& ctx, Id value) { return ctx.OpUnpackHalf2x16(ctx.F32[2], value); } +Id EmitPackUnorm2x16(EmitContext& ctx, Id value) { + return ctx.OpPackUnorm2x16(ctx.U32[1], value); +} + +Id EmitUnpackUnorm2x16(EmitContext& ctx, Id value) { + return ctx.OpUnpackUnorm2x16(ctx.F32[2], value); +} + +Id EmitPackSnorm2x16(EmitContext& ctx, Id value) { + return ctx.OpPackSnorm2x16(ctx.U32[1], value); +} + +Id EmitUnpackSnorm2x16(EmitContext& ctx, Id value) { + return ctx.OpUnpackSnorm2x16(ctx.F32[2], value); +} + +Id EmitPackUint2x16(EmitContext& ctx, Id value) { + // No SPIR-V instruction for this, do it manually. + const auto x{ctx.OpCompositeExtract(ctx.U32[1], value, 0)}; + const auto y{ctx.OpCompositeExtract(ctx.U32[1], value, 1)}; + return ctx.OpBitFieldInsert(ctx.U32[1], x, y, ctx.ConstU32(16U), ctx.ConstU32(16U)); +} + +Id EmitUnpackUint2x16(EmitContext& ctx, Id value) { + // No SPIR-V instruction for this, do it manually. + const auto x{ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.ConstU32(0U), ctx.ConstU32(16U))}; + const auto y{ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.ConstU32(16U), ctx.ConstU32(16U))}; + return ctx.OpCompositeConstruct(ctx.U32[2], x, y); +} + +Id EmitPackSint2x16(EmitContext& ctx, Id value) { + // No SPIR-V instruction for this, do it manually. + const auto x{ctx.OpCompositeExtract(ctx.U32[1], value, 0)}; + const auto y{ctx.OpCompositeExtract(ctx.U32[1], value, 1)}; + return ctx.OpBitFieldInsert(ctx.U32[1], x, y, ctx.ConstU32(16U), ctx.ConstU32(16U)); +} + +Id EmitUnpackSint2x16(EmitContext& ctx, Id value) { + // No SPIR-V instruction for this, do it manually. + const auto x{ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.ConstU32(0U), ctx.ConstU32(16U))}; + const auto y{ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.ConstU32(16U), ctx.ConstU32(16U))}; + return ctx.OpCompositeConstruct(ctx.U32[2], x, y); +} + } // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 4833dc9d0..842b13207 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -197,6 +197,14 @@ Id EmitPackFloat2x16(EmitContext& ctx, Id value); Id EmitUnpackFloat2x16(EmitContext& ctx, Id value); Id EmitPackHalf2x16(EmitContext& ctx, Id value); Id EmitUnpackHalf2x16(EmitContext& ctx, Id value); +Id EmitPackUnorm2x16(EmitContext& ctx, Id value); +Id EmitUnpackUnorm2x16(EmitContext& ctx, Id value); +Id EmitPackSnorm2x16(EmitContext& ctx, Id value); +Id EmitUnpackSnorm2x16(EmitContext& ctx, Id value); +Id EmitPackUint2x16(EmitContext& ctx, Id value); +Id EmitUnpackUint2x16(EmitContext& ctx, Id value); +Id EmitPackSint2x16(EmitContext& ctx, Id value); +Id EmitUnpackSint2x16(EmitContext& ctx, Id value); Id EmitFPAbs16(EmitContext& ctx, Id value); Id EmitFPAbs32(EmitContext& ctx, Id value); Id EmitFPAbs64(EmitContext& ctx, Id value); diff --git a/src/shader_recompiler/frontend/translate/export.cpp b/src/shader_recompiler/frontend/translate/export.cpp index 38ff9ae14..84c2ee658 100644 --- a/src/shader_recompiler/frontend/translate/export.cpp +++ b/src/shader_recompiler/frontend/translate/export.cpp @@ -7,6 +7,125 @@ namespace Shader::Gcn { +u32 SwizzleMrtComponent(const FragmentRuntimeInfo::PsColorBuffer& color_buffer, u32 comp) { + const auto [r, g, b, a] = color_buffer.swizzle; + const std::array swizzle_array = {r, g, b, a}; + const auto swizzled_comp_type = static_cast(swizzle_array[comp]); + constexpr auto min_comp_type = static_cast(AmdGpu::CompSwizzle::Red); + return swizzled_comp_type >= min_comp_type ? swizzled_comp_type - min_comp_type : comp; +} + +void Translator::ExportMrtValue(IR::Attribute attribute, u32 comp, const IR::F32& value, + const FragmentRuntimeInfo::PsColorBuffer& color_buffer) { + const auto converted = ApplyWriteNumberConversion(ir, value, color_buffer.num_conversion); + ir.SetAttribute(attribute, converted, comp); +} + +void Translator::ExportMrtCompressed(IR::Attribute attribute, u32 idx, const IR::U32& value) { + const u32 color_buffer_idx = + static_cast(attribute) - static_cast(IR::Attribute::RenderTarget0); + const auto color_buffer = runtime_info.fs_info.color_buffers[color_buffer_idx]; + + IR::Value unpacked_value; + bool is_integer = false; + switch (color_buffer.export_format) { + case AmdGpu::Liverpool::ShaderExportFormat::Zero: + // No export + return; + case AmdGpu::Liverpool::ShaderExportFormat::ABGR_FP16: + unpacked_value = ir.UnpackHalf2x16(value); + break; + case AmdGpu::Liverpool::ShaderExportFormat::ABGR_UNORM16: + unpacked_value = ir.UnpackUnorm2x16(value); + break; + case AmdGpu::Liverpool::ShaderExportFormat::ABGR_SNORM16: + unpacked_value = ir.UnpackSnorm2x16(value); + break; + case AmdGpu::Liverpool::ShaderExportFormat::ABGR_UINT16: + unpacked_value = ir.UnpackUint2x16(value); + is_integer = true; + break; + case AmdGpu::Liverpool::ShaderExportFormat::ABGR_SINT16: + unpacked_value = ir.UnpackSint2x16(value); + is_integer = true; + break; + default: + UNREACHABLE_MSG("Unimplemented compressed MRT export format {}", + static_cast(color_buffer.export_format)); + break; + } + + const auto r = ir.CompositeExtract(unpacked_value, 0); + const auto g = ir.CompositeExtract(unpacked_value, 1); + const IR::F32 float_r = is_integer ? ir.BitCast(IR::U32{r}) : IR::F32{r}; + const IR::F32 float_g = is_integer ? ir.BitCast(IR::U32{g}) : IR::F32{g}; + + const auto swizzled_r = SwizzleMrtComponent(color_buffer, idx * 2); + const auto swizzled_g = SwizzleMrtComponent(color_buffer, idx * 2 + 1); + + ExportMrtValue(attribute, swizzled_r, float_r, color_buffer); + ExportMrtValue(attribute, swizzled_g, float_g, color_buffer); +} + +void Translator::ExportMrtUncompressed(IR::Attribute attribute, u32 comp, const IR::F32& value) { + const u32 color_buffer_idx = + static_cast(attribute) - static_cast(IR::Attribute::RenderTarget0); + const auto color_buffer = runtime_info.fs_info.color_buffers[color_buffer_idx]; + const auto swizzled_comp = SwizzleMrtComponent(color_buffer, comp); + + switch (color_buffer.export_format) { + case AmdGpu::Liverpool::ShaderExportFormat::Zero: + // No export + return; + case AmdGpu::Liverpool::ShaderExportFormat::R_32: + // Red only + if (swizzled_comp != 0) { + return; + } + break; + case AmdGpu::Liverpool::ShaderExportFormat::GR_32: + // Red and Green only + if (swizzled_comp != 0 && swizzled_comp != 1) { + return; + } + break; + case AmdGpu::Liverpool::ShaderExportFormat::AR_32: + // Red and Alpha only + if (swizzled_comp != 0 && swizzled_comp != 3) { + return; + } + break; + case AmdGpu::Liverpool::ShaderExportFormat::ABGR_32: + // All components + break; + default: + UNREACHABLE_MSG("Unimplemented uncompressed MRT export format {}", + static_cast(color_buffer.export_format)); + break; + } + ExportMrtValue(attribute, swizzled_comp, value, color_buffer); +} + +void Translator::ExportCompressed(IR::Attribute attribute, u32 idx, const IR::U32& value) { + if (IsMrt(attribute)) { + ExportMrtCompressed(attribute, idx, value); + return; + } + const IR::Value unpacked_value = ir.UnpackHalf2x16(value); + const IR::F32 r = IR::F32{ir.CompositeExtract(unpacked_value, 0)}; + const IR::F32 g = IR::F32{ir.CompositeExtract(unpacked_value, 1)}; + ir.SetAttribute(attribute, r, idx * 2); + ir.SetAttribute(attribute, g, idx * 2 + 1); +} + +void Translator::ExportUncompressed(IR::Attribute attribute, u32 comp, const IR::F32& value) { + if (IsMrt(attribute)) { + ExportMrtUncompressed(attribute, comp, value); + return; + } + ir.SetAttribute(attribute, value, comp); +} + void Translator::EmitExport(const GcnInst& inst) { if (ir.block->has_multiple_predecessors && info.stage == Stage::Fragment) { ir.Discard(ir.LogicalNot(ir.GetExec())); @@ -26,41 +145,15 @@ void Translator::EmitExport(const GcnInst& inst) { IR::VectorReg(inst.src[3].code), }; - const auto set_attribute = [&](u32 comp, IR::F32 value) { - if (!IR::IsMrt(attrib)) { - ir.SetAttribute(attrib, value, comp); - return; - } - const u32 index = u32(attrib) - u32(IR::Attribute::RenderTarget0); - const auto col_buf = runtime_info.fs_info.color_buffers[index]; - const auto converted = IR::ApplyWriteNumberConversion(ir, value, col_buf.num_conversion); - const auto [r, g, b, a] = col_buf.swizzle; - const std::array swizzle_array = {r, g, b, a}; - const auto swizzled_comp = swizzle_array[comp]; - if (u32(swizzled_comp) < u32(AmdGpu::CompSwizzle::Red)) { - ir.SetAttribute(attrib, converted, comp); - return; - } - ir.SetAttribute(attrib, converted, u32(swizzled_comp) - u32(AmdGpu::CompSwizzle::Red)); - }; - - const auto unpack = [&](u32 idx) { - const IR::Value value = ir.UnpackHalf2x16(ir.GetVectorReg(vsrc[idx])); - const IR::F32 r = IR::F32{ir.CompositeExtract(value, 0)}; - const IR::F32 g = IR::F32{ir.CompositeExtract(value, 1)}; - set_attribute(idx * 2, r); - set_attribute(idx * 2 + 1, g); - }; - // Components are float16 packed into a VGPR if (exp.compr) { // Export R, G if (exp.en & 1) { - unpack(0); + ExportCompressed(attrib, 0, ir.GetVectorReg(vsrc[0])); } // Export B, A if ((exp.en >> 2) & 1) { - unpack(1); + ExportCompressed(attrib, 1, ir.GetVectorReg(vsrc[1])); } } else { // Components are float32 into separate VGPRS @@ -69,8 +162,7 @@ void Translator::EmitExport(const GcnInst& inst) { if ((mask & 1) == 0) { continue; } - const IR::F32 comp = ir.GetVectorReg(vsrc[i]); - set_attribute(i, comp); + ExportUncompressed(attrib, i, ir.GetVectorReg(vsrc[i])); } } if (IR::IsMrt(attrib)) { diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 496455b50..287885854 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -170,6 +170,7 @@ public: void V_SUBBREV_U32(const GcnInst& inst); void V_LDEXP_F32(const GcnInst& inst); void V_CVT_PKNORM_U16_F32(const GcnInst& inst); + void V_CVT_PKNORM_I16_F32(const GcnInst& inst); void V_CVT_PKRTZ_F16_F32(const GcnInst& inst); // VOP1 @@ -244,6 +245,7 @@ public: void V_SAD(const GcnInst& inst); void V_SAD_U32(const GcnInst& inst); void V_CVT_PK_U16_U32(const GcnInst& inst); + void V_CVT_PK_I16_I32(const GcnInst& inst); void V_CVT_PK_U8_F32(const GcnInst& inst); void V_LSHL_B64(const GcnInst& inst); void V_MUL_F64(const GcnInst& inst); @@ -306,6 +308,13 @@ private: IR::F32 SelectCubeResult(const IR::F32& x, const IR::F32& y, const IR::F32& z, const IR::F32& x_res, const IR::F32& y_res, const IR::F32& z_res); + void ExportMrtValue(IR::Attribute attribute, u32 comp, const IR::F32& value, + const FragmentRuntimeInfo::PsColorBuffer& color_buffer); + void ExportMrtCompressed(IR::Attribute attribute, u32 idx, const IR::U32& value); + void ExportMrtUncompressed(IR::Attribute attribute, u32 comp, const IR::F32& value); + void ExportCompressed(IR::Attribute attribute, u32 idx, const IR::U32& value); + void ExportUncompressed(IR::Attribute attribute, u32 comp, const IR::F32& value); + void LogMissingOpcode(const GcnInst& inst); private: diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 42dbcc513..f73618dbe 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -96,6 +96,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_LDEXP_F32(inst); case Opcode::V_CVT_PKNORM_U16_F32: return V_CVT_PKNORM_U16_F32(inst); + case Opcode::V_CVT_PKNORM_I16_F32: + return V_CVT_PKNORM_I16_F32(inst); case Opcode::V_CVT_PKRTZ_F16_F32: return V_CVT_PKRTZ_F16_F32(inst); @@ -376,6 +378,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_SAD_U32(inst); case Opcode::V_CVT_PK_U16_U32: return V_CVT_PK_U16_U32(inst); + case Opcode::V_CVT_PK_I16_I32: + return V_CVT_PK_I16_I32(inst); case Opcode::V_CVT_PK_U8_F32: return V_CVT_PK_U8_F32(inst); case Opcode::V_LSHL_B64: @@ -645,12 +649,15 @@ void Translator::V_LDEXP_F32(const GcnInst& inst) { } void Translator::V_CVT_PKNORM_U16_F32(const GcnInst& inst) { - const IR::F32 src0{GetSrc(inst.src[0])}; - const IR::F32 src1{GetSrc(inst.src[1])}; - const IR::U32 dst0 = ir.ConvertFToU(32, ir.FPMul(src0, ir.Imm32(65535.f))); - const IR::U32 dst1 = ir.ConvertFToU(32, ir.FPMul(src1, ir.Imm32(65535.f))); - const IR::VectorReg dst_reg{inst.dst[0].code}; - ir.SetVectorReg(dst_reg, ir.BitFieldInsert(dst0, dst1, ir.Imm32(16), ir.Imm32(16))); + const IR::Value vec_f32 = + ir.CompositeConstruct(GetSrc(inst.src[0]), GetSrc(inst.src[1])); + SetDst(inst.dst[0], ir.PackUnorm2x16(vec_f32)); +} + +void Translator::V_CVT_PKNORM_I16_F32(const GcnInst& inst) { + const IR::Value vec_f32 = + ir.CompositeConstruct(GetSrc(inst.src[0]), GetSrc(inst.src[1])); + SetDst(inst.dst[0], ir.PackSnorm2x16(vec_f32)); } void Translator::V_CVT_PKRTZ_F16_F32(const GcnInst& inst) { @@ -1237,11 +1244,15 @@ void Translator::V_SAD_U32(const GcnInst& inst) { } void Translator::V_CVT_PK_U16_U32(const GcnInst& inst) { - const IR::U32 src0{GetSrc(inst.src[0])}; - const IR::U32 src1{GetSrc(inst.src[1])}; - const IR::U32 lo = ir.IMin(src0, ir.Imm32(0xFFFF), false); - const IR::U32 hi = ir.IMin(src1, ir.Imm32(0xFFFF), false); - SetDst(inst.dst[0], ir.BitFieldInsert(lo, hi, ir.Imm32(16), ir.Imm32(16))); + const IR::Value vec_u32 = + ir.CompositeConstruct(GetSrc(inst.src[0]), GetSrc(inst.src[1])); + SetDst(inst.dst[0], ir.PackUint2x16(vec_u32)); +} + +void Translator::V_CVT_PK_I16_I32(const GcnInst& inst) { + const IR::Value vec_u32 = + ir.CompositeConstruct(GetSrc(inst.src[0]), GetSrc(inst.src[1])); + SetDst(inst.dst[0], ir.PackSint2x16(vec_u32)); } void Translator::V_CVT_PK_U8_F32(const GcnInst& inst) { diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index f0558665b..ecbe1f838 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -795,6 +795,38 @@ Value IREmitter::UnpackHalf2x16(const U32& value) { return Inst(Opcode::UnpackHalf2x16, value); } +U32 IREmitter::PackUnorm2x16(const Value& vector) { + return Inst(Opcode::PackUnorm2x16, vector); +} + +Value IREmitter::UnpackUnorm2x16(const U32& value) { + return Inst(Opcode::UnpackUnorm2x16, value); +} + +U32 IREmitter::PackSnorm2x16(const Value& vector) { + return Inst(Opcode::PackSnorm2x16, vector); +} + +Value IREmitter::UnpackSnorm2x16(const U32& value) { + return Inst(Opcode::UnpackSnorm2x16, value); +} + +U32 IREmitter::PackUint2x16(const Value& value) { + return Inst(Opcode::PackUint2x16, value); +} + +Value IREmitter::UnpackUint2x16(const U32& value) { + return Inst(Opcode::UnpackUint2x16, value); +} + +U32 IREmitter::PackSint2x16(const Value& value) { + return Inst(Opcode::PackSint2x16, value); +} + +Value IREmitter::UnpackSint2x16(const U32& value) { + return Inst(Opcode::UnpackSint2x16, value); +} + F32F64 IREmitter::FPMul(const F32F64& a, const F32F64& b) { if (a.Type() != b.Type()) { UNREACHABLE_MSG("Mismatching types {} and {}", a.Type(), b.Type()); diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index 5dd49ce7a..97b94187a 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -175,6 +175,14 @@ public: [[nodiscard]] U32 PackHalf2x16(const Value& vector); [[nodiscard]] Value UnpackHalf2x16(const U32& value); + [[nodiscard]] U32 PackUnorm2x16(const Value& vector); + [[nodiscard]] Value UnpackUnorm2x16(const U32& value); + [[nodiscard]] U32 PackSnorm2x16(const Value& vector); + [[nodiscard]] Value UnpackSnorm2x16(const U32& value); + [[nodiscard]] U32 PackUint2x16(const Value& value); + [[nodiscard]] Value UnpackUint2x16(const U32& value); + [[nodiscard]] U32 PackSint2x16(const Value& value); + [[nodiscard]] Value UnpackSint2x16(const U32& value); [[nodiscard]] F32F64 FPAdd(const F32F64& a, const F32F64& b); [[nodiscard]] F32F64 FPSub(const F32F64& a, const F32F64& b); diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 63a4e1e62..6750be5a6 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -187,6 +187,14 @@ OPCODE(PackFloat2x16, U32, F16x OPCODE(UnpackFloat2x16, F16x2, U32, ) OPCODE(PackHalf2x16, U32, F32x2, ) OPCODE(UnpackHalf2x16, F32x2, U32, ) +OPCODE(PackUnorm2x16, U32, F32x2, ) +OPCODE(UnpackUnorm2x16, F32x2, U32, ) +OPCODE(PackSnorm2x16, U32, F32x2, ) +OPCODE(UnpackSnorm2x16, F32x2, U32, ) +OPCODE(PackUint2x16, U32, U32x2, ) +OPCODE(UnpackUint2x16, U32x2, U32, ) +OPCODE(PackSint2x16, U32, U32x2, ) +OPCODE(UnpackSint2x16, U32x2, U32, ) // Floating-point operations OPCODE(FPAbs32, F32, F32, ) diff --git a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp index 12a1b56e9..c72b9e835 100644 --- a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp @@ -348,6 +348,22 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) { return FoldInverseFunc(inst, IR::Opcode::UnpackFloat2x16); case IR::Opcode::UnpackFloat2x16: return FoldInverseFunc(inst, IR::Opcode::PackFloat2x16); + case IR::Opcode::PackUnorm2x16: + return FoldInverseFunc(inst, IR::Opcode::UnpackUnorm2x16); + case IR::Opcode::UnpackUnorm2x16: + return FoldInverseFunc(inst, IR::Opcode::PackUnorm2x16); + case IR::Opcode::PackSnorm2x16: + return FoldInverseFunc(inst, IR::Opcode::UnpackSnorm2x16); + case IR::Opcode::UnpackSnorm2x16: + return FoldInverseFunc(inst, IR::Opcode::PackSnorm2x16); + case IR::Opcode::PackUint2x16: + return FoldInverseFunc(inst, IR::Opcode::UnpackUint2x16); + case IR::Opcode::UnpackUint2x16: + return FoldInverseFunc(inst, IR::Opcode::PackUint2x16); + case IR::Opcode::PackSint2x16: + return FoldInverseFunc(inst, IR::Opcode::UnpackSint2x16); + case IR::Opcode::UnpackSint2x16: + return FoldInverseFunc(inst, IR::Opcode::PackSint2x16); case IR::Opcode::SelectU1: case IR::Opcode::SelectU8: case IR::Opcode::SelectU16: diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 2bf5e3f0a..103c1faa8 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -184,6 +184,7 @@ struct FragmentRuntimeInfo { AmdGpu::NumberFormat num_format; AmdGpu::NumberConversion num_conversion; AmdGpu::CompMapping swizzle; + AmdGpu::Liverpool::ShaderExportFormat export_format; auto operator<=>(const PsColorBuffer&) const noexcept = default; }; diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index ec9c92ef1..67821b0f2 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -266,6 +266,10 @@ struct Liverpool { BitField<20, 4, ShaderExportFormat> col5; BitField<24, 4, ShaderExportFormat> col6; BitField<28, 4, ShaderExportFormat> col7; + + [[nodiscard]] ShaderExportFormat GetFormat(const u32 buf_idx) const { + return static_cast((raw >> (buf_idx * 4)) & 0xfu); + } }; union VsOutputControl { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 9a20f94c1..8c5cb1f3b 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -35,9 +35,8 @@ struct GraphicsPipelineKey { std::array stage_hashes; u32 num_color_attachments; std::array color_formats; - std::array color_num_formats; - std::array color_num_conversions; - std::array color_swizzles; + std::array + color_buffers; vk::Format depth_format; vk::Format stencil_format; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index c6a56745d..3728a55fb 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -167,11 +167,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS }; } for (u32 i = 0; i < Shader::MaxColorBuffers; i++) { - info.fs_info.color_buffers[i] = { - .num_format = graphics_key.color_num_formats[i], - .num_conversion = graphics_key.color_num_conversions[i], - .swizzle = graphics_key.color_swizzles[i], - }; + info.fs_info.color_buffers[i] = graphics_key.color_buffers[i]; } break; } @@ -309,11 +305,9 @@ bool PipelineCache::RefreshGraphicsKey() { // order. We need to do some arrays compaction at this stage key.num_color_attachments = 0; key.color_formats.fill(vk::Format::eUndefined); - key.color_num_formats.fill(AmdGpu::NumberFormat::Unorm); - key.color_num_conversions.fill(AmdGpu::NumberConversion::None); + key.color_buffers.fill({}); key.blend_controls.fill({}); key.write_masks.fill({}); - key.color_swizzles.fill({}); key.vertex_buffer_formats.fill(vk::Format::eUndefined); key.patch_control_points = 0; @@ -338,9 +332,12 @@ bool PipelineCache::RefreshGraphicsKey() { key.color_formats[remapped_cb] = LiverpoolToVK::SurfaceFormat(col_buf.GetDataFmt(), col_buf.GetNumberFmt()); - key.color_num_formats[remapped_cb] = col_buf.GetNumberFmt(); - key.color_num_conversions[remapped_cb] = col_buf.GetNumberConversion(); - key.color_swizzles[remapped_cb] = col_buf.Swizzle(); + key.color_buffers[remapped_cb] = { + .num_format = col_buf.GetNumberFmt(), + .num_conversion = col_buf.GetNumberConversion(), + .swizzle = col_buf.Swizzle(), + .export_format = regs.color_export_format.GetFormat(cb), + }; } fetch_shader = std::nullopt; @@ -456,7 +453,7 @@ bool PipelineCache::RefreshGraphicsKey() { // of the latter we need to change format to undefined, and either way we need to // increment the index for the null attachment binding. key.color_formats[remapped_cb] = vk::Format::eUndefined; - key.color_swizzles[remapped_cb] = {}; + key.color_buffers[remapped_cb] = {}; ++remapped_cb; continue; } From a51c8c17e07063065fe1780d648c3b4808f31d59 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 12:47:04 -0800 Subject: [PATCH 43/68] shader_recompiler: Fix image write swizzles. (#2236) --- .../ir/passes/resource_tracking_pass.cpp | 4 +-- src/video_core/amdgpu/types.h | 31 ++++++++++++++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index d94c5223a..43d125bf0 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -569,7 +569,7 @@ void PatchTextureBufferArgs(IR::Block& block, IR::Inst& inst, Info& info) { inst.SetArg(1, CalculateBufferAddress(ir, inst, info, buffer, 1U)); if (inst.GetOpcode() == IR::Opcode::StoreBufferFormatF32) { - const auto swizzled = ApplySwizzle(ir, inst.Arg(2), buffer.DstSelect()); + const auto swizzled = ApplySwizzle(ir, inst.Arg(2), buffer.DstSelect().Inverse()); const auto converted = ApplyWriteNumberConversionVec4(ir, swizzled, buffer.GetNumberConversion()); inst.SetArg(2, converted); @@ -829,7 +829,7 @@ void PatchImageArgs(IR::Block& block, IR::Inst& inst, Info& info) { auto texel = inst.Arg(4); if (is_storage) { // Storage image requires shader swizzle. - texel = ApplySwizzle(ir, texel, image.DstSelect()); + texel = ApplySwizzle(ir, texel, image.DstSelect().Inverse()); } const auto converted = ApplyWriteNumberConversionVec4(ir, texel, image.GetNumberConversion()); diff --git a/src/video_core/amdgpu/types.h b/src/video_core/amdgpu/types.h index 63e184cc5..b442b2f1e 100644 --- a/src/video_core/amdgpu/types.h +++ b/src/video_core/amdgpu/types.h @@ -200,10 +200,10 @@ enum class NumberConversion : u32 { }; struct CompMapping { - CompSwizzle r : 3; - CompSwizzle g : 3; - CompSwizzle b : 3; - CompSwizzle a : 3; + CompSwizzle r; + CompSwizzle g; + CompSwizzle b; + CompSwizzle a; auto operator<=>(const CompMapping& other) const = default; @@ -217,6 +217,15 @@ struct CompMapping { }; } + [[nodiscard]] CompMapping Inverse() const { + CompMapping result{}; + InverseSingle(result.r, CompSwizzle::Red); + InverseSingle(result.g, CompSwizzle::Green); + InverseSingle(result.b, CompSwizzle::Blue); + InverseSingle(result.a, CompSwizzle::Alpha); + return result; + } + private: template T ApplySingle(const std::array& data, const CompSwizzle swizzle) const { @@ -237,6 +246,20 @@ private: UNREACHABLE(); } } + + void InverseSingle(CompSwizzle& dst, const CompSwizzle target) const { + if (r == target) { + dst = CompSwizzle::Red; + } else if (g == target) { + dst = CompSwizzle::Green; + } else if (b == target) { + dst = CompSwizzle::Blue; + } else if (a == target) { + dst = CompSwizzle::Alpha; + } else { + dst = CompSwizzle::Zero; + } + } }; inline DataFormat RemapDataFormat(const DataFormat format) { From 361532418ce8a252fd873eacff0d0b83504eeffb Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 12:47:24 -0800 Subject: [PATCH 44/68] externals: Update SPIRV-Cross for MoltenVK (#2237) --- externals/MoltenVK/SPIRV-Cross | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/MoltenVK/SPIRV-Cross b/externals/MoltenVK/SPIRV-Cross index 6173e24b3..1a7b7ef6d 160000 --- a/externals/MoltenVK/SPIRV-Cross +++ b/externals/MoltenVK/SPIRV-Cross @@ -1 +1 @@ -Subproject commit 6173e24b31f09a0c3217103a130e74c4ddec14a6 +Subproject commit 1a7b7ef6de02cf6767e42b10ddad217c45e90d47 From 7072dfc99fdca87e833bf588587debef3e9b08bd Mon Sep 17 00:00:00 2001 From: hspir404 Date: Fri, 24 Jan 2025 21:56:21 +0000 Subject: [PATCH 45/68] Fix stale heap read in UnmapMemoryImpl (#2232) --- src/core/memory.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index a8dd72acc..271092eaf 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -418,8 +418,9 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma vma.phys_base = 0; vma.disallow_merge = false; vma.name = ""; - MergeAdjacent(vma_map, new_it); - bool readonly_file = vma.prot == MemoryProt::CpuRead && type == VMAType::File; + const auto post_merge_it = MergeAdjacent(vma_map, new_it); + auto& post_merge_vma = post_merge_it->second; + bool readonly_file = post_merge_vma.prot == MemoryProt::CpuRead && type == VMAType::File; if (type != VMAType::Reserved && type != VMAType::PoolReserved) { // Unmap the memory region. impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + adjusted_size, From 73b7d344600a6a7aabb55d44dfe888deb8df312f Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 19:55:27 -0800 Subject: [PATCH 46/68] hotfix: Drop scePadSetLightBar log to debug level. Some games like to spam this a lot, and we already handle it. --- src/core/libraries/pad/pad.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index f2b81fbe0..d940d0cfa 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -473,7 +473,7 @@ int PS4_SYSV_ABI scePadSetForceIntercepted() { int PS4_SYSV_ABI scePadSetLightBar(s32 handle, const OrbisPadLightBarParam* pParam) { if (pParam != nullptr) { - LOG_INFO(Lib_Pad, "scePadSetLightBar called handle = {} rgb = {} {} {}", handle, pParam->r, + LOG_DEBUG(Lib_Pad, "called handle = {} rgb = {} {} {}", handle, pParam->r, pParam->g, pParam->b); if (pParam->r < 0xD && pParam->g < 0xD && pParam->b < 0xD) { From e433f3116dd9a36a353d054e1a371e7ff257f87a Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 24 Jan 2025 19:57:04 -0800 Subject: [PATCH 47/68] hotfix 2: clang format --- src/core/libraries/pad/pad.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index d940d0cfa..18709bcb2 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -473,8 +473,8 @@ int PS4_SYSV_ABI scePadSetForceIntercepted() { int PS4_SYSV_ABI scePadSetLightBar(s32 handle, const OrbisPadLightBarParam* pParam) { if (pParam != nullptr) { - LOG_DEBUG(Lib_Pad, "called handle = {} rgb = {} {} {}", handle, pParam->r, - pParam->g, pParam->b); + LOG_DEBUG(Lib_Pad, "called handle = {} rgb = {} {} {}", handle, pParam->r, pParam->g, + pParam->b); if (pParam->r < 0xD && pParam->g < 0xD && pParam->b < 0xD) { LOG_INFO(Lib_Pad, "Invalid lightbar setting"); From 564dbc7b94b02c70febd901f613ccb567923cb5e Mon Sep 17 00:00:00 2001 From: Ian Carpenter Date: Sat, 25 Jan 2025 04:00:52 -0500 Subject: [PATCH 48/68] system_service: Add simple event queue and push an EntitlementUpdate event to it when app content is initialized (#2238) --- .../libraries/app_content/app_content.cpp | 10 ++++++++ src/core/libraries/system/systemservice.cpp | 23 ++++++++++++++++--- src/core/libraries/system/systemservice.h | 4 ++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/core/libraries/app_content/app_content.cpp b/src/core/libraries/app_content/app_content.cpp index 1d23e7f44..1223022c5 100644 --- a/src/core/libraries/app_content/app_content.cpp +++ b/src/core/libraries/app_content/app_content.cpp @@ -12,6 +12,7 @@ #include "core/file_sys/fs.h" #include "core/libraries/app_content/app_content_error.h" #include "core/libraries/libs.h" +#include "core/libraries/system/systemservice.h" namespace Libraries::AppContent { @@ -262,6 +263,15 @@ int PS4_SYSV_ABI sceAppContentInitialize(const OrbisAppContentInitParam* initPar entitlement_label.copy(info.entitlement_label, sizeof(info.entitlement_label)); } } + + if (addcont_count > 0) { + SystemService::OrbisSystemServiceEvent event{}; + event.event_type = SystemService::OrbisSystemServiceEventType::EntitlementUpdate; + event.service_entitlement_update.user_id = 0; + event.service_entitlement_update.np_service_label = 0; + SystemService::PushSystemServiceEvent(event); + } + return ORBIS_OK; } diff --git a/src/core/libraries/system/systemservice.cpp b/src/core/libraries/system/systemservice.cpp index ebb9f4392..a67b9a2fc 100644 --- a/src/core/libraries/system/systemservice.cpp +++ b/src/core/libraries/system/systemservice.cpp @@ -10,6 +10,8 @@ namespace Libraries::SystemService { bool g_splash_status{true}; +std::queue g_event_queue; +std::mutex g_event_queue_mutex; bool IsSplashVisible() { return Config::showSplash() && g_splash_status; @@ -1772,7 +1774,9 @@ s32 PS4_SYSV_ABI sceSystemServiceGetStatus(OrbisSystemServiceStatus* status) { LOG_ERROR(Lib_SystemService, "OrbisSystemServiceStatus is null"); return ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER; } - status->event_num = 0; + + std::lock_guard lock(g_event_queue_mutex); + status->event_num = static_cast(g_event_queue.size()); status->is_system_ui_overlaid = false; status->is_in_background_execution = false; status->is_cpu_mode7_cpu_normal = true; @@ -1940,11 +1944,19 @@ int PS4_SYSV_ABI sceSystemServiceRaiseExceptionLocalProcess() { } s32 PS4_SYSV_ABI sceSystemServiceReceiveEvent(OrbisSystemServiceEvent* event) { - LOG_ERROR(Lib_SystemService, "(STUBBED) called"); + LOG_TRACE(Lib_SystemService, "called"); if (event == nullptr) { return ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER; } - return ORBIS_SYSTEM_SERVICE_ERROR_NO_EVENT; + + std::lock_guard lock(g_event_queue_mutex); + if (g_event_queue.empty()) { + return ORBIS_SYSTEM_SERVICE_ERROR_NO_EVENT; + } + + *event = g_event_queue.front(); + g_event_queue.pop(); + return ORBIS_OK; } int PS4_SYSV_ABI sceSystemServiceReenableMusicPlayer() { @@ -2412,6 +2424,11 @@ int PS4_SYSV_ABI Func_CB5E885E225F69F0() { return ORBIS_OK; } +void PushSystemServiceEvent(const OrbisSystemServiceEvent& event) { + std::lock_guard lock(g_event_queue_mutex); + g_event_queue.push(event); +} + void RegisterlibSceSystemService(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("alZfRdr2RP8", "libSceAppMessaging", 1, "libSceSystemService", 1, 1, sceAppMessagingClearEventFlag); diff --git a/src/core/libraries/system/systemservice.h b/src/core/libraries/system/systemservice.h index cdd3c15e8..c22ccc88f 100644 --- a/src/core/libraries/system/systemservice.h +++ b/src/core/libraries/system/systemservice.h @@ -4,6 +4,8 @@ // https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/master/include/orbis/_types/sys_service.h #pragma once +#include +#include #include "common/types.h" namespace Core::Loader { @@ -603,5 +605,7 @@ int PS4_SYSV_ABI sceSystemServiceReenableVoiceRecognition(); int PS4_SYSV_ABI Func_6B1CDB955F0EBD65(); int PS4_SYSV_ABI Func_CB5E885E225F69F0(); +void PushSystemServiceEvent(const OrbisSystemServiceEvent& event); + void RegisterlibSceSystemService(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::SystemService From a5a1253185fc6b3231e30477ebb8dba5375ac936 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 25 Jan 2025 04:12:18 -0800 Subject: [PATCH 49/68] liverpool: Implement PM4 MEM_SEMAPHORE. (#2235) --- src/video_core/amdgpu/liverpool.cpp | 24 ++++++++++++ src/video_core/amdgpu/pm4_cmds.h | 61 +++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 8355dd1e6..2f40d4136 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -636,6 +636,18 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); + if (mem_semaphore->IsSignaling()) { + mem_semaphore->Signal(); + } else { + while (!mem_semaphore->Signaled()) { + YIELD_GFX(); + } + mem_semaphore->Decrement(); + } + break; + } case PM4ItOpcode::AcquireMem: { // const auto* acquire_mem = reinterpret_cast(header); break; @@ -848,6 +860,18 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } break; } + case PM4ItOpcode::MemSemaphore: { + const auto* mem_semaphore = reinterpret_cast(header); + if (mem_semaphore->IsSignaling()) { + mem_semaphore->Signal(); + } else { + while (!mem_semaphore->Signaled()) { + YIELD_ASC(vqid); + } + mem_semaphore->Decrement(); + } + break; + } case PM4ItOpcode::WaitRegMem: { const auto* wait_reg_mem = reinterpret_cast(header); ASSERT(wait_reg_mem->engine.Value() == PM4CmdWaitRegMem::Engine::Me); diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index 311f4d4d0..e92ba17fa 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -884,4 +884,65 @@ struct PM4CmdDrawIndexIndirectMulti { u32 draw_initiator; ///< Draw Initiator Register }; +struct PM4CmdMemSemaphore { + enum class ClientCode : u32 { + CommandProcessor = 0u, + CommandBuffer = 1u, + DataBuffer = 2u, + }; + enum class Select : u32 { + SignalSemaphore = 6u, + WaitSemaphore = 7u, + }; + enum class SignalType : u32 { + Increment = 0u, + Write = 1u, + }; + + PM4Type3Header header; ///< header + union { + u32 dw1; + BitField<3, 29, u32> addr_lo; ///< Semaphore address bits [31:3] + }; + union { + u32 dw2; + BitField<0, 8, u32> addr_hi; ///< Semaphore address bits [39:32] + BitField<16, 1, u32> use_mailbox; ///< Enables waiting until mailbox is written to + BitField<20, 1, SignalType> signal_type; ///< Indicates the type of signal sent + BitField<24, 2, ClientCode> client_code; + BitField<29, 3, Select> sem_sel; ///< Indicates whether to do a signal or wait operation + }; + + template + [[nodiscard]] T Address() const { + return std::bit_cast(u64(addr_lo) << 3 | (u64(addr_hi) << 32)); + } + + [[nodiscard]] bool IsSignaling() const { + return sem_sel == Select::SignalSemaphore; + } + + [[nodiscard]] bool Signaled() const { + return *Address() > 0; + } + + void Decrement() const { + *Address() -= 1; + } + + void Signal() const { + auto* ptr = Address(); + switch (signal_type) { + case SignalType::Increment: + *ptr += 1; + break; + case SignalType::Write: + *ptr = 1; + break; + default: + UNREACHABLE_MSG("Unknown signal type {}", static_cast(signal_type.Value())); + } + } +}; + } // namespace AmdGpu From f1bc3b4f3dc492f4c067693a97e5932c8be38491 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 25 Jan 2025 14:59:18 -0800 Subject: [PATCH 50/68] shader_recompiler: Add another constant propagation pass near the end. (#2231) --- src/shader_recompiler/recompiler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shader_recompiler/recompiler.cpp b/src/shader_recompiler/recompiler.cpp index bb027a11e..01518ab8f 100644 --- a/src/shader_recompiler/recompiler.cpp +++ b/src/shader_recompiler/recompiler.cpp @@ -90,6 +90,7 @@ IR::Program TranslateProgram(std::span code, Pools& pools, Info& info Shader::Optimization::ResourceTrackingPass(program); Shader::Optimization::IdentityRemovalPass(program.blocks); Shader::Optimization::DeadCodeEliminationPass(program); + Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::CollectShaderInfoPass(program); Shader::Optimization::SharedMemoryBarrierPass(program, profile); From 3960283a67af70fbaf2a64a1ac97f695acf47a16 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 26 Jan 2025 01:00:22 -0800 Subject: [PATCH 51/68] hotfix: Fix missing embedded PS shader address bits. If the emulator code is above a 40-bit address, the embedded shaders need to use address-hi to work. Embedded VS shader already supplies it, PS shader should as well. --- src/core/libraries/gnmdriver/gnmdriver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index fdc3a1acd..06124167c 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -1366,7 +1366,7 @@ s32 PS4_SYSV_ABI sceGnmSetEmbeddedPsShader(u32* cmdbuf, u32 size, u32 shader_id, // pointer to a stack memory, so the check will likely fail. To workaround it we will // repeat set shader functionality here as it is trivial. cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 8u, ps_regs[0], - 0u); // SPI_SHADER_PGM_LO_PS/SPI_SHADER_PGM_HI_PS + ps_regs[1]); // SPI_SHADER_PGM_LO_PS/SPI_SHADER_PGM_HI_PS cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 10u, ps_regs[2], ps_regs[3]); // SPI_SHADER_PGM_RSRC1_PS/SPI_SHADER_PGM_RSRC2_PS cmdbuf = PM4CmdSetData::SetContextReg(cmdbuf, 0x1c4u, ps_regs[4], From 461148c22706ea35c8ae4985f21d695e46be01ff Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 26 Jan 2025 04:32:14 -0800 Subject: [PATCH 52/68] qt: Prevent interacting with empty grid cells. (#2243) --- src/qt_gui/game_grid_frame.cpp | 3 ++- src/qt_gui/gui_context_menus.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/qt_gui/game_grid_frame.cpp b/src/qt_gui/game_grid_frame.cpp index 2ebb09e5d..d719ac878 100644 --- a/src/qt_gui/game_grid_frame.cpp +++ b/src/qt_gui/game_grid_frame.cpp @@ -38,17 +38,18 @@ GameGridFrame::GameGridFrame(std::shared_ptr game_info_get, void GameGridFrame::onCurrentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn) { - cellClicked = true; crtRow = currentRow; crtColumn = currentColumn; columnCnt = this->columnCount(); auto itemID = (crtRow * columnCnt) + currentColumn; if (itemID > m_game_info->m_games.count() - 1) { + cellClicked = false; validCellSelected = false; BackgroundMusicPlayer::getInstance().stopMusic(); return; } + cellClicked = true; validCellSelected = true; SetGridBackgroundImage(crtRow, crtColumn); auto snd0Path = QString::fromStdString(m_game_info->m_games[itemID].snd0_path.string()); diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 0e8675c0c..72affeca7 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -41,8 +41,8 @@ public: itemID = widget->currentRow() * widget->columnCount() + widget->currentColumn(); } - // Do not show the menu if an item is selected - if (itemID == -1) { + // Do not show the menu if no item is selected + if (itemID < 0 || itemID >= m_games.size()) { return; } From 46b5437fdfea7e7b3add555d62540766daa4534d Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 26 Jan 2025 08:01:15 -0800 Subject: [PATCH 53/68] emulator: Use correct game folder mount when opening update eboot directly. (#2244) --- src/emulator.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index e77c2b87f..97215c06f 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -100,9 +100,21 @@ Emulator::~Emulator() { } void Emulator::Run(const std::filesystem::path& file, const std::vector args) { + const auto eboot_name = file.filename().string(); + auto game_folder = file.parent_path(); + if (const auto game_folder_name = game_folder.filename().string(); + game_folder_name.ends_with("-UPDATE")) { + // If an executable was launched from a separate update directory, + // use the base game directory as the game folder. + const auto base_name = game_folder_name.substr(0, game_folder_name.size() - 7); + const auto base_path = game_folder.parent_path() / base_name; + if (std::filesystem::is_directory(base_path)) { + game_folder = base_path; + } + } + // Applications expect to be run from /app0 so mount the file's parent path as app0. auto* mnt = Common::Singleton::Instance(); - const auto game_folder = file.parent_path(); mnt->Mount(game_folder, "/app0"); // Certain games may use /hostapp as well such as CUSA001100 mnt->Mount(game_folder, "/hostapp"); @@ -223,7 +235,7 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorGetHLESymbols()); // Load the module with the linker - const auto eboot_path = mnt->GetHostPath("/app0/" + file.filename().string()); + const auto eboot_path = mnt->GetHostPath("/app0/" + eboot_name); linker->LoadModule(eboot_path); // check if we have system modules to load From 7d1631f9f43f3b781a20dd91d7ebc0980b55b8ee Mon Sep 17 00:00:00 2001 From: Ian Carpenter Date: Sun, 26 Jan 2025 18:46:59 -0500 Subject: [PATCH 54/68] memory_patcher: Remove hardcoded repositories when loading patches (#2241) --- src/common/memory_patcher.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/common/memory_patcher.cpp b/src/common/memory_patcher.cpp index 6b79edb9f..f81a0ed83 100644 --- a/src/common/memory_patcher.cpp +++ b/src/common/memory_patcher.cpp @@ -7,6 +7,7 @@ #include #include #ifdef ENABLE_QT_GUI +#include #include #include #include @@ -189,14 +190,16 @@ void OnGameLoaded() { // We use the QT headers for the xml and json parsing, this define is only true on QT builds QString patchDir; Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir)); - QString repositories[] = {"GoldHEN", "shadPS4"}; + QDir dir(patchDir); + QStringList folders = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - for (const QString& repository : repositories) { - QString filesJsonPath = patchDir + "/" + repository + "/files.json"; + for (const QString& folder : folders) { + QString filesJsonPath = patchDir + "/" + folder + "/files.json"; QFile jsonFile(filesJsonPath); if (!jsonFile.open(QIODevice::ReadOnly)) { - LOG_ERROR(Loader, "Unable to open files.json for reading."); + LOG_ERROR(Loader, "Unable to open files.json for reading in repository {}", + folder.toStdString()); continue; } @@ -220,11 +223,12 @@ void OnGameLoaded() { } if (selectedFileName.isEmpty()) { - LOG_ERROR(Loader, "No patch file found for the current serial."); + LOG_ERROR(Loader, "No patch file found for the current serial in repository {}", + folder.toStdString()); continue; } - QString filePath = patchDir + "/" + repository + "/" + selectedFileName; + QString filePath = patchDir + "/" + folder + "/" + selectedFileName; QFile file(filePath); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { LOG_ERROR(Loader, "Unable to open the file for reading."); From 6f04ea18e4f3d87e9a89f0fefcc1c8c722a87de6 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 26 Jan 2025 19:28:58 -0800 Subject: [PATCH 55/68] externals: Update MoltenVK (#2249) Fixes broken vertex binding dynamic stride when used with tessellation. --- externals/MoltenVK/MoltenVK | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK index 2473ce6f0..6fa077fb8 160000 --- a/externals/MoltenVK/MoltenVK +++ b/externals/MoltenVK/MoltenVK @@ -1 +1 @@ -Subproject commit 2473ce6f0ab7d5d8a49aa91b2e37f3447a939f18 +Subproject commit 6fa077fb8ed8dac4e4cd66b6b1ebd7b4d955a754 From 191e64bfa11e45992e869e7a9df787d6fbe5038b Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Mon, 27 Jan 2025 11:17:23 +0300 Subject: [PATCH 56/68] renderer: respect zmin/zmax even if clipping is disabled (#2250) --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 88b510eca..7a0652ed7 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1172,6 +1172,8 @@ void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) { continue; } + const auto zoffset = vp_ctl.zoffset_enable ? vp.zoffset : 0.f; + const auto zscale = vp_ctl.zscale_enable ? vp.zscale : 1.f; if (pipeline.IsClipDisabled()) { // In case if clipping is disabled we patch the shader to convert vertex position // from screen space coordinates to NDC by defining a render space as full hardware @@ -1181,16 +1183,14 @@ void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) { .y = 0.f, .width = float(std::min(instance.GetMaxViewportWidth(), 16_KB)), .height = float(std::min(instance.GetMaxViewportHeight(), 16_KB)), - .minDepth = 0.0, - .maxDepth = 1.0, + .minDepth = zoffset - zscale * reduce_z, + .maxDepth = zscale + zoffset, }); } else { const auto xoffset = vp_ctl.xoffset_enable ? vp.xoffset : 0.f; const auto xscale = vp_ctl.xscale_enable ? vp.xscale : 1.f; const auto yoffset = vp_ctl.yoffset_enable ? vp.yoffset : 0.f; const auto yscale = vp_ctl.yscale_enable ? vp.yscale : 1.f; - const auto zoffset = vp_ctl.zoffset_enable ? vp.zoffset : 0.f; - const auto zscale = vp_ctl.zscale_enable ? vp.zscale : 1.f; viewports.push_back({ .x = xoffset - xscale, .y = yoffset - yscale, From 665261efc574562aad240466a9eed8b05b6b9a50 Mon Sep 17 00:00:00 2001 From: F1219R <109141852+F1219R@users.noreply.github.com> Date: Mon, 27 Jan 2025 16:39:27 +0100 Subject: [PATCH 57/68] Update sq translation (#2251) --- src/qt_gui/translations/sq.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 2f88b4390..15c18d2f6 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -249,7 +249,7 @@ Open shadPS4 Folder - Open shadPS4 Folder + Hap dosjen e shadPS4 Exit @@ -542,7 +542,7 @@ Fullscreen Mode - Modaliteti i Plotë + Mënyra me ekran të plotë Enable Separate Update Folder @@ -550,7 +550,7 @@ Default tab when opening settings - Skeda e parazgjedhur kur hapni cilësimet + Skeda e parazgjedhur kur hapen cilësimet Show Game Size In List @@ -594,7 +594,7 @@ Open Log Location - Hap vendndodhjen e regjistrit + Hap vendndodhjen e Ditarit Input @@ -630,11 +630,11 @@ Gui - Ndërfaqe + Ndërfaqja User - Përdorues + Përdoruesi Graphics Device @@ -822,7 +822,7 @@ GUIMusicGroupBox - Luaj muzikën e titullit:\nNëse një lojë e mbështet, aktivizohet luajtja e muzikës të veçantë kur të zgjidhësh lojën në GUI. + Luaj muzikën e titullit:\nNëse një lojë e mbështet, aktivizohet luajtja e muzikës të veçantë kur të zgjidhësh lojën në ndërfaqe. disableTrophycheckBox From 2837d848eddf2c4b2b361626c220aa415f8b241c Mon Sep 17 00:00:00 2001 From: panzone91 <150828896+panzone91@users.noreply.github.com> Date: Mon, 27 Jan 2025 21:40:58 +0100 Subject: [PATCH 58/68] linker: handle relocation for exported modules (#2247) --- src/core/linker.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/core/linker.h b/src/core/linker.h index 00da3a08c..357b39664 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -109,10 +109,13 @@ public: void RelocateAnyImports(Module* m) { Relocate(m); - for (auto& module : m_modules) { - const auto imports = module->GetImportModules(); - if (std::ranges::contains(imports, m->name, &ModuleInfo::name)) { - Relocate(module.get()); + const auto exports = m->GetExportModules(); + for (auto& export_mod : exports) { + for (auto& module : m_modules) { + const auto imports = module->GetImportModules(); + if (std::ranges::contains(imports, export_mod.name, &ModuleInfo::name)) { + Relocate(module.get()); + } } } } From d2127b38deace732f0c41b6cc1881e21f4ed268b Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 28 Jan 2025 00:12:48 -0800 Subject: [PATCH 59/68] vk_rasterizer: Keep viewport depth offset even without native depth clip control. (#2257) --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 7a0652ed7..7c77f2519 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1155,10 +1155,7 @@ void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) { const auto& vp_ctl = regs.viewport_control; const float reduce_z = - instance.IsDepthClipControlSupported() && - regs.clipper_control.clip_space == AmdGpu::Liverpool::ClipSpace::MinusWToW - ? 1.0f - : 0.0f; + regs.clipper_control.clip_space == AmdGpu::Liverpool::ClipSpace::MinusWToW ? 1.0f : 0.0f; if (regs.polygon_control.enable_window_offset) { LOG_ERROR(Render_Vulkan, From 8379922f8ab1209d25b4cbec14303401aa75eec9 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 28 Jan 2025 02:31:26 -0800 Subject: [PATCH 60/68] hotfix: Reduce requested videodec memory block sizes. This really needs a more accurate implementation, but for the stub lowering the value helps games that run out of memory space if it is too large. --- src/core/libraries/videodec/videodec.cpp | 2 +- src/core/libraries/videodec/videodec2.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/videodec/videodec.cpp b/src/core/libraries/videodec/videodec.cpp index 6c9a9b8c0..79001e76f 100644 --- a/src/core/libraries/videodec/videodec.cpp +++ b/src/core/libraries/videodec/videodec.cpp @@ -9,7 +9,7 @@ namespace Libraries::Videodec { -static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying +static constexpr u64 kMinimumMemorySize = 8_MB; ///> Fake minimum memory size for querying int PS4_SYSV_ABI sceVideodecCreateDecoder(const OrbisVideodecConfigInfo* pCfgInfoIn, const OrbisVideodecResourceInfo* pRsrcInfoIn, diff --git a/src/core/libraries/videodec/videodec2.cpp b/src/core/libraries/videodec/videodec2.cpp index 07f7a274b..be0db4ea3 100644 --- a/src/core/libraries/videodec/videodec2.cpp +++ b/src/core/libraries/videodec/videodec2.cpp @@ -10,7 +10,7 @@ namespace Libraries::Vdec2 { -static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying +static constexpr u64 kMinimumMemorySize = 8_MB; ///> Fake minimum memory size for querying s32 PS4_SYSV_ABI sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo) { From 4b93b8b57460c83421d0d859e243dd05a864bdc1 Mon Sep 17 00:00:00 2001 From: Martin <67326368+Martini-141@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:47:00 +0100 Subject: [PATCH 61/68] add missing translations and other corrections nb (#2253) --- src/qt_gui/translations/nb.ts | 42 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index d6d4090df..0b7d2699a 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -98,7 +98,7 @@ Open Folder... - Åpne mappen... + Åpne mappe... Open Game Folder @@ -126,7 +126,7 @@ Copy All - Kopier alle + Kopier alt Delete... @@ -146,19 +146,19 @@ Compatibility... - Compatibility... + Kompatibilitet... Update database - Update database + Oppdater database View report - View report + Vis rapport Submit a report - Submit a report + Send inn en rapport Shortcut creation @@ -574,11 +574,11 @@ Trophy Key - Trophy Key + Trofénøkkel Trophy - Trophy + Trofé Logger @@ -658,7 +658,7 @@ Enable Shaders Dumping - Aktiver dumping av skyggelegger + Aktiver skyggeleggerdumping Enable NULL GPU @@ -666,7 +666,7 @@ Paths - Stier + Mapper Game Folders @@ -686,7 +686,7 @@ Enable Debug Dumping - Aktiver dumping av feilretting + Aktiver feilrettingsdumping Enable Vulkan Validation Layers @@ -718,7 +718,7 @@ GUI Settings - GUI-innstillinger + Grensesnitt-innstillinger Disable Trophy Pop-ups @@ -730,7 +730,7 @@ Update Compatibility Database On Startup - Oppdater kompatibilitets-database ved oppstart + Oppdater database ved oppstart Game Compatibility @@ -750,7 +750,7 @@ Audio Backend - Audio Backend + Lydsystem Save @@ -806,7 +806,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + Trofénøkkel:\nNøkkel brukes til å dekryptere trofeer. Må hentes fra din konsoll (jailbroken).\nMå bare inneholde sekskantede tegn. logTypeGroupBox @@ -846,7 +846,7 @@ checkCompatibilityOnStartupCheckBox - Oppdater kompatibilitets-data ved oppstart:\nOppdaterer kompatibilitets-databasen automatisk når shadPS4 starter. + Oppdater database ved oppstart:\nOppdaterer kompatibilitets-databasen automatisk når shadPS4 starter. updateCompatibilityButton @@ -894,7 +894,7 @@ dumpShadersCheckBox - Aktiver dumping av skyggelegger:\nFor teknisk feilsøking lagrer skyggeleggerne fra spillet i en mappe mens de gjengis. + Aktiver skyggeleggerdumping:\nFor teknisk feilsøking lagrer skyggeleggerne fra spillet i en mappe mens de gjengis. nullGpuCheckBox @@ -914,7 +914,7 @@ debugDump - Aktiver dumping av feilsøking:\nLagrer import- og eksport-symbolene og filoverskriftsinformasjonen til det nåværende kjørende PS4-programmet i en katalog. + Aktiver feilrettingsdumping:\nLagrer import- og eksport-symbolene og filoverskriftsinformasjonen til det nåværende kjørende PS4-programmet i en katalog. vkValidationCheckBox @@ -1188,7 +1188,7 @@ Path - Sti + Adresse Play Time @@ -1232,7 +1232,7 @@ Game can be completed with playable performance and no major glitches - Spillet kan fullføres med spillbar ytelse og ingen store feil + Spillet kan fullføres med spillbar ytelse og uten store feil
@@ -1361,4 +1361,4 @@ TB - \ No newline at end of file + From 2cdd873681dd3b03d0752bda5c84412f06f4f0b8 Mon Sep 17 00:00:00 2001 From: slick-daddy <129640104+slick-daddy@users.noreply.github.com> Date: Tue, 28 Jan 2025 19:47:12 +0300 Subject: [PATCH 62/68] Update tr_TR.ts (#2255) --- src/qt_gui/translations/tr_TR.ts | 54 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 6436278de..a2368bfee 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -546,7 +546,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + Ayrı Güncelleme Klasörünü Etkinleştir Default tab when opening settings @@ -554,7 +554,7 @@ Show Game Size In List - Göster oyun boyutunu listede + Oyun Boyutunu Listede Göster Show Splash @@ -574,11 +574,11 @@ Trophy Key - Trophy Key + Kupa Anahtarı Trophy - Trophy + Kupa Logger @@ -722,7 +722,7 @@ Disable Trophy Pop-ups - Disable Trophy Pop-ups + Kupa Açılır Pencerelerini Devre Dışı Bırak Play title music @@ -730,23 +730,23 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + Başlangıçta Uyumluluk Veritabanını Güncelle Game Compatibility - Game Compatibility + Oyun Uyumluluğu Display Compatibility Data - Display Compatibility Data + Uyumluluk Verilerini Göster Update Compatibility Database - Update Compatibility Database + Uyumluluk Veritabanını Güncelle Volume - Ses seviyesi + Ses Seviyesi Audio Backend @@ -1105,11 +1105,11 @@ The game is in version: %1 - Oyun sürümde: %1 + Oyun sürümü: %1 The downloaded patch only works on version: %1 - İndirilen yamanın sadece sürümde çalışıyor: %1 + İndirilen yama sadece şu sürümde çalışıyor: %1 You may need to update your game. @@ -1168,7 +1168,7 @@ Compatibility - Compatibility + Uyumluluk Region @@ -1196,35 +1196,35 @@ Never Played - Never Played + Hiç Oynanmadı h - h + sa m - m + dk s - s + sn Compatibility is untested - Compatibility is untested + Uyumluluk test edilmemiş Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + Oyun düzgün bir şekilde başlatılamıyor / emülatörü çökertiyor Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + Oyun başlatılabiliyor ancak yalnızca boş bir ekran gösteriyor Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + Oyun bir resim gösteriyor ancak menüleri geçemiyor Game has game-breaking glitches or unplayable performance @@ -1232,7 +1232,7 @@ Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + Oyun, oynanabilir performansla tamamlanabilir ve büyük aksaklık yok @@ -1267,7 +1267,7 @@ Your version is already up to date! - Versiyonunuz zaten güncel! + Sürümünüz zaten güncel! Update Available @@ -1279,11 +1279,11 @@ Current Version - Mevcut Versiyon + Mevcut Sürüm Latest Version - Son Versiyon + Son Sürüm Do you want to update? @@ -1335,7 +1335,7 @@ Failed to create the update script file - Güncelleme betiği dosyası oluşturulamadı + Güncelleme komut dosyası oluşturulamadı @@ -1361,4 +1361,4 @@ TB - \ No newline at end of file + From a78f8afe58a995cecdb4eb1f1e6d6e58320444f6 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 28 Jan 2025 08:48:19 -0800 Subject: [PATCH 63/68] libraries: Implement libSceZlib. (#2256) * libraries: add zlib hle skeleton/stub * libraries: Implement libSceZlib. * zlib: Make variables static. --------- Co-authored-by: Nenkai --- CMakeLists.txt | 6 + src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/core/libraries/libs.cpp | 2 + src/core/libraries/zlib/zlib.cpp | 183 +++++++++++++++++++++++++++ src/core/libraries/zlib/zlib_error.h | 18 +++ src/core/libraries/zlib/zlib_sce.h | 21 +++ 7 files changed, 232 insertions(+) create mode 100644 src/core/libraries/zlib/zlib.cpp create mode 100644 src/core/libraries/zlib/zlib_error.h create mode 100644 src/core/libraries/zlib/zlib_sce.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c9dad307..78e3c7997 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -440,6 +440,11 @@ set(NP_LIBS src/core/libraries/np_common/np_common.cpp src/core/libraries/np_party/np_party.h ) +set(ZLIB_LIB src/core/libraries/zlib/zlib.cpp + src/core/libraries/zlib/zlib_sce.h + src/core/libraries/zlib/zlib_error.h +) + set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp src/core/libraries/screenshot/screenshot.h src/core/libraries/move/move.cpp @@ -612,6 +617,7 @@ set(CORE src/core/aerolib/stubs.cpp ${PLAYGO_LIB} ${RANDOM_LIB} ${USBD_LIB} + ${ZLIB_LIB} ${MISC_LIBS} ${IME_LIB} ${FIBER_LIB} diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index f1d3a9499..dd708c528 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -133,6 +133,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Mouse) \ SUB(Lib, WebBrowserDialog) \ SUB(Lib, NpParty) \ + SUB(Lib, Zlib) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index d5530312c..54f8cdd0b 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -100,6 +100,7 @@ enum class Class : u8 { Lib_Mouse, ///< The LibSceMouse implementation Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation Lib_NpParty, ///< The LibSceNpParty implementation + Lib_Zlib, ///< The LibSceZlib implementation. Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index e09de1cee..8cf286d13 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -54,6 +54,7 @@ #include "core/libraries/videodec/videodec2.h" #include "core/libraries/videoout/video_out.h" #include "core/libraries/web_browser_dialog/webbrowserdialog.h" +#include "core/libraries/zlib/zlib_sce.h" #include "fiber/fiber.h" #include "jpeg/jpegenc.h" @@ -111,6 +112,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Mouse::RegisterlibSceMouse(sym); Libraries::WebBrowserDialog::RegisterlibSceWebBrowserDialog(sym); Libraries::NpParty::RegisterlibSceNpParty(sym); + Libraries::Zlib::RegisterlibSceZlib(sym); } } // namespace Libraries diff --git a/src/core/libraries/zlib/zlib.cpp b/src/core/libraries/zlib/zlib.cpp new file mode 100644 index 000000000..899cb5bf6 --- /dev/null +++ b/src/core/libraries/zlib/zlib.cpp @@ -0,0 +1,183 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include + +#include "common/logging/log.h" +#include "common/thread.h" +#include "core/libraries/kernel/threads.h" +#include "core/libraries/libs.h" +#include "core/libraries/zlib/zlib_error.h" +#include "core/libraries/zlib/zlib_sce.h" + +namespace Libraries::Zlib { + +struct InflateTask { + u64 request_id; + const void* src; + u32 src_length; + void* dst; + u32 dst_length; +}; + +struct InflateResult { + u32 length; + s32 status; +}; + +static Kernel::Thread task_thread; + +static std::mutex mutex; +static std::queue task_queue; +static std::condition_variable_any task_queue_cv; +static std::queue done_queue; +static std::condition_variable_any done_queue_cv; +static std::unordered_map results; +static u64 next_request_id; + +void ZlibTaskThread(const std::stop_token& stop) { + Common::SetCurrentThreadName("shadPS4:ZlibTaskThread"); + + while (!stop.stop_requested()) { + InflateTask task; + { + // Lock and pop from the task queue, unless stop has been requested. + std::unique_lock lock(mutex); + if (!task_queue_cv.wait(lock, stop, [&] { return !task_queue.empty(); })) { + break; + } + task = task_queue.back(); + task_queue.pop(); + } + + uLongf decompressed_length = task.dst_length; + const auto ret = uncompress(static_cast(task.dst), &decompressed_length, + static_cast(task.src), task.src_length); + + { + // Lock, insert the new result, and push the finished request ID to the done queue. + std::unique_lock lock(mutex); + results[task.request_id] = InflateResult{ + .length = static_cast(decompressed_length), + .status = ret == Z_BUF_ERROR ? ORBIS_ZLIB_ERROR_NOSPACE + : ret == Z_OK ? ORBIS_OK + : ORBIS_ZLIB_ERROR_FATAL, + }; + done_queue.push(task.request_id); + } + done_queue_cv.notify_one(); + } +} + +s32 PS4_SYSV_ABI sceZlibInitialize(const void* buffer, u32 length) { + LOG_INFO(Lib_Zlib, "called"); + if (task_thread.Joinable()) { + return ORBIS_ZLIB_ERROR_ALREADY_INITIALIZED; + } + + // Initialize with empty task data + task_queue = std::queue(); + done_queue = std::queue(); + results.clear(); + next_request_id = 1; + + task_thread.Run([](const std::stop_token& stop) { ZlibTaskThread(stop); }); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceZlibInflate(const void* src, u32 src_len, void* dst, u32 dst_len, + u64* request_id) { + LOG_DEBUG(Lib_Zlib, "(STUBBED) called"); + if (!task_thread.Joinable()) { + return ORBIS_ZLIB_ERROR_NOT_INITIALIZED; + } + if (!src || !src_len || !dst || !dst_len || !request_id || dst_len > 64_KB || + dst_len % 2_KB != 0) { + return ORBIS_ZLIB_ERROR_INVALID; + } + + { + std::unique_lock lock(mutex); + *request_id = next_request_id++; + task_queue.emplace(InflateTask{ + .request_id = *request_id, + .src = src, + .src_length = src_len, + .dst = dst, + .dst_length = dst_len, + }); + task_queue_cv.notify_one(); + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceZlibWaitForDone(u64* request_id, const u32* timeout) { + LOG_DEBUG(Lib_Zlib, "(STUBBED) called"); + if (!task_thread.Joinable()) { + return ORBIS_ZLIB_ERROR_NOT_INITIALIZED; + } + if (!request_id) { + return ORBIS_ZLIB_ERROR_INVALID; + } + + { + // Pop from the done queue, unless the timeout is reached. + std::unique_lock lock(mutex); + const auto pred = [] { return !done_queue.empty(); }; + if (timeout) { + if (!done_queue_cv.wait_for(lock, std::chrono::milliseconds(*timeout), pred)) { + return ORBIS_ZLIB_ERROR_TIMEDOUT; + } + } else { + done_queue_cv.wait(lock, pred); + } + *request_id = done_queue.back(); + done_queue.pop(); + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceZlibGetResult(const u64 request_id, u32* dst_length, s32* status) { + LOG_DEBUG(Lib_Zlib, "(STUBBED) called"); + if (!task_thread.Joinable()) { + return ORBIS_ZLIB_ERROR_NOT_INITIALIZED; + } + if (!dst_length || !status) { + return ORBIS_ZLIB_ERROR_INVALID; + } + + { + std::unique_lock lock(mutex); + if (!results.contains(request_id)) { + return ORBIS_ZLIB_ERROR_NOT_FOUND; + } + const auto result = results[request_id]; + *dst_length = result.length; + *status = result.status; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceZlibFinalize() { + LOG_INFO(Lib_Zlib, "called"); + if (!task_thread.Joinable()) { + return ORBIS_ZLIB_ERROR_NOT_INITIALIZED; + } + task_thread.Stop(); + return ORBIS_OK; +} + +void RegisterlibSceZlib(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("m1YErdIXCp4", "libSceZlib", 1, "libSceZlib", 1, 1, sceZlibInitialize); + LIB_FUNCTION("6na+Sa-B83w", "libSceZlib", 1, "libSceZlib", 1, 1, sceZlibFinalize); + LIB_FUNCTION("TLar1HULv1Q", "libSceZlib", 1, "libSceZlib", 1, 1, sceZlibInflate); + LIB_FUNCTION("uB8VlDD4e0s", "libSceZlib", 1, "libSceZlib", 1, 1, sceZlibWaitForDone); + LIB_FUNCTION("2eDcGHC0YaM", "libSceZlib", 1, "libSceZlib", 1, 1, sceZlibGetResult); +}; + +} // namespace Libraries::Zlib diff --git a/src/core/libraries/zlib/zlib_error.h b/src/core/libraries/zlib/zlib_error.h new file mode 100644 index 000000000..59574f0b2 --- /dev/null +++ b/src/core/libraries/zlib/zlib_error.h @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// Zlib library +constexpr int ORBIS_ZLIB_ERROR_NOT_FOUND = 0x81120002; +constexpr int ORBIS_ZLIB_ERROR_BUSY = 0x8112000B; +constexpr int ORBIS_ZLIB_ERROR_FAULT = 0x8112000E; +constexpr int ORBIS_ZLIB_ERROR_INVALID = 0x81120016; +constexpr int ORBIS_ZLIB_ERROR_NOSPACE = 0x8112001C; +constexpr int ORBIS_ZLIB_ERROR_NOT_SUPPORTED = 0x81120025; +constexpr int ORBIS_ZLIB_ERROR_TIMEDOUT = 0x81120027; +constexpr int ORBIS_ZLIB_ERROR_NOT_INITIALIZED = 0x81120032; +constexpr int ORBIS_ZLIB_ERROR_ALREADY_INITIALIZED = 0x81120033; +constexpr int ORBIS_ZLIB_ERROR_FATAL = 0x811200FF; diff --git a/src/core/libraries/zlib/zlib_sce.h b/src/core/libraries/zlib/zlib_sce.h new file mode 100644 index 000000000..6f8cf9468 --- /dev/null +++ b/src/core/libraries/zlib/zlib_sce.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} +namespace Libraries::Zlib { + +s32 PS4_SYSV_ABI sceZlibInitialize(const void* buffer, u32 length); +s32 PS4_SYSV_ABI sceZlibInflate(const void* src, u32 src_len, void* dst, u32 dst_len, + u64* request_id); +s32 PS4_SYSV_ABI sceZlibWaitForDone(u64* request_id, const u32* timeout); +s32 PS4_SYSV_ABI sceZlibGetResult(u64 request_id, u32* dst_length, s32* status); +s32 PS4_SYSV_ABI sceZlibFinalize(); + +void RegisterlibSceZlib(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Zlib \ No newline at end of file From 0575853be18d8f0504482b4a5a407fb14a23d492 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:56:05 -0800 Subject: [PATCH 64/68] externals: Update MoltenVK. (#2264) Adds support for VK_EXT_depth_clip_control --- externals/MoltenVK/MoltenVK | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK index 6fa077fb8..aba997657 160000 --- a/externals/MoltenVK/MoltenVK +++ b/externals/MoltenVK/MoltenVK @@ -1 +1 @@ -Subproject commit 6fa077fb8ed8dac4e4cd66b6b1ebd7b4d955a754 +Subproject commit aba997657b94d6de1794ebad36ce5634341252c7 From 78a0a755c5f2ff06c340ec0dc3431fc247aaa3e4 Mon Sep 17 00:00:00 2001 From: ElBread3 <92335081+ElBread3@users.noreply.github.com> Date: Tue, 28 Jan 2025 21:14:47 -0600 Subject: [PATCH 65/68] qt_gui: Some game install features and fixes (#2261) * open update folder + delete save folder + bulk install checkbox * delete pkg on install checkbox + use game icon for finish window --- src/qt_gui/gui_context_menus.h | 45 ++++++++++++++++++++++++++++--- src/qt_gui/install_dir_select.cpp | 24 ++++++++++++++--- src/qt_gui/install_dir_select.h | 12 +++++++++ src/qt_gui/main_window.cpp | 26 +++++++++++++++--- src/qt_gui/main_window.h | 4 +++ 5 files changed, 101 insertions(+), 10 deletions(-) diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 72affeca7..bdc2aec0c 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -52,10 +52,12 @@ public: // "Open Folder..." submenu QMenu* openFolderMenu = new QMenu(tr("Open Folder..."), widget); QAction* openGameFolder = new QAction(tr("Open Game Folder"), widget); + QAction* openUpdateFolder = new QAction(tr("Open Update Folder"), widget); QAction* openSaveDataFolder = new QAction(tr("Open Save Data Folder"), widget); QAction* openLogFolder = new QAction(tr("Open Log Folder"), widget); openFolderMenu->addAction(openGameFolder); + openFolderMenu->addAction(openUpdateFolder); openFolderMenu->addAction(openSaveDataFolder); openFolderMenu->addAction(openLogFolder); @@ -87,10 +89,12 @@ public: QMenu* deleteMenu = new QMenu(tr("Delete..."), widget); QAction* deleteGame = new QAction(tr("Delete Game"), widget); QAction* deleteUpdate = new QAction(tr("Delete Update"), widget); + QAction* deleteSaveData = new QAction(tr("Delete Save Data"), widget); QAction* deleteDLC = new QAction(tr("Delete DLC"), widget); deleteMenu->addAction(deleteGame); deleteMenu->addAction(deleteUpdate); + deleteMenu->addAction(deleteSaveData); deleteMenu->addAction(deleteDLC); menu.addMenu(deleteMenu); @@ -122,6 +126,18 @@ public: QDesktopServices::openUrl(QUrl::fromLocalFile(folderPath)); } + if (selected == openUpdateFolder) { + QString open_update_path; + Common::FS::PathToQString(open_update_path, m_games[itemID].path); + open_update_path += "-UPDATE"; + if (!std::filesystem::exists(Common::FS::PathFromQString(open_update_path))) { + QMessageBox::critical(nullptr, tr("Error"), + QString(tr("This game has no update folder to open!"))); + } else { + QDesktopServices::openUrl(QUrl::fromLocalFile(open_update_path)); + } + } + if (selected == openSaveDataFolder) { QString userPath; Common::FS::PathToQString(userPath, @@ -143,7 +159,7 @@ public: PSF psf; std::filesystem::path game_folder_path = m_games[itemID].path; std::filesystem::path game_update_path = game_folder_path; - game_update_path += "UPDATE"; + game_update_path += "-UPDATE"; if (std::filesystem::exists(game_update_path)) { game_folder_path = game_update_path; } @@ -238,6 +254,11 @@ public: QString trophyPath, gameTrpPath; Common::FS::PathToQString(trophyPath, m_games[itemID].serial); Common::FS::PathToQString(gameTrpPath, m_games[itemID].path); + auto game_update_path = Common::FS::PathFromQString(gameTrpPath); + game_update_path += "-UPDATE"; + if (std::filesystem::exists(game_update_path)) { + Common::FS::PathToQString(gameTrpPath, game_update_path); + } TrophyViewer* trophyViewer = new TrophyViewer(trophyPath, gameTrpPath); trophyViewer->show(); connect(widget->parent(), &QWidget::destroyed, trophyViewer, @@ -335,14 +356,18 @@ public: clipboard->setText(combinedText); } - if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC) { + if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC || + selected == deleteSaveData) { bool error = false; - QString folder_path, game_update_path, dlc_path; + QString folder_path, game_update_path, dlc_path, save_data_path; Common::FS::PathToQString(folder_path, m_games[itemID].path); game_update_path = folder_path + "-UPDATE"; Common::FS::PathToQString( dlc_path, Config::getAddonInstallDir() / Common::FS::PathFromQString(folder_path).parent_path().filename()); + Common::FS::PathToQString(save_data_path, + Common::FS::GetUserPath(Common::FS::PathType::UserDir) / + "savedata/1" / m_games[itemID].serial); QString message_type = tr("Game"); if (selected == deleteUpdate) { @@ -363,6 +388,15 @@ public: folder_path = dlc_path; message_type = tr("DLC"); } + } else if (selected == deleteSaveData) { + if (!std::filesystem::exists(Common::FS::PathFromQString(save_data_path))) { + QMessageBox::critical(nullptr, tr("Error"), + QString(tr("This game has no save data to delete!"))); + error = true; + } else { + folder_path = save_data_path; + message_type = tr("Save Data"); + } } if (!error) { QString gameName = QString::fromStdString(m_games[itemID].name); @@ -374,7 +408,10 @@ public: QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { dir.removeRecursively(); - widget->removeRow(itemID); + if (selected == deleteGame) { + widget->removeRow(itemID); + m_games.removeAt(itemID); + } } } } diff --git a/src/qt_gui/install_dir_select.cpp b/src/qt_gui/install_dir_select.cpp index e0951b123..e90a10ee6 100644 --- a/src/qt_gui/install_dir_select.cpp +++ b/src/qt_gui/install_dir_select.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include #include @@ -15,10 +16,11 @@ #include "install_dir_select.h" InstallDirSelect::InstallDirSelect() : selected_dir() { - selected_dir = Config::getGameInstallDirs().empty() ? "" : Config::getGameInstallDirs().front(); + auto install_dirs = Config::getGameInstallDirs(); + selected_dir = install_dirs.empty() ? "" : install_dirs.front(); - if (!Config::getGameInstallDirs().empty() && Config::getGameInstallDirs().size() == 1) { - reject(); + if (!install_dirs.empty() && install_dirs.size() == 1) { + accept(); } auto layout = new QVBoxLayout(this); @@ -53,6 +55,14 @@ QWidget* InstallDirSelect::SetupInstallDirList() { vlayout->addWidget(m_path_list); + auto checkbox = new QCheckBox(tr("Install All Queued to Selected Folder")); + connect(checkbox, &QCheckBox::toggled, this, &InstallDirSelect::setUseForAllQueued); + vlayout->addWidget(checkbox); + + auto checkbox2 = new QCheckBox(tr("Delete PKG File on Install")); + connect(checkbox2, &QCheckBox::toggled, this, &InstallDirSelect::setDeleteFileOnInstall); + vlayout->addWidget(checkbox2); + group->setLayout(vlayout); return group; } @@ -66,6 +76,14 @@ void InstallDirSelect::setSelectedDirectory(QListWidgetItem* item) { } } +void InstallDirSelect::setUseForAllQueued(bool enabled) { + use_for_all_queued = enabled; +} + +void InstallDirSelect::setDeleteFileOnInstall(bool enabled) { + delete_file_on_install = enabled; +} + QWidget* InstallDirSelect::SetupDialogActions() { auto actions = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); diff --git a/src/qt_gui/install_dir_select.h b/src/qt_gui/install_dir_select.h index e3e81575a..e11cbf381 100644 --- a/src/qt_gui/install_dir_select.h +++ b/src/qt_gui/install_dir_select.h @@ -22,9 +22,21 @@ public: return selected_dir; } + bool useForAllQueued() { + return use_for_all_queued; + } + + bool deleteFileOnInstall() { + return delete_file_on_install; + } + private: QWidget* SetupInstallDirList(); QWidget* SetupDialogActions(); void setSelectedDirectory(QListWidgetItem* item); + void setDeleteFileOnInstall(bool enabled); + void setUseForAllQueued(bool enabled); std::filesystem::path selected_dir; + bool delete_file_on_install = false; + bool use_for_all_queued = false; }; diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 3ee392613..3678b3a82 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -725,9 +725,20 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int return; } auto category = psf.GetString("CATEGORY"); - InstallDirSelect ids; - ids.exec(); - auto game_install_dir = ids.getSelectedDirectory(); + + if (!use_for_all_queued || pkgNum == 1) { + InstallDirSelect ids; + const auto selected = ids.exec(); + if (selected == QDialog::Rejected) { + return; + } + + last_install_dir = ids.getSelectedDirectory(); + delete_file_on_install = ids.deleteFileOnInstall(); + use_for_all_queued = ids.useForAllQueued(); + } + std::filesystem::path game_install_dir = last_install_dir; + auto game_folder_path = game_install_dir / pkg.GetTitleID(); QString pkgType = QString::fromStdString(pkg.GetPkgFlags()); bool use_game_update = pkgType.contains("PATCH") && Config::getSeparateUpdateEnabled(); @@ -879,8 +890,14 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int if (pkgNum == nPkg) { QString path; Common::FS::PathToQString(path, game_install_dir); + QIcon windowIcon( + Common::FS::PathToUTF8String(game_folder_path / "sce_sys/icon0.png") + .c_str()); QMessageBox extractMsgBox(this); extractMsgBox.setWindowTitle(tr("Extraction Finished")); + if (!windowIcon.isNull()) { + extractMsgBox.setWindowIcon(windowIcon); + } extractMsgBox.setText( QString(tr("Game successfully installed at %1")).arg(path)); extractMsgBox.addButton(QMessageBox::Ok); @@ -894,6 +911,9 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int }); extractMsgBox.exec(); } + if (delete_file_on_install) { + std::filesystem::remove(file); + } }); connect(&dialog, &QProgressDialog::canceled, [&]() { futureWatcher.cancel(); }); connect(&futureWatcher, &QFutureWatcher::progressValueChanged, &dialog, diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index f4163defa..5ac56e44c 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -123,4 +123,8 @@ protected: } void resizeEvent(QResizeEvent* event) override; + + std::filesystem::path last_install_dir = ""; + bool delete_file_on_install = false; + bool use_for_all_queued = false; }; From 9bad66b24d4082240d00849a3d342d4d4a7c52d3 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 29 Jan 2025 01:29:52 -0800 Subject: [PATCH 66/68] hotfix: Raise videodec memory back up to 16MB. Found a game that needs more, still should be low enough compared to before to fix some games. --- src/core/libraries/videodec/videodec.cpp | 2 +- src/core/libraries/videodec/videodec2.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/videodec/videodec.cpp b/src/core/libraries/videodec/videodec.cpp index 79001e76f..02ea61509 100644 --- a/src/core/libraries/videodec/videodec.cpp +++ b/src/core/libraries/videodec/videodec.cpp @@ -9,7 +9,7 @@ namespace Libraries::Videodec { -static constexpr u64 kMinimumMemorySize = 8_MB; ///> Fake minimum memory size for querying +static constexpr u64 kMinimumMemorySize = 16_MB; ///> Fake minimum memory size for querying int PS4_SYSV_ABI sceVideodecCreateDecoder(const OrbisVideodecConfigInfo* pCfgInfoIn, const OrbisVideodecResourceInfo* pRsrcInfoIn, diff --git a/src/core/libraries/videodec/videodec2.cpp b/src/core/libraries/videodec/videodec2.cpp index be0db4ea3..a7e520b41 100644 --- a/src/core/libraries/videodec/videodec2.cpp +++ b/src/core/libraries/videodec/videodec2.cpp @@ -10,7 +10,7 @@ namespace Libraries::Vdec2 { -static constexpr u64 kMinimumMemorySize = 8_MB; ///> Fake minimum memory size for querying +static constexpr u64 kMinimumMemorySize = 16_MB; ///> Fake minimum memory size for querying s32 PS4_SYSV_ABI sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo) { From aa847de043e2e04512d5d01b4d9e40adc4706b0a Mon Sep 17 00:00:00 2001 From: isshininu Date: Wed, 29 Jan 2025 13:54:08 +0400 Subject: [PATCH 67/68] Update ru_RU translation (#2267) Several changes in ru_RU translation file. --- src/qt_gui/translations/ru_RU.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 6423f5ba4..0fddb1e7f 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -126,11 +126,11 @@ Copy All - Копировать все + Копировать всё Delete... - Удаление... + Удалить... Delete Game @@ -158,7 +158,7 @@ Submit a report - Отправить отчет + Отправить отчёт Shortcut creation @@ -297,7 +297,7 @@ Elf Viewer - Elf + Исполняемый файл Game Install Directory @@ -542,7 +542,7 @@ Fullscreen Mode - Режим Полного Экран + Тип полноэкранного режима Enable Separate Update Folder @@ -574,11 +574,11 @@ Trophy Key - Trophy Key + Ключ трофеев Trophy - Trophy + Трофеи Logger @@ -750,7 +750,7 @@ Audio Backend - Звуковая Подсистема + Звуковая подсистема Save @@ -826,7 +826,7 @@ disableTrophycheckBox - Отключить уведомления о трофеях:\nОтключает внутриигровые уведомления о трофеях. Прогресс трофеев по прежнему можно отслеживать в меню Просмотр трофеев (правая кнопка мыши по игре в главном окне). + Отключить уведомления о трофеях:\nОтключает внутриигровые уведомления о трофеях. Прогресс трофеев по прежнему можно отслеживать в меню просмотра трофеев (правая кнопка мыши по игре в главном окне). hideCursorGroupBox @@ -1192,11 +1192,11 @@ Play Time - Времени в игре + Время в игре Never Played - Вы не играли + Нет h @@ -1361,4 +1361,4 @@ ТБ - \ No newline at end of file + From 4bb578f9fb73ce77680e26621717db460a93b0e8 Mon Sep 17 00:00:00 2001 From: Missake212 Date: Wed, 29 Jan 2025 11:45:44 +0100 Subject: [PATCH 68/68] updates french translation (#2262) * updates the french translation * fix space at the end * forgot to translate a line * final changes * final changes 2 --- src/qt_gui/translations/fr.ts | 66 +++++++++++++++++------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 601ece8b4..0f87b087b 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -146,19 +146,19 @@ Compatibility... - Compatibility... + Compatibilité... Update database - Update database + Mettre à jour la base de données View report - View report + Voir rapport Submit a report - Submit a report + Soumettre un rapport Shortcut creation @@ -186,7 +186,7 @@ requiresEnableSeparateUpdateFolder_MSG - This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + Cette fonctionnalité nécessite l'option 'Dossier séparé pour les mises à jour' pour fonctionner. Si vous voulez utiliser cette fonctionnalité, veuillez l'activer. This game has no update to delete! @@ -546,7 +546,7 @@ Enable Separate Update Folder - Dossier séparé pour les mises à jours + Dossier séparé pour les mises à jour Default tab when opening settings @@ -574,11 +574,11 @@ Trophy Key - Trophy Key + Clé de trophée Trophy - Trophy + Trophée Logger @@ -586,11 +586,11 @@ Log Type - Type + Type de journal Log Filter - Filtre + Filtre du journal Open Log Location @@ -722,7 +722,7 @@ Disable Trophy Pop-ups - Disable Trophy Pop-ups + Désactiver les notifications de trophées Play title music @@ -730,19 +730,19 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + Mettre à jour la base de données de compatibilité au lancement Game Compatibility - Game Compatibility + Compatibilité du jeu Display Compatibility Data - Display Compatibility Data + Afficher les données de compatibilité Update Compatibility Database - Update Compatibility Database + Mettre à jour la base de données de compatibilité Volume @@ -750,7 +750,7 @@ Audio Backend - Audio Backend + Back-end audio Save @@ -786,7 +786,7 @@ separateUpdatesCheckBox - Dossier séparé pour les mises à jours:\nInstalle les mises à jours des jeux dans un dossier séparé pour une gestion plus facile. + Dossier séparé pour les mises à jour:\nInstalle les mises à jours des jeux dans un dossier séparé pour une gestion plus facile. showSplashCheckBox @@ -806,7 +806,7 @@ TrophyKey - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + Clé de trophées:\nClé utilisée pour décrypter les trophées. Doit être obtenu à partir de votre console jailbreakée.\nDoit contenir des caractères hexadécimaux uniquement. logTypeGroupBox @@ -826,7 +826,7 @@ disableTrophycheckBox - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + Désactiver les notifications de trophées:\nDésactive les notifications de trophées en jeu. La progression des trophées peut toujours être suivie à l'aide de la Visionneuse de trophées (clique droit sur le jeu sur la fenêtre principale). hideCursorGroupBox @@ -842,15 +842,15 @@ enableCompatibilityCheckBox - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + Afficher les données de compatibilité:\nAffiche les informations de compatibilité des jeux dans une colonne dédiée. Activez "Mettre à jour la compatibilité au démarrage" pour avoir des informations à jour. checkCompatibilityOnStartupCheckBox - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + Mettre à jour la compatibilité au démarrage:\nMettre à jour automatiquement la base de données de compatibilité au démarrage de shadPS4. updateCompatibilityButton - Update Compatibility Database:\nImmediately update the compatibility database. + Mettre à jour la compatibilité au démarrage:\nMet à jour immédiatement la base de données de compatibilité. Never @@ -1093,7 +1093,7 @@ DownloadComplete_MSG - Patchs téléchargés avec succès ! Tous les patches disponibles pour tous les jeux ont été téléchargés, il n'est pas nécessaire de les télécharger individuellement pour chaque jeu comme c'est le cas pour les Cheats. Si le correctif n'apparaît pas, il se peut qu'il n'existe pas pour le numéro de série et la version spécifiques du jeu. + Patchs téléchargés avec succès ! Tous les patches disponibles pour tous les jeux ont été téléchargés, il n'est pas nécessaire de les télécharger individuellement pour chaque jeu comme c'est le cas pour les Cheats. Si le correctif n'apparaît pas, il se peut qu'il n'existe pas pour la série et la version spécifiques du jeu. Failed to parse JSON data from HTML. @@ -1113,7 +1113,7 @@ You may need to update your game. - Vous devrez peut-être mettre à jour votre jeu. + Vous devriez peut-être mettre à jour votre jeu. Incompatibility Notice @@ -1137,7 +1137,7 @@ Directory does not exist: - Répertoire n'existe pas: + Le répertoire n'existe pas: Failed to open files.json for reading. @@ -1149,7 +1149,7 @@ Can't apply cheats before the game is started - Impossible d'appliquer les Cheats avant que le jeu ne commence. + Impossible d'appliquer les cheats avant que le jeu ne soit lancé @@ -1168,7 +1168,7 @@ Compatibility - Compatibility + Compatibilité Region @@ -1212,27 +1212,27 @@ Compatibility is untested - Compatibility is untested + La compatibilité n'a pas été testé Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + Le jeu ne se lance pas correctement / crash l'émulateur Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + Le jeu démarre, mais n'affiche qu'un écran noir Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + Le jeu affiche une image mais ne dépasse pas le menu Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + Le jeu a des problèmes majeurs ou des performances qui le rendent injouable Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + Le jeu peut être terminé avec des performances acceptables et sans problèmes majeurs