From 4019319d92b289e40fae8257e94562b949fca3e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?=
Date: Fri, 30 May 2025 21:04:31 +0200
Subject: [PATCH 01/19] Provide custom border color to samplers (#3014)
---
src/video_core/amdgpu/liverpool.h | 15 ++++++++-
.../renderer_vulkan/vk_rasterizer.cpp | 2 +-
src/video_core/texture_cache/sampler.cpp | 31 +++++++++++++++++--
src/video_core/texture_cache/sampler.h | 3 +-
.../texture_cache/texture_cache.cpp | 6 ++--
src/video_core/texture_cache/texture_cache.h | 4 ++-
6 files changed, 53 insertions(+), 8 deletions(-)
diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h
index a62141099..245e34d35 100644
--- a/src/video_core/amdgpu/liverpool.h
+++ b/src/video_core/amdgpu/liverpool.h
@@ -608,6 +608,16 @@ struct Liverpool {
}
};
+ struct BorderColorBufferBase {
+ u32 base_addr_lo;
+ BitField<0, 8, u32> base_addr_hi;
+
+ template
+ T Address() const {
+ return std::bit_cast(u64(base_addr_hi) << 40 | u64(base_addr_lo) << 8);
+ }
+ };
+
struct IndexBufferBase {
BitField<0, 8, u32> base_addr_hi;
u32 base_addr_lo;
@@ -1299,7 +1309,9 @@ struct Liverpool {
Scissor screen_scissor;
INSERT_PADDING_WORDS(0xA010 - 0xA00C - 2);
DepthBuffer depth_buffer;
- INSERT_PADDING_WORDS(0xA080 - 0xA018);
+ INSERT_PADDING_WORDS(8);
+ BorderColorBufferBase ta_bc_base;
+ INSERT_PADDING_WORDS(0xA080 - 0xA020 - 2);
WindowOffset window_offset;
ViewportScissor window_scissor;
INSERT_PADDING_WORDS(0xA08E - 0xA081 - 2);
@@ -1626,6 +1638,7 @@ static_assert(GFX6_3D_REG_INDEX(depth_htile_data_base) == 0xA005);
static_assert(GFX6_3D_REG_INDEX(screen_scissor) == 0xA00C);
static_assert(GFX6_3D_REG_INDEX(depth_buffer.z_info) == 0xA010);
static_assert(GFX6_3D_REG_INDEX(depth_buffer.depth_slice) == 0xA017);
+static_assert(GFX6_3D_REG_INDEX(ta_bc_base) == 0xA020);
static_assert(GFX6_3D_REG_INDEX(window_offset) == 0xA080);
static_assert(GFX6_3D_REG_INDEX(window_scissor) == 0xA081);
static_assert(GFX6_3D_REG_INDEX(color_target_mask) == 0xA08E);
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 4bdb08bf2..dff4e5a5f 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -716,7 +716,7 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin
ssharp.max_aniso.Assign(AmdGpu::AnisoRatio::One);
}
}
- const auto vk_sampler = texture_cache.GetSampler(ssharp);
+ const auto vk_sampler = texture_cache.GetSampler(ssharp, liverpool->regs.ta_bc_base);
image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral);
set_writes.push_back({
.dstSet = VK_NULL_HANDLE,
diff --git a/src/video_core/texture_cache/sampler.cpp b/src/video_core/texture_cache/sampler.cpp
index 8dbdd2912..e18c79a59 100644
--- a/src/video_core/texture_cache/sampler.cpp
+++ b/src/video_core/texture_cache/sampler.cpp
@@ -9,7 +9,8 @@
namespace VideoCore {
-Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler) {
+Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler,
+ const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base) {
if (sampler.force_degamma) {
LOG_WARNING(Render_Vulkan, "Texture requires gamma correction");
}
@@ -20,7 +21,33 @@ Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sample
const float maxAnisotropy =
anisotropyEnable ? std::clamp(sampler.MaxAniso(), 1.0f, instance.MaxSamplerAnisotropy())
: 1.0f;
+ auto borderColor = LiverpoolToVK::BorderColor(sampler.border_color_type);
+ if (!instance.IsCustomBorderColorSupported()) {
+ LOG_WARNING(Render_Vulkan, "Custom border color is not supported, falling back to black");
+ borderColor = vk::BorderColor::eFloatOpaqueBlack;
+ }
+
+ const auto customColor = [&]() -> std::optional {
+ if (borderColor == vk::BorderColor::eFloatCustomEXT) {
+ const auto borderColorIndex = sampler.border_color_ptr.Value();
+ const auto borderColorBuffer = border_color_base.Address*>();
+ const auto customBorderColorArray = borderColorBuffer[borderColorIndex];
+
+ const vk::SamplerCustomBorderColorCreateInfoEXT ret{
+ .customBorderColor =
+ vk::ClearColorValue{
+ .float32 = customBorderColorArray,
+ },
+ .format = vk::Format::eR32G32B32A32Sfloat,
+ };
+ return ret;
+ } else {
+ return std::nullopt;
+ }
+ }();
+
const vk::SamplerCreateInfo sampler_ci = {
+ .pNext = customColor ? &*customColor : nullptr,
.magFilter = LiverpoolToVK::Filter(sampler.xy_mag_filter),
.minFilter = LiverpoolToVK::Filter(sampler.xy_min_filter),
.mipmapMode = LiverpoolToVK::MipFilter(sampler.mip_filter),
@@ -34,7 +61,7 @@ Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sample
.compareOp = LiverpoolToVK::DepthCompare(sampler.depth_compare_func),
.minLod = sampler.MinLod(),
.maxLod = sampler.MaxLod(),
- .borderColor = LiverpoolToVK::BorderColor(sampler.border_color_type),
+ .borderColor = borderColor,
.unnormalizedCoordinates = false, // Handled in shader due to Vulkan limitations.
};
auto [sampler_result, smplr] = instance.GetDevice().createSamplerUnique(sampler_ci);
diff --git a/src/video_core/texture_cache/sampler.h b/src/video_core/texture_cache/sampler.h
index 856d39763..28ba0f67b 100644
--- a/src/video_core/texture_cache/sampler.h
+++ b/src/video_core/texture_cache/sampler.h
@@ -14,7 +14,8 @@ namespace VideoCore {
class Sampler {
public:
- explicit Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler);
+ explicit Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler,
+ const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base);
~Sampler();
Sampler(const Sampler&) = delete;
diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp
index ddb4ea799..63cfc4431 100644
--- a/src/video_core/texture_cache/texture_cache.cpp
+++ b/src/video_core/texture_cache/texture_cache.cpp
@@ -636,9 +636,11 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule
image.flags &= ~ImageFlagBits::Dirty;
}
-vk::Sampler TextureCache::GetSampler(const AmdGpu::Sampler& sampler) {
+vk::Sampler TextureCache::GetSampler(
+ const AmdGpu::Sampler& sampler,
+ const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base) {
const u64 hash = XXH3_64bits(&sampler, sizeof(sampler));
- const auto [it, new_sampler] = samplers.try_emplace(hash, instance, sampler);
+ const auto [it, new_sampler] = samplers.try_emplace(hash, instance, sampler, border_color_base);
return it->second.Handle();
}
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index b6bf88958..ccfeb36b2 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -139,7 +139,9 @@ public:
void RefreshImage(Image& image, Vulkan::Scheduler* custom_scheduler = nullptr);
/// Retrieves the sampler that matches the provided S# descriptor.
- [[nodiscard]] vk::Sampler GetSampler(const AmdGpu::Sampler& sampler);
+ [[nodiscard]] vk::Sampler GetSampler(
+ const AmdGpu::Sampler& sampler,
+ const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base);
/// Retrieves the image with the specified id.
[[nodiscard]] Image& GetImage(ImageId id) {
From c09e463b8ee86233ee883c1357bc883ce97bccaa Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Sat, 31 May 2025 09:35:52 -0500
Subject: [PATCH 02/19] Revert new GPU map logic (#3019)
Something's wrong somewhere, and there's just too many places that somewhere could be for me to debug it right now.
---
src/core/memory.cpp | 51 ++++++++++++++-------------------------------
1 file changed, 16 insertions(+), 35 deletions(-)
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index ab59219b2..bf9d1cabd 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -262,10 +262,7 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot)
void* out_addr = impl.Map(mapped_addr, size, alignment, -1, false);
TRACK_ALLOC(out_addr, size, "VMEM");
- if (prot >= MemoryProt::GpuRead) {
- // PS4s only map to GPU memory when the protection includes GPU access.
- // If the address to map to is too high, PS4s throw a page fault and crash.
- ASSERT_MSG(IsValidGpuMapping(mapped_addr, size), "Invalid address for GPU mapping");
+ if (IsValidGpuMapping(mapped_addr, size)) {
rasterizer->MapMemory(mapped_addr, size);
}
@@ -345,19 +342,15 @@ s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, Memo
MergeAdjacent(vma_map, new_vma_handle);
}
- if (prot >= MemoryProt::GpuRead) {
- // PS4s only map to GPU memory when the protection includes GPU access.
- // If the address to map to is too high, PS4s throw a page fault and crash.
- ASSERT_MSG(IsValidGpuMapping(mapped_addr, size), "Invalid address for GPU mapping");
- rasterizer->MapMemory(mapped_addr, size);
- }
-
if (type == VMAType::Reserved || type == VMAType::PoolReserved) {
// For Reserved/PoolReserved mappings, we don't perform any address space allocations.
// Just set out_addr to mapped_addr instead.
*out_addr = std::bit_cast(mapped_addr);
} else {
- // Type is either Direct, Flexible, or Code, these need to be mapped in our address space.
+ // If this is not a reservation, then map to GPU and address space
+ if (IsValidGpuMapping(mapped_addr, size)) {
+ rasterizer->MapMemory(mapped_addr, size);
+ }
*out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec);
}
@@ -429,7 +422,6 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) {
const bool is_exec = vma_base.is_exec;
const auto start_in_vma = virtual_addr - vma_base_addr;
const auto type = vma_base.type;
- const auto prot = vma_base.prot;
if (type != VMAType::PoolReserved && type != VMAType::Pooled) {
LOG_ERROR(Kernel_Vmm, "Attempting to decommit non-pooled memory!");
@@ -437,15 +429,15 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) {
}
if (type == VMAType::Pooled) {
+ // We always map PoolCommitted memory to GPU, so unmap when decomitting.
+ if (IsValidGpuMapping(virtual_addr, size)) {
+ rasterizer->UnmapMemory(virtual_addr, size);
+ }
+
// Track how much pooled memory is decommitted
pool_budget += size;
}
- if (prot >= MemoryProt::GpuRead) {
- // If this mapping has GPU access, unmap from GPU.
- rasterizer->UnmapMemory(virtual_addr, size);
- }
-
// Mark region as free and attempt to coalesce it with neighbours.
const auto new_it = CarveVMA(virtual_addr, size);
auto& vma = new_it->second;
@@ -486,15 +478,11 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma
if (type == VMAType::Free) {
return adjusted_size;
}
+
if (type == VMAType::Flexible) {
flexible_usage -= adjusted_size;
}
- if (prot >= MemoryProt::GpuRead) {
- // If this mapping has GPU access, unmap from GPU.
- rasterizer->UnmapMemory(virtual_addr, size);
- }
-
// Mark region as free and attempt to coalesce it with neighbours.
const auto new_it = CarveVMA(virtual_addr, adjusted_size);
auto& vma = new_it->second;
@@ -507,6 +495,11 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma
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) {
+ // If this mapping has GPU access, unmap from GPU.
+ if (IsValidGpuMapping(virtual_addr, size)) {
+ rasterizer->UnmapMemory(virtual_addr, size);
+ }
+
// Unmap the memory region.
impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + adjusted_size,
phys_base, is_exec, has_backing, readonly_file);
@@ -576,18 +569,6 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s
return ORBIS_KERNEL_ERROR_EINVAL;
}
- if (vma_base.prot < MemoryProt::GpuRead && prot >= MemoryProt::GpuRead) {
- // New protection will give the GPU access to this VMA, perform a rasterizer map
- ASSERT_MSG(IsValidGpuMapping(addr, size), "Invalid address for GPU mapping");
- rasterizer->MapMemory(addr, size);
- }
-
- if (vma_base.prot >= MemoryProt::GpuRead && prot < MemoryProt::GpuRead) {
- // New protection will remove the GPU's access to this VMA, perform a rasterizer unmap
- ASSERT_MSG(IsValidGpuMapping(addr, size), "Invalid address for GPU unmap");
- rasterizer->UnmapMemory(addr, size);
- }
-
// Change protection
vma_base.prot = prot;
From bb199865cf2f09695535d98db38dc7c78fd2d054 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com>
Date: Sun, 1 Jun 2025 13:52:09 +0200
Subject: [PATCH 03/19] Qt: Set Minimum Icon Size List to 48 (#3018)
---
src/qt_gui/main_window.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp
index 1966aa52b..77b088b78 100644
--- a/src/qt_gui/main_window.cpp
+++ b/src/qt_gui/main_window.cpp
@@ -387,9 +387,9 @@ void MainWindow::CreateConnects() {
connect(ui->sizeSlider, &QSlider::valueChanged, this, [this](int value) {
if (isTableList) {
m_game_list_frame->icon_size =
- 36 + value; // 36 is the minimum icon size to use due to text disappearing.
- m_game_list_frame->ResizeIcons(36 + value);
- Config::setIconSize(36 + value);
+ 48 + value; // 48 is the minimum icon size to use due to text disappearing.
+ m_game_list_frame->ResizeIcons(48 + value);
+ Config::setIconSize(48 + value);
Config::setSliderPosition(value);
} else {
m_game_grid_frame->icon_size = 69 + value;
From b1af1334c91382d0e28b9cec06f4604d93357bea Mon Sep 17 00:00:00 2001
From: WujekFoliarz <72314465+WujekFoliarz@users.noreply.github.com>
Date: Sun, 1 Jun 2025 18:13:02 +0200
Subject: [PATCH 04/19] Fix touchpad handling and change gyro calculation
(#3006)
* Change touchpad handling and orientation calculation
* remove unnecessary includes in pad.cpp
* remove the cmake command arguments
* remove the weird file
* try to fix formatting
* limit new gyro and touchpad logic to controller 1
* remove cout
* fix formatting and add the handle check to scePadRead
* swap y and z back
---
src/core/libraries/pad/pad.cpp | 137 +++++++++++++++++++++++++++++---
src/input/controller.cpp | 141 ++++++++++++++++++++-------------
src/input/controller.h | 22 +++++
3 files changed, 232 insertions(+), 68 deletions(-)
diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp
index 5dfc68e90..42582783b 100644
--- a/src/core/libraries/pad/pad.cpp
+++ b/src/core/libraries/pad/pad.cpp
@@ -316,22 +316,79 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
pData[i].angularVelocity.y = states[i].angularVelocity.y;
pData[i].angularVelocity.z = states[i].angularVelocity.z;
pData[i].orientation = {0.0f, 0.0f, 0.0f, 1.0f};
- if (engine) {
+ pData[i].acceleration.x = states[i].acceleration.x * 0.098;
+ pData[i].acceleration.y = states[i].acceleration.y * 0.098;
+ pData[i].acceleration.z = states[i].acceleration.z * 0.098;
+ pData[i].angularVelocity.x = states[i].angularVelocity.x;
+ pData[i].angularVelocity.y = states[i].angularVelocity.y;
+ pData[i].angularVelocity.z = states[i].angularVelocity.z;
+
+ if (engine && handle == 1) {
const auto gyro_poll_rate = engine->GetAccelPollRate();
if (gyro_poll_rate != 0.0f) {
- GameController::CalculateOrientation(pData[i].acceleration,
- pData[i].angularVelocity,
- 1.0f / gyro_poll_rate, pData[i].orientation);
+ auto now = std::chrono::steady_clock::now();
+ float deltaTime = std::chrono::duration_cast(
+ now - controller->GetLastUpdate())
+ .count() /
+ 1000000.0f;
+ controller->SetLastUpdate(now);
+ Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation();
+ Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
+ GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
+ deltaTime, lastOrientation, outputOrientation);
+ pData[i].orientation = outputOrientation;
+ controller->SetLastOrientation(outputOrientation);
}
}
+
pData[i].touchData.touchNum =
(states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0);
+
+ if (handle == 1) {
+ if (controller->GetTouchCount() >= 127) {
+ controller->SetTouchCount(0);
+ }
+
+ if (controller->GetSecondaryTouchCount() >= 127) {
+ controller->SetSecondaryTouchCount(0);
+ }
+
+ if (pData->touchData.touchNum == 1 && controller->GetPreviousTouchNum() == 0) {
+ controller->SetTouchCount(controller->GetTouchCount() + 1);
+ controller->SetSecondaryTouchCount(controller->GetTouchCount());
+ } else if (pData->touchData.touchNum == 2 && controller->GetPreviousTouchNum() == 1) {
+ controller->SetSecondaryTouchCount(controller->GetSecondaryTouchCount() + 1);
+ } else if (pData->touchData.touchNum == 0 && controller->GetPreviousTouchNum() > 0) {
+ if (controller->GetTouchCount() < controller->GetSecondaryTouchCount()) {
+ controller->SetTouchCount(controller->GetSecondaryTouchCount());
+ } else {
+ if (controller->WasSecondaryTouchReset()) {
+ controller->SetTouchCount(controller->GetSecondaryTouchCount());
+ controller->UnsetSecondaryTouchResetBool();
+ }
+ }
+ }
+
+ controller->SetPreviousTouchNum(pData->touchData.touchNum);
+
+ if (pData->touchData.touchNum == 1) {
+ states[i].touchpad[0].ID = controller->GetTouchCount();
+ states[i].touchpad[1].ID = 0;
+ } else if (pData->touchData.touchNum == 2) {
+ states[i].touchpad[0].ID = controller->GetTouchCount();
+ states[i].touchpad[1].ID = controller->GetSecondaryTouchCount();
+ }
+ } else {
+ states[i].touchpad[0].ID = 1;
+ states[i].touchpad[1].ID = 2;
+ }
+
pData[i].touchData.touch[0].x = states[i].touchpad[0].x;
pData[i].touchData.touch[0].y = states[i].touchpad[0].y;
- pData[i].touchData.touch[0].id = 1;
+ pData[i].touchData.touch[0].id = states[i].touchpad[0].ID;
pData[i].touchData.touch[1].x = states[i].touchpad[1].x;
pData[i].touchData.touch[1].y = states[i].touchpad[1].y;
- pData[i].touchData.touch[1].id = 2;
+ pData[i].touchData.touch[1].id = states[i].touchpad[1].ID;
pData[i].connected = connected;
pData[i].timestamp = states[i].time;
pData[i].connectedCount = connected_count;
@@ -376,31 +433,85 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
pData->leftStick.x = state.axes[static_cast(Input::Axis::LeftX)];
pData->leftStick.y = state.axes[static_cast(Input::Axis::LeftY)];
pData->rightStick.x = state.axes[static_cast(Input::Axis::RightX)];
+ pData->rightStick.x = state.axes[static_cast(Input::Axis::RightX)];
pData->rightStick.y = state.axes[static_cast(Input::Axis::RightY)];
pData->analogButtons.l2 = state.axes[static_cast(Input::Axis::TriggerLeft)];
pData->analogButtons.r2 = state.axes[static_cast(Input::Axis::TriggerRight)];
- pData->acceleration.x = state.acceleration.x;
- pData->acceleration.y = state.acceleration.y;
- pData->acceleration.z = state.acceleration.z;
+ pData->acceleration.x = state.acceleration.x * 0.098;
+ pData->acceleration.y = state.acceleration.y * 0.098;
+ pData->acceleration.z = state.acceleration.z * 0.098;
pData->angularVelocity.x = state.angularVelocity.x;
pData->angularVelocity.y = state.angularVelocity.y;
pData->angularVelocity.z = state.angularVelocity.z;
pData->orientation = {0.0f, 0.0f, 0.0f, 1.0f};
- if (engine) {
+
+ // Only do this on handle 1 for now
+ if (engine && handle == 1) {
const auto gyro_poll_rate = engine->GetAccelPollRate();
if (gyro_poll_rate != 0.0f) {
+ auto now = std::chrono::steady_clock::now();
+ float deltaTime = std::chrono::duration_cast(
+ now - controller->GetLastUpdate())
+ .count() /
+ 1000000.0f;
+ controller->SetLastUpdate(now);
+ Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation();
+ Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
- 1.0f / gyro_poll_rate, pData->orientation);
+ deltaTime, lastOrientation, outputOrientation);
+ pData->orientation = outputOrientation;
+ controller->SetLastOrientation(outputOrientation);
}
}
pData->touchData.touchNum =
(state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0);
+
+ // Only do this on handle 1 for now
+ if (handle == 1) {
+ if (controller->GetTouchCount() >= 127) {
+ controller->SetTouchCount(0);
+ }
+
+ if (controller->GetSecondaryTouchCount() >= 127) {
+ controller->SetSecondaryTouchCount(0);
+ }
+
+ if (pData->touchData.touchNum == 1 && controller->GetPreviousTouchNum() == 0) {
+ controller->SetTouchCount(controller->GetTouchCount() + 1);
+ controller->SetSecondaryTouchCount(controller->GetTouchCount());
+ } else if (pData->touchData.touchNum == 2 && controller->GetPreviousTouchNum() == 1) {
+ controller->SetSecondaryTouchCount(controller->GetSecondaryTouchCount() + 1);
+ } else if (pData->touchData.touchNum == 0 && controller->GetPreviousTouchNum() > 0) {
+ if (controller->GetTouchCount() < controller->GetSecondaryTouchCount()) {
+ controller->SetTouchCount(controller->GetSecondaryTouchCount());
+ } else {
+ if (controller->WasSecondaryTouchReset()) {
+ controller->SetTouchCount(controller->GetSecondaryTouchCount());
+ controller->UnsetSecondaryTouchResetBool();
+ }
+ }
+ }
+
+ controller->SetPreviousTouchNum(pData->touchData.touchNum);
+
+ if (pData->touchData.touchNum == 1) {
+ state.touchpad[0].ID = controller->GetTouchCount();
+ state.touchpad[1].ID = 0;
+ } else if (pData->touchData.touchNum == 2) {
+ state.touchpad[0].ID = controller->GetTouchCount();
+ state.touchpad[1].ID = controller->GetSecondaryTouchCount();
+ }
+ } else {
+ state.touchpad[0].ID = 1;
+ state.touchpad[1].ID = 2;
+ }
+
pData->touchData.touch[0].x = state.touchpad[0].x;
pData->touchData.touch[0].y = state.touchpad[0].y;
- pData->touchData.touch[0].id = 1;
+ pData->touchData.touch[0].id = state.touchpad[0].ID;
pData->touchData.touch[1].x = state.touchpad[1].x;
pData->touchData.touch[1].y = state.touchpad[1].y;
- pData->touchData.touch[1].id = 2;
+ pData->touchData.touch[1].id = state.touchpad[1].ID;
pData->timestamp = state.time;
pData->connected = true; // isConnected; //TODO fix me proper
pData->connectedCount = 1; // connectedCount;
diff --git a/src/input/controller.cpp b/src/input/controller.cpp
index bb8db9a7c..42cabb837 100644
--- a/src/input/controller.cpp
+++ b/src/input/controller.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include
@@ -165,69 +165,37 @@ void GameController::Acceleration(int id, const float acceleration[3]) {
AddState(state);
}
-// Stolen from
-// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
-float eInt[3] = {0.0f, 0.0f, 0.0f}; // Integral error terms
-const float Kp = 50.0f; // Proportional gain
-const float Ki = 1.0f; // Integral gain
-Libraries::Pad::OrbisFQuaternion o = {1, 0, 0, 0};
void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime,
+ Libraries::Pad::OrbisFQuaternion& lastOrientation,
Libraries::Pad::OrbisFQuaternion& orientation) {
- float ax = acceleration.x, ay = acceleration.y, az = acceleration.z;
- float gx = angularVelocity.x, gy = angularVelocity.y, gz = angularVelocity.z;
+ Libraries::Pad::OrbisFQuaternion q = lastOrientation;
+ Libraries::Pad::OrbisFQuaternion ω = {angularVelocity.x, angularVelocity.y, angularVelocity.z,
+ 0.0f};
- float q1 = o.w, q2 = o.x, q3 = o.y, q4 = o.z;
+ Libraries::Pad::OrbisFQuaternion qω = {q.w * ω.x + q.x * ω.w + q.y * ω.z - q.z * ω.y,
+ q.w * ω.y + q.y * ω.w + q.z * ω.x - q.x * ω.z,
+ q.w * ω.z + q.z * ω.w + q.x * ω.y - q.y * ω.x,
+ q.w * ω.w - q.x * ω.x - q.y * ω.y - q.z * ω.z};
- // Normalize accelerometer measurement
- float norm = std::sqrt(ax * ax + ay * ay + az * az);
- if (norm == 0.0f || deltaTime == 0.0f)
- return; // Handle NaN
- norm = 1.0f / norm;
- ax *= norm;
- ay *= norm;
- az *= norm;
+ Libraries::Pad::OrbisFQuaternion qDot = {0.5f * qω.x, 0.5f * qω.y, 0.5f * qω.z, 0.5f * qω.w};
- // Estimated direction of gravity
- float vx = 2.0f * (q2 * q4 - q1 * q3);
- float vy = 2.0f * (q1 * q2 + q3 * q4);
- float vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
+ q.x += qDot.x * deltaTime;
+ q.y += qDot.y * deltaTime;
+ q.z += qDot.z * deltaTime;
+ q.w += qDot.w * deltaTime;
- // Error is cross product between estimated direction and measured direction of gravity
- float ex = (ay * vz - az * vy);
- float ey = (az * vx - ax * vz);
- float ez = (ax * vy - ay * vx);
- if (Ki > 0.0f) {
- eInt[0] += ex * deltaTime; // Accumulate integral error
- eInt[1] += ey * deltaTime;
- eInt[2] += ez * deltaTime;
- } else {
- eInt[0] = eInt[1] = eInt[2] = 0.0f; // Prevent integral wind-up
- }
+ float norm = std::sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
+ q.x /= norm;
+ q.y /= norm;
+ q.z /= norm;
+ q.w /= norm;
- // Apply feedback terms
- gx += Kp * ex + Ki * eInt[0];
- gy += Kp * ey + Ki * eInt[1];
- gz += Kp * ez + Ki * eInt[2];
-
- //// Integrate rate of change of quaternion
- q1 += (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltaTime);
- q2 += (q1 * gx + q3 * gz - q4 * gy) * (0.5f * deltaTime);
- q3 += (q1 * gy - q2 * gz + q4 * gx) * (0.5f * deltaTime);
- q4 += (q1 * gz + q2 * gy - q3 * gx) * (0.5f * deltaTime);
-
- // Normalize quaternion
- norm = std::sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4);
- norm = 1.0f / norm;
- orientation.w = q1 * norm;
- orientation.x = q2 * norm;
- orientation.y = q3 * norm;
- orientation.z = q4 * norm;
- o.w = q1 * norm;
- o.x = q2 * norm;
- o.y = q3 * norm;
- o.z = q4 * norm;
+ orientation.x = q.x;
+ orientation.y = q.y;
+ orientation.z = q.z;
+ orientation.w = q.w;
LOG_DEBUG(Lib_Pad, "Calculated orientation: {:.2f} {:.2f} {:.2f} {:.2f}", orientation.x,
orientation.y, orientation.z, orientation.w);
}
@@ -260,6 +228,69 @@ void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, f
}
}
+u8 GameController::GetTouchCount() {
+ std::scoped_lock lock{m_mutex};
+ return m_touch_count;
+}
+
+void GameController::SetTouchCount(u8 touchCount) {
+ std::scoped_lock lock{m_mutex};
+ m_touch_count = touchCount;
+}
+
+u8 GameController::GetSecondaryTouchCount() {
+ std::scoped_lock lock{m_mutex};
+ return m_secondary_touch_count;
+}
+
+void GameController::SetSecondaryTouchCount(u8 touchCount) {
+ std::scoped_lock lock{m_mutex};
+ m_secondary_touch_count = touchCount;
+ if (touchCount == 0) {
+ m_was_secondary_reset = true;
+ }
+}
+
+u8 GameController::GetPreviousTouchNum() {
+ std::scoped_lock lock{m_mutex};
+ return m_previous_touchnum;
+}
+
+void GameController::SetPreviousTouchNum(u8 touchNum) {
+ std::scoped_lock lock{m_mutex};
+ m_previous_touchnum = touchNum;
+}
+
+bool GameController::WasSecondaryTouchReset() {
+ std::scoped_lock lock{m_mutex};
+ return m_was_secondary_reset;
+}
+
+void GameController::UnsetSecondaryTouchResetBool() {
+ std::scoped_lock lock{m_mutex};
+ m_was_secondary_reset = false;
+}
+
+void GameController::SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation) {
+ std::scoped_lock lock{m_mutex};
+ m_orientation = orientation;
+}
+
+Libraries::Pad::OrbisFQuaternion GameController::GetLastOrientation() {
+ std::scoped_lock lock{m_mutex};
+ return m_orientation;
+}
+
+std::chrono::steady_clock::time_point GameController::GetLastUpdate() {
+ std::scoped_lock lock{m_mutex};
+ return m_last_update;
+}
+
+void GameController::SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate) {
+ std::scoped_lock lock{m_mutex};
+ m_last_update = lastUpdate;
+}
+
void GameController::SetEngine(std::unique_ptr engine) {
std::scoped_lock _{m_mutex};
m_engine = std::move(engine);
diff --git a/src/input/controller.h b/src/input/controller.h
index bbaed75ea..f427a55ec 100644
--- a/src/input/controller.h
+++ b/src/input/controller.h
@@ -23,6 +23,7 @@ enum class Axis {
};
struct TouchpadEntry {
+ u8 ID = 0;
bool state{};
u16 x{};
u16 y{};
@@ -82,9 +83,23 @@ public:
Engine* GetEngine();
u32 Poll();
+ u8 GetTouchCount();
+ void SetTouchCount(u8 touchCount);
+ u8 GetSecondaryTouchCount();
+ void SetSecondaryTouchCount(u8 touchCount);
+ u8 GetPreviousTouchNum();
+ void SetPreviousTouchNum(u8 touchNum);
+ bool WasSecondaryTouchReset();
+ void UnsetSecondaryTouchResetBool();
+
+ void SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation);
+ Libraries::Pad::OrbisFQuaternion GetLastOrientation();
+ std::chrono::steady_clock::time_point GetLastUpdate();
+ void SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate);
static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime,
+ Libraries::Pad::OrbisFQuaternion& lastOrientation,
Libraries::Pad::OrbisFQuaternion& orientation);
private:
@@ -98,8 +113,15 @@ private:
int m_connected_count = 0;
u32 m_states_num = 0;
u32 m_first_state = 0;
+ u8 m_touch_count = 0;
+ u8 m_secondary_touch_count = 0;
+ u8 m_previous_touch_count = 0;
+ u8 m_previous_touchnum = 0;
+ bool m_was_secondary_reset = false;
std::array m_states;
std::array m_private;
+ std::chrono::steady_clock::time_point m_last_update = {};
+ Libraries::Pad::OrbisFQuaternion m_orientation = {0.0f, 0.0f, 0.0f, 1.0f};
std::unique_ptr m_engine = nullptr;
};
From 6bdd83684bc8f3a9bd7e960ae55eea669fc152dd Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Sun, 1 Jun 2025 11:30:03 -0500
Subject: [PATCH 05/19] Libs: libSceVoice stubs (#3022)
* voice lib stubs
Primarily for GTA V
* Clang
---
CMakeLists.txt | 2 +
src/common/logging/filter.cpp | 1 +
src/common/logging/types.h | 1 +
src/core/libraries/libs.cpp | 2 +
src/core/libraries/voice/voice.cpp | 203 +++++++++++++++++++++++++++++
src/core/libraries/voice/voice.h | 56 ++++++++
6 files changed, 265 insertions(+)
create mode 100644 src/core/libraries/voice/voice.cpp
create mode 100644 src/core/libraries/voice/voice.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f85d22b6c..20d33ac95 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -296,6 +296,8 @@ set(AJM_LIB src/core/libraries/ajm/ajm.cpp
set(AUDIO_LIB src/core/libraries/audio/audioin.cpp
src/core/libraries/audio/audioin.h
+ src/core/libraries/voice/voice.cpp
+ src/core/libraries/voice/voice.h
src/core/libraries/audio/audioout.cpp
src/core/libraries/audio/audioout.h
src/core/libraries/audio/audioout_backend.h
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index fd0614e1b..05935fbdc 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -141,6 +141,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, Camera) \
SUB(Lib, CompanionHttpd) \
SUB(Lib, CompanionUtil) \
+ SUB(Lib, Voice) \
CLS(Frontend) \
CLS(Render) \
SUB(Render, Vulkan) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index bbfd7455b..1da84b219 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -98,6 +98,7 @@ enum class Class : u8 {
Lib_Fiber, ///< The LibSceFiber implementation.
Lib_Vdec2, ///< The LibSceVideodec2 implementation.
Lib_Videodec, ///< The LibSceVideodec implementation.
+ Lib_Voice, ///< The LibSceVoice implementation.
Lib_RazorCpu, ///< The LibRazorCpu implementation.
Lib_Mouse, ///< The LibSceMouse implementation
Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation
diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp
index 45b32846f..762c1e762 100644
--- a/src/core/libraries/libs.cpp
+++ b/src/core/libraries/libs.cpp
@@ -60,6 +60,7 @@
#include "core/libraries/videodec/videodec.h"
#include "core/libraries/videodec/videodec2.h"
#include "core/libraries/videoout/video_out.h"
+#include "core/libraries/voice/voice.h"
#include "core/libraries/web_browser_dialog/webbrowserdialog.h"
#include "core/libraries/zlib/zlib_sce.h"
#include "fiber/fiber.h"
@@ -128,6 +129,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
Libraries::Camera::RegisterlibSceCamera(sym);
Libraries::CompanionHttpd::RegisterlibSceCompanionHttpd(sym);
Libraries::CompanionUtil::RegisterlibSceCompanionUtil(sym);
+ Libraries::Voice::RegisterlibSceVoice(sym);
}
} // namespace Libraries
diff --git a/src/core/libraries/voice/voice.cpp b/src/core/libraries/voice/voice.cpp
new file mode 100644
index 000000000..caa16431a
--- /dev/null
+++ b/src/core/libraries/voice/voice.cpp
@@ -0,0 +1,203 @@
+// 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/voice/voice.h"
+
+namespace Libraries::Voice {
+
+s32 PS4_SYSV_ABI sceVoiceConnectIPortToOPort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceCreatePort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceDeletePort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceDisconnectIPortFromOPort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceEnd() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetBitRate(u32 port_id, u32* bitrate) {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ *bitrate = 48000;
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetMuteFlag() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetPortAttr() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetPortInfo(u32 port_id, OrbisVoicePortInfo* info) {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ info->port_type = 0;
+ info->state = 3;
+ info->byte_count = 0;
+ info->frame_size = 1;
+ info->edge_count = 0;
+ info->reserved = 0;
+
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetResourceInfo() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetVolume() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceInit() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceInitHQ() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoicePausePort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoicePausePortAll() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceReadFromOPort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceResetPort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceResumePort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceResumePortAll() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceSetBitRate() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceSetMuteFlag() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceSetMuteFlagAll() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceSetThreadsParams() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceSetVolume() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceStart() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceStop() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceUpdatePort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceVADAdjustment() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceVADSetVersion() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceWriteToIPort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+void RegisterlibSceVoice(Core::Loader::SymbolsResolver* sym) {
+ LIB_FUNCTION("oV9GAdJ23Gw", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceConnectIPortToOPort);
+ LIB_FUNCTION("nXpje5yNpaE", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceCreatePort);
+ LIB_FUNCTION("b7kJI+nx2hg", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceDeletePort);
+ LIB_FUNCTION("ajVj3QG2um4", "libSceVoice", 1, "libSceVoice", 0, 0,
+ sceVoiceDisconnectIPortFromOPort);
+ LIB_FUNCTION("Oo0S5PH7FIQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceEnd);
+ LIB_FUNCTION("cJLufzou6bc", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetBitRate);
+ LIB_FUNCTION("Pc4z1QjForU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetMuteFlag);
+ LIB_FUNCTION("elcxZTEfHZM", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetPortAttr);
+ LIB_FUNCTION("CrLqDwWLoXM", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetPortInfo);
+ LIB_FUNCTION("Z6QV6j7igvE", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetResourceInfo);
+ LIB_FUNCTION("jjkCjneOYSs", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetVolume);
+ LIB_FUNCTION("9TrhuGzberQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceInit);
+ LIB_FUNCTION("IPHvnM5+g04", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceInitHQ);
+ LIB_FUNCTION("x0slGBQW+wY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoicePausePort);
+ LIB_FUNCTION("Dinob0yMRl8", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoicePausePortAll);
+ LIB_FUNCTION("cQ6DGsQEjV4", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceReadFromOPort);
+ LIB_FUNCTION("udAxvCePkUs", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResetPort);
+ LIB_FUNCTION("gAgN+HkiEzY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResumePort);
+ LIB_FUNCTION("jbkJFmOZ9U0", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResumePortAll);
+ LIB_FUNCTION("TexwmOHQsDg", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetBitRate);
+ LIB_FUNCTION("gwUynkEgNFY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetMuteFlag);
+ LIB_FUNCTION("oUha0S-Ij9Q", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetMuteFlagAll);
+ LIB_FUNCTION("clyKUyi3RYU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetThreadsParams);
+ LIB_FUNCTION("QBFoAIjJoXQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetVolume);
+ LIB_FUNCTION("54phPH2LZls", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceStart);
+ LIB_FUNCTION("Ao2YNSA7-Qo", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceStop);
+ LIB_FUNCTION("jSZNP7xJrcw", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceUpdatePort);
+ LIB_FUNCTION("hg9T73LlRiU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceVADAdjustment);
+ LIB_FUNCTION("wFeAxEeEi-8", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceVADSetVersion);
+ LIB_FUNCTION("YeJl6yDlhW0", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceWriteToIPort);
+};
+
+} // namespace Libraries::Voice
\ No newline at end of file
diff --git a/src/core/libraries/voice/voice.h b/src/core/libraries/voice/voice.h
new file mode 100644
index 000000000..8f008f2cc
--- /dev/null
+++ b/src/core/libraries/voice/voice.h
@@ -0,0 +1,56 @@
+// 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::Voice {
+
+struct OrbisVoicePortInfo {
+ s32 port_type;
+ s32 state;
+ u32* edge;
+ u32 byte_count;
+ u32 frame_size;
+ u16 edge_count;
+ u16 reserved;
+};
+
+s32 PS4_SYSV_ABI sceVoiceConnectIPortToOPort();
+s32 PS4_SYSV_ABI sceVoiceCreatePort();
+s32 PS4_SYSV_ABI sceVoiceDeletePort();
+s32 PS4_SYSV_ABI sceVoiceDisconnectIPortFromOPort();
+s32 PS4_SYSV_ABI sceVoiceEnd();
+s32 PS4_SYSV_ABI sceVoiceGetBitRate(u32 port_id, u32* bitrate);
+s32 PS4_SYSV_ABI sceVoiceGetMuteFlag();
+s32 PS4_SYSV_ABI sceVoiceGetPortAttr();
+s32 PS4_SYSV_ABI sceVoiceGetPortInfo(u32 port_id, OrbisVoicePortInfo* info);
+s32 PS4_SYSV_ABI sceVoiceGetResourceInfo();
+s32 PS4_SYSV_ABI sceVoiceGetVolume();
+s32 PS4_SYSV_ABI sceVoiceInit();
+s32 PS4_SYSV_ABI sceVoiceInitHQ();
+s32 PS4_SYSV_ABI sceVoicePausePort();
+s32 PS4_SYSV_ABI sceVoicePausePortAll();
+s32 PS4_SYSV_ABI sceVoiceReadFromOPort();
+s32 PS4_SYSV_ABI sceVoiceResetPort();
+s32 PS4_SYSV_ABI sceVoiceResumePort();
+s32 PS4_SYSV_ABI sceVoiceResumePortAll();
+s32 PS4_SYSV_ABI sceVoiceSetBitRate();
+s32 PS4_SYSV_ABI sceVoiceSetMuteFlag();
+s32 PS4_SYSV_ABI sceVoiceSetMuteFlagAll();
+s32 PS4_SYSV_ABI sceVoiceSetThreadsParams();
+s32 PS4_SYSV_ABI sceVoiceSetVolume();
+s32 PS4_SYSV_ABI sceVoiceStart();
+s32 PS4_SYSV_ABI sceVoiceStop();
+s32 PS4_SYSV_ABI sceVoiceUpdatePort();
+s32 PS4_SYSV_ABI sceVoiceVADAdjustment();
+s32 PS4_SYSV_ABI sceVoiceVADSetVersion();
+s32 PS4_SYSV_ABI sceVoiceWriteToIPort();
+
+void RegisterlibSceVoice(Core::Loader::SymbolsResolver* sym);
+} // namespace Libraries::Voice
\ No newline at end of file
From 1930a2132c8c5b61eb72b0646f6712716c900b3f Mon Sep 17 00:00:00 2001
From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com>
Date: Mon, 2 Jun 2025 18:02:37 +0200
Subject: [PATCH 06/19] Fix SSH remote links (#3025)
---
src/common/scm_rev.cpp.in | 23 +++++++++++++++++++++++
src/common/scm_rev.h | 4 ++++
src/emulator.cpp | 10 +---------
src/qt_gui/main_window.cpp | 10 +---------
4 files changed, 29 insertions(+), 18 deletions(-)
diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in
index 71c4c2d0a..0b113eb31 100644
--- a/src/common/scm_rev.cpp.in
+++ b/src/common/scm_rev.cpp.in
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include
+
#include "common/scm_rev.h"
namespace Common {
@@ -15,5 +17,26 @@ constexpr char g_scm_remote_name[] = "@GIT_REMOTE_NAME@";
constexpr char g_scm_remote_url[] = "@GIT_REMOTE_URL@";
constexpr char g_scm_date[] = "@BUILD_DATE@";
+const std::string GetRemoteNameFromLink() {
+ std::string remote_url(Common::g_scm_remote_url);
+ std::string remote_host;
+ try {
+ if (remote_url.starts_with("http")) {
+ if (*remote_url.rbegin() == '/') {
+ remote_url.pop_back();
+ }
+ remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
+ } else if (remote_url.starts_with("git@")) {
+ auto after_comma_pos = remote_url.find(':') + 1, slash_pos = remote_url.find('/');
+ remote_host = remote_url.substr(after_comma_pos, slash_pos - after_comma_pos);
+ } else {
+ remote_host = "unknown";
+ }
+ } catch (...) {
+ remote_host = "unknown";
+ }
+ return remote_host;
+}
+
} // namespace
diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h
index 36b844e94..2f6d770bb 100644
--- a/src/common/scm_rev.h
+++ b/src/common/scm_rev.h
@@ -3,6 +3,8 @@
#pragma once
+#include
+
namespace Common {
extern const char g_version[];
@@ -15,4 +17,6 @@ extern const char g_scm_remote_name[];
extern const char g_scm_remote_url[];
extern const char g_scm_date[];
+const std::string GetRemoteNameFromLink();
+
} // namespace Common
diff --git a/src/emulator.cpp b/src/emulator.cpp
index 2ad8446ab..4fcb0cfc2 100644
--- a/src/emulator.cpp
+++ b/src/emulator.cpp
@@ -194,15 +194,7 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector", id, title, app_version);
std::string window_title = "";
std::string remote_url(Common::g_scm_remote_url);
- std::string remote_host;
- try {
- if (*remote_url.rbegin() == '/') {
- remote_url.pop_back();
- }
- remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
- } catch (...) {
- remote_host = "unknown";
- }
+ std::string remote_host = Common::GetRemoteNameFromLink();
if (Common::g_is_release) {
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
window_title = fmt::format("shadPS4 v{} | {}", Common::g_version, game_title);
diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp
index 77b088b78..0f6773751 100644
--- a/src/qt_gui/main_window.cpp
+++ b/src/qt_gui/main_window.cpp
@@ -56,15 +56,7 @@ bool MainWindow::Init() {
setMinimumSize(720, 405);
std::string window_title = "";
std::string remote_url(Common::g_scm_remote_url);
- std::string remote_host;
- try {
- if (*remote_url.rbegin() == '/') {
- remote_url.pop_back();
- }
- remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
- } catch (...) {
- remote_host = "unknown";
- }
+ std::string remote_host = Common::GetRemoteNameFromLink();
if (Common::g_is_release) {
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
window_title = fmt::format("shadPS4 v{}", Common::g_version);
From 2c782721851ba4bb66ad5964944c4f6ee6f990f7 Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Tue, 3 Jun 2025 00:02:51 -0500
Subject: [PATCH 07/19] Emulate libSceGnmDriver's init behavior (#3024)
This time, perform it after the LoadSharedLibraries call, which places it after the various init memory mappings GFD engine titles perform.
---
src/core/linker.cpp | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/core/linker.cpp b/src/core/linker.cpp
index 3e6d8c22e..c50b03a8f 100644
--- a/src/core/linker.cpp
+++ b/src/core/linker.cpp
@@ -117,6 +117,18 @@ void Linker::Execute(const std::vector args) {
Common::SetCurrentThreadName("GAME_MainThread");
LoadSharedLibraries();
+ // Simulate libSceGnmDriver initialization, which maps a chunk of direct memory.
+ // Some games fail without accurately emulating this behavior.
+ s64 phys_addr{};
+ s32 result = Libraries::Kernel::sceKernelAllocateDirectMemory(
+ 0, Libraries::Kernel::sceKernelGetDirectMemorySize(), 0x10000, 0x10000, 3, &phys_addr);
+ if (result == 0) {
+ void* addr{reinterpret_cast(0xfe0000000)};
+ result = Libraries::Kernel::sceKernelMapNamedDirectMemory(
+ &addr, 0x10000, 0x13, 0, phys_addr, 0x10000, "SceGnmDriver");
+ }
+ ASSERT_MSG(result == 0, "Unable to emulate libSceGnmDriver initialization");
+
// Start main module.
EntryParams params{};
params.argc = 1;
From eed99141b3170b6417c9403df74bbb3e0920d915 Mon Sep 17 00:00:00 2001
From: mailwl
Date: Tue, 3 Jun 2025 09:11:18 +0300
Subject: [PATCH 08/19] Network Play: set user signed in (#2944)
* Network Play: set user signed in
* get signedIn status from config
---------
Co-authored-by: georgemoralis
---
src/common/config.cpp | 14 +++++++-
src/common/config.h | 4 ++-
src/core/libraries/np_manager/np_manager.cpp | 36 ++++++++++++++------
src/core/libraries/np_manager/np_manager.h | 9 ++++-
4 files changed, 49 insertions(+), 14 deletions(-)
diff --git a/src/common/config.cpp b/src/common/config.cpp
index 6bccd0f37..6565ab82a 100644
--- a/src/common/config.cpp
+++ b/src/common/config.cpp
@@ -69,7 +69,7 @@ static bool vkGuestMarkers = false;
static bool rdocEnable = false;
static bool isFpsColor = true;
static bool isSeparateLogFilesEnabled = false;
-static s16 cursorState = HideCursorState::Idle;
+static int cursorState = HideCursorState::Idle;
static int cursorHideTimeout = 5; // 5 seconds (default)
static double trophyNotificationDuration = 6.0;
static bool useUnifiedInputConfig = true;
@@ -78,6 +78,7 @@ static int controllerCustomColorRGB[3] = {0, 0, 255};
static bool compatibilityData = false;
static bool checkCompatibilityOnStartup = false;
static std::string trophyKey;
+static bool isPSNSignedIn = false;
// Gui
static bool load_game_size = true;
@@ -730,6 +731,14 @@ void setShowBackgroundImage(bool show) {
showBackgroundImage = show;
}
+bool getPSNSignedIn() {
+ return isPSNSignedIn;
+}
+
+void setPSNSignedIn(bool sign) {
+ isPSNSignedIn = sign;
+}
+
void load(const std::filesystem::path& path) {
// If the configuration file does not exist, create it and return
std::error_code error;
@@ -754,6 +763,7 @@ void load(const std::filesystem::path& path) {
isNeo = toml::find_or(general, "isPS4Pro", false);
isDevKit = toml::find_or(general, "isDevKit", false);
+ isPSNSignedIn = toml::find_or(general, "isPSNSignedIn", false);
playBGM = toml::find_or(general, "playBGM", false);
isTrophyPopupDisabled = toml::find_or(general, "isTrophyPopupDisabled", false);
trophyNotificationDuration =
@@ -953,6 +963,7 @@ void save(const std::filesystem::path& path) {
data["General"]["isPS4Pro"] = isNeo;
data["General"]["isDevKit"] = isDevKit;
+ data["General"]["isPSNSignedIn"] = isPSNSignedIn;
data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled;
data["General"]["trophyNotificationDuration"] = trophyNotificationDuration;
data["General"]["playBGM"] = playBGM;
@@ -1098,6 +1109,7 @@ void setDefaultValues() {
isHDRAllowed = false;
isNeo = false;
isDevKit = false;
+ isPSNSignedIn = false;
isFullscreen = false;
isTrophyPopupDisabled = false;
playBGM = false;
diff --git a/src/common/config.h b/src/common/config.h
index aba23621c..404854ae2 100644
--- a/src/common/config.h
+++ b/src/common/config.h
@@ -14,7 +14,7 @@ struct GameInstallDir {
bool enabled;
};
-enum HideCursorState : s16 { Never, Idle, Always };
+enum HideCursorState : int { Never, Idle, Always };
void load(const std::filesystem::path& path);
void save(const std::filesystem::path& path);
@@ -39,6 +39,7 @@ bool getCompatibilityEnabled();
bool getCheckCompatibilityOnStartup();
int getBackgroundImageOpacity();
bool getShowBackgroundImage();
+bool getPSNSignedIn();
std::string getLogFilter();
std::string getLogType();
@@ -111,6 +112,7 @@ void setCompatibilityEnabled(bool use);
void setCheckCompatibilityOnStartup(bool use);
void setBackgroundImageOpacity(int opacity);
void setShowBackgroundImage(bool show);
+void setPSNSignedIn(bool sign);
void setCursorState(s16 cursorState);
void setCursorHideTimeout(int newcursorHideTimeout);
diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp
index a60dcd86f..bc920b5a9 100644
--- a/src/core/libraries/np_manager/np_manager.cpp
+++ b/src/core/libraries/np_manager/np_manager.cpp
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "common/config.h"
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
@@ -10,6 +11,8 @@
namespace Libraries::NpManager {
+#define SIGNEDIN_STATUS (Config::getPSNSignedIn() ? ORBIS_OK : ORBIS_NP_ERROR_SIGNED_OUT)
+
int PS4_SYSV_ABI Func_EF4378573542A508() {
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
return ORBIS_OK;
@@ -921,9 +924,16 @@ int PS4_SYSV_ABI sceNpGetAccountCountry() {
return ORBIS_OK;
}
-int PS4_SYSV_ABI sceNpGetAccountCountryA() {
- LOG_ERROR(Lib_NpManager, "(STUBBED) called");
- return ORBIS_OK;
+int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id,
+ OrbisNpCountryCode* country_code) {
+ LOG_INFO(Lib_NpManager, "(STUBBED) called, user_id = {}", user_id);
+ if (country_code == nullptr) {
+ return ORBIS_NP_ERROR_INVALID_ARGUMENT;
+ }
+ ::memset(country_code, 0, sizeof(OrbisNpCountryCode));
+ // TODO: get NP country code from config
+ ::memcpy(country_code->country_code, "us", 2);
+ return SIGNEDIN_STATUS;
}
int PS4_SYSV_ABI sceNpGetAccountDateOfBirth() {
@@ -941,8 +951,8 @@ int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id)
if (online_id == nullptr || account_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
- *account_id = 0;
- return ORBIS_NP_ERROR_SIGNED_OUT;
+ *account_id = 0xFEEDFACE;
+ return SIGNEDIN_STATUS;
}
int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id) {
@@ -950,8 +960,8 @@ int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account
if (account_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
- *account_id = 0;
- return ORBIS_NP_ERROR_SIGNED_OUT;
+ *account_id = 0xFEEDFACE;
+ return SIGNEDIN_STATUS;
}
int PS4_SYSV_ABI sceNpGetAccountLanguage() {
@@ -984,7 +994,9 @@ int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id)
if (np_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
- return ORBIS_NP_ERROR_SIGNED_OUT;
+ memset(np_id, 0, sizeof(OrbisNpId));
+ strncpy(np_id->handle.data, Config::getUserName().c_str(), sizeof(np_id->handle.data));
+ return SIGNEDIN_STATUS;
}
int PS4_SYSV_ABI sceNpGetNpReachabilityState() {
@@ -997,7 +1009,9 @@ int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineI
if (online_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
- return ORBIS_NP_ERROR_SIGNED_OUT;
+ memset(online_id, 0, sizeof(OrbisNpOnlineId));
+ strncpy(online_id->data, Config::getUserName().c_str(), sizeof(online_id->data));
+ return SIGNEDIN_STATUS;
}
int PS4_SYSV_ABI sceNpGetParentalControlInfo() {
@@ -1014,8 +1028,8 @@ int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* sta
if (state == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
- *state = OrbisNpState::SignedOut;
- LOG_DEBUG(Lib_NpManager, "Signed out");
+ *state = Config::getPSNSignedIn() ? OrbisNpState::SignedIn : OrbisNpState::SignedOut;
+ LOG_DEBUG(Lib_NpManager, "Signed {}", Config::getPSNSignedIn() ? "in" : "out");
return ORBIS_OK;
}
diff --git a/src/core/libraries/np_manager/np_manager.h b/src/core/libraries/np_manager/np_manager.h
index 02a1a32f6..1078a9f3e 100644
--- a/src/core/libraries/np_manager/np_manager.h
+++ b/src/core/libraries/np_manager/np_manager.h
@@ -32,6 +32,12 @@ struct OrbisNpId {
u8 reserved[8];
};
+struct OrbisNpCountryCode {
+ char country_code[2];
+ char end;
+ char pad;
+};
+
int PS4_SYSV_ABI Func_EF4378573542A508();
int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromKernel();
int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromPool();
@@ -215,7 +221,8 @@ int PS4_SYSV_ABI sceNpCreateRequest();
int PS4_SYSV_ABI sceNpDeleteRequest(int reqId);
int PS4_SYSV_ABI sceNpGetAccountAge();
int PS4_SYSV_ABI sceNpGetAccountCountry();
-int PS4_SYSV_ABI sceNpGetAccountCountryA();
+int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id,
+ OrbisNpCountryCode* country_code);
int PS4_SYSV_ABI sceNpGetAccountDateOfBirth();
int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA();
int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id);
From bb3f8af81ad6b4c60cd8d1d33c18e7472759fe57 Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Tue, 3 Jun 2025 01:29:25 -0500
Subject: [PATCH 09/19] Core: Protect fixes (#3029)
* Swap do-while to while
If we use a do-while loop, we waste time if `aligned_size = 0`. This is also still accurate to FreeBSD behavior, where it returns success if `start == end` during mprotect.
This also effectively prevents the memory assert seen in updated versions of RESIDENT EVIL 2 (CUSA09193)
* Move prot validation outside loop
The prot variable shouldn't change during a mprotect call, so we can check the flags before protecting instead.
Also cleans up the code for prot validation.
This should improve performance, and is more accurate to FreeBSD code.
* Add logging for protect calls
This will help in debugging future problems
---
src/core/libraries/kernel/memory.cpp | 4 ++++
src/core/memory.cpp | 32 +++++++++++++++-------------
2 files changed, 21 insertions(+), 15 deletions(-)
diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp
index ce694dc1e..18676cbdf 100644
--- a/src/core/libraries/kernel/memory.cpp
+++ b/src/core/libraries/kernel/memory.cpp
@@ -264,6 +264,8 @@ int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void**
}
s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot) {
+ LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size,
+ prot);
Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast(prot);
return memory_manager->Protect(std::bit_cast(addr), size, protection_flags);
@@ -279,6 +281,8 @@ s32 PS4_SYSV_ABI posix_mprotect(const void* addr, u64 size, s32 prot) {
}
s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s32 prot) {
+ LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size,
+ prot);
Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast(prot);
return memory_manager->Protect(std::bit_cast(addr), size, protection_flags);
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index bf9d1cabd..8550ece17 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -557,18 +557,6 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s
return adjusted_size;
}
- // Validate protection flags
- constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead |
- MemoryProt::CpuReadWrite | MemoryProt::GpuRead |
- MemoryProt::GpuWrite | MemoryProt::GpuReadWrite;
-
- MemoryProt invalid_flags = prot & ~valid_flags;
- if (u32(invalid_flags) != 0 && u32(invalid_flags) != u32(MemoryProt::NoAccess)) {
- LOG_ERROR(Kernel_Vmm, "Invalid protection flags: prot = {:#x}, invalid flags = {:#x}",
- u32(prot), u32(invalid_flags));
- return ORBIS_KERNEL_ERROR_EINVAL;
- }
-
// Change protection
vma_base.prot = prot;
@@ -598,11 +586,25 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s
s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) {
std::scoped_lock lk{mutex};
- s64 protected_bytes = 0;
+ // Validate protection flags
+ constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead |
+ MemoryProt::CpuReadWrite | MemoryProt::GpuRead |
+ MemoryProt::GpuWrite | MemoryProt::GpuReadWrite;
+
+ MemoryProt invalid_flags = prot & ~valid_flags;
+ if (invalid_flags != MemoryProt::NoAccess) {
+ LOG_ERROR(Kernel_Vmm, "Invalid protection flags");
+ return ORBIS_KERNEL_ERROR_EINVAL;
+ }
+
+ // Align addr and size to the nearest page boundary.
auto aligned_addr = Common::AlignDown(addr, 16_KB);
auto aligned_size = Common::AlignUp(size + addr - aligned_addr, 16_KB);
- do {
+
+ // Protect all VMAs between aligned_addr and aligned_addr + aligned_size.
+ s64 protected_bytes = 0;
+ while (protected_bytes < aligned_size) {
auto it = FindVMA(aligned_addr + protected_bytes);
auto& vma_base = it->second;
ASSERT_MSG(vma_base.Contains(addr + protected_bytes, 0), "Address {:#x} is out of bounds",
@@ -615,7 +617,7 @@ s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) {
return result;
}
protected_bytes += result;
- } while (protected_bytes < aligned_size);
+ }
return ORBIS_OK;
}
From ca0f458505e42bcab4b2c277b8abd7d302f760be Mon Sep 17 00:00:00 2001
From: Schweeeeeeeeeeeeeeee
<88291316+Schweeeeeeeeeeeeeeee@users.noreply.github.com>
Date: Tue, 3 Jun 2025 08:40:31 +0200
Subject: [PATCH 10/19] Add missing dependency for Fedora (#3005)
Propablly missing because of fedora 42?
---
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 cdc8ba12f..bd07b2eff 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 \
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 libpng-devel
+ vulkan-devel vulkan-validation-layers libpng-devel libuuid-devel
```
#### Arch Linux
From c09d1c3cffa8591db056347c05063a9cd2b6c84e Mon Sep 17 00:00:00 2001
From: Fire Cube
Date: Tue, 3 Jun 2025 12:34:29 +0200
Subject: [PATCH 11/19] Add support for game folder and fail early if eboot.bin
is missing or corrupt (#3027)
---
src/emulator.cpp | 19 +++++++++++++++++--
src/emulator.h | 2 +-
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/src/emulator.cpp b/src/emulator.cpp
index 4fcb0cfc2..bb50b8686 100644
--- a/src/emulator.cpp
+++ b/src/emulator.cpp
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include
#include
#include
@@ -62,8 +63,13 @@ Emulator::~Emulator() {
Config::saveMainWindow(config_dir / "config.toml");
}
-void Emulator::Run(const std::filesystem::path& file, const std::vector args) {
+void Emulator::Run(std::filesystem::path file, const std::vector args) {
+ if (std::filesystem::is_directory(file)) {
+ file /= "eboot.bin";
+ }
+
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") || game_folder_name.ends_with("-patch")) {
@@ -114,6 +120,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorGetHostPath("/app0/" + eboot_name);
- linker->LoadModule(eboot_path);
+ if (linker->LoadModule(eboot_path) == -1) {
+ LOG_CRITICAL(Loader, "Failed to load game's eboot.bin: {}",
+ std::filesystem::absolute(eboot_path).string());
+ std::quick_exit(0);
+ }
// check if we have system modules to load
LoadSystemModules(game_info.game_serial);
diff --git a/src/emulator.h b/src/emulator.h
index 08c2807a1..257ccd694 100644
--- a/src/emulator.h
+++ b/src/emulator.h
@@ -25,7 +25,7 @@ public:
Emulator();
~Emulator();
- void Run(const std::filesystem::path& file, const std::vector args = {});
+ void Run(std::filesystem::path file, const std::vector args = {});
void UpdatePlayTime(const std::string& serial);
private:
From 8cdd8dd725ae5c5b383ead35ad3eff59b3b55d09 Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Tue, 3 Jun 2025 05:43:56 -0500
Subject: [PATCH 12/19] Core: Pthread affinity fixups (#3021)
* posix_pthread_attr_getschedparam
* Fixes for scePthreadGetAffinity and scePthreadSetAffinity
Looking at FreeBSD source, and our other pthread functions, we should be using our FindThread function to get the appropriate thread if thread != g_curthread.
---
src/core/libraries/kernel/threads/pthread.cpp | 31 ++++++++++++++++---
.../libraries/kernel/threads/pthread_attr.cpp | 2 ++
2 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp
index 61310bfb5..59b427d22 100644
--- a/src/core/libraries/kernel/threads/pthread.cpp
+++ b/src/core/libraries/kernel/threads/pthread.cpp
@@ -576,8 +576,19 @@ int PS4_SYSV_ABI posix_pthread_getaffinity_np(PthreadT thread, size_t cpusetsize
if (thread == nullptr || cpusetp == nullptr) {
return POSIX_EINVAL;
}
+
+ auto* thread_state = ThrState::Instance();
+ if (thread == g_curthread) {
+ g_curthread->lock.lock();
+ } else if (auto ret = thread_state->FindThread(thread, /*include dead*/ 0); ret != 0) {
+ return ret;
+ }
+
auto* attr_ptr = &thread->attr;
- return posix_pthread_attr_getaffinity_np(&attr_ptr, cpusetsize, cpusetp);
+ auto ret = posix_pthread_attr_getaffinity_np(&attr_ptr, cpusetsize, cpusetp);
+
+ thread->lock.unlock();
+ return ret;
}
int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize,
@@ -585,11 +596,23 @@ int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize
if (thread == nullptr || cpusetp == nullptr) {
return POSIX_EINVAL;
}
- auto* attr_ptr = &thread->attr;
- if (const auto ret = posix_pthread_attr_setaffinity_np(&attr_ptr, cpusetsize, cpusetp)) {
+
+ auto* thread_state = ThrState::Instance();
+ if (thread == g_curthread) {
+ g_curthread->lock.lock();
+ } else if (auto ret = thread_state->FindThread(thread, /*include dead*/ 0); ret != 0) {
return ret;
}
- return thread->SetAffinity(thread->attr.cpuset);
+
+ auto* attr_ptr = &thread->attr;
+ auto ret = posix_pthread_attr_setaffinity_np(&attr_ptr, cpusetsize, cpusetp);
+
+ if (ret == ORBIS_OK) {
+ ret = thread->SetAffinity(thread->attr.cpuset);
+ }
+
+ thread->lock.unlock();
+ return ret;
}
int PS4_SYSV_ABI scePthreadGetaffinity(PthreadT thread, u64* mask) {
diff --git a/src/core/libraries/kernel/threads/pthread_attr.cpp b/src/core/libraries/kernel/threads/pthread_attr.cpp
index 71f6438a6..e098b00a4 100644
--- a/src/core/libraries/kernel/threads/pthread_attr.cpp
+++ b/src/core/libraries/kernel/threads/pthread_attr.cpp
@@ -306,6 +306,8 @@ void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) {
posix_pthread_attr_getdetachstate);
LIB_FUNCTION("JKyG3SWyA10", "libScePosix", 1, "libkernel", 1, 1,
posix_pthread_attr_setguardsize);
+ LIB_FUNCTION("qlk9pSLsUmM", "libScePosix", 1, "libkernel", 1, 1,
+ posix_pthread_attr_getschedparam);
// Orbis
LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1,
From 5b6fc788b31bfa3ed6e3f81770b2751f87d31fcc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?=
Date: Tue, 3 Jun 2025 20:42:20 +0200
Subject: [PATCH 13/19] Fix passing user data in user-triggered equeue events
(#2948)
---
src/core/libraries/kernel/equeue.cpp | 2 ++
src/core/libraries/kernel/equeue.h | 6 ++++++
2 files changed, 8 insertions(+)
diff --git a/src/core/libraries/kernel/equeue.cpp b/src/core/libraries/kernel/equeue.cpp
index 263cf24b8..911ae4cd5 100644
--- a/src/core/libraries/kernel/equeue.cpp
+++ b/src/core/libraries/kernel/equeue.cpp
@@ -145,6 +145,8 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) {
if (event.event.ident == ident && event.event.filter == filter) {
if (filter == SceKernelEvent::Filter::VideoOut) {
event.TriggerDisplay(trigger_data);
+ } else if (filter == SceKernelEvent::Filter::User) {
+ event.TriggerUser(trigger_data);
} else {
event.Trigger(trigger_data);
}
diff --git a/src/core/libraries/kernel/equeue.h b/src/core/libraries/kernel/equeue.h
index a0367c66a..e6e3c0c53 100644
--- a/src/core/libraries/kernel/equeue.h
+++ b/src/core/libraries/kernel/equeue.h
@@ -98,6 +98,12 @@ struct EqueueEvent {
event.data = reinterpret_cast(data);
}
+ void TriggerUser(void* data) {
+ is_triggered = true;
+ event.fflags++;
+ event.udata = data;
+ }
+
void TriggerDisplay(void* data) {
is_triggered = true;
if (data != nullptr) {
From 88f6cb4d4197748c2dde637e3c9340823193228c Mon Sep 17 00:00:00 2001
From: Dmugetsu <168934208+diegolix29@users.noreply.github.com>
Date: Tue, 3 Jun 2025 15:26:04 -0600
Subject: [PATCH 14/19] clean up main window from extraction remains (#3035)
---
src/qt_gui/main_window.cpp | 1 -
src/qt_gui/main_window.h | 1 -
2 files changed, 2 deletions(-)
diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp
index 0f6773751..906a3066e 100644
--- a/src/qt_gui/main_window.cpp
+++ b/src/qt_gui/main_window.cpp
@@ -372,7 +372,6 @@ void MainWindow::CreateConnects() {
connect(ui->refreshGameListAct, &QAction::triggered, this, &MainWindow::RefreshGameTable);
connect(ui->refreshButton, &QPushButton::clicked, this, &MainWindow::RefreshGameTable);
connect(ui->showGameListAct, &QAction::triggered, this, &MainWindow::ShowGameList);
- connect(this, &MainWindow::ExtractionFinished, this, &MainWindow::RefreshGameTable);
connect(ui->toggleLabelsAct, &QAction::toggled, this, &MainWindow::toggleLabelsUnderIcons);
connect(ui->fullscreenButton, &QPushButton::clicked, this, &MainWindow::toggleFullscreen);
diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h
index a5ec08d36..97c56433d 100644
--- a/src/qt_gui/main_window.h
+++ b/src/qt_gui/main_window.h
@@ -29,7 +29,6 @@ class MainWindow : public QMainWindow {
Q_OBJECT
signals:
void WindowResized(QResizeEvent* event);
- void ExtractionFinished();
public:
explicit MainWindow(QWidget* parent = nullptr);
From 23710f397eae17724777cc3dfa5333163c552f89 Mon Sep 17 00:00:00 2001
From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com>
Date: Wed, 4 Jun 2025 00:17:35 -0500
Subject: [PATCH 15/19] Reduce clamp threshold to 2MB (#3034)
A proposed solution to the non-GPU memory asserts seen in RESIDENT EVIL 2 (CUSA09193)
---
src/core/memory.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 8550ece17..ba3640877 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -68,7 +68,7 @@ void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1
}
u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) {
- static constexpr u64 MinSizeToClamp = 512_MB;
+ static constexpr u64 MinSizeToClamp = 2_MB;
// Dont bother with clamping if the size is small so we dont pay a map lookup on every buffer.
if (size < MinSizeToClamp) {
return size;
From 4d1a1ce9c2aae933a3d4e9a0a07e55eb5e5875e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?=
Date: Thu, 5 Jun 2025 01:55:47 +0200
Subject: [PATCH 16/19] v_rcp_legacy_f32 (#3040)
---
.../frontend/translate/translate.h | 1 +
.../frontend/translate/vector_alu.cpp | 16 ++++++++++++++++
2 files changed, 17 insertions(+)
diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h
index 7b4b03f27..2584d5c5e 100644
--- a/src/shader_recompiler/frontend/translate/translate.h
+++ b/src/shader_recompiler/frontend/translate/translate.h
@@ -204,6 +204,7 @@ public:
void V_EXP_F32(const GcnInst& inst);
void V_LOG_F32(const GcnInst& inst);
void V_RCP_F32(const GcnInst& inst);
+ void V_RCP_LEGACY_F32(const GcnInst& inst);
void V_RCP_F64(const GcnInst& inst);
void V_RSQ_F32(const GcnInst& inst);
void V_SQRT_F32(const GcnInst& inst);
diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp
index fb3f52c7f..3b88e4dec 100644
--- a/src/shader_recompiler/frontend/translate/vector_alu.cpp
+++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp
@@ -158,6 +158,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) {
return V_LOG_F32(inst);
case Opcode::V_RCP_F32:
return V_RCP_F32(inst);
+ case Opcode::V_RCP_LEGACY_F32:
+ return V_RCP_LEGACY_F32(inst);
case Opcode::V_RCP_F64:
return V_RCP_F64(inst);
case Opcode::V_RCP_IFLAG_F32:
@@ -798,6 +800,20 @@ void Translator::V_RCP_F32(const GcnInst& inst) {
SetDst(inst.dst[0], ir.FPRecip(src0));
}
+void Translator::V_RCP_LEGACY_F32(const GcnInst& inst) {
+ const IR::F32 src0{GetSrc(inst.src[0])};
+ const auto result = ir.FPRecip(src0);
+ const auto inf = ir.FPIsInf(result);
+
+ const auto raw_result = ir.ConvertFToU(32, result);
+ const auto sign_bit = ir.ShiftRightLogical(raw_result, ir.Imm32(31u));
+ const auto sign_bit_set = ir.INotEqual(sign_bit, ir.Imm32(0u));
+ const IR::F32 inf_result{ir.Select(sign_bit_set, ir.Imm32(-0.0f), ir.Imm32(0.0f))};
+ const IR::F32 val{ir.Select(inf, inf_result, result)};
+
+ SetDst(inst.dst[0], val);
+}
+
void Translator::V_RCP_F64(const GcnInst& inst) {
const IR::F64 src0{GetSrc64(inst.src[0])};
SetDst64(inst.dst[0], ir.FPRecip(src0));
From d4fbeea085d704a94151e44a5dcf686e2d33a7b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?=
Date: Thu, 5 Jun 2025 02:00:11 +0200
Subject: [PATCH 17/19] Stub PM4 COPY_DATA opcode (#3032)
---
src/video_core/amdgpu/liverpool.cpp | 15 ++++++--
src/video_core/amdgpu/pm4_cmds.h | 55 +++++++++++++++++++++++++++++
2 files changed, 67 insertions(+), 3 deletions(-)
diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp
index 4db7648c6..118c43cef 100644
--- a/src/video_core/amdgpu/liverpool.cpp
+++ b/src/video_core/amdgpu/liverpool.cpp
@@ -394,7 +394,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span dcb, std::span(header);
- LOG_DEBUG(Render_Vulkan,
- "Encountered EventWrite: event_type = {}, event_index = {}",
+ LOG_DEBUG(Render, "Encountered EventWrite: event_type = {}, event_index = {}",
magic_enum::enum_name(event->event_type.Value()),
magic_enum::enum_name(event->event_index.Value()));
if (event->event_type.Value() == EventType::SoVgtStreamoutFlush) {
@@ -673,6 +672,16 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header);
+ LOG_WARNING(Render,
+ "unhandled IT_COPY_DATA src_sel = {}, dst_sel = {}, "
+ "count_sel = {}, wr_confirm = {}, engine_sel = {}",
+ u32(copy_data->src_sel.Value()), u32(copy_data->dst_sel.Value()),
+ copy_data->count_sel.Value(), copy_data->wr_confirm.Value(),
+ u32(copy_data->engine_sel.Value()));
+ break;
+ }
case PM4ItOpcode::MemSemaphore: {
const auto* mem_semaphore = reinterpret_cast(header);
if (mem_semaphore->IsSignaling()) {
diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h
index 58ecda93e..011e47bf0 100644
--- a/src/video_core/amdgpu/pm4_cmds.h
+++ b/src/video_core/amdgpu/pm4_cmds.h
@@ -554,6 +554,61 @@ struct PM4DmaData {
}
};
+enum class CopyDataSrc : u32 {
+ MappedRegister = 0,
+ Memory = 1,
+ TCL2 = 2,
+ Gds = 3,
+ // Reserved = 4,
+ Immediate = 5,
+ Atomic = 6,
+ GdsAtomic0 = 7,
+ GdsAtomic1 = 8,
+ GpuClock = 9,
+};
+
+enum class CopyDataDst : u32 {
+ MappedRegister = 0,
+ MemorySync = 1,
+ TCL2 = 2,
+ Gds = 3,
+ // Reserved = 4,
+ MemoryAsync = 5,
+};
+
+enum class CopyDataEngine : u32 {
+ Me = 0,
+ Pfp = 1,
+ Ce = 2,
+ // Reserved = 3
+};
+
+struct PM4CmdCopyData {
+ PM4Type3Header header;
+ union {
+ BitField<0, 4, CopyDataSrc> src_sel;
+ BitField<8, 4, CopyDataDst> dst_sel;
+ BitField<16, 1, u32> count_sel;
+ BitField<20, 1, u32> wr_confirm;
+ BitField<30, 2, CopyDataEngine> engine_sel;
+ u32 control;
+ };
+ u32 src_addr_lo;
+ u32 src_addr_hi;
+ u32 dst_addr_lo;
+ u32 dst_addr_hi;
+
+ template
+ T SrcAddress() const {
+ return std::bit_cast(src_addr_lo | u64(src_addr_hi) << 32);
+ }
+
+ template
+ T DstAddress() const {
+ return std::bit_cast(dst_addr_lo | u64(dst_addr_hi) << 32);
+ }
+};
+
struct PM4CmdRewind {
PM4Type3Header header;
union {
From 285df1b5befcedb1287007ed992e1805b148025f Mon Sep 17 00:00:00 2001
From: DanielSvoboda
Date: Thu, 5 Jun 2025 02:48:47 -0300
Subject: [PATCH 18/19] QT: AutoUpdate - Fix Changelog Error (#3042)
---
.github/workflows/build.yml | 6 +++---
src/qt_gui/check_update.cpp | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ceb915f6a..bb3d157b7 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -494,7 +494,7 @@ jobs:
with:
token: ${{ secrets.SHADPS4_TOKEN_REPO }}
name: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}"
- tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}"
+ tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.fullhash }}"
draft: false
prerelease: true
body: "Full Changelog: [${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }}](https://github.com/shadps4-emu/shadPS4/compare/${{ env.last_release_tag }}...${{ needs.get-info.outputs.fullhash }})"
@@ -530,14 +530,14 @@ jobs:
# Check if release already exists and get ID
release_id=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
- "https://api.github.com/repos/$REPO/releases/tags/Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}" | jq -r '.id')
+ "https://api.github.com/repos/$REPO/releases/tags/Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.fullhash }}" | jq -r '.id')
if [[ "$release_id" == "null" ]]; then
echo "Creating release in $REPO for $filename"
release_id=$(curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
-d '{
- "tag_name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}",
+ "tag_name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.fullhash }}",
"name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}",
"draft": false,
"prerelease": true,
diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp
index 550fdddb5..b0858840a 100644
--- a/src/qt_gui/check_update.cpp
+++ b/src/qt_gui/check_update.cpp
@@ -137,7 +137,7 @@ tr("The Auto Updater allows up to 60 update checks per hour.\\nYou have reached
}
}
- latestRev = latestVersion.right(7);
+ latestRev = latestVersion.right(40);
latestDate = jsonObj["published_at"].toString();
QJsonArray assets = jsonObj["assets"].toArray();
@@ -167,7 +167,7 @@ tr("The Auto Updater allows up to 60 update checks per hour.\\nYou have reached
QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate);
latestDate = dateTime.isValid() ? dateTime.toString("yyyy-MM-dd HH:mm:ss") : "Unknown date";
- if (latestRev == currentRev.left(7)) {
+ if (latestRev == currentRev) {
if (showMessage) {
QMessageBox::information(this, tr("Auto Updater"),
tr("Your version is already up to date!"));
@@ -215,7 +215,7 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate,
"%3 | "
"(%4) | "
"
")
- .arg(currentRev.left(7), currentDate, latestRev, latestDate);
+ .arg(currentRev.left(7), currentDate, latestRev.left(7), latestDate);
QLabel* updateLabel = new QLabel(updateText, this);
layout->addWidget(updateLabel);
From 93222c6f9f01c15855b3cee23c7856b963b6b1e2 Mon Sep 17 00:00:00 2001
From: georgemoralis
Date: Thu, 5 Jun 2025 08:49:32 +0300
Subject: [PATCH 19/19] New Crowdin updates (#3038)
* New translations en_us.ts (Portuguese, Brazilian)
* New translations en_us.ts (Turkish)
---
src/qt_gui/translations/pt_BR.ts | 2 +-
src/qt_gui/translations/tr_TR.ts | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts
index 34d31f240..9f254e272 100644
--- a/src/qt_gui/translations/pt_BR.ts
+++ b/src/qt_gui/translations/pt_BR.ts
@@ -2048,7 +2048,7 @@
* Unsupported Vulkan Version
- * Unsupported Vulkan Version
+ * Versão do Vulkan não suportada
diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts
index e61985e90..c6d641470 100644
--- a/src/qt_gui/translations/tr_TR.ts
+++ b/src/qt_gui/translations/tr_TR.ts
@@ -138,7 +138,7 @@
File Exists
- Dosya mevcut
+ Dosya Mevcut
File already exists. Do you want to replace it?
@@ -1221,7 +1221,7 @@
Exit shadPS4
- shadPS4'ten Çık
+ shadPS4 Çıkış
Exit the application.
@@ -1381,7 +1381,7 @@
Game Boot
- Oyun Başlatma
+ Oyun Başlat
Only one file can be selected!