mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-28 12:05:06 +00:00
776 lines
28 KiB
C++
776 lines
28 KiB
C++
#include "prx/libSceSaveData/SaveDataCore.hpp"
|
|
#include "prx/libkernel/include/Api.hpp"
|
|
#include "prx/libc/include/Api.hpp"
|
|
#include "prx/libSceNpCommon/include/Api.hpp"
|
|
#include "prx/libSceUserService/UserService.hpp"
|
|
#include "prx/libSceRtc/include/Rtc.hpp"
|
|
#include <algorithm>
|
|
#include <atomic>
|
|
#include <iterator>
|
|
#include <mutex>
|
|
#include <random>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include "SceTypes.hpp"
|
|
#include "prx/libc/include/MallocManagedSize.hpp"
|
|
#include "prx/libc/include/General.hpp"
|
|
#include "prx/libkernel/Pthread/include/Cond.hpp"
|
|
#include "prx/libkernel/Pthread/include/Mutex.hpp"
|
|
#include "prx/libkernel/Semaphore/include/Semaphore.hpp"
|
|
|
|
static_assert(sizeof(NpCallout) == 0x28);
|
|
|
|
static_assert(sizeof(NpCalloutContext) == 0x28);
|
|
|
|
static_assert(sizeof(NpKernelMemory) == 0x18);
|
|
|
|
static_assert(sizeof(NpMemoryHeap) == 0x20);
|
|
|
|
namespace {
|
|
|
|
constexpr int NP_ERROR_NULL_ARGUMENT = static_cast<int>(0x80550003);
|
|
constexpr int NP_ERROR_IPC_ALREADY_INITIALIZED = static_cast<int>(0x80559D00);
|
|
constexpr int NP_ERROR_IPC_NOT_INITIALIZED = static_cast<int>(0x80559D01);
|
|
constexpr int NP_ERROR_ONLINE_ID_MISMATCH = static_cast<int>(0x80550609);
|
|
constexpr int NP_ERROR_INVALID_NP_ID = static_cast<int>(0x80550605);
|
|
constexpr std::size_t NP_ID_OPT_OFFSET = 0x14;
|
|
constexpr std::size_t NP_ID_PLATFORM_OFFSET = 0x18;
|
|
constexpr std::size_t NP_ID_TERMINATOR_FLAG_OFFSET = 0x1C;
|
|
|
|
std::uint32_t NpIdWord(const std::uint8_t* npId, std::size_t offset) {
|
|
std::uint32_t value;
|
|
std::memcpy(&value, npId + offset, sizeof(value));
|
|
return value;
|
|
}
|
|
constexpr std::size_t NP_ONLINE_ID_MAX = 16;
|
|
constexpr std::size_t NP_ONLINE_ID_MIN = 3;
|
|
constexpr int NP_ERROR_INVALID_ARGUMENT_UTIL = static_cast<int>(0x80550601);
|
|
constexpr std::size_t NP_LANGUAGE_CODE_SIZE = 6;
|
|
constexpr std::size_t NP_LANGUAGE_CODE2_SIZE = 0x24;
|
|
|
|
struct NpLanguageCodes {
|
|
const char* code;
|
|
const char* tag;
|
|
};
|
|
|
|
constexpr NpLanguageCodes NP_LANGUAGE_CODES[] {
|
|
{"ja", "ja-JP"}, {"en", "en-US"}, {"fr", "fr-FR"}, {"es", "es-ES"}, {"de", "de-DE"}, {"it", "it-IT"},
|
|
{"nl", "nl-NL"}, {"pt", "pt-PT"}, {"ru", "ru-RU"}, {"ko", "ko-KR"}, {"zh-TW", "zh-Hant"}, {"zh-CN", "zh-Hans"},
|
|
{"fi", "fi-FI"}, {"sv", "sv-SE"}, {"da", "da-DK"}, {"no", "no-NO"}, {"pl", "pl-PL"}, {"pt-BR", "pt-BR"},
|
|
{"en-GB", "en-GB"}, {"tr", "tr-TR"}, {"es-MX", "es-419"}, {"ar", "ar-AE"}, {"fr-CA", "fr-CA"}, {"cs", "cs-CZ"},
|
|
{"hu", "hu-HU"}, {"el", "el-GR"}, {"ro", "ro-RO"}, {"th", "th-TH"}, {"vi", "vi-VN"}, {"id", "id-ID"},
|
|
{"uk", "uk-UA"},
|
|
};
|
|
|
|
const NpLanguageCodes* FindLanguageCodes(int language) {
|
|
if (language < 0 || static_cast<std::size_t>(language) >= std::size(NP_LANGUAGE_CODES)) return nullptr;
|
|
return &NP_LANGUAGE_CODES[language];
|
|
}
|
|
|
|
void CopyText(char* out, const char* text, std::size_t size) {
|
|
std::strncpy(out, text, size - 1);
|
|
out[size - 1] = '\0';
|
|
}
|
|
|
|
std::atomic<std::uint32_t> g_debugIds {0};
|
|
|
|
constexpr int NP_UTIL_ERROR_BUFFER_TOO_SMALL = static_cast<int>(0x80550602);
|
|
constexpr int NP_UTIL_ERROR_OUT_OF_RANGE = static_cast<int>(0x8055060A);
|
|
constexpr std::size_t NP_TITLE_ID_SIZE = 13;
|
|
constexpr std::uint8_t NP_TITLE_ID_INDEX_MAX = 99;
|
|
constexpr std::size_t NP_DUMP_LINE_BYTES = 16;
|
|
|
|
bool IsAsciiLetter(char c) {
|
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
|
}
|
|
|
|
bool IsOnlineIdText(const char* text) {
|
|
const std::size_t length = std::strlen(text);
|
|
if (length < NP_ONLINE_ID_MIN || length > NP_ONLINE_ID_MAX || !IsAsciiLetter(text[0])) return false;
|
|
return std::all_of(text + 1, text + length, [](char c) {
|
|
return IsAsciiLetter(c) || (c >= '0' && c <= '9') || c == '_' || c == '-';
|
|
});
|
|
}
|
|
|
|
constexpr int NP_ERROR_BUFFER_TOO_SMALL = static_cast<int>(0x80558008);
|
|
constexpr std::size_t NP_SDK_VERSION_SIZE = 8;
|
|
constexpr std::size_t NP_RFC3339_BUFFER_MIN = 0x80;
|
|
constexpr std::size_t NP_USER_ID_LIST_SIZE = 20;
|
|
constexpr int USER_ID_INVALID = -1;
|
|
constexpr KernelClockid NP_SYSTEM_CLOCK = 4;
|
|
|
|
static_assert(sizeof(SceKernelSystemSwVersion) == 0x28);
|
|
|
|
std::uint32_t SystemVersion() {
|
|
SceKernelSystemSwVersion version {};
|
|
version.size = sizeof(version);
|
|
sceKernelGetSystemSwVersion_nid_postfix(&version);
|
|
|
|
return version.version;
|
|
}
|
|
|
|
int BcdPair(std::uint32_t value, int shift) {
|
|
return static_cast<int>((value >> (shift + 4)) & 0xF) * 10 + static_cast<int>((value >> shift) & 0xF);
|
|
}
|
|
|
|
constexpr int NP_ERROR_OUT_OF_MEMORY = static_cast<int>(0x80550005);
|
|
constexpr int NP_ERROR_NULL_OBJECT = static_cast<int>(0x80558005);
|
|
constexpr int NP_ERROR_HEAP_CREATE = static_cast<int>(0x8055800A);
|
|
constexpr std::uint64_t NP_KERNEL_MEMORY_ALIGNMENT = 0x4000;
|
|
constexpr int NP_KERNEL_MEMORY_PROTECTION = 3;
|
|
constexpr std::size_t NP_GLOBAL_HEAP_SIZE = 0x80000;
|
|
constexpr char NP_GLOBAL_HEAP_NAME[] = "NpCommonGlobalHeap";
|
|
constexpr std::size_t NP_IPC_CLIENT_HEAP_SIZE = 0x10000;
|
|
constexpr char NP_IPC_CLIENT_HEAP_NAME[] = "NpCommonIpcClientHeap";
|
|
|
|
void* APS5_VABI HeapAllocate(std::size_t size, void* context) {
|
|
auto* heap = static_cast<NpHeap*>(context);
|
|
return heap->mspace != nullptr ? sceLibcMspaceMalloc_nid_postfix(heap->mspace, size) : nullptr;
|
|
}
|
|
|
|
void* APS5_VABI HeapReallocate(void* pointer, std::size_t size, void* context) {
|
|
auto* heap = static_cast<NpHeap*>(context);
|
|
return heap->mspace != nullptr ? sceLibcMspaceRealloc_nid_postfix(heap->mspace, pointer, size) : nullptr;
|
|
}
|
|
|
|
void APS5_VABI HeapRelease(void* pointer, void* context) {
|
|
auto* heap = static_cast<NpHeap*>(context);
|
|
if (heap->mspace != nullptr && pointer != nullptr) sceLibcMspaceFree_nid_postfix(heap->mspace, pointer);
|
|
}
|
|
|
|
void* APS5_VABI MemoryHeapAllocate(std::size_t size, void* context) {
|
|
return HeapAllocate(size, &static_cast<NpMemoryHeap*>(context)->heap);
|
|
}
|
|
|
|
void* APS5_VABI MemoryHeapReallocate(void* pointer, std::size_t size, void* context) {
|
|
return HeapReallocate(pointer, size, &static_cast<NpMemoryHeap*>(context)->heap);
|
|
}
|
|
|
|
void APS5_VABI MemoryHeapRelease(void* pointer, void* context) {
|
|
HeapRelease(pointer, &static_cast<NpMemoryHeap*>(context)->heap);
|
|
}
|
|
|
|
std::uint64_t AlignKernelMemory(std::uint64_t size) {
|
|
const std::uint64_t remainder = size & (NP_KERNEL_MEMORY_ALIGNMENT - 1);
|
|
return remainder == 0 ? size : size + NP_KERNEL_MEMORY_ALIGNMENT - remainder;
|
|
}
|
|
|
|
int MapKernelMemory(NpKernelMemory* memory, std::uint64_t size, const char* name) {
|
|
memory->reserved = 0;
|
|
memory->size = size;
|
|
return sceKernelMapNamedFlexibleMemory(&memory->address, size, NP_KERNEL_MEMORY_PROTECTION, 0, name);
|
|
}
|
|
|
|
void UnmapKernelMemory(NpKernelMemory* memory) {
|
|
if (memory->address != nullptr) sceKernelMunmap(reinterpret_cast<std::uint64_t>(memory->address), memory->size);
|
|
memory->address = nullptr;
|
|
}
|
|
|
|
int InitHeap(NpHeap* heap, void* base, std::size_t size, const char* name) {
|
|
heap->mspace = sceLibcMspaceCreate_nid_postfix(name, base, size, 0);
|
|
return heap->mspace != nullptr ? 0 : NP_ERROR_HEAP_CREATE;
|
|
}
|
|
|
|
void DestroyHeap(NpHeap* heap) {
|
|
if (heap->mspace == nullptr) return;
|
|
sceLibcMspaceDestroy_nid_postfix(heap->mspace);
|
|
|
|
heap->mspace = nullptr;
|
|
}
|
|
|
|
int InitMemoryHeap(NpMemoryHeap* heap, std::uint64_t size, const char* name) {
|
|
if (heap == nullptr) return NP_ERROR_NULL_OBJECT;
|
|
int result = MapKernelMemory(&heap->memory, AlignKernelMemory(size), name);
|
|
if (result < 0) return result;
|
|
result = InitHeap(&heap->heap, heap->memory.address, heap->memory.size, name);
|
|
if (result >= 0) return result;
|
|
UnmapKernelMemory(&heap->memory);
|
|
*heap = {};
|
|
return result;
|
|
}
|
|
|
|
void DestroyMemoryHeap(NpMemoryHeap* heap) {
|
|
DestroyHeap(&heap->heap);
|
|
UnmapKernelMemory(&heap->memory);
|
|
}
|
|
|
|
NpAllocator MemoryHeapAllocator(NpMemoryHeap* heap) {
|
|
return {MemoryHeapAllocate, MemoryHeapReallocate, MemoryHeapRelease, heap};
|
|
}
|
|
|
|
constexpr int NP_ERROR_NOT_INITIALIZED = static_cast<int>(0x80558001);
|
|
constexpr int NP_ERROR_ALREADY_INITIALIZED = static_cast<int>(0x80558002);
|
|
constexpr int NP_ERROR_ALREADY_QUEUED = static_cast<int>(0x80558006);
|
|
constexpr std::uint64_t NP_CALLOUT_MAX_WAIT = 0xFFFFFFFF;
|
|
|
|
std::mutex g_randomMutex;
|
|
std::mt19937 g_random {std::random_device {}()};
|
|
|
|
NpMemoryHeap g_globalHeap {};
|
|
NpAllocator g_globalAllocator {};
|
|
NpAllocatorEx g_globalAllocatorEx {};
|
|
int g_archReferences = 0;
|
|
std::mutex g_archMutex;
|
|
std::mutex g_ipcClientMutex;
|
|
bool g_ipcClientInitialized = false;
|
|
NpMemoryHeap g_ipcClientHeap {};
|
|
|
|
constexpr int KERNEL_ERROR_EBUSY = static_cast<int>(0x80020010);
|
|
constexpr int KERNEL_ERROR_ETIMEDOUT = static_cast<int>(0x8002003C);
|
|
constexpr int NP_ERROR_INVALID_ARGUMENT = static_cast<int>(0x80558009);
|
|
constexpr int NP_ERROR_TIMEOUT = static_cast<int>(0x8055800B);
|
|
constexpr int NP_ERROR_EVENT_FLAG_BUSY = static_cast<int>(0x8055800C);
|
|
constexpr int NP_ERROR_SEMA_BUSY = static_cast<int>(0x8055800D);
|
|
constexpr int NP_ERROR_MUTEX_BUSY = static_cast<int>(0x8055800F);
|
|
constexpr std::uint32_t NP_MUTEX_FLAG_RECURSIVE = 1;
|
|
constexpr int PTHREAD_MUTEX_TYPE_RECURSIVE = 2;
|
|
constexpr std::uint32_t NP_SEMA_FLAG_PRIORITY = 1;
|
|
constexpr std::uint32_t KERNEL_SEMA_ATTR_FIFO = 1;
|
|
constexpr std::uint32_t KERNEL_SEMA_ATTR_PRIORITY_SHIFT = 8;
|
|
constexpr int NP_THREAD_EXPLICIT_SCHED = 0;
|
|
constexpr int NP_THREAD_SCHED_POLICY = 1;
|
|
|
|
int NpResult(int result) {
|
|
return result < 0 ? result : 0;
|
|
}
|
|
|
|
int NpBusyResult(int result, int busy) {
|
|
return result == KERNEL_ERROR_EBUSY ? busy : NpResult(result);
|
|
}
|
|
|
|
int InitMutex(PthreadMutex* mutex, const char* name, std::uint32_t flags) {
|
|
PthreadMutexattr attr {};
|
|
int result = scePthreadMutexattrInit(&attr);
|
|
if (result < 0) return result;
|
|
if ((flags & NP_MUTEX_FLAG_RECURSIVE) != 0) result = scePthreadMutexattrSettype(&attr, PTHREAD_MUTEX_TYPE_RECURSIVE);
|
|
if (result >= 0) result = scePthreadMutexInit(mutex, &attr, name);
|
|
scePthreadMutexattrDestroy(&attr);
|
|
|
|
return NpResult(result);
|
|
}
|
|
|
|
int InitCond(PthreadCond* cond, const char* name) {
|
|
PthreadCondattr attr {};
|
|
const int result = scePthreadCondattrInit(&attr);
|
|
if (result < 0) return result;
|
|
const int created = scePthreadCondInit(cond, &attr, name);
|
|
scePthreadCondattrDestroy(&attr);
|
|
|
|
return created;
|
|
}
|
|
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
int APS5_VABI sceNpAtomicCas32(std::int32_t* value, std::int32_t expected, std::int32_t desired) {
|
|
std::atomic_ref<std::int32_t>(*value).compare_exchange_strong(expected, desired);
|
|
return expected;
|
|
}
|
|
|
|
int APS5_VABI sceNpAtomicInc32(std::int32_t* value) {
|
|
return std::atomic_ref<std::int32_t>(*value).fetch_add(1);
|
|
}
|
|
|
|
int APS5_VABI sceNpAtomicDec32(std::int32_t* value) {
|
|
return std::atomic_ref<std::int32_t>(*value).fetch_sub(1);
|
|
}
|
|
|
|
int APS5_VABI sceNpCondSignalAll(PthreadCond* cond) {
|
|
return scePthreadCondBroadcast(cond);
|
|
}
|
|
|
|
int APS5_VABI sceNpCondSignalTo(PthreadCond* cond, Pthread thread) {
|
|
return scePthreadCondSignalto(cond, thread);
|
|
}
|
|
|
|
int APS5_VABI sceNpCondWait(PthreadCond* cond, PthreadMutex* mutex) {
|
|
return scePthreadCondWait(cond, mutex);
|
|
}
|
|
|
|
int APS5_VABI sceNpLwCondInit(PthreadCond* cond, const char* name) {
|
|
return InitCond(cond, name);
|
|
}
|
|
|
|
int APS5_VABI sceNpLwCondDestroy(PthreadCond* cond) {
|
|
return scePthreadCondDestroy(cond);
|
|
}
|
|
|
|
int APS5_VABI sceNpLwCondSignal(PthreadCond* cond) {
|
|
return scePthreadCondSignal(cond);
|
|
}
|
|
|
|
int APS5_VABI sceNpLwCondSignalAll(PthreadCond* cond) {
|
|
return scePthreadCondBroadcast(cond);
|
|
}
|
|
|
|
int APS5_VABI sceNpLwCondSignalTo(PthreadCond* cond, Pthread thread) {
|
|
return scePthreadCondSignalto(cond, thread);
|
|
}
|
|
|
|
int APS5_VABI sceNpLwCondWait(PthreadCond* cond, PthreadMutex* mutex) {
|
|
return scePthreadCondWait(cond, mutex);
|
|
}
|
|
|
|
int APS5_VABI sceNpCreateEventFlag(KernelEventFlag* flag, const char* name, std::uint32_t attr, std::uint64_t pattern) {
|
|
return NpResult(sceKernelCreateEventFlag(flag, name, attr, pattern, nullptr));
|
|
}
|
|
|
|
int APS5_VABI sceNpDeleteEventFlag(KernelEventFlag flag) {
|
|
return NpResult(sceKernelDeleteEventFlag(flag));
|
|
}
|
|
|
|
int APS5_VABI sceNpOpenEventFlag(KernelEventFlag* flag, const char* name) {
|
|
return NpResult(sceKernelOpenEventFlag(flag, name));
|
|
}
|
|
|
|
int APS5_VABI sceNpCloseEventFlag(KernelEventFlag flag) {
|
|
return NpResult(sceKernelCloseEventFlag(flag));
|
|
}
|
|
|
|
int APS5_VABI sceNpSetEventFlag(KernelEventFlag flag, std::uint64_t pattern) {
|
|
return NpResult(sceKernelSetEventFlag(flag, pattern));
|
|
}
|
|
|
|
int APS5_VABI sceNpClearEventFlag(KernelEventFlag flag, std::uint64_t pattern) {
|
|
return NpResult(sceKernelClearEventFlag(flag, pattern));
|
|
}
|
|
|
|
int APS5_VABI sceNpPollEventFlag(KernelEventFlag flag, std::uint64_t pattern, std::uint32_t mode, std::uint64_t* result) {
|
|
return NpBusyResult(sceKernelPollEventFlag(flag, pattern, mode, result), NP_ERROR_EVENT_FLAG_BUSY);
|
|
}
|
|
|
|
int APS5_VABI sceNpCancelEventFlag(KernelEventFlag flag, std::uint64_t pattern, int* waiting) {
|
|
return NpResult(sceKernelCancelEventFlag(flag, pattern, waiting));
|
|
}
|
|
|
|
int APS5_VABI sceNpCreateSema(KernelSema* sema, std::uint32_t flags, int initial, int maximum, const char* name) {
|
|
const std::uint32_t attr = KERNEL_SEMA_ATTR_FIFO + ((flags & NP_SEMA_FLAG_PRIORITY) << KERNEL_SEMA_ATTR_PRIORITY_SHIFT);
|
|
return NpResult(sceKernelCreateSema(sema, name, attr, initial, maximum, nullptr));
|
|
}
|
|
|
|
int APS5_VABI sceNpDeleteSema(KernelSema sema) {
|
|
return NpResult(sceKernelDeleteSema(sema));
|
|
}
|
|
|
|
int APS5_VABI sceNpPollSema(KernelSema sema) {
|
|
return NpBusyResult(sceKernelPollSema(sema, 1), NP_ERROR_SEMA_BUSY);
|
|
}
|
|
|
|
int APS5_VABI sceNpSignalSema(KernelSema sema) {
|
|
return NpResult(sceKernelSignalSema(sema, 1));
|
|
}
|
|
|
|
int APS5_VABI sceNpCreateThread(Pthread* thread, PthreadEntry entry, void* arg, int priority, std::size_t stackSize, KernelCpumask affinity, const char* name) {
|
|
PthreadAttr attr {};
|
|
int result = scePthreadAttrInit(&attr);
|
|
if (result < 0) return result;
|
|
result = scePthreadAttrSetstacksize(&attr, stackSize);
|
|
if (result < 0) {
|
|
scePthreadAttrDestroy(&attr);
|
|
|
|
return result;
|
|
}
|
|
if (priority != 0) {
|
|
result = scePthreadAttrSetinheritsched(&attr, NP_THREAD_EXPLICIT_SCHED);
|
|
if (result >= 0) result = scePthreadAttrSetschedpolicy(&attr, NP_THREAD_SCHED_POLICY);
|
|
if (result >= 0) {
|
|
KernelSchedParam param {};
|
|
param.sched_priority = priority;
|
|
result = scePthreadAttrSetschedparam(&attr, ¶m);
|
|
}
|
|
}
|
|
if (result >= 0 && affinity != 0) result = scePthreadAttrSetaffinity(&attr, affinity);
|
|
if (result >= 0) result = scePthreadCreate(thread, &attr, entry, arg, name);
|
|
scePthreadAttrDestroy(&attr);
|
|
|
|
return result;
|
|
}
|
|
|
|
int APS5_VABI sceNpThreadGetId(Pthread* thread) {
|
|
if (thread == nullptr) return NP_ERROR_INVALID_ARGUMENT;
|
|
*thread = scePthreadSelf();
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNpGetProcessId(void) {
|
|
return getpid_nid_postfix();
|
|
}
|
|
|
|
[[noreturn]] void APS5_VABI sceNpPanic(void) {
|
|
std::abort();
|
|
}
|
|
|
|
int APS5_VABI sceNpDbgDumpText(const char* text, int length) {
|
|
return std::printf("%.*s", length, text);
|
|
}
|
|
|
|
int APS5_VABI sceNpIpc2ClientInit(void) {
|
|
std::lock_guard lock(g_ipcClientMutex);
|
|
if (g_ipcClientInitialized) return NP_ERROR_IPC_ALREADY_INITIALIZED;
|
|
const int result = InitMemoryHeap(&g_ipcClientHeap, NP_IPC_CLIENT_HEAP_SIZE, NP_IPC_CLIENT_HEAP_NAME);
|
|
if (result < 0) return result;
|
|
g_ipcClientInitialized = true;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNpIpc2ClientTerm(void) {
|
|
std::lock_guard lock(g_ipcClientMutex);
|
|
if (!g_ipcClientInitialized) return NP_ERROR_IPC_NOT_INITIALIZED;
|
|
DestroyMemoryHeap(&g_ipcClientHeap);
|
|
g_ipcClientInitialized = false;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNpArchInit(void) {
|
|
std::lock_guard lock(g_archMutex);
|
|
if (g_archReferences != 0) {
|
|
++g_archReferences;
|
|
return 0;
|
|
}
|
|
const int result = InitMemoryHeap(&g_globalHeap, NP_GLOBAL_HEAP_SIZE, NP_GLOBAL_HEAP_NAME);
|
|
if (result < 0) return result;
|
|
g_archReferences = 1;
|
|
return 0;
|
|
}
|
|
|
|
void APS5_VABI sceNpArchTerm(void) {
|
|
std::lock_guard lock(g_archMutex);
|
|
if (g_archReferences == 0) return;
|
|
if (--g_archReferences == 0) DestroyMemoryHeap(&g_globalHeap);
|
|
}
|
|
|
|
int APS5_VABI sceNpHeapInit(NpHeap* heap, void* base, std::size_t size, const char* name) {
|
|
return InitHeap(heap, base, size, name);
|
|
}
|
|
|
|
void APS5_VABI sceNpHeapDestroy(NpHeap* heap) {
|
|
DestroyHeap(heap);
|
|
}
|
|
|
|
void APS5_VABI sceNpHeapGetAllocator(NpHeap* heap, NpAllocator* allocator) {
|
|
*allocator = {HeapAllocate, HeapReallocate, HeapRelease, heap};
|
|
}
|
|
|
|
int APS5_VABI sceNpHeapGetStat(const NpHeap* heap, NpHeapStat* stat) {
|
|
MallocManagedSize stats {};
|
|
stats.size = sizeof(stats);
|
|
stats.version = 1;
|
|
const int result = sceLibcMspaceMallocStats_nid_postfix(heap->mspace, &stats);
|
|
if (result == 0) *stat = {stats.maxSystemSize, stats.maxInuseSize, stats.currentInuseSize};
|
|
return result;
|
|
}
|
|
|
|
void APS5_VABI sceNpHeapShowStat(const NpHeap* heap) {
|
|
NpHeapStat stat {};
|
|
if (sceNpHeapGetStat(heap, &stat) != 0) {
|
|
std::printf("[NpCommon] heap statistics unavailable\n");
|
|
return;
|
|
}
|
|
std::printf("[NpCommon] heap: system %llu bytes, peak in use %llu bytes, in use %llu bytes\n",
|
|
static_cast<unsigned long long>(stat.maxSystemSize), static_cast<unsigned long long>(stat.maxInuseSize),
|
|
static_cast<unsigned long long>(stat.currentInuseSize));
|
|
}
|
|
|
|
int APS5_VABI sceNpMemoryHeapInit(NpMemoryHeap* heap, std::uint64_t size, const char* name) {
|
|
return InitMemoryHeap(heap, size, name);
|
|
}
|
|
|
|
void APS5_VABI sceNpMemoryHeapDestroy(NpMemoryHeap* heap) {
|
|
DestroyMemoryHeap(heap);
|
|
}
|
|
|
|
void APS5_VABI sceNpMemoryHeapGetAllocator(NpMemoryHeap* heap, NpAllocator* allocator) {
|
|
*allocator = MemoryHeapAllocator(heap);
|
|
}
|
|
|
|
void APS5_VABI sceNpMemoryHeapGetAllocatorEx(NpMemoryHeap* heap, int outOfMemoryError, NpAllocatorEx* allocator) {
|
|
*allocator = {MemoryHeapAllocator(heap), outOfMemoryError};
|
|
}
|
|
|
|
void APS5_VABI sceNpGlobalHeapGetAllocator(NpAllocator* allocator) {
|
|
*allocator = MemoryHeapAllocator(&g_globalHeap);
|
|
}
|
|
|
|
void APS5_VABI sceNpGlobalHeapGetAllocatorEx(NpAllocatorEx* allocator) {
|
|
*allocator = {MemoryHeapAllocator(&g_globalHeap), NP_ERROR_OUT_OF_MEMORY};
|
|
}
|
|
|
|
NpAllocator* APS5_VABI sceNpGlobalHeapGetAllocatorPtr(void) {
|
|
g_globalAllocator = MemoryHeapAllocator(&g_globalHeap);
|
|
return &g_globalAllocator;
|
|
}
|
|
|
|
NpAllocatorEx* APS5_VABI sceNpGlobalHeapGetAllocatorExPtr(void) {
|
|
g_globalAllocatorEx = {MemoryHeapAllocator(&g_globalHeap), NP_ERROR_OUT_OF_MEMORY};
|
|
return &g_globalAllocatorEx;
|
|
}
|
|
|
|
int APS5_VABI sceNpAllocateKernelMemoryNoAlignment(std::uint64_t size, const char* name, void** address, NpKernelMemory* memory) {
|
|
if (memory == nullptr) return NP_ERROR_NULL_OBJECT;
|
|
const int result = MapKernelMemory(memory, size, name);
|
|
if (result >= 0 && address != nullptr) *address = memory->address;
|
|
return result;
|
|
}
|
|
|
|
int APS5_VABI sceNpAllocateKernelMemoryWithAlignment(std::uint64_t size, const char* name, void** address, NpKernelMemory* memory) {
|
|
return sceNpAllocateKernelMemoryNoAlignment(AlignKernelMemory(size), name, address, memory);
|
|
}
|
|
|
|
int APS5_VABI sceNpFreeKernelMemory(NpKernelMemory* memory) {
|
|
if (memory == nullptr) return NP_ERROR_NULL_OBJECT;
|
|
UnmapKernelMemory(memory);
|
|
return 0;
|
|
}
|
|
|
|
void APS5_VABI sceNpGetSdkVersionUInt(std::uint32_t* version) {
|
|
const std::uint32_t value = SystemVersion();
|
|
if (version != nullptr) *version = value;
|
|
}
|
|
|
|
int APS5_VABI sceNpRtcConvertToPosixTime(const RtcTick* tick, std::int64_t* seconds) {
|
|
if (tick->tick == 0) return NP_ERROR_INVALID_ARGUMENT;
|
|
RtcDateTime time {};
|
|
const int result = sceRtcSetTick(&time, tick);
|
|
if (result < 0) return result;
|
|
return NpResult(sceRtcGetTime_t(&time, seconds));
|
|
}
|
|
|
|
int APS5_VABI sceNpRtcFormatRFC3339(char* text, std::size_t size, const RtcTick* utc, int timeZoneMinutes) {
|
|
if (size < NP_RFC3339_BUFFER_MIN) return NP_ERROR_BUFFER_TOO_SMALL;
|
|
const int result = sceRtcFormatRFC3339(text, utc, timeZoneMinutes);
|
|
if (result < 0) return result;
|
|
text[size - 1] = 0;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNpUserGetUserIdList(int* users, std::size_t capacity, int* count) {
|
|
if (capacity < NP_USER_ID_LIST_SIZE) return NP_ERROR_INVALID_ARGUMENT;
|
|
int registered[NP_USER_ID_LIST_SIZE];
|
|
std::fill(std::begin(registered), std::end(registered), USER_ID_INVALID);
|
|
static_assert(sizeof(UserServiceRegisteredUserIdList) <= sizeof(registered));
|
|
const int result = sceUserServiceGetRegisteredUserIdList(reinterpret_cast<UserServiceRegisteredUserIdList*>(registered));
|
|
if (result < 0) return result;
|
|
int found = 0;
|
|
for (const int user : registered)
|
|
if (user != USER_ID_INVALID) users[found++] = user;
|
|
*count = found;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNpRtcParseRFC3339(RtcTick* utc, const char* text) {
|
|
return sceRtcParseRFC3339(utc, text);
|
|
}
|
|
|
|
static std::uint64_t ClockUsec() {
|
|
std::int64_t usec = 0;
|
|
sceNpGetSystemClockUsec(&usec);
|
|
|
|
return static_cast<std::uint64_t>(usec);
|
|
}
|
|
|
|
static void* APS5_VABI CalloutThread(void* arg) {
|
|
auto* context = static_cast<NpCalloutContext*>(arg);
|
|
sceNpMutexLock(&context->mutex);
|
|
|
|
for (;;) {
|
|
const std::uint64_t now = ClockUsec();
|
|
NpCallout* head = context->pending;
|
|
if (head != nullptr && head->deadline <= now) {
|
|
context->pending = head->next;
|
|
sceNpMutexUnlock(&context->mutex);
|
|
|
|
head->handler(head->arg);
|
|
sceNpMutexLock(&context->mutex);
|
|
|
|
continue;
|
|
}
|
|
if (context->stopping > 0) break;
|
|
const std::uint64_t wait = head != nullptr ? std::min(head->deadline - now, NP_CALLOUT_MAX_WAIT) : 0;
|
|
sceNpCondTimedwait(&context->cond, &context->mutex, static_cast<std::uint32_t>(wait));
|
|
|
|
if (context->stopping > 0) break;
|
|
}
|
|
sceNpMutexUnlock(&context->mutex);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
int APS5_VABI sceNpCalloutInitCtx(NpCalloutContext* context, const char* name, std::size_t stackSize, int priority,
|
|
KernelCpumask affinity) {
|
|
if (context->initialized > 0) return NP_ERROR_ALREADY_INITIALIZED;
|
|
context->stopping = 0;
|
|
context->pending = nullptr;
|
|
int result = sceNpMutexInit(&context->mutex, name, 1);
|
|
if (result < 0) return result;
|
|
result = sceNpCondInit(&context->cond, name, 0);
|
|
if (result >= 0) {
|
|
result = sceNpCreateThread(&context->thread, CalloutThread, context, priority, stackSize, affinity, name);
|
|
if (result >= 0) {
|
|
context->initialized = 1;
|
|
return 0;
|
|
}
|
|
sceNpCondDestroy(&context->cond);
|
|
|
|
}
|
|
sceNpMutexDestroy(&context->mutex);
|
|
|
|
return result;
|
|
}
|
|
|
|
void APS5_VABI sceNpCalloutTermCtx(NpCalloutContext* context) {
|
|
if (context->initialized == 0) return;
|
|
sceNpMutexLock(&context->mutex);
|
|
|
|
context->stopping = 1;
|
|
sceNpCondSignal(&context->cond);
|
|
sceNpMutexUnlock(&context->mutex);
|
|
sceNpJoinThread(context->thread, nullptr);
|
|
sceNpCondDestroy(&context->cond);
|
|
sceNpMutexDestroy(&context->mutex);
|
|
|
|
context->initialized = 0;
|
|
}
|
|
|
|
int APS5_VABI sceNpCalloutStartOnCtx64(NpCalloutContext* context, NpCallout* callout, std::uint64_t usec,
|
|
void (APS5_VABI *handler)(void* arg), void* arg) {
|
|
if (context->initialized == 0) return NP_ERROR_NOT_INITIALIZED;
|
|
const std::uint64_t deadline = ClockUsec() + usec;
|
|
sceNpMutexLock(&context->mutex);
|
|
|
|
for (NpCallout* entry = context->pending; entry != nullptr; entry = entry->next) {
|
|
if (entry == callout) {
|
|
sceNpMutexUnlock(&context->mutex);
|
|
|
|
return NP_ERROR_ALREADY_QUEUED;
|
|
}
|
|
}
|
|
*callout = {};
|
|
callout->deadline = deadline;
|
|
callout->handler = handler;
|
|
callout->arg = arg;
|
|
NpCallout** link = &context->pending;
|
|
while (*link != nullptr && callout->deadline > (*link)->deadline) link = &(*link)->next;
|
|
callout->next = *link;
|
|
*link = callout;
|
|
sceNpCondSignal(&context->cond);
|
|
sceNpMutexUnlock(&context->mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNpCalloutStartOnCtx(NpCalloutContext* context, NpCallout* callout, std::uint32_t usec,
|
|
void (APS5_VABI *handler)(void* arg), void* arg) {
|
|
return sceNpCalloutStartOnCtx64(context, callout, usec, handler, arg);
|
|
}
|
|
|
|
int APS5_VABI sceNpCalloutStopOnCtx(NpCalloutContext* context, NpCallout* callout, int* wasPending) {
|
|
if (context->initialized == 0) return NP_ERROR_NOT_INITIALIZED;
|
|
sceNpMutexLock(&context->mutex);
|
|
|
|
int removed = 0;
|
|
for (NpCallout** link = &context->pending; *link != nullptr; link = &(*link)->next) {
|
|
if (*link != callout) continue;
|
|
*link = callout->next;
|
|
removed = 1;
|
|
break;
|
|
}
|
|
if (wasPending != nullptr) *wasPending = removed;
|
|
sceNpMutexUnlock(&context->mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
std::uint32_t APS5_VABI sceNpGetRandom(void) {
|
|
std::lock_guard lock(g_randomMutex);
|
|
return static_cast<std::uint32_t>(g_random());
|
|
}
|
|
|
|
int APS5_VABI sceNpUtilCmpAccountId(std::uint64_t left, std::uint64_t right) {
|
|
return left != right ? 1 : 0;
|
|
}
|
|
|
|
int APS5_VABI sceNpCmpNpId(const void* npId1, const void* npId2) {
|
|
if (!npId1 || !npId2) return NP_ERROR_NULL_ARGUMENT;
|
|
const auto* left = static_cast<const std::uint8_t*>(npId1);
|
|
const auto* right = static_cast<const std::uint8_t*>(npId2);
|
|
if (left[NP_ID_TERMINATOR_FLAG_OFFSET] != 1 || right[NP_ID_TERMINATOR_FLAG_OFFSET] != 1) return NP_ERROR_INVALID_NP_ID;
|
|
if (std::strncmp(static_cast<const char*>(npId1), static_cast<const char*>(npId2), NP_ONLINE_ID_MAX) != 0) return NP_ERROR_ONLINE_ID_MISMATCH;
|
|
if (NpIdWord(left, NP_ID_OPT_OFFSET) != NpIdWord(right, NP_ID_OPT_OFFSET)) return NP_ERROR_ONLINE_ID_MISMATCH;
|
|
if (NpIdWord(left, NP_ID_PLATFORM_OFFSET) == NpIdWord(right, NP_ID_PLATFORM_OFFSET)) return 0;
|
|
if (left[NP_ID_PLATFORM_OFFSET] == 0 || right[NP_ID_PLATFORM_OFFSET] == 0) return 0;
|
|
return NP_ERROR_ONLINE_ID_MISMATCH;
|
|
}
|
|
|
|
bool APS5_VABI sceNpIntIsOnlineIdString(const char* text) {
|
|
return text != nullptr && IsOnlineIdText(text);
|
|
}
|
|
|
|
int APS5_VABI sceNpUtilGetNpLanguageCode(int language, char* code) {
|
|
const NpLanguageCodes* codes = FindLanguageCodes(language);
|
|
if (codes == nullptr || code == nullptr) return NP_ERROR_INVALID_ARGUMENT_UTIL;
|
|
CopyText(code, codes->code, NP_LANGUAGE_CODE_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNpUtilGetNpLanguageCode2(int language, char* tag) {
|
|
const NpLanguageCodes* codes = FindLanguageCodes(language);
|
|
if (codes == nullptr || tag == nullptr) return NP_ERROR_INVALID_ARGUMENT_UTIL;
|
|
CopyText(tag, codes->tag, NP_LANGUAGE_CODE2_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
const char* APS5_VABI sceNpUtilGetNpLanguageCodeStr(int language) {
|
|
const NpLanguageCodes* codes = FindLanguageCodes(language);
|
|
return codes != nullptr ? codes->code : nullptr;
|
|
}
|
|
|
|
const char* APS5_VABI sceNpUtilGetNpLanguageCode2Str(int language) {
|
|
const NpLanguageCodes* codes = FindLanguageCodes(language);
|
|
return codes != nullptr ? codes->tag : nullptr;
|
|
}
|
|
|
|
std::uint32_t APS5_VABI sceNpDbgAssignDebugId(void) {
|
|
std::uint32_t id = g_debugIds.fetch_add(1) + 1;
|
|
if (id == 0) id = g_debugIds.fetch_add(1) + 1;
|
|
return id;
|
|
}
|
|
|
|
int APS5_VABI sceNpUtilBuildTitleId(const NpTitleIdParts* parts, char* titleId, std::size_t size) {
|
|
if (parts == nullptr || titleId == nullptr) return NP_ERROR_INVALID_ARGUMENT_UTIL;
|
|
if (size < NP_TITLE_ID_SIZE) return NP_UTIL_ERROR_BUFFER_TOO_SMALL;
|
|
if (parts->index > NP_TITLE_ID_INDEX_MAX) return NP_UTIL_ERROR_OUT_OF_RANGE;
|
|
std::memcpy(titleId, parts->id, sizeof(parts->id));
|
|
titleId[9] = '_';
|
|
titleId[10] = static_cast<char>('0' + parts->index / 10);
|
|
titleId[11] = static_cast<char>('0' + parts->index % 10);
|
|
titleId[12] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
void APS5_VABI sceNpDbgDumpBinary(const void* data, std::size_t size) {
|
|
const auto* bytes = static_cast<const std::uint8_t*>(data);
|
|
for (std::size_t line = 0; line < size; line += NP_DUMP_LINE_BYTES) {
|
|
const std::size_t count = std::min(NP_DUMP_LINE_BYTES, size - line);
|
|
for (std::size_t i = 0; i < NP_DUMP_LINE_BYTES; ++i) {
|
|
if (i < count) {
|
|
std::printf("%02x", bytes[line + i]);
|
|
} else {
|
|
std::printf(" ");
|
|
}
|
|
if ((i & 1) != 0) std::putchar(' ');
|
|
}
|
|
std::putchar(' ');
|
|
for (std::size_t i = 0; i < count; ++i) {
|
|
const std::uint8_t c = bytes[line + i];
|
|
std::putchar(c >= 0x20 && c <= 0x7E ? c : '.');
|
|
}
|
|
std::putchar('\n');
|
|
}
|
|
std::putchar('\n');
|
|
}
|
|
|
|
}
|