diff --git a/CMakeLists.txt b/CMakeLists.txt index d430b5d7b..f0f3845ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -501,6 +501,8 @@ set(HLE_LIBC_INTERNAL_LIB src/core/libraries/libc_internal/libc_internal.cpp src/core/libraries/libc_internal/libc_internal_str.h src/core/libraries/libc_internal/libc_internal_math.cpp src/core/libraries/libc_internal/libc_internal_math.h + src/core/libraries/libc_internal/libc_internal_threads.cpp + src/core/libraries/libc_internal/libc_internal_threads.h src/core/libraries/libc_internal/printf.h ) @@ -618,6 +620,7 @@ set(NP_LIBS src/core/libraries/np/np_error.h src/core/libraries/np/np_sns_facebook_dialog.h src/core/libraries/np/np_partner.cpp src/core/libraries/np/np_partner.h + src/core/libraries/np/object_manager.h ) set(ZLIB_LIB src/core/libraries/zlib/zlib.cpp diff --git a/src/core/libraries/kernel/file_system.h b/src/core/libraries/kernel/file_system.h index 507f0952c..d8989828a 100644 --- a/src/core/libraries/kernel/file_system.h +++ b/src/core/libraries/kernel/file_system.h @@ -65,6 +65,9 @@ constexpr int ORBIS_KERNEL_O_DSYNC = 0x1000; constexpr int ORBIS_KERNEL_O_DIRECT = 0x00010000; constexpr int ORBIS_KERNEL_O_DIRECTORY = 0x00020000; +s32 PS4_SYSV_ABI posix_open(const char* path, s32 flags, u16 mode); +s32 PS4_SYSV_ABI posix_close(s32 fd); +s64 PS4_SYSV_ABI posix_lseek(s32 fd, s64 offset, s32 whence); s64 PS4_SYSV_ABI sceKernelWrite(s32 fd, const void* buf, u64 nbytes); s64 PS4_SYSV_ABI sceKernelRead(s32 fd, void* buf, u64 nbytes); s64 PS4_SYSV_ABI sceKernelPread(s32 fd, void* buf, u64 nbytes, s64 offset); diff --git a/src/core/libraries/kernel/threads.h b/src/core/libraries/kernel/threads.h index bcccf1695..42ab0b13f 100644 --- a/src/core/libraries/kernel/threads.h +++ b/src/core/libraries/kernel/threads.h @@ -28,6 +28,16 @@ int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr int PS4_SYSV_ABI posix_pthread_join(PthreadT pthread, void** thread_return); +int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr); +int PS4_SYSV_ABI posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, PthreadMutexType type); +int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr); + +int PS4_SYSV_ABI scePthreadMutexInit(PthreadMutexT* mutex, const PthreadMutexAttrT* mutex_attr, + const char* name); +int PS4_SYSV_ABI posix_pthread_mutex_lock(PthreadMutexT* mutex); +int PS4_SYSV_ABI posix_pthread_mutex_unlock(PthreadMutexT* mutex); +int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex); + void RegisterThreads(Core::Loader::SymbolsResolver* sym); class Thread { diff --git a/src/core/libraries/libc_internal/libc_internal.cpp b/src/core/libraries/libc_internal/libc_internal.cpp index df35680a4..6a92d2317 100644 --- a/src/core/libraries/libc_internal/libc_internal.cpp +++ b/src/core/libraries/libc_internal/libc_internal.cpp @@ -11,6 +11,7 @@ #include "libc_internal_math.h" #include "libc_internal_memory.h" #include "libc_internal_str.h" +#include "libc_internal_threads.h" #include "printf.h" namespace Libraries::LibcInternal { @@ -20,5 +21,11 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) { RegisterlibSceLibcInternalStr(sym); RegisterlibSceLibcInternalMemory(sym); RegisterlibSceLibcInternalIo(sym); + RegisterlibSceLibcInternalThreads(sym); +} + +void ForceRegisterLib(Core::Loader::SymbolsResolver* sym) { + // Used to forcibly enable HLEs for broken LLE functions. + ForceRegisterlibSceLibcInternalIo(sym); } } // namespace Libraries::LibcInternal \ No newline at end of file diff --git a/src/core/libraries/libc_internal/libc_internal.h b/src/core/libraries/libc_internal/libc_internal.h index 9b00b72d8..1494a2ab2 100644 --- a/src/core/libraries/libc_internal/libc_internal.h +++ b/src/core/libraries/libc_internal/libc_internal.h @@ -15,4 +15,5 @@ namespace Libraries::LibcInternal { // so everything is just in the .cpp file void RegisterLib(Core::Loader::SymbolsResolver* sym); +void ForceRegisterLib(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::LibcInternal \ No newline at end of file diff --git a/src/core/libraries/libc_internal/libc_internal_io.cpp b/src/core/libraries/libc_internal/libc_internal_io.cpp index 8105b66cc..504ba5b48 100644 --- a/src/core/libraries/libc_internal/libc_internal_io.cpp +++ b/src/core/libraries/libc_internal/libc_internal_io.cpp @@ -3,21 +3,484 @@ #include #include +#include #include +#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" +#include "core/libraries/kernel/file_system.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/posix_error.h" +#include "core/libraries/libc_internal/libc_internal_io.h" +#include "core/libraries/libc_internal/libc_internal_threads.h" #include "core/libraries/libs.h" -#include "libc_internal_io.h" #include "printf.h" namespace Libraries::LibcInternal { -int PS4_SYSV_ABI internal_snprintf(char* s, size_t n, VA_ARGS) { + +s32 PS4_SYSV_ABI internal_snprintf(char* s, u64 n, VA_ARGS) { VA_CTX(ctx); return snprintf_ctx(s, n, &ctx); } + +std::map g_files{}; +// Constants for tracking accurate file indexes. +// Since the file struct is exposed to the application, accuracy is important. +static constexpr s32 g_initial_files = 5; +static constexpr s32 g_max_files = 0x100; + +OrbisFILE* PS4_SYSV_ABI internal__Fofind() { + u64 index = g_initial_files; + while (index != g_max_files) { + OrbisFILE* file = g_files[index]; + // If file doesn't exist, create it. + if (file == nullptr) { + file = new OrbisFILE(); + if (file == nullptr) { + return nullptr; + } + // Store new file in the array, initialize default values, and return it. + g_files[index] = file; + file->_Mode = 0x80; + file->_Idx = index; + return file; + } + // Special case, files with mode 0 are returned? + if (file->_Mode == 0) { + file->_Mode = 0xff7f; + return file; + } + index++; + } + return nullptr; +} + +void PS4_SYSV_ABI internal__Lockfilelock(OrbisFILE* file) { + if (file != nullptr && file->_Mutex != nullptr) { + internal__Mtxlock(&file->_Mutex); + } +} + +void PS4_SYSV_ABI internal__Unlockfilelock(OrbisFILE* file) { + if (file != nullptr && file->_Mutex != nullptr) { + internal__Mtxunlock(&file->_Mutex); + } +} + +OrbisFILE* PS4_SYSV_ABI internal__Foprep(const char* path, const char* mode, OrbisFILE* file, + s32 fd, s32 s_mode, s32 flag) { + if (file == nullptr) { + *Kernel::__Error() = POSIX_ENOMEM; + } + + // Preserve mode and index + Libraries::Kernel::PthreadMutexT mtx = file->_Mutex; + Libraries::Kernel::PthreadMutexT* mtx_ptr = &file->_Mutex; + u8 file_index = file->_Idx; + u16 file_mode = file->_Mode & 0x80; + + // Real library does a memcpy using a static global FILE object. + // This stored file is just zeros, with the only exception being a handle of -1. + memset(file, 0, sizeof(OrbisFILE)); + file->_Handle = -1; + + // Not sure what this magic is for, but I'll replicate it. + u8* ptr = &file->_Cbuf; + // Note: this field is supposed to be a pthread mutex. + // Since we don't export pthread HLEs for other functions, I'll avoid handling this for now. + file->_Mutex = nullptr; + file->_Idx = file_index; + file->_Buf = ptr; + file->_Bend = &file->unk2; + file->_Next = ptr; + file->_Rend = ptr; + file->_WRend = ptr; + file->_Wend = ptr; + file->_WWend = ptr; + file->_Rback = ptr; + file->_WRback = &file->unk1; + + // Parse inputted mode string + const char* mode_str = mode; + u16 calc_mode = 0; + u16 access_mode = 0; + if (mode_str[0] == 'r') { + calc_mode = 1 | file_mode; + } else if (mode_str[0] == 'w') { + calc_mode = 0x1a | file_mode; + } else if (mode_str[0] == 'a') { + calc_mode = 0x16 | file_mode; + } else { + // Closes the file and returns EINVAL. + file->_Mode = file_mode; + if (flag == 0) { + internal__Mtxinit(mtx_ptr, nullptr); + } else { + file->_Mutex = mtx; + internal__Unlockfilelock(file); + } + internal_fclose(file); + *Kernel::__Error() = POSIX_EINVAL; + return nullptr; + } + file->_Mode = calc_mode; + + do { + // This is all basically straight from decomp, need to cleanup at some point. + if (mode_str[1] == '+') { + file_mode = 3; + if ((~calc_mode & 3) == 0) { + break; + } + } else if (mode_str[1] != 'b') { + file_mode = 0x20; + if ((calc_mode & 0x20) != 0) { + break; + } + } + mode_str++; + calc_mode = file_mode | calc_mode; + file->_Mode = calc_mode; + } while (true); + + if (path == nullptr && fd >= 0) { + // I guess this is for some internal behavior? + file->_Handle = fd; + } else { + fd = internal__Fopen(path, calc_mode, s_mode == 0x55); + file->_Handle = fd; + } + + // Error case + if (fd < 0) { + // Closes the file, but ensures errno is unchanged. + if (flag == 0) { + internal__Mtxinit(mtx_ptr, nullptr); + } else { + file->_Mutex = mtx; + internal__Unlockfilelock(file); + } + s32 old_errno = *Kernel::__Error(); + internal_fclose(file); + *Kernel::__Error() = old_errno; + return nullptr; + } + + if (flag == 0) { + char mtx_name[0x20]; + std::snprintf(mtx_name, 0x20, "FileFD:0x%08X", fd); + internal__Mtxinit(mtx_ptr, mtx_name); + } else { + file->_Mutex = mtx; + } + return file; +} + +s32 PS4_SYSV_ABI internal__Fopen(const char* path, u16 mode, bool flag) { + u32 large_mode = mode; + u16 open_mode = 0600; + if (!flag) { + open_mode = 0666; + } + // Straight from decomp, should probably get cleaned up at some point. + s32 creat_flag = large_mode << 5 & 0x200; + s32 excl_flag = large_mode << 5 & 0x800; + s32 misc_flags = (large_mode & 8) * 0x80 + (large_mode & 4) * 2; + // Real library has an array for this, where large_mode & 3 is used as an index. + // That array has values [0, 0, 1, 2], so this call should match the result. + s32 access_flag = std::max((large_mode & 3) - 1, 0); + s32 open_flags = creat_flag | misc_flags | excl_flag | access_flag; + return Libraries::Kernel::posix_open(path, open_flags, open_mode); +} + +OrbisFILE* PS4_SYSV_ABI internal_fopen(const char* path, const char* mode) { + std::scoped_lock lk{g_file_mtx}; + LOG_INFO(Lib_LibcInternal, "called, path {}, mode {}", path, mode); + OrbisFILE* file = internal__Fofind(); + return internal__Foprep(path, mode, file, -1, 0, 0); +} + +s32 PS4_SYSV_ABI internal_fflush(OrbisFILE* file) { + if (file == nullptr) { + std::scoped_lock lk{g_file_mtx}; + s32 fflush_result = 0; + for (auto& file : g_files) { + s32 res = internal_fflush(file.second); + if (res < 0) { + fflush_result = -1; + } + } + return fflush_result; + } + if ((file->_Mode & 0x2000) != 0) { + internal__Lockfilelock(file); + u16 file_mode = file->_Mode; + u8* file_buf_start = file->_Buf; + u8* file_buf_end = file->_Next; + while (file_buf_start < file_buf_end) { + u64 size_to_write = static_cast(file_buf_end - file_buf_start); + s32 write_bytes = + Libraries::Kernel::sceKernelWrite(file->_Handle, file_buf_start, size_to_write); + if (write_bytes < 1) { + file_buf_start = file->_Buf; + file->_Next = file_buf_start; + file->_Wend = file_buf_start; + file->_WWend = file_buf_start; + u8* off_mode = reinterpret_cast(&file->_Mode) + 1; + *off_mode = *off_mode | 2; + internal__Unlockfilelock(file); + return -1; + } + file_buf_end = file->_Next; + file_buf_start += write_bytes; + } + file->_Next = file_buf_start; + file->_Wend = file_buf_start; + file->_WWend = file_buf_start; + file->_Mode = file_mode & 0xdfff; + internal__Unlockfilelock(file); + } + return 0; +} + +s64 PS4_SYSV_ABI internal__Nnl(OrbisFILE* file, u8* val1, u8* val2) { + if (val1 < val2) { + return val2 - val1; + } + return 0; +} + +s32 PS4_SYSV_ABI internal__Fspos(OrbisFILE* file, Orbisfpos_t* file_pos, s64 offset, s32 whence) { + if ((file->_Mode & 3) == 0) { + return -1; + } + if (internal_fflush(file) != 0) { + return -1; + } + if (whence >= 3) { + *Libraries::Kernel::__Error() = POSIX_EINVAL; + return -1; + } + if (file_pos != nullptr) { + offset = offset + file_pos->_Off; + } + if (whence == 1 && (file->_Mode & 0x1000) != 0) { + s64 val1 = internal__Nnl(file, file->_Rback, &file->_Cbuf); + u8* rsave_ptr = file->_Rsave; + if (rsave_ptr == nullptr) { + rsave_ptr = file->_Rend; + } + s64 val2 = internal__Nnl(file, file->_Next, rsave_ptr); + s64 val3 = internal__Nnl(file, file->_Next, file->_WRend); + offset = offset - (val1 + val2 + val3); + } + s64 result = 0; + if (whence == 2 || (whence == 1 && offset != 0) || (whence == 0 && offset != -1)) { + result = Libraries::Kernel::posix_lseek(file->_Handle, offset, whence); + } + if (result == -1) { + return -1; + } + + u16 file_mode = file->_Mode; + if ((file_mode & 0x3000) != 0) { + u8* file_buf = file->_Buf; + file->_Next = file_buf; + file->_Rend = file_buf; + file->_WRend = file_buf; + file->_Wend = file_buf; + file->_WWend = file_buf; + file->_Rback = &file->_Cbuf; + file->_WRback = &file->unk1; + file->_Rsave = nullptr; + } + if (file_pos != nullptr) { + std::memcpy(&file->_Wstate, &file_pos->_Wstate, sizeof(Orbis_Mbstatet)); + } + file->_Mode = file_mode & 0xceff; + return 0; +} + +s32 PS4_SYSV_ABI internal_fseek(OrbisFILE* file, s64 offset, s32 whence) { + internal__Lockfilelock(file); + LOG_TRACE(Lib_LibcInternal, "called, file handle {:#x}, offset {:#x}, whence {:#x}", + file->_Handle, offset, whence); + s32 result = internal__Fspos(file, nullptr, offset, whence); + internal__Unlockfilelock(file); + return result; +} + +s32 PS4_SYSV_ABI internal__Frprep(OrbisFILE* file) { + if (file->_Rend > file->_Next) { + return 1; + } + if ((file->_Mode & 0x100) != 0) { + return 0; + } + u16 mode = file->_Mode; + if ((mode & 0xa001) != 1) { + // Lot of magic here, might be valuable to figure out what this does. + file->_Mode = (((mode ^ 0x8000) >> 0xf) << 0xe) | mode | 0x200; + return -1; + } + + u8* file_buf = file->_Buf; + if ((mode & 0x800) == 0 && file_buf == &file->_Cbuf) { + // Allocate a new file buffer, for now, we'll use host malloc to create it. + // When we have an HLE for malloc, that should be used instead. + u8* new_buffer = std::bit_cast(std::malloc(0x10000)); + if (new_buffer == nullptr) { + file->_Buf = file_buf; + file->_Bend = file_buf + 1; + } else { + file->_Mode = file->_Mode | 0x40; + file->_Buf = new_buffer; + file->_Bend = new_buffer + 0x10000; + file->_WRend = new_buffer; + file->_WWend = new_buffer; + file_buf = new_buffer; + } + } + file->_Next = file_buf; + file->_Rend = file_buf; + file->_Wend = file_buf; + // Intentional shrinking here, library treats value as 32-bit. + s32 read_result = + Libraries::Kernel::sceKernelRead(file->_Handle, file_buf, file->_Bend - file_buf); + if (read_result < 0) { + u8* off_mode = reinterpret_cast(&file->_Mode) + 1; + *off_mode = *off_mode | 0x42; + return -1; + } else if (read_result != 0) { + file->_Mode = file->_Mode | 0x5000; + file->_Rend = file->_Rend + read_result; + return 1; + } + file->_Mode = (file->_Mode & 0xaeff) | 0x4100; + return 0; +} + +u64 PS4_SYSV_ABI internal_fread(char* ptr, u64 size, u64 nmemb, OrbisFILE* file) { + if (size == 0 || nmemb == 0) { + return 0; + } + internal__Lockfilelock(file); + LOG_TRACE(Lib_LibcInternal, "called, file handle {:#x}, size {:#x}, nmemb {:#x}", file->_Handle, + size, nmemb); + s64 total_size = size * nmemb; + s64 remaining_size = total_size; + if ((file->_Mode & 0x4000) != 0) { + while (remaining_size != 0) { + u8* rback_ptr = file->_Rback; + if (&file->_Cbuf <= rback_ptr) { + break; + } + file->_Rback = rback_ptr + 1; + *ptr = *rback_ptr; + ptr++; + remaining_size--; + } + } + + while (remaining_size != 0) { + u8* file_ptr = file->_Rsave; + if (file_ptr == nullptr) { + file_ptr = file->_Rend; + } else { + file->_Rend = file_ptr; + file->_Rsave = nullptr; + } + u8* src = file->_Next; + if (file_ptr <= src) { + s32 res = internal__Frprep(file); + if (res < 1) { + internal__Unlockfilelock(file); + return (total_size - remaining_size) / size; + } + src = file->_Next; + file_ptr = file->_Rend; + } + u64 copy_bytes = std::min(file_ptr - src, remaining_size); + std::memcpy(ptr, src, copy_bytes); + file->_Next += copy_bytes; + ptr += copy_bytes; + remaining_size -= copy_bytes; + } + internal__Unlockfilelock(file); + return (total_size - remaining_size) / size; +} + +void PS4_SYSV_ABI internal__Fofree(OrbisFILE* file) { + u8* cbuf_ptr = &file->_Cbuf; + s8 trunc_mode = static_cast(file->_Mode); + + file->_Mode = 0; + file->_Handle = -1; + file->_Buf = cbuf_ptr; + file->_Next = cbuf_ptr; + file->_Rend = cbuf_ptr; + file->_WRend = cbuf_ptr; + file->_Wend = cbuf_ptr; + file->_WWend = cbuf_ptr; + file->_Rback = cbuf_ptr; + file->_WRback = &file->unk1; + if (trunc_mode < 0) { + // Remove file from vector + g_files.erase(file->_Idx); + internal__Mtxdst(&file->_Mutex); + free(file); + } +} + +s32 PS4_SYSV_ABI internal_fclose(OrbisFILE* file) { + if (file == nullptr) { + return -1; + } + + LOG_INFO(Lib_LibcInternal, "called, file handle {:#x}", file->_Handle); + if ((file->_Mode & 3) == 0 || file->_Handle < 0) { + std::scoped_lock lk{g_file_mtx}; + internal__Fofree(file); + *Libraries::Kernel::__Error() = POSIX_EBADF; + } else { + s32 fflush_result = internal_fflush(file); + std::scoped_lock lk{g_file_mtx}; + if ((file->_Mode & 0x40) != 0) { + std::free(file->_Buf); + } + file->_Buf = nullptr; + s32 close_result = Libraries::Kernel::posix_close(file->_Handle); + internal__Fofree(file); + // Need to figure out what exactly this means. + return ~-(close_result == 0) | fflush_result; + } + return 0; +} + void RegisterlibSceLibcInternalIo(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("eLdDw6l0-bU", "libSceLibcInternal", 1, "libSceLibcInternal", internal_snprintf); + LIB_FUNCTION("MUjC4lbHrK4", "libSceLibcInternal", 1, "libSceLibcInternal", internal_fflush); + LIB_FUNCTION("xGT4Mc55ViQ", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Fofind); + LIB_FUNCTION("dREVnZkAKRE", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Foprep); + LIB_FUNCTION("sQL8D-jio7U", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Fopen); + LIB_FUNCTION("A+Y3xfrWLLo", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Fspos); + LIB_FUNCTION("Ss3108pBuZY", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Nnl); + LIB_FUNCTION("9s3P+LCvWP8", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Frprep); + LIB_FUNCTION("jVDuvE3s5Bs", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Fofree); + LIB_FUNCTION("vZkmJmvqueY", "libSceLibcInternal", 1, "libSceLibcInternal", + internal__Lockfilelock); + LIB_FUNCTION("0x7rx8TKy2Y", "libSceLibcInternal", 1, "libSceLibcInternal", + internal__Unlockfilelock); } + +void ForceRegisterlibSceLibcInternalIo(Core::Loader::SymbolsResolver* sym) { + // Goal is to be minimally intrusive here to allow LLE for printf/stdout writes. + LIB_FUNCTION("xeYO4u7uyJ0", "libSceLibcInternal", 1, "libSceLibcInternal", internal_fopen); + LIB_FUNCTION("rQFVBXp-Cxg", "libSceLibcInternal", 1, "libSceLibcInternal", internal_fseek); + LIB_FUNCTION("lbB+UlZqVG0", "libSceLibcInternal", 1, "libSceLibcInternal", internal_fread); + LIB_FUNCTION("uodLYyUip20", "libSceLibcInternal", 1, "libSceLibcInternal", internal_fclose); +} + } // namespace Libraries::LibcInternal \ No newline at end of file diff --git a/src/core/libraries/libc_internal/libc_internal_io.h b/src/core/libraries/libc_internal/libc_internal_io.h index f5291526b..27915d8fa 100644 --- a/src/core/libraries/libc_internal/libc_internal_io.h +++ b/src/core/libraries/libc_internal/libc_internal_io.h @@ -3,12 +3,96 @@ #pragma once +#include #include "common/types.h" +#include "core/libraries/kernel/threads.h" namespace Core::Loader { class SymbolsResolver; } namespace Libraries::LibcInternal { + +static std::recursive_mutex g_file_mtx{}; + +union Orbis__mbstate_t { + u8 __mbstate8[128]; + s64 _mbstateL; +}; + +struct Orbis_Mbstatet { + u64 _Wchar; + u16 _Byte, _State; + s32 : 32; +}; + +struct Orbisfpos_t { + s64 _Off; + Orbis_Mbstatet _Wstate; +}; + +struct Orbis__sbuf { + u8* _base; + s32 _size; +}; + +struct OrbisFILE { + u16 _Mode; + u8 _Idx; + s32 _Handle; + u8 *_Buf, *_Bend, *_Next; + u8 *_Rend, *_Wend, *_Rback; + u16 *_WRback, _WBack[2]; + u16 unk1; + u8 *_Rsave, *_WRend, *_WWend; + Orbis_Mbstatet _Wstate; + u8* _Tmpnam; + u8 _Back[6], _Cbuf; + u8 unk2; + Libraries::Kernel::PthreadMutexT _Mutex; + u8* _p; + s32 _r; + s32 _w; + s16 _flags; + s16 _file; + Orbis__sbuf _bf; + s32 _lbfsize; + void* _cookie; + s32 PS4_SYSV_ABI (*_close)(void*); + s32 PS4_SYSV_ABI (*_read)(void*, char*, s32); + Orbisfpos_t PS4_SYSV_ABI (*_seek)(void*, Orbisfpos_t, s32); + s32 (*_write)(void*, const char*, s32); + Orbis__sbuf _ub; + u8* _up; + s32 _ur; + u8 _ubuf[3]; + u8 _nbuf[1]; + Orbis__sbuf _lb; + s32 _blksize; + Orbisfpos_t _offset; + void* _fl_mutex; + void* _fl_owner; + s32 _fl_count; + s32 _orientation; + Orbis__mbstate_t _mbstate; +}; + +s32 PS4_SYSV_ABI internal_snprintf(char* s, u64 n, VA_ARGS); +void PS4_SYSV_ABI internal__Lockfilelock(OrbisFILE* file); +void PS4_SYSV_ABI internal__Unlockfilelock(OrbisFILE* file); +OrbisFILE* PS4_SYSV_ABI internal__Fofind(); +OrbisFILE* PS4_SYSV_ABI internal__Foprep(const char* path, const char* mode, OrbisFILE* file, + s32 fd, s32 flag1, s32 flag2); +s32 PS4_SYSV_ABI internal__Fopen(const char* path, u16 mode, bool flag); +OrbisFILE* PS4_SYSV_ABI internal_fopen(const char* path, const char* mode); +s64 PS4_SYSV_ABI internal__Nnl(OrbisFILE* file, u8* val1, u8* val2); +s32 PS4_SYSV_ABI internal__Fspos(OrbisFILE* file, Orbisfpos_t* file_pos, s64 offset, s32 whence); +s32 PS4_SYSV_ABI internal_fflush(OrbisFILE* file); +s32 PS4_SYSV_ABI internal_fseek(OrbisFILE* file, s64 offset, s32 whence); +s32 PS4_SYSV_ABI internal__Frprep(OrbisFILE* file); +u64 PS4_SYSV_ABI internal_fread(char* ptr, u64 size, u64 nmemb, OrbisFILE* file); +s32 PS4_SYSV_ABI internal_fclose(OrbisFILE* file); + void RegisterlibSceLibcInternalIo(Core::Loader::SymbolsResolver* sym); +void ForceRegisterlibSceLibcInternalIo(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::LibcInternal \ No newline at end of file diff --git a/src/core/libraries/libc_internal/libc_internal_threads.cpp b/src/core/libraries/libc_internal/libc_internal_threads.cpp new file mode 100644 index 000000000..2d8ddaccb --- /dev/null +++ b/src/core/libraries/libc_internal/libc_internal_threads.cpp @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/kernel/threads.h" +#include "core/libraries/libc_internal/libc_internal_threads.h" +#include "core/libraries/libs.h" + +namespace Libraries::LibcInternal { + +void getMutexName(char* buf, u64 size, const char* name) { + if (name != nullptr) { + std::snprintf(buf, size, "SceLibcI_%s", name); + } else { + std::snprintf(buf, size, "SceLibcI"); + } +} + +s32 PS4_SYSV_ABI internal__Mtxinit(Libraries::Kernel::PthreadMutexT* mtx, const char* name) { + char mtx_name[0x20]; + getMutexName(mtx_name, sizeof(mtx_name), name); + + Libraries::Kernel::PthreadMutexAttrT attr{}; + s32 result = Libraries::Kernel::posix_pthread_mutexattr_init(&attr); + if (result != 0) { + return 1; + } + + result = Libraries::Kernel::posix_pthread_mutexattr_settype( + &attr, Libraries::Kernel::PthreadMutexType::Recursive); + if (result == 0) { + s32 mtx_init_result = Libraries::Kernel::scePthreadMutexInit(mtx, &attr, mtx_name); + result = Libraries::Kernel::posix_pthread_mutexattr_destroy(&attr); + if (mtx_init_result == 0 && result == 0) { + return 0; + } + } else { + Libraries::Kernel::posix_pthread_mutexattr_destroy(&attr); + } + + return 1; +} + +s32 PS4_SYSV_ABI internal__Mtxlock(Libraries::Kernel::PthreadMutexT* mtx) { + s32 result = Libraries::Kernel::posix_pthread_mutex_lock(mtx); + return result != 0; +} + +s32 PS4_SYSV_ABI internal__Mtxunlock(Libraries::Kernel::PthreadMutexT* mtx) { + s32 result = Libraries::Kernel::posix_pthread_mutex_unlock(mtx); + return result != 0; +} + +s32 PS4_SYSV_ABI internal__Mtxdst(Libraries::Kernel::PthreadMutexT* mtx) { + s32 result = Libraries::Kernel::posix_pthread_mutex_destroy(mtx); + return result != 0; +} + +void RegisterlibSceLibcInternalThreads(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("z7STeF6abuU", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Mtxinit); + LIB_FUNCTION("pE4Ot3CffW0", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Mtxlock); + LIB_FUNCTION("cMwgSSmpE5o", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Mtxunlock); + LIB_FUNCTION("LaPaA6mYA38", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Mtxdst); +} + +} // namespace Libraries::LibcInternal \ No newline at end of file diff --git a/src/core/libraries/libc_internal/libc_internal_threads.h b/src/core/libraries/libc_internal/libc_internal_threads.h new file mode 100644 index 000000000..74e6d41b1 --- /dev/null +++ b/src/core/libraries/libc_internal/libc_internal_threads.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/types.h" +#include "core/libraries/kernel/threads.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::LibcInternal { + +s32 PS4_SYSV_ABI internal__Mtxinit(Libraries::Kernel::PthreadMutexT* mtx, const char* name); +s32 PS4_SYSV_ABI internal__Mtxlock(Libraries::Kernel::PthreadMutexT* mtx); +s32 PS4_SYSV_ABI internal__Mtxunlock(Libraries::Kernel::PthreadMutexT* mtx); +s32 PS4_SYSV_ABI internal__Mtxdst(Libraries::Kernel::PthreadMutexT* mtx); + +void RegisterlibSceLibcInternalThreads(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::LibcInternal \ No newline at end of file diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index d9c387454..1b5542735 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -81,6 +81,7 @@ namespace Libraries { void InitHLELibs(Core::Loader::SymbolsResolver* sym) { LOG_INFO(Lib_Kernel, "Initializing HLE libraries"); Libraries::Kernel::RegisterLib(sym); + Libraries::LibcInternal::ForceRegisterLib(sym); Libraries::GnmDriver::RegisterLib(sym); Libraries::VideoOut::RegisterLib(sym); Libraries::UserService::RegisterLib(sym); diff --git a/src/core/libraries/np/np_error.h b/src/core/libraries/np/np_error.h index cf2014148..518344ba3 100644 --- a/src/core/libraries/np/np_error.h +++ b/src/core/libraries/np/np_error.h @@ -13,4 +13,12 @@ constexpr int ORBIS_NP_ERROR_INVALID_SIZE = 0x80550011; constexpr int ORBIS_NP_ERROR_ABORTED = 0x80550012; constexpr int ORBIS_NP_ERROR_REQUEST_MAX = 0x80550013; constexpr int ORBIS_NP_ERROR_REQUEST_NOT_FOUND = 0x80550014; -constexpr int ORBIS_NP_ERROR_INVALID_ID = 0x80550015; \ No newline at end of file +constexpr int ORBIS_NP_ERROR_INVALID_ID = 0x80550015; + +constexpr int ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT = 0x80550704; +constexpr int ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS = 0x80550706; +constexpr int ORBIS_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT = 0x8055070c; +constexpr int ORBIS_NP_COMMUNITY_ERROR_INVALID_ID = 0x8055070e; +constexpr int ORBIS_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT = 0x80550714; +constexpr int ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID = 0x80550718; +constexpr int ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_NPID = 0x80550719; \ No newline at end of file diff --git a/src/core/libraries/np/np_manager.h b/src/core/libraries/np/np_manager.h index 078fa804a..49250db03 100644 --- a/src/core/libraries/np/np_manager.h +++ b/src/core/libraries/np/np_manager.h @@ -93,6 +93,8 @@ struct OrbisNpCreateAsyncRequestParameter { void RegisterNpCallback(std::string key, std::function cb); void DeregisterNpCallback(std::string key); +s32 PS4_SYSV_ABI sceNpGetNpId(Libraries::UserService::OrbisUserServiceUserId user_id, + OrbisNpId* np_id); s32 PS4_SYSV_ABI sceNpGetOnlineId(Libraries::UserService::OrbisUserServiceUserId user_id, OrbisNpOnlineId* online_id); diff --git a/src/core/libraries/np/np_tus.cpp b/src/core/libraries/np/np_tus.cpp index e0d0aaad1..d18cf4ec0 100644 --- a/src/core/libraries/np/np_tus.cpp +++ b/src/core/libraries/np/np_tus.cpp @@ -1,15 +1,87 @@ // SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include + #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" +#include "core/libraries/np/np_error.h" +#include "core/libraries/np/np_manager.h" #include "core/libraries/np/np_tus.h" +#include "core/libraries/np/np_types.h" +#include "core/libraries/np/object_manager.h" +#include "core/libraries/system/userservice.h" namespace Libraries::Np::NpTus { -s32 PS4_SYSV_ABI sceNpTssCreateNpTitleCtx() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); +int PackReqId(int libCtxId, int reqId) { + return ((libCtxId & 0xFFFF) << 16) | (reqId & 0xFFFF); +} + +std::pair UnpackReqId(int reqId) { + return {reqId >> 16, reqId & 0xFFFF}; +} + +bool IsReqId(int id) { + return id > (1 << 16); +} + +struct NpTusRequest { + std::future task; + + void Start(auto lambda) { + this->task = std::async(std::launch::async, lambda); + } +}; + +using NpTusRequestsManager = + ObjectManager; + +struct NpTusTitleContext { + u32 serviceLabel; + OrbisNpId npId; + NpTusRequestsManager requestsManager; + + s32 GetRequest(int reqId, NpTusRequest** out) { + NpTusRequest* req = nullptr; + if (auto ret = requestsManager.GetObject(reqId, &req); ret < 0) { + return ret; + } + + *out = req; + + return ORBIS_OK; + } + + s32 DeleteRequest(int reqId) { + return requestsManager.DeleteObject(reqId); + } +}; + +using NpTusContextManager = + ObjectManager; + +static NpTusContextManager ctxManager; + +s32 GetRequest(int requestId, NpTusRequest** out) { + auto [ctxId, reqId] = UnpackReqId(requestId); + + NpTusTitleContext* ctx = nullptr; + if (auto ret = ctxManager.GetObject(ctxId, &ctx); ret < 0) { + return ret; + } + + NpTusRequest* req = nullptr; + if (auto ret = ctx->GetRequest(reqId, &req); ret < 0) { + return ret; + } + + *out = req; + return ORBIS_OK; } @@ -33,9 +105,34 @@ s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableVUserAsync() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusCreateNpTitleCtx() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceNpTusCreateNpTitleCtx(OrbisNpServiceLabel serviceLabel, OrbisNpId* npId) { + if (!npId) { + return ORBIS_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT; + } + if (serviceLabel == ORBIS_NP_INVALID_SERVICE_LABEL) { + return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; + } + LOG_ERROR(Lib_NpTus, "serviceLabel = {}, npId->data = {}", serviceLabel, npId->handle.data); + + return ctxManager.CreateObject(serviceLabel, *npId); +} + +s32 PS4_SYSV_ABI sceNpTusCreateNpTitleCtxA(OrbisNpServiceLabel serviceLabel, + Libraries::UserService::OrbisUserServiceUserId userId) { + LOG_ERROR(Lib_NpTus, "serviceLabel = {}, userId = {}", serviceLabel, userId); + OrbisNpId npId; + auto ret = NpManager::sceNpGetNpId(userId, &npId); + + if (ret < 0) { + return ret; + } + + return sceNpTusCreateNpTitleCtx(serviceLabel, &npId); +} + +s32 PS4_SYSV_ABI sceNpTssCreateNpTitleCtx(OrbisNpServiceLabel serviceLabel, OrbisNpId* npId) { + LOG_INFO(Lib_NpTus, "redirecting"); + return sceNpTusCreateNpTitleCtx(serviceLabel, npId); } s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotData() { @@ -58,14 +155,33 @@ s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableAsync() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusGetData() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpTusGetDataAsync(int reqId, OrbisNpId* npId, OrbisNpTusSlotId slotId, + OrbisNpTusDataStatus* dataStatus, u64 dataStatusSize, + void* data, u64 dataSize, void* option) { + LOG_INFO( + Lib_NpTus, + "reqId = {:#x}, slotId = {}, dataStatusSize = {}, data = {}, dataSize = {}, option = {}", + reqId, slotId, dataStatusSize, data, dataSize, fmt::ptr(option)); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusGetDataAsync() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceNpTusGetData(int reqId, OrbisNpId* npId, OrbisNpTusSlotId slotId, + OrbisNpTusDataStatus* dataStatus, u64 dataStatusSize, void* data, + u64 dataSize, void* option) { + LOG_ERROR( + Lib_NpTus, + "reqId = {:#x}, slotId = {}, dataStatusSize = {}, data = {}, dataSize = {}, option = {}", + reqId, slotId, dataStatusSize, data, dataSize, fmt::ptr(option)); + + auto ret = sceNpTusGetDataAsync(reqId, npId, slotId, dataStatus, dataStatusSize, data, dataSize, + option); + if (ret < 0) { + return ret; + } + + sceNpTusWaitAsync(reqId, &ret); + + return ret; } s32 PS4_SYSV_ABI sceNpTusGetDataVUser() { @@ -118,13 +234,45 @@ s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusVUserAsync() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariable() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAsync(int reqId, OrbisNpId* npId, + OrbisNpTusSlotId* slotIds, s64* variables, + u64 variablesSize, int arrayLen, void* option) { + LOG_INFO(Lib_NpTus, + "reqId = {}, npId = {}, slotIds = {}, variables = {}, variablesSize = {}, arrayLen = " + "{}, option = {}", + reqId, npId ? npId->handle.data : "", fmt::ptr(slotIds), fmt::ptr(variables), + variablesSize, arrayLen, fmt::ptr(option)); + + NpTusRequest* req = nullptr; + if (auto ret = GetRequest(reqId, &req); ret < 0) { + return ret; + } + + req->Start([=]() { + // + return 0; + }); + return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAsync() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariable(int reqId, OrbisNpId* npId, OrbisNpTusSlotId* slotIds, + s64* variables, u64 variablesSize, int arrayLen, + void* option) { + LOG_INFO(Lib_NpTus, + "reqId = {}, npId = {}, slotIds = {}, variables = {}, variablesSize = {}, arrayLen = " + "{}, option = {}", + reqId, npId ? npId->handle.data : "", fmt::ptr(slotIds), fmt::ptr(variables), + variablesSize, arrayLen, fmt::ptr(option)); + + auto ret = sceNpTusGetMultiSlotVariableAsync(reqId, npId, slotIds, variables, variablesSize, + arrayLen, option); + if (ret < 0) { + return ret; + } + + sceNpTusWaitAsync(reqId, &ret); + return ORBIS_OK; } @@ -143,8 +291,24 @@ s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatus() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAsync() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAsync(int reqId, OrbisNpId* npIds, + OrbisNpTusSlotId slotId, + OrbisNpTusDataStatus* statuses, + u64 statusesBytes, int arrayLen, + void* option) { + LOG_ERROR(Lib_NpTus, "(STUBBED) reqId = {:#x}, slotId = {}, arrayLen = {}, option = {}", reqId, + slotId, arrayLen, fmt::ptr(option)); + + NpTusRequest* req = nullptr; + if (auto ret = GetRequest(reqId, &req); ret < 0) { + return ret; + } + + req->Start([=]() { + // + return 0; + }); + return ORBIS_OK; } @@ -178,13 +342,49 @@ s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableVUserAsync() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusSetData() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpTusSetDataAsync(int reqId, OrbisNpId* npId, OrbisNpTusSlotId slotId, + u64 totalSize, u64 sendSize, const void* data, + const OrbisNpTusDataInfo* info, u64 infoSize, + const OrbisNpId* lastChangedAuthor, + Libraries::Rtc::OrbisRtcTick lastChanged, void* option) { + LOG_INFO(Lib_NpTus, + "reqId = {:#x}, npId = {}, slotId = {}, totalSize = {}, sendSize = {}, " + "info->size = {}, infoSize = {}, lastChangedAuthor = {}", + reqId, npId ? npId->handle.data : "", slotId, totalSize, sendSize, + info ? info->size : 0, infoSize, + lastChangedAuthor ? lastChangedAuthor->handle.data : ""); + + NpTusRequest* req = nullptr; + if (auto ret = GetRequest(reqId, &req); ret < 0) { + return ret; + } + req->Start([=]() { + // + return 0; + }); + return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusSetDataAsync() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpTusSetData(int reqId, OrbisNpId* npId, OrbisNpTusSlotId slotId, u64 totalSize, + u64 sendSize, const void* data, const OrbisNpTusDataInfo* info, + u64 infoSize, const OrbisNpId* lastChangedAuthor, + Libraries::Rtc::OrbisRtcTick lastChanged, void* option) { + LOG_INFO(Lib_NpTus, + "reqId = {:#x}, npId = {}, slotId = {}, totalSize = {}, sendSize = {}, " + "info->size = {}, infoSize = {}, lastChangedAuthor = {}", + reqId, npId ? npId->handle.data : "", slotId, totalSize, sendSize, + info ? info->size : 0, infoSize, + lastChangedAuthor ? lastChangedAuthor->handle.data : ""); + + auto ret = sceNpTusSetDataAsync(reqId, npId, slotId, totalSize, sendSize, data, info, infoSize, + lastChangedAuthor, lastChanged, option); + if (ret < 0) { + return ret; + } + + sceNpTusWaitAsync(reqId, &ret); + return ORBIS_OK; } @@ -203,8 +403,39 @@ s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariable() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableAsync() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableAsync(int reqId, OrbisNpId* npId, + OrbisNpTusSlotId* slotIds, s64* variables, + int arrayLen, void* option) { + LOG_INFO(Lib_NpTus, + "reqId = {}, npId = {}, slotIds = {}, variables = {}, arrayLen = {}, option = {}", + reqId, npId ? npId->handle.data : "", fmt::ptr(slotIds), fmt::ptr(variables), arrayLen, + fmt::ptr(option)); + + if (!slotIds || !variables) { + return ORBIS_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT; + } + if (arrayLen < 1 || option) { + return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; + } + if (arrayLen > 64) { + return ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID; + } + if (std::ranges::any_of( + std::vector>(slotIds, slotIds + arrayLen), + [](auto id) { return id < 0; })) { + return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; + } + + NpTusRequest* req = nullptr; + if (auto ret = GetRequest(reqId, &req); ret < 0) { + return ret; + } + + req->Start([=]() { + // + return 0; + }); + return ORBIS_OK; } @@ -228,19 +459,59 @@ s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableVUserAsync() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTssCreateNpTitleCtxA() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpTssCreateNpTitleCtxA(OrbisNpServiceLabel serviceLabel, + Libraries::UserService::OrbisUserServiceUserId userId) { + LOG_DEBUG(Lib_NpTus, "redirecting"); + return sceNpTusCreateNpTitleCtxA(serviceLabel, userId); +} + +s32 PS4_SYSV_ABI sceNpTssGetDataAsync(int reqId, OrbisNpTssSlotId slotId, + OrbisNpTssDataStatus* dataStatus, u64 dataStatusSize, + void* data, u64 dataSize, OrbisNpTssGetDataOptParam* option) { + LOG_INFO(Lib_NpTus, "reqId = {:#x}, slotId = {}, dataStatusSize = {}, dataSize = {}", reqId, + slotId, dataStatusSize, dataSize); + + if (option && option->size != 0x20) { + return ORBIS_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT; + } + if (dataStatus && dataStatusSize != 0x18) { + return ORBIS_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT; + } + if (slotId < 0 || slotId > 15) { + return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; + } + + NpTusRequest* req = nullptr; + if (auto ret = GetRequest(reqId, &req); ret < 0) { + return ret; + } + + req->Start([=]() { + if (dataStatus) { + dataStatus->status = OrbisNpTssStatus::Ok; + dataStatus->contentLength = 0; + } + return 0; + }); + return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTssGetData() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); - return ORBIS_OK; -} +s32 PS4_SYSV_ABI sceNpTssGetData(int reqId, OrbisNpTssSlotId slotId, + OrbisNpTssDataStatus* dataStatus, u64 dataStatusSize, void* data, + u64 dataSize, OrbisNpTssGetDataOptParam* option) { + LOG_INFO(Lib_NpTus, "reqId = {:#x}, slotId = {}, dataStatusSize = {}, dataSize = {}", reqId, + slotId, dataStatusSize, dataSize); -s32 PS4_SYSV_ABI sceNpTssGetDataAsync() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); - return ORBIS_OK; + auto ret = + sceNpTssGetDataAsync(reqId, slotId, dataStatus, dataStatusSize, data, dataSize, option); + if (ret < 0) { + return ret; + } + + sceNpTusWaitAsync(reqId, &ret); + + return ret; } s32 PS4_SYSV_ABI sceNpTssGetSmallStorage() { @@ -313,14 +584,20 @@ s32 PS4_SYSV_ABI sceNpTusChangeModeForOtherSaveDataOwners() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusCreateNpTitleCtxA() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); - return ORBIS_OK; -} +s32 PS4_SYSV_ABI sceNpTusCreateRequest(int libCtxId) { + LOG_INFO(Lib_NpTus, "libCtxId = {}", libCtxId); -s32 PS4_SYSV_ABI sceNpTusCreateRequest() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); - return ORBIS_OK; + NpTusTitleContext* ctx = nullptr; + if (auto ret = ctxManager.GetObject(libCtxId, &ctx); ret < 0) { + return ret; + } + + auto req = ctx->requestsManager.CreateObject(); + if (req < 0) { + return req; + } + + return PackReqId(libCtxId, req); } s32 PS4_SYSV_ABI sceNpTusCreateTitleCtx() { @@ -368,24 +645,70 @@ s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableVUserAsync() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusDeleteNpTitleCtx() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpTusDeleteNpTitleCtx(int ctxId) { + LOG_INFO(Lib_NpTus, "ctxId = {}", ctxId); + + return ctxManager.DeleteObject(ctxId); +} + +s32 PS4_SYSV_ABI sceNpTusDeleteRequest(int requestId) { + LOG_INFO(Lib_NpTus, "requestId = {:#x}", requestId); + + auto [ctxId, reqId] = UnpackReqId(requestId); + + NpTusTitleContext* ctx = nullptr; + if (auto ret = ctxManager.GetObject(ctxId, &ctx); ret < 0) { + return ret; + } + + return ctx->DeleteRequest(reqId); +} + +s32 PS4_SYSV_ABI sceNpTusGetDataAAsync(int reqId, OrbisNpAccountId accountId, + OrbisNpTusSlotId slotId, OrbisNpTusDataStatusA* dataStatus, + u64 dataStatusSize, void* data, u64 dataSize, void* option) { + LOG_INFO(Lib_NpTus, + "reqId = {:#x}, accountId = {:#x}, slotId = {}, dataStatus = {}, dataStatusSize = {}, " + "dataSize = {}", + reqId, accountId, slotId, fmt::ptr(dataStatus), dataStatusSize, dataSize); + + if (slotId < 0 || option) { + return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; + } + if (dataStatusSize != sizeof(OrbisNpTusDataStatusA)) { + return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; + } + + NpTusRequest* req = nullptr; + if (auto ret = GetRequest(reqId, &req); ret < 0) { + return ret; + } + + req->Start([=]() { + // + return 0; + }); + return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusDeleteRequest() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); - return ORBIS_OK; -} +s32 PS4_SYSV_ABI sceNpTusGetDataA(int reqId, OrbisNpAccountId accountId, OrbisNpTusSlotId slotId, + OrbisNpTusDataStatusA* dataStatus, u64 dataStatusSize, void* data, + u64 dataSize, void* option) { + LOG_INFO(Lib_NpTus, + "reqId = {:#x}, accountId = {:#x}, slotId = {}, dataStatus = {}, dataStatusSize = {}, " + "dataSize = {}", + reqId, accountId, slotId, fmt::ptr(dataStatus), dataStatusSize, dataSize); -s32 PS4_SYSV_ABI sceNpTusGetDataA() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); - return ORBIS_OK; -} + auto ret = sceNpTusGetDataAAsync(reqId, accountId, slotId, dataStatus, dataStatusSize, data, + dataSize, option); + if (ret < 0) { + return ret; + } -s32 PS4_SYSV_ABI sceNpTusGetDataAAsync() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); - return ORBIS_OK; + sceNpTusWaitAsync(reqId, &ret); + + return ret; } s32 PS4_SYSV_ABI sceNpTusGetDataAVUser() { @@ -458,13 +781,62 @@ s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableForCrossSaveAsync() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusA() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAAsync(int reqId, OrbisNpAccountId accountId, + OrbisNpTusSlotId* slotIds, + OrbisNpTusDataStatusA* statuses, + u64 statusesSize, int arrayLen, + void* option) { + LOG_ERROR(Lib_NpTus, "reqId = {:#x}, accountId = {}, arrayLen = {}, option = {}", reqId, + accountId, arrayLen, fmt::ptr(option)); + + if (!slotIds || !statuses) { + return ORBIS_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT; + } + if (arrayLen < 1 || option) { + return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; + } + if (arrayLen * sizeof(OrbisNpTusDataStatusA) != statusesSize) { + return ORBIS_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT; + } + if (arrayLen > 64) { + return ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID; + } + if (std::ranges::any_of( + std::vector>(slotIds, slotIds + arrayLen), + [](auto id) { return id < 0; })) { + return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; + } + + // if sdk_ver >= 5.50 clear the statuses array + + NpTusRequest* req = nullptr; + if (auto ret = GetRequest(reqId, &req); ret < 0) { + return ret; + } + + req->Start([=]() { + // + return 0; + }); + return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAAsync() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusA(int reqId, OrbisNpAccountId accountId, + OrbisNpTusSlotId* slotIds, + OrbisNpTusDataStatusA* statuses, u64 statusesSize, + int arrayLen, void* option) { + LOG_ERROR(Lib_NpTus, "reqId = {:#x}, accountId = {}, arrayLen = {}, option = {}", reqId, + accountId, arrayLen, fmt::ptr(option)); + + auto ret = sceNpTusGetMultiSlotDataStatusAAsync(reqId, accountId, slotIds, statuses, + statusesSize, arrayLen, option); + if (ret < 0) { + return ret; + } + + sceNpTusWaitAsync(reqId, &ret); + return ORBIS_OK; } @@ -618,18 +990,72 @@ s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableForCrossSaveVUserAsync() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusPollAsync() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpTusPollAsync(int reqId, int* result) { + LOG_INFO(Lib_NpTus, "reqId = {:#x}", reqId); + + NpTusRequest* req = nullptr; + if (auto ret = GetRequest(reqId, &req); ret < 0) { + return ret; + } + + if (!req->task.valid()) { + LOG_ERROR(Lib_NpTus, "request not started"); + return 1; + } + if (req->task.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { + LOG_DEBUG(Lib_NpTus, "request finished"); + if (result) { + *result = req->task.get(); + } + return 0; + } + + return 1; +} + +s32 PS4_SYSV_ABI sceNpTusSetDataAAsync(int reqId, OrbisNpAccountId accountId, + OrbisNpTusSlotId slotId, u64 totalSize, u64 sendSize, + const void* data, const OrbisNpTusDataInfo* info, + u64 infoSize, const OrbisNpAccountId* lastChangedAuthor, + Libraries::Rtc::OrbisRtcTick* lastChanged, void* option) { + LOG_INFO(Lib_NpTus, + "reqId = {:#x}, accountId = {}, slotId = {}, totalSize = {}, sendSize = {}, " + "info->size = {}, infoSize = {}, lastChangedAuthor = {}", + reqId, accountId, slotId, totalSize, sendSize, info ? info->size : 0, infoSize, + lastChangedAuthor ? *lastChangedAuthor : 0); + + NpTusRequest* req = nullptr; + if (auto ret = GetRequest(reqId, &req); ret < 0) { + return ret; + } + + req->Start([=]() { + // + return 0; + }); + return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusSetDataA() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); - return ORBIS_OK; -} +s32 PS4_SYSV_ABI sceNpTusSetDataA(int reqId, OrbisNpAccountId accountId, OrbisNpTusSlotId slotId, + u64 totalSize, u64 sendSize, const void* data, + const OrbisNpTusDataInfo* info, u64 infoSize, + const OrbisNpAccountId* lastChangedAuthor, + Libraries::Rtc::OrbisRtcTick* lastChanged, void* option) { + LOG_INFO(Lib_NpTus, + "reqId = {:#x}, accountId = {}, slotId = {}, totalSize = {}, sendSize = {}, " + "info->size = {}, infoSize = {}, lastChangedAuthor = {}", + reqId, accountId, slotId, totalSize, sendSize, info ? info->size : 0, infoSize, + lastChangedAuthor ? *lastChangedAuthor : 0); + + auto ret = sceNpTusSetDataAAsync(reqId, accountId, slotId, totalSize, sendSize, data, info, + infoSize, lastChangedAuthor, lastChanged, option); + if (ret < 0) { + return ret; + } + + sceNpTusWaitAsync(reqId, &ret); -s32 PS4_SYSV_ABI sceNpTusSetDataAAsync() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); return ORBIS_OK; } @@ -713,8 +1139,25 @@ s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableForCrossSaveVUserAsync() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTusWaitAsync() { - LOG_ERROR(Lib_NpTus, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpTusWaitAsync(int reqId, int* result) { + LOG_INFO(Lib_NpTus, "reqId = {:#x}", reqId); + + NpTusRequest* req = nullptr; + if (auto ret = GetRequest(reqId, &req); ret < 0) { + return ret; + } + + if (!req->task.valid()) { + LOG_ERROR(Lib_NpTus, "request not started"); + return 1; + } + + req->task.wait(); + + LOG_DEBUG(Lib_NpTus, "request finished"); + if (result) { + *result = req->task.get(); + } return ORBIS_OK; } diff --git a/src/core/libraries/np/np_tus.h b/src/core/libraries/np/np_tus.h index 3c18099b2..ef4554ec3 100644 --- a/src/core/libraries/np/np_tus.h +++ b/src/core/libraries/np/np_tus.h @@ -4,6 +4,7 @@ #pragma once #include "common/types.h" +#include "core/libraries/rtc/rtc.h" namespace Core::Loader { class SymbolsResolver; @@ -11,148 +12,76 @@ class SymbolsResolver; namespace Libraries::Np::NpTus { -s32 PS4_SYSV_ABI sceNpTssCreateNpTitleCtx(); -s32 PS4_SYSV_ABI sceNpTusAddAndGetVariable(); -s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableAsync(); -s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableVUser(); -s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusCreateNpTitleCtx(); -s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotData(); -s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotDataAsync(); -s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariable(); -s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableAsync(); -s32 PS4_SYSV_ABI sceNpTusGetData(); -s32 PS4_SYSV_ABI sceNpTusGetDataAsync(); -s32 PS4_SYSV_ABI sceNpTusGetDataVUser(); -s32 PS4_SYSV_ABI sceNpTusGetDataVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatus(); -s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatusAsync(); -s32 PS4_SYSV_ABI sceNpTusGetFriendsVariable(); -s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatus(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusVUser(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariable(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableVUser(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatus(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusVUser(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariable(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableVUser(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusSetData(); -s32 PS4_SYSV_ABI sceNpTusSetDataAsync(); -s32 PS4_SYSV_ABI sceNpTusSetDataVUser(); -s32 PS4_SYSV_ABI sceNpTusSetDataVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariable(); -s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableAsync(); -s32 PS4_SYSV_ABI sceNpTusTryAndSetVariable(); -s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableAsync(); -s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableVUser(); -s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableVUserAsync(); -s32 PS4_SYSV_ABI sceNpTssCreateNpTitleCtxA(); -s32 PS4_SYSV_ABI sceNpTssGetData(); -s32 PS4_SYSV_ABI sceNpTssGetDataAsync(); -s32 PS4_SYSV_ABI sceNpTssGetSmallStorage(); -s32 PS4_SYSV_ABI sceNpTssGetSmallStorageAsync(); -s32 PS4_SYSV_ABI sceNpTssGetStorage(); -s32 PS4_SYSV_ABI sceNpTssGetStorageAsync(); -s32 PS4_SYSV_ABI sceNpTusAbortRequest(); -s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableA(); -s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableAAsync(); -s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableAVUser(); -s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableAVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableForCrossSave(); -s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableForCrossSaveAsync(); -s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableForCrossSaveVUser(); -s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableForCrossSaveVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusChangeModeForOtherSaveDataOwners(); -s32 PS4_SYSV_ABI sceNpTusCreateNpTitleCtxA(); -s32 PS4_SYSV_ABI sceNpTusCreateRequest(); -s32 PS4_SYSV_ABI sceNpTusCreateTitleCtx(); -s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotDataA(); -s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotDataAAsync(); -s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotDataVUser(); -s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotDataVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableA(); -s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableAAsync(); -s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableVUser(); -s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusDeleteNpTitleCtx(); -s32 PS4_SYSV_ABI sceNpTusDeleteRequest(); -s32 PS4_SYSV_ABI sceNpTusGetDataA(); -s32 PS4_SYSV_ABI sceNpTusGetDataAAsync(); -s32 PS4_SYSV_ABI sceNpTusGetDataAVUser(); -s32 PS4_SYSV_ABI sceNpTusGetDataAVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusGetDataForCrossSave(); -s32 PS4_SYSV_ABI sceNpTusGetDataForCrossSaveAsync(); -s32 PS4_SYSV_ABI sceNpTusGetDataForCrossSaveVUser(); -s32 PS4_SYSV_ABI sceNpTusGetDataForCrossSaveVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatusA(); -s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatusAAsync(); -s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatusForCrossSave(); -s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatusForCrossSaveAsync(); -s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableA(); -s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableAAsync(); -s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableForCrossSave(); -s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableForCrossSaveAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusA(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAVUser(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusForCrossSave(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusForCrossSaveAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusForCrossSaveVUser(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusForCrossSaveVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableA(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAVUser(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableForCrossSave(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableForCrossSaveAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableForCrossSaveVUser(); -s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableForCrossSaveVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusA(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAVUser(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusForCrossSave(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusForCrossSaveAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusForCrossSaveVUser(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusForCrossSaveVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableA(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableAAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableAVUser(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableAVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableForCrossSave(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableForCrossSaveAsync(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableForCrossSaveVUser(); -s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableForCrossSaveVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusPollAsync(); -s32 PS4_SYSV_ABI sceNpTusSetDataA(); -s32 PS4_SYSV_ABI sceNpTusSetDataAAsync(); -s32 PS4_SYSV_ABI sceNpTusSetDataAVUser(); -s32 PS4_SYSV_ABI sceNpTusSetDataAVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableA(); -s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableAAsync(); -s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableVUser(); -s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusSetThreadParam(); -s32 PS4_SYSV_ABI sceNpTusSetTimeout(); -s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableA(); -s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableAAsync(); -s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableAVUser(); -s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableAVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableForCrossSave(); -s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableForCrossSaveAsync(); -s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableForCrossSaveVUser(); -s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableForCrossSaveVUserAsync(); -s32 PS4_SYSV_ABI sceNpTusWaitAsync(); +using OrbisNpTssSlotId = s32; +using OrbisNpTusSlotId = s32; + +struct OrbisNpTusVariable { + OrbisNpId npId; + int set; + Libraries::Rtc::OrbisRtcTick lastChanged; + u8 pad1[4]; + OrbisNpId lastChangedAuthor; + s64 variable; + s64 oldVariable; + OrbisNpAccountId owner; + OrbisNpAccountId lastChangedAuthorId; +}; + +struct OrbisNpTusDataInfo { + u64 size; + u8 data[384]; +}; + +struct OrbisNpTusDataStatus { + OrbisNpId npId; + int set; + Libraries::Rtc::OrbisRtcTick lastChanged; + OrbisNpId lastChangedAuthor; + u8 pad2[4]; + void* data; + u64 dataSize; + OrbisNpTusDataInfo info; +}; + +static_assert(sizeof(OrbisNpTusDataStatus) == 0x1F0); + +struct OrbisNpTusDataStatusA { + OrbisNpOnlineId onlineId; + u8 pad[16]; + int set; + Libraries::Rtc::OrbisRtcTick lastChanged; + OrbisNpOnlineId lastChangedAuthor; + u8 pad2[20]; + void* data; + u64 dataSize; + OrbisNpTusDataInfo info; + OrbisNpAccountId owner; + OrbisNpAccountId lastChangedAuthorId; + u8 pad3[16]; +}; + +static_assert(sizeof(OrbisNpTusDataStatusA) == 0x210); + +enum class OrbisNpTssStatus : int { + Ok = 0, + Partial = 1, + NotModified = 2, +}; + +struct OrbisNpTssDataStatus { + Libraries::Rtc::OrbisRtcTick modified; + OrbisNpTssStatus status; + u64 contentLength; +}; + +struct OrbisNpTssGetDataOptParam { + u64 size; + u64* offset; + u64* last; + void* param; +}; + +s32 PS4_SYSV_ABI sceNpTusWaitAsync(int reqId, int* result); void RegisterLib(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Np::NpTus \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api.cpp b/src/core/libraries/np/np_web_api.cpp index e51b79a3c..0c633e0d1 100644 --- a/src/core/libraries/np/np_web_api.cpp +++ b/src/core/libraries/np/np_web_api.cpp @@ -54,12 +54,12 @@ s32 PS4_SYSV_ABI sceNpWebApiCreateServicePushEventFilter( } s32 PS4_SYSV_ABI sceNpWebApiDeletePushEventFilter(s32 libCtxId, s32 filterId) { - LOG_INFO(Lib_NpWebApi, "called, libCtxId = {:#x}, filterId = {:#x}"); + LOG_INFO(Lib_NpWebApi, "called, libCtxId = {:#x}, filterId = {:#x}", libCtxId, filterId); return deletePushEventFilter(libCtxId, filterId); } s32 PS4_SYSV_ABI sceNpWebApiDeleteServicePushEventFilter(s32 libCtxId, s32 filterId) { - LOG_INFO(Lib_NpWebApi, "called, libCtxId = {:#x}, filterId = {:#x}"); + LOG_INFO(Lib_NpWebApi, "called, libCtxId = {:#x}, filterId = {:#x}", libCtxId, filterId); return deleteServicePushEventFilter(libCtxId, filterId); } diff --git a/src/core/libraries/np/object_manager.h b/src/core/libraries/np/object_manager.h new file mode 100644 index 000000000..5f6ed9663 --- /dev/null +++ b/src/core/libraries/np/object_manager.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +template +struct ObjectManager { + s32 GetObject(int objectId, T** out) { + std::scoped_lock lk{mutex}; + if (objectId < 1 || objectId > N) { + return INVALID_OBJECT_ID_ERROR; + } + auto obj = objects[objectId - 1]; + if (!obj) { + return OBJECT_NOT_FOUND_ERROR; + } + *out = obj; + return ORBIS_OK; + } + + template + s32 CreateObject(Args&&... args) { + std::scoped_lock lk{mutex}; + + if (auto slot = std::ranges::find(objects, nullptr); slot != objects.end()) { + *slot = new T{args...}; + + return std::ranges::distance(objects.begin(), slot) + 1; + } + + return MAX_OBJECTS_ERROR; + } + + s32 DeleteObject(int objectId) { + std::scoped_lock lk{mutex}; + + if (objectId < 1 || objectId > N) { + return INVALID_OBJECT_ID_ERROR; + } + auto obj = objects[objectId - 1]; + if (!obj) { + return OBJECT_NOT_FOUND_ERROR; + } + + delete obj; + objects[objectId - 1] = nullptr; + + return ORBIS_OK; + } + +private: + std::mutex mutex; + std::array objects = {nullptr}; +}; \ No newline at end of file diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index f433a87cc..5f50b8a7d 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/config.h" @@ -94,18 +94,6 @@ int PS4_SYSV_ABI scePadGetCapability() { int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerInformation* pInfo) { LOG_DEBUG(Lib_Pad, "called handle = {}", handle); - if (handle < 0) { - pInfo->touchPadInfo.pixelDensity = 1; - pInfo->touchPadInfo.resolution.x = 1920; - pInfo->touchPadInfo.resolution.y = 950; - pInfo->stickInfo.deadZoneLeft = 1; - pInfo->stickInfo.deadZoneRight = 1; - pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; - pInfo->connectedCount = 1; - pInfo->connected = false; - pInfo->deviceClass = OrbisPadDeviceClass::Standard; - return ORBIS_OK; - } pInfo->touchPadInfo.pixelDensity = 1; pInfo->touchPadInfo.resolution.x = 1920; pInfo->touchPadInfo.resolution.y = 950; @@ -113,8 +101,12 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->stickInfo.deadZoneRight = 1; pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; pInfo->connectedCount = 1; - pInfo->connected = true; pInfo->deviceClass = OrbisPadDeviceClass::Standard; + if (handle < 0) { + pInfo->connected = false; + return ORBIS_OK; + } + pInfo->connected = true; if (Config::getUseSpecialPad()) { pInfo->connectionType = ORBIS_PAD_PORT_TYPE_SPECIAL; pInfo->deviceClass = (OrbisPadDeviceClass)Config::getSpecialPadClass(); @@ -302,20 +294,16 @@ int PS4_SYSV_ABI scePadOutputReport() { return ORBIS_OK; } -int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { - LOG_TRACE(Lib_Pad, "called"); - int connected_count = 0; - bool connected = false; - Input::State states[64]; - auto* controller = Common::Singleton::Instance(); - const auto* engine = controller->GetEngine(); - int ret_num = controller->ReadStates(states, num, &connected, &connected_count); - +int ProcessStates(s32 handle, OrbisPadData* pData, Input::State* states, s32 num, bool connected, + u32 connected_count) { if (!connected) { - ret_num = 1; + pData[0] = {}; + pData[0].orientation = {0.0f, 0.0f, 0.0f, 1.0f}; + pData[0].connected = false; + return 1; } - for (int i = 0; i < ret_num; i++) { + for (int i = 0; i < num; i++) { pData[i].buttons = states[i].buttonsState; pData[i].leftStick.x = states[i].axes[static_cast(Input::Axis::LeftX)]; pData[i].leftStick.y = states[i].axes[static_cast(Input::Axis::LeftY)]; @@ -323,20 +311,16 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { pData[i].rightStick.y = states[i].axes[static_cast(Input::Axis::RightY)]; pData[i].analogButtons.l2 = states[i].axes[static_cast(Input::Axis::TriggerLeft)]; pData[i].analogButtons.r2 = states[i].axes[static_cast(Input::Axis::TriggerRight)]; - pData[i].acceleration.x = states[i].acceleration.x; - pData[i].acceleration.y = states[i].acceleration.y; - pData[i].acceleration.z = states[i].acceleration.z; - pData[i].angularVelocity.x = states[i].angularVelocity.x; - 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}; 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; + pData[i].orientation = {0.0f, 0.0f, 0.0f, 1.0f}; + auto* controller = Common::Singleton::Instance(); + const auto* engine = controller->GetEngine(); if (engine && handle == 1) { const auto gyro_poll_rate = engine->GetAccelPollRate(); if (gyro_poll_rate != 0.0f) { @@ -354,7 +338,6 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { controller->SetLastOrientation(outputOrientation); } } - pData[i].touchData.touchNum = (states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0); @@ -409,7 +392,18 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { pData[i].deviceUniqueDataLen = 0; } - return ret_num; + return num; +} + +int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { + LOG_TRACE(Lib_Pad, "called"); + int connected_count = 0; + bool connected = false; + std::vector states(64); + auto* controller = Common::Singleton::Instance(); + const auto* engine = controller->GetEngine(); + int ret_num = controller->ReadStates(states.data(), num, &connected, &connected_count); + return ProcessStates(handle, pData, states.data(), ret_num, connected, connected_count); } int PS4_SYSV_ABI scePadReadBlasterForTracker() { @@ -439,95 +433,11 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { } auto* controller = Common::Singleton::Instance(); const auto* engine = controller->GetEngine(); - int connectedCount = 0; - bool isConnected = false; + int connected_count = 0; + bool connected = false; Input::State state; - controller->ReadState(&state, &isConnected, &connectedCount); - pData->buttons = state.buttonsState; - 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 * 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}; - - // Only do this on handle 1 for now - if (engine && handle == 1) { - 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->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 = 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 = state.touchpad[1].ID; - pData->timestamp = state.time; - pData->connected = true; // isConnected; //TODO fix me proper - pData->connectedCount = 1; // connectedCount; - pData->deviceUniqueDataLen = 0; - + controller->ReadState(&state, &connected, &connected_count); + ProcessStates(handle, pData, &state, 1, connected, connected_count); return ORBIS_OK; } diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 6657f4036..3606ad5d2 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-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -61,110 +61,51 @@ void State::OnAccel(const float accel[3]) { acceleration.z = accel[2]; } -GameController::GameController() { - m_states_num = 0; - m_last_state = State(); -} +GameController::GameController() : m_states_queue(64) {} void GameController::ReadState(State* state, bool* isConnected, int* connectedCount) { - std::scoped_lock lock{m_mutex}; - *isConnected = m_connected; *connectedCount = m_connected_count; - *state = GetLastState(); + *state = m_state; } int GameController::ReadStates(State* states, int states_num, bool* isConnected, int* connectedCount) { - std::scoped_lock lock{m_mutex}; - *isConnected = m_connected; *connectedCount = m_connected_count; int ret_num = 0; - if (m_connected) { - if (m_states_num == 0) { - ret_num = 1; - states[0] = m_last_state; - } else { - for (uint32_t i = 0; i < m_states_num; i++) { - if (ret_num >= states_num) { - break; - } - auto index = (m_first_state + i) % MAX_STATES; - if (!m_private[index].obtained) { - m_private[index].obtained = true; - - states[ret_num++] = m_states[index]; - } + std::lock_guard lg(m_states_queue_mutex); + for (int i = 0; i < states_num; i++) { + auto o_state = m_states_queue.Pop(); + if (!o_state) { + break; } + states[ret_num++] = *o_state; } } - return ret_num; } -State GameController::GetLastState() const { - if (m_states_num == 0) { - return m_last_state; - } - const u32 last = (m_first_state + m_states_num - 1) % MAX_STATES; - return m_states[last]; -} - -void GameController::AddState(const State& state) { - if (m_states_num >= MAX_STATES) { - m_states_num = MAX_STATES - 1; - m_first_state = (m_first_state + 1) % MAX_STATES; - } - - const u32 index = (m_first_state + m_states_num) % MAX_STATES; - m_states[index] = state; - m_last_state = state; - m_private[index].obtained = false; - m_states_num++; -} - -void GameController::CheckButton(int id, OrbisPadButtonDataOffset button, bool is_pressed) { - std::scoped_lock lock{m_mutex}; - auto state = GetLastState(); - - state.time = Libraries::Kernel::sceKernelGetProcessTime(); - state.OnButton(button, is_pressed); - - AddState(state); +void GameController::Button(int id, OrbisPadButtonDataOffset button, bool is_pressed) { + m_state.OnButton(button, is_pressed); + PushState(); } void GameController::Axis(int id, Input::Axis axis, int value) { - std::scoped_lock lock{m_mutex}; - auto state = GetLastState(); - - state.time = Libraries::Kernel::sceKernelGetProcessTime(); - state.OnAxis(axis, value); - - AddState(state); + m_state.OnAxis(axis, value); + PushState(); } void GameController::Gyro(int id, const float gyro[3]) { - std::scoped_lock lock{m_mutex}; - auto state = GetLastState(); - state.time = Libraries::Kernel::sceKernelGetProcessTime(); - - // Update the angular velocity (gyro data) - state.OnGyro(gyro); - - AddState(state); + m_state.OnGyro(gyro); + PushState(); } + void GameController::Acceleration(int id, const float acceleration[3]) { - std::scoped_lock lock{m_mutex}; - auto state = GetLastState(); - state.time = Libraries::Kernel::sceKernelGetProcessTime(); - - // Update the acceleration values - state.OnAccel(acceleration); - - AddState(state); + m_state.OnAccel(acceleration); + PushState(); } void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration, @@ -206,7 +147,6 @@ void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) { if (!m_engine) { return; } - std::scoped_lock _{m_mutex}; m_engine->SetLightBarRGB(r, g, b); } @@ -214,39 +154,29 @@ void GameController::SetVibration(u8 smallMotor, u8 largeMotor) { if (!m_engine) { return; } - std::scoped_lock _{m_mutex}; m_engine->SetVibration(smallMotor, largeMotor); } void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, float y) { if (touchIndex < 2) { - std::scoped_lock lock{m_mutex}; - auto state = GetLastState(); - - state.time = Libraries::Kernel::sceKernelGetProcessTime(); - state.OnTouchpad(touchIndex, touchDown, x, y); - - AddState(state); + m_state.OnTouchpad(touchIndex, touchDown, x, y); + PushState(); } } 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; @@ -254,47 +184,38 @@ void GameController::SetSecondaryTouchCount(u8 touchCount) { } 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); if (m_engine) { m_engine->Init(); @@ -305,24 +226,17 @@ Engine* GameController::GetEngine() { return m_engine.get(); } +void GameController::PushState() { + std::lock_guard lg(m_states_queue_mutex); + m_state.time = Libraries::Kernel::sceKernelGetProcessTime(); + m_states_queue.Push(m_state); +} + u32 GameController::Poll() { if (m_connected) { - std::scoped_lock lock{m_mutex}; - auto time = Libraries::Kernel::sceKernelGetProcessTime(); - if (m_states_num == 0) { - auto diff = (time - m_last_state.time) / 1000; - if (diff >= 100) { - AddState(GetLastState()); - } - } else { - auto index = (m_first_state - 1 + m_states_num) % MAX_STATES; - auto diff = (time - m_states[index].time) / 1000; - if (m_private[index].obtained && diff >= 100) { - AddState(GetLastState()); - } - } + PushState(); } - return 100; + return 33; } } // namespace Input diff --git a/src/input/controller.h b/src/input/controller.h index dfde521be..6c13fdf99 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -6,7 +6,10 @@ #include #include #include +#include + #include + #include "common/types.h" #include "core/libraries/pad/pad.h" @@ -63,7 +66,36 @@ inline int GetAxis(int min, int max, int value) { return std::clamp((255 * (value - min)) / (max - min), 0, 255); } -constexpr u32 MAX_STATES = 32; +template +class RingBufferQueue { +public: + RingBufferQueue(size_t size) : m_storage(size) {} + + void Push(T item) { + const size_t index = (m_begin + m_size) % m_storage.size(); + m_storage[index] = std::move(item); + if (m_size < m_storage.size()) { + m_size += 1; + } else { + m_begin = (m_begin + 1) % m_storage.size(); + } + } + + std::optional Pop() { + if (m_size == 0) { + return {}; + } + const size_t index = m_begin; + m_begin = (m_begin + 1) % m_storage.size(); + m_size -= 1; + return std::move(m_storage[index]); + } + +private: + size_t m_begin = 0; + size_t m_size = 0; + std::vector m_storage; +}; class GameController { public: @@ -72,9 +104,8 @@ public: void ReadState(State* state, bool* isConnected, int* connectedCount); int ReadStates(State* states, int states_num, bool* isConnected, int* connectedCount); - State GetLastState() const; - void CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button, bool isPressed); - void AddState(const State& state); + + void Button(int id, Libraries::Pad::OrbisPadButtonDataOffset button, bool isPressed); void Axis(int id, Input::Axis axis, int value); void Gyro(int id, const float gyro[3]); void Acceleration(int id, const float acceleration[3]); @@ -105,26 +136,22 @@ public: Libraries::Pad::OrbisFQuaternion& orientation); private: - struct StateInternal { - bool obtained = false; - }; + void PushState(); - std::mutex m_mutex; bool m_connected = true; - State m_last_state; - int m_connected_count = 0; - u32 m_states_num = 0; - u32 m_first_state = 0; + int m_connected_count = 1; 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}; + State m_state; + + std::mutex m_states_queue_mutex; + RingBufferQueue m_states_queue; + std::unique_ptr m_engine = nullptr; }; diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp index 6e5014c1b..e44693fbf 100644 --- a/src/input/input_handler.cpp +++ b/src/input/input_handler.cpp @@ -557,15 +557,15 @@ void ControllerOutput::FinalizeUpdate() { switch (button) { case SDL_GAMEPAD_BUTTON_TOUCHPAD_LEFT: controller->SetTouchpadState(0, new_button_state, 0.25f, 0.5f); - controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state); + controller->Button(0, SDLGamepadToOrbisButton(button), new_button_state); break; case SDL_GAMEPAD_BUTTON_TOUCHPAD_CENTER: controller->SetTouchpadState(0, new_button_state, 0.50f, 0.5f); - controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state); + controller->Button(0, SDLGamepadToOrbisButton(button), new_button_state); break; case SDL_GAMEPAD_BUTTON_TOUCHPAD_RIGHT: controller->SetTouchpadState(0, new_button_state, 0.75f, 0.5f); - controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state); + controller->Button(0, SDLGamepadToOrbisButton(button), new_button_state); break; case LEFTJOYSTICK_HALFMODE: leftjoystick_halfmode = new_button_state; @@ -617,7 +617,7 @@ void ControllerOutput::FinalizeUpdate() { SetMouseGyroRollMode(new_button_state); break; default: // is a normal key (hopefully) - controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state); + controller->Button(0, SDLGamepadToOrbisButton(button), new_button_state); break; } } else if (axis != SDL_GAMEPAD_AXIS_INVALID && positive_axis) { @@ -648,12 +648,12 @@ void ControllerOutput::FinalizeUpdate() { case Axis::TriggerLeft: ApplyDeadzone(new_param, lefttrigger_deadzone); controller->Axis(0, c_axis, GetAxis(0x0, 0x7f, *new_param)); - controller->CheckButton(0, OrbisPadButtonDataOffset::L2, *new_param > 0x20); + controller->Button(0, OrbisPadButtonDataOffset::L2, *new_param > 0x20); return; case Axis::TriggerRight: ApplyDeadzone(new_param, righttrigger_deadzone); controller->Axis(0, c_axis, GetAxis(0x0, 0x7f, *new_param)); - controller->CheckButton(0, OrbisPadButtonDataOffset::R2, *new_param > 0x20); + controller->Button(0, OrbisPadButtonDataOffset::R2, *new_param > 0x20); return; default: break; diff --git a/src/input/input_mouse.cpp b/src/input/input_mouse.cpp index 996c35ef9..0dc44608b 100644 --- a/src/input/input_mouse.cpp +++ b/src/input/input_mouse.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -104,8 +104,8 @@ void EmulateTouchpad(GameController* controller, u32 interval) { controller->SetTouchpadState(0, (mouse_buttons & SDL_BUTTON_LMASK) != 0, std::clamp(x / g_window->GetWidth(), 0.0f, 1.0f), std::clamp(y / g_window->GetHeight(), 0.0f, 1.0f)); - controller->CheckButton(0, Libraries::Pad::OrbisPadButtonDataOffset::TouchPad, - (mouse_buttons & SDL_BUTTON_RMASK) != 0); + controller->Button(0, Libraries::Pad::OrbisPadButtonDataOffset::TouchPad, + (mouse_buttons & SDL_BUTTON_RMASK) != 0); } void ApplyMouseInputBlockers() { diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 476a56b52..c9183f301 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "SDL3/SDL_events.h" @@ -471,7 +471,7 @@ void WindowSDL::WaitEvent() { } void WindowSDL::InitTimers() { - SDL_AddTimer(100, &PollController, controller); + SDL_AddTimer(33, &PollController, controller); SDL_AddTimer(33, Input::MousePolling, (void*)controller); } @@ -540,7 +540,7 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) { // as it would break the entire touchpad handling // You can still bind other things to it though if (event->gbutton.button == SDL_GAMEPAD_BUTTON_TOUCHPAD) { - controller->CheckButton(0, OrbisPadButtonDataOffset::TouchPad, input_down); + controller->Button(0, OrbisPadButtonDataOffset::TouchPad, input_down); return; }