Merge main into codex/opus-native-decoder-20261021

This commit is contained in:
Dean Galvin committed 2026-10-23 15:40:07 +13:00
commit c54134ed4b
247 files changed
+10601 -1131

No files matched your search

+6
View File
@@ -55,3 +55,9 @@
[submodule "3rdparty/vgmstream"]
path = 3rdparty/vgmstream
url = https://github.com/vgmstream/vgmstream.git
[submodule "3rdparty/ogg"]
path = 3rdparty/ogg
url = https://github.com/xiph/ogg.git
[submodule "3rdparty/vorbis"]
path = 3rdparty/vorbis
url = https://github.com/xiph/vorbis.git
Vendored Submodule
+1
Submodule 3rdparty/ogg added at be05b13e98.
Vendored Submodule
+1
Submodule 3rdparty/vorbis added at 0657aee69d.
+33 -1
View File
@@ -248,6 +248,15 @@ if(BUILD_TESTING)
set_tests_properties(guest_posix_scalar_io PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_kernel_sys_sdk_version_tests tests/GuestKernelSysSdkVersion.cpp)
target_include_directories(guest_kernel_sys_sdk_version_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_kernel_sys_sdk_version_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_sdk_version_tests)
add_test(NAME guest_kernel_sys_sdk_version COMMAND guest_kernel_sys_sdk_version_tests)
if(WIN32)
set_tests_properties(guest_kernel_sys_sdk_version PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
endif()
add_test_executable(nid_buffer_validation_tests nid/tests/BufferValidation.cpp)
target_include_directories(nid_buffer_validation_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/nid/include)
target_compile_features(nid_buffer_validation_tests PRIVATE cxx_std_20)
@@ -422,6 +431,17 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceHttp>")
endif()
add_test_executable(guest_entitlements_tests tests/GuestEntitlements.cpp)
target_include_directories(guest_entitlements_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_entitlements_tests PRIVATE libSceNpEntitlementAccess libc)
configure_windows_unwind(guest_entitlements_tests)
add_test(NAME guest_entitlements COMMAND guest_entitlements_tests)
add_test(NAME guest_entitlements_missing COMMAND guest_entitlements_tests missing)
if(WIN32)
set_tests_properties(guest_entitlements guest_entitlements_missing PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpEntitlementAccess>")
endif()
add_test_executable(guest_signin_dialog_tests tests/GuestSigninDialog.cpp)
target_include_directories(guest_signin_dialog_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_signin_dialog_tests PRIVATE libSceSigninDialog libc)
@@ -621,6 +641,16 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpCppWebApi>")
endif()
add_test_executable(guest_np_cpp_web_api_signed_out_tests tests/GuestNpCppWebApiSignedOut.cpp)
target_include_directories(guest_np_cpp_web_api_signed_out_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_np_cpp_web_api_signed_out_tests PRIVATE libSceNpCppWebApi libc)
configure_windows_unwind(guest_np_cpp_web_api_signed_out_tests)
add_test(NAME guest_np_cpp_web_api_signed_out COMMAND guest_np_cpp_web_api_signed_out_tests)
if(WIN32)
set_tests_properties(guest_np_cpp_web_api_signed_out PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpCppWebApi>")
endif()
add_test_executable(savedata_core_write_replacement_tests tests/SaveDataWriteReplacement.cpp)
target_include_directories(savedata_core_write_replacement_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(savedata_core_write_replacement_tests PRIVATE libSceSaveData)
@@ -713,9 +743,11 @@ if(BUILD_TESTING)
add_test_executable(guest_vorbis_dec_tests tests/GuestVorbisDec.cpp)
target_include_directories(guest_vorbis_dec_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_vorbis_dec_tests PRIVATE libSceVorbisDec libc)
target_compile_definitions(guest_vorbis_dec_tests PRIVATE OV_EXCLUDE_STATIC_CALLBACKS)
target_link_libraries(guest_vorbis_dec_tests PRIVATE libSceVorbisDec vorbisenc vorbisfile vorbis ogg libc)
configure_windows_unwind(guest_vorbis_dec_tests)
add_test(NAME guest_vorbis_dec COMMAND guest_vorbis_dec_tests)
set_tests_properties(guest_vorbis_dec PROPERTIES TIMEOUT 30)
if(WIN32)
set_tests_properties(guest_vorbis_dec PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceVorbisDec>")
+13
View File
@@ -267,6 +267,19 @@ if(BUILD_TESTING)
endif()
endif()
add_test_executable(agc_instrumentation_query_size_tests EXCLUDE_FROM_ALL tests/InstrumentationQuerySize.cpp)
target_include_directories(agc_instrumentation_query_size_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_instrumentation_query_size_tests PRIVATE libSceAgc libc)
configure_windows_unwind(agc_instrumentation_query_size_tests)
if(BUILD_TESTING)
set_target_properties(agc_instrumentation_query_size_tests PROPERTIES EXCLUDE_FROM_ALL FALSE)
add_test(NAME agc_instrumentation_query_size_tests COMMAND agc_instrumentation_query_size_tests)
if(WIN32)
set_tests_properties(agc_instrumentation_query_size_tests PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
add_test_executable(agc_context_state_size_tests EXCLUDE_FROM_ALL tests/ContextStateSize.cpp)
target_include_directories(agc_context_state_size_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_context_state_size_tests PRIVATE libSceAgc libc)
@@ -22,13 +22,12 @@ std::uint32_t* APS5_VABI sceAgcDcbGetLodStats(CommandBuffer* buf, std::uint8_t c
}
std::uint32_t APS5_VABI sceAgcDcbBeginOcclusionQueryGetSize(std::int32_t queryType) {
NotImplemented_nid_no_patch(__func__);
return 0;
if (queryType == 1) return 0x120u;
return queryType == 0 ? 0x10u : 0u;
}
std::uint32_t APS5_VABI sceAgcDcbEndOcclusionQueryGetSize() {
NotImplemented_nid_no_patch(__func__);
return 0;
return 0x10u;
}
}
@@ -1,9 +1,16 @@
#include "prx/libSceAgc/Misc/include/Debug.hpp"
#include <atomic>
#include <cstdint>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
namespace {
std::atomic<std::uint32_t> shaderInstrumentation{0};
}
extern "C" {
int APS5_VABI sceAgcDebugRaiseException(std::uint32_t exceptionId) {
@@ -11,4 +18,13 @@ int APS5_VABI sceAgcDebugRaiseException(std::uint32_t exceptionId) {
return 0;
}
int APS5_VABI sceAgcSetShaderInstrumentation(std::uint32_t flags) {
shaderInstrumentation.store(flags, std::memory_order_relaxed);
return 0;
}
std::uint32_t APS5_VABI sceAgcGetShaderInstrumentation() {
return shaderInstrumentation.load(std::memory_order_relaxed);
}
}
@@ -53,12 +53,9 @@ std::uint32_t CreateInterpolantValueSplitF16(std::uint32_t psWord, const ShaderS
: CreateInterpolantValue(psWord, gsSemantic);
}
std::uint32_t UpdateInterpolantValue(std::uint32_t psWord, const ShaderSemantic* gsSemantic) {
const std::uint32_t f16 = (psWord >> 20u) & 0x3u;
if (f16 != 0 && f16 != InterpolantF16HiOnly && ((psWord >> 28u) & 0x3u) != ((psWord >> 30u) & 0x3u)) {
throw std::runtime_error(std::string(__func__) + ": DEFAULT_VAL of an f16 input with different low and high defaults is unknown");
}
return CreateInterpolantValueSplitF16(psWord, gsSemantic);
std::uint32_t CreateInterpolantValueFlatDefault(std::uint32_t psWord, const ShaderSemantic* gsSemantic) {
const std::uint32_t value = CreateInterpolantValue(psWord, gsSemantic);
return ((psWord & 0x00700000u) == 0x00400000u) ? (value | 0x00000020u) : value;
}
int CreateInterpolantMapping(const char* fn, ShaderRegister* regs, const Shader* gs, const Shader* ps, CreateInterpolantValueFn createValue, bool fillIdentityTail) {
@@ -132,7 +129,15 @@ int APS5_VABI sceAgcCreateInterpolantMapping_10000(ShaderRegister* regs, const S
}
int APS5_VABI sceAgcUpdateInterpolantMapping_10000(ShaderRegister* regs, const Shader* gs, const Shader* ps) {
return CreateInterpolantMapping(__func__, regs, gs, ps, UpdateInterpolantValue, false);
return CreateInterpolantMapping(__func__, regs, gs, ps, CreateInterpolantValueSplitF16, false);
}
int APS5_VABI sceAgcCreateInterpolantMapping_nid_postfix(ShaderRegister* regs, const Shader* gs, const Shader* ps) {
return CreateInterpolantMapping(__func__, regs, gs, ps, CreateInterpolantValueFlatDefault, true);
}
int APS5_VABI sceAgcUpdateInterpolantMapping_nid_postfix(ShaderRegister* regs, const Shader* gs, const Shader* ps) {
return CreateInterpolantMapping(__func__, regs, gs, ps, CreateInterpolantValueFlatDefault, false);
}
}
-10
View File
@@ -40,14 +40,4 @@ std::uint32_t* APS5_VABI sceAgcCbMemsetExclusive(CommandBuffer* buffer, void* de
return 0;
}
int APS5_VABI sceAgcSetShaderInstrumentation(std::uint32_t flags) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAgcGetShaderInstrumentation() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}
@@ -0,0 +1,48 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include "prx/libc/include/Shutdown.hpp"
#include <cstdint>
#include <cstdio>
#include <stdexcept>
extern "C" {
int APS5_VABI sceAgcSetShaderInstrumentation(std::uint32_t);
std::uint32_t APS5_VABI sceAgcGetShaderInstrumentation();
std::uint32_t APS5_VABI sceAgcDcbBeginOcclusionQueryGetSize(std::int32_t);
std::uint32_t APS5_VABI sceAgcDcbEndOcclusionQueryGetSize();
}
namespace {
void check(bool condition, const char* message) {
if (!condition) throw std::runtime_error(message);
}
void testShaderInstrumentation() {
check(sceAgcGetShaderInstrumentation() == 0, "shader instrumentation starts cleared");
check(sceAgcSetShaderInstrumentation(0x80000005u) == 0, "setting shader instrumentation failed");
check(sceAgcGetShaderInstrumentation() == 0x80000005u, "shader instrumentation flags were not kept");
check(sceAgcSetShaderInstrumentation(0) == 0 && sceAgcGetShaderInstrumentation() == 0, "shader instrumentation was not cleared");
}
void testOcclusionQuerySizes() {
check(sceAgcDcbBeginOcclusionQueryGetSize(0) == 0x10u, "begin occlusion query size for type 0");
check(sceAgcDcbBeginOcclusionQueryGetSize(1) == 0x120u, "begin occlusion query size for type 1");
check(sceAgcDcbBeginOcclusionQueryGetSize(2) == 0, "begin occlusion query size for an unknown type");
check(sceAgcDcbEndOcclusionQueryGetSize() == 0x10u, "end occlusion query size");
}
}
int main() {
try {
testShaderInstrumentation();
testOcclusionQuerySizes();
LibcRunShutdown_nid_postfix();
std::puts("AGC shader instrumentation and occlusion query size tests passed");
return 0;
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());
return 1;
}
}
@@ -14,6 +14,8 @@
extern "C" int APS5_VABI sceAgcCreateInterpolantMapping_0100(ShaderRegister* regs, const Shader* gs, const Shader* ps);
extern "C" int APS5_VABI sceAgcCreateInterpolantMapping_10000(ShaderRegister* regs, const Shader* gs, const Shader* ps);
extern "C" int APS5_VABI sceAgcUpdateInterpolantMapping_10000(ShaderRegister* regs, const Shader* gs, const Shader* ps);
extern "C" int APS5_VABI sceAgcCreateInterpolantMapping_nid_postfix(ShaderRegister* regs, const Shader* gs, const Shader* ps);
extern "C" int APS5_VABI sceAgcUpdateInterpolantMapping_nid_postfix(ShaderRegister* regs, const Shader* gs, const Shader* ps);
namespace {
@@ -158,13 +160,46 @@ void testUpdateMapping() {
check(std::memcmp(create.data(), update.data(), written) == 0, "update differs from the split f16 mapping");
checkIdentity(create, static_cast<std::uint32_t>(inputs.size()), "unused interpolants are not the identity");
for (const std::uint32_t index : {1u, 3u}) {
inputs[index].default_value = 0;
update = filled();
expectFailure([&] { sceAgcUpdateInterpolantMapping_10000(update.data(), &gs, &ps); });
check(std::memcmp(update.data(), untouched.data(), sizeof(update)) == 0, "rejected update wrote registers");
inputs[index].default_value = inputs[index].default_value_hi;
inputs[1].default_value = 0;
inputs[3].default_value = 0;
update = filled();
check(sceAgcUpdateInterpolantMapping_10000(update.data(), &gs, &ps) == 0, "update with split f16 defaults failed");
check(update[1].value == 0x01380007u && update[3].value == 0x03680409u, "f16 DEFAULT_VAL does not come from the low default");
}
void testUnversionedMapping() {
std::array<ShaderSemantic, 5> inputs{
semantic(1, 0, 2), semantic(2, 0, 2), semantic(5, 0, 2), semantic(3, 0, 0), semantic(4, 0, 3)};
inputs[0].is_flat_shaded = 1;
inputs[0].default_value = 1;
inputs[0].default_value_hi = 3;
inputs[1].default_value_hi = 1;
inputs[2].is_flat_shaded = 1;
inputs[2].default_value_hi = 2;
inputs[3].is_flat_shaded = 1;
inputs[3].default_value = 2;
std::array<ShaderSemantic, 4> outputs{semantic(1, 7, 3), semantic(2, 4, 1), semantic(3, 9, 0), semantic(4, 2, 1)};
Shader gs{};
gs.output_semantics = outputs.data();
gs.num_output_semantics = static_cast<std::uint16_t>(outputs.size());
Shader ps{};
ps.input_semantics = inputs.data();
ps.num_input_semantics = static_cast<std::uint32_t>(inputs.size());
const std::array<std::uint32_t, 5> flatDefault{0x02680527u, 0x02380024u, 0x02580020u, 0x00000629u, 0x03180002u};
const std::size_t written = inputs.size() * sizeof(ShaderRegister);
const auto untouched = filled();
auto create = filled();
check(sceAgcCreateInterpolantMapping_nid_postfix(create.data(), &gs, &ps) == 0, "unversioned mapping failed");
for (std::uint32_t i = 0; i < inputs.size(); ++i) {
check(create[i].offset == ShaderRegs::SPI_PS_INPUT_CNTL_0 + i && create[i].value == flatDefault[i], "unversioned mapping changed");
}
checkIdentity(create, static_cast<std::uint32_t>(inputs.size()), "unused interpolants are not the identity");
auto update = filled();
check(sceAgcUpdateInterpolantMapping_nid_postfix(update.data(), &gs, &ps) == 0, "unversioned update failed");
check(std::memcmp(update.data(), create.data(), written) == 0, "unversioned update differs from its mapping");
check(std::memcmp(update.data() + inputs.size(), untouched.data(), sizeof(update) - written) == 0, "unversioned update wrote past its inputs");
}
void testRejections() {
@@ -208,8 +243,9 @@ int main() {
testIdentity();
testMapping();
testUpdateMapping();
testUnversionedMapping();
testRejections();
check(mappings == 7u && links == 5u, "mapping preparation call count changed");
check(mappings == 10u && links == 8u, "mapping preparation call count changed");
LibcRunShutdown_nid_postfix();
std::puts("AGC interpolant mapping tests passed");
return 0;
@@ -433,6 +433,16 @@ if(MINGW OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
endif()
if(BUILD_TESTING)
add_test_executable(agc_driver_unsupported_diagnostics_tests tests/UnsupportedDiagnostics.cpp)
target_include_directories(agc_driver_unsupported_diagnostics_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_driver_unsupported_diagnostics_tests PRIVATE libSceAgcDriver)
configure_windows_unwind(agc_driver_unsupported_diagnostics_tests)
add_test(NAME agc_driver_unsupported_diagnostics COMMAND agc_driver_unsupported_diagnostics_tests)
if(WIN32)
set_tests_properties(agc_driver_unsupported_diagnostics PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceAgcDriver>")
endif()
add_test_executable(agc_driver_draw_program_trace_tests
tests/DrawProgramTrace.cpp
Execution/src/Driver/Draw/DrawProgramTrace.cpp
@@ -459,6 +469,16 @@ if(WIN32)
endif()
if(BUILD_TESTING)
add_test_executable(agc_driver_nop_data_packet_tests tests/NopDataWritePacket.cpp)
target_include_directories(agc_driver_nop_data_packet_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_driver_nop_data_packet_tests PRIVATE libSceAgcDriver)
configure_windows_unwind(agc_driver_nop_data_packet_tests)
add_test(NAME agc_driver_nop_data_packet COMMAND agc_driver_nop_data_packet_tests)
if(WIN32)
set_tests_properties(agc_driver_nop_data_packet PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test_executable(agc_frame_dump_io_tests tests/FrameDumpIo.cpp)
target_include_directories(agc_frame_dump_io_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_frame_dump_io_tests PRIVATE libc)
@@ -250,9 +250,10 @@ DrawVerdict Driver::draw(QueueState& queue, std::span<const std::uint32_t> packe
if (program.binary.stage == Stage::Fragment || roles[i] == Role::GeometryBack) return;
decodeReads[i].clear();
vertexInfos[i] = Graphics::DecodeVertexStageInfo(program.binary.header, program.binary.headerAddress, program.userData, &decodeReads[i], false, 8u - program.firstUserSgpr);
if (vertexInfos[i]) vertexInfos[i]->paClVsOutCntl = PositionExportLayout(program.snapshot.get(), program.codeOffset, program.binary.code, readRegister(queue.context, 0x207), program.tailContinuation.get()) | graphics.layerExports;
if (vertexInfos[i]) {
vertexInfos[i]->paClVsOutCntl = PositionExportLayout(program.snapshot.get(), program.codeOffset, program.binary.code, readRegister(queue.context, 0x207), program.tailContinuation.get()) | graphics.layerExports;
vertexInfos[i]->depthRemapMode = graphics.depthRemap.mode;
Graphics::ApplyPointSize(graphics, *vertexInfos[i]);
}
};
if (!registerKey) {
@@ -2,6 +2,7 @@
#include "prx/libSceAgcDriver/Execution/include/Driver/Diagnostics.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include <algorithm>
#include <bit>
namespace AgcDriver::DriverDetail {
@@ -51,6 +52,10 @@ void Driver::lookupDraw(const Submission& submission, const std::shared_ptr<Vulk
mix(vertex.fetchBufferReg);
mix(vertex.fetchEmbedded);
mix(vertex.paClVsOutCntl);
mix(vertex.pointList);
mix(std::bit_cast<std::uint32_t>(vertex.pointSize));
mix(std::bit_cast<std::uint32_t>(vertex.pointSizeMin));
mix(std::bit_cast<std::uint32_t>(vertex.pointSizeMax));
for (std::uint32_t r = 0; r < vertex.resourcesNum; ++r) {
for (const auto field : vertex.resources[r].fields) mix(field);
const auto& destination = vertex.resourcesDst[r];
@@ -213,7 +213,7 @@ void Driver::traceDrawKeyLookup(const std::array<std::uint64_t, 5>& components,
}
bool Driver::sameVertexInfo(const ShaderRecompiler::ShaderVertexStageInfo& a, const ShaderRecompiler::ShaderVertexStageInfo& b) {
if (a.resourcesNum != b.resourcesNum || a.fetchAttribReg != b.fetchAttribReg || a.fetchBufferReg != b.fetchBufferReg || a.fetchEmbedded != b.fetchEmbedded || a.paClVsOutCntl != b.paClVsOutCntl) return false;
if (a.resourcesNum != b.resourcesNum || a.fetchAttribReg != b.fetchAttribReg || a.fetchBufferReg != b.fetchBufferReg || a.fetchEmbedded != b.fetchEmbedded || a.paClVsOutCntl != b.paClVsOutCntl || a.pointList != b.pointList || a.pointSize != b.pointSize || a.pointSizeMin != b.pointSizeMin || a.pointSizeMax != b.pointSizeMax) return false;
if (a.depthRemapMode != b.depthRemapMode) return false;
for (std::uint32_t i = 0; i < a.resourcesNum && i < a.resources.size(); ++i) {
if (a.resources[i].fields != b.resources[i].fields) return false;
@@ -1440,7 +1440,10 @@ std::vector<PreparedGraphicsStage> PrepareGraphicsStages(const DrawDecode& decod
prepared.push_back({program.snapshot, {program.codeOffset, nullptr}, program.binary.stage, offset, nullptr});
continue;
}
if (vertex) vertex->paClVsOutCntl = PositionExportLayout(program.snapshot.get(), program.codeOffset, program.binary.code, decoded.state.paClVsOutCntl, program.tailContinuation.get()) | decoded.state.layerExports;
if (vertex) {
vertex->paClVsOutCntl = PositionExportLayout(program.snapshot.get(), program.codeOffset, program.binary.code, decoded.state.paClVsOutCntl, program.tailContinuation.get()) | decoded.state.layerExports;
Graphics::ApplyPointSize(decoded.state, *vertex);
}
ShaderRecompiler::RecompileRequest request{program.binary, {wave, program.firstUserSgpr, program.userData, {}, fragment ? std::optional(decoded.pixel) : std::nullopt, vertex, memory, RegisteredFloatMode(*program.snapshot)}, target, {0, 0, offset, room}, ShaderRecompiler::GraphicsCompileContext{program.firstUserSgpr, linked, decoded.state.stages.mesh, decoded.state.stages.tessellation, {}}};
std::vector<std::uint64_t> key;
ShaderRecompiler::BuildPreparedShaderKey(request, key);
@@ -16,8 +16,12 @@ namespace {
std::atomic<bool> requested{false};
std::filesystem::path directory() {
#ifdef _WIN32
const wchar_t* configured = _wgetenv(L"ANYPS5_SCREENSHOTS");
#else
const char* configured = std::getenv("ANYPS5_SCREENSHOTS");
if (configured != nullptr && configured[0] != '\0') return configured;
#endif
if (configured != nullptr && configured[0] != 0) return configured;
const std::unique_ptr<char, decltype(&SDL_free)> basePath(SDL_GetBasePath(), SDL_free);
if (!basePath) throw std::runtime_error(std::string("Screenshot: cannot locate the executable: ") + SDL_GetError());
return std::filesystem::path(reinterpret_cast<const char8_t*>(basePath.get())) / "anyps5-screenshots";
@@ -316,6 +316,7 @@ struct VulkanDevice::State {
bool fragmentShaderBarycentric = false;
bool shaderTerminateInvocation = false;
bool geometryShader = false;
bool largePoints = false;
bool sampleRateShading = false;
bool storageImageMultisample = false;
bool storageImageWriteWithoutFormat = false;
@@ -1718,6 +1719,8 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window, bool compileOnly) :
enabled.sampleRateShading = available.sampleRateShading;
enabled.geometryShader = available.geometryShader;
state->geometryShader = enabled.geometryShader == VK_TRUE;
enabled.largePoints = available.largePoints;
state->largePoints = enabled.largePoints == VK_TRUE;
state->sampleRateShading = enabled.sampleRateShading == VK_TRUE;
if (enabled.sampleRateShading) state->capabilities.push_back(spv::CapabilityInterpolationFunction);
if (enabled.geometryShader) state->capabilities.push_back(spv::CapabilityGeometry);
@@ -3847,6 +3850,7 @@ Graphics::Context VulkanDevice::buildContext() const {
context.imageTableUpdateAfterBind = ImageTableUpdateAfterBind(context);
context.samplerTableUpdateAfterBind = SamplerTableUpdateAfterBind(context);
context.geometryShader = state->geometryShader;
context.largePoints = state->largePoints;
context.sampleRateShading = state->sampleRateShading;
context.fillModeNonSolid = state->fillModeNonSolid;
context.maintenance5 = state->maintenance5;
@@ -239,6 +239,7 @@ struct Context {
bool imageTableUpdateAfterBind = false;
bool samplerTableUpdateAfterBind = false;
bool geometryShader = false;
bool largePoints = false;
bool sampleRateShading = false;
bool fillModeNonSolid = false;
bool maintenance5 = false;
@@ -156,6 +156,10 @@ struct State {
bool customSampleLocations = false;
bool sampleLocationGrid = false;
std::uint32_t paClVsOutCntl = 0;
bool vertexPoints = false;
float pointSize = 1.0f;
float pointSizeMin = 0.0f;
float pointSizeMax = 0.0f;
std::array<std::uint16_t, 4> quadSampleMask{0xffffu, 0xffffu, 0xffffu, 0xffffu};
bool pixelEarlyZ = false;
};
@@ -165,6 +169,7 @@ struct State {
inline constexpr std::uint32_t PositionExportLayoutBits = 0xffffu | (1u << 21u) | (1u << 22u) | (1u << 23u);
ShaderStages DecodeShaderStages(const QueueState& queue, bool vertexWorkgroup = false);
void ApplyPointSize(const State& state, ShaderRecompiler::ShaderVertexStageInfo& vertex);
State DecodeState(const QueueState& queue, bool vertexWorkgroup = false);
void ApplyDepthRemap(const Context& context, State& state);
void NormalizeUnusedViewportDepth(State& state, const ShaderRecompiler::ShaderPixelStageInfo& pixel);
@@ -671,6 +671,7 @@ bool ValidationKey(const Context& context, std::span<const CompiledShader> shade
add(context.viewportIndexLayer);
add(context.shaderTerminateInvocation);
add(state.paClVsOutCntl);
add(state.vertexPoints);
add(state.stages.mesh.has_value());
if (state.stages.mesh) {
const auto& mesh = *state.stages.mesh;
@@ -5,6 +5,8 @@
#include "prx/libSceAgcDriver/Graphics/include/TextureFormat.hpp"
#include "RdnaDecoder/RdnaDescriptorFormat.hpp"
#include <algorithm>
#include <array>
#include <optional>
#include <stdexcept>
#include <string>
@@ -50,6 +52,45 @@ TextureDimension resolveDimension(std::uint32_t raw) {
}
}
bool ZeroTail(std::span<const std::uint32_t> words) {
return words[4] == 0u && words[5] == 0u && words[6] == 0u && words[7] == 0u;
}
bool SameLevels(const TileMipLayout& a, const TileMipLayout& b) {
return a.tiledOffset == b.tiledOffset && a.tiledSize == b.tiledSize && a.width == b.width && a.height == b.height && a.blocksPerRow == b.blocksPerRow && a.pitchBytes == b.pitchBytes && a.tail == b.tail && a.tailX == b.tailX && a.tailY == b.tailY;
}
std::optional<std::array<std::uint32_t, 8>> R128ResidentLevelWords(std::span<const std::uint32_t> words) {
const auto baseLevel = (words[3] >> 12u) & 0xfu;
const auto lastLevel = (words[3] >> 16u) & 0xfu;
const auto typeRaw = (words[3] >> 28u) & 0xfu;
if (baseLevel == 0u || baseLevel > lastLevel || (typeRaw != 8u && typeRaw != 9u)) return std::nullopt;
const std::array<std::uint32_t, 8> chainWords{words[0], words[1], words[2], words[3], 0u, lastLevel << 4u, 0u, 0u};
const auto width = (((words[1] >> 30u) & 0x3u) | ((words[2] & 0xfffu) << 2u)) + 1u;
const auto height = ((words[2] >> 14u) & 0x3fffu) + 1u;
const auto minLod = (words[1] >> 8u) & 0xfffu;
const auto widthMinus1 = std::max(width >> baseLevel, 1u) - 1u;
const auto heightMinus1 = std::max(height >> baseLevel, 1u) - 1u;
const auto rebasedMinLod = minLod > baseLevel * 256u ? minLod - baseLevel * 256u : 0u;
const auto levels = lastLevel - baseLevel;
auto rebased = chainWords;
rebased[1] = (words[1] & ~((0xfffu << 8u) | (0x3u << 30u))) | (rebasedMinLod << 8u) | ((widthMinus1 & 0x3u) << 30u);
rebased[2] = (words[2] & ~(0xfffu | (0x3fffu << 14u))) | ((widthMinus1 >> 2u) & 0xfffu) | ((heightMinus1 & 0x3fffu) << 14u);
rebased[3] = (words[3] & ~(0xffu << 12u)) | (levels << 16u);
rebased[5] = levels << 4u;
try {
const auto chain = DescribeSurface(DecodeTextureResource(chainWords));
const auto view = DescribeSurface(DecodeTextureResource(rebased));
if (chain.mips.size() != lastLevel + 1u || view.mips.size() != levels + 1u || chain.layers != view.layers) return std::nullopt;
for (std::uint32_t level = baseLevel; level <= lastLevel; ++level) {
if (!SameLevels(chain.mips[level], view.mips[level - baseLevel])) return std::nullopt;
}
} catch (const std::exception&) {
return std::nullopt;
}
return rebased;
}
}
bool IsNullTextureResource(std::span<const std::uint32_t> words) {
@@ -238,7 +279,11 @@ GuestTextureResource DecodeTextureResource(std::span<const std::uint32_t> words)
result.minLod = minLod;
result.samples = samples;
if (baseLevel > maxMip) {
Require(LevelsFitAllocation(result, lastLevel + 1u), "guest texture descriptor starts past the surface's last mip level at levels that would move the surface's own" + describe());
const bool fits = LevelsFitAllocation(result, lastLevel + 1u);
if (!fits && ZeroTail(words)) {
if (const auto resident = R128ResidentLevelWords(words)) return DecodeTextureResource(*resident);
}
Require(fits, "guest texture descriptor starts past the surface's last mip level at levels that would move the surface's own" + describe());
result.mipCount = lastLevel + 1u;
}
return result;
@@ -347,6 +347,7 @@ Pipeline::Pipeline(const Context& context, const State& state, const VertexInput
Require(!state.negativeOneToOne || context.depthClipControl, "negative-one-to-one depth clipping requires VK_EXT_depth_clip_control with depthClipControl enabled");
Require(!state.perSampleShading || context.sampleRateShading, "per-sample pixel shading requires the sampleRateShading device feature");
Require(!state.mergedCoverage || (context.mergedCoverageSampleCounts & state.samples) != 0, "an overrasterized draw (a multisampled rasterizer into one-sample color targets) requires VK_NV_framebuffer_mixed_samples with merged coverage reduction");
Require(!(state.vertexPoints || (state.stages.mesh.has_value() && state.stages.mesh->outputPrimitiveSize == 1u)) || context.largePoints || (state.pointSize == 1.0f && ((state.paClVsOutCntl & (1u << 16u)) == 0 || (state.pointSizeMin == 1.0f && state.pointSizeMax == 1.0f))), "points of a size other than one pixel require the largePoints device feature");
Require(!state.truncatedCoverage || (context.truncatedCoverageSampleCounts & state.samples) != 0, "one-sample color targets beside a multisampled depth target require VK_AMD_mixed_attachment_samples or VK_NV_framebuffer_mixed_samples with truncated coverage reduction");
Require(state.conservativeRasterization == VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT || context.conservativeRasterization, "conservative rasterization requires VK_EXT_conservative_rasterization with at most 1/256 pixel of overestimation and degenerate triangles rasterized");
ValidateProvokingVertex(context, state, shaders);
@@ -35,7 +35,7 @@ static_assert(sizeof(VkStencilOpState) == 28, "VkStencilOpState changed: update
static_assert(sizeof(VkVertexInputBindingDescription) == 12, "VkVertexInputBindingDescription changed: update the recipe codec");
static_assert(sizeof(VkVertexInputAttributeDescription) == 16, "VkVertexInputAttributeDescription changed: update the recipe codec");
#if defined(__linux__) && defined(__x86_64__) && defined(__GLIBCXX__)
static_assert(sizeof(State) == 808, "State changed: update CanonicalPipelineState and the recipe codec");
static_assert(sizeof(State) == 824, "State changed: update CanonicalPipelineState and the recipe codec");
static_assert(sizeof(ShaderStages) == 112, "ShaderStages changed: update the recipe codec");
static_assert(sizeof(ShaderRecompiler::MeshConfiguration) == 40, "MeshConfiguration changed: update the recipe codec");
static_assert(sizeof(ShaderRecompiler::TessellationConfiguration) == 40, "TessellationConfiguration changed: update the recipe codec");
@@ -44,7 +44,7 @@ static_assert(sizeof(ShaderRecompiler::TessellationConfiguration) == 40, "Tessel
constexpr std::uint32_t FileMagic = 0x4c4a5041u;
// Bumped when the recipe codec, CanonicalPipelineState or the store key change in a way the
// layout guards above do not catch (a field renumbered or reinterpreted at the same size).
constexpr std::uint32_t FileFormat = 7u;
constexpr std::uint32_t FileFormat = 8u;
constexpr std::uint32_t ModuleRecord = 1u;
constexpr std::uint32_t GraphicsRecord = 2u;
constexpr std::uint32_t ComputeRecord = 3u;
@@ -220,6 +220,10 @@ void encodeState(Writer& writer, const State& state) {
writer.Bool(state.truncatedCoverage);
for (const auto mask : state.quadSampleMask) writer.Raw(mask);
writer.U32(state.depthRemap.mode);
writer.Bool(state.vertexPoints);
writer.Raw(state.pointSize);
writer.Raw(state.pointSizeMin);
writer.Raw(state.pointSizeMax);
}
void decodeState(Reader& reader, State& state) {
@@ -282,6 +286,10 @@ void decodeState(Reader& reader, State& state) {
state.truncatedCoverage = reader.Bool();
for (auto& mask : state.quadSampleMask) mask = reader.Raw<std::uint16_t>();
state.depthRemap.mode = reader.U32();
state.vertexPoints = reader.Bool();
state.pointSize = reader.Raw<float>();
state.pointSizeMin = reader.Raw<float>();
state.pointSizeMax = reader.Raw<float>();
}
void encodeRecipe(Writer& writer, const GraphicsPipelineRecipe& recipe) {
@@ -1056,6 +1064,10 @@ State CanonicalPipelineState(const State& state) {
canonical.truncatedCoverage = state.truncatedCoverage;
canonical.quadSampleMask = state.quadSampleMask;
canonical.depthRemap.mode = state.depthRemap.mode;
canonical.vertexPoints = state.vertexPoints;
canonical.pointSize = state.pointSize;
canonical.pointSizeMin = state.pointSizeMin;
canonical.pointSizeMax = state.pointSizeMax;
return canonical;
}
@@ -53,6 +53,8 @@ struct Module {
bool layerOutput = false;
bool meshCapability = false;
std::uint32_t primitiveInputs = 0;
bool pointSizeOutput = false;
bool pointSize = false;
std::map<std::uint32_t, std::vector<std::uint32_t>> modes;
const std::vector<std::uint32_t>& Type(std::uint32_t id) const {
@@ -138,6 +140,10 @@ struct Module {
Require(input && (clipInput || (value == spv::BuiltInPosition && signature == "f32x4") || (value == spv::BuiltInTessCoord && stage == Stage::TessellationEvaluation && signature == "f32x3") || ((value == spv::BuiltInInvocationId || value == spv::BuiltInPrimitiveId || value == spv::BuiltInPatchVertices) && (signature == "i32" || signature == "u32"))), "unsupported tessellation built-in");
return;
}
if (!input && value == spv::BuiltInPointSize && pointSizeOutput && signature == "f32" && !pointSize) {
pointSize = true;
return;
}
if (!input && value == spv::BuiltInPrimitivePointIndicesEXT) primitiveIndices = 1u;
if (!input && value == spv::BuiltInPrimitiveLineIndicesEXT) primitiveIndices = 2u;
if (!input && value == spv::BuiltInPrimitiveTriangleIndicesEXT) primitiveIndices = 3u;
@@ -149,6 +155,10 @@ struct Module {
Require((value == spv::BuiltInVertexIndex || value == spv::BuiltInInstanceIndex || value == spv::BuiltInBaseInstance) && signature == "i32", "unsupported vertex built-in input");
} else if (vertex && storage == spv::StorageClassOutput) {
if (value == spv::BuiltInLayer && layerOutput && (signature == "u32" || signature == "i32")) return;
if (value == spv::BuiltInPointSize && pointSizeOutput && signature == "f32" && !pointSize) {
pointSize = true;
return;
}
Require(value == spv::BuiltInPosition && signature == "f32x4" && !position, "unsupported or duplicate vertex built-in output");
position = true;
} else {
@@ -186,6 +196,7 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi
Require(words[4] == 0, "unsupported SPIR-V schema: " + std::to_string(words[4]));
Module module;
module.pointSizeOutput = (state.vertexPoints && stage == Stage::Vertex) || (mesh && state.stages.mesh.has_value() && state.stages.mesh->outputPrimitiveSize == 1u);
std::uint32_t entries = 0;
std::uint32_t memoryModels = 0;
std::uint32_t entryPoint = 0;
@@ -190,6 +190,37 @@ std::uint32_t inactiveVertexPointSize(const QueueState& queue) {
}
}
bool pointOutput(const QueueState& queue) {
const auto stages = find(queue.context, 0x2d5);
const auto primitive = find(queue.userConfig, 0x242, RegisterBank::UserConfig);
if (stages == queue.context.end()) return false;
if ((stages->second & 0x24u) == 0) return primitive != queue.userConfig.end() && primitive->second == 1u;
const auto output = find(queue.context, 0x29b);
return (stages->second & 0x24u) == 0x20u && output != queue.context.end() && output->second == 0u;
}
void decodePointSize(const QueueState& queue, State& result) {
const auto& cx = queue.context;
const auto size = read(cx, 0x280);
const auto limits = read(cx, 0x281);
const bool fromVertex = (result.paClVsOutCntl & (1u << 16u)) != 0;
Require(!fromVertex || (result.paClVsOutCntl & (1u << 21u)) != 0, "USE_VTX_POINT_SIZE without VS_OUT_MISC_VEC_ENA is unsupported");
Require(fromVertex || (size & 0xffffu) == size >> 16u, "points whose PA_SU_POINT_SIZE width and height differ are unsupported");
Require(fromVertex || (size & 0xffffu) != 0, "points of size 0 (PA_SU_POINT_SIZE 0) are unsupported");
Require((limits & 0xffffu) <= limits >> 16u, "PA_SU_POINT_MINMAX with a minimum above its maximum is unsupported");
Require(!fromVertex || (limits >> 16u) != 0, "a vertex point size clamped to 0 (PA_SU_POINT_MINMAX maximum 0) is unsupported");
result.pointSize = static_cast<float>(size & 0xffffu) / 8.0f;
result.pointSizeMin = static_cast<float>(limits & 0xffffu) / 8.0f;
result.pointSizeMax = static_cast<float>(limits >> 16u) / 8.0f;
const auto interpolation = find(cx, 0x1b5);
const auto inputs = find(cx, 0x1b6);
if (interpolation == cx.end() || (interpolation->second & 2u) == 0 || inputs == cx.end()) return;
for (std::uint32_t input = 0; input < (inputs->second & 0x3fu); ++input) {
const auto control = find(cx, 0x191 + input);
Require(control == cx.end() || (control->second & 0x820000u) == 0, "point sprite texture coordinates (SPI_PS_INPUT_CNTL PT_SPRITE_TEX) are not implemented");
}
}
constexpr std::uint32_t DepthControlMask = ~0x007007f0u;
// EXEC_ON_HIER_FAIL / EXEC_ON_NOOP / EXEC_IF_OVERLAPPED (bits 9, 10, 17) only force the pixel shader
// to run, which it always does here.
@@ -1082,6 +1113,15 @@ std::array<std::uint8_t, 32> PixelSampleLocations(const State& state, const Shad
return locations;
}
void ApplyPointSize(const State& state, ShaderRecompiler::ShaderVertexStageInfo& vertex) {
if (!state.vertexPoints && !(state.stages.mesh.has_value() && state.stages.mesh->outputPrimitiveSize == 1u)) return;
vertex.paClVsOutCntl |= state.paClVsOutCntl & (1u << 16u);
vertex.pointList = true;
vertex.pointSize = state.pointSize;
vertex.pointSizeMin = state.pointSizeMin;
vertex.pointSizeMax = state.pointSizeMax;
}
ShaderStages DecodeShaderStages(const QueueState& queue, bool vertexWorkgroup) {
const auto value = read(queue.context, 0x2d5);
const auto validate = [&](bool condition, const char* reason) {
@@ -1256,7 +1296,11 @@ State DecodeState(const QueueState& queue, bool vertexWorkgroup) {
const auto primitive = read(queue.userConfig, 0x242, RegisterBank::UserConfig);
APS5_LOG_OUT_DEBUG("DecodeState primitive=%u path=%u vertexWave=%u", primitive, static_cast<unsigned>(result.stages.path), result.stages.vertexWaveSize);
switch (primitive) {
case 1: Require(result.stages.mesh.has_value(), "point-list vertex rendering requires point-size output support"); result.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST; break;
case 1:
Require(result.stages.mesh.has_value() || result.stages.path == ShaderPath::Vertex, "point lists are only drawn through the vertex and geometry paths");
result.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
result.vertexPoints = result.stages.path == ShaderPath::Vertex;
break;
case 2: result.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST; break;
case 3: result.topology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; break;
case 7:
@@ -1281,9 +1325,10 @@ State DecodeState(const QueueState& queue, bool vertexWorkgroup) {
}
APS5_LOG_OUT_DEBUG("Topology=%u", static_cast<unsigned>(result.topology));
result.primitiveRestart = read(queue.userConfig, 0x24b, RegisterBank::UserConfig) != 0 && !result.rectList && !result.quadList && result.topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
zero(cx, 0x207, ~(LayerExports | ClipCullExports | inactiveVertexPointSize(queue)), "layer, viewport or auxiliary vertex exports");
zero(cx, 0x207, ~(LayerExports | ClipCullExports | ((result.vertexPoints || (result.stages.mesh.has_value() && result.stages.mesh->outputPrimitiveSize == 1u)) ? 1u << 16u : inactiveVertexPointSize(queue))), "layer, viewport or auxiliary vertex exports");
Require(std::popcount(read(cx, 0x207) & 0xffffu) <= 8, "more than eight clip and cull distances are unsupported");
result.paClVsOutCntl = read(cx, 0x207);
if (result.vertexPoints || (result.stages.mesh.has_value() && result.stages.mesh->outputPrimitiveSize == 1u)) decodePointSize(queue, result);
result.samples = decodeSamples(cx);
Require(coveredCentroidSupported(queue), "COVERED_CENTROID_IS_CENTER with active or unspecified centroid inputs is not implemented");
decodeSampleLocations(cx, result);
@@ -2053,7 +2098,7 @@ std::string DrawRejection(const QueueState& queue, bool indexed) {
if (value(queue.userConfig, 0x242, primitive) && (primitive & 0x3fu) != 1 && (primitive & 0x3fu) != 2 && (primitive & 0x3fu) != 3 && (primitive & 0x3fu) != 4 && (primitive & 0x3fu) != 5 && (primitive & 0x3fu) != 6 && (primitive & 0x3fu) != 21) return "AGC graphics: primitive restart is only supported for point, line and triangle topologies";
if (value(cx, 0x103, resetIndex) && (resetIndex & 0xffffu) != 0xffffu) return "AGC graphics: primitive restart index other than all ones is unsupported";
}
if (auto reason = nonzero(cx, 0x207, ~(LayerExports | ClipCullExports | inactiveVertexPointSize(queue)), "layer, viewport or auxiliary vertex exports"); !reason.empty()) return reason;
if (auto reason = nonzero(cx, 0x207, ~(LayerExports | ClipCullExports | (pointOutput(queue) ? 1u << 16u : inactiveVertexPointSize(queue))), "layer, viewport or auxiliary vertex exports"); !reason.empty()) return reason;
if (value(cx, 0x207, word) && std::popcount(word & 0xffffu) > 8) return require(false, "more than eight clip and cull distances are unsupported");
if (value(cx, 0x200, word)) {
const auto renderControl = find(cx, 0x000);
@@ -5,6 +5,12 @@
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
namespace {
constexpr int CAPTURE_UNAVAILABLE = static_cast<int>(0x8A6C1000);
}
extern "C" {
bool APS5_VABI sceAgcDriverIsCaptureInProgress(void) {
@@ -21,18 +27,15 @@ bool APS5_VABI sceAgcDriverIsSubmitValidationEnabled(void) {
int APS5_VABI sceAgcDriverRequestCaptureStart(const char* path) {
(void)path;
NotImplemented_nid_no_patch(__func__);
return 0;
return CAPTURE_UNAVAILABLE;
}
int APS5_VABI sceAgcDriverRequestCaptureStop() {
NotImplemented_nid_no_patch(__func__);
return 0;
return CAPTURE_UNAVAILABLE;
}
int APS5_VABI sceAgcDriverTriggerCapture() {
NotImplemented_nid_no_patch(__func__);
return 0;
return CAPTURE_UNAVAILABLE;
}
}
+14 -10
View File
@@ -57,6 +57,17 @@ int APS5_VABI sceAgcDriverGetHsOffchipParam(uint16_t* first, uint16_t* second) {
return 0;
}
int APS5_VABI sceAgcDriverNopDataWritePacket(void* destination, uint32_t address, const void* data, uint32_t bytes) {
const auto alignedBytes = (static_cast<std::uint64_t>(bytes) + 3) & ~std::uint64_t{3};
const auto totalBytes = alignedBytes + 8;
std::memset(destination, 0, static_cast<std::size_t>(totalBytes));
const auto header = (static_cast<std::uint32_t>(totalBytes) << 14) + 0x3ffe0000U | 0xc0001000U;
std::memcpy(destination, &header, sizeof(header));
std::memcpy(static_cast<std::byte*>(destination) + 4, &address, sizeof(address));
if (data != nullptr && bytes != 0) std::memcpy(static_cast<std::byte*>(destination) + 8, data, bytes);
return data == nullptr || bytes == 0 ? 2 : static_cast<int>(totalBytes / 4);
}
int APS5_VABI sceAgcDriverSetValidationDisabled() {
return DEBUG_UNAVAILABLE;
@@ -154,9 +165,8 @@ int APS5_VABI sceAgcDriverAcquireHDRScopesACQ() {
return 0;
}
int APS5_VABI sceAgcDriverGetCoredumpAddress() {
NotImplemented_nid_no_patch(__func__);
return 0;
void* APS5_VABI sceAgcDriverGetCoredumpAddress() {
return nullptr;
}
int APS5_VABI sceAgcDriverHp3dLockEnd() {
@@ -174,19 +184,13 @@ int APS5_VABI sceAgcDriverIsEsvmProcess() {
return 0;
}
int APS5_VABI sceAgcDriverNopDataWritePacket() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAgcDriverReleaseHDRScopesACQ() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAgcDriverSetTargetRingForDiag() {
NotImplemented_nid_no_patch(__func__);
return 0;
return DRIVER_FEATURE_UNAVAILABLE;
}
int APS5_VABI sceAgcDriverSetValueToSharedGds() {
@@ -163,9 +163,15 @@ void inactiveVertexPointSizeTests() {
}
throw std::runtime_error("potentially active point size passed the state decoder");
};
for (const auto primitive : {1u, 9u}) {
{
auto queue = pointSizeState();
queue.userConfig[0x242] = primitive;
queue.userConfig[0x242] = 9;
rejected(queue);
}
{
auto queue = pointSizeState();
queue.userConfig[0x242] = 1;
queue.context[0x2d5] = 0x2020;
rejected(queue);
}
for (const auto stages : {0x2004u, 0x2020u}) {
@@ -222,6 +228,73 @@ void PolygonPrimitiveTests() {
Require(indexedState.topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN && indexedState.primitiveRestart, "indexed polygon decoding did not keep primitive restart");
}
void vertexPointListTests() {
const auto pointState = [](std::uint32_t control, std::uint32_t size, std::uint32_t limits) {
auto queue = makeState();
queue.userConfig[0x242] = 1;
queue.context[0x1b3] = queue.context[0x1b4] = 2;
queue.context[0x207] = control;
queue.context[0x280] = size;
queue.context[0x281] = limits;
return queue;
};
{
const auto queue = pointState(0x01210000u, 0x00080008u, 0x00080008u);
const auto rejection = AgcDriver::Graphics::DrawRejection(queue, false);
Require(rejection.empty(), "a vertex-path point list with a vertex point size failed the draw precheck: " + rejection);
const auto state = AgcDriver::Graphics::DecodeState(queue);
Require(state.topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST && state.paClVsOutCntl == 0x01210000u, "a vertex-path point list with a vertex point size did not decode as a point list");
Require(AgcDriver::Graphics::RasterPrimitiveOf(state) == ShaderRecompiler::RasterPrimitive::Point, "a vertex-path point list is not rasterized as points");
Require(state.vertexPoints && state.pointSize == 1.0f && state.pointSizeMin == 1.0f && state.pointSizeMax == 1.0f, "PA_SU_POINT_SIZE and PA_SU_POINT_MINMAX of 8 did not decode as one-pixel points");
ShaderRecompiler::ShaderVertexStageInfo vertex{};
vertex.paClVsOutCntl = 1u << 21u;
AgcDriver::Graphics::ApplyPointSize(state, vertex);
Require(vertex.pointList && vertex.paClVsOutCntl == ((1u << 16u) | (1u << 21u)) && vertex.pointSize == 1.0f && vertex.pointSizeMin == 1.0f && vertex.pointSizeMax == 1.0f, "the vertex stage did not receive the vertex point size");
}
{
const auto state = AgcDriver::Graphics::DecodeState(pointState(0, 0x00200020u, 0x00100004u));
Require(state.vertexPoints && state.pointSize == 4.0f && state.pointSizeMin == 0.5f && state.pointSizeMax == 2.0f, "a register point size did not decode as diameters");
ShaderRecompiler::ShaderVertexStageInfo vertex{};
vertex.paClVsOutCntl = 1u << 21u;
AgcDriver::Graphics::ApplyPointSize(state, vertex);
Require(vertex.pointList && vertex.paClVsOutCntl == (1u << 21u) && vertex.pointSize == 4.0f, "a register point size turned on the vertex point size");
}
{
const auto state = AgcDriver::Graphics::DecodeState(pointState(0x01210000u, 0x00200010u, 0x00400000u));
Require(state.pointSizeMin == 0.0f && state.pointSizeMax == 8.0f, "a vertex point size refused a register size whose width and height differ");
}
{
auto queue = makeState();
queue.context[0x1b3] = queue.context[0x1b4] = 2;
queue.context[0x207] = 0x01210000u;
const auto state = AgcDriver::Graphics::DecodeState(queue);
Require(!state.vertexPoints, "a triangle draw decoded as vertex points");
ShaderRecompiler::ShaderVertexStageInfo vertex{};
AgcDriver::Graphics::ApplyPointSize(state, vertex);
Require(!vertex.pointList && vertex.paClVsOutCntl == 0, "a triangle draw received a point size");
}
expectFailure([&] { AgcDriver::Graphics::DecodeState(pointState(0, 0x00200010u, 0x00080008u)); }, "points whose PA_SU_POINT_SIZE width and height differ are unsupported");
expectFailure([&] { AgcDriver::Graphics::DecodeState(pointState(0, 0x00080008u, 0x00080010u)); }, "PA_SU_POINT_MINMAX with a minimum above its maximum is unsupported");
expectFailure([&] { AgcDriver::Graphics::DecodeState(pointState(1u << 16u, 0x00080008u, 0x00080008u)); }, "USE_VTX_POINT_SIZE without VS_OUT_MISC_VEC_ENA is unsupported");
expectFailure([&] { AgcDriver::Graphics::DecodeState(pointState(0, 0, 0x00080008u)); }, "points of size 0 (PA_SU_POINT_SIZE 0) are unsupported");
expectFailure([&] { AgcDriver::Graphics::DecodeState(pointState(0x01210000u, 0x00080008u, 0)); }, "a vertex point size clamped to 0 (PA_SU_POINT_MINMAX maximum 0) is unsupported");
{
const auto state = AgcDriver::Graphics::DecodeState(pointState(0x01210000u, 0, 0x00200000u));
Require(state.pointSize == 0.0f && state.pointSizeMin == 0.0f && state.pointSizeMax == 4.0f, "a vertex point size refused a zero register size or a zero clamp minimum");
static_cast<void>(AgcDriver::Graphics::DecodeState(pointState(0, 0x00080008u, 0)));
}
for (const auto sprite : {1u << 17u, 1u << 23u}) {
auto queue = pointState(0x01210000u, 0x00080008u, 0x00080008u);
queue.context[0x1b5] = 0x486bu;
queue.context[0x1b6] = 2;
queue.context[0x191] = 0;
queue.context[0x192] = sprite;
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "point sprite texture coordinates (SPI_PS_INPUT_CNTL PT_SPRITE_TEX) are not implemented");
queue.context[0x1b5] = 0x4869u;
Require(AgcDriver::Graphics::DecodeState(queue).vertexPoints, "PT_SPRITE_TEX was refused with point sprites off");
}
}
void retainedPixelStageTests() {
using namespace AgcDriver::Graphics;
auto queue = makeState();
@@ -1080,11 +1153,30 @@ void ShaderStageTests() {
lines.context[0x29b] = 1;
Require(AgcDriver::Graphics::DecodeState(lines).stages.mesh->provokingVertex == 1, "a last-vertex geometry draw does not take its flat inputs and layer from the second vertex of each output line");
lines.context[0x29b] = 0;
expectFailure([&] { AgcDriver::Graphics::DecodeState(lines); }, "missing register in context bank at DWORD 0x280");
lines.context[0x280] = 0x00080008u;
expectFailure([&] { AgcDriver::Graphics::DecodeState(lines); }, "missing register in context bank at DWORD 0x281");
lines.context[0x281] = 0xffff0000u;
state = AgcDriver::Graphics::DecodeState(lines);
mesh = *state.stages.mesh;
Require(mesh.outputPrimitiveSize == 1 && mesh.maxPrimitives == 255 && AgcDriver::Graphics::RasterPrimitiveOf(state) == ShaderRecompiler::RasterPrimitive::Point, "a point-list subgroup did not decode as points");
Require(mesh.provokingVertex == 0, "a point draw has provoking vertex 0");
Require(state.pointSize == 1.0f && state.pointSizeMin == 0.0f && state.pointSizeMax == 8191.875f, "geometry points did not decode the queue's default point size and limits");
lines.context[0x205] = raster;
auto points = lines;
points.userConfig[0x242] = 6;
points.userConfig[0x25b] = 0x5c2e;
points.context[0x1ff] = 0x8a;
points.context[0x2ce] = 3;
points.context[0x29b] = 0;
points.context[0x280] = 0x00180018u;
points.context[0x281] = 0x00400008u;
const auto pointState = AgcDriver::Graphics::DecodeState(points);
const auto& pointMesh = *pointState.stages.mesh;
ShaderRecompiler::ShaderVertexStageInfo pointInfo{};
AgcDriver::Graphics::ApplyPointSize(pointState, pointInfo);
Require(pointInfo.pointList && pointInfo.pointSize == 3.0f && pointInfo.pointSizeMin == 1.0f && pointInfo.pointSizeMax == 8.0f, "geometry points lost their register sizes");
Require(pointMesh.outputPrimitiveSize == 1u && pointMesh.primitivesPerGroup == 44u && pointMesh.verticesPerGroup == 46u && pointMesh.maxVertices == 138u && pointMesh.maxPrimitives == 138u && pointMesh.threadsPerGroup == 192u && AgcDriver::Graphics::RasterPrimitiveOf(pointState) == ShaderRecompiler::RasterPrimitive::Point, "triangle-strip input with point output did not decode the reported subgroup");
lines.context[0x29b] = 1;
lines.context[0x2ce] = 1;
expectFailure([&] { AgcDriver::Graphics::DecodeState(lines); }, "unsupported geometry input or output assembly");
@@ -3386,6 +3478,8 @@ void msaaEnableClearDepthTests() {
queue.context[0x2ce] = 5;
queue.context[0x29b] = 0;
queue.context[0x2ab] = 4;
queue.context[0x280] = 0x00080008u; // point-output geometry programs decode PA_SU_POINT_SIZE / PA_SU_POINT_MINMAX before this rejection
queue.context[0x281] = 0x00080008u;
queue.shader[0x8a] = 3u << 29u;
queue.shader[0x8b] = 3u << 16u;
expectFailure([&] { DecodeState(queue); }, "multisampled targets rasterized without MSAA_ENABLE are unsupported for points or alpha-to-coverage");
@@ -9387,6 +9481,8 @@ void ConservativeRasterizationTests() {
geometry.context[0x2ce] = 3;
geometry.context[0x29b] = 2;
geometry.context[0x2ab] = 4;
geometry.context[0x280] = 0x00080008u; // PA_SU_POINT_SIZE and PA_SU_POINT_MINMAX: a point-output geometry program decodes them
geometry.context[0x281] = 0x00080008u;
geometry.shader[0x8a] = 3u << 29u;
geometry.shader[0x8b] = 3u << 16u;
for (const auto input : {1u, 2u, 4u}) {
@@ -10849,6 +10945,22 @@ void pipelineJournalTests(bool longPath = false, bool extended = false) {
GraphicsPipelineRecipe lineDecoded;
Require(DecodeGraphicsPipelineRecipe(EncodeGraphicsPipelineRecipe(MakeGraphicsPipelineRecipe(lineState, input, layouts.View(), shaders, VK_IMAGE_LAYOUT_GENERAL)), lineDecoded), "the line strip mesh recipe does not decode");
Require(lineDecoded.state.stages.mesh && lineDecoded.state.stages.mesh->outputPrimitiveSize == 2u && lineDecoded.state.stages.mesh->esgsItemSize == 4u, "the recipe dropped the mesh output primitive size");
auto pointState = state;
pointState.vertexPoints = true;
pointState.pointSize = 4.0f;
pointState.pointSizeMin = 0.5f;
pointState.pointSizeMax = 8.0f;
GraphicsPipelineRecipe pointDecoded;
Require(DecodeGraphicsPipelineRecipe(EncodeGraphicsPipelineRecipe(MakeGraphicsPipelineRecipe(pointState, input, layouts.View(), shaders, VK_IMAGE_LAYOUT_GENERAL)), pointDecoded), "the point list recipe does not decode");
Require(pointDecoded.state.vertexPoints && pointDecoded.state.pointSize == 4.0f && pointDecoded.state.pointSizeMin == 0.5f && pointDecoded.state.pointSizeMax == 8.0f, "the recipe dropped the vertex point list or its point sizes");
auto meshPointState = pointState;
meshPointState.vertexPoints = false;
meshPointState.stages.mesh = *lineState.stages.mesh;
meshPointState.stages.mesh->outputPrimitiveSize = 1u;
meshPointState.stages.mesh->provokingVertex = 0u;
GraphicsPipelineRecipe meshPointDecoded;
Require(DecodeGraphicsPipelineRecipe(EncodeGraphicsPipelineRecipe(MakeGraphicsPipelineRecipe(meshPointState, input, layouts.View(), shaders, VK_IMAGE_LAYOUT_GENERAL)), meshPointDecoded), "the mesh point recipe does not decode");
Require(!meshPointDecoded.state.vertexPoints && meshPointDecoded.state.stages.mesh && meshPointDecoded.state.stages.mesh->outputPrimitiveSize == 1u && meshPointDecoded.state.pointSize == 4.0f && meshPointDecoded.state.pointSizeMin == 0.5f && meshPointDecoded.state.pointSizeMax == 8.0f, "the recipe dropped mesh points or their sizes without the vertex point flag");
auto fourSampleState = state;
fourSampleState.samples = 4;
auto maskState = fourSampleState;
@@ -11330,6 +11442,7 @@ int main(int argc, char** argv) {
NggVertexWorkgroupTests();
inactiveVertexPointSizeTests();
PolygonPrimitiveTests();
vertexPointListTests();
TuningFieldTests();
PixelInputLayoutTests();
UnexportedPixelInputTests();
@@ -95,6 +95,52 @@ void rejectFields(const Fields& f, std::string_view reason) {
}
static void RunR128TextureTests() {
constexpr std::array<std::uint32_t, 8> streamed{0xa0c18800u, 0xcad00000u, 0x01ffc1ffu, 0x905b2facu, 0u, 0u, 0u, 0u};
const auto view = DecodeTextureResource(streamed);
Require(view.baseAddress == 0xa0c1880000ull && view.format == 173u && view.tileMode == TextureTileMode::kStandard4KB && view.width == 512u && view.height == 512u && view.baseLevel == 0u && view.lastLevel == 9u && view.mipCount == 10u && view.allocatedMipCount == 10u && view.minLod == 0u,
"a 128-bit BC3 T# (2048x2048, levels 2..11, words 4-7 zero) must read as the 512x512 chain of its 10 resident levels, got " + std::to_string(view.width) + "x" + std::to_string(view.height) + " levels " + std::to_string(view.baseLevel) + ".." + std::to_string(view.lastLevel) + " of " + std::to_string(view.mipCount));
auto chainWords = streamed;
chainWords[3] &= ~(0xfu << 12u);
chainWords[5] = 11u << 4u;
const auto full = DecodeTextureResource(chainWords);
Require(full.width == 2048u && full.mipCount == 12u, "the same surface with MAX_MIP 11 must be the 12-level 2048x2048 chain");
const auto chain = DescribeSurface(full);
const auto resident = DescribeSurface(view);
Require(chain.mips.size() == 12u && resident.mips.size() == 10u, "the chain and the resident levels must have 12 and 10 levels");
for (std::uint32_t level = 0; level < 10u; ++level) {
const auto& a = chain.mips[level + 2u];
const auto& b = resident.mips[level];
Require(a.tiledOffset == b.tiledOffset && a.tiledSize == b.tiledSize && a.width == b.width && a.height == b.height && a.blocksPerRow == b.blocksPerRow && a.tail == b.tail && a.tailX == b.tailX && a.tailY == b.tailY, "resident level " + std::to_string(level) + " must be level " + std::to_string(level + 2u) + " of the 12-level chain");
}
Require(resident.guestBytes <= chain.mips[1].tiledOffset && chain.mips[0].tiledOffset > chain.mips[1].tiledOffset, "the resident levels must end before the chain's levels 1 and 0, which come last in a GFX10 swizzled chain");
auto levelZero = streamed;
levelZero[3] &= ~(0xfu << 12u);
const auto single = DecodeTextureResource(levelZero);
Require(single.mipCount == 1u && single.lastLevel == 0u, "a T# with words 4-7 zero that starts at level 0 must stay one level");
auto tailed = streamed;
tailed[5] = 4u << 20u;
reject([&] { DecodeTextureResource(tailed); }, "would move the surface's own");
constexpr std::array<std::uint32_t, 8> tall{0xa0c1de00u, 0xcad00000u, 0x01ffc0ffu, 0x905b4facu, 0u, 0u, 0u, 0u};
const auto tallView = DecodeTextureResource(tall);
Require(tallView.width == 64u && tallView.height == 128u && tallView.lastLevel == 7u && tallView.mipCount == 8u, "a 128-bit 1024x2048 T# of levels 4..11 must read as its 64x128 chain of 8 levels");
Fields clamped;
clamped.base40 = 0x55ea000ull;
clamped.format = 20;
clamped.width = 1024;
clamped.height = 1024;
clamped.tileModeRaw = 0x1b;
clamped.baseLevel = 2;
clamped.lastLevel = 10;
clamped.minLod = 2u * 256u + 64u;
const auto clampedView = DecodeTextureResource(pack(clamped));
Require(clampedView.width == 256u && clampedView.mipCount == 9u && clampedView.minLod == 64u, "reading a 128-bit T# from its resident levels must move its MIN_LOD clamp with the base level");
clamped.minLod = 256u;
Require(DecodeTextureResource(pack(clamped)).minLod == 0u, "a MIN_LOD clamp below the base level must vanish when read from the resident levels");
}
void RunGuestTextureResourceTests() {
Fields base;
const auto baseWords = pack(base);
@@ -629,6 +675,7 @@ void RunGuestTextureResourceTests() {
pastUntailed.maxMip = 0;
pastUntailed.baseLevel = 1;
pastUntailed.lastLevel = 1;
pastUntailed.perfMod = 4;
rejectFields(pastUntailed, "would move the surface's own");
auto untailedChain = tailView;
untailedChain.mipCount = 1;
@@ -636,6 +683,8 @@ void RunGuestTextureResourceTests() {
untailedChain.mipCount = 2;
Require(untailedAllocated == 0xff0000u && DescribeSurface(untailedChain).guestBytes == 0x1470000u, "a second level must grow a 1920x1080 64 bpp SW_64KB_R_X surface from addrlib's 0xff0000 to 0x1470000 bytes");
RunR128TextureTests();
std::array<std::uint32_t, 4> shortWords{};
reject([&] { DecodeTextureResource(shortWords); }, "8 dwords");
+64 -1
View File
@@ -5,6 +5,7 @@
#include "prx/libc/include/GuestAllocations.hpp"
#include <algorithm>
#include <array>
#include <bit>
#include <cmath>
#include <cstdio>
#include <cstdlib>
@@ -144,6 +145,7 @@ struct MeshDraw {
VkCullModeFlags cull = VK_CULL_MODE_NONE;
std::span<const std::uint32_t> code = {};
std::uint32_t stagesRegister = 0x20u;
ShaderRecompiler::ShaderVertexStageInfo vertex{};
};
bool ReadsNumWorkgroups(std::span<const std::uint32_t> words) {
@@ -176,7 +178,7 @@ void DrawMesh(AgcDriver::VulkanDevice& device, const MeshDraw& setup) {
const std::array<ShaderRecompiler::MemoryRegion, 1> geometryMemory{{{reinterpret_cast<std::uintptr_t>(code.data()), std::as_bytes(std::span(code))}}};
ShaderRecompiler::RecompileRequest geometry{
{ShaderStage::Mesh, reinterpret_cast<std::uintptr_t>(code.data()), code, 0, {}},
{setup.waveSize, 0, userData, std::nullopt, std::nullopt, ShaderRecompiler::ShaderVertexStageInfo{}, geometryMemory},
{setup.waveSize, 0, userData, std::nullopt, std::nullopt, setup.vertex, geometryMemory},
target,
{0, 0, 0, setup.geometryPushBytes},
ShaderRecompiler::GraphicsCompileContext{0, {}, setup.mesh, std::nullopt, {setup.draw.indexAddress, setup.draw.indexCount, setup.draw.indexSize, setup.draw.instanceCount}}
@@ -207,6 +209,10 @@ void DrawMesh(AgcDriver::VulkanDevice& device, const MeshDraw& setup) {
AgcDriver::Graphics::State state{};
state.stages = {AgcDriver::Graphics::ShaderPath::Geometry, setup.stagesRegister, setup.waveSize, 64, setup.mesh, std::nullopt};
state.pointSize = setup.vertex.pointSize;
state.pointSizeMin = setup.vertex.pointSizeMin;
state.pointSizeMax = setup.vertex.pointSizeMax;
state.paClVsOutCntl = setup.vertex.paClVsOutCntl;
state.color = {reinterpret_cast<std::uintptr_t>(Pixels.data()), {Width, Height}, VK_FORMAT_R8G8B8A8_UNORM, Pixels.size(), 0xe4u};
state.colors = {state.color};
state.hasColorTarget = true;
@@ -394,6 +400,63 @@ int main() {
}
}
ClearPixels();
auto pointSubgroup = SmallSubgroup;
pointSubgroup.outputPrimitiveSize = 1u;
for (const auto pointWave : {32u, 64u}) {
ClearPixels();
DrawMesh(device, {pointSubgroup, {0, static_cast<std::uint32_t>(Ordered.size()), 0, 1, 0, false}, VertexBufferDescriptor(Ordered.data(), static_cast<std::uint32_t>(Ordered.size())), ShaderRecompiler::MeshDrawPushOffsetBytes, false, pointWave});
for (std::uint32_t triangle = 0; triangle < Triangles; ++triangle) {
const auto& position = Ordered[3u * triangle].position;
const auto x = static_cast<std::uint32_t>((position[0] + 1.0f) * 0.5f * Width);
const auto y = static_cast<std::uint32_t>((1.0f - position[1]) * 0.5f * Height);
const auto offset = (static_cast<std::size_t>(y) * Width + x) * 4u;
Require(PixelIs(offset, TriangleColor(triangle)), "mesh point missing at first exported index for primitive " + std::to_string(triangle));
Require(PixelIs(CellPixel(triangle), Background), "mesh point output filled the interior of its packed triangle");
}
}
for (const auto pointWave : {32u, 64u}) {
for (const auto size : {3.0f, 4.0f}) {
ClearPixels();
MeshDraw points{pointSubgroup, {0, static_cast<std::uint32_t>(Ordered.size()), 0, 1, 0, false}, VertexBufferDescriptor(Ordered.data(), static_cast<std::uint32_t>(Ordered.size()))};
points.waveSize = pointWave;
points.vertex.pointSize = size;
DrawMesh(device, points);
for (std::uint32_t primitive = 0; primitive < Triangles; ++primitive) {
std::uint32_t covered = 0;
for (std::size_t pixel = 0; pixel < Pixels.size(); pixel += 4u) covered += PixelIs(pixel, TriangleColor(primitive));
Require(covered == static_cast<std::uint32_t>(size * size), "mesh point did not cover its register-sized square");
}
}
for (const auto exportedSize : {6.0f, -3.0f}) {
ClearPixels();
const auto& baseCode = pointWave == 32u ? Wave32GeometryCode : GeometryCode;
std::array<std::uint32_t, GeometryCode.size() + 4u> sizedCode{};
std::copy_n(baseCode.begin(), 101u, sizedCode.begin());
sizedCode[92] = 0xbf88000eu;
sizedCode[99] = 0xf80000cfu;
sizedCode[101] = 0x7e6802ffu;
sizedCode[102] = std::bit_cast<std::uint32_t>(exportedSize);
sizedCode[103] = 0xf80008d1u;
sizedCode[104] = 0x00000034u;
std::copy(baseCode.begin() + 101u, baseCode.end(), sizedCode.begin() + 105u);
MeshDraw exported{pointSubgroup, {0, static_cast<std::uint32_t>(Ordered.size()), 0, 1, 0, false}, VertexBufferDescriptor(Ordered.data(), static_cast<std::uint32_t>(Ordered.size()))};
exported.waveSize = pointWave;
exported.code = sizedCode;
exported.vertex.paClVsOutCntl = (1u << 16u) | (1u << 21u);
exported.vertex.pointSizeMin = 3.0f;
exported.vertex.pointSizeMax = 4.0f;
DrawMesh(device, exported);
for (std::uint32_t primitive = 0; primitive < Triangles; ++primitive) {
std::uint32_t covered = 0;
for (std::size_t pixel = 0; pixel < Pixels.size(); pixel += 4u) covered += PixelIs(pixel, TriangleColor(primitive));
const auto side = exportedSize < 3.0f ? 3u : 4u;
Require(covered == side * side, "mesh point ignored its exported size or clamp");
}
}
}
ClearPixels();
auto lineSubgroup = FastLaunchSubgroup;
lineSubgroup.maxPrimitives = 2u;
@@ -0,0 +1,38 @@
#include "SceTypes.hpp"
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
extern "C" int APS5_VABI sceAgcDriverNopDataWritePacket(void* destination, uint32_t address, const void* data, uint32_t bytes);
int main() {
std::array<std::byte, 24> storage;
storage.fill(std::byte{0xaa});
const std::array<std::byte, 5> data{std::byte{0x11}, std::byte{0x22}, std::byte{0x33}, std::byte{0x44}, std::byte{0x55}};
const auto words = sceAgcDriverNopDataWritePacket(storage.data() + 4, 0x12345678, data.data(), static_cast<std::uint32_t>(data.size()));
std::uint32_t header = 0;
std::uint32_t address = 0;
std::memcpy(&header, storage.data() + 4, sizeof(header));
std::memcpy(&address, storage.data() + 8, sizeof(address));
if (words != 4 || header != 0xc0021000U || address != 0x12345678U) return 1;
if (!std::equal(data.begin(), data.end(), storage.begin() + 12)) return 2;
if (storage[17] != std::byte{0} || storage[18] != std::byte{0} || storage[19] != std::byte{0}) return 3;
if (storage[3] != std::byte{0xaa} || storage[20] != std::byte{0xaa}) return 4;
storage.fill(std::byte{0xaa});
const auto emptyWords = sceAgcDriverNopDataWritePacket(storage.data() + 4, 0x87654321, nullptr, 5);
std::memcpy(&header, storage.data() + 4, sizeof(header));
std::memcpy(&address, storage.data() + 8, sizeof(address));
if (emptyWords != 2 || header != 0xc0021000U || address != 0x87654321U) return 5;
if (storage[12] != std::byte{0} || storage[19] != std::byte{0} || storage[20] != std::byte{0xaa}) return 6;
storage.fill(std::byte{0xaa});
const auto zeroBytesWords = sceAgcDriverNopDataWritePacket(storage.data() + 4, 0x01020304, data.data(), 0);
std::memcpy(&header, storage.data() + 4, sizeof(header));
std::memcpy(&address, storage.data() + 8, sizeof(address));
if (zeroBytesWords != 2 || header != 0xc0001000U || address != 0x01020304U) return 7;
if (storage[12] != std::byte{0xaa}) return 8;
return 0;
}
@@ -21,9 +21,9 @@ void require(bool condition, const char* message) {
void setScreenshotDirectory(const std::filesystem::path& directory) {
#ifdef _WIN32
require(_putenv_s("ANYPS5_SCREENSHOTS", directory.string().c_str()) == 0, "set ANYPS5_SCREENSHOTS");
require(_wputenv_s(L"ANYPS5_SCREENSHOTS", directory.wstring().c_str()) == 0, "set ANYPS5_SCREENSHOTS");
#else
require(::setenv("ANYPS5_SCREENSHOTS", directory.string().c_str(), 1) == 0, "set ANYPS5_SCREENSHOTS");
require(::setenv("ANYPS5_SCREENSHOTS", directory.c_str(), 1) == 0, "set ANYPS5_SCREENSHOTS");
#endif
}
@@ -73,7 +73,7 @@ void rejectsInvalidFrames() {
int main() try {
const auto root = std::filesystem::temp_directory_path() / ("anyps5_screenshot-" + std::to_string(std::random_device{}()));
const auto directory = root / "nested";
const auto directory = root / std::filesystem::path(u8"nested-a\u00e7\u00e3o-\u65e5\u672c");
std::filesystem::remove_all(root);
setScreenshotDirectory(directory);
requestsMerge();
@@ -4513,13 +4513,15 @@ void verifyMeshConfiguration() {
RecompileCacheKey::Build(other, key);
require(key != first && RecompileCacheKey::ContextHash(request) != RecompileCacheKey::ContextHash(other), "the cache keys ignore the mesh configuration");
require(mesh.outputPrimitiveSize == 3u && replay.request.graphics->mesh->outputPrimitiveSize == 3u, "a mesh configuration does not output triangles by default");
otherMesh = mesh;
otherMesh.outputPrimitiveSize = 2u;
other.graphics = GraphicsCompileContext{0u, {}, otherMesh, std::nullopt, {}};
const auto lineReplay = RequestSerializer{}.Deserialize(RequestSerializer{}.Serialize(other));
require(lineReplay.request.graphics.has_value() && lineReplay.request.graphics->mesh.has_value() && lineReplay.request.graphics->mesh->outputPrimitiveSize == 2u, "the mesh output primitive size was lost in serialization");
RecompileCacheKey::Build(other, key);
require(key != first && RecompileCacheKey::ContextHash(request) != RecompileCacheKey::ContextHash(other), "the cache keys ignore the mesh output primitive size");
for (const auto primitiveSize : {1u, 2u}) {
otherMesh = mesh;
otherMesh.outputPrimitiveSize = primitiveSize;
other.graphics = GraphicsCompileContext{0u, {}, otherMesh, std::nullopt, {}};
const auto primitiveReplay = RequestSerializer{}.Deserialize(RequestSerializer{}.Serialize(other));
require(primitiveReplay.request.graphics.has_value() && primitiveReplay.request.graphics->mesh.has_value() && primitiveReplay.request.graphics->mesh->outputPrimitiveSize == primitiveSize, "the mesh output primitive size was lost in serialization");
RecompileCacheKey::Build(other, key);
require(key != first && RecompileCacheKey::ContextHash(request) != RecompileCacheKey::ContextHash(other), "the cache keys ignore the mesh output primitive size");
}
RecompileRequest hull{};
hull.shader = {ShaderStage::TessellationControl, 0x10000u, code, 0, {}};
hull.context.waveSize = 64;
@@ -4896,7 +4898,7 @@ void verifyPixelRequestSerialization() {
minimal.context.pixel->layerExported = true;
minimal.context.pixel->ditheredAlphaToCoverage = true;
const auto encoded = serializer.Serialize(minimal);
require(requestPrefix(encoded, 8u) == "NVNQQSoAAAA=", "new requests did not use serialization version 42");
require(requestPrefix(encoded, 8u) == "NVNQQSsAAAA=", "new requests did not use serialization version 43");
const std::array<std::uint32_t, 2> fallbacks{0x40u, 0x1ac4u};
auto withFallbacks = minimal;
withFallbacks.context.linkedCallFallbacks = fallbacks;
@@ -5014,7 +5016,7 @@ void verifyPixelRequestSerialization() {
for (std::size_t bytes = 0; bytes < 45u; ++bytes) {
expectFailure([&] { static_cast<void>(serializer.Deserialize(requestPrefix(encoded, mappingOffset + bytes))); }, "truncated data", "a truncated pixel mapping, packing, dual-source, alpha-to-coverage, stencil reference export flag, primitive class, layer export, dither or front-face flag, quad pixel mask or quad sample mask was accepted");
}
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQSsAAAA="}) {
for (const auto unsupported : {"NVNQQQAAAAA=", "NVNQQSwAAAA="}) {
expectFailure([&] { static_cast<void>(serializer.Deserialize(unsupported)); }, "serialization version", "an unsupported request version was accepted");
}
static constexpr std::string_view version18Request{
@@ -5303,15 +5305,16 @@ void verifyDepthRemapRequests() {
const auto replay = serializer.Deserialize(remapped);
require(replay.request.target.depthRangeRemap && replay.request.context.vertex.has_value() && replay.request.context.vertex->paClVsOutCntl == 0x89abcdefu, "the depth remap target flag or the vertex info did not round-trip");
require(replay.request.context.vertex->depthRemapMode == 2u, "the depth remap mode did not round-trip");
require(serializer.Serialize(replay.request) == remapped, "a version 35 depth remap request changed on re-serialization");
require(serializer.Serialize(replay.request) == remapped, "the depth remap request changed on re-serialization");
const auto plainBytes = decodeBase64(plain);
const auto remappedBytes = decodeBase64(remapped);
require(plainBytes.size() == remappedBytes.size() && plainBytes.size() > 5u && std::equal(plainBytes.begin(), plainBytes.end() - 7, remappedBytes.begin()), "the depth remap flag and mode are not the 6 bytes before the f32 flush and denormal preserve flags of a vertex request");
constexpr std::size_t pointTailBytes = 13u;
require(plainBytes.size() == remappedBytes.size() && plainBytes.size() > 7u + pointTailBytes && std::equal(plainBytes.begin(), plainBytes.end() - 7 - pointTailBytes, remappedBytes.begin()) && std::equal(plainBytes.end() - pointTailBytes, plainBytes.end(), remappedBytes.end() - pointTailBytes), "the depth remap flag and mode are not the 6 bytes before the f32 flush and denormal preserve flags and the point tail of a vertex request");
for (const std::uint8_t legacyVersion : {32u, 34u}) {
auto legacyBytes = remappedBytes;
legacyBytes.pop_back();
legacyBytes[4] = legacyVersion;
legacyBytes.resize(legacyBytes.size() - 6u);
legacyBytes.resize(legacyBytes.size() - 6u - pointTailBytes);
const auto legacy = serializer.Deserialize(encodeBase64(legacyBytes));
require(!legacy.request.target.depthRangeRemap && legacy.request.context.vertex.has_value() && legacy.request.context.vertex->depthRemapMode == 0u, ("a version " + std::to_string(legacyVersion) + " vertex request read depth remap fields").c_str());
require(serializer.Serialize(legacy.request) == plain, ("a version " + std::to_string(legacyVersion) + " vertex request misread the fields before the depth remap").c_str());
@@ -5319,10 +5322,10 @@ void verifyDepthRemapRequests() {
auto version35 = remappedBytes;
version35.pop_back();
version35[4] = 35u;
version35.pop_back();
version35.resize(version35.size() - 1u - pointTailBytes);
const auto replay35 = serializer.Deserialize(encodeBase64(version35));
require(replay35.request.target.depthRangeRemap && replay35.request.context.vertex->depthRemapMode == 2u && !replay35.request.target.float32FlushToZero && serializer.Serialize(replay35.request) == remapped, "a version 35 request misread the depth remap fields or read an f32 flush flag");
auto truncated = remappedBytes;
auto truncated = version35;
truncated.resize(truncated.size() - 2u);
expectFailure([&] { static_cast<void>(serializer.Deserialize(encodeBase64(truncated))); }, "truncated data", "a truncated depth remap mode was accepted");
auto trailing = remappedBytes;
@@ -5363,9 +5366,12 @@ void verifyMergedHullConfiguration() {
const auto replay = serializer.Deserialize(merged);
require(replay.request.graphics.has_value() && replay.request.graphics->tessellation.has_value() && replay.request.graphics->tessellation->mergedHull, "a merged hull configuration was lost in serialization");
require(!serializer.Deserialize(separate).request.graphics->tessellation->mergedHull, "a separate hull configuration was read as merged");
constexpr std::size_t pointTailBytes = 13u;
auto legacy = decodeBase64(merged);
require(legacy.size() > 53u + pointTailBytes && legacy[4] == 43u, "a merged hull request did not use serialization version 43");
legacy.resize(legacy.size() - pointTailBytes);
legacy.pop_back();
require(legacy.size() > 52u && legacy[legacy.size() - 52u] == 1u, "the merged hull flag is not the last byte of the tessellation configuration");
require(legacy[legacy.size() - 52u] == 1u, "the merged hull flag is not the last byte of the tessellation configuration");
legacy.erase(legacy.end() - 52);
for (const auto version : {30u, 31u, 32u, 34u, 35u, 36u, 37u, 38u}) {
auto older = legacy;
@@ -0,0 +1,10 @@
#include "SceTypes.hpp"
extern "C" void* APS5_VABI sceAgcDriverGetCoredumpAddress();
extern "C" int APS5_VABI sceAgcDriverSetTargetRingForDiag();
int main() {
if (sceAgcDriverGetCoredumpAddress() != nullptr) return 1;
if (sceAgcDriverSetTargetRingForDiag() != static_cast<int>(0x8A6D0001)) return 2;
return 0;
}
@@ -120,7 +120,7 @@ void Run(AgcDriver::VulkanDevice& device, std::uint32_t baseArray = BaseArray) {
device.WaitIdle();
for (std::uint32_t tid = 0; tid < Threads; ++tid) {
const float u = std::bit_cast<float>(Input[tid * 4u + 0u]);
const float layer = std::clamp(std::nearbyint(std::bit_cast<float>(Input[tid * 4u + 1u])), 0.0f, static_cast<float>(Layers - 1u));
const float layer = std::clamp(std::nearbyint(std::bit_cast<float>(Input[tid * 4u + 1u])), 0.0f, static_cast<float>(LastArray - baseArray));
const auto x = static_cast<std::uint32_t>(std::clamp(static_cast<int>(std::floor(u * static_cast<float>(Width))), 0, static_cast<int>(Width) - 1));
Expect(tid, "image_sample_lz on a window of a 1D array past maxImageArrayLayers", &Output[tid * 8u], Texel(x, baseArray + static_cast<std::uint32_t>(layer)));
Expect(tid, "image_load on a window of a 1D array past maxImageArrayLayers", &Output[tid * 8u + 4u], Texel(Input[tid * 4u + 2u], baseArray + Input[tid * 4u + 3u]));
@@ -0,0 +1,165 @@
#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
#include "Recompiler.hpp"
#include "VulkanTestDevice.hpp"
#include <algorithm>
#include <array>
#include <bit>
#include <cstdint>
#include <cstring>
#include <exception>
#include <iostream>
#include <optional>
#include <span>
#include <string>
#include <vector>
namespace {
using AgcDriver::Graphics::Require;
using ShaderRecompiler::ShaderStage;
constexpr std::uint32_t Extent = 64u;
constexpr std::uint32_t MiscVector = 1u << 21u;
constexpr std::uint32_t VertexPointSize = 1u << 16u;
alignas(256) std::array<std::byte, Extent * Extent * 4u> pixels{};
alignas(256) std::array<std::byte, 512> vertices{};
constexpr std::array<std::uint32_t, 11> positionCode{0xf4080100u, 0xfa000000u, 0x4a0a0a02u, 0x4a0a0b08u, 0x4a0a0a03u, 0xe00c2000u, 0x80010005u, 0xbf8c3f70u, 0xf80008cfu, 0x03020100u, 0xbf810000u};
constexpr std::array<std::uint32_t, 5> pixelCode{0x7e0002f2u, 0x7e020280u, 0xf800180fu, 0x00010100u, 0xbf810000u};
std::vector<std::uint32_t> SizeExportCode(float size) {
std::vector<std::uint32_t> code(positionCode.begin(), positionCode.begin() + 8);
code.insert(code.end(), {0x7e0802ffu, std::bit_cast<std::uint32_t>(size), 0xf80000cfu, 0x03020100u, 0xf80008d1u, 0x00000004u, 0xbf810000u});
return code;
}
struct PointSize {
float size = 1.0f;
float minimum = 0.0f;
float maximum = 0.0f;
bool fromVertex = false;
};
struct Square {
unsigned pixels = 0;
std::uint32_t left = Extent, top = Extent, right = 0, bottom = 0;
};
std::vector<Square> RunCase(AgcDriver::VulkanDevice& device, const char* name, std::span<const std::uint32_t> vertexCode, const PointSize& points, float offset) {
const std::array<std::array<float, 2>, 4> centres{{{16.0f, 16.0f}, {48.0f, 16.0f}, {16.0f, 48.0f}, {48.0f, 48.0f}}};
vertices.fill(std::byte{0});
constexpr std::uint32_t stride = 16u;
for (std::size_t index = 0; index < centres.size(); ++index) {
const std::array<float, 4> position{(centres[index][0] + offset) / Extent * 2.0f - 1.0f, (centres[index][1] + offset) / Extent * 2.0f - 1.0f, 0.0f, 1.0f};
std::memcpy(vertices.data() + index * stride, position.data(), sizeof(position));
}
std::array<std::uint32_t, 4> descriptor{};
const auto tableAddress = reinterpret_cast<std::uintptr_t>(descriptor.data());
std::array<std::uint32_t, 4> userData{static_cast<std::uint32_t>(tableAddress), static_cast<std::uint32_t>(tableAddress >> 32u), 0u, 0u};
const std::array<ShaderRecompiler::MemoryRegion, 1> memory{{{tableAddress, std::as_bytes(std::span(descriptor))}}};
ShaderRecompiler::ShaderVertexStageInfo vertexInfo{};
vertexInfo.fetchEmbedded = true;
vertexInfo.resourcesNum = 1u;
vertexInfo.resourcesDst[0] = {0, 4, 0, 0};
vertexInfo.paClVsOutCntl = points.fromVertex ? MiscVector | VertexPointSize : 0u;
vertexInfo.pointList = true;
vertexInfo.pointSize = points.size;
vertexInfo.pointSizeMin = points.minimum;
vertexInfo.pointSizeMax = points.maximum;
ShaderRecompiler::RecompileRequest request{{ShaderStage::Vertex, reinterpret_cast<std::uintptr_t>(vertexCode.data()), vertexCode, 0, {}}, {32u, 0u, userData, std::nullopt, std::nullopt, vertexInfo, memory}, device.Target(), {0u, 0u, 0u, 96u}};
const auto address = reinterpret_cast<std::uintptr_t>(vertices.data());
descriptor = {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>(address >> 32u) | (stride << 16u), static_cast<std::uint32_t>(centres.size()), 0x01000facu | (77u << 12u)};
request.context.vertex->resources[0].fields = descriptor;
const auto vertex = ShaderRecompiler::Recompile(request);
const auto pushBytes = static_cast<std::uint32_t>(vertex.pushConstants.size());
ShaderRecompiler::ShaderPixelStageInfo pixelInfo{};
pixelInfo.wave32 = true;
pixelInfo.targetOutputMode[0] = 9u;
pixelInfo.targetExportMapping.fill(0xe4u);
pixelInfo.rasterPrimitive = ShaderRecompiler::RasterPrimitive::Point;
ShaderRecompiler::RecompileRequest pixelRequest{{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(pixelCode.data()), pixelCode, 0, {}}, {32u, 0u, {}, std::nullopt, pixelInfo, std::nullopt, {}}, device.Target(), PixelPushLayout(pushBytes, device.Target())};
const auto pixel = ShaderRecompiler::Recompile(pixelRequest);
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{{ShaderStage::Vertex, &vertex, 0u}, {ShaderStage::Fragment, &pixel, PixelPushOffset(pushBytes, device.Target())}}};
AgcDriver::Graphics::State state{};
state.stages = {AgcDriver::Graphics::ShaderPath::Vertex, 0u, 32u, 32u, std::nullopt, std::nullopt};
state.color = {reinterpret_cast<std::uintptr_t>(pixels.data()), {Extent, Extent}, VK_FORMAT_R8G8B8A8_UNORM, pixels.size(), 0xe4u};
state.colors = {state.color};
state.hasColorTarget = true;
state.renderExtent = {Extent, Extent};
state.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
state.paClVsOutCntl = vertexInfo.paClVsOutCntl;
state.vertexPoints = true;
state.pointSize = points.size;
state.pointSizeMin = points.minimum;
state.pointSizeMax = points.maximum;
state.viewport = {0.0f, static_cast<float>(Extent), static_cast<float>(Extent), -static_cast<float>(Extent), 0.0f, 1.0f};
state.scissor = {{0, 0}, {Extent, Extent}};
state.cullMode = VK_CULL_MODE_NONE;
state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
state.blend.colorWriteMask = 15u;
state.blends = {state.blend};
AgcDriver::Pm4::DrawParameters draw{0u, static_cast<std::uint32_t>(centres.size()), 0u, 1u, 0u, false};
draw.firstInstance = 0u;
pixels.fill(std::byte{0});
device.Draw(state, draw, shaders);
device.WaitIdle();
std::vector<Square> squares(centres.size());
for (std::uint32_t y = 0; y < Extent; ++y) {
for (std::uint32_t x = 0; x < Extent; ++x) {
const auto index = (y * Extent + x) * 4u;
if (pixels[index] == std::byte{0} && pixels[index + 3u] == std::byte{0}) continue;
auto& square = squares[(y >= Extent / 2u ? 2u : 0u) + (x >= Extent / 2u ? 1u : 0u)];
++square.pixels;
square.left = std::min(square.left, x);
square.top = std::min(square.top, y);
square.right = std::max(square.right, x + 1u);
square.bottom = std::max(square.bottom, y + 1u);
}
}
std::cout << "case " << name << ":";
for (const auto& square : squares) std::cout << " " << square.pixels << " [" << square.left << "," << square.top << ")-(" << square.right << "," << square.bottom << ")";
std::cout << "\n";
return squares;
}
void RequireSquares(const std::vector<Square>& squares, std::uint32_t side, const char* name) {
for (const auto& square : squares) {
Require(square.pixels == side * side && square.right - square.left == side && square.bottom - square.top == side, std::string(name) + ": a point is not a " + std::to_string(side) + "x" + std::to_string(side) + " square");
}
}
void RequireAtMostOnePixel(const std::vector<Square>& squares, const char* name) {
for (const auto& square : squares) Require(square.pixels <= 1u, std::string(name) + ": a point with no positive size covers more than one pixel");
}
void Run(AgcDriver::VulkanDevice& device) {
std::cout << "device: " << device.DeviceName() << "\n";
RequireSquares(RunCase(device, "register size 1", positionCode, {1.0f, 1.0f, 1.0f, false}, 0.5f), 1u, "register size 1");
RequireSquares(RunCase(device, "register size 4", positionCode, {4.0f, 1.0f, 1.0f, false}, 0.0f), 4u, "register size 4");
RequireSquares(RunCase(device, "register size 3 ignores the clamp", positionCode, {3.0f, 6.0f, 8.0f, false}, 0.5f), 3u, "register size 3 ignores the clamp");
const auto two = SizeExportCode(2.0f);
RequireSquares(RunCase(device, "vertex size 2 within [1, 8]", two, {1.0f, 1.0f, 8.0f, true}, 0.0f), 2u, "vertex size 2 within [1, 8]");
const auto six = SizeExportCode(6.0f);
RequireSquares(RunCase(device, "vertex size 6 clamped to 4", six, {1.0f, 0.5f, 4.0f, true}, 0.0f), 4u, "vertex size 6 clamped to 4");
const auto one = SizeExportCode(1.0f);
RequireSquares(RunCase(device, "vertex size 1 clamped to 5", one, {1.0f, 5.0f, 8.0f, true}, 0.5f), 5u, "vertex size 1 clamped to 5");
RequireSquares(RunCase(device, "vertex size over a register size", six, {1.0f, 1.0f, 8.0f, true}, 0.0f), 6u, "vertex size over a register size");
const auto zero = SizeExportCode(0.0f);
RequireAtMostOnePixel(RunCase(device, "vertex size 0 within [0, 4]", zero, {0.0f, 0.0f, 4.0f, true}, 0.5f), "vertex size 0 within [0, 4]");
const auto negative = SizeExportCode(-3.0f);
RequireAtMostOnePixel(RunCase(device, "vertex size -3 within [0, 4]", negative, {1.0f, 0.0f, 4.0f, true}, 0.5f), "vertex size -3 within [0, 4]");
}
}
int main() {
try {
const auto device = OpenVulkanTestDevice();
if (!device) return VulkanTestSkipped;
Run(*device);
return 0;
} catch (const std::exception& error) {
std::cerr << error.what() << '\n';
return 1;
}
}
@@ -62,6 +62,11 @@ if(BUILD_TESTING)
target_link_libraries(guest_ajm_vibration_tests PRIVATE libSceAjm.native libc)
configure_windows_unwind(guest_ajm_vibration_tests)
add_test(NAME guest_ajm_vibration COMMAND guest_ajm_vibration_tests)
add_test_executable(guest_ajm_wvorbis_tests ${LIBS_INCLUDE_DIR}/tests/GuestAjmWVorbis.cpp)
target_include_directories(guest_ajm_wvorbis_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_ajm_wvorbis_tests PRIVATE libSceAjm.native libc)
configure_windows_unwind(guest_ajm_wvorbis_tests)
add_test(NAME guest_ajm_wvorbis COMMAND guest_ajm_wvorbis_tests)
if(UNIX)
add_test_executable(guest_ajm_teardown_tests ${LIBS_INCLUDE_DIR}/tests/GuestAjmTeardown.cpp)
target_include_directories(guest_ajm_teardown_tests PRIVATE ${LIBS_INCLUDE_DIR})
@@ -70,7 +75,7 @@ if(BUILD_TESTING)
add_test(NAME guest_ajm_teardown COMMAND guest_ajm_teardown_tests $<TARGET_FILE:libSceAjm.native>)
endif()
if(WIN32)
set_tests_properties(guest_ajm guest_ajm_vibration PROPERTIES ENVIRONMENT_MODIFICATION
set_tests_properties(guest_ajm guest_ajm_vibration guest_ajm_wvorbis PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceAjm.native>")
endif()
endif()
+72 -2
View File
@@ -4,6 +4,7 @@
#include "prx/libc/include/General.hpp"
#include <atomic>
#include <cstdio>
#include <cstring>
// Entry points this library does not implement: reported before the unimplemented-call exception so an
// APS5_TRACE_AJM run shows which one a title reached.
@@ -43,6 +44,38 @@ static_assert(offsetof(WVorbisHeaderPacketParameter, setupPacket) == 32);
static_assert(sizeof(WVorbisSetupPacketParameter) == 56);
static_assert(offsetof(WVorbisSetupPacketParameter, input) == 16);
namespace {
constexpr int AJM_WVORBIS_ERROR_INVALID_PARAMETER = static_cast<int>(0x8A6E0001);
constexpr int AJM_WVORBIS_ERROR_INVALID_SIZE = static_cast<int>(0x8A6E0002);
constexpr int AJM_WVORBIS_ERROR_OUTPUT_TOO_SMALL = static_cast<int>(0x8A6E0003);
constexpr int AJM_WVORBIS_ERROR_INVALID_SETUP = static_cast<int>(0x8A6E0004);
constexpr std::size_t WVORBIS_LENGTH_TABLE_SIZE = 10;
constexpr std::size_t VORBIS_IDENTIFICATION_SIZE = 30;
constexpr std::size_t VORBIS_COMMENT_SIZE = 16;
constexpr std::size_t WVORBIS_SETUP_MIN_SIZE = 9;
constexpr std::size_t WVORBIS_SETUP_PART1_LENGTH_OFFSET = 7;
constexpr std::int32_t VORBIS_BLOCK_SIZE_MIN = 64;
constexpr std::int32_t VORBIS_BLOCK_SIZE_MAX = 8192;
constexpr std::uint8_t VORBIS_MAGIC[] {'v', 'o', 'r', 'b', 'i', 's'};
std::int32_t WVorbisBlockSize(std::int32_t log2) {
return static_cast<std::int32_t>(1u << (static_cast<std::uint32_t>(log2) & 31u));
}
std::uint8_t* PutLittle(std::uint8_t* out, std::uint32_t value, std::size_t bytes) {
for (std::size_t i = 0; i < bytes; ++i) out[i] = static_cast<std::uint8_t>(value >> (8 * i));
return out + bytes;
}
std::uint8_t* PutVorbisHeaderStart(std::uint8_t* out, std::uint8_t type) {
*out++ = type;
std::memcpy(out, VORBIS_MAGIC, sizeof(VORBIS_MAGIC));
return out + sizeof(VORBIS_MAGIC);
}
}
extern "C" {
int APS5_VABI sceAjmBatchJobEncode(AjmBatchInfo* info, uint32_t instance, const void* pcm_input, size_t pcm_input_size, void* bitstream_output, size_t bitstream_output_size, void* result) {
@@ -63,8 +96,45 @@ const char* APS5_VABI sceAjmStrError(int error) {
return nullptr;
}
int APS5_VABI sceAjmDecWVorbisCreateHeaderPacket(WVorbisHeaderPacketParameter* parameter) {
NotImplemented_nid_no_patch(__func__);
int APS5_VABI sceAjmDecWVorbisCreateHeaderPacket(WVorbisHeaderPacketParameter* param) {
if (param == nullptr) return AJM_WVORBIS_ERROR_INVALID_PARAMETER;
if (param->size != sizeof(WVorbisHeaderPacketParameter)) return AJM_WVORBIS_ERROR_INVALID_SIZE;
if (param->bytesPerSecond < 0) return AJM_WVORBIS_ERROR_INVALID_PARAMETER;
if (WVorbisBlockSize(param->shortBlockExponent) < VORBIS_BLOCK_SIZE_MIN) return AJM_WVORBIS_ERROR_INVALID_PARAMETER;
if (param->longBlockExponent < param->shortBlockExponent || WVorbisBlockSize(param->longBlockExponent) > VORBIS_BLOCK_SIZE_MAX)
return AJM_WVORBIS_ERROR_INVALID_PARAMETER;
if (param->setupPacket == nullptr) return AJM_WVORBIS_ERROR_INVALID_PARAMETER;
if (param->setupPacketSize < WVORBIS_SETUP_MIN_SIZE) return AJM_WVORBIS_ERROR_INVALID_SETUP;
if (param->output == nullptr) return AJM_WVORBIS_ERROR_INVALID_PARAMETER;
constexpr std::size_t headers = WVORBIS_LENGTH_TABLE_SIZE + VORBIS_IDENTIFICATION_SIZE + VORBIS_COMMENT_SIZE;
if (param->outputCapacity < headers || param->setupPacketSize > param->outputCapacity - headers) return AJM_WVORBIS_ERROR_OUTPUT_TOO_SMALL;
const auto* setup = static_cast<const std::uint8_t*>(param->setupPacket);
const auto part1 = static_cast<std::uint16_t>(setup[WVORBIS_SETUP_PART1_LENGTH_OFFSET] | setup[WVORBIS_SETUP_PART1_LENGTH_OFFSET + 1] << 8);
auto* out = static_cast<std::uint8_t*>(param->output);
out = PutLittle(out, 0, 2);
out = PutLittle(out, VORBIS_IDENTIFICATION_SIZE, 2);
out = PutLittle(out, VORBIS_COMMENT_SIZE, 2);
out = PutLittle(out, part1, 2);
out = PutLittle(out, static_cast<std::uint16_t>(param->setupPacketSize) - part1, 2);
out = PutVorbisHeaderStart(out, 1);
out = PutLittle(out, 0, 4);
out = PutLittle(out, static_cast<std::uint32_t>(param->channels), 1);
out = PutLittle(out, param->sampleRate, 4);
out = PutLittle(out, 0, 4);
out = PutLittle(out, static_cast<std::uint32_t>(param->bytesPerSecond) * 8u, 4);
out = PutLittle(out, 0, 4);
*out++ = static_cast<std::uint8_t>((param->shortBlockExponent & 0xF) | (param->longBlockExponent & 0xF) << 4);
*out++ = 1;
out = PutVorbisHeaderStart(out, 3);
out = PutLittle(out, 0, 4);
out = PutLittle(out, 0, 4);
*out++ = 1;
std::memcpy(out, setup, param->setupPacketSize);
param->outputSize = headers + param->setupPacketSize;
return 0;
}
@@ -22,6 +22,15 @@ if(BUILD_TESTING)
set_tests_properties(guest_audio3d_port PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceAudio3d>")
endif()
add_test_executable(guest_audio3d_speaker_array_tests ${LIBS_INCLUDE_DIR}/tests/GuestAudio3dSpeakerArray.cpp)
target_include_directories(guest_audio3d_speaker_array_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_audio3d_speaker_array_tests PRIVATE libSceAudio3d libSceAudioOut libc)
configure_windows_unwind(guest_audio3d_speaker_array_tests)
add_test(NAME guest_audio3d_speaker_array COMMAND guest_audio3d_speaker_array_tests)
if(WIN32)
set_tests_properties(guest_audio3d_speaker_array PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceAudio3d>")
endif()
add_test_executable(guest_audio3d_mixer_tests ${LIBS_INCLUDE_DIR}/tests/GuestAudio3dMixer.cpp)
target_include_directories(guest_audio3d_mixer_tests PRIVATE
${LIBS_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
+28 -15
View File
@@ -472,29 +472,42 @@ int APS5_VABI sceAudio3dReportUnregisterHandler(void) {
return 0;
}
int APS5_VABI sceAudio3dGetSpeakerArrayMemorySize(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
size_t APS5_VABI sceAudioOut2GetSpeakerArrayMemorySize(uint32_t num_speakers, uint8_t is_3d, uint8_t is_ambisonics);
int APS5_VABI sceAudioOut2SpeakerArrayCreate(AudioOut2SpeakerArrayHandle* handle, const void* vbap_params, const void* ambi_params);
int APS5_VABI sceAudioOut2SpeakerArrayDestroy(AudioOut2SpeakerArrayHandle handle);
int APS5_VABI sceAudioOut2GetSpeakerArrayCoefficients(AudioOut2SpeakerArrayHandle handle, AudioOut2Position pos, float spread, float* coefficients, uint32_t num_coefficients, uint8_t height_aware, float downmix_spread_radius);
struct Audio3dSpeakerArrayParameters {
const AudioOut2Position* positions;
std::uint32_t numSpeakers;
std::uint8_t is3d;
void* memory;
std::size_t memorySize;
};
static_assert(sizeof(Audio3dSpeakerArrayParameters) == 0x20);
size_t APS5_VABI sceAudio3dGetSpeakerArrayMemorySize(uint32_t num_speakers, uint8_t is_3d) {
return sceAudioOut2GetSpeakerArrayMemorySize(num_speakers, is_3d, 0);
}
int APS5_VABI sceAudio3dCreateSpeakerArray(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceAudio3dCreateSpeakerArray(AudioOut2SpeakerArrayHandle* handle, const Audio3dSpeakerArrayParameters* parameters) {
if (!handle || !parameters) return AUDIO3D_ERROR_INVALID_PARAMETER;
unsigned char extended[0x28] = {};
std::memcpy(extended, parameters, sizeof(*parameters));
return sceAudioOut2SpeakerArrayCreate(handle, extended, nullptr);
}
int APS5_VABI sceAudio3dDeleteSpeakerArray(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceAudio3dDeleteSpeakerArray(AudioOut2SpeakerArrayHandle handle) {
return sceAudioOut2SpeakerArrayDestroy(handle);
}
int APS5_VABI sceAudio3dGetSpeakerArrayMixCoefficients(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceAudio3dGetSpeakerArrayMixCoefficients(AudioOut2SpeakerArrayHandle handle, AudioOut2Position pos, float spread, float* coefficients, uint32_t num_coefficients) {
return sceAudioOut2GetSpeakerArrayCoefficients(handle, pos, spread, coefficients, num_coefficients, 0, 0.1f);
}
int APS5_VABI sceAudio3dGetSpeakerArrayMixCoefficients2(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceAudio3dGetSpeakerArrayMixCoefficients2(AudioOut2SpeakerArrayHandle handle, AudioOut2Position pos, float spread, float* coefficients, uint32_t num_coefficients, uint8_t height_aware, float downmix_spread_radius) {
return sceAudioOut2GetSpeakerArrayCoefficients(handle, pos, spread, coefficients, num_coefficients, height_aware, downmix_spread_radius);
}
}
+3 -3
View File
@@ -191,9 +191,9 @@ int32_t APS5_VABI sceAudioInAsyncOpen(int32_t userId, uint32_t type, uint32_t in
}
int32_t APS5_VABI sceAudioInIsPadEmbeddedMic(int32_t handle) {
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
std::lock_guard lock(g_mutex);
if (!find(handle)) return AUDIO_IN_ERROR_INVALID_HANDLE;
return 0;
}
int32_t APS5_VABI sceAudioInGetGain(int32_t handle, float* gain) {
@@ -47,6 +47,21 @@ if(BUILD_TESTING)
endif()
endforeach()
add_test_executable(audio_out_buffer_size_tests tests/AudioOutBufferSize.cpp src/AudioOut.cpp src/AudioOut2PadMix.cpp src/AudioOutShutdown.cpp)
target_compile_definitions(audio_out_buffer_size_tests PRIVATE SDL_MAIN_HANDLED)
target_include_directories(audio_out_buffer_size_tests PRIVATE ${LIBS_INCLUDE_DIR} ${ANYPS5_SDL2_INCLUDE_DIR})
target_link_libraries(audio_out_buffer_size_tests PRIVATE libc libkernel libScePad)
configure_windows_unwind(audio_out_buffer_size_tests)
foreach(bufferSizeMode IN ITEMS pass_through wrap mix threshold)
set(bufferSizeTest "audio_out_buffer_size_${bufferSizeMode}")
add_test(NAME ${bufferSizeTest} COMMAND audio_out_buffer_size_tests ${bufferSizeMode})
set_tests_properties(${bufferSizeTest} PROPERTIES TIMEOUT 5)
if(WIN32)
set_tests_properties(${bufferSizeTest} PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endforeach()
add_test_executable(audio_out_vibration_tests tests/AudioOutVibration.cpp)
target_include_directories(audio_out_vibration_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(audio_out_vibration_tests PRIVATE libSceAudioOut libScePad libc)
@@ -22,6 +22,11 @@ int APS5_VABI sceAudioOut2MasteringInit(uint32_t flags) {
return 0;
}
int APS5_VABI sceAudioOut2MasteringInitEx(uint32_t flags) {
if ((flags & 0x7FFFFFFFu) != 0) NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAudioOut2MasteringTerm(void) {
return 0;
}
@@ -68,7 +68,7 @@ extern "C" {
int APS5_VABI sceAudioOut2SpeakerArrayCreate(AudioOut2SpeakerArrayHandle* handle, const void* vbap_params, const void* ambi_params) {
const auto* params = static_cast<const GuestVbapParams*>(vbap_params);
if (handle == nullptr || params == nullptr || ambi_params == nullptr || params->positions == nullptr ||
if (handle == nullptr || params == nullptr || params->positions == nullptr ||
params->numSpeakers == 0 || params->numSpeakers > MaxSpeakers || params->memory == nullptr ||
params->memorySize < sizeof(SpeakerArray) || reinterpret_cast<std::uintptr_t>(params->memory) % alignof(SpeakerArray) != 0) {
APS5_INVALID_ARG_EX;
@@ -1,4 +1,5 @@
#include <atomic>
#include <cstddef>
#include <cstdint>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
@@ -33,7 +34,15 @@ int APS5_VABI sceAudioOut2GetSystemState(AudioOut2SystemState* state) {
}
int APS5_VABI sceAudioOut2SetSystemDebugState(const AudioOut2SystemDebugStateParam* param) {
if (param == nullptr) return static_cast<int>(0x80260502);
constexpr int ErrorInvalidPointer = static_cast<int>(0x8026800C);
constexpr std::uint32_t DebugStateIdLoudness = 1;
constexpr std::uint32_t DebugStateIdUnmodelled = 0x20;
constexpr std::size_t LoudnessParamSize = 8;
constexpr std::size_t UnmodelledParamSize = 4;
if (param == nullptr) return ErrorInvalidPointer;
if (param->debug_state_id == DebugStateIdUnmodelled && param->param_size == UnmodelledParamSize) NotImplemented_nid_no_patch(__func__);
if (param->debug_state_id != DebugStateIdLoudness || param->param_size != LoudnessParamSize) return SCE_AUDIO_OUT2_ERROR_INVALID_PARAM;
if (param->param == nullptr) NotImplemented_nid_no_patch(__func__);
AUDIOOUT2_TRACE("t=%.3f SetSystemDebugState: debug state %u recorded; the host has no system mix to apply it to\n", AudioOut2TraceSeconds(), param->debug_state_id);
return 0;
}
@@ -365,11 +365,6 @@ int APS5_VABI sceAudioOut2GetSystemStateEx(void) {
return 0;
}
int APS5_VABI sceAudioOut2MasteringInitEx(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAudioOut2SetDebugHeadphoneMode(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -14,6 +14,7 @@ int APS5_VABI sceAudioOut2LoInit();
int APS5_VABI sceAudioOut2LoTerminate();
int APS5_VABI sceAudioOut2Set3DLatency(int, std::uint32_t);
int APS5_VABI sceAudioOut2MasteringInit(std::uint32_t);
int APS5_VABI sceAudioOut2MasteringInitEx(std::uint32_t);
int APS5_VABI sceAudioOut2MasteringTerm();
int APS5_VABI sceAudioOut2MasteringSetParam(const void*, std::uint32_t, std::uint32_t);
int APS5_VABI sceAudioOut2MasteringGetState(void*, std::uint32_t, std::uint64_t);
@@ -49,7 +50,7 @@ static_assert(sizeof(AudioOut2MasteringStatesV2) == 400);
constexpr int systemUser = 0xFF;
constexpr int user = 0x10000000;
constexpr int errorInvalidParam = static_cast<int>(0x80268001);
constexpr int errorInvalidArgument = static_cast<int>(0x80260502);
constexpr int errorInvalidPointer = static_cast<int>(0x8026800C);
constexpr int errorMasteringInvalidApiParam = static_cast<int>(0x80268201);
constexpr int errorMasteringInvalidStatesId = static_cast<int>(0x80268204);
@@ -69,6 +70,10 @@ void TestSet3DLatency() {
void TestMasteringInit() {
Require(sceAudioOut2MasteringInit(0) == 0, "flags 0 must be accepted");
Require(ThrowsRuntimeError([] { sceAudioOut2MasteringInit(1); }), "flags 1 must throw");
Require(sceAudioOut2MasteringInitEx(0) == 0, "InitEx flags 0 must be accepted");
Require(sceAudioOut2MasteringInitEx(0x80000000u) == 0, "InitEx flag bit 31 must be accepted");
Require(ThrowsRuntimeError([] { sceAudioOut2MasteringInitEx(1); }), "InitEx flag bit 0 must throw");
Require(ThrowsRuntimeError([] { sceAudioOut2MasteringInitEx(0x40000000u); }), "InitEx flag bit 30 must throw");
}
void TestMasteringTerm() {
@@ -109,10 +114,25 @@ void TestMasteringGetState() {
}
void TestSetSystemDebugState() {
const std::uint64_t param[4] = {1, 0, 0, 0};
Require(sceAudioOut2SetSystemDebugState(reinterpret_cast<const void*>(param)) == 0, "a debug state must be accepted");
Require(sceAudioOut2SetSystemDebugState(reinterpret_cast<const void*>(param)) == 0, "a debug state must be accepted again");
Require(sceAudioOut2SetSystemDebugState(nullptr) == errorInvalidArgument, "a null debug state is refused");
std::uint32_t loudness[2] = {1, 0};
const auto set = [&loudness](std::uint32_t id, void* data, std::size_t size) {
const AudioOut2SystemDebugStateParam param{id, 0, data, size};
return sceAudioOut2SetSystemDebugState(&param);
};
Require(set(1, loudness, 8) == 0, "the loudness debug state must be accepted");
Require(set(1, loudness, 8) == 0, "the loudness debug state must be accepted again");
Require(set(1, loudness, 0) == errorInvalidParam, "a loudness debug state of size 0 is refused");
Require(set(1, loudness, 4) == errorInvalidParam, "a loudness debug state of size 4 is refused");
Require(set(1, loudness, 16) == errorInvalidParam, "a loudness debug state of size 16 is refused");
Require(set(0, loudness, 8) == errorInvalidParam, "debug state id 0 is refused");
Require(set(2, loudness, 8) == errorInvalidParam, "debug state id 2 is refused");
Require(set(0x21, loudness, 8) == errorInvalidParam, "debug state id 0x21 is refused");
Require(set(0xFFFFFFFFu, loudness, 8) == errorInvalidParam, "a large debug state id is refused");
Require(set(0x20, loudness, 8) == errorInvalidParam, "debug state id 0x20 of size 8 is refused");
Require(set(0x20, loudness, 0) == errorInvalidParam, "debug state id 0x20 of size 0 is refused");
Require(ThrowsRuntimeError([&] { set(0x20, loudness, 4); }), "debug state id 0x20 of size 4 must throw");
Require(ThrowsRuntimeError([&] { set(1, nullptr, 8); }), "a loudness debug state without a param must throw");
Require(sceAudioOut2SetSystemDebugState(nullptr) == errorInvalidPointer, "a null debug state is refused");
}
void TestEnableChat() {
@@ -0,0 +1,100 @@
#include "SDL.h"
#include "prx/libSceAudioOut/include/AudioOutSdl.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <stdexcept>
#include <string_view>
#include <vector>
extern "C" {
int APS5_VABI sceAudioOutOpen(int, int, int, std::uint32_t, std::uint32_t, std::uint32_t);
int APS5_VABI sceAudioOutClose(int);
int APS5_VABI sceAudioOutOutput(int, const void*);
}
namespace {
constexpr SDL_AudioDeviceID device = 7;
int opened = 0;
int closed = 0;
int queued = 0;
std::uint32_t queuedBytes = 0;
const void* supplied = nullptr;
std::uint32_t suppliedBytes = 0;
void Require(bool value, const char* message) {
if (!value) throw std::runtime_error(message);
}
void RunPassThrough() {
std::vector<float> samples(512u * 8u);
for (std::size_t i = 0; i < samples.size(); ++i) samples[i] = static_cast<float>(i % 97u) / 97.0f;
const int handle = sceAudioOutOpen(0, 0, 0, 512, 48000, 5);
Require(handle > 0 && opened == 1, "a 512-frame F32 8-channel port did not open its device");
supplied = samples.data();
suppliedBytes = static_cast<std::uint32_t>(samples.size() * sizeof(float));
Require(sceAudioOutOutput(handle, samples.data()) == 512, "a 512-frame output did not return 512");
Require(queued == 1 && queuedBytes == 16384, "a 512-frame F32 8-channel output did not queue 16384 bytes");
Require(sceAudioOutClose(handle) == 0 && closed == 1, "the port did not close its device");
}
void RunOverflow(std::uint32_t frames, std::uint32_t format, std::uint32_t frameBytes) {
std::vector<std::uint8_t> frame(frameBytes);
const int handle = sceAudioOutOpen(0, 0, 0, frames, 48000, format);
Require(handle > 0 && opened == 1, "a port with an oversized length did not open its device");
supplied = frame.data();
suppliedBytes = frameBytes;
bool threw = false;
try {
static_cast<void>(sceAudioOutOutput(handle, frame.data()));
} catch (const std::runtime_error&) {
threw = true;
}
Require(threw, "an output whose buffer size overflows did not throw");
Require(queued == 0, "an output whose buffer size overflows queued audio");
Require(sceAudioOutClose(handle) == 0 && closed == 1, "the port did not close after the refused output");
}
}
int InitializeSdlAudio_nid_no_patch() { return 0; }
extern "C" Uint32 SDLCALL SDL_WasInit(Uint32) { return SDL_INIT_AUDIO; }
extern "C" int SDLCALL SDL_GetNumAudioDevices(int) { return 1; }
extern "C" const char* SDLCALL SDL_GetAudioDeviceName(int, int) { return "Main speakers"; }
extern "C" int SDLCALL SDL_GetAudioDeviceSpec(int, int, SDL_AudioSpec* spec) { *spec = {}; spec->channels = 2; return 0; }
extern "C" const char* SDLCALL SDL_GetCurrentAudioDriver() { return "dummy"; }
extern "C" const char* SDLCALL SDL_GetError() { return "test error"; }
extern "C" SDL_AudioDeviceID SDLCALL SDL_OpenAudioDevice(const char* name, int capture, const SDL_AudioSpec* desired, SDL_AudioSpec* obtained, int) {
Require(name == nullptr && capture == 0 && desired->callback == nullptr, "the main port did not open the default device");
++opened;
*obtained = *desired;
return device;
}
extern "C" void SDLCALL SDL_PauseAudioDevice(SDL_AudioDeviceID id, int) { Require(id == device, "wrong device paused"); }
extern "C" Uint32 SDLCALL SDL_GetQueuedAudioSize(SDL_AudioDeviceID id) { Require(id == device, "wrong queue queried"); return 0; }
extern "C" int SDLCALL SDL_QueueAudio(SDL_AudioDeviceID id, const void* data, Uint32 bytes) {
Require(id == device, "wrong queue fed");
++queued;
queuedBytes = bytes;
if (bytes <= suppliedBytes) Require(std::memcmp(data, supplied, bytes) == 0, "the queued audio is not the supplied samples");
return 0;
}
extern "C" void SDLCALL SDL_ClearQueuedAudio(SDL_AudioDeviceID id) { Require(id == device, "wrong queue cleared"); }
extern "C" void SDLCALL SDL_CloseAudioDevice(SDL_AudioDeviceID id) { Require(id == device, "wrong device closed"); ++closed; }
int main(int argc, char** argv) {
try {
Require(argc == 2, "invalid test arguments");
const std::string_view mode = argv[1];
if (mode == "pass_through") RunPassThrough();
else if (mode == "wrap") RunOverflow(0x80000000u, 5, 8u * sizeof(float));
else if (mode == "mix") RunOverflow(0x80000000u, 7, 8u * sizeof(float));
else if (mode == "threshold") RunOverflow(0x40000000u, 0, sizeof(std::int16_t));
else Require(false, "invalid buffer size mode");
std::puts("audio out buffer size tests passed");
return 0;
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());
return 1;
}
}
+4 -4
View File
@@ -21,10 +21,10 @@ constexpr int CES_ERROR_UNASSIGNED_CODE = static_cast<int>(0x805C0020);
constexpr int CES_ERROR_INVALID_DST_BUFFER = static_cast<int>(0x805C0030);
constexpr int CES_ERROR_DST_BUFFER_END = static_cast<int>(0x805C0031);
constexpr uint8_t PROFILES[3] = {0, 1, 2};
constexpr const uint8_t* CP1252_PROFILE = &PROFILES[0];
constexpr const uint8_t* CP1251_PROFILE = &PROFILES[1];
constexpr const uint8_t* CP1250_PROFILE = &PROFILES[2];
constexpr uint8_t PROFILES[3][8] = {{0}, {1}, {2}};
constexpr const uint8_t* CP1252_PROFILE = PROFILES[0];
constexpr const uint8_t* CP1251_PROFILE = PROFILES[1];
constexpr const uint8_t* CP1250_PROFILE = PROFILES[2];
constexpr uint16_t CP1252_HIGH[32] = {
0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F,
@@ -248,6 +248,7 @@ inline bool ValidateCommonDialogBaseParam(const CommonDialogBaseParam& param) {
}
constexpr int COMMON_DIALOG_OK = 0;
constexpr int COMMON_DIALOG_ERROR_ALREADY_SYSTEM_INITIALIZED = static_cast<int>(0x80B80002);
constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast<int>(0x80B80004);
#endif
+1 -1
View File
@@ -12,7 +12,7 @@ extern "C" {
int APS5_VABI sceCommonDialogInitialize(void) {
bool expected = false;
if (!g_initialized.compare_exchange_strong(expected, true)) {
return COMMON_DIALOG_ERROR_ALREADY_INITIALIZED;
return COMMON_DIALOG_ERROR_ALREADY_SYSTEM_INITIALIZED;
}
return COMMON_DIALOG_OK;
}
+1 -2
View File
@@ -78,8 +78,7 @@ int APS5_VABI sceFontFtSupportTrueType(FontLibrary library) {
}
int APS5_VABI sceFontFtSupportTrueTypeGx(FontLibrary library) {
NotImplemented_nid_no_patch(__func__);
return 0;
return FontFt::SupportFormats(library, {"truetype"});
}
int APS5_VABI sceFontFtSupportType1(FontLibrary library) {
@@ -1,6 +1,7 @@
add_library(libSceFreeType SHARED EXCLUDE_FROM_ALL
Export.cpp
src/GuestObjects.cpp
src/Names.cpp
)
target_include_directories(libSceFreeType PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(libSceFreeType PRIVATE freetype)
@@ -20,7 +21,21 @@ if(BUILD_TESTING)
target_link_libraries(guest_freetype_charmap_tests PRIVATE libSceFreeType freetype libc)
configure_windows_unwind(guest_freetype_charmap_tests)
add_test(NAME guest_freetype_charmap COMMAND guest_freetype_charmap_tests)
add_test_executable(guest_freetype_names_tests ${LIBS_INCLUDE_DIR}/tests/GuestFreeTypeNames.cpp)
target_include_directories(guest_freetype_names_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_freetype_names_tests PRIVATE libSceFreeType freetype libc)
configure_windows_unwind(guest_freetype_names_tests)
add_test(NAME guest_freetype_names COMMAND guest_freetype_names_tests)
add_test_executable(guest_freetype_sfnt_names_tests ${LIBS_INCLUDE_DIR}/tests/GuestFreeTypeSfntNames.cpp)
target_include_directories(guest_freetype_sfnt_names_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_freetype_sfnt_names_tests PRIVATE libSceFreeType freetype libc)
configure_windows_unwind(guest_freetype_sfnt_names_tests)
add_test(NAME guest_freetype_sfnt_names COMMAND guest_freetype_sfnt_names_tests)
if(WIN32)
set_tests_properties(guest_freetype_sfnt_names PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceFreeType>")
set_tests_properties(guest_freetype_names PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceFreeType>")
set_tests_properties(guest_freetype PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceFreeType>")
set_tests_properties(guest_freetype_charmap PROPERTIES ENVIRONMENT_MODIFICATION
+38
View File
@@ -149,6 +149,28 @@ int APS5_VABI FT_Set_Pixel_Sizes_nid_postfix(GuestFaceRec* face, std::uint32_t p
return error;
}
int APS5_VABI FT_Set_Char_Size_nid_postfix(GuestFaceRec* face, std::int64_t charWidth, std::int64_t charHeight,
std::uint32_t horizontalResolution, std::uint32_t verticalResolution) {
GuestFace* owner = FaceOf(face);
if (!owner) return FT_Err_Invalid_Face_Handle;
if (!owner->host->size) return FT_Err_Invalid_Size_Handle;
if (!charWidth) charWidth = charHeight;
else if (!charHeight) charHeight = charWidth;
if (charWidth < 64) charWidth = 64;
if (charHeight < 64) charHeight = 64;
if (!horizontalResolution) horizontalResolution = verticalResolution;
else if (!verticalResolution) verticalResolution = horizontalResolution;
if (!horizontalResolution) horizontalResolution = verticalResolution = 72;
const auto maximum = std::numeric_limits<FT_Long>::max();
if (charWidth > (maximum - 36) / horizontalResolution || charHeight > (maximum - 36) / verticalResolution) {
throw std::runtime_error("FT_Set_Char_Size: a character size or resolution outside the host arithmetic range is not implemented");
}
const FT_Error error = FT_Set_Char_Size(owner->host, static_cast<FT_F26Dot6>(charWidth), static_cast<FT_F26Dot6>(charHeight),
horizontalResolution, verticalResolution);
SyncSize(*owner);
return error;
}
std::uint32_t APS5_VABI FT_Get_Char_Index_nid_postfix(GuestFaceRec* face, std::uint64_t charcode) {
const GuestFace* owner = FaceOf(face);
return FT_Get_Char_Index(owner ? owner->host : nullptr, static_cast<FT_ULong>(static_cast<std::uint32_t>(charcode)));
@@ -205,4 +227,20 @@ std::uint64_t APS5_VABI FT_Get_Next_Char_nid_postfix(GuestFaceRec* face, std::ui
return FT_Get_Next_Char(owner ? owner->host : nullptr, static_cast<FT_ULong>(static_cast<std::uint32_t>(charcode)), glyphIndex);
}
void APS5_VABI FT_Set_Transform_nid_postfix(GuestFaceRec* face, const GuestMatrix* matrix, const GuestVector* delta) {
GuestFace* owner = FaceOf(face);
if (!owner) return;
const auto component = [](std::int64_t value) {
if (value < std::numeric_limits<FT_Long>::min() || value > std::numeric_limits<FT_Long>::max()) {
throw std::runtime_error("FT_Set_Transform: a component outside the host FT_Long range is not implemented");
}
return static_cast<FT_Long>(value);
};
FT_Matrix nativeMatrix{};
FT_Vector nativeDelta{};
if (matrix) nativeMatrix = {component(matrix->xx), component(matrix->xy), component(matrix->yx), component(matrix->yy)};
if (delta) nativeDelta = {component(delta->x), component(delta->y)};
FT_Set_Transform(owner->host, matrix ? &nativeMatrix : nullptr, delta ? &nativeDelta : nullptr);
}
}
@@ -18,6 +18,13 @@ struct GuestVector {
std::int64_t y;
};
struct GuestMatrix {
std::int64_t xx;
std::int64_t xy;
std::int64_t yx;
std::int64_t yy;
};
struct GuestBBox {
std::int64_t xMin;
std::int64_t yMin;
@@ -40,6 +47,15 @@ struct GuestCharMapRec {
std::uint16_t encoding_id;
};
struct GuestSfntName {
std::uint16_t platform_id;
std::uint16_t encoding_id;
std::uint16_t language_id;
std::uint16_t name_id;
unsigned char* string;
std::uint32_t string_len;
};
struct GuestSizeMetrics {
std::uint16_t x_ppem;
std::uint16_t y_ppem;
@@ -149,9 +165,11 @@ struct GuestFaceRec {
void* internal;
};
static_assert(sizeof(GuestMatrix) == 32 && offsetof(GuestMatrix, xy) == 8 && offsetof(GuestMatrix, yx) == 16 && offsetof(GuestMatrix, yy) == 24);
static_assert(sizeof(GuestGeneric) == 16 && offsetof(GuestGeneric, finalizer) == 8);
static_assert(sizeof(GuestBitmapSize) == 32 && offsetof(GuestBitmapSize, size) == 8);
static_assert(sizeof(GuestCharMapRec) == 16 && offsetof(GuestCharMapRec, platform_id) == 12);
static_assert(sizeof(GuestSfntName) == 24 && offsetof(GuestSfntName, string) == 8 && offsetof(GuestSfntName, string_len) == 16);
static_assert(sizeof(GuestSizeMetrics) == 56 && offsetof(GuestSizeMetrics, x_scale) == 8);
static_assert(sizeof(GuestSizeRec) == 88 && offsetof(GuestSizeRec, metrics) == 24);
static_assert(sizeof(GuestBitmap) == 40 && offsetof(GuestBitmap, buffer) == 16 && offsetof(GuestBitmap, palette) == 32);
@@ -0,0 +1,55 @@
#include "prx/libSceFreeType/include/GuestObjects.hpp"
#include "prx/libc/include/GuestArena.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include FT_SFNT_NAMES_H
#include <cstdint>
using namespace FreeType;
extern "C" {
int APS5_VABI FT_Get_Glyph_Name_nid_postfix(GuestFaceRec* face, std::uint32_t glyphIndex, void* buffer, std::uint32_t bufferSize) {
const GuestFace* owner = FaceOf(face);
const FT_Face host = owner ? owner->host : nullptr;
if (!host || !buffer || bufferSize == 0) return FT_Get_Glyph_Name(host, glyphIndex, buffer, bufferSize);
GuestArena::HostWrite write(buffer, bufferSize);
if (static_cast<std::int64_t>(glyphIndex) >= host->num_glyphs) {
static_cast<char*>(buffer)[0] = '\0';
return FT_Err_Invalid_Glyph_Index;
}
return FT_Get_Glyph_Name(host, glyphIndex, buffer, bufferSize);
}
std::uint32_t APS5_VABI FT_Get_Name_Index_nid_postfix(GuestFaceRec* face, const char* glyphName) {
const GuestFace* owner = FaceOf(face);
return FT_Get_Name_Index(owner ? owner->host : nullptr, glyphName);
}
const char* APS5_VABI FT_Get_Postscript_Name_nid_postfix(GuestFaceRec* face) {
const GuestFace* owner = FaceOf(face);
return FT_Get_Postscript_Name(owner ? owner->host : nullptr);
}
std::uint32_t APS5_VABI FT_Get_Sfnt_Name_Count_nid_postfix(GuestFaceRec* face) {
const GuestFace* owner = FaceOf(face);
return FT_Get_Sfnt_Name_Count(owner ? owner->host : nullptr);
}
int APS5_VABI FT_Get_Sfnt_Name_nid_postfix(GuestFaceRec* face, std::uint32_t index, GuestSfntName* name) {
const GuestFace* owner = FaceOf(face);
FT_SfntName native{};
const FT_Error error = FT_Get_Sfnt_Name(owner ? owner->host : nullptr, index, name ? &native : nullptr);
if (!error) {
name->platform_id = native.platform_id;
name->encoding_id = native.encoding_id;
name->language_id = native.language_id;
name->name_id = native.name_id;
name->string = native.string;
name->string_len = native.string_len;
}
return error;
}
}
@@ -5,6 +5,8 @@
#include "prx/libSceUserService/UserService.hpp"
#include <cstring>
#include <mutex>
#include <stdexcept>
#include <string>
static std::mutex g_mutex;
static bool g_initialized = false;
@@ -271,8 +273,10 @@ int APS5_VABI sceGameLiveStreamingStopDebugBroadcast(void) {
return 0;
}
int APS5_VABI sceGameLiveStreamingEnableLiveStreaming(void) {
NotImplemented_nid_no_patch(__func__);
int APS5_VABI sceGameLiveStreamingEnableLiveStreaming(bool isEnableLiveStreaming) {
std::lock_guard lock(g_mutex);
if (!g_initialized) throw std::logic_error(std::string(__func__) + ": not initialized");
(void)isEnableLiveStreaming;
return 0;
}
+2
View File
@@ -1,11 +1,13 @@
add_library(libSceHmd SHARED EXCLUDE_FROM_ALL Export.cpp)
target_include_directories(libSceHmd PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(libSceHmd PRIVATE libc)
configure_windows_unwind(libSceHmd)
if(BUILD_TESTING)
add_test_executable(guest_hmd_tests ${LIBS_INCLUDE_DIR}/tests/GuestHmd.cpp)
target_include_directories(guest_hmd_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_hmd_tests PRIVATE libSceHmd)
configure_windows_unwind(guest_hmd_tests)
add_test(NAME guest_hmd COMMAND guest_hmd_tests)
if(WIN32)
set_tests_properties(guest_hmd PROPERTIES ENVIRONMENT_MODIFICATION
+55 -20
View File
@@ -1,7 +1,12 @@
#include "Hmd.hpp"
#include "prx/libc/include/ApplicationHeap.hpp"
#include "prx/libc/include/General.hpp"
#include <atomic>
#include <cstddef>
#include <cstring>
#include <initializer_list>
#include <string>
struct HmdDistortionCommandParameter;
struct HmdWideNearDistortionCommandParameter;
@@ -64,6 +69,36 @@ std::int32_t Initialize(const Hmd::InitializeParam* param) {
return initialized.exchange(true) ? Hmd::AlreadyInitialized : 0;
}
std::uint32_t CompiledSdkVersion() {
static const auto* processParameters = static_cast<const std::byte*>(ApplicationProcessParameters_nid_no_patch());
std::uint32_t version;
std::memcpy(&version, processParameters + 0x10, sizeof(version));
return version;
}
std::int32_t CommandQueryHandle(std::int32_t handle, const char* query) {
if (handle != 0) return Hmd::InvalidHandle;
if (initialized.load()) NotImplemented_nid_no_patch((std::string(query) + " with handle 0 after sceHmdInitialize").c_str());
if (CompiledSdkVersion() >= Hmd::StartupStateSdkVersion) NotImplemented_nid_no_patch((std::string(query) + " with handle 0 before sceHmdInitialize from SDK 4.50").c_str());
return Hmd::NotInitialized;
}
template <typename T>
T ParameterWord(const Hmd2dVrCommandParameter* parameter, std::size_t offset) {
T value;
std::memcpy(&value, reinterpret_cast<const unsigned char*>(parameter) + offset, sizeof(value));
return value;
}
std::int32_t Vr2dCommandArguments(const Hmd2dVrCommandParameter* parameter, const HmdCommandBuffer* commandBuffer) {
if (!parameter || !commandBuffer) return Hmd::ParameterNull;
for (const std::size_t offset : {0x00u, 0x08u, 0x20u}) {
if (ParameterWord<std::uint64_t>(parameter, offset) == 0) return Hmd::ParameterNull;
}
if (ParameterWord<std::uint32_t>(parameter, 0x34) != 0 || ParameterWord<std::uint64_t>(parameter, 0x38) != 0) return Hmd::ParameterInvalid;
return 0;
}
}
struct HmdEyeDistances {
@@ -428,8 +463,7 @@ int APS5_VABI sceHmdReprojectionStopCapture(HmdCaptureInfo* info) {
}
bool APS5_VABI sceHmdInternalIsCommonDlgMiniAppVr2d(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
return false;
}
int APS5_VABI sceHmdInternalMmapGetSensorCalibrationData(void* buffer, std::uint32_t size) {
@@ -438,8 +472,9 @@ int APS5_VABI sceHmdInternalMmapGetSensorCalibrationData(void* buffer, std::uint
}
int APS5_VABI sceHmdDistortionGetCorrectionCommand(std::int32_t handle, const HmdDistortionCommandParameter* parameter, HmdCommandBuffer* commandBuffer) {
NotImplemented_nid_no_patch(__func__);
return 0;
(void)parameter;
(void)commandBuffer;
return CommandQueryHandle(handle, __func__);
}
int APS5_VABI sceHmdReprojectionQueryOnionBuffAlign(void) {
@@ -452,13 +487,11 @@ int APS5_VABI sceHmdInternalGetDebugMode(std::uint8_t* mode) {
}
bool APS5_VABI sceHmdInternalIsMiniAppVr2d(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
return false;
}
bool APS5_VABI sceHmdInternalIsCommonDlgVr2d(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
return false;
}
int APS5_VABI sceHmdReprojectionSetOutputMinColor(float red, float green, float blue) {
@@ -572,8 +605,9 @@ int APS5_VABI sceHmdInternalGetHmuSerialNumber(std::uint8_t* serialNumber) {
}
int APS5_VABI sceHmdDistortionGetWideNearCorrectionCommand(std::int32_t handle, const HmdWideNearDistortionCommandParameter* parameter, HmdCommandBuffer* commandBuffer) {
NotImplemented_nid_no_patch(__func__);
return 0;
(void)parameter;
(void)commandBuffer;
return CommandQueryHandle(handle, __func__);
}
int APS5_VABI sceHmdInternalCheckS3dPassModeAvailable(bool* available) {
@@ -597,8 +631,7 @@ int APS5_VABI sceHmdReprojectionStartLiveCapture(const HmdLiveCaptureParameter*
}
bool APS5_VABI sceHmdInternalIsGameVr2d(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
return false;
}
int APS5_VABI sceHmdGetAssyError(HmdAssemblyError* data) {
@@ -727,8 +760,8 @@ int APS5_VABI sceHmdInternalMmapGetModeId(std::uint32_t* modeId) {
}
int APS5_VABI sceHmdDistortionGet2dVrCommand(std::int32_t handle, const Hmd2dVrCommandParameter* parameter, HmdCommandBuffer* commandBuffer) {
NotImplemented_nid_no_patch(__func__);
return 0;
if (const auto result = Vr2dCommandArguments(parameter, commandBuffer); result != 0) return result;
return CommandQueryHandle(handle, __func__);
}
int APS5_VABI sceHmdInternalDfuSendSize(std::int32_t size) {
@@ -737,8 +770,9 @@ int APS5_VABI sceHmdInternalDfuSendSize(std::int32_t size) {
}
int APS5_VABI sceHmdGetWideNearDistortionCorrectionCommand(std::int32_t handle, const HmdWideNearDistortionCommandParameter* parameter, HmdCommandBuffer* commandBuffer) {
NotImplemented_nid_no_patch(__func__);
return 0;
(void)parameter;
(void)commandBuffer;
return CommandQueryHandle(handle, __func__);
}
int APS5_VABI sceHmdReprojectionStartMultilayer2(const HmdReprojectionMultilayerParameter* layers, std::uint32_t layerCount, const HmdReprojectionSubmissionParameter* submission, std::uint64_t flipArg, const void* reserved) {
@@ -747,8 +781,9 @@ int APS5_VABI sceHmdReprojectionStartMultilayer2(const HmdReprojectionMultilayer
}
int APS5_VABI sceHmdGetDistortionCorrectionCommand(std::int32_t handle, const HmdDistortionCommandParameter* parameter, HmdCommandBuffer* commandBuffer) {
NotImplemented_nid_no_patch(__func__);
return 0;
(void)parameter;
(void)commandBuffer;
return CommandQueryHandle(handle, __func__);
}
int APS5_VABI sceHmdInternalSetDeviceConnection(std::uint64_t connection) {
@@ -962,8 +997,8 @@ int APS5_VABI sceHmdReprojectionQueryGarlicBuffSize(void) {
}
int APS5_VABI sceHmdGet2dVrCommand(std::int32_t handle, const Hmd2dVrCommandParameter* parameter, HmdCommandBuffer* commandBuffer) {
NotImplemented_nid_no_patch(__func__);
return 0;
if (const auto result = Vr2dCommandArguments(parameter, commandBuffer); result != 0) return result;
return CommandQueryHandle(handle, __func__);
}
}
+15
View File
@@ -15,6 +15,7 @@ inline constexpr std::int32_t DeviceDisconnected = static_cast<std::int32_t>(0x8
inline constexpr std::int32_t ParameterNull = static_cast<std::int32_t>(0x81110008);
inline constexpr std::int32_t ParameterInvalid = static_cast<std::int32_t>(0x81110009);
inline constexpr std::int32_t UnsupportedFeature = static_cast<std::int32_t>(0x81110016);
inline constexpr std::uint32_t StartupStateSdkVersion = 0x04500000;
enum class DeviceStatus : std::uint32_t {
Ready = 0,
@@ -55,6 +56,10 @@ struct HmdFieldOfView;
struct HmdEyeOffset;
struct HmdInertialSensorData;
struct HmdAssemblyError;
struct HmdDistortionCommandParameter;
struct HmdWideNearDistortionCommandParameter;
struct Hmd2dVrCommandParameter;
struct HmdCommandBuffer;
extern "C" {
std::int32_t APS5_VABI sceHmdInitialize(const Hmd::InitializeParam* param);
@@ -78,6 +83,16 @@ int APS5_VABI sceHmdGetFieldOfView(std::int32_t handle, HmdFieldOfView* fieldOfV
int APS5_VABI sceHmdGet2DEyeOffset(std::int32_t handle, HmdEyeOffset* leftOffset, HmdEyeOffset* rightOffset);
int APS5_VABI sceHmdGetInertialSensorData(std::int32_t handle, HmdInertialSensorData* data, std::uint32_t count);
int APS5_VABI sceHmdGetAssyError(HmdAssemblyError* data);
int APS5_VABI sceHmdDistortionGet2dVrCommand(std::int32_t handle, const Hmd2dVrCommandParameter* parameter, HmdCommandBuffer* commandBuffer);
int APS5_VABI sceHmdDistortionGetCorrectionCommand(std::int32_t handle, const HmdDistortionCommandParameter* parameter, HmdCommandBuffer* commandBuffer);
int APS5_VABI sceHmdDistortionGetWideNearCorrectionCommand(std::int32_t handle, const HmdWideNearDistortionCommandParameter* parameter, HmdCommandBuffer* commandBuffer);
int APS5_VABI sceHmdGet2dVrCommand(std::int32_t handle, const Hmd2dVrCommandParameter* parameter, HmdCommandBuffer* commandBuffer);
int APS5_VABI sceHmdGetDistortionCorrectionCommand(std::int32_t handle, const HmdDistortionCommandParameter* parameter, HmdCommandBuffer* commandBuffer);
int APS5_VABI sceHmdGetWideNearDistortionCorrectionCommand(std::int32_t handle, const HmdWideNearDistortionCommandParameter* parameter, HmdCommandBuffer* commandBuffer);
bool APS5_VABI sceHmdInternalIsGameVr2d(void);
bool APS5_VABI sceHmdInternalIsMiniAppVr2d(void);
bool APS5_VABI sceHmdInternalIsCommonDlgVr2d(void);
bool APS5_VABI sceHmdInternalIsCommonDlgMiniAppVr2d(void);
}
#endif
+12
View File
@@ -14,3 +14,15 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceHmd2>")
endif()
endif()
if(BUILD_TESTING)
add_test_executable(guest_hmd2_gaze_tests ${LIBS_INCLUDE_DIR}/tests/GuestHmd2Gaze.cpp)
target_include_directories(guest_hmd2_gaze_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_hmd2_gaze_tests PRIVATE libSceHmd2 libc)
configure_windows_unwind(guest_hmd2_gaze_tests)
add_test(NAME guest_hmd2_gaze COMMAND guest_hmd2_gaze_tests)
if(WIN32)
set_tests_properties(guest_hmd2_gaze PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceHmd2>")
endif()
endif()
+31 -5
View File
@@ -291,14 +291,40 @@ std::int32_t APS5_VABI sceHmd2Close(std::int32_t handle) {
return 0;
}
int APS5_VABI sceHmd2GazeGetResult(const Hmd2::GazeResultParameter* parameter, Hmd2::GazeResult* result) {
NotImplemented_nid_no_patch(__func__);
static std::int32_t Hmd2GazeHandle(const Hmd2::GazeResultParameter* param, const void* result, std::uint32_t resultSize, const char* name) {
if (!param || !result) return Hmd2::ParameterNull;
std::uint32_t size;
std::memcpy(&size, result, sizeof(size));
if (param->size != sizeof(Hmd2::GazeResultParameter) || size != resultSize) return Hmd2::ParameterInvalid;
std::lock_guard lock(stateMutex);
if (openHandle == 0 || param->handle != openHandle) return Hmd2::InvalidHandle;
NotImplemented_nid_no_patch(name);
return 0;
}
int APS5_VABI sceHmd2GazeGetResultForFoveatedRendering(const Hmd2::GazeResultParameter* parameter, Hmd2::FoveatedRenderingResult* result) {
NotImplemented_nid_no_patch(__func__);
return 0;
static void Hmd2StoreInvalidGaze(std::byte* result, std::size_t offset) {
const std::int32_t invalidIndex = -1;
const float invalidPair[2] = {-0.0f, 0.0f};
const float invalidScalar = -1.0f;
std::memcpy(result + 4, &invalidIndex, sizeof(invalidIndex));
std::memcpy(result + offset, invalidPair, sizeof(invalidPair));
std::memcpy(result + offset + sizeof(invalidPair), &invalidScalar, sizeof(invalidScalar));
}
int APS5_VABI sceHmd2GazeGetResult(const Hmd2::GazeResultParameter* param, Hmd2::GazeResult* result) {
const std::int32_t status = Hmd2GazeHandle(param, result, 0x38, __func__);
if (status != Hmd2::InvalidHandle) return status;
auto* bytes = reinterpret_cast<std::byte*>(result);
std::memset(bytes + 4, 0, 0x38 - 4);
Hmd2StoreInvalidGaze(bytes, 0x14);
return status;
}
int APS5_VABI sceHmd2GazeGetResultForFoveatedRendering(const Hmd2::GazeResultParameter* param, Hmd2::FoveatedRenderingResult* result) {
const std::int32_t status = Hmd2GazeHandle(param, result, 0x18, __func__);
if (status != Hmd2::InvalidHandle) return status;
Hmd2StoreInvalidGaze(reinterpret_cast<std::byte*>(result), 8);
return status;
}
std::int32_t APS5_VABI sceHmd2GetDeviceInformation(Hmd2::DeviceInformation* info) {
+10 -6
View File
@@ -317,9 +317,8 @@ int APS5_VABI sceHttpSendRequest(int request_id, const void* post_data, size_t s
}
int APS5_VABI sceHttpSetAuthEnabled(int id, int enable) {
(void)id;
(void)enable;
return 0;
if (!HttpInitialised()) return ERROR_BEFORE_INIT;
return SetHttpOption(id, &HttpOptions::authEnabled, enable != 0) ? 0 : ERROR_INVALID_ID;
}
int APS5_VABI sceHttpSetAuthInfoCallback(int id, HttpAuthInfoCallback callback, void* userArg) {
@@ -727,12 +726,17 @@ int APS5_VABI sceHttpAddRequestHeaderRaw(int id, const char* header, int mode) {
}
int APS5_VABI sceHttpRemoveRequestHeader(int id, const char* name) {
NotImplemented_nid_no_patch(__func__);
(void)id;
(void)name;
return 0;
}
int APS5_VABI sceHttpGetAuthEnabled(int id, int* enabled) {
NotImplemented_nid_no_patch(__func__);
int APS5_VABI sceHttpGetAuthEnabled(int id, int* enable) {
if (!HttpInitialised()) return ERROR_BEFORE_INIT;
if (!enable) return ERROR_INVALID_VALUE;
const auto options = FindHttpOptions(id);
if (!options) return ERROR_INVALID_ID;
*enable = options->authEnabled ? 1 : 0;
return 0;
}
+1
View File
@@ -6,6 +6,7 @@
struct HttpOptions {
bool autoRedirect = true;
bool authEnabled = false;
bool nonblock = false;
bool acceptEncodingGzip = false;
HttpEpollHandle epoll = nullptr;
+11 -10
View File
@@ -11,10 +11,10 @@ bool isAsciiDigit(char c) {
return c >= '0' && c <= '9';
}
bool parseVersionNumber(const char* line, size_t lineLen, size_t& index, int32_t* value) {
if (index >= lineLen || !isAsciiDigit(line[index])) return false;
bool parseVersionNumber(const char* line, size_t& index, int32_t* value) {
if (!isAsciiDigit(line[index])) return false;
uint32_t number = 0;
while (index < lineLen && isAsciiDigit(line[index])) {
while (isAsciiDigit(line[index])) {
number = number * 10 + static_cast<uint32_t>(line[index] - '0');
++index;
}
@@ -36,11 +36,11 @@ int APS5_VABI sceHttpParseStatusLine(const char* statusLine, size_t lineLen, int
if (lineLen < 8 || std::memcmp(statusLine, "HTTP/", 5) != 0) return ERROR_PARSE_HTTP_INVALID_RESPONSE;
size_t index = 5;
if (!parseVersionNumber(statusLine, lineLen, index, httpMajorVer)) return ERROR_PARSE_HTTP_INVALID_RESPONSE;
if (index >= lineLen || statusLine[index] != '.') return ERROR_PARSE_HTTP_INVALID_RESPONSE;
if (!parseVersionNumber(statusLine, index, httpMajorVer)) return ERROR_PARSE_HTTP_INVALID_RESPONSE;
if (statusLine[index] != '.') return ERROR_PARSE_HTTP_INVALID_RESPONSE;
++index;
if (!parseVersionNumber(statusLine, lineLen, index, httpMinorVer)) return ERROR_PARSE_HTTP_INVALID_RESPONSE;
if (index >= lineLen || statusLine[index] != ' ') return ERROR_PARSE_HTTP_INVALID_RESPONSE;
if (!parseVersionNumber(statusLine, index, httpMinorVer)) return ERROR_PARSE_HTTP_INVALID_RESPONSE;
if (statusLine[index] != ' ') return ERROR_PARSE_HTTP_INVALID_RESPONSE;
++index;
if (lineLen - index < 3) return ERROR_PARSE_HTTP_INVALID_RESPONSE;
@@ -51,10 +51,11 @@ int APS5_VABI sceHttpParseStatusLine(const char* statusLine, size_t lineLen, int
index += 3;
const char* phrase = statusLine + index;
const void* lineFeed = std::memchr(phrase, '\n', lineLen - index);
if (!lineFeed) return ERROR_PARSE_HTTP_INVALID_RESPONSE;
const size_t searched = lineLen - index + 1;
size_t length = 0;
while (length != searched && phrase[length] != '\n') ++length;
if (length == searched) return ERROR_PARSE_HTTP_INVALID_RESPONSE;
size_t length = static_cast<size_t>(static_cast<const char*>(lineFeed) - phrase);
const size_t consumed = index + length + 1;
if (consumed > static_cast<size_t>(INT32_MAX)) {
throw std::out_of_range("sceHttpParseStatusLine: consumed byte count exceeds int");
+78 -74
View File
@@ -10,6 +10,8 @@
#include "prx/libc/include/ApplicationHeap.hpp"
#include "prx/libSceHttp/src/HttpErrors.hpp"
extern "C" int APS5_VABI sceHttpUriSweepPath(char* dst, const char* src, size_t srcSize);
namespace {
constexpr uint32_t URI_BUILD_WITH_SCHEME = 0x01;
@@ -29,6 +31,7 @@ struct UriParts {
std::string_view password;
std::string_view hostname;
std::string path;
size_t pathSpace = 0;
std::string_view query;
std::string_view fragment;
uint16_t port = 0;
@@ -55,8 +58,8 @@ bool equalsIgnoreCase(std::string_view left, std::string_view right) {
}
uint16_t defaultPort(std::string_view scheme) {
if (equalsIgnoreCase(scheme, "http")) return 80;
if (equalsIgnoreCase(scheme, "https")) return 443;
if (scheme.size() >= 5 && equalsIgnoreCase(scheme.substr(0, 5), "https")) return 443;
if (scheme.size() >= 4 && equalsIgnoreCase(scheme.substr(0, 4), "http")) return 80;
return 0;
}
@@ -64,31 +67,21 @@ bool isHostCharacter(char c) {
return isAsciiAlpha(c) || isAsciiDigit(c) || c == '-' || c == '.' || c == '_';
}
std::string removeDotSegments(std::string_view input) {
std::string output;
while (!input.empty()) {
if (input.starts_with("../")) input.remove_prefix(3);
else if (input.starts_with("./")) input.remove_prefix(2);
else if (input.starts_with("/./")) input.remove_prefix(2);
else if (input == "/.") input = "/";
else if (input.starts_with("/../") || input == "/..") {
input = input.size() == 3 ? std::string_view("/") : input.substr(3);
const size_t slash = output.rfind('/');
output.erase(slash == std::string::npos ? 0 : slash);
} else if (input == "." || input == "..") input = {};
else {
const size_t end = input.find('/', 1);
const size_t length = end == std::string_view::npos ? input.size() : end;
output.append(input.substr(0, length));
input.remove_prefix(length);
}
}
return output;
bool isUserinfoCharacter(char c) {
return isAsciiAlpha(c) || isAsciiDigit(c) || std::string_view("-._~!$&'()*+,;=%:").find(c) != std::string_view::npos;
}
std::string sweptPath(std::string_view path) {
const std::string source(path);
std::string swept(source.size() + 1, '\0');
sceHttpUriSweepPath(swept.data(), source.c_str(), source.size() + 1);
swept.resize(std::strlen(swept.c_str()));
return swept;
}
size_t schemeLength(std::string_view uri) {
size_t schemeEnd = 0;
if (!uri.empty() && isAsciiAlpha(uri[0])) {
if (!uri.empty() && (isAsciiAlpha(uri[0]) || uri[0] == '+' || uri[0] == '-' || uri[0] == '.')) {
schemeEnd = 1;
while (schemeEnd < uri.size() && (isAsciiAlpha(uri[schemeEnd]) || isAsciiDigit(uri[schemeEnd]) || uri[schemeEnd] == '+'
|| uri[schemeEnd] == '-' || uri[schemeEnd] == '.')) ++schemeEnd;
@@ -110,49 +103,51 @@ int parseUri(std::string_view uri, UriParts& parts) {
parts.opaque = !uri.starts_with("//");
if (!parts.opaque) uri.remove_prefix(2);
std::string_view authority = uri.substr(0, uri.find_first_of("/?#"));
uri.remove_prefix(authority.size());
const size_t at = authority.rfind('@');
if (at != std::string_view::npos) {
const std::string_view userinfo = authority.substr(0, at);
size_t userinfoEnd = 0;
while (userinfoEnd < uri.size() && isUserinfoCharacter(uri[userinfoEnd])) ++userinfoEnd;
if (userinfoEnd < uri.size() && uri[userinfoEnd] == '@') {
const std::string_view userinfo = uri.substr(0, userinfoEnd);
const size_t colon = userinfo.find(':');
parts.username = userinfo.substr(0, colon);
if (colon != std::string_view::npos) parts.password = userinfo.substr(colon + 1);
authority.remove_prefix(at + 1);
uri.remove_prefix(userinfoEnd + 1);
}
std::string_view port;
if (authority.starts_with('[')) {
const size_t close = authority.find(']');
if (uri.starts_with('[')) {
const size_t close = uri.find(']');
if (close == std::string_view::npos) return ERROR_INVALID_URL;
parts.hostname = authority.substr(1, close - 1);
authority.remove_prefix(close + 1);
if (!authority.empty() && authority[0] != ':') return ERROR_INVALID_URL;
if (!authority.empty()) port = authority.substr(1);
parts.hostname = uri.substr(1, close - 1);
for (const char c : parts.hostname) {
if (!isHostCharacter(c) && c != ':') return ERROR_INVALID_URL;
}
uri.remove_prefix(close + 1);
} else {
const size_t colon = authority.find(':');
parts.hostname = authority.substr(0, colon);
if (colon != std::string_view::npos) port = authority.substr(colon + 1);
for (const char c : parts.hostname) {
if (!isHostCharacter(c)) return ERROR_INVALID_URL;
size_t hostEnd = 0;
if (!uri.starts_with('.')) {
while (hostEnd < uri.size() && isHostCharacter(uri[hostEnd])) ++hostEnd;
}
parts.hostname = uri.substr(0, hostEnd);
uri.remove_prefix(hostEnd);
}
if (port.size() > 5) return ERROR_INVALID_URL;
uint32_t portValue = 0;
for (const char c : port) {
if (!isAsciiDigit(c)) return ERROR_INVALID_URL;
portValue = portValue * 10 + static_cast<uint32_t>(c - '0');
parts.port = defaultPort(parts.scheme);
if (uri.starts_with(':')) {
uri.remove_prefix(1);
const std::string_view port = uri.substr(0, uri.find('/'));
uri.remove_prefix(port.size());
if (port.size() > 5) return ERROR_INVALID_URL;
uint32_t portValue = 0;
for (const char c : port) {
if (!isAsciiDigit(c)) return ERROR_INVALID_URL;
portValue = portValue * 10 + static_cast<uint32_t>(c - '0');
}
if (portValue > 0x10000) return ERROR_INVALID_URL;
if (!port.empty()) parts.port = static_cast<uint16_t>(portValue);
}
if (portValue > 0xFFFF) return ERROR_INVALID_URL;
parts.port = port.empty() ? defaultPort(parts.scheme) : static_cast<uint16_t>(portValue);
const std::string_view path = uri.substr(0, uri.find_first_of("?#"));
parts.path = removeDotSegments(path);
parts.path = sweptPath(path);
parts.pathSpace = path.size();
uri.remove_prefix(path.size());
if (uri.starts_with('?')) {
@@ -164,8 +159,8 @@ int parseUri(std::string_view uri, UriParts& parts) {
}
size_t poolSize(const UriParts& parts) {
const std::string_view fields[] = {parts.scheme, parts.username, parts.password, parts.hostname, parts.path, parts.query, parts.fragment};
size_t size = 0;
const std::string_view fields[] = {parts.scheme, parts.username, parts.password, parts.hostname, parts.query, parts.fragment};
size_t size = parts.pathSpace + 1;
for (const std::string_view field : fields) size += field.size() + 1;
return size;
}
@@ -345,10 +340,12 @@ int APS5_VABI sceHttpUriParse(SceHttpUriElement* out, const char* src_url, void*
if (const int result = parseUri(src_url, parts); result != 0) return result;
const std::string_view fields[] = {parts.scheme, parts.username, parts.password, parts.hostname, parts.path, parts.query, parts.fragment};
const size_t spaces[] = {parts.scheme.size(), parts.username.size(), parts.password.size(), parts.hostname.size(), parts.pathSpace, parts.query.size(),
parts.fragment.size()};
const size_t size = poolSize(parts);
if (write && prepare < size) return ERROR_OUT_OF_MEMORY;
if (require) *require = size;
if (!write) return 0;
if (prepare < size) return ERROR_OUT_OF_MEMORY;
char* next = static_cast<char*>(pool);
char* strings[7];
@@ -356,7 +353,7 @@ int APS5_VABI sceHttpUriParse(SceHttpUriElement* out, const char* src_url, void*
strings[i] = next;
if (!fields[i].empty()) std::memcpy(next, fields[i].data(), fields[i].size());
next[fields[i].size()] = '\0';
next += fields[i].size() + 1;
next += spaces[i] + 1;
}
*out = {};
out->opaque = parts.opaque;
@@ -376,31 +373,38 @@ int APS5_VABI sceHttpUriCopy(SceHttpUriElement* out, const SceHttpUriElement* sr
const bool write = out && pool;
if (!write && !require) return ERROR_INVALID_VALUE;
if (write) {
std::memset(out, 0, sizeof(*out));
std::memset(pool, 0, prepare);
}
const char* const fields[] = {src->scheme, src->hostname, src->path, src->username, src->password};
SceHttpUriElement copy{};
char** const slots[] = {&copy.scheme, &copy.hostname, &copy.path, &copy.username, &copy.password};
if (write) std::memset(pool, 0, prepare);
const uint16_t port = src->port;
size_t lengths[5];
size_t used = 0;
for (size_t i = 0; i < 5; ++i) {
const size_t length = fields[i] ? std::strlen(fields[i]) : 0;
const size_t needed = length + 1;
if (write) {
if (needed > prepare - used) {
*out = {};
std::memset(pool, 0, prepare);
return ERROR_OUT_OF_MEMORY;
}
char* next = static_cast<char*>(pool) + used;
if (length != 0) std::memcpy(next, fields[i], length);
next[length] = '\0';
*slots[i] = next;
}
used += needed;
lengths[i] = fields[i] ? std::strlen(fields[i]) : 0;
used += lengths[i] + 1;
}
if (write) {
copy.port = src->port;
*out = copy;
if (!write) {
*require = used;
return 0;
}
if (used > prepare) return ERROR_OUT_OF_MEMORY;
char* next = static_cast<char*>(pool);
char* strings[5];
for (size_t i = 0; i < 5; ++i) {
strings[i] = next;
if (lengths[i] != 0) std::memcpy(next, fields[i], lengths[i]);
next[lengths[i]] = '\0';
next += lengths[i] + 1;
}
out->scheme = strings[0];
out->hostname = strings[1];
out->path = strings[2];
out->username = strings[3];
out->password = strings[4];
out->port = port;
if (require) *require = used;
return 0;
}
+9 -6
View File
@@ -17,6 +17,7 @@ constexpr std::uint32_t EndStatusUserCanceled = 1;
constexpr std::uint32_t EndStatusAborted = 2;
constexpr std::uint32_t MaxTextLength = 2048;
constexpr int ErrorBusy = static_cast<int>(0x80bc0001u);
constexpr int ErrorSuspending = static_cast<int>(0x80bc0009u);
constexpr int ErrorNotOpened = static_cast<int>(0x80bc0002u);
constexpr int ErrorNotInUse = static_cast<int>(0x80bc0107u);
constexpr int ErrorInvalidMaxTextLength = static_cast<int>(0x80bc0016u);
@@ -190,18 +191,20 @@ int APS5_VABI sceImeDialogForceClose(void) {
}
int APS5_VABI sceImeDialogGetCurrentStarState(int64_t state) {
NotImplemented_nid_no_patch(__func__);
std::lock_guard lock(g_mutex);
if (g_dialog.status == StatusNone) return ErrorNotInUse;
if (state == 0) return ErrorInvalidAddress;
*reinterpret_cast<std::uint32_t*>(state) = 0;
return 0;
}
int APS5_VABI sceImeDialogInitInternal(Param* param, ExtendedParam* extended) {
NotImplemented_nid_no_patch(__func__);
return 0;
return sceImeDialogInit(param, extended);
}
int APS5_VABI sceImeDialogSetPanelPosition(int32_t posx, int32_t posy) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceImeDialogSetPanelPosition(int32_t, int32_t) {
std::lock_guard lock(g_mutex);
return g_dialog.status == StatusNone ? ErrorNotInUse : ErrorSuspending;
}
+51 -3
View File
@@ -1,10 +1,58 @@
#include <cstddef>
#include <cstdint>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
extern "C" {
APS5_DUMMY_FUN
int APS5_VABI sceJemspaceCalloc() {
NotImplemented_nid_no_patch(__func__);
}
int APS5_VABI sceJemspaceCreate() {
NotImplemented_nid_no_patch(__func__);
}
int APS5_VABI sceJemspaceDestroy() {
NotImplemented_nid_no_patch(__func__);
}
int APS5_VABI sceJemspaceFree() {
NotImplemented_nid_no_patch(__func__);
}
int APS5_VABI sceJemspaceGetStats() {
NotImplemented_nid_no_patch(__func__);
}
int APS5_VABI sceJemspaceMalloc() {
NotImplemented_nid_no_patch(__func__);
}
int APS5_VABI sceJemspaceMallocUsableSize() {
NotImplemented_nid_no_patch(__func__);
}
int APS5_VABI sceJemspaceMemalign() {
NotImplemented_nid_no_patch(__func__);
}
int APS5_VABI sceJemspacePosixMemalign() {
NotImplemented_nid_no_patch(__func__);
}
int APS5_VABI sceJemspacePrintStats() {
NotImplemented_nid_no_patch(__func__);
}
int APS5_VABI sceJemspacePurge() {
NotImplemented_nid_no_patch(__func__);
}
int APS5_VABI sceJemspaceRealloc() {
NotImplemented_nid_no_patch(__func__);
}
int APS5_VABI sceJemspaceReallocalign() {
NotImplemented_nid_no_patch(__func__);
}
}
+5
View File
@@ -763,6 +763,11 @@ const Value* APS5_VABI _ZNK3sce4Json5Array4backEv(const Array* self) {
if (self->items->empty()) throw std::out_of_range("sce::Json::Array::back: empty array");
return &self->items->back();
}
void APS5_VABI _ZN3sce4Json5Array9pop_frontEv(Array* self) {
if (self->items->empty()) throw std::out_of_range("sce::Json::Array::pop_front: empty array");
Destroy(self->items->front());
self->items->pop_front();
}
void APS5_VABI _ZN3sce4Json5Array8iteratorD1Ev(ArrayIterator* self) { delete self->state; self->state = nullptr; }
ArrayIterator* APS5_VABI _ZN3sce4Json5Array8iteratorppEv(ArrayIterator* self) {
if (self->state && self->state->position != self->state->end) ++self->state->position;
+15
View File
@@ -0,0 +1,15 @@
add_library(libSceJxr SHARED EXCLUDE_FROM_ALL Export.cpp)
target_include_directories(libSceJxr PRIVATE ${LIBS_INCLUDE_DIR})
configure_windows_unwind(libSceJxr)
if(BUILD_TESTING)
add_test_executable(guest_jxr_tests ${LIBS_INCLUDE_DIR}/tests/GuestJxr.cpp)
target_include_directories(guest_jxr_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_jxr_tests PRIVATE libSceJxr libc)
configure_windows_unwind(guest_jxr_tests)
add_test(NAME guest_jxr COMMAND guest_jxr_tests)
if(WIN32)
set_tests_properties(guest_jxr PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceJxr>")
endif()
endif()
+12 -3
View File
@@ -3,12 +3,21 @@
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
namespace {
constexpr std::int64_t WmpErrSuccess = 0;
}
extern "C" {
void APS5_VABI free_nid_postfix(void* memory);
APS5_DUMMY_FUN
int APS5_VABI PKFree_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
std::int64_t APS5_VABI PKFree_nid_postfix(void** memory) {
if (memory != nullptr) {
free_nid_postfix(*memory);
*memory = nullptr;
}
return WmpErrSuccess;
}
int APS5_VABI PixelFormatLookup_nid_postfix(void) {
+5 -7
View File
@@ -27,6 +27,10 @@ int APS5_VABI sceKeyboardReadState(int32_t handle, KeyboardData* data) {
return Keyboard::ReadState(handle, data);
}
int APS5_VABI sceKeyboardGetConnection(std::int32_t handle, bool* connected) {
return Keyboard::GetConnection(handle, connected);
}
int APS5_VABI sceKeyboardGetKey2Char(std::int32_t handle, std::int32_t arrange, std::uint8_t led, std::uint8_t modifierKey, std::uint16_t keyCode, KeyboardCharData* charData) {
return Keyboard::GetKey2Char(handle, arrange, led, modifierKey, keyCode, charData);
}
@@ -47,8 +51,7 @@ int APS5_VABI sceKeyboardDeviceOpen(std::uint64_t deviceId, std::int32_t index,
}
int APS5_VABI sceKeyboardDisconnectAll(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
return Keyboard::DisconnectAll();
}
int APS5_VABI sceKeyboardDisconnectDevice(std::int32_t deviceId) {
@@ -61,11 +64,6 @@ int APS5_VABI sceKeyboardDisconnectPort(std::int32_t first, std::int32_t second)
return 0;
}
int APS5_VABI sceKeyboardGetConnection(std::int32_t handle, bool* connected) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceKeyboardGetDeviceInfo(std::int32_t deviceId, KeyboardDeviceInfo* info) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -18,6 +18,8 @@ int Open(int userId, std::int32_t type, std::int32_t index);
int Close(std::int32_t handle);
int Read(std::int32_t handle, KeyboardData* data, std::int32_t num);
int ReadState(std::int32_t handle, KeyboardData* data);
int GetConnection(std::int32_t handle, bool* connected);
int DisconnectAll();
int GetKey2Char(std::int32_t handle, std::int32_t arrange, std::uint32_t led, std::uint32_t modifierKey, std::uint16_t keyCode, KeyboardCharData* charData);
void Publish(const KeyboardInputEvent& event);
}
@@ -154,6 +154,23 @@ int ReadState(std::int32_t handle, KeyboardData* data) {
return KEYBOARD_OK;
}
int GetConnection(std::int32_t handle, bool* isConnected) {
std::lock_guard lock(keyboardMutex);
if (!initialized) return KEYBOARD_ERROR_NOT_INITIALIZED;
if (handle != KEYBOARD_HANDLE || !opened) return KEYBOARD_ERROR_INVALID_HANDLE;
if (isConnected == nullptr) return KEYBOARD_ERROR_INVALID_ARG;
*isConnected = connected;
return KEYBOARD_OK;
}
int DisconnectAll() {
KeyboardInputEvent event{};
event.connectionChange = true;
event.connected = false;
Publish(event);
return KEYBOARD_OK;
}
int GetKey2Char(std::int32_t handle, std::int32_t arrange, std::uint32_t ledState, std::uint32_t modifierKey, std::uint16_t keyCode, KeyboardCharData* charData) {
{
std::lock_guard lock(keyboardMutex);
@@ -13,6 +13,7 @@ int APS5_VABI sceKeyboardOpen(int, std::int32_t, std::int32_t, const void*);
int APS5_VABI sceKeyboardClose(std::int32_t);
int APS5_VABI sceKeyboardRead(std::int32_t, KeyboardData*, std::int32_t);
int APS5_VABI sceKeyboardReadState(std::int32_t, KeyboardData*);
int APS5_VABI sceKeyboardGetConnection(std::int32_t, bool*);
int APS5_VABI sceKeyboardGetKey2Char(std::int32_t, std::int32_t, std::uint8_t, std::uint8_t, std::uint16_t, KeyboardCharData*);
int APS5_VABI sceKeyboardConnectPort(void);
int APS5_VABI sceKeyboardDisconnectAll(void);
@@ -44,14 +45,20 @@ static std::uint16_t Jis(std::uint32_t modifierKey, std::uint16_t keyCode, std::
int main() {
KeyboardData data[16]{};
KeyboardCharData charData{};
bool connected = false;
Require(sceKeyboardOpen(1, 0, 0, nullptr) == KEYBOARD_ERROR_NOT_INITIALIZED);
Require(sceKeyboardGetConnection(KEYBOARD_HANDLE, &connected) == KEYBOARD_ERROR_NOT_INITIALIZED);
Require(sceKeyboardInit() == KEYBOARD_OK);
Require(sceKeyboardInit() == KEYBOARD_OK);
Require(sceKeyboardOpen(1, 0, 1, nullptr) == KEYBOARD_ERROR_INVALID_ARG);
Require(sceKeyboardOpen(1, 1, 0, nullptr) == KEYBOARD_ERROR_INVALID_ARG);
Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, KEYBOARD_ARRANGEMENT_101, 0, 0, 0x04, &charData) == KEYBOARD_ERROR_INVALID_HANDLE);
Require(sceKeyboardGetConnection(KEYBOARD_HANDLE, &connected) == KEYBOARD_ERROR_INVALID_HANDLE);
Require(sceKeyboardOpen(1, 0, 0, nullptr) == KEYBOARD_HANDLE);
Require(sceKeyboardOpen(1, 0, 0, nullptr) == KEYBOARD_ERROR_ALREADY_OPENED);
Require(sceKeyboardGetConnection(42, &connected) == KEYBOARD_ERROR_INVALID_HANDLE);
Require(sceKeyboardGetConnection(KEYBOARD_HANDLE, nullptr) == KEYBOARD_ERROR_INVALID_ARG);
Require(sceKeyboardGetConnection(KEYBOARD_HANDLE, &connected) == KEYBOARD_OK && connected);
Require(sceKeyboardRead(42, data, 1) == KEYBOARD_ERROR_INVALID_HANDLE);
Require(sceKeyboardRead(KEYBOARD_HANDLE, nullptr, 1) == KEYBOARD_ERROR_INVALID_ARG);
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 17) == KEYBOARD_ERROR_INVALID_ARG);
@@ -96,9 +103,11 @@ int main() {
window.window.event = SDL_WINDOWEVENT_CLOSE;
input.HandleEvent(window, 7);
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 1 && !data[0].connected && data[0].length == 1 && data[0].key_code[0] == 0);
Require(sceKeyboardGetConnection(KEYBOARD_HANDLE, &connected) == KEYBOARD_OK && !connected);
window.window.event = SDL_WINDOWEVENT_FOCUS_GAINED;
input.HandleEvent(window, 7);
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 1 && data[0].connected);
Require(sceKeyboardGetConnection(KEYBOARD_HANDLE, &connected) == KEYBOARD_OK && connected);
for (std::uint16_t key = 0x04; key < 0x04 + 20; ++key) {
KeyboardInputEvent press{};
@@ -143,11 +152,31 @@ int main() {
Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, 2, 0, 0, 0x04, &charData) == KEYBOARD_ERROR_INVALID_ARG);
Require(sceKeyboardClose(KEYBOARD_HANDLE) == KEYBOARD_OK);
Require(sceKeyboardGetConnection(KEYBOARD_HANDLE, &connected) == KEYBOARD_ERROR_INVALID_HANDLE);
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == KEYBOARD_ERROR_INVALID_HANDLE);
Require(sceKeyboardReadState(KEYBOARD_HANDLE, data) == KEYBOARD_ERROR_INVALID_HANDLE);
Require(sceKeyboardClose(KEYBOARD_HANDLE) == KEYBOARD_ERROR_INVALID_HANDLE);
Require(sceKeyboardOpen(1, 0, 0, nullptr) == KEYBOARD_HANDLE);
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 1 && data[0].length == 1 && data[0].key_code[0] == 0);
Require(data[0].connected);
KeyboardInputEvent pressed{};
pressed.keyCode = 0x04;
pressed.pressed = true;
KeyboardPublishInput_nid_postfix(pressed);
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 1 && data[0].key_code[0] == 0x04);
Require(sceKeyboardDisconnectAll() == KEYBOARD_OK);
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 1 && !data[0].connected && data[0].length == 1 && data[0].key_code[0] == 0);
Require(sceKeyboardReadState(KEYBOARD_HANDLE, data) == KEYBOARD_OK && !data[0].connected && data[0].key_code[0] == 0);
Require(sceKeyboardDisconnectAll() == KEYBOARD_OK);
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 0);
pressed.keyCode = 0x05;
KeyboardPublishInput_nid_postfix(pressed);
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 0);
window.window.event = SDL_WINDOWEVENT_FOCUS_LOST;
input.HandleEvent(window, 7);
window.window.event = SDL_WINDOWEVENT_FOCUS_GAINED;
input.HandleEvent(window, 7);
Require(sceKeyboardRead(KEYBOARD_HANDLE, data, 1) == 1 && data[0].connected);
Require(sceKeyboardClose(KEYBOARD_HANDLE) == KEYBOARD_OK);
bool threwConnect = false;
@@ -157,12 +186,4 @@ int main() {
threwConnect = true;
}
Require(threwConnect);
bool threwDisconnect = false;
try {
sceKeyboardDisconnectAll();
} catch (const std::runtime_error&) {
threwDisconnect = true;
}
Require(threwDisconnect);
}
@@ -0,0 +1,16 @@
add_library(libSceLibreSsl SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp)
target_include_directories(libSceLibreSsl PRIVATE ${LIBS_INCLUDE_DIR})
configure_windows_unwind(libSceLibreSsl)
if(BUILD_TESTING)
add_test_executable(guest_libressl_digest_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSslDigest.cpp)
target_include_directories(guest_libressl_digest_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_libressl_digest_tests PRIVATE libSceLibreSsl libc)
configure_windows_unwind(guest_libressl_digest_tests)
add_test(NAME guest_libressl_digest COMMAND guest_libressl_digest_tests)
set_tests_properties(guest_libressl_digest PROPERTIES TIMEOUT 30)
if(WIN32)
set_tests_properties(guest_libressl_digest PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibreSsl>")
endif()
endif()
-110
View File
@@ -846,36 +846,6 @@ int APS5_VABI HMAC_Update_nid_postfix(void) {
return 0;
}
int APS5_VABI MD4_Final_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI MD4_Init_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI MD4_Update_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI MD5_Final_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI MD5_Init_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI MD5_Update_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI OBJ_cleanup_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -1116,86 +1086,6 @@ int APS5_VABI RSA_verify_PKCS1_PSS_nid_postfix(void) {
return 0;
}
int APS5_VABI SHA1_Final_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SHA1_Init_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SHA1_Update_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SHA224_Final_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SHA224_Init_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SHA224_Update_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SHA256_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SHA256_Final_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SHA256_Init_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SHA256_Update_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SHA384_Final_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SHA384_Init_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SHA384_Update_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SHA512_Final_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SHA512_Init_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SHA512_Update_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SSL_CIPHER_get_bits_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
+549
View File
@@ -0,0 +1,549 @@
#include <bit>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <stdexcept>
#include <string>
#include "prx/libc/include/General.hpp"
namespace {
struct Md4Context {
std::uint32_t A, B, C, D;
std::uint32_t Nl, Nh;
std::uint32_t data[16];
std::uint32_t num;
};
struct Md5Context {
std::uint32_t A, B, C, D;
std::uint32_t Nl, Nh;
std::uint32_t data[16];
std::uint32_t num;
};
struct Sha1Context {
std::uint32_t h[5];
std::uint32_t Nl, Nh;
std::uint32_t data[16];
std::uint32_t num;
};
struct Sha256Context {
std::uint32_t h[8];
std::uint32_t Nl, Nh;
std::uint32_t data[16];
std::uint32_t num, mdLength;
};
struct Sha512Context {
std::uint64_t h[8];
std::uint64_t Nl, Nh;
std::uint64_t data[16];
std::uint32_t num, mdLength;
};
static_assert(sizeof(Md4Context) == 92);
static_assert(sizeof(Md5Context) == 92);
static_assert(sizeof(Sha1Context) == 96);
static_assert(sizeof(Sha256Context) == 112);
static_assert(sizeof(Sha512Context) == 216);
constexpr std::size_t Sha224Length = 28;
constexpr std::size_t Sha256Length = 32;
constexpr std::size_t Sha384Length = 48;
constexpr std::size_t Sha512Length = 64;
std::uint32_t LoadLittle32(const unsigned char* p) {
return std::uint32_t(p[0]) | std::uint32_t(p[1]) << 8 | std::uint32_t(p[2]) << 16 | std::uint32_t(p[3]) << 24;
}
std::uint32_t LoadBig32(const unsigned char* p) {
return std::uint32_t(p[0]) << 24 | std::uint32_t(p[1]) << 16 | std::uint32_t(p[2]) << 8 | std::uint32_t(p[3]);
}
std::uint64_t LoadBig64(const unsigned char* p) {
return std::uint64_t(LoadBig32(p)) << 32 | LoadBig32(p + 4);
}
void StoreLittle32(unsigned char* p, std::uint32_t value) {
for (int i = 0; i < 4; ++i) p[i] = static_cast<unsigned char>(value >> (8 * i));
}
void StoreBig32(unsigned char* p, std::uint32_t value) {
for (int i = 0; i < 4; ++i) p[i] = static_cast<unsigned char>(value >> (24 - 8 * i));
}
void StoreBig64(unsigned char* p, std::uint64_t value) {
StoreBig32(p, static_cast<std::uint32_t>(value >> 32));
StoreBig32(p + 4, static_cast<std::uint32_t>(value));
}
void Md4Block(Md4Context& c, const unsigned char* block) {
std::uint32_t x[16];
for (int i = 0; i < 16; ++i) x[i] = LoadLittle32(block + 4 * i);
std::uint32_t a = c.A, b = c.B, cc = c.C, d = c.D;
const auto f = [](std::uint32_t x1, std::uint32_t y, std::uint32_t z) { return (x1 & y) | (~x1 & z); };
const auto g = [](std::uint32_t x1, std::uint32_t y, std::uint32_t z) { return (x1 & y) | (x1 & z) | (y & z); };
const auto h = [](std::uint32_t x1, std::uint32_t y, std::uint32_t z) { return x1 ^ y ^ z; };
static constexpr int shifts1[4] {3, 7, 11, 19};
static constexpr int shifts2[4] {3, 5, 9, 13};
static constexpr int shifts3[4] {3, 9, 11, 15};
static constexpr int order2[16] {0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15};
static constexpr int order3[16] {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15};
for (int i = 0; i < 16; ++i) {
const auto t = std::rotl(a + f(b, cc, d) + x[i], shifts1[i % 4]);
a = d; d = cc; cc = b; b = t;
}
for (int i = 0; i < 16; ++i) {
const auto t = std::rotl(a + g(b, cc, d) + x[order2[i]] + 0x5a827999u, shifts2[i % 4]);
a = d; d = cc; cc = b; b = t;
}
for (int i = 0; i < 16; ++i) {
const auto t = std::rotl(a + h(b, cc, d) + x[order3[i]] + 0x6ed9eba1u, shifts3[i % 4]);
a = d; d = cc; cc = b; b = t;
}
c.A += a; c.B += b; c.C += cc; c.D += d;
}
void Md5Block(Md5Context& c, const unsigned char* block) {
static constexpr std::uint32_t constants[64] {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
};
static constexpr int shifts[16] {7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21};
std::uint32_t x[16];
for (int i = 0; i < 16; ++i) x[i] = LoadLittle32(block + 4 * i);
std::uint32_t a = c.A, b = c.B, cc = c.C, d = c.D;
for (int i = 0; i < 64; ++i) {
std::uint32_t mix;
int index;
switch (i / 16) {
case 0: mix = (b & cc) | (~b & d); index = i; break;
case 1: mix = (d & b) | (~d & cc); index = (5 * i + 1) % 16; break;
case 2: mix = b ^ cc ^ d; index = (3 * i + 5) % 16; break;
default: mix = cc ^ (b | ~d); index = (7 * i) % 16; break;
}
const auto t = d;
d = cc;
cc = b;
b += std::rotl(a + mix + constants[i] + x[index], shifts[(i / 16) * 4 + i % 4]);
a = t;
}
c.A += a; c.B += b; c.C += cc; c.D += d;
}
void Sha1Block(Sha1Context& c, const unsigned char* block) {
std::uint32_t w[80];
for (int i = 0; i < 16; ++i) w[i] = LoadBig32(block + 4 * i);
for (int i = 16; i < 80; ++i) w[i] = std::rotl(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
std::uint32_t a = c.h[0], b = c.h[1], cc = c.h[2], d = c.h[3], e = c.h[4];
for (int i = 0; i < 80; ++i) {
std::uint32_t f, k;
if (i < 20) { f = (b & cc) | (~b & d); k = 0x5a827999; }
else if (i < 40) { f = b ^ cc ^ d; k = 0x6ed9eba1; }
else if (i < 60) { f = (b & cc) | (b & d) | (cc & d); k = 0x8f1bbcdc; }
else { f = b ^ cc ^ d; k = 0xca62c1d6; }
const auto t = std::rotl(a, 5) + f + e + k + w[i];
e = d; d = cc; cc = std::rotl(b, 30); b = a; a = t;
}
c.h[0] += a; c.h[1] += b; c.h[2] += cc; c.h[3] += d; c.h[4] += e;
}
void Sha256Block(Sha256Context& c, const unsigned char* block) {
static constexpr std::uint32_t constants[64] {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
};
std::uint32_t w[64];
for (int i = 0; i < 16; ++i) w[i] = LoadBig32(block + 4 * i);
for (int i = 16; i < 64; ++i) {
const auto s0 = std::rotr(w[i - 15], 7) ^ std::rotr(w[i - 15], 18) ^ (w[i - 15] >> 3);
const auto s1 = std::rotr(w[i - 2], 17) ^ std::rotr(w[i - 2], 19) ^ (w[i - 2] >> 10);
w[i] = w[i - 16] + s0 + w[i - 7] + s1;
}
std::uint32_t v[8];
std::memcpy(v, c.h, sizeof(v));
for (int i = 0; i < 64; ++i) {
const auto s1 = std::rotr(v[4], 6) ^ std::rotr(v[4], 11) ^ std::rotr(v[4], 25);
const auto choice = (v[4] & v[5]) ^ (~v[4] & v[6]);
const auto t1 = v[7] + s1 + choice + constants[i] + w[i];
const auto s0 = std::rotr(v[0], 2) ^ std::rotr(v[0], 13) ^ std::rotr(v[0], 22);
const auto majority = (v[0] & v[1]) ^ (v[0] & v[2]) ^ (v[1] & v[2]);
std::memmove(v + 1, v, 7 * sizeof(v[0]));
v[4] += t1;
v[0] = t1 + s0 + majority;
}
for (int i = 0; i < 8; ++i) c.h[i] += v[i];
}
void Sha512Block(Sha512Context& c, const unsigned char* block) {
static constexpr std::uint64_t constants[80] {
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 0x3956c25bf348b538,
0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x12835b0145706fbe,
0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235,
0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 0x983e5152ee66dfab,
0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725,
0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed,
0x53380d139d95b3df, 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218,
0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, 0x19a4c116b8d2d0c8, 0x1e376c085141ab53,
0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373,
0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 0xca273eceea26619c,
0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, 0x0a637dc5a2c898a6,
0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817,
};
std::uint64_t w[80];
for (int i = 0; i < 16; ++i) w[i] = LoadBig64(block + 8 * i);
for (int i = 16; i < 80; ++i) {
const auto s0 = std::rotr(w[i - 15], 1) ^ std::rotr(w[i - 15], 8) ^ (w[i - 15] >> 7);
const auto s1 = std::rotr(w[i - 2], 19) ^ std::rotr(w[i - 2], 61) ^ (w[i - 2] >> 6);
w[i] = w[i - 16] + s0 + w[i - 7] + s1;
}
std::uint64_t v[8];
std::memcpy(v, c.h, sizeof(v));
for (int i = 0; i < 80; ++i) {
const auto s1 = std::rotr(v[4], 14) ^ std::rotr(v[4], 18) ^ std::rotr(v[4], 41);
const auto choice = (v[4] & v[5]) ^ (~v[4] & v[6]);
const auto t1 = v[7] + s1 + choice + constants[i] + w[i];
const auto s0 = std::rotr(v[0], 28) ^ std::rotr(v[0], 34) ^ std::rotr(v[0], 39);
const auto majority = (v[0] & v[1]) ^ (v[0] & v[2]) ^ (v[1] & v[2]);
std::memmove(v + 1, v, 7 * sizeof(v[0]));
v[4] += t1;
v[0] = t1 + s0 + majority;
}
for (int i = 0; i < 8; ++i) c.h[i] += v[i];
}
template<class TContext> TContext& RequireContext(TContext* context, const char* function) {
if (!context) throw std::invalid_argument(std::string(function) + ": null context");
return *context;
}
void RequireInput(const void* data, std::size_t length, const char* function) {
if (!data && length) throw std::invalid_argument(std::string(function) + ": null input");
}
void RequireDigest(const unsigned char* digest, const char* function) {
if (!digest) throw std::invalid_argument(std::string(function) + ": null digest buffer");
}
void RequireBuffered(std::uint32_t buffered, std::size_t block, const char* function) {
if (buffered >= block) throw std::invalid_argument(std::string(function) + ": buffered byte count is not below the block size");
}
template<class TContext, class TBlock> void Update32(TContext& c, const void* input, std::size_t length, TBlock block) {
constexpr std::size_t blockSize = 64;
auto* buffer = reinterpret_cast<unsigned char*>(c.data);
auto* data = static_cast<const unsigned char*>(input);
const std::uint64_t bits = (std::uint64_t(c.Nh) << 32 | c.Nl) + (std::uint64_t(length) << 3);
c.Nl = static_cast<std::uint32_t>(bits);
c.Nh = static_cast<std::uint32_t>(bits >> 32);
if (c.num) {
const std::size_t room = blockSize - c.num;
if (length < room) {
std::memcpy(buffer + c.num, data, length);
c.num += static_cast<std::uint32_t>(length);
return;
}
std::memcpy(buffer + c.num, data, room);
block(c, buffer);
data += room;
length -= room;
c.num = 0;
std::memset(buffer, 0, blockSize);
}
for (; length >= blockSize; data += blockSize, length -= blockSize) block(c, data);
if (length) {
std::memcpy(buffer, data, length);
c.num = static_cast<std::uint32_t>(length);
}
}
template<class TContext, class TBlock> void Final32(TContext& c, bool bigEndian, TBlock block) {
constexpr std::size_t blockSize = 64;
auto* buffer = reinterpret_cast<unsigned char*>(c.data);
std::size_t n = c.num;
buffer[n++] = 0x80;
if (n > blockSize - 8) {
std::memset(buffer + n, 0, blockSize - n);
n = 0;
block(c, buffer);
}
std::memset(buffer + n, 0, blockSize - 8 - n);
if (bigEndian) {
StoreBig32(buffer + 56, c.Nh);
StoreBig32(buffer + 60, c.Nl);
} else {
StoreLittle32(buffer + 56, c.Nl);
StoreLittle32(buffer + 60, c.Nh);
}
block(c, buffer);
c.num = 0;
std::memset(buffer, 0, blockSize);
}
void Sha512Update(Sha512Context& c, const void* input, std::size_t length) {
constexpr std::size_t blockSize = 128;
auto* buffer = reinterpret_cast<unsigned char*>(c.data);
auto* data = static_cast<const unsigned char*>(input);
const std::uint64_t low = c.Nl + (std::uint64_t(length) << 3);
if (low < c.Nl) ++c.Nh;
c.Nh += std::uint64_t(length) >> 61;
c.Nl = low;
if (c.num) {
const std::size_t room = blockSize - c.num;
if (length < room) {
std::memcpy(buffer + c.num, data, length);
c.num += static_cast<std::uint32_t>(length);
return;
}
std::memcpy(buffer + c.num, data, room);
Sha512Block(c, buffer);
data += room;
length -= room;
c.num = 0;
}
for (; length >= blockSize; data += blockSize, length -= blockSize) Sha512Block(c, data);
if (length) {
std::memcpy(buffer, data, length);
c.num = static_cast<std::uint32_t>(length);
}
}
int Sha512Final(unsigned char* digest, Sha512Context& c) {
constexpr std::size_t blockSize = 128;
auto* buffer = reinterpret_cast<unsigned char*>(c.data);
std::size_t n = c.num;
buffer[n++] = 0x80;
if (n > blockSize - 16) {
std::memset(buffer + n, 0, blockSize - n);
n = 0;
Sha512Block(c, buffer);
}
std::memset(buffer + n, 0, blockSize - 16 - n);
StoreBig64(buffer + 112, c.Nh);
StoreBig64(buffer + 120, c.Nl);
Sha512Block(c, buffer);
if (!digest) return 0;
if (c.mdLength == Sha224Length || c.mdLength == Sha256Length)
NotImplemented_nid_no_patch("SHA512_Final: SHA-512/224 and SHA-512/256 digest lengths");
if (c.mdLength != Sha384Length && c.mdLength != Sha512Length) return 0;
for (std::size_t i = 0; i < c.mdLength / 8; ++i) StoreBig64(digest + 8 * i, c.h[i]);
return 1;
}
int Sha256Final(unsigned char* digest, Sha256Context& c, const char* function) {
RequireDigest(digest, function);
RequireBuffered(c.num, 64, function);
Final32(c, true, Sha256Block);
if (c.mdLength > Sha256Length) return 0;
for (std::size_t i = 0; i < c.mdLength / 4; ++i) StoreBig32(digest + 4 * i, c.h[i]);
return 1;
}
}
extern "C" {
int APS5_VABI MD4_Init_nid_postfix(Md4Context* context) {
auto& c = RequireContext(context, __func__);
std::memset(&c, 0, sizeof(c));
c.A = 0x67452301;
c.B = 0xefcdab89;
c.C = 0x98badcfe;
c.D = 0x10325476;
return 1;
}
int APS5_VABI MD4_Update_nid_postfix(Md4Context* context, const void* data, std::size_t length) {
auto& c = RequireContext(context, __func__);
if (!length) return 1;
RequireInput(data, length, __func__);
RequireBuffered(c.num, 64, __func__);
Update32(c, data, length, Md4Block);
return 1;
}
int APS5_VABI MD4_Final_nid_postfix(unsigned char* digest, Md4Context* context) {
auto& c = RequireContext(context, __func__);
RequireDigest(digest, __func__);
RequireBuffered(c.num, 64, __func__);
Final32(c, false, Md4Block);
StoreLittle32(digest, c.A);
StoreLittle32(digest + 4, c.B);
StoreLittle32(digest + 8, c.C);
StoreLittle32(digest + 12, c.D);
return 1;
}
int APS5_VABI MD5_Init_nid_postfix(Md5Context* context) {
auto& c = RequireContext(context, __func__);
std::memset(&c, 0, sizeof(c));
c.A = 0x67452301;
c.B = 0xefcdab89;
c.C = 0x98badcfe;
c.D = 0x10325476;
return 1;
}
int APS5_VABI MD5_Update_nid_postfix(Md5Context* context, const void* data, std::size_t length) {
auto& c = RequireContext(context, __func__);
if (!length) return 1;
RequireInput(data, length, __func__);
RequireBuffered(c.num, 64, __func__);
Update32(c, data, length, Md5Block);
return 1;
}
int APS5_VABI MD5_Final_nid_postfix(unsigned char* digest, Md5Context* context) {
auto& c = RequireContext(context, __func__);
RequireDigest(digest, __func__);
RequireBuffered(c.num, 64, __func__);
Final32(c, false, Md5Block);
StoreLittle32(digest, c.A);
StoreLittle32(digest + 4, c.B);
StoreLittle32(digest + 8, c.C);
StoreLittle32(digest + 12, c.D);
return 1;
}
int APS5_VABI SHA1_Init_nid_postfix(Sha1Context* context) {
auto& c = RequireContext(context, __func__);
std::memset(&c, 0, sizeof(c));
c.h[0] = 0x67452301;
c.h[1] = 0xefcdab89;
c.h[2] = 0x98badcfe;
c.h[3] = 0x10325476;
c.h[4] = 0xc3d2e1f0;
return 1;
}
int APS5_VABI SHA1_Update_nid_postfix(Sha1Context* context, const void* data, std::size_t length) {
auto& c = RequireContext(context, __func__);
if (!length) return 1;
RequireInput(data, length, __func__);
RequireBuffered(c.num, 64, __func__);
Update32(c, data, length, Sha1Block);
return 1;
}
int APS5_VABI SHA1_Final_nid_postfix(unsigned char* digest, Sha1Context* context) {
auto& c = RequireContext(context, __func__);
RequireDigest(digest, __func__);
RequireBuffered(c.num, 64, __func__);
Final32(c, true, Sha1Block);
for (int i = 0; i < 5; ++i) StoreBig32(digest + 4 * i, c.h[i]);
return 1;
}
int APS5_VABI SHA224_Init_nid_postfix(Sha256Context* context) {
static constexpr std::uint32_t initial[8] {0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4};
auto& c = RequireContext(context, __func__);
std::memset(&c, 0, sizeof(c));
std::memcpy(c.h, initial, sizeof(initial));
c.mdLength = Sha224Length;
return 1;
}
int APS5_VABI SHA256_Init_nid_postfix(Sha256Context* context) {
static constexpr std::uint32_t initial[8] {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
auto& c = RequireContext(context, __func__);
std::memset(&c, 0, sizeof(c));
std::memcpy(c.h, initial, sizeof(initial));
c.mdLength = Sha256Length;
return 1;
}
int APS5_VABI SHA256_Update_nid_postfix(Sha256Context* context, const void* data, std::size_t length) {
auto& c = RequireContext(context, __func__);
if (!length) return 1;
RequireInput(data, length, __func__);
RequireBuffered(c.num, 64, __func__);
Update32(c, data, length, Sha256Block);
return 1;
}
int APS5_VABI SHA224_Update_nid_postfix(Sha256Context* context, const void* data, std::size_t length) {
return SHA256_Update_nid_postfix(context, data, length);
}
int APS5_VABI SHA256_Final_nid_postfix(unsigned char* digest, Sha256Context* context) {
return Sha256Final(digest, RequireContext(context, __func__), __func__);
}
int APS5_VABI SHA224_Final_nid_postfix(unsigned char* digest, Sha256Context* context) {
return Sha256Final(digest, RequireContext(context, __func__), __func__);
}
unsigned char* APS5_VABI SHA256_nid_postfix(const unsigned char* data, std::size_t length, unsigned char* digest) {
RequireInput(data, length, __func__);
if (!digest) NotImplemented_nid_no_patch("SHA256: null digest buffer");
Sha256Context c;
SHA256_Init_nid_postfix(&c);
if (length) Update32(c, data, length, Sha256Block);
Sha256Final(digest, c, __func__);
return digest;
}
int APS5_VABI SHA384_Init_nid_postfix(Sha512Context* context) {
static constexpr std::uint64_t initial[8] {
0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939,
0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4,
};
auto& c = RequireContext(context, __func__);
std::memset(&c, 0, sizeof(c));
std::memcpy(c.h, initial, sizeof(initial));
c.mdLength = Sha384Length;
return 1;
}
int APS5_VABI SHA512_Init_nid_postfix(Sha512Context* context) {
static constexpr std::uint64_t initial[8] {
0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
};
auto& c = RequireContext(context, __func__);
std::memset(&c, 0, sizeof(c));
std::memcpy(c.h, initial, sizeof(initial));
c.mdLength = Sha512Length;
return 1;
}
int APS5_VABI SHA512_Update_nid_postfix(Sha512Context* context, const void* data, std::size_t length) {
auto& c = RequireContext(context, __func__);
if (!length) return 1;
RequireInput(data, length, __func__);
RequireBuffered(c.num, 128, __func__);
Sha512Update(c, data, length);
return 1;
}
int APS5_VABI SHA384_Update_nid_postfix(Sha512Context* context, const void* data, std::size_t length) {
return SHA512_Update_nid_postfix(context, data, length);
}
int APS5_VABI SHA512_Final_nid_postfix(unsigned char* digest, Sha512Context* context) {
auto& c = RequireContext(context, __func__);
RequireBuffered(c.num, 128, __func__);
return Sha512Final(digest, c);
}
int APS5_VABI SHA384_Final_nid_postfix(unsigned char* digest, Sha512Context* context) {
return SHA512_Final_nid_postfix(digest, context);
}
}
@@ -0,0 +1,22 @@
add_library(libSceLibreSsl3 SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp)
target_include_directories(libSceLibreSsl3 PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(libSceLibreSsl3 PRIVATE libSceLibreSsl)
configure_windows_unwind(libSceLibreSsl3)
if(BUILD_TESTING)
add_test_executable(guest_crypto_memcmp_tests ${LIBS_INCLUDE_DIR}/tests/GuestCryptoMemcmp.cpp)
target_include_directories(guest_crypto_memcmp_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_crypto_memcmp_tests PRIVATE libSceLibreSsl3)
add_test(NAME guest_crypto_memcmp COMMAND guest_crypto_memcmp_tests)
set_tests_properties(guest_crypto_memcmp PROPERTIES TIMEOUT 20)
add_test_executable(guest_libressl3_digest_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSsl3Digest.cpp)
target_include_directories(guest_libressl3_digest_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_libressl3_digest_tests PRIVATE libSceLibreSsl3 libc)
configure_windows_unwind(guest_libressl3_digest_tests)
add_test(NAME guest_libressl3_digest COMMAND guest_libressl3_digest_tests)
set_tests_properties(guest_libressl3_digest PROPERTIES TIMEOUT 30)
if(WIN32)
set_tests_properties(guest_crypto_memcmp guest_libressl3_digest PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibreSsl3>")
endif()
endif()
+7 -13
View File
@@ -31,9 +31,13 @@ int APS5_VABI BIO_vfree_nid_postfix(void) {
return 0;
}
int APS5_VABI CRYPTO_memcmp_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI CRYPTO_memcmp_nid_postfix(const void* first, const void* second, std::size_t length) {
const auto* firstBytes = static_cast<const volatile unsigned char*>(first);
const auto* secondBytes = static_cast<const volatile unsigned char*>(second);
unsigned int difference = 0;
for (std::size_t index = 0; index < length; ++index)
difference |= firstBytes[index] ^ secondBytes[index];
return static_cast<int>(difference);
}
int APS5_VABI DH_get0_key_nid_postfix(void) {
@@ -216,11 +220,6 @@ int APS5_VABI EVP_sha224_nid_postfix(void) {
return 0;
}
int APS5_VABI MD5_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI OBJ_cmp_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -281,11 +280,6 @@ int APS5_VABI RSA_up_ref_nid_postfix(void) {
return 0;
}
int APS5_VABI SHA1_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SSL_CTX_add0_chain_cert_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -0,0 +1,30 @@
#include <cstddef>
#include "prx/libc/include/General.hpp"
extern "C" {
int APS5_VABI MD5_Init_nid_postfix(void* context);
int APS5_VABI MD5_Update_nid_postfix(void* context, const void* data, std::size_t length);
int APS5_VABI MD5_Final_nid_postfix(unsigned char* digest, void* context);
int APS5_VABI SHA1_Init_nid_postfix(void* context);
int APS5_VABI SHA1_Update_nid_postfix(void* context, const void* data, std::size_t length);
int APS5_VABI SHA1_Final_nid_postfix(unsigned char* digest, void* context);
unsigned char* APS5_VABI MD5_nid_postfix(const unsigned char* data, std::size_t length, unsigned char* digest) {
if (!digest) NotImplemented_nid_no_patch("MD5: null digest buffer");
alignas(8) unsigned char context[92];
MD5_Init_nid_postfix(context);
MD5_Update_nid_postfix(context, data, length);
MD5_Final_nid_postfix(digest, context);
return digest;
}
unsigned char* APS5_VABI SHA1_nid_postfix(const unsigned char* data, std::size_t length, unsigned char* digest) {
if (!digest) NotImplemented_nid_no_patch("SHA1: null digest buffer");
alignas(8) unsigned char context[96];
SHA1_Init_nid_postfix(context);
SHA1_Update_nid_postfix(context, data, length);
SHA1_Final_nid_postfix(digest, context);
return digest;
}
}
+749 -1
View File
@@ -5,6 +5,754 @@
extern "C" {
APS5_DUMMY_FUN
int APS5_VABI sceNKWebKitUnknown____URPJNZ18_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("++-URPJNZ18", sceNKWebKitUnknown____URPJNZ18_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown___67uYkhqBU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("++67uYkhqBU", sceNKWebKitUnknown___67uYkhqBU_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown___8RYr2gjrw_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("++8RYr2gjrw", sceNKWebKitUnknown___8RYr2gjrw_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown___BntMcao1o_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("++BntMcao1o", sceNKWebKitUnknown___BntMcao1o_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown___R8IXi10PI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("++R8IXi10PI", sceNKWebKitUnknown___R8IXi10PI_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown___j2h_QO22w_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("++j2h+QO22w", sceNKWebKitUnknown___j2h_QO22w_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown____7XwmYLnI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+-+7XwmYLnI", sceNKWebKitUnknown____7XwmYLnI_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown___wmVyLhgm0_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+-wmVyLhgm0", sceNKWebKitUnknown___wmVyLhgm0_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__0QEt8ZQWgg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+0QEt8ZQWgg", sceNKWebKitUnknown__0QEt8ZQWgg_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__0YNxcA_duk_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+0YNxcA-duk", sceNKWebKitUnknown__0YNxcA_duk_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__0ZZzGYxx_I_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+0ZZzGYxx+I", sceNKWebKitUnknown__0ZZzGYxx_I_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__0rdnkSicqk_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+0rdnkSicqk", sceNKWebKitUnknown__0rdnkSicqk_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__1Ex5GCP8Xs_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+1Ex5GCP8Xs", sceNKWebKitUnknown__1Ex5GCP8Xs_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__1FfZ_0sxvQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+1FfZ+0sxvQ", sceNKWebKitUnknown__1FfZ_0sxvQ_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__1ObAtzCrYE_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+1ObAtzCrYE", sceNKWebKitUnknown__1ObAtzCrYE_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__1n_h4FUu9E_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+1n+h4FUu9E", sceNKWebKitUnknown__1n_h4FUu9E_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__26aZpK535w_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+26aZpK535w", sceNKWebKitUnknown__26aZpK535w_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__2XuBaAXb_4_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+2XuBaAXb+4", sceNKWebKitUnknown__2XuBaAXb_4_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__2rTX2KM7Ms_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+2rTX2KM7Ms", sceNKWebKitUnknown__2rTX2KM7Ms_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__31dbB3S_6Y_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+31dbB3S-6Y", sceNKWebKitUnknown__31dbB3S_6Y_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__3p6wRzzzlQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+3p6wRzzzlQ", sceNKWebKitUnknown__3p6wRzzzlQ_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__4_GNOUIBt0_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+4-GNOUIBt0", sceNKWebKitUnknown__4_GNOUIBt0_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__45BiDnCTxw_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+45BiDnCTxw", sceNKWebKitUnknown__45BiDnCTxw_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__4lVmXsIwqM_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+4lVmXsIwqM", sceNKWebKitUnknown__4lVmXsIwqM_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__5E2YZZIstM_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+5E2YZZIstM", sceNKWebKitUnknown__5E2YZZIstM_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__5V_UOk8zS4_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+5V-UOk8zS4", sceNKWebKitUnknown__5V_UOk8zS4_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__63IB3UdMOY_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+63IB3UdMOY", sceNKWebKitUnknown__63IB3UdMOY_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__6boXsT6XFA_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+6boXsT6XFA", sceNKWebKitUnknown__6boXsT6XFA_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__79w68212vg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+79w68212vg", sceNKWebKitUnknown__79w68212vg_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__7HpqmfJz48_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+7HpqmfJz48", sceNKWebKitUnknown__7HpqmfJz48_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__8T1y8Y_lgQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+8T1y8Y-lgQ", sceNKWebKitUnknown__8T1y8Y_lgQ_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__8VuTEFp13s_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+8VuTEFp13s", sceNKWebKitUnknown__8VuTEFp13s_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__9TKACifR2g_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+9TKACifR2g", sceNKWebKitUnknown__9TKACifR2g_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__9ZBRWNf4og_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+9ZBRWNf4og", sceNKWebKitUnknown__9ZBRWNf4og_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__9us0nrDaCU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+9us0nrDaCU", sceNKWebKitUnknown__9us0nrDaCU_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__AIEKk6K05M_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+AIEKk6K05M", sceNKWebKitUnknown__AIEKk6K05M_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__Abk7CfTivU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+Abk7CfTivU", sceNKWebKitUnknown__Abk7CfTivU_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__AnsPWo12m8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+AnsPWo12m8", sceNKWebKitUnknown__AnsPWo12m8_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__BDqCDede1o_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+BDqCDede1o", sceNKWebKitUnknown__BDqCDede1o_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__BSUP6XSeAw_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+BSUP6XSeAw", sceNKWebKitUnknown__BSUP6XSeAw_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__BZCJi3OIiQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+BZCJi3OIiQ", sceNKWebKitUnknown__BZCJi3OIiQ_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__ByVxWl1pW8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+ByVxWl1pW8", sceNKWebKitUnknown__ByVxWl1pW8_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__CgSI_4D534_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+CgSI-4D534", sceNKWebKitUnknown__CgSI_4D534_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__CpPA9_CKUA_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+CpPA9+CKUA", sceNKWebKitUnknown__CpPA9_CKUA_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__DcUw5YwkSY_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+DcUw5YwkSY", sceNKWebKitUnknown__DcUw5YwkSY_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__DpDBrSZWUc_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+DpDBrSZWUc", sceNKWebKitUnknown__DpDBrSZWUc_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__E3D_W4kpl8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+E3D-W4kpl8", sceNKWebKitUnknown__E3D_W4kpl8_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__EN6MgNmVv4_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+EN6MgNmVv4", sceNKWebKitUnknown__EN6MgNmVv4_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__ErgockOJwc_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+ErgockOJwc", sceNKWebKitUnknown__ErgockOJwc_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__FKudG3uS8A_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+FKudG3uS8A", sceNKWebKitUnknown__FKudG3uS8A_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__FWUl_T8yG8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+FWUl-T8yG8", sceNKWebKitUnknown__FWUl_T8yG8_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__Fca5Eyr4BU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+Fca5Eyr4BU", sceNKWebKitUnknown__Fca5Eyr4BU_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__FcvpbbdrZM_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+FcvpbbdrZM", sceNKWebKitUnknown__FcvpbbdrZM_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__FvUhrQaiJQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+FvUhrQaiJQ", sceNKWebKitUnknown__FvUhrQaiJQ_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__GOwQvxqztE_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+GOwQvxqztE", sceNKWebKitUnknown__GOwQvxqztE_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__Gp0M0sPj9I_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+Gp0M0sPj9I", sceNKWebKitUnknown__Gp0M0sPj9I_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__H_bnpXdWtk_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+H-bnpXdWtk", sceNKWebKitUnknown__H_bnpXdWtk_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__H5PTMieXNE_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+H5PTMieXNE", sceNKWebKitUnknown__H5PTMieXNE_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__HuPyGuX_jY_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+HuPyGuX+jY", sceNKWebKitUnknown__HuPyGuX_jY_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__IMGO_dXHis_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+IMGO+dXHis", sceNKWebKitUnknown__IMGO_dXHis_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__ImJ026_DAw_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+ImJ026-DAw", sceNKWebKitUnknown__ImJ026_DAw_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__IoqHgIw0PM_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+IoqHgIw0PM", sceNKWebKitUnknown__IoqHgIw0PM_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__JnngM7WyHI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+JnngM7WyHI", sceNKWebKitUnknown__JnngM7WyHI_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__Kb7j57jDzs_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+Kb7j57jDzs", sceNKWebKitUnknown__Kb7j57jDzs_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__Kx_IV_21hk_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+Kx-IV+21hk", sceNKWebKitUnknown__Kx_IV_21hk_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__LUabdo811U_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+LUabdo811U", sceNKWebKitUnknown__LUabdo811U_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__LiOmqon2ZI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+LiOmqon2ZI", sceNKWebKitUnknown__LiOmqon2ZI_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__LoAenYdWlM_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+LoAenYdWlM", sceNKWebKitUnknown__LoAenYdWlM_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__LrmM3KN3N8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+LrmM3KN3N8", sceNKWebKitUnknown__LrmM3KN3N8_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__MzGFuNN348_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+MzGFuNN348", sceNKWebKitUnknown__MzGFuNN348_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__N_GwpCV4yk_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+N-GwpCV4yk", sceNKWebKitUnknown__N_GwpCV4yk_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__N6UhRaLrJE_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+N6UhRaLrJE", sceNKWebKitUnknown__N6UhRaLrJE_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__NGrXdCa0q8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+NGrXdCa0q8", sceNKWebKitUnknown__NGrXdCa0q8_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__NiLTGGKF_E_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+NiLTGGKF+E", sceNKWebKitUnknown__NiLTGGKF_E_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__Nqfy8HVG84_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+Nqfy8HVG84", sceNKWebKitUnknown__Nqfy8HVG84_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__NuhuZH75ak_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+NuhuZH75ak", sceNKWebKitUnknown__NuhuZH75ak_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__OCxbaKiBUo_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+OCxbaKiBUo", sceNKWebKitUnknown__OCxbaKiBUo_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__OOjOkuWL6g_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+OOjOkuWL6g", sceNKWebKitUnknown__OOjOkuWL6g_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__OkTn3_PBxM_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+OkTn3+PBxM", sceNKWebKitUnknown__OkTn3_PBxM_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__PDx3sfB_l4_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+PDx3sfB-l4", sceNKWebKitUnknown__PDx3sfB_l4_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__POsWAWjXo8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+POsWAWjXo8", sceNKWebKitUnknown__POsWAWjXo8_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__PeIrQMgYpw_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+PeIrQMgYpw", sceNKWebKitUnknown__PeIrQMgYpw_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__Q7xLvpc0PA_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+Q7xLvpc0PA", sceNKWebKitUnknown__Q7xLvpc0PA_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__QL3zQ6ZNy4_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+QL3zQ6ZNy4", sceNKWebKitUnknown__QL3zQ6ZNy4_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__QqzEnc5c80_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+QqzEnc5c80", sceNKWebKitUnknown__QqzEnc5c80_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__R0GXLrsPM0_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+R0GXLrsPM0", sceNKWebKitUnknown__R0GXLrsPM0_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__R2eXISeSvw_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+R2eXISeSvw", sceNKWebKitUnknown__R2eXISeSvw_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__RkwSDm5kGU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+RkwSDm5kGU", sceNKWebKitUnknown__RkwSDm5kGU_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__RtBREW92M0_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+RtBREW92M0", sceNKWebKitUnknown__RtBREW92M0_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__S72K_fa2_8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+S72K+fa2-8", sceNKWebKitUnknown__S72K_fa2_8_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__S7ftAAWhoQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+S7ftAAWhoQ", sceNKWebKitUnknown__S7ftAAWhoQ_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__SHzgUiuQDE_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+SHzgUiuQDE", sceNKWebKitUnknown__SHzgUiuQDE_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__Sgy63JgEgQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+Sgy63JgEgQ", sceNKWebKitUnknown__Sgy63JgEgQ_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__Sozphy1Wa8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+Sozphy1Wa8", sceNKWebKitUnknown__Sozphy1Wa8_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__TPntL1OK80_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+TPntL1OK80", sceNKWebKitUnknown__TPntL1OK80_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__TT_AplhQWA_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+TT+AplhQWA", sceNKWebKitUnknown__TT_AplhQWA_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__TjNmuhxdBQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+TjNmuhxdBQ", sceNKWebKitUnknown__TjNmuhxdBQ_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__U_nxwdrnDY_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+U-nxwdrnDY", sceNKWebKitUnknown__U_nxwdrnDY_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__UKI_MUuiCU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+UKI-MUuiCU", sceNKWebKitUnknown__UKI_MUuiCU_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__VvktcfIouQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+VvktcfIouQ", sceNKWebKitUnknown__VvktcfIouQ_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__WS2yRv5aBI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+WS2yRv5aBI", sceNKWebKitUnknown__WS2yRv5aBI_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__XZfnHByVO0_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+XZfnHByVO0", sceNKWebKitUnknown__XZfnHByVO0_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__YBfWz5udq4_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+YBfWz5udq4", sceNKWebKitUnknown__YBfWz5udq4_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__YPwmEWTZwE_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+YPwmEWTZwE", sceNKWebKitUnknown__YPwmEWTZwE_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__YYEOsi3IMk_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+YYEOsi3IMk", sceNKWebKitUnknown__YYEOsi3IMk_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__YyqQTUIQJQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+YyqQTUIQJQ", sceNKWebKitUnknown__YyqQTUIQJQ_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__ZgGbFykhls_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+ZgGbFykhls", sceNKWebKitUnknown__ZgGbFykhls_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__ZwjLJ74Y38_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+ZwjLJ74Y38", sceNKWebKitUnknown__ZwjLJ74Y38_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__b1xwU7aUwU_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+b1xwU7aUwU", sceNKWebKitUnknown__b1xwU7aUwU_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__b5TfVQ3_4s_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+b5TfVQ3-4s", sceNKWebKitUnknown__b5TfVQ3_4s_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__bDb7iby5NI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+bDb7iby5NI", sceNKWebKitUnknown__bDb7iby5NI_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__baqz7ahOng_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+baqz7ahOng", sceNKWebKitUnknown__baqz7ahOng_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__bvtn3pCtek_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+bvtn3pCtek", sceNKWebKitUnknown__bvtn3pCtek_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__byORTSkWQo_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+byORTSkWQo", sceNKWebKitUnknown__byORTSkWQo_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__dAwquupevQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+dAwquupevQ", sceNKWebKitUnknown__dAwquupevQ_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__dTlroLZao8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+dTlroLZao8", sceNKWebKitUnknown__dTlroLZao8_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__eVN0UdRT8c_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+eVN0UdRT8c", sceNKWebKitUnknown__eVN0UdRT8c_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__fTKZiGqo8E_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+fTKZiGqo8E", sceNKWebKitUnknown__fTKZiGqo8E_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__feeFP8Y2_c_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+feeFP8Y2+c", sceNKWebKitUnknown__feeFP8Y2_c_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__iLBOBrdx5A_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+iLBOBrdx5A", sceNKWebKitUnknown__iLBOBrdx5A_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__iLdIVuhDhY_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+iLdIVuhDhY", sceNKWebKitUnknown__iLdIVuhDhY_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__jG4wqqA8yg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+jG4wqqA8yg", sceNKWebKitUnknown__jG4wqqA8yg_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__jLvgfDA97c_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+jLvgfDA97c", sceNKWebKitUnknown__jLvgfDA97c_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__ji7IBGwnCM_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+ji7IBGwnCM", sceNKWebKitUnknown__ji7IBGwnCM_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__k3JGU_UUXo_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+k3JGU-UUXo", sceNKWebKitUnknown__k3JGU_UUXo_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__kX_CdbguB0_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+kX-CdbguB0", sceNKWebKitUnknown__kX_CdbguB0_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__keSg8ay8cE_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+keSg8ay8cE", sceNKWebKitUnknown__keSg8ay8cE_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__lI9zCCS_HQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+lI9zCCS-HQ", sceNKWebKitUnknown__lI9zCCS_HQ_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__lwLqZJLo3g_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+lwLqZJLo3g", sceNKWebKitUnknown__lwLqZJLo3g_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__mS029ncrbo_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+mS029ncrbo", sceNKWebKitUnknown__mS029ncrbo_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__mYy22Ucg7Y_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+mYy22Ucg7Y", sceNKWebKitUnknown__mYy22Ucg7Y_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__nHWcLWOmb4_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+nHWcLWOmb4", sceNKWebKitUnknown__nHWcLWOmb4_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__nSg5kHtXkg_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+nSg5kHtXkg", sceNKWebKitUnknown__nSg5kHtXkg_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__nc3IouJMEA_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+nc3IouJMEA", sceNKWebKitUnknown__nc3IouJMEA_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__ndmq8LhAjA_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+ndmq8LhAjA", sceNKWebKitUnknown__ndmq8LhAjA_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__nrEMsyYHiw_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+nrEMsyYHiw", sceNKWebKitUnknown__nrEMsyYHiw_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__oJHrfcyJJM_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+oJHrfcyJJM", sceNKWebKitUnknown__oJHrfcyJJM_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__ovAafdG0O8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+ovAafdG0O8", sceNKWebKitUnknown__ovAafdG0O8_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__pWGccNXto8_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+pWGccNXto8", sceNKWebKitUnknown__pWGccNXto8_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__q0GELad_mY_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+q0GELad-mY", sceNKWebKitUnknown__q0GELad_mY_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__qXuNlEFSRQ_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+qXuNlEFSRQ", sceNKWebKitUnknown__qXuNlEFSRQ_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__qsTbJFS86A_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+qsTbJFS86A", sceNKWebKitUnknown__qsTbJFS86A_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__r_TJDA2yIM_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+r+TJDA2yIM", sceNKWebKitUnknown__r_TJDA2yIM_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__r0UH41I1dI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+r0UH41I1dI", sceNKWebKitUnknown__r0UH41I1dI_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__r8OGkuL4dc_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+r8OGkuL4dc", sceNKWebKitUnknown__r8OGkuL4dc_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__rFA8ZR_qaI_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+rFA8ZR+qaI", sceNKWebKitUnknown__rFA8ZR_qaI_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__rPvFDsPbXM_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+rPvFDsPbXM", sceNKWebKitUnknown__rPvFDsPbXM_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__sUvE9LF80k_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+sUvE9LF80k", sceNKWebKitUnknown__sUvE9LF80k_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__suuwfNT5Oc_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+suuwfNT5Oc", sceNKWebKitUnknown__suuwfNT5Oc_nid_no_patch);
int APS5_VABI sceNKWebKitUnknown__t_MaTLADe4_nid_no_patch() {
NotImplemented_nid_no_patch(__func__);
}
APS5_EXPORT("+t-MaTLADe4", sceNKWebKitUnknown__t_MaTLADe4_nid_no_patch);
}
+89 -2
View File
@@ -131,7 +131,7 @@ static bool HasIpv4Callback() {
return false;
}
static bool HostHasAddress(int family) {
static bool QueryHostHasAddress(int family) {
#ifdef _WIN32
static const int socket_status = [] {
WSADATA data{};
@@ -188,6 +188,74 @@ struct HostIpv4Interface {
std::vector<in_addr> dns;
};
#ifdef _WIN32
namespace {
struct HostAddressCache {
std::mutex lock;
bool registered = false;
bool tried = false;
std::atomic<std::uint64_t> generation{1};
std::uint64_t seen[3]{};
bool value[3]{};
std::uint64_t interfaceSeen = 0;
bool interfaceFound = false;
HostIpv4Interface interfaceValue;
};
HostAddressCache& AddressCache() {
static HostAddressCache cache;
return cache;
}
void CALLBACK HostAddressChanged(PVOID, PMIB_UNICASTIPADDRESS_ROW, MIB_NOTIFICATION_TYPE) {
AddressCache().generation.fetch_add(1, std::memory_order_acq_rel);
}
void CALLBACK HostInterfaceChanged(PVOID, PMIB_IPINTERFACE_ROW, MIB_NOTIFICATION_TYPE) {
AddressCache().generation.fetch_add(1, std::memory_order_acq_rel);
}
void CALLBACK HostRouteChanged(PVOID, PMIB_IPFORWARD_ROW2, MIB_NOTIFICATION_TYPE) {
AddressCache().generation.fetch_add(1, std::memory_order_acq_rel);
}
void WatchHostAddresses(HostAddressCache& cache) {
if (cache.tried) return;
cache.tried = true;
HANDLE addresses = nullptr;
HANDLE interfaces = nullptr;
HANDLE routes = nullptr;
cache.registered = NotifyUnicastIpAddressChange(AF_UNSPEC, HostAddressChanged, nullptr, FALSE, &addresses) == NO_ERROR &&
NotifyIpInterfaceChange(AF_UNSPEC, HostInterfaceChanged, nullptr, FALSE, &interfaces) == NO_ERROR &&
NotifyRouteChange2(AF_UNSPEC, HostRouteChanged, nullptr, FALSE, &routes) == NO_ERROR;
if (!cache.registered) {
if (addresses != nullptr) CancelMibChangeNotify2(addresses);
if (interfaces != nullptr) CancelMibChangeNotify2(interfaces);
if (routes != nullptr) CancelMibChangeNotify2(routes);
}
}
}
#endif
static bool HostHasAddress(int family) {
#ifdef _WIN32
auto& cache = AddressCache();
const int slot = family == AF_INET ? 0 : family == AF_INET6 ? 1 : 2;
std::lock_guard guard(cache.lock);
WatchHostAddresses(cache);
const auto generation = cache.generation.load(std::memory_order_acquire);
if (cache.registered && cache.seen[slot] == generation) return cache.value[slot];
const bool value = QueryHostHasAddress(family);
cache.value[slot] = value;
cache.seen[slot] = generation;
return value;
#else
return QueryHostHasAddress(family);
#endif
}
static bool UsableIpv4(in_addr address) {
const auto host = ntohl(address.s_addr);
return host != 0 && (host >> 24) != 127;
@@ -310,7 +378,7 @@ static std::vector<in_addr> HostDnsServers() {
}
#endif
static bool FindHostIpv4Interface(HostIpv4Interface& found) {
static bool QueryHostIpv4Interface(HostIpv4Interface& found) {
#ifdef _WIN32
ULONG size = 0;
const ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_INCLUDE_GATEWAYS;
@@ -371,6 +439,25 @@ static bool FindHostIpv4Interface(HostIpv4Interface& found) {
#endif
}
static bool FindHostIpv4Interface(HostIpv4Interface& found) {
#ifdef _WIN32
auto& cache = AddressCache();
std::lock_guard guard(cache.lock);
WatchHostAddresses(cache);
const auto generation = cache.generation.load(std::memory_order_acquire);
if (!cache.registered || cache.interfaceSeen != generation) {
HostIpv4Interface value;
cache.interfaceFound = QueryHostIpv4Interface(value);
cache.interfaceValue = std::move(value);
cache.interfaceSeen = generation;
}
if (cache.interfaceFound) found = cache.interfaceValue;
return cache.interfaceFound;
#else
return QueryHostIpv4Interface(found);
#endif
}
extern "C" std::size_t NetCtlHostDnsServers_nid_no_patch(std::uint32_t* servers, std::size_t capacity) {
HostIpv4Interface host;
if (!FindHostIpv4Interface(host)) return 0;
@@ -21,6 +21,13 @@ static constexpr int SCE_NGS2_ERROR_INVALID_VOICE_HANDLE = static_cast<int>(0x80
static constexpr int SCE_NGS2_ERROR_NULL_PARAM_LIST = static_cast<int>(0x804A8360);
static constexpr int SCE_NGS2_ERROR_INVALID_GEOM_LISTENER_ADDRESS = static_cast<int>(0x804A8461);
static constexpr int SCE_NGS2_ERROR_INVALID_GEOM_FLAG = static_cast<int>(0x804A8463);
static constexpr int SCE_NGS2_ERROR_INVALID_OPTION_SIZE = static_cast<int>(0x804A8013);
static constexpr int SCE_NGS2_ERROR_INVALID_NUM_CHANNELS = static_cast<int>(0x804A8052);
static constexpr int SCE_NGS2_ERROR_INVALID_PITCH_SHIFT_OPTION = static_cast<int>(0x804A8098);
static constexpr int SCE_NGS2_ERROR_INVALID_REVERB_OPTION = static_cast<int>(0x804A8099);
static constexpr int SCE_NGS2_ERROR_INVALID_STATE_SIZE = static_cast<int>(0x804A8302);
static constexpr int SCE_NGS2_ERROR_INVALID_BUFFER_INDEX = static_cast<int>(0x804A8306);
static constexpr int SCE_NGS2_ERROR_INVALID_NUM_BUFFERS = static_cast<int>(0x804A830B);
static constexpr std::uint32_t SCE_NGS2_RACK_ID_SAMPLER = 0x1000;
static constexpr std::uint32_t SCE_NGS2_RACK_ID_SUBMIXER = 0x2000;
@@ -71,14 +78,25 @@ static constexpr std::uint32_t SCE_NGS2_CUSTOM_SAMPLER_VOICE_PARAM_EXIT_LOOP = 0
static constexpr std::uint32_t SCE_NGS2_CUSTOM_SAMPLER_VOICE_PARAM_PITCH = 0x40010005;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_SUBMIXER_VOICE_PARAM_SETUP = 0x40020000;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_VOICE_PARAM_FILTER = 0x40001300;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_VOICE_PARAM_GAIN = 0x40001500;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_VOICE_PARAM_USER_FX = 0x40001d00;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_VOICE_PARAM_USER_FX2 = 0x40001f00;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_MAX_MODULES = 24;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_MAX_PORTS = 16;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_MAX_BUFFERS = 4;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_SUBMIXER_MAX_CHANNELS = 37;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_SUBMIXER_MAX_INPUTS = 2;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_REVERB_OPTION_SIZE = 12;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_PITCH_SHIFT_OPTION_SIZE = 8;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_PITCH_SHIFT_STATE_SIZE = 8;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_MODULE_ID_FILTER = 0x13;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_MODULE_ID_GAIN = 0x15;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_MODULE_ID_REVERB = 0x1b;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_MODULE_ID_USER_FX = 0x1d;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_MODULE_ID_USER_FX2 = 0x1f;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_MODULE_ID_PITCH_SHIFT = 0x20;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_MODULE_ID_CONVOLUTION = 0x23;
static constexpr std::uint32_t SCE_NGS2_WAVEFORM_BLOCKS_FLAG_CONTINUE = 1;
static constexpr std::uint32_t SCE_NGS2_WAVEFORM_BLOCKS_FLAG_APPEND = 2;
@@ -527,6 +545,12 @@ struct Ngs2CustomVoiceUserFx2Param {
};
static_assert(sizeof(Ngs2CustomVoiceUserFx2Param) == 24);
struct Ngs2CustomVoiceGainParam {
Ngs2VoiceParamHeader header;
float gain[SCE_NGS2_CUSTOM_SUBMIXER_MAX_CHANNELS];
};
static_assert(sizeof(Ngs2CustomVoiceGainParam) == 156);
struct Ngs2CustomVoiceFilterParam {
Ngs2VoiceParamHeader header;
std::uint32_t type;
+39 -1
View File
@@ -26,6 +26,7 @@ static constexpr std::uint32_t MAX_SAMPLE_RATE = 192000;
static constexpr std::uint32_t WAVEFORM_TYPE_PCM_FIRST = 0x10;
static constexpr std::uint32_t WAVEFORM_TYPE_PCM_LAST = 0x1b;
static constexpr std::uint32_t WAVEFORM_TYPE_VAG = 0x1c;
static constexpr std::uint32_t VAG_FRAME_SAMPLES = 28;
static constexpr std::uint32_t PCM_BYTES_PER_SAMPLE[] = {1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 8, 8};
static bool IsPcmType(std::uint32_t type) {
@@ -43,6 +44,18 @@ static Ngs2WaveformBlock PcmBlock(std::uint32_t type, std::uint32_t channels, st
return block;
}
static Ngs2WaveformBlock VagBlock(std::uint32_t channels, std::uint32_t pos, std::uint32_t count) {
const std::uint32_t frameBytes = 16u * channels;
const std::uint32_t first = pos / VAG_FRAME_SAMPLES;
const std::uint32_t past = (pos + count + VAG_FRAME_SAMPLES - 1u) / VAG_FRAME_SAMPLES;
const auto offset = static_cast<std::uint32_t>(static_cast<std::uint64_t>(first) * frameBytes);
Ngs2WaveformBlock block{};
block.data_offset = offset;
block.data_size = static_cast<std::uint32_t>(past * frameBytes - offset);
block.num_samples = (past - first) * VAG_FRAME_SAMPLES;
return block;
}
void Ngs2Atrac9DecoderDeleter::operator()(void* handle) const {
Atrac9ReleaseHandle(handle);
}
@@ -432,7 +445,10 @@ int APS5_VABI sceNgs2CalcWaveformBlock(const Ngs2WaveformFormat* format, uint32_
*block = PcmBlock(type, format->num_channels, sample_pos, num_samples);
return SCE_NGS2_OK;
}
if (type == WAVEFORM_TYPE_VAG) throw std::runtime_error("NGS2: waveform blocks of type " + Ngs2Hex(type) + " are not implemented");
if (type == WAVEFORM_TYPE_VAG) {
*block = VagBlock(format->num_channels, sample_pos, num_samples);
return SCE_NGS2_OK;
}
if (type != SCE_NGS2_WAVEFORM_TYPE_ATRAC9) return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT;
if (format->num_channels == 0 || format->num_channels > NGS2_MAX_CHANNELS || format->sample_rate == 0 || format->sample_rate > MAX_SAMPLE_RATE) {
return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
@@ -450,6 +466,28 @@ int APS5_VABI sceNgs2CalcWaveformBlock(const Ngs2WaveformFormat* format, uint32_
return SCE_NGS2_OK;
}
int APS5_VABI sceNgs2GetWaveformFrameInfo(const Ngs2WaveformFormat* format, uint32_t* out_frame_size, uint32_t* out_num_frame_samples, uint32_t* out_units_per_frame, uint32_t* out_num_delay_samples) {
if (out_frame_size == nullptr || out_num_frame_samples == nullptr || out_units_per_frame == nullptr || out_num_delay_samples == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
if (format == nullptr || format->num_channels == 0 || format->num_channels > NGS2_MAX_CHANNELS || format->sample_rate == 0 || format->sample_rate > MAX_SAMPLE_RATE) {
return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
}
if (format->waveform_type == SCE_NGS2_WAVEFORM_TYPE_PCM_I16L) {
*out_frame_size = format->num_channels * sizeof(std::int16_t);
*out_num_frame_samples = 1;
*out_units_per_frame = 1;
*out_num_delay_samples = 0;
return SCE_NGS2_OK;
}
if (format->waveform_type != SCE_NGS2_WAVEFORM_TYPE_ATRAC9) throw std::runtime_error("NGS2: the frame info of waveform type " + Ngs2Hex(format->waveform_type) + " is not implemented");
Atrac9CodecInfo codec{};
if (!MatchingCodecInfo(*format, codec)) return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
*out_frame_size = static_cast<std::uint32_t>(codec.superframeSize);
*out_num_frame_samples = static_cast<std::uint32_t>(codec.frameSamples * codec.framesInSuperframe);
*out_units_per_frame = static_cast<std::uint32_t>(codec.framesInSuperframe);
*out_num_delay_samples = static_cast<std::uint32_t>(codec.frameSamples);
return SCE_NGS2_OK;
}
}
#pragma GCC visibility pop
+82 -7
View File
@@ -12,28 +12,75 @@
static constexpr std::uint32_t USER_FX2_FLAG_FIRST_PROCESS = 1;
static constexpr std::uint32_t USER_FX2_FLAG_PARAM_CHANGED = 2;
void Ngs2CheckCustomRack(const Ngs2CustomRackOption& option) {
if (option.num_buffers != 1) throw std::runtime_error("NGS2: custom racks with " + std::to_string(option.num_buffers) + " buffers are not implemented");
static const char* BuiltInModuleName(std::uint32_t moduleId) {
switch (moduleId) {
case SCE_NGS2_CUSTOM_MODULE_ID_GAIN: return "Gain";
case SCE_NGS2_CUSTOM_MODULE_ID_REVERB: return "Reverb";
case SCE_NGS2_CUSTOM_MODULE_ID_PITCH_SHIFT: return "PitchShift";
case SCE_NGS2_CUSTOM_MODULE_ID_CONVOLUTION: return "Convolution";
default: return nullptr;
}
}
static std::uint32_t OptionWord(const Ngs2CustomModuleOption& option) {
std::uint32_t word = 0;
std::memcpy(&word, reinterpret_cast<const std::uint8_t*>(&option) + sizeof(option), sizeof(word));
return word;
}
static int CheckBuiltInModule(const Ngs2CustomRackModuleInfo& info) {
const auto stateSizeIs = [&info](std::uint32_t size) { return info.state_size == 0 || info.state_size == size; };
if (info.module_id == SCE_NGS2_CUSTOM_MODULE_ID_GAIN) {
if (!stateSizeIs(0)) return SCE_NGS2_ERROR_INVALID_STATE_SIZE;
} else if (info.module_id == SCE_NGS2_CUSTOM_MODULE_ID_REVERB) {
if (!stateSizeIs(0)) return SCE_NGS2_ERROR_INVALID_STATE_SIZE;
if (info.option != nullptr && info.option->size != SCE_NGS2_CUSTOM_REVERB_OPTION_SIZE) return SCE_NGS2_ERROR_INVALID_OPTION_SIZE;
if (info.option != nullptr && OptionWord(*info.option) > 1) return SCE_NGS2_ERROR_INVALID_REVERB_OPTION;
} else if (info.module_id == SCE_NGS2_CUSTOM_MODULE_ID_PITCH_SHIFT) {
if (!stateSizeIs(SCE_NGS2_CUSTOM_PITCH_SHIFT_STATE_SIZE)) return SCE_NGS2_ERROR_INVALID_STATE_SIZE;
if (info.option != nullptr && info.option->size == SCE_NGS2_CUSTOM_PITCH_SHIFT_OPTION_SIZE && OptionWord(*info.option) != 0) {
return SCE_NGS2_ERROR_INVALID_PITCH_SHIFT_OPTION;
}
}
return SCE_NGS2_OK;
}
int Ngs2CheckCustomRack(std::uint32_t rackId, const Ngs2CustomRackOption& option, std::uint32_t maxInputs) {
if (option.num_buffers == 0 || option.num_buffers > SCE_NGS2_CUSTOM_MAX_BUFFERS || option.num_buffers < maxInputs) {
return SCE_NGS2_ERROR_INVALID_NUM_BUFFERS;
}
if (option.num_modules > SCE_NGS2_CUSTOM_MAX_MODULES || option.rack_option.max_ports > SCE_NGS2_CUSTOM_MAX_PORTS) APS5_INVALID_ARG_EX;
for (std::uint32_t m = 0; m < option.num_modules; m++) {
const auto& info = option.module[m];
if (info.module_id == SCE_NGS2_CUSTOM_MODULE_ID_USER_FX2) {
if (info.option == nullptr || info.option->size != sizeof(Ngs2CustomUserFx2ModuleOption)) APS5_INVALID_ARG_EX;
if (info.source_buffer_id != 0 || info.dest_buffer_id != 0) APS5_INVALID_ARG_EX;
if (reinterpret_cast<const Ngs2CustomUserFx2ModuleOption*>(info.option)->process_handler == nullptr) APS5_INVALID_ARG_EX;
} else if (info.module_id == SCE_NGS2_CUSTOM_MODULE_ID_FILTER) {
if (info.option != nullptr || info.state_size != 0 || info.state_offset != 0) APS5_INVALID_ARG_EX;
if (info.source_buffer_id != 0 || info.extra_buffer_id != 0 || info.dest_buffer_id != 0) APS5_INVALID_ARG_EX;
} else if (info.module_id == SCE_NGS2_CUSTOM_MODULE_ID_USER_FX) {
if (info.source_buffer_id != 0 || info.dest_buffer_id != 0) APS5_INVALID_ARG_EX;
if (info.option != nullptr && info.option->size < sizeof(Ngs2CustomModuleOption)) APS5_INVALID_ARG_EX;
} else {
} else if (BuiltInModuleName(info.module_id) == nullptr) {
throw std::runtime_error("NGS2: custom module id " + Ngs2Hex(info.module_id) + " is not implemented");
}
if (info.source_buffer_id >= option.num_buffers || info.extra_buffer_id >= option.num_buffers || info.dest_buffer_id >= option.num_buffers) {
return SCE_NGS2_ERROR_INVALID_BUFFER_INDEX;
}
const int result = CheckBuiltInModule(info);
if (result != SCE_NGS2_OK) return result;
}
if (option.num_buffers != 1) throw std::runtime_error("NGS2: custom racks with " + std::to_string(option.num_buffers) + " buffers are not implemented");
for (std::uint32_t m = 0; m < option.num_modules; m++) {
const auto moduleId = option.module[m].module_id;
if (moduleId == SCE_NGS2_CUSTOM_MODULE_ID_GAIN && option.module[m].option != nullptr) throw std::runtime_error("NGS2: an option for the custom Gain module is not implemented");
if (moduleId == SCE_NGS2_CUSTOM_MODULE_ID_GAIN && rackId == SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER) continue;
if (moduleId == SCE_NGS2_CUSTOM_MODULE_ID_GAIN) throw std::runtime_error("NGS2: the custom Gain module on a custom sampler rack is not implemented");
const char* name = BuiltInModuleName(moduleId);
if (name != nullptr) throw std::runtime_error(std::string("NGS2: the custom ") + name + " module is not implemented");
}
for (std::uint32_t p = 0; p < option.rack_option.max_ports; p++) {
if (option.port[p].source_buffer_id != 0) APS5_INVALID_ARG_EX;
}
return SCE_NGS2_OK;
}
static Ngs2UserFx2SetupContext UserFxContext(Ngs2Rack& rack, std::size_t module, std::uint32_t voiceIndex) {
@@ -49,6 +96,7 @@ void Ngs2SetupUserFx(Ngs2Rack& rack, const Ngs2CustomRackOption& option) {
for (auto& voice : rack.voices) {
voice.userFx.resize(option.num_modules);
voice.customUserFx.resize(option.num_modules);
voice.customGain.assign(option.num_modules, {});
}
for (std::uint32_t m = 0; m < option.num_modules; m++) {
rack.customModuleIds[m] = option.module[m].module_id;
@@ -116,6 +164,26 @@ static void SetCustomUserFxParam(Ngs2Voice& voice, std::uint32_t index, const Ng
fx.flags = 1;
}
static void SetGainParam(Ngs2Voice& voice, std::uint32_t index, const Ngs2CustomVoiceGainParam& param) {
if (index >= voice.customGain.size() || voice.rack->customModuleIds[index] != SCE_NGS2_CUSTOM_MODULE_ID_GAIN) APS5_INVALID_ARG_EX;
for (const float gain : param.gain) {
if (!(gain >= -32.0f && gain <= 32.0f)) throw std::runtime_error("NGS2: a custom Gain level outside -32 to 32 is not implemented");
}
std::copy(std::begin(param.gain), std::end(param.gain), voice.customGain[index].target.begin());
}
static void ProcessGain(Ngs2Voice& voice, Ngs2VoiceCustomGain& gain, const std::vector<float*>& channels, std::uint32_t grain) {
if (voice.hasSamples) {
for (std::uint32_t c = 0; c < voice.channels; c++) {
const float from = gain.current[c];
const float step = (gain.target[c] - from) / static_cast<float>(grain);
for (std::uint32_t i = 0; i < grain; i++) channels[c][i] *= from + step * static_cast<float>(i + 1);
}
voice.hasSamples = std::any_of(voice.samples.begin(), voice.samples.end(), [](float sample) { return sample != 0.0f; });
}
gain.current = gain.target;
}
void Ngs2ApplyCustomParam(Ngs2Voice& voice, const Ngs2VoiceParamHeader& param) {
const auto rackId = voice.rack->rackId;
if (param.id == SCE_NGS2_CUSTOM_SUBMIXER_VOICE_PARAM_SETUP) {
@@ -148,6 +216,11 @@ void Ngs2ApplyCustomParam(Ngs2Voice& voice, const Ngs2VoiceParamHeader& param) {
Ngs2SetFilter(voice, filter);
return;
}
if ((param.id & ~0x1fu) == SCE_NGS2_CUSTOM_VOICE_PARAM_GAIN) {
if (rackId != SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER) throw std::invalid_argument("NGS2: voice param " + Ngs2Hex(param.id) + " belongs to another rack");
SetGainParam(voice, param.id & 0x1f, ParamAs<Ngs2CustomVoiceGainParam>(param));
return;
}
if ((param.id & ~0x1fu) == SCE_NGS2_CUSTOM_VOICE_PARAM_USER_FX2) {
if (rackId != SCE_NGS2_RACK_ID_CUSTOM_SAMPLER && rackId != SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER) {
throw std::invalid_argument("NGS2: voice param " + Ngs2Hex(param.id) + " belongs to another rack");
@@ -169,7 +242,9 @@ void Ngs2ProcessUserFx(Ngs2Voice& voice, std::uint32_t grain, std::uint32_t samp
std::vector<float*> channels(voice.channels);
for (std::uint32_t c = 0; c < voice.channels; c++) channels[c] = voice.samples.data() + static_cast<std::size_t>(c) * grain;
for (std::size_t m = 0; m < voice.rack->customModuleIds.size(); m++) {
if (voice.rack->customModuleIds[m] == SCE_NGS2_CUSTOM_MODULE_ID_USER_FX2) {
if (voice.rack->customModuleIds[m] == SCE_NGS2_CUSTOM_MODULE_ID_GAIN) {
ProcessGain(voice, voice.customGain[m], channels, grain);
} else if (voice.rack->customModuleIds[m] == SCE_NGS2_CUSTOM_MODULE_ID_USER_FX2) {
if (!voice.hasSamples) continue;
auto& fx = voice.userFx[m];
const auto& module = voice.rack->userFx[m];
@@ -108,6 +108,11 @@ struct Ngs2VoiceCustomUserFx {
std::uint32_t flags = 0;
};
struct Ngs2VoiceCustomGain {
std::array<float, SCE_NGS2_CUSTOM_SUBMIXER_MAX_CHANNELS> current{};
std::array<float, SCE_NGS2_CUSTOM_SUBMIXER_MAX_CHANNELS> target{};
};
struct Ngs2Voice {
Ngs2Rack* rack = nullptr;
Ngs2PlayState state = Ngs2PlayState::Empty;
@@ -136,6 +141,7 @@ struct Ngs2Voice {
float lfeLevel = 1.0f;
std::vector<Ngs2UserFx2> userFx;
std::vector<Ngs2VoiceCustomUserFx> customUserFx;
std::vector<Ngs2VoiceCustomGain> customGain;
Ngs2UserFxProcessHandler userFxHandler = nullptr;
std::array<std::uintptr_t, 3> userFxData{};
std::uint32_t userFxFlags = 0;
@@ -229,7 +235,7 @@ std::size_t Ngs2Atrac9BlockBytes(const Ngs2Voice& voice, const Ngs2WaveformBlock
void Ngs2RestartAtrac9(Ngs2Voice& voice);
void Ngs2ConsumeAtrac9(Ngs2Voice& voice, std::uint32_t grain, std::uint32_t systemRate);
bool Ngs2FinishBlock(Ngs2Voice& voice);
void Ngs2CheckCustomRack(const Ngs2CustomRackOption& option);
int Ngs2CheckCustomRack(std::uint32_t rackId, const Ngs2CustomRackOption& option, std::uint32_t maxInputs);
void Ngs2SetupUserFx(Ngs2Rack& rack, const Ngs2CustomRackOption& option);
void Ngs2CleanupUserFx(Ngs2Rack& rack);
void Ngs2ApplyCustomParam(Ngs2Voice& voice, const Ngs2VoiceParamHeader& param);
+29 -10
View File
@@ -87,22 +87,35 @@ static std::uint32_t RackMaxChannels(std::uint32_t rackId, const RackOptions& op
return NGS2_MAX_CHANNELS;
}
static RackOptions CheckedRackOption(std::uint32_t rackId, const Ngs2RackOption* option) {
static int CheckedRackOption(std::uint32_t rackId, const Ngs2RackOption* option, RackOptions& options) {
const auto size = RackOptionSize(rackId);
if (option == nullptr) return DefaultRackOption(rackId);
if (option == nullptr) {
options = DefaultRackOption(rackId);
return SCE_NGS2_OK;
}
if (option->size != size) throw std::invalid_argument("NGS2: unexpected rack option size " + std::to_string(option->size));
RackOptions options{};
options = {};
std::memcpy(&options, option, size);
const auto maxChannels = RackMaxChannels(rackId, options);
if (options.common.max_voices == 0 || maxChannels == 0 || maxChannels > NGS2_MAX_CHANNELS) {
if (rackId == SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER && maxChannels > SCE_NGS2_CUSTOM_SUBMIXER_MAX_CHANNELS) return SCE_NGS2_ERROR_INVALID_NUM_CHANNELS;
if (rackId == SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER && (options.customSubmixer.max_inputs == 0 || options.customSubmixer.max_inputs > SCE_NGS2_CUSTOM_SUBMIXER_MAX_INPUTS)) {
throw std::runtime_error("NGS2: custom submixer racks with " + std::to_string(options.customSubmixer.max_inputs) + " inputs are not implemented");
}
if (options.common.max_voices == 0 || maxChannels == 0) throw std::invalid_argument("NGS2: invalid rack voice or channel count");
int result = SCE_NGS2_OK;
if (rackId == SCE_NGS2_RACK_ID_CUSTOM_SAMPLER) result = Ngs2CheckCustomRack(rackId, options.customSampler.custom_rack_option, 0);
if (rackId == SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER) result = Ngs2CheckCustomRack(rackId, options.customSubmixer.custom_rack_option, options.customSubmixer.max_inputs);
if (result != SCE_NGS2_OK) return result;
if (maxChannels > NGS2_MAX_CHANNELS) {
if (rackId == SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER) {
throw std::runtime_error("NGS2: custom submixer racks with " + std::to_string(maxChannels) + " channels are not implemented");
}
throw std::invalid_argument("NGS2: invalid rack voice or channel count");
}
if (rackId == SCE_NGS2_RACK_ID_CUSTOM_SAMPLER) Ngs2CheckCustomRack(options.customSampler.custom_rack_option);
if (rackId == SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER) Ngs2CheckCustomRack(options.customSubmixer.custom_rack_option);
if (rackId == SCE_NGS2_RACK_ID_REVERB && options.reverb.reverb_size != 1) {
throw std::runtime_error("NGS2: reverb rack size " + std::to_string(options.reverb.reverb_size) + " is not implemented");
}
return options;
return SCE_NGS2_OK;
}
static int CreateRack(Ngs2Handle systemHandle, std::uint32_t rackId, const RackOptions& options, const Ngs2ContextBufferInfo& bufferInfo,
@@ -165,7 +178,9 @@ extern "C" {
int APS5_VABI sceNgs2RackQueryBufferSize(uint32_t rack_id, const Ngs2RackOption* option, Ngs2ContextBufferInfo* buffer_info) {
if (buffer_info == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
CheckedRackOption(rack_id, option);
RackOptions options{};
const int result = CheckedRackOption(rack_id, option, options);
if (result != SCE_NGS2_OK) throw std::invalid_argument("NGS2: invalid rack option, rejected at creation with " + Ngs2Hex(static_cast<std::uint32_t>(result)));
*buffer_info = {};
buffer_info->host_buffer_size = sizeof(Ngs2Rack);
return SCE_NGS2_OK;
@@ -173,16 +188,20 @@ int APS5_VABI sceNgs2RackQueryBufferSize(uint32_t rack_id, const Ngs2RackOption*
int APS5_VABI sceNgs2RackCreate(uintptr_t system_handle, uint32_t rack_id, const Ngs2RackOption* option, const Ngs2ContextBufferInfo* buffer_info, uintptr_t* handle) {
if (handle == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
const auto options = CheckedRackOption(rack_id, option);
if (buffer_info == nullptr) APS5_INVALID_ARG_EX;
RackOptions options{};
const int result = CheckedRackOption(rack_id, option, options);
if (result != SCE_NGS2_OK) return result;
const Ngs2Lock lock;
return CreateRack(system_handle, rack_id, options, *buffer_info, {}, handle);
}
int APS5_VABI sceNgs2RackCreateWithAllocator(uintptr_t system_handle, uint32_t rack_id, const Ngs2RackOption* option, const Ngs2BufferAllocator* allocator, uintptr_t* handle) {
if (handle == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
const auto options = CheckedRackOption(rack_id, option);
if (allocator == nullptr || allocator->alloc_handler == nullptr || allocator->free_handler == nullptr) APS5_INVALID_ARG_EX;
RackOptions options{};
const int checked = CheckedRackOption(rack_id, option, options);
if (checked != SCE_NGS2_OK) return checked;
const Ngs2Lock lock;
if (Ngs2FindSystem(system_handle) == nullptr) return SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE;
Ngs2ContextBufferInfo bufferInfo{};
@@ -195,6 +195,45 @@ static void TestCalcBlock() {
Require(sceNgs2CalcWaveformBlock(&silent, 0, 1, &block) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
}
static void TestFrameInfo() {
const Ngs2WaveformFormat supported[] = {
{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 1, 48000, Config, 0, 0},
{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 1, 48000, Config, 7, 9},
};
const Ngs2WaveformFormat rejected[] = {
{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 2, 48000, Config, 0, 0},
{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 0, 48000, Config, 0, 0},
{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 9, 48000, Config, 0, 0},
{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 1, 44100, Config, 0, 0},
{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 1, 0, Config, 0, 0},
{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 1, 192001, Config, 0, 0},
{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 1, 48000, 0, 0, 0},
};
for (const auto& format : supported) {
Ngs2WaveformBlock block{};
Require(sceNgs2CalcWaveformBlock(&format, 0, SuperframeSamples, &block) == SCE_NGS2_OK && block.data_size == SuperframeBytes);
std::uint32_t values[4] = {0xa1, 0xa2, 0xa3, 0xa4};
Require(sceNgs2GetWaveformFrameInfo(&format, &values[0], &values[1], &values[2], &values[3]) == SCE_NGS2_OK);
Require(values[0] == SuperframeBytes && values[1] == SuperframeSamples && values[2] == 4 && values[3] == SuperframeSamples / 4);
}
for (const auto& format : rejected) {
Ngs2WaveformBlock block{};
const int calc = sceNgs2CalcWaveformBlock(&format, 0, SuperframeSamples, &block);
Require(calc == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
std::uint32_t values[4] = {0xa1, 0xa2, 0xa3, 0xa4};
Require(sceNgs2GetWaveformFrameInfo(&format, &values[0], &values[1], &values[2], &values[3]) == calc);
Require(values[0] == 0xa1 && values[1] == 0xa2 && values[2] == 0xa3 && values[3] == 0xa4);
}
std::uint32_t values[4] = {0xa1, 0xa2, 0xa3, 0xa4};
Require(sceNgs2GetWaveformFrameInfo(nullptr, &values[0], &values[1], &values[2], &values[3]) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
Require(sceNgs2GetWaveformFrameInfo(&supported[0], nullptr, &values[1], &values[2], &values[3]) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(sceNgs2GetWaveformFrameInfo(&supported[0], &values[0], nullptr, &values[2], &values[3]) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(sceNgs2GetWaveformFrameInfo(&supported[0], &values[0], &values[1], nullptr, &values[3]) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(sceNgs2GetWaveformFrameInfo(&supported[0], &values[0], &values[1], &values[2], nullptr) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(sceNgs2GetWaveformFrameInfo(nullptr, nullptr, nullptr, nullptr, nullptr) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(values[0] == 0xa1 && values[1] == 0xa2 && values[2] == 0xa3 && values[3] == 0xa4);
}
static void RequirePcmBlock(std::uint32_t type, std::uint32_t channels, std::uint32_t pos, std::uint32_t count, std::uint64_t offset, std::uint64_t size) {
const Ngs2WaveformFormat format{type, channels, 48000, 0, 0, 0};
Ngs2WaveformBlock block;
@@ -229,6 +268,26 @@ static void TestCalcPcmBlock() {
Require(sceNgs2CalcWaveformBlock(nullptr, 0, 1, &block) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT && block.data_size == 0 && block.num_samples == 0);
}
static void RequireVagBlock(std::uint32_t channels, std::uint32_t pos, std::uint32_t count, std::uint64_t offset, std::uint64_t size, std::uint32_t samples) {
const Ngs2WaveformFormat format{0x1c, channels, 48000, 0, 0, 0};
Ngs2WaveformBlock block;
std::memset(&block, 0xab, sizeof(block));
Require(sceNgs2CalcWaveformBlock(&format, pos, count, &block) == SCE_NGS2_OK);
Require(block.data_offset == offset && block.data_size == size && block.num_samples == samples);
Require(block.num_repeats == 0 && block.num_skip_samples == 0 && block.reserved == 0 && block.user_data == 0);
}
static void TestCalcVagBlock() {
RequireVagBlock(1, 0, 28, 0, 16, 28);
RequireVagBlock(1, 27, 2, 0, 32, 56);
RequireVagBlock(1, 28, 28, 16, 16, 28);
RequireVagBlock(2, 30, 1, 32, 32, 28);
RequireVagBlock(6, 56, 57, 192, 288, 84);
RequireVagBlock(1, 0, 0, 0, 0, 0);
RequireVagBlock(1, 5, 0, 0, 16, 28);
RequireVagBlock(0, 100, 50, 0, 0, 84);
}
static void Set32(std::vector<std::uint8_t>& bytes, std::size_t offset, std::uint32_t value) {
for (int i = 0; i < 4; ++i) bytes[offset + i] = static_cast<std::uint8_t>(value >> (i * 8));
}
@@ -538,7 +597,9 @@ int main() {
TestParseLoops();
TestMalformedLoops();
TestCalcBlock();
TestFrameInfo();
TestCalcPcmBlock();
TestCalcVagBlock();
TestCorruptSuperframeAtPageEnd();
TestStreamPartitions();
TestRefill(reference);
@@ -356,6 +356,153 @@ static void TestCustomFilter() {
}
struct ModuleOptionWords {
std::uint32_t size;
std::uint32_t words[15];
};
static int allocCalls = 0;
static std::int32_t APS5_VABI CountAlloc(Ngs2ContextBufferInfo*) {
allocCalls++;
return SCE_NGS2_OK;
}
static std::int32_t APS5_VABI CountFree(Ngs2ContextBufferInfo*) {
return SCE_NGS2_OK;
}
static Ngs2CustomSubmixerRackOption BuiltInRackOption(const Ngs2CustomUserFx2ModuleOption& userFx2, std::uint32_t moduleId,
const ModuleOptionWords* moduleOption, std::uint32_t stateSize) {
auto option = RackOption(&userFx2, 1);
auto& module = option.custom_rack_option.module[0];
module.module_id = moduleId;
module.option = reinterpret_cast<const Ngs2CustomModuleOption*>(moduleOption);
module.state_size = stateSize;
return option;
}
static void TestCustomRackChecks() {
calls = {};
const auto system = CreateSystem();
const auto userFx2 = Module(Gain);
const auto good = RackOption(&userFx2, 1);
Ngs2ContextBufferInfo query{};
Require(sceNgs2RackQueryBufferSize(SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER, &good.custom_rack_option.rack_option, &query) == SCE_NGS2_OK);
const auto info = Buffer(query);
const auto create = [&](const Ngs2CustomSubmixerRackOption& option) {
uintptr_t rack = 0;
const int result = sceNgs2RackCreate(system, SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER, &option.custom_rack_option.rack_option, &info, &rack);
Require(rack == 0);
return result;
};
auto option = good;
option.custom_rack_option.num_buffers = 0;
Require(create(option) == SCE_NGS2_ERROR_INVALID_NUM_BUFFERS);
Require(CreateThrows<std::invalid_argument>(option));
option.custom_rack_option.num_buffers = SCE_NGS2_CUSTOM_MAX_BUFFERS + 1;
Require(create(option) == SCE_NGS2_ERROR_INVALID_NUM_BUFFERS);
option = good;
option.max_inputs = 2;
Require(create(option) == SCE_NGS2_ERROR_INVALID_NUM_BUFFERS);
option.custom_rack_option.num_buffers = 2;
Require(CreateThrows<std::runtime_error>(option));
option.custom_rack_option.num_buffers = SCE_NGS2_CUSTOM_MAX_BUFFERS;
option.custom_rack_option.module[0].dest_buffer_id = SCE_NGS2_CUSTOM_MAX_BUFFERS - 1;
Require(CreateThrows<std::runtime_error>(option));
option.custom_rack_option.module[0].dest_buffer_id = SCE_NGS2_CUSTOM_MAX_BUFFERS;
Require(create(option) == SCE_NGS2_ERROR_INVALID_BUFFER_INDEX);
option = good;
option.max_inputs = 0;
Require(CreateThrows<std::runtime_error>(option));
option.max_inputs = SCE_NGS2_CUSTOM_SUBMIXER_MAX_INPUTS + 1;
Require(CreateThrows<std::runtime_error>(option));
option = good;
option.custom_rack_option.module[0].source_buffer_id = 1;
Require(create(option) == SCE_NGS2_ERROR_INVALID_BUFFER_INDEX);
option = good;
option.custom_rack_option.module[0].extra_buffer_id = 1;
Require(create(option) == SCE_NGS2_ERROR_INVALID_BUFFER_INDEX);
option = good;
option.max_channels = SCE_NGS2_CUSTOM_SUBMIXER_MAX_CHANNELS + 1;
Require(create(option) == SCE_NGS2_ERROR_INVALID_NUM_CHANNELS);
option.max_channels = 16;
Require(CreateThrows<std::runtime_error>(option));
option.max_channels = SCE_NGS2_CUSTOM_SUBMIXER_MAX_CHANNELS;
Require(CreateThrows<std::runtime_error>(option));
Require(!CreateThrows<std::runtime_error>(BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_GAIN, nullptr, 0)));
Require(create(BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_GAIN, nullptr, 4)) == SCE_NGS2_ERROR_INVALID_STATE_SIZE);
const ModuleOptionWords gainOption{sizeof(Ngs2CustomModuleOption), {}};
Require(CreateThrows<std::runtime_error>(BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_GAIN, &gainOption, 0)));
const ModuleOptionWords reverb{SCE_NGS2_CUSTOM_REVERB_OPTION_SIZE, {1}};
const ModuleOptionWords reverbOff{SCE_NGS2_CUSTOM_REVERB_OPTION_SIZE, {0}};
const ModuleOptionWords reverbSize{16, {1}};
const ModuleOptionWords reverbWord{SCE_NGS2_CUSTOM_REVERB_OPTION_SIZE, {2}};
Require(CreateThrows<std::runtime_error>(BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_REVERB, &reverb, 0)));
Require(CreateThrows<std::runtime_error>(BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_REVERB, &reverbOff, 0)));
Require(create(BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_REVERB, &reverbSize, 0)) == SCE_NGS2_ERROR_INVALID_OPTION_SIZE);
Require(create(BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_REVERB, &reverbWord, 0)) == SCE_NGS2_ERROR_INVALID_REVERB_OPTION);
Require(create(BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_REVERB, &reverb, 4)) == SCE_NGS2_ERROR_INVALID_STATE_SIZE);
const ModuleOptionWords pitchShift{SCE_NGS2_CUSTOM_PITCH_SHIFT_OPTION_SIZE, {0}};
const ModuleOptionWords pitchShiftWord{SCE_NGS2_CUSTOM_PITCH_SHIFT_OPTION_SIZE, {1}};
const auto pitchShiftState = SCE_NGS2_CUSTOM_PITCH_SHIFT_STATE_SIZE;
Require(CreateThrows<std::runtime_error>(BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_PITCH_SHIFT, &pitchShift, pitchShiftState)));
Require(CreateThrows<std::runtime_error>(BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_PITCH_SHIFT, &pitchShift, 0)));
Require(create(BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_PITCH_SHIFT, &pitchShift, 4)) == SCE_NGS2_ERROR_INVALID_STATE_SIZE);
Require(create(BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_PITCH_SHIFT, &pitchShiftWord, pitchShiftState)) == SCE_NGS2_ERROR_INVALID_PITCH_SHIFT_OPTION);
const ModuleOptionWords convolution{64, {}};
Require(CreateThrows<std::runtime_error>(BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_CONVOLUTION, &convolution, 64)));
option = BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_CONVOLUTION, &convolution, 64);
option.custom_rack_option.module[0].dest_buffer_id = 1;
Require(create(option) == SCE_NGS2_ERROR_INVALID_BUFFER_INDEX);
option = BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_GAIN, nullptr, 0);
option.custom_rack_option.num_modules = 2;
option.custom_rack_option.module[1] = good.custom_rack_option.module[0];
option.custom_rack_option.module[1].dest_buffer_id = 1;
Require(create(option) == SCE_NGS2_ERROR_INVALID_BUFFER_INDEX);
Require(CreateThrows<std::runtime_error>(BuiltInRackOption(userFx2, 0x19, nullptr, 0)));
option = good;
option.custom_rack_option.num_buffers = 0;
bool rejected = false;
uintptr_t rack = 0;
try {
sceNgs2RackCreate(system, SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER, &option.custom_rack_option.rack_option, nullptr, &rack);
} catch (const std::invalid_argument&) {
rejected = true;
}
Require(rejected);
const Ngs2BufferAllocator allocator{CountAlloc, CountFree, 0};
Require(sceNgs2RackCreateWithAllocator(system, SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER, &option.custom_rack_option.rack_option, &allocator, &rack) == SCE_NGS2_ERROR_INVALID_NUM_BUFFERS);
Require(rack == 0 && allocCalls == 0 && calls.setupVoices.empty());
Ngs2CustomSamplerRackOption sampler{};
sampler.custom_rack_option.rack_option.size = sizeof(sampler);
sampler.custom_rack_option.rack_option.max_grain_samples = 512;
sampler.custom_rack_option.rack_option.max_voices = 1;
sampler.custom_rack_option.num_modules = 1;
sampler.custom_rack_option.module[0].module_id = SCE_NGS2_CUSTOM_MODULE_ID_FILTER;
sampler.max_channel_works = 1;
Require(sceNgs2RackCreate(system, SCE_NGS2_RACK_ID_CUSTOM_SAMPLER, &sampler.custom_rack_option.rack_option, &info, &rack) == SCE_NGS2_ERROR_INVALID_NUM_BUFFERS);
sampler.custom_rack_option.num_buffers = 1;
sampler.custom_rack_option.module[0].extra_buffer_id = 1;
Require(sceNgs2RackCreate(system, SCE_NGS2_RACK_ID_CUSTOM_SAMPLER, &sampler.custom_rack_option.rack_option, &info, &rack) == SCE_NGS2_ERROR_INVALID_BUFFER_INDEX);
Require(rack == 0);
sampler.custom_rack_option.module[0].extra_buffer_id = 0;
Require(sceNgs2RackCreate(system, SCE_NGS2_RACK_ID_CUSTOM_SAMPLER, &sampler.custom_rack_option.rack_option, &info, &rack) == SCE_NGS2_OK);
sampler.custom_rack_option.module[0].module_id = SCE_NGS2_CUSTOM_MODULE_ID_GAIN;
Require(CreateSamplerThrows<std::runtime_error>(sampler));
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
}
struct LegacyUserFxState {
std::uint32_t calls = 0;
std::uint32_t flags = 0;
@@ -467,11 +614,62 @@ static void TestCustomLegacyUserFx() {
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
}
static void TestCustomGain() {
const auto system = CreateSystem();
const auto master = Mastering(system, 2);
const auto userFx2 = Module(Gain);
const auto rack = CustomRack(system, BuiltInRackOption(userFx2, SCE_NGS2_CUSTOM_MODULE_ID_GAIN, nullptr, 0));
const auto custom = Voice(rack);
Control(custom, SCE_NGS2_CUSTOM_SUBMIXER_VOICE_PARAM_SETUP, Ngs2CustomSubmixerVoiceSetupParam{{}, 2, 2, 0, 0});
Patch(custom, master);
Event(custom, SCE_NGS2_VOICE_EVENT_PLAY);
std::vector<std::int16_t> pcm(Grain * 2 * 8, 16384);
const auto sampler = Sampler(system, pcm);
Patch(sampler, custom);
Event(sampler, SCE_NGS2_VOICE_EVENT_PLAY);
auto out = Render(system);
for (std::uint32_t i = 0; i < Grain; i++) Require(out[i * 2] == 0.0f && out[i * 2 + 1] == 0.0f);
Ngs2CustomVoiceGainParam gain{};
gain.gain[0] = 2.0f;
gain.gain[1] = -0.5f;
Control(custom, SCE_NGS2_CUSTOM_VOICE_PARAM_GAIN | 0, gain);
out = Render(system);
for (std::uint32_t i = 0; i < Grain; i++) {
Require(out[i * 2] == 0.5f * 2.0f * static_cast<float>(i + 1) / Grain);
Require(out[i * 2 + 1] == 0.5f * -0.5f * static_cast<float>(i + 1) / Grain);
}
out = Render(system);
for (std::uint32_t i = 0; i < Grain; i++) Require(out[i * 2] == 1.0f && out[i * 2 + 1] == -0.25f);
gain.gain[0] = 1.0f;
Control(custom, SCE_NGS2_CUSTOM_VOICE_PARAM_GAIN | 0, gain);
out = Render(system);
for (std::uint32_t i = 0; i < Grain; i++) Require(out[i * 2] == 0.5f * (2.0f - static_cast<float>(i + 1) / Grain));
auto outside = gain;
outside.gain[2] = 33.0f;
Require(ControlThrows<std::runtime_error>(custom, SCE_NGS2_CUSTOM_VOICE_PARAM_GAIN | 0, outside));
Require(ControlThrows<std::invalid_argument>(custom, SCE_NGS2_CUSTOM_VOICE_PARAM_GAIN | 1, gain));
Require(ControlThrows<std::invalid_argument>(sampler, SCE_NGS2_CUSTOM_VOICE_PARAM_GAIN | 0, gain));
struct ShortGainParam {
Ngs2VoiceParamHeader header;
float gain[2];
};
Require(ControlThrows<std::invalid_argument>(custom, SCE_NGS2_CUSTOM_VOICE_PARAM_GAIN | 0, ShortGainParam{}));
Require(sceNgs2RackDestroy(rack, nullptr) == SCE_NGS2_OK);
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
}
int main() {
TestUserFxChain();
TestRackOptions();
TestCustomRackChecks();
TestCustomSampler();
TestCustomFilter();
TestCustomLegacyUserFx();
TestCustomGain();
return 0;
}
@@ -113,9 +113,59 @@ static void TestParseFileBank() {
std::filesystem::remove(path);
}
static void TestFrameInfoPcm() {
for (std::uint32_t channels = 1; channels <= 8; channels++) {
for (const std::uint32_t rate : {1u, 22050u, 48000u, 192000u}) {
const Ngs2WaveformFormat format{SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, channels, rate, 0, 0, 0};
std::uint32_t frameSize = 0xa1, frameSamples = 0xa2, units = 0xa3, delay = 0xa4;
Require(sceNgs2GetWaveformFrameInfo(&format, &frameSize, &frameSamples, &units, &delay) == SCE_NGS2_OK);
Require(frameSize == channels * 2 && frameSamples == 1 && units == 1 && delay == 0);
const auto file = PcmFile(channels, rate, 16, 10);
Ngs2WaveformInfo info{};
Require(sceNgs2ParseWaveformData(file.data(), file.size(), &info) == SCE_NGS2_OK);
Require(info.audio_frame_size == frameSize && info.num_audio_frame_samples == frameSamples);
Require(info.num_audio_unit_per_frame == units && info.num_delay_samples == delay);
}
}
const Ngs2WaveformFormat ignored{SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, 2, 48000, 0x12345678, 3, 5};
std::uint32_t frameSize = 0xa1, frameSamples = 0xa2, units = 0xa3, delay = 0xa4;
Require(sceNgs2GetWaveformFrameInfo(&ignored, &frameSize, &frameSamples, &units, &delay) == SCE_NGS2_OK);
Require(frameSize == 4 && frameSamples == 1 && units == 1 && delay == 0);
}
static void TestFrameInfoErrors() {
const Ngs2WaveformFormat pcm{SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, 2, 48000, 0, 0, 0};
std::uint32_t values[4] = {0xa1, 0xa2, 0xa3, 0xa4};
const auto untouched = [&] { return values[0] == 0xa1 && values[1] == 0xa2 && values[2] == 0xa3 && values[3] == 0xa4; };
Require(sceNgs2GetWaveformFrameInfo(&pcm, nullptr, &values[1], &values[2], &values[3]) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(sceNgs2GetWaveformFrameInfo(&pcm, &values[0], nullptr, &values[2], &values[3]) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(sceNgs2GetWaveformFrameInfo(&pcm, &values[0], &values[1], nullptr, &values[3]) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(sceNgs2GetWaveformFrameInfo(&pcm, &values[0], &values[1], &values[2], nullptr) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(sceNgs2GetWaveformFrameInfo(nullptr, nullptr, nullptr, nullptr, nullptr) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(sceNgs2GetWaveformFrameInfo(nullptr, &values[0], &values[1], &values[2], &values[3]) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
Require(untouched());
const Ngs2WaveformFormat invalid[] = {
{SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, 0, 48000, 0, 0, 0},
{SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, 9, 48000, 0, 0, 0},
{SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, 2, 0, 0, 0, 0},
{SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, 2, 192001, 0, 0, 0},
{0x99, 0, 48000, 0, 0, 0},
{SCE_NGS2_WAVEFORM_TYPE_PCM_F32L, 2, 0, 0, 0, 0},
};
for (const auto& format : invalid) {
Require(sceNgs2GetWaveformFrameInfo(&format, &values[0], &values[1], &values[2], &values[3]) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
Require(untouched());
}
for (const std::uint32_t type : {0u, 0x10u, 0x11u, 0x13u, 0x14u, 0x16u, SCE_NGS2_WAVEFORM_TYPE_PCM_F32L, 0x19u, 0x41u, 0xffffffffu}) {
Require(FrameInfoThrows({type, 2, 48000, 0, 0, 0}));
}
}
int main() {
TestParsePcm();
TestParseFile();
TestParseFileBank();
TestFrameInfoPcm();
TestFrameInfoErrors();
return 0;
}
@@ -6,12 +6,14 @@
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <vector>
extern "C" {
int APS5_VABI sceNgs2ParseWaveformData(const void*, size_t, Ngs2WaveformInfo*);
int APS5_VABI sceNgs2ParseWaveformFile(const char*, uint32_t, Ngs2WaveformInfo*);
int APS5_VABI sceNgs2CalcWaveformBlock(const Ngs2WaveformFormat*, uint32_t, uint32_t, Ngs2WaveformBlock*);
int APS5_VABI sceNgs2GetWaveformFrameInfo(const Ngs2WaveformFormat*, uint32_t*, uint32_t*, uint32_t*, uint32_t*);
int APS5_VABI sceNgs2SystemResetOption(Ngs2SystemOption*);
int APS5_VABI sceNgs2SystemQueryBufferSize(const Ngs2SystemOption*, Ngs2ContextBufferInfo*);
int APS5_VABI sceNgs2SystemCreate(const Ngs2SystemOption*, const Ngs2ContextBufferInfo*, uintptr_t*);
@@ -60,6 +62,18 @@ inline void Check(bool value, int line) {
}
#define Require(value) Check((value), __LINE__)
inline bool FrameInfoThrows(const Ngs2WaveformFormat& format) {
std::uint32_t values[4] = {0xa1, 0xa2, 0xa3, 0xa4};
bool threw = false;
try {
sceNgs2GetWaveformFrameInfo(&format, &values[0], &values[1], &values[2], &values[3]);
} catch (const std::exception&) {
threw = true;
}
Require(values[0] == 0xa1 && values[1] == 0xa2 && values[2] == 0xa3 && values[3] == 0xa4);
return threw;
}
inline constexpr std::uint32_t Grain = 8;
inline std::vector<std::vector<std::uint64_t>> buffers;
@@ -21,10 +21,6 @@ int APS5_VABI sceNgs2FftQuerySize() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2GetWaveformFrameInfo(void* format, std::uint32_t* outFrameSize, std::uint32_t* outNumFrameSamples, std::uint32_t* outUnitsPerFrame, std::uint32_t* outNumDelaySamples) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2ParseWaveformUser(std::uintptr_t handler, std::uintptr_t userData, void* outInfo) {
NotImplemented_nid_no_patch(__func__);
return 0;
+467
View File
@@ -1890,4 +1890,471 @@ std::uint64_t APS5_VABI _ZNK3sce2Np9CppWebApi17TitleCloudStorage2V15Owner12getAc
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V110FriendsApi10getFriendsEiRKNS4_21ParameterToGetFriendsERNS1_6Common11TransactionINS8_12IntrusivePtrINS3_18GetFriendsResponseEEENSA_INS8_18ResponseHeaderBaseEEEEE() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V110FriendsApi21ParameterToGetFriends10initializeEPNS1_6Common10LibContextEPKc() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V110FriendsApi21ParameterToGetFriends9terminateEv() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V110FriendsApi21ParameterToGetFriendsC1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V110FriendsApi21ParameterToGetFriendsD1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V111PresenceApi17getBasicPresencesEiRKNS4_28ParameterToGetBasicPresencesERNS1_6Common11TransactionINS8_12IntrusivePtrINS3_25GetBasicPresencesResponseEEENSA_INS8_18ResponseHeaderBaseEEEEE() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V111PresenceApi28ParameterToGetBasicPresences10initializeEPNS1_6Common10LibContextEPKc() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V111PresenceApi28ParameterToGetBasicPresences9terminateEv() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V111PresenceApi28ParameterToGetBasicPresencesC1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V111PresenceApi28ParameterToGetBasicPresencesD1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V118GetFriendsResponse10getFriendsEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V124GetBlockingUsersResponse9getBlocksEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V125GetBasicPresencesResponse17getBasicPresencesEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V19BlocksApi16getBlockingUsersEiRKNS4_27ParameterToGetBlockingUsersERNS1_6Common11TransactionINS8_12IntrusivePtrINS3_24GetBlockingUsersResponseEEENSA_INS8_18ResponseHeaderBaseEEEEE() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V19BlocksApi27ParameterToGetBlockingUsers10initializeEPNS1_6Common10LibContextE() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V19BlocksApi27ParameterToGetBlockingUsers9terminateEv() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V19BlocksApi27ParameterToGetBlockingUsersC1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V19BlocksApi27ParameterToGetBlockingUsersD1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi15ProfanityFilter2V212ProfanityApi15filterProfanityEiRKNS4_26ParameterToFilterProfanityERNS1_6Common11TransactionINS8_12IntrusivePtrINS3_23FilterProfanityResponseEEENSA_INS8_18ResponseHeaderBaseEEEEE() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZN3sce2Np9CppWebApi15ProfanityFilter2V212ProfanityApi26ParameterToFilterProfanity10initializeEPNS1_6Common10LibContextEPKcSA_NS6_12IntrusivePtrINS3_19WebApiFilterRequestEEE() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi15ProfanityFilter2V212ProfanityApi26ParameterToFilterProfanity9terminateEv() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi15ProfanityFilter2V212ProfanityApi26ParameterToFilterProfanityC1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi15ProfanityFilter2V212ProfanityApi26ParameterToFilterProfanityD1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi15ProfanityFilter2V226WebApiFilterRequestFactory6createEPNS1_6Common10LibContextEPKcPNS5_12IntrusivePtrINS3_19WebApiFilterRequestEEE() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZN3sce2Np9CppWebApi30CommunicationRestrictionStatus2V333CommunicationRestrictionStatusApi33getCommunicationRestrictionStatusEiRKNS4_44ParameterToGetCommunicationRestrictionStatusERNS1_6Common11TransactionINS8_12IntrusivePtrINS3_38CommunicationRestrictionStatusResponseEEENSA_INS8_18ResponseHeaderBaseEEEEE() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZN3sce2Np9CppWebApi30CommunicationRestrictionStatus2V333CommunicationRestrictionStatusApi44ParameterToGetCommunicationRestrictionStatus10initializeEPNS1_6Common10LibContextEm() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi30CommunicationRestrictionStatus2V333CommunicationRestrictionStatusApi44ParameterToGetCommunicationRestrictionStatus9terminateEv() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi30CommunicationRestrictionStatus2V333CommunicationRestrictionStatusApi44ParameterToGetCommunicationRestrictionStatusC1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi30CommunicationRestrictionStatus2V333CommunicationRestrictionStatusApi44ParameterToGetCommunicationRestrictionStatusD1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEE21setOnFinishedCallbackEPFvliS8_PvEPFvliSC_ESC_() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEEC1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEED1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE21setOnFinishedCallbackEPFvliS8_PvEPFvliSC_ESC_() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEEC1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEED1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE21setOnFinishedCallbackEPFvliS8_PvEPFvliSC_ESC_() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEEC1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEED1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_15ProfanityFilter2V223FilterProfanityResponseEEENS4_INS2_18ResponseHeaderBaseEEEE21setOnFinishedCallbackEPFvliS8_PvEPFvliSC_ESC_() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_15ProfanityFilter2V223FilterProfanityResponseEEENS4_INS2_18ResponseHeaderBaseEEEEC1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_15ProfanityFilter2V223FilterProfanityResponseEEENS4_INS2_18ResponseHeaderBaseEEEED1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEENS4_INS2_18ResponseHeaderBaseEEEE21setOnFinishedCallbackEPFvliS8_PvEPFvliSC_ESC_() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEENS4_INS2_18ResponseHeaderBaseEEEEC1Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEENS4_INS2_18ResponseHeaderBaseEEEED1Ev(void* self) {
(void)self;
return 0;
}
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_15ProfanityFilter2V219WebApiFilterRequestEEC1ERS7_(IntrusivePtr* self, const IntrusivePtr* source) {
CopyIntrusivePtr(self, source, ReferenceCountOffset);
}
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_15ProfanityFilter2V219WebApiFilterRequestEEC1Ev(IntrusivePtr* self) {
*self = {};
}
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_15ProfanityFilter2V219WebApiFilterRequestEED1Ev(IntrusivePtr* self) {
ReleaseIntrusivePtr(self, ReferenceCountOffset, __func__);
}
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_18ResponseHeaderBaseEEC1Ev(IntrusivePtr* self) {
*self = {};
}
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_18ResponseHeaderBaseEED1Ev(IntrusivePtr* self) {
ReleaseIntrusivePtr(self, ReferenceCountOffset, __func__);
}
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6StringEEC1Ev(IntrusivePtr* self) {
*self = {};
}
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6StringEED1Ev(IntrusivePtr* self) {
ReleaseIntrusivePtr(self, ReferenceCountOffset, __func__);
}
void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_11UserProfile2V113BasicPresenceEEEEEED1Ev(IntrusivePtr* self) {
ReleaseIntrusivePtr(self, VectorReferenceCountOffset, __func__);
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common13ConstIteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEED2Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common13ConstIteratorImED2Ev(void* self) {
(void)self;
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEE28setResponseInformationOptionEP37SceNpWebApi2ResponseInformationOption() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5startEPNS2_10LibContextE() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE28setResponseInformationOptionEP37SceNpWebApi2ResponseInformationOption() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5startEPNS2_10LibContextE() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE28setResponseInformationOptionEP37SceNpWebApi2ResponseInformationOption() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5startEPNS2_10LibContextE() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_15ProfanityFilter2V223FilterProfanityResponseEEENS4_INS2_18ResponseHeaderBaseEEEE28setResponseInformationOptionEP37SceNpWebApi2ResponseInformationOption() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_15ProfanityFilter2V223FilterProfanityResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5startEPNS2_10LibContextE() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_15ProfanityFilter2V223FilterProfanityResponseEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEENS4_INS2_18ResponseHeaderBaseEEEE28setResponseInformationOptionEP37SceNpWebApi2ResponseInformationOption() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5startEPNS2_10LibContextE() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv() {
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common18ResponseHeaderBase14getHeaderValueEPKcRNS2_12IntrusivePtrINS2_6StringEEE() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEE3endEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEE5beginEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorImE3endEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorImE5beginEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEppEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorImEppEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence12getAccountIdEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence12getInContextEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence15getOnlineStatusEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi15ProfanityFilter2V223FilterProfanityResponse10getMessageEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponse13getRestrictedEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEptEv(const IntrusivePtr* self) {
return self->object;
}
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEptEv(const IntrusivePtr* self) {
return self->object;
}
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEptEv(const IntrusivePtr* self) {
return self->object;
}
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEptEv(const IntrusivePtr* self) {
return self->object;
}
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_15ProfanityFilter2V223FilterProfanityResponseEEptEv(const IntrusivePtr* self) {
return self->object;
}
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEptEv(const IntrusivePtr* self) {
return self->object;
}
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_18ResponseHeaderBaseEE3getEv(const IntrusivePtr* self) {
return self->object;
}
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_18ResponseHeaderBaseEEptEv(const IntrusivePtr* self) {
return self->object;
}
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6StringEE3getEv(const IntrusivePtr* self) {
return self->object;
}
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6StringEEptEv(const IntrusivePtr* self) {
return self->object;
}
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_11UserProfile2V113BasicPresenceEEEEEEdeEv(const IntrusivePtr* self) {
return self->object;
}
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_11UserProfile2V113BasicPresenceEEEEEEptEv(const IntrusivePtr* self) {
return self->object;
}
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorImEEEdeEv(const IntrusivePtr* self) {
return self->object;
}
void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorImEEEptEv(const IntrusivePtr* self) {
return self->object;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEE18getResponseHeadersERSA_() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE18getResponseHeadersERSA_() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE18getResponseHeadersERSA_() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_15ProfanityFilter2V223FilterProfanityResponseEEENS4_INS2_18ResponseHeaderBaseEEEE18getResponseHeadersERSA_() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEENS4_INS2_18ResponseHeaderBaseEEEE18getResponseHeadersERSA_() {
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEE4sizeEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorImE4sizeEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEdeEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEneERKS9_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorImEdeEv() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorImEneERKS4_() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}
@@ -23,8 +23,14 @@ static constexpr uint32_t SKU_FLAG_FULL = 3;
namespace {
#ifdef _WIN32
const wchar_t* ConfiguredEntitlements() { return _wgetenv(L"ANYPS5_ENTITLEMENTS"); }
#else
const char* ConfiguredEntitlements() { return std::getenv("ANYPS5_ENTITLEMENTS"); }
#endif
std::filesystem::path EntitlementsPath() {
if (const char* configured = std::getenv("ANYPS5_ENTITLEMENTS"); configured != nullptr && configured[0] != '\0') return configured;
if (const auto* configured = ConfiguredEntitlements(); configured != nullptr && configured[0] != 0) return configured;
#ifdef _WIN32
wchar_t module[MAX_PATH];
const auto length = GetModuleFileNameW(nullptr, module, MAX_PATH);
@@ -50,7 +56,7 @@ const std::vector<NpEntitlementAccessAddcontEntitlementInfo>& OwnedAddons() {
const auto path = EntitlementsPath();
std::ifstream file(path);
if (!file) {
if (std::getenv("ANYPS5_ENTITLEMENTS") != nullptr || std::filesystem::exists(path)) throw std::runtime_error("NpEntitlementAccess: cannot read " + path.string());
if (ConfiguredEntitlements() != nullptr || std::filesystem::exists(path)) throw std::runtime_error("NpEntitlementAccess: cannot read " + path.string());
return addons;
}
std::string line;
+28 -19
View File
@@ -20,6 +20,7 @@ static constexpr int SCE_NP_ERROR_CALLBACK_MAX = static_cast<int>(0x8055001D);
static constexpr uint32_t NP_STATE_SIGNED_OUT = 1;
static constexpr int NP_POLL_ASYNC_FINISHED = 0;
static constexpr uint32_t NP_REACHABILITY_STATE_UNAVAILABLE = 0;
static constexpr uint32_t NP_GAME_PRESENCE_STATUS_OFFLINE = 0;
static std::atomic<int> g_nextRequest{1};
@@ -37,6 +38,12 @@ int AddRequest() {
return reqId;
}
int SignedOutRequest(int reqId) {
if (reqId == 0) return SCE_NP_ERROR_INVALID_ARGUMENT;
std::lock_guard lock(requestMutex);
return liveRequests.count(reqId) ? SCE_NP_ERROR_SIGNED_OUT : SCE_NP_ERROR_REQUEST_NOT_FOUND;
}
using NpStateCallbackA = void (APS5_VABI *)(int userId, uint32_t state, void* userdata);
using NpStateCallback = void (APS5_VABI *)(int userId, uint32_t state, NpId* npId, void* userdata);
@@ -253,29 +260,31 @@ int APS5_VABI sceNpAsmTerminate(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpCheckNpAvailabilityA(void) {
NotImplemented_nid_no_patch(__func__);
int APS5_VABI sceNpCheckNpAvailabilityA(int reqId, int userId) {
if (userId == USER_SERVICE_USER_ID_INVALID) return SCE_NP_ERROR_INVALID_ARGUMENT;
return SignedOutRequest(reqId);
}
int APS5_VABI sceNpGetAccountDateOfBirthA(int userId, void* dateOfBirth) {
if (!dateOfBirth || userId == USER_SERVICE_USER_ID_INVALID) return SCE_NP_ERROR_INVALID_ARGUMENT;
return SCE_NP_ERROR_SIGNED_OUT;
}
int APS5_VABI sceNpGetAccountLanguageA(int reqId, int userId, void* language) {
if (!language || userId == USER_SERVICE_USER_ID_INVALID) return SCE_NP_ERROR_INVALID_ARGUMENT;
return SignedOutRequest(reqId);
}
int APS5_VABI sceNpGetGamePresenceStatus(const NpOnlineId* onlineId, uint32_t* status) {
if (!onlineId || !status) return SCE_NP_ERROR_INVALID_ARGUMENT;
*status = NP_GAME_PRESENCE_STATUS_OFFLINE;
return 0;
}
int APS5_VABI sceNpGetAccountDateOfBirthA(void) {
NotImplemented_nid_no_patch(__func__);
int APS5_VABI sceNpGetGamePresenceStatusA(int userId, uint32_t* status) {
if (!status || userId == USER_SERVICE_USER_ID_INVALID) return SCE_NP_ERROR_INVALID_ARGUMENT;
*status = NP_GAME_PRESENCE_STATUS_OFFLINE;
return 0;
}
int APS5_VABI sceNpGetAccountLanguageA(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpGetGamePresenceStatus(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpGetGamePresenceStatusA(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpGetParentalControlInfoA(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpGetParentalControlInfoA(int reqId, int userId, int8_t* age, void* info) {
if (!age || !info || userId == USER_SERVICE_USER_ID_INVALID) return SCE_NP_ERROR_INVALID_ARGUMENT;
return SignedOutRequest(reqId);
}
int APS5_VABI sceNpIdMapperAbortRequest(void) {
NotImplemented_nid_no_patch(__func__);
+35 -27
View File
@@ -1,3 +1,4 @@
#include <atomic>
#include <cstdint>
#include <cstddef>
#include "SceTypes.hpp"
@@ -5,9 +6,19 @@
namespace {
constexpr int NP_PARTY_ERROR_ALREADY_INITIALIZED = static_cast<int>(0x80552502u);
constexpr int NP_PARTY_ERROR_NOT_INITIALIZED = static_cast<int>(0x80552503u);
constexpr int NP_PARTY_ERROR_INVALID_ARGUMENT = static_cast<int>(0x80552504u);
constexpr int NP_PARTY_ERROR_NOT_IN_PARTY = static_cast<int>(0x80552506u);
constexpr std::uint16_t NP_PARTY_STATE_NOT_IN_PARTY = 2;
std::atomic<bool> g_initialized{false};
int notInParty(const void* out) {
if (!out) return NP_PARTY_ERROR_INVALID_ARGUMENT;
return NP_PARTY_ERROR_NOT_IN_PARTY;
}
}
extern "C" {
@@ -17,8 +28,9 @@ int APS5_VABI sceNpPartyGetState(std::uint16_t* state) {
*state = NP_PARTY_STATE_NOT_IN_PARTY;
return 0;
}
int APS5_VABI sceNpPartyCheckCallback(void) {
NotImplemented_nid_no_patch(__func__);
if (!g_initialized) return NP_PARTY_ERROR_NOT_INITIALIZED;
return 0;
}
@@ -32,9 +44,8 @@ int APS5_VABI sceNpPartyCreateA(void) {
return 0;
}
int APS5_VABI sceNpPartyGetId(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpPartyGetId(void* id) {
return notInParty(id);
}
int APS5_VABI sceNpPartyGetMemberInfo(void) {
@@ -42,19 +53,16 @@ int APS5_VABI sceNpPartyGetMemberInfo(void) {
return 0;
}
int APS5_VABI sceNpPartyGetMemberInfoA(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpPartyGetMemberInfoA(std::uint16_t, void* memberInfo) {
return notInParty(memberInfo);
}
int APS5_VABI sceNpPartyGetMemberSessionInfo(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpPartyGetMemberSessionInfo(std::uint16_t, void* memberGameSessionInfo) {
return notInParty(memberGameSessionInfo);
}
int APS5_VABI sceNpPartyGetMemberVoiceInfo(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpPartyGetMemberVoiceInfo(std::uint16_t, std::uint16_t, std::uint8_t* memberVoiceState) {
return notInParty(memberVoiceState);
}
int APS5_VABI sceNpPartyGetMembers(void) {
@@ -62,9 +70,8 @@ int APS5_VABI sceNpPartyGetMembers(void) {
return 0;
}
int APS5_VABI sceNpPartyGetMembersA(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpPartyGetMembersA(void* memberList) {
return notInParty(memberList);
}
int APS5_VABI sceNpPartyGetStateAsUser(void) {
@@ -72,9 +79,8 @@ int APS5_VABI sceNpPartyGetStateAsUser(void) {
return 0;
}
int APS5_VABI sceNpPartyGetStateAsUserA(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpPartyGetStateAsUserA(std::int32_t, std::uint16_t* state) {
return sceNpPartyGetState(state);
}
int APS5_VABI sceNpPartyGetVoiceChatPriority(void) {
@@ -82,8 +88,10 @@ int APS5_VABI sceNpPartyGetVoiceChatPriority(void) {
return 0;
}
int APS5_VABI sceNpPartyInitialize(void) {
NotImplemented_nid_no_patch(__func__);
int APS5_VABI sceNpPartyInitialize(const void* param) {
if (!param) return NP_PARTY_ERROR_INVALID_ARGUMENT;
bool expected = false;
if (!g_initialized.compare_exchange_strong(expected, true)) return NP_PARTY_ERROR_ALREADY_INITIALIZED;
return 0;
}
@@ -102,8 +110,9 @@ int APS5_VABI sceNpPartyRegisterHandler(void) {
return 0;
}
int APS5_VABI sceNpPartyRegisterHandlerA(void) {
NotImplemented_nid_no_patch(__func__);
int APS5_VABI sceNpPartyRegisterHandlerA(const void* handlers, void*) {
if (!g_initialized) return NP_PARTY_ERROR_NOT_INITIALIZED;
if (!handlers) return NP_PARTY_ERROR_INVALID_ARGUMENT;
return 0;
}
@@ -112,9 +121,8 @@ int APS5_VABI sceNpPartyRegisterPrivateHandler(void) {
return 0;
}
int APS5_VABI sceNpPartySendBinaryMessage(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpPartySendBinaryMessage(const void* sendParams) {
return notInParty(sendParams);
}
int APS5_VABI sceNpPartySetVoiceChatPriority(void) {
@@ -133,7 +141,7 @@ int APS5_VABI sceNpPartyShowInvitationListA(void) {
}
int APS5_VABI sceNpPartyTerminate(void) {
NotImplemented_nid_no_patch(__func__);
if (!g_initialized.exchange(false)) return NP_PARTY_ERROR_NOT_INITIALIZED;
return 0;
}
+17 -11
View File
@@ -88,6 +88,16 @@ int Complete(int reqId, int asyncResult) {
if (const int error = WaitOffline(reqId, &result)) return error;
return result;
}
constexpr std::uint32_t INVALID_SERVICE_LABEL = 0xFFFFFFFF;
int CreateTitleContext() {
std::lock_guard lock(tusMutex);
if (titleContexts.size() >= MAX_TITLE_CONTEXTS) return SCE_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS;
const int contextId = nextTitleContext++;
titleContexts.insert(contextId);
return contextId;
}
}
extern "C" {
@@ -95,11 +105,7 @@ extern "C" {
int APS5_VABI sceNpTusCreateNpTitleCtxA(std::uint32_t serviceLabel, int userId) {
(void)serviceLabel;
(void)userId;
std::lock_guard lock(tusMutex);
if (titleContexts.size() >= MAX_TITLE_CONTEXTS) return SCE_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS;
const int contextId = nextTitleContext++;
titleContexts.insert(contextId);
return contextId;
return CreateTitleContext();
}
int APS5_VABI sceNpTusCreateRequest(int titleCtxId) {
@@ -177,14 +183,14 @@ int APS5_VABI sceNpTusSetThreadParam(void) {
return 0;
}
int APS5_VABI sceNpTssCreateNpTitleCtx(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpTssCreateNpTitleCtx(std::uint32_t serviceLabel, const NpId* npId) {
if (!npId) return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
if (serviceLabel == INVALID_SERVICE_LABEL) return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
return CreateTitleContext();
}
int APS5_VABI sceNpTssCreateNpTitleCtxA(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
int APS5_VABI sceNpTssCreateNpTitleCtxA(std::uint32_t serviceLabel, int userId) {
return sceNpTusCreateNpTitleCtxA(serviceLabel, userId);
}
int APS5_VABI sceNpTusCreateTitleCtx(void) {
+6 -6
View File
@@ -923,18 +923,18 @@ bool APS5_VABI scePadIsValidHandle(std::int32_t handle, std::int32_t portType) {
}
bool APS5_VABI scePadIsDS4Connected(std::int32_t handle) {
NotImplemented_nid_no_patch(__func__);
return 0;
(void)handle;
return false;
}
bool APS5_VABI scePadIsBlasterConnected(std::int32_t handle) {
NotImplemented_nid_no_patch(__func__);
return 0;
(void)handle;
return false;
}
bool APS5_VABI scePadIsMoveConnected(std::int32_t handle) {
NotImplemented_nid_no_patch(__func__);
return 0;
(void)handle;
return false;
}
int APS5_VABI scePadGetBluetoothAddress(std::int32_t handle, std::uint64_t* address) {
Loaded 100 of 247 files, more files were not shown because too many files have changed in this diff. Show more