Widescreen experiment: native render size via dxwrapper, classic menus
- mods/widescreen: redirect the world view's reads of the 1024.0/768.0
constants and the 3D ortho extents to our variables; render size =
monitor; relocate the world view's tile draw list (1750 entries, no
bounds check) into an enlarged object (16384 entries).
- mods/dxwrapper: route DirectDraw through dxwrapper (Dd7to9, D3D9) since
Windows' D3D7 cannot render to targets larger than 2048; fix the game's
device info GUID pointer that dxwrapper's enumeration leaves invalid.
Pinned release fetched by CMake (hash-checked), installed next to the DLL.
- Main menu (UI thread) and loading screen render in a 1024x768 viewport
and are presented scaled ("classic" layout); in-game uses the full size.
- re/patch: verified code patches (operand redirect, push immediates,
displacements) and startup hooks; build.ps1/CMake download fixes.
In-game view untested yet; HUD still at the top-left.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c8dc689bc6
commit
795fe8f087
19 files changed
+860
-28
No files matched your search
+54
-23
@@ -26,12 +26,15 @@
|
||||
// SetCursorPos by a game-pixel delta, which is not scaled to screen pixels.
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
#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"
|
||||
#include "re/hook.h"
|
||||
@@ -39,17 +42,28 @@
|
||||
|
||||
namespace {
|
||||
|
||||
struct Viewport {
|
||||
bool active = false;
|
||||
HWND window = nullptr;
|
||||
bool runInBackground = false;
|
||||
// Where a gameWidth x gameHeight image appears in the window.
|
||||
struct Layout {
|
||||
int gameWidth = 0;
|
||||
int gameHeight = 0;
|
||||
RECT client = {}; // scaled game image, window client coordinates
|
||||
RECT screen = {}; // same rectangle, screen coordinates
|
||||
};
|
||||
|
||||
struct Viewport {
|
||||
bool active = false;
|
||||
HWND window = nullptr;
|
||||
bool runInBackground = false;
|
||||
Layout full; // the whole back buffer
|
||||
Layout classic; // its top-left 1024x768 (menus, loading screen; widescreen mod)
|
||||
};
|
||||
|
||||
Viewport g_viewport;
|
||||
std::atomic<bool> g_classicShown = false; // which layout the last presented frame used
|
||||
|
||||
const Layout& CurrentLayout() {
|
||||
return g_classicShown ? g_viewport.classic : g_viewport.full;
|
||||
}
|
||||
|
||||
bool Enabled() {
|
||||
return re::ConfigInt("borderless", "Enabled", 1) != 0;
|
||||
@@ -86,35 +100,43 @@ void ApplyBorderless(game::DxDriver7* driver) {
|
||||
SetWindowLongW(window, GWL_STYLE, WS_POPUP | WS_VISIBLE);
|
||||
SetWindowPos(window, HWND_TOP, area.left, area.top, areaWidth, areaHeight, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
|
||||
|
||||
const bool integerScale = re::ConfigInt("borderless", "IntegerScale", 0) != 0;
|
||||
auto makeLayout = [&](int width, int height) {
|
||||
Layout layout;
|
||||
layout.gameWidth = width;
|
||||
layout.gameHeight = height;
|
||||
layout.client = FitRect(areaWidth, areaHeight, width, height, integerScale);
|
||||
layout.screen = layout.client;
|
||||
OffsetRect(&layout.screen, area.left, area.top);
|
||||
return layout;
|
||||
};
|
||||
|
||||
Viewport viewport;
|
||||
viewport.window = window;
|
||||
viewport.runInBackground = re::ConfigInt("borderless", "RunInBackground", 1) != 0;
|
||||
viewport.gameWidth = driver->mode.width;
|
||||
viewport.gameHeight = driver->mode.height;
|
||||
viewport.client = FitRect(areaWidth, areaHeight, viewport.gameWidth, viewport.gameHeight,
|
||||
re::ConfigInt("borderless", "IntegerScale", 0) != 0);
|
||||
viewport.screen = viewport.client;
|
||||
OffsetRect(&viewport.screen, area.left, area.top);
|
||||
viewport.full = makeLayout(driver->mode.width, driver->mode.height);
|
||||
viewport.classic = makeLayout(std::min<int>(driver->mode.width, 1024), std::min<int>(driver->mode.height, 768));
|
||||
viewport.active = true;
|
||||
g_viewport = viewport;
|
||||
g_classicShown = false;
|
||||
|
||||
// Paint the bars: the window class background brush is black.
|
||||
InvalidateRect(window, nullptr, TRUE);
|
||||
|
||||
re::Log("borderless: %dx%d game -> %ldx%ld monitor, image at (%ld,%ld) %ldx%ld", viewport.gameWidth,
|
||||
viewport.gameHeight, static_cast<long>(areaWidth), static_cast<long>(areaHeight), viewport.client.left,
|
||||
viewport.client.top, viewport.client.right - viewport.client.left,
|
||||
viewport.client.bottom - viewport.client.top);
|
||||
const RECT& image = viewport.full.client;
|
||||
re::Log("borderless: %dx%d game -> %ldx%ld monitor, image at (%ld,%ld) %ldx%ld", viewport.full.gameWidth,
|
||||
viewport.full.gameHeight, static_cast<long>(areaWidth), static_cast<long>(areaHeight), image.left,
|
||||
image.top, image.right - image.left, image.bottom - image.top);
|
||||
}
|
||||
|
||||
// Window client coordinates -> game pixels.
|
||||
POINT ToGame(POINT p) {
|
||||
const Viewport& v = g_viewport;
|
||||
const int width = v.client.right - v.client.left;
|
||||
const int height = v.client.bottom - v.client.top;
|
||||
const Layout& layout = CurrentLayout();
|
||||
const int width = layout.client.right - layout.client.left;
|
||||
const int height = layout.client.bottom - layout.client.top;
|
||||
POINT result;
|
||||
result.x = std::clamp(MulDiv(p.x - v.client.left, v.gameWidth, width), 0, v.gameWidth - 1);
|
||||
result.y = std::clamp(MulDiv(p.y - v.client.top, v.gameHeight, height), 0, v.gameHeight - 1);
|
||||
result.x = std::clamp(MulDiv(p.x - layout.client.left, layout.gameWidth, width), 0, layout.gameWidth - 1);
|
||||
result.y = std::clamp(MulDiv(p.y - layout.client.top, layout.gameHeight, height), 0, layout.gameHeight - 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -122,9 +144,9 @@ POINT ToGame(POINT p) {
|
||||
void PaintBars(HDC dc) {
|
||||
RECT window;
|
||||
GetClientRect(g_viewport.window, &window);
|
||||
const RECT& image = CurrentLayout().client;
|
||||
const int saved = SaveDC(dc);
|
||||
ExcludeClipRect(dc, g_viewport.client.left, g_viewport.client.top, g_viewport.client.right,
|
||||
g_viewport.client.bottom);
|
||||
ExcludeClipRect(dc, image.left, image.top, image.right, image.bottom);
|
||||
FillRect(dc, &window, static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)));
|
||||
RestoreDC(dc, saved);
|
||||
}
|
||||
@@ -146,11 +168,13 @@ 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);
|
||||
}
|
||||
DxDisplayMode windowed = *mode;
|
||||
windowed.fullscreen = 0;
|
||||
mods::widescreen::ApplyRenderSize(windowed, self->window);
|
||||
g_viewport.active = false;
|
||||
const int result = DxDriver7_SetDisplayMode_original(self, edx, deviceCaps, &windowed, unknown);
|
||||
if (result) {
|
||||
@@ -163,8 +187,15 @@ int __fastcall DxDriver7_Flip(DxDriver7* self, void* edx) {
|
||||
// The original computes the blit rectangles once, for a centred window of
|
||||
// exactly the game's size; override them on every present.
|
||||
if (g_viewport.active && self->window == g_viewport.window) {
|
||||
self->blitSource = {0, 0, g_viewport.gameWidth, g_viewport.gameHeight};
|
||||
self->blitDest = g_viewport.screen;
|
||||
// Menus and the loading screen only fill the top-left 1024x768 of a
|
||||
// widened back buffer; show just that part, scaled like before.
|
||||
const bool classic = mods::widescreen::IsClassicFrame();
|
||||
if (g_classicShown.exchange(classic) != classic) {
|
||||
InvalidateRect(self->window, nullptr, TRUE); // repaint the bars for the new layout
|
||||
}
|
||||
const Layout& layout = CurrentLayout();
|
||||
self->blitSource = {0, 0, layout.gameWidth, layout.gameHeight};
|
||||
self->blitDest = layout.screen;
|
||||
}
|
||||
return DxDriver7_Flip_original(self, edx);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user