Initial sacred_re scaffold with borderless fullscreen mod
- Launcher (sacred_re.exe) starts sacred.exe with sacred_re.dll injected via Detours; the DLL verifies the Sacred Gold 2.28 build before hooking. - Hook framework: RE_HOOK for game addresses, RE_HOOK_API for Windows APIs, per-hook enable/disable in sacred_re.ini, sacred_re.log. - Game code: Log (0x66f1c0) hooked, CmdLineHasFlag (0x813140) rewritten. - Borderless mod: forces windowed mode, covers the monitor, scales the 1024x768 image aspect-correct, remaps mouse input, keeps running in the background with keyboard input hidden while unfocused. - CMake/Ninja/MSVC x86 build, VS Code setup, disasm tool, function map. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
d9f5c85430
31 files changed
+1184
No files matched your search
@@ -0,0 +1,49 @@
|
||||
// Command-line switches.
|
||||
//
|
||||
// Original (0x00813140), called from WinMain with lpCmdLine, e.g. for "log",
|
||||
// "tabblocker", "win" and the language codes "US", "DE", ... (table 0x00897394).
|
||||
//
|
||||
// Status: rewritten. Behaviour matches the original, including its quirks:
|
||||
// - only the flag is lowercased, not the command line: "-LOG" does not enable
|
||||
// logging, and the language code "DE" is matched as "de";
|
||||
// - nothing before the match is checked, so "-xlog" also counts as "log";
|
||||
// - a match must be followed by a space or the end of the command line.
|
||||
// Deviation: the original copies the flag into a 256-byte stack buffer
|
||||
// unchecked; we truncate instead of overflowing.
|
||||
|
||||
#include "game/engine/cmdline.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "game/engine/log.h"
|
||||
#include "re/hook.h"
|
||||
|
||||
namespace game {
|
||||
|
||||
RE_HOOK(0x00813140, CmdLineHasFlag);
|
||||
|
||||
bool __cdecl CmdLineHasFlag(const char* cmdline, const char* flag, bool quiet) {
|
||||
char needle[256];
|
||||
strncpy_s(needle, flag, _TRUNCATE);
|
||||
for (char* c = needle; *c; ++c) {
|
||||
if (*c >= 'A' && *c <= 'Z') {
|
||||
*c += 'a' - 'A';
|
||||
}
|
||||
}
|
||||
const size_t length = strlen(needle);
|
||||
|
||||
for (const char* match = strstr(cmdline, needle); match; match = strstr(match + 1, needle)) {
|
||||
const char* space = strchr(match, ' ');
|
||||
const bool endsAtSpace = space && static_cast<size_t>(space - match) == length;
|
||||
const bool endsAtEnd = strlen(match) == length;
|
||||
if (endsAtSpace || endsAtEnd) {
|
||||
if (!quiet) {
|
||||
Log("cmd: %s\n", flag);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
namespace game {
|
||||
|
||||
// sacred.exe 0x00813140
|
||||
bool __cdecl CmdLineHasFlag(const char* cmdline, const char* flag, bool quiet);
|
||||
|
||||
} // namespace game
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace game {
|
||||
|
||||
// sacred.exe 0x0066e500. GetCursorPos(), made relative to `window`'s client
|
||||
// area when `window` is non-null. The game's main source of mouse positions.
|
||||
void __cdecl GetMouseClientPos(HWND window, POINT* position);
|
||||
|
||||
// sacred.exe 0x008122a0. Window procedure of the main "Sacred" window class.
|
||||
LRESULT CALLBACK MainWindowProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
} // namespace game
|
||||
@@ -0,0 +1,42 @@
|
||||
// Game debug log.
|
||||
//
|
||||
// Original (0x0066f1c0): printf-style. Does nothing unless logging is enabled
|
||||
// (byte 0x017e5ee0, set by the "log" command-line flag in WinMain). When
|
||||
// enabled it lazily opens DEBUG.LOG and ExpLog.LOG, writes the message to
|
||||
// DEBUG.LOG, then formats it into a 1024-byte buffer and passes it to an
|
||||
// optional callback (0x017e5ee4) and to either OutputDebugString or the
|
||||
// in-game console object (0x017e5edc).
|
||||
//
|
||||
// Status: hooked, not rewritten. Every message is mirrored into sacred_re.log
|
||||
// whether or not the game's own logging is enabled, then forwarded.
|
||||
|
||||
#include "game/engine/log.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "re/hook.h"
|
||||
#include "re/log.h"
|
||||
|
||||
namespace game {
|
||||
|
||||
RE_HOOK(0x0066f1c0, Log);
|
||||
|
||||
void __cdecl Log(const char* fmt, ...) {
|
||||
char message[1024];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(message, sizeof(message), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
size_t length = strlen(message);
|
||||
while (length > 0 && (message[length - 1] == '\n' || message[length - 1] == '\r')) {
|
||||
--length;
|
||||
}
|
||||
re::Log("[game] %.*s", static_cast<int>(length), message);
|
||||
|
||||
Log_original("%s", message);
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
namespace game {
|
||||
|
||||
// sacred.exe 0x0066f1c0
|
||||
void __cdecl Log(const char* fmt, ...);
|
||||
|
||||
} // namespace game
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
// The engine's DirectDraw 7 / Direct3D 7 renderer ("dxDriver7" in its log
|
||||
// messages). Single instance, pointer at 0x00cda99c.
|
||||
//
|
||||
// These structs mirror the original 32-bit memory layout field by field and
|
||||
// are only valid inside the game process; only the fields we use are named.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace game {
|
||||
|
||||
// Display mode request, passed to DxDriver7::SetDisplayMode and copied to
|
||||
// DxDriver7::mode.
|
||||
struct DxDisplayMode {
|
||||
uint8_t unknown00[0x08];
|
||||
uint16_t height; // +0x08
|
||||
uint8_t unknown0a[0x02];
|
||||
uint16_t width; // +0x0c
|
||||
uint8_t unknown0e[0x6e];
|
||||
int32_t fullscreen; // +0x7c 1 = exclusive fullscreen, 0 = windowed
|
||||
uint32_t bitsPerPixel; // +0x80
|
||||
};
|
||||
static_assert(sizeof(DxDisplayMode) == 0x84);
|
||||
static_assert(offsetof(DxDisplayMode, fullscreen) == 0x7c);
|
||||
|
||||
struct DxDriver7 {
|
||||
uint8_t unknown000[0x14];
|
||||
DxDisplayMode mode; // +0x14
|
||||
uint8_t unknown098[0x18];
|
||||
HWND window; // +0xb0
|
||||
void* directDraw; // +0xb4 IDirectDraw7*
|
||||
void* primarySurface; // +0xb8 IDirectDrawSurface7*
|
||||
void* backBuffer; // +0xbc IDirectDrawSurface7*
|
||||
uint8_t unknown0c0[0x11c];
|
||||
RECT blitSource; // +0x1dc back-buffer rect copied by Flip (windowed)
|
||||
RECT blitDest; // +0x1ec screen-space destination of that copy
|
||||
};
|
||||
static_assert(offsetof(DxDriver7, window) == 0xb0);
|
||||
static_assert(offsetof(DxDriver7, blitSource) == 0x1dc);
|
||||
static_assert(offsetof(DxDriver7, blitDest) == 0x1ec);
|
||||
|
||||
// Member functions are __thiscall; we declare them __fastcall with an unused
|
||||
// second parameter (EDX), which has the same register/stack contract.
|
||||
|
||||
// sacred.exe 0x00645390. Creates DirectDraw, the surfaces and the D3D device for
|
||||
// `mode`. Windowed: resizes the popup window to mode.width x mode.height,
|
||||
// centred on the desktop. Returns non-zero on success.
|
||||
int __fastcall DxDriver7_SetDisplayMode(DxDriver7* self, void* edx, void* deviceCaps, const DxDisplayMode* mode,
|
||||
uint32_t unknown);
|
||||
|
||||
// sacred.exe 0x00645f70. Presents the back buffer: Flip() in fullscreen,
|
||||
// primary->Blt(blitDest, backBuffer, blitSource) in windowed mode.
|
||||
int __fastcall DxDriver7_Flip(DxDriver7* self, void* edx);
|
||||
|
||||
} // namespace game
|
||||
Reference in new issue
Block a user