merge: main into test/player-review-dialog-mode-1-and-null-result

This commit is contained in:
Dean Galvin committed 2026-10-27 22:08:34 +13:00
commit 560a9c0be6
305 files changed
+14620 -1423

No files matched your search

+28 -5
View File
@@ -56,6 +56,7 @@ jobs:
relinker: ${{ steps.gate.outputs.relinker }}
register-context: ${{ steps.gate.outputs.register-context }}
write-watch: ${{ steps.gate.outputs.write-watch }}
mingw-host-tests: ${{ steps.gate.outputs.mingw-host-tests }}
full: ${{ steps.gate.outputs.full }}
relinker-hosts: ${{ steps.gate.outputs.relinker-hosts }}
steps:
@@ -138,6 +139,10 @@ jobs:
registerContext: edits('register-context') ||
touches('core/libs/tests/register_context/', 'core/libs/tests/RegisterContext.cpp', libc),
writeWatch: edits('write-watch') || touches('core/libs/tests/write_watch/', 'core/libs/tests/WriteWatch.cpp', libc),
mingwHostTests: edits('eligibility') || edits('windows') || touches(
'core/libs/', 'tools/build_mingw_host_only_tests.py',
'tools/tests/test_mingw_host_only_test_targets.py', 'CMakeLists.txt'
),
};
};
const relinkerHosts = (full, linux) => JSON.stringify([
@@ -172,6 +177,7 @@ jobs:
core.setOutput('relinker', run.relinker);
core.setOutput('register-context', run.registerContext);
core.setOutput('write-watch', run.writeWatch);
core.setOutput('mingw-host-tests', false);
core.setOutput('full', true);
core.setOutput('relinker-hosts', relinkerHosts(true, true));
await core.summary.addRaw(
@@ -261,6 +267,7 @@ jobs:
core.setOutput('relinker', relinker);
core.setOutput('register-context', registerContext);
core.setOutput('write-watch', writeWatch);
core.setOutput('mingw-host-tests', !manual && run.mingwHostTests);
core.setOutput('full', full);
core.setOutput('relinker-hosts', relinkerHosts(full, linux));
const merged = pulls.map((pull) => `PR #${pull.number} at ${pull.head} merged into main at ${main.commit.sha}`);
@@ -526,7 +533,7 @@ jobs:
env:
BASE: ${{ needs.eligibility.outputs.sha }}
run: |
git diff --name-only "$BASE" HEAD > changed.txt
git diff --name-only -z "$BASE" HEAD -- > changed.txt
python3 tools/select_tests.py select build test-graph.json changed.txt
- id: build
run: |
@@ -561,7 +568,7 @@ jobs:
windows:
needs: eligibility
if: ${{ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && needs.eligibility.outputs.full == 'true' }}
if: ${{ ((github.event_name == 'push' || github.event_name == 'workflow_dispatch') && needs.eligibility.outputs.full == 'true') || (github.event_name == 'pull_request' && needs.eligibility.outputs.mingw-host-tests == 'true') }}
runs-on: windows-latest
timeout-minutes: 45
env:
@@ -646,6 +653,9 @@ jobs:
with:
path: configure-seed.cmake
key: configure-seed-${{ env.WINLIBS_ZIP }}-${{ steps.submodules.outputs.key }}-${{ hashFiles('CMakeLists.txt', 'cmake/**', 'core/libs/CMakeLists.txt', 'tools/configure_seed.py', '.github/workflows/build.yml') }}
- if: github.event_name == 'pull_request'
shell: bash
run: mkdir -p build/.cmake/api/v1/query && touch build/.cmake/api/v1/query/codemodel-v2
- shell: cmd
run: if exist configure-seed.cmake (cmake -C configure-seed.cmake -S . -B build %CONFIGURE% "-DANYPS5_AGC_EXECUTION_TESTS=F16ToF32Unpack;F64FmaSubnormals;RegistrationPrepareShutdown") else (cmake -S . -B build %CONFIGURE% "-DANYPS5_AGC_EXECUTION_TESTS=F16ToF32Unpack;F64FmaSubnormals;RegistrationPrepareShutdown")
- if: steps.seed-cache.outputs.cache-hit != 'true'
@@ -678,17 +688,30 @@ jobs:
BASE: ${{ needs.eligibility.outputs.sha }}
shell: cmd
run: |
git diff --name-only %BASE% HEAD > changed.txt
git diff --name-only -z %BASE% HEAD -- > changed.txt
python tools/select_tests.py select build test-graph.json changed.txt
- id: build
if: github.event_name != 'pull_request'
shell: cmd
run: if exist build\select.ninja (ninja -C build -f select.ninja selected) else (cmake --build build)
- name: List changed files for host-only test linking
if: github.event_name == 'pull_request'
env:
BASE: ${{ needs.eligibility.outputs.sha }}
run: git diff --name-only "$BASE" HEAD > changed.txt
- name: Link changed host-only core/libs test executables
if: github.event_name == 'pull_request'
shell: cmd
run: python tools/build_mingw_host_only_tests.py build changed.txt
- id: libs
if: github.event_name != 'pull_request'
shell: cmd
run: cmake --build build --target libs
- shell: cmd
- if: github.event_name != 'pull_request'
shell: cmd
run: python tools/select_tests.py ctest build --output-on-failure --timeout 120 --parallel 4 --output-log ctest.log --output-junit ctest.xml
- run: python tools/check_skips.py build/ctest.xml --job windows
- if: github.event_name != 'pull_request'
run: python tools/check_skips.py build/ctest.xml --job windows
- name: Write the test graph of main
if: ${{ !cancelled() && github.event_name == 'push' && steps.build.outcome == 'success' && steps.libs.outcome == 'success' }}
continue-on-error: true
+1 -1
+5
View File
@@ -149,6 +149,8 @@ if(BUILD_TESTING)
add_test(NAME pad_command_file COMMAND pad_command_file_tests)
set_tests_properties(pad_command_file PROPERTIES TIMEOUT 20)
if(Python3_Interpreter_FOUND)
add_test(NAME mingw_host_only_test_targets COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_mingw_host_only_test_targets.py)
add_test(NAME shader_cache_native_paths COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler/tests/test_shader_cache_native_paths.py
${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler/ShaderCacheVersion.cmake ${CMAKE_COMMAND})
@@ -294,6 +296,9 @@ if(BUILD_TESTING)
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_trace_digest.py)
add_test(NAME symbolize_samples COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_symbolize_samples.py)
add_test(NAME ab_split COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_ab_split.py)
set_tests_properties(ab_split PROPERTIES TIMEOUT 20)
add_test(NAME conventions_bom_shebang COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_conventions_bom_shebang.py)
set_tests_properties(conventions_bom_shebang PROPERTIES TIMEOUT 20)
+4
View File
@@ -258,6 +258,8 @@ endif()
add_custom_target(libs DEPENDS ${PATCHED_OUTPUTS})
include(prx/libSceLibreSsl/ProtocolSelection.cmake)
if(BUILD_TESTING)
add_test_executable(guest_posix_scalar_io_tests tests/GuestPosixScalarIo.cpp)
target_include_directories(guest_posix_scalar_io_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
@@ -1097,3 +1099,5 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceGnmDriverForNeoMode>")
endif()
endif()
include(prx/libSceFreeType/CurveBounds.cmake)
+3 -1
View File
@@ -1432,7 +1432,9 @@ struct NetEpollEvent {
struct NetCtlNatInfo {
std::uint32_t size;
std::uint8_t opaque[12];
std::int32_t stun_status;
std::int32_t nat_type;
std::uint32_t mapped_addr;
};
static_assert(sizeof(NetCtlNatInfo) == 16);
+38 -9
View File
@@ -327,6 +327,7 @@ set(agcDriverSources
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/PipelineLibrary.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/PipelineCache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/PipelineJournal.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/RuntimeSwitches.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/Resources.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/DeferredDestruction.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/PlaceholderImages.cpp
@@ -535,6 +536,18 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test_executable(agc_runtime_switches_tests tests/RuntimeSwitches.cpp Graphics/src/RuntimeSwitches.cpp)
target_include_directories(agc_runtime_switches_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_runtime_switches_tests PRIVATE libc)
configure_windows_unwind(agc_runtime_switches_tests)
add_test(NAME agc_runtime_switches COMMAND agc_runtime_switches_tests)
add_test(NAME agc_runtime_switches_refused COMMAND agc_runtime_switches_tests refused)
set_tests_properties(agc_runtime_switches agc_runtime_switches_refused PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(agc_runtime_switches agc_runtime_switches_refused PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test_executable(agc_async_log_tests tests/AsyncLog.cpp)
target_include_directories(agc_async_log_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_async_log_tests PRIVATE libc)
@@ -3541,7 +3554,7 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
if(WIN32)
if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_test_executable(agc_driver_frame_capture_eop_tests tests/FrameCaptureEop.cpp Execution/src/FrameCapture.cpp)
target_include_directories(agc_driver_frame_capture_eop_tests PRIVATE ${LIBS_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/core/shader/recompiler ${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR} ${ANYPS5_SPIRV_HEADERS_INCLUDE_DIR})
target_link_libraries(agc_driver_frame_capture_eop_tests PRIVATE libSceAgcDriver libkernel libc libzstd_static)
@@ -3551,8 +3564,10 @@ if(BUILD_TESTING)
set_tests_properties(agc_driver_frame_capture_eop PROPERTIES
ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_FRAME_CAPTURE=${CMAKE_CURRENT_BINARY_DIR}/frame-capture-eop;APS5_FRAME_CAPTURE_AT_FLIP=1;APS5_FRAME_CAPTURE_FRAMES=3"
TIMEOUT 120)
set_tests_properties(agc_driver_frame_capture_eop PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
if(WIN32)
set_tests_properties(agc_driver_frame_capture_eop PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
add_test_executable(agc_driver_frame_replay_state_tests tests/FrameReplayState.cpp Execution/src/FrameCapture.cpp)
@@ -3561,9 +3576,15 @@ if(BUILD_TESTING)
set_target_properties(agc_driver_frame_replay_state_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_driver_frame_replay_state_tests)
set(agcFrameReplayModes order tokens blocking)
if(WIN32)
if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
list(APPEND agcFrameReplayModes zero shader shader_host)
endif()
if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
list(APPEND agcFrameReplayModes unwatched)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
list(APPEND agcFrameReplayModes shared_page host_neighbors)
endif()
foreach(mode IN LISTS agcFrameReplayModes)
set(capture "${CMAKE_CURRENT_BINARY_DIR}/frame-replay-${mode}")
add_test(NAME agc_driver_frame_replay_${mode} COMMAND agc_driver_frame_replay_state_tests ${mode} "${capture}")
@@ -3572,13 +3593,17 @@ if(BUILD_TESTING)
set_tests_properties(agc_driver_frame_replay_${mode} PROPERTIES ENVIRONMENT
"APS5_FRAME_CAPTURE=${capture};APS5_FRAME_CAPTURE_AT_FLIP=1;APS5_FRAME_CAPTURE_FRAMES=1")
endif()
if(mode STREQUAL "unwatched")
set_tests_properties(agc_driver_frame_replay_${mode} PROPERTIES ENVIRONMENT
"APS5_NO_WRITE_WATCH=1;APS5_FRAME_CAPTURE=${capture};APS5_FRAME_CAPTURE_AT_FLIP=1;APS5_FRAME_CAPTURE_FRAMES=1")
endif()
if(WIN32)
set_tests_properties(agc_driver_frame_replay_${mode} PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endforeach()
if(WIN32)
if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_test_executable(agc_driver_frame_replay_tests tests/FrameReplay.cpp Execution/src/FrameCapture.cpp)
target_include_directories(agc_driver_frame_replay_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_driver_frame_replay_tests PRIVATE libSceAgc libSceAgcDriver libkernel libc libzstd_static)
@@ -3589,14 +3614,18 @@ if(BUILD_TESTING)
set_tests_properties(agc_driver_frame_replay PROPERTIES
ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_FRAME_CAPTURE=${CMAKE_CURRENT_BINARY_DIR}/frame-replay-capture;APS5_FRAME_CAPTURE_AT_FLIP=1;APS5_FRAME_CAPTURE_FRAMES=2"
TIMEOUT 120)
set_tests_properties(agc_driver_frame_replay PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
if(WIN32)
set_tests_properties(agc_driver_frame_replay PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_driver_frame_replay_concurrent COMMAND agc_driver_frame_replay_tests $<TARGET_FILE:agc_frame_replay> concurrent)
set_tests_properties(agc_driver_frame_replay_concurrent PROPERTIES
ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_FRAME_CAPTURE=${CMAKE_CURRENT_BINARY_DIR}/frame-replay-concurrent-capture;APS5_FRAME_CAPTURE_AT_FLIP=1;APS5_FRAME_CAPTURE_FRAMES=8"
TIMEOUT 120)
set_tests_properties(agc_driver_frame_replay_concurrent PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
if(WIN32)
set_tests_properties(agc_driver_frame_replay_concurrent PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
endif()
@@ -2,6 +2,7 @@
#include "prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp"
#include "ThreadOwned.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Diagnostics.hpp"
#include "prx/libSceAgcDriver/Graphics/include/RuntimeSwitches.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawPipeline.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Synchronization/SynchronizationStatistics.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Synchronization/DeferredLabels.hpp"
@@ -408,6 +409,16 @@ void Driver::execute(const Submission& submission) {
++flipsCounted;
if (batchesAtFlip != 0) flipSerial = batchesAtFlip;
flipBatchesUnsignaled += unsignaledAtFlip;
if (Graphics::AbConfigured()) {
static const auto abClock = std::chrono::steady_clock::now();
std::vector<std::string> abLines;
{
GuestMemory::TagGpuLockSite(GuestMemory::GpuLockSite::Flush);
std::lock_guard gpuLock(GuestMemory::GpuMutex());
abLines = Graphics::AbAtPresent(flipsCounted.load(), std::chrono::duration<double>(std::chrono::steady_clock::now() - abClock).count());
}
for (const auto& line : abLines) AgcDriver::ProfilePrint_nid_no_patch("%s", line.c_str());
}
timing.Mark("flip_flush");
timing.FinishPacket(submission.serial, cursor, 0xffffu, count);
@@ -8,6 +8,7 @@
#include "prx/libc/include/General.hpp"
#include <algorithm>
#include <array>
#include <cerrno>
#include <charconv>
#include <cstdio>
#include <cstdlib>
@@ -26,6 +27,10 @@
#include <unistd.h>
extern char** environ;
#endif
#ifdef __linux__
#include "prx/libc/include/specifics/linux/HostMapping.hpp"
#include <sys/uio.h>
#endif
namespace AgcDriver {
namespace {
@@ -89,6 +94,39 @@ std::uint64_t flipValue(std::int64_t value) {
return static_cast<std::uint64_t>(value);
}
#ifdef __linux__
std::uint32_t windowsProtection(const HostMapping::Mapping& mapping) {
if (mapping.executable) return mapping.writable ? 0x40u : mapping.readable ? 0x20u : 0x10u;
if (mapping.writable) return 0x04u;
return mapping.readable ? 0x02u : 0x01u;
}
bool pageReadable(std::uint64_t address) {
char byte = 0;
iovec local{&byte, 1};
iovec remote{reinterpret_cast<void*>(static_cast<std::uintptr_t>(address)), 1};
const auto read = process_vm_readv(getpid(), &local, 1, &remote, 1, 0);
if (read == 1) return true;
if (read < 0 && errno == EFAULT) return false;
throw std::runtime_error(std::string("frame capture: cannot probe a file mapping with process_vm_readv: ") + (read < 0 ? std::strerror(errno) : "nothing was read"));
}
std::uint64_t fileBackedEnd(const HostMapping::Mapping& mapping) {
if (mapping.inode == 0 || !mapping.readable) return mapping.end;
const auto page = static_cast<std::uint64_t>(sysconf(_SC_PAGESIZE));
if (pageReadable(mapping.end - page)) return mapping.end;
if (!pageReadable(mapping.start)) return mapping.start;
std::uint64_t readable = 0;
std::uint64_t unreadable = (mapping.end - mapping.start) / page - 1;
while (unreadable - readable > 1) {
const auto middle = readable + (unreadable - readable) / 2;
if (pageReadable(mapping.start + middle * page)) readable = middle;
else unreadable = middle;
}
return mapping.start + unreadable * page;
}
#endif
std::uint64_t contentHash(std::span<const std::byte> bytes) {
std::uint64_t hash = 0xcbf29ce484222325ull;
for (const auto value : bytes) hash = (hash ^ static_cast<std::uint64_t>(value)) * 0x100000001b3ull;
@@ -479,6 +517,7 @@ void FrameRecorder::start() {
}
return;
}
if (!GuestMemory::WriteWatched()) throw std::runtime_error("frame capture: the guest write watch is unavailable, so CPU stores during the frames cannot be recorded");
startedAt = std::chrono::steady_clock::now();
startBoundary = boundaries;
allocationsGeneration = GuestAllocations::GuestAllocationsGeneration_nid_postfix();
@@ -598,8 +637,46 @@ std::vector<FrameRecorder::Range> FrameRecorder::committedRanges() {
}
}
return result;
#elif defined(__linux__)
std::uintptr_t base = 0;
std::size_t size = 0;
GuestArena::GuestArenaRange_nid_postfix(&base, &size);
if (size == 0) throw std::runtime_error("frame capture: the guest arena is not reserved");
std::vector<Range> result;
for (const auto& allocation : GuestAllocations::GuestAllocationsAcquireAll_nid_postfix()) {
if (!allocation->readable) continue;
const auto first = allocation->address;
const auto end = first + allocation->bytes;
const bool host = first < base || end > base + size;
const auto access = FrameRangeAccess::Readable | (allocation->writable ? FrameRangeAccess::Writable : 0u) | (allocation->gpu ? FrameRangeAccess::Gpu : 0u);
HostMapping::Walk(first, end, "frame capture", [&](const HostMapping::Mapping& mapping) {
if (!mapping.readable && !mapping.writable && !mapping.executable) return;
const auto add = [&](std::uint64_t address, std::uint64_t regionEnd, bool readable) {
if (address >= regionEnd) return;
Range range;
range.address = address;
range.bytes = regionEnd - address;
range.protection = windowsProtection(mapping);
range.access = access;
range.readable = readable;
if (host) range.kind = FrameRangeKind::Host;
else if (GuestMemory::Watched(range.address, static_cast<std::size_t>(range.bytes))) range.kind = FrameRangeKind::Watched;
else if (!mapping.shared && mapping.inode == 0) range.kind = FrameRangeKind::Unwatched;
else range.kind = FrameRangeKind::Section;
auto& last = result.empty() ? range : result.back();
if (!result.empty() && last.address + last.bytes == range.address && last.kind == range.kind && last.protection == range.protection && last.readable == range.readable && last.access == range.access) last.bytes += range.bytes;
else result.push_back(range);
};
const auto address = std::max<std::uint64_t>(first, mapping.start);
const auto regionEnd = std::min<std::uint64_t>(end, mapping.end);
const auto backed = std::clamp(fileBackedEnd(mapping), address, regionEnd);
add(address, backed, mapping.readable);
add(backed, regionEnd, false);
});
}
return result;
#else
throw std::runtime_error("frame capture: guest memory capture is implemented on Windows only");
throw std::runtime_error("frame capture: guest memory capture is implemented on Windows and Linux only");
#endif
}
@@ -202,7 +202,7 @@ bool CaptureComputePipeline(std::size_t words) {
using ResourceCache = Graphics::ResourceCache;
std::uint32_t DumpScale();
void WriteFrameBmp(int index, std::uint32_t fullWidth, std::uint32_t fullHeight, std::span<const std::byte> full, std::uint32_t scale);
void WriteFrameBmp(int index, std::uint32_t width, std::uint32_t height, std::span<const std::byte> pixels);
FrameDumpOutput frameDumpOutput;
class FrameDumpWriter {
@@ -216,7 +216,7 @@ public:
if (worker.joinable()) worker.join();
}
void Enqueue(int index, std::uint32_t width, std::uint32_t height, std::uint32_t scale, bool screenshot, std::vector<std::byte> pixels) {
void Enqueue(int index, std::uint32_t width, std::uint32_t height, bool screenshot, std::vector<std::byte> pixels) {
std::unique_lock lock(mutex);
require(!stopping, "frame dump writer has stopped");
if (!screenshot && !frameDumpOutput.Enabled_nid_no_patch()) return;
@@ -225,7 +225,7 @@ public:
require(!stopping, "frame dump writer has stopped");
if (!worker.joinable()) worker = std::thread([this] { run(); });
const auto bytes = pixels.size();
pending.push_back({index, width, height, scale, screenshot, std::move(pixels)});
pending.push_back({index, width, height, screenshot, std::move(pixels)});
queuedBytes += bytes;
changed.notify_all();
}
@@ -240,7 +240,6 @@ private:
int index;
std::uint32_t width;
std::uint32_t height;
std::uint32_t scale;
bool screenshot;
std::vector<std::byte> pixels;
};
@@ -257,7 +256,7 @@ private:
}
for (const auto& frame : batch) {
if (frame.screenshot) std::fprintf(stderr, "[gpu] screenshot saved: %s\n", Screenshot::Write(GetAppTitleId_nid_postfix().value, frame.width, frame.height, frame.pixels).string().c_str());
else WriteFrameBmp(frame.index, frame.width, frame.height, frame.pixels, frame.scale);
else WriteFrameBmp(frame.index, frame.width, frame.height, frame.pixels);
std::lock_guard lock(mutex);
queuedBytes -= frame.pixels.size();
changed.notify_all();
@@ -815,7 +814,7 @@ struct VulkanDevice::State {
const auto index = slot.dumpIndex;
if (slot.dumpScreenshot) {
require(full.size() == static_cast<std::size_t>(slot.dumpWidth) * slot.dumpHeight * 4, "invalid screenshot pixels");
dumpWriter.Enqueue(index, slot.dumpWidth, slot.dumpHeight, 1, true, std::vector<std::byte>(full.begin(), full.end()));
dumpWriter.Enqueue(index, slot.dumpWidth, slot.dumpHeight, true, std::vector<std::byte>(full.begin(), full.end()));
return;
}
const auto scale = slot.dumpScale;
@@ -828,7 +827,7 @@ struct VulkanDevice::State {
}
static const auto start = std::chrono::steady_clock::now();
AgcDriver::ProfilePrint_nid_no_patch("[gpu] frame_%03d.bmp: %ux%u display read back on the GPU at %.1f s\n", index, slot.dumpWidth, slot.dumpHeight, std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count());
dumpWriter.Enqueue(index, width, height, 1, false, std::move(pixels));
dumpWriter.Enqueue(index, width, height, false, std::move(pixels));
}
// Blocks on the fence, or polls it every 50 us with APS5_PRESENT_POLL_FENCE=1 (the difference
@@ -3265,16 +3264,20 @@ std::uint32_t DumpScale() {
return scale;
}
// frame_<index>.bmp: every DumpScale()-th pixel of a BGRA8 image, 32-bit top-down.
void WriteFrameBmp(int index, std::uint32_t fullWidth, std::uint32_t fullHeight, std::span<const std::byte> full, std::uint32_t scale) {
require(scale != 0 && full.size() == static_cast<std::size_t>(fullWidth) * fullHeight * 4, "invalid frame dump pixels");
if (!frameDumpOutput.Enabled_nid_no_patch()) return;
void QueueFrameDump(FrameDumpWriter& writer, int index, std::uint32_t fullWidth, std::uint32_t fullHeight, std::span<const std::byte> full) {
const auto scale = DumpScale();
require(full.size() == static_cast<std::size_t>(fullWidth) * fullHeight * 4, "invalid frame dump pixels");
const std::uint32_t width = (fullWidth + scale - 1) / scale;
const std::uint32_t height = (fullHeight + scale - 1) / scale;
std::vector<std::byte> pixels(static_cast<std::size_t>(width) * height * 4);
for (std::uint32_t y = 0; y < height; ++y) {
for (std::uint32_t x = 0; x < width; ++x) std::memcpy(pixels.data() + (static_cast<std::size_t>(y) * width + x) * 4, full.data() + (static_cast<std::size_t>(y) * scale * fullWidth + x * scale) * 4, 4);
}
writer.Enqueue(index, width, height, false, std::move(pixels));
}
void WriteFrameBmp(int index, std::uint32_t width, std::uint32_t height, std::span<const std::byte> pixels) {
if (!frameDumpOutput.Enabled_nid_no_patch()) return;
char name[32];
std::snprintf(name, sizeof(name), "frame_%03d.bmp", index);
frameDumpOutput.Write_nid_no_patch(name, width, height, pixels);
@@ -3450,24 +3453,25 @@ bool VulkanDevice::PresentDisplayBuffer(const DisplayBuffer& buffer, const Displ
if (passthrough) ensureHdrScaler(DisplayTexelFormat(buffer.pixelFormat));
const auto pixels = passthrough ? ReadDisplayBufferTexels(buffer) : ReadDisplayBuffer(buffer);
bool dumpFrame = false;
std::optional<int> cpuDump;
if (frameDumpOutput.Enabled_nid_no_patch() && dumps.dumped < dumps.limit && ++dumps.presents % static_cast<std::uint64_t>(dumps.every) == 0) {
if (passthrough) {
WriteFrameBmp(dumps.dumped++, buffer.width, buffer.height, ReadDisplayBuffer(buffer), DumpScale());
} else if (dumps.cpu) {
WriteFrameBmp(dumps.dumped++, buffer.width, buffer.height, pixels, DumpScale());
if (passthrough || dumps.cpu) {
cpuDump = dumps.dumped++;
} else {
state->nextDumpIndex = dumps.dumped++;
dumpFrame = true;
}
}
const bool screenshot = takeScreenshot(dumpFrame);
if (cpuDump && passthrough) QueueFrameDump(state->dumpWriter, *cpuDump, buffer.width, buffer.height, ReadDisplayBuffer(buffer));
else if (cpuDump) QueueFrameDump(state->dumpWriter, *cpuDump, buffer.width, buffer.height, pixels);
auto shot = screenshot && passthrough ? ReadDisplayBuffer(buffer) : std::vector<std::byte>{};
if (!present(buffer.width, buffer.height, true, pixels, nullptr, nullptr, VK_FILTER_LINEAR, dumpFrame || (screenshot && !passthrough), false, nullptr, 0, passthrough, plane)) {
if (dumpFrame) --dumps.dumped;
if (screenshot) Screenshot::Request();
return false;
}
if (!shot.empty()) state->dumpWriter.Enqueue(0, buffer.width, buffer.height, 1, true, std::move(shot));
if (!shot.empty()) state->dumpWriter.Enqueue(0, buffer.width, buffer.height, true, std::move(shot));
return true;
}
static const bool profile = std::getenv("APS5_PROFILE_DRAW") != nullptr;
@@ -3500,9 +3504,10 @@ bool VulkanDevice::PresentDisplayBuffer(const DisplayBuffer& buffer, const Displ
}
const auto cleared = buffer.dccAddress != 0 && resident == nullptr ? CompressedClearPixel(buffer, bytes) : std::nullopt;
bool dumpFrame = false;
std::optional<int> cpuDump;
if (frameDumpOutput.Enabled_nid_no_patch() && dumps.dumped < dumps.limit && ++dumps.presents % static_cast<std::uint64_t>(dumps.every) == 0) {
if (dumps.cpu || (passthrough && resident == nullptr)) {
WriteFrameBmp(dumps.dumped++, buffer.width, buffer.height, DisplayPixels(buffer, cleared), DumpScale());
cpuDump = dumps.dumped++;
} else {
state->nextDumpIndex = dumps.dumped++;
dumpFrame = true;
@@ -3514,6 +3519,7 @@ bool VulkanDevice::PresentDisplayBuffer(const DisplayBuffer& buffer, const Displ
AgcDriver::ProfilePrint_nid_no_patch("[flip] (cumulative): %llu presents from the resident image (%llu refreshed first), through guest memory: %llu not pending, %llu unsuitable; %llu GPU frame dumps\n", static_cast<unsigned long long>(residentPresents), static_cast<unsigned long long>(refreshedPresents), static_cast<unsigned long long>(notPending), static_cast<unsigned long long>(unsuitable), static_cast<unsigned long long>(gpuDumps));
}
const bool screenshot = takeScreenshot(dumpFrame);
if (cpuDump) QueueFrameDump(state->dumpWriter, *cpuDump, buffer.width, buffer.height, DisplayPixels(buffer, cleared));
const bool cpuScreenshot = screenshot && passthrough && resident == nullptr;
auto shot = cpuScreenshot ? DisplayPixels(buffer, cleared) : std::vector<std::byte>{};
CaptureTrace::Log("present dump=%d address=%llx width=%u height=%u resident=%d generation=%llu", dumpFrame ? state->nextDumpIndex : -1, static_cast<unsigned long long>(buffer.address), buffer.width, buffer.height, resident != nullptr, static_cast<unsigned long long>(resident ? resident->Generation() : 0));
@@ -3531,7 +3537,7 @@ bool VulkanDevice::PresentDisplayBuffer(const DisplayBuffer& buffer, const Displ
if (screenshot) Screenshot::Request();
return false;
}
if (cpuScreenshot) state->dumpWriter.Enqueue(0, buffer.width, buffer.height, 1, true, std::move(shot));
if (cpuScreenshot) state->dumpWriter.Enqueue(0, buffer.width, buffer.height, true, std::move(shot));
return true;
}
@@ -72,7 +72,7 @@ VkImageView MultisampleTargetView(const Context& context, const ColorTarget& tar
VkImageView MultisampleTargetView(const Context& context, const ColorTarget& target, std::shared_ptr<void>& owner, VkImage* image = nullptr);
void KeepMultisampleTarget_nid_no_patch(const std::shared_ptr<void>& owner);
void MaterializeMultisampleTargetClear(const Context& context, const ColorTarget& target);
VkImageView MultisampleTargetStorageView(const Context& context, const GuestTextureResource& resource);
VkImageView MultisampleTargetStorageView(const Context& context, const GuestTextureResource& resource, bool uintStorage = false);
bool MultisampleTargetStorable(const Context& context, const ColorTarget& target);
void ResolveMultisampleTarget(const Context& context, const ColorTarget& source, StorageTexture& destination, VkImageView destinationView);
bool MultisampledColorFillAbsorbs(std::uint64_t address, std::size_t bytes, std::uint32_t pattern);
@@ -146,6 +146,7 @@ bool HasStencil(VkFormat format);
// Device limit checks of the viewport, which is dynamic state and so no longer checked by Pipeline.
void ValidateViewport(const Context& context, const VkViewport& viewport);
void ValidateDepthBounds(const Context& context, const State& state);
void ValidateMeshLimits(const Context& context, const ShaderRecompiler::MeshConfiguration& mesh, std::uint32_t invocations);
void ValidateProvokingVertex(const Context& context, const State& state, std::span<const CompiledShader> shaders = {});
bool BarycentricPatches(const State& state, std::span<const CompiledShader> shaders);
bool ValidateSampleLocations(const Context& context, const State& state);
@@ -0,0 +1,67 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_GRAPHICS_INCLUDE_RUNTIMESWITCHES_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_GRAPHICS_INCLUDE_RUNTIMESWITCHES_HPP
#include <cstddef>
#include <cstdint>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
namespace AgcDriver::Graphics {
enum class RuntimeSwitch : std::size_t {
NoBarrierBatch,
Count,
};
namespace RuntimeSwitches {
const char* Name(RuntimeSwitch id);
bool Set(RuntimeSwitch id);
void Assign(RuntimeSwitch id, bool set);
std::optional<RuntimeSwitch> Find(std::string_view name);
}
class AbSchedule {
public:
struct Config {
double phaseSeconds = 0;
double settleSeconds = 2;
std::uint64_t startPresent = 0;
std::optional<double> startSeconds;
std::vector<RuntimeSwitch> switches;
};
enum class Event { None, Begin, Settled };
struct Step {
Event event = Event::None;
std::uint64_t phase = 0;
bool b = false;
};
static Config Parse(std::string_view ab, std::string_view start, std::string_view settle);
explicit AbSchedule(Config config);
Step AtPresent(std::uint64_t present, double seconds);
const Config& Settings() const { return config; }
private:
Config config;
bool started = false;
bool settled = false;
std::uint64_t phase = 0;
double phaseStart = 0;
std::optional<double> firstSeconds;
};
bool AbConfigured();
std::vector<std::string> AbAtPresent(std::uint64_t present, double seconds);
}
#endif
@@ -599,14 +599,6 @@ private:
std::vector<VkDescriptorBufferInfo> descriptorBuffers;
std::vector<VkDescriptorImageInfo> descriptorImages;
std::vector<VkWriteDescriptorSet> descriptorWrites;
struct MovedWordBuffer {
std::size_t allocation;
std::vector<std::uint32_t> words;
std::size_t size;
std::shared_ptr<Buffer> buffer;
};
mutable std::vector<MovedWordBuffer> movedWordBuffers;
mutable std::size_t movedWordBufferNext = 0;
mutable std::vector<VkDescriptorPoolSize> drawPoolSizes;
// Guest buffer elements bound read-only: each use of this object skips that many pending-write
// notes (counted in MarkGpuWrites for the [buffers] line).
@@ -185,7 +185,7 @@ public:
// so successive mip writes of a chain share one image and one write-back.
VkImageView View(std::uint32_t mip);
VkImageView FirstLayerView(std::uint32_t mip);
VkImageView StorageView(std::uint32_t mip, bool firstLayer);
VkImageView StorageView(std::uint32_t mip, bool firstLayer, bool uintStorage = false);
VkImageView ElementView();
VkImageView AtomicView(std::uint32_t mip, bool firstLayer);
VkImageView Atomic64View(std::uint32_t mip, bool firstLayer);
@@ -4190,13 +4190,17 @@ bool ImportMappedRanges(const Context& context, const GuestAllocations::Mapped&
for (const auto& mapped : ranges) {
const auto range = mapped.lock();
if (range == nullptr || range->bytes <= 0x10000) continue;
if (current.empty()) current = GuestAllocations::GuestAllocationsAcquire_nid_postfix();
if (current.empty()) current = GuestAllocations::GuestAllocationsAcquireMapped_nid_no_patch(ranges);
if (leasedRangeOwner(current, range->address) != range) continue;
try {
const auto run = importRun(current, range->address, std::numeric_limits<std::uint64_t>::max(), effectiveBudget(context, state));
const bool replacing = wouldRetireImport(state, run);
if (!GuestMemory::GpuMutex().HeldByThisThread()) {
const auto run = importRun(current, range->address, std::numeric_limits<std::uint64_t>::max(), effectiveBudget(context, state));
const std::array<std::pair<std::uint64_t, std::uint64_t>, 1> span{{{run.base, run.base + run.bytes}}};
if (wouldRetireImport(state, run) || StorageTexture::AnyPendingOverlaps(span)) return false;
if (replacing || StorageTexture::AnyPendingOverlaps(span)) return false;
} else if (replacing) {
current = GuestAllocations::GuestAllocationsAcquire_nid_postfix();
if (leasedRangeOwner(current, range->address) != range) continue;
}
if (!state.importedAtMap.emplace(range->address, range->bytes).second && findImport(state, range->address, range->address + range->bytes) == nullptr) continue;
importAllocation(context, state, range->address, current, ImportRequester::Map, 0, false);
@@ -33,6 +33,23 @@
namespace AgcDriver::Graphics {
namespace {
VkFormat uintStorageViewFormat(VkFormat format) {
switch (format) {
case VK_FORMAT_R8G8_SINT: return VK_FORMAT_R8G8_UINT;
case VK_FORMAT_R8G8B8A8_SINT: return VK_FORMAT_R8G8B8A8_UINT;
case VK_FORMAT_R16_SINT:
case VK_FORMAT_R16_SFLOAT: return VK_FORMAT_R16_UINT;
case VK_FORMAT_R16G16_SINT:
case VK_FORMAT_R16G16_SFLOAT: return VK_FORMAT_R16G16_UINT;
case VK_FORMAT_R16G16B16A16_SINT:
case VK_FORMAT_R16G16B16A16_SFLOAT: return VK_FORMAT_R16G16B16A16_UINT;
case VK_FORMAT_R32_SINT: return VK_FORMAT_R32_UINT;
case VK_FORMAT_R32G32_SINT: return VK_FORMAT_R32G32_UINT;
case VK_FORMAT_R32G32B32A32_SINT: return VK_FORMAT_R32G32B32A32_UINT;
default: return VK_FORMAT_UNDEFINED;
}
}
std::uint32_t MultisampledBlockLog2(ColorTileMode mode) {
switch (mode) {
case ColorTileMode::Standard4KB: return 12u;
@@ -146,12 +163,19 @@ public:
Check(context.imageFormatProperties(context.physical, target.format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, usage, orderFlags, &supported), "vkGetPhysicalDeviceImageFormatProperties multisampled target");
Require((supported.sampleCounts & samples) != 0 && target.extent.width <= supported.maxExtent.width && target.extent.height <= supported.maxExtent.height, "multisampled render target exceeds device image limits");
storageFormat = HostStorageFormat(context, target.format);
storageFlags = storageFormat != target.format ? VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT : 0u;
const auto uintFormat = uintStorageViewFormat(storageFormat);
storageFlags = storageFormat != target.format || (uintFormat != VK_FORMAT_UNDEFINED && uintFormat != target.format) ? VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT : 0u;
const auto storageSupports = [&](VkFormat format, VkImageUsageFlags formatUsage, VkImageCreateFlags flags) {
VkImageFormatProperties properties{};
return context.imageFormatProperties(context.physical, format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, formatUsage, flags, &properties) == VK_SUCCESS && (properties.sampleCounts & samples) != 0;
};
if (storageFormat != VK_FORMAT_UNDEFINED && (!context.storageImageMultisample || !storageSupports(storageFormat, VK_IMAGE_USAGE_STORAGE_BIT, 0u) || !storageSupports(target.format, usage | VK_IMAGE_USAGE_STORAGE_BIT, storageFlags))) storageFormat = VK_FORMAT_UNDEFINED;
const auto uintStorageSupported = [&] {
if (uintFormat == VK_FORMAT_UNDEFINED) return true;
VkFormatProperties uintProperties{};
context.formatProperties(context.physical, uintFormat, &uintProperties);
return (uintProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0 && storageSupports(uintFormat, VK_IMAGE_USAGE_STORAGE_BIT, 0u);
};
if (storageFormat != VK_FORMAT_UNDEFINED && (!context.storageImageMultisample || !storageSupports(storageFormat, VK_IMAGE_USAGE_STORAGE_BIT, 0u) || !storageSupports(target.format, usage | VK_IMAGE_USAGE_STORAGE_BIT, storageFlags) || !uintStorageSupported())) storageFormat = VK_FORMAT_UNDEFINED;
twinFormat = colorTwinFormat(context, target.format);
if (twinFormat != VK_FORMAT_UNDEFINED) {
VkFormatProperties twinProperties{};
@@ -355,7 +379,7 @@ public:
});
}
VkImageView Storage(VkFormat format) {
VkImageView Storage(VkFormat format, bool uintStorage) {
Require(storageFormat != VK_FORMAT_UNDEFINED, "the device cannot write the multisampled color target's format as a storage image at its sample count");
Require(format == storageFormat, "a storage image access to a multisampled color target in another format than the target's storage form is not implemented");
Require(cmask.Refusal().empty(), cmask.Refusal());
@@ -366,20 +390,28 @@ public:
Keep();
makeStorable(*recording);
ApplyFastClear(target);
if (storageView == VK_NULL_HANDLE) {
const auto uintFormat = uintStorage ? uintStorageViewFormat(storageFormat) : VK_FORMAT_UNDEFINED;
const bool useUintStorage = uintStorage && uintFormat != VK_FORMAT_UNDEFINED;
auto& selectedView = useUintStorage ? uintStorageView : storageView;
if (selectedView == VK_NULL_HANDLE) {
VkImageViewCreateInfo viewInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
viewInfo.image = image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = storageFormat;
viewInfo.format = useUintStorage ? uintFormat : storageFormat;
viewInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
Check(context.Function<PFN_vkCreateImageView>("vkCreateImageView")(context.device, &viewInfo, nullptr, &storageView), "vkCreateImageView multisampled target storage");
VkImageViewUsageCreateInfo viewUsage{VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO};
if (useUintStorage) {
viewUsage.usage = VK_IMAGE_USAGE_STORAGE_BIT;
viewInfo.pNext = &viewUsage;
}
Check(context.Function<PFN_vkCreateImageView>("vkCreateImageView")(context.device, &viewInfo, nullptr, &selectedView), "vkCreateImageView multisampled target storage");
}
recordCommands(context, [&](VkCommandBuffer commands, Recorder* recorder) {
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
if (recorder != nullptr) Recorder::CountBarriers(Recorder::CommandClass::Draw, 1);
});
storageWritten = true;
return storageView;
return selectedView;
}
bool Storable() const {
@@ -528,6 +560,7 @@ private:
VkImageView twinView = VK_NULL_HANDLE;
std::vector<Allocation> retired;
VkImageView storageView = VK_NULL_HANDLE;
VkImageView uintStorageView = VK_NULL_HANDLE;
bool storageWritten = false;
VkDeviceSize allocationBytes = 0;
std::optional<GuestMemory::UnchangedQuery> fmaskRange;
@@ -717,6 +750,7 @@ private:
if (orderView) context.Function<PFN_vkDestroyImageView>("vkDestroyImageView")(context.device, orderView, nullptr);
if (twinView) context.Function<PFN_vkDestroyImageView>("vkDestroyImageView")(context.device, twinView, nullptr);
if (storageView) context.Function<PFN_vkDestroyImageView>("vkDestroyImageView")(context.device, storageView, nullptr);
if (uintStorageView) context.Function<PFN_vkDestroyImageView>("vkDestroyImageView")(context.device, uintStorageView, nullptr);
destroy({image, memory, view});
for (const auto& allocation : retired) destroy(allocation);
}
@@ -1297,7 +1331,7 @@ void MaterializeMultisampleTargetClear(const Context& context, const ColorTarget
acquire(context, target);
}
VkImageView MultisampleTargetStorageView(const Context& context, const GuestTextureResource& resource) {
VkImageView MultisampleTargetStorageView(const Context& context, const GuestTextureResource& resource, bool uintStorage) {
std::lock_guard lock(targetsMutex());
discardRetiredTargets();
const auto format = ResolveTextureFormat(resource.format);
@@ -1308,13 +1342,13 @@ VkImageView MultisampleTargetStorageView(const Context& context, const GuestText
const auto& rendered = target->target;
if (sameStorageForm(context, rendered.format, format) && rendered.extent.width == resource.width && rendered.extent.height == resource.height && rendered.samples == resource.samples && rendered.mip == 0) {
if (!holdHostOnly(*target)) continue;
return target->Storage(StorageFormatForGuest(context, resource.format));
return target->Storage(StorageFormatForGuest(context, resource.format), uintStorage);
}
other = target.get();
}
if (other == nullptr) {
Require(Recorder::Active() != nullptr, "a multisampled color target is only written by recorded dispatches");
return createFilled(context, resource, format, "writing", true).Storage(StorageFormatForGuest(context, resource.format));
return createFilled(context, resource, format, "writing", true).Storage(StorageFormatForGuest(context, resource.format), uintStorage);
}
char text[320];
std::snprintf(text, sizeof(text), "AGC graphics: storage image access to multisampled color target 0x%llx (%ux%u, %u samples, vk format %d) as a %ux%u texture of %u samples and guest format %u (vk %d) is not implemented", static_cast<unsigned long long>(resource.baseAddress), other->target.extent.width, other->target.extent.height, other->target.samples, static_cast<int>(other->target.format), resource.width, resource.height, resource.samples, resource.format, static_cast<int>(format));
@@ -251,6 +251,15 @@ bool ValidateSampleLocations(const Context& context, const State& state) {
return enabled;
}
void ValidateMeshLimits(const Context& context, const ShaderRecompiler::MeshConfiguration& mesh, std::uint32_t invocations) {
const auto& limits = context.meshLimits;
Require(invocations <= limits.maxMeshWorkGroupInvocations && invocations <= limits.maxMeshWorkGroupSize[0], "mesh workgroup of " + std::to_string(invocations) + " invocations exceeds device limits (" + std::to_string(limits.maxMeshWorkGroupInvocations) + " invocations, " + std::to_string(limits.maxMeshWorkGroupSize[0]) + " wide)");
Require(mesh.maxVertices <= limits.maxMeshOutputVertices, "mesh output vertices " + std::to_string(mesh.maxVertices) + " exceed the device limit " + std::to_string(limits.maxMeshOutputVertices));
Require(mesh.maxPrimitives <= limits.maxMeshOutputPrimitives, "mesh output primitives " + std::to_string(mesh.maxPrimitives) + " exceed the device limit " + std::to_string(limits.maxMeshOutputPrimitives));
const auto sharedBytes = static_cast<std::uint64_t>(mesh.ldsSizeDwords) * 4;
Require(sharedBytes <= limits.maxMeshSharedMemorySize, "mesh LDS " + std::to_string(sharedBytes) + " bytes exceeds the device shared memory limit " + std::to_string(limits.maxMeshSharedMemorySize));
}
void ValidateViewport(const Context& context, const VkViewport& viewport) {
Require(std::isfinite(viewport.minDepth) && std::isfinite(viewport.maxDepth), "non-finite viewport depth range");
Require(context.depthRangeUnrestricted || (viewport.minDepth >= 0 && viewport.minDepth <= 1 && viewport.maxDepth >= 0 && viewport.maxDepth <= 1), "viewport depth outside [0, 1] requires VK_EXT_depth_range_unrestricted");
@@ -479,8 +488,7 @@ Pipeline::Pipeline(const Context& context, const State& state, const VertexInput
Require(context.meshShader, "device does not support VK_EXT_mesh_shader");
const auto& mesh = *state.stages.mesh;
const auto invocations = state.stages.vertexWaveSize == 64u && context.subgroup.subgroupSize == 32u ? mesh.threadsPerGroup / 2u : mesh.threadsPerGroup;
Require(invocations <= context.meshLimits.maxMeshWorkGroupInvocations && invocations <= context.meshLimits.maxMeshWorkGroupSize[0], "mesh workgroup exceeds device limits");
Require(mesh.maxVertices <= context.meshLimits.maxMeshOutputVertices && mesh.maxPrimitives <= context.meshLimits.maxMeshOutputPrimitives && static_cast<std::uint64_t>(mesh.ldsSizeDwords) * 4 <= context.meshLimits.maxMeshSharedMemorySize, "mesh output or LDS exceeds device limits");
ValidateMeshLimits(context, mesh, invocations);
}
const auto pushStages = PushConstantStages(shaders);
Require(pushStages == 0 || context.limits.maxPushConstantsSize >= PushBlockBytes(dynamicRendering), "graphics push constant range exceeds device limit");
@@ -6,6 +6,7 @@
#include "prx/libSceAgcDriver/Graphics/include/Recorder.hpp"
#include "prx/libkernel/Time/include/SleepWake.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Resources.hpp"
#include "prx/libSceAgcDriver/Graphics/include/RuntimeSwitches.hpp"
#include "prx/libSceAgcDriver/Graphics/include/ParallelCompare.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Texture.hpp"
#include "prx/libSceAgcDriver/Graphics/include/DeferredDestruction.hpp"
@@ -1930,8 +1931,7 @@ bool Recorder::MergeBarriers() {
}
bool Recorder::BarrierBatch() {
static const bool batch = MergeBarriers() && std::getenv("APS5_NO_BARRIER_BATCH") == nullptr;
return batch;
return MergeBarriers() && !RuntimeSwitches::Set(RuntimeSwitch::NoBarrierBatch);
}
bool Recorder::ElideCovered(CommandClass which, const BarrierMask& wanted, VkAccessFlags covered) {
@@ -0,0 +1,212 @@
#include "prx/libSceAgcDriver/Graphics/include/RuntimeSwitches.hpp"
#include <algorithm>
#include <array>
#include <atomic>
#include <cerrno>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <mutex>
#include <stdexcept>
#include <utility>
namespace AgcDriver::Graphics {
namespace {
constexpr std::size_t SwitchCount = static_cast<std::size_t>(RuntimeSwitch::Count);
constexpr std::array<const char*, SwitchCount> SwitchNames{
"APS5_NO_BARRIER_BATCH",
};
struct SwitchValues {
std::array<std::atomic<bool>, SwitchCount> set{};
SwitchValues() {
const std::array<bool, SwitchCount> environment{
std::getenv("APS5_NO_BARRIER_BATCH") != nullptr,
};
for (std::size_t index = 0; index < SwitchCount; ++index) set[index].store(environment[index], std::memory_order_relaxed);
}
};
SwitchValues& Values() {
static SwitchValues values;
return values;
}
std::string_view Trim(std::string_view text) {
while (!text.empty() && (text.front() == ' ' || text.front() == '\t')) text.remove_prefix(1);
while (!text.empty() && (text.back() == ' ' || text.back() == '\t' || text.back() == '\r' || text.back() == '\n')) text.remove_suffix(1);
return text;
}
double ParseSeconds(std::string_view text, const char* what) {
const std::string copy(Trim(text));
char* end = nullptr;
const double value = std::strtod(copy.c_str(), &end);
if (copy.empty() || end != copy.c_str() + copy.size() || !std::isfinite(value) || value < 0) throw std::invalid_argument(std::string(what) + ": '" + copy + "' is not a number of seconds");
return value;
}
std::string FlippableList() {
std::string list;
for (const auto* name : SwitchNames) list += std::string(list.empty() ? "" : ", ") + name;
return list;
}
}
namespace RuntimeSwitches {
const char* Name(RuntimeSwitch id) {
return SwitchNames.at(static_cast<std::size_t>(id));
}
bool Set(RuntimeSwitch id) {
return Values().set.at(static_cast<std::size_t>(id)).load(std::memory_order_relaxed);
}
void Assign(RuntimeSwitch id, bool set) {
Values().set.at(static_cast<std::size_t>(id)).store(set, std::memory_order_relaxed);
}
std::optional<RuntimeSwitch> Find(std::string_view name) {
for (std::size_t index = 0; index < SwitchCount; ++index) {
if (name == SwitchNames[index]) return static_cast<RuntimeSwitch>(index);
}
return std::nullopt;
}
}
AbSchedule::Config AbSchedule::Parse(std::string_view ab, std::string_view start, std::string_view settle) {
Config config;
ab = Trim(ab);
const auto colon = ab.find(':');
if (colon == std::string_view::npos) throw std::invalid_argument("APS5_AB: expected <seconds>:<switch>[+<switch>...], got '" + std::string(ab) + "'");
config.phaseSeconds = ParseSeconds(ab.substr(0, colon), "APS5_AB phase length");
if (config.phaseSeconds < 1) throw std::invalid_argument("APS5_AB: a phase must last at least 1 s");
auto list = ab.substr(colon + 1);
if (Trim(list).empty()) throw std::invalid_argument("APS5_AB: no switch listed after the phase length");
for (;;) {
const auto plus = list.find('+');
const auto name = Trim(list.substr(0, plus));
if (name.empty()) throw std::invalid_argument("APS5_AB: an empty switch name in '" + std::string(ab) + "'");
const auto found = RuntimeSwitches::Find(name);
if (!found.has_value()) throw std::invalid_argument("APS5_AB: " + std::string(name) + " cannot change while the title runs; switches the A/B can change: " + FlippableList());
if (std::find(config.switches.begin(), config.switches.end(), *found) != config.switches.end()) throw std::invalid_argument("APS5_AB: " + std::string(name) + " is listed twice");
config.switches.push_back(*found);
if (plus == std::string_view::npos) break;
list = list.substr(plus + 1);
}
start = Trim(start);
if (!start.empty()) {
if (start.back() == 's') {
config.startSeconds = ParseSeconds(start.substr(0, start.size() - 1), "APS5_AB_START");
} else {
const std::string copy(start);
char* end = nullptr;
errno = 0;
const auto value = std::strtoull(copy.c_str(), &end, 10);
if (end != copy.c_str() + copy.size() || errno == ERANGE || copy.front() < '0' || copy.front() > '9') throw std::invalid_argument("APS5_AB_START: '" + copy + "' is neither a present index nor <n>s");
config.startPresent = value;
}
}
if (!Trim(settle).empty()) config.settleSeconds = ParseSeconds(settle, "APS5_AB_SETTLE");
if (config.settleSeconds >= config.phaseSeconds) throw std::invalid_argument("APS5_AB_SETTLE must be shorter than the phase");
return config;
}
AbSchedule::AbSchedule(Config config) : config(std::move(config)) {}
AbSchedule::Step AbSchedule::AtPresent(std::uint64_t present, double seconds) {
if (!firstSeconds.has_value()) firstSeconds = seconds;
if (!started) {
const bool due = config.startSeconds.has_value() ? seconds - *firstSeconds >= *config.startSeconds : present >= config.startPresent;
if (!due) return {};
started = true;
settled = false;
phase = 0;
phaseStart = seconds;
return {Event::Begin, 0, false};
}
if (seconds - phaseStart >= config.phaseSeconds) {
++phase;
settled = false;
phaseStart = seconds;
return {Event::Begin, phase, (phase & 1u) != 0};
}
if (!settled && seconds - phaseStart >= config.settleSeconds) {
settled = true;
return {Event::Settled, phase, (phase & 1u) != 0};
}
return {};
}
namespace {
struct AbState {
std::mutex mutex;
std::optional<AbSchedule> schedule;
std::array<bool, SwitchCount> environment{};
};
AbState& Ab() {
static AbState* state = [] {
auto* created = new AbState();
const char* ab = std::getenv("APS5_AB");
const char* start = std::getenv("APS5_AB_START");
const char* settle = std::getenv("APS5_AB_SETTLE");
if (ab != nullptr) {
created->schedule.emplace(AbSchedule::Parse(ab, start != nullptr ? start : "", settle != nullptr ? settle : ""));
for (std::size_t index = 0; index < SwitchCount; ++index) created->environment[index] = RuntimeSwitches::Set(static_cast<RuntimeSwitch>(index));
}
return created;
}();
return *state;
}
std::string SwitchSettings(const AbState& state, bool b) {
std::string text;
for (const auto id : state.schedule->Settings().switches) {
const bool set = state.environment[static_cast<std::size_t>(id)] != b;
text += std::string(text.empty() ? "" : ", ") + RuntimeSwitches::Name(id) + (set ? " set" : " unset");
}
return text;
}
}
bool AbConfigured() {
static const bool configured = std::getenv("APS5_AB") != nullptr;
return configured;
}
std::vector<std::string> AbAtPresent(std::uint64_t present, double seconds) {
if (!AbConfigured()) return {};
auto& state = Ab();
std::lock_guard lock(state.mutex);
std::vector<std::string> lines;
char text[512];
const auto& config = state.schedule->Settings();
const auto step = state.schedule->AtPresent(present, seconds);
if (step.event == AbSchedule::Event::None) return lines;
if (step.event == AbSchedule::Event::Begin) {
if (step.phase == 0) {
char from[96];
if (config.startSeconds.has_value()) std::snprintf(from, sizeof(from), "%.1f s after the first present", *config.startSeconds);
else std::snprintf(from, sizeof(from), "present %llu", static_cast<unsigned long long>(config.startPresent));
std::snprintf(text, sizeof(text), "[ab] in-run A/B: phases of %.1f s from %s, settle %.1f s; A: %s; B: %s\n", config.phaseSeconds, from, config.settleSeconds, SwitchSettings(state, false).c_str(), SwitchSettings(state, true).c_str());
lines.emplace_back(text);
}
for (const auto id : config.switches) RuntimeSwitches::Assign(id, state.environment[static_cast<std::size_t>(id)] != step.b);
std::snprintf(text, sizeof(text), "[ab] phase %c %llu begins at present %llu (t %.3f s): %s\n", step.b ? 'B' : 'A', static_cast<unsigned long long>(step.phase), static_cast<unsigned long long>(present), seconds, SwitchSettings(state, step.b).c_str());
} else {
std::snprintf(text, sizeof(text), "[ab] phase %c %llu settled at present %llu (t %.3f s)\n", step.b ? 'B' : 'A', static_cast<unsigned long long>(step.phase), static_cast<unsigned long long>(present), seconds);
}
lines.emplace_back(text);
return lines;
}
}
@@ -2455,7 +2455,7 @@ void ShaderResources::buildComplete() {
for (auto element = first; element < last; ++element) {
const auto index = binding.imageAllocations[element];
if (binding.layout.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) images.push_back({samplers[index]->Handle(), VK_NULL_HANDLE, VK_IMAGE_LAYOUT_UNDEFINED});
else if (binding.layout.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) images.push_back({VK_NULL_HANDLE, storageAtomic64[index] ? storageTextures[index]->Atomic64View(storageMips[index], storageFirstLayer[index]) : storageAtomic[index] ? storageTextures[index]->AtomicView(storageMips[index], storageFirstLayer[index]) : storageTextures[index]->StorageView(storageMips[index], storageFirstLayer[index]), VK_IMAGE_LAYOUT_GENERAL});
else if (binding.layout.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) images.push_back({VK_NULL_HANDLE, storageAtomic64[index] ? storageTextures[index]->Atomic64View(storageMips[index], storageFirstLayer[index]) : storageAtomic[index] ? storageTextures[index]->AtomicView(storageMips[index], storageFirstLayer[index]) : storageTextures[index]->StorageView(storageMips[index], storageFirstLayer[index], binding.layout.binding >= ShaderRecompiler::RuntimeAbi::FirstStorageImageBinding + 7u && binding.layout.binding < ShaderRecompiler::RuntimeAbi::FirstStorageImageBinding + 14u), VK_IMAGE_LAYOUT_GENERAL});
else images.push_back({VK_NULL_HANDLE, textureFirstLayer[index] ? textures[index]->FirstLayerView() : textures[index]->View(), textures[index]->Layout()});
}
if (recorded) runs.emplace_back(write.dstArrayElement, write.descriptorCount);
@@ -2500,7 +2500,7 @@ void ShaderResources::buildComplete() {
for (const auto index : binding.imageAllocations) {
if (index == std::numeric_limits<std::size_t>::max()) images.push_back(absentImage(binding.layout.binding));
else if (storageTextures[index] == nullptr) images.push_back({VK_NULL_HANDLE, multisampleStorageViews[index], VK_IMAGE_LAYOUT_GENERAL});
else images.push_back({VK_NULL_HANDLE, storageAtomic64[index] ? storageTextures[index]->Atomic64View(storageMips[index], storageFirstLayer[index]) : storageAtomic[index] ? storageTextures[index]->AtomicView(storageMips[index], storageFirstLayer[index]) : storageTextures[index]->StorageView(storageMips[index], storageFirstLayer[index]), VK_IMAGE_LAYOUT_GENERAL});
else images.push_back({VK_NULL_HANDLE, storageAtomic64[index] ? storageTextures[index]->Atomic64View(storageMips[index], storageFirstLayer[index]) : storageAtomic[index] ? storageTextures[index]->AtomicView(storageMips[index], storageFirstLayer[index]) : storageTextures[index]->StorageView(storageMips[index], storageFirstLayer[index], binding.layout.binding >= ShaderRecompiler::RuntimeAbi::FirstStorageImageBinding + 7u && binding.layout.binding < ShaderRecompiler::RuntimeAbi::FirstStorageImageBinding + 14u), VK_IMAGE_LAYOUT_GENERAL});
}
Require(binding.imageAllocations.size() == binding.layout.descriptorCount, "descriptor allocations disagree with compact binding");
break;
@@ -4931,7 +4931,8 @@ void ShaderResources::resolveImageBinding(const ShaderRecompiler::DescriptorBind
Require(!(element < binding.imageAtomic.size() && binding.imageAtomic[element]) && !(element < binding.imageAtomic64.size() && binding.imageAtomic64[element]), "storage image atomics on a multisampled texture are not implemented");
storageTextures.push_back(nullptr);
multisampleStorageViews.resize(storageTextures.size(), VK_NULL_HANDLE);
multisampleStorageViews.back() = MultisampleTargetStorageView(context, resource);
const bool uintStorage = binding.binding >= ShaderRecompiler::RuntimeAbi::FirstStorageImageBinding + 7u && binding.binding < ShaderRecompiler::RuntimeAbi::FirstStorageImageBinding + 14u;
multisampleStorageViews.back() = MultisampleTargetStorageView(context, resource, uintStorage);
multisampleSources.push_back(resource.baseAddress);
storageMips.push_back(0);
storageKeys.push_back(0);
@@ -5643,23 +5644,13 @@ std::shared_ptr<ShaderResources::DrawBindings> ShaderResources::PrepareDrawBindi
const auto& item = allocations[index];
const auto override = std::find_if(moved.begin(), moved.end(), [&](const MovedBuffer& entry) { return entry.allocation == index; });
if (override != moved.end() && !override->words.empty()) {
constexpr std::size_t MovedWordBufferSlots = 8;
const auto held = std::find_if(movedWordBuffers.begin(), movedWordBuffers.end(), [&](const MovedWordBuffer& entry) { return entry.allocation == index && entry.size == override->size && std::ranges::equal(entry.words, override->words); });
std::shared_ptr<Buffer> buffer;
if (held != movedWordBuffers.end()) {
buffer = held->buffer;
} else {
buffer = std::make_shared<Buffer>(context, override->size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
std::memcpy(buffer->Bytes().data(), override->words.data(), override->size);
for (const auto& patch : dataPatches) {
if (patch.allocation == index && patch.byte < override->size) buffer->Bytes()[patch.byte] = static_cast<std::byte>(patch.adjustment);
}
MovedWordBuffer entry{index, {override->words.begin(), override->words.end()}, override->size, buffer};
if (movedWordBuffers.size() < MovedWordBufferSlots) movedWordBuffers.push_back(std::move(entry));
else movedWordBuffers[movedWordBufferNext++ % MovedWordBufferSlots] = std::move(entry);
auto slice = recorder.DrawUpload(override->size);
std::memcpy(slice.bytes, override->words.data(), override->size);
for (const auto& patch : dataPatches) {
if (patch.allocation == index && patch.byte < override->size) slice.bytes[patch.byte] = static_cast<std::byte>(patch.adjustment);
}
selected.push_back(index);
keep({0, std::move(buffer), 0, override->size});
keep({0, std::move(slice.buffer), slice.offset, override->size});
continue;
}
std::uint64_t address = item.address;
@@ -1277,7 +1277,7 @@ VkFormat StorageFormatOrUndefined(const Context& context, VkFormat format) {
return storage;
}
VkFormat UintFormatOfSint(VkFormat format) {
VkFormat UintStorageViewFormat(VkFormat format) {
switch (format) {
case VK_FORMAT_R8G8_SINT: return VK_FORMAT_R8G8_UINT;
case VK_FORMAT_R8G8B8A8_SINT: return VK_FORMAT_R8G8B8A8_UINT;
@@ -1287,6 +1287,9 @@ VkFormat UintFormatOfSint(VkFormat format) {
case VK_FORMAT_R32_SINT: return VK_FORMAT_R32_UINT;
case VK_FORMAT_R32G32_SINT: return VK_FORMAT_R32G32_UINT;
case VK_FORMAT_R32G32B32A32_SINT: return VK_FORMAT_R32G32B32A32_UINT;
case VK_FORMAT_R16_SFLOAT: return VK_FORMAT_R16_UINT;
case VK_FORMAT_R16G16_SFLOAT: return VK_FORMAT_R16G16_UINT;
case VK_FORMAT_R16G16B16A16_SFLOAT: return VK_FORMAT_R16G16B16A16_UINT;
default: return VK_FORMAT_UNDEFINED;
}
}
@@ -2038,9 +2041,9 @@ VkImageView StorageTexture::FirstLayerView(std::uint32_t mip) {
return created;
}
VkImageView StorageTexture::StorageView(std::uint32_t mip, bool firstLayer) {
VkImageView StorageTexture::StorageView(std::uint32_t mip, bool firstLayer, bool uintStorage) {
const auto packed = PackedStorageViewFormat(storageFormat);
const auto format = packed != VK_FORMAT_UNDEFINED ? packed : UintFormatOfSint(storageFormat);
const auto format = packed != VK_FORMAT_UNDEFINED ? packed : uintStorage ? UintStorageViewFormat(storageFormat) : VK_FORMAT_UNDEFINED;
if (format == VK_FORMAT_UNDEFINED) return firstLayer ? FirstLayerView(mip) : View(mip);
if (format == VK_FORMAT_R32_UINT) return AtomicView(mip, firstLayer);
const auto found = uintViews.find({mip, firstLayer});
@@ -4,6 +4,8 @@
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include <algorithm>
#include <iterator>
#include <limits>
#include <stdexcept>
#include <cstdint>
#include <cstddef>
@@ -12,10 +14,16 @@
int AgcDriver::SubmitMany(std::uint32_t* const* addresses, const std::uint32_t* sizes, std::uint32_t count, std::uint32_t queue) {
if (count == 0) return 0;
if (count > std::numeric_limits<std::uint32_t>::max() / sizeof(std::uint32_t*)) {
throw std::runtime_error("AGC driver: multi-submit count overflow");
}
if (!addresses || !sizes) throw std::runtime_error("AGC driver: null multi-submit arrays");
AgcDriver::GuestMemory::CheckRange(addresses, count * sizeof(*addresses), alignof(std::uint32_t*));
AgcDriver::GuestMemory::CheckRange(sizes, count * sizeof(*sizes), alignof(std::uint32_t));
for (std::uint32_t i = 0; i < count; ++i) {
if (sizes[i] != 0 && addresses[i] == nullptr) {
throw std::runtime_error("AGC driver: null multi-submit command buffer");
}
const Packet packet{addresses[i], sizes[i], 0, {}};
AgcDriver::Submit(&packet, queue);
}
@@ -5,6 +5,7 @@
#include "prx/libSceAgcDriver/Submit/include/Dcb.hpp"
#include "prx/libc/include/GuestAllocations.hpp"
#include "prx/libc/include/GuestArena.hpp"
#include "prx/libc/include/GuestWriteWatch.hpp"
#include "SceTypes.hpp"
#include <algorithm>
#include <atomic>
@@ -80,6 +81,9 @@ Layout allocate() {
auto* const block = static_cast<std::byte*>(GuestArena::GuestArenaAllocate_nid_postfix(BlockBytes, 65536));
check(block != nullptr, "cannot allocate guest memory");
GuestArena::GuestArenaCommit_nid_postfix(block, BlockBytes, 0x04, BlockBytes);
#ifdef __linux__
GuestWriteWatch::GuestWriteWatchRegister_nid_postfix(block, BlockBytes);
#endif
{
GuestAllocations::Mutation mutation;
mutation.Add(block, BlockBytes, true, true, true);
@@ -184,8 +188,11 @@ void testCaptureAndReplay(const std::filesystem::path& tool) {
const auto firstDump = capture / "first.bin";
const auto secondDump = capture / "second.bin";
const auto command = "\"\"" + tool.string() + "\" \"" + capture.string() + "\" --strict --dump " + std::to_string(at(layout.firstTarget)) + ":" + std::to_string(DataBytes) + ":\"" + firstDump.string() +
"\" --dump " + std::to_string(at(late.firstTarget)) + ":" + std::to_string(DataBytes) + ":\"" + secondDump.string() + "\"\"";
auto command = "\"" + tool.string() + "\" \"" + capture.string() + "\" --strict --dump " + std::to_string(at(layout.firstTarget)) + ":" + std::to_string(DataBytes) + ":\"" + firstDump.string() +
"\" --dump " + std::to_string(at(late.firstTarget)) + ":" + std::to_string(DataBytes) + ":\"" + secondDump.string() + "\"";
#ifdef _WIN32
command = "\"" + command + "\"";
#endif
const auto code = std::system(command.c_str());
check(code == 0, "agc_frame_replay exited with " + std::to_string(code));
check(readFile(firstDump) == first, "the replay did not reproduce the first copy");
@@ -241,7 +248,10 @@ void testConcurrentCopies(const std::filesystem::path& tool) {
check(memory != 0, "the capture holds no memory deltas");
const auto dump = capture / "targets.bin";
const auto command = "\"\"" + tool.string() + "\" \"" + capture.string() + "\" --strict --dump " + std::to_string(at(targets)) + ":" + std::to_string(Copies * DataBytes) + ":\"" + dump.string() + "\"\"";
auto command = "\"" + tool.string() + "\" \"" + capture.string() + "\" --strict --dump " + std::to_string(at(targets)) + ":" + std::to_string(Copies * DataBytes) + ":\"" + dump.string() + "\"";
#ifdef _WIN32
command = "\"" + command + "\"";
#endif
const auto code = std::system(command.c_str());
check(code == 0, "agc_frame_replay exited with " + std::to_string(code));
const auto replayed = readFile(dump);
@@ -2,12 +2,15 @@
#include "../tools/FrameReplay.cpp"
#undef main
#include "prx/libSceAgcDriver/Execution/include/FrameRecorder.hpp"
#include "prx/libc/include/GuestWriteWatch.hpp"
#include <barrier>
namespace {
using AgcDriver::FrameCaptureWriter;
constexpr std::uint32_t ReadWrite = 0x04;
void check(bool condition, const char* reason) {
if (!condition) throw std::runtime_error(reason);
}
@@ -20,6 +23,9 @@ std::byte* allocate(std::size_t size, bool gpu = true) {
auto* bytes = static_cast<std::byte*>(GuestArena::GuestArenaAllocate_nid_postfix(size, 65536));
check(bytes != nullptr, "guest allocation failed");
GuestArena::GuestArenaCommit_nid_postfix(bytes, size, 0x04, size);
#ifdef __linux__
GuestWriteWatch::GuestWriteWatchRegister_nid_postfix(bytes, size);
#endif
GuestAllocations::Mutation mutation;
mutation.Add(bytes, size, true, true, gpu);
return bytes;
@@ -61,7 +67,7 @@ void testMemoryOrder(const std::filesystem::path& directory) {
}
void testReusedMemory(const std::filesystem::path& directory) {
#ifdef _WIN32
#if defined(_WIN32) || defined(__linux__)
constexpr std::size_t Size = 2 * FrameCaptureWriter::BlockBytes;
auto* bytes = allocate(Size);
auto* cpu = allocate(FrameCaptureWriter::BlockBytes, false);
@@ -73,8 +79,8 @@ void testReusedMemory(const std::filesystem::path& directory) {
std::size_t arenaSize = 0;
GuestArena::GuestArenaRange_nid_postfix(&base, &arenaSize);
FrameCaptureWriter writer(directory);
writer.WriteRange(address(bytes), Size, FrameRangeKind::Watched, PAGE_READWRITE, FrameRangeAccess::Readable | FrameRangeAccess::Writable | FrameRangeAccess::Gpu);
writer.WriteRange(address(cpu), FrameCaptureWriter::BlockBytes, FrameRangeKind::Unwatched, PAGE_READWRITE, FrameRangeAccess::Readable | FrameRangeAccess::Writable);
writer.WriteRange(address(bytes), Size, FrameRangeKind::Watched, ReadWrite, FrameRangeAccess::Readable | FrameRangeAccess::Writable | FrameRangeAccess::Gpu);
writer.WriteRange(address(cpu), FrameCaptureWriter::BlockBytes, FrameRangeKind::Unwatched, ReadWrite, FrameRangeAccess::Readable | FrameRangeAccess::Writable);
check(writer.WriteSnapshot(address(bytes), expected) == FrameCaptureWriter::BlockBytes, "zero snapshot block was not elided");
writer.Finish("{\"arena_base\":" + std::to_string(base) + "}");
const FrameCaptureReader reader(directory);
@@ -85,6 +91,67 @@ void testReusedMemory(const std::filesystem::path& directory) {
#endif
}
void testSharedPage(const std::filesystem::path& directory) {
#ifdef __linux__
constexpr std::size_t Page = 65536;
auto* page = static_cast<std::byte*>(GuestArena::GuestArenaAllocate_nid_postfix(Page, Page));
check(page != nullptr, "guest reservation failed");
GuestArena::GuestArenaRelease_nid_postfix(page, Page);
const std::vector<std::byte> gpu(0x20, std::byte{0x3c});
const std::vector<std::byte> cpu(0x40, std::byte{0x5a});
std::uintptr_t base = 0;
std::size_t arenaSize = 0;
GuestArena::GuestArenaRange_nid_postfix(&base, &arenaSize);
FrameCaptureWriter writer(directory);
writer.WriteRange(address(page + 0x20), gpu.size(), FrameRangeKind::Watched, ReadWrite, FrameRangeAccess::Readable | FrameRangeAccess::Writable | FrameRangeAccess::Gpu);
writer.WriteRange(address(page + 0x40), cpu.size(), FrameRangeKind::Unwatched, ReadWrite, FrameRangeAccess::Readable | FrameRangeAccess::Writable);
writer.WriteSnapshot(address(page + 0x20), gpu);
writer.WriteSnapshot(address(page + 0x40), cpu);
writer.Finish("{\"arena_base\":" + std::to_string(base) + "}");
const FrameCaptureReader reader(directory);
const auto skipped = restoreMemory(reader);
check(skipped.bytes == 0, "a range sharing a page with an earlier one was skipped");
check(std::equal(gpu.begin(), gpu.end(), page + 0x20) && std::equal(cpu.begin(), cpu.end(), page + 0x40), "ranges sharing a page were not restored");
#else
static_cast<void>(directory);
#endif
}
void testHostNeighbors(const std::filesystem::path& directory) {
#ifdef __linux__
const auto page = static_cast<std::size_t>(sysconf(_SC_PAGESIZE));
void* mapped = mmap(nullptr, 2 * page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
check(mapped != MAP_FAILED, "host allocation failed");
auto* occupied = static_cast<std::byte*>(mapped);
auto* available = occupied + page;
check(munmap(available, page) == 0, "host page release failed");
std::uintptr_t base = 0;
std::size_t arenaSize = 0;
GuestArena::GuestArenaRange_nid_postfix(&base, &arenaSize);
FrameCaptureWriter writer(directory);
writer.WriteRange(address(occupied), page, FrameRangeKind::Host, ReadWrite, FrameRangeAccess::Readable | FrameRangeAccess::Writable);
writer.WriteRange(address(available), page, FrameRangeKind::Host, ReadWrite, FrameRangeAccess::Readable | FrameRangeAccess::Writable | FrameRangeAccess::Gpu);
writer.Finish("{\"arena_base\":" + std::to_string(base) + "}");
const FrameCaptureReader reader(directory);
const auto skipped = restoreMemory(reader);
check(skipped.bytes == page, "an occupied CPU host range was not the only range skipped");
#else
static_cast<void>(directory);
#endif
}
void testUnwatched() {
auto* recorder = AgcDriver::FrameRecorder::Active();
check(recorder != nullptr, "frame recorder is disabled");
try {
recorder->FrameBoundary();
} catch (const std::runtime_error& error) {
check(std::string_view(error.what()).find("write watch is unavailable") != std::string_view::npos, "a capture without the write watch failed for another reason");
return;
}
check(false, "a capture started without the write watch");
}
void testEopTokens() {
Frames frames;
auto output = std::make_shared<ReplayOutput>(frames);
@@ -170,9 +237,14 @@ void testShaderCode(const std::filesystem::path& directory, bool host) {
auto* shader = reinterpret_cast<Shader*>(bytes);
auto* code = bytes + (1u << 19) + 256;
constexpr std::size_t CodeBytes = 3 * FrameCaptureWriter::BlockBytes;
#ifdef _WIN32
#if defined(_WIN32) || defined(__linux__)
if (host) {
#ifdef _WIN32
auto* memory = static_cast<std::byte*>(VirtualAlloc(nullptr, CodeBytes + 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
#else
void* mapped = mmap(nullptr, CodeBytes + 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
auto* memory = mapped != MAP_FAILED ? static_cast<std::byte*>(mapped) : nullptr;
#endif
check(memory != nullptr, "host shader allocation failed");
GuestAllocations::Mutation mutation;
mutation.Add(memory, CodeBytes + 4096, true, true, true);
@@ -226,6 +298,9 @@ int main(int argc, char** argv) {
const std::filesystem::path directory = argv[2];
if (mode == "order") testMemoryOrder(directory);
else if (mode == "zero") testReusedMemory(directory);
else if (mode == "shared_page") testSharedPage(directory);
else if (mode == "host_neighbors") testHostNeighbors(directory);
else if (mode == "unwatched") testUnwatched();
else if (mode == "tokens") testEopTokens();
else if (mode == "blocking") testBlockingSubmission(directory);
else if (mode == "shader" || mode == "shader_host") testShaderCode(directory, mode == "shader_host");
@@ -6252,8 +6252,8 @@ void descriptorSnapshotTests() {
Require(descriptor.buffer == original[index].buffer && descriptor.offset == original[index].offset && descriptor.range == original[index].range, "snapshot update changed an untouched buffer descriptor");
} else {
const auto& item = snapshots->snapshots[snapshot];
Require(descriptor.buffer == item.buffer->Handle() && descriptor.offset == 0 && descriptor.range == moved[snapshot].size, "snapshot descriptor used another element's buffer or range");
Require(sameBytes(bufferBytes(descriptor.buffer), moved[snapshot].words.data(), moved[snapshot].size), "snapshot descriptor lost its buffer data");
Require(descriptor.buffer == item.buffer->Handle() && descriptor.offset == item.offset && descriptor.range == moved[snapshot].size, "snapshot descriptor used another element's buffer or range");
Require(sameBytes(bufferBytes(descriptor.buffer).subspan(descriptor.offset), moved[snapshot].words.data(), moved[snapshot].size), "snapshot descriptor lost its buffer data");
++snapshot;
}
}
@@ -6879,7 +6879,7 @@ void imageTableSnapshotAllocationTests() {
const auto snapshots = resources.PrepareDrawBindings(recorder, moved);
Require(snapshots != nullptr && snapshots->snapshots.size() == 1 && cache.Counters().sets == sets + 1, "an empty image table prevented draw snapshot allocation");
Require(mock.variableDescriptorCount == 1u, "an empty draw snapshot table did not reserve one physical image descriptor");
Require(sameBytes(bufferBytes(snapshots->snapshots[0].buffer->Handle()), moved[0].words.data(), moved[0].size), "an empty image table changed draw snapshot contents");
Require(sameBytes(bufferBytes(snapshots->snapshots[0].buffer->Handle()).subspan(snapshots->snapshots[0].offset), moved[0].words.data(), moved[0].size), "an empty image table changed draw snapshot contents");
const auto& currentMap = findWrite(map.binding).buffers.at(0);
Require(currentMap.buffer == originalMap.buffer && currentMap.offset == originalMap.offset && currentMap.range == originalMap.range, "draw snapshots replaced the empty image table map");
std::array<std::uint32_t, Abi::HeaderWords> header{};
@@ -11478,6 +11478,38 @@ void meshArgumentTests() {
Require(same(ResolveMeshArguments(record(0xffffffffu, 1, 0), rules(points, 0xffffffffu)), {0, 0, 0, 0xffffffffu, 0}), "groups over the device limit draw nothing");
}
void meshLimitTests() {
AgcDriver::Graphics::Context context{};
context.meshLimits.maxMeshWorkGroupInvocations = 128;
context.meshLimits.maxMeshWorkGroupSize[0] = 96;
context.meshLimits.maxMeshOutputVertices = 256;
context.meshLimits.maxMeshOutputPrimitives = 512;
context.meshLimits.maxMeshSharedMemorySize = 12284;
ShaderRecompiler::MeshConfiguration mesh{
.inputPrimitive = 4u,
.primitivesPerGroup = 32u,
.verticesPerGroup = 96u,
.maxVertices = 256u,
.maxPrimitives = 512u,
.threadsPerGroup = 128u,
.ldsSizeDwords = 3071u,
.provokingVertex = 0u,
.esgsItemSize = 4u,
};
AgcDriver::Graphics::ValidateMeshLimits(context, mesh, 96);
expectFailure([&] { AgcDriver::Graphics::ValidateMeshLimits(context, mesh, 97); }, "mesh workgroup of 97 invocations exceeds device limits (128 invocations, 96 wide)");
mesh.maxVertices = 257;
expectFailure([&] { AgcDriver::Graphics::ValidateMeshLimits(context, mesh, 96); }, "mesh output vertices 257 exceed the device limit 256");
mesh.maxVertices = 256;
mesh.maxPrimitives = 513;
expectFailure([&] { AgcDriver::Graphics::ValidateMeshLimits(context, mesh, 96); }, "mesh output primitives 513 exceed the device limit 512");
mesh.maxPrimitives = 512;
mesh.ldsSizeDwords = 3072;
expectFailure([&] { AgcDriver::Graphics::ValidateMeshLimits(context, mesh, 96); }, "mesh LDS 12288 bytes exceeds the device shared memory limit 12284");
mesh.ldsSizeDwords = 3071;
AgcDriver::Graphics::ValidateMeshLimits(context, mesh, 96);
}
void debugBranchTests() {
for (const auto opcode : {0x17u, 0x18u, 0x19u, 0x1au}) Require(recompilesDebugBranch(opcode), "a conditional debug branch did not recompile");
}
@@ -12721,6 +12753,7 @@ int main(int argc, char** argv) {
storageTextureBudgetTests();
sampledBudgetReportTests();
meshArgumentTests();
meshLimitTests();
meshIndexBufferTests();
stagingPoolBudgetTests();
validationTests();
@@ -4343,8 +4343,10 @@ void drawDescriptorWriteTests(const Device& device, Recorder& recorder) {
Require(actual.buffers.size() == original.buffers.size() && actual.images.size() == original.images.size(), "snapshot update omitted unchanged descriptors");
for (const auto& [key, before] : original.buffers) {
const auto after = actual.buffers.at(key);
const auto expected = key.first == (allocation == 0 ? 2u : 7u) ? draw->snapshots.front().buffer->Handle() : before.buffer;
Require(after.buffer == expected && after.offset == before.offset && after.range == before.range, "snapshot update replaced the wrong buffer or changed its range");
const bool replaced = key.first == (allocation == 0 ? 2u : 7u);
const auto expected = replaced ? draw->snapshots.front().buffer->Handle() : before.buffer;
const auto expectedOffset = replaced ? draw->snapshots.front().offset : before.offset;
Require(after.buffer == expected && after.offset == expectedOffset && after.range == before.range, "snapshot update replaced the wrong buffer or changed its range");
}
for (const auto& [key, before] : original.images) {
const auto after = actual.images.at(key);
@@ -0,0 +1,151 @@
#include "prx/libSceAgcDriver/Graphics/include/RuntimeSwitches.hpp"
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <stdexcept>
#include <string>
#include <string_view>
using AgcDriver::Graphics::AbSchedule;
using AgcDriver::Graphics::RuntimeSwitch;
namespace RuntimeSwitches = AgcDriver::Graphics::RuntimeSwitches;
namespace {
void Require(bool condition, const std::string& message) {
if (!condition) throw std::runtime_error(message);
}
void SetEnvironment(const char* name, const char* value) {
#ifdef _WIN32
_putenv_s(name, value != nullptr ? value : "");
#else
if (value != nullptr) setenv(name, value, 1);
else unsetenv(name);
#endif
}
std::string Refusal(const char* ab, const char* start = "", const char* settle = "") {
try {
AbSchedule::Parse(ab, start, settle);
} catch (const std::invalid_argument& error) {
return error.what();
}
return {};
}
void registry() {
for (std::size_t index = 0; index < static_cast<std::size_t>(RuntimeSwitch::Count); ++index) {
const auto id = static_cast<RuntimeSwitch>(index);
Require(RuntimeSwitches::Find(RuntimeSwitches::Name(id)) == id, std::string("the name of a switch does not find it: ") + RuntimeSwitches::Name(id));
Require(RuntimeSwitches::Set(id) == (std::getenv(RuntimeSwitches::Name(id)) != nullptr), std::string("a switch does not start as the environment sets it: ") + RuntimeSwitches::Name(id));
}
Require(RuntimeSwitches::Find("APS5_NO_BARRIER_BATCH") == RuntimeSwitch::NoBarrierBatch, "APS5_NO_BARRIER_BATCH is not a runtime switch");
Require(RuntimeSwitches::Set(RuntimeSwitch::NoBarrierBatch), "a switch set in the environment did not start set");
Require(!RuntimeSwitches::Find("APS5_FULL_BARRIERS").has_value() && !RuntimeSwitches::Find("APS5_NO_BARRIER_BATCH ").has_value(), "a name that is not a runtime switch was found");
RuntimeSwitches::Assign(RuntimeSwitch::NoBarrierBatch, false);
Require(!RuntimeSwitches::Set(RuntimeSwitch::NoBarrierBatch), "an assigned switch did not read back unset");
RuntimeSwitches::Assign(RuntimeSwitch::NoBarrierBatch, true);
Require(RuntimeSwitches::Set(RuntimeSwitch::NoBarrierBatch), "an assigned switch did not read back set");
}
void parse() {
const auto config = AbSchedule::Parse(" 30 : APS5_NO_BARRIER_BATCH ", "815", "3");
Require(config.phaseSeconds == 30 && config.settleSeconds == 3 && config.startPresent == 815 && !config.startSeconds.has_value(), "the phase settings were not parsed");
Require(config.switches.size() == 1 && config.switches[0] == RuntimeSwitch::NoBarrierBatch, "the switch list was not parsed");
const auto bySeconds = AbSchedule::Parse("60:APS5_NO_BARRIER_BATCH", "90s", "");
Require(bySeconds.startSeconds == 90.0 && bySeconds.settleSeconds == 2 && bySeconds.startPresent == 0, "a start in seconds was not parsed");
Require(Refusal("60:APS5_FULL_BARRIERS").find("APS5_FULL_BARRIERS cannot change while the title runs; switches the A/B can change: APS5_NO_BARRIER_BATCH") != std::string::npos, "a startup-only variable was not refused with the list of switches");
Require(Refusal("60:APS5_NO_BARRIER_BATCH+APS5_NO_BARRIER_BATCH").find("listed twice") != std::string::npos, "a duplicate switch was not refused");
Require(Refusal("60:+APS5_NO_BARRIER_BATCH").find("empty switch name") != std::string::npos && Refusal("60:APS5_NO_BARRIER_BATCH+").find("empty switch name") != std::string::npos, "an empty switch name was not refused");
Require(Refusal("60:").find("no switch listed") != std::string::npos, "an empty switch list was not refused");
Require(!Refusal("APS5_NO_BARRIER_BATCH").empty(), "a missing phase length was not refused");
Require(!Refusal("0.5:APS5_NO_BARRIER_BATCH").empty() && !Refusal("x:APS5_NO_BARRIER_BATCH").empty() && !Refusal("-5:APS5_NO_BARRIER_BATCH").empty() && !Refusal("inf:APS5_NO_BARRIER_BATCH").empty(), "a bad phase length was not refused");
Require(!Refusal("10:APS5_NO_BARRIER_BATCH", "", "10").empty() && !Refusal("10:APS5_NO_BARRIER_BATCH", "", "x").empty(), "a bad settle was not refused");
for (const char* start : {"-3", "abc", "s", "+3", "3x", "99999999999999999999999"}) {
Require(Refusal("10:APS5_NO_BARRIER_BATCH", start).find("APS5_AB_START") != std::string::npos, std::string("the start '") + start + "' was not refused");
}
}
void schedule() {
AbSchedule plan(AbSchedule::Parse("60:APS5_NO_BARRIER_BATCH", "10", "2"));
using Event = AbSchedule::Event;
const auto expect = [&](std::uint64_t present, double seconds, Event event, std::uint64_t phase, bool b) {
const auto step = plan.AtPresent(present, seconds);
Require(step.event == event && (event == Event::None || (step.phase == phase && step.b == b)), "unexpected step at present " + std::to_string(present));
};
expect(1, 0.0, Event::None, 0, false);
expect(9, 100.0, Event::None, 0, false);
expect(10, 102.0, Event::Begin, 0, false);
expect(11, 103.0, Event::None, 0, false);
expect(12, 104.5, Event::Settled, 0, false);
expect(13, 150.0, Event::None, 0, false);
expect(14, 161.9, Event::None, 0, false);
expect(15, 162.0, Event::Begin, 1, true);
expect(16, 163.0, Event::None, 1, true);
expect(17, 300.0, Event::Begin, 2, false);
expect(18, 301.0, Event::None, 2, false);
expect(19, 302.5, Event::Settled, 2, false);
expect(20, 362.5, Event::Begin, 3, true);
AbSchedule timed(AbSchedule::Parse("30:APS5_NO_BARRIER_BATCH", "90s", ""));
Require(timed.AtPresent(500, 1000.0).event == Event::None, "a start in seconds began at the first present");
Require(timed.AtPresent(501, 1089.0).event == Event::None, "a start in seconds began early");
const auto begin = timed.AtPresent(502, 1090.0);
Require(begin.event == Event::Begin && begin.phase == 0 && !begin.b, "a start in seconds did not begin phase A");
}
void glue() {
Require(AgcDriver::Graphics::AbConfigured(), "APS5_AB is not seen");
Require(AgcDriver::Graphics::AbAtPresent(1, 0.0).empty(), "the A/B printed before its start");
RuntimeSwitches::Assign(RuntimeSwitch::NoBarrierBatch, false);
auto lines = AgcDriver::Graphics::AbAtPresent(3, 1.0);
Require(lines.size() == 2 && lines[0] == "[ab] in-run A/B: phases of 5.0 s from present 3, settle 2.0 s; A: APS5_NO_BARRIER_BATCH set; B: APS5_NO_BARRIER_BATCH unset\n" && lines[1] == "[ab] phase A 0 begins at present 3 (t 1.000 s): APS5_NO_BARRIER_BATCH set\n", "the start lines differ: " + (lines.empty() ? std::string() : lines.front()));
Require(RuntimeSwitches::Set(RuntimeSwitch::NoBarrierBatch), "phase A does not have the environment's setting");
lines = AgcDriver::Graphics::AbAtPresent(4, 3.5);
Require(lines.size() == 1 && lines[0] == "[ab] phase A 0 settled at present 4 (t 3.500 s)\n", "the settle line differs");
lines = AgcDriver::Graphics::AbAtPresent(5, 6.0);
Require(lines.size() == 1 && lines[0] == "[ab] phase B 1 begins at present 5 (t 6.000 s): APS5_NO_BARRIER_BATCH unset\n", "the phase B line differs");
Require(!RuntimeSwitches::Set(RuntimeSwitch::NoBarrierBatch), "phase B did not toggle the listed switch");
Require(AgcDriver::Graphics::AbAtPresent(6, 7.0).empty(), "a present inside the settle printed");
lines = AgcDriver::Graphics::AbAtPresent(7, 11.5);
Require(lines.size() == 1 && lines[0] == "[ab] phase A 2 begins at present 7 (t 11.500 s): APS5_NO_BARRIER_BATCH set\n", "phase A did not come back");
Require(RuntimeSwitches::Set(RuntimeSwitch::NoBarrierBatch), "phase A did not restore the environment's setting");
}
int refused() {
SetEnvironment("APS5_AB", "5:APS5_FULL_BARRIERS");
try {
AgcDriver::Graphics::AbAtPresent(1, 0.0);
} catch (const std::invalid_argument& error) {
if (std::string_view(error.what()).find("APS5_FULL_BARRIERS cannot change while the title runs") != std::string_view::npos) {
std::printf("runtime switch refusal test passed\n");
return 0;
}
std::fprintf(stderr, "FAILED: unexpected refusal: %s\n", error.what());
return 1;
}
std::fprintf(stderr, "FAILED: APS5_AB naming a startup-only variable was not refused at the first present\n");
return 1;
}
}
int main(int argc, char** argv) {
if (argc > 1 && std::string_view(argv[1]) == "refused") return refused();
try {
SetEnvironment("APS5_NO_BARRIER_BATCH", "1");
SetEnvironment("APS5_AB", "5:APS5_NO_BARRIER_BATCH");
SetEnvironment("APS5_AB_START", "3");
SetEnvironment("APS5_AB_SETTLE", nullptr);
registry();
parse();
schedule();
glue();
} catch (const std::exception& error) {
std::fprintf(stderr, "FAILED: %s\n", error.what());
return 1;
}
std::printf("runtime switch tests passed\n");
return 0;
}
@@ -445,6 +445,9 @@ void testMultiAcbs() {
check(sceAgcDriverSubmitMultiAcbs(0, nullptr, nullptr, 0) == 0, "empty multi-ACB submit checked its queue");
expectFailure([&] { sceAgcDriverSubmitMultiAcbs(0x20, nullptr, sizes.data(), 1); });
expectFailure([&] { sceAgcDriverSubmitMultiAcbs(0x20, addresses.data(), nullptr, 1); });
std::array<std::uint32_t*, 1> nullAcbs{nullptr};
std::array<std::uint32_t, 1> nonZeroAcbSizes{4};
expectFailure([&] { sceAgcDriverSubmitMultiAcbs(0x20, nullAcbs.data(), nonZeroAcbSizes.data(), 1); });
check(value == 0, "a rejected multi-ACB submit ran a command buffer");
for (std::uint32_t queue : {0x20u, 0x57u}) {
value = 0;
@@ -500,6 +503,11 @@ void testMultiSubmissions() {
expectFailure([&] { sceAgcDriverAgrSubmitMultiDcbs(invalidAddresses.data(), nullptr, 1); });
expectFailure([&] { sceAgcDriverSubmitMultiDcbs(invalidAddresses.data(), invalidSizes.data(), 1); });
expectFailure([&] { sceAgcDriverAgrSubmitMultiDcbs(invalidAddresses.data(), invalidSizes.data(), 1); });
std::array<std::uint32_t*, 1> nullAddresses{nullptr};
std::array<std::uint32_t, 1> nonZeroSizes{4};
expectFailure([&] { sceAgcDriverSubmitMultiDcbs(nullAddresses.data(), nonZeroSizes.data(), 1); });
expectFailure([&] { sceAgcDriverAgrSubmitMultiDcbs(nullAddresses.data(), nonZeroSizes.data(), 1); });
expectFailure([&] { sceAgcDriverSubmitMultiDcbs(invalidAddresses.data(), invalidSizes.data(), std::numeric_limits<std::uint32_t>::max()); });
for (bool agr : {false, true}) {
value = 0;
done = 0;
@@ -0,0 +1,210 @@
#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Texture.hpp"
#include "prx/libSceAgcDriver/Graphics/include/TextureTiling.hpp"
#include "prx/libc/include/GuestAllocations.hpp"
#include "Recompiler.hpp"
#include "VulkanTestDevice.hpp"
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#endif
#include <algorithm>
#include <array>
#include <bit>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <span>
#include <string>
#include <vector>
namespace {
using AgcDriver::Graphics::Require;
using ShaderRecompiler::ShaderStage;
constexpr std::uint32_t Threads = 32;
constexpr std::uint32_t Width = 32;
constexpr std::uint32_t Height = 8;
constexpr std::uint32_t Type2D = 9;
constexpr std::uint32_t IdentitySwizzle = 0xfacu;
constexpr std::uint32_t TwoChannelSwizzle = 0x22cu;
constexpr std::size_t BlockBytes = 65536;
constexpr std::size_t TexelBytes = 8192;
alignas(256) std::array<std::uint32_t, Threads * 4> Input{};
alignas(256) constexpr std::array<std::uint32_t, 12> StoreCode{
0x34020084, 0xe0381000, 0x80001401, 0xbf8c3f70, 0x7e3c0300, 0x7e3e0280, 0xf0201f08, 0x0001141e,
0x7e3e0281, 0xf0201508, 0x0001141e, 0xbf810000,
};
struct Format {
const char* name;
std::uint32_t value;
std::uint32_t components;
};
constexpr std::array<Format, 3> Formats{{
{"16_FLOAT", 13u, 1u},
{"16_16_FLOAT", 29u, 2u},
{"16_16_16_16_FLOAT", 71u, 4u},
}};
struct Write {
std::uint32_t row;
std::uint32_t dmask;
};
constexpr std::array<Write, 2> Writes{{{0u, 0xfu}, {1u, 0x5u}}};
constexpr std::array<std::uint32_t, 24> EdgeBits{
0x00000000u, 0x80000000u, 0x3f800000u, 0xbf800000u, 0x3f801000u, 0x3f803000u,
0x3f800fffu, 0x3f801001u, 0x477fe000u, 0x477ff000u, 0x47800000u, 0xc7800000u,
0x7f800000u, 0xff800000u, 0x7fc12345u, 0x7f812345u, 0x38800000u, 0x33800000u,
0x33000000u, 0x000116c2u, 0x3eaaaaabu, 0x40490fdbu, 0x00000001u, 0x80000001u,
};
class GuestBlock {
public:
GuestBlock() {
#ifdef _WIN32
block = static_cast<std::uint8_t*>(VirtualAlloc(nullptr, BlockBytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
#else
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "image store float rounding: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {
GuestAllocations::Mutation().Remove(block);
#ifdef _WIN32
VirtualFree(block, 0, MEM_RELEASE);
#else
std::free(block);
#endif
}
std::uint8_t* Data() { return block; }
private:
std::uint8_t* block = nullptr;
};
std::uint16_t HalfTowardZero(std::uint32_t bits) {
const auto sign = static_cast<std::uint16_t>((bits >> 16u) & 0x8000u);
const auto exponent = (bits >> 23u) & 0xffu;
const auto mantissa = bits & 0x7fffffu;
if (exponent == 0xffu) {
auto payload = static_cast<std::uint16_t>(mantissa >> 13u);
if (mantissa != 0u && payload == 0u) payload = 1u;
return static_cast<std::uint16_t>(sign | 0x7c00u | payload);
}
if (exponent == 0u) return sign;
const int unbiased = static_cast<int>(exponent) - 127;
if (unbiased > 15) return static_cast<std::uint16_t>(sign | 0x7bffu);
if (unbiased < -14) {
const int shift = -14 - unbiased + 13;
if (shift >= 32) return sign;
const auto significand = 0x800000u | mantissa;
return static_cast<std::uint16_t>(sign | (significand >> static_cast<unsigned>(shift)));
}
return static_cast<std::uint16_t>(sign | (static_cast<std::uint16_t>(unbiased + 15) << 10u) | (mantissa >> 13u));
}
std::uint32_t Value(std::uint32_t tid, std::uint32_t component) {
const auto slot = tid * 4u + component;
if (slot < EdgeBits.size()) return EdgeBits[slot];
const auto seed = (slot + 1u) * 0x9e3779b1u;
const auto mixed = seed ^ (seed >> 16u);
return (mixed & 0x807fffffu) | (((mixed >> 23u) % 31u + 110u) << 23u);
}
std::uint64_t TexelOffset(const Format& format, std::uint32_t x, std::uint32_t y) {
const auto mip = AgcDriver::Graphics::ComputeMipLayout(AgcDriver::Graphics::TextureTileMode::kLinear, format.value, Width, Height, 1u).at(0);
return mip.tiledOffset + static_cast<std::uint64_t>(y) * mip.pitchBytes + static_cast<std::uint64_t>(x) * format.components * sizeof(std::uint16_t);
}
std::array<std::uint32_t, 4> BufferDescriptor(const void* data, std::uint32_t bytes) {
const auto address = reinterpret_cast<std::uintptr_t>(data);
return {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu), bytes, 0x31016facu};
}
std::array<std::uint32_t, 8> TextureDescriptor(const void* data, const Format& format) {
const auto address = static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(data));
return {static_cast<std::uint32_t>(address >> 8u), static_cast<std::uint32_t>((address >> 40u) & 0xffu) | (format.value << 20u) | (((Width - 1u) & 3u) << 30u),
((Width - 1u) >> 2u) | ((Height - 1u) << 14u), (format.components == 2u ? TwoChannelSwizzle : IdentitySwizzle) | (Type2D << 28u), 0u, 0u, 0u, 0u};
}
void Store(AgcDriver::VulkanDevice& device, std::uint8_t* texels, const Format& format) {
for (std::uint32_t tid = 0; tid < Threads; ++tid) {
for (std::uint32_t component = 0; component < 4u; ++component) Input[tid * 4u + component] = Value(tid, component);
}
std::fill(texels, texels + TexelBytes, 0x5au);
std::vector<std::uint32_t> userData(16, 0u);
const auto buffer = BufferDescriptor(Input.data(), static_cast<std::uint32_t>(Input.size() * sizeof(Input[0])));
const auto texture = TextureDescriptor(texels, format);
std::copy(buffer.begin(), buffer.end(), userData.begin());
std::copy(texture.begin(), texture.end(), userData.begin() + 4);
const std::span<const std::uint32_t> code(StoreCode);
const std::array<ShaderRecompiler::MemoryRegion, 1> memory{{{reinterpret_cast<std::uintptr_t>(StoreCode.data()), std::as_bytes(code)}}};
const ShaderRecompiler::ShaderComputeStageInfo compute{{Threads, 1, 1}, 0u, {false, false, false}, false, 1};
ShaderRecompiler::RecompileRequest request{
{ShaderStage::Compute, reinterpret_cast<std::uintptr_t>(StoreCode.data()), code, 0, {}},
{32, 0, userData, compute, std::nullopt, std::nullopt, memory},
device.Target(),
{0, 0, 0, 128}
};
request.useCache = false;
const auto result = ShaderRecompiler::Recompile(request);
device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast<std::uintptr_t>(StoreCode.data()));
device.WaitIdle();
AgcDriver::Graphics::StorageTexture::FlushPending(reinterpret_cast<std::uintptr_t>(texels), TexelBytes, nullptr, "test");
device.WaitIdle();
}
std::uint16_t ExpectedComponent(std::uint32_t tid, std::uint32_t dmask, std::uint32_t component) {
if (((dmask >> component) & 1u) == 0u) return 0u;
std::uint32_t source = 0u;
for (std::uint32_t lower = 0u; lower < component; ++lower) source += (dmask >> lower) & 1u;
return HalfTowardZero(Value(tid, source));
}
void Check(const std::uint8_t* texels, const Format& format) {
for (std::uint32_t y = 0; y < Height; ++y) {
for (std::uint32_t x = 0; x < Width; ++x) {
const auto offset = TexelOffset(format, x, y);
for (std::uint32_t component = 0; component < format.components; ++component) {
std::uint16_t actual = 0u;
std::memcpy(&actual, texels + offset + component * sizeof(actual), sizeof(actual));
const auto dmask = y == Writes[0].row ? Writes[0].dmask : y == Writes[1].row ? Writes[1].dmask : 0u;
const auto expected = dmask != 0u ? ExpectedComponent(x, dmask, component) : 0x5a5au;
Require(actual == expected, std::string("image_store of ") + format.name + ": texel (" + std::to_string(x) + ", " + std::to_string(y) + ") component " + std::to_string(component) + " is " + std::to_string(actual) + ", expected " + std::to_string(expected));
}
}
}
}
}
int main() {
try {
const auto device = OpenVulkanTestDevice();
if (!device) return VulkanTestSkipped;
GuestBlock block;
for (const auto& format : Formats) {
Store(*device, block.Data(), format);
Check(block.Data(), format);
}
std::puts("image store float rounding tests passed");
return 0;
} catch (const std::exception& error) {
std::cerr << error.what() << '\n';
return 1;
}
}
@@ -35,6 +35,11 @@
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef __linux__
#include "prx/libc/include/specifics/linux/HostMapping.hpp"
#include <sys/mman.h>
#include <unistd.h>
#endif
namespace {
@@ -142,6 +147,13 @@ bool committedWritable(std::uint64_t address, std::uint64_t bytes) {
at = reinterpret_cast<std::uintptr_t>(info.BaseAddress) + info.RegionSize;
}
return true;
#elif defined(__linux__)
auto cursor = address;
HostMapping::Walk(address, address + bytes, "frame replay", [&](const HostMapping::Mapping& mapping) {
if (mapping.start > cursor || !mapping.readable || !mapping.writable) return;
cursor = std::max(cursor, mapping.end);
});
return cursor >= address + bytes;
#else
static_cast<void>(address);
static_cast<void>(bytes);
@@ -149,6 +161,21 @@ bool committedWritable(std::uint64_t address, std::uint64_t bytes) {
#endif
}
void commit(const FrameMemoryChunk& chunk) {
auto* const pointer = reinterpret_cast<void*>(static_cast<std::uintptr_t>(chunk.address));
const auto bytes = static_cast<std::size_t>(chunk.bytes);
#ifdef __linux__
const auto page = static_cast<std::uint64_t>(sysconf(_SC_PAGESIZE));
const auto begin = chunk.address & ~(page - 1);
const auto end = (chunk.address + chunk.bytes + page - 1) & ~(page - 1);
if (begin != chunk.address || end != chunk.address + chunk.bytes) {
GuestArena::GuestArenaSetProtection_nid_postfix(static_cast<std::uintptr_t>(begin), static_cast<std::size_t>(end - begin), 0x04);
return;
}
#endif
GuestArena::GuestArenaCommit_nid_postfix(pointer, bytes, 0x04, bytes);
}
std::pair<std::uint64_t, std::uint64_t> progress();
struct Skipped {
@@ -189,6 +216,31 @@ void mapHostRanges(const FrameCaptureReader& reader, Skipped& skipped) {
if ((chunk->access & FrameRangeAccess::Gpu) != 0) throw std::runtime_error("frame replay: cannot map the GPU-visible host range at " + std::to_string(chunk->address));
skipped.Add(*chunk);
}
#elif defined(__linux__)
const auto page = static_cast<std::uint64_t>(sysconf(_SC_PAGESIZE));
std::sort(host.begin(), host.end(), [](const auto* left, const auto* right) { return left->address < right->address; });
std::vector<std::pair<std::uint64_t, std::uint64_t>> envelopes;
for (const auto* chunk : host) {
const auto begin = chunk->address & ~(page - 1);
const auto end = (chunk->address + chunk->bytes + page - 1) & ~(page - 1);
if (!envelopes.empty() && begin < envelopes.back().second) envelopes.back().second = std::max(envelopes.back().second, end);
else envelopes.emplace_back(begin, end);
}
std::vector<std::pair<std::uint64_t, std::uint64_t>> refused;
for (const auto& [begin, end] : envelopes) {
auto* const wanted = reinterpret_cast<void*>(static_cast<std::uintptr_t>(begin));
const auto bytes = static_cast<std::size_t>(end - begin);
void* const placed = mmap(wanted, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0);
if (placed == wanted) continue;
if (placed != MAP_FAILED) munmap(placed, bytes);
refused.emplace_back(begin, end);
}
for (const auto* chunk : host) {
const bool blocked = std::any_of(refused.begin(), refused.end(), [&](const auto& range) { return chunk->address < range.second && range.first < chunk->address + chunk->bytes; });
if (!blocked) continue;
if ((chunk->access & FrameRangeAccess::Gpu) != 0) throw std::runtime_error("frame replay: cannot map the GPU-visible host range at " + std::to_string(chunk->address));
skipped.Add(*chunk);
}
#else
for (const auto* chunk : host) skipped.Add(*chunk);
#endif
@@ -202,12 +254,15 @@ Skipped restoreMemory(const FrameCaptureReader& reader) {
progress();
Skipped skipped;
mapHostRanges(reader, skipped);
std::vector<bool> owned;
for (const auto& chunk : reader.Chunks()) owned.push_back(chunk.kind == FrameMemoryKind::Range && chunk.rangeKind != FrameRangeKind::Host && committedWritable(chunk.address, chunk.bytes));
{
GuestAllocations::Mutation mutation;
for (const auto& chunk : reader.Chunks()) {
for (std::size_t index = 0; index < reader.Chunks().size(); ++index) {
const auto& chunk = reader.Chunks()[index];
if (chunk.kind != FrameMemoryKind::Range || chunk.rangeKind == FrameRangeKind::Host) continue;
const bool gpu = (chunk.access & FrameRangeAccess::Gpu) != 0;
if (committedWritable(chunk.address, chunk.bytes)) {
if (owned[index]) {
if (!gpu) skipped.Add(chunk);
else std::memset(reinterpret_cast<void*>(static_cast<std::uintptr_t>(chunk.address)), 0, static_cast<std::size_t>(chunk.bytes));
continue;
@@ -221,7 +276,7 @@ Skipped restoreMemory(const FrameCaptureReader& reader) {
skipped.Add(chunk);
continue;
}
GuestArena::GuestArenaCommit_nid_postfix(pointer, bytes, 0x04, bytes);
commit(chunk);
mutation.Add(pointer, bytes, (chunk.access & FrameRangeAccess::Readable) != 0, (chunk.access & FrameRangeAccess::Writable) != 0, gpu);
}
}
+21 -18
View File
@@ -88,20 +88,17 @@ static int AppendRead(Apr::CommandBufferObject* buffer, Apr::Opcode opcode, std:
}
static std::uint64_t NopBytes(std::uint32_t dwords) {
return sizeof(Apr::CommandHeader) + std::uint64_t{dwords} * 4u;
return std::uint64_t{dwords} * 4u;
}
static int AppendNop(Apr::CommandBufferObject* buffer, std::uint32_t dwords, const std::uint32_t* data) {
if (!buffer) return SCE_KERNEL_ERROR_EINVAL;
const std::uint64_t bytes = NopBytes(dwords);
if (!buffer->base) return SCE_KERNEL_ERROR_EPERM;
if (bytes > RemainingCommandBytes(*buffer)) return SCE_KERNEL_ERROR_EBUSY;
const Apr::CommandHeader header{Apr::Opcode::Nop, static_cast<std::uint32_t>(bytes)};
std::uint8_t* destination = buffer->base + buffer->offset;
static int AppendNop(Apr::CommandBufferObject& buffer, std::uint32_t header, const std::uint32_t* data) {
const std::uint64_t bytes = NopBytes(Apr::NopDwords(header));
if (bytes > RemainingCommandBytes(buffer)) return SCE_KERNEL_ERROR_EBUSY;
std::uint8_t* destination = buffer.base + buffer.offset;
std::memcpy(destination, &header, sizeof(header));
if (data) std::memcpy(destination + sizeof(header), data, std::uint64_t{dwords} * 4u);
buffer->offset += static_cast<std::uint32_t>(bytes);
++buffer->numCommands;
if (data) std::memcpy(destination + sizeof(header), data, bytes - sizeof(header));
buffer.offset += static_cast<std::uint32_t>(bytes);
++buffer.numCommands;
return 0;
}
@@ -353,19 +350,21 @@ int APS5_VABI sceAmprCommandBufferConstructMarker(Apr::CommandBufferObject* buff
}
int APS5_VABI sceAmprCommandBufferConstructNop(Apr::CommandBufferObject* buffer, std::int16_t type, const void* payload, std::uint32_t payloadBytes, const std::uint32_t* word) {
(void)type;
if (!buffer) return SCE_KERNEL_ERROR_EINVAL;
if (!buffer->base) return SCE_KERNEL_ERROR_EPERM;
if (payloadBytes > (word ? 56u : 60u)) return SCE_KERNEL_ERROR_EINVAL;
if (payloadBytes != 0u && !payload) throw std::invalid_argument("sceAmprCommandBufferConstructNop: null payload");
const std::uint32_t dwords = (payloadBytes + 3u) / 4u + (word ? 1u : 0u);
const std::uint32_t offset = buffer ? buffer->offset : 0u;
const int result = AppendNop(buffer, dwords, nullptr);
const std::uint32_t offset = buffer->offset;
const int result = AppendNop(*buffer, Apr::NopHeader(dwords, static_cast<std::uint32_t>(type)), nullptr);
if (result != 0) return result;
std::uint8_t* data = buffer->base + offset + sizeof(Apr::CommandHeader);
std::uint8_t* data = buffer->base + offset + Apr::NopHeaderBytes;
if (word) {
std::memcpy(data, word, sizeof(*word));
data += sizeof(*word);
}
if (payloadBytes != 0u) std::memcpy(data, payload, payloadBytes);
std::memset(data + payloadBytes, 0, (4u - payloadBytes % 4u) % 4u);
return 0;
}
@@ -401,13 +400,17 @@ uint32_t APS5_VABI sceAmprCommandBufferGetType(const Apr::CommandBufferObject* b
}
int APS5_VABI sceAmprCommandBufferNop(Apr::CommandBufferObject* buffer, std::uint32_t dwords) {
if (!buffer) return SCE_KERNEL_ERROR_EINVAL;
if (!buffer->base) return SCE_KERNEL_ERROR_EPERM;
if (dwords == 0u || dwords > 16u) return SCE_KERNEL_ERROR_EINVAL;
return AppendNop(buffer, dwords, nullptr);
return AppendNop(*buffer, Apr::NopHeader(dwords - 1u), nullptr);
}
int APS5_VABI sceAmprCommandBufferNopWithData(Apr::CommandBufferObject* buffer, std::uint32_t dwords, const std::uint32_t* data) {
if (!buffer) return SCE_KERNEL_ERROR_EINVAL;
if (!buffer->base) return SCE_KERNEL_ERROR_EPERM;
if (dwords > 15u) return SCE_KERNEL_ERROR_EINVAL;
return AppendNop(buffer, dwords, data);
return AppendNop(*buffer, Apr::NopHeader(dwords), data);
}
int APS5_VABI sceAmprCommandBufferPopMarker(Apr::CommandBufferObject* buffer) {
@@ -513,7 +516,7 @@ std::uint64_t APS5_VABI sceAmprMeasureCommandSizeNop(std::uint32_t dwords) {
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeNopWithData(std::uint32_t dwords) {
if (dwords == 0u || dwords > 16u) return MeasureInvalid;
return NopBytes(dwords - 1u);
return NopBytes(dwords);
}
std::uint64_t APS5_VABI sceAmprMeasureCommandSizePopMarker() {
+35 -19
View File
@@ -228,24 +228,34 @@ int APS5_VABI sceAppContentGetAddcontDownloadProgress(uint32_t service_label, co
if (!validEntitlementLabel(*entitlement_label, 0x03500000)) return SCE_APP_CONTENT_ERROR_PARAMETER;
return SCE_APP_CONTENT_ERROR_NOT_FOUND;
}
int APS5_VABI sceAppContentAddcontEnqueueDownloadByEntitlemetId(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceAppContentAddcontEnqueueDownloadByEntitlemetId(const char* entitlementId) {
if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED;
if (!entitlementId) return SCE_APP_CONTENT_ERROR_PARAMETER;
return SCE_APP_CONTENT_ERROR_NOT_FOUND;
}
int APS5_VABI sceAppContentAddcontEnqueueDownloadSp(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceAppContentAddcontEnqueueDownloadSp(uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabel) {
if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED;
(void)serviceLabel;
if (!entitlementLabel) return SCE_APP_CONTENT_ERROR_PARAMETER;
if (!validEntitlementLabel(*entitlementLabel, 0x03500000)) return SCE_APP_CONTENT_ERROR_PARAMETER;
if (!validEntitlementLabel(*entitlementLabel, 0)) throw std::runtime_error("sceAppContentAddcontEnqueueDownloadSp: padded entitlement label before SDK 0x03500000");
return SCE_APP_CONTENT_ERROR_NOT_FOUND;
}
int APS5_VABI sceAppContentAddcontMountByEntitlemetId(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceAppContentAddcontMountByEntitlemetId(const char* entitlementId, AppContentMountPoint* mountPoint) {
if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED;
if (!entitlementId || !mountPoint) return SCE_APP_CONTENT_ERROR_PARAMETER;
return SCE_APP_CONTENT_ERROR_NOT_FOUND;
}
int APS5_VABI sceAppContentAddcontShrink(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceAppContentAddcontShrink(uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabel) {
if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED;
(void)serviceLabel;
if (!entitlementLabel) return SCE_APP_CONTENT_ERROR_PARAMETER;
if (!validEntitlementLabel(*entitlementLabel, 0x03500000)) return SCE_APP_CONTENT_ERROR_PARAMETER;
if (!validEntitlementLabel(*entitlementLabel, 0)) throw std::runtime_error("sceAppContentAddcontShrink: padded entitlement label before SDK 0x03500000");
return SCE_APP_CONTENT_ERROR_NOT_FOUND;
}
int APS5_VABI sceAppContentAppParamGetString(void) {
@@ -273,13 +283,17 @@ int APS5_VABI sceAppContentDownload1Shrink(void) {
return 0;
}
int APS5_VABI sceAppContentGetAddcontInfoByEntitlementId(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceAppContentGetAddcontInfoByEntitlementId(const char* entitlementId, AppContentAddcontInfo* info) {
if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED;
if (!entitlementId || !info) return SCE_APP_CONTENT_ERROR_PARAMETER;
return SCE_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT;
}
int APS5_VABI sceAppContentGetAddcontInfoListByIroTag(void) {
NotImplemented_nid_no_patch(__func__);
int APS5_VABI sceAppContentGetAddcontInfoListByIroTag(uint32_t iroTag, AppContentAddcontInfo* list, uint32_t listNum, uint32_t* hitNum) {
if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED;
(void)iroTag;
if ((!list || listNum == 0) && !hitNum) return SCE_APP_CONTENT_ERROR_PARAMETER;
if (hitNum) *hitNum = 0;
return 0;
}
@@ -296,8 +310,10 @@ int APS5_VABI sceAppContentGetEntitlementKey(uint32_t serviceLabel, const NpUnif
return SCE_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT;
}
int APS5_VABI sceAppContentGetPftFlag(void) {
NotImplemented_nid_no_patch(__func__);
int APS5_VABI sceAppContentGetPftFlag(uint32_t* pftFlag) {
if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED;
if (!pftFlag) return SCE_APP_CONTENT_ERROR_PARAMETER;
*pftFlag = 0;
return 0;
}
@@ -28,20 +28,20 @@ int APS5_VABI sceAppContentTemporaryDataUnmount(const AppContentMountPoint* moun
int APS5_VABI sceAppContentAddcontEnqueueDownload(uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabel);
int APS5_VABI sceAppContentAddcontDelete(uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabel);
int APS5_VABI sceAppContentGetAddcontDownloadProgress(uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabel, void* progress);
int APS5_VABI sceAppContentAddcontEnqueueDownloadByEntitlemetId(void);
int APS5_VABI sceAppContentAddcontEnqueueDownloadSp(void);
int APS5_VABI sceAppContentAddcontMountByEntitlemetId(void);
int APS5_VABI sceAppContentAddcontShrink(void);
int APS5_VABI sceAppContentAddcontEnqueueDownloadByEntitlemetId(const char* entitlementId);
int APS5_VABI sceAppContentAddcontEnqueueDownloadSp(uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabel);
int APS5_VABI sceAppContentAddcontMountByEntitlemetId(const char* entitlementId, AppContentMountPoint* mountPoint);
int APS5_VABI sceAppContentAddcontShrink(uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabel);
int APS5_VABI sceAppContentAppParamGetString(void);
int APS5_VABI sceAppContentDownload0Expand(void);
int APS5_VABI sceAppContentDownload0Shrink(void);
int APS5_VABI sceAppContentDownload1Expand(void);
int APS5_VABI sceAppContentDownload1Shrink(void);
int APS5_VABI sceAppContentGetAddcontInfoByEntitlementId(void);
int APS5_VABI sceAppContentGetAddcontInfoListByIroTag(void);
int APS5_VABI sceAppContentGetAddcontInfoByEntitlementId(const char* entitlementId, AppContentAddcontInfo* info);
int APS5_VABI sceAppContentGetAddcontInfoListByIroTag(uint32_t iroTag, AppContentAddcontInfo* list, uint32_t listNum, uint32_t* hitNum);
int APS5_VABI sceAppContentGetDownloadedStoreCountry(void);
int APS5_VABI sceAppContentGetEntitlementKey(uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabel, AppContentEntitlementKey* key);
int APS5_VABI sceAppContentGetPftFlag(void);
int APS5_VABI sceAppContentGetPftFlag(uint32_t* pftFlag);
int APS5_VABI sceAppContentGetRegion(void);
int APS5_VABI sceAppContentRequestPatchInstall(void);
int APS5_VABI sceAppContentTemporaryDataMount(AppContentMountPoint* mountPoint);
+520 -2
View File
@@ -6,8 +6,6 @@
extern "C" {
APS5_DUMMY_FUN
int APS5_VABI sceAvControlGetPuTypeByMonitorInfo(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -128,4 +126,524 @@ int APS5_VABI sceAvControlDebugAddCallbacks(void) {
return 0;
}
int APS5_VABI sceAvControlChangeOutputLayout() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlChangeOutputMode() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlChangeOutputModeForDiag() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlChangeProcessAttribute() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlChangeScanInMode() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlGetCurrentDeviceId() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlGetCurrentOutputMode() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlGetCurrentOutputModeWithSync() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlGetDetailHdcpStatus() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlGetDeviceInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlGetHdcpStatus() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlGetMonitorInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlGetNativeMonitorInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlGetPuStatus() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlGetPuType() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlInit() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlIsAudioOutModeSupportedByMonitorInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlIsColorimetryHdr() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlIsDeepColorSupportedByMonitorInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlIsDeviceConnected() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlIsHdrSupportedByMonitorInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlIsModeGen1Vr() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlIsModeSystemDefault() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlIsSuspendedProcessOutputModeAvailable() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlIsVideoOutModeSupportedByMonitorInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlSet2dVrMode() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlSetAudioMuteForShutdown() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlSetAvOutputMode() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlSetColorEffect() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlSetDefaultAudioOutMode() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlSetDefaultVideoOutMode() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlSetDispclk() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlSetGamutMetadata() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlSetHdcpEncryption() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlSetHdcpStatus() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlSetHdmiGamutMetadata() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlSetHdrMetadata() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlSetVideoOutSource() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvControlSimulateProcessOutputModeArbitration() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingAddCallbacks() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingAddCallbacksForLnc() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingAddCallbacksForSocialScreen() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingCallbackFuncsInit() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingCallbackFuncsInitForLnc() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingCallbackFuncsInitForLnc_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingCallbackFuncsInit_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingChangeOutputMode() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingChangeOutputMode2() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingChangeOutputMode3() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingChangeOutputModeForDiag() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingChangeProcessAttribute() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingCheckCallback() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingControlHdcpEncryption() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingDebugAddCallbacks() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingDebugClearDiagCommand() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingDebugSetDiagState() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingDebugSetHdmiMonitorInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingDebugSetProcessAttribute() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingDriverChangeConnectionStatus() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingDriverUpdateStatus() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingEnterAudioMuteForShutdown() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingGetCurrentDeviceInfo_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingGetCurrentHdmiDeviceId() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingGetCurrentOutputMode2_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingGetCurrentOutputMode_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingGetDetailedHdcpStatus() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingGetDeviceInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingGetHdcpStatus() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingGetHdmiConnectDisconnectNum() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingGetHdmiKsvList() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingGetHdmiMonitorInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingGetHdmiRawEdid() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingGetMorpheusPuStatus() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingGetNativeHdmiMonitorInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingGetRawHdmiMonitorInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingInit() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingIsSupportedAudioOutModeByHdmiMonitorInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingIsSupportedHdcpVersionByHdmiMonitorInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingIsSupportedVideoOutModeByHdmiMonitorInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingIsSuspendedProcessOutputModeAvailable() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingIsSuspendedProcessOutputModeAvailable2() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingIsVrSupportedByHdmiMonitorInfo2() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingNotifyAudioOutMode() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingNotifyResponseForSocialScreen() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingNotifyUmdEvent() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingNotifyVideoOutMode() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingRemoveCallbacks() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingRemoveProcessOutputMode() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingSet2dVrMode() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingSetAudioOutModeAny_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingSetAudioOutModeInvalid_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingSetAvOutputMode() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingSetDispclk() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingSetDisplayBlackLevel() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingSetHdcpMode() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingSetHdcpStatus() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingSetHdmiGamutMetadata_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingSetVideoOutModeAny_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingSetVideoOutModeInvalid_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingSetVideoOutSource() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingSetVideoOutputColorEffect() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingSimulateProcessOutputModeArbitration_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingTerm() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAvSettingWaitCallback() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}
@@ -28,5 +28,109 @@ int APS5_VABI sceAvControlCheckVideoOutMode(void);
int APS5_VABI sceAvControlSetCheckoutputMode(void);
int APS5_VABI sceAvControlSetSystemVideoMode(void);
int APS5_VABI sceAvControlDebugAddCallbacks(void);
int APS5_VABI sceAvControlChangeOutputLayout(void);
int APS5_VABI sceAvControlChangeOutputMode(void);
int APS5_VABI sceAvControlChangeOutputModeForDiag(void);
int APS5_VABI sceAvControlChangeProcessAttribute(void);
int APS5_VABI sceAvControlChangeScanInMode(void);
int APS5_VABI sceAvControlGetCurrentDeviceId(void);
int APS5_VABI sceAvControlGetCurrentOutputMode(void);
int APS5_VABI sceAvControlGetCurrentOutputModeWithSync(void);
int APS5_VABI sceAvControlGetDetailHdcpStatus(void);
int APS5_VABI sceAvControlGetDeviceInfo(void);
int APS5_VABI sceAvControlGetHdcpStatus(void);
int APS5_VABI sceAvControlGetMonitorInfo(void);
int APS5_VABI sceAvControlGetNativeMonitorInfo(void);
int APS5_VABI sceAvControlGetPuStatus(void);
int APS5_VABI sceAvControlGetPuType(void);
int APS5_VABI sceAvControlInit(void);
int APS5_VABI sceAvControlIsAudioOutModeSupportedByMonitorInfo(void);
int APS5_VABI sceAvControlIsColorimetryHdr(void);
int APS5_VABI sceAvControlIsDeepColorSupportedByMonitorInfo(void);
int APS5_VABI sceAvControlIsDeviceConnected(void);
int APS5_VABI sceAvControlIsHdrSupportedByMonitorInfo(void);
int APS5_VABI sceAvControlIsModeGen1Vr(void);
int APS5_VABI sceAvControlIsModeSystemDefault(void);
int APS5_VABI sceAvControlIsSuspendedProcessOutputModeAvailable(void);
int APS5_VABI sceAvControlIsVideoOutModeSupportedByMonitorInfo(void);
int APS5_VABI sceAvControlSet2dVrMode(void);
int APS5_VABI sceAvControlSetAudioMuteForShutdown(void);
int APS5_VABI sceAvControlSetAvOutputMode(void);
int APS5_VABI sceAvControlSetColorEffect(void);
int APS5_VABI sceAvControlSetDefaultAudioOutMode(void);
int APS5_VABI sceAvControlSetDefaultVideoOutMode(void);
int APS5_VABI sceAvControlSetDispclk(void);
int APS5_VABI sceAvControlSetGamutMetadata(void);
int APS5_VABI sceAvControlSetHdcpEncryption(void);
int APS5_VABI sceAvControlSetHdcpStatus(void);
int APS5_VABI sceAvControlSetHdmiGamutMetadata(void);
int APS5_VABI sceAvControlSetHdrMetadata(void);
int APS5_VABI sceAvControlSetVideoOutSource(void);
int APS5_VABI sceAvControlSimulateProcessOutputModeArbitration(void);
int APS5_VABI sceAvSettingAddCallbacks(void);
int APS5_VABI sceAvSettingAddCallbacksForLnc(void);
int APS5_VABI sceAvSettingAddCallbacksForSocialScreen(void);
int APS5_VABI sceAvSettingCallbackFuncsInit(void);
int APS5_VABI sceAvSettingCallbackFuncsInitForLnc(void);
int APS5_VABI sceAvSettingCallbackFuncsInitForLnc_(void);
int APS5_VABI sceAvSettingCallbackFuncsInit_(void);
int APS5_VABI sceAvSettingChangeOutputMode(void);
int APS5_VABI sceAvSettingChangeOutputMode2(void);
int APS5_VABI sceAvSettingChangeOutputMode3(void);
int APS5_VABI sceAvSettingChangeOutputModeForDiag(void);
int APS5_VABI sceAvSettingChangeProcessAttribute(void);
int APS5_VABI sceAvSettingCheckCallback(void);
int APS5_VABI sceAvSettingControlHdcpEncryption(void);
int APS5_VABI sceAvSettingDebugAddCallbacks(void);
int APS5_VABI sceAvSettingDebugClearDiagCommand(void);
int APS5_VABI sceAvSettingDebugSetDiagState(void);
int APS5_VABI sceAvSettingDebugSetHdmiMonitorInfo(void);
int APS5_VABI sceAvSettingDebugSetProcessAttribute(void);
int APS5_VABI sceAvSettingDriverChangeConnectionStatus(void);
int APS5_VABI sceAvSettingDriverUpdateStatus(void);
int APS5_VABI sceAvSettingEnterAudioMuteForShutdown(void);
int APS5_VABI sceAvSettingGetCurrentDeviceInfo_(void);
int APS5_VABI sceAvSettingGetCurrentHdmiDeviceId(void);
int APS5_VABI sceAvSettingGetCurrentOutputMode2_(void);
int APS5_VABI sceAvSettingGetCurrentOutputMode_(void);
int APS5_VABI sceAvSettingGetDetailedHdcpStatus(void);
int APS5_VABI sceAvSettingGetDeviceInfo(void);
int APS5_VABI sceAvSettingGetHdcpStatus(void);
int APS5_VABI sceAvSettingGetHdmiConnectDisconnectNum(void);
int APS5_VABI sceAvSettingGetHdmiKsvList(void);
int APS5_VABI sceAvSettingGetHdmiMonitorInfo(void);
int APS5_VABI sceAvSettingGetHdmiRawEdid(void);
int APS5_VABI sceAvSettingGetMorpheusPuStatus(void);
int APS5_VABI sceAvSettingGetNativeHdmiMonitorInfo(void);
int APS5_VABI sceAvSettingGetRawHdmiMonitorInfo(void);
int APS5_VABI sceAvSettingInit(void);
int APS5_VABI sceAvSettingIsSupportedAudioOutModeByHdmiMonitorInfo(void);
int APS5_VABI sceAvSettingIsSupportedHdcpVersionByHdmiMonitorInfo(void);
int APS5_VABI sceAvSettingIsSupportedVideoOutModeByHdmiMonitorInfo(void);
int APS5_VABI sceAvSettingIsSuspendedProcessOutputModeAvailable(void);
int APS5_VABI sceAvSettingIsSuspendedProcessOutputModeAvailable2(void);
int APS5_VABI sceAvSettingIsVrSupportedByHdmiMonitorInfo2(void);
int APS5_VABI sceAvSettingNotifyAudioOutMode(void);
int APS5_VABI sceAvSettingNotifyResponseForSocialScreen(void);
int APS5_VABI sceAvSettingNotifyUmdEvent(void);
int APS5_VABI sceAvSettingNotifyVideoOutMode(void);
int APS5_VABI sceAvSettingRemoveCallbacks(void);
int APS5_VABI sceAvSettingRemoveProcessOutputMode(void);
int APS5_VABI sceAvSettingSet2dVrMode(void);
int APS5_VABI sceAvSettingSetAudioOutModeAny_(void);
int APS5_VABI sceAvSettingSetAudioOutModeInvalid_(void);
int APS5_VABI sceAvSettingSetAvOutputMode(void);
int APS5_VABI sceAvSettingSetDispclk(void);
int APS5_VABI sceAvSettingSetDisplayBlackLevel(void);
int APS5_VABI sceAvSettingSetHdcpMode(void);
int APS5_VABI sceAvSettingSetHdcpStatus(void);
int APS5_VABI sceAvSettingSetHdmiGamutMetadata_(void);
int APS5_VABI sceAvSettingSetVideoOutModeAny_(void);
int APS5_VABI sceAvSettingSetVideoOutModeInvalid_(void);
int APS5_VABI sceAvSettingSetVideoOutSource(void);
int APS5_VABI sceAvSettingSetVideoOutputColorEffect(void);
int APS5_VABI sceAvSettingSimulateProcessOutputModeArbitration_(void);
int APS5_VABI sceAvSettingTerm(void);
int APS5_VABI sceAvSettingWaitCallback(void);
}
+4 -1
View File
@@ -4,6 +4,7 @@
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
#include <stdexcept>
#include <string>
#include <mutex>
@@ -118,7 +119,9 @@ int APS5_VABI sceBluetoothHidInterruptOutput(std::uint64_t deviceHandle, std::ui
}
int APS5_VABI sceBluetoothHidParamInitialize(BluetoothHidParam* param, const BluetoothHidThreadParam* threadParam) {
NotImplemented_nid_no_patch(__func__);
if (param == nullptr) return InvalidArgument;
param->threadParam = threadParam;
param->reserved = 0;
return 0;
}
+55 -1
View File
@@ -1,3 +1,4 @@
#include "prx/libSceCdlgUtilServer/include/Api.hpp"
#include <cstddef>
#include <cstdint>
#include "SceTypes.hpp"
@@ -5,6 +6,59 @@
extern "C" {
APS5_DUMMY_FUN
int APS5_VABI _ZN3sce16CommonDialogUtil6Server13setFinishDataEPvm() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce16CommonDialogUtil6Server6setRunEPvm() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce16CommonDialogUtil6Server9setFinishEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce16CommonDialogUtil6Server9setResultEi() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce16CommonDialogUtil6ServerC2EPKcmmis() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce16CommonDialogUtil6ServerD0Ev() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce16CommonDialogUtil6ServerD1Ev() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce16CommonDialogUtil6ServerD2Ev() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK3sce16CommonDialogUtil6Server10getVersionERj() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK3sce16CommonDialogUtil6Server10isCloseReqEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK3sce16CommonDialogUtil6Server14getServerStateEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}
@@ -0,0 +1,19 @@
#pragma once
#include "SceTypes.hpp"
extern "C" {
int APS5_VABI _ZN3sce16CommonDialogUtil6Server13setFinishDataEPvm(void);
int APS5_VABI _ZN3sce16CommonDialogUtil6Server6setRunEPvm(void);
int APS5_VABI _ZN3sce16CommonDialogUtil6Server9setFinishEv(void);
int APS5_VABI _ZN3sce16CommonDialogUtil6Server9setResultEi(void);
int APS5_VABI _ZN3sce16CommonDialogUtil6ServerC2EPKcmmis(void);
int APS5_VABI _ZN3sce16CommonDialogUtil6ServerD0Ev(void);
int APS5_VABI _ZN3sce16CommonDialogUtil6ServerD1Ev(void);
int APS5_VABI _ZN3sce16CommonDialogUtil6ServerD2Ev(void);
int APS5_VABI _ZNK3sce16CommonDialogUtil6Server10getVersionERj(void);
int APS5_VABI _ZNK3sce16CommonDialogUtil6Server10isCloseReqEv(void);
int APS5_VABI _ZNK3sce16CommonDialogUtil6Server14getServerStateEv(void);
}
@@ -1 +1,3 @@
#pragma once
#include "prx/libSceCdlgUtilServer/include/Api.hpp"
+28 -17
View File
@@ -371,11 +371,17 @@ constexpr uint32_t GB_CELLS = 126 * 190;
struct Gb18030Tables {
std::span<const uint32_t[3]> twoByte;
std::span<const uint32_t[3]> fourByte;
std::span<const uint32_t[2]> fallback;
bool edition2005;
};
constexpr Gb18030Tables GB18030_2000{Gb18030::TWO_BYTE_2000, Gb18030::FOUR_BYTE_2000, {}};
constexpr Gb18030Tables GB18030_2005{Gb18030::TWO_BYTE_2005, Gb18030::FOUR_BYTE_2005, Gb18030::FALLBACK_2005};
constexpr Gb18030Tables GB18030_2000{Gb18030::TWO_BYTE_2000, Gb18030::FOUR_BYTE_2000, false};
constexpr Gb18030Tables GB18030_2005{Gb18030::TWO_BYTE_2000, Gb18030::FOUR_BYTE_2000, true};
constexpr uint32_t GB18030_SWAPPED_CELL = (0xA8 - 0x81) * 190 + (0xBC - 0x41);
constexpr uint32_t GB18030_SWAPPED_LINEAR = (5 * 126 + (0xF4 - 0x81)) * 10 + 7;
bool mapsGb18030Supplementary(const Gb18030Tables& tables, uint32_t code) {
return tables.edition2005 && code >= 0x20000 && code <= 0x2FFFF;
}
const Gb18030Tables* gb18030Tables(const void* profile) {
if (profile == nullptr) return nullptr;
@@ -400,10 +406,20 @@ const uint32_t* findRun(std::span<const uint32_t[3]> runs, uint32_t key) {
}
uint32_t gb18030Cell(const Gb18030Tables& tables, uint32_t cell) {
if (tables.edition2005 && cell == GB18030_SWAPPED_CELL) return 0x1E3F;
const uint32_t* run = findRun(tables.twoByte, cell);
return run ? run[1] + cell - run[0] : Cp936::TO_UCS2[cell];
}
void putGbLinear(uint32_t linear, uint8_t* mbcs) {
mbcs[3] = static_cast<uint8_t>(0x30 + linear % 10);
linear /= 10;
mbcs[2] = static_cast<uint8_t>(0x81 + linear % 126);
linear /= 126;
mbcs[1] = static_cast<uint8_t>(0x30 + linear % 10);
mbcs[0] = static_cast<uint8_t>(0x81 + linear / 10);
}
void putGbCell(uint32_t cell, uint8_t* mbcs) {
const uint32_t column = cell % 190;
mbcs[0] = static_cast<uint8_t>(0x81 + cell / 190);
@@ -427,8 +443,9 @@ int decodeGb18030(const Gb18030Tables& tables, const uint8_t* mbcs, uint32_t mbc
const uint32_t linear = (((lead - 0x81) * 10 + (second - 0x30)) * 126 + (mbcs[2] - 0x81)) * 10 + (mbcs[3] - 0x30);
const uint32_t* run = findRun(tables.fourByte, linear);
if (!run) return unassigned(4, length);
code = tables.edition2005 && linear == GB18030_SWAPPED_LINEAR ? 0xE7C7 : run[1] + linear - run[0];
if (code > 0xFFFF && !mapsGb18030Supplementary(tables, code)) return unassigned(4, length);
length = 4;
code = run[1] + linear - run[0];
return 0;
}
if (second < 0x40 || second == 0x7F || second == 0xFF) return rejectTrail(second, 2, length);
@@ -463,22 +480,16 @@ int encodeGb18030(const Gb18030Tables& tables, uint32_t code, uint8_t* mbcs, uin
length = 2;
return 0;
}
for (const auto& run : tables.fourByte) {
if (code - run[1] >= run[2]) continue;
uint32_t linear = run[0] + code - run[1];
mbcs[3] = static_cast<uint8_t>(0x30 + linear % 10);
linear /= 10;
mbcs[2] = static_cast<uint8_t>(0x81 + linear % 126);
linear /= 126;
mbcs[1] = static_cast<uint8_t>(0x30 + linear % 10);
mbcs[0] = static_cast<uint8_t>(0x81 + linear / 10);
if (code > 0xFFFF && !mapsGb18030Supplementary(tables, code)) return CES_ERROR_UNASSIGNED_CODE;
if (tables.edition2005 && code == 0xE7C7) {
putGbLinear(GB18030_SWAPPED_LINEAR, mbcs);
length = 4;
return 0;
}
for (const auto& entry : tables.fallback) {
if (entry[0] != code) continue;
putGbCell(entry[1], mbcs);
length = 2;
for (const auto& run : tables.fourByte) {
if (code - run[1] >= run[2]) continue;
putGbLinear(run[0] + code - run[1], mbcs);
length = 4;
return 0;
}
return CES_ERROR_UNASSIGNED_CODE;
-68
View File
@@ -64,72 +64,4 @@ inline constexpr uint32_t FOUR_BYTE_2000[207][3] = {
{0x9961, 0xFF5F, 0x81}, {0x99E2, 0xFFE6, 0x1A}, {0x2E248, 0x10000, 0x100000},
};
inline constexpr uint32_t TWO_BYTE_2005[118][3] = {
{0x17C0, 0xE4C6, 0x60}, {0x187E, 0xE526, 0x60}, {0x18E8, 0xE766, 0x6}, {0x1920, 0x20AC, 0x1}, {0x1921, 0xE76D, 0x1}, {0x192C, 0xE76E, 0x2},
{0x193A, 0xE770, 0x2}, {0x193C, 0xE586, 0x60}, {0x19FA, 0xE5E6, 0x60}, {0x1AAD, 0xE772, 0xB}, {0x1AB8, 0xE646, 0x60}, {0x1B6E, 0xE77D, 0x8},
{0x1B76, 0xE6A6, 0x60}, {0x1BEE, 0xE785, 0x8}, {0x1C0E, 0xFE10, 0x1}, {0x1C0F, 0xFE12, 0x1}, {0x1C10, 0xFE11, 0x1}, {0x1C11, 0xFE13, 0x4},
{0x1C21, 0xFE17, 0x2}, {0x1C28, 0xFE19, 0x1}, {0x1C2B, 0xE797, 0x9}, {0x1C34, 0xE706, 0x60}, {0x1CB5, 0xE7A0, 0xF}, {0x1CE5, 0xE7AF, 0xD},
{0x1D47, 0xE7BC, 0xB}, {0x1D6D, 0x1E3F, 0x1}, {0x1D70, 0x1F9, 0x1}, {0x1D72, 0xE7C9, 0x4}, {0x1D9B, 0xE7CD, 0x15}, {0x1DC8, 0xE7E2, 0x1},
{0x1DCB, 0xE7E3, 0x1}, {0x1DCD, 0xE7E4, 0x3}, {0x1DF8, 0x303E, 0x1}, {0x1DF9, 0x2FF0, 0xC}, {0x1E06, 0xE7F4, 0xD}, {0x1E5F, 0xE801, 0xF},
{0x1ECE, 0xE000, 0x5E}, {0x1F8C, 0xE05E, 0x5E}, {0x204A, 0xE0BC, 0x5E}, {0x2108, 0xE11A, 0x5E}, {0x21C6, 0xE178, 0x5E}, {0x2284, 0xE1D6, 0x5E},
{0x408D, 0xE810, 0x5}, {0x58B2, 0xE234, 0x5E}, {0x5970, 0xE292, 0x5E}, {0x5A2E, 0xE2F0, 0x5E}, {0x5AEC, 0xE34E, 0x5E}, {0x5BAA, 0xE3AC, 0x5E},
{0x5C68, 0xE40A, 0x5E}, {0x5CD6, 0x2E81, 0x1}, {0x5CD7, 0x20087, 0x1}, {0x5CD8, 0x20089, 0x1}, {0x5CD9, 0x200CC, 0x1}, {0x5CDA, 0x2E84, 0x1},
{0x5CDB, 0x3473, 0x1}, {0x5CDC, 0x3447, 0x1}, {0x5CDD, 0x2E88, 0x1}, {0x5CDE, 0x2E8B, 0x1}, {0x5CDF, 0x9FB4, 0x1}, {0x5CE0, 0x359E, 0x1},
{0x5CE1, 0x361A, 0x1}, {0x5CE2, 0x360E, 0x1}, {0x5CE3, 0x2E8C, 0x1}, {0x5CE4, 0x2E97, 0x1}, {0x5CE5, 0x396E, 0x1}, {0x5CE6, 0x3918, 0x1},
{0x5CE7, 0x9FB5, 0x1}, {0x5CE8, 0x39CF, 0x1}, {0x5CE9, 0x39DF, 0x1}, {0x5CEA, 0x3A73, 0x1}, {0x5CEB, 0x39D0, 0x1}, {0x5CEC, 0x9FB6, 0x2},
{0x5CEE, 0x3B4E, 0x1}, {0x5CEF, 0x3C6E, 0x1}, {0x5CF0, 0x3CE0, 0x1}, {0x5CF1, 0x2EA7, 0x1}, {0x5CF2, 0x215D7, 0x1}, {0x5CF3, 0x9FB8, 0x1},
{0x5CF4, 0x2EAA, 0x1}, {0x5CF5, 0x4056, 0x1}, {0x5CF6, 0x415F, 0x1}, {0x5CF7, 0x2EAE, 0x1}, {0x5CF8, 0x4337, 0x1}, {0x5CF9, 0x2EB3, 0x1},
{0x5CFA, 0x2EB6, 0x2}, {0x5CFC, 0x2298F, 0x1}, {0x5CFD, 0x43B1, 0x1}, {0x5CFE, 0x43AC, 0x1}, {0x5CFF, 0x2EBB, 0x1}, {0x5D00, 0x43DD, 0x1},
{0x5D01, 0x44D6, 0x1}, {0x5D02, 0x4661, 0x1}, {0x5D03, 0x464C, 0x1}, {0x5D04, 0x9FB9, 0x1}, {0x5D05, 0x4723, 0x1}, {0x5D06, 0x4729, 0x1},
{0x5D07, 0x477C, 0x1}, {0x5D08, 0x478D, 0x1}, {0x5D09, 0x2ECA, 0x1}, {0x5D0A, 0x4947, 0x1}, {0x5D0B, 0x497A, 0x1}, {0x5D0C, 0x497D, 0x1},
{0x5D0D, 0x4982, 0x2}, {0x5D0F, 0x4985, 0x2}, {0x5D11, 0x499F, 0x1}, {0x5D12, 0x499B, 0x1}, {0x5D13, 0x49B7, 0x1}, {0x5D14, 0x49B6, 0x1},
{0x5D15, 0x9FBA, 0x1}, {0x5D16, 0x241FE, 0x1}, {0x5D17, 0x4CA3, 0x1}, {0x5D18, 0x4C9F, 0x3}, {0x5D1B, 0x4C77, 0x1}, {0x5D1C, 0x4CA2, 0x1},
{0x5D1D, 0x4D13, 0x7}, {0x5D24, 0x4DAE, 0x1}, {0x5D25, 0x9FBB, 0x1}, {0x5D26, 0xE468, 0x5E},
};
inline constexpr uint32_t FOUR_BYTE_2005[209][3] = {
{0x0, 0x80, 0x24}, {0x24, 0xA5, 0x2}, {0x26, 0xA9, 0x7}, {0x2D, 0xB2, 0x5}, {0x32, 0xB8, 0x1F}, {0x51, 0xD8, 0x8},
{0x59, 0xE2, 0x6}, {0x5F, 0xEB, 0x1}, {0x60, 0xEE, 0x4}, {0x64, 0xF4, 0x3}, {0x67, 0xF8, 0x1}, {0x68, 0xFB, 0x1},
{0x69, 0xFD, 0x4}, {0x6D, 0x102, 0x11}, {0x7E, 0x114, 0x7}, {0x85, 0x11C, 0xF}, {0x94, 0x12C, 0x18}, {0xAC, 0x145, 0x3},
{0xAF, 0x149, 0x4}, {0xB3, 0x14E, 0x1D}, {0xD0, 0x16C, 0x62}, {0x132, 0x1CF, 0x1}, {0x133, 0x1D1, 0x1}, {0x134, 0x1D3, 0x1},
{0x135, 0x1D5, 0x1}, {0x136, 0x1D7, 0x1}, {0x137, 0x1D9, 0x1}, {0x138, 0x1DB, 0x1}, {0x139, 0x1DD, 0x1C}, {0x155, 0x1FA, 0x57},
{0x1AC, 0x252, 0xF}, {0x1BB, 0x262, 0x65}, {0x220, 0x2C8, 0x1}, {0x221, 0x2CC, 0xD}, {0x22E, 0x2DA, 0xB7}, {0x2E5, 0x3A2, 0x1},
{0x2E6, 0x3AA, 0x7}, {0x2ED, 0x3C2, 0x1}, {0x2EE, 0x3CA, 0x37}, {0x325, 0x402, 0xE}, {0x333, 0x450, 0x1}, {0x334, 0x452, 0x19ED},
{0x1D21, 0xE7C7, 0x1}, {0x1D22, 0x1E40, 0x1D0}, {0x1EF2, 0x2011, 0x2}, {0x1EF4, 0x2017, 0x1}, {0x1EF5, 0x201A, 0x2}, {0x1EF7, 0x201E, 0x7},
{0x1EFE, 0x2027, 0x9}, {0x1F07, 0x2031, 0x1}, {0x1F08, 0x2034, 0x1}, {0x1F09, 0x2036, 0x5}, {0x1F0E, 0x203C, 0x70}, {0x1F7E, 0x20AD, 0x56},
{0x1FD4, 0x2104, 0x1}, {0x1FD5, 0x2106, 0x3}, {0x1FD8, 0x210A, 0xC}, {0x1FE4, 0x2117, 0xA}, {0x1FEE, 0x2122, 0x3E}, {0x202C, 0x216C, 0x4},
{0x2030, 0x217A, 0x16}, {0x2046, 0x2194, 0x2}, {0x2048, 0x219A, 0x6E}, {0x20B6, 0x2209, 0x6}, {0x20BC, 0x2210, 0x1}, {0x20BD, 0x2212, 0x3},
{0x20C0, 0x2216, 0x4}, {0x20C4, 0x221B, 0x2}, {0x20C6, 0x2221, 0x2}, {0x20C8, 0x2224, 0x1}, {0x20C9, 0x2226, 0x1}, {0x20CA, 0x222C, 0x2},
{0x20CC, 0x222F, 0x5}, {0x20D1, 0x2238, 0x5}, {0x20D6, 0x223E, 0xA}, {0x20E0, 0x2249, 0x3}, {0x20E3, 0x224D, 0x5}, {0x20E8, 0x2253, 0xD},
{0x20F5, 0x2262, 0x2}, {0x20F7, 0x2268, 0x6}, {0x20FD, 0x2270, 0x25}, {0x2122, 0x2296, 0x3}, {0x2125, 0x229A, 0xB}, {0x2130, 0x22A6, 0x19},
{0x2149, 0x22C0, 0x52}, {0x219B, 0x2313, 0x14D}, {0x22E8, 0x246A, 0xA}, {0x22F2, 0x249C, 0x64}, {0x2356, 0x254C, 0x4}, {0x235A, 0x2574, 0xD},
{0x2367, 0x2590, 0x3}, {0x236A, 0x2596, 0xA}, {0x2374, 0x25A2, 0x10}, {0x2384, 0x25B4, 0x8}, {0x238C, 0x25BE, 0x8}, {0x2394, 0x25C8, 0x3},
{0x2397, 0x25CC, 0x2}, {0x2399, 0x25D0, 0x12}, {0x23AB, 0x25E6, 0x1F}, {0x23CA, 0x2607, 0x2}, {0x23CC, 0x260A, 0x36}, {0x2402, 0x2641, 0x1},
{0x2403, 0x2643, 0x83E}, {0x2C41, 0x2E82, 0x2}, {0x2C43, 0x2E85, 0x3}, {0x2C46, 0x2E89, 0x2}, {0x2C48, 0x2E8D, 0xA}, {0x2C52, 0x2E98, 0xF},
{0x2C61, 0x2EA8, 0x2}, {0x2C63, 0x2EAB, 0x3}, {0x2C66, 0x2EAF, 0x4}, {0x2C6A, 0x2EB4, 0x2}, {0x2C6C, 0x2EB8, 0x3}, {0x2C6F, 0x2EBC, 0xE},
{0x2C7D, 0x2ECB, 0x125}, {0x2DA2, 0x2FFC, 0x4}, {0x2DA6, 0x3004, 0x1}, {0x2DA7, 0x3018, 0x5}, {0x2DAC, 0x301F, 0x2}, {0x2DAE, 0x302A, 0x14},
{0x2DC2, 0x303F, 0x2}, {0x2DC4, 0x3094, 0x7}, {0x2DCB, 0x309F, 0x2}, {0x2DCD, 0x30F7, 0x5}, {0x2DD2, 0x30FF, 0x6}, {0x2DD8, 0x312A, 0xF6},
{0x2ECE, 0x322A, 0x7}, {0x2ED5, 0x3232, 0x71}, {0x2F46, 0x32A4, 0xEA}, {0x3030, 0x3390, 0xC}, {0x303C, 0x339F, 0x2}, {0x303E, 0x33A2, 0x22},
{0x3060, 0x33C5, 0x9}, {0x3069, 0x33CF, 0x2}, {0x306B, 0x33D3, 0x2}, {0x306D, 0x33D6, 0x71}, {0x30DE, 0x3448, 0x2B}, {0x3109, 0x3474, 0x12A},
{0x3233, 0x359F, 0x6F}, {0x32A2, 0x360F, 0xB}, {0x32AD, 0x361B, 0x2FD}, {0x35AA, 0x3919, 0x55}, {0x35FF, 0x396F, 0x60}, {0x365F, 0x39D1, 0xE},
{0x366D, 0x39E0, 0x93}, {0x3700, 0x3A74, 0xDA}, {0x37DA, 0x3B4F, 0x11F}, {0x38F9, 0x3C6F, 0x71}, {0x396A, 0x3CE1, 0x375}, {0x3CDF, 0x4057, 0x108},
{0x3DE7, 0x4160, 0x1D7}, {0x3FBE, 0x4338, 0x74}, {0x4032, 0x43AD, 0x4}, {0x4036, 0x43B2, 0x2B}, {0x4061, 0x43DE, 0xF8}, {0x4159, 0x44D7, 0x175},
{0x42CE, 0x464D, 0x14}, {0x42E2, 0x4662, 0xC1}, {0x43A3, 0x4724, 0x5}, {0x43A8, 0x472A, 0x52}, {0x43FA, 0x477D, 0x10}, {0x440A, 0x478E, 0x1B9},
{0x45C3, 0x4948, 0x32}, {0x45F5, 0x497B, 0x2}, {0x45F7, 0x497E, 0x4}, {0x45FB, 0x4984, 0x1}, {0x45FC, 0x4987, 0x14}, {0x4610, 0x499C, 0x3},
{0x4613, 0x49A0, 0x16}, {0x4629, 0x49B8, 0x2BF}, {0x48E8, 0x4C78, 0x27}, {0x490F, 0x4CA4, 0x6F}, {0x497E, 0x4D1A, 0x94}, {0x4A12, 0x4DAF, 0x51},
{0x4A63, 0x9FA6, 0x385A}, {0x82BD, 0xE76C, 0x1}, {0x82BE, 0xE7C8, 0x1}, {0x82BF, 0xE7E7, 0xD}, {0x82CC, 0xE815, 0x1}, {0x82CD, 0xE819, 0x5},
{0x82D2, 0xE81F, 0x7}, {0x82D9, 0xE827, 0x4}, {0x82DD, 0xE82D, 0x4}, {0x82E1, 0xE833, 0x8}, {0x82E9, 0xE83C, 0x7}, {0x82F0, 0xE844, 0x10},
{0x8300, 0xE856, 0xE}, {0x830E, 0xE865, 0x10C7}, {0x93D5, 0xF92D, 0x4C}, {0x9421, 0xF97A, 0x1B}, {0x943C, 0xF996, 0x51}, {0x948D, 0xF9E8, 0x9},
{0x9496, 0xF9F2, 0x1A}, {0x94B0, 0xFA10, 0x1}, {0x94B1, 0xFA12, 0x1}, {0x94B2, 0xFA15, 0x3}, {0x94B5, 0xFA19, 0x6}, {0x94BB, 0xFA22, 0x1},
{0x94BC, 0xFA25, 0x2}, {0x94BE, 0xFA2A, 0x406}, {0x98C4, 0xFE32, 0x1}, {0x98C5, 0xFE45, 0x4}, {0x98C9, 0xFE53, 0x1}, {0x98CA, 0xFE58, 0x1},
{0x98CB, 0xFE67, 0x1}, {0x98CC, 0xFE6C, 0x95}, {0x9961, 0xFF5F, 0x81}, {0x99E2, 0xFFE6, 0x1A}, {0x2E248, 0x10000, 0x100000},
};
inline constexpr uint32_t FALLBACK_2005[24][2] = {
{0xE78D, 0x1C0E}, {0xE78E, 0x1C0F}, {0xE78F, 0x1C10}, {0xE790, 0x1C11}, {0xE791, 0x1C12}, {0xE792, 0x1C13},
{0xE793, 0x1C14}, {0xE794, 0x1C21}, {0xE795, 0x1C22}, {0xE796, 0x1C28}, {0xE816, 0x5CD7}, {0xE817, 0x5CD8},
{0xE818, 0x5CD9}, {0xE81E, 0x5CDF}, {0xE826, 0x5CE7}, {0xE82B, 0x5CEC}, {0xE82C, 0x5CED}, {0xE831, 0x5CF2},
{0xE832, 0x5CF3}, {0xE83B, 0x5CFC}, {0xE843, 0x5D04}, {0xE854, 0x5D15}, {0xE855, 0x5D16}, {0xE864, 0x5D25},
};
}
+16 -1
View File
@@ -1,4 +1,5 @@
#include "prx/libSceCommonDialog/include/Api.hpp"
#include "prx/libSceCommonDialog/include/DialogUse.hpp"
#include <atomic>
#include <cstdint>
#include <cstddef>
@@ -7,6 +8,7 @@
#include "prx/libc/include/General.hpp"
static std::atomic<bool> g_initialized{false};
static std::atomic<bool> g_used{false};
extern "C" {
@@ -19,7 +21,20 @@ int APS5_VABI sceCommonDialogInitialize(void) {
}
bool APS5_VABI sceCommonDialogIsUsed(void) {
return false;
return g_used.load();
}
bool CommonDialogSystemInitialized_nid_no_patch() {
return g_initialized.load();
}
bool CommonDialogAcquire_nid_no_patch() {
bool expected = false;
return g_used.compare_exchange_strong(expected, true);
}
void CommonDialogRelease_nid_no_patch() {
g_used.store(false);
}
}
@@ -0,0 +1,8 @@
#ifndef CORE_LIBS_PRX_LIBSCECOMMONDIALOG_INCLUDE_DIALOGUSE_HPP
#define CORE_LIBS_PRX_LIBSCECOMMONDIALOG_INCLUDE_DIALOGUSE_HPP
extern "C" bool CommonDialogSystemInitialized_nid_no_patch();
extern "C" bool CommonDialogAcquire_nid_no_patch();
extern "C" void CommonDialogRelease_nid_no_patch();
#endif
+4 -2
View File
@@ -28,12 +28,14 @@ int APS5_VABI sceContentDeleteByPath(const char* contentPath) {
if (!g_initialized.load()) return NotInitialized;
if (contentPath == nullptr) APS5_INVALID_ARG_EX;
if (*contentPath == 0) return InvalidArgument;
throw std::logic_error(std::string(__func__) + ": " + contentPath + " is not in the host content library, which is empty, and the console's error for it is unknown");
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceContentDeleteById(std::int64_t contentId) {
if (!g_initialized.load()) return NotInitialized;
throw std::logic_error(std::string(__func__) + ": content id " + std::to_string(contentId) + " is not in the host content library, which is empty, and the console's error for it is unknown");
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceContentDeleteByIdListForSf(const std::int64_t* contentIds, std::size_t count) {
@@ -121,6 +121,9 @@ int StyleStateGetSlantRatio(const StyleStateBlock* style, float* slantRatio);
int StyleStateSetWeightScale(StyleStateBlock* style, float weightXScale, float weightYScale);
int StyleStateGetWeightScale(const StyleStateBlock* style, float* weightXScale, float* weightYScale, std::uint32_t* mode);
bool ValidStyleFrame(const FontStyleFrame* frame);
const FontStyleFrame* SurfaceStyleFrame(const FontRenderSurface* surface);
const StyleStateBlock* ApplyStyleFrame(const StyleStateBlock* base, const FontStyleFrame* frame, StyleStateBlock& temporary);
int RenderCodeWithStyle(FontHandle handle, std::uint32_t code, const StyleStateBlock* style, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result);
std::uint8_t CachedStyleCacheFlags(const CachedStyle& style);
void CachedStyleSetCacheFlags(CachedStyle& style, std::uint8_t flags);
+40 -24
View File
@@ -68,28 +68,7 @@ void ClearVerticalLayout(std::uint8_t* outWords) {
}
const StyleStateBlock* ResolveSurfaceStyleState(const StyleStateBlock* cachedState, const FontRenderSurface* surface, StyleStateBlock& temporary) {
const auto* systemUse = reinterpret_cast<const RenderSurfaceSystemUse*>(surface->reserved_q);
if ((surface->styleFlag & 0x1) == 0 || !ValidStyleFrame(systemUse->styleframe)) return cachedState;
const FontStyleFrame* frame = systemUse->styleframe;
temporary = *cachedState;
if ((frame->flags1 & STYLE_FRAME_FLAG_SCALE) != 0) {
temporary.scale_unit = frame->scaleUnit;
temporary.scale_w = frame->scalePixelW;
temporary.scale_h = frame->scalePixelH;
temporary.dpi_x = frame->hDpi ? frame->hDpi : 0x48;
temporary.dpi_y = frame->vDpi ? frame->vDpi : 0x48;
} else {
const std::uint32_t wantX = frame->hDpi == 0 ? temporary.dpi_x : frame->hDpi;
const std::uint32_t wantY = frame->vDpi == 0 ? temporary.dpi_y : frame->vDpi;
temporary.dpi_x = wantX != 0 ? 0x48 : 0;
temporary.dpi_y = wantY != 0 ? 0x48 : 0;
}
if ((frame->flags1 & STYLE_FRAME_FLAG_SLANT) != 0) temporary.slant_ratio = frame->slantRatio;
if ((frame->flags1 & STYLE_FRAME_FLAG_WEIGHT) != 0) {
temporary.effect_weight_x = frame->effectWeightX;
temporary.effect_weight_y = frame->effectWeightY;
}
return &temporary;
return ApplyStyleFrame(cachedState, SurfaceStyleFrame(surface), temporary);
}
void FillImageMetrics(FontRenderOutput* result, const FontGlyphMetrics* metrics, float x, float y) {
@@ -440,6 +419,35 @@ int Font::GetCharGlyphMetrics(FontHandle handle, std::uint32_t code, FontGlyphMe
return finish(SCE_FONT_OK);
}
const FontStyleFrame* Font::SurfaceStyleFrame(const FontRenderSurface* surface) {
const auto* systemUse = reinterpret_cast<const RenderSurfaceSystemUse*>(surface->reserved_q);
if ((surface->styleFlag & 0x1) == 0 || !ValidStyleFrame(systemUse->styleframe)) return nullptr;
return systemUse->styleframe;
}
const StyleStateBlock* Font::ApplyStyleFrame(const StyleStateBlock* base, const FontStyleFrame* frame, StyleStateBlock& temporary) {
if (!frame) return base;
temporary = *base;
if ((frame->flags1 & STYLE_FRAME_FLAG_SCALE) != 0) {
temporary.scale_unit = frame->scaleUnit;
temporary.scale_w = frame->scalePixelW;
temporary.scale_h = frame->scalePixelH;
temporary.dpi_x = frame->hDpi ? frame->hDpi : 0x48;
temporary.dpi_y = frame->vDpi ? frame->vDpi : 0x48;
} else {
const std::uint32_t wantX = frame->hDpi == 0 ? temporary.dpi_x : frame->hDpi;
const std::uint32_t wantY = frame->vDpi == 0 ? temporary.dpi_y : frame->vDpi;
temporary.dpi_x = wantX != 0 ? 0x48 : 0;
temporary.dpi_y = wantY != 0 ? 0x48 : 0;
}
if ((frame->flags1 & STYLE_FRAME_FLAG_SLANT) != 0) temporary.slant_ratio = frame->slantRatio;
if ((frame->flags1 & STYLE_FRAME_FLAG_WEIGHT) != 0) {
temporary.effect_weight_x = frame->effectWeightX;
temporary.effect_weight_y = frame->effectWeightY;
}
return &temporary;
}
int Font::RenderCharGlyphImageCore(FontHandle handle, std::uint32_t code, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result) {
const auto fail = [&](int rc) {
ClearRenderOutputs(metrics, result);
@@ -451,13 +459,21 @@ int Font::RenderCharGlyphImageCore(FontHandle handle, std::uint32_t code, FontRe
auto* font = GetNativeFont(handle);
if (font->magic != HANDLE_MAGIC) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE);
if (!font->renderer) return fail(SCE_FONT_ERROR_NOT_BOUND_RENDERER);
StyleStateBlock surfaceStyle{};
return RenderCodeWithStyle(handle, code, ResolveSurfaceStyleState(&font->cached_style.state, surface, surfaceStyle), surface, x, y, metrics, result);
}
int Font::RenderCodeWithStyle(FontHandle handle, std::uint32_t code, const StyleStateBlock* style, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result) {
const auto fail = [&](int rc) {
ClearRenderOutputs(metrics, result);
return rc;
};
auto* font = GetNativeFont(handle);
auto* library = static_cast<FontLibNative*>(font->library);
if (!library || library->magic != LIBRARY_MAGIC) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE);
const SysDriver* driver = library->sys_driver;
if (!driver || !driver->glyph_index || !driver->set_char_with_dpi || !driver->set_char_default_dpi) return fail(SCE_FONT_ERROR_FATAL);
const std::uint32_t modeLow = font->flags & 0x0Fu;
StyleStateBlock surfaceStyle{};
const StyleStateBlock* style = ResolveSurfaceStyleState(&font->cached_style.state, surface, surfaceStyle);
FontObj* obj = nullptr;
std::uint32_t lockWord = 0;
auto* entry = AcquireFontCtxEntry(FontContext(library, font), font->open_info.ctx_entry_index, modeLow, &obj, &lockWord);
+52
View File
@@ -13,6 +13,7 @@
#include FT_FREETYPE_H
#include "prx/libSceFont/include/FontInternal.hpp"
#include "prx/libc/include/General.hpp"
namespace {
@@ -121,6 +122,45 @@ int RenderDirectional(FontHandle fontHandle, std::uint32_t code, FontRenderSurfa
return rc;
}
int RenderGlyph(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result, std::uint16_t direction) {
const auto fail = [&](int rc) {
ClearRenderOutputs(metrics, result);
return rc;
};
auto* generated = TryGetGeneratedGlyph(glyph);
if (!generated) return fail(SCE_FONT_ERROR_INVALID_GLYPH);
if (!renderer || static_cast<const RendererNative*>(renderer)->magic != RENDERER_MAGIC) return fail(SCE_FONT_ERROR_INVALID_RENDERER);
if (!surface || !metrics || !result) return fail(SCE_FONT_ERROR_INVALID_PARAMETER);
if (direction == 2) NotImplemented_nid_no_patch("sceFontGlyphRenderImageVertical");
const FontHandle owner = generated->owner;
auto* font = TryGetState(owner) ? GetNativeFont(owner) : nullptr;
std::uint32_t fontLock = 0;
if (!font || font->magic != HANDLE_MAGIC || !AcquireFontLock(font, fontLock)) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE);
StyleStateBlock glyphStyle{};
glyphStyle.scale_w = generated->glyph.scale_x;
glyphStyle.scale_h = generated->glyph.base_scale;
StyleStateBlock framed{};
const FontStyleFrame* surfaceFrame = SurfaceStyleFrame(surface);
const StyleStateBlock* style = ApplyStyleFrame(&glyphStyle, surfaceFrame ? surfaceFrame : styleFrame, framed);
float yUsed = y;
int rc = SCE_FONT_OK;
if (direction == 0) {
if (static_cast<std::int16_t>(font->flags) < 0) {
ReleaseFontLock(font, fontLock);
NotImplemented_nid_no_patch("sceFontGlyphRenderImage: a vertical font");
}
std::uint8_t layout[HORIZONTAL_LAYOUT_SIZE] = {};
rc = ComputeHorizontalLayout(owner, style, layout);
yUsed = y + LoadFloat(layout, HORIZONTAL_BASELINE);
direction = 1;
}
CachedStyleSetDirectionWord(font->cached_style, direction);
if (rc == SCE_FONT_OK) rc = RenderCodeWithStyle(owner, generated->codepoint, style, surface, x, yUsed, metrics, result);
ReleaseFontLock(font, fontLock);
if (rc != SCE_FONT_OK) ClearRenderOutputs(metrics, result);
return rc;
}
}
#pragma GCC visibility push(default)
@@ -559,6 +599,18 @@ FontGlyphOutline* APS5_VABI sceFontGlyphRefersOutline(FontGlyph glyph) {
return &generated->outline;
}
int APS5_VABI sceFontGlyphRenderImage(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* output) {
return RenderGlyph(glyph, styleFrame, renderer, surface, x, y, metrics, output, 0);
}
int APS5_VABI sceFontGlyphRenderImageHorizontal(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* output) {
return RenderGlyph(glyph, styleFrame, renderer, surface, x, y, metrics, output, 1);
}
int APS5_VABI sceFontGlyphRenderImageVertical(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* output) {
return RenderGlyph(glyph, styleFrame, renderer, surface, x, y, metrics, output, 2);
}
}
#pragma GCC visibility pop
@@ -106,21 +106,6 @@ int APS5_VABI sceFontGetGlyphExpandBufferState(FontHandle font, FontGlyphExpandB
return 0;
}
int APS5_VABI sceFontGlyphRenderImage(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* output) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGlyphRenderImageHorizontal(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* output) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGlyphRenderImageVertical(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* output) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsBeginFrame(FontGraphicsFrame* frame, std::uint64_t flags, FontGraphicsResource** resource) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -31,6 +31,18 @@ if(MINGW OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
endif()
configure_windows_unwind(libSceFreeType)
target_sources(libSceFreeType PRIVATE src/Sfnt.cpp)
if(BUILD_TESTING)
add_test_executable(guest_freetype_sfnt_tests ${LIBS_INCLUDE_DIR}/tests/GuestFreeTypeSfnt.cpp)
target_include_directories(guest_freetype_sfnt_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_freetype_sfnt_tests PRIVATE libSceFreeType freetype libc)
configure_windows_unwind(guest_freetype_sfnt_tests)
add_test(NAME guest_freetype_sfnt COMMAND guest_freetype_sfnt_tests)
if(WIN32)
set_tests_properties(guest_freetype_sfnt PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceFreeType>")
endif()
endif()
if(BUILD_TESTING)
add_test_executable(guest_freetype_bitmap_tests ${LIBS_INCLUDE_DIR}/tests/GuestFreeTypeBitmap.cpp)
target_include_directories(guest_freetype_bitmap_tests PRIVATE ${LIBS_INCLUDE_DIR})
@@ -67,6 +79,15 @@ if(BUILD_TESTING)
target_link_libraries(guest_freetype_face_info_tests PRIVATE libSceFreeType freetype libc)
configure_windows_unwind(guest_freetype_face_info_tests)
add_test(NAME guest_freetype_face_info COMMAND guest_freetype_face_info_tests)
add_test_executable(guest_freetype_bitmap_init_tests ${LIBS_INCLUDE_DIR}/tests/GuestFreeTypeBitmapInit.cpp)
target_include_directories(guest_freetype_bitmap_init_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_freetype_bitmap_init_tests PRIVATE libSceFreeType freetype libc)
configure_windows_unwind(guest_freetype_bitmap_init_tests)
add_test(NAME guest_freetype_bitmap_init COMMAND guest_freetype_bitmap_init_tests)
if(WIN32)
set_tests_properties(guest_freetype_bitmap_init PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceFreeType>")
endif()
add_test_executable(guest_freetype_charmap_tests ${LIBS_INCLUDE_DIR}/tests/GuestFreeTypeCharmap.cpp)
target_include_directories(guest_freetype_charmap_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_freetype_charmap_tests PRIVATE libSceFreeType freetype libc)
@@ -114,3 +135,5 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceFreeType>")
endif()
endif()
include("${CMAKE_CURRENT_LIST_DIR}/ModuleProperties.cmake")
@@ -0,0 +1,13 @@
target_sources(libSceFreeType PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/CurveBounds.cpp)
if(BUILD_TESTING)
add_test_executable(guest_freetype_curve_bounds_tests ${LIBS_INCLUDE_DIR}/tests/GuestFreeTypeCurveBounds.cpp)
target_include_directories(guest_freetype_curve_bounds_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_freetype_curve_bounds_tests PRIVATE libSceFreeType freetype libc)
configure_windows_unwind(guest_freetype_curve_bounds_tests)
add_test(NAME guest_freetype_curve_bounds COMMAND guest_freetype_curve_bounds_tests)
if(WIN32)
set_tests_properties(guest_freetype_curve_bounds PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceFreeType>")
endif()
endif()
@@ -0,0 +1,21 @@
target_sources(libSceFreeType PRIVATE ${LIBS_INCLUDE_DIR}/prx/libSceFreeType/src/ModuleProperties.cpp)
if(BUILD_TESTING)
add_test_executable(guest_freetype_module_properties_tests ${LIBS_INCLUDE_DIR}/tests/GuestFreeTypeModuleProperties.cpp)
target_include_directories(guest_freetype_module_properties_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_freetype_module_properties_tests PRIVATE freetype ${CMAKE_DL_LIBS})
if(WIN32)
target_link_libraries(guest_freetype_module_properties_tests PRIVATE libc)
endif()
configure_windows_unwind(guest_freetype_module_properties_tests)
add_dependencies(guest_freetype_module_properties_tests libs)
add_test(NAME guest_freetype_module_properties COMMAND guest_freetype_module_properties_tests $<TARGET_FILE:libSceFreeType> unpatched)
add_test(NAME guest_freetype_module_properties_nid COMMAND guest_freetype_module_properties_tests ${PATCHED_DIR}/libSceFreeType.prx patched)
set_tests_properties(guest_freetype_module_properties guest_freetype_module_properties_nid PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_freetype_module_properties PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceFreeType>")
set_tests_properties(guest_freetype_module_properties_nid PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:${PATCHED_DIR}")
endif()
endif()
@@ -58,13 +58,22 @@ std::uint32_t APS5_VABI FT_Get_Name_Index_nid_postfix(FreeType::GuestFaceRec* fa
const char* APS5_VABI FT_Get_Postscript_Name_nid_postfix(FreeType::GuestFaceRec* face);
std::uint32_t APS5_VABI FT_Get_Sfnt_Name_Count_nid_postfix(FreeType::GuestFaceRec* face);
int APS5_VABI FT_Get_Sfnt_Name_nid_postfix(FreeType::GuestFaceRec* face, std::uint32_t index, FreeType::GuestSfntName* name);
int APS5_VABI FT_Sfnt_Table_Info_nid_postfix(FreeType::GuestFaceRec* face, std::uint32_t tableIndex, std::uint64_t* tag, std::uint64_t* length);
int APS5_VABI FT_Load_Sfnt_Table_nid_postfix(FreeType::GuestFaceRec* face, std::uint64_t tag, std::int64_t offset, unsigned char* buffer, std::uint64_t* length);
int APS5_VABI FT_Outline_Check_nid_postfix(FreeType::GuestOutline* outline);
void APS5_VABI FT_Outline_Get_CBox_nid_postfix(const FreeType::GuestOutline* outline, FreeType::GuestBBox* box);
int APS5_VABI FT_Outline_Get_BBox_nid_postfix(FreeType::GuestOutline* outline, FreeType::GuestBBox* boundingBox);
int APS5_VABI sceFreeTypeOtOutlineGetBBox_nid_no_patch();
void APS5_VABI FT_Outline_Translate_nid_postfix(const FreeType::GuestOutline* outline, std::int64_t xOffset, std::int64_t yOffset);
void APS5_VABI FT_Outline_Reverse_nid_postfix(FreeType::GuestOutline* outline);
int APS5_VABI FT_Request_Size_nid_postfix(FreeType::GuestFaceRec* face, const FreeType::GuestSizeRequestRec* request);
int APS5_VABI FT_Property_Get_nid_postfix(FreeType::GuestLibrary* library, const char* moduleName, const char* propertyName, void* value);
int APS5_VABI sceFreeTypeOtPropertyGet_nid_no_patch();
int APS5_VABI FT_Property_Set_nid_postfix(FreeType::GuestLibrary* library, const char* moduleName, const char* propertyName, const void* value);
int APS5_VABI sceFreeTypeOtPropertySet_nid_no_patch();
void APS5_VABI FT_Bitmap_New_nid_postfix(FreeType::GuestBitmap* bitmap);
void APS5_VABI FT_Bitmap_Init_nid_postfix(FreeType::GuestBitmap* bitmap);
int APS5_VABI FT_Bitmap_Copy_nid_postfix(FreeType::GuestLibrary* library, const FreeType::GuestBitmap* source, FreeType::GuestBitmap* target);
int APS5_VABI FT_Bitmap_Convert_nid_postfix(FreeType::GuestLibrary* library, const FreeType::GuestBitmap* source, FreeType::GuestBitmap* target, std::int32_t alignment);
int APS5_VABI FT_Bitmap_Done_nid_postfix(FreeType::GuestLibrary* library, FreeType::GuestBitmap* bitmap);
@@ -58,6 +58,10 @@ void APS5_VABI FT_Bitmap_New_nid_postfix(GuestBitmap* bitmap) {
if (bitmap) SyncBitmap(*bitmap, {});
}
void APS5_VABI FT_Bitmap_Init_nid_postfix(GuestBitmap* bitmap) {
FT_Bitmap_New_nid_postfix(bitmap);
}
int APS5_VABI FT_Bitmap_Copy_nid_postfix(GuestLibrary* library, const GuestBitmap* source, GuestBitmap* target) {
if (!library) return FT_Err_Invalid_Library_Handle;
if (!source || !target) return FT_Err_Invalid_Argument;
@@ -0,0 +1,58 @@
#include "prx/libSceFreeType/include/Api.hpp"
#include "prx/libSceFreeType/include/GuestFreeType.hpp"
#include "prx/libc/include/General.hpp"
#include <ft2build.h>
#include FT_BBOX_H
#include <limits>
#include <vector>
namespace {
FT_Pos NativePosition(std::int64_t value) {
if (value < std::numeric_limits<FT_Pos>::min() || value > std::numeric_limits<FT_Pos>::max())
NotImplemented_nid_no_patch("FT_Outline_Get_BBox: coordinate outside the native FT_Pos range");
return static_cast<FT_Pos>(value);
}
}
extern "C" int APS5_VABI FT_Outline_Get_BBox_nid_postfix(FreeType::GuestOutline* outline, FreeType::GuestBBox* boundingBox) {
if (!boundingBox) return FT_Outline_Get_BBox(nullptr, nullptr);
FT_BBox result{};
if (!outline) return FT_Outline_Get_BBox(nullptr, &result);
FT_Outline native{outline->n_contours, outline->n_points, nullptr, outline->tags, outline->contours, outline->flags};
std::vector<FT_Vector> points;
if (outline->n_points != 0 && outline->n_contours != 0) {
if (!outline->points || !outline->tags || !outline->contours)
NotImplemented_nid_no_patch("FT_Outline_Get_BBox: missing nonempty outline arrays");
if (outline->n_contours > outline->n_points)
NotImplemented_nid_no_patch("FT_Outline_Get_BBox: inconsistent contour bounds");
std::size_t first = 0;
for (std::size_t contour = 0; contour < outline->n_contours; ++contour) {
const std::size_t last = outline->contours[contour];
if (last < first || last >= outline->n_points)
NotImplemented_nid_no_patch("FT_Outline_Get_BBox: inconsistent contour bounds");
first = last + 1;
}
if (first != outline->n_points)
NotImplemented_nid_no_patch("FT_Outline_Get_BBox: inconsistent contour bounds");
points.reserve(outline->n_points);
constexpr std::int64_t curveLimit = std::numeric_limits<std::int32_t>::max() / 4;
bool curved = false;
bool wide = false;
for (std::size_t index = 0; index < outline->n_points; ++index) {
points.push_back({NativePosition(outline->points[index].x), NativePosition(outline->points[index].y)});
curved |= (outline->tags[index] & 3) != FT_CURVE_TAG_ON;
wide |= outline->points[index].x < -curveLimit || outline->points[index].x > curveLimit ||
outline->points[index].y < -curveLimit || outline->points[index].y > curveLimit;
}
if (curved && wide)
NotImplemented_nid_no_patch("FT_Outline_Get_BBox: curved coordinates exceed the portable arithmetic range");
native.points = points.data();
}
const FT_Error error = FT_Outline_Get_BBox(&native, &result);
if (error == 0) *boundingBox = {result.xMin, result.yMin, result.xMax, result.yMax};
return error;
}
@@ -0,0 +1,79 @@
#include "prx/libSceFreeType/include/Api.hpp"
#include "prx/libSceFreeType/include/GuestObjects.hpp"
#include "prx/libc/include/General.hpp"
#include FT_MODULE_H
#include <cstdint>
#include <cstring>
using namespace FreeType;
static_assert(sizeof(FT_UInt) == sizeof(std::uint32_t));
static_assert(sizeof(FT_Int32) == sizeof(std::int32_t));
static_assert(sizeof(FT_Int) == sizeof(std::int32_t));
static_assert(sizeof(FT_Bool) == sizeof(std::uint8_t));
namespace {
enum class PropertyType { Unsigned, Signed, Boolean, Darkening, Unsupported };
using DarkeningParameters = FT_Int[8];
PropertyType TypeOf(const char* moduleName, const char* propertyName) {
const bool postScript = std::strcmp(moduleName, "cff") == 0 || std::strcmp(moduleName, "type1") == 0 ||
std::strcmp(moduleName, "t1cid") == 0;
const bool autoHinter = std::strcmp(moduleName, "autofitter") == 0;
if (std::strcmp(moduleName, "truetype") == 0 && std::strcmp(propertyName, "interpreter-version") == 0) {
return PropertyType::Unsigned;
}
if (postScript && std::strcmp(propertyName, "hinting-engine") == 0) return PropertyType::Unsigned;
if (postScript && std::strcmp(propertyName, "random-seed") == 0) return PropertyType::Signed;
if (postScript || autoHinter) {
if (std::strcmp(propertyName, "no-stem-darkening") == 0) return PropertyType::Boolean;
if (std::strcmp(propertyName, "darkening-parameters") == 0) return PropertyType::Darkening;
}
return PropertyType::Unsupported;
}
template <typename TValue>
int Read(FT_Library library, const char* moduleName, const char* propertyName, void* output) {
TValue value{};
const auto error = FT_Property_Get(library, moduleName, propertyName, &value);
if (!error) std::memcpy(output, &value, sizeof(value));
return error;
}
template <typename TValue>
int Write(FT_Library library, const char* moduleName, const char* propertyName, const void* input) {
TValue value{};
std::memcpy(&value, input, sizeof(value));
return FT_Property_Set(library, moduleName, propertyName, &value);
}
}
extern "C" int APS5_VABI FT_Property_Get_nid_postfix(GuestLibrary* library, const char* moduleName,
const char* propertyName, void* value) {
const auto host = library ? library->host : nullptr;
if (!host || !moduleName || !propertyName || !value) return FT_Property_Get(host, moduleName, propertyName, value);
switch (TypeOf(moduleName, propertyName)) {
case PropertyType::Unsigned: return Read<FT_UInt>(host, moduleName, propertyName, value);
case PropertyType::Signed: return Read<FT_Int32>(host, moduleName, propertyName, value);
case PropertyType::Boolean: return Read<FT_Bool>(host, moduleName, propertyName, value);
case PropertyType::Darkening: return Read<DarkeningParameters>(host, moduleName, propertyName, value);
default: NotImplemented_nid_no_patch(__func__);
}
}
extern "C" int APS5_VABI FT_Property_Set_nid_postfix(GuestLibrary* library, const char* moduleName,
const char* propertyName, const void* value) {
const auto host = library ? library->host : nullptr;
if (!host || !moduleName || !propertyName || !value) return FT_Property_Set(host, moduleName, propertyName, value);
switch (TypeOf(moduleName, propertyName)) {
case PropertyType::Unsigned: return Write<FT_UInt>(host, moduleName, propertyName, value);
case PropertyType::Signed: return Write<FT_Int32>(host, moduleName, propertyName, value);
case PropertyType::Boolean: return Write<FT_Bool>(host, moduleName, propertyName, value);
case PropertyType::Darkening: return Write<DarkeningParameters>(host, moduleName, propertyName, value);
default: NotImplemented_nid_no_patch(__func__);
}
}
+95
View File
@@ -0,0 +1,95 @@
#include "prx/libSceFreeType/include/GuestObjects.hpp"
#include "prx/libc/include/GuestArena.hpp"
#include FT_TRUETYPE_TABLES_H
#include <algorithm>
#include <bit>
#include <cstdint>
#include <limits>
#include <stdexcept>
using namespace FreeType;
namespace {
FT_Error QueryTableLength(FT_Face face, std::uint64_t tag, FT_ULong& length) {
length = 0;
if constexpr (sizeof(FT_ULong) < sizeof(tag)) {
if (tag > std::numeric_limits<FT_ULong>::max()) {
const auto error = FT_Load_Sfnt_Table(face, 0, 0, nullptr, &length);
return error != 0 ? error : FT_Err_Table_Missing;
}
}
return FT_Load_Sfnt_Table(face, static_cast<FT_ULong>(tag), 0, nullptr, &length);
}
}
extern "C" {
int APS5_VABI FT_Sfnt_Table_Info_nid_postfix(GuestFaceRec* face, std::uint32_t tableIndex, std::uint64_t* tag, std::uint64_t* length) {
const GuestFace* owner = FaceOf(face);
FT_ULong nativeTag = 0;
FT_ULong nativeLength = 0;
const auto error = FT_Sfnt_Table_Info(owner ? owner->host : nullptr, tableIndex, tag ? &nativeTag : nullptr, length ? &nativeLength : nullptr);
if (error == 0) {
if (tag) {
GuestArena::HostWrite write(tag, sizeof(*tag));
*tag = nativeTag;
}
if (length) {
GuestArena::HostWrite write(length, sizeof(*length));
*length = nativeLength;
}
}
return error;
}
int APS5_VABI FT_Load_Sfnt_Table_nid_postfix(GuestFaceRec* face, std::uint64_t tag, std::int64_t offset, unsigned char* buffer, std::uint64_t* length) {
const GuestFace* owner = FaceOf(face);
const FT_Face native = owner ? owner->host : nullptr;
FT_ULong tableSize = 0;
const auto error = QueryTableLength(native, tag, tableSize);
if (error != 0) return error;
const std::uint64_t requested = length ? *length : tableSize;
if (length && requested == 0) {
GuestArena::HostWrite write(length, sizeof(*length));
*length = tableSize;
return 0;
}
FT_ULong fileSize = tableSize;
if (tag != 0) {
const auto fileError = QueryTableLength(native, 0, fileSize);
if (fileError != 0) return fileError;
}
FT_ULong nativeTag = static_cast<FT_ULong>(tag);
FT_Long nativeOffset = 0;
if constexpr (sizeof(FT_Long) < sizeof(offset)) {
std::uint64_t absolute = static_cast<std::uint64_t>(offset);
if (tag != 0) {
FT_Stream stream = native->stream;
if (stream->read) throw std::runtime_error("FT_Load_Sfnt_Table: tagged reads through a native stream callback are not implemented on this host");
const auto position = stream->pos;
FT_ULong probeLength = 1;
unsigned char probe = 0;
const auto probeError = FT_Load_Sfnt_Table(native, nativeTag, 0, &probe, &probeLength);
const auto afterProbe = stream->pos;
stream->pos = position;
if (probeError != 0) return probeError;
absolute += static_cast<std::uint64_t>(afterProbe - 1);
}
if (absolute >= fileSize) return FT_Err_Invalid_Stream_Operation;
nativeOffset = std::bit_cast<FT_Long>(static_cast<FT_ULong>(absolute));
nativeTag = 0;
} else {
nativeOffset = static_cast<FT_Long>(offset);
}
FT_ULong nativeLength = static_cast<FT_ULong>(std::min(requested, static_cast<std::uint64_t>(std::numeric_limits<FT_ULong>::max())));
GuestArena::HostWrite write(buffer, static_cast<std::size_t>(std::min<std::uint64_t>(nativeLength, fileSize)));
const auto readError = FT_Load_Sfnt_Table(native, nativeTag, nativeOffset, buffer, &nativeLength);
if (readError == 0 && requested > std::numeric_limits<FT_ULong>::max()) return FT_Err_Invalid_Stream_Operation;
return readError;
}
}
+7 -6
View File
@@ -1,3 +1,4 @@
#include "prx/libSceFreeType/include/Api.hpp"
#include <cstddef>
#include <cstdint>
#include "SceTypes.hpp"
@@ -206,10 +207,10 @@ int APS5_VABI sceFreeTypeOtUnknown_BgUMBGvhATU_nid_no_patch() {
}
APS5_EXPORT("BgUMBGvhATU", sceFreeTypeOtUnknown_BgUMBGvhATU_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_0Aal_TIC4so_nid_no_patch() {
int APS5_VABI sceFreeTypeOtOutlineGetBBox_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("0Aal-TIC4so", sceFreeTypeOtUnknown_0Aal_TIC4so_nid_no_patch);
APS5_EXPORT("0Aal-TIC4so", sceFreeTypeOtOutlineGetBBox_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_4RRjK_Wd1lU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
@@ -241,15 +242,15 @@ int APS5_VABI sceFreeTypeOtUnknown_0Lv6K1d47jg_nid_no_patch() {
}
APS5_EXPORT("0Lv6K1d47jg", sceFreeTypeOtUnknown_0Lv6K1d47jg_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_8fAMdZCnewA_nid_no_patch() {
int APS5_VABI sceFreeTypeOtPropertyGet_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("8fAMdZCnewA", sceFreeTypeOtUnknown_8fAMdZCnewA_nid_no_patch);
APS5_EXPORT("8fAMdZCnewA", sceFreeTypeOtPropertyGet_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_LsB538_IPrE_nid_no_patch() {
int APS5_VABI sceFreeTypeOtPropertySet_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("LsB538+IPrE", sceFreeTypeOtUnknown_LsB538_IPrE_nid_no_patch);
APS5_EXPORT("LsB538+IPrE", sceFreeTypeOtPropertySet_nid_no_patch);
int APS5_VABI sceFreeTypeOtUnknown_hz_J3hohXW8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
+25 -1
View File
@@ -1,3 +1,4 @@
#include "prx/libSceGvMp4Parser/include/Api.hpp"
#include <cstddef>
#include <cstdint>
#include "SceTypes.hpp"
@@ -5,6 +6,29 @@
extern "C" {
APS5_DUMMY_FUN
int APS5_VABI _ZN23sceMetadataReaderWriter11gvMp4Parser19gvMp4ParserFinalizeEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter11gvMp4Parser21gvMp4ParserInitializeEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter11gvMp4Parser23gvMp4ParserReadMetadataERKSsRKNS_8MetadataERS3_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter11gvMp4Parser24gvMp4ParserWriteMetadataERKSsRKNS_8MetadataE() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter11gvMp4Parser27gvMp4ParserExtractThumbnailERKSsRNS_9ThumbnailE() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}
@@ -0,0 +1,13 @@
#pragma once
#include "SceTypes.hpp"
extern "C" {
int APS5_VABI _ZN23sceMetadataReaderWriter11gvMp4Parser19gvMp4ParserFinalizeEv(void);
int APS5_VABI _ZN23sceMetadataReaderWriter11gvMp4Parser21gvMp4ParserInitializeEv(void);
int APS5_VABI _ZN23sceMetadataReaderWriter11gvMp4Parser23gvMp4ParserReadMetadataERKSsRKNS_8MetadataERS3_(void);
int APS5_VABI _ZN23sceMetadataReaderWriter11gvMp4Parser24gvMp4ParserWriteMetadataERKSsRKNS_8MetadataE(void);
int APS5_VABI _ZN23sceMetadataReaderWriter11gvMp4Parser27gvMp4ParserExtractThumbnailERKSsRNS_9ThumbnailE(void);
}
@@ -1 +1,3 @@
#pragma once
#include "prx/libSceGvMp4Parser/include/Api.hpp"
+115 -2
View File
@@ -6,8 +6,6 @@
extern "C" {
APS5_DUMMY_FUN
int APS5_VABI sceHidControlEnableMicBeamForming(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -208,4 +206,119 @@ int APS5_VABI sceHidControlGetPswDeviceIdFromIdxAdd(void) {
return 0;
}
int APS5_VABI sceHidControlConnectHandle() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlConnectPort() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlDisconnectDevice() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlDisconnectHandle() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlDisconnectPort() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlGetAuthInformation() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlGetBatteryState() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlGetDeviceId() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlGetDeviceInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlGetDeviceName() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlGetJediExtensionUnitInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlGetJediState() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlGetSparkState() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlGetVersionInfo() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlInit() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlSetApplicationFocus() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlSetHandleState() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlSetJediAudioOutPath() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlSetJediVolumeGain() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlSetProcessFocus() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlSetRevokeList() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlSetRevokeList2() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceHidControlSetUserColor() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}
@@ -44,5 +44,28 @@ int APS5_VABI sceHidControlGetPswDeviceInfo(void);
int APS5_VABI sceHidControlSetPswCommand(void);
int APS5_VABI sceHidControlGetPswCommand(void);
int APS5_VABI sceHidControlGetPswDeviceIdFromIdxAdd(void);
int APS5_VABI sceHidControlConnectHandle(void);
int APS5_VABI sceHidControlConnectPort(void);
int APS5_VABI sceHidControlDisconnectDevice(void);
int APS5_VABI sceHidControlDisconnectHandle(void);
int APS5_VABI sceHidControlDisconnectPort(void);
int APS5_VABI sceHidControlGetAuthInformation(void);
int APS5_VABI sceHidControlGetBatteryState(void);
int APS5_VABI sceHidControlGetDeviceId(void);
int APS5_VABI sceHidControlGetDeviceInfo(void);
int APS5_VABI sceHidControlGetDeviceName(void);
int APS5_VABI sceHidControlGetJediExtensionUnitInfo(void);
int APS5_VABI sceHidControlGetJediState(void);
int APS5_VABI sceHidControlGetSparkState(void);
int APS5_VABI sceHidControlGetVersionInfo(void);
int APS5_VABI sceHidControlInit(void);
int APS5_VABI sceHidControlSetApplicationFocus(void);
int APS5_VABI sceHidControlSetHandleState(void);
int APS5_VABI sceHidControlSetJediAudioOutPath(void);
int APS5_VABI sceHidControlSetJediVolumeGain(void);
int APS5_VABI sceHidControlSetProcessFocus(void);
int APS5_VABI sceHidControlSetRevokeList(void);
int APS5_VABI sceHidControlSetRevokeList2(void);
int APS5_VABI sceHidControlSetUserColor(void);
}
+14 -1
View File
@@ -1,4 +1,4 @@
add_library(libSceIcu SHARED EXCLUDE_FROM_ALL Export.cpp Invariant.cpp Version.cpp)
add_library(libSceIcu SHARED EXCLUDE_FROM_ALL Export.cpp Invariant.cpp Version.cpp Unescape.cpp)
target_include_directories(libSceIcu PRIVATE ${LIBS_INCLUDE_DIR})
configure_windows_unwind(libSceIcu)
@@ -14,3 +14,16 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceIcu>")
endif()
endif()
if(BUILD_TESTING)
add_test_executable(guest_icu_unescape_tests ${CMAKE_CURRENT_SOURCE_DIR}/tests/Unescape.cpp)
target_include_directories(guest_icu_unescape_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_icu_unescape_tests PRIVATE libSceIcu)
configure_windows_unwind(guest_icu_unescape_tests)
add_test(NAME guest_icu_unescape COMMAND guest_icu_unescape_tests)
set_tests_properties(guest_icu_unescape PROPERTIES TIMEOUT 30)
if(WIN32)
set_tests_properties(guest_icu_unescape PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceIcu>")
endif()
endif()
+157
View File
@@ -0,0 +1,157 @@
#include "prx/libSceIcu/libSceIcu.hpp"
#include "prx/libc/include/General.hpp"
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <limits>
namespace {
bool IsLead(std::int32_t value) {
return value >= 0xd800 && value <= 0xdbff;
}
bool IsTrail(std::int32_t value) {
return value >= 0xdc00 && value <= 0xdfff;
}
std::int32_t Supplementary(std::int32_t lead, std::int32_t trail) {
return 0x10000 + ((lead - 0xd800) << 10) + trail - 0xdc00;
}
int Digit(char16_t value, int base) {
int digit = value >= u'0' && value <= u'9' ? value - u'0'
: value >= u'a' && value <= u'f' ? value - u'a' + 10
: value >= u'A' && value <= u'F' ? value - u'A' + 10 : -1;
return digit < base ? digit : -1;
}
std::int32_t Decode(Icu::UnescapeCharacterAt characterAt, std::int32_t& position, std::int32_t length, void* context) {
const auto start = position;
if (position < 0 || position >= length) return -1;
if (!characterAt) NotImplemented_nid_no_patch("u_unescapeAt: null callback");
const auto initial = characterAt(position++, context);
bool braces = false;
int minimum = 0;
int maximum = 0;
int digits = 0;
int base = 16;
std::uint32_t value = 0;
switch (initial) {
case u'u': minimum = maximum = 4; break;
case u'U': minimum = maximum = 8; break;
case u'x':
minimum = 1;
braces = position < length && characterAt(position, context) == u'{';
maximum = braces ? 8 : 2;
if (braces) ++position;
break;
default:
if (initial >= u'0' && initial <= u'7') {
minimum = 1;
maximum = 3;
digits = 1;
base = 8;
value = initial - u'0';
}
break;
}
if (minimum) {
char16_t last = initial;
while (position < length && digits < maximum) {
last = characterAt(position, context);
const int digit = Digit(last, base);
if (digit < 0) break;
value = value * base + digit;
++position;
++digits;
}
if (digits < minimum || (braces && last != u'}') || value > 0x10ffff) {
position = start;
return -1;
}
if (braces) ++position;
if (IsLead(value) && position < length) {
auto ahead = position + 1;
std::int32_t tail = characterAt(position, context);
if (tail == u'\\' && ahead < length) {
const auto limit = ahead + std::min<std::int32_t>(11, length - ahead);
tail = Decode(characterAt, ahead, limit, context);
}
if (IsTrail(tail)) {
position = ahead;
return Supplementary(value, tail);
}
}
return static_cast<std::int32_t>(value);
}
switch (initial) {
case u'a': return 7;
case u'b': return 8;
case u'e': return 27;
case u'f': return 12;
case u'n': return 10;
case u'r': return 13;
case u't': return 9;
case u'v': return 11;
default: break;
}
const bool control = initial == u'c' && position < length;
std::int32_t result = control ? characterAt(position++, context) : initial;
if (IsLead(result) && position < length) {
const auto tail = characterAt(position, context);
if (IsTrail(tail)) {
++position;
result = Supplementary(result, tail);
}
}
return control ? result & 0x1f : result;
}
char16_t APS5_VABI ByteAt(std::int32_t position, void* context) {
return static_cast<const unsigned char*>(context)[position];
}
} // namespace
extern "C" {
std::int32_t APS5_VABI u_unescapeAt_nid_postfix(Icu::UnescapeCharacterAt characterAt, std::int32_t* offset, std::int32_t length, void* context) {
if (!offset) NotImplemented_nid_no_patch(__func__);
return Decode(characterAt, *offset, length, context);
}
std::int32_t APS5_VABI u_unescape_nid_postfix(const char* source, char16_t* destination, std::int32_t capacity) {
if (!source || (destination && capacity < 0)) NotImplemented_nid_no_patch(__func__);
const auto bytes = std::strlen(source);
if (bytes > static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max()))
NotImplemented_nid_no_patch(__func__);
const auto length = static_cast<std::int32_t>(bytes);
std::int32_t written = 0;
for (std::int32_t position = 0; position < length;) {
const auto byte = static_cast<unsigned char>(source[position++]);
if (byte != '\\') {
if (destination && written < capacity) destination[written] = byte;
++written;
continue;
}
const auto value = Decode(ByteAt, position, length, const_cast<char*>(source));
if (value < 0) {
if (destination && capacity > 0) destination[0] = 0;
return 0;
}
const int units = value > 0xffff ? 2 : 1;
if (destination && written <= capacity && units <= capacity - written) {
if (units == 1) destination[written] = static_cast<char16_t>(value);
else {
destination[written] = static_cast<char16_t>(0xd800 + ((value - 0x10000) >> 10));
destination[written + 1] = static_cast<char16_t>(0xdc00 + ((value - 0x10000) & 0x3ff));
}
}
written += units;
}
if (destination && written < capacity) destination[written] = 0;
return written;
}
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdint>
namespace Icu {
using UnescapeCharacterAt = char16_t (APS5_VABI*)(std::int32_t, void*);
} // namespace Icu
extern "C" {
std::int32_t APS5_VABI u_unescape_nid_postfix(const char*, char16_t*, std::int32_t);
std::int32_t APS5_VABI u_unescapeAt_nid_postfix(Icu::UnescapeCharacterAt, std::int32_t*, std::int32_t, void*);
}
+1
View File
@@ -1,3 +1,4 @@
#pragma once
#include "Invariant.hpp"
#include "Version.hpp"
#include "Unescape.hpp"
+189
View File
@@ -0,0 +1,189 @@
#include "prx/libSceIcu/libSceIcu.hpp"
#include <algorithm>
#include <array>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <limits>
#include <source_location>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace {
void Require(bool condition, std::source_location location = std::source_location::current()) {
if (condition) return;
std::fprintf(stderr, "ICU unescape check failed at line %u\n", location.line());
std::abort();
}
template<typename TAction>
void RequireThrows(TAction action) {
try { action(); } catch (const std::exception&) { return; }
Require(false);
}
struct Input {
std::u16string text;
std::vector<std::int32_t> positions;
std::int32_t* offset = nullptr;
std::int32_t base = 0;
std::vector<std::pair<std::int32_t, std::int32_t>> observations;
};
char16_t APS5_VABI Read_nid_no_patch(std::int32_t position, void* context) {
auto& input = *static_cast<Input*>(context);
const auto relative = static_cast<std::int64_t>(position) - input.base;
Require(relative >= 0 && static_cast<std::size_t>(relative) < input.text.size());
input.positions.push_back(position);
if (input.offset) input.observations.emplace_back(static_cast<std::int32_t>(relative), *input.offset - input.base);
return input.text[relative];
}
void At(std::u16string text, std::int32_t expected, std::int32_t consumed) {
Input input{std::move(text), {}};
std::int32_t offset = 0;
Require(u_unescapeAt_nid_postfix(Read_nid_no_patch, &offset, input.text.size(), &input) == expected);
Require(offset == consumed);
}
void CallbackOrder() {
const auto check = [](std::u16string text, std::int32_t expected, std::int32_t consumed,
std::vector<std::pair<std::int32_t, std::int32_t>> observations, std::int32_t base = 0) {
auto offset = base;
Input input{std::move(text), {}, &offset, base, {}};
const auto length = static_cast<std::int32_t>(static_cast<std::int64_t>(base) + input.text.size());
Require(u_unescapeAt_nid_postfix(Read_nid_no_patch, &offset, length, &input) == expected);
Require(offset - base == consumed && input.observations == observations);
};
check(u"u0041!", 0x41, 5, {{0, 1}, {1, 1}, {2, 2}, {3, 3}, {4, 4}});
check(u"x{41}", 0x41, 5, {{0, 1}, {1, 1}, {2, 2}, {3, 3}, {4, 4}});
check(u"u12", -1, 0, {{0, 1}, {1, 1}, {2, 2}});
check(u"uD83D\\uDE00", 0x1f600, 11,
{{0, 1}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 5}, {7, 5}, {8, 5}, {9, 5}, {10, 5}});
check(u"uD800\\u12", 0xd800, 5,
{{0, 1}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 5}, {7, 5}, {8, 5}});
check(std::u16string(u"uD83D") + char16_t{0xde00}, 0x1f600, 6,
{{0, 1}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}, std::numeric_limits<std::int32_t>::max() - 6);
check(u"u", -1, 0, {{0, 1}}, std::numeric_limits<std::int32_t>::max() - 1);
}
void SingleEscapes() {
At(u"u0041!", 0x41, 5);
At(u"U0010ffff!", 0x10ffff, 9);
At(u"xF!", 15, 2);
At(u"xFF0", 255, 3);
At(u"x{10FfFf}!", 0x10ffff, 9);
At(u"x{010ffff}!", 0x10ffff, 10);
At(u"x{0010ffff}!", -1, 0);
At(u"7777", 511, 3);
At(u"078", 7, 2);
At(u"8", u'8', 1);
At(u"cA", 1, 2);
At(u"c", u'c', 1);
At(u"c\U0001f600", 0, 3);
At(u"\U0001f600", 0x1f600, 2);
At(u"uD83D\\uDE00!", 0x1f600, 11);
At(std::u16string(u"uD83D") + char16_t{0xde00} + u"!", 0x1f600, 6);
At(u"uD83D\\x{0DE00}!", 0x1f600, 14);
At(u"uD800\\uD801\\uDC00", 0xd800, 5);
At(u"uD800\\u12", 0xd800, 5);
At(u"uDC00", 0xdc00, 5);
At(u"\\", u'\\', 1);
At(u"-", u'-', 1);
At(u"a", 7, 1);
At(u"b", 8, 1);
At(u"e", 27, 1);
At(u"f", 12, 1);
At(u"n", 10, 1);
At(u"r", 13, 1);
At(u"t", 9, 1);
At(u"v", 11, 1);
for (const auto* text : {u"", u"u", u"u123", u"U0000000", u"U00110000", u"Uffffffff", u"x", u"x{}", u"x{1", u"x{110000}"})
At(text, -1, 0);
Input input{u"prefixu0041suffix", {}};
std::int32_t offset = 6;
Require(u_unescapeAt_nid_postfix(Read_nid_no_patch, &offset, input.text.size(), &input) == 0x41 && offset == 11);
for (const auto invalid : {-1, 17, std::numeric_limits<std::int32_t>::max()}) {
offset = invalid;
const auto calls = input.positions.size();
Require(u_unescapeAt_nid_postfix(Read_nid_no_patch, &offset, input.text.size(), &input) == -1);
Require(offset == invalid && input.positions.size() == calls);
}
offset = 0;
Require(u_unescapeAt_nid_postfix(nullptr, &offset, -1, nullptr) == -1 && offset == 0);
RequireThrows([&] { u_unescapeAt_nid_postfix(nullptr, &offset, 1, nullptr); });
RequireThrows([] { u_unescapeAt_nid_postfix(Read_nid_no_patch, nullptr, 1, nullptr); });
}
void String(const char* escaped, std::u16string_view expected) {
Require(u_unescape_nid_postfix(escaped, nullptr, std::numeric_limits<std::int32_t>::min()) == static_cast<int>(expected.size()));
for (std::int32_t capacity = 0; capacity <= static_cast<std::int32_t>(expected.size()) + 2; ++capacity) {
std::vector<char16_t> output(expected.size() + 4, 0xa55a);
Require(u_unescape_nid_postfix(escaped, output.data() + 1, capacity) == static_cast<int>(expected.size()));
auto prefix = std::min<std::size_t>(capacity, expected.size());
if (prefix > 0 && prefix < expected.size() && expected[prefix - 1] >= 0xd800 && expected[prefix - 1] <= 0xdbff
&& expected[prefix] >= 0xdc00 && expected[prefix] <= 0xdfff) --prefix;
Require(std::equal(expected.begin(), expected.begin() + prefix, output.begin() + 1));
for (auto index = prefix; index < std::min<std::size_t>(capacity, expected.size()); ++index)
Require(output[index + 1] == 0xa55a);
if (capacity > static_cast<int>(expected.size())) Require(output[expected.size() + 1] == 0);
Require(output.front() == 0xa55a);
for (std::size_t index = capacity + 1; index < output.size(); ++index) Require(output[index] == 0xa55a);
}
}
void Strings() {
String("", u"");
String("plain text", u"plain text");
String("A\\u0042", u"AB");
String("\\a\\b\\t\\n\\v\\f\\r\\e\\\"\\'\\?\\\\", u"\a\b\t\n\v\f\r\x1b\"'?\\");
String("[a\\-z]\\cA\\777", u"[a-z]\x01\u01ff");
String("A\\u0042\\U0001F600Z", u"AB\U0001f600Z");
String("\\uD83D\\uDE00", u"\U0001f600");
const char16_t unpaired[] = {0xd800, 0xd801, 0xdc00};
String("\\uD800\\uD801\\uDC00", std::u16string_view(unpaired, 3));
String("\\x{10ffff}\\xA\\123", u"\U0010ffff\nS");
const char16_t embedded[] = {u'A', 0, u'B'};
String("A\\0B", std::u16string_view(embedded, 3));
for (unsigned int byte = 1; byte <= 255; ++byte) {
if (byte == '\\') continue;
const char source[] = {static_cast<char>(byte), 0};
const char16_t expected[] = {static_cast<char16_t>(byte)};
String(source, std::u16string_view(expected, 1));
}
char16_t destination[] = {0xa55a, 0xa55a, 0xa55a};
Require(u_unescape_nid_postfix("\\U0001f600Z", destination, 1) == 3);
Require(destination[0] == 0xa55a && destination[1] == 0xa55a);
Require(u_unescape_nid_postfix("a\\u12", destination, 3) == 0 && destination[0] == 0 && destination[1] == 0xa55a);
destination[0] = 0xa55a;
Require(u_unescape_nid_postfix("abc\\u12", destination, 3) == 0 && destination[0] == 0 && destination[1] == u'b' && destination[2] == u'c');
for (auto capacity : {-1, std::numeric_limits<std::int32_t>::min()}) {
std::fill(std::begin(destination), std::end(destination), 0xa55a);
RequireThrows([&] { u_unescape_nid_postfix("a\\U0001f600", destination, capacity); });
Require(destination[0] == 0xa55a && destination[1] == 0xa55a && destination[2] == 0xa55a);
}
RequireThrows([] { u_unescape_nid_postfix(nullptr, nullptr, 0); });
for (std::uint32_t value = 0; value <= 0x10ffff; value += 17) {
char escaped[16];
std::snprintf(escaped, sizeof(escaped), "\\U%08x", value);
std::array<char16_t, 3> output;
output.fill(0xa55a);
const auto size = u_unescape_nid_postfix(escaped, output.data(), output.size());
if (value <= 0xffff) Require(size == 1 && output[0] == value && output[1] == 0);
else Require(size == 2 && output[0] == 0xd800 + ((value - 0x10000) >> 10) && output[1] == 0xdc00 + ((value - 0x10000) & 0x3ff) && output[2] == 0);
}
}
} // namespace
int main() {
SingleEscapes();
CallbackOrder();
Strings();
std::puts("ICU escape decoding tests passed");
}
@@ -0,0 +1,16 @@
add_library(libSceInvitationDialog SHARED EXCLUDE_FROM_ALL Export.cpp)
target_include_directories(libSceInvitationDialog PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(libSceInvitationDialog PRIVATE libSceCommonDialog libc)
configure_windows_unwind(libSceInvitationDialog)
if(BUILD_TESTING)
add_test_executable(guest_invitation_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestInvitationDialog.cpp)
target_include_directories(guest_invitation_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_invitation_dialog_tests PRIVATE libSceInvitationDialog libSceCommonDialog libc)
configure_windows_unwind(guest_invitation_dialog_tests)
add_test(NAME guest_invitation_dialog COMMAND guest_invitation_dialog_tests)
if(WIN32)
set_tests_properties(guest_invitation_dialog PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceInvitationDialog>")
endif()
endif()
+96 -19
View File
@@ -1,37 +1,114 @@
#include "prx/libSceInvitationDialog/include/Api.hpp"
#include "prx/libSceCommonDialog/include/DialogUse.hpp"
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
#include <algorithm>
#include <cstdint>
#include <iterator>
#include <mutex>
#include <stdexcept>
#include <string>
namespace {
constexpr int COMMON_DIALOG_STATUS_NONE = 0;
constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1;
constexpr int COMMON_DIALOG_STATUS_FINISHED = 3;
constexpr int COMMON_DIALOG_ERROR_NOT_SYSTEM_INITIALIZED = static_cast<int>(0x80B80001u);
constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast<int>(0x80B80003u);
constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast<int>(0x80B80008u);
constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast<int>(0x80B80005u);
constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast<int>(0x80B8000Du);
constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1;
constexpr std::int32_t INVITATION_DIALOG_MODE_SEND = 1;
constexpr std::int32_t INVITATION_DIALOG_MODE_RECV = 2;
struct DialogState {
std::mutex mutex;
int status = COMMON_DIALOG_STATUS_NONE;
void* callbackArg = nullptr;
};
DialogState& Dialog() {
static DialogState state;
return state;
}
[[noreturn]] void Unsettled(const char* function, const std::string& what) {
throw std::runtime_error(std::string(function) + ": " + what);
}
}
extern "C" {
int APS5_VABI sceInvitationDialogGetResult(InvitationDialogResult* result) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceInvitationDialogInitialize(void) {
DialogState& dialog = Dialog();
std::lock_guard lock(dialog.mutex);
if (!CommonDialogSystemInitialized_nid_no_patch()) return COMMON_DIALOG_ERROR_NOT_SYSTEM_INITIALIZED;
if (dialog.status != COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_ALREADY_INITIALIZED;
if (!CommonDialogAcquire_nid_no_patch()) return COMMON_DIALOG_ERROR_BUSY;
dialog.status = COMMON_DIALOG_STATUS_INITIALIZED;
dialog.callbackArg = nullptr;
return COMMON_DIALOG_OK;
}
int APS5_VABI sceInvitationDialogInitialize() {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceInvitationDialogTerminate(void) {
DialogState& dialog = Dialog();
std::lock_guard lock(dialog.mutex);
if (dialog.status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED;
dialog.status = COMMON_DIALOG_STATUS_NONE;
dialog.callbackArg = nullptr;
CommonDialogRelease_nid_no_patch();
return COMMON_DIALOG_OK;
}
int APS5_VABI sceInvitationDialogOpen() {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceInvitationDialogUpdateStatus(void) {
DialogState& dialog = Dialog();
std::lock_guard lock(dialog.mutex);
return dialog.status;
}
int APS5_VABI sceInvitationDialogTerminate() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceInvitationDialogUpdateStatus() {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceInvitationDialogOpen(const InvitationDialogParam* param) {
DialogState& dialog = Dialog();
std::lock_guard lock(dialog.mutex);
if (dialog.status != COMMON_DIALOG_STATUS_INITIALIZED && dialog.status != COMMON_DIALOG_STATUS_FINISHED) {
Unsettled(__func__, "status " + std::to_string(dialog.status) + " is neither INITIALIZED nor FINISHED, and the error for it is unsettled");
}
if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL;
if (param->size != sizeof(InvitationDialogParam)) {
Unsettled(__func__, "param size " + std::to_string(param->size) + " is not the " + std::to_string(sizeof(InvitationDialogParam)) + "-byte layout this library reads");
}
if (param->mode != INVITATION_DIALOG_MODE_SEND && param->mode != INVITATION_DIALOG_MODE_RECV) {
Unsettled(__func__, "mode " + std::to_string(param->mode) + " is neither SEND nor RECV, and the error for it is unsettled");
}
dialog.callbackArg = param->callbackArg;
dialog.status = COMMON_DIALOG_STATUS_FINISHED;
return COMMON_DIALOG_OK;
}
int APS5_VABI sceInvitationDialogClose(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
DialogState& dialog = Dialog();
std::lock_guard lock(dialog.mutex);
if (dialog.status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED;
Unsettled(__func__, "status " + std::to_string(dialog.status) + " is not RUNNING, and the error for closing a dialog that is not running is unsettled");
}
int APS5_VABI sceInvitationDialogGetResult(InvitationDialogResult* result) {
DialogState& dialog = Dialog();
std::lock_guard lock(dialog.mutex);
if (dialog.status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED;
if (dialog.status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED;
if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL;
if (std::any_of(std::begin(result->reserved), std::end(result->reserved), [](std::uint8_t value) { return value != 0; })) {
Unsettled(__func__, "the module checks the reserved bytes 0x18..0x37 of the result, and the error for a non-zero one is unsettled");
}
result->callbackArg = dialog.callbackArg;
result->errorCode = COMMON_DIALOG_OK;
result->result = COMMON_DIALOG_RESULT_USER_CANCELED;
if (result->recipients != nullptr) result->recipients->count = 0;
return COMMON_DIALOG_OK;
}
}
@@ -1,27 +1,47 @@
#pragma once
#include "prx/libSceCommonDialog/CommonDialog.hpp"
#include "SceTypes.hpp"
#include <cstddef>
#include <cstdint>
struct InvitationDialogParam {
CommonDialogBaseParam baseParam;
std::uint32_t size;
std::int32_t mode;
std::int32_t userId;
std::uint32_t padding;
void* callbackArg;
const void* dataParam;
std::uint8_t reserved[64];
};
struct InvitationDialogRecipients {
std::uint32_t count;
NpOnlineId onlineIds[16];
};
struct InvitationDialogResult {
std::uint8_t opaque00[8];
void* callbackArg;
std::int32_t errorCode;
std::uint32_t opaque0C;
std::int32_t result;
InvitationDialogRecipients* recipients;
std::uint8_t reserved[32];
};
static_assert(sizeof(CommonDialogBaseParam) == 48);
static_assert(offsetof(InvitationDialogParam, size) == 48);
static_assert(offsetof(InvitationDialogParam, mode) == 52);
static_assert(offsetof(InvitationDialogParam, userId) == 56);
static_assert(offsetof(InvitationDialogParam, callbackArg) == 64);
static_assert(offsetof(InvitationDialogParam, dataParam) == 72);
static_assert(offsetof(InvitationDialogParam, reserved) == 80);
static_assert(sizeof(InvitationDialogParam) == 144);
static_assert(sizeof(NpOnlineId) == 20);
static_assert(offsetof(InvitationDialogRecipients, onlineIds) == 4);
static_assert(sizeof(InvitationDialogRecipients) == 324);
static_assert(offsetof(InvitationDialogResult, errorCode) == 0x08);
static_assert(offsetof(InvitationDialogResult, opaque0C) == 0x0C);
static_assert(offsetof(InvitationDialogResult, result) == 0x0C);
static_assert(offsetof(InvitationDialogResult, recipients) == 0x10);
static_assert(offsetof(InvitationDialogResult, reserved) == 0x18);
static_assert(sizeof(InvitationDialogResult) == 0x38);
@@ -29,10 +49,10 @@ static_assert(sizeof(InvitationDialogResult) == 0x38);
extern "C" {
int APS5_VABI sceInvitationDialogGetResult(InvitationDialogResult* result);
int APS5_VABI sceInvitationDialogInitialize();
int APS5_VABI sceInvitationDialogOpen();
int APS5_VABI sceInvitationDialogTerminate();
int APS5_VABI sceInvitationDialogUpdateStatus();
int APS5_VABI sceInvitationDialogInitialize(void);
int APS5_VABI sceInvitationDialogOpen(const InvitationDialogParam* param);
int APS5_VABI sceInvitationDialogTerminate(void);
int APS5_VABI sceInvitationDialogUpdateStatus(void);
int APS5_VABI sceInvitationDialogClose(void);
}
+25 -1
View File
@@ -1,3 +1,4 @@
#include "prx/libSceJpegParser/include/Api.hpp"
#include <cstddef>
#include <cstdint>
#include "SceTypes.hpp"
@@ -5,6 +6,29 @@
extern "C" {
APS5_DUMMY_FUN
int APS5_VABI _ZN23sceMetadataReaderWriter10jpegParser18jpegParserFinalizeEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter10jpegParser20jpegParserInitializeEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter10jpegParser22jpegParserReadMetadataERKSsRKNS_8MetadataERS3_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter10jpegParser23jpegParserWriteMetadataERKSsRKNS_8MetadataE() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter10jpegParser26jpegParserExtractThumbnailERKSsRNS_9ThumbnailE() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}
@@ -0,0 +1,13 @@
#pragma once
#include "SceTypes.hpp"
extern "C" {
int APS5_VABI _ZN23sceMetadataReaderWriter10jpegParser18jpegParserFinalizeEv(void);
int APS5_VABI _ZN23sceMetadataReaderWriter10jpegParser20jpegParserInitializeEv(void);
int APS5_VABI _ZN23sceMetadataReaderWriter10jpegParser22jpegParserReadMetadataERKSsRKNS_8MetadataERS3_(void);
int APS5_VABI _ZN23sceMetadataReaderWriter10jpegParser23jpegParserWriteMetadataERKSsRKNS_8MetadataE(void);
int APS5_VABI _ZN23sceMetadataReaderWriter10jpegParser26jpegParserExtractThumbnailERKSsRNS_9ThumbnailE(void);
}
@@ -1 +1,3 @@
#pragma once
#include "prx/libSceJpegParser/include/Api.hpp"
+15
View File
@@ -0,0 +1,15 @@
add_library(libSceJsc SHARED EXCLUDE_FROM_ALL Export.cpp)
target_include_directories(libSceJsc PRIVATE ${LIBS_INCLUDE_DIR})
configure_windows_unwind(libSceJsc)
if(BUILD_TESTING)
add_test_executable(guest_jsc_string_tests ${LIBS_INCLUDE_DIR}/tests/GuestJscString.cpp)
target_include_directories(guest_jsc_string_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_jsc_string_tests PRIVATE libSceJsc libc)
configure_windows_unwind(guest_jsc_string_tests)
add_test(NAME guest_jsc_string COMMAND guest_jsc_string_tests)
if(WIN32)
set_tests_properties(guest_jsc_string PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceJsc>")
endif()
endif()
+147 -3
View File
@@ -1,10 +1,154 @@
#include "prx/libSceJsc/include/Api.hpp"
#include "prx/libc/include/General.hpp"
#include <atomic>
#include <cstddef>
#include <cstdint>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
#include <limits>
#include <memory>
#include <stdexcept>
#include <vector>
namespace {
std::unique_ptr<GuestJSString> Decode(const char* text) {
auto result = std::make_unique<GuestJSString>();
const auto* cursor = reinterpret_cast<const unsigned char*>(text);
while (*cursor) {
std::uint32_t codepoint = *cursor++;
unsigned following = 0;
std::uint32_t minimum = 0;
if (codepoint >= 0xC2 && codepoint <= 0xDF) {
codepoint &= 0x1F;
following = 1;
minimum = 0x80;
} else if (codepoint >= 0xE0 && codepoint <= 0xEF) {
codepoint &= 0x0F;
following = 2;
minimum = 0x800;
} else if (codepoint >= 0xF0 && codepoint <= 0xF4) {
codepoint &= 7;
following = 3;
minimum = 0x10000;
} else if (codepoint >= 0x80) {
result->nullString = true;
break;
}
while (following > 0) {
--following;
if ((*cursor & 0xC0) != 0x80) {
result->nullString = true;
break;
}
codepoint = codepoint << 6 | (*cursor++ & 0x3F);
}
if (result->nullString || codepoint < minimum || codepoint > 0x10FFFF || (codepoint >= 0xD800 && codepoint <= 0xDFFF)) {
result->nullString = true;
break;
}
if (codepoint > 0xFFFF) {
const auto supplementary = codepoint - 0x10000;
result->characters.push_back(static_cast<std::uint16_t>(0xD800 + (supplementary >> 10)));
result->characters.push_back(static_cast<std::uint16_t>(0xDC00 + (supplementary & 0x3FF)));
} else result->characters.push_back(static_cast<std::uint16_t>(codepoint));
}
if (result->nullString) result->characters.clear();
result->characters.push_back(0);
return result;
}
bool Equal(const GuestJSString* first, const GuestJSString* second) {
if (first == second) return true;
if (!first || !second) return false;
return first->nullString == second->nullString && first->characters == second->characters;
}
}
extern "C" {
APS5_DUMMY_FUN
GuestJSString* APS5_VABI JSStringCreateWithCharacters(const std::uint16_t* characters, std::size_t count) {
if (!characters && count) NotImplemented_nid_no_patch(__func__);
if (count > static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max())) NotImplemented_nid_no_patch(__func__);
auto string = std::make_unique<GuestJSString>();
string->nullString = !characters;
if (count) string->characters.assign(characters, characters + count);
string->characters.push_back(0);
return string.release();
}
GuestJSString* APS5_VABI JSStringCreateWithUTF8CString(const char* text) {
if (!text) NotImplemented_nid_no_patch(__func__);
return Decode(text).release();
}
GuestJSString* APS5_VABI JSStringRetain(GuestJSString* string) {
if (!string) NotImplemented_nid_no_patch(__func__);
auto references = string->references.load(std::memory_order_relaxed);
for (;;) {
if (references == std::numeric_limits<std::size_t>::max()) NotImplemented_nid_no_patch(__func__);
if (string->references.compare_exchange_weak(references, references + 1, std::memory_order_relaxed)) return string;
}
}
void APS5_VABI JSStringRelease(GuestJSString* string) {
if (!string) NotImplemented_nid_no_patch(__func__);
if (string->references.fetch_sub(1, std::memory_order_acq_rel) == 1) delete string;
}
std::size_t APS5_VABI JSStringGetLength(const GuestJSString* string) {
return string ? string->characters.size() - 1 : 0;
}
const std::uint16_t* APS5_VABI JSStringGetCharactersPtr(const GuestJSString* string) {
return string && !string->nullString ? string->characters.data() : nullptr;
}
std::size_t APS5_VABI JSStringGetMaximumUTF8CStringSize(const GuestJSString* string) {
if (!string) NotImplemented_nid_no_patch(__func__);
const auto length = string->characters.size() - 1;
if (length > (std::numeric_limits<std::size_t>::max() - 1) / 3) NotImplemented_nid_no_patch(__func__);
return length * 3 + 1;
}
std::size_t APS5_VABI JSStringGetUTF8CString(const GuestJSString* string, char* buffer, std::size_t bufferSize) {
if (!string || !buffer || !bufferSize) return 0;
std::size_t written = 0;
const auto count = string->characters.size() - 1;
for (std::size_t index = 0; index < count; ++index) {
std::uint32_t codepoint = string->characters[index];
if (codepoint >= 0xD800 && codepoint <= 0xDBFF) {
if (index + 1 == count) break;
const auto low = string->characters[index + 1];
if (low < 0xDC00 || low > 0xDFFF) break;
codepoint = 0x10000 + ((codepoint - 0xD800) << 10) + low - 0xDC00;
++index;
} else if (codepoint >= 0xDC00 && codepoint <= 0xDFFF) break;
const std::size_t bytes = codepoint < 0x80 ? 1 : codepoint < 0x800 ? 2 : codepoint < 0x10000 ? 3 : 4;
if (bytes > bufferSize - 1 - written) break;
if (bytes == 1) buffer[written++] = static_cast<char>(codepoint);
else {
const unsigned prefix = bytes == 2 ? 0xC0 : bytes == 3 ? 0xE0 : 0xF0;
buffer[written++] = static_cast<char>(prefix | (codepoint >> (6 * (bytes - 1))));
for (auto remaining = bytes - 1; remaining > 0; --remaining) {
buffer[written++] = static_cast<char>(0x80 | ((codepoint >> (6 * (remaining - 1))) & 0x3F));
}
}
}
buffer[written] = '\0';
return written + 1;
}
bool APS5_VABI JSStringIsEqual(const GuestJSString* first, const GuestJSString* second) {
return Equal(first, second);
}
bool APS5_VABI JSStringIsEqualToUTF8CString(const GuestJSString* string, const char* text) {
if (!text) NotImplemented_nid_no_patch(__func__);
const auto other = Decode(text);
return Equal(string, other.get());
}
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include "prx/libc/include/general/VabiMacros.hpp"
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <vector>
struct GuestJSString {
std::atomic<std::size_t> references{1};
std::vector<std::uint16_t> characters;
bool nullString = false;
};
extern "C" {
GuestJSString* APS5_VABI JSStringCreateWithCharacters(const std::uint16_t* characters, std::size_t count);
GuestJSString* APS5_VABI JSStringCreateWithUTF8CString(const char* text);
GuestJSString* APS5_VABI JSStringRetain(GuestJSString* string);
void APS5_VABI JSStringRelease(GuestJSString* string);
std::size_t APS5_VABI JSStringGetLength(const GuestJSString* string);
const std::uint16_t* APS5_VABI JSStringGetCharactersPtr(const GuestJSString* string);
std::size_t APS5_VABI JSStringGetMaximumUTF8CStringSize(const GuestJSString* string);
std::size_t APS5_VABI JSStringGetUTF8CString(const GuestJSString* string, char* buffer, std::size_t bufferSize);
bool APS5_VABI JSStringIsEqual(const GuestJSString* first, const GuestJSString* second);
bool APS5_VABI JSStringIsEqualToUTF8CString(const GuestJSString* string, const char* text);
}
+2
View File
@@ -1 +1,3 @@
#pragma once
#include "prx/libSceJsc/include/Api.hpp"
+12 -1
View File
@@ -1,4 +1,4 @@
add_library(libSceLibreSsl SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Runtime.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Aes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Error.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ocsp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Stack.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ui.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rsa.cpp)
add_library(libSceLibreSsl SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Runtime.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Aes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Error.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ocsp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Stack.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ui.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rsa.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Pkey.cpp)
target_include_directories(libSceLibreSsl PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(libSceLibreSsl PRIVATE mbedcrypto)
configure_windows_unwind(libSceLibreSsl)
@@ -121,6 +121,17 @@ if(BUILD_TESTING)
set_tests_properties(guest_libressl_rsa PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibreSsl>")
endif()
add_test_executable(guest_libressl_pkey_type_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSslPkeyType.cpp)
target_include_directories(guest_libressl_pkey_type_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_libressl_pkey_type_tests PRIVATE libSceLibreSsl libc)
configure_windows_unwind(guest_libressl_pkey_type_tests)
add_test(NAME guest_libressl_pkey_type COMMAND guest_libressl_pkey_type_tests)
set_tests_properties(guest_libressl_pkey_type PROPERTIES TIMEOUT 30)
if(WIN32)
set_tests_properties(guest_libressl_pkey_type PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibreSsl>")
endif()
endif()
target_sources(libSceLibreSsl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/Cipher.cpp)
-10
View File
@@ -639,11 +639,6 @@ int APS5_VABI EVP_PKEY_set1_RSA_nid_postfix(void) {
return 0;
}
int APS5_VABI EVP_PKEY_type_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI EVP_aes_128_cbc_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -1304,11 +1299,6 @@ int APS5_VABI SSL_read_nid_postfix(void) {
return 0;
}
int APS5_VABI SSL_select_next_proto_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SSL_set_SSL_CTX_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -0,0 +1,22 @@
target_sources(libSceLibreSsl PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/ProtocolSelection.cpp)
if(BUILD_TESTING)
add_test_executable(guest_libressl_protocol_tests ${CMAKE_CURRENT_SOURCE_DIR}/tests/GuestLibreSslProtocol.cpp)
target_include_directories(guest_libressl_protocol_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_libressl_protocol_tests PRIVATE ${CMAKE_DL_LIBS})
if(WIN32)
target_link_libraries(guest_libressl_protocol_tests PRIVATE libc)
endif()
add_dependencies(guest_libressl_protocol_tests libs)
configure_windows_unwind(guest_libressl_protocol_tests)
add_test(NAME guest_libressl_protocol COMMAND guest_libressl_protocol_tests
$<TARGET_FILE:libSceLibreSsl> SSL_select_next_proto_nid_postfix)
add_test(NAME guest_libressl_protocol_nid COMMAND guest_libressl_protocol_tests
${PATCHED_DIR}/libSceLibreSsl.prx CpIzEs1Q1Ug)
if(WIN32)
set_tests_properties(guest_libressl_protocol PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibreSsl>")
set_tests_properties(guest_libressl_protocol_nid PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:${PATCHED_DIR}")
endif()
endif()
+2 -2
View File
@@ -246,7 +246,7 @@ int APS5_VABI EVP_PKEY_get1_RSA_nid_postfix(void);
int APS5_VABI EVP_PKEY_id_nid_postfix(void);
int APS5_VABI EVP_PKEY_new_nid_postfix(void);
int APS5_VABI EVP_PKEY_set1_RSA_nid_postfix(void);
int APS5_VABI EVP_PKEY_type_nid_postfix(void);
int APS5_VABI EVP_PKEY_type_nid_postfix(int type);
int APS5_VABI EVP_aes_128_cbc_nid_postfix(void);
int APS5_VABI EVP_aes_128_cfb8_nid_postfix(void);
int APS5_VABI EVP_aes_128_ctr_nid_postfix(void);
@@ -388,7 +388,7 @@ int APS5_VABI SSL_new_nid_postfix(void);
int APS5_VABI SSL_peek_nid_postfix(void);
int APS5_VABI SSL_pending_nid_postfix(void);
int APS5_VABI SSL_read_nid_postfix(void);
int APS5_VABI SSL_select_next_proto_nid_postfix(void);
int APS5_VABI SSL_select_next_proto_nid_postfix(unsigned char** output, unsigned char* outputLength, const unsigned char* peer, std::uint32_t peerLength, const unsigned char* supported, std::uint32_t supportedLength);
int APS5_VABI SSL_set_SSL_CTX_nid_postfix(void);
int APS5_VABI SSL_set_bio_nid_postfix(void);
int APS5_VABI SSL_set_connect_state_nid_postfix(void);
+50 -21
View File
@@ -1,12 +1,13 @@
#include "prx/libSceLibreSsl/libSceLibreSsl.hpp"
#include "prx/libc/include/Api.hpp"
#include "prx/libc/include/General.hpp"
#include "prx/libSceLibreSsl/include/Error.hpp"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
#include <span>
#include <vector>
#include <stdexcept>
namespace {
@@ -14,6 +15,19 @@ static_assert(sizeof(Asn1String) == 24);
static_assert(offsetof(Asn1String, data) == 8);
static_assert(offsetof(Asn1String, flags) == 16);
constexpr int Asn1Library = 13;
constexpr int AnyFunction = 0xfff;
constexpr int MallocFailure = 65;
constexpr int IllegalCharacters = 124;
constexpr int InvalidBmpStringLength = 129;
constexpr int InvalidUniversalStringLength = 133;
constexpr int InvalidUtf8String = 134;
int Asn1Error(int reason) {
LibreSslPutError_nid_no_patch(Asn1Library, AnyFunction, reason);
return -1;
}
int CharacterWidth(int type) {
switch (type) {
case 12: return 0;
@@ -52,18 +66,20 @@ bool ReadCharacter(std::span<const unsigned char>& input, int width, std::uint32
return value <= 0x10ffff && (value < 0xd800 || value > 0xdfff);
}
void AppendCharacter(std::vector<unsigned char>& output, std::uint32_t value) {
const int bytes = value < 0x80 ? 1 : value < 0x800 ? 2 : value < 0x10000 ? 3 : 4;
if (output.size() >= static_cast<std::size_t>(std::numeric_limits<int>::max() - bytes))
NotImplemented_nid_no_patch("ASN1_STRING_to_UTF8: output length overflow");
int CharacterBytes(std::uint32_t value) {
return value < 0x80 ? 1 : value < 0x800 ? 2 : value < 0x10000 ? 3 : 4;
}
void WriteCharacter(unsigned char*& output, std::uint32_t value) {
const int bytes = CharacterBytes(value);
if (bytes == 1) {
output.push_back(static_cast<unsigned char>(value));
*output++ = static_cast<unsigned char>(value);
return;
}
constexpr unsigned char prefix[] = {0, 0, 0xc0, 0xe0, 0xf0};
output.push_back(static_cast<unsigned char>(prefix[bytes] | (value >> (6 * (bytes - 1)))));
*output++ = static_cast<unsigned char>(prefix[bytes] | (value >> (6 * (bytes - 1))));
for (int index = bytes - 2; index >= 0; --index)
output.push_back(static_cast<unsigned char>(0x80 | ((value >> (6 * index)) & 0x3f)));
*output++ = static_cast<unsigned char>(0x80 | ((value >> (6 * index)) & 0x3f));
}
} // namespace
@@ -106,23 +122,36 @@ int APS5_VABI ASN1_STRING_to_UTF8_nid_postfix(unsigned char** output, Asn1String
const auto& string = *value;
const int width = CharacterWidth(string.type);
if (width < 0) return -1;
if (!output || (!string.data && string.length != 0)) NotImplemented_nid_no_patch(__func__);
std::size_t length = string.length < 0 ? std::strlen(reinterpret_cast<const char*>(string.data))
if (!string.data && string.length < 0) throw std::invalid_argument("ASN1_STRING_to_UTF8: null terminated-string data");
const std::size_t length = string.length < 0 ? std::strlen(reinterpret_cast<const char*>(string.data))
: static_cast<std::size_t>(string.length);
if (length > static_cast<std::size_t>(std::numeric_limits<int>::max())) NotImplemented_nid_no_patch(__func__);
std::span<const unsigned char> input(string.data, length);
std::vector<unsigned char> converted;
while (!input.empty()) {
if (length > static_cast<std::size_t>(std::numeric_limits<int>::max())) throw std::length_error("ASN1_STRING_to_UTF8: input length overflow");
if (width == 2 && length % 2 != 0) return Asn1Error(InvalidBmpStringLength);
if (width == 4 && length % 4 != 0) return Asn1Error(InvalidUniversalStringLength);
if (!string.data && length != 0) throw std::invalid_argument("ASN1_STRING_to_UTF8: null nonempty string data");
const std::span<const unsigned char> characters(string.data, length);
std::size_t size = 0;
for (auto input = characters; !input.empty();) {
std::uint32_t character;
if (!ReadCharacter(input, width, character)) NotImplemented_nid_no_patch(__func__);
AppendCharacter(converted, character);
if (!ReadCharacter(input, width, character)) return Asn1Error(width == 0 ? InvalidUtf8String : IllegalCharacters);
size += static_cast<std::size_t>(CharacterBytes(character));
if (size > static_cast<std::size_t>(std::numeric_limits<int>::max() - 1)) throw std::length_error("ASN1_STRING_to_UTF8: output length overflow");
}
auto* result = static_cast<unsigned char*>(malloc_nid_postfix(converted.size() + 1));
if (!result) NotImplemented_nid_no_patch(__func__);
if (!converted.empty()) std::memcpy(result, converted.data(), converted.size());
result[converted.size()] = 0;
if (!output) throw std::invalid_argument("ASN1_STRING_to_UTF8: null output");
auto* result = static_cast<unsigned char*>(malloc_nid_postfix(size + 1));
if (!result) {
if (width == 0) Asn1Error(MallocFailure);
return Asn1Error(MallocFailure);
}
unsigned char* cursor = result;
for (auto input = characters; !input.empty();) {
std::uint32_t character;
ReadCharacter(input, width, character);
WriteCharacter(cursor, character);
}
*cursor = 0;
*output = result;
return static_cast<int>(converted.size());
return static_cast<int>(size);
}
}
+44
View File
@@ -0,0 +1,44 @@
#include "prx/libSceLibreSsl/include/Api.hpp"
#include "prx/libc/include/General.hpp"
namespace {
constexpr int NidUndef = 0;
constexpr int NidRsaEncryption = 6;
constexpr int NidRsa = 19;
constexpr int NidDhKeyAgreement = 28;
constexpr int NidDsaWithSha = 66;
constexpr int NidDsa2 = 67;
constexpr int NidDsaWithSha1Old = 70;
constexpr int NidDsaWithSha1 = 113;
constexpr int NidDsa = 116;
constexpr int NidEcPublicKey = 408;
constexpr int NidHmac = 855;
constexpr int NidCmac = 894;
}
extern "C" {
int APS5_VABI EVP_PKEY_type_nid_postfix(int type) {
switch (type) {
case NidRsaEncryption:
case NidRsa:
return NidRsaEncryption;
case NidDhKeyAgreement:
case NidEcPublicKey:
case NidHmac:
case NidCmac:
return type;
case NidDsaWithSha:
case NidDsa2:
case NidDsaWithSha1Old:
case NidDsaWithSha1:
case NidDsa:
return NidDsa;
default:
return NidUndef;
}
}
}
@@ -0,0 +1,37 @@
#include "prx/libSceLibreSsl/include/Api.hpp"
#include "prx/libc/include/General.hpp"
#include <cstddef>
#include <cstdint>
#include <cstring>
namespace {
bool ValidProtocols(const unsigned char* protocols, std::size_t size) {
if (!protocols || size == 0) return false;
for (std::size_t offset = 0; offset < size;) {
const std::size_t length = protocols[offset++];
if (length == 0 || length > size - offset) return false;
offset += length;
}
return true;
}
}
extern "C" int APS5_VABI SSL_select_next_proto_nid_postfix(unsigned char** output, unsigned char* outputLength,
const unsigned char* peer, std::uint32_t peerLength, const unsigned char* supported, std::uint32_t supportedLength) {
if (!output || !outputLength || !ValidProtocols(peer, peerLength) || !ValidProtocols(supported, supportedLength))
NotImplemented_nid_no_patch("SSL_select_next_proto: null output or invalid protocol list");
for (std::size_t offered = 0; offered < peerLength; offered += 1 + peer[offered]) {
for (std::size_t local = 0; local < supportedLength; local += 1 + supported[local]) {
if (peer[offered] != supported[local] || std::memcmp(peer + offered + 1, supported + local + 1, peer[offered]) != 0) continue;
*output = const_cast<unsigned char*>(peer + offered + 1);
*outputLength = peer[offered];
return 1;
}
}
*output = const_cast<unsigned char*>(supported + 1);
*outputLength = supported[0];
return 2;
}
+69 -1
View File
@@ -11,6 +11,7 @@
#include <exception>
#include <limits>
#include <span>
#include <utility>
#include <stdexcept>
#include <vector>
@@ -63,6 +64,45 @@ void CheckConversion(int type, std::span<const unsigned char> input, std::span<c
free_nid_postfix(result);
}
constexpr std::uint64_t Asn1Error(int reason) {
return (std::uint64_t{13} << 24) | (std::uint64_t{0xfff} << 12) | static_cast<std::uint64_t>(reason);
}
void RequireConversionError(unsigned char** output, int type, const unsigned char* data, int length, int reason, const char* message) {
ERR_clear_error_nid_postfix();
Asn1String value{length, type, const_cast<unsigned char*>(data), 0};
const unsigned char* before = output ? *output : nullptr;
Require(ASN1_STRING_to_UTF8_nid_postfix(output, &value) == -1, message);
Require(!output || *output == before, "a failed conversion leaves the output alone");
Require(ERR_get_error_nid_postfix() == Asn1Error(reason) && ERR_get_error_nid_postfix() == 0, message);
}
void MalformedStrings() {
unsigned char sentinel = 0xab;
unsigned char* result = &sentinel;
constexpr unsigned char oddBmp[] = {0, 0x41, 0};
RequireConversionError(&result, 30, oddBmp, 3, 129, "odd BMPString length");
RequireConversionError(&result, 30, nullptr, 1, 129, "BMPString length is checked before the data");
constexpr unsigned char bmpSurrogate[] = {0, 0x41, 0xd8, 0};
RequireConversionError(&result, 30, bmpSurrogate, 4, 124, "BMPString surrogate");
constexpr unsigned char shortUniversal[] = {0, 0, 0, 0x41, 0};
RequireConversionError(&result, 28, shortUniversal, 5, 133, "UniversalString length not a multiple of four");
RequireConversionError(&result, 28, nullptr, 2, 133, "UniversalString length is checked before the data");
constexpr unsigned char universalBeyond[] = {0, 0x11, 0, 0};
RequireConversionError(&result, 28, universalBeyond, 4, 124, "UniversalString above U+10FFFF");
constexpr unsigned char universalSurrogate[] = {0, 0, 0xdc, 0};
RequireConversionError(&result, 28, universalSurrogate, 4, 124, "UniversalString surrogate");
constexpr unsigned char overlong[] = {0xc0, 0x80};
constexpr unsigned char truncated[] = {'A', 0xe2, 0x82};
constexpr unsigned char surrogate[] = {0xed, 0xa0, 0x80};
constexpr unsigned char beyond[] = {0xf5, 0x80, 0x80, 0x80};
constexpr unsigned char continuation[] = {0x80};
for (const auto& [data, length] : {std::pair{overlong, 2}, std::pair{truncated, 3}, std::pair{surrogate, 3},
std::pair{beyond, 4}, std::pair{continuation, 1}})
RequireConversionError(&result, 12, data, length, 134, "malformed UTF8String");
RequireConversionError(nullptr, 30, oddBmp, 3, 129, "a malformed string fails before the output is used");
}
void Accessors() {
unsigned char data[] = {'a', 0, 'b'};
Asn1String value{3, 12, data, std::numeric_limits<std::int64_t>::max()};
@@ -185,12 +225,13 @@ void Strings() {
std::size_t lastAllocationSize = 0;
int allocationCount = 0;
bool failAllocation = false;
int freeCount = 0;
void* APS5_VABI Allocate_nid_no_patch(std::size_t size) {
lastAllocationSize = size;
++allocationCount;
return std::malloc(size);
return failAllocation ? nullptr : std::malloc(size);
}
void APS5_VABI Free_nid_no_patch(void* pointer) {
@@ -214,6 +255,32 @@ void Allocator() {
constexpr unsigned char expected[] = {0xc3, 0xbf};
CheckConversion(20, input, expected);
Require(allocationCount == 1 && lastAllocationSize == 3 && freeCount == 1, "conversion uses the application allocator");
constexpr unsigned char universal[] = {0, 0, 0, 'A', 0, 0, 0x03, 0xa9, 0, 0, 0x20, 0xac, 0, 0x01, 0xf6, 0x00};
constexpr unsigned char mixed[] = {'A', 0xce, 0xa9, 0xe2, 0x82, 0xac, 0xf0, 0x9f, 0x98, 0x80};
CheckConversion(28, universal, mixed);
Require(allocationCount == 2 && lastAllocationSize == sizeof(mixed) + 1 && freeCount == 2,
"a conversion with every UTF-8 length makes one allocation of the exact size");
unsigned char badUtf8[] = {'A', 0xc0, 0x80};
Asn1String malformed{3, 12, badUtf8, 0};
unsigned char* untouched = nullptr;
ERR_clear_error_nid_postfix();
Require(ASN1_STRING_to_UTF8_nid_postfix(&untouched, &malformed) == -1 && untouched == nullptr && allocationCount == 2,
"a malformed string allocates nothing");
ERR_clear_error_nid_postfix();
failAllocation = true;
unsigned char sentinel = 0xab;
unsigned char* result = &sentinel;
unsigned char latin[] = {0xff};
Asn1String converted{1, 20, latin, 0};
ERR_clear_error_nid_postfix();
Require(ASN1_STRING_to_UTF8_nid_postfix(&result, &converted) == -1 && result == &sentinel, "allocation failure of a converted string");
Require(ERR_get_error_nid_postfix() == Asn1Error(65) && ERR_get_error_nid_postfix() == 0, "one malloc failure for a converted string");
unsigned char utf8[] = {'A'};
Asn1String copied{1, 12, utf8, 0};
Require(ASN1_STRING_to_UTF8_nid_postfix(&result, &copied) == -1 && result == &sentinel, "allocation failure of a UTF8String copy");
Require(ERR_get_error_nid_postfix() == Asn1Error(65) && ERR_get_error_nid_postfix() == Asn1Error(65) && ERR_get_error_nid_postfix() == 0,
"ASN1_STRING_set and ASN1_mbstring_ncopy both report the malloc failure of a UTF8String copy");
failAllocation = false;
}
} // namespace
@@ -228,4 +295,5 @@ int main(int argc, char**) {
Accessors();
Integers();
Strings();
MalformedStrings();
}
+8 -2
View File
@@ -1,4 +1,4 @@
add_library(libSceLibreSsl3 SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/IntegerDecoder.cpp)
add_library(libSceLibreSsl3 SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/IntegerDecoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Key.cpp)
target_include_directories(libSceLibreSsl3 PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(libSceLibreSsl3 PRIVATE libSceLibreSsl libc)
configure_windows_unwind(libSceLibreSsl3)
@@ -21,8 +21,14 @@ if(BUILD_TESTING)
configure_windows_unwind(guest_libressl3_asn1_tests)
add_test(NAME guest_libressl3_asn1 COMMAND guest_libressl3_asn1_tests)
set_tests_properties(guest_libressl3_asn1 PROPERTIES TIMEOUT 30)
add_test_executable(guest_libressl3_key_reference_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSsl3KeyReference.cpp)
target_include_directories(guest_libressl3_key_reference_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_libressl3_key_reference_tests PRIVATE libSceLibreSsl3)
configure_windows_unwind(guest_libressl3_key_reference_tests)
add_test(NAME guest_libressl3_key_reference COMMAND guest_libressl3_key_reference_tests)
set_tests_properties(guest_libressl3_key_reference PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_crypto_memcmp guest_libressl3_digest guest_libressl3_asn1 PROPERTIES ENVIRONMENT_MODIFICATION
set_tests_properties(guest_crypto_memcmp guest_libressl3_digest guest_libressl3_asn1 guest_libressl3_key_reference PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibreSsl3>")
endif()
endif()
-10
View File
@@ -61,11 +61,6 @@ int APS5_VABI DSA_get0_pqg_nid_postfix(void) {
return 0;
}
int APS5_VABI EC_KEY_up_ref_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI ERR_load_crypto_strings_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -181,11 +176,6 @@ int APS5_VABI EVP_PKEY_size_nid_postfix(void) {
return 0;
}
int APS5_VABI EVP_PKEY_up_ref_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI EVP_SignFinal_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -1,6 +1,7 @@
#pragma once
#include "SceTypes.hpp"
#include "prx/libSceLibreSsl3/include/Key.hpp"
#include "prx/libSceLibreSsl/include/Api.hpp"
extern "C" {
@@ -15,7 +16,7 @@ int APS5_VABI DH_get0_key_nid_postfix(void);
int APS5_VABI DH_get0_pqg_nid_postfix(void);
int APS5_VABI DSA_get0_key_nid_postfix(void);
int APS5_VABI DSA_get0_pqg_nid_postfix(void);
int APS5_VABI EC_KEY_up_ref_nid_postfix(void);
int APS5_VABI EC_KEY_up_ref_nid_postfix(LibreSsl3EcKey* key);
int APS5_VABI ERR_load_crypto_strings_nid_postfix(void);
int APS5_VABI ERR_peek_last_error_nid_postfix(void);
int APS5_VABI ERR_print_errors_cb_nid_postfix(void);
@@ -39,7 +40,7 @@ int APS5_VABI EVP_PKEY_get0_RSA_nid_postfix(void);
int APS5_VABI EVP_PKEY_new_mac_key_nid_postfix(void);
int APS5_VABI EVP_PKEY_set1_EC_KEY_nid_postfix(void);
int APS5_VABI EVP_PKEY_size_nid_postfix(void);
int APS5_VABI EVP_PKEY_up_ref_nid_postfix(void);
int APS5_VABI EVP_PKEY_up_ref_nid_postfix(LibreSsl3EvpPkey* key);
int APS5_VABI EVP_SignFinal_nid_postfix(void);
int APS5_VABI EVP_VerifyFinal_nid_postfix(void);
int APS5_VABI EVP_aes_128_wrap_nid_postfix(void);
@@ -0,0 +1,30 @@
#pragma once
#include <cstddef>
#include <cstdint>
struct LibreSsl3EvpPkey {
std::int32_t type;
std::int32_t references;
const void* method;
void* key;
std::int32_t saveParameters;
};
struct LibreSsl3EcKey {
const void* method;
std::int32_t version;
void* group;
void* publicKey;
void* privateKey;
std::uint32_t encodingFlags;
std::int32_t conversionForm;
std::int32_t references;
std::int32_t flags;
void* extraData;
};
static_assert(sizeof(LibreSsl3EvpPkey) == 32);
static_assert(offsetof(LibreSsl3EvpPkey, references) == 4);
static_assert(sizeof(LibreSsl3EcKey) == 64);
static_assert(offsetof(LibreSsl3EcKey, references) == 48);
+38
View File
@@ -0,0 +1,38 @@
#include "prx/libSceLibreSsl3/libSceLibreSsl3.hpp"
#include <atomic>
#include <limits>
#include <stdexcept>
namespace {
static_assert(std::atomic_ref<std::int32_t>::required_alignment <= alignof(std::int32_t));
int Retain_nid_no_patch(std::int32_t& references) {
std::atomic_ref<std::int32_t> count(references);
auto previous = count.load();
for (;;) {
if (previous == std::numeric_limits<std::int32_t>::max())
throw std::overflow_error("LibreSSL3 key reference count overflow");
if (count.compare_exchange_weak(previous, previous + 1))
return previous > 0;
}
}
} // namespace
extern "C" {
int APS5_VABI EVP_PKEY_up_ref_nid_postfix(LibreSsl3EvpPkey* key) {
if (!key)
throw std::invalid_argument("EVP_PKEY_up_ref requires a key");
return Retain_nid_no_patch(key->references);
}
int APS5_VABI EC_KEY_up_ref_nid_postfix(LibreSsl3EcKey* key) {
if (!key)
throw std::invalid_argument("EC_KEY_up_ref requires a key");
return Retain_nid_no_patch(key->references);
}
}
@@ -1,3 +1,4 @@
#include "prx/libSceMetadataReaderWriter/include/Api.hpp"
#include <cstddef>
#include <cstdint>
#include "SceTypes.hpp"
@@ -5,6 +6,199 @@
extern "C" {
APS5_DUMMY_FUN
int APS5_VABI _ZN23sceMetadataReaderWriter10initializeEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter11getMimeTypeERKSsRSt6vectorIjSaIjEE() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter12getMimeTypesERKSsiPi() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter12readMetadataERKSsRKNS_8MetadataEjRS2_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter13writeMetadataERKSsRKNS_8MetadataEj() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter13writeMetadataERKSsRKNS_8MetadataEjPS2_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter15ParserInterfaceC1ERKS0_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter15ParserInterfaceC2ERKS0_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter16extractThumbnailERKSsjRNS_9ThumbnailE() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter16getNumOfMimeTypeERKSsPi() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter17getParserInfoListERSt6vectorINS_10ParserInfoESaIS1_EE() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter18registerParserInfoERKSt6vectorINS_10ParserInfoESaIS1_EE() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter20unregisterParserInfoEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter23registerParserInterfaceEjRKNS_15ParserInterfaceE() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter5ValueaSERS0_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8Metadata11DeserializeERN3sce4Json5ValueERS0_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8Metadata11deleteFieldERKSs() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8Metadata15iterateAllFieldEPFiRKSsRKNS_5ValueEbE() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8Metadata8getFieldEiRSsRNS_5ValueERb() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8Metadata8setFieldERKSs() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8Metadata8setFieldERKSsRKNS_5ValueE() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8Metadata9SerializeERS0_RN3sce4Json5ValueE() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC1EPFPvmEPFvS1_E() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC1ERKS0_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC1ERKS0_PFPvmEPFvS3_E() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC1Ev() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC2EPFPvmEPFvS1_E() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC2ERKS0_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC2ERKS0_PFPvmEPFvS3_E() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC2Ev() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataD0Ev() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataD1Ev() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataD2Ev() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataaSERKS0_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN23sceMetadataReaderWriter8finalizeEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK23sceMetadataReaderWriter8Metadata13getFieldCountEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK23sceMetadataReaderWriter8Metadata8getFieldEiRSsRNS_5ValueE() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK23sceMetadataReaderWriter8Metadata8getValueERKSsRNS_5ValueE() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK23sceMetadataReaderWriter8Metadata9dumpValueEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}
@@ -0,0 +1,47 @@
#pragma once
#include "SceTypes.hpp"
extern "C" {
int APS5_VABI _ZN23sceMetadataReaderWriter10initializeEv(void);
int APS5_VABI _ZN23sceMetadataReaderWriter11getMimeTypeERKSsRSt6vectorIjSaIjEE(void);
int APS5_VABI _ZN23sceMetadataReaderWriter12getMimeTypesERKSsiPi(void);
int APS5_VABI _ZN23sceMetadataReaderWriter12readMetadataERKSsRKNS_8MetadataEjRS2_(void);
int APS5_VABI _ZN23sceMetadataReaderWriter13writeMetadataERKSsRKNS_8MetadataEj(void);
int APS5_VABI _ZN23sceMetadataReaderWriter13writeMetadataERKSsRKNS_8MetadataEjPS2_(void);
int APS5_VABI _ZN23sceMetadataReaderWriter15ParserInterfaceC1ERKS0_(void);
int APS5_VABI _ZN23sceMetadataReaderWriter15ParserInterfaceC2ERKS0_(void);
int APS5_VABI _ZN23sceMetadataReaderWriter16extractThumbnailERKSsjRNS_9ThumbnailE(void);
int APS5_VABI _ZN23sceMetadataReaderWriter16getNumOfMimeTypeERKSsPi(void);
int APS5_VABI _ZN23sceMetadataReaderWriter17getParserInfoListERSt6vectorINS_10ParserInfoESaIS1_EE(void);
int APS5_VABI _ZN23sceMetadataReaderWriter18registerParserInfoERKSt6vectorINS_10ParserInfoESaIS1_EE(void);
int APS5_VABI _ZN23sceMetadataReaderWriter20unregisterParserInfoEv(void);
int APS5_VABI _ZN23sceMetadataReaderWriter23registerParserInterfaceEjRKNS_15ParserInterfaceE(void);
int APS5_VABI _ZN23sceMetadataReaderWriter5ValueaSERS0_(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8Metadata11DeserializeERN3sce4Json5ValueERS0_(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8Metadata11deleteFieldERKSs(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8Metadata15iterateAllFieldEPFiRKSsRKNS_5ValueEbE(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8Metadata8getFieldEiRSsRNS_5ValueERb(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8Metadata8setFieldERKSs(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8Metadata8setFieldERKSsRKNS_5ValueE(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8Metadata9SerializeERS0_RN3sce4Json5ValueE(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC1EPFPvmEPFvS1_E(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC1ERKS0_(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC1ERKS0_PFPvmEPFvS3_E(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC1Ev(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC2EPFPvmEPFvS1_E(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC2ERKS0_(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC2ERKS0_PFPvmEPFvS3_E(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataC2Ev(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataD0Ev(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataD1Ev(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataD2Ev(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8MetadataaSERKS0_(void);
int APS5_VABI _ZN23sceMetadataReaderWriter8finalizeEv(void);
int APS5_VABI _ZNK23sceMetadataReaderWriter8Metadata13getFieldCountEv(void);
int APS5_VABI _ZNK23sceMetadataReaderWriter8Metadata8getFieldEiRSsRNS_5ValueE(void);
int APS5_VABI _ZNK23sceMetadataReaderWriter8Metadata8getValueERKSsRNS_5ValueE(void);
int APS5_VABI _ZNK23sceMetadataReaderWriter8Metadata9dumpValueEv(void);
}
@@ -1 +1,3 @@
#pragma once
#include "prx/libSceMetadataReaderWriter/include/Api.hpp"
+10
View File
@@ -3,6 +3,16 @@ target_include_directories(libSceNetCtl PRIVATE ${LIBS_INCLUDE_DIR})
configure_windows_unwind(libSceNetCtl)
if(BUILD_TESTING)
add_test_executable(guest_net_ctl_v6_callbacks_tests tests/V6Callbacks.cpp)
target_include_directories(guest_net_ctl_v6_callbacks_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_net_ctl_v6_callbacks_tests PRIVATE libSceNetCtl libc)
configure_windows_unwind(guest_net_ctl_v6_callbacks_tests)
add_test(NAME guest_net_ctl_v6_callbacks COMMAND guest_net_ctl_v6_callbacks_tests)
set_tests_properties(guest_net_ctl_v6_callbacks PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_net_ctl_v6_callbacks PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNetCtl>")
endif()
add_test_executable(guest_net_ctl_tests ${LIBS_INCLUDE_DIR}/tests/GuestNetCtl.cpp)
target_include_directories(guest_net_ctl_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_net_ctl_tests PRIVATE libSceNetCtl libc)
+416 -12
View File
@@ -9,8 +9,10 @@
#include <ifaddrs.h>
#include <net/if.h>
#include <net/route.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#ifdef __linux__
@@ -21,18 +23,22 @@
#endif
#endif
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstdint>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
#include "prx/libc/include/HostEntropy.hpp"
#include <fstream>
#include <memory>
#include <mutex>
#include <sstream>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>
// Network-control state reflects host interface addressing, not PSN sign-in status.
@@ -49,6 +55,25 @@ static constexpr int NET_CTL_STATE_DISCONNECTED = 0;
static constexpr int NET_CTL_STATE_IPOBTAINED = 3;
static constexpr int NET_CTL_EVENT_DISCONNECTED = 1;
static constexpr int NET_CTL_EVENT_IPOBTAINED = 3;
static constexpr int NET_CTL_STUN_STATUS_FAILED = 1;
static constexpr int NET_CTL_STUN_STATUS_OK = 2;
static constexpr int NET_CTL_NAT_TYPE_NONE = 0;
static constexpr int NET_CTL_NAT_TYPE_1 = 1;
static constexpr int NET_CTL_NAT_TYPE_2 = 2;
static constexpr int NET_CTL_NAT_TYPE_3 = 3;
static constexpr std::uint16_t STUN_BINDING_REQUEST = 0x0001;
static constexpr std::uint16_t STUN_BINDING_RESPONSE = 0x0101;
static constexpr std::uint32_t STUN_MAGIC_COOKIE = 0x2112A442u;
static constexpr std::uint16_t STUN_ATTR_MAPPED_ADDRESS = 0x0001;
static constexpr std::uint16_t STUN_ATTR_CHANGED_ADDRESS = 0x0005;
static constexpr std::uint16_t STUN_ATTR_XOR_MAPPED_ADDRESS = 0x0020;
static constexpr std::uint16_t STUN_ATTR_XOR_MAPPED_ADDRESS_LEGACY = 0x8020;
static constexpr std::uint16_t STUN_ATTR_OTHER_ADDRESS = 0x802b;
static constexpr std::size_t STUN_HEADER_SIZE = 20;
static constexpr std::size_t STUN_TRANSACTION_ID_SIZE = 12;
static constexpr int STUN_DEFAULT_TIMEOUT_MS = 300;
static constexpr int STUN_TOTAL_BUDGET_MS = 1000;
static constexpr int STUN_CACHE_TTL_MS = 60000;
static constexpr int NET_CTL_INFO_DEVICE = 1;
static constexpr int NET_CTL_INFO_ETHER_ADDR = 2;
static constexpr int NET_CTL_INFO_MTU = 3;
@@ -457,6 +482,346 @@ extern "C" std::size_t NetCtlHostDnsServers_nid_no_patch(std::uint32_t* servers,
return host.dns.size();
}
#ifdef _WIN32
using StunSocket = SOCKET;
static constexpr StunSocket INVALID_STUN_SOCKET = INVALID_SOCKET;
static void CloseStunSocket(StunSocket descriptor) { closesocket(descriptor); }
static bool EnsureSocketRuntime() {
static const int status = [] {
WSADATA data{};
return WSAStartup(MAKEWORD(2, 2), &data);
}();
return status == 0;
}
#else
using StunSocket = int;
static constexpr StunSocket INVALID_STUN_SOCKET = -1;
static void CloseStunSocket(StunSocket descriptor) { close(descriptor); }
static bool EnsureSocketRuntime() { return true; }
#endif
struct StunEndpoint {
std::uint32_t address = 0;
std::uint16_t port = 0;
bool valid = false;
};
struct StunProbeResponse {
StunEndpoint mapped;
StunEndpoint other;
};
struct StunConfig {
std::mutex lock;
bool overrideActive = false;
std::vector<sockaddr_in> overrideServers;
int overrideTimeoutMs = STUN_DEFAULT_TIMEOUT_MS;
std::uint64_t configGeneration = 1;
std::uint64_t cachedConfigGeneration = 0;
std::uint32_t cachedLocalAddr = 0;
std::chrono::steady_clock::time_point cachedUntil{};
bool hasCachedResult = false;
NetCtlNatInfo cachedResult{};
};
static StunConfig& GetStunConfig() {
static StunConfig config;
return config;
}
extern "C" void NetCtlSetStunServers_nid_no_patch(const sockaddr_in* servers, std::size_t count, int timeoutMs) {
auto& config = GetStunConfig();
std::lock_guard guard(config.lock);
++config.configGeneration;
config.hasCachedResult = false;
config.overrideServers.clear();
if (!servers && count == 0 && timeoutMs <= 0) {
config.overrideActive = false;
config.overrideTimeoutMs = STUN_DEFAULT_TIMEOUT_MS;
return;
}
config.overrideActive = true;
config.overrideTimeoutMs = timeoutMs > 0 ? timeoutMs : STUN_DEFAULT_TIMEOUT_MS;
if (servers && count > 0) config.overrideServers.assign(servers, servers + count);
}
static void AppendUniqueStunServer(std::vector<sockaddr_in>& servers, const sockaddr_in& candidate) {
if (candidate.sin_family != AF_INET || candidate.sin_addr.s_addr == 0 || candidate.sin_port == 0) return;
for (const auto& existing : servers) {
if (existing.sin_addr.s_addr == candidate.sin_addr.s_addr && existing.sin_port == candidate.sin_port) return;
}
servers.push_back(candidate);
}
static std::vector<sockaddr_in> ResolveStunServers(int& timeoutMs, std::uint64_t& generationOut, std::chrono::steady_clock::time_point totalDeadline) {
auto& config = GetStunConfig();
{
std::lock_guard guard(config.lock);
generationOut = config.configGeneration;
if (config.overrideActive) {
timeoutMs = config.overrideTimeoutMs;
return config.overrideServers;
}
}
timeoutMs = STUN_DEFAULT_TIMEOUT_MS;
struct DefaultStunHost {
const char* host;
const char* port;
};
static constexpr DefaultStunHost kDefaultHosts[] = {
{"stun.playstation.net", "3478"},
{"stun.cloudflare.com", "3478"},
{"stun.l.google.com", "19302"},
{"stun1.l.google.com", "19302"},
};
struct DnsQueryState {
std::mutex lock;
std::condition_variable cv;
std::vector<sockaddr_in> servers;
bool done = false;
};
const auto now = std::chrono::steady_clock::now();
if (!EnsureSocketRuntime() || now >= totalDeadline) return {};
const auto dnsCap = now + std::chrono::milliseconds(STUN_DEFAULT_TIMEOUT_MS * 2);
const auto dnsDeadline = dnsCap < totalDeadline ? dnsCap : totalDeadline;
auto state = std::make_shared<DnsQueryState>();
std::thread([state, dnsDeadline] {
for (const auto& entry : kDefaultHosts) {
if (std::chrono::steady_clock::now() >= dnsDeadline) break;
addrinfo hints{};
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
addrinfo* list = nullptr;
if (getaddrinfo(entry.host, entry.port, &hints, &list) != 0 || !list) continue;
{
std::lock_guard guard(state->lock);
for (auto* item = list; item; item = item->ai_next) {
if (item->ai_addr && item->ai_addrlen >= sizeof(sockaddr_in) && item->ai_addr->sa_family == AF_INET) {
AppendUniqueStunServer(state->servers, *reinterpret_cast<const sockaddr_in*>(item->ai_addr));
}
}
}
freeaddrinfo(list);
}
{
std::lock_guard guard(state->lock);
state->done = true;
}
state->cv.notify_one();
}).detach();
std::unique_lock lock(state->lock);
state->cv.wait_until(lock, dnsDeadline, [&] { return state->done; });
return state->servers;
}
static StunEndpoint DecodeStunIpv4Endpoint(const std::uint8_t* value, std::uint16_t length, bool xorMask) {
StunEndpoint endpoint{};
if (length < 8 || value[1] != 0x01) return endpoint;
const auto rawPort = static_cast<std::uint16_t>((static_cast<std::uint16_t>(value[2]) << 8) | value[3]);
std::uint32_t rawAddress = 0;
std::memcpy(&rawAddress, value + 4, sizeof(rawAddress));
endpoint.port = xorMask ? static_cast<std::uint16_t>(rawPort ^ static_cast<std::uint16_t>(STUN_MAGIC_COOKIE >> 16)) : rawPort;
endpoint.address = xorMask ? (rawAddress ^ htonl(STUN_MAGIC_COOKIE)) : rawAddress;
endpoint.valid = endpoint.address != 0 && endpoint.port != 0;
return endpoint;
}
static bool ParseStunBindingResponse(const std::uint8_t* packet, std::size_t size, const std::uint8_t* transactionId, StunProbeResponse& response) {
if (size < STUN_HEADER_SIZE) return false;
const auto type = static_cast<std::uint16_t>((static_cast<std::uint16_t>(packet[0]) << 8) | packet[1]);
const auto length = static_cast<std::uint16_t>((static_cast<std::uint16_t>(packet[2]) << 8) | packet[3]);
if (type != STUN_BINDING_RESPONSE || (length & 3u) != 0 || STUN_HEADER_SIZE + length > size) return false;
const std::uint32_t expectedCookie = htonl(STUN_MAGIC_COOKIE);
if (std::memcmp(packet + 4, &expectedCookie, sizeof(expectedCookie)) != 0) return false;
if (std::memcmp(packet + 8, transactionId, STUN_TRANSACTION_ID_SIZE) != 0) return false;
response = {};
bool hasXorMapped = false;
const std::size_t end = STUN_HEADER_SIZE + length;
for (std::size_t offset = STUN_HEADER_SIZE; offset < end;) {
if (offset + 4 > end) return false;
const auto attrType = static_cast<std::uint16_t>((static_cast<std::uint16_t>(packet[offset]) << 8) | packet[offset + 1]);
const auto attrLen = static_cast<std::uint16_t>((static_cast<std::uint16_t>(packet[offset + 2]) << 8) | packet[offset + 3]);
const std::size_t valueOffset = offset + 4;
if (valueOffset + attrLen > end) return false;
const std::uint8_t* value = packet + valueOffset;
if (attrType == STUN_ATTR_XOR_MAPPED_ADDRESS || attrType == STUN_ATTR_XOR_MAPPED_ADDRESS_LEGACY) {
const auto endpoint = DecodeStunIpv4Endpoint(value, attrLen, true);
if (endpoint.valid) {
response.mapped = endpoint;
hasXorMapped = true;
}
} else if (attrType == STUN_ATTR_MAPPED_ADDRESS && !hasXorMapped) {
const auto endpoint = DecodeStunIpv4Endpoint(value, attrLen, false);
if (endpoint.valid) response.mapped = endpoint;
} else if (attrType == STUN_ATTR_OTHER_ADDRESS || attrType == STUN_ATTR_CHANGED_ADDRESS) {
const auto endpoint = DecodeStunIpv4Endpoint(value, attrLen, false);
if (endpoint.valid) response.other = endpoint;
}
offset = valueOffset + ((static_cast<std::size_t>(attrLen) + 3u) & ~std::size_t(3u));
}
return response.mapped.valid;
}
static bool ProbeStunServer(StunSocket descriptor, const sockaddr_in& server, int timeoutMs, std::chrono::steady_clock::time_point totalDeadline, StunProbeResponse& response) {
const auto start = std::chrono::steady_clock::now();
if (start >= totalDeadline) return false;
std::uint8_t request[STUN_HEADER_SIZE] = {};
request[0] = static_cast<std::uint8_t>(STUN_BINDING_REQUEST >> 8);
request[1] = static_cast<std::uint8_t>(STUN_BINDING_REQUEST & 0xFF);
const std::uint32_t cookie = htonl(STUN_MAGIC_COOKIE);
std::memcpy(request + 4, &cookie, sizeof(cookie));
HostEntropy_nid_no_patch(request + 8, STUN_TRANSACTION_ID_SIZE);
const auto sent = sendto(descriptor, reinterpret_cast<const char*>(request), sizeof(request), 0,
reinterpret_cast<const sockaddr*>(&server), sizeof(server));
if (sent != static_cast<int>(sizeof(request))) return false;
const auto probeDeadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeoutMs > 0 ? timeoutMs : 1);
const auto deadline = probeDeadline < totalDeadline ? probeDeadline : totalDeadline;
for (;;) {
const auto now = std::chrono::steady_clock::now();
if (now >= deadline) return false;
const auto remainingUs = std::chrono::duration_cast<std::chrono::microseconds>(deadline - now).count();
fd_set readSet;
FD_ZERO(&readSet);
FD_SET(descriptor, &readSet);
timeval tv{};
tv.tv_sec = static_cast<long>(remainingUs / 1000000);
tv.tv_usec = static_cast<long>(remainingUs % 1000000);
if (tv.tv_sec == 0 && tv.tv_usec == 0) tv.tv_usec = 1000;
#ifdef _WIN32
const int ready = select(0, &readSet, nullptr, nullptr, &tv);
#else
const int ready = select(descriptor + 1, &readSet, nullptr, nullptr, &tv);
#endif
if (ready <= 0) return false;
std::uint8_t buffer[512];
sockaddr_in from{};
#ifdef _WIN32
int fromLen = sizeof(from);
#else
socklen_t fromLen = sizeof(from);
#endif
const auto received = recvfrom(descriptor, reinterpret_cast<char*>(buffer), sizeof(buffer), 0,
reinterpret_cast<sockaddr*>(&from), &fromLen);
#ifdef _WIN32
if (received < 0) {
if (WSAGetLastError() == WSAECONNRESET) continue;
return false;
}
#else
if (received < 0) return false;
#endif
if (received == 0) return false;
if (ParseStunBindingResponse(buffer, static_cast<std::size_t>(received), request + 8, response)) return true;
}
}
static NetCtlNatInfo DiscoverNatInfo(in_addr localAddress) {
NetCtlNatInfo result{};
result.size = sizeof(NetCtlNatInfo);
result.stun_status = NET_CTL_STUN_STATUS_FAILED;
result.nat_type = NET_CTL_NAT_TYPE_NONE;
result.mapped_addr = 0;
const auto totalDeadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(STUN_TOTAL_BUDGET_MS);
int timeoutMs = STUN_DEFAULT_TIMEOUT_MS;
std::uint64_t generation = 0;
const auto servers = ResolveStunServers(timeoutMs, generation, totalDeadline);
if (servers.empty() || !EnsureSocketRuntime()) return result;
const StunSocket descriptor = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (descriptor == INVALID_STUN_SOCKET) return result;
#ifdef _WIN32
BOOL newBehavior = FALSE;
DWORD bytesReturned = 0;
WSAIoctl(descriptor, _WSAIOW(IOC_VENDOR, 12), &newBehavior, sizeof(newBehavior), nullptr, 0, &bytesReturned, nullptr, nullptr);
#endif
sockaddr_in bindAddr{};
bindAddr.sin_family = AF_INET;
bindAddr.sin_addr = (ntohl(servers[0].sin_addr.s_addr) >> 24) == 127 ? servers[0].sin_addr : localAddress;
bindAddr.sin_port = 0;
if (bind(descriptor, reinterpret_cast<const sockaddr*>(&bindAddr), sizeof(bindAddr)) != 0) {
CloseStunSocket(descriptor);
return result;
}
sockaddr_in boundAddr{};
#ifdef _WIN32
int boundLen = sizeof(boundAddr);
#else
socklen_t boundLen = sizeof(boundAddr);
#endif
if (getsockname(descriptor, reinterpret_cast<sockaddr*>(&boundAddr), &boundLen) != 0) {
CloseStunSocket(descriptor);
return result;
}
const std::uint16_t localPort = ntohs(boundAddr.sin_port);
StunProbeResponse firstResponse{};
sockaddr_in primaryServer{};
std::size_t primaryIndex = servers.size();
for (std::size_t i = 0; i < servers.size(); ++i) {
if (ProbeStunServer(descriptor, servers[i], timeoutMs, totalDeadline, firstResponse)) {
primaryServer = servers[i];
primaryIndex = i;
break;
}
}
if (primaryIndex == servers.size()) {
CloseStunSocket(descriptor);
return result;
}
if (firstResponse.mapped.address == localAddress.s_addr && firstResponse.mapped.port == localPort) {
CloseStunSocket(descriptor);
result.stun_status = NET_CTL_STUN_STATUS_OK;
result.nat_type = NET_CTL_NAT_TYPE_1;
result.mapped_addr = firstResponse.mapped.address;
return result;
}
std::vector<sockaddr_in> secondaryServers;
if (firstResponse.other.valid) {
sockaddr_in otherServer{};
otherServer.sin_family = AF_INET;
otherServer.sin_addr.s_addr = firstResponse.other.address;
otherServer.sin_port = htons(firstResponse.other.port);
if (otherServer.sin_addr.s_addr != primaryServer.sin_addr.s_addr || otherServer.sin_port != primaryServer.sin_port) {
AppendUniqueStunServer(secondaryServers, otherServer);
}
if (otherServer.sin_port != primaryServer.sin_port) {
sockaddr_in sameIpOtherPort = primaryServer;
sameIpOtherPort.sin_port = otherServer.sin_port;
AppendUniqueStunServer(secondaryServers, sameIpOtherPort);
}
}
for (std::size_t i = 0; i < servers.size(); ++i) {
if (i == primaryIndex) continue;
if (servers[i].sin_addr.s_addr != primaryServer.sin_addr.s_addr || servers[i].sin_port != primaryServer.sin_port) {
AppendUniqueStunServer(secondaryServers, servers[i]);
}
}
for (const auto& secondary : secondaryServers) {
StunProbeResponse secondResponse{};
if (ProbeStunServer(descriptor, secondary, timeoutMs, totalDeadline, secondResponse)) {
CloseStunSocket(descriptor);
result.stun_status = NET_CTL_STUN_STATUS_OK;
result.nat_type = (secondResponse.mapped.address == firstResponse.mapped.address &&
secondResponse.mapped.port == firstResponse.mapped.port)
? NET_CTL_NAT_TYPE_2
: NET_CTL_NAT_TYPE_3;
result.mapped_addr = firstResponse.mapped.address;
return result;
}
}
CloseStunSocket(descriptor);
return result;
}
static void WriteInfoWord(NetCtlInfo* info, std::uint32_t value) {
std::memcpy(info->opaque, &value, sizeof(value));
}
@@ -504,28 +869,41 @@ static void HostTraffic(std::uint64_t& sent, std::uint64_t& received) {
#endif
}
static int CheckCallbacks(CallbackSlot (&callbacks)[MAX_CALLBACKS]) {
static int CheckCallbacks(CallbackSlot (&callbacks)[MAX_CALLBACKS], bool includeV6 = false) {
if (!g_initialized.load()) return SCE_NET_CTL_ERROR_NOT_INITIALIZED;
if (!HasCallback(callbacks)) return 0;
const int event = HostHasAddress(AF_UNSPEC) ? NET_CTL_EVENT_IPOBTAINED : NET_CTL_EVENT_DISCONNECTED;
CallbackSlot due[MAX_CALLBACKS];
const bool primary = HasCallback(callbacks);
const bool ipv6 = includeV6 && HasCallback(g_callbacksV6);
if (!primary && !ipv6) return 0;
const int primaryEvent = primary ? (HostHasAddress(AF_UNSPEC) ? NET_CTL_EVENT_IPOBTAINED : NET_CTL_EVENT_DISCONNECTED) : 0;
const int ipv6Event = ipv6 ? (HostHasAddress(AF_INET6) ? NET_CTL_EVENT_IPOBTAINED : NET_CTL_EVENT_DISCONNECTED) : 0;
struct Delivery {
CallbackSlot callback;
int event;
};
Delivery due[2 * MAX_CALLBACKS];
int count = 0;
{
std::lock_guard lock(g_callbackLock);
for (auto& slot : callbacks) {
if (!slot.func || slot.lastEvent == event) continue;
slot.lastEvent = event;
due[count++] = slot;
}
const auto collect = [&](CallbackSlot (&slots)[MAX_CALLBACKS], int event) {
if (event == 0) return;
for (auto& slot : slots) {
if (!slot.func || slot.lastEvent == event) continue;
slot.lastEvent = event;
due[count++] = {slot, event};
}
};
collect(callbacks, primaryEvent);
if (includeV6) collect(g_callbacksV6, ipv6Event);
}
for (int index = 0; index < count; ++index) due[index].func(event, due[index].arg);
for (int index = 0; index < count; ++index)
due[index].callback.func(due[index].event, due[index].callback.arg);
return 0;
}
extern "C" {
int APS5_VABI sceNetCtlCheckCallback(void) {
return CheckCallbacks(g_callbacks);
return CheckCallbacks(g_callbacks, true);
}
int APS5_VABI sceNetCtlGetIfStat(NetCtlIfStat* stat) {
@@ -574,7 +952,33 @@ int APS5_VABI sceNetCtlGetNatInfo(NetCtlNatInfo* info) {
if (!g_initialized.load()) return SCE_NET_CTL_ERROR_NOT_INITIALIZED;
if (!info) return SCE_NET_CTL_ERROR_INVALID_ADDR;
if (info->size != sizeof(NetCtlNatInfo)) return SCE_NET_CTL_ERROR_INVALID_SIZE;
throw std::logic_error("sceNetCtlGetNatInfo: NAT discovery is not implemented");
HostIpv4Interface host;
if (!FindHostIpv4Interface(host)) return SCE_NET_CTL_ERROR_NOT_CONNECTED;
auto& config = GetStunConfig();
std::uint64_t generation = 0;
const auto now = std::chrono::steady_clock::now();
{
std::lock_guard guard(config.lock);
generation = config.configGeneration;
if (config.hasCachedResult && config.cachedConfigGeneration == generation &&
config.cachedLocalAddr == host.address.s_addr && now < config.cachedUntil) {
*info = config.cachedResult;
return 0;
}
}
const NetCtlNatInfo discovered = DiscoverNatInfo(host.address);
if (discovered.stun_status == NET_CTL_STUN_STATUS_OK) {
std::lock_guard guard(config.lock);
if (config.configGeneration == generation) {
config.cachedConfigGeneration = generation;
config.cachedLocalAddr = host.address.s_addr;
config.cachedUntil = std::chrono::steady_clock::now() + std::chrono::milliseconds(STUN_CACHE_TTL_MS);
config.cachedResult = discovered;
config.hasCachedResult = true;
}
}
*info = discovered;
return 0;
}
int APS5_VABI sceNetCtlGetResult(int event, int* result) {
@@ -0,0 +1,127 @@
#include "prx/libSceNetCtl/libSceNetCtl.hpp"
#include "SceTypes.hpp"
#include <array>
#include <cstdio>
#include <cstdlib>
#include <source_location>
#include <thread>
static void Require(bool value, std::source_location location = std::source_location::current()) {
if (value) return;
std::fprintf(stderr, "NetCtl IPv6 callback check failed at line %u\n", location.line());
std::abort();
}
struct Probe {
int calls = 0;
int event = 0;
std::thread::id thread;
};
static void APS5_VABI Record(int event, void* argument) {
auto& probe = *static_cast<Probe*>(argument);
++probe.calls;
probe.event = event;
probe.thread = std::this_thread::get_id();
}
static int ExpectedEvent(bool ipv6) {
int state = -1;
Require((ipv6 ? sceNetCtlGetStateV6(&state) : sceNetCtlGetState(&state)) == 0);
return state == 3 ? 3 : 1;
}
struct RegisterDuringCheck {
Probe current;
Probe later;
int laterId = -1;
};
static void APS5_VABI RegisterV6(int event, void* argument) {
auto& request = *static_cast<RegisterDuringCheck*>(argument);
Record(event, &request.current);
Require(sceNetCtlRegisterCallbackV6(Record, &request.later, &request.laterId) == 0);
}
struct Reentrant {
Probe probe;
int id = -1;
int unregisterResult = -1;
int checkResult = -1;
};
static void APS5_VABI UnregisterAndReenter(int event, void* argument) {
auto& request = *static_cast<Reentrant*>(argument);
Record(event, &request.probe);
request.unregisterResult = sceNetCtlUnregisterCallbackV6(request.id);
request.checkResult = sceNetCtlCheckCallback();
}
int main() {
Require(sceNetCtlCheckCallback() == 0);
const auto checkingThread = std::this_thread::get_id();
const int ipv6Event = ExpectedEvent(true);
Probe v6;
int v6Id = -1;
Require(sceNetCtlRegisterCallbackV6(Record, &v6, &v6Id) == 0);
Require(v6.calls == 0);
Require(sceNetCtlCheckCallback() == 0);
Require(v6.calls == 1 && v6.event == ipv6Event && v6.thread == checkingThread);
Require(sceNetCtlCheckCallback() == 0 && v6.calls == 1);
Require(sceNetCtlUnregisterCallbackV6(v6Id) == 0);
Require(sceNetCtlCheckCallback() == 0 && v6.calls == 1);
RegisterDuringCheck registration;
int v4Id = -1;
Require(sceNetCtlRegisterCallback(RegisterV6, &registration, &v4Id) == 0);
Require(sceNetCtlRegisterCallbackV6(Record, &v6, &v6Id) == 0);
Require(sceNetCtlCheckCallback() == 0);
Require(registration.current.calls == 1 && registration.current.event == ExpectedEvent(false));
Require(registration.later.calls == 0 && v6.calls == 2);
Require(sceNetCtlCheckCallback() == 0);
Require(registration.later.calls == 1 && registration.later.event == ipv6Event && registration.later.thread == checkingThread);
Require(v6.calls == 2 && registration.current.calls == 1);
Require(sceNetCtlUnregisterCallback(v4Id) == 0);
Require(sceNetCtlUnregisterCallbackV6(v6Id) == 0);
Require(sceNetCtlUnregisterCallbackV6(registration.laterId) == 0);
Reentrant reentrant;
Probe other;
Require(sceNetCtlRegisterCallbackV6(UnregisterAndReenter, &reentrant, &reentrant.id) == 0);
Require(sceNetCtlRegisterCallbackV6(Record, &other, &v6Id) == 0);
Require(sceNetCtlCheckCallback() == 0);
Require(reentrant.probe.calls == 1 && reentrant.probe.event == ipv6Event);
Require(reentrant.unregisterResult == 0 && reentrant.checkResult == 0);
Require(other.calls == 1 && other.event == ipv6Event);
Require(sceNetCtlCheckCallback() == 0 && other.calls == 1);
Require(sceNetCtlUnregisterCallbackV6(v6Id) == 0);
std::array<Probe, 8> probes{};
std::array<int, 8> ids{};
std::array<Probe, 8> ipv4Probes{};
std::array<int, 8> ipv4Ids{};
for (std::size_t index = 0; index < probes.size(); ++index) {
Require(sceNetCtlRegisterCallbackV6(Record, &probes[index], &ids[index]) == 0);
Require(sceNetCtlRegisterCallback(Record, &ipv4Probes[index], &ipv4Ids[index]) == 0);
}
Require(sceNetCtlCheckCallback() == 0);
for (std::size_t index = 0; index < probes.size(); ++index) {
Require(probes[index].calls == 1 && probes[index].event == ipv6Event && probes[index].thread == checkingThread);
Require(ipv4Probes[index].calls == 1 && ipv4Probes[index].event == ExpectedEvent(false) && ipv4Probes[index].thread == checkingThread);
Require(sceNetCtlUnregisterCallbackV6(ids[index]) == 0);
Require(sceNetCtlUnregisterCallback(ipv4Ids[index]) == 0);
}
Probe toolkit, isolatedV6;
int toolkitId = -1;
Require(sceNetCtlRegisterCallbackForNpToolkit(Record, &toolkit, &toolkitId) == 0);
Require(sceNetCtlRegisterCallbackV6(Record, &isolatedV6, &v6Id) == 0);
Require(sceNetCtlCheckCallbackForNpToolkit() == 0);
Require(toolkit.calls == 1 && toolkit.event == ExpectedEvent(false) && isolatedV6.calls == 0);
Require(sceNetCtlCheckCallback() == 0);
Require(toolkit.calls == 1 && isolatedV6.calls == 1 && isolatedV6.event == ipv6Event);
Require(sceNetCtlCheckCallbackForNpToolkit() == 0 && toolkit.calls == 1);
Require(sceNetCtlUnregisterCallbackForNpToolkit(toolkitId) == 0);
Require(sceNetCtlUnregisterCallbackV6(v6Id) == 0);
}
+11 -8
View File
@@ -21,7 +21,9 @@ static constexpr std::uint16_t WAVE_FORMAT_IEEE_FLOAT = 0x3;
static constexpr std::uint16_t WAVE_FORMAT_EXTENSIBLE = 0xfffe;
static constexpr std::uint8_t ATRAC9_GUID[16] = {0xd2, 0x42, 0xe1, 0x47, 0xba, 0x36, 0x8d, 0x4d, 0x88, 0xfc, 0x61, 0x65, 0x4f, 0x8c, 0x83, 0x6c};
static constexpr std::uint8_t PCM_GUID[16] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71};
static constexpr std::uint8_t TYPE_43_GUID[16] = {0x1c, 0xe0, 0x10, 0x9e, 0xcc, 0x8b, 0xd9, 0x47, 0x8f, 0x3b, 0xe3, 0x0b, 0x8b, 0xdc, 0x35, 0x29};
static constexpr std::size_t FMT_ATRAC9_SIZE = 52;
static constexpr std::size_t FMT_EXTENSIBLE_SIZE = 40;
static constexpr std::size_t FMT_GUID_OFFSET = 24;
static constexpr std::size_t FMT_CONFIG_OFFSET = 44;
static constexpr std::size_t FACT_ATRAC9_SIZE = 12;
@@ -404,17 +406,18 @@ static int ParsePcm(const RiffChunks& chunks, std::size_t size, Ngs2WaveformInfo
static int ParseWaveform(const RiffChunks& chunks, std::size_t size, Ngs2WaveformInfo& info) {
if (chunks.formatSize < 2) return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
const std::uint16_t tag = ReadLe16(chunks.format);
if (tag == WAVE_FORMAT_EXTENSIBLE && chunks.formatSize >= FMT_GUID_OFFSET + sizeof(ATRAC9_GUID) &&
std::memcmp(chunks.format + FMT_GUID_OFFSET, ATRAC9_GUID, sizeof(ATRAC9_GUID)) == 0) {
return ParseAtrac9(chunks, info);
}
if (tag == WAVE_FORMAT_EXTENSIBLE && chunks.formatSize >= FMT_GUID_OFFSET + sizeof(PCM_GUID) &&
std::memcmp(chunks.format + FMT_GUID_OFFSET, PCM_GUID, sizeof(PCM_GUID)) == 0) {
return ParsePcm(chunks, size, info, false);
if (tag == WAVE_FORMAT_EXTENSIBLE) {
if (chunks.formatSize < FMT_EXTENSIBLE_SIZE) return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT;
const auto* guid = chunks.format + FMT_GUID_OFFSET;
if (std::memcmp(guid, PCM_GUID, sizeof(PCM_GUID)) == 0) return ParsePcm(chunks, size, info, false);
if (std::memcmp(guid, ATRAC9_GUID, sizeof(ATRAC9_GUID)) == 0) return ParseAtrac9(chunks, info);
if (chunks.formatSize == FMT_ATRAC9_SIZE) throw std::runtime_error("NGS2: parsing an extensible waveform of format size 0x34 without the ATRAC9 GUID is not implemented");
if (std::memcmp(guid, TYPE_43_GUID, sizeof(TYPE_43_GUID)) == 0) throw std::runtime_error("NGS2: parsing waveform type 0x43 is not implemented");
return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT;
}
if (tag == WAVE_FORMAT_PCM) return ParsePcm(chunks, size, info, false);
if (tag == WAVE_FORMAT_IEEE_FLOAT) return ParsePcm(chunks, size, info, true);
throw std::runtime_error("NGS2: parsing waveform format tag " + Ngs2Hex(tag) + " is not implemented");
return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT;
}
#pragma GCC visibility push(default)
@@ -50,6 +50,24 @@ static std::vector<std::uint8_t> PcmFile(std::uint32_t channels, std::uint32_t s
return file;
}
static std::vector<std::uint8_t> ExtensibleFile(std::uint32_t formatSize, const std::uint8_t* guid) {
const auto pcm = PcmFile(1, 44100, 16, 10, 0xfffe);
std::vector<std::uint8_t> format(pcm.begin() + 20, pcm.begin() + 60);
std::memcpy(format.data() + 24, guid, 16);
format.resize(formatSize);
std::vector<std::uint8_t> file;
PutTag(file, "RIFF");
Put32(file, 0);
PutTag(file, "WAVE");
PutTag(file, "fmt ");
Put32(file, formatSize);
file.insert(file.end(), format.begin(), format.end());
file.insert(file.end(), pcm.begin() + 60, pcm.end());
const auto riffSize = static_cast<std::uint32_t>(file.size() - 8);
std::memcpy(file.data() + 4, &riffSize, sizeof(riffSize));
return file;
}
static void TestParsePcm() {
const auto file = PcmFile(2, 44100, 16, 100);
Ngs2WaveformInfo info{};
@@ -90,6 +108,36 @@ static void TestParsePcm() {
Require(sceNgs2ParseWaveformData(noChannels.data(), noChannels.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
}
static void TestParseUnknownFormats() {
const std::uint8_t pcmGuid[16] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71};
const std::uint8_t unknownGuid[16] = {0xaa, 0xbb, 0xcc, 0xdd, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc};
Ngs2WaveformInfo info{};
for (const std::uint32_t tag : {0x0u, 0x2u, 0x11u, 0x55u, 0x165u, 0xffffu}) {
const auto wave = PcmFile(1, 44100, 16, 10, tag);
Require(sceNgs2ParseWaveformData(wave.data(), wave.size(), &info) == SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT);
}
for (const std::uint32_t formatSize : {0x10u, 0x18u, 0x26u}) {
const auto wave = ExtensibleFile(formatSize, pcmGuid);
Require(sceNgs2ParseWaveformData(wave.data(), wave.size(), &info) == SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT);
}
for (const std::uint32_t formatSize : {0x28u, 0x30u, 0x36u}) {
const auto wave = ExtensibleFile(formatSize, unknownGuid);
Require(sceNgs2ParseWaveformData(wave.data(), wave.size(), &info) == SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT);
}
const auto pcm = ExtensibleFile(0x34, pcmGuid);
Require(sceNgs2ParseWaveformData(pcm.data(), pcm.size(), &info) == SCE_NGS2_OK && info.format.waveform_type == SCE_NGS2_WAVEFORM_TYPE_PCM_I16L && info.num_samples == 10);
const auto wave = PcmFile(1, 44100, 16, 10, 0x55);
const char* const guestPath = "/aps5_ngs2_parse_unknown.wav";
const auto path = ResolvePath_nid_no_patch(guestPath);
{
std::ofstream out(path, std::ios::binary);
out.write(reinterpret_cast<const char*>(wave.data()), static_cast<std::streamsize>(wave.size()));
}
Require(sceNgs2ParseWaveformFile(guestPath, 0, &info) == SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT);
std::filesystem::remove(path);
}
static void TestParseFile() {
const auto wave = PcmFile(1, 22050, 16, 64);
const char* const guestPath = "/aps5_ngs2_parse_file.wav";
@@ -187,6 +235,7 @@ static void TestFrameInfoErrors() {
int main() {
TestParsePcm();
TestParseUnknownFormats();
TestParseFile();
TestParseFileBank();
TestFrameInfoPcm();
+32 -48
View File
@@ -8,6 +8,7 @@
#include "prx/libc/include/General.hpp"
static constexpr int SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN = static_cast<int>(0x80552907);
static constexpr int TransactionNotStarted = static_cast<int>(0x80553508);
// Offline PSN: request entry points (API calls, transaction start/readData, factories) fail with
// SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN, the constant shadPS4/fpPS4 document for this code; parameter
@@ -419,7 +420,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_12Leaderboards2V122GetRankingResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_12Leaderboards2V123RecordScoreResponseBodyEEENS4_INS6_9RecordApi26RecordScoreResponseHeadersEEEE5startEPNS2_10LibContextE() {
@@ -427,11 +428,11 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_12Leaderboards2V123RecordScoreResponseBodyEEENS4_INS6_9RecordApi26RecordScoreResponseHeadersEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_12Leaderboards2V127RecordLargeDataResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_12Leaderboards2V130GetBoardDefinitionResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5startEPNS2_10LibContextE() {
@@ -439,7 +440,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_12Leaderboards2V130GetBoardDefinitionResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common19UpStreamTransactionINS2_12IntrusivePtrINS1_12Leaderboards2V127RecordLargeDataResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5startEPNS2_10LibContextEm() {
@@ -1272,7 +1273,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V1
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V130PostPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
bool APS5_VABI _ZNK3sce2Np9CppWebApi14SessionManager2V136UsersPlayerSessionsInvitationForRead14sessionIdIsSetEv(const void* self) {
@@ -1438,7 +1439,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V1
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V152PostPlayerSessionsSessionIdMemberPlayersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_15DefaultResponseENS2_12IntrusivePtrINS2_18ResponseHeaderBaseEEEE5startEPNS2_10LibContextE() {
@@ -1446,7 +1447,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_15DefaultResponse
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_15DefaultResponseENS2_12IntrusivePtrINS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
bool APS5_VABI _ZNK3sce2Np9CppWebApi14SessionManager2V136UsersPlayerSessionsInvitationForRead22receivedTimestampIsSetEv(const void* self) {
@@ -1465,7 +1466,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14Sessi
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V154GetUsersAccountIdPlayerSessionsInvitationsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_15DefaultResponseENS2_12IntrusivePtrINS2_18ResponseHeaderBaseEEEE11getResponseERS4_() {
@@ -1668,8 +1669,7 @@ void* APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_17Ti
}
void APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEEC1Ev(void* self) {
(void)self;
NotImplemented_nid_no_patch(__func__);
std::memset(self, 0, 0x10);
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEE11getResponseERSB_(const void* self, void* response) {
@@ -1681,12 +1681,10 @@ int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS2_6
void APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEED1Ev(void* self) {
(void)self;
NotImplemented_nid_no_patch(__func__);
}
void APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_17TitleCloudStorage2V132GetMultiDataStatusesResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEEC1Ev(void* self) {
(void)self;
NotImplemented_nid_no_patch(__func__);
std::memset(self, 0, 0x10);
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_17TitleCloudStorage2V132GetMultiDataStatusesResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE11getResponseERS8_(const void* self, void* response) {
@@ -1698,13 +1696,11 @@ int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_1
void APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_17TitleCloudStorage2V132GetMultiDataStatusesResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEED1Ev(void* self) {
(void)self;
NotImplemented_nid_no_patch(__func__);
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv(void* self) {
(void)self;
NotImplemented_nid_no_patch(__func__);
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEE5startEPNS2_10LibContextE(void* self, void* context) {
@@ -1716,8 +1712,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_17TitleCloudStorage2V132GetMultiDataStatusesResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv(void* self) {
(void)self;
NotImplemented_nid_no_patch(__func__);
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_17TitleCloudStorage2V132GetMultiDataStatusesResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5startEPNS2_10LibContextE(void* self, void* context) {
@@ -2418,7 +2413,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common13ConstIteratorImED2Ev(void* self) {
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V161PostPlayerSessionsSessionIdJoinableSpecifiedUsersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_14SessionManager2V127ResponsePlayerSessionPlayerEEEEEED1Ev(IntrusivePtr* self) {
@@ -2430,8 +2425,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V129GetPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
return TransactionNotStarted;
}
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V149PostPlayerSessionsSessionIdInvitationsRequestBodyEEC1Ev(IntrusivePtr* self) {
@@ -2439,7 +2433,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V1
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V129GetPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V112JoinableUserEEEE8pushBackERKS8_(IntrusivePtr* self, const void* element) {
@@ -2451,7 +2445,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V1
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V150PostPlayerSessionsSessionIdInvitationsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V113PlayerSessionEEaSERS7_(IntrusivePtr* self, const IntrusivePtr* source) {
@@ -2488,7 +2482,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE28setResponseInformationOptionEP37SceNpWebApi2ResponseInformationOption() {
@@ -2500,7 +2494,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE28setResponseInformationOptionEP37SceNpWebApi2ResponseInformationOption() {
@@ -2512,7 +2506,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_15ProfanityFilter2V223FilterProfanityResponseEEENS4_INS2_18ResponseHeaderBaseEEEE28setResponseInformationOptionEP37SceNpWebApi2ResponseInformationOption() {
@@ -2524,7 +2518,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_15ProfanityFilter2V223FilterProfanityResponseEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEENS4_INS2_18ResponseHeaderBaseEEEE28setResponseInformationOptionEP37SceNpWebApi2ResponseInformationOption() {
@@ -2536,7 +2530,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common18ResponseHeaderBase14getHeaderValueEPKcRNS2_12IntrusivePtrINS2_6StringEEE() {
@@ -2545,8 +2539,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common18ResponseHeaderBase14getHeaderValueEPK
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V150PostPlayerSessionsSessionIdInvitationsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEE3endEv() {
@@ -2781,8 +2774,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V1
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V161PostPlayerSessionsSessionIdJoinableSpecifiedUsersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorImE4sizeEv() {
@@ -2790,8 +2782,7 @@ int APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorImE4sizeEv() {
return 0;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V129GetPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() {
NotImplemented_nid_no_patch(__func__);
std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V129GetPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() {
return 0;
}
@@ -2837,8 +2828,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_14
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V130PostPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEneERKS9_() {
@@ -2896,8 +2886,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14Sessi
self->libContext = libContext;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V161PostPlayerSessionsSessionIdJoinableSpecifiedUsersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() {
NotImplemented_nid_no_patch(__func__);
std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V161PostPlayerSessionsSessionIdJoinableSpecifiedUsersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() {
return 0;
}
@@ -2910,8 +2899,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V1
ReleaseIntrusivePtr(self, 0x00, __func__);
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V130PostPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() {
NotImplemented_nid_no_patch(__func__);
std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V130PostPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() {
return 0;
}
@@ -2948,8 +2936,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V152PostPlayerSessionsSessionIdMemberPlayersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
return TransactionNotStarted;
}
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V149PostPlayerSessionsSessionIdInvitationsRequestBodyEED1Ev(IntrusivePtr* self) {
@@ -2964,8 +2951,7 @@ void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2
return self->object;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V152PostPlayerSessionsSessionIdMemberPlayersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() {
NotImplemented_nid_no_patch(__func__);
std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V152PostPlayerSessionsSessionIdMemberPlayersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() {
return 0;
}
@@ -3236,8 +3222,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common6StringeqEPKc(const CommonString* self,
return other != nullptr && std::strcmp(self->buffer != nullptr ? self->buffer : "", other) == 0;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_15DefaultResponseENS2_12IntrusivePtrINS2_18ResponseHeaderBaseEEEE5getIdEv() {
NotImplemented_nid_no_patch(__func__);
std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_15DefaultResponseENS2_12IntrusivePtrINS2_18ResponseHeaderBaseEEEE5getIdEv() {
return 0;
}
@@ -3247,8 +3232,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorImEEEaSERS6_
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_15DefaultResponseENS2_12IntrusivePtrINS2_18ResponseHeaderBaseEEEE5abortEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
return TransactionNotStarted;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_6StringEE5beginEv() {
@@ -580,7 +580,7 @@ int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_1
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V12ToEED1Ev(IntrusivePtr* self);
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V161PostPlayerSessionsSessionIdJoinableSpecifiedUsersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv();
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorImE4sizeEv();
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V129GetPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv();
std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V129GetPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv();
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEdeEv();
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_14SessionManager2V152PostPlayerSessionsSessionIdMemberPlayersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE11hasResponseEv();
void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V127ResponsePlayerSessionPlayerEEEEixEm(IntrusivePtr* self, unsigned long index);
@@ -603,10 +603,10 @@ void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_14SessionManager2V129GetPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE11getResponseERS8_();
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V113PlayerSessionEEC1Ev(IntrusivePtr* self);
void APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V130RequestPlayerSessionInvitationEEEEC1EPNS2_10LibContextE(IntrusivePtr* self, void* libContext);
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V161PostPlayerSessionsSessionIdJoinableSpecifiedUsersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv();
std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V161PostPlayerSessionsSessionIdJoinableSpecifiedUsersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv();
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorImEdeEv();
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V118LeaderWithOnlineIdEED1Ev(IntrusivePtr* self);
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V130PostPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv();
std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V130PostPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv();
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorImEneERKS4_();
std::uint64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V127ResponsePlayerSessionPlayerEEEE4sizeEv(const IntrusivePtr* self);
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_14SessionManager2V120PlayerSessionForReadEEEEEED1Ev(IntrusivePtr* self);
@@ -617,7 +617,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V149PostPlayerSessionsSessionIdInvitationsRequestBodyEED1Ev(IntrusivePtr* self);
void APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V130RequestPlayerSessionInvitationEEEED1Ev(IntrusivePtr* self);
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEptEv(const IntrusivePtr* self);
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V152PostPlayerSessionsSessionIdMemberPlayersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv();
std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V152PostPlayerSessionsSessionIdMemberPlayersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv();
int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V127ResponsePlayerSessionPlayerEEEE8copyFromERKS9_();
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V150PostPlayerSessionsSessionIdInvitationsResponseBodyEED1Ev(IntrusivePtr* self);
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_14SessionManager2V113PlayerSessionEEEEEEC1Ev(IntrusivePtr* self);
@@ -673,7 +673,7 @@ int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_15DefaultResponseENS
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS2_6StringEEEEptEv(const IntrusivePtr* self);
int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_6StringEE3endEv();
int APS5_VABI _ZN3sce2Np9CppWebApi6Common6StringeqEPKc(const CommonString* self, const char* other);
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_15DefaultResponseENS2_12IntrusivePtrINS2_18ResponseHeaderBaseEEEE5getIdEv();
std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_15DefaultResponseENS2_12IntrusivePtrINS2_18ResponseHeaderBaseEEEE5getIdEv();
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorImEEEaSERS6_(IntrusivePtr* self, const IntrusivePtr* source);
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_15DefaultResponseENS2_12IntrusivePtrINS2_18ResponseHeaderBaseEEEE5abortEv();
int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_6StringEE5beginEv();
Loaded 100 of 305 files, more files were not shown because too many files have changed in this diff. Show more