mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 06:40:42 -07:00
popn: hook DisplayConfigGetDeviceInfo to allow for any GPU port to be used for monitor, get past I/O check (#622)
## Link to GitHub Issue or related Pull Request, if one exists #618 ## Description of change The game expects the main display to be *not* HDMI, and at port 2. Hooking DisplayConfigGetDeviceInfo allows us to boot the game on any monitor. Add basic I/O hook. It gets stuck in `USB I/O Checking...` for a couple minutes but it'll eventually skip and boot to title screen. ## Testing ugh.
This commit is contained in:
@@ -9,14 +9,85 @@
|
|||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
#include "cfg/button.h"
|
#include "cfg/button.h"
|
||||||
#include "cfg/api.h"
|
#include "cfg/api.h"
|
||||||
|
#include "hooks/setupapihook.h"
|
||||||
#include "hooks/sleephook.h"
|
#include "hooks/sleephook.h"
|
||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "launcher/logger.h"
|
#include "launcher/logger.h"
|
||||||
#include "misc/eamuse.h"
|
#include "misc/eamuse.h"
|
||||||
|
#include "util/sysutils.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
|
|
||||||
namespace games::popn {
|
namespace games::popn {
|
||||||
|
|
||||||
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
|
static decltype(DisplayConfigGetDeviceInfo) *DisplayConfigGetDeviceInfo_orig = nullptr;
|
||||||
|
|
||||||
|
static
|
||||||
|
LONG
|
||||||
|
WINAPI
|
||||||
|
DisplayConfigGetDeviceInfo_hook(DISPLAYCONFIG_DEVICE_INFO_HEADER* requestPacket)
|
||||||
|
{
|
||||||
|
if (requestPacket == nullptr) {
|
||||||
|
return DisplayConfigGetDeviceInfo_orig(requestPacket);
|
||||||
|
}
|
||||||
|
const auto ret = DisplayConfigGetDeviceInfo_orig(requestPacket);
|
||||||
|
log_misc(
|
||||||
|
"popn",
|
||||||
|
"DisplayConfigGetDeviceInfo returned {}, type={}, size={}, id={}, luid={}/{}",
|
||||||
|
ret,
|
||||||
|
static_cast<int>(requestPacket->type),
|
||||||
|
requestPacket->size,
|
||||||
|
requestPacket->id,
|
||||||
|
requestPacket->adapterId.HighPart,
|
||||||
|
requestPacket->adapterId.LowPart);
|
||||||
|
if (ret == ERROR_SUCCESS) {
|
||||||
|
if (requestPacket->type == DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME) {
|
||||||
|
const auto sourceName = reinterpret_cast<DISPLAYCONFIG_SOURCE_DEVICE_NAME*>(requestPacket);
|
||||||
|
log_misc(
|
||||||
|
"popn",
|
||||||
|
"... name={}",
|
||||||
|
ws2s(sourceName->viewGdiDeviceName));
|
||||||
|
} else if (requestPacket->type == DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME) {
|
||||||
|
const auto targetName = reinterpret_cast<DISPLAYCONFIG_TARGET_DEVICE_NAME*>(requestPacket);
|
||||||
|
log_misc(
|
||||||
|
"popn",
|
||||||
|
"... flags={}, outputTechnology: {}, connectorInstance={}, friendlyname={} path={}",
|
||||||
|
targetName->flags.value,
|
||||||
|
static_cast<int>(targetName->outputTechnology),
|
||||||
|
targetName->connectorInstance,
|
||||||
|
ws2s(targetName->monitorFriendlyDeviceName),
|
||||||
|
ws2s(targetName->monitorDevicePath));
|
||||||
|
|
||||||
|
// need to fix up some values for the primary monitor...
|
||||||
|
const auto monitors = sysutils::enumerate_monitors();
|
||||||
|
for (const auto& monitor : monitors) {
|
||||||
|
if (monitor.id == targetName->header.id &&
|
||||||
|
monitor.adapter_id_HighPart == targetName->header.adapterId.HighPart &&
|
||||||
|
monitor.adapter_id_LowPart == targetName->header.adapterId.LowPart) {
|
||||||
|
|
||||||
|
if (monitor.is_primary) {
|
||||||
|
targetName->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL;
|
||||||
|
targetName->connectorInstance = 2;
|
||||||
|
log_info(
|
||||||
|
"popn",
|
||||||
|
"... overriding primary monitor ({}) to pretend to be DP port #2",
|
||||||
|
monitor.display_name);
|
||||||
|
} else {
|
||||||
|
// TODO: is this what the game expects for subscreen?
|
||||||
|
// TODO: what if there are 3+ monitors?
|
||||||
|
targetName->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static int __cdecl usbCheckAlive() {
|
static int __cdecl usbCheckAlive() {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -320,5 +391,29 @@ namespace games::popn {
|
|||||||
detour::inline_hook((void *) usbWdtStartDone,
|
detour::inline_hook((void *) usbWdtStartDone,
|
||||||
libutils::try_proc(ezusb, "?usbWdtStartDone@@YAHXZ"));
|
libutils::try_proc(ezusb, "?usbWdtStartDone@@YAHXZ"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SPICE64 && !SPICE_XP
|
||||||
|
|
||||||
|
if (is_pikapika_model()) {
|
||||||
|
// monitor hook
|
||||||
|
DisplayConfigGetDeviceInfo_orig = detour::iat_try("DisplayConfigGetDeviceInfo", DisplayConfigGetDeviceInfo_hook, avs::game::DLL_INSTANCE);
|
||||||
|
|
||||||
|
// TODO: io emulation
|
||||||
|
SETUPAPI_SETTINGS settings{};
|
||||||
|
settings.class_guid[0] = 0x86E0D1E0;
|
||||||
|
settings.class_guid[1] = 0x11D08089;
|
||||||
|
settings.class_guid[2] = 0x0008E49C;
|
||||||
|
settings.class_guid[3] = 0x731F303E;
|
||||||
|
const char property[] = "1CCF(8050)_000";
|
||||||
|
const char property_hardwareid[] = "USB\\VID_1CCF&PID_8050&MI_00\\000";
|
||||||
|
memcpy(settings.property_devicedesc, property, sizeof(property));
|
||||||
|
memcpy(settings.property_hardwareid, property_hardwareid, sizeof(property_hardwareid));
|
||||||
|
setupapihook_init(avs::game::DLL_INSTANCE);
|
||||||
|
setupapihook_add(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1139,8 +1139,6 @@ static void graphics_d3d9_ldj_init_sub_screen(IDirect3DDevice9Ex *device, D3DPRE
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (GRAPHICS_WINDOWED) {
|
if (GRAPHICS_WINDOWED) {
|
||||||
log_info("graphics::d3d9", "creating additional swap chain");
|
|
||||||
|
|
||||||
present_params->Windowed = true;
|
present_params->Windowed = true;
|
||||||
present_params->FullScreen_RefreshRateInHz = 0;
|
present_params->FullScreen_RefreshRateInHz = 0;
|
||||||
|
|
||||||
@@ -1149,18 +1147,23 @@ static void graphics_d3d9_ldj_init_sub_screen(IDirect3DDevice9Ex *device, D3DPRE
|
|||||||
hr = device->CreateAdditionalSwapChain(present_params, &SUB_SWAP_CHAIN);
|
hr = device->CreateAdditionalSwapChain(present_params, &SUB_SWAP_CHAIN);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
log_warning("graphics::d3d9", "failed to create additional swap chain, hr={}", FMT_HRESULT(hr));
|
log_warning("graphics::d3d9", "failed to create additional swap chain, hr={}", FMT_HRESULT(hr));
|
||||||
|
} else {
|
||||||
|
log_info("graphics::d3d9", "created additional swap chain for windowed mode");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
hr = device->GetSwapChain(1, &SUB_SWAP_CHAIN);
|
hr = device->GetSwapChain(1, &SUB_SWAP_CHAIN);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
log_warning("graphics::d3d9", "failed to acquire fullscreen sub swap chain, hr={}", FMT_HRESULT(hr));
|
log_warning("graphics::d3d9", "failed to acquire fullscreen sub swap chain, hr={}", FMT_HRESULT(hr));
|
||||||
} else {
|
} else {
|
||||||
|
log_info("graphics::d3d9", "acquired fullscreen sub swap chain");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = device->CreateAdditionalSwapChain(present_params, &SUB_SWAP_CHAIN);
|
hr = device->CreateAdditionalSwapChain(present_params, &SUB_SWAP_CHAIN);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
log_warning("graphics::d3d9", "failed to get additional swap chain, hr={}", FMT_HRESULT(hr));
|
log_warning("graphics::d3d9", "failed to get additional swap chain, hr={}", FMT_HRESULT(hr));
|
||||||
|
} else {
|
||||||
|
log_info("graphics::d3d9", "created additional swap chain for fullscreen mode");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -263,7 +263,6 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateAdditionalSwapChain(
|
|||||||
if (SUCCEEDED(hr) && !sub_swapchain) {
|
if (SUCCEEDED(hr) && !sub_swapchain) {
|
||||||
sub_swapchain = new WrappedIDirect3DSwapChain9(this, *ppSwapChain);
|
sub_swapchain = new WrappedIDirect3DSwapChain9(this, *ppSwapChain);
|
||||||
sub_swapchain->should_run_hooks = false;
|
sub_swapchain->should_run_hooks = false;
|
||||||
log_info("graphics::d3d9", "created additional swap chain");
|
|
||||||
} else if (FAILED(hr) && !fake_sub_swapchain) {
|
} else if (FAILED(hr) && !fake_sub_swapchain) {
|
||||||
log_warning("graphics::d3d9",
|
log_warning("graphics::d3d9",
|
||||||
"failed to create sub swap chain, hr={}, using fake swap chain",
|
"failed to create sub swap chain, hr={}, using fake swap chain",
|
||||||
|
|||||||
@@ -4540,9 +4540,11 @@ namespace overlay::windows {
|
|||||||
for (const auto &monitor : monitors) {
|
for (const auto &monitor : monitors) {
|
||||||
const bool is_selected = option.value == monitor.display_name;
|
const bool is_selected = option.value == monitor.display_name;
|
||||||
auto friendly = wchar_to_u8(monitor.friendly_name.c_str());
|
auto friendly = wchar_to_u8(monitor.friendly_name.c_str());
|
||||||
if (ImGui::Selectable(
|
auto str = fmt::format("{} ({})", monitor.display_name, friendly);
|
||||||
fmt::format("{} ({})", monitor.display_name, friendly).c_str(),
|
if (monitor.is_primary) {
|
||||||
is_selected)) {
|
str += " [Primary]";
|
||||||
|
}
|
||||||
|
if (ImGui::Selectable(str.c_str(), is_selected)) {
|
||||||
option.value = monitor.display_name;
|
option.value = monitor.display_name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -409,7 +409,26 @@ namespace sysutils {
|
|||||||
|
|
||||||
std::vector<MonitorEntry> result;
|
std::vector<MonitorEntry> result;
|
||||||
for (const auto& path : paths) {
|
for (const auto& path : paths) {
|
||||||
MonitorEntry entry;
|
MonitorEntry entry = {};
|
||||||
|
entry.adapter_id_LowPart = path.targetInfo.adapterId.LowPart;
|
||||||
|
entry.adapter_id_HighPart = path.targetInfo.adapterId.HighPart;
|
||||||
|
entry.id = path.targetInfo.id;
|
||||||
|
|
||||||
|
// primary monitor?
|
||||||
|
for (const auto& mode : modes) {
|
||||||
|
if (mode.infoType != DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode.adapterId.HighPart == path.sourceInfo.adapterId.HighPart &&
|
||||||
|
mode.adapterId.LowPart == path.sourceInfo.adapterId.LowPart &&
|
||||||
|
mode.id == path.sourceInfo.id) {
|
||||||
|
if (mode.sourceMode.position.x == 0 && mode.sourceMode.position.y == 0) {
|
||||||
|
entry.is_primary = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// device ID (\\.\DISPLAYn)
|
// device ID (\\.\DISPLAYn)
|
||||||
DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name = {};
|
DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name = {};
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ namespace sysutils {
|
|||||||
struct MonitorEntry {
|
struct MonitorEntry {
|
||||||
std::string display_name; // \\.\DISPLAY1
|
std::string display_name; // \\.\DISPLAY1
|
||||||
std::wstring friendly_name; // Dell S2204T; wstring so that it can be converted to utf8 or string
|
std::wstring friendly_name; // Dell S2204T; wstring so that it can be converted to utf8 or string
|
||||||
|
bool is_primary;
|
||||||
|
uint32_t adapter_id_LowPart;
|
||||||
|
int32_t adapter_id_HighPart;
|
||||||
|
uint32_t id;
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::vector<MonitorEntry> &enumerate_monitors();
|
const std::vector<MonitorEntry> &enumerate_monitors();
|
||||||
|
|||||||
Reference in New Issue
Block a user