This commit is contained in:
Antonio 2024-08-05 01:06:40 -04:00
commit d9da4a9d2b
158 changed files with 4404 additions and 1662 deletions

View file

@ -34,10 +34,7 @@ constexpr VAddr USER_MAX = 0xFBFFFFFFFFULL;
static constexpr size_t SystemManagedSize = SYSTEM_MANAGED_MAX - SYSTEM_MANAGED_MIN + 1;
static constexpr size_t SystemReservedSize = SYSTEM_RESERVED_MAX - SYSTEM_RESERVED_MIN + 1;
// User area size is normally larger than this. However games are unlikely to map to high
// regions of that area, so by default we allocate a smaller virtual address space (about 1/4th).
// to save space on page tables.
static constexpr size_t UserSize = 1ULL << 39;
static constexpr size_t UserSize = 1ULL << 40;
/**
* Represents the user virtual address space backed by a dmem memory block

View file

@ -285,20 +285,24 @@ static void GenerateTcbAccess(const ZydisDecodedOperand* operands, Xbyak::CodeGe
const auto slot = GetTcbKey();
#if defined(_WIN32)
// The following logic is based on the wine implementation of TlsGetValue
// https://github.com/wine-mirror/wine/blob/a27b9551/dlls/kernelbase/thread.c#L719
// The following logic is based on the Kernel32.dll asm of TlsGetValue
static constexpr u32 TlsSlotsOffset = 0x1480;
static constexpr u32 TlsExpansionSlotsOffset = 0x1780;
static constexpr u32 TlsMinimumAvailable = 64;
const u32 teb_offset = slot < TlsMinimumAvailable ? TlsSlotsOffset : TlsExpansionSlotsOffset;
const u32 tls_index = slot < TlsMinimumAvailable ? slot : slot - TlsMinimumAvailable;
// Load the pointer to the table of TLS slots.
c.putSeg(gs);
c.mov(dst, ptr[reinterpret_cast<void*>(teb_offset)]);
// Load the pointer to our buffer.
c.mov(dst, qword[dst + tls_index * sizeof(LPVOID)]);
if (slot < TlsMinimumAvailable) {
// Load the pointer to TLS slots.
c.mov(dst, ptr[reinterpret_cast<void*>(TlsSlotsOffset + slot * sizeof(LPVOID))]);
} else {
const u32 tls_index = slot - TlsMinimumAvailable;
// Load the pointer to the table of TLS expansion slots.
c.mov(dst, ptr[reinterpret_cast<void*>(TlsExpansionSlotsOffset)]);
// Load the pointer to our buffer.
c.mov(dst, qword[dst + tls_index * sizeof(LPVOID)]);
}
#elif defined(__APPLE__)
// The following logic is based on the Darwin implementation of _os_tsd_get_direct, used by
// pthread_getspecific https://github.com/apple/darwin-xnu/blob/main/libsyscall/os/tsd.h#L89-L96

View file

@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/io_file.h"
#include "playgo_chunk.h"
bool PlaygoChunk::Open(const std::filesystem::path& filepath) {
Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read);
if (!file.IsOpen()) {
return false;
}
file.Read(playgoHeader);
return true;
}

View file

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <filesystem>
#include "common/types.h"
struct PlaygoHeader {
u32 magic;
u16 version_major;
u16 version_minor;
u16 image_count;
u16 chunk_count;
u16 mchunk_count;
u16 scenario_count;
// TODO fill the rest
};
class PlaygoChunk {
public:
PlaygoChunk() = default;
~PlaygoChunk() = default;
bool Open(const std::filesystem::path& filepath);
PlaygoHeader GetPlaygoHeader() {
return playgoHeader;
}
private:
PlaygoHeader playgoHeader;
};

View file

@ -26,23 +26,27 @@ void MntPoints::UnmountAll() {
}
std::filesystem::path MntPoints::GetHostPath(const std::string& guest_directory) {
const MntPair* mount = GetMount(guest_directory);
// Evil games like Turok2 pass double slashes e.g /app0//game.kpf
auto corrected_path = guest_directory;
size_t pos = corrected_path.find("//");
while (pos != std::string::npos) {
corrected_path.replace(pos, 2, "/");
pos = corrected_path.find("//", pos + 1);
}
const MntPair* mount = GetMount(corrected_path);
if (!mount) {
return guest_directory;
return "";
}
// Nothing to do if getting the mount itself.
if (guest_directory == mount->mount) {
if (corrected_path == mount->mount) {
return mount->host_path;
}
// Remove device (e.g /app0) from path to retrieve relative path.
u32 pos = mount->mount.size() + 1;
// Evil games like Turok2 pass double slashes e.g /app0//game.kpf
if (guest_directory[pos] == '/') {
pos++;
}
const auto rel_path = std::string_view(guest_directory).substr(pos);
pos = mount->mount.size() + 1;
const auto rel_path = std::string_view(corrected_path).substr(pos);
const auto host_path = mount->host_path / rel_path;
if (!NeedsCaseInsensiveSearch) {
return host_path;
@ -66,7 +70,7 @@ std::filesystem::path MntPoints::GetHostPath(const std::string& guest_directory)
// exist in filesystem but in different case.
auto guest_path = current_path;
while (!path_parts.empty()) {
const auto& part = path_parts.back();
const auto part = path_parts.back();
const auto add_match = [&](const auto& host_part) {
current_path /= host_part;
guest_path /= part;

View file

@ -198,13 +198,9 @@ int PS4_SYSV_ABI sceAppContentTemporaryDataMount() {
int PS4_SYSV_ABI sceAppContentTemporaryDataMount2(OrbisAppContentTemporaryDataOption option,
OrbisAppContentMountPoint* mountPoint) {
if (std::string_view(mountPoint->data).empty()) // causing issues with save_data.
if (mountPoint == nullptr)
return ORBIS_APP_CONTENT_ERROR_PARAMETER;
auto* param_sfo = Common::Singleton<PSF>::Instance();
std::string id(param_sfo->GetString("CONTENT_ID"), 7, 9);
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::TempDataDir) / id;
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
mnt->Mount(mount_dir, mountPoint->data);
strncpy(mountPoint->data, "/temp0", 16);
LOG_INFO(Lib_AppContent, "sceAppContentTemporaryDataMount2: option = {}, mountPoint = {}",
option, mountPoint->data);
return ORBIS_OK;

View file

@ -233,6 +233,9 @@ constexpr int SCE_KERNEL_ERROR_ESDKVERSION = 0x80020063;
constexpr int SCE_KERNEL_ERROR_ESTART = 0x80020064;
constexpr int SCE_KERNEL_ERROR_ESTOP = 0x80020065;
// libSceRandom error codes
constexpr int SCE_RANDOM_ERROR_INVALID = 0x817C0016;
// videoOut
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_VALUE = 0x80290001; // invalid argument
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_ADDRESS = 0x80290002; // invalid addresses

View file

@ -20,13 +20,12 @@
extern Frontend::WindowSDL* g_window;
std::unique_ptr<Vulkan::RendererVulkan> renderer;
std::unique_ptr<AmdGpu::Liverpool> liverpool;
namespace Libraries::GnmDriver {
using namespace AmdGpu;
static std::unique_ptr<AmdGpu::Liverpool> liverpool;
enum GnmEventIdents : u64 {
Compute0RelMem = 0x00,
Compute1RelMem = 0x01,
@ -958,7 +957,7 @@ int PS4_SYSV_ABI sceGnmGetGpuBlockStatus() {
}
int PS4_SYSV_ABI sceGnmGetGpuCoreClockFrequency() {
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
LOG_DEBUG(Lib_GnmDriver, "(STUBBED) called");
return ORBIS_OK;
}
@ -2131,6 +2130,7 @@ int PS4_SYSV_ABI sceGnmSubmitDone() {
if (!liverpool->IsGpuIdle()) {
submission_lock = true;
}
liverpool->SubmitDone();
send_init_packet = true;
++frames_submitted;
return ORBIS_OK;

View file

@ -78,9 +78,7 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) {
std::scoped_lock lock{m_mutex};
for (auto& event : m_events) {
ASSERT_MSG(event.event.filter == filter,
"Event to trigger doesn't match to queue events");
if (event.event.ident == ident) {
if ((event.event.ident == ident) && (event.event.filter == filter)) {
event.Trigger(trigger_data);
has_found = true;
}

View file

@ -53,6 +53,9 @@ int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) {
if (std::string_view{path} == "/dev/stdout") {
return 2002;
}
if (std::string_view{path} == "/dev/urandom") {
return 2003;
}
u32 handle = h->CreateHandle();
auto* file = h->GetFile(handle);
if (directory) {
@ -113,6 +116,9 @@ int PS4_SYSV_ABI sceKernelClose(int d) {
if (d < 3) { // d probably hold an error code
return ORBIS_KERNEL_ERROR_EPERM;
}
if (d == 2003) { // dev/urandom case
return SCE_OK;
}
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(d);
if (file == nullptr) {
@ -223,6 +229,13 @@ s64 PS4_SYSV_ABI posix_lseek(int d, s64 offset, int whence) {
}
s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes) {
if (d == 2003) // dev urandom case
{
auto rbuf = static_cast<char*>(buf);
for (size_t i = 0; i < nbytes; i++)
rbuf[i] = std::rand() & 0xFF;
return nbytes;
}
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(d);
if (file == nullptr) {
@ -459,7 +472,30 @@ s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) {
return file->f.WriteRaw<u8>(buf, nbytes);
}
s32 PS4_SYSV_ABI sceKernelRename(const char* from, const char* to) {
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
const auto src_path = mnt->GetHostPath(from);
if (!std::filesystem::exists(src_path)) {
return ORBIS_KERNEL_ERROR_ENOENT;
}
const auto dst_path = mnt->GetHostPath(to);
const bool src_is_dir = std::filesystem::is_directory(src_path);
const bool dst_is_dir = std::filesystem::is_directory(dst_path);
if (src_is_dir && !dst_is_dir) {
return ORBIS_KERNEL_ERROR_ENOTDIR;
}
if (!src_is_dir && dst_is_dir) {
return ORBIS_KERNEL_ERROR_EISDIR;
}
if (dst_is_dir && !std::filesystem::is_empty(dst_path)) {
return ORBIS_KERNEL_ERROR_ENOTEMPTY;
}
std::filesystem::copy(src_path, dst_path, std::filesystem::copy_options::overwrite_existing);
return ORBIS_OK;
}
void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
std::srand(std::time(nullptr));
LIB_FUNCTION("1G3lF1Gg1k8", "libkernel", 1, "libkernel", 1, 1, sceKernelOpen);
LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open);
LIB_FUNCTION("UK2Tl2DWUns", "libkernel", 1, "libkernel", 1, 1, sceKernelClose);
@ -479,6 +515,7 @@ void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("kBwCPsYX-m4", "libkernel", 1, "libkernel", 1, 1, sceKernelFStat);
LIB_FUNCTION("mqQMh1zPPT8", "libScePosix", 1, "libkernel", 1, 1, posix_fstat);
LIB_FUNCTION("VW3TVZiM4-E", "libkernel", 1, "libkernel", 1, 1, sceKernelFtruncate);
LIB_FUNCTION("52NcYU9+lEo", "libkernel", 1, "libkernel", 1, 1, sceKernelRename);
LIB_FUNCTION("E6ao34wPw+U", "libScePosix", 1, "libkernel", 1, 1, posix_stat);
LIB_FUNCTION("+r3rMFwItV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPread);

View file

@ -7,6 +7,7 @@
#include <boost/asio/io_context.hpp>
#include "common/assert.h"
#include "common/debug.h"
#include "common/logging/log.h"
#include "common/polyfill_thread.h"
#include "common/singleton.h"
@ -84,6 +85,9 @@ static PS4_SYSV_ABI void stack_chk_fail() {
int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) {
LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}", fmt::ptr(addr), len);
if (len == 0) {
return ORBIS_OK;
}
auto* memory = Core::Memory::Instance();
memory->UnmapMemory(std::bit_cast<VAddr>(addr), len);
return SCE_OK;
@ -407,6 +411,7 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("2SKEx6bSq-4", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap);
LIB_FUNCTION("kBJzF8x4SyE", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap2);
LIB_FUNCTION("DGMG3JshrZU", "libkernel", 1, "libkernel", 1, 1, sceKernelSetVirtualRangeName);
// equeue
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue);

View file

@ -246,7 +246,8 @@ int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut
s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries,
int* numEntriesOut) {
return sceKernelBatchMap2(entries, numEntries, numEntriesOut, 0x10); // 0x10 : Fixed / 0x410
return sceKernelBatchMap2(entries, numEntries, numEntriesOut,
MemoryFlags::SCE_KERNEL_MAP_FIXED); // 0x10, 0x410?
}
int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len);
@ -261,7 +262,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
break; // break and assign a value to numEntriesOut.
}
if (entries[i].operation == 0) { // MAP_DIRECT
if (entries[i].operation == MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_DIRECT) {
result = sceKernelMapNamedDirectMemory(&entries[i].start, entries[i].length,
entries[i].protection, flags,
static_cast<s64>(entries[i].offset), 0, "");
@ -274,13 +275,18 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
if (result == 0)
processed++;
<<<<<<< HEAD
} else if (entries[i].operation == 1) { // UNMAP
=======
} else if (entries[i].operation == MemoryOpTypes::ORBIS_KERNEL_MAP_OP_UNMAP) {
>>>>>>> cdff4af38da1d832e35d8c057d698f38c64b2932
result = sceKernelMunmap(entries[i].start, entries[i].length);
LOG_INFO(Kernel_Vmm, "BatchMap: entry = {}, operation = {}, len = {:#x}, result = {}",
i, entries[i].operation, entries[i].length, result);
if (result == 0)
processed++;
<<<<<<< HEAD
} else if (entries[i].operation == 4) { // MPROTECT
result =
sceKernelMProtect(entries[i].start, entries[i].length, entries[i].protection);
@ -296,6 +302,19 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
}
}
else {
=======
} else if (entries[i].operation == MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_FLEXIBLE) {
result = sceKernelMapNamedFlexibleMemory(&entries[i].start, entries[i].length,
entries[i].protection, flags, "");
LOG_INFO(Kernel_Vmm,
"BatchMap: entry = {}, operation = {}, len = {:#x}, type = {}, "
"result = {}",
i, entries[i].operation, entries[i].length, (u8)entries[i].type, result);
if (result == 0)
processed++;
} else {
>>>>>>> cdff4af38da1d832e35d8c057d698f38c64b2932
UNREACHABLE_MSG("called: Unimplemented Operation = {}", entries[i].operation);
}
}
@ -305,4 +324,19 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
return result;
}
s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name) {
static constexpr size_t MaxNameSize = 32;
if (std::strlen(name) > MaxNameSize) {
LOG_ERROR(Kernel_Vmm, "name exceeds 32 bytes!");
return ORBIS_KERNEL_ERROR_ENAMETOOLONG;
}
if (name == nullptr) {
LOG_ERROR(Kernel_Vmm, "name is invalid!");
return ORBIS_KERNEL_ERROR_EFAULT;
}
auto* memory = Core::Memory::Instance();
memory->NameVirtualRange(std::bit_cast<VAddr>(addr), len, name);
return ORBIS_OK;
}
} // namespace Libraries::Kernel

View file

@ -31,6 +31,14 @@ enum MemoryProtection : u32 {
SCE_KERNEL_PROT_GPU_RW = 0x30 // Permit reads/writes from the GPU
};
enum MemoryOpTypes : u32 {
ORBIS_KERNEL_MAP_OP_MAP_DIRECT = 0,
ORBIS_KERNEL_MAP_OP_UNMAP = 1,
ORBIS_KERNEL_MAP_OP_PROTECT = 2,
ORBIS_KERNEL_MAP_OP_MAP_FLEXIBLE = 3,
ORBIS_KERNEL_MAP_OP_TYPE_PROTECT = 4
};
struct OrbisQueryInfo {
uintptr_t start;
uintptr_t end;
@ -100,4 +108,6 @@ s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEnt
s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries,
int* numEntriesOut, int flags);
s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name);
} // namespace Libraries::Kernel

View file

@ -394,6 +394,18 @@ int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpuma
return result;
}
int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u64* mask) {
LOG_INFO(Kernel_Pthread, "called");
if (thread == nullptr) {
return SCE_KERNEL_ERROR_ESRCH;
}
auto result = scePthreadAttrGetaffinity(&thread->attr, mask);
return result;
}
ScePthreadMutex* createMutex(ScePthreadMutex* addr) {
if (addr == nullptr || *addr != nullptr) {
return addr;
@ -427,11 +439,7 @@ int PS4_SYSV_ABI scePthreadMutexInit(ScePthreadMutex* mutex, const ScePthreadMut
int result = pthread_mutex_init(&(*mutex)->pth_mutex, &(*attr)->pth_mutex_attr);
static auto mutex_loc = MUTEX_LOCATION("mutex");
(*mutex)->tracy_lock = std::make_unique<tracy::LockableCtx>(&mutex_loc);
if (name != nullptr) {
(*mutex)->tracy_lock->CustomName(name, std::strlen(name));
LOG_INFO(Kernel_Pthread, "name={}, result={}", name, result);
}
@ -543,15 +551,11 @@ int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex) {
return SCE_KERNEL_ERROR_EINVAL;
}
(*mutex)->tracy_lock->BeforeLock();
int result = pthread_mutex_lock(&(*mutex)->pth_mutex);
if (result != 0) {
LOG_TRACE(Kernel_Pthread, "Locked name={}, result={}", (*mutex)->name, result);
}
(*mutex)->tracy_lock->AfterLock();
switch (result) {
case 0:
return SCE_OK;
@ -577,8 +581,6 @@ int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex) {
LOG_TRACE(Kernel_Pthread, "Unlocking name={}, result={}", (*mutex)->name, result);
}
(*mutex)->tracy_lock->AfterUnlock();
switch (result) {
case 0:
return SCE_OK;
@ -1183,8 +1185,6 @@ int PS4_SYSV_ABI scePthreadMutexTrylock(ScePthreadMutex* mutex) {
LOG_TRACE(Kernel_Pthread, "name={}, result={}", (*mutex)->name, result);
}
(*mutex)->tracy_lock->AfterTryLock(result == 0);
switch (result) {
case 0:
return ORBIS_OK;
@ -1243,6 +1243,40 @@ int PS4_SYSV_ABI posix_pthread_attr_destroy(ScePthreadAttr* attr) {
return result;
}
int PS4_SYSV_ABI posix_pthread_attr_setschedparam(ScePthreadAttr* attr,
const SceKernelSchedParam* param) {
int result = scePthreadAttrSetschedparam(attr, param);
if (result < 0) {
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
? result + -SCE_KERNEL_ERROR_UNKNOWN
: POSIX_EOTHER;
return rt;
}
return result;
}
int PS4_SYSV_ABI posix_pthread_attr_setinheritsched(ScePthreadAttr* attr, int inheritSched) {
int result = scePthreadAttrSetinheritsched(attr, inheritSched);
if (result < 0) {
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
? result + -SCE_KERNEL_ERROR_UNKNOWN
: POSIX_EOTHER;
return rt;
}
return result;
}
int PS4_SYSV_ABI posix_pthread_setprio(ScePthread thread, int prio) {
int result = scePthreadSetprio(thread, prio);
if (result < 0) {
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
? result + -SCE_KERNEL_ERROR_UNKNOWN
: POSIX_EOTHER;
return rt;
}
return result;
}
int PS4_SYSV_ABI posix_pthread_attr_setdetachstate(ScePthreadAttr* attr, int detachstate) {
// LOG_INFO(Kernel_Pthread, "posix pthread_mutexattr_init redirect to scePthreadMutexattrInit");
int result = scePthreadAttrSetdetachstate(attr, detachstate);
@ -1336,14 +1370,56 @@ int PS4_SYSV_ABI posix_sem_wait(sem_t* sem) {
return sem_wait(sem);
}
#ifndef HAVE_SEM_TIMEDWAIT
int sem_timedwait(sem_t* sem, const struct timespec* abstime) {
int rc;
while ((rc = sem_trywait(sem)) == EAGAIN) {
struct timespec curr_time;
clock_gettime(CLOCK_REALTIME, &curr_time);
s64 remaining_ns = 0;
remaining_ns +=
(static_cast<s64>(abstime->tv_sec) - static_cast<s64>(curr_time.tv_sec)) * 1000000000L;
remaining_ns += static_cast<s64>(abstime->tv_nsec) - static_cast<s64>(curr_time.tv_nsec);
if (remaining_ns <= 0) {
return ETIMEDOUT;
}
struct timespec sleep_time;
sleep_time.tv_sec = 0;
if (remaining_ns < 5000000L) {
sleep_time.tv_nsec = remaining_ns;
} else {
sleep_time.tv_nsec = 5000000;
}
nanosleep(&sleep_time, nullptr);
}
return rc;
}
#endif
int PS4_SYSV_ABI posix_sem_timedwait(sem_t* sem, const timespec* t) {
return sem_timedwait(sem, t);
}
int PS4_SYSV_ABI posix_sem_post(sem_t* sem) {
return sem_post(sem);
}
int PS4_SYSV_ABI posix_sem_destroy(sem_t* sem) {
return sem_destroy(sem);
}
int PS4_SYSV_ABI posix_sem_getvalue(sem_t* sem, int* sval) {
return sem_getvalue(sem, sval);
}
int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const pthread_attr_t* attr, size_t* size) {
return pthread_attr_getstacksize(attr, size);
}
int PS4_SYSV_ABI scePthreadGetschedparam(ScePthread thread, int* policy,
SceKernelSchedParam* param) {
return pthread_getschedparam(thread->pth, policy, param);
@ -1403,6 +1479,26 @@ int PS4_SYSV_ABI posix_pthread_condattr_setclock(ScePthreadCondattr* attr, clock
return SCE_OK;
}
int PS4_SYSV_ABI posix_pthread_getschedparam(ScePthread thread, int* policy,
SceKernelSchedParam* param) {
return scePthreadGetschedparam(thread, policy, param);
}
int PS4_SYSV_ABI posix_pthread_setschedparam(ScePthread thread, int policy,
const SceKernelSchedParam* param) {
return scePthreadSetschedparam(thread, policy, param);
}
int PS4_SYSV_ABI posix_pthread_attr_getschedpolicy(const ScePthreadAttr* attr, int* policy) {
return scePthreadAttrGetschedpolicy(attr, policy);
}
int PS4_SYSV_ABI scePthreadRename(ScePthread thread, const char* name) {
thread->name = name;
LOG_INFO(Kernel_Pthread, "scePthreadRename: name = {}", thread->name);
return SCE_OK;
}
void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate);
LIB_FUNCTION("0TyVk4MSLt0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_init);
@ -1427,6 +1523,7 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, scePthreadGetthreadid);
LIB_FUNCTION("1tKyG7RlMJo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetprio);
LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, scePthreadSetprio);
LIB_FUNCTION("GBUY7ywdULE", "libkernel", 1, "libkernel", 1, 1, scePthreadRename);
LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, scePthreadSelf);
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
@ -1442,6 +1539,7 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create);
LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create);
LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", 1, 1, scePthreadSetaffinity);
LIB_FUNCTION("rcrVFJsQWRY", "libkernel", 1, "libkernel", 1, 1, scePthreadGetaffinity);
LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1, scePthreadCreate);
LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, scePthreadYield);
LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_yield);
@ -1498,6 +1596,8 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("EjllaAqAPZo", "libScePosix", 1, "libkernel", 1, 1,
posix_pthread_condattr_setclock);
LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once);
LIB_FUNCTION("RtLRV-pBTTY", "libScePosix", 1, "libkernel", 1, 1,
posix_pthread_attr_getschedpolicy);
// openorbis weird functions
LIB_FUNCTION("7H0iTOciTLo", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_lock);
@ -1507,15 +1607,26 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("E+tyo3lp5Lw", "libScePosix", 1, "libkernel", 1, 1,
posix_pthread_attr_setdetachstate);
LIB_FUNCTION("zHchY8ft5pk", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_destroy);
LIB_FUNCTION("euKRgm0Vn2M", "libScePosix", 1, "libkernel", 1, 1,
posix_pthread_attr_setschedparam);
LIB_FUNCTION("7ZlAakEf0Qg", "libScePosix", 1, "libkernel", 1, 1,
posix_pthread_attr_setinheritsched);
LIB_FUNCTION("a2P9wYGeZvc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setprio);
LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np);
LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create);
LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_detach);
LIB_FUNCTION("CBNtXOoef-E", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_max);
LIB_FUNCTION("m0iS6jNsXds", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_min);
LIB_FUNCTION("FIs3-UQT9sg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getschedparam);
LIB_FUNCTION("Xs9hdiD7sAA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setschedparam);
LIB_FUNCTION("pDuPEf3m4fI", "libScePosix", 1, "libkernel", 1, 1, posix_sem_init);
LIB_FUNCTION("YCV5dGGBcCo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_wait);
LIB_FUNCTION("w5IHyvahg-o", "libScePosix", 1, "libkernel", 1, 1, posix_sem_timedwait);
LIB_FUNCTION("IKP8typ0QUk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_post);
LIB_FUNCTION("cDW233RAwWo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_destroy);
LIB_FUNCTION("Bq+LRV-N6Hk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_getvalue);
LIB_FUNCTION("0qOtCR-ZHck", "libScePosix", 1, "libkernel", 1, 1,
posix_pthread_attr_getstacksize);
// libs
RwlockSymbolsRegister(sym);
SemaphoreSymbolsRegister(sym);

View file

@ -9,7 +9,6 @@
#include <vector>
#include <pthread.h>
#include <sched.h>
#include "common/debug.h"
#include "common/types.h"
namespace Core::Loader {
@ -74,7 +73,6 @@ struct PthreadMutexInternal {
u8 reserved[256];
std::string name;
pthread_mutex_t pth_mutex;
std::unique_ptr<tracy::LockableCtx> tracy_lock;
};
struct PthreadMutexattrInternal {
@ -169,9 +167,12 @@ ScePthread PS4_SYSV_ABI scePthreadSelf();
int PS4_SYSV_ABI scePthreadAttrSetaffinity(ScePthreadAttr* pattr,
const /*SceKernelCpumask*/ u64 mask);
int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpumask*/ u64 mask);
int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u64* mask);
int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr,
PthreadEntryFunc start_routine, void* arg, const char* name);
int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio);
/***
* Mutex calls
*/

