#include "ProcessParametersFixture.hpp" #include "prx/libSceHmd/Hmd.hpp" #include "prx/libSceHmd/include/Api.hpp" #include #include #include #include #include #include #include #include namespace { constexpr std::uint32_t LastSdkBeforeStartupState = 0x044fffff; constexpr std::uint32_t FirstSdkWithStartupState = 0x04500000; alignas(16) std::array process{}; void Require(bool condition, const char* message) { if (!condition) { std::fprintf(stderr, "HMD: %s\n", message); std::abort(); } } void InstallProcessParameters() { const std::uint64_t size = process.size(); const std::uint32_t magic = 0x4942524f; std::memcpy(process.data(), &size, sizeof(size)); std::memcpy(process.data() + 8, &magic, sizeof(magic)); Require(ProcessParametersFixture::Install(process.data(), process.size()), "process parameters were not installed"); } void SetCompiledSdkVersion(std::uint32_t version) { std::memcpy(process.data() + 0x10, &version, sizeof(version)); } void CheckDisconnected() { Hmd::DeviceStatus status = Hmd::DeviceStatus::Ready; Require(sceHmdInternalGetDeviceStatus(&status) == 0, "status query failed"); Require(status == Hmd::DeviceStatus::NotDetected, "headset reported connected"); Require(sceHmdInternalGetDeviceStatus(nullptr) == Hmd::ParameterNull, "null status"); Require(sceHmdInternalMmapIsConnect() == 0, "connection reported present"); Require(!sceHmdInternalIsGameVr2d(), "game reported in VR2D without a headset"); Require(!sceHmdInternalIsMiniAppVr2d(), "mini app reported in VR2D without a headset"); Require(!sceHmdInternalIsCommonDlgVr2d(), "common dialog reported in VR2D without a headset"); Require(!sceHmdInternalIsCommonDlgMiniAppVr2d(), "common dialog mini app reported in VR2D without a headset"); } void CheckBufferQueries() { Require(sceHmdDistortionGetWorkMemoryAlign() == 0x400, "distortion work memory alignment"); Require(sceHmdGetDistortionWorkMemoryAlign() == 0x400, "distortion work memory alignment query"); Require(sceHmdDistortionGetWorkMemorySize() == 0x20000, "distortion work memory size"); Require(sceHmdGetDistortionWorkMemorySize() == 0x20000, "distortion work memory size query"); Require(sceHmdReprojectionQueryGarlicBuffAlign() == 0x100, "reprojection garlic buffer alignment"); Require(sceHmdReprojectionQueryGarlicBuffSize() == 0x100000, "reprojection garlic buffer size"); Require(sceHmdReprojectionQueryOnionBuffAlign() == 0x100, "reprojection onion buffer alignment"); Require(sceHmdReprojectionQueryOnionBuffSize() == 0x810, "reprojection onion buffer size"); } void CheckHandleQueries(std::int32_t expected) { unsigned char left[16]; unsigned char right[16]; unsigned char fieldOfView[16]; unsigned char sensor[64]; std::memset(left, 0x5a, sizeof(left)); std::memset(right, 0x5a, sizeof(right)); std::memset(fieldOfView, 0x5a, sizeof(fieldOfView)); std::memset(sensor, 0x5a, sizeof(sensor)); for (const auto handle : {0, -1, 1, 0x0f000000}) { Require(sceHmdGetFieldOfView(handle, nullptr) == Hmd::ParameterNull, "null field of view accepted"); Require(sceHmdGetFieldOfView(handle, reinterpret_cast(fieldOfView)) == expected, "field of view without a headset"); Require(sceHmdGet2DEyeOffset(handle, reinterpret_cast(left), reinterpret_cast(right)) == expected, "eye offset without a headset"); Require(sceHmdGet2DEyeOffset(handle, nullptr, nullptr) == expected, "eye offset with null outputs"); Require(sceHmdGetInertialSensorData(handle, reinterpret_cast(sensor), 1) == expected, "sensor data without a headset"); } for (const auto* bytes : {left, right, fieldOfView}) { for (std::size_t index = 0; index < 16; ++index) Require(bytes[index] == 0x5a, "a refused query wrote its output"); } for (const auto byte : sensor) Require(byte == 0x5a, "a refused sensor query wrote its output"); } template bool Unimplemented(Call call) { try { static_cast(call()); } catch (const std::runtime_error&) { return true; } return false; } void CheckCommandQueries(bool handleZeroMeasured) { unsigned char zeroed[64] = {}; unsigned char vr2dBytes[64] = {}; unsigned char commands[16]; std::memset(commands, 0x5a, sizeof(commands)); for (const std::size_t offset : {0x00u, 0x08u, 0x20u}) std::memset(vr2dBytes + offset, 0x11, 8); const auto* distortion = reinterpret_cast(zeroed); const auto* wideNear = reinterpret_cast(zeroed); const auto* zeroedVr2d = reinterpret_cast(zeroed); const auto* vr2d = reinterpret_cast(vr2dBytes); auto* commandBuffer = reinterpret_cast(commands); const auto vr2dWith = [&](std::size_t offset, unsigned char value, std::size_t bytes, auto check) { unsigned char changed[64]; std::memcpy(changed, vr2dBytes, sizeof(changed)); std::memset(changed + offset, value, bytes); check(reinterpret_cast(changed)); }; for (const auto handle : {-1, 1, 0x0f000000}) { Require(sceHmdGetDistortionCorrectionCommand(handle, distortion, commandBuffer) == Hmd::InvalidHandle, "distortion command accepted a handle sceHmdOpen never issued"); Require(sceHmdDistortionGetCorrectionCommand(handle, distortion, commandBuffer) == Hmd::InvalidHandle, "aliased distortion command accepted a handle sceHmdOpen never issued"); Require(sceHmdGetWideNearDistortionCorrectionCommand(handle, wideNear, commandBuffer) == Hmd::InvalidHandle, "wide-near distortion command accepted a handle sceHmdOpen never issued"); Require(sceHmdDistortionGetWideNearCorrectionCommand(handle, wideNear, commandBuffer) == Hmd::InvalidHandle, "aliased wide-near distortion command accepted a handle sceHmdOpen never issued"); Require(sceHmdGet2dVrCommand(handle, vr2d, commandBuffer) == Hmd::InvalidHandle, "2D VR command accepted a handle sceHmdOpen never issued"); Require(sceHmdDistortionGet2dVrCommand(handle, vr2d, commandBuffer) == Hmd::InvalidHandle, "aliased 2D VR command accepted a handle sceHmdOpen never issued"); } for (const auto handle : {0, -1, 1, 0x0f000000}) { for (const auto query : {sceHmdGet2dVrCommand, sceHmdDistortionGet2dVrCommand}) { Require(query(handle, zeroedVr2d, commandBuffer) == Hmd::ParameterNull, "2D VR command took a parameter with null pointers"); Require(query(handle, nullptr, commandBuffer) == Hmd::ParameterNull, "2D VR command took a null parameter"); Require(query(handle, vr2d, nullptr) == Hmd::ParameterNull, "2D VR command took a null command buffer"); for (const std::size_t offset : {0x00u, 0x08u, 0x20u}) { vr2dWith(offset, 0, 8, [&](const Hmd2dVrCommandParameter* changed) { Require(query(handle, changed, commandBuffer) == Hmd::ParameterNull, "2D VR command took a null pointer in its parameter"); }); } vr2dWith(0x34, 0x01, 4, [&](const Hmd2dVrCommandParameter* changed) { Require(query(handle, changed, commandBuffer) == Hmd::ParameterInvalid, "2D VR command took a non-zero field at 0x34"); }); vr2dWith(0x38, 0x01, 8, [&](const Hmd2dVrCommandParameter* changed) { Require(query(handle, changed, commandBuffer) == Hmd::ParameterInvalid, "2D VR command took a non-zero field at 0x38"); }); } } if (!handleZeroMeasured) { Require(Unimplemented([&] { return sceHmdGetDistortionCorrectionCommand(0, distortion, commandBuffer); }), "distortion command answered handle 0 where its answer is not measured"); Require(Unimplemented([&] { return sceHmdDistortionGetCorrectionCommand(0, distortion, commandBuffer); }), "aliased distortion command answered handle 0 where its answer is not measured"); Require(Unimplemented([&] { return sceHmdGetWideNearDistortionCorrectionCommand(0, wideNear, commandBuffer); }), "wide-near distortion command answered handle 0 where its answer is not measured"); Require(Unimplemented([&] { return sceHmdDistortionGetWideNearCorrectionCommand(0, wideNear, commandBuffer); }), "aliased wide-near distortion command answered handle 0 where its answer is not measured"); Require(Unimplemented([&] { return sceHmdGet2dVrCommand(0, vr2d, commandBuffer); }), "2D VR command answered handle 0 where its answer is not measured"); Require(Unimplemented([&] { return sceHmdDistortionGet2dVrCommand(0, vr2d, commandBuffer); }), "aliased 2D VR command answered handle 0 where its answer is not measured"); } else { Require(sceHmdGetDistortionCorrectionCommand(0, distortion, commandBuffer) == Hmd::NotInitialized, "distortion command before initialize"); Require(sceHmdDistortionGetCorrectionCommand(0, distortion, commandBuffer) == Hmd::NotInitialized, "aliased distortion command before initialize"); Require(sceHmdGetWideNearDistortionCorrectionCommand(0, wideNear, commandBuffer) == Hmd::NotInitialized, "wide-near distortion command before initialize"); Require(sceHmdDistortionGetWideNearCorrectionCommand(0, wideNear, commandBuffer) == Hmd::NotInitialized, "aliased wide-near distortion command before initialize"); Require(sceHmdGet2dVrCommand(0, vr2d, commandBuffer) == Hmd::NotInitialized, "2D VR command before initialize"); Require(sceHmdDistortionGet2dVrCommand(0, vr2d, commandBuffer) == Hmd::NotInitialized, "aliased 2D VR command before initialize"); } for (const auto byte : commands) Require(byte == 0x5a, "a refused command query wrote its output"); } void CheckAssyError(std::int32_t expected) { unsigned char data[32]; std::memset(data, 0x5a, sizeof(data)); Require(sceHmdGetAssyError(nullptr) == Hmd::ParameterNull, "null assembly error output accepted"); Require(sceHmdGetAssyError(reinterpret_cast(data)) == expected, "assembly error query without a headset"); for (const auto byte : data) Require(byte == 0x5a, "a refused assembly error query wrote its output"); } } int main() { Hmd::InitializeParam param{}; Hmd::DeviceInformation info; std::memset(&info, 0xa5, sizeof(info)); const auto untouched = info; InstallProcessParameters(); CheckDisconnected(); CheckBufferQueries(); Require(sceHmdTerminate() == Hmd::NotInitialized, "terminate before initialize"); Require(sceHmdOpen(1, 0, 0, nullptr) == Hmd::NotInitialized, "open before initialize"); Require(sceHmdClose(0) == Hmd::NotInitialized, "close before initialize"); Require(sceHmdGetDeviceInformation(nullptr) == Hmd::ParameterNull, "null information"); Require(sceHmdGetDeviceInformation(&info) == Hmd::NotInitialized, "query before initialize"); Require(std::memcmp(&info, &untouched, sizeof(info)) == 0, "failed query modified output"); CheckHandleQueries(Hmd::NotInitialized); SetCompiledSdkVersion(LastSdkBeforeStartupState); CheckCommandQueries(true); SetCompiledSdkVersion(FirstSdkWithStartupState); CheckCommandQueries(false); CheckAssyError(Hmd::NotInitialized); Require(sceHmdGetDeviceInformationByHandle(0, &info) == Hmd::NotInitialized, "handle query before initialize"); Require(sceHmdInitialize(nullptr) == Hmd::ParameterNull, "null initialize"); Require(sceHmdInitialize315(nullptr) == Hmd::ParameterNull, "null initialize315"); param.reserved0 = ¶m; Require(sceHmdInitialize(¶m) == Hmd::UnsupportedFeature, "distortion unsupported"); Require(sceHmdInitialize315(¶m) == Hmd::UnsupportedFeature, "distortion315 unsupported"); param.reserved0 = nullptr; for (const auto initialize : {sceHmdInitialize, sceHmdInitialize315}) { Require(initialize(¶m) == 0, "initialize failed"); Require(sceHmdInitialize(¶m) == Hmd::AlreadyInitialized, "duplicate initialize"); Require(sceHmdInitialize315(¶m) == Hmd::AlreadyInitialized, "shared initialization"); Require(sceHmdInitialize(nullptr) == Hmd::AlreadyInitialized, "duplicate precedence"); CheckDisconnected(); Require(sceHmdGetDeviceInformation(&info) == 0, "device query failed"); Hmd::DeviceInformation expected{}; expected.status = Hmd::DeviceStatus::NotDetected; expected.userId = -1; Require(std::memcmp(&info, &expected, sizeof(info)) == 0, "incorrect absent-device output"); Require(sceHmdGetDeviceInformation(nullptr) == Hmd::ParameterNull, "null device query"); Require(sceHmdOpen(-1, 0, 0, nullptr) == Hmd::ParameterInvalid, "invalid user"); Require(sceHmdOpen(0xff, 0, 0, nullptr) == Hmd::ParameterInvalid, "system user"); Require(sceHmdOpen(1, 1, 0, nullptr) == Hmd::ParameterInvalid, "invalid type"); Require(sceHmdOpen(1, 0, 1, nullptr) == Hmd::ParameterInvalid, "invalid index"); Require(sceHmdOpen(1, 0, 0, reinterpret_cast(¶m)) == Hmd::ParameterInvalid, "non-null reserved open parameter"); Require(sceHmdOpen(1, 0, 0, nullptr) == Hmd::DeviceDisconnected, "open faked a handle"); Require(sceHmdOpen(1, 0, 0, nullptr) == Hmd::DeviceDisconnected, "failed open created state"); for (const auto handle : {0, -1, 1, 0x0f000000}) { Require(sceHmdClose(handle) == Hmd::InvalidHandle, "close accepted invalid handle"); Require(sceHmdGetDeviceInformationByHandle(handle, &info) == Hmd::InvalidHandle, "query accepted invalid handle"); Require(std::memcmp(&info, &expected, sizeof(info)) == 0, "invalid handle modified output"); } CheckHandleQueries(Hmd::InvalidHandle); CheckCommandQueries(false); CheckAssyError(Hmd::DeviceDisconnected); Require(sceHmdGetDeviceInformationByHandle(0, nullptr) == Hmd::InvalidHandle, "null query accepted invalid handle"); Require(sceHmdTerminate() == 0, "terminate failed"); Require(sceHmdTerminate() == Hmd::NotInitialized, "duplicate terminate"); Require(sceHmdGetDeviceInformation(&info) == Hmd::NotInitialized, "query after terminate"); CheckDisconnected(); } }