diff --git a/src/common/emulatorConfig.cpp b/src/common/emulatorConfig.cpp index 762a43b19..6ebc71534 100644 --- a/src/common/emulatorConfig.cpp +++ b/src/common/emulatorConfig.cpp @@ -55,6 +55,10 @@ bool FullscreenEnabled() { return g_config->fullscreen_enabled; } +bool VrEnabled() { + return g_config->vr_enabled; +} + uint32_t GetVblankFrequency() { return std::clamp(g_config->vblank_frequency, 30u, 360u); } diff --git a/src/common/emulatorConfig.h b/src/common/emulatorConfig.h index 61b26f2d8..5c43c5609 100644 --- a/src/common/emulatorConfig.h +++ b/src/common/emulatorConfig.h @@ -46,6 +46,7 @@ struct ConfigOptions { PresentMode present_mode = PresentMode::Mailbox; int32_t gpu_index = -1; bool fullscreen_enabled = false; + bool vr_enabled = false; uint32_t vblank_frequency = 60; uint32_t console_language = DEFAULT_CONSOLE_LANGUAGE; bool vulkan_validation_enabled = false; @@ -79,6 +80,7 @@ int32_t GetUserId(); PresentMode GetPresentMode(); int32_t GetGpuIndex(); bool FullscreenEnabled(); +bool VrEnabled(); uint32_t GetVblankFrequency(); uint32_t GetConsoleLanguage(); bool VulkanValidationEnabled(); diff --git a/src/libs/libHmd2.cpp b/src/libs/libHmd2.cpp new file mode 100644 index 000000000..dc643c661 --- /dev/null +++ b/src/libs/libHmd2.cpp @@ -0,0 +1,98 @@ +#include "common/emulatorConfig.h" +#include "libs/errno.h" +#include "libs/libs.h" +#include "loader/symbolDatabase.h" + +#include +#include +#include + +namespace Libs { + +LIB_VERSION("Hmd2", 1, "Hmd2", 1, 1); + +namespace Hmd2 { + +constexpr int32_t ERROR_ALREADY_INITIALIZED = -1972240383; // 0x8a720001 +constexpr int32_t ERROR_NOT_INITIALIZED = -1972240382; // 0x8a720002 +constexpr int32_t ERROR_PARAMETER_NULL = -1972240376; // 0x8a720008 + +struct SceHmd2InitializeParam { + void* reserved0; + uint8_t reserved[8]; +}; + +struct SceHmd2DeviceInformation { + uint32_t status; + uint32_t reserve0; + struct { + struct { + uint32_t width; + uint32_t height; + } panelResolution; + struct { + uint16_t refreshRate90Hz; + uint16_t refreshRate120Hz; + } flipToDisplayLatency; + } deviceInfo; + uint8_t hmuMount; + uint8_t lensSeparationDistance; + uint8_t reserve1[2]; + float virtualImageDistance; +}; + +static_assert(sizeof(SceHmd2InitializeParam) == 16); +static_assert(offsetof(SceHmd2InitializeParam, reserved) == 8); +static_assert(sizeof(SceHmd2DeviceInformation) == 28); +static_assert(offsetof(SceHmd2DeviceInformation, deviceInfo) == 8); +static_assert(offsetof(SceHmd2DeviceInformation, hmuMount) == 20); +static_assert(offsetof(SceHmd2DeviceInformation, virtualImageDistance) == 24); + +static std::atomic g_initialized = false; + +static int32_t KYTY_SYSV_ABI Hmd2Initialize(const SceHmd2InitializeParam* param) { + PRINT_NAME(); + if (param == nullptr) { + return ERROR_PARAMETER_NULL; + } + if (g_initialized.exchange(true)) { + return ERROR_ALREADY_INITIALIZED; + } + LOGF("Hmd2: initialized, virtual headset=%s\n", Config::VrEnabled() ? "enabled" : "disabled"); + return OK; +} + +static int32_t KYTY_SYSV_ABI Hmd2GetDeviceInformation(SceHmd2DeviceInformation* info) { + PRINT_NAME(); + if (!g_initialized.load()) { + return ERROR_NOT_INITIALIZED; + } + if (info == nullptr) { + return ERROR_PARAMETER_NULL; + } + *info = {}; + if (Config::VrEnabled()) { + // Virtual headset defaults: READY, MOUNT, 63 mm lens separation. + info->hmuMount = 1; + info->lensSeparationDistance = 63; + // Headset characteristics: 4000x2040 panel and 2 m virtual image distance. + info->deviceInfo.panelResolution = {4000, 2040}; + info->virtualImageDistance = 2.0f; + // Host presentation has no emulated headset scan-out delay; latency remains zero. + } else { + info->status = 2; // SCE_HMD2_DEVICE_STATUS_NOT_DETECTED + } + LOGF("Hmd2: device status=%u panel=%ux%u mounted=%u\n", info->status, + info->deviceInfo.panelResolution.width, info->deviceInfo.panelResolution.height, + info->hmuMount); + return OK; +} + +} // namespace Hmd2 + +LIB_DEFINE(InitHmd2_1) { + LIB_FUNC("c812oYs7Vsc", Hmd2::Hmd2Initialize); + LIB_FUNC("bIi4YUfSRys", Hmd2::Hmd2GetDeviceInformation); +} + +} // namespace Libs diff --git a/src/libs/libs.cpp b/src/libs/libs.cpp index c7da9a186..f98a82b77 100644 --- a/src/libs/libs.cpp +++ b/src/libs/libs.cpp @@ -82,6 +82,7 @@ LIB_DEFINE(InitDialog_1); LIB_DEFINE(InitFont_1); LIB_DEFINE(InitFontFt_1); LIB_DEFINE(InitAgcDriver_1); +LIB_DEFINE(InitHmd2_1); LIB_DEFINE(InitLibKernel_1); LIB_DEFINE(InitNet_1); LIB_DEFINE(InitPad_1); @@ -115,6 +116,7 @@ void InitAll(Loader::SymbolDatabase* s) { InitFont_1(s); InitFontFt_1(s); InitAgcDriver_1(s); + InitHmd2_1(s); InitLibKernel_1(s); LibMouse::InitMouse_1(s); LibKeyboard::InitKeyboard_1(s); diff --git a/src/main.cpp b/src/main.cpp index 6eb4f055b..a5dd5be52 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -54,6 +54,7 @@ static void PrintUsage() { ::printf( " --gpu Vulkan physical device index. Default: auto.\n"); ::printf(" --fullscreen Run in borderless desktop fullscreen.\n"); + ::printf(" --vr Enable the virtual VR headset.\n"); ::printf(" --vblank-frequency Virtual vblank frequency. Default: 60.\n"); ::printf(" --console-language <0-29> Console language. Default: 1 (English US).\n"); ::printf(" --vulkan-validation Enable Vulkan validation.\n"); @@ -161,6 +162,11 @@ static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_he continue; } + if (arg == "--vr") { + options.config.vr_enabled = true; + continue; + } + if (arg == "--playgo-hack") { options.config.playgo_hack_enabled = true; continue;