Replace the partial mechanics transcriptions with a separate, readable C11 reconstruction of the complete Win16 image while preserving the original raw Ghidra export as immutable evidence. Cover all ordinary and overlapping entry points, Borland runtime behavior, Win16 imports, segmented data, callbacks, resources, indirect control flow, physics, rendering, persistence, and startup/shutdown lifecycles. Add deterministic extraction and audit tooling plus address-linked ledgers for functions, imports, DGROUP ranges and objects, relocations, resources, and callbacks. The final gate records zero raw, partial, restored, unknown, blocked, or unclassified required units. Keep the semantic-fidelity boundary explicit: the portable C is not claimed to reproduce a byte-identical Borland NE build. Add strict focused harnesses for every reconstructed C unit, exact resource round-trip checks, and a 16-bit Borland Real48 reference probe. No Rust source or Cargo metadata is changed in this phase. Test Plan: - `bash original/tools/test_reconstructed_c.sh` -- passed - `bash original/tools/probe_real48_reference.sh` -- passed bit-for-bit - `python3 original/tools/audit_reconstruction.py --require-complete` -- passed - `git diff --cached --check` -- passed - `git diff HEAD -- '*.rs' Cargo.toml Cargo.lock` -- empty
197 lines
7.1 KiB
Python
Executable File
197 lines
7.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Populate the import ledger from Wine 11.15 Win16 specs and target evidence."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import csv
|
|
import hashlib
|
|
import re
|
|
import urllib.request
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
LEDGER = ROOT / "IMPORT_RECONSTRUCTION.tsv"
|
|
WINE_TAG = "wine-11.15"
|
|
WINE_BASE = f"https://gitlab.winehq.org/wine/wine/-/raw/{WINE_TAG}"
|
|
SPEC_SOURCES = {
|
|
"USER": (
|
|
"dlls/user.exe16/user.exe16.spec",
|
|
"d873fa88079c4f922cbba2fb3a75f40e2ff23b4d56434b0f4326169397539e32",
|
|
),
|
|
"GDI": (
|
|
"dlls/gdi.exe16/gdi.exe16.spec",
|
|
"3bd84b26a9d4b7aa91a9114d0a46dc13e4d6fcd71290120afdd9b7cfaffe76ab",
|
|
),
|
|
"KERNEL": (
|
|
"dlls/krnl386.exe16/krnl386.exe16.spec",
|
|
"8d799b701d1cf16d4e882b5e354dc6e357056d4adcd83e629571dc4b33b02075",
|
|
),
|
|
"MMSYSTEM": (
|
|
"dlls/mmsystem.dll16/mmsystem.dll16.spec",
|
|
"31e54179db61fa95a1d90a9a230d2ae5afb6013ba37ae54e78990d1795694a73",
|
|
),
|
|
"KEYBOARD": (
|
|
"dlls/keyboard.drv16/keyboard.drv16.spec",
|
|
"ca9a9e697e9afe0baf5cff7099ebb442169f36fa05293e78a0bb50fbefaa096d",
|
|
),
|
|
}
|
|
SPEC_FUNCTION = re.compile(
|
|
r"^\s*(?P<ordinal>\d+)\s+"
|
|
r"(?P<kind>pascal|cdecl|varargs|stub|register)"
|
|
r"(?P<flags>(?:\s+-\S+)*)\s+"
|
|
r"(?P<name>[A-Za-z0-9_@]+)\((?P<arguments>[^)]*)\)"
|
|
)
|
|
SPEC_EQUATE = re.compile(
|
|
r"^\s*(?P<ordinal>\d+)\s+equate\s+(?P<name>[A-Za-z0-9_@]+)\s+(?P<value>\S+)"
|
|
)
|
|
|
|
|
|
def fetch_specs() -> dict[str, tuple[str, list[str]]]:
|
|
specs = {}
|
|
for library, (path, expected_digest) in SPEC_SOURCES.items():
|
|
url = f"{WINE_BASE}/{path}"
|
|
with urllib.request.urlopen(url, timeout=30) as response:
|
|
payload = response.read()
|
|
digest = hashlib.sha256(payload).hexdigest()
|
|
if digest != expected_digest:
|
|
raise RuntimeError(
|
|
f"Wine source drift for {path}: expected {expected_digest}, got {digest}"
|
|
)
|
|
specs[library] = (url, payload.decode("utf-8").splitlines())
|
|
return specs
|
|
|
|
|
|
def parse_specs(
|
|
specs: dict[str, tuple[str, list[str]]],
|
|
) -> dict[tuple[str, str], dict[str, str]]:
|
|
declarations = {}
|
|
for library, (url, lines) in specs.items():
|
|
for line_number, line in enumerate(lines, 1):
|
|
match = SPEC_FUNCTION.match(line)
|
|
if match:
|
|
name = match.group("name")
|
|
flags = match.group("flags").strip()
|
|
kind = match.group("kind")
|
|
prefix = f"{kind}16"
|
|
if flags:
|
|
prefix += f" {flags}"
|
|
declaration = f"{prefix} {name}({match.group('arguments').strip()})"
|
|
declarations[(library, name.upper())] = {
|
|
"prototype": declaration,
|
|
"evidence": f"Wine {WINE_TAG} {url}#L{line_number}",
|
|
"notes": (
|
|
f"Wine Win16 ordinal {match.group('ordinal')}; "
|
|
"every TDKPIN call site still requires argument-level review."
|
|
),
|
|
"status": "partial",
|
|
"stage": "prototype-sourced",
|
|
}
|
|
continue
|
|
match = SPEC_EQUATE.match(line)
|
|
if match:
|
|
name = match.group("name")
|
|
declarations[(library, name.upper())] = {
|
|
"prototype": f"equate16 {name} = {match.group('value')}",
|
|
"evidence": f"Wine {WINE_TAG} {url}#L{line_number}",
|
|
"notes": (
|
|
f"Wine Win16 ordinal {match.group('ordinal')}; this is runtime data, "
|
|
"not a callable function."
|
|
),
|
|
"status": "verified",
|
|
"stage": "runtime-data",
|
|
}
|
|
return declarations
|
|
|
|
|
|
def recover_special_declarations(
|
|
declarations: dict[tuple[str, str], dict[str, str]],
|
|
) -> None:
|
|
keyboard_url = f"{WINE_BASE}/{SPEC_SOURCES['KEYBOARD'][0]}"
|
|
declarations[("KEYBOARD", "ORDINAL_5")] = {
|
|
"prototype": "pascal16 -ret16 AnsiToOem(str ptr)",
|
|
"evidence": f"Wine {WINE_TAG} {keyboard_url}#L5",
|
|
"notes": "Imported by ordinal; Wine identifies ordinal 5 as AnsiToOem.",
|
|
"status": "partial",
|
|
"stage": "prototype-sourced",
|
|
}
|
|
declarations[("KEYBOARD", "ORDINAL_6")] = {
|
|
"prototype": "pascal16 -ret16 OemToAnsi(str ptr)",
|
|
"evidence": f"Wine {WINE_TAG} {keyboard_url}#L6",
|
|
"notes": "Imported by ordinal; Wine identifies ordinal 6 as OemToAnsi.",
|
|
"status": "partial",
|
|
"stage": "prototype-sourced",
|
|
}
|
|
declarations[("MMTIMER", "SYSTEMTIMERMAKE")] = {
|
|
"prototype": (
|
|
"pascal16 -ret16 SystemTimerMake("
|
|
"HWND16 recipient, word delay_ms, word resolution_ms, word one_shot)"
|
|
),
|
|
"evidence": (
|
|
"MMTIMER.DLL 1000:0039 disassembly; export ordinal 1; "
|
|
"f1d9ac980c7bfba5dc53eaa9e7cb2c3cd9b82f879ee8962ad40bf863d641bb49"
|
|
),
|
|
"notes": (
|
|
"Binary-reviewed wrapper around timeSetEvent16: it installs MMTIMER 1000:0002, "
|
|
"posts message 0x0580 to recipient, and returns the multimedia timer id."
|
|
),
|
|
"status": "verified",
|
|
"stage": "binary-reviewed",
|
|
}
|
|
|
|
|
|
def main() -> int:
|
|
with LEDGER.open(newline="", encoding="utf-8") as handle:
|
|
reader = csv.DictReader(handle, delimiter="\t")
|
|
columns = list(reader.fieldnames or ())
|
|
rows = list(reader)
|
|
if "stage" not in columns:
|
|
columns.insert(columns.index("status") + 1, "stage")
|
|
|
|
declarations = parse_specs(fetch_specs())
|
|
recover_special_declarations(declarations)
|
|
import_keys = {(row["library"], row["import_name"].upper()) for row in rows}
|
|
missing = sorted(import_keys - set(declarations))
|
|
if missing:
|
|
raise RuntimeError(f"no Win16 declaration for: {missing}")
|
|
|
|
for row in rows:
|
|
recovered = declarations[(row["library"], row["import_name"].upper())]
|
|
preserve_review = row.get("stage") not in {
|
|
"",
|
|
"untyped-import",
|
|
"prototype-sourced",
|
|
"runtime-data",
|
|
"binary-reviewed",
|
|
}
|
|
preserved = (
|
|
{key: row.get(key, "") for key in ("status", "stage", "evidence", "notes")}
|
|
if preserve_review
|
|
else None
|
|
)
|
|
row.update(recovered)
|
|
if preserved:
|
|
row.update(preserved)
|
|
|
|
temporary = LEDGER.with_suffix(LEDGER.suffix + ".tmp")
|
|
with temporary.open("w", newline="", encoding="utf-8") as handle:
|
|
writer = csv.DictWriter(
|
|
handle, fieldnames=columns, delimiter="\t", lineterminator="\n"
|
|
)
|
|
writer.writeheader()
|
|
writer.writerows(rows)
|
|
temporary.replace(LEDGER)
|
|
|
|
statuses = {}
|
|
for row in rows:
|
|
statuses[row["status"]] = statuses.get(row["status"], 0) + 1
|
|
print(f"updated_import_slots={len(rows)}")
|
|
print(
|
|
f"unique_declarations={len({(row['library'], row['import_name']) for row in rows})}"
|
|
)
|
|
print("statuses=" + ",".join(f"{key}:{statuses[key]}" for key in sorted(statuses)))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|