diff --git a/core/libs/CMakeLists.txt b/core/libs/CMakeLists.txt index 0679e4c24b..fe8f6265a2 100644 --- a/core/libs/CMakeLists.txt +++ b/core/libs/CMakeLists.txt @@ -261,6 +261,16 @@ add_custom_target(libs DEPENDS ${PATCHED_OUTPUTS}) include(prx/libSceLibreSsl/ProtocolSelection.cmake) if(BUILD_TESTING) + add_test_executable(guest_beisobmf_boxes_tests tests/GuestBeisobmfBoxes.cpp) + target_include_directories(guest_beisobmf_boxes_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(guest_beisobmf_boxes_tests PRIVATE libSceBeisobmf libc) + configure_windows_unwind(guest_beisobmf_boxes_tests) + add_test(NAME guest_beisobmf_boxes COMMAND guest_beisobmf_boxes_tests) + if(WIN32) + set_tests_properties(guest_beisobmf_boxes PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() + add_test_executable(guest_posix_scalar_io_tests tests/GuestPosixScalarIo.cpp) target_include_directories(guest_posix_scalar_io_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(guest_posix_scalar_io_tests PRIVATE libkernel libc) diff --git a/core/libs/prx/libSceBeisobmf/Export.cpp b/core/libs/prx/libSceBeisobmf/Export.cpp index 31c0e4b2fe..2001e95233 100644 --- a/core/libs/prx/libSceBeisobmf/Export.cpp +++ b/core/libs/prx/libSceBeisobmf/Export.cpp @@ -1,100 +1,163 @@ +#include "SceTypes.hpp" #include "prx/libSceBeisobmf/include/Api.hpp" +#include "prx/libc/include/General.hpp" +#include "prx/libc/include/GuestArena.hpp" #include #include -#include "SceTypes.hpp" -#include "prx/libc/include/General.hpp" +#include +#include + +namespace { + +template +TValue ReadField(const void *object, std::size_t offset) { + if (!object) + throw std::invalid_argument("isobmf box pointer is null"); + TValue value{}; + std::memcpy(&value, static_cast(object) + offset, + sizeof(value)); + return value; +} + +std::uintptr_t ReadPointer(const void *object, std::size_t offset) { + return ReadField(object, offset); +} + +void WritePointer(void *object, std::size_t offset, std::uintptr_t value) { + std::memcpy(static_cast(object) + offset, &value, sizeof(value)); +} + +std::uintptr_t FindMember(void *box, const char *name) { + if (!box || !name) + throw std::invalid_argument("isobmf member lookup requires a box and name"); + auto *cursor = static_cast(box) + 0x50; + const GuestArena::HostWrite write(cursor, sizeof(std::uintptr_t)); + if (!write.Open()) + throw std::runtime_error( + "isobmf member cursor is not writable guest memory"); + WritePointer(box, 0x50, ReadPointer(box, 0x40)); + for (auto member = ReadPointer(box, 0x50); member != 0; + member = ReadPointer(box, 0x50)) { + const auto *memberName = reinterpret_cast(member + 0x18); + if (std::strncmp(memberName, name, 0x40) == 0) + return member; + WritePointer(box, 0x50, + ReadPointer(reinterpret_cast(member), 0x10)); + } + return 0; +} + +void *MemberPointer(std::uintptr_t member) { + const auto type = + ReadField(reinterpret_cast(member), 0x6c); + if (type < 20 && ((0x97800u >> (type & 0x1f)) & 1u) != 0) + return reinterpret_cast( + ReadPointer(reinterpret_cast(member), 0x60)); + return reinterpret_cast(member + 0x58); +} + +} // namespace extern "C" { -APS5_DUMMY_FUN -int APS5_VABI isobmf_box_getchildbox_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +void *APS5_VABI isobmf_box_getchildbox_nid_postfix(void *box) { + return reinterpret_cast(ReadPointer(box, 0x10)); } -int APS5_VABI isobmf_box_getdiskoffset_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +std::uint64_t APS5_VABI isobmf_box_getdiskoffset_nid_postfix(void *box) { + return ReadField(box, 0x80); } -int APS5_VABI isobmf_box_getflags32b_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); +std::uint32_t APS5_VABI isobmf_box_getflags32b_nid_postfix(void *box) { + if (!box) return 0; + const auto member = FindMember(box, "flags"); + if (!member) + return 0; + const auto *value = static_cast(MemberPointer(member)); + return static_cast(value[0]) | + (static_cast(value[1]) << 8) | + (static_cast(value[2]) << 16); } -int APS5_VABI isobmf_box_getmember_length_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +std::uint32_t APS5_VABI +isobmf_box_getmember_length_nid_postfix(void *box, const char *name) { + const auto member = FindMember(box, name); + return member ? ReadField( + reinterpret_cast(member), 0x68) + : 0; } -int APS5_VABI isobmf_box_getmember_ptr_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +void *APS5_VABI isobmf_box_getmember_ptr_nid_postfix(void *box, + const char *name) { + const auto member = FindMember(box, name); + return member ? MemberPointer(member) : nullptr; } -int APS5_VABI isobmf_box_getmember_type_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +std::uint32_t APS5_VABI +isobmf_box_getmember_type_nid_postfix(void *box, const char *name) { + const auto member = FindMember(box, name); + return member ? ReadField( + reinterpret_cast(member), 0x6c) + : 0; } -int APS5_VABI isobmf_box_getnextbox_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +void *APS5_VABI isobmf_box_getnextbox_nid_postfix(void *box) { + return reinterpret_cast(ReadPointer(box, 0x20)); } -int APS5_VABI isobmf_box_getstate_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +std::uint32_t APS5_VABI isobmf_box_getstate_nid_postfix(void *box) { + return ReadField(box, 0x8c); } -int APS5_VABI isobmf_box_gettype_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +std::uint32_t APS5_VABI isobmf_box_gettype_nid_postfix(void *box) { + return ReadField(box, 0x90); } int APS5_VABI isobmf_decoder_clear_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + NotImplemented_nid_no_patch(__func__); + return 0; } int APS5_VABI isobmf_decoder_create_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + NotImplemented_nid_no_patch(__func__); + return 0; } int APS5_VABI isobmf_decoder_decode_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + NotImplemented_nid_no_patch(__func__); + return 0; } int APS5_VABI isobmf_decoder_destroy_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + NotImplemented_nid_no_patch(__func__); + return 0; } int APS5_VABI isobmf_decoder_get_decoder_tree_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + NotImplemented_nid_no_patch(__func__); + return 0; } int APS5_VABI isobmf_decoder_getquerydata_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + NotImplemented_nid_no_patch(__func__); + return 0; } -int APS5_VABI isobmf_decoder_next_disk_offset_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +std::uint64_t APS5_VABI +isobmf_decoder_next_disk_offset_nid_postfix(void *decoder) { + if (!decoder) + return 0xffffffff; + return ReadField(decoder, 0xc0); } int APS5_VABI isobmf_decoder_query_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + NotImplemented_nid_no_patch(__func__); + return 0; } int APS5_VABI isobmf_memmanager_set_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + NotImplemented_nid_no_patch(__func__); + return 0; } - } diff --git a/core/libs/prx/libSceBeisobmf/include/Api.hpp b/core/libs/prx/libSceBeisobmf/include/Api.hpp index 60bc60a75e..51e0cc1ae1 100644 --- a/core/libs/prx/libSceBeisobmf/include/Api.hpp +++ b/core/libs/prx/libSceBeisobmf/include/Api.hpp @@ -4,23 +4,26 @@ extern "C" { -int APS5_VABI isobmf_box_getchildbox_nid_postfix(void); -int APS5_VABI isobmf_box_getdiskoffset_nid_postfix(void); -int APS5_VABI isobmf_box_getflags32b_nid_postfix(void); -int APS5_VABI isobmf_box_getmember_length_nid_postfix(void); -int APS5_VABI isobmf_box_getmember_ptr_nid_postfix(void); -int APS5_VABI isobmf_box_getmember_type_nid_postfix(void); -int APS5_VABI isobmf_box_getnextbox_nid_postfix(void); -int APS5_VABI isobmf_box_getstate_nid_postfix(void); -int APS5_VABI isobmf_box_gettype_nid_postfix(void); +void *APS5_VABI isobmf_box_getchildbox_nid_postfix(void *box); +std::uint64_t APS5_VABI isobmf_box_getdiskoffset_nid_postfix(void *box); +std::uint32_t APS5_VABI isobmf_box_getflags32b_nid_postfix(void *box); +std::uint32_t APS5_VABI +isobmf_box_getmember_length_nid_postfix(void *box, const char *name); +void *APS5_VABI isobmf_box_getmember_ptr_nid_postfix(void *box, + const char *name); +std::uint32_t APS5_VABI isobmf_box_getmember_type_nid_postfix(void *box, + const char *name); +void *APS5_VABI isobmf_box_getnextbox_nid_postfix(void *box); +std::uint32_t APS5_VABI isobmf_box_getstate_nid_postfix(void *box); +std::uint32_t APS5_VABI isobmf_box_gettype_nid_postfix(void *box); int APS5_VABI isobmf_decoder_clear_nid_postfix(void); int APS5_VABI isobmf_decoder_create_nid_postfix(void); int APS5_VABI isobmf_decoder_decode_nid_postfix(void); int APS5_VABI isobmf_decoder_destroy_nid_postfix(void); int APS5_VABI isobmf_decoder_get_decoder_tree_nid_postfix(void); int APS5_VABI isobmf_decoder_getquerydata_nid_postfix(void); -int APS5_VABI isobmf_decoder_next_disk_offset_nid_postfix(void); +std::uint64_t APS5_VABI +isobmf_decoder_next_disk_offset_nid_postfix(void *decoder); int APS5_VABI isobmf_decoder_query_nid_postfix(void); int APS5_VABI isobmf_memmanager_set_nid_postfix(void); - } diff --git a/core/libs/tests/GuestBeisobmfBoxes.cpp b/core/libs/tests/GuestBeisobmfBoxes.cpp new file mode 100644 index 0000000000..224fdd9c08 --- /dev/null +++ b/core/libs/tests/GuestBeisobmfBoxes.cpp @@ -0,0 +1,112 @@ +#include "prx/libSceBeisobmf/include/Api.hpp" +#include "prx/libc/include/GuestArena.hpp" + +#include +#include +#include +#include +#include +#include + +namespace { + +void Require(bool condition, const char *message) { + if (!condition) { + std::fprintf(stderr, "BEISOBMF BOXES: %s\n", message); + std::abort(); + } +} + +void Write(void *destination, const void *source, std::size_t bytes) { + const GuestArena::HostWrite access(destination, bytes); + Require(access.Open(), "guest fixture memory was not writable"); + std::memcpy(destination, source, bytes); +} + +template +void WriteField(void *object, std::size_t offset, TValue value) { + Write(static_cast(object) + offset, &value, sizeof(value)); +} + +void *Allocate(std::size_t bytes) { + void *memory = GuestArena::GuestArenaAllocate_nid_postfix(bytes, 16); + Require(memory != nullptr, "guest fixture allocation failed"); + const std::array zeros{}; + auto *destination = static_cast(memory); + for (std::size_t offset = 0; offset < bytes; offset += zeros.size()) + Write(destination + offset, zeros.data(), + (bytes - offset < zeros.size()) ? bytes - offset : zeros.size()); + return memory; +} + +} // namespace + +int main() { + auto *box = static_cast(Allocate(0xa0)); + auto *inlineMember = static_cast(Allocate(0x70)); + auto *pointerMember = static_cast(Allocate(0x70)); + auto *payload = static_cast(Allocate(8)); + auto *child = Allocate(0x10); + auto *next = Allocate(0x10); + auto *decoder = static_cast(Allocate(0xd0)); + + WriteField(box, 0x10, child); + WriteField(box, 0x20, next); + WriteField(box, 0x40, inlineMember); + WriteField(box, 0x80, std::uint64_t{0x1122334455667788}); + WriteField(box, 0x8c, std::uint32_t{7}); + WriteField(box, 0x90, std::uint32_t{0x1234}); + WriteField(inlineMember, 0x10, pointerMember); + constexpr char flagsName[] = "flags"; + constexpr char inlineName[] = "inline"; + constexpr char pointerName[] = "external"; + Write(inlineMember + 0x18, flagsName, sizeof(flagsName)); + Write(inlineMember + 0x58, "\x34\x12\x01", 3); + WriteField(inlineMember, 0x68, std::uint32_t{3}); + WriteField(inlineMember, 0x6c, std::uint32_t{0}); + Write(pointerMember + 0x18, pointerName, sizeof(pointerName)); + WriteField(pointerMember, 0x68, std::uint32_t{8}); + WriteField(pointerMember, 0x6c, std::uint32_t{11}); + WriteField(pointerMember, 0x60, payload); + WriteField(decoder, 0xc0, std::uint64_t{0xfedcba9876543210}); + + Require(isobmf_box_getchildbox_nid_postfix(box) == child, + "child box offset is wrong"); + Require(isobmf_box_getnextbox_nid_postfix(box) == next, + "next box offset is wrong"); + Require(isobmf_box_getdiskoffset_nid_postfix(box) == 0x1122334455667788, + "box disk offset is wrong"); + Require(isobmf_box_getstate_nid_postfix(box) == 7, + "box state offset is wrong"); + Require(isobmf_box_gettype_nid_postfix(box) == 0x1234, + "box type offset is wrong"); + Require(isobmf_box_getflags32b_nid_postfix(box) == 0x011234, + "flags value is wrong"); + Require(isobmf_box_getmember_length_nid_postfix(box, flagsName) == 3, + "inline member length is wrong"); + Require(isobmf_box_getmember_type_nid_postfix(box, flagsName) == 0, + "inline member type is wrong"); + Require(isobmf_box_getmember_ptr_nid_postfix(box, flagsName) == + inlineMember + 0x58, + "inline member pointer is wrong"); + Require(isobmf_box_getmember_length_nid_postfix(box, pointerName) == 8, + "external member length is wrong"); + Require(isobmf_box_getmember_type_nid_postfix(box, pointerName) == 11, + "external member type is wrong"); + Require(isobmf_box_getmember_ptr_nid_postfix(box, pointerName) == payload, + "external member pointer is wrong"); + Require(isobmf_box_getmember_ptr_nid_postfix(box, "missing") == nullptr, + "missing member pointer is not null"); + Require(isobmf_box_getmember_length_nid_postfix(box, "missing") == 0, + "missing member length is not zero"); + Require(isobmf_box_getmember_type_nid_postfix(box, "missing") == 0, + "missing member type is not zero"); + Require(isobmf_decoder_next_disk_offset_nid_postfix(decoder) == + 0xfedcba9876543210, + "decoder disk offset is wrong"); + Require(isobmf_decoder_next_disk_offset_nid_postfix(nullptr) == 0xffffffff, + "null decoder sentinel is wrong"); + + std::puts("BEISOBMF BOXES tests passed"); + return 0; +}