From fe6aba89a827d1829384c9cd2fbcd57826e34f2d Mon Sep 17 00:00:00 2001 From: Yossi Yadgar Date: Tue, 20 Oct 2026 10:12:03 +0300 Subject: [PATCH] feat(libSceHmd): refuse the field of view, eye offset and sensor queries without a headset No handle is ever open, as sceHmdOpen reports the headset disconnected, so they return HANDLE_INVALID after initialization and NOT_INITIALIZED before it, without writing their outputs. --- core/libs/prx/libSceHmd/Export.cpp | 23 ++++++++++++++--------- core/libs/prx/libSceHmd/Hmd.hpp | 3 +++ core/libs/tests/GuestHmd.cpp | 24 ++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index e90bf60b5b..6b533528b0 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -262,9 +262,11 @@ int APS5_VABI sceHmdReprojectionInitializeCapture(void) { return 0; } -int APS5_VABI sceHmdGet2DEyeOffset(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +int APS5_VABI sceHmdGet2DEyeOffset(std::int32_t handle, void* leftOffset, void* rightOffset) { + (void)handle; + (void)leftOffset; + (void)rightOffset; + return initialized.load() ? Hmd::InvalidHandle : Hmd::NotInitialized; } int APS5_VABI sceHmdInternalSetDebugSocialScreenMode(void) { @@ -376,9 +378,10 @@ int APS5_VABI sceHmdInternalSetM2LedOn(void) { return 0; } -int APS5_VABI sceHmdGetFieldOfView(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +int APS5_VABI sceHmdGetFieldOfView(std::int32_t handle, void* fieldOfView) { + (void)handle; + if (!fieldOfView) return Hmd::ParameterNull; + return initialized.load() ? Hmd::InvalidHandle : Hmd::NotInitialized; } int APS5_VABI sceHmdReprojectionAddDisplayBuffer(void) { @@ -764,9 +767,11 @@ int APS5_VABI sceHmdInternalSetDefaultLedData(void) { return 0; } -int APS5_VABI sceHmdGetInertialSensorData(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +int APS5_VABI sceHmdGetInertialSensorData(std::int32_t handle, void* data, std::int32_t count) { + (void)handle; + (void)data; + (void)count; + return initialized.load() ? Hmd::InvalidHandle : Hmd::NotInitialized; } int APS5_VABI sceHmdInternal3dAudioClose(void) { diff --git a/core/libs/prx/libSceHmd/Hmd.hpp b/core/libs/prx/libSceHmd/Hmd.hpp index 2daaf92e9b..ee11c14431 100644 --- a/core/libs/prx/libSceHmd/Hmd.hpp +++ b/core/libs/prx/libSceHmd/Hmd.hpp @@ -69,6 +69,9 @@ int APS5_VABI sceHmdReprojectionQueryGarlicBuffAlign(void); int APS5_VABI sceHmdReprojectionQueryGarlicBuffSize(void); int APS5_VABI sceHmdReprojectionQueryOnionBuffAlign(void); int APS5_VABI sceHmdReprojectionQueryOnionBuffSize(void); +int APS5_VABI sceHmdGetFieldOfView(std::int32_t handle, void* fieldOfView); +int APS5_VABI sceHmdGet2DEyeOffset(std::int32_t handle, void* leftOffset, void* rightOffset); +int APS5_VABI sceHmdGetInertialSensorData(std::int32_t handle, void* data, std::int32_t count); } #endif diff --git a/core/libs/tests/GuestHmd.cpp b/core/libs/tests/GuestHmd.cpp index 6cdaa32c4e..a7bbe47dcd 100644 --- a/core/libs/tests/GuestHmd.cpp +++ b/core/libs/tests/GuestHmd.cpp @@ -31,6 +31,28 @@ void CheckBufferQueries() { 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, fieldOfView) == expected, "field of view without a headset"); + Require(sceHmdGet2DEyeOffset(handle, left, right) == expected, "eye offset without a headset"); + Require(sceHmdGet2DEyeOffset(handle, nullptr, nullptr) == expected, "eye offset with null outputs"); + Require(sceHmdGetInertialSensorData(handle, 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"); +} + } int main() { @@ -47,6 +69,7 @@ int main() { 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); Require(sceHmdGetDeviceInformationByHandle(0, &info) == Hmd::NotInitialized, "handle query before initialize"); Require(sceHmdInitialize(nullptr) == Hmd::ParameterNull, "null initialize"); @@ -83,6 +106,7 @@ int main() { "query accepted invalid handle"); Require(std::memcmp(&info, &expected, sizeof(info)) == 0, "invalid handle modified output"); } + CheckHandleQueries(Hmd::InvalidHandle); Require(sceHmdGetDeviceInformationByHandle(0, nullptr) == Hmd::InvalidHandle, "null query accepted invalid handle"); Require(sceHmdTerminate() == 0, "terminate failed");