Merge pull request #644 from Celegans12/feat/psml-mfsr-unavailable

feat(psml): implement the MFSR 1100 calls for hardware without PSSR
This commit is contained in:
boykopovar 2026-10-05 19:52:11 +03:00 committed by GitHub
commit a4c290f50b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 102 additions and 6 deletions

View File

@ -0,0 +1,20 @@
add_library(libScePsml_debug SHARED EXCLUDE_FROM_ALL Export.cpp)
target_include_directories(libScePsml_debug PRIVATE ${LIBS_INCLUDE_DIR})
configure_windows_unwind(libScePsml_debug)
if(MINGW)
foreach(runtimeLinkFlag IN ITEMS -static-libgcc -static-libstdc++ -static)
string(REPLACE "${runtimeLinkFlag}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
endforeach()
endif()
if(BUILD_TESTING)
add_test_executable(guest_psml_tests ${LIBS_INCLUDE_DIR}/tests/GuestPsml.cpp)
target_include_directories(guest_psml_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_psml_tests PRIVATE libScePsml_debug)
add_test(NAME guest_psml COMMAND guest_psml_tests)
if(WIN32)
set_tests_properties(guest_psml PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libScePsml_debug>")
endif()
endif()

View File

@ -3,22 +3,45 @@
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
namespace {
constexpr std::int32_t SCE_PSML_ERROR_NOT_INITIALIZED = static_cast<std::int32_t>(0x8A810001);
}
extern "C" {
std::int32_t APS5_VABI scePsmlMfsrGetContextBufferRequirement1100(void* requirement, const void* param) {
(void)requirement;
(void)param;
return SCE_PSML_ERROR_NOT_INITIALIZED;
}
std::int32_t APS5_VABI scePsmlMfsrCreateContext1100(void** context, const void* param) {
(void)context;
(void)param;
return SCE_PSML_ERROR_NOT_INITIALIZED;
}
std::int32_t APS5_VABI scePsmlMfsrGetDispatchMfsrPacket1100(void* context, void* commandBuffer, const void* param) {
(void)context;
(void)commandBuffer;
(void)param;
return SCE_PSML_ERROR_NOT_INITIALIZED;
}
APS5_EXPORT("+2KpvixvL6E", scePsmlUnknown__P2KpvixvL6E);
int APS5_VABI scePsmlUnknown__P2KpvixvL6E() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
APS5_EXPORT("3WVD91e12ZQ", scePsmlUnknown_3WVD91e12ZQ);
int APS5_VABI scePsmlUnknown_3WVD91e12ZQ() {
int APS5_VABI scePsmlMfsrInit() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
APS5_EXPORT("AHalTX9wFZY", scePsmlUnknown_AHalTX9wFZY);
int APS5_VABI scePsmlUnknown_AHalTX9wFZY() {
int APS5_VABI scePsmlMfsrGetDispatchMfsrPacketSizeInDwords() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
@ -47,8 +70,7 @@ int APS5_VABI scePsmlUnknown_GJY0MvuTcs8() {
return 0;
}
APS5_EXPORT("JaLBe0P3jSU", scePsmlUnknown_JaLBe0P3jSU);
int APS5_VABI scePsmlUnknown_JaLBe0P3jSU() {
int APS5_VABI scePsmlMfsrReleaseContext() {
NotImplemented_nid_no_patch(__func__);
return 0;
}

View File

@ -0,0 +1,53 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include <array>
#include <cstdint>
#include <cstdlib>
extern "C" {
std::int32_t APS5_VABI scePsmlMfsrGetContextBufferRequirement1100(void* requirement, const void* param);
std::int32_t APS5_VABI scePsmlMfsrCreateContext1100(void** context, const void* param);
std::int32_t APS5_VABI scePsmlMfsrGetDispatchMfsrPacket1100(void* context, void* commandBuffer, const void* param);
}
namespace {
void Require(bool value) { if (!value) std::abort(); }
constexpr std::int32_t kErrNotInitialized = static_cast<std::int32_t>(0x8A810001);
struct CommandBuffer {
std::uint32_t* cursor;
std::uint32_t sizeInDwords;
};
}
int main() {
std::array<std::uint8_t, 0x158> param{};
std::array<std::uint8_t, 0x18> requirement;
requirement.fill(0xa5);
const auto untouchedRequirement = requirement;
Require(scePsmlMfsrGetContextBufferRequirement1100(requirement.data(), param.data()) == kErrNotInitialized);
Require(requirement == untouchedRequirement);
Require(scePsmlMfsrGetContextBufferRequirement1100(nullptr, nullptr) == kErrNotInitialized);
std::uint64_t contextStorage = 0;
void* const sentinel = &contextStorage;
void* context = sentinel;
Require(scePsmlMfsrCreateContext1100(&context, param.data()) == kErrNotInitialized);
Require(context == sentinel);
Require(scePsmlMfsrCreateContext1100(nullptr, nullptr) == kErrNotInitialized);
std::array<std::uint32_t, 64> dwords;
dwords.fill(0xa5a5a5a5u);
const auto untouchedDwords = dwords;
CommandBuffer commandBuffer{dwords.data(), static_cast<std::uint32_t>(dwords.size())};
Require(scePsmlMfsrGetDispatchMfsrPacket1100(&contextStorage, &commandBuffer, param.data()) == kErrNotInitialized);
Require(commandBuffer.cursor == dwords.data());
Require(commandBuffer.sizeInDwords == dwords.size());
Require(dwords == untouchedDwords);
Require(scePsmlMfsrGetDispatchMfsrPacket1100(nullptr, &commandBuffer, param.data()) == kErrNotInitialized);
Require(commandBuffer.cursor == dwords.data());
Require(dwords == untouchedDwords);
}

View File

@ -56,6 +56,7 @@ Throughout the project, every function at every stage either **does exactly what
- [sceAudioOutGetLastOutputTime](../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) - signature and behaviour taken from the [shadPS4](https://github.com/shadps4-emu/shadPS4) and [fpPS4](https://github.com/red-prig/fpPS4) reimplementations of the PS4 library, not confirmed on a PS5 title: the time is the process time at which `sceAudioOutOutput` or `sceAudioOutOutputs` accepted the port's last block, not the time the block reached the device
- [AOWqIYsgVHs](../../core/libs/prx/libSceContentExport/Export.cpp) (libSceContentExport) - unknown name, signature
- [GQTObcITIXI](../../core/libs/prx/libSceShare/Export.cpp) (libSceShare) - unknown name, signature
- [scePsmlMfsrGetContextBufferRequirement1100, scePsmlMfsrCreateContext1100, scePsmlMfsrGetDispatchMfsrPacket1100](../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_debug) - argument layouts unknown; only 0x8A810001 (not initialized) is returned
- [BnMAMrsfVWo](../../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libc) - unknown name, signature
- [scePngEncEncode](../../core/libs/prx/libScePngEnc/Export.cpp) (libScePngEnc) - whether rows may stay unfiltered when `filter_type` names a subset of the filters is unknown; as in shadPS4, they may only for 0 and for the all-filters mask
- AudioIn, NpSessionSignaling and PlayerInvitationDialog exports added without an implementation have assumed signatures