Remove dxwrapper; test scripts focus the game window
The default render size (1835x768) is below Direct3D 7's 2048 limit, so the D3D7 -> D3D9 wrapper and its device-info workaround are no longer needed. The game pauses start-up until its window is active; game-input.ps1 now activates it reliably (Alt + SetForegroundWindow, polled), which dxwrapper had masked. borderless logs the first failed presents. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
df31c9c105
commit
f73c4154ad
9 files changed
+38
-344
No files matched your search
@@ -33,7 +33,6 @@
|
||||
|
||||
#include "game/engine/input.h"
|
||||
#include "game/render/dx_driver7.h"
|
||||
#include "mods/dxwrapper.h"
|
||||
#include "mods/widescreen.h"
|
||||
#include "re/config.h"
|
||||
#include "game/addresses.h"
|
||||
@@ -168,7 +167,6 @@ RE_HOOK(addr::MainWindowProc, MainWindowProc);
|
||||
|
||||
int __fastcall DxDriver7_SetDisplayMode(DxDriver7* self, void* edx, void* deviceCaps, const DxDisplayMode* mode,
|
||||
uint32_t unknown) {
|
||||
mods::dxwrapper::FixDeviceInfo(deviceCaps);
|
||||
if (!Enabled()) {
|
||||
return DxDriver7_SetDisplayMode_original(self, edx, deviceCaps, mode, unknown);
|
||||
}
|
||||
@@ -197,7 +195,14 @@ int __fastcall DxDriver7_Flip(DxDriver7* self, void* edx) {
|
||||
self->blitSource = {0, 0, layout.gameWidth, layout.gameHeight};
|
||||
self->blitDest = layout.screen;
|
||||
}
|
||||
return DxDriver7_Flip_original(self, edx);
|
||||
const int result = DxDriver7_Flip_original(self, edx);
|
||||
static std::atomic<int> failures = 0;
|
||||
if (result != 0 && failures++ < 5) {
|
||||
re::Log("borderless: present failed (0x%08x), source %ldx%ld -> dest (%ld,%ld)-(%ld,%ld)",
|
||||
static_cast<unsigned>(result), self->blitSource.right, self->blitSource.bottom, self->blitDest.left,
|
||||
self->blitDest.top, self->blitDest.right, self->blitDest.bottom);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void __cdecl GetMouseClientPos(HWND window, POINT* position) {
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
// Route the game's DirectDraw 7 / Direct3D 7 through dxwrapper (Dd7to9:
|
||||
// translation to Direct3D 9). Windows' own D3D7 runtime cannot render to
|
||||
// surfaces larger than 2048x2048, which the widescreen mod needs.
|
||||
//
|
||||
// The build puts dxwrapper's ddraw.dll stub, dxwrapper.dll and our
|
||||
// config/dxwrapper.ini into dxwrapper/ next to sacred_re.dll; the game folder
|
||||
// is not touched. sacred.exe imports three functions from DDRAW.dll; we hook
|
||||
// them and forward to the wrapper, which is loaded on the first call (never
|
||||
// from DllMain). Calls made while the wrapper is running (it may use the real
|
||||
// DirectDraw itself) go to the original functions.
|
||||
//
|
||||
// sacred_re.ini:
|
||||
// [renderer]
|
||||
// DxWrapper=1 ; 0 = Windows' DirectDraw (render size limited to 2048)
|
||||
|
||||
#include "mods/dxwrapper.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include <windows.h>
|
||||
#include <ddraw.h>
|
||||
|
||||
#include "re/config.h"
|
||||
#include "re/hook.h"
|
||||
#include "re/log.h"
|
||||
|
||||
namespace mods::dxwrapper {
|
||||
namespace {
|
||||
|
||||
HMODULE g_wrapper = nullptr;
|
||||
bool g_loadAttempted = false;
|
||||
thread_local int g_insideWrapper = 0;
|
||||
|
||||
std::wstring WrapperPath() {
|
||||
return re::ModuleDirectory() + L"dxwrapper\\ddraw.dll";
|
||||
}
|
||||
|
||||
bool Enabled() {
|
||||
return re::ConfigInt("renderer", "DxWrapper", 1) != 0;
|
||||
}
|
||||
|
||||
FARPROC WrapperFunction(const char* name) {
|
||||
if (!g_loadAttempted) {
|
||||
g_loadAttempted = true;
|
||||
const std::wstring path = WrapperPath();
|
||||
g_wrapper = LoadLibraryW(path.c_str());
|
||||
if (g_wrapper) {
|
||||
re::Log("dxwrapper: loaded %ls", path.c_str());
|
||||
} else {
|
||||
re::Log("dxwrapper: could not load %ls (error %lu), using Windows' DirectDraw", path.c_str(),
|
||||
GetLastError());
|
||||
}
|
||||
}
|
||||
return g_wrapper ? GetProcAddress(g_wrapper, name) : nullptr;
|
||||
}
|
||||
|
||||
// Calls `function` from the wrapper if possible, else `original`.
|
||||
template <typename Function, typename... Args>
|
||||
HRESULT Forward(const char* name, Function original, Args... args) {
|
||||
if (g_insideWrapper == 0 && Enabled()) {
|
||||
if (auto wrapped = reinterpret_cast<Function>(WrapperFunction(name))) {
|
||||
++g_insideWrapper;
|
||||
const HRESULT result = wrapped(args...);
|
||||
--g_insideWrapper;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return original(args...);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool IsActive() {
|
||||
if (!Enabled()) {
|
||||
return false;
|
||||
}
|
||||
if (g_loadAttempted) {
|
||||
return g_wrapper != nullptr;
|
||||
}
|
||||
return GetFileAttributesW(WrapperPath().c_str()) != INVALID_FILE_ATTRIBUTES;
|
||||
}
|
||||
|
||||
void FixDeviceInfo(void* deviceInfo) {
|
||||
auto* bytes = static_cast<uint8_t*>(deviceInfo);
|
||||
const uint32_t guidAddress = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(bytes + 0x3f4));
|
||||
uint32_t guidPointer;
|
||||
memcpy(&guidPointer, bytes + 4, 4);
|
||||
if (guidPointer != guidAddress) {
|
||||
re::Log("dxwrapper: device info GUID pointer was 0x%08x, set to 0x%08x", guidPointer, guidAddress);
|
||||
memcpy(bytes + 4, &guidAddress, 4);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT WINAPI DirectDrawCreateExViaWrapper(GUID* guid, LPVOID* directDraw, REFIID iid, IUnknown* outer);
|
||||
HRESULT WINAPI DirectDrawCreateViaWrapper(GUID* guid, LPDIRECTDRAW* directDraw, IUnknown* outer);
|
||||
HRESULT WINAPI DirectDrawEnumerateExAViaWrapper(LPDDENUMCALLBACKEXA callback, LPVOID context, DWORD flags);
|
||||
|
||||
RE_HOOK_API(DirectDrawCreateEx, DirectDrawCreateExViaWrapper);
|
||||
RE_HOOK_API(DirectDrawCreate, DirectDrawCreateViaWrapper);
|
||||
RE_HOOK_API(DirectDrawEnumerateExA, DirectDrawEnumerateExAViaWrapper);
|
||||
|
||||
HRESULT WINAPI DirectDrawCreateExViaWrapper(GUID* guid, LPVOID* directDraw, REFIID iid, IUnknown* outer) {
|
||||
return Forward("DirectDrawCreateEx", DirectDrawCreateExViaWrapper_original, guid, directDraw, iid, outer);
|
||||
}
|
||||
|
||||
HRESULT WINAPI DirectDrawCreateViaWrapper(GUID* guid, LPDIRECTDRAW* directDraw, IUnknown* outer) {
|
||||
return Forward("DirectDrawCreate", DirectDrawCreateViaWrapper_original, guid, directDraw, outer);
|
||||
}
|
||||
|
||||
HRESULT WINAPI DirectDrawEnumerateExAViaWrapper(LPDDENUMCALLBACKEXA callback, LPVOID context, DWORD flags) {
|
||||
return Forward("DirectDrawEnumerateExA", DirectDrawEnumerateExAViaWrapper_original, callback, context, flags);
|
||||
}
|
||||
|
||||
} // namespace mods::dxwrapper
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace mods::dxwrapper {
|
||||
|
||||
// True when the game's DirectDraw calls go to dxwrapper (Direct3D 9), which
|
||||
// has no 2048x2048 render target limit.
|
||||
bool IsActive();
|
||||
|
||||
// The device info the game passes to DxDriver7::SetDisplayMode keeps a pointer
|
||||
// to its own device GUID at +4 (the GUID itself is at +0x3f4); the game uses
|
||||
// it for EnumZBufferFormats. Under dxwrapper the game's device enumeration
|
||||
// leaves garbage there (0x3c0) and dxwrapper crashes dereferencing it.
|
||||
// Restores the pointer Windows' DirectDraw would have produced.
|
||||
void FixDeviceInfo(void* deviceInfo);
|
||||
|
||||
} // namespace mods::dxwrapper
|
||||
@@ -1,5 +1,5 @@
|
||||
// Widescreen: render the world 1:1 at the monitor's resolution, showing more
|
||||
// of it, instead of scaling a 1024x768 image.
|
||||
// Widescreen: show more of the world horizontally on wide monitors while
|
||||
// everything keeps its original on-screen size.
|
||||
//
|
||||
// The world view sizes itself from zoom * 1024.0 / 768.0 (float constants that
|
||||
// the UI also reads) and projects 3D models with an ortho camera of
|
||||
@@ -15,8 +15,8 @@
|
||||
// ; 0 = monitor height, i.e. native 1:1 (everything smaller)
|
||||
// Width=0 ; 0 = Height * monitor aspect ratio
|
||||
//
|
||||
// Windows' Direct3D 7 limits the render size to 2048x2048 unless the dxwrapper
|
||||
// mod (Direct3D 9) is active; the borderless mod centres a smaller image 1:1.
|
||||
// Windows' Direct3D 7 cannot render to targets larger than 2048x2048, so the
|
||||
// render size is clamped to that (only reachable with Height=0 / large Width).
|
||||
//
|
||||
// Experimental: the UI is still drawn at the top-left 1024x768, and creatures
|
||||
// are only active within the original distance.
|
||||
@@ -32,7 +32,6 @@
|
||||
|
||||
#include "game/addresses.h"
|
||||
#include "game/ui/ui_manager.h"
|
||||
#include "mods/dxwrapper.h"
|
||||
#include "re/config.h"
|
||||
#include "re/hook.h"
|
||||
#include "re/log.h"
|
||||
@@ -246,7 +245,7 @@ void ApplyRenderSize(game::DxDisplayMode& mode, HWND window) {
|
||||
// wider or taller than 2048 pixels (CreateDevice fails with
|
||||
// DDERR_INVALIDOBJECT); anything larger needs a different renderer.
|
||||
constexpr int kMaxD3D7Surface = 2048;
|
||||
if (!mods::dxwrapper::IsActive() && (width > kMaxD3D7Surface || height > kMaxD3D7Surface)) {
|
||||
if (width > kMaxD3D7Surface || height > kMaxD3D7Surface) {
|
||||
re::Log("widescreen: %dx%d exceeds the Direct3D 7 limit, clamping to %d", width, height, kMaxD3D7Surface);
|
||||
width = std::min(width, kMaxD3D7Surface);
|
||||
height = std::min(height, kMaxD3D7Surface);
|
||||
|
||||
Reference in new issue
Block a user