View file

@ -41,7 +41,6 @@ public:
AddWaiter(waiter);
// Perform the wait.
std::exchange(lk, std::unique_lock{waiter.mutex});
return waiter.Wait(lk, timeout);
}
@ -59,10 +58,9 @@ public:
it++;
continue;
}
std::scoped_lock lk2{waiter.mutex};
it = wait_list.erase(it);
token_count -= waiter.need_count;
waiter.cv.notify_one();
it = wait_list.erase(it);
}
return true;
@ -84,7 +82,6 @@ public:
public:
struct WaitingThread : public ListBaseHook {
std::mutex mutex;
std::string name;
std::condition_variable cv;
u32 priority;

View file

@ -214,6 +214,22 @@ int PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) {
return SCE_KERNEL_ERROR_EINVAL;
}
int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
OrbisKernelTimezone* timezone, int* dst_seconds) {
LOG_INFO(Kernel, "called");
if (timezone) {
sceKernelGettimezone(timezone);
param_1 -= (timezone->tz_minuteswest + timezone->tz_dsttime) * 60;
if (seconds)
*seconds = param_1;
if (dst_seconds)
*dst_seconds = timezone->tz_dsttime * 60;
} else {
return SCE_KERNEL_ERROR_EINVAL;
}
return SCE_OK;
}
void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
clock = std::make_unique<Common::NativeClock>();
initial_ptc = clock->GetUptime();
@ -239,6 +255,7 @@ void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("lLMT9vJAck0", "libkernel", 1, "libkernel", 1, 1, posix_clock_gettime);
LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, posix_clock_gettime);
LIB_FUNCTION("smIj7eqzZE8", "libScePosix", 1, "libkernel", 1, 1, posix_clock_getres);
LIB_FUNCTION("0NTHN1NKONI", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertLocaltimeToUtc);
}
} // namespace Libraries::Kernel

