feat(libSceHmd): add a disconnected-headset HLE stub
This commit is contained in:
parent
bc51b337ad
commit
0c4dace39e
|
|
@ -0,0 +1,71 @@
|
|||
#include "Hmd.hpp"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace {
|
||||
|
||||
std::atomic<bool> initialized{false};
|
||||
|
||||
std::int32_t Initialize(const Hmd::InitializeParam* param) {
|
||||
if (initialized.load()) return Hmd::AlreadyInitialized;
|
||||
if (!param) return Hmd::ParameterNull;
|
||||
if (param->reserved0) return Hmd::UnsupportedFeature;
|
||||
return initialized.exchange(true) ? Hmd::AlreadyInitialized : 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
std::int32_t APS5_VABI sceHmdInitialize(const Hmd::InitializeParam* param) {
|
||||
return Initialize(param);
|
||||
}
|
||||
|
||||
std::int32_t APS5_VABI sceHmdInitialize315(const Hmd::InitializeParam* param) {
|
||||
return Initialize(param);
|
||||
}
|
||||
|
||||
std::int32_t APS5_VABI sceHmdTerminate() {
|
||||
return initialized.exchange(false) ? 0 : Hmd::NotInitialized;
|
||||
}
|
||||
|
||||
std::int32_t APS5_VABI sceHmdOpen(std::int32_t userId, std::int32_t type, std::int32_t index,
|
||||
Hmd::OpenParam* param) {
|
||||
if (!initialized.load()) return Hmd::NotInitialized;
|
||||
if (type != 0 || index != 0 || param || userId == -1 || userId == 0xff)
|
||||
return Hmd::ParameterInvalid;
|
||||
return Hmd::DeviceDisconnected;
|
||||
}
|
||||
|
||||
std::int32_t APS5_VABI sceHmdClose(std::int32_t handle) {
|
||||
(void)handle;
|
||||
return initialized.load() ? Hmd::InvalidHandle : Hmd::NotInitialized;
|
||||
}
|
||||
|
||||
std::int32_t APS5_VABI sceHmdGetDeviceInformation(Hmd::DeviceInformation* info) {
|
||||
if (!info) return Hmd::ParameterNull;
|
||||
if (!initialized.load()) return Hmd::NotInitialized;
|
||||
*info = {};
|
||||
info->status = Hmd::DeviceStatus::NotDetected;
|
||||
info->userId = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::int32_t APS5_VABI sceHmdGetDeviceInformationByHandle(std::int32_t handle,
|
||||
Hmd::DeviceInformation* info) {
|
||||
(void)handle;
|
||||
(void)info;
|
||||
return initialized.load() ? Hmd::InvalidHandle : Hmd::NotInitialized;
|
||||
}
|
||||
|
||||
std::int32_t APS5_VABI sceHmdInternalGetDeviceStatus(Hmd::DeviceStatus* status) {
|
||||
if (!status) return Hmd::ParameterNull;
|
||||
*status = Hmd::DeviceStatus::NotDetected;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::int32_t APS5_VABI sceHmdInternalMmapIsConnect() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCEHMD_HMD_HPP
|
||||
#define CORE_LIBS_PRX_LIBSCEHMD_HMD_HPP
|
||||
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Hmd {
|
||||
|
||||
inline constexpr std::int32_t AlreadyInitialized = static_cast<std::int32_t>(0x81110001);
|
||||
inline constexpr std::int32_t NotInitialized = static_cast<std::int32_t>(0x81110002);
|
||||
inline constexpr std::int32_t InvalidHandle = static_cast<std::int32_t>(0x81110003);
|
||||
inline constexpr std::int32_t DeviceDisconnected = static_cast<std::int32_t>(0x81110004);
|
||||
inline constexpr std::int32_t ParameterNull = static_cast<std::int32_t>(0x81110008);
|
||||
inline constexpr std::int32_t ParameterInvalid = static_cast<std::int32_t>(0x81110009);
|
||||
inline constexpr std::int32_t UnsupportedFeature = static_cast<std::int32_t>(0x81110016);
|
||||
|
||||
enum class DeviceStatus : std::uint32_t {
|
||||
Ready = 0,
|
||||
NotReady = 1,
|
||||
NotDetected = 2,
|
||||
HmuDisconnected = 3,
|
||||
};
|
||||
|
||||
struct InitializeParam {
|
||||
void* reserved0;
|
||||
std::uint8_t reserved[8];
|
||||
};
|
||||
|
||||
struct OpenParam;
|
||||
|
||||
struct DeviceInformation {
|
||||
DeviceStatus status;
|
||||
std::int32_t userId;
|
||||
std::uint8_t reserved0[4];
|
||||
std::uint32_t panelWidth;
|
||||
std::uint32_t panelHeight;
|
||||
std::uint16_t latency90Hz;
|
||||
std::uint16_t latency120Hz;
|
||||
std::uint8_t hmuMount;
|
||||
std::uint8_t reserved1[7];
|
||||
};
|
||||
|
||||
static_assert(sizeof(InitializeParam) == 16);
|
||||
static_assert(sizeof(DeviceInformation) == 32);
|
||||
static_assert(offsetof(DeviceInformation, userId) == 4);
|
||||
static_assert(offsetof(DeviceInformation, panelWidth) == 12);
|
||||
static_assert(offsetof(DeviceInformation, latency90Hz) == 20);
|
||||
static_assert(offsetof(DeviceInformation, hmuMount) == 24);
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
std::int32_t APS5_VABI sceHmdInitialize(const Hmd::InitializeParam* param);
|
||||
std::int32_t APS5_VABI sceHmdInitialize315(const Hmd::InitializeParam* param);
|
||||
std::int32_t APS5_VABI sceHmdTerminate();
|
||||
std::int32_t APS5_VABI sceHmdOpen(std::int32_t userId, std::int32_t type, std::int32_t index,
|
||||
Hmd::OpenParam* param);
|
||||
std::int32_t APS5_VABI sceHmdClose(std::int32_t handle);
|
||||
std::int32_t APS5_VABI sceHmdGetDeviceInformation(Hmd::DeviceInformation* info);
|
||||
std::int32_t APS5_VABI sceHmdGetDeviceInformationByHandle(std::int32_t handle,
|
||||
Hmd::DeviceInformation* info);
|
||||
std::int32_t APS5_VABI sceHmdInternalGetDeviceStatus(Hmd::DeviceStatus* status);
|
||||
std::int32_t APS5_VABI sceHmdInternalMmapIsConnect();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
#include "prx/libSceHmd/Hmd.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace {
|
||||
|
||||
void Require(bool condition, const char* message) {
|
||||
if (!condition) {
|
||||
std::fprintf(stderr, "HMD: %s\n", message);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
Hmd::InitializeParam param{};
|
||||
Hmd::DeviceInformation info;
|
||||
std::memset(&info, 0xa5, sizeof(info));
|
||||
const auto untouched = info;
|
||||
|
||||
CheckDisconnected();
|
||||
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");
|
||||
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<Hmd::OpenParam*>(¶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");
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
Throughout the project, every function at every stage either **does exactly what it's supposed to or throws an exception**. Everywhere... except:
|
||||
- [libSceSaveDataDialog.native](../../core/libs/prx/libSceSaveDataDialog.native/Export.cpp)
|
||||
- [libSceCommonDialog](../../core/libs/prx/libSceCommonDialog/Export.cpp)
|
||||
- [libSceHmd](../../core/libs/prx/libSceHmd/Export.cpp) implements only the disconnected-headset path: initialization succeeds, device queries report `NotDetected`, and opening a device returns `DeviceDisconnected`. Headset support, tracking, rendering and additional HMD exports are not implemented; SDK-level ABI compatibility and in-game behaviour remain unverified.
|
||||
- The shader recompiler [skips baryctric coordinates](../../core/shader/recompiler/Recompiler.cpp) (is not even passed to SpirvTargetOptions at row 212).
|
||||
|
||||
### Unknown function info
|
||||
|
|
|
|||
Loading…
Reference in New Issue