diff --git a/core/libs/prx/libSceAgc/CMakeLists.txt b/core/libs/prx/libSceAgc/CMakeLists.txt index f1822c8ce..8553cfd66 100644 --- a/core/libs/prx/libSceAgc/CMakeLists.txt +++ b/core/libs/prx/libSceAgc/CMakeLists.txt @@ -83,6 +83,19 @@ if(BUILD_TESTING) endif() endif() +add_test_executable(agc_packet_info_tests EXCLUDE_FROM_ALL tests/PacketInfo.cpp) +target_include_directories(agc_packet_info_tests PRIVATE ${LIBS_INCLUDE_DIR}) +target_link_libraries(agc_packet_info_tests PRIVATE libSceAgc libc) +configure_windows_unwind(agc_packet_info_tests) +if(BUILD_TESTING) + set_target_properties(agc_packet_info_tests PROPERTIES EXCLUDE_FROM_ALL FALSE) + add_test(NAME agc_packet_info_tests COMMAND agc_packet_info_tests) + if(WIN32) + set_tests_properties(agc_packet_info_tests PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() + add_test_executable(agc_register_command_tests EXCLUDE_FROM_ALL tests/RegisterCommands.cpp Command/src/Packet.cpp diff --git a/core/libs/prx/libSceAgc/Misc/src/PacketInfo.cpp b/core/libs/prx/libSceAgc/Misc/src/PacketInfo.cpp index 12f74258a..a0d16bb57 100644 --- a/core/libs/prx/libSceAgc/Misc/src/PacketInfo.cpp +++ b/core/libs/prx/libSceAgc/Misc/src/PacketInfo.cpp @@ -9,9 +9,12 @@ extern "C" { uint32_t APS5_VABI sceAgcGetPacketSize(uint32_t* packet) { - (void)packet; - NotImplemented_nid_no_patch(__func__); - return 0; + Agc::Command::CheckAddress(reinterpret_cast(packet), alignof(uint32_t), __func__); + Agc::Command::Require((packet[0] & 0xc0000000u) == 0xc0000000u, __func__, "source pointer is not a PM4 packet"); + if ((packet[0] & 0x3fffff00u) == 0x3fff1000u) { + return 1; + } + return ((packet[0] >> 16u) & 0x3fffu) + 2u; } APS5_EXPORT("V++UgBtQhn0", sceAgcGetDataPacketPayloadAddressUnk); diff --git a/core/libs/prx/libSceAgc/tests/PacketInfo.cpp b/core/libs/prx/libSceAgc/tests/PacketInfo.cpp new file mode 100644 index 000000000..5e95906b5 --- /dev/null +++ b/core/libs/prx/libSceAgc/tests/PacketInfo.cpp @@ -0,0 +1,66 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include "prx/libc/include/Shutdown.hpp" + +#include +#include +#include +#include +#include + +extern "C" std::uint32_t APS5_VABI sceAgcGetPacketSize(std::uint32_t* packet); + +namespace { + +void check(bool condition, const char* message) { + if (!condition) throw std::runtime_error(message); +} + +template +void expectFailure(TAction action) { + try { + action(); + } catch (const std::runtime_error& error) { + check(error.what()[0] != '\0', "empty exception message"); + return; + } + throw std::runtime_error("expected an exception"); +} + +void testPacketSize() { + const std::array, 7> sizes{{ + {0xc0047600u, 6}, {0xc0001000u, 2}, {0xfffe1000u, 0x4000}, {0xffff7600u, 0x4001}, + {0xffff1000u, 1}, {0xffff1001u, 1}, {0xffff10fcu, 1}}}; + for (const auto& [header, size] : sizes) { + std::array words{header, 0xffff1000u}; + check(sceAgcGetPacketSize(words.data()) == size, "packet size mismatch"); + check(words[0] == header && words[1] == 0xffff1000u, "packet size query modified the packet"); + } +} + +void testRejections() { + for (const auto header : {0x80000000u, 0x3fff1000u, 0x40001000u, 0x00047600u}) { + std::uint32_t word = header; + expectFailure([&] { sceAgcGetPacketSize(&word); }); + } + expectFailure([] { sceAgcGetPacketSize(nullptr); }); + std::array words{0x00100000u, 0x000000c0u}; + auto* misaligned = reinterpret_cast(reinterpret_cast(words.data()) + 1); + expectFailure([&] { sceAgcGetPacketSize(misaligned); }); +} + +} + +int main() { + try { + testPacketSize(); + testRejections(); + LibcRunShutdown_nid_postfix(); + std::puts("AGC packet info tests passed"); + return 0; + } catch (const std::exception& error) { + std::fprintf(stderr, "%s\n", error.what()); + try { LibcRunShutdown_nid_postfix(); } + catch (const std::exception& shutdown) { std::fprintf(stderr, "shutdown: %s\n", shutdown.what()); } + return 1; + } +}