Skip to content

Architecture

The oracle idea

The target binaries were compiled with Microsoft Visual C++ 6.0. To reproduce them byte-for-byte, each function is transcribed from its original machine code into a __declspec(naked) MSVC6 inline-assembly function. The compiler emits the transcribed instructions directly; no C++ code generation is involved.

Every func_<VA>.c source is therefore both:

  • a readable, annotated record of the function's instructions and behavior, and
  • an input to a build that must reproduce the reference bytes exactly.

The result is an oracle: a source tree that compiles to the canonical binary. It is not an attempt to recover the original C++ source.

Source layout

Sources are organized by binary target and PE section, not by subsystem:

src/
  mw4mercs/
    text/        func_<VA>.c + in-.text data_<VA>.c
    rdata/       data_0x*.c section runs
    data/        data_0x*.c section runs
    rsrc/        data_0x*.c section runs
    txt2/        SafeDisc .txt2 leftover
    artifacts/   committed PE build artifacts + CSV metadata
  blade/         text/ rdata/ data/ artifacts/
  gundll/        text/ rdata/ data/ artifacts/
  language/      text/ rdata/ data/ artifacts/
  missionlang_mercs/  text/ rdata/ data/ artifacts/
  mw4stats/      text/ rdata/ data/ artifacts/
  zloglib/       text/ rdata/ data/ artifacts/

Filenames are canonical: func_<8-hex-va>.c and data_<8-hex-va>.c. Semantics live in the file header and inline comments, never in the filename. The per-binary subsystem indexes under docs/va_index/ provide the semantic map.

Non-.text sections (.rdata, .data, .rsrc, .txt2) are byte-exact data runs. The generated data_0x*.c sources are committed, so the default build does not need the reference. nmake data remains the optional regeneration path; see BUILD.md.

Building an image

The default EXE build has three stages:

  1. Compile .text sources — all src/mw4mercs/text/*.c are compiled with the pinned MSVC6 flags into build/obj/.
  2. Compile data sources — the committed src/mw4mercs/{rdata,data,rsrc,txt2}/data_0x*.c runs are compiled into build/obj/data/.
  3. Assemble — exe_image_assemble.py places every object at its reference RVA, resolves relocation sites, fills pads from the committed artifact, composes the sections, and writes build/MW4Mercs.exe. --diff compares the result to the reference when it is present.

The PE header, inter-function pad bytes, and residual extern table are committed artifacts. exe_image_assemble.py --init is the optional regeneration path that rebuilds them from a reference EXE.

Module DLLs use the same model with data_assemble.py and image_assemble.py; their data sources and .bin artifacts are committed. gundll's in-scope VA list is provided at reference/in_scope/gundll.txt for its coverage report.

Relocation resolution

An unlinked object's relocation slots contain placeholder values. The assembler resolves each slot from the symbol name:

  1. Address-embedded names (FUN_00401000, g_0076B000, ...).
  2. The committed src/<target>/artifacts/extern_addrs.csv table for residual symbols whose names carry no address.
  3. The import map parsed from the assembled .rdata for imported functions.

The committed extern table is authoritative. The source-comment fallback is legacy and must never drive the build; the assembler hard-errors if a symbol would resolve that way.

Verification model

There are four levels of checking:

  • Hash gate — compares the built image to the canonical SHA-256; this is the reference-free acceptance check.
  • Full-image byte diff — composes the complete binary and compares it to a reference binary when one is present. It catches ordering, padding, alignment, relocation, and section-composition errors.
  • Masked per-function diff — compares one compiled object against the reference function, masking 4-byte relocation sites.
  • No-mask per-function check — places the object at its VA, resolves every relocation, and compares every byte.

The hash gate is the default gate. The byte-level checks are optional diagnostics that require the reference binaries.

What is intentionally out of scope

  • The .txt2 SafeDisc leftover (preserved only as committed bytes for layout).
  • External DRM/SafeDisc components and the bundled DRM DLLs; the in-image wrapper is part of the platform surface.
  • External CRT and middleware code; CRT ranges inside a target module are part of that module's surface.
  • The four editor/config tools shipped with the game (documented under docs/editor_tools/, not oracle targets).
  • ScriptStrings_MERCS.dll (resource-only, no code).