View file

@ -20,6 +20,7 @@
#include "core/libraries/np_trophy/np_trophy.h"
#include "core/libraries/pad/pad.h"
#include "core/libraries/playgo/playgo.h"
#include "core/libraries/random/random.h"
#include "core/libraries/rtc/rtc.h"
#include "core/libraries/save_data/savedata.h"
#include "core/libraries/screenshot/screenshot.h"
@ -43,8 +44,8 @@ namespace Libraries {
void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
LOG_INFO(Lib_Kernel, "Initializing HLE libraries");
Libraries::Kernel::LibKernel_Register(sym);
Libraries::VideoOut::RegisterLib(sym);
Libraries::GnmDriver::RegisterlibSceGnmDriver(sym);
Libraries::VideoOut::RegisterLib(sym);
if (!Config::isLleLibc()) {
Libraries::LibC::libcSymbolsRegister(sym);
}
@ -71,6 +72,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
Libraries::AppContent::RegisterlibSceAppContent(sym);
Libraries::PngDec::RegisterlibScePngDec(sym);
Libraries::PlayGo::RegisterlibScePlayGo(sym);
Libraries::Random::RegisterlibSceRandom(sym);
Libraries::Usbd::RegisterlibSceUsbd(sym);
Libraries::Pad::RegisterlibScePad(sym);
Libraries::Ajm::RegisterlibSceAjm(sym);

View file

@ -559,7 +559,7 @@ int PS4_SYSV_ABI sceNetEpollDestroy() {
}
int PS4_SYSV_ABI sceNetEpollWait() {
LOG_ERROR(Lib_Net, "(STUBBED) called");
LOG_TRACE(Lib_Net, "(STUBBED) called");
return ORBIS_OK;
}

View file

@ -79,7 +79,7 @@ int PS4_SYSV_ABI sceNetCtlUnregisterCallbackV6() {
}
int PS4_SYSV_ABI sceNetCtlCheckCallback() {
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
LOG_TRACE(Lib_NetCtl, "(STUBBED) called");
return ORBIS_OK;
}

View file

@ -870,7 +870,7 @@ int PS4_SYSV_ABI sceNpAsmTerminate() {
}
int PS4_SYSV_ABI sceNpCheckCallback() {
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
LOG_TRACE(Lib_NpManager, "(STUBBED) called");
return ORBIS_OK;
}
@ -3510,4 +3510,4 @@ void RegisterlibSceNpManager(Core::Loader::SymbolsResolver* sym) {
sceNpUnregisterStateCallbackForToolkit);
};
} // namespace Libraries::NpManager
} // namespace Libraries::NpManager

