mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30: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 "cfg/button.h"
|
||||
#include "cfg/api.h"
|
||||
#include "hooks/setupapihook.h"
|
||||
#include "hooks/sleephook.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "launcher/logger.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/sysutils.h"
|
||||
#include "io.h"
|
||||
|
||||
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() {
|
||||
return 1;
|
||||
}
|
||||
@@ -320,5 +391,29 @@ namespace games::popn {
|
||||
detour::inline_hook((void *) usbWdtStartDone,
|
||||
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) {
|
||||
log_info("graphics::d3d9", "creating additional swap chain");
|
||||
|
||||
present_params->Windowed = true;
|
||||
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);
|
||||
if (FAILED(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 {
|
||||
hr = device->GetSwapChain(1, &SUB_SWAP_CHAIN);
|
||||
if (FAILED(hr)) {
|
||||
log_warning("graphics::d3d9", "failed to acquire fullscreen sub swap chain, hr={}", FMT_HRESULT(hr));
|
||||
} else {
|
||||
log_info("graphics::d3d9", "acquired fullscreen sub swap chain");
|
||||
return;
|
||||
}
|
||||
|
||||
hr = device->CreateAdditionalSwapChain(present_params, &SUB_SWAP_CHAIN);
|
||||
if (FAILED(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) {
|
||||
sub_swapchain = new WrappedIDirect3DSwapChain9(this, *ppSwapChain);
|
||||
sub_swapchain->should_run_hooks = false;
|
||||
log_info("graphics::d3d9", "created additional swap chain");
|
||||
} else if (FAILED(hr) && !fake_sub_swapchain) {
|
||||
log_warning("graphics::d3d9",
|
||||
"failed to create sub swap chain, hr={}, using fake swap chain",
|
||||
|
||||
@@ -4540,9 +4540,11 @@ namespace overlay::windows {
|
||||
for (const auto &monitor : monitors) {
|
||||
const bool is_selected = option.value == monitor.display_name;
|
||||
auto friendly = wchar_to_u8(monitor.friendly_name.c_str());
|
||||
if (ImGui::Selectable(
|
||||
fmt::format("{} ({})", monitor.display_name, friendly).c_str(),
|
||||
is_selected)) {
|
||||
auto str = fmt::format("{} ({})", monitor.display_name, friendly);
|
||||
if (monitor.is_primary) {
|
||||
str += " [Primary]";
|
||||
}
|
||||
if (ImGui::Selectable(str.c_str(), is_selected)) {
|
||||
option.value = monitor.display_name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,7 +409,26 @@ namespace sysutils {
|
||||
|
||||
std::vector<MonitorEntry> result;
|
||||
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)
|
||||
DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name = {};
|
||||
|
||||
@@ -12,6 +12,10 @@ namespace sysutils {
|
||||
struct MonitorEntry {
|
||||
std::string display_name; // \\.\DISPLAY1
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user