Merge branch 'main' into audio3

This commit is contained in:
georgemoralis 2026-02-11 12:29:38 +02:00 committed by GitHub
commit f98e87ef71
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 1425 additions and 477 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,21 +3,484 @@
#include <cstdarg>
#include <cstdio>
#include <map>
#include <common/va_ctx.h>
#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<s32, OrbisFILE*> 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<s32>((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<u64>(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<u8*>(&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<u8*>(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<u8*>(&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<u64>(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<s8>(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

View File

@ -3,12 +3,96 @@
#pragma once
#include <mutex>
#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

View File

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

View File

@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#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

View File

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

View File

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

View File

@ -93,6 +93,8 @@ struct OrbisNpCreateAsyncRequestParameter {
void RegisterNpCallback(std::string key, std::function<void()> 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);

View File

@ -1,15 +1,87 @@
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fstream>
#include <future>
#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<int, int> UnpackReqId(int reqId) {
return {reqId >> 16, reqId & 0xFFFF};
}
bool IsReqId(int id) {
return id > (1 << 16);
}
struct NpTusRequest {
std::future<int> task;
void Start(auto lambda) {
this->task = std::async(std::launch::async, lambda);
}
};
using NpTusRequestsManager =
ObjectManager<NpTusRequest, 32, ORBIS_NP_COMMUNITY_ERROR_INVALID_ID,
ORBIS_NP_COMMUNITY_ERROR_INVALID_ID, ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS>;
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<NpTusTitleContext, 32, ORBIS_NP_COMMUNITY_ERROR_INVALID_ID,
ORBIS_NP_COMMUNITY_ERROR_INVALID_ID, ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS>;
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<std::reference_wrapper<OrbisNpTusSlotId>>(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<std::reference_wrapper<OrbisNpTusSlotId>>(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;
}

View File

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

View File

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

View File

@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <mutex>
template <typename T, size_t N, int INVALID_OBJECT_ID_ERROR, int OBJECT_NOT_FOUND_ERROR,
int MAX_OBJECTS_ERROR>
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 <typename... Args>
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<T*, N> objects = {nullptr};
};

View File

@ -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<GameController>::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<int>(Input::Axis::LeftX)];
pData[i].leftStick.y = states[i].axes[static_cast<int>(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<int>(Input::Axis::RightY)];
pData[i].analogButtons.l2 = states[i].axes[static_cast<int>(Input::Axis::TriggerLeft)];
pData[i].analogButtons.r2 = states[i].axes[static_cast<int>(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<GameController>::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<Input::State> states(64);
auto* controller = Common::Singleton<GameController>::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<GameController>::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<int>(Input::Axis::LeftX)];
pData->leftStick.y = state.axes[static_cast<int>(Input::Axis::LeftY)];
pData->rightStick.x = state.axes[static_cast<int>(Input::Axis::RightX)];
pData->rightStick.x = state.axes[static_cast<int>(Input::Axis::RightX)];
pData->rightStick.y = state.axes[static_cast<int>(Input::Axis::RightY)];
pData->analogButtons.l2 = state.axes[static_cast<int>(Input::Axis::TriggerLeft)];
pData->analogButtons.r2 = state.axes[static_cast<int>(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<std::chrono::microseconds>(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;
}

View File

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

View File

@ -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 <algorithm>
#include <memory>
#include <mutex>
#include <vector>
#include <SDL3/SDL_gamepad.h>
#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 T>
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<T> 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<T> 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<State, MAX_STATES> m_states;
std::array<StateInternal, MAX_STATES> 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<State> m_states_queue;
std::unique_ptr<Engine> m_engine = nullptr;
};

View File

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

View File

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

View File

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