View file

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <core/file_format/playgo_chunk.h>
#include "common/logging/log.h"
#include "common/singleton.h"
#include "core/libraries/error_codes.h"
@ -50,9 +51,16 @@ s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoCh
uint32_t numberOfEntries, OrbisPlayGoLocus* outLoci) {
LOG_ERROR(Lib_PlayGo, "(STUBBED)called handle = {}, chunkIds = {}, numberOfEntries = {}",
handle, *chunkIds, numberOfEntries);
// assign all now so that scePlayGoGetLocus is not called again for every single entry
std::fill(outLoci, outLoci + numberOfEntries,
OrbisPlayGoLocusValue::ORBIS_PLAYGO_LOCUS_LOCAL_FAST);
auto* playgo = Common::Singleton<PlaygoChunk>::Instance();
for (uint32_t i = 0; i < numberOfEntries; i++) {
if (chunkIds[i] <= playgo->GetPlaygoHeader().mchunk_count) {
outLoci[i] = OrbisPlayGoLocusValue::ORBIS_PLAYGO_LOCUS_LOCAL_FAST;
} else {
return ORBIS_PLAYGO_ERROR_BAD_CHUNK_ID;
}
}
return ORBIS_OK;
}
@ -68,7 +76,7 @@ s32 PS4_SYSV_ABI scePlayGoGetProgress(OrbisPlayGoHandle handle, const OrbisPlayG
s32 PS4_SYSV_ABI scePlayGoGetToDoList(OrbisPlayGoHandle handle, OrbisPlayGoToDo* outTodoList,
u32 numberOfEntries, u32* outEntries) {
LOG_ERROR(Lib_PlayGo, "(STUBBED)called");
if (handle != shadMagic)
if (handle != 1)
return ORBIS_PLAYGO_ERROR_BAD_HANDLE;
if (outTodoList == nullptr)
return ORBIS_PLAYGO_ERROR_BAD_POINTER;
@ -86,7 +94,7 @@ s32 PS4_SYSV_ABI scePlayGoInitialize(OrbisPlayGoInitParams* param) {
}
s32 PS4_SYSV_ABI scePlayGoOpen(OrbisPlayGoHandle* outHandle, const void* param) {
*outHandle = shadMagic;
*outHandle = 1;
LOG_INFO(Lib_PlayGo, "(STUBBED)called");
return ORBIS_OK;
}

View file

@ -0,0 +1,27 @@
// 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 "random.h"
namespace Libraries::Random {
s32 PS4_SYSV_ABI sceRandomGetRandomNumber(u8* buf, size_t size) {
LOG_TRACE(Lib_Random, "called");
if (size > SCE_RANDOM_MAX_SIZE) {
return SCE_RANDOM_ERROR_INVALID;
}
for (auto i = 0; i < size; ++i) {
buf[i] = std::rand() & 0xFF;
}
return ORBIS_OK;
}
void RegisterlibSceRandom(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("PI7jIZj4pcE", "libSceRandom", 1, "libSceRandom", 1, 1, sceRandomGetRandomNumber);
};
} // namespace Libraries::Random

View file

@ -0,0 +1,17 @@
// 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::Random {
constexpr int32_t SCE_RANDOM_MAX_SIZE = 64;
s32 PS4_SYSV_ABI sceRandomGetRandomNumber(u8* buf, size_t size);
void RegisterlibSceRandom(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Random

View file

@ -15,7 +15,7 @@
#include "error_codes.h"
namespace Libraries::SaveData {
bool is_rw_mode = false;
static constexpr std::string_view g_mount_point = "/savedata0"; // temp mount point (todo)
std::string game_serial;
@ -180,27 +180,31 @@ int PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond*
OrbisSaveDataDirNameSearchResult* result) {
if (cond == nullptr)
return ORBIS_SAVE_DATA_ERROR_PARAMETER;
LOG_ERROR(Lib_SaveData, "TODO sceSaveDataDirNameSearch: Add params");
LOG_INFO(Lib_SaveData, "called");
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
std::to_string(cond->userId) / game_serial;
if (!mount_dir.empty() && std::filesystem::exists(mount_dir)) {
if (cond->dirName == nullptr) { // look for all dirs if no dir is provided.
if (cond->dirName == nullptr || std::string_view(cond->dirName->data)
.empty()) { // look for all dirs if no dir is provided.
for (int i = 0; const auto& entry : std::filesystem::directory_iterator(mount_dir)) {
if (std::filesystem::is_directory(entry.path())) {
i++;
result->dirNamesNum = 0; // why is it 1024? is it max?
if (std::filesystem::is_directory(entry.path()) &&
entry.path().filename().string() != "sdmemory") {
// sceSaveDataDirNameSearch does not search for dataMemory1/2 dirs.
// copy dir name to be used by sceSaveDataMount in read mode.
strncpy(result->dirNames[i].data, entry.path().filename().string().c_str(), 32);
result->hitNum = i + 1;
result->dirNamesNum = i + 1; // to confirm
result->setNum = i + 1; // to confirm
i++;
result->hitNum = i;
result->dirNamesNum = i;
result->setNum = i;
}
}
} else { // Need a game to test.
LOG_ERROR(Lib_SaveData, "Check Me. sceSaveDataDirNameSearch: dirName = {}",
cond->dirName->data);
strncpy(result->dirNames[0].data, cond->dirName->data, 32);
result->hitNum = 1;
result->dirNamesNum = 1; // to confirm
result->setNum = 1; // to confirm
result->dirNamesNum = 1;
result->setNum = 1;
}
} else {
result->hitNum = 0;
@ -303,8 +307,51 @@ int PS4_SYSV_ABI sceSaveDataGetMountInfo(const OrbisSaveDataMountPoint* mountPoi
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSaveDataGetParam() {
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
int PS4_SYSV_ABI sceSaveDataGetParam(const OrbisSaveDataMountPoint* mountPoint,
const OrbisSaveDataParamType paramType, void* paramBuf,
const size_t paramBufSize, size_t* gotSize) {
if (mountPoint == nullptr)
return ORBIS_SAVE_DATA_ERROR_PARAMETER;
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
const auto mount_dir = mnt->GetHostPath(mountPoint->data);
Common::FS::IOFile file(mount_dir / "param.txt", Common::FS::FileAccessMode::Read);
OrbisSaveDataParam params;
file.Read(params);
LOG_INFO(Lib_SaveData, "called");
switch (paramType) {
case ORBIS_SAVE_DATA_PARAM_TYPE_ALL: {
memcpy(paramBuf, &params, sizeof(OrbisSaveDataParam));
*gotSize = sizeof(OrbisSaveDataParam);
} break;
case ORBIS_SAVE_DATA_PARAM_TYPE_TITLE: {
std::memcpy(paramBuf, &params.title, ORBIS_SAVE_DATA_TITLE_MAXSIZE);
*gotSize = ORBIS_SAVE_DATA_TITLE_MAXSIZE;
} break;
case ORBIS_SAVE_DATA_PARAM_TYPE_SUB_TITLE: {
std::memcpy(paramBuf, &params.subTitle, ORBIS_SAVE_DATA_SUBTITLE_MAXSIZE);
*gotSize = ORBIS_SAVE_DATA_SUBTITLE_MAXSIZE;
} break;
case ORBIS_SAVE_DATA_PARAM_TYPE_DETAIL: {
std::memcpy(paramBuf, &params.detail, ORBIS_SAVE_DATA_DETAIL_MAXSIZE);
*gotSize = ORBIS_SAVE_DATA_DETAIL_MAXSIZE;
} break;
case ORBIS_SAVE_DATA_PARAM_TYPE_USER_PARAM: {
std::memcpy(paramBuf, &params.userParam, sizeof(u32));
*gotSize = sizeof(u32);
} break;
case ORBIS_SAVE_DATA_PARAM_TYPE_MTIME: {
std::memcpy(paramBuf, &params.mtime, sizeof(time_t));
*gotSize = sizeof(time_t);
} break;
default: {
UNREACHABLE_MSG("Unknown Param = {}", paramType);
} break;
}
return ORBIS_OK;
}
@ -321,7 +368,7 @@ int PS4_SYSV_ABI sceSaveDataGetSaveDataCount() {
int PS4_SYSV_ABI sceSaveDataGetSaveDataMemory(const u32 userId, void* buf, const size_t bufSize,
const int64_t offset) {
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
std::to_string(userId) / game_serial / "save_mem1.sav";
std::to_string(userId) / game_serial / "sdmemory/save_mem1.sav";
Common::FS::IOFile file(mount_dir, Common::FS::FileAccessMode::Read);
if (!file.IsOpen()) {
@ -336,7 +383,7 @@ int PS4_SYSV_ABI sceSaveDataGetSaveDataMemory(const u32 userId, void* buf, const
int PS4_SYSV_ABI sceSaveDataGetSaveDataMemory2(OrbisSaveDataMemoryGet2* getParam) {
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
std::to_string(getParam->userId) / game_serial;
std::to_string(getParam->userId) / game_serial / "sdmemory";
if (getParam == nullptr)
return ORBIS_SAVE_DATA_ERROR_PARAMETER;
if (getParam->data != nullptr) {
@ -443,6 +490,7 @@ s32 saveDataMount(u32 user_id, char* dir_name, u32 mount_mode,
case ORBIS_SAVE_DATA_MOUNT_MODE_RDWR:
case ORBIS_SAVE_DATA_MOUNT_MODE_RDWR | ORBIS_SAVE_DATA_MOUNT_MODE_DESTRUCT_OFF:
case ORBIS_SAVE_DATA_MOUNT_MODE_RDONLY | ORBIS_SAVE_DATA_MOUNT_MODE_DESTRUCT_OFF: {
is_rw_mode = (mount_mode == ORBIS_SAVE_DATA_MOUNT_MODE_RDWR) ? true : false;
if (!std::filesystem::exists(mount_dir)) {
return ORBIS_SAVE_DATA_ERROR_NOT_FOUND;
}
@ -460,10 +508,6 @@ s32 saveDataMount(u32 user_id, char* dir_name, u32 mount_mode,
case ORBIS_SAVE_DATA_MOUNT_MODE_CREATE | ORBIS_SAVE_DATA_MOUNT_MODE_DESTRUCT_OFF |
ORBIS_SAVE_DATA_MOUNT_MODE_COPY_ICON: {
if (std::filesystem::exists(mount_dir)) {
g_mount_point.copy(mount_result->mount_point.data, 16);
mnt->Mount(mount_dir, mount_result->mount_point.data);
mount_result->required_blocks = 0;
mount_result->mount_status = 0;
return ORBIS_SAVE_DATA_ERROR_EXISTS;
}
if (std::filesystem::create_directories(mount_dir)) {
@ -483,7 +527,7 @@ s32 saveDataMount(u32 user_id, char* dir_name, u32 mount_mode,
mount_result->mount_status = 1;
} break;
default:
UNREACHABLE();
UNREACHABLE_MSG("Unknown mount mode = {}", mount_mode);
}
mount_result->required_blocks = 0;
@ -583,15 +627,46 @@ int PS4_SYSV_ABI sceSaveDataSetEventInfo() {
int PS4_SYSV_ABI sceSaveDataSetParam(const OrbisSaveDataMountPoint* mountPoint,
OrbisSaveDataParamType paramType, const void* paramBuf,
size_t paramBufSize) {
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
const auto mount_dir = mnt->GetHostPath(mountPoint->data);
LOG_INFO(Lib_SaveData, "called = {}, mountPoint->data = {}", mount_dir.string(),
mountPoint->data);
if (paramBuf == nullptr)
return ORBIS_SAVE_DATA_ERROR_PARAMETER;
if (paramBuf != nullptr) {
Common::FS::IOFile file(mount_dir / "param.txt", Common::FS::FileAccessMode::Write);
file.WriteRaw<u8>(paramBuf, paramBufSize);
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
const auto mount_dir = mnt->GetHostPath(mountPoint->data) / "param.txt";
OrbisSaveDataParam params;
if (std::filesystem::exists(mount_dir)) {
Common::FS::IOFile file(mount_dir, Common::FS::FileAccessMode::Read);
file.ReadRaw<u8>(&params, sizeof(OrbisSaveDataParam));
}
LOG_INFO(Lib_SaveData, "called");
switch (paramType) {
case ORBIS_SAVE_DATA_PARAM_TYPE_ALL: {
memcpy(&params, paramBuf, sizeof(OrbisSaveDataParam));
} break;
case ORBIS_SAVE_DATA_PARAM_TYPE_TITLE: {
strncpy(params.title, static_cast<const char*>(paramBuf), paramBufSize);
} break;
case ORBIS_SAVE_DATA_PARAM_TYPE_SUB_TITLE: {
strncpy(params.subTitle, static_cast<const char*>(paramBuf), paramBufSize);
} break;
case ORBIS_SAVE_DATA_PARAM_TYPE_DETAIL: {
strncpy(params.detail, static_cast<const char*>(paramBuf), paramBufSize);
} break;
case ORBIS_SAVE_DATA_PARAM_TYPE_USER_PARAM: {
params.userParam = *(static_cast<const u32*>(paramBuf));
} break;
case ORBIS_SAVE_DATA_PARAM_TYPE_MTIME: {
params.mtime = *(static_cast<const time_t*>(paramBuf));
} break;
default: {
UNREACHABLE_MSG("Unknown Param = {}", paramType);
}
}
Common::FS::IOFile file(mount_dir, Common::FS::FileAccessMode::Write);
file.WriteRaw<u8>(&params, sizeof(OrbisSaveDataParam));
return ORBIS_OK;
}
@ -604,11 +679,11 @@ int PS4_SYSV_ABI sceSaveDataSetSaveDataMemory(const u32 userId, const void* buf,
const size_t bufSize, const int64_t offset) {
LOG_INFO(Lib_SaveData, "called");
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
std::to_string(userId) / game_serial / "save_mem1.sav";
std::to_string(userId) / game_serial / "sdmemory/save_mem1.sav";
Common::FS::IOFile file(mount_dir, Common::FS::FileAccessMode::Write);
file.Seek(offset);
file.WriteRaw<u8>((void*)buf, bufSize);
file.WriteRaw<u8>(buf, bufSize);
return ORBIS_OK;
}
@ -616,13 +691,13 @@ int PS4_SYSV_ABI sceSaveDataSetSaveDataMemory(const u32 userId, const void* buf,
int PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(const OrbisSaveDataMemorySet2* setParam) {
LOG_INFO(Lib_SaveData, "called: dataNum = {}, slotId= {}", setParam->dataNum, setParam->slotId);
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
std::to_string(setParam->userId) / game_serial;
std::to_string(setParam->userId) / game_serial / "sdmemory";
if (setParam->data != nullptr) {
Common::FS::IOFile file(mount_dir / "save_mem2.sav", Common::FS::FileAccessMode::Write);
if (!file.IsOpen())
return -1;
file.Seek(setParam->data->offset);
file.WriteRaw<u8>((void*)setParam->data->buf, setParam->data->bufSize);
file.WriteRaw<u8>(setParam->data->buf, setParam->data->bufSize);
}
if (setParam->param != nullptr) {
@ -632,7 +707,7 @@ int PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(const OrbisSaveDataMemorySet2* se
if (setParam->icon != nullptr) {
Common::FS::IOFile file(mount_dir / "save_icon.png", Common::FS::FileAccessMode::Write);
file.WriteRaw<u8>((void*)setParam->icon->buf, setParam->icon->bufSize);
file.WriteRaw<u8>(setParam->icon->buf, setParam->icon->bufSize);
}
return ORBIS_OK;
@ -644,7 +719,7 @@ int PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory(u32 userId, size_t memorySize,
LOG_INFO(Lib_SaveData, "called:userId = {}, memorySize = {}", userId, memorySize);
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
std::to_string(userId) / game_serial;
std::to_string(userId) / game_serial / "sdmemory";
if (std::filesystem::exists(mount_dir)) {
return ORBIS_SAVE_DATA_ERROR_EXISTS;
@ -663,7 +738,7 @@ int PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetup2
LOG_INFO(Lib_SaveData, "called");
// if (setupParam->option == 1) { // check this later.
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
std::to_string(setupParam->userId) / game_serial;
std::to_string(setupParam->userId) / game_serial / "sdmemory";
if (std::filesystem::exists(mount_dir) &&
std::filesystem::exists(mount_dir / "save_mem2.sav")) {
Common::FS::IOFile file(mount_dir / "save_mem2.sav", Common::FS::FileAccessMode::Read);
@ -717,16 +792,17 @@ int PS4_SYSV_ABI sceSaveDataTransferringMount() {
}
s32 PS4_SYSV_ABI sceSaveDataUmount(const OrbisSaveDataMountPoint* mountPoint) {
LOG_INFO(Lib_SaveData, "mountPoint = {}", std::string(mountPoint->data));
if (std::string(mountPoint->data).empty()) {
return ORBIS_SAVE_DATA_ERROR_NOT_MOUNTED;
}
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
std::to_string(1) / game_serial / mountPoint->data;
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
const auto& guest_path = mnt->GetHostPath(mountPoint->data);
if (guest_path.empty())
return ORBIS_SAVE_DATA_ERROR_NOT_MOUNTED;
mnt->Unmount(mount_dir, mountPoint->data);
LOG_INFO(Lib_SaveData, "mountPoint = {}", std::string(mountPoint->data));
return ORBIS_OK;
}
@ -736,23 +812,33 @@ int PS4_SYSV_ABI sceSaveDataUmountSys() {
}
int PS4_SYSV_ABI sceSaveDataUmountWithBackup(const OrbisSaveDataMountPoint* mountPoint) {
LOG_ERROR(Lib_SaveData, "called = {}", std::string(mountPoint->data));
LOG_INFO(Lib_SaveData, "called mount = {}, is_rw_mode = {}", std::string(mountPoint->data),
is_rw_mode);
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
const auto mount_dir = mnt->GetHostPath(mountPoint->data);
if (!std::filesystem::exists(mount_dir)) {
return ORBIS_SAVE_DATA_ERROR_NOT_FOUND;
}
// leave disabled for now. and just unmount.
std::filesystem::create_directories(mount_dir.parent_path() / "backup");
/* if (is_rw_mode) { // backup is done only when mount mode is ReadWrite.
auto backup_path = mount_dir;
std::string save_data_dir = (mount_dir.string() + "_backup");
backup_path.replace_filename(save_data_dir);
for (const auto& entry : std::filesystem::recursive_directory_iterator(mount_dir)) {
const auto& path = entry.path();
const auto target_path = mount_dir.parent_path() / "backup";
if (std::filesystem::is_regular_file(path)) {
std::filesystem::copy(path, target_path,
std::filesystem::copy_options::overwrite_existing);
std::filesystem::create_directories(backup_path);
for (const auto& entry : std::filesystem::recursive_directory_iterator(mount_dir)) {
const auto& path = entry.path();
if (std::filesystem::is_regular_file(path)) {
std::filesystem::copy(path, save_data_dir,
std::filesystem::copy_options::overwrite_existing);
}
}
}
}*/
const auto& guest_path = mnt->GetHostPath(mountPoint->data);
if (guest_path.empty())
return ORBIS_SAVE_DATA_ERROR_NOT_MOUNTED;
mnt->Unmount(mount_dir, mountPoint->data);
return ORBIS_OK;

View file

@ -242,6 +242,13 @@ struct OrbisSaveDataMemorySync {
u8 reserved[28];
};
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_ALL = 0;
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_TITLE = 1;
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_SUB_TITLE = 2;
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_DETAIL = 3;
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_USER_PARAM = 4;
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_MTIME = 5;
int PS4_SYSV_ABI sceSaveDataAbort();
int PS4_SYSV_ABI sceSaveDataBackup();
int PS4_SYSV_ABI sceSaveDataBindPsnAccount();
@ -291,7 +298,9 @@ int PS4_SYSV_ABI sceSaveDataGetFormat();
int PS4_SYSV_ABI sceSaveDataGetMountedSaveDataCount();
int PS4_SYSV_ABI sceSaveDataGetMountInfo(const OrbisSaveDataMountPoint* mountPoint,
OrbisSaveDataMountInfo* info);
int PS4_SYSV_ABI sceSaveDataGetParam();
int PS4_SYSV_ABI sceSaveDataGetParam(const OrbisSaveDataMountPoint* mountPoint,
const OrbisSaveDataParamType paramType, void* paramBuf,
const size_t paramBufSize, size_t* gotSize);
int PS4_SYSV_ABI sceSaveDataGetProgress();
int PS4_SYSV_ABI sceSaveDataGetSaveDataCount();
int PS4_SYSV_ABI sceSaveDataGetSaveDataMemory(const u32 userId, void* buf, const size_t bufSize,

View file

@ -3,14 +3,16 @@
#include <pthread.h>
#include "common/assert.h"
#include "common/config.h"
#include "common/debug.h"
#include "common/thread.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/time_management.h"
#include "core/libraries/videoout/driver.h"
#include "core/platform.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h"
extern std::unique_ptr<Vulkan::RendererVulkan> renderer;
extern std::unique_ptr<AmdGpu::Liverpool> liverpool;
namespace Libraries::VideoOut {
@ -41,20 +43,18 @@ VideoOutDriver::VideoOutDriver(u32 width, u32 height) {
main_port.resolution.fullHeight = height;
main_port.resolution.paneWidth = width;
main_port.resolution.paneHeight = height;
present_thread = std::jthread([&](std::stop_token token) { PresentThread(token); });
}
VideoOutDriver::~VideoOutDriver() = default;
int VideoOutDriver::Open(const ServiceThreadParams* params) {
std::scoped_lock lock{mutex};
if (main_port.is_open) {
return ORBIS_VIDEO_OUT_ERROR_RESOURCE_BUSY;
}
int handle = 1;
main_port.is_open = true;
return handle;
liverpool->SetVoPort(&main_port);
return 1;
}
void VideoOutDriver::Close(s32 handle) {
@ -158,31 +158,22 @@ int VideoOutDriver::UnregisterBuffers(VideoOutPort* port, s32 attributeIndex) {
return ORBIS_OK;
}
void VideoOutDriver::Flip(std::chrono::microseconds timeout) {
Request req;
{
std::unique_lock lock{mutex};
submit_cond.wait_for(lock, timeout, [&] { return !requests.empty(); });
if (requests.empty()) {
renderer->ShowSplash();
return;
}
// Retrieve the request.
req = requests.front();
requests.pop();
std::chrono::microseconds VideoOutDriver::Flip(const Request& req) {
if (!req) {
return std::chrono::microseconds{0};
}
const auto start = std::chrono::high_resolution_clock::now();
// Whatever the game is rendering show splash if it is active
if (!renderer->ShowSplash(req.frame)) {
// Present the frame.
renderer->Present(req.frame);
}
std::scoped_lock lock{mutex};
// Update flip status.
auto& flip_status = req.port->flip_status;
auto* port = req.port;
auto& flip_status = port->flip_status;
flip_status.count++;
flip_status.processTime = Libraries::Kernel::sceKernelGetProcessTime();
flip_status.tsc = Libraries::Kernel::sceKernelReadTsc();
@ -192,7 +183,7 @@ void VideoOutDriver::Flip(std::chrono::microseconds timeout) {
flip_status.flipPendingNum = static_cast<int>(requests.size());
// Trigger flip events for the port.
for (auto& event : req.port->flip_events) {
for (auto& event : port->flip_events) {
if (event != nullptr) {
event->TriggerEvent(SCE_VIDEO_OUT_EVENT_FLIP, Kernel::SceKernelEvent::Filter::VideoOut,
reinterpret_cast<void*>(req.flip_arg));
@ -201,21 +192,23 @@ void VideoOutDriver::Flip(std::chrono::microseconds timeout) {
// Reset flip label
if (req.index != -1) {
req.port->buffer_labels[req.index] = 0;
port->buffer_labels[req.index] = 0;
port->SignalVoLabel();
}
const auto end = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(end - start);
}
bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg,
bool is_eop /*= false*/) {
std::scoped_lock lock{mutex};
Vulkan::Frame* frame;
if (index == -1) {
frame = renderer->PrepareBlankFrame();
} else {
const auto& buffer = port->buffer_slots[index];
const auto& group = port->groups[buffer.group_index];
frame = renderer->PrepareFrame(group, buffer.address_left);
frame = renderer->PrepareFrame(group, buffer.address_left, is_eop);
}
if (index != -1 && requests.size() >= port->NumRegisteredBuffers()) {
@ -223,6 +216,7 @@ bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg,
return false;
}
std::scoped_lock lock{mutex};
requests.push({
.frame = frame,
.port = port,
@ -234,24 +228,53 @@ bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg,
port->flip_status.flipPendingNum = static_cast<int>(requests.size());
port->flip_status.gcQueueNum = 0;
submit_cond.notify_one();
return true;
}
void VideoOutDriver::Vblank() {
std::scoped_lock lock{mutex};
void VideoOutDriver::PresentThread(std::stop_token token) {
static constexpr std::chrono::milliseconds VblankPeriod{16};
Common::SetCurrentThreadName("PresentThread");
auto& vblank_status = main_port.vblank_status;
vblank_status.count++;
vblank_status.processTime = Libraries::Kernel::sceKernelGetProcessTime();
vblank_status.tsc = Libraries::Kernel::sceKernelReadTsc();
const auto receive_request = [this] -> Request {
std::scoped_lock lk{mutex};
if (!requests.empty()) {
const auto request = requests.front();
requests.pop();
return request;
}
return {};
};
// Trigger flip events for the port.
for (auto& event : main_port.vblank_events) {
if (event != nullptr) {
event->TriggerEvent(SCE_VIDEO_OUT_EVENT_VBLANK,
Kernel::SceKernelEvent::Filter::VideoOut, nullptr);
auto vblank_period = VblankPeriod / Config::vblankDiv();
auto delay = std::chrono::microseconds{0};
while (!token.stop_requested()) {
// Sleep for most of the vblank duration.
std::this_thread::sleep_for(vblank_period - delay);
// Check if it's time to take a request.
auto& vblank_status = main_port.vblank_status;
if (vblank_status.count % (main_port.flip_rate + 1) == 0) {
const auto request = receive_request();
delay = Flip(request);
FRAME_END;
}
{
// Needs lock here as can be concurrently read by `sceVideoOutGetVblankStatus`
std::unique_lock lock{main_port.vo_mutex};
vblank_status.count++;
vblank_status.processTime = Libraries::Kernel::sceKernelGetProcessTime();
vblank_status.tsc = Libraries::Kernel::sceKernelReadTsc();
main_port.vblank_cv.notify_all();
}
// Trigger flip events for the port.
for (auto& event : main_port.vblank_events) {
if (event != nullptr) {
event->TriggerEvent(SCE_VIDEO_OUT_EVENT_VBLANK,
Kernel::SceKernelEvent::Filter::VideoOut, nullptr);
}
}
}
}

View file

@ -3,10 +3,13 @@
#pragma once
#include "common/debug.h"
#include "common/polyfill_thread.h"
#include "core/libraries/videoout/video_out.h"
#include <condition_variable>
#include <mutex>
#include <queue>
#include "core/libraries/videoout/video_out.h"
namespace Vulkan {
struct Frame;
@ -25,6 +28,9 @@ struct VideoOutPort {
SceVideoOutVblankStatus vblank_status;
std::vector<Kernel::SceKernelEqueue> flip_events;
std::vector<Kernel::SceKernelEqueue> vblank_events;
std::mutex vo_mutex;
std::condition_variable vo_cv;
std::condition_variable vblank_cv;
int flip_rate = 0;
s32 FindFreeGroup() const {
@ -35,6 +41,22 @@ struct VideoOutPort {
return index;
}
bool IsVoLabel(const u64* address) const {
const u64* start = &buffer_labels[0];
const u64* end = &buffer_labels[MaxDisplayBuffers - 1];
return address >= start && address <= end;
}
void WaitVoLabel(auto&& pred) {
std::unique_lock lk{vo_mutex};
vo_cv.wait(lk, pred);
}
void SignalVoLabel() {
std::scoped_lock lk{vo_mutex};
vo_cv.notify_one();
}
[[nodiscard]] int NumRegisteredBuffers() const {
return std::count_if(buffer_slots.cbegin(), buffer_slots.cend(),
[](auto& buffer) { return buffer.group_index != -1; });
@ -63,11 +85,8 @@ public:
const BufferAttribute* attribute);
int UnregisterBuffers(VideoOutPort* port, s32 attributeIndex);
void Flip(std::chrono::microseconds timeout);
bool SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop = false);
void Vblank();
private:
struct Request {
Vulkan::Frame* frame;
@ -76,14 +95,19 @@ private:
s64 flip_arg;
u64 submit_tsc;
bool eop;
operator bool() const noexcept {
return frame != nullptr;
}
};
std::chrono::microseconds Flip(const Request& req);
void PresentThread(std::stop_token token);
std::mutex mutex;
VideoOutPort main_port{};
std::condition_variable_any submit_cond;
std::condition_variable done_cond;
std::jthread present_thread;
std::queue<Request> requests;
bool is_neo{};
};
} // namespace Libraries::VideoOut

View file

@ -183,6 +183,7 @@ s32 PS4_SYSV_ABI sceVideoOutGetVblankStatus(int handle, SceVideoOutVblankStatus*
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
}
std::unique_lock lock{port->vo_mutex};
*status = port->vblank_status;
return ORBIS_OK;
}
@ -229,14 +230,6 @@ s32 PS4_SYSV_ABI sceVideoOutUnregisterBuffers(s32 handle, s32 attributeIndex) {
return driver->UnregisterBuffers(port, attributeIndex);
}
void Flip(std::chrono::microseconds micros) {
return driver->Flip(micros);
}
void Vblank() {
return driver->Vblank();
}
void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr) {
auto* port = driver->GetPort(handle);
ASSERT(port);
@ -266,6 +259,18 @@ s32 PS4_SYSV_ABI sceVideoOutGetDeviceCapabilityInfo(
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideoOutWaitVblank(s32 handle) {
auto* port = driver->GetPort(handle);
if (!port) {
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
}
std::unique_lock lock{port->vo_mutex};
const auto prev_counter = port->vblank_status.count;
port->vblank_cv.wait(lock, [&]() { return prev_counter != port->vblank_status.count; });
return ORBIS_OK;
}
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
driver = std::make_unique<VideoOutDriver>(Config::getScreenWidth(), Config::getScreenHeight());
@ -294,6 +299,7 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) {
sceVideoOutGetVblankStatus);
LIB_FUNCTION("kGVLc3htQE8", "libSceVideoOut", 1, "libSceVideoOut", 0, 0,
sceVideoOutGetDeviceCapabilityInfo);
LIB_FUNCTION("j6RaAUlaLv0", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutWaitVblank);
// openOrbis appears to have libSceVideoOut_v1 module libSceVideoOut_v1.1
LIB_FUNCTION("Up36PTk687E", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutOpen);

View file

@ -92,11 +92,12 @@ void PS4_SYSV_ABI sceVideoOutSetBufferAttribute(BufferAttribute* attribute, Pixe
u32 tilingMode, u32 aspectRatio, u32 width,
u32 height, u32 pitchInPixel);
s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata);
s32 PS4_SYSV_ABI sceVideoOutAddVBlankEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata);
s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata);
s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* const* addresses,
s32 bufferNum, const BufferAttribute* attribute);
s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate);
s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle);
s32 PS4_SYSV_ABI sceVideoOutWaitVblank(s32 handle);
s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode, s64 flipArg);
s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status);
s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status);
@ -104,9 +105,6 @@ s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 i
const void* param);
s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle);
void Flip(std::chrono::microseconds micros);
void Vblank();
// Internal system functions
void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr);
s32 sceVideoOutSubmitEopFlip(s32 handle, u32 buf_id, u32 mode, u32 arg, void** unk);

View file

@ -193,7 +193,7 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem
// Find first free area to map the file.
if (False(flags & MemoryMapFlags::Fixed)) {
mapped_addr = SearchFree(mapped_addr, size_aligned);
mapped_addr = SearchFree(mapped_addr, size_aligned, 1);
}
if (True(flags & MemoryMapFlags::Fixed)) {
@ -240,6 +240,7 @@ void MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) {
vma.prot = MemoryProt::NoAccess;
vma.phys_base = 0;
vma.disallow_merge = false;
vma.name = "";
MergeAdjacent(vma_map, new_it);
// Unmap the memory region.
@ -334,6 +335,7 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags,
info->is_flexible.Assign(vma.type == VMAType::Flexible);
info->is_direct.Assign(vma.type == VMAType::Direct);
info->is_commited.Assign(vma.type != VMAType::Free);
vma.name.copy(info->name.data(), std::min(info->name.size(), vma.name.size()));
if (vma.type == VMAType::Direct) {
const auto dmem_it = FindDmemArea(vma.phys_base);
ASSERT(dmem_it != dmem_map.end());
@ -392,8 +394,23 @@ std::pair<vk::Buffer, size_t> MemoryManager::GetVulkanBuffer(VAddr addr) {
return std::make_pair(*it->second.buffer, addr - it->first);
}
VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) {
void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name) {
auto it = FindVMA(virtual_addr);
ASSERT_MSG(it->second.Contains(virtual_addr, size),
"Range provided is not fully containted in vma");
it->second.name = name;
}
VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) {
// If the requested address is below the mapped range, start search from the lowest address
auto min_search_address = impl.SystemManagedVirtualBase();
if (virtual_addr < min_search_address) {
virtual_addr = min_search_address;
}
auto it = FindVMA(virtual_addr);
ASSERT_MSG(it != vma_map.end(), "Specified mapping address was not found!");
// If the VMA is free and contains the requested mapping we are done.
if (it->second.IsFree() && it->second.Contains(virtual_addr, size)) {
return virtual_addr;

View file

@ -179,6 +179,8 @@ public:
int GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut, void** directMemoryStartOut,
void** directMemoryEndOut);
void NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name);
private:
VMAHandle FindVMA(VAddr target) {
return std::prev(vma_map.upper_bound(target));