Merge remote-tracking branch 'upstream/main' into reduce-clamp-threshold

This commit is contained in:
Stephen Miller 2025-06-03 15:12:12 -05:00
commit a74b8edca3
17 changed files with 167 additions and 55 deletions

View File

@ -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

View File

@ -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<bool>(general, "isPS4Pro", false);
isDevKit = toml::find_or<bool>(general, "isDevKit", false);
isPSNSignedIn = toml::find_or<bool>(general, "isPSNSignedIn", false);
playBGM = toml::find_or<bool>(general, "playBGM", false);
isTrophyPopupDisabled = toml::find_or<bool>(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;

View File

@ -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);

View File

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string>
#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

View File

@ -3,6 +3,8 @@
#pragma once
#include <string>
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

View File

@ -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);
}

View File

@ -98,6 +98,12 @@ struct EqueueEvent {
event.data = reinterpret_cast<uintptr_t>(data);
}
void TriggerUser(void* data) {
is_triggered = true;
event.fflags++;
event.udata = data;
}
void TriggerDisplay(void* data) {
is_triggered = true;
if (data != nullptr) {

View File

@ -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<Core::MemoryProt>(prot);
return memory_manager->Protect(std::bit_cast<VAddr>(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<Core::MemoryProt>(prot);
return memory_manager->Protect(std::bit_cast<VAddr>(addr), size, protection_flags);

View File

@ -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) {

View File

@ -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,

View File

@ -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;
}

View File

@ -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);

View File

@ -117,6 +117,18 @@ void Linker::Execute(const std::vector<std::string> 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<void*>(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;

View File

@ -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;
}

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <filesystem>
#include <set>
#include <fmt/core.h>
@ -62,8 +63,13 @@ Emulator::~Emulator() {
Config::saveMainWindow(config_dir / "config.toml");
}
void Emulator::Run(const std::filesystem::path& file, const std::vector<std::string> args) {
void Emulator::Run(std::filesystem::path file, const std::vector<std::string> 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::vector<std::str
Common::Log::Initialize();
}
Common::Log::Start();
if (!std::filesystem::exists(file)) {
LOG_CRITICAL(Loader, "eboot.bin does not exist: {}",
std::filesystem::absolute(file).string());
std::quick_exit(0);
}
LOG_INFO(Loader, "Starting shadps4 emulator v{} ", Common::g_version);
LOG_INFO(Loader, "Revision {}", Common::g_scm_rev);
@ -194,15 +205,7 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
std::string game_title = fmt::format("{} - {} <{}>", 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);
@ -258,7 +261,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
// Load the module with the linker
const auto eboot_path = mnt->GetHostPath("/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);

View File

@ -25,7 +25,7 @@ public:
Emulator();
~Emulator();
void Run(const std::filesystem::path& file, const std::vector<std::string> args = {});
void Run(std::filesystem::path file, const std::vector<std::string> args = {});
void UpdatePlayTime(const std::string& serial);
private:

View File

@ -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);