mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-29 12:15:39 +00:00
merge: main into test/player-review-dialog-mode-1-and-null-result
This commit is contained in:
commit
560a9c0be6
305 files changed
+14620
-1423
No files matched your search
@@ -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
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/vorbis updated: 0657aee69d...c2aa86b05e.
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -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;
|
||||
|
||||
@@ -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},
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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__);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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__);
|
||||
|
||||
@@ -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"
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,3 +1,4 @@
|
||||
#pragma once
|
||||
#include "Invariant.hpp"
|
||||
#include "Version.hpp"
|
||||
#include "Unescape.hpp"
|
||||
@@ -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()
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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()
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -1 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "prx/libSceJsc/include/Api.hpp"
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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);
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
|
||||
@@ -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, ®istration, &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);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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
Reference in new issue
Block a user