Merge branch 'main' into fix/libc-acoshf-domain-fenv

This commit is contained in:
Dean Galvin committed 2026-10-26 17:40:43 +13:00
commit 7461106f53
61 files changed
+2303 -189

No files matched your search

+9
View File
@@ -269,6 +269,15 @@ if(BUILD_TESTING)
set_tests_properties(guest_kernel_sys_sdk_version PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
endif()
add_test_executable(guest_kernel_sys_console_identity_tests tests/GuestKernelSysConsoleIdentity.cpp)
target_include_directories(guest_kernel_sys_console_identity_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_kernel_sys_console_identity_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_console_identity_tests)
add_test(NAME guest_kernel_sys_console_identity COMMAND guest_kernel_sys_console_identity_tests)
if(WIN32)
set_tests_properties(guest_kernel_sys_console_identity PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
endif()
add_test_executable(nid_buffer_validation_tests nid/tests/BufferValidation.cpp)
target_include_directories(nid_buffer_validation_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/nid/include)
target_compile_features(nid_buffer_validation_tests PRIVATE cxx_std_20)
@@ -33,6 +33,8 @@ extern "C" void AgcDriverSuspendPoint_nid_postfix();
extern "C" void AgcDriverSerialProgress_nid_postfix(std::uint64_t* accepted, std::uint64_t* completed);
extern "C" std::size_t AgcDriverAwaitedWaits_nid_postfix(AgcDriver::AwaitedWait* waits, std::size_t capacity);
extern "C" std::uint64_t AgcDriverDispatchesAdoptedAhead_nid_postfix();
extern "C" std::uint64_t AgcDriverDispatchesRejectedAhead_nid_postfix();
extern "C" void AgcDriverSetAheadAdoptHook_nid_postfix(void (*hook)());
extern "C" void AgcDriverRegisterShader_nid_postfix(const Shader* shader);
extern "C" void AgcDriverResolveShaderAbi_nid_postfix(const Shader* shader, std::span<const ShaderRegister> context, std::span<const ShaderRegister> primitive);
extern "C" void AgcDriverResolveGraphicsStagesAbi_nid_postfix(std::span<const Shader* const> stages, std::span<const ShaderRegister> context, std::span<const ShaderRegister> primitive);
@@ -59,6 +59,8 @@ public:
void Shutdown();
void WaitIdle();
std::uint64_t DispatchesAdoptedAhead() const;
std::uint64_t DispatchesRejectedAhead() const;
void SetAheadAdoptHook(void (*hook)());
void CheckFailure();
void ReportFailure(std::exception_ptr error);
void Submit(const Packet* packet, std::uint32_t queue, bool cpuFlip = false);
@@ -317,7 +319,7 @@ private:
std::chrono::steady_clock::time_point resolvedAt{};
};
struct GroupCaptureStats {
std::uint64_t runs = 0, resolved = 0, resolvedHits = 0, adopted = 0, addressMismatches = 0, keyMismatches = 0, failures = 0, stale = 0, labelOverlaps = 0, writtenBefore = 0, deviceRejects = 0, argumentMismatches = 0;
std::uint64_t runs = 0, resolved = 0, resolvedHits = 0, adopted = 0, addressMismatches = 0, keyMismatches = 0, memoryMismatches = 0, failures = 0, stale = 0, labelOverlaps = 0, writtenBefore = 0, deviceRejects = 0, argumentMismatches = 0;
double resolveMs = 0, resolveMaxMs = 0, ageMs = 0, ageMaxMs = 0;
std::chrono::steady_clock::time_point reported = std::chrono::steady_clock::now();
};
@@ -474,6 +476,8 @@ private:
std::atomic<std::uint64_t> evidenceReads{0};
std::atomic<std::uint64_t> evidenceValidations{0};
std::atomic<std::uint64_t> dispatchesAdoptedAhead{0};
std::atomic<std::uint64_t> dispatchesRejectedAhead{0};
std::atomic<void (*)()> aheadAdoptHook{nullptr};
AgcDriver::Mutex writtenBuffersMutex;
std::deque<WrittenBuffer> writtenBuffers;
@@ -16,6 +16,7 @@
#include "Optimization/ResourceProgram.hpp"
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <optional>
#include <stdexcept>
#include <utility>
@@ -57,6 +58,19 @@ void ReportDispatchItems(bool profile) {
std::fprintf(stderr, "[dispatch-item] %llu commit items (%llu recipes rebuilt on the committer); drained instead, cumulative:%s\n", static_cast<unsigned long long>(itemCommits.load(std::memory_order_relaxed)), static_cast<unsigned long long>(itemRebuilds.load(std::memory_order_relaxed)), drained.c_str());
}
bool CapturedCurrent(std::span<const ShaderRecompiler::MemoryRegion> captured, const ShaderSnapshot& code) {
for (const auto& region : captured) {
const auto address = region.guestAddress;
const auto bytes = region.bytes.size();
if (bytes == 0 || (address >= code.codeAddress && address < code.codeAddress + code.code.size() * sizeof(std::uint32_t)) || (!code.header.empty() && address >= code.headerAddress && address < code.headerAddress + code.header.size())) continue;
if (Driver::QueryPendingSpan(address, bytes, {}) != ShaderMemory::PendingWrite::None || !GuestMemory::Accessible(reinterpret_cast<const void*>(address), bytes)) return false;
GuestMemory::GuardRead(address, bytes);
GuestMemory::FlushGpuWrites(address, bytes);
if (std::memcmp(region.bytes.data(), reinterpret_cast<const void*>(address), bytes) != 0) return false;
}
return true;
}
}
bool Driver::pipelineDispatch() {
@@ -281,6 +295,14 @@ void Driver::dispatch(QueueState& queue, std::span<const std::uint32_t> packet,
fromAhead = false;
++groupStats.keyMismatches;
}
if (fromAhead) {
if (const auto hook = aheadAdoptHook.load(std::memory_order_acquire)) hook();
if (!CapturedCurrent(ahead.captured, snapshot)) {
fromAhead = false;
++dispatchesRejectedAhead;
++groupStats.memoryMismatches;
}
}
phaseTiming.Phase(PhaseKey);
timing.Mark("decode_key_device_setup");
bool readsClear = false;
@@ -42,6 +42,14 @@ std::uint64_t Driver::DispatchesAdoptedAhead() const {
return dispatchesAdoptedAhead.load(std::memory_order_relaxed);
}
std::uint64_t Driver::DispatchesRejectedAhead() const {
return dispatchesRejectedAhead.load(std::memory_order_relaxed);
}
void Driver::SetAheadAdoptHook(void (*hook)()) {
aheadAdoptHook.store(hook, std::memory_order_release);
}
namespace {
std::size_t& PreviewedUntil() {
@@ -326,9 +334,9 @@ void Driver::reportGroupCapture(std::uint32_t queue) {
auto& stats = groupCaptureStats();
const auto now = std::chrono::steady_clock::now();
if (now - stats.reported < std::chrono::seconds(10)) return;
AgcDriver::ProfilePrint_nid_no_patch("[groupcap] queue 0x%x (10 s): %llu runs resolved %llu dispatches (%llu cache hits) in %.1f ms (max %.2f ms); %llu adopted %.2f ms after the resolve on average, max %.2f ms; mismatches: address %llu key %llu; %llu resolve failures, %llu stale, %llu label overlaps, %llu read an earlier dispatch's output, %llu rejected by the device, %llu dropped because the argument re-read differed\n",
AgcDriver::ProfilePrint_nid_no_patch("[groupcap] queue 0x%x (10 s): %llu runs resolved %llu dispatches (%llu cache hits) in %.1f ms (max %.2f ms); %llu adopted %.2f ms after the resolve on average, max %.2f ms; mismatches: address %llu key %llu memory %llu; %llu resolve failures, %llu stale, %llu label overlaps, %llu read an earlier dispatch's output, %llu rejected by the device, %llu dropped because the argument re-read differed\n",
queue, static_cast<unsigned long long>(stats.runs), static_cast<unsigned long long>(stats.resolved), static_cast<unsigned long long>(stats.resolvedHits), stats.resolveMs, stats.resolveMaxMs,
static_cast<unsigned long long>(stats.adopted), stats.adopted != 0 ? stats.ageMs / static_cast<double>(stats.adopted) : 0.0, stats.ageMaxMs, static_cast<unsigned long long>(stats.addressMismatches), static_cast<unsigned long long>(stats.keyMismatches),
static_cast<unsigned long long>(stats.adopted), stats.adopted != 0 ? stats.ageMs / static_cast<double>(stats.adopted) : 0.0, stats.ageMaxMs, static_cast<unsigned long long>(stats.addressMismatches), static_cast<unsigned long long>(stats.keyMismatches), static_cast<unsigned long long>(stats.memoryMismatches),
static_cast<unsigned long long>(stats.failures), static_cast<unsigned long long>(stats.stale), static_cast<unsigned long long>(stats.labelOverlaps), static_cast<unsigned long long>(stats.writtenBefore), static_cast<unsigned long long>(stats.deviceRejects), static_cast<unsigned long long>(stats.argumentMismatches));
stats = GroupCaptureStats{};
stats.reported = now;
@@ -157,6 +157,14 @@ extern "C" std::uint64_t AgcDriverDispatchesAdoptedAhead_nid_postfix() {
return AgcDriver::DriverDetail::Driver::Get().DispatchesAdoptedAhead();
}
extern "C" std::uint64_t AgcDriverDispatchesRejectedAhead_nid_postfix() {
return AgcDriver::DriverDetail::Driver::Get().DispatchesRejectedAhead();
}
extern "C" void AgcDriverSetAheadAdoptHook_nid_postfix(void (*hook)()) {
AgcDriver::DriverDetail::Driver::Get().SetAheadAdoptHook(hook);
}
extern "C" void AgcDriverRegisterVideoOutput_nid_postfix(std::uint32_t handle, const std::shared_ptr<AgcDriver::IVideoOutput>& output) {
AgcDriver::RegisterVideoOutput(handle, output);
}
@@ -32,7 +32,7 @@ struct LoadCase {
std::uint32_t components = 1;
};
std::vector<std::uint32_t> emitLoad(const LoadCase& test, bool active, std::uint32_t offset, std::uint32_t records, bool robust) {
std::vector<std::uint32_t> emitLoad(const LoadCase& test, bool active, std::uint32_t offset, std::uint32_t records, bool robust, bool valid = true) {
IrProgram program;
program.SetWaveSize(32);
program.Resources().stage = IrShaderStage::Compute;
@@ -76,7 +76,8 @@ std::vector<std::uint32_t> emitLoad(const LoadCase& test, bool active, std::uint
program.Metadata().bindings.pushDataStartDword = 0;
program.Metadata().bindings.memoryOffsetCount = 1;
auto& size = constant(records);
auto& handle = emit(IrOpcode::GetBufferResource, IrType::BufferResource, {&zero, &zero, &size, &zero});
auto& base = constant(valid ? 0x100u : 0u);
auto& handle = emit(IrOpcode::GetBufferResource, IrType::BufferResource, {&base, &zero, &size, &zero});
emit(test.opcode, test.type, {&handle, &zero, &address, &zero, &enabled});
} else {
if (test.kind == ResourceKind::Gds) program.Metadata().bindings.descriptors.push_back({DescriptorBindingKind::Gds, {0}});
@@ -104,8 +105,8 @@ std::vector<std::uint32_t> emitLoad(const LoadCase& test, bool active, std::uint
class AccessTrace {
public:
AccessTrace(const std::vector<std::uint32_t>& module, std::uint32_t misalignment = 0, bool valid = true)
: words(module), misalignment(misalignment), valid(valid) {
AccessTrace(const std::vector<std::uint32_t>& module, std::uint32_t misalignment = 0)
: words(module), misalignment(misalignment) {
require(words.size() >= 5, "SPIR-V module header is missing");
for (std::size_t offset = 5; offset < words.size();) {
const auto count = words[offset] >> 16;
@@ -206,7 +207,6 @@ private:
const auto spec = specializations.at(id);
if (spec == PipelineSpecialization::BufferBase) return 0;
if (spec == PipelineSpecialization::BufferBase + 1) return 0x30001000;
if (spec == PipelineSpecialization::BufferBase + 2) return valid;
if (spec == PipelineSpecialization::BufferBase + 3) return misalignment;
if (spec == PipelineSpecialization::PushDataOffset) return 0;
throw std::runtime_error("unexpected specialization in access fixture");
@@ -250,7 +250,6 @@ private:
const std::vector<std::uint32_t>& words;
std::uint32_t misalignment;
bool valid;
std::uint32_t entryFunction = 0;
std::map<std::uint32_t, std::span<const std::uint32_t>> definitions;
std::map<std::uint32_t, std::string> names;
@@ -281,8 +280,8 @@ std::uint32_t ldsLength(const std::vector<std::uint32_t>& words) {
}
void expectReads(const LoadCase& test, bool robust, bool active, std::uint32_t offset, std::vector<std::uint32_t> expected, std::uint32_t records = 64, std::uint32_t misalignment = 0, bool valid = true) {
const auto module = emitLoad(test, active, offset, records, robust);
const auto actual = AccessTrace(module, misalignment, valid).Run();
const auto module = emitLoad(test, active, offset, records, robust, valid);
const auto actual = AccessTrace(module, misalignment).Run();
if (actual != expected) {
std::string detail = std::string(robust ? "robust" : "plain") + " guest reads:";
for (const auto index : actual) detail += " " + std::to_string(index);
@@ -0,0 +1,208 @@
#include "VulkanTestDevice.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Submit/include/Acb.hpp"
#include <array>
#include <atomic>
#include <chrono>
#include <cstdint>
#include <initializer_list>
#include <iostream>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>
namespace {
void Require(bool condition, const std::string& message) {
if (!condition) throw std::runtime_error(message);
}
alignas(256) std::array<std::uint32_t, 7> CounterCode{0xf4080200u, 0xfa000000u, 0xbf8cc07fu, 0x7e000281u, 0xe0c80000u, 0x80020000u, 0xbf810000u};
alignas(256) std::array<std::uint32_t, 7> StoreCode{0xf4080200u, 0xfa000000u, 0xbf8cc07fu, 0x7e000202u, 0xe0700000u, 0x80020000u, 0xbf810000u};
alignas(256) std::array<std::uint32_t, 15> SpinCode{0xf4080200u, 0xfa000000u, 0xbe840381u, 0x9304ff04u, 0x0019660du, 0x8004ff04u, 0x3c6ef35fu, 0x80828102u, 0xbf078002u, 0xbf85fff9u, 0xbf8cc07fu, 0x7e000204u, 0xe0700000u, 0x80020000u, 0xbf810000u};
constexpr std::uint32_t Compute = 0x20u;
constexpr std::uint32_t OtherCompute = 0x28u;
constexpr std::uint32_t SpinIterations = 1u << 20u;
alignas(256) std::array<std::uint32_t, 4> Stale{};
alignas(256) std::array<std::uint32_t, 4> Fresh{};
alignas(256) std::array<std::uint32_t, 4> Target{};
alignas(256) std::array<std::uint32_t, 4> TargetDescriptor{};
alignas(256) std::array<std::uint32_t, 4> Spun{};
alignas(256) std::array<std::uint32_t, 4> SpunDescriptor{};
alignas(256) volatile std::uint32_t Gate = 1;
alignas(256) volatile std::uint32_t Done = 0;
alignas(256) volatile std::uint32_t OtherDone = 0;
enum class Window { None, CpuStore, QueuedGpuStore };
std::atomic<Window> Armed{Window::None};
std::uint32_t SpinDispatches = 16;
std::vector<std::uint32_t> OtherWords;
bool SawPending = false;
std::uint32_t Low(const volatile void* pointer) {
return static_cast<std::uint32_t>(reinterpret_cast<std::uintptr_t>(pointer));
}
std::uint32_t High(const volatile void* pointer) {
return static_cast<std::uint32_t>(static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(pointer)) >> 32u);
}
std::array<std::uint32_t, 4> Descriptor(const void* pointer, std::uint32_t bytes) {
return {Low(pointer), High(pointer) & 0xffffu, bytes, 0x31016facu};
}
void SetShaderRegister(std::vector<std::uint32_t>& words, std::uint32_t offset, std::uint32_t value) {
words.insert(words.end(), {0xc0017600u, offset, value});
}
void Program(std::vector<std::uint32_t>& words, const void* code, std::initializer_list<std::uint32_t> userData) {
const auto address = static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(code));
SetShaderRegister(words, 0x20c, static_cast<std::uint32_t>(address >> 8u));
SetShaderRegister(words, 0x20d, static_cast<std::uint32_t>(address >> 40u) & 0xffu);
for (const std::uint32_t offset : {0x207u, 0x208u, 0x209u}) SetShaderRegister(words, offset, 1);
SetShaderRegister(words, 0x212, 0);
SetShaderRegister(words, 0x213, static_cast<std::uint32_t>(userData.size()) << 1u);
std::uint32_t offset = 0x240;
for (const auto value : userData) SetShaderRegister(words, offset++, value);
}
void Dispatch(std::vector<std::uint32_t>& words, const void* code, std::initializer_list<std::uint32_t> userData) {
Program(words, code, userData);
words.insert(words.end(), {0xc0031500u, 1, 1, 1, 0x8041u});
}
void WriteData(std::vector<std::uint32_t>& words, const volatile std::uint32_t* target, std::uint32_t value) {
words.insert(words.end(), {0xc0033700u, 0x00100200u, Low(target), High(target), value});
}
void Submit(std::uint32_t queue, std::vector<std::uint32_t>& words) {
Packet packet{words.data(), static_cast<std::uint32_t>(words.size()), 0, {}};
Require(sceAgcDriverSubmitAcb(queue, &packet) == 0, "compute submit failed");
}
void QueueStoreAhead() {
OtherWords.clear();
for (std::uint32_t index = 0; index < SpinDispatches; ++index) Dispatch(OtherWords, SpinCode.data(), {Low(SpunDescriptor.data()), High(SpunDescriptor.data()), SpinIterations});
Dispatch(OtherWords, StoreCode.data(), {Low(TargetDescriptor.data()), High(TargetDescriptor.data()), Low(Fresh.data())});
WriteData(OtherWords, &OtherDone, 1);
Submit(OtherCompute, OtherWords);
const auto start = std::chrono::steady_clock::now();
while (std::chrono::steady_clock::now() - start < std::chrono::seconds(5) && OtherDone == 0) {
if (AgcDriver::DriverDetail::Driver::QueryPendingSpan(reinterpret_cast<std::uintptr_t>(Target.data()), sizeof(Target), {}) != AgcDriver::ShaderMemory::PendingWrite::None) {
SawPending = true;
return;
}
std::this_thread::yield();
}
}
void BeforeAdoption() {
const auto window = Armed.exchange(Window::None);
if (window == Window::CpuStore) reinterpret_cast<volatile std::uint32_t&>(Target[0]) = Low(Fresh.data());
else if (window == Window::QueuedGpuStore) QueueStoreAhead();
}
void WaitIdle() {
AgcDriverWaitIdle_nid_postfix();
for (const auto* buffer : {&Stale, &Fresh, &Target}) AgcDriver::GuestMemory::FlushGpuWrites(reinterpret_cast<std::uintptr_t>(buffer->data()), sizeof(*buffer));
}
void Reset() {
WaitIdle();
Stale = {};
Fresh = {};
Target = Descriptor(Stale.data(), sizeof(Stale));
Done = 0;
OtherDone = 0;
SawPending = false;
}
void Gated(Window window) {
Reset();
std::vector<std::uint32_t> words;
words.insert(words.end(), {0xc0053c00u, 0x13u, Low(&Gate), High(&Gate), 1u, 0xffffffffu, 0x19u});
Dispatch(words, CounterCode.data(), {Low(Target.data()), High(Target.data())});
WriteData(words, &Done, 1);
Armed = window;
Submit(Compute, words);
WaitIdle();
Armed = Window::None;
Require(Done == 1, "the gated submission did not finish");
}
std::string Counts() {
return "(stale " + std::to_string(Stale[0]) + ", fresh " + std::to_string(Fresh[0]) + ")";
}
}
int main() {
try {
auto device = OpenVulkanTestDevice();
if (!device) return VulkanTestSkipped;
device.reset();
Require(High(Stale.data()) == High(Fresh.data()), "the stale and fresh buffers differ in their high address word");
TargetDescriptor = Descriptor(Target.data(), sizeof(Target));
SpunDescriptor = Descriptor(Spun.data(), sizeof(Spun));
AgcDriverSetAheadAdoptHook_nid_postfix(&BeforeAdoption);
Reset();
std::vector<std::uint32_t> warmUp;
Dispatch(warmUp, CounterCode.data(), {Low(Target.data()), High(Target.data())});
WriteData(warmUp, &Done, 1);
Submit(Compute, warmUp);
std::vector<std::uint32_t> otherWarmUp;
Dispatch(otherWarmUp, SpinCode.data(), {Low(SpunDescriptor.data()), High(SpunDescriptor.data()), SpinIterations});
Dispatch(otherWarmUp, StoreCode.data(), {Low(TargetDescriptor.data()), High(TargetDescriptor.data()), Low(Fresh.data())});
Submit(OtherCompute, otherWarmUp);
WaitIdle();
Require(Done == 1 && Target[0] == Low(Fresh.data()), "the warm-up dispatches did not run");
Gated(Window::None);
Require(Stale[0] == 1 && Fresh[0] == 0, "the gated dispatch without a store in the window did not count into the stale buffer " + Counts());
const auto adopted = AgcDriverDispatchesAdoptedAhead_nid_postfix();
Require(adopted != 0, "the gated dispatch was not resolved ahead and adopted");
auto rejected = AgcDriverDispatchesRejectedAhead_nid_postfix();
Gated(Window::CpuStore);
Require(Fresh[0] == 1 && Stale[0] == 0, "a dispatch resolved ahead whose descriptor the CPU replaced before its adoption used the old descriptor " + Counts());
Require(AgcDriverDispatchesRejectedAhead_nid_postfix() == rejected + 1, "the CPU store in the adoption window was not caught by the adoption check");
std::cout << "cpu store before adoption: passed\n";
bool conclusive = false;
for (int attempt = 0; attempt < 4 && !conclusive; ++attempt, SpinDispatches *= 4) {
rejected = AgcDriverDispatchesRejectedAhead_nid_postfix();
Gated(Window::QueuedGpuStore);
conclusive = SawPending;
if (!conclusive) {
std::cout << "queued gpu store before adoption: the store finished before the adoption with " << SpinDispatches << " spin dispatches, retrying\n";
continue;
}
Require(Fresh[0] == 1 && Stale[0] == 0, "a dispatch resolved ahead whose descriptor a GPU store queued on another queue replaces before its adoption used the old descriptor " + Counts());
Require(AgcDriverDispatchesRejectedAhead_nid_postfix() == rejected + 1, "the queued GPU store in the adoption window was not caught by the adoption check");
}
AgcDriverSetAheadAdoptHook_nid_postfix(nullptr);
if (!conclusive) {
std::cout << "queued gpu store before adoption: never observed the store pending at the adoption\n";
AgcDriverShutdown_nid_postfix();
return VulkanTestSkipped;
}
std::cout << "queued gpu store before adoption: passed\n";
std::cout << "dispatch ahead adopt check test passed\n";
AgcDriverShutdown_nid_postfix();
return 0;
} catch (const std::exception& error) {
std::cerr << error.what() << '\n';
try {
AgcDriverShutdown_nid_postfix();
} catch (const std::exception& shutdownError) {
std::cerr << shutdownError.what() << '\n';
}
return 1;
}
}
@@ -6,6 +6,9 @@
extern "C" {
std::int32_t APS5_VABI FT_Outline_GetInsideBorder_nid_postfix(const FreeType::GuestOutline* outline);
std::int32_t APS5_VABI FT_Outline_GetOutsideBorder_nid_postfix(const FreeType::GuestOutline* outline);
int APS5_VABI FT_Init_FreeType_nid_postfix(FreeType::GuestLibrary** alibrary);
int APS5_VABI FT_Done_FreeType_nid_postfix(FreeType::GuestLibrary* library);
int APS5_VABI FT_New_Face_nid_postfix(FreeType::GuestLibrary* library, const char* filepathname, std::int64_t faceIndex, FreeType::GuestFaceRec** aface);
@@ -0,0 +1,68 @@
#include "prx/libSceFreeType/include/Api.hpp"
#include <algorithm>
#include <bit>
#include <cstdint>
#include <stdexcept>
namespace {
bool Clockwise(const FreeType::GuestOutline* outline) {
if (!outline || outline->n_points == 0) return true;
if (!outline->points) throw std::invalid_argument("FreeType outline border: null points");
auto minimum = outline->points[0];
auto maximum = minimum;
for (unsigned i = 1; i < outline->n_points; ++i) {
const auto point = outline->points[i];
minimum.x = std::min(minimum.x, point.x);
minimum.y = std::min(minimum.y, point.y);
maximum.x = std::max(maximum.x, point.x);
maximum.y = std::max(maximum.y, point.y);
}
if (minimum.x == maximum.x || minimum.y == maximum.y) return false;
constexpr std::int64_t limit = 0x1000000;
if (minimum.x < -limit || minimum.y < -limit || maximum.x > limit || maximum.y > limit) return false;
if (outline->n_contours == 0) return false;
if (!outline->contours) throw std::invalid_argument("FreeType outline border: null contours");
unsigned first = 0;
for (unsigned contour = 0; contour < outline->n_contours; ++contour) {
const unsigned last = outline->contours[contour];
if (last < first || last >= outline->n_points)
throw std::invalid_argument("FreeType outline border: invalid contour endpoint");
first = last + 1;
}
const auto magnitude = [](std::int64_t value) { return static_cast<std::uint32_t>(value < 0 ? -value : value); };
const auto shift = [](std::uint32_t value) { const auto bits = std::bit_width(value); return bits > 15 ? bits - 15 : 0; };
const auto xShift = shift(magnitude(maximum.x) | magnitude(minimum.x));
const auto yShift = shift(static_cast<std::uint32_t>(maximum.y - minimum.y));
std::int64_t area = 0;
first = 0;
for (unsigned contour = 0; contour < outline->n_contours; ++contour) {
const unsigned last = outline->contours[contour];
auto previous = outline->points[last];
previous.x >>= xShift;
previous.y >>= yShift;
for (unsigned i = first; i <= last; ++i) {
auto current = outline->points[i];
current.x >>= xShift;
current.y >>= yShift;
area += (current.y - previous.y) * (current.x + previous.x);
previous = current;
}
first = last + 1;
}
return area < 0;
}
}
extern "C" {
std::int32_t APS5_VABI FT_Outline_GetInsideBorder_nid_postfix(const FreeType::GuestOutline* outline) {
return Clockwise(outline) ? 1 : 0;
}
std::int32_t APS5_VABI FT_Outline_GetOutsideBorder_nid_postfix(const FreeType::GuestOutline* outline) {
return Clockwise(outline) ? 0 : 1;
}
}
@@ -0,0 +1,38 @@
add_library(libSceFreeTypeOl SHARED EXCLUDE_FROM_ALL Export.cpp ../libSceFreeType/src/OutlineBorders.cpp)
target_include_directories(libSceFreeTypeOl PRIVATE ${LIBS_INCLUDE_DIR})
configure_windows_unwind(libSceFreeTypeOl)
if(BUILD_TESTING)
add_test_executable(guest_freetype_outline_border_tests ${LIBS_INCLUDE_DIR}/tests/GuestFreeTypeOutlineBorder.cpp)
target_include_directories(guest_freetype_outline_border_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_freetype_outline_border_tests PRIVATE freetype ${CMAKE_DL_LIBS})
if(WIN32)
target_link_libraries(guest_freetype_outline_border_tests PRIVATE libc)
endif()
configure_windows_unwind(guest_freetype_outline_border_tests)
add_custom_target(freetype_outline_border_patched DEPENDS
${PATCHED_DIR}/libSceFreeTypeOl.prx ${PATCHED_DIR}/libSceFreeTypeOt.prx ${PATCHED_DIR}/libkernel.prx ${LIBC_PATCHED})
add_dependencies(guest_freetype_outline_border_tests freetype_outline_border_patched)
add_test(NAME guest_freetype_outline_border_reference COMMAND guest_freetype_outline_border_tests reference)
foreach(module IN ITEMS Ol Ot)
foreach(border IN ITEMS inside outside)
add_test(NAME guest_freetype_outline_border_${module}_${border}
COMMAND guest_freetype_outline_border_tests $<TARGET_FILE:libSceFreeType${module}> ${border} raw)
add_test(NAME guest_freetype_outline_border_${module}_${border}_patched
COMMAND guest_freetype_outline_border_tests ${PATCHED_DIR}/libSceFreeType${module}.prx ${border} patched)
set_tests_properties(guest_freetype_outline_border_${module}_${border}
guest_freetype_outline_border_${module}_${border}_patched PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_freetype_outline_border_${module}_${border} PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceFreeTypeOl>")
set_tests_properties(guest_freetype_outline_border_${module}_${border}_patched PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:${PATCHED_DIR}")
endif()
endforeach()
endforeach()
set_tests_properties(guest_freetype_outline_border_reference PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_freetype_outline_border_reference PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceFreeTypeOl>")
endif()
endif()
@@ -1 +1,3 @@
#pragma once
#include "prx/libSceFreeType/include/Api.hpp"
@@ -0,0 +1,3 @@
add_library(libSceFreeTypeOt SHARED EXCLUDE_FROM_ALL Export.cpp ../libSceFreeType/src/OutlineBorders.cpp)
target_include_directories(libSceFreeTypeOt PRIVATE ${LIBS_INCLUDE_DIR})
configure_windows_unwind(libSceFreeTypeOt)
+740 -1
View File
@@ -5,6 +5,745 @@
extern "C" {
APS5_DUMMY_FUN
int APS5_VABI sceFreeTypeOtUnknown_UD7IFu7ML2U_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("UD7IFu7ML2U", sceFreeTypeOtUnknown_UD7IFu7ML2U_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_2T7lbS_CSzA_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("2T7lbS+CSzA", sceFreeTypeOtUnknown_2T7lbS_CSzA_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_MGEIQsujKaY_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("MGEIQsujKaY", sceFreeTypeOtUnknown_MGEIQsujKaY_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_mkmgcRnAJog_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("mkmgcRnAJog", sceFreeTypeOtUnknown_mkmgcRnAJog_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_m18bTUNF_0g_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("m18bTUNF-0g", sceFreeTypeOtUnknown_m18bTUNF_0g_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_vTZ34rw1sfg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("vTZ34rw1sfg", sceFreeTypeOtUnknown_vTZ34rw1sfg_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_PvCNusrp9xQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("PvCNusrp9xQ", sceFreeTypeOtUnknown_PvCNusrp9xQ_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_dlM4ASZdcjk_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("dlM4ASZdcjk", sceFreeTypeOtUnknown_dlM4ASZdcjk_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_Ev3BqJ_U1Fg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("Ev3BqJ-U1Fg", sceFreeTypeOtUnknown_Ev3BqJ_U1Fg_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_ZhtAXOoLNyo_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("ZhtAXOoLNyo", sceFreeTypeOtUnknown_ZhtAXOoLNyo_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_73vg_nRH68g_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("73vg+nRH68g", sceFreeTypeOtUnknown_73vg_nRH68g_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_wBKAYO7yEXw_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("wBKAYO7yEXw", sceFreeTypeOtUnknown_wBKAYO7yEXw_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_nSIux4OQyJM_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("nSIux4OQyJM", sceFreeTypeOtUnknown_nSIux4OQyJM_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_Dr3F3qrSQ2c_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("Dr3F3qrSQ2c", sceFreeTypeOtUnknown_Dr3F3qrSQ2c_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_avLzN0tTxLI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("avLzN0tTxLI", sceFreeTypeOtUnknown_avLzN0tTxLI_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_0rKmI1ZbMAc_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("0rKmI1ZbMAc", sceFreeTypeOtUnknown_0rKmI1ZbMAc_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_BBRhYfYBDgU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("BBRhYfYBDgU", sceFreeTypeOtUnknown_BBRhYfYBDgU_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_c1nbr8oSWlg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("c1nbr8oSWlg", sceFreeTypeOtUnknown_c1nbr8oSWlg_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_AVEEb57HhUw_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("AVEEb57HhUw", sceFreeTypeOtUnknown_AVEEb57HhUw_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_x8y0qzLiYOU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("x8y0qzLiYOU", sceFreeTypeOtUnknown_x8y0qzLiYOU_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_VW4GD_TaIUc_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("VW4GD-TaIUc", sceFreeTypeOtUnknown_VW4GD_TaIUc_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_GiuNQjoj4fs_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("GiuNQjoj4fs", sceFreeTypeOtUnknown_GiuNQjoj4fs_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_5qqbXOGlHw4_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("5qqbXOGlHw4", sceFreeTypeOtUnknown_5qqbXOGlHw4_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_4LY5pyZ5IQk_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("4LY5pyZ5IQk", sceFreeTypeOtUnknown_4LY5pyZ5IQk_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_zE0V_0aouZ4_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("zE0V+0aouZ4", sceFreeTypeOtUnknown_zE0V_0aouZ4_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_LsyswTUNTRs_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("LsyswTUNTRs", sceFreeTypeOtUnknown_LsyswTUNTRs_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_AfL_nqNC5fY_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("AfL-nqNC5fY", sceFreeTypeOtUnknown_AfL_nqNC5fY_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_WIvTHYs7ZQQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("WIvTHYs7ZQQ", sceFreeTypeOtUnknown_WIvTHYs7ZQQ_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_yrtsLmTfa48_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("yrtsLmTfa48", sceFreeTypeOtUnknown_yrtsLmTfa48_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_tCMhTh7h8D4_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("tCMhTh7h8D4", sceFreeTypeOtUnknown_tCMhTh7h8D4_nid_no_patch);
int APS5_VABI sceFreeTypeOtNewSize_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("zyezaHweY3U", sceFreeTypeOtNewSize_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_C_PpdS5zris_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("C-PpdS5zris", sceFreeTypeOtUnknown_C_PpdS5zris_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_g8eLxL9VAhU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("g8eLxL9VAhU", sceFreeTypeOtUnknown_g8eLxL9VAhU_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_OeNH41MA2Vs_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("OeNH41MA2Vs", sceFreeTypeOtUnknown_OeNH41MA2Vs_nid_no_patch);
int APS5_VABI sceFreeTypeOtOutlineCopy_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("Bhm8Injl0KA", sceFreeTypeOtOutlineCopy_nid_no_patch);
int APS5_VABI sceFreeTypeOtOutlineDecompose_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("++lgV9pRsO4", sceFreeTypeOtOutlineDecompose_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_BqGJsJcFYaM_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("BqGJsJcFYaM", sceFreeTypeOtUnknown_BqGJsJcFYaM_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_umIqupy8UU0_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("umIqupy8UU0", sceFreeTypeOtUnknown_umIqupy8UU0_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_l6Cf9rb9fZQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("l6Cf9rb9fZQ", sceFreeTypeOtUnknown_l6Cf9rb9fZQ_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_BgUMBGvhATU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("BgUMBGvhATU", sceFreeTypeOtUnknown_BgUMBGvhATU_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_0Aal_TIC4so_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("0Aal-TIC4so", sceFreeTypeOtUnknown_0Aal_TIC4so_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_4RRjK_Wd1lU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("4RRjK-Wd1lU", sceFreeTypeOtUnknown_4RRjK_Wd1lU_nid_no_patch);
int APS5_VABI sceFreeTypeOtOutlineGetOrientation_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("weVsBCT+TTQ", sceFreeTypeOtOutlineGetOrientation_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_QH2emcgeFck_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("QH2emcgeFck", sceFreeTypeOtUnknown_QH2emcgeFck_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_NG7OgYsgcF8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("NG7OgYsgcF8", sceFreeTypeOtUnknown_NG7OgYsgcF8_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_9km02mPqPJE_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("9km02mPqPJE", sceFreeTypeOtUnknown_9km02mPqPJE_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_0Lv6K1d47jg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("0Lv6K1d47jg", sceFreeTypeOtUnknown_0Lv6K1d47jg_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_8fAMdZCnewA_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("8fAMdZCnewA", sceFreeTypeOtUnknown_8fAMdZCnewA_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_LsB538_IPrE_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("LsB538+IPrE", sceFreeTypeOtUnknown_LsB538_IPrE_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_hz_J3hohXW8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("hz-J3hohXW8", sceFreeTypeOtUnknown_hz_J3hohXW8_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_KVbmKCGGowg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("KVbmKCGGowg", sceFreeTypeOtUnknown_KVbmKCGGowg_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_hF3stKUWbWg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("hF3stKUWbWg", sceFreeTypeOtUnknown_hF3stKUWbWg_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_p5GNJaXO2yE_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("p5GNJaXO2yE", sceFreeTypeOtUnknown_p5GNJaXO2yE_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_8cnVx1yH9_I_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("8cnVx1yH9-I", sceFreeTypeOtUnknown_8cnVx1yH9_I_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_HVSaZbxYi2s_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("HVSaZbxYi2s", sceFreeTypeOtUnknown_HVSaZbxYi2s_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_qAPXM5DN62Q_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("qAPXM5DN62Q", sceFreeTypeOtUnknown_qAPXM5DN62Q_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_5ipBcLYVc2k_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("5ipBcLYVc2k", sceFreeTypeOtUnknown_5ipBcLYVc2k_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_8A0cAU4yBVY_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("8A0cAU4yBVY", sceFreeTypeOtUnknown_8A0cAU4yBVY_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_z9Wsaf34JA4_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("z9Wsaf34JA4", sceFreeTypeOtUnknown_z9Wsaf34JA4_nid_no_patch);
int APS5_VABI sceFreeTypeOtRequestSize_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("PaDk9bdmdhU", sceFreeTypeOtRequestSize_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_Z8KG6700sw4_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("Z8KG6700sw4", sceFreeTypeOtUnknown_Z8KG6700sw4_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_uK7vGxAQVxM_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("uK7vGxAQVxM", sceFreeTypeOtUnknown_uK7vGxAQVxM_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_zetC8Tp1S8w_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("zetC8Tp1S8w", sceFreeTypeOtUnknown_zetC8Tp1S8w_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_DAmwG6U7YIc_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("DAmwG6U7YIc", sceFreeTypeOtUnknown_DAmwG6U7YIc_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_Yu9oXRVmTTo_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("Yu9oXRVmTTo", sceFreeTypeOtUnknown_Yu9oXRVmTTo_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_ihIHD8BTNxI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("ihIHD8BTNxI", sceFreeTypeOtUnknown_ihIHD8BTNxI_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_yCEJIsjIOO8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("yCEJIsjIOO8", sceFreeTypeOtUnknown_yCEJIsjIOO8_nid_no_patch);
int APS5_VABI sceFreeTypeOtSin_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("L1uHkWTazkI", sceFreeTypeOtSin_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_C7N2GuwqTA0_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("C7N2GuwqTA0", sceFreeTypeOtUnknown_C7N2GuwqTA0_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_JgtSc0g3G6E_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("JgtSc0g3G6E", sceFreeTypeOtUnknown_JgtSc0g3G6E_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_gVf7xRLdWm8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("gVf7xRLdWm8", sceFreeTypeOtUnknown_gVf7xRLdWm8_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_FHdeCFVf_f8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("FHdeCFVf-f8", sceFreeTypeOtUnknown_FHdeCFVf_f8_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_Qmgc0XACzRg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("Qmgc0XACzRg", sceFreeTypeOtUnknown_Qmgc0XACzRg_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_0rGx_oFMLyk_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("0rGx-oFMLyk", sceFreeTypeOtUnknown_0rGx_oFMLyk_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_thA48N_0VwQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("thA48N-0VwQ", sceFreeTypeOtUnknown_thA48N_0VwQ_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_jl0VxYZb8_o_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("jl0VxYZb8-o", sceFreeTypeOtUnknown_jl0VxYZb8_o_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_Er0Lin_u20o_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("Er0Lin-u20o", sceFreeTypeOtUnknown_Er0Lin_u20o_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_KHj92a3WDn4_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("KHj92a3WDn4", sceFreeTypeOtUnknown_KHj92a3WDn4_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_tDssQCH2syY_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("tDssQCH2syY", sceFreeTypeOtUnknown_tDssQCH2syY_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_dXJK59vXRxs_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("dXJK59vXRxs", sceFreeTypeOtUnknown_dXJK59vXRxs_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_BB4gl7SgLdk_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("BB4gl7SgLdk", sceFreeTypeOtUnknown_BB4gl7SgLdk_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_Fv8MWCUH8k8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("Fv8MWCUH8k8", sceFreeTypeOtUnknown_Fv8MWCUH8k8_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_RzgDJTjQst4_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("RzgDJTjQst4", sceFreeTypeOtUnknown_RzgDJTjQst4_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_4yt9cD1qcEI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("4yt9cD1qcEI", sceFreeTypeOtUnknown_4yt9cD1qcEI_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_u6Xquj1bFwc_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("u6Xquj1bFwc", sceFreeTypeOtUnknown_u6Xquj1bFwc_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown__Xm1cLEpBec_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("-Xm1cLEpBec", sceFreeTypeOtUnknown__Xm1cLEpBec_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_uDXTnGbAz44_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("uDXTnGbAz44", sceFreeTypeOtUnknown_uDXTnGbAz44_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_iruPsrXCN7U_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("iruPsrXCN7U", sceFreeTypeOtUnknown_iruPsrXCN7U_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_a7RlARfexoo_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("a7RlARfexoo", sceFreeTypeOtUnknown_a7RlARfexoo_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_jX1zq1i8KfI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("jX1zq1i8KfI", sceFreeTypeOtUnknown_jX1zq1i8KfI_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_U_0WKVAePww_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("U+0WKVAePww", sceFreeTypeOtUnknown_U_0WKVAePww_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_dlbnv6Wass8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("dlbnv6Wass8", sceFreeTypeOtUnknown_dlbnv6Wass8_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_cmd41395g7Y_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("cmd41395g7Y", sceFreeTypeOtUnknown_cmd41395g7Y_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_SX5I_XzH8HI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("SX5I+XzH8HI", sceFreeTypeOtUnknown_SX5I_XzH8HI_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_Bmi8PdvsM3M_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("Bmi8PdvsM3M", sceFreeTypeOtUnknown_Bmi8PdvsM3M_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_jJh4SMt8NFQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("jJh4SMt8NFQ", sceFreeTypeOtUnknown_jJh4SMt8NFQ_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_1z6bBg1qBfU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("1z6bBg1qBfU", sceFreeTypeOtUnknown_1z6bBg1qBfU_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_mF0B64O954g_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("mF0B64O954g", sceFreeTypeOtUnknown_mF0B64O954g_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_c002A4zTOgA_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("c002A4zTOgA", sceFreeTypeOtUnknown_c002A4zTOgA_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_mcT796Qgj_g_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("mcT796Qgj+g", sceFreeTypeOtUnknown_mcT796Qgj_g_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_b5gZp_r3WWI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("b5gZp-r3WWI", sceFreeTypeOtUnknown_b5gZp_r3WWI_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_S_n0Md8Rjfw_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("S+n0Md8Rjfw", sceFreeTypeOtUnknown_S_n0Md8Rjfw_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_6a1p3QgXvo0_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("6a1p3QgXvo0", sceFreeTypeOtUnknown_6a1p3QgXvo0_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_JFSCOXTEvfg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("JFSCOXTEvfg", sceFreeTypeOtUnknown_JFSCOXTEvfg_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_xejRyEbsLcw_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("xejRyEbsLcw", sceFreeTypeOtUnknown_xejRyEbsLcw_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_R1mZutnlTiM_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("R1mZutnlTiM", sceFreeTypeOtUnknown_R1mZutnlTiM_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_4J4Baf8ana8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("4J4Baf8ana8", sceFreeTypeOtUnknown_4J4Baf8ana8_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_Az4H9FlfxCA_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("Az4H9FlfxCA", sceFreeTypeOtUnknown_Az4H9FlfxCA_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_xj3tNJptfLI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("xj3tNJptfLI", sceFreeTypeOtUnknown_xj3tNJptfLI_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_TPKhRt1QIeE_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("TPKhRt1QIeE", sceFreeTypeOtUnknown_TPKhRt1QIeE_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_6_yXRlVrjbc_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("6+yXRlVrjbc", sceFreeTypeOtUnknown_6_yXRlVrjbc_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown__RLa_6fbshA_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+RLa-6fbshA", sceFreeTypeOtUnknown__RLa_6fbshA_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_pBqh_Uh_gns_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("pBqh-Uh-gns", sceFreeTypeOtUnknown_pBqh_Uh_gns_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_2rcvKZfm5rI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("2rcvKZfm5rI", sceFreeTypeOtUnknown_2rcvKZfm5rI_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_7cIS2cFG3PE_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("7cIS2cFG3PE", sceFreeTypeOtUnknown_7cIS2cFG3PE_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_LfyUi_LkdCk_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("LfyUi-LkdCk", sceFreeTypeOtUnknown_LfyUi_LkdCk_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_rzIWh7FJnmU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("rzIWh7FJnmU", sceFreeTypeOtUnknown_rzIWh7FJnmU_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_cKndiZnt0EA_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("cKndiZnt0EA", sceFreeTypeOtUnknown_cKndiZnt0EA_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_eqGCaeizvWM_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("eqGCaeizvWM", sceFreeTypeOtUnknown_eqGCaeizvWM_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_RvFx7dv9ThU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("RvFx7dv9ThU", sceFreeTypeOtUnknown_RvFx7dv9ThU_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_Po8kyd3DlIw_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("Po8kyd3DlIw", sceFreeTypeOtUnknown_Po8kyd3DlIw_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_B3uU5aDZn94_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("B3uU5aDZn94", sceFreeTypeOtUnknown_B3uU5aDZn94_nid_no_patch);
int APS5_VABI sceFreeTypeOtTan_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("dojyaLKzKN0", sceFreeTypeOtTan_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_08_mhD_AAds_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("08-mhD-AAds", sceFreeTypeOtUnknown_08_mhD_AAds_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_64XMq6sDxUo_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("64XMq6sDxUo", sceFreeTypeOtUnknown_64XMq6sDxUo_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_4xG87W9dTSg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("4xG87W9dTSg", sceFreeTypeOtUnknown_4xG87W9dTSg_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_4_SScYDT5Pg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("4+SScYDT5Pg", sceFreeTypeOtUnknown_4_SScYDT5Pg_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_2rmvbvOCyjY_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("2rmvbvOCyjY", sceFreeTypeOtUnknown_2rmvbvOCyjY_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_1jmC2QcpRl0_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("1jmC2QcpRl0", sceFreeTypeOtUnknown_1jmC2QcpRl0_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_HMcNe70D9TI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("HMcNe70D9TI", sceFreeTypeOtUnknown_HMcNe70D9TI_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_6wJwUIVwAxg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("6wJwUIVwAxg", sceFreeTypeOtUnknown_6wJwUIVwAxg_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_PrZtZyFwM6c_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("PrZtZyFwM6c", sceFreeTypeOtUnknown_PrZtZyFwM6c_nid_no_patch);
int APS5_VABI sceFreeTypeOtVectorUnit_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("3WpTQrvGUcU", sceFreeTypeOtVectorUnit_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_P6WOAeb0lJ8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("P6WOAeb0lJ8", sceFreeTypeOtUnknown_P6WOAeb0lJ8_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_XGVABnpNksQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("XGVABnpNksQ", sceFreeTypeOtUnknown_XGVABnpNksQ_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_Ln9I6YfmYJU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("Ln9I6YfmYJU", sceFreeTypeOtUnknown_Ln9I6YfmYJU_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_7cVMwHmN308_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("7cVMwHmN308", sceFreeTypeOtUnknown_7cVMwHmN308_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_AEU2tkjdZ2w_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("AEU2tkjdZ2w", sceFreeTypeOtUnknown_AEU2tkjdZ2w_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_2lSRg9FfIOY_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("2lSRg9FfIOY", sceFreeTypeOtUnknown_2lSRg9FfIOY_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_fPrLu1YGOaw_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("fPrLu1YGOaw", sceFreeTypeOtUnknown_fPrLu1YGOaw_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_t3vCgnT5YL0_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("t3vCgnT5YL0", sceFreeTypeOtUnknown_t3vCgnT5YL0_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_8bkQ_YRnpOE_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("8bkQ+YRnpOE", sceFreeTypeOtUnknown_8bkQ_YRnpOE_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_q4qhGebYuGI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("q4qhGebYuGI", sceFreeTypeOtUnknown_q4qhGebYuGI_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_ni_K_leoQNg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("ni-K-leoQNg", sceFreeTypeOtUnknown_ni_K_leoQNg_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_XfcgJB6V2a0_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("XfcgJB6V2a0", sceFreeTypeOtUnknown_XfcgJB6V2a0_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_TWzf5iykiGw_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("TWzf5iykiGw", sceFreeTypeOtUnknown_TWzf5iykiGw_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown__b4iVKArt94_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("-b4iVKArt94", sceFreeTypeOtUnknown__b4iVKArt94_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_uSNqxrARpEQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("uSNqxrARpEQ", sceFreeTypeOtUnknown_uSNqxrARpEQ_nid_no_patch);
}
@@ -1 +1,3 @@
#pragma once
#include "prx/libSceFreeType/include/Api.hpp"
+30 -15
View File
@@ -1,4 +1,5 @@
#include "prx/libSceNpGameIntent/include/Api.hpp"
#include <atomic>
#include <cstdint>
#include <cstddef>
#include <cstring>
@@ -8,43 +9,57 @@
namespace {
constexpr int NpGameIntentErrorNotInitialized = -2141898750;
constexpr int NpGameIntentErrorInvalidArgument = -2141898748;
constexpr int NpGameIntentErrorIntentNotFound = -2141898746;
constexpr int NpGameIntentErrorValueNotFound = -2141898745;
constexpr int NpGameIntentUserIdInvalid = -1;
std::atomic_bool& NpGameIntentInitialized() {
static std::atomic_bool initialized{false};
return initialized;
}
}
extern "C" {
int APS5_VABI sceNpGameIntentGetPropertyValueString(const NpGameIntentData* intentData, const char* key, char* valueBuf, size_t bufSize) {
if (intentData == nullptr || key == nullptr || valueBuf == nullptr || bufSize == 0) {
APS5_INVALID_ARG_EX;
}
if (!NpGameIntentInitialized()) {
return NpGameIntentErrorNotInitialized;
}
if (intentData == nullptr || key == nullptr || key[0] == '\0' || valueBuf == nullptr || bufSize == 0) {
return NpGameIntentErrorInvalidArgument;
}
valueBuf[0] = '\0';
return NpGameIntentErrorValueNotFound;
valueBuf[0] = '\0';
return NpGameIntentErrorValueNotFound;
}
int APS5_VABI sceNpGameIntentInitialize(const void* initParam) {
(void)initParam;
return 0;
(void)initParam;
NpGameIntentInitialized() = true;
return 0;
}
int APS5_VABI sceNpGameIntentReceiveIntent(NpGameIntentInfo* intentInfo) {
if (intentInfo == nullptr) {
APS5_INVALID_ARG_EX;
}
if (!NpGameIntentInitialized()) {
return NpGameIntentErrorNotInitialized;
}
if (intentInfo == nullptr || intentInfo->size != 0x4140) {
return NpGameIntentErrorInvalidArgument;
}
intentInfo->user_id = NpGameIntentUserIdInvalid;
std::memset(intentInfo->intent_type, 0, sizeof(intentInfo->intent_type));
std::memset(&intentInfo->intent_data, 0, sizeof(intentInfo->intent_data));
intentInfo->user_id = NpGameIntentUserIdInvalid;
std::memset(intentInfo->intent_type, 0, sizeof(intentInfo->intent_type));
std::memset(&intentInfo->intent_data, 0, sizeof(intentInfo->intent_data));
return NpGameIntentErrorIntentNotFound;
return NpGameIntentErrorIntentNotFound;
}
int APS5_VABI sceNpGameIntentTerminate(void) {
return 0;
NpGameIntentInitialized() = false;
return 0;
}
int APS5_VABI sceNpGameIntentInitParamInit(NpGameIntentInitParam* param) {
+95 -47
View File
@@ -1,6 +1,7 @@
#include "prx/libSceNpUtility/include/Api.hpp"
#include <cstdint>
#include <cstddef>
#include <condition_variable>
#include <cstring>
#include <mutex>
#include <unordered_map>
@@ -16,6 +17,7 @@ static constexpr int SCE_NP_COMMUNITY_ERROR_ABORTED = static_cast<int>(0x8055070
static constexpr int SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT = static_cast<int>(0x8055070C);
static constexpr int SCE_NP_COMMUNITY_ERROR_INVALID_ID = static_cast<int>(0x8055070E);
static constexpr int SCE_NP_COMMUNITY_ERROR_INVALID_TYPE = static_cast<int>(0x80550711);
static constexpr int SCE_NP_LOOKUP_ERROR_SYNCHRONOUS_REQUEST = static_cast<int>(0x80550307);
static constexpr int POLL_FINISHED = 0;
static constexpr int POLL_RUNNING = 1;
static constexpr std::size_t MAX_TITLE_CONTEXTS = 32;
@@ -23,7 +25,9 @@ static constexpr std::size_t MAX_REQUESTS = 32;
static constexpr int WORD_FILTER_TITLE_CTX_ID_OFFSET = 0x01000000;
static constexpr int WORD_FILTER_REQUEST_ID_OFFSET = 0x02000000;
static constexpr std::size_t WORD_FILTER_COMMENT_MAX_LENGTH = 0x400;
static constexpr std::uint64_t WORD_FILTER_ASYNC_PARAMETER_SIZE = 0x18;
static constexpr std::uint64_t ASYNC_REQUEST_PARAMETER_SIZE = 0x18;
static constexpr std::uint32_t RESOLVE_TIMEOUT_MIN = 1000000;
static constexpr std::uint32_t TRANSFER_TIMEOUT_MIN = 10000000;
namespace {
struct Request {
@@ -38,6 +42,7 @@ struct Request {
struct Service {
Service(int firstTitleContext, int firstRequest) : nextTitleContext(firstTitleContext), nextRequest(firstRequest) {}
std::mutex mutex;
std::condition_variable finished;
std::unordered_set<int> titleContexts;
std::unordered_map<int, Request> requests;
int nextTitleContext;
@@ -60,6 +65,7 @@ int DeleteTitleContext(Service& service, int titleCtxId) {
std::lock_guard lock(service.mutex);
if (!service.titleContexts.erase(titleCtxId)) return SCE_NP_COMMUNITY_ERROR_INVALID_ID;
std::erase_if(service.requests, [titleCtxId](const auto& entry) { return entry.second.titleCtxId == titleCtxId; });
service.finished.notify_all();
return 0;
}
@@ -74,7 +80,9 @@ int CreateRequest(Service& service, int titleCtxId, bool async) {
int DeleteRequest(Service& service, int reqId) {
std::lock_guard lock(service.mutex);
return service.requests.erase(reqId) ? 0 : SCE_NP_COMMUNITY_ERROR_INVALID_ID;
if (!service.requests.erase(reqId)) return SCE_NP_COMMUNITY_ERROR_INVALID_ID;
service.finished.notify_all();
return 0;
}
int StartOffline(Service& service, int reqId) {
@@ -86,9 +94,62 @@ int StartOffline(Service& service, int reqId) {
request->second.used = true;
request->second.finished = true;
request->second.result = SCE_NP_ERROR_SIGNED_OUT;
service.finished.notify_all();
return request->second.async ? 0 : SCE_NP_ERROR_SIGNED_OUT;
}
int AbortRequest(Service& service, int reqId) {
std::lock_guard lock(service.mutex);
const auto request = service.requests.find(reqId);
if (request == service.requests.end()) return SCE_NP_COMMUNITY_ERROR_INVALID_ID;
request->second.aborted = true;
if (!request->second.finished) {
request->second.finished = true;
request->second.result = SCE_NP_COMMUNITY_ERROR_ABORTED;
service.finished.notify_all();
}
return 0;
}
int PollAsync(Service& service, int reqId, int* result, int synchronousError) {
if (!result) return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
std::lock_guard lock(service.mutex);
const auto request = service.requests.find(reqId);
if (request == service.requests.end()) return SCE_NP_COMMUNITY_ERROR_INVALID_ID;
if (!request->second.async) return synchronousError;
if (!request->second.finished) return POLL_RUNNING;
*result = request->second.result;
return POLL_FINISHED;
}
int WaitAsync(Service& service, int reqId, int* result, int synchronousError) {
if (!result) return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
std::unique_lock lock(service.mutex);
auto request = service.requests.find(reqId);
if (request == service.requests.end()) return SCE_NP_COMMUNITY_ERROR_INVALID_ID;
if (!request->second.async) return synchronousError;
service.finished.wait(lock, [&] {
request = service.requests.find(reqId);
return request == service.requests.end() || request->second.finished;
});
if (request == service.requests.end()) return SCE_NP_COMMUNITY_ERROR_INVALID_ID;
*result = request->second.result;
return 0;
}
bool ValidTimeout(std::uint32_t timeout, std::uint32_t minimum) {
return timeout == 0 || timeout >= minimum;
}
int SetTimeout(Service& service, int id, int resolveRetry, std::uint32_t resolveTimeout, std::uint32_t connTimeout, std::uint32_t sendTimeout, std::uint32_t recvTimeout) {
if (resolveRetry < 0 || !ValidTimeout(resolveTimeout, RESOLVE_TIMEOUT_MIN) || !ValidTimeout(connTimeout, TRANSFER_TIMEOUT_MIN) ||
!ValidTimeout(sendTimeout, TRANSFER_TIMEOUT_MIN) || !ValidTimeout(recvTimeout, TRANSFER_TIMEOUT_MIN))
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
if (resolveRetry == 0 && resolveTimeout == 0 && connTimeout == 0 && sendTimeout == 0 && recvTimeout == 0) return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
std::lock_guard lock(service.mutex);
return service.titleContexts.contains(id) || service.requests.contains(id) ? 0 : SCE_NP_COMMUNITY_ERROR_INVALID_ID;
}
int CheckComment(const char* comment) {
return strnlen(comment, WORD_FILTER_COMMENT_MAX_LENGTH + 1) > WORD_FILTER_COMMENT_MAX_LENGTH ? SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT : 0;
}
@@ -237,14 +298,6 @@ int APS5_VABI sceNpBandwidthTestShutdownWithDetailedInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpLookupAbortRequest() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpLookupCreateAsyncRequest() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpLookupCreateTitleCtxA() {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -313,18 +366,6 @@ int APS5_VABI sceNpLookupNetTerm() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpLookupPollAsync() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpLookupSetTimeout() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpLookupWaitAsync() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpServiceChecker2IntAbortRequest() {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -421,14 +462,6 @@ int APS5_VABI sceNpWordFilterCreateTitleCtxA() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpWordFilterSetTimeout() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpWordFilterWaitAsync() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}
extern "C" {
@@ -438,6 +471,15 @@ int APS5_VABI sceNpBandwidthTestInitStart() {
return 0;
}
int APS5_VABI sceNpLookupAbortRequest(int reqId) {
return AbortRequest(lookup, reqId);
}
int APS5_VABI sceNpLookupCreateAsyncRequest(int titleCtxId, const std::uint64_t* param) {
if (!param || *param != ASYNC_REQUEST_PARAMETER_SIZE) return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
return CreateRequest(lookup, titleCtxId, true);
}
int APS5_VABI sceNpLookupCreateRequest(int titleCtxId) {
return CreateRequest(lookup, titleCtxId, false);
}
@@ -460,16 +502,20 @@ int APS5_VABI sceNpLookupNpId(int reqId, const NpOnlineId* onlineId, void* npId,
return StartOffline(lookup, reqId);
}
int APS5_VABI sceNpLookupPollAsync(int reqId, int* result) {
return PollAsync(lookup, reqId, result, SCE_NP_LOOKUP_ERROR_SYNCHRONOUS_REQUEST);
}
int APS5_VABI sceNpLookupSetTimeout(int id, int resolveRetry, std::uint32_t resolveTimeout, std::uint32_t connTimeout, std::uint32_t sendTimeout, std::uint32_t recvTimeout) {
return SetTimeout(lookup, id, resolveRetry, resolveTimeout, connTimeout, sendTimeout, recvTimeout);
}
int APS5_VABI sceNpLookupWaitAsync(int reqId, int* result) {
return WaitAsync(lookup, reqId, result, SCE_NP_LOOKUP_ERROR_SYNCHRONOUS_REQUEST);
}
int APS5_VABI sceNpWordFilterAbortRequest(int reqId) {
std::lock_guard lock(wordFilter.mutex);
const auto request = wordFilter.requests.find(reqId);
if (request == wordFilter.requests.end()) return SCE_NP_COMMUNITY_ERROR_INVALID_ID;
request->second.aborted = true;
if (!request->second.finished) {
request->second.finished = true;
request->second.result = SCE_NP_COMMUNITY_ERROR_ABORTED;
}
return 0;
return AbortRequest(wordFilter, reqId);
}
int APS5_VABI sceNpWordFilterCensorComment(int reqId, const char* comment, void* option) {
@@ -480,7 +526,7 @@ int APS5_VABI sceNpWordFilterCensorComment(int reqId, const char* comment, void*
}
int APS5_VABI sceNpWordFilterCreateAsyncRequest(int titleCtxId, const std::uint64_t* param) {
if (!param || *param != WORD_FILTER_ASYNC_PARAMETER_SIZE) return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
if (!param || *param != ASYNC_REQUEST_PARAMETER_SIZE) return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
return CreateRequest(wordFilter, titleCtxId, true);
}
@@ -501,13 +547,7 @@ int APS5_VABI sceNpWordFilterDeleteTitleCtx(int titleCtxId) {
}
int APS5_VABI sceNpWordFilterPollAsync(int reqId, int* result) {
if (!result) return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
std::lock_guard lock(wordFilter.mutex);
const auto request = wordFilter.requests.find(reqId);
if (request == wordFilter.requests.end() || !request->second.async) return SCE_NP_COMMUNITY_ERROR_INVALID_ID;
if (!request->second.finished) return POLL_RUNNING;
*result = request->second.result;
return POLL_FINISHED;
return PollAsync(wordFilter, reqId, result, SCE_NP_COMMUNITY_ERROR_INVALID_ID);
}
int APS5_VABI sceNpWordFilterSanitizeComment(int reqId, const char* comment, char* sanitizedComment, void* option) {
@@ -517,4 +557,12 @@ int APS5_VABI sceNpWordFilterSanitizeComment(int reqId, const char* comment, cha
return StartOffline(wordFilter, reqId);
}
int APS5_VABI sceNpWordFilterSetTimeout(int id, int resolveRetry, std::uint32_t resolveTimeout, std::uint32_t connTimeout, std::uint32_t sendTimeout, std::uint32_t recvTimeout) {
return SetTimeout(wordFilter, id, resolveRetry, resolveTimeout, connTimeout, sendTimeout, recvTimeout);
}
int APS5_VABI sceNpWordFilterWaitAsync(int reqId, int* result) {
return WaitAsync(wordFilter, reqId, result, SCE_NP_COMMUNITY_ERROR_INVALID_ID);
}
}
@@ -23,6 +23,13 @@ int APS5_VABI sceNpWordFilterCreateTitleCtx(const void* selfNpId);
int APS5_VABI sceNpWordFilterDeleteRequest(int reqId);
int APS5_VABI sceNpWordFilterDeleteTitleCtx(int titleCtxId);
int APS5_VABI sceNpWordFilterPollAsync(int reqId, int* result);
int APS5_VABI sceNpLookupAbortRequest(int reqId);
int APS5_VABI sceNpLookupCreateAsyncRequest(int titleCtxId, const std::uint64_t* param);
int APS5_VABI sceNpLookupPollAsync(int reqId, int* result);
int APS5_VABI sceNpLookupSetTimeout(int id, int resolveRetry, std::uint32_t resolveTimeout, std::uint32_t connTimeout, std::uint32_t sendTimeout, std::uint32_t recvTimeout);
int APS5_VABI sceNpLookupWaitAsync(int reqId, int* result);
int APS5_VABI sceNpWordFilterSetTimeout(int id, int resolveRetry, std::uint32_t resolveTimeout, std::uint32_t connTimeout, std::uint32_t sendTimeout, std::uint32_t recvTimeout);
int APS5_VABI sceNpWordFilterWaitAsync(int reqId, int* result);
int APS5_VABI sceNpWordFilterSanitizeComment(int reqId, const char* comment, char* sanitizedComment, void* option);
int APS5_VABI sceNpAppInfoIntAbortRequest();
@@ -53,8 +60,6 @@ int APS5_VABI sceNpAppLaunchLinkIntGetCompatibleTitleIdList();
int APS5_VABI sceNpAppLaunchLinkIntGetCompatibleTitleIdNum();
int APS5_VABI sceNpAppLaunchLinkIntInitialize();
int APS5_VABI sceNpBandwidthTestShutdownWithDetailedInfo();
int APS5_VABI sceNpLookupAbortRequest();
int APS5_VABI sceNpLookupCreateAsyncRequest();
int APS5_VABI sceNpLookupCreateTitleCtxA();
int APS5_VABI sceNpLookupNetAbortRequest();
int APS5_VABI sceNpLookupNetCensorComment();
@@ -72,9 +77,6 @@ int APS5_VABI sceNpLookupNetNpId();
int APS5_VABI sceNpLookupNetSanitizeComment();
int APS5_VABI sceNpLookupNetSetTimeout();
int APS5_VABI sceNpLookupNetTerm();
int APS5_VABI sceNpLookupPollAsync();
int APS5_VABI sceNpLookupSetTimeout();
int APS5_VABI sceNpLookupWaitAsync();
int APS5_VABI sceNpServiceChecker2IntAbortRequest();
int APS5_VABI sceNpServiceChecker2IntCreateRequest();
int APS5_VABI sceNpServiceChecker2IntDestroyRequest();
@@ -99,7 +101,5 @@ int APS5_VABI sceNpTitleMetadataIntGetNpTitleId();
int APS5_VABI sceNpUtilityInit();
int APS5_VABI sceNpUtilityTerm();
int APS5_VABI sceNpWordFilterCreateTitleCtxA();
int APS5_VABI sceNpWordFilterSetTimeout();
int APS5_VABI sceNpWordFilterWaitAsync();
}
@@ -38,34 +38,8 @@ int APS5_VABI sceVideoOutCursorSetPosition(int32_t, int32_t, uint32_t, uint32_t)
return 0;
}
int APS5_VABI sceVideoOutAdjustColor_(int handle, const VideoOutColorSettings* settings, uint32_t settings_size) {
if (settings_size < sizeof(VideoOutColorSettings)) throw std::runtime_error("sceVideoOutAdjustColor_: VIDEO_OUT_ERROR_INVALID_VALUE");
return sceVideoOutAdjustColor(handle, settings);
}
int APS5_VABI sceVideoOutColorSettingsSetGamma_(VideoOutColorSettings* settings, float gamma, uint32_t settings_size) {
if (settings_size < sizeof(VideoOutColorSettings)) throw std::runtime_error("sceVideoOutColorSettingsSetGamma_: VIDEO_OUT_ERROR_INVALID_VALUE");
return sceVideoOutColorSettingsSetGamma(settings, gamma);
}
int APS5_VABI sceVideoOutColorSpaceConversionSettingsSetDefaultSdrToHdrConversion_(VideoOutColorSpaceConversionSettings* settings, uint32_t settings_size) {
if (settings_size < sizeof(VideoOutColorSpaceConversionSettings)) throw std::runtime_error("sceVideoOutColorSpaceConversionSettingsSetDefaultSdrToHdrConversion_: VIDEO_OUT_ERROR_INVALID_VALUE");
return sceVideoOutColorSpaceConversionSettingsSetDefaultSdrToHdrConversion(settings);
}
int APS5_VABI sceVideoOutColorSpaceConversionSettingsSetSdrToHdrConversion_(VideoOutColorSpaceConversionSettings* settings, float lumaScale, uint32_t option, uint32_t settings_size) {
if (settings_size < sizeof(VideoOutColorSpaceConversionSettings)) throw std::runtime_error("sceVideoOutColorSpaceConversionSettingsSetSdrToHdrConversion_: VIDEO_OUT_ERROR_INVALID_VALUE");
return sceVideoOutColorSpaceConversionSettingsSetSdrToHdrConversion(settings, lumaScale, option);
}
int APS5_VABI sceVideoOutAdjustColorSpaceConversion_(int handle, const VideoOutColorSpaceConversionSettings* settings, uint32_t settings_size) {
if (settings_size < sizeof(VideoOutColorSpaceConversionSettings)) throw std::runtime_error("sceVideoOutAdjustColorSpaceConversion_: VIDEO_OUT_ERROR_INVALID_VALUE");
return sceVideoOutAdjustColorSpaceConversion(handle, settings);
}
int APS5_VABI sceDbgVideoOutAddOutputModeVr60Privilege() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}
@@ -371,6 +371,31 @@ int APS5_VABI sceVideoOutAdjustColorSpaceConversion(int handle, const VideoOutCo
LibcAwaitExit_nid_postfix();
}
int APS5_VABI sceVideoOutAdjustColor_(int handle, const VideoOutColorSettings* settings, uint32_t settings_size) {
if (settings_size < sizeof(VideoOutColorSettings)) throw std::runtime_error("sceVideoOutAdjustColor_: VIDEO_OUT_ERROR_INVALID_VALUE");
return sceVideoOutAdjustColor(handle, settings);
}
int APS5_VABI sceVideoOutColorSettingsSetGamma_(VideoOutColorSettings* settings, float gamma, uint32_t settings_size) {
if (settings_size < sizeof(VideoOutColorSettings)) throw std::runtime_error("sceVideoOutColorSettingsSetGamma_: VIDEO_OUT_ERROR_INVALID_VALUE");
return sceVideoOutColorSettingsSetGamma(settings, gamma);
}
int APS5_VABI sceVideoOutColorSpaceConversionSettingsSetDefaultSdrToHdrConversion_(VideoOutColorSpaceConversionSettings* settings, uint32_t settings_size) {
if (settings_size < sizeof(VideoOutColorSpaceConversionSettings)) throw std::runtime_error("sceVideoOutColorSpaceConversionSettingsSetDefaultSdrToHdrConversion_: VIDEO_OUT_ERROR_INVALID_VALUE");
return sceVideoOutColorSpaceConversionSettingsSetDefaultSdrToHdrConversion(settings);
}
int APS5_VABI sceVideoOutColorSpaceConversionSettingsSetSdrToHdrConversion_(VideoOutColorSpaceConversionSettings* settings, float lumaScale, uint32_t option, uint32_t settings_size) {
if (settings_size < sizeof(VideoOutColorSpaceConversionSettings)) throw std::runtime_error("sceVideoOutColorSpaceConversionSettingsSetSdrToHdrConversion_: VIDEO_OUT_ERROR_INVALID_VALUE");
return sceVideoOutColorSpaceConversionSettingsSetSdrToHdrConversion(settings, lumaScale, option);
}
int APS5_VABI sceVideoOutAdjustColorSpaceConversion_(int handle, const VideoOutColorSpaceConversionSettings* settings, uint32_t settings_size) {
if (settings_size < sizeof(VideoOutColorSpaceConversionSettings)) throw std::runtime_error("sceVideoOutAdjustColorSpaceConversion_: VIDEO_OUT_ERROR_INVALID_VALUE");
return sceVideoOutAdjustColorSpaceConversion(handle, settings);
}
int APS5_VABI sceVideoOutSetGlobalBlendSpace(int handle, uint32_t space, uint32_t option) try {
auto cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
@@ -2,6 +2,7 @@
#include "prx/libSceVideoOut/include/VideoOutDriver.hpp"
#include "prx/libSceVideoOut/include/Buffer.hpp"
#include "prx/libSceVideoOut/include/Output.hpp"
#include "prx/libSceVideoOut/include/VideoOut.hpp"
#include "prx/libSceVideoOut/include/Event.hpp"
#include "prx/libkernel/Equeue/Equeue.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver.hpp"
@@ -445,6 +446,22 @@ void testControls() {
}
VideoOutColorSettings settings{};
expectFailure([&] { sceVideoOutColorSettingsSetGamma(&settings, std::numeric_limits<float>::quiet_NaN()); });
expectFailure([&] { sceVideoOutColorSettingsSetGamma_(&settings, 1.0f, sizeof(VideoOutColorSettings) - 1); });
check(sceVideoOutColorSettingsSetGamma_(&settings, 1.2f, sizeof(VideoOutColorSettings)) == 0, "gamma wrapper rejected valid size");
check(settings.gamma == 1.2f, "gamma wrapper failed to forward value");
expectFailure([&] { sceVideoOutAdjustColor_(handle, &settings, sizeof(VideoOutColorSettings) - 1); });
check(sceVideoOutAdjustColor_(handle, &settings, sizeof(VideoOutColorSettings)) == 0, "adjust color wrapper rejected valid size");
VideoOutColorSpaceConversionSettings cscSettings{};
expectFailure([&] { sceVideoOutColorSpaceConversionSettingsSetDefaultSdrToHdrConversion_(&cscSettings, sizeof(VideoOutColorSpaceConversionSettings) - 1); });
check(sceVideoOutColorSpaceConversionSettingsSetDefaultSdrToHdrConversion_(&cscSettings, sizeof(VideoOutColorSpaceConversionSettings)) == 0, "default SDR to HDR wrapper rejected valid size");
expectFailure([&] { sceVideoOutColorSpaceConversionSettingsSetSdrToHdrConversion_(&cscSettings, 1.0f, 0, sizeof(VideoOutColorSpaceConversionSettings) - 1); });
check(sceVideoOutColorSpaceConversionSettingsSetSdrToHdrConversion_(&cscSettings, 1.0f, 0, sizeof(VideoOutColorSpaceConversionSettings)) == 0, "SDR to HDR conversion wrapper rejected valid size");
expectFailure([&] { sceVideoOutAdjustColorSpaceConversion_(handle, &cscSettings, sizeof(VideoOutColorSpaceConversionSettings) - 1); });
check(sceVideoOutAdjustColorSpaceConversion_(handle, &cscSettings, sizeof(VideoOutColorSpaceConversionSettings)) == 0, "adjust color space conversion wrapper rejected valid size");
KernelEqueue queue = 0;
check(sceKernelCreateEqueue(&queue, "VideoOut test") == 0, "event queue creation failed");
auto owner = EqueuePin_nid_postfix(queue);
+3 -3
View File
@@ -329,18 +329,18 @@ constexpr auto g_lowerTable = MakeCaseTable(false);
constexpr auto g_upperTable = MakeCaseTable(true);
void ValidateCharacter(int value) {
if (value != EOF && (value < 0 || value > UCHAR_MAX)) throw std::invalid_argument("Invalid character value");
if (value < SCHAR_MIN || value > UCHAR_MAX) throw std::invalid_argument("Invalid character value");
}
int ClassifyCharacter(int value, short mask) {
ValidateCharacter(value);
if (value == EOF) return 0;
return g_classificationTable[static_cast<std::size_t>(value) + 1] & mask;
return g_classificationTable[static_cast<std::size_t>(value < 0 ? value + UCHAR_MAX + 1 : value) + 1] & mask;
}
int ConvertCharacter(int value, bool upper) {
ValidateCharacter(value);
if (value == EOF) return EOF;
if (value < 0) return value;
const auto& facet = std::use_facet<std::ctype<char>>(std::locale::classic());
const auto character = static_cast<char>(value);
return static_cast<unsigned char>(upper ? facet.toupper(character) : facet.tolower(character));
+8 -8
View File
@@ -416,9 +416,9 @@ int APS5_VABI __isfinitef_nid_postfix(float x) { return std::isfinite(x) ? 1 : 0
int APS5_VABI __isnormal_nid_postfix(double x) { return std::isnormal(x) ? 1 : 0; }
int APS5_VABI __isnormalf_nid_postfix(float x) { return std::isnormal(x) ? 1 : 0; }
int APS5_VABI __isinff_nid_postfix(float x) { return std::isinf(x) ? 1 : 0; }
float APS5_VABI nearbyintf_nid_postfix(float x) { return std::nearbyintf(x); }
float APS5_VABI nearbyintf_nid_postfix(float x) { return std::isnan(x) ? x + x : std::nearbyintf(x); }
long double APS5_VABI nearbyintl_nid_postfix(long double x) { return std::nearbyint(x); }
double APS5_VABI nearbyint_nid_postfix(double x) { return std::nearbyint(x); }
double APS5_VABI nearbyint_nid_postfix(double x) { return std::isnan(x) ? x + x : std::nearbyint(x); }
double APS5_VABI cbrt_nid_postfix(double x) { return std::cbrt(x); }
double APS5_VABI asin_nid_postfix(double x) {
@@ -441,12 +441,12 @@ float APS5_VABI copysignf_nid_postfix(float x, float y) {
return std::copysign(x, y);
}
int APS5_VABI abs_nid_postfix(int x) { return x < 0 ? static_cast<int>(0u - static_cast<unsigned>(x)) : x; }
double APS5_VABI trunc_nid_postfix(double x) { return std::trunc(x); }
double APS5_VABI ceil_nid_postfix(double x) { return std::ceil(x); }
double APS5_VABI floor_nid_postfix(double x) { return std::floor(x); }
float APS5_VABI truncf_nid_postfix(float x) { return std::trunc(x); }
float APS5_VABI ceilf_nid_postfix(float x) { return std::ceil(x); }
float APS5_VABI floorf_nid_postfix(float x) { return std::floor(x); }
double APS5_VABI trunc_nid_postfix(double x) { return std::isnan(x) ? x + x : std::trunc(x); }
double APS5_VABI ceil_nid_postfix(double x) { return std::isnan(x) ? x + x : std::ceil(x); }
double APS5_VABI floor_nid_postfix(double x) { return std::isnan(x) ? x + x : std::floor(x); }
float APS5_VABI truncf_nid_postfix(float x) { return std::isnan(x) ? x + x : std::trunc(x); }
float APS5_VABI ceilf_nid_postfix(float x) { return std::isnan(x) ? x + x : std::ceil(x); }
float APS5_VABI floorf_nid_postfix(float x) { return std::isnan(x) ? x + x : std::floor(x); }
double APS5_VABI fma_nid_postfix(double x, double y, double z) { return std::fma(x, y, z); }
float APS5_VABI fmaf_nid_postfix(float x, float y, float z) { return std::fma(x, y, z); }
double APS5_VABI fmin_nid_postfix(double x, double y) { return GuestFmin(x, y); }
+5
View File
@@ -2004,6 +2004,11 @@ if(BUILD_TESTING)
add_test(NAME guest_raise_in_system_call COMMAND guest_raise_in_system_call_tests)
set_tests_properties(guest_raise_in_system_call PROPERTIES TIMEOUT 30 ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
foreach(delay IN ITEMS raise return)
add_test(NAME guest_raise_in_system_call_delay_${delay} COMMAND guest_raise_in_system_call_tests --delay-${delay})
set_tests_properties(guest_raise_in_system_call_delay_${delay} PROPERTIES TIMEOUT 30 ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endforeach()
add_test_executable(guest_raise_in_host_code_tests ${LIBS_INCLUDE_DIR}/tests/GuestRaiseInHostCode.cpp)
target_include_directories(guest_raise_in_host_code_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_raise_in_host_code_tests PRIVATE libkernel libc)
+2
View File
@@ -99,5 +99,7 @@ inline constexpr int SCE_KERNEL_ERROR_ENOLINK = SceKernelError(91);
inline constexpr int SCE_KERNEL_ERROR_EPROTO = SceKernelError(92);
inline constexpr int SCE_KERNEL_ERROR_ENOTCAPABLE = SceKernelError(93);
inline constexpr int SCE_KERNEL_ERROR_ECAPMODE = SceKernelError(94);
inline constexpr int SCE_KERNEL_ERROR_ENOTRECOVERABLE = SceKernelError(95);
inline constexpr int SCE_KERNEL_ERROR_EOWNERDEAD = SceKernelError(96);
#endif
+4 -4
View File
@@ -36,15 +36,13 @@ const void* imageContaining(const void* address, bool reference, void*& handle,
#ifdef _WIN32
const DWORD flags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | (reference ? 0 : GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT);
HMODULE module = nullptr;
if (!GetModuleHandleExW(flags, reinterpret_cast<LPCWSTR>(address), &module))
throw std::runtime_error(std::string(caller) + ": address outside every loaded image");
if (!GetModuleHandleExW(flags, reinterpret_cast<LPCWSTR>(address), &module)) return nullptr;
if (reference) handle = module;
return module;
#else
Dl_info info{};
link_map* image = nullptr;
if (!dladdr1(address, &info, reinterpret_cast<void**>(&image), RTLD_DL_LINKMAP) || !image)
throw std::runtime_error(std::string(caller) + ": address outside every loaded image");
if (!dladdr1(address, &info, reinterpret_cast<void**>(&image), RTLD_DL_LINKMAP) || !image) return nullptr;
if (reference && image->l_name && *image->l_name) {
handle = dlopen(image->l_name, RTLD_LAZY | RTLD_NOLOAD);
if (!handle) throw std::runtime_error(std::string(caller) + ": cannot reference " + image->l_name);
@@ -56,6 +54,7 @@ const void* imageContaining(const void* address, bool reference, void*& handle,
int referenceThreadAtexitImage(const void* address, const char* caller) {
void* handle = nullptr;
const void* image = imageContaining(address, true, handle, caller);
if (!image) return -1;
std::lock_guard lock(threadAtexitMutex);
threadAtexitReferences.emplace(image, handle);
return 0;
@@ -64,6 +63,7 @@ int referenceThreadAtexitImage(const void* address, const char* caller) {
int releaseThreadAtexitImage(const void* address, const char* caller) {
void* handle = nullptr;
const void* image = imageContaining(address, false, handle, caller);
if (!image) return 0;
{
std::lock_guard lock(threadAtexitMutex);
const auto found = threadAtexitReferences.find(image);
@@ -69,11 +69,6 @@ int APS5_VABI futimesat_nid_postfix(void) {
return 0;
}
int APS5_VABI get_authinfo_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI get_module_info_list_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -979,11 +974,6 @@ int APS5_VABI sceKernelGetMemoryPstate(void) {
return 0;
}
int APS5_VABI sceKernelGetOpenPsIdForSystem(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceKernelGetPagingStatsOfAllObjects(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -1405,21 +1395,11 @@ int APS5_VABI sceKernelIsAuthenticNeo(void) {
return 1;
}
int APS5_VABI sceKernelIsCEX(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceKernelIsDebuggerAttached(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceKernelIsDevKit(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceKernelIsExperimentalBeta(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
+23 -3
View File
@@ -62,6 +62,7 @@ static int GuestFstat(int fd, bool* withoutVnode = nullptr) {
constexpr int GuestEnoent = 2;
constexpr int GuestEperm = 1;
constexpr int GuestEsrch = 3;
constexpr int GuestEio = 5;
constexpr int GuestEbadf = 9;
constexpr int GuestEfault = 14;
@@ -337,6 +338,27 @@ std::uint16_t APS5_VABI umask_nid_postfix(std::uint16_t mask) {
return File::ExchangeCreationMask_nid_no_patch(mask);
}
int APS5_VABI sceKernelGetOpenPsIdForSystem(void* id) {
if (id == nullptr) return GuestEinval;
return sceKernelGetOpenPsId(id);
}
int APS5_VABI sceKernelIsCEX(void) {
return 1;
}
int APS5_VABI sceKernelIsDevKit(void) {
return 0;
}
int APS5_VABI get_authinfo_nid_postfix(int pid, KernelAuthInfo* info) {
if (info == nullptr) return static_cast<int>(Fail(GuestEperm));
if (pid != 0 && pid != getpid_nid_postfix()) return static_cast<int>(Fail(GuestEsrch));
*info = {};
info->capabilities[0] = 0x2000000000000000;
return 0;
}
int APS5_VABI fstatfs_nid_postfix(int d, KernelStatfs* buf) {
return _fstatfs_nid_postfix(d, buf);
}
@@ -725,15 +747,13 @@ int APS5_VABI lchmod_nid_postfix(const char* path, std::uint16_t mode) {
}
int APS5_VABI fchown_nid_postfix(int fd, std::uint32_t uid, std::uint32_t gid) {
constexpr std::uint32_t Unchanged = 0xFFFFFFFFu;
if (uid != 0 && uid != Unchanged) NotImplemented_nid_no_patch("fchown: an owner other than the guest user");
if (gid != 0 && gid != Unchanged) NotImplemented_nid_no_patch("fchown: a group other than the guest group");
#ifdef _WIN32
if (GuestFstat(fd) != 0) return static_cast<int>(Fail(GuestEbadf));
#else
struct stat status{};
if (::fstat(fd, &status) != 0) return static_cast<int>(Fail(GuestEbadf));
#endif
RequireGuestOwner("fchown", uid, gid);
return 0;
}
+10 -2
View File
@@ -24,6 +24,14 @@ struct KernelTitleWorkaround {
std::uint64_t ids[2];
};
struct KernelAuthInfo {
std::uint64_t programAuthorityId;
std::uint64_t capabilities[4];
std::uint64_t attributes[4];
std::uint64_t credentials[8];
};
static_assert(sizeof(KernelAuthInfo) == 0x88);
extern "C" {
int APS5_VABI aio_read_nid_postfix(GuestAiocb* aiocb);
@@ -81,7 +89,7 @@ int APS5_VABI cpuset_getid_nid_postfix(void);
int APS5_VABI cpuset_setid_nid_postfix(void);
int APS5_VABI dynlib_get_obj_member_nid_postfix(void);
int APS5_VABI futimesat_nid_postfix(void);
int APS5_VABI get_authinfo_nid_postfix(void);
int APS5_VABI get_authinfo_nid_postfix(int pid, KernelAuthInfo* info);
int APS5_VABI get_module_info_list_nid_postfix(void);
int APS5_VABI get_page_table_stats_nid_postfix(void);
int APS5_VABI get_sdk_compiled_version_nid_postfix(void);
@@ -265,7 +273,7 @@ int APS5_VABI sceKernelGetIpcPath(void);
int APS5_VABI sceKernelGetKnobs(void);
int APS5_VABI sceKernelGetKqueueFromEqueue(void);
int APS5_VABI sceKernelGetMemoryPstate(void);
int APS5_VABI sceKernelGetOpenPsIdForSystem(void);
int APS5_VABI sceKernelGetOpenPsIdForSystem(void* id);
int APS5_VABI sceKernelGetPagingStatsOfAllObjects(void);
int APS5_VABI sceKernelGetPagingStatsOfAllThreads(void);
int APS5_VABI sceKernelGetParentApp(void);
+10
View File
@@ -37,8 +37,18 @@ static void CheckConsoleClassValues() {
Require(isprint_nid_postfix(c) == (bits & 0x3e) && isgraph_nid_postfix(c) == (bits & 0x3a) && ispunct_nid_postfix(c) == (bits & 0x08));
}
}
static void CheckSignedCharValues() {
for (int c = -128; c <= -2; ++c) {
Require(isupper_nid_postfix(c) == 0 && islower_nid_postfix(c) == 0 && isalpha_nid_postfix(c) == 0 && isdigit_nid_postfix(c) == 0);
Require(isalnum_nid_postfix(c) == 0 && isspace_nid_postfix(c) == 0 && isblank_nid_postfix(c) == 0 && iscntrl_nid_postfix(c) == 0);
Require(isprint_nid_postfix(c) == 0 && isgraph_nid_postfix(c) == 0 && ispunct_nid_postfix(c) == 0 && isxdigit_nid_postfix(c) == 0);
Require(toupper_nid_postfix(c) == c && tolower_nid_postfix(c) == c);
}
}
int main() {
CheckConsoleClassValues();
CheckSignedCharValues();
Require(std::setlocale(LC_CTYPE, "C") != nullptr);
for (int category = 0; category <= 6; ++category) {
const char* result = setlocale_nid_postfix(category, "C");
@@ -18,5 +18,11 @@ int main() {
std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion));
if (!ProcessParametersFixture::Install(process.data(), process.size())) return 2;
std::uint32_t type = 0xFFFFFFFFu;
std::uint16_t virtualKey = 0;
char16_t character = 0;
if (sceConvertKeycodeGetVirtualKeycode(0, 0, nullptr) != static_cast<int>(0x80BC0031u)) return 1;
if (sceConvertKeycodeGetVirtualKeycode(0, 0, &virtualKey) != static_cast<int>(0x80BC001Du)) return 1;
if (sceConvertKeycodeGetCharacter(0, 0, 0, nullptr) != static_cast<int>(0x80BC0031u)) return 1;
if (sceConvertKeycodeGetCharacter(0, 0, 0, &character) != static_cast<int>(0x80BC001Du)) return 1;
return sceConvertKeycodeGetImeKeyboardType(0x10000000, &type) == 0 && type == 4 ? 0 : 1;
}
@@ -10,6 +10,50 @@ static void Require(bool value) { if (!value) std::abort(); }
constexpr int Relaxed = 0, Acquire = 2, Release = 3, AcqRel = 4, SeqCst = 5;
static void CheckRemainingTyped() {
std::uint8_t byte = 0;
Require(_Atomic_load_1_nid_postfix(&byte, SeqCst) == 0);
Require(_Atomic_exchange_1_nid_postfix(&byte, 0xa5, SeqCst) == 0 && byte == 0xa5);
std::uint8_t expectedByte = 0;
Require(_Atomic_compare_exchange_strong_1_nid_postfix(&byte, &expectedByte, 1, SeqCst, SeqCst) == 0 && expectedByte == 0xa5 && byte == 0xa5);
Require(_Atomic_compare_exchange_strong_1_nid_postfix(&byte, &expectedByte, 0x3c, SeqCst, SeqCst) == 1 && byte == 0x3c);
expectedByte = 0;
Require(_Atomic_compare_exchange_weak_1_nid_postfix(&byte, &expectedByte, 1, SeqCst, Relaxed) == 0 && expectedByte == 0x3c);
Require(_Atomic_compare_exchange_weak_1_nid_postfix(&byte, &expectedByte, 0x0f, SeqCst, Relaxed) == 1 && byte == 0x0f);
Require(_Atomic_fetch_sub_1_nid_postfix(&byte, 0x10, SeqCst) == 0x0f && byte == 0xff);
Require(_Atomic_fetch_and_1_nid_postfix(&byte, 0x3c, Relaxed) == 0xff && byte == 0x3c);
Require(_Atomic_fetch_xor_1_nid_postfix(&byte, 0xff, AcqRel) == 0x3c && byte == 0xc3);
std::uint16_t half = 0;
_Atomic_store_2_nid_postfix(&half, 0x1234, Release);
Require(half == 0x1234 && _Atomic_exchange_2_nid_postfix(&half, 0xfff0, SeqCst) == 0x1234 && half == 0xfff0);
Require(_Atomic_fetch_add_2_nid_postfix(&half, 0x20, SeqCst) == 0xfff0 && half == 0x0010);
Require(_Atomic_fetch_and_2_nid_postfix(&half, 0x0018, SeqCst) == 0x0010 && half == 0x0010);
Require(_Atomic_fetch_or_2_nid_postfix(&half, 0x0101, SeqCst) == 0x0010 && half == 0x0111);
Require(_Atomic_fetch_xor_2_nid_postfix(&half, 0x0110, SeqCst) == 0x0111 && half == 0x0001);
std::uint16_t expectedHalf = 2;
Require(_Atomic_compare_exchange_weak_2_nid_postfix(&half, &expectedHalf, 9, SeqCst, Relaxed) == 0 && expectedHalf == 1);
Require(_Atomic_compare_exchange_weak_2_nid_postfix(&half, &expectedHalf, 9, SeqCst, Relaxed) == 1 && half == 9);
std::uint32_t word = 0;
_Atomic_store_4_nid_postfix(&word, 0x00ff00ffu, SeqCst);
Require(word == 0x00ff00ffu && _Atomic_fetch_or_4_nid_postfix(&word, 0xff000000u, SeqCst) == 0x00ff00ffu && word == 0xffff00ffu);
Require(_Atomic_fetch_xor_4_nid_postfix(&word, 0xffffffffu, SeqCst) == 0xffff00ffu && word == 0x0000ff00u);
std::uint32_t expectedWord = 0;
Require(_Atomic_compare_exchange_strong_4_nid_postfix(&word, &expectedWord, 1, SeqCst, SeqCst) == 0 && expectedWord == 0x0000ff00u);
Require(_Atomic_compare_exchange_strong_4_nid_postfix(&word, &expectedWord, 1, SeqCst, SeqCst) == 1 && word == 1);
std::uint64_t wide = 0x8000000000000001ull;
Require(_Atomic_fetch_sub_8_nid_postfix(&wide, 2, SeqCst) == 0x8000000000000001ull && wide == 0x7fffffffffffffffull);
Require(_Atomic_fetch_and_8_nid_postfix(&wide, 0xffffffff00000000ull, SeqCst) == 0x7fffffffffffffffull && wide == 0x7fffffff00000000ull);
Require(_Atomic_fetch_or_8_nid_postfix(&wide, 0xffull, SeqCst) == 0x7fffffff00000000ull && wide == 0x7fffffff000000ffull);
std::uint64_t expectedWide = 0;
Require(_Atomic_compare_exchange_strong_8_nid_postfix(&wide, &expectedWide, 5, SeqCst, SeqCst) == 0 && expectedWide == 0x7fffffff000000ffull);
Require(_Atomic_compare_exchange_strong_8_nid_postfix(&wide, &expectedWide, 5, SeqCst, SeqCst) == 1 && wide == 5);
Require(_Atomic_is_lock_free_1_nid_postfix() == 1 && _Atomic_is_lock_free_2_nid_postfix() == 1 && _Atomic_is_lock_free_8_nid_postfix() == 1);
}
static void CheckTyped() {
std::uint8_t byte = 0;
_Atomic_store_1_nid_postfix(&byte, 7, SeqCst);
@@ -130,6 +174,7 @@ static void CheckSized() {
}
int main() {
CheckRemainingTyped();
CheckTyped();
CheckSized();
CheckFlagsAndFences();
+4 -2
View File
@@ -270,9 +270,11 @@ int main(int argc, char** argv) {
Require(_sceKernelRtldThreadAtexitDecrement_nid_postfix(reinterpret_cast<const void*>(add)) == 0);
Require(_sceKernelRtldThreadAtexitIncrement_nid_postfix(reinterpret_cast<const void*>(&ThrowsRuntimeError<void (*)()>)) == 0);
Require(_sceKernelRtldThreadAtexitDecrement_nid_postfix(reinterpret_cast<const void*>(&ThrowsRuntimeError<void (*)()>)) == 0);
Require(ThrowsRuntimeError([] { _sceKernelRtldThreadAtexitDecrement_nid_postfix(reinterpret_cast<const void*>(&Require)); }));
int local = 0;
Require(ThrowsRuntimeError([&] { _sceKernelRtldThreadAtexitIncrement_nid_postfix(&local); }));
Require(_sceKernelRtldThreadAtexitIncrement_nid_postfix(&local) == -1);
Require(_sceKernelRtldThreadAtexitIncrement_nid_postfix(nullptr) == -1);
Require(_sceKernelRtldThreadAtexitDecrement_nid_postfix(&local) == 0);
Require(_sceKernelRtldThreadAtexitDecrement_nid_postfix(nullptr) == 0);
#ifndef _WIN32
Require(dlsym_nid_postfix(nullptr, "malloc") == nullptr);
Require(dlsym_nid_postfix(nullptr, "dlopen") == nullptr);
@@ -0,0 +1,274 @@
#include "prx/libSceFreeType/include/Api.hpp"
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_STROKER_H
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <limits>
#include <stdexcept>
#include <string>
#include <thread>
#include <type_traits>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#include <sys/mman.h>
#include <unistd.h>
#endif
using FreeType::GuestOutline;
using FreeType::GuestVector;
using Query = decltype(&FT_Outline_GetInsideBorder_nid_postfix);
static_assert(std::is_same_v<Query, decltype(&FT_Outline_GetOutsideBorder_nid_postfix)>);
static_assert(sizeof(GuestOutline) == 40 && alignof(GuestOutline) == 8);
static_assert(offsetof(GuestOutline, points) == 8 && offsetof(GuestOutline, contours) == 24);
static_assert(offsetof(GuestOutline, flags) == 32 && sizeof(GuestVector) == 16);
static_assert(sizeof(FT_StrokerBorder) == 4);
namespace {
bool nativeInside;
void Require(bool result, const char* message) {
if (result) return;
std::fprintf(stderr, "FreeType outline border: %s\n", message);
std::exit(1);
}
int APS5_VABI Native(const GuestOutline* input) {
if (!input) return nativeInside ? FT_Outline_GetInsideBorder(nullptr) : FT_Outline_GetOutsideBorder(nullptr);
FT_Outline outline{};
outline.n_points = input->n_points;
outline.n_contours = input->n_contours;
outline.flags = input->flags;
std::vector<FT_Vector> points;
std::vector<unsigned short> contours;
if (outline.n_points > 0) {
for (int i = 0; i < outline.n_points; ++i) {
Require(input->points[i].x >= std::numeric_limits<FT_Pos>::min() && input->points[i].x <= std::numeric_limits<FT_Pos>::max(), "reference x range");
Require(input->points[i].y >= std::numeric_limits<FT_Pos>::min() && input->points[i].y <= std::numeric_limits<FT_Pos>::max(), "reference y range");
points.push_back({static_cast<FT_Pos>(input->points[i].x), static_cast<FT_Pos>(input->points[i].y)});
}
if (outline.n_contours > 0)
for (int i = 0; i < outline.n_contours; ++i) contours.push_back(input->contours[i]);
}
outline.points = points.data();
outline.contours = contours.data();
return nativeInside ? FT_Outline_GetInsideBorder(&outline) : FT_Outline_GetOutsideBorder(&outline);
}
int Expected(bool inside, bool clockwise) { return inside == clockwise ? 1 : 0; }
class ReadOnlyTail {
void* memory;
std::size_t page;
void* data;
public:
ReadOnlyTail(const void* source, std::size_t bytes) {
#ifdef _WIN32
SYSTEM_INFO info;
GetSystemInfo(&info);
page = info.dwPageSize;
memory = VirtualAlloc(nullptr, page * 2, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Require(memory != nullptr, "allocate guard pages");
#else
page = static_cast<std::size_t>(sysconf(_SC_PAGESIZE));
memory = mmap(nullptr, page * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
Require(memory != MAP_FAILED, "allocate guard pages");
#endif
Require(bytes <= page, "guard fixture size");
data = static_cast<unsigned char*>(memory) + page - bytes;
std::memcpy(data, source, bytes);
#ifdef _WIN32
DWORD previous;
Require(VirtualProtect(memory, page, PAGE_READONLY, &previous) != 0, "read-only input");
Require(VirtualProtect(static_cast<unsigned char*>(memory) + page, page, PAGE_NOACCESS, &previous) != 0, "inaccessible neighbor");
#else
Require(mprotect(memory, page, PROT_READ) == 0, "read-only input");
Require(mprotect(static_cast<unsigned char*>(memory) + page, page, PROT_NONE) == 0, "inaccessible neighbor");
#endif
}
~ReadOnlyTail() {
#ifdef _WIN32
VirtualFree(memory, 0, MEM_RELEASE);
#else
munmap(memory, page * 2);
#endif
}
template<class T> T* As() const { return static_cast<T*>(data); }
void* End() const { return static_cast<unsigned char*>(memory) + page; }
};
void Cases(Query query, bool inside, bool guest) {
Require(query(nullptr) == Expected(inside, true), "null outline");
GuestOutline outline{};
outline.points = reinterpret_cast<GuestVector*>(std::uintptr_t{1});
outline.contours = reinterpret_cast<std::uint16_t*>(std::uintptr_t{1});
Require(query(&outline) == Expected(inside, true), "empty outline does not read points or contours");
std::array<GuestVector, 4> points{{{0, 0}, {0, 64}, {64, 64}, {64, 0}}};
std::uint16_t end = 3;
outline = {1, 4, points.data(), nullptr, &end, 0};
Require(query(&outline) == Expected(inside, true), "clockwise polygon");
const auto original = points;
for (const int flags : {0, 1, 2, -1}) {
outline.flags = flags;
Require(query(&outline) == Expected(inside, true), "orientation ignores outline flags");
}
Require(std::memcmp(points.data(), original.data(), sizeof(points)) == 0, "input points unchanged");
std::reverse(points.begin(), points.end());
Require(query(&outline) == Expected(inside, false), "counterclockwise polygon");
for (auto& point : points) point.y = 0;
Require(query(&outline) == Expected(inside, false), "collapsed polygon uses no-orientation fallback");
points = original;
outline.n_contours = 0;
Require(query(&outline) == Expected(inside, false), "no contours");
outline.n_contours = 1;
for (const std::int64_t edge : {std::int64_t{0x1000000}, std::int64_t{0x1000001}}) {
points = {{{-edge, -edge}, {-edge, edge}, {edge, edge}, {edge, -edge}}};
Require(query(&outline) == (edge == 0x1000000 ? Expected(inside, true) : Expected(inside, false)), "coordinate-limit boundary");
}
std::array<GuestVector, 8> mixed{};
std::copy(original.begin(), original.end(), mixed.begin());
std::copy(original.rbegin(), original.rend(), mixed.begin() + 4);
std::array<std::uint16_t, 2> mixedEnds{{3, 7}};
GuestOutline cancelled{2, 8, mixed.data(), nullptr, mixedEnds.data(), 0};
Require(query(&cancelled) == Expected(inside, false), "non-collapsed opposite contours cancel area");
std::vector<GuestVector> manyPoints(65535);
std::copy(original.rbegin(), original.rend(), manyPoints.begin());
std::uint16_t manyEnd = 65534;
GuestOutline many{1, 65535, manyPoints.data(), nullptr, &manyEnd, 0};
Require(query(&many) == Expected(inside, false), "unsigned point count above 32767");
std::fill(manyPoints.begin(), manyPoints.end(), GuestVector{});
std::copy(original.begin(), original.end(), manyPoints.end() - 4);
std::vector<std::uint16_t> manyEnds(65532);
for (unsigned i = 0; i < manyEnds.size() - 1; ++i) manyEnds[i] = static_cast<std::uint16_t>(i);
manyEnds.back() = 65534;
many.n_contours = 65532;
many.contours = manyEnds.data();
Require(query(&many) == Expected(inside, true), "unsigned contour count above 32767");
if (guest) {
for (const auto edge : {std::numeric_limits<std::int64_t>::min(), std::numeric_limits<std::int64_t>::max()}) {
points = original;
points[0].x = edge;
Require(query(&outline) == Expected(inside, false), "64-bit out-of-range coordinate");
}
points = original;
std::array<GuestVector, 20> repeated{};
std::array<std::uint16_t, 5> ends{};
const std::array<GuestVector, 4> large{{{0, 0}, {0, 16384}, {16384, 16384}, {16384, 0}}};
for (unsigned i = 0; i < 5; ++i) {
std::copy(large.begin(), large.end(), repeated.begin() + i * 4);
ends[i] = static_cast<std::uint16_t>(i * 4 + 3);
}
GuestOutline wide{5, 20, repeated.data(), nullptr, ends.data(), 0};
Require(query(&wide) == Expected(inside, true), "LP64 area must not wrap at 32 bits");
ReadOnlyTail guardedPoints(original.data(), sizeof(original));
ReadOnlyTail guardedEnds(&end, sizeof(end));
GuestOutline guarded{1, 4, guardedPoints.As<GuestVector>(), static_cast<unsigned char*>(guardedPoints.End()), guardedEnds.As<std::uint16_t>(), -1};
ReadOnlyTail guardedOutline(&guarded, sizeof(guarded));
Require(query(guardedOutline.As<GuestOutline>()) == Expected(inside, true), "read-only bounded inputs and unused inaccessible tags");
outline = {1, 4, nullptr, nullptr, &end, 0};
bool threw = false;
try { query(&outline); } catch (const std::invalid_argument&) { threw = true; }
Require(threw, "null positive-count points are explicitly unsupported");
outline.points = points.data();
outline.contours = nullptr;
threw = false;
try { query(&outline); } catch (const std::invalid_argument&) { threw = true; }
Require(threw, "null positive-count contours are explicitly unsupported");
for (const std::uint16_t badEnd : {std::uint16_t{4}, std::uint16_t{0xffff}}) {
auto invalidEnd = badEnd;
outline.contours = &invalidEnd;
threw = false;
try { query(&outline); } catch (const std::invalid_argument&) { threw = true; }
Require(threw, "invalid contour endpoint is explicitly unsupported");
}
for (const std::uint16_t nextEnd : {std::uint16_t{2}, std::uint16_t{3}}) {
std::array<std::uint16_t, 2> invalidEnds{{3, nextEnd}};
outline.n_contours = 2;
outline.contours = invalidEnds.data();
threw = false;
try { query(&outline); } catch (const std::invalid_argument&) { threw = true; }
Require(threw, "non-increasing contour endpoints are explicitly unsupported");
}
}
std::uint32_t state = 7;
for (unsigned iteration = 0; iteration < 512; ++iteration) {
for (auto& point : points) {
state = state * 1664525u + 1013904223u;
point.x = static_cast<int>((state >> 16) & 255) - 128;
state = state * 1664525u + 1013904223u;
point.y = static_cast<int>((state >> 16) & 255) - 128;
}
outline = {1, 4, points.data(), nullptr, &end, 0};
nativeInside = inside;
Require(query(&outline) == Native(&outline), "bounded polygon matches bundled FreeType");
}
if (guest) {
points = original;
outline = {1, 4, points.data(), nullptr, &end, 0};
std::array<std::thread, 4> threads;
for (auto& thread : threads) thread = std::thread([&] {
for (unsigned i = 0; i < 100; ++i) Require(query(&outline) == Expected(inside, true), "concurrent independent query");
});
for (auto& thread : threads) thread.join();
}
}
}
int main(int argc, char** argv) {
#ifdef _WIN32
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
#endif
if (argc == 2 && std::strcmp(argv[1], "reference") == 0) {
for (const bool inside : {false, true}) {
nativeInside = inside;
Cases(Native, inside, false);
}
std::puts("Bundled FreeType reference cases passed");
return 0;
}
Require(argc == 4, "expected library, border and export mode");
const bool inside = std::strcmp(argv[2], "inside") == 0;
const bool patched = std::strcmp(argv[3], "patched") == 0;
const char* symbol = patched ? (inside ? "Pz262plj20w" : "sKneJjUXWkQ") :
(inside ? "FT_Outline_GetInsideBorder_nid_postfix" : "FT_Outline_GetOutsideBorder_nid_postfix");
#ifdef _WIN32
const auto library = LoadLibraryA(argv[1]);
Require(library != nullptr, "load requested library");
const auto checkPath = [](HMODULE module, const std::filesystem::path& expected) {
Require(module != nullptr, "load matching runtime module");
std::array<wchar_t, 32768> path{};
const auto length = GetModuleFileNameW(module, path.data(), static_cast<DWORD>(path.size()));
Require(length != 0 && length < path.size(), "read loaded module path");
Require(std::filesystem::equivalent(expected, std::filesystem::path(path.data())), "loaded module variant path");
};
const auto requested = std::filesystem::absolute(argv[1]);
checkPath(library, requested);
checkPath(GetModuleHandleW(L"libc.prx"), requested.parent_path() / "libc.prx");
auto query = reinterpret_cast<Query>(GetProcAddress(library, symbol));
#else
const auto library = dlopen(argv[1], RTLD_NOW | RTLD_LOCAL);
if (!library) std::fprintf(stderr, "%s\n", dlerror());
Require(library != nullptr, "load requested library");
auto query = reinterpret_cast<Query>(dlsym(library, symbol));
#endif
if (!query) std::fprintf(stderr, "Missing %s\n", symbol);
Require(query != nullptr, "missing outline-border export");
Cases(query, inside, true);
std::puts("Guest outline-border cases passed");
#ifdef _WIN32
FreeLibrary(library);
#else
dlclose(library);
#endif
}
+5
View File
@@ -1107,7 +1107,12 @@ int main() {
#endif
Require(fchown_nid_postfix(base, 0, 0) == 0);
Require(fchown_nid_postfix(base, 0xFFFFFFFFu, 0xFFFFFFFFu) == 0);
Require(fchown_nid_postfix(base, 0, 0xFFFFFFFFu) == 0);
Require(fchown_nid_postfix(base, 0xFFFFFFFFu, 0) == 0);
Require(fchown_nid_postfix(-1, 0, 0) == -1 && Error() == 9);
Require(fchown_nid_postfix(-1, 1, 0) == -1 && Error() == 9);
Require(fchown_nid_postfix(-1, 0, 1) == -1 && Error() == 9);
Require(fchown_nid_postfix(-1, 1, 1) == -1 && Error() == 9);
bool fchownThrew = false;
try { fchown_nid_postfix(base, 1, 0); } catch (const std::exception&) { fchownThrew = true; }
Require(fchownThrew);
@@ -0,0 +1,71 @@
#include "prx/libkernel_sys/libkernel_sys.hpp"
#include "prx/libc/libc.hpp"
#include <array>
#include <bit>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
namespace {
constexpr int GuestEperm = 1;
constexpr int GuestEsrch = 3;
void Require(bool condition, const char* message) {
if (!condition) {
std::fprintf(stderr, "KERNEL SYS CONSOLE IDENTITY: %s\n", message);
std::abort();
}
}
void CheckConsoleKind() {
Require(sceKernelIsCEX() == 1, "the console must be reported as a retail unit");
Require(sceKernelIsDevKit() == 0, "the console must not be reported as a development kit");
}
void CheckAuthInfo() {
struct {
KernelAuthInfo value;
std::uint64_t guard;
} info;
*__error_nid_postfix() = 0;
Require(get_authinfo_nid_postfix(0, nullptr) == -1 && *__error_nid_postfix() == GuestEperm, "a null result must fail with EPERM");
std::memset(&info, 0x5a, sizeof(info));
*__error_nid_postfix() = 0;
Require(get_authinfo_nid_postfix(getpid_nid_postfix() + 1, &info.value) == -1 && *__error_nid_postfix() == GuestEsrch,
"another process must fail with ESRCH");
for (const auto byte : std::bit_cast<std::array<std::uint8_t, sizeof(info)>>(info))
Require(byte == 0x5a, "a refused call must not write the result");
for (const int pid : {0, getpid_nid_postfix()}) {
std::memset(&info, 0x5a, sizeof(info));
Require(get_authinfo_nid_postfix(pid, &info.value) == 0, "the guest process must be described");
Require(info.value.programAuthorityId == 0 && info.value.capabilities[0] == 0x2000000000000000, "the authority ID or first capability word is wrong");
const auto bytes = std::bit_cast<std::array<std::uint8_t, sizeof(KernelAuthInfo)>>(info.value);
for (std::size_t i = 16; i < bytes.size(); ++i) Require(bytes[i] == 0, "the rest of the result must be cleared");
Require(info.guard == 0x5a5a5a5a5a5a5a5a, "get_authinfo wrote past its 0x88-byte result");
}
}
void CheckOpenPsId() {
std::array<std::uint8_t, 16> plain{};
std::array<std::uint8_t, 24> system;
system.fill(0xa5);
*__error_nid_postfix() = GuestEsrch;
Require(sceKernelGetOpenPsIdForSystem(nullptr) == 22, "a null system OpenPSID must return positive EINVAL");
Require(*__error_nid_postfix() == GuestEsrch, "a null system OpenPSID must leave errno unchanged");
for (auto byte : system) Require(byte == 0xa5, "a null system OpenPSID must not write output");
Require(sceKernelGetOpenPsId(plain.data()) == 0, "the OpenPSID must be readable");
Require(sceKernelGetOpenPsIdForSystem(system.data()) == 0, "the system OpenPSID must be readable");
Require(std::memcmp(plain.data(), system.data(), plain.size()) == 0, "both OpenPSID queries must agree");
for (std::size_t i = plain.size(); i < system.size(); ++i) Require(system[i] == 0xa5, "the system OpenPSID wrote past 16 bytes");
}
}
int main() {
CheckConsoleKind();
CheckAuthInfo();
CheckOpenPsId();
return 0;
}
+78 -3
View File
@@ -5,15 +5,15 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include <array>
#include <atomic>
#include <barrier>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <limits>
#include <stdexcept>
#include <thread>
extern "C" {
}
static void Require(bool value) { if (!value) std::abort(); }
@@ -275,6 +275,80 @@ static unsigned APS5_VABI TestPadGo(GuestPad* base) {
return 42;
}
static void RequireExit(bool value, const char* message) {
if (!value) {
std::fprintf(stderr, "CRT exit lifecycle: %s\n", message);
std::abort();
}
}
struct CrtExitState {
int code = 0;
int key = -1;
std::barrier<>* ready = nullptr;
void* parent = nullptr;
void* self = nullptr;
std::atomic<unsigned> destructors{0};
std::atomic<bool> returned{false};
};
static void APS5_VABI CrtExitDestructor(void* value) {
auto& state = *static_cast<CrtExitState*>(value);
RequireExit(_Thrd_current_nid_postfix() == state.self, "identity during destructor");
RequireExit(_Tss_get_nid_postfix(state.key) == nullptr, "value cleared before destructor");
if (state.destructors.fetch_add(1) == 0)
RequireExit(_Tss_set_nid_postfix(state.key, &state) == 0, "reinstall value for another cleanup pass");
}
static auto volatile crtExit = &_Thrd_exit_nid_postfix;
static void* APS5_VABI CrtExitEntry(void* value) {
auto& state = *static_cast<CrtExitState*>(value);
state.self = _Thrd_current_nid_postfix();
RequireExit(state.self && _Thrd_equal_nid_postfix(state.self, state.self) == 1, "current identity");
RequireExit(_Thrd_equal_nid_postfix(state.self, state.parent) == 0, "different parent identity");
RequireExit(_Tss_get_nid_postfix(state.key) == nullptr, "child starts with empty value");
RequireExit(_Tss_set_nid_postfix(state.key, &state) == 0, "set child value");
state.ready->arrive_and_wait();
RequireExit(_Tss_get_nid_postfix(state.key) == &state, "child value isolation");
crtExit(state.code);
state.returned = true;
return nullptr;
}
static void ThreadExitLifecycle() {
constexpr std::array<int, 5> codes{0, 42, -1, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()};
constexpr std::array<std::uintptr_t, 5> expected{0, 42, 0xffffffffu, 0x80000000u, 0x7fffffffu};
std::barrier ready(6);
std::array<CrtExitState, 5> states;
std::array<Pthread, 5> threads{};
int key = -1, parentValue = 31;
void* parent = _Thrd_current_nid_postfix();
RequireExit(_Tss_create_nid_postfix(&key, CrtExitDestructor) == 0, "create destructor key");
RequireExit(_Tss_set_nid_postfix(key, &parentValue) == 0, "set parent value");
for (unsigned i = 0; i < states.size(); ++i) {
states[i].code = codes[i];
states[i].key = key;
states[i].ready = &ready;
states[i].parent = parent;
RequireExit(scePthreadCreate(&threads[i], nullptr, CrtExitEntry, &states[i], "CRT exit audit") == 0, "create child");
}
RequireExit(_Tss_get_nid_postfix(key) == &parentValue, "parent isolated before release");
ready.arrive_and_wait();
for (unsigned i = 0; i < states.size(); ++i) {
void* result = nullptr;
RequireExit(scePthreadJoin(threads[i], &result) == 0, "join exiting child");
RequireExit(reinterpret_cast<std::uintptr_t>(result) == expected[i], "full zero-extended exit result");
RequireExit(!states[i].returned, "exit must not return");
RequireExit(states[i].destructors == 2, "reinstalled destructor runs twice before join returns");
RequireExit(states[i].self == threads[i], "created and current identity match");
}
RequireExit(_Tss_get_nid_postfix(key) == &parentValue, "parent isolated after children exit");
RequireExit(_Tss_delete_nid_postfix(key) == 0, "delete key");
RequireExit(_Tss_get_nid_postfix(key) == nullptr, "deleted key value unavailable");
std::puts("PASS CRT lifecycle: five concurrent exit codes, full return width, identity, parent/child isolation, two destructor passes, key deletion");
}
static void PadLaunchAndJoin() {
unsigned (APS5_VABI* vtable[1])(GuestPad*) = {TestPadGo};
TestPad unregPad{};
@@ -508,6 +582,7 @@ static void ThreadHelpers() {
}
int main() {
ThreadExitLifecycle();
KernelKeys();
VersionedSync();
ForwardedCalls();
+21
View File
@@ -630,6 +630,26 @@ static void CheckLog1pConsoleBits() {
}
}
static void CheckRoundingNaNConsoleBits() {
constexpr std::uint64_t cases[][2] = {
{0x7ff0000000000001ull, 0x7ff8000000000001ull}, {0xfff4000000000123ull, 0xfffc000000000123ull}, {0x7ff8000000000456ull, 0x7ff8000000000456ull},
{0x7ff0000000000000ull, 0x7ff0000000000000ull}, {0xfff0000000000000ull, 0xfff0000000000000ull},
};
constexpr std::uint32_t floatCases[][2] = {
{0x7f800001u, 0x7fc00001u}, {0xffa00123u, 0xffe00123u}, {0x7fc00456u, 0x7fc00456u}, {0x7f800000u, 0x7f800000u}, {0xff800000u, 0xff800000u},
};
for (const auto function : {trunc_nid_postfix, ceil_nid_postfix, floor_nid_postfix, nearbyint_nid_postfix}) {
for (const auto& [input, expected] : cases) {
Require(std::bit_cast<std::uint64_t>(function(std::bit_cast<double>(input))) == expected);
}
}
for (const auto function : {truncf_nid_postfix, ceilf_nid_postfix, floorf_nid_postfix, nearbyintf_nid_postfix}) {
for (const auto& [input, expected] : floatCases) {
Require(std::bit_cast<std::uint32_t>(function(std::bit_cast<float>(input))) == expected);
}
}
}
static void CheckAsinl() {
const struct { std::uint64_t inputMantissa; std::uint16_t inputSignExponent; std::uint64_t outputMantissa; std::uint16_t outputSignExponent; } cases[] = {
{0, 0x0000, 0, 0x0000}, {0, 0x8000, 0, 0x8000},
@@ -1773,6 +1793,7 @@ int main() {
CheckExpm1ConsoleBits();
CheckLog1pConsoleBits();
CheckNextafterConsoleBits();
CheckRoundingNaNConsoleBits();
CheckAsinl();
Require(scalbn_nid_postfix(0.75, -2) == 0.1875);
Require(scalbnf_nid_postfix(0.75f, 4) == 12.f);
+60 -4
View File
@@ -4,6 +4,7 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stdexcept>
static void Require(bool value, const char* what) {
@@ -23,6 +24,8 @@ static bool Throws(TCall call) {
}
int main() {
constexpr int kNotInitialized = -2141898750;
constexpr int kInvalidArgument = -2141898748;
constexpr int kIntentNotFound = -2141898746;
constexpr int kValueNotFound = -2141898745;
@@ -41,22 +44,75 @@ int main() {
Require(info.size == sizeof(NpGameIntentInfo), "info size mismatch");
Require(info.user_id == -1, "info user_id mismatch");
char preBuf[64]{};
Require(sceNpGameIntentGetPropertyValueString(nullptr, "key", preBuf, sizeof(preBuf)) == kNotInitialized,
"uninitialized get property string with null data must report not-initialized");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", preBuf, sizeof(preBuf)) == kNotInitialized,
"uninitialized get property string with valid data must report not-initialized");
Require(sceNpGameIntentReceiveIntent(nullptr) == kNotInitialized,
"uninitialized receive intent with null pointer must report not-initialized");
Require(sceNpGameIntentReceiveIntent(&info) == kNotInitialized,
"uninitialized receive intent with valid pointer must report not-initialized");
Require(sceNpGameIntentInitialize(&initParam) == 0, "initialization failed");
Require(sceNpGameIntentReceiveIntent(&info) == kIntentNotFound, "receive intent mismatch");
Require(sceNpGameIntentReceiveIntent(nullptr) == kInvalidArgument, "null receive intent did not fail");
Require(sceNpGameIntentReceiveIntent(&info) == kInvalidArgument, "mismatched struct size must report invalid argument");
info.size = 0;
Require(sceNpGameIntentReceiveIntent(&info) == kInvalidArgument, "zero size must report invalid argument");
info.size = 0x413f;
Require(sceNpGameIntentReceiveIntent(&info) == kInvalidArgument, "0x413f size must report invalid argument");
info.size = 0x4141;
Require(sceNpGameIntentReceiveIntent(&info) == kInvalidArgument, "0x4141 size must report invalid argument");
info.size = sizeof(NpGameIntentInfo);
char buf[64]{};
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", buf, sizeof(buf)) == kValueNotFound,
Require(sceNpGameIntentGetPropertyValueString(nullptr, "key", buf, sizeof(buf)) == kInvalidArgument,
"null intent data did not fail");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, nullptr, buf, sizeof(buf)) == kInvalidArgument,
"null key did not fail");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "", buf, sizeof(buf)) == kInvalidArgument,
"empty key did not fail");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", nullptr, sizeof(buf)) == kInvalidArgument,
"null value buf did not fail");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", buf, 0) == kInvalidArgument,
"zero buf size did not fail");
char canaryBuf[64]{};
std::memset(canaryBuf, 'X', sizeof(canaryBuf));
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", canaryBuf, 0) == kInvalidArgument,
"zero buf size canary call did not fail");
Require(canaryBuf[0] == 'X', "buffer was modified on zero size error");
Require(sceNpGameIntentGetPropertyValueString(nullptr, "key", canaryBuf, sizeof(canaryBuf)) == kInvalidArgument,
"null data canary call did not fail");
Require(canaryBuf[0] == 'X', "buffer was modified on null data error");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, nullptr, canaryBuf, sizeof(canaryBuf)) == kInvalidArgument,
"null key canary call did not fail");
Require(canaryBuf[0] == 'X', "buffer was modified on null key error");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "", canaryBuf, sizeof(canaryBuf)) == kInvalidArgument,
"empty key canary call did not fail");
Require(canaryBuf[0] == 'X', "buffer was modified on empty key error");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", canaryBuf, sizeof(canaryBuf)) == kValueNotFound,
"get property value mismatch");
Require(canaryBuf[0] == '\0', "valid call did not write terminator");
canaryBuf[0] = 'Y';
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", canaryBuf, 1) == kValueNotFound,
"bufSize 1 did not succeed");
Require(canaryBuf[0] == '\0', "bufSize 1 did not write terminator");
Require(sceNpGameIntentVshInitialize() == 0, "vsh init failed");
Require(sceNpGameIntentVshTerminate() == 0, "vsh terminate failed");
Require(sceNpGameIntentTerminate() == 0, "terminate failed");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", buf, sizeof(buf)) == kNotInitialized,
"post-terminate get property value must report not-initialized");
Require(sceNpGameIntentReceiveIntent(&info) == kNotInitialized,
"post-terminate receive intent must report not-initialized");
unsigned char appLauncherParam[0x28]{};
*reinterpret_cast<std::uint64_t*>(appLauncherParam) = 0x28;
Require(sceNpAppLauncherInitialize(appLauncherParam) == 0, "app launcher init failed");
Require(Throws<std::runtime_error>([] { sceNpAppLauncherLaunchApp(); }),
"app launcher launch must throw until implemented");
Require(sceNpAppLauncherTerminate() == 0, "app launcher terminate failed");
*reinterpret_cast<std::uint64_t*>(appLauncherParam) = 0x10;
Require(sceNpAppLauncherInitialize(appLauncherParam) == 0,
+7
View File
@@ -14,6 +14,7 @@ namespace {
constexpr int InvalidArgument = static_cast<int>(0x80551604u);
constexpr int InsufficientBuffer = static_cast<int>(0x80551605u);
constexpr int InvalidHandle = static_cast<int>(0x80551608u);
constexpr int InvalidContext = static_cast<int>(0x80551609u);
constexpr int InvalidTrophyId = static_cast<int>(0x8055160au);
constexpr int AlreadyUnlocked = static_cast<int>(0x8055160cu);
@@ -97,6 +98,12 @@ int main() {
Require(sceNpTrophyCreateContext(&missing, 1, 1, 0) == 0, "context for a label without a package");
Require(sceNpTrophyRegisterContext(missing, handle, 0) == TitleConfNotInstalled, "register without a trophy package");
Require(sceNpTrophyCreateHandle(nullptr) == InvalidArgument, "null handle creation");
Require(sceNpTrophyCreateContext(nullptr, 1, 0, 0) == InvalidArgument, "null context creation");
Require(sceNpTrophyGetGameIcon(context, handle, icon, nullptr) == InvalidArgument, "null game icon size pointer");
Require(sceNpTrophyGetGroupIcon(context, handle, 0, icon, nullptr) == InvalidArgument, "null group icon size pointer");
Require(sceNpTrophyGetTrophyIcon(context, handle, 0, icon, nullptr) == InvalidArgument, "null trophy icon size pointer");
Require(sceNpTrophyDestroyHandle(-1) == InvalidHandle, "invalid handle destruction");
Require(sceNpTrophyDestroyContext(missing) == 0, "destroy the second context");
Require(sceNpTrophyDestroyHandle(handle) == 0 && sceNpTrophyDestroyContext(context) == 0, "destroy the handle and context");
Require(sceNpTrophyDestroyContext(context) == InvalidContext, "destroy the context twice");
+101
View File
@@ -3,6 +3,9 @@
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <atomic>
#include <chrono>
#include <thread>
namespace {
@@ -13,6 +16,7 @@ constexpr int Aborted = static_cast<int>(0x80550707u);
constexpr int InsufficientArgument = static_cast<int>(0x8055070Cu);
constexpr int InvalidId = static_cast<int>(0x8055070Eu);
constexpr int InvalidType = static_cast<int>(0x80550711u);
constexpr int LookupSynchronousRequest = static_cast<int>(0x80550307u);
constexpr int PollFinished = 0;
constexpr int PollRunning = 1;
constexpr int WordFilterTitleCtxBase = 0x01000000;
@@ -118,11 +122,108 @@ void CheckWordFilter() {
}
int WaitInThread(const char* name, int (APS5_VABI *wait)(int, int*), int reqId, int* result, void (*finish)(int)) {
int status = 7;
std::atomic<bool> entered{false};
std::atomic<bool> returned{false};
std::thread waiter([&] {
entered = true;
status = wait(reqId, result);
returned = true;
});
while (!entered) std::this_thread::yield();
std::this_thread::sleep_for(std::chrono::milliseconds(20));
Require(!returned, name, "the wait returned before the request finished");
finish(reqId);
waiter.join();
return status;
}
void StartLookup(int reqId) {
NpOnlineId onlineId{};
std::strcpy(onlineId.data, "player_2");
sceNpLookupNpId(reqId, &onlineId, npId, nullptr);
}
void AbortLookup(int reqId) {
sceNpLookupAbortRequest(reqId);
}
void DeleteLookup(int reqId) {
sceNpLookupDeleteRequest(reqId);
}
void CheckLookupAsync() {
const int context = sceNpLookupCreateTitleCtx(selfNpId);
Require(sceNpLookupCreateAsyncRequest(context, nullptr) == InvalidArgument, "sceNpLookupCreateAsyncRequest", "a null parameter was taken");
Require(sceNpLookupCreateAsyncRequest(context, wrongAsyncParam) == InvalidArgument, "sceNpLookupCreateAsyncRequest", "a parameter size other than 0x18 was taken");
Require(sceNpLookupCreateAsyncRequest(context + 100, asyncParam) == InvalidId, "sceNpLookupCreateAsyncRequest", "an unknown context was taken");
Require(sceNpLookupSetTimeout(context, -1, 0, 0, 0, 0) == InvalidArgument, "sceNpLookupSetTimeout", "a negative retry count was taken");
Require(sceNpLookupSetTimeout(context, 0, 0, 0, 0, 0) == InvalidArgument, "sceNpLookupSetTimeout", "settings without any effect were taken");
Require(sceNpLookupSetTimeout(context, 0, 999999, 0, 0, 0) == InvalidArgument, "sceNpLookupSetTimeout", "a resolve timeout under 1 s was taken");
Require(sceNpLookupSetTimeout(context, 0, 0, 9999999, 0, 0) == InvalidArgument, "sceNpLookupSetTimeout", "a connect timeout under 10 s was taken");
Require(sceNpLookupSetTimeout(context, 0, 0, 0, 9999999, 0) == InvalidArgument, "sceNpLookupSetTimeout", "a send timeout under 10 s was taken");
Require(sceNpLookupSetTimeout(context, 0, 0, 0, 0, 9999999) == InvalidArgument, "sceNpLookupSetTimeout", "a receive timeout under 10 s was taken");
Require(sceNpLookupSetTimeout(context + 100, 1, 0, 0, 0, 0) == InvalidId, "sceNpLookupSetTimeout", "an unknown id was taken");
Require(sceNpLookupSetTimeout(context, 1, 0, 0, 0, 0) == 0, "sceNpLookupSetTimeout", "a context timeout was refused");
const int async = sceNpLookupCreateAsyncRequest(context, asyncParam);
Require(async > 0, "sceNpLookupCreateAsyncRequest", "creating an asynchronous request failed");
Require(sceNpLookupSetTimeout(async, 0, 1000000, 10000000, 10000000, 10000000) == 0, "sceNpLookupSetTimeout", "a request timeout was refused");
int result = 7;
Require(sceNpLookupPollAsync(async + 100, &result) == InvalidId, "sceNpLookupPollAsync", "an unknown request was taken");
Require(sceNpLookupPollAsync(async, nullptr) == InvalidArgument, "sceNpLookupPollAsync", "a null result was taken");
Require(sceNpLookupPollAsync(async + 100, nullptr) == InvalidArgument, "sceNpLookupPollAsync", "an unknown request was checked before a null result");
Require(sceNpLookupWaitAsync(async, nullptr) == InvalidArgument, "sceNpLookupWaitAsync", "a null result was taken");
Require(sceNpLookupPollAsync(async, &result) == PollRunning && result == 7, "sceNpLookupPollAsync", "an unstarted request was not running");
Require(WaitInThread("sceNpLookupWaitAsync", sceNpLookupWaitAsync, async, &result, StartLookup) == 0 && result == SignedOut, "sceNpLookupWaitAsync", "waiting did not end with SIGNED_OUT when the lookup started");
Require(sceNpLookupPollAsync(async, nullptr) == InvalidArgument, "sceNpLookupPollAsync", "a null result was taken on a finished request");
result = 7;
Require(sceNpLookupPollAsync(async, &result) == PollFinished && result == SignedOut, "sceNpLookupPollAsync", "the request did not finish with SIGNED_OUT");
const int sync = sceNpLookupCreateRequest(context);
StartLookup(sync);
result = 7;
Require(sceNpLookupWaitAsync(sync, &result) == LookupSynchronousRequest && result == 7, "sceNpLookupWaitAsync", "a synchronous request was not refused with 0x80550307");
Require(sceNpLookupPollAsync(sync, &result) == LookupSynchronousRequest && result == 7, "sceNpLookupPollAsync", "a synchronous request was not refused with 0x80550307");
const int aborted = sceNpLookupCreateAsyncRequest(context, asyncParam);
Require(sceNpLookupAbortRequest(aborted + 100) == InvalidId, "sceNpLookupAbortRequest", "an unknown request was taken");
Require(WaitInThread("sceNpLookupWaitAsync", sceNpLookupWaitAsync, aborted, &result, AbortLookup) == 0 && result == Aborted, "sceNpLookupAbortRequest", "waiting did not end with ABORTED");
Require(sceNpLookupNpId(aborted, nullptr, npId, nullptr) == InsufficientArgument, "sceNpLookupNpId", "arguments were not checked before the abort");
const int deleted = sceNpLookupCreateAsyncRequest(context, asyncParam);
Require(WaitInThread("sceNpLookupWaitAsync", sceNpLookupWaitAsync, deleted, &result, DeleteLookup) == InvalidId, "sceNpLookupWaitAsync", "waiting on a deleted request did not return INVALID_ID");
Require(sceNpLookupWaitAsync(deleted, &result) == InvalidId, "sceNpLookupWaitAsync", "an unknown request was taken");
sceNpLookupDeleteTitleCtx(context);
}
void CheckWordFilterAsync() {
const int context = sceNpWordFilterCreateTitleCtx(selfNpId);
Require(sceNpWordFilterSetTimeout(context, 0, 0, 0, 0, 0) == InvalidArgument, "sceNpWordFilterSetTimeout", "settings without any effect were taken");
Require(sceNpWordFilterSetTimeout(context, 0, 0, 0, 0, 9999999) == InvalidArgument, "sceNpWordFilterSetTimeout", "a receive timeout under 10 s was taken");
Require(sceNpWordFilterSetTimeout(context, 1, 0, 0, 0, 0) == 0, "sceNpWordFilterSetTimeout", "a context timeout was refused");
Require(sceNpWordFilterSetTimeout(1, 1, 0, 0, 0, 0) == InvalidId, "sceNpWordFilterSetTimeout", "a lookup id was taken");
const int sync = sceNpWordFilterCreateRequest(context);
const int async = sceNpWordFilterCreateAsyncRequest(context, asyncParam);
Require(sceNpWordFilterSetTimeout(async, 2, 1000000, 0, 0, 0) == 0, "sceNpWordFilterSetTimeout", "a request timeout was refused");
int result = 7;
Require(sceNpWordFilterWaitAsync(async, nullptr) == InvalidArgument, "sceNpWordFilterWaitAsync", "a null result was taken");
Require(sceNpWordFilterWaitAsync(sync, &result) == InvalidId, "sceNpWordFilterWaitAsync", "a synchronous request was taken");
Require(sceNpWordFilterWaitAsync(async + 100, &result) == InvalidId, "sceNpWordFilterWaitAsync", "an unknown request was taken");
Require(sceNpWordFilterCensorComment(async, "word", nullptr) == 0, "sceNpWordFilterCensorComment", "the asynchronous request did not start");
Require(sceNpWordFilterWaitAsync(async, &result) == 0 && result == SignedOut, "sceNpWordFilterWaitAsync", "the request did not finish with SIGNED_OUT");
sceNpWordFilterDeleteTitleCtx(context);
}
int main() {
std::memset(selfNpId, 0, sizeof(selfNpId));
std::strcpy(reinterpret_cast<char*>(selfNpId), "player_1");
CheckLookup();
CheckWordFilter();
CheckLookupAsync();
CheckWordFilterAsync();
if (failures != 0) return 1;
std::puts("NpUtility offline tests passed");
return 0;
+16
View File
@@ -287,6 +287,22 @@ int main(int argc, char** argv) {
RunLayout(argv[0], "--package-chunk-table", {{"playgo-chunk.dat", ChunkTableFile()}});
RunLayout(argv[0], "--package-chunk-table", {{"playgo-chunk.dat", ChunkTableFile("plgo")}});
RunLayout(argv[0], "--scenario-with-chunk-table", {{"playgo-chunk.dat", ChunkTableFile()}, {"playgo-scenario.json", "{}"}});
std::int64_t eta = -1;
std::int32_t speed = -1;
PlayGoInitParams fixtureInit{};
int validHandle = -1;
Require(scePlayGoInitialize(&fixtureInit) == 0);
Require(scePlayGoOpen(&validHandle, nullptr) == 0);
constexpr int badHandleError = static_cast<int>(0x80B20009);
constexpr int badPointerError = static_cast<int>(0x80B2000A);
Require(scePlayGoGetEta(-1, nullptr, 0, &eta) == badHandleError);
Require(scePlayGoGetEta(validHandle, nullptr, 0, nullptr) == badPointerError);
Require(scePlayGoGetInstallSpeed(-1, &speed) == badHandleError);
Require(scePlayGoGetInstallSpeed(0x506C476F, &speed) == badHandleError && speed == -1);
Require(scePlayGoGetInstallSpeed(validHandle, nullptr) == badPointerError);
Require(scePlayGoGetInstallSpeed(validHandle, &speed) == 0 && speed == 2);
Require(scePlayGoPrefetch(-1, nullptr, 0, 0) == badHandleError);
Require(scePlayGoClose(validHandle) == 0);
RunLayout(argv[0], "--scenario-only", {{"playgo-scenario.json", "{}"}});
RunLayout(argv[0], "--scenario-only-installed", {{"playgo-scenario.json", "{}"}});
RunLayout(argv[0], "--scenario-only-absent", {{"playgo-scenario.json", "{}"}});
+59 -16
View File
@@ -5,75 +5,118 @@
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <io.h>
#include <string>
#include <string_view>
#include <thread>
#include <windows.h>
static constexpr int SigUsr1 = 30;
static constexpr int Raises = 10;
static constexpr DWORD PipeBytes = 4096;
static constexpr std::uint64_t BlockBytes = 4u * PipeBytes;
static constexpr std::size_t RipOffset = 0xe0;
static constexpr ULONG ThreadSuspendCount = 35;
static void Require(bool value) { if (!value) std::abort(); }
static std::FILE* shared = nullptr;
static std::atomic<int> deliveries{0};
static std::atomic<int> released{0};
static std::atomic<bool> stop{false};
static std::atomic<int> blocks{0};
static std::atomic<int> written{0};
static std::atomic<int> blocked{0};
static std::atomic<HANDLE> writerThread{nullptr};
static bool delayReturn = false;
static void APS5_VABI Handler(int, void*) {
static bool InTestImage(std::uint64_t rip) {
const auto base = reinterpret_cast<std::uint64_t>(GetModuleHandleW(nullptr));
const auto* dos = reinterpret_cast<const IMAGE_DOS_HEADER*>(base);
const auto* nt = reinterpret_cast<const IMAGE_NT_HEADERS*>(base + dos->e_lfanew);
return rip >= base && rip - base < nt->OptionalHeader.SizeOfImage;
}
static bool Suspended(HANDLE thread) {
using Query = LONG(NTAPI*)(HANDLE, ULONG, void*, ULONG, ULONG*);
static const auto query = reinterpret_cast<Query>(GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQueryInformationThread"));
ULONG count = 0;
Require(query != nullptr && query(thread, ThreadSuspendCount, &count, sizeof(count), nullptr) >= 0);
return count > 0;
}
static void APS5_VABI Handler(int, void* context) {
const auto delivery = deliveries.fetch_add(1) + 1;
std::uint64_t rip = 0;
std::memcpy(&rip, static_cast<const std::byte*>(context) + RipOffset, sizeof(rip));
Require(InTestImage(rip));
while (released.load() < delivery) std::this_thread::yield();
}
static void* APS5_VABI Writer(void*) {
const std::string block(16u * PipeBytes, 'x');
while (!stop.load()) {
HANDLE self = nullptr;
Require(DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &self, THREAD_QUERY_INFORMATION, FALSE, 0) != 0);
writerThread.store(self);
const std::string block(BlockBytes, 'x');
for (int raise = 1; raise <= Raises; ++raise) {
Require(std::fwrite(block.data(), 1, block.size(), shared) == block.size());
blocks.fetch_add(1);
volatile std::uint64_t spin = 0;
for (std::uint64_t i = 0; i < 200000; ++i) spin = spin + i;
if (delayReturn) {
volatile std::uint64_t spin = 0;
for (std::uint64_t i = 0; i < 100000000; ++i) spin = spin + i;
}
written.store(raise);
while (deliveries.load() < raise) {
}
}
return nullptr;
}
int main() {
int main(int argc, char** argv) {
const std::string_view mode = argc > 1 ? argv[1] : "";
Require(mode.empty() || mode == "--delay-raise" || mode == "--delay-return");
delayReturn = mode == "--delay-return";
std::atomic<bool> done{false};
std::thread watchdog([&done] {
for (int i = 0; i < 25000 && !done.load(); ++i) std::this_thread::sleep_for(std::chrono::milliseconds(1));
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(25);
while (!done.load() && std::chrono::steady_clock::now() < deadline) std::this_thread::sleep_for(std::chrono::milliseconds(10));
if (!done.load()) std::abort();
});
HANDLE read = nullptr;
HANDLE write = nullptr;
Require(CreatePipe(&read, &write, nullptr, PipeBytes) != 0);
shared = _fdopen(_open_osfhandle(reinterpret_cast<intptr_t>(write), _O_WRONLY | _O_BINARY), "wb");
Require(shared != nullptr);
std::atomic<bool> draining{true};
Require(shared != nullptr && std::setvbuf(shared, nullptr, _IONBF, 0) == 0);
std::thread reader([&] {
char chunk[PipeBytes];
DWORD got = 0;
std::uint64_t total = 0;
int block = 0;
while (ReadFile(read, chunk, sizeof(chunk), &got, nullptr) != 0 && got != 0) {
if (draining.load()) std::this_thread::sleep_for(std::chrono::milliseconds(10));
total += got;
if (block == Raises || total <= static_cast<std::uint64_t>(block) * (BlockBytes + 1)) continue;
blocked.store(++block);
while (!Suspended(writerThread.load())) {
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
Require(written.load() < block);
}
});
Require(sceKernelInstallExceptionHandler(SigUsr1, reinterpret_cast<void*>(&Handler)) == 0);
Pthread writer = nullptr;
Require(scePthreadCreate(&writer, nullptr, Writer, nullptr, "pipe writer") == 0);
while (blocks.load() == 0) std::this_thread::yield();
for (int raise = 1; raise <= Raises; ++raise) {
while (blocked.load() < raise) std::this_thread::yield();
if (mode == "--delay-raise") std::this_thread::sleep_for(std::chrono::milliseconds(200));
Require(sceKernelRaiseException(writer, SigUsr1) == 0);
while (deliveries.load() < raise) std::this_thread::yield();
Require(std::fputc('y', shared) == 'y');
released.store(raise);
}
stop.store(true);
draining.store(false);
Require(scePthreadJoin(writer, nullptr) == 0);
Require(sceKernelRemoveExceptionHandler(SigUsr1) == 0);
std::fclose(shared);
reader.join();
CloseHandle(read);
CloseHandle(writerThread.load());
done.store(true);
watchdog.join();
}
@@ -241,6 +241,7 @@ int main() {
const auto previous = std::filesystem::current_path();
std::filesystem::current_path(root);
CheckUnmountedCalls();
Check(sceSaveDataSyncSaveDataMemory(nullptr) == static_cast<int>(0x809F0000), "null param rejected in memory sync");
Check(sceSaveDataInitialize3(nullptr) == 0, "SaveData initializes");
MountAndCheck("fresh", MountReadWrite | MountCreate2, 1u, "CREATE2 of a missing save");
MountAndCheck("fresh", MountReadWrite | MountCreate2, 0u, "CREATE2 of an existing save");
@@ -61,6 +61,7 @@ int main() {
for (const auto byte : vrr) Require(byte == std::byte{0xff});
Require(Rejects(handle, nullptr));
Require(Rejects(-1, nullptr));
Require(Rejects(0, &status));
Require(Rejects(-1, &status));
Require(Rejects(NEVER_OPENED_HANDLE, &status));
+5 -2
View File
@@ -12,14 +12,17 @@ static void Require(bool value) { if (!value) throw std::runtime_error("Kernel e
int main() {
*__error_nid_postfix() = 123;
const std::array<std::pair<std::int32_t, std::int32_t>, 6> cases{{
const std::array<std::pair<std::int32_t, std::int32_t>, 8> cases{{
{0, 0}, {1, SCE_KERNEL_ERROR_EPERM}, {2, SCE_KERNEL_ERROR_ENOENT},
{4, SCE_KERNEL_ERROR_EINTR}, {14, SCE_KERNEL_ERROR_EFAULT}, {22, SCE_KERNEL_ERROR_EINVAL}
{4, SCE_KERNEL_ERROR_EINTR}, {14, SCE_KERNEL_ERROR_EFAULT}, {22, SCE_KERNEL_ERROR_EINVAL},
{95, SCE_KERNEL_ERROR_ENOTRECOVERABLE}, {96, SCE_KERNEL_ERROR_EOWNERDEAD}
}};
for (const auto [input, expected] : cases) {
Require(sceKernelError(input) == expected);
Require(*__error_nid_postfix() == 123);
}
Require(SCE_KERNEL_ERROR_ENOTRECOVERABLE == static_cast<std::int32_t>(0x8002005fu));
Require(SCE_KERNEL_ERROR_EOWNERDEAD == static_cast<std::int32_t>(0x80020060u));
Require(sceKernelError(-1) == static_cast<std::int32_t>(0x8001ffffu));
Require(sceKernelError(std::numeric_limits<std::int32_t>::max()) == 0x1ffff);
Require(sceKernelError(std::numeric_limits<std::int32_t>::min()) == 0x20000);
+2
View File
@@ -574,6 +574,8 @@ if(BUILD_TESTING)
set_tests_properties(macos_tls_alu PROPERTIES TIMEOUT 60)
add_test(NAME linux_load_alignment COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_linux_load_alignment.py $<TARGET_FILE:relinker>)
add_test(NAME linux_load_bounds COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_linux_load_bounds.py $<TARGET_FILE:relinker>)
add_test(NAME linux_eh_frame_header COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_linux_eh_frame_header.py $<TARGET_FILE:relinker>)
add_test(NAME linux_dynamic_strings COMMAND ${Python3_EXECUTABLE}
@@ -22,6 +22,8 @@ namespace Elfpatcher::Linux {
namespace {
constexpr std::uint64_t kLinuxUserAddressEnd = 0x800000000000;
constexpr std::uint64_t kLazyPltGotBytes = 24;
constexpr std::uint64_t kPltGotEntryBytes = 8;
std::string Hex(const std::uint64_t value) {
std::ostringstream text;
@@ -101,6 +103,19 @@ void RequireFileRange(const std::vector<std::uint8_t>& buf, const Domain::Progra
throw Domain::RelinkerException("PT_LOAD at " + Hex(load.MappedAddress) + " has file bytes outside the input");
}
void RequireLoaded(const std::vector<Domain::ProgramHeader>& headers, const std::uint64_t address, const std::uint64_t size, const std::uint32_t flags, const std::string& what) {
for (const auto& header : headers)
if (header.Type == PT_LOAD && (header.Flags == 0 || (header.Flags & flags) == flags) && address >= header.MappedAddress && address - header.MappedAddress < header.MemorySize && size <= header.MemorySize - (address - header.MappedAddress))
return;
const auto range = size > 1 ? " (" + std::to_string(size) + " bytes) does not fit inside " : std::string(" is not inside ");
throw Domain::RelinkerException(what + " " + Hex(address) + range + ((flags & PF_X) != 0 ? "an executable" : "a") + " PT_LOAD segment");
}
std::uint64_t RelocationWidth(const std::vector<std::uint8_t>& relocations, const std::size_t entry) {
const auto type = static_cast<std::uint32_t>(Io::ReadU64(relocations, entry + 8));
return type == R_X86_64_PC32 || type == R_X86_64_32 ? 4 : 8;
}
void SeparateSharedLoadPages(std::vector<std::uint8_t>& buf, std::vector<Domain::ProgramHeader>& headers) {
std::vector<std::size_t> loads;
for (std::size_t index = 0; index < headers.size(); ++index) {
@@ -363,6 +378,12 @@ std::vector<std::uint8_t> LinuxElfPatcher::Patch(
throw Domain::RelinkerException("--trace-imports requires libc.prx among the libraries");
if (_fixedBase)
RequireOnlyDependencies(dynSection.DynamicSegmentData);
RequireLoaded(originalHeaders, Io::ReadU64(sourceElf, kEhdrEntryOffset), 1, PF_X, "e_entry");
for (const auto& [relocationTable, name] : {std::pair{&dynSection.RelaData, "RELA r_offset"}, std::pair{&dynSection.RelaPltData, "JMPREL r_offset"}})
for (std::size_t entry = 0; entry < relocationTable->size(); entry += kRelaEntrySize)
RequireLoaded(originalHeaders, Io::ReadU64(*relocationTable, entry), RelocationWidth(*relocationTable, entry), 0, name);
if (!dynSection.RelaPltData.empty())
RequireLoaded(originalHeaders, originalPltGotVaddr, lazyBinding ? kLazyPltGotBytes : kPltGotEntryBytes, 0, "DT_PLTGOT");
const auto traced = _traceImports ? TracedImports(dynSection) : std::vector<TracedImport>{};
const std::uint64_t base = _fixedBase.value_or(0);
const auto address = [this, base](const std::uint64_t vaddr) {
@@ -18,7 +18,7 @@ def fixture(header_count, with_frame=True):
image = bytearray(0x8000)
image[:16] = b"\x7fELF\x02\x01\x01" + bytes(9)
struct.pack_into("<HHIQQQIHHHHHH", image, 16,
3, 62, 1, 0x4000, 64, 0, 0, 64, 56, header_count, 64, 0, 0)
3, 62, 1, 0, 64, 0, 0, 64, 56, header_count, 64, 0, 0)
image[0x4000:0x4006] = b"\xb8\x2a\x00\x00\x00\xc3"
tags = [(5, 0x600), (10, 1), (6, 0x620), (11, 24), (7, 0x700), (8, 0), (9, 24), (0, 0)]
struct.pack_into("<IIQQQQQQ", image, 64,
@@ -14,7 +14,7 @@ def fixture():
image = bytearray(0x8000)
image[:16] = b"\x7fELF\x02\x01\x01" + bytes(9)
struct.pack_into("<HHIQQQIHHHHHH", image, 16,
3, 62, 1, 0x4000, 64, 0, 0, 64, 56, 6, 64, 0, 0)
3, 62, 1, 0, 64, 0, 0, 64, 56, 6, 64, 0, 0)
image[0x4000:0x4006] = b"\xb8\x2a\x00\x00\x00\xc3"
tags = [(5, 0x600), (10, 1), (6, 0x620), (11, 24), (7, 0x700), (8, 0), (9, 24), (0, 0)]
struct.pack_into("<IIQQQQQQ", image, 64,
@@ -0,0 +1,122 @@
"""Check that a Linux relink rejects an entry point, relocation offset or DT_PLTGOT outside PT_LOAD memory."""
from pathlib import Path
import struct
import subprocess
import sys
import tempfile
sys.path.insert(0, str(Path(__file__).resolve().parent))
from test_jmprel_table_bounds import fixture
PT_LOAD = 1
PT_SCE_RELRO = 0x61000010
PF_X = 1
PF_W = 2
PF_R = 4
R_X86_64_PC32 = 2
R_X86_64_32 = 10
E_ENTRY = 24
FIRST_LOAD_FLAGS = 64 + 4
SPARE_HEADER = 64 + 3 * 56
PLTGOT_VALUE = 0x400 + 9 * 16 + 8
OUTSIDE = 0x2000
ENTRY = 0x200
FIXED_BASE = 0x400000
def with_bss(image):
struct.pack_into("<IIQQQQQQ", image, SPARE_HEADER, PT_LOAD, PF_R | PF_W, 0x1000, 0x1000, 0x1000, 0, 0x1000, 0x1000)
return image
def with_relro(image):
struct.pack_into("<IIQQQQQQ", image, SPARE_HEADER, PT_SCE_RELRO, PF_R, 0, 0x1000, 0x1000, 0, 0x1000, 1)
return with_pltgot(image, 0x1800)
def with_entry(image, entry):
struct.pack_into("<Q", image, E_ENTRY, entry)
return image
def with_pltgot(image, value):
struct.pack_into("<Q", image, PLTGOT_VALUE, value)
return image
def without_load_flags(image):
struct.pack_into("<I", image, FIRST_LOAD_FLAGS, 0)
return image
def load_flags_at(elf, address):
phoff, = struct.unpack_from("<Q", elf, 0x20)
phentsize, phnum = struct.unpack_from("<HH", elf, 0x36)
headers = [struct.unpack_from("<IIQQQQQQ", elf, phoff + index * phentsize) for index in range(phnum)]
return [header[1] for header in headers if header[0] == PT_LOAD and header[3] <= address < header[3] + header[6]]
def relink(relinker, work, name, image, options, error):
source = work / (name + ".elf")
output = work / (name + ".out")
source.write_bytes(image)
result = subprocess.run([str(relinker), "--skip-sce-module", *options, str(source), str(output)],
capture_output=True, text=True, timeout=20)
if error is None:
assert result.returncode == 0 and output.exists(), (name, options, result.returncode, result.stdout, result.stderr)
elf = output.read_bytes()
output.unlink()
return elf
assert result.returncode == 2 and error in result.stderr and not output.exists(), (
name, options, result.returncode, result.stdout, result.stderr)
return None
def main():
relinker = Path(sys.argv[1]).resolve()
cases = [
("plt-valid", fixture(linux=True), None),
("rela-valid", fixture(linux=True, data_relocations=True), None),
("rela-in-bss", with_bss(fixture(linux=True, data_relocations=True, target=0x1ff8)), None),
("rela-in-relro", with_relro(fixture(linux=True, data_relocations=True, target=0x1ff8)), None),
("jmprel-in-relro", with_relro(fixture(linux=True, target=0x1ff8)), None),
("pc32-load-tail", fixture(linux=True, data_relocations=True, relocation_type=R_X86_64_PC32, target=0xFFC), None),
("abs32-load-tail", fixture(linux=True, data_relocations=True, relocation_type=R_X86_64_32, target=0xFFC), None),
("entry-outside", with_entry(fixture(linux=True), 0x7FFFF000),
"e_entry 0x7ffff000 is not inside an executable PT_LOAD segment"),
("entry-not-executable", with_entry(with_bss(fixture(linux=True)), 0x1000),
"e_entry 0x1000 is not inside an executable PT_LOAD segment"),
("rela-outside", fixture(linux=True, data_relocations=True, target=OUTSIDE),
"RELA r_offset 0x2000 (8 bytes) does not fit inside a PT_LOAD segment"),
("rela-past-load-end", fixture(linux=True, data_relocations=True, target=0xFFC),
"RELA r_offset 0xffc (8 bytes) does not fit inside a PT_LOAD segment"),
("abs32-past-load-end", fixture(linux=True, data_relocations=True, relocation_type=R_X86_64_32, target=0xFFE),
"RELA r_offset 0xffe (4 bytes) does not fit inside a PT_LOAD segment"),
("jmprel-outside", fixture(linux=True, target=OUTSIDE),
"JMPREL r_offset 0x2000 (8 bytes) does not fit inside a PT_LOAD segment"),
]
with tempfile.TemporaryDirectory(prefix="anyps5-linux-load-bounds-") as directory:
work = Path(directory)
for name, image, error in cases:
for options in ([], ["--lazy-binding"]):
relink(relinker, work, name, image, options, error)
for options, base in (([], FIXED_BASE), (["--lazy-binding"], 0)):
elf = relink(relinker, work, "entry-in-flagless-load", without_load_flags(fixture(linux=True)), options, None)
flags = load_flags_at(elf, base + ENTRY)
assert flags == [PF_R | PF_W | PF_X], ("entry-in-flagless-load", options, flags)
pltgot_cases = [
("pltgot-outside", OUTSIDE, "DT_PLTGOT 0x2000 (8 bytes) does not fit inside a PT_LOAD segment",
"DT_PLTGOT 0x2000 (24 bytes) does not fit inside a PT_LOAD segment"),
("pltgot-load-tail", 0xFF0, None, "DT_PLTGOT 0xff0 (24 bytes) does not fit inside a PT_LOAD segment"),
("pltgot-lazy-fits", 0xFE8, None, None),
]
for name, pltgot, eager_error, lazy_error in pltgot_cases:
image = with_pltgot(fixture(linux=True), pltgot)
relink(relinker, work, name, image, [], eager_error)
relink(relinker, work, name, image, ["--lazy-binding"], lazy_error)
print("Linux load bounds tests passed")
if __name__ == "__main__":
main()
@@ -11,6 +11,8 @@ POINTER = 0x810
TABLE = 0x820
FUNCTION = CODE + 0x40
SECOND_FUNCTION = CODE + 0x60
REJECTED = {"got-slot-address-size-underflow":
"RELA r_offset 0xfffff008 (8 bytes) does not fit inside a PT_LOAD segment"}
def rel(next_address, target):
@@ -62,6 +64,11 @@ def run(relinker, work, name, image, level=1):
source.write_bytes(image)
result = subprocess.run([str(relinker), "--skip-sce-module", "--registry", f"unused-filter={level}",
str(source), str(output)], capture_output=True, text=True, timeout=20)
if name in REJECTED:
assert result.returncode == 2 and REJECTED[name] in result.stderr and not output.exists(), (
name, result.returncode, result.stdout, result.stderr)
assert "CFG/GOT filtering: 1 -> 1;" in result.stdout, (name, result.stdout)
return True
assert result.returncode == 0 and output.exists(), (name, result.returncode, result.stdout, result.stderr)
registry = json.loads(output.with_suffix(".registry.json").read_text())
kept = len(registry)
@@ -344,7 +344,6 @@ DescriptorBindingPlan DescriptorBindingBuilder::Prepare(const IrBindingLayout& l
const auto first = PipelineSpecialization::BufferBase + index * PipelineSpecialization::BufferWords;
plan.specialization.push_back({first, descriptor.dwords[1] & 0xffff0000u});
plan.specialization.push_back({first + 1u, descriptor.dwords[3]});
plan.specialization.push_back({first + 2u, present ? 1u : 0u});
plan.specialization.push_back({first + 3u, misalignment});
}
if (buffer.formattedReadMask != 0u) {
@@ -107,7 +107,9 @@ RuntimeDescriptor descriptor(SpirvValueEmitContext& context, const IrValue& inst
}
if (memory.gpuDescriptor) faultIf(context, instruction, invalid, base, 0u, BdaAbi::FaultReason::InvalidDescriptor);
const auto stride = field(state, word1, 16u, 14u);
const auto present = runtime ? both(state, nonzero(state, context.Arg(*handle, 2u)), Binary(state, spv::OpLogicalOr, TypeBool(state), nonzero(state, context.Arg(*handle, 0u)), nonzero(state, field(state, word1, 0u, 16u)))) : nonzero(state, state.module.SpecializationConstant(u32, first + 2u, 0u));
auto baseHigh = runtime ? word1 : context.Arg(*handle, 1u);
if (!runtime && GcnGuestShaders()) baseHigh = Binary(state, spv::OpBitwiseAnd, u32, baseHigh, ConstantU32(state, GcnBufferWord1Mask));
const auto present = both(state, nonzero(state, context.Arg(*handle, 2u)), Binary(state, spv::OpLogicalOr, TypeBool(state), nonzero(state, context.Arg(*handle, 0u)), nonzero(state, field(state, baseHigh, 0u, 16u))));
const auto valid = memory.gpuDescriptor ? both(state, Unary(state, spv::OpLogicalNot, TypeBool(state), invalid), Binary(state, spv::OpINotEqual, TypeBool(state), base, BdaConstant(state, 0u))) : present;
return {base, context.Arg(*handle, 2u), stride, word3, both(state, nonzero(state, stride), nonzero(state, field(state, word1, 31u, 1u))), index, valid};
}
@@ -668,8 +670,7 @@ std::uint32_t formattedCall(SpirvValueEmitContext& context, const IrValue& instr
const auto word1 = state.module.SpecializationConstant(u32, first, 0u);
const auto stride = field(state, word1, 16u, 14u);
const auto swizzle = both(state, nonzero(state, stride), nonzero(state, field(state, word1, 31u, 1u)));
const auto valid = nonzero(state, state.module.SpecializationConstant(u32, first + 2u, 0u));
shared = {ConstantU32(state, 0u), parameters[3], stride, state.module.SpecializationConstant(u32, first + 1u, 0u), swizzle, parameters[0], valid, parameters[1], parameters[2]};
shared = {ConstantU32(state, 0u), parameters[3], stride, state.module.SpecializationConstant(u32, first + 1u, 0u), swizzle, parameters[0], nonzero(state, parameters[8]), parameters[1], parameters[2]};
}
const auto value = formatSwitch(context, instruction, shared, components, store, store ? parameters[9] : 0u);
if (store) state.module.AddFunction(spv::OpReturn);
+9
View File
@@ -0,0 +1,9 @@
# libSceFreeTypeOt technical debt
### Unknown function info
- [FT_Outline_GetInsideBorder, FT_Outline_GetOutsideBorder](../../../core/libs/prx/libSceFreeTypeOt/CMakeLists.txt) (libSceFreeTypeOl, libSceFreeTypeOt) - console prototypes, enum/layout equivalence, FreeType configuration and module/library versions are unverified. The existing LP64 outline layout and unsigned 16-bit counts follow bundled FreeType 2.13.3; border selection follows [ftstroke.c](https://github.com/freetype/freetype/blob/42608f77f20749dd6ddc9e0536788eaad70ea4b5/src/base/ftstroke.c), with the shifted polygon area from [ftoutln.c](https://github.com/freetype/freetype/blob/42608f77f20749dd6ddc9e0536788eaad70ea4b5/src/base/ftoutln.c) accumulated in 64 bits. Null positive-count arrays and out-of-range or non-increasing contour endpoints throw when reached; their console behavior is unknown. Tags and flags are not read, and input arrays remain caller-owned.
- The 140 `sceFreeTypeOtUnknown_*_nid_no_patch` exports in [Export.cpp](../../../core/libs/prx/libSceFreeTypeOt/Export.cpp) are present in the PS5 3.20 export inventory from [PS5-3.20_Libs](https://github.com/DNNDHH/PS5-3.20_Libs). Their prototypes and behavior are unverified. Each uses the no-parameter import-resolution convention and throws through `NotImplemented_nid_no_patch`; these declarations make no ABI or behavior claim.
- The FT_Outline_Get_Orientation, FT_Request_Size, FT_Sin, FT_Tan, FT_Vector_Unit, FT_New_Size, FT_Outline_Copy and FT_Outline_Decompose NIDs in [Export.cpp](../../../core/libs/prx/libSceFreeTypeOt/Export.cpp) are identified, but their provider-specific prototypes and behavior remain unverified. Their named helpers retain the explicit inventory aliases and throw; the separate libSceFreeType implementations do not supply this provider.
+4 -3
View File
@@ -2,11 +2,12 @@
### Silent stubs
- [sceNpGameIntentVshInitialize, sceNpGameIntentVshTerminate](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - VSH initialization and termination return 0 without performing shell operations, matching `sceNpGameIntentInitialize` and `sceNpGameIntentTerminate`
- [sceNpAppLauncherInitialize, sceNpAppLauncherTerminate](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - app-launcher lifecycle returns success without tracking launch state; `sceNpAppLauncherInitialize` accepts its init parameter without validation, following the silent-stub precedent of `sceNpGameIntentInitialize` — a title calls it early in boot, and returning success lets it continue
- [sceNpGameIntentVshInitialize, sceNpGameIntentVshTerminate](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - VSH initialization and termination return 0 without performing shell operations
- [sceNpAppLauncherInitialize, sceNpAppLauncherTerminate](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - app-launcher lifecycle returns success without tracking launch state; `sceNpAppLauncherInitialize` accepts its init parameter without validation — a title calls it early in boot, and returning success lets it continue
### Unknown function info
- [sceNpGameIntentReceiveIntent, sceNpGameIntentGetPropertyValueString](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - firmware 13.42 traces report `0x80553802` before `sceNpGameIntentInitialize` (precedence over argument validity), and `0x80553804` for invalid arguments (null pointer, empty key, or zero buffer size); `sceNpGameIntentTerminate` resetting initialized state is modeled lifecycle behavior rather than hardware-verified. `sceNpGameIntentReceiveIntent` reportedly requires `info->size == 0x4140` on firmware 13.42, while AnyPS5 `NpGameIntentInfo` is `0x560` bytes (matching fpPS4) and set by `sceNpGameIntentInfoInit`, so initialized objects are rejected by the receiver's measured size guard. Complete retail layout, output field offsets, and retail behavior of `sceNpGameIntentInfoInit` are unverified; expanding the type with unverified reserved bytes is not an evidence-backed solution.
- [sceNpAppLauncherLaunchApp, sceNpGameIntentGetUdsNpComId, sceNpGameIntentLaunchApp, sceNpGameIntentLaunchApp2, sceNpGameIntentNotifyAppLaunched, sceNpSessionServiceGetJoinedPartySession, sceNpSessionServiceGetRunningGamePlayerSession and sceNpSessionServiceSendPlayerSessionInvitations](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpGameIntent.c). Call sites in a title's executable give a partial prototype: `sceNpAppLauncherLaunchApp` takes `(user_id, uri struct, context)` and returns int — throws because no launcher-service semantics are derivable. The rest are declared without parameters solely to resolve known imports and make no ABI claim.
- [sceNpGameIntentInitParamInit](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - struct layout matches [fpPS4](https://github.com/red-prig/fpPS4/blob/master/src/np/ps4_libscenpgameintent.pas#L17) `SceNpGameIntentInitParam`; setting `size`, zeroing reserved bytes, and throwing on null pointer follow Sony SDK `*ParamInit` conventions but are unverified on retail hardware
- [sceNpGameIntentInfoInit](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - struct layout matches [fpPS4](https://github.com/red-prig/fpPS4/blob/master/src/np/ps4_libscenpgameintent.pas#L28) `SceNpGameIntentInfo`; setting `size`, `user_id = -1`, zeroing fields, and throwing on null pointer match `sceNpGameIntentReceiveIntent` defaults but are unverified on retail hardware
- [sceNpGameIntentInfoInit](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - struct layout matches [fpPS4](https://github.com/red-prig/fpPS4/blob/master/src/np/ps4_libscenpgameintent.pas#L28) `SceNpGameIntentInfo`; setting `size = 0x560`, `user_id = -1`, zeroing fields, and throwing on null pointer follow fpPS4 defaults, but reported firmware 13.42 behavior requires `0x4140` for `sceNpGameIntentReceiveIntent`, so the helper's `0x560` size does not match retail receiver defaults; the complete retail structure layout and retail behavior of `sceNpGameIntentInfoInit` remain unverified
+5 -5
View File
@@ -6,17 +6,17 @@
- [sceNpAppInfoIntCheckServiceAvailabilityA, sceNpAppInfoIntCheckServiceAvailabilityAll, sceNpAppInfoIntCheckServiceAvailabilityAllA, sceNpAppInfoIntCreateRequest, sceNpAppInfoIntDestroyRequest and sceNpAppInfoIntFinalize](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpAppInfoIntInitialize, sceNpAppLaunchLink2IntAbortRequest, sceNpAppLaunchLink2IntCreateRequest, sceNpAppLaunchLink2IntDestroyRequest, sceNpAppLaunchLink2IntFinalize and sceNpAppLaunchLink2IntGetCompatibleTitleIdList](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpAppLaunchLink2IntGetCompatibleTitleIdNum, sceNpAppLaunchLink2IntInitialize, sceNpAppLaunchLinkIntAbortRequest, sceNpAppLaunchLinkIntCreateRequest, sceNpAppLaunchLinkIntDestroyRequest and sceNpAppLaunchLinkIntFinalize](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpAppLaunchLinkIntGetCompatibleTitleIdList, sceNpAppLaunchLinkIntGetCompatibleTitleIdNum, sceNpAppLaunchLinkIntInitialize, sceNpBandwidthTestShutdownWithDetailedInfo and sceNpLookupAbortRequest](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpLookupCreateAsyncRequest, sceNpLookupCreateTitleCtxA, sceNpLookupNetAbortRequest and sceNpLookupNetCensorComment](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpAppLaunchLinkIntGetCompatibleTitleIdList, sceNpAppLaunchLinkIntGetCompatibleTitleIdNum, sceNpAppLaunchLinkIntInitialize and sceNpBandwidthTestShutdownWithDetailedInfo](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpLookupCreateTitleCtxA, sceNpLookupNetAbortRequest and sceNpLookupNetCensorComment](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpLookupNetConvertJidToNpId, sceNpLookupNetConvertNpIdToJid, sceNpLookupNetCreateRequest, sceNpLookupNetCreateTitleCtx, sceNpLookupNetDeleteRequest, sceNpLookupNetDeleteTitleCtx and sceNpLookupNetInit](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpLookupNetInitWithFunctionPointer, sceNpLookupNetInitWithMemoryPool, sceNpLookupNetIsInit, sceNpLookupNetNpId, sceNpLookupNetSanitizeComment, sceNpLookupNetSetTimeout, sceNpLookupNetTerm and sceNpLookupPollAsync](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpLookupSetTimeout, sceNpLookupWaitAsync, sceNpServiceChecker2IntAbortRequest, sceNpServiceChecker2IntCreateRequest, sceNpServiceChecker2IntDestroyRequest and sceNpServiceChecker2IntFinalize](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpLookupNetInitWithFunctionPointer, sceNpLookupNetInitWithMemoryPool, sceNpLookupNetIsInit, sceNpLookupNetNpId, sceNpLookupNetSanitizeComment, sceNpLookupNetSetTimeout and sceNpLookupNetTerm](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpServiceChecker2IntAbortRequest, sceNpServiceChecker2IntCreateRequest, sceNpServiceChecker2IntDestroyRequest and sceNpServiceChecker2IntFinalize](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpServiceChecker2IntGetServiceAvailability, sceNpServiceChecker2IntGetServiceAvailabilityA, sceNpServiceChecker2IntInitialize, sceNpServiceChecker2IntIsSetServiceType and sceNpServiceCheckerIntAbortRequest](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpServiceCheckerIntCreateRequest, sceNpServiceCheckerIntDestroyRequest, sceNpServiceCheckerIntFinalize, sceNpServiceCheckerIntGetAvailability, sceNpServiceCheckerIntGetAvailabilityList and sceNpServiceCheckerIntInitialize](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpServiceCheckerIntIsCached, sceNpTitleMetadataIntAbortRequest, sceNpTitleMetadataIntCreateRequest, sceNpTitleMetadataIntDeleteRequest, sceNpTitleMetadataIntGetInfo, sceNpTitleMetadataIntGetNpTitleId and sceNpUtilityInit](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpUtilityTerm and sceNpWordFilterCreateTitleCtxA](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
- [sceNpWordFilterSetTimeout and sceNpWordFilterWaitAsync](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - The list is the intersection of the export table of the console's libSceNpUtility (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo; names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpUtility.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified.
### Functional
- [NpLookup and NpWordFilter](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - offline only, not measured on a PS5: the argument checks and their order, the limit of 32 title contexts and 32 requests, the word filter id bases `0x01000001` and `0x02000001`, the 0x400-byte comment limit and single-use requests (`INVALID_TYPE`) follow shadPS4's [lookup](https://github.com/shadps4-emu/shadPS4/blob/91e6afa69b0a64b0afaa0281b80f8fbb0cd88136/src/core/libraries/np/np_utility/np_utility.cpp#L41-L222) and [word filter](https://github.com/shadps4-emu/shadPS4/blob/91e6afa69b0a64b0afaa0281b80f8fbb0cd88136/src/core/libraries/np/np_utility/np_utility.cpp#L705-L909) calls. `sceNpLookupNpId`, `sceNpWordFilterCensorComment` and `sceNpWordFilterSanitizeComment` then finish with `SCE_NP_ERROR_SIGNED_OUT` and write no NpId or comment; shadPS4's word filter accepts every comment as a stand-in for the server, so its result is not used
- [NpLookup and NpWordFilter waits and timeouts](../../../core/libs/prx/libSceNpUtility/Export.cpp) (libSceNpUtility) - IDs here carry no service: on the console, measured on firmware 13.42's module in an emulator (#10640), each ID holds its service in its high bits (Lookup requests `0x2xxxxxxx` and title contexts `0x4xxxxxxx`, word filter `0x02xxxxxx` and `0x04xxxxxx`), and `SetTimeout`, `AbortRequest`, `PollAsync` and `WaitAsync` find the object by those bits whichever service's call is made; here each call finds only its own service's IDs, which keep the ranges above. The timeouts are checked as measured and then have no effect, since no request reaches a server; the console stores them. A wait on a request deleted meanwhile returns `INVALID_ID`, unmeasured. The measured checks are followed: a null result first (`0x80550704`), a synchronous request (`0x80550307` for Lookup, `INVALID_ID` for the word filter), the timeout bounds and the 0x18-byte async parameter
+1
View File
@@ -9,6 +9,7 @@
### Unknown function info
- PPSA29866 imports declared without parameters, signatures unknown, added without an implementation because the title fails to load without them: [sceVideoOutSetFlipMaster and sceVideoOutClearFlipMaster](../../../core/libs/prx/libSceVideoOut/Unimplemented.cpp) (libSceVideoOut)
- [sceVideoOutColorSpaceConversionSettingsSetDefaultSdrToHdrConversion_, sceVideoOutColorSpaceConversionSettingsSetSdrToHdrConversion_, sceVideoOutAdjustColorSpaceConversion_, sceVideoOutSetGlobalBlendSpace](../../../core/libs/prx/libSceVideoOut/src/Output.cpp) (libSceVideoOut) - PPSA30630 imports these to configure color space conversion and global blend space. Color space conversion settings are accepted with size validation (32 bytes) but conversion blocks and blend space parameters are not applied to the presentation swapchain
- [sceVideoOutDeleteVrrActiveStatusEvent](../../../core/libs/prx/libSceVideoOut/src/Event.cpp) (libSceVideoOut, NID `-q-vbO859Tw`) - the name hashes to the NID PPSA02530 imports and is listed in [sce_symbols](https://github.com/zecoxao/sce_symbols) `aerolib.csv` at `fe2cc2948e`. The signature `(eq, handle)` and the behaviour, removing the handle's VRR status registration from the queue as the other `Delete*Event` calls do, follow sceVideoOutAddVrrActiveStatusEvent; no title has been observed calling it
- [sceVideoOutCursorEnable, sceVideoOutCursorDisable, sceVideoOutCursorSetImageAddress, sceVideoOutCursorSetPosition](../../../core/libs/prx/libSceVideoOut/Unimplemented.cpp) (libSceVideoOut, NIDs `UPZWCH8qTM4`, `fEPEwROdCwM`, `u-9bhWQApq8`, `N17AK88NdD0`) - the names hash to the NIDs Prospero Win (PPSA99995) imports, and the parameter lists `(handle, index, address)`, `(handle, index)`, `(handle, index, address)` and `(handle, index, x, y)` follow its [declarations](https://github.com/mpereiraesaa/prospero-win/blob/644e32e400c555e340d507383398c5d21b39b0b0/wine/ps5/pw_vulkan_radv.c#L65-L68). The cursor image's format and size and the console's return values are not known; the functions report that they are not implemented.
+4
View File
@@ -1,5 +1,9 @@
# libc technical debt
### Silent stubs
- [_Mtx_current_owns](../../../core/libs/prx/libc/src/Threads.cpp) (libSceLibcInternal, implemented in libc, NID `VYQwFs4CC4Y`) returns 1 and ignores its argument. This follows the PS5 12.00 body reading reported in [PR8859](https://github.com/boykopovar/AnyPS5/pull/8859), rather than inspecting host mutex ownership. The firmware observation is attributed to that contributor; other firmware versions and the argument's console type remain unverified.
### Unknown function info
- [strtonum](../../../core/libs/prx/libc/src/StringExtensions.cpp) (libc) is unverified on the console. Its signature, decimal parsing, inclusive bounds, error strings, error precedence and errno preservation follow [FreeBSD 11.4 strtonum.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/stdlib/strtonum.c). Parsing uses the host `strtoll` with base 10. It is not exported by `libSceLibcInternal.sprx` on a PS5 (firmware 13.60), so a payload process cannot call it there
+1 -1
View File
@@ -85,7 +85,7 @@
- [Map hints inside a reservation](../../../core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp) (libkernel) - `sceKernelMapDirectMemory` and the flexible and anonymous maps place a mapping without `MAP_FIXED` at its hint address when the whole range lies in a range still reserved by `sceKernelReserveVirtualRange`, as if `MAP_FIXED` were set. Taken from PPSA01474, which reserves 64 GiB and then maps its heaps inside it with hints and no `MAP_FIXED`, and uses the hint addresses afterwards; not measured on the console. Hints outside a reservation, or over a range that is partly mapped, stay hints
- [Fixed anonymous mmap](../../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp) (libkernel) - `MAP_PRIVATE | MAP_ANON | MAP_FIXED` replaces the requested range through the existing guest mapping machinery and zeroes the new memory. `MAP_EXCL` is checked under the allocation-registry mutation lock, including virtual reservations, and returns `ENOMEM` without changing an occupied range; without `MAP_FIXED` it is `EINVAL`. Lengths are rounded to 16 KiB and fixed addresses must be aligned. The complete rounded range must lie inside the guest arena or, for POSIX fixed anonymous `mmap` on Windows, one current or registered native thread stack; other mapping APIs do not opt into stack replacement. Native stack replacement keeps the reservation, decommits and recommits fresh pages, and rejects foreign allocations, ranges spanning reservations and host `PAGE_GUARD` pages before mutation. Current-stack ranges must end at least 64 KiB below the active host frame; callers replacing another thread's stack must coordinate with that thread. If recommitting fails, the call returns `ENOMEM` with the range still reserved; thread cleanup attempts to recommit it before restoring write access. This follows FreeBSD 11.4 [argument validation and rounding](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/vm/vm_mmap.c#L226-L299), [fixed replacement](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/vm/vm_map.c#L1455-L1464), and [collision errno](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/vm/vm_mmap.c#L1604-L1613). The console behaviour has not been measured. Linux and Windows accept addresses from 64 KiB that no host mapping occupies, such as the 0x400000 imported by PICO-8 PPSA99808; macOS keeps the 0x200000000 start. Non-fixed private anonymous mappings keep their hint; null hints still use the 8 GiB area. On Windows the host's `KUSER_SHARED_DATA` always occupies 0x7ffe0000, so Wine's shared user data page (Forever Launcher PPSA99030) cannot be placed there. The console address-space layout is unmeasured; executable placement outside the host arena is tracked in #5217. Static catalog disassembly finds `0x1012` in PPSA98001/PPSA99808 and `0x5012` in PPSA99808; these titles were not run.
- [Mapping hints with no room above them](../../../core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp) (libkernel) - a mapping without `MAP_FIXED` whose hint lies above the guest address space, or has no free range at or above it, is placed from the bottom of the guest arena, as FreeBSD 11.4's [`vm_mmap_object`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/vm/vm_mmap.c#L1569) retries through [`vm_map_find_min`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/vm/vm_map.c#L1555-L1584) from its minimum address. FreeBSD's minimum is the end of the largest possible data segment rather than the start of the arena, and the console's address-space layout is not measured. PS5CEMU-HAR (PPSA99360) gives `sceKernelReserveVirtualRange` a hint near its own code, which is a host address here because the relinked executable lies outside the guest arena, so its JIT gets memory more than 2 GiB from its code and does not start
- [_sceKernelRtldThreadAtexitIncrement, _sceKernelRtldThreadAtexitDecrement](../../../core/libs/prx/libkernel/System/src/Rtld.cpp) (libkernel) - the argument is the `dso` address a title's `libc.prx` passes to `__cxa_thread_atexit` (PPSA14632), which fails when Increment returns nonzero. As in the [fpPS4](https://github.com/red-prig/fpPS4) reimplementation, Increment takes a reference on the loaded image containing the address and Decrement releases one; the variants without the leading underscore are assumed to match
- [_sceKernelRtldThreadAtexitIncrement, _sceKernelRtldThreadAtexitDecrement](../../../core/libs/prx/libkernel/System/src/Rtld.cpp) (libkernel) - the argument is the `dso` address a title's `libc.prx` passes to `__cxa_thread_atexit` (PPSA14632), which fails when Increment returns nonzero. As in a payload process on a PS5 (firmware 13.60), Increment returns -1 for an address outside every module (null, stack, heap) and 0 otherwise, and Decrement never fails on an address outside every module or after a matching Increment; the console's Decrement returns nothing, here 0. A Decrement on a module's address without a matching Increment was not measured and throws here. The module's `ref_count` did not change on the console. As in [fpPS4](https://github.com/red-prig/fpPS4), Increment here takes a reference on the image and Decrement releases one. The variants without the leading underscore are not exported by firmware 13.60's `libkernel_sys`; here they match
- [pthread_cleanup_push, pthread_cleanup_pop](../../../core/libs/prx/libkernel/Pthread/src/Thread.cpp) (libkernel, NIDs `4ZeZWcMsAV0`, `RVxb0Ssa5t0`) share the caller-record cleanup list with `__pthread_cleanup_push_imp` and `__pthread_cleanup_pop_imp`, following [FreeBSD 11.4 thr_clean.c](https://github.com/freebsd/freebsd-src/blob/d106002967eff712894ab8b5337bbbff106ce5e0/lib/libthr/thread/thr_clean.c). The firmware body reading reported by [PR8428](https://github.com/boykopovar/AnyPS5/pull/8428) also sets the thread's unwind-disabled flag in the heap form; that flag is not modeled here, and runtime-created guest threads skip guest-frame unwinding in these exit paths. The firmware observation is attributed to that contributor, not independently reproduced. Heap records use C++ `new` here, whereas the FreeBSD reference leaves the cleanup list unchanged if `malloc` fails; console allocation-failure behavior and its interaction with unwinding remain unverified.
- [tcgetattr, tcsetattr](../../../core/libs/prx/libkernel/File/src/Terminal.cpp) (libkernel) - every file and pipe descriptor is taken as not a terminal (`ENOTTY`), and a socket gets `EADDRNOTAVAIL` for `AF_INET` and `EOPNOTSUPP` otherwise, as FreeBSD's `in_control` and `pru_control_notsupp` answer a termios ioctl; what descriptors 0 to 2 of a title are on the console, and whether any of them is a terminal there, is unknown
- [cfsetspeed](../../../core/libs/prx/libkernel/File/src/Terminal.cpp) (libkernel, NID `C1MQ6OaXRjo`) - console behavior and module/library versions are unverified. The prototype, uint32 speed and 44-byte termios layout (input speed at 36, output speed at 40) follow the public PS5 payload SDK [termios.h](https://github.com/ps5-payload-dev/sdk/blob/a7685ce8e97ce1f8a4f38253a41d54241c9124f1/include/freebsd/termios.h) and [sys/_termios.h](https://github.com/ps5-payload-dev/sdk/blob/a7685ce8e97ce1f8a4f38253a41d54241c9124f1/include/freebsd/sys/_termios.h). The module association follows its libkernel stub table and the NID rehashes to the aerolib name. Both speed fields accept every uint32 value and no flags or control characters change, as [FreeBSD 11.4 termios.c](https://github.com/freebsd/freebsd-src/blob/d106002967eff712894ab8b5337bbbff106ce5e0/lib/libc/gen/termios.c) does. No terminal I/O is performed. Null pointers throw where the reference dereferences them.
+1
View File
@@ -28,6 +28,7 @@
- [fork](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) throws: creating a child process is not supported. PPSA19111 imports it and fails to load without it. Its engine calls `fork` in `Posix_Daemonize`, only with `-daemonize`, and in `Platform_ShellExecute` before `execvp("xdg-open")`, which the main menu's Previews button reaches, so pressing Previews ends in this throw. [setsid](../../../core/libs/prx/libkernel_sys/Export.cpp) fails with `EPERM`, as FreeBSD's does for a process group leader, since the guest is taken to lead its own process group (see `kill`). [umask](../../../core/libs/prx/libkernel_sys/Export.cpp) keeps the guest's mask (initially 022, masked to 07777) and returns the previous one, Linux named shared-memory creation applies it, but ordinary files are still created with the host process's mask
- [sceKernelGetModuleInfoInternal and sceKernelGetModuleListInternal](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) answer for every loaded module, system modules included. `sceKernelGetModuleInfoInternal` fills the 0x1A8-byte `SceKernelModuleInfoEx` of a module handle the way `sceKernelGetModuleInfoFromAddr` does for an address, with [shadPS4's checks](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/kernel/process.cpp#L243-L258) in their order: `EFAULT` for a null pointer, `EINVAL` when `st_size` is not 0x1A8, `ESRCH` for an unknown handle. `sceKernelGetModuleListInternal` returns the same list as `sceKernelGetModuleList`, as [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd4/kernel/ps4_libkernel.pas#L852-L858) does; fpPS4 marks that as a guess, and neither is checked on a PS5
- [chroot](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) follows FreeBSD 11.4's [`sys_chroot`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/vfs_syscalls.c#L776-L807) and [`pwd_chroot`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_descrip.c#L3059-L3085) for a root caller: `EFAULT` for a null path, `ENOENT` for an empty or missing one, `ENAMETOOLONG` for a path of 1024 bytes or more or a component over 255, and `ENOTDIR` for a path that is not a directory or passes through a file. A directory that is the guest root (`/`, `/..`, or `.` after `fchdir` to a descriptor of `/`) succeeds and changes nothing, as the root stays the same directory; the guest is taken never to have changed its root, so open directory descriptors do not make it fail with `EPERM` (`kern.chroot_allow_open_directories` 1). Any other directory throws, naming the path: the guest path resolver has a fixed root and mount aliases (`/app0`, `/savedata0`) that do not move with it. On the console a title starts in a sandbox root, where `pwd_chroot` refuses with `EPERM` while a directory is open; whether that holds for titles, and whether a title holds `PRIV_VFS_CHROOT`, are unverified. Porpoise (PPSA99764) weak-imports it and calls it only as `fchdir` to its saved descriptor of `/` then `chroot(".")`, to put its sandbox root back after a jailbreak daemon replaced it ([`put_root_back`](https://github.com/elripalda/Porpoise-Dolphin-Emulator-for-PS5/blob/2ed1ab5a06995aa198f7bccd4da0f90c3fc29d39/src/porpoise_jailbreak.cpp#L303-L331))
- [Console identity](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) - `get_authinfo` errors and capability bits remain unverified on PS5; they follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/396c2a09a38d22eaa02aab32c1967263dceab95f/src/core/libraries/kernel/kernel.cpp), leaving the authority ID and other capability, attribute and credential words zero. The host models a retail unit (`IsCEX` 1, `IsDevKit` 0). The system OpenPSID reuses the ordinary ID; firmware 13.42 reads a separate `machdep.openpsid_for_sys`, whose value is unknown. Null returns positive 22 per the [module report](https://github.com/boykopovar/AnyPS5/pull/10524#issuecomment-6393566914).
- [fchown](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) - ownership is not modelled: the guest's own owner and group (0) and the unchanged value (-1) are accepted, a descriptor that is not open fails with `EBADF`, and any other owner or group throws, naming it. The check reads the descriptor with `fstat`, ignoring the CRT's invalid-parameter handler on Windows as `DescriptorControl` does. The console's ownership rules are unverified. Morrowind (`mshivam019/morrowind-ps5`) imports it
- [chown, lchown and fchownat](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) - ownership is not modelled, as for `fchown`: owner and group 0 and the unchanged value (-1) are accepted, and any other owner or group throws, naming it. The path is looked up first, as in FreeBSD 11's `kern_fchownat`, with `stat`, with `lstat` for `lchown`, and with `fstatat` for `fchownat`, so a lookup error is returned before the owner is checked and nothing is changed on the host. `fchownat` flags other than `AT_SYMLINK_NOFOLLOW` (0x200) return `EINVAL`, as in FreeBSD 11. The console's ownership rules are unverified
- [linkat](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) resolves `AT_FDCWD` (-100) and absolute paths as `link` does, and flags other than `AT_SYMLINK_FOLLOW` (0x400) return `EINVAL`, as in FreeBSD 11. Any other relative path is joined to the descriptor's host directory, found the same way as for `fchdir`, so guest path aliases and case-insensitive name matching do not apply to it, and a `..` component is not kept inside the guest root. It returns `EBADF` for a descriptor that is not open and `ENOTDIR` for one that is not a directory. A symbolic link as the source without `AT_SYMLINK_FOLLOW` throws, where FreeBSD links the symbolic link itself. Console behaviour is unverified