Add a reproducible dump of all 175 initialized collision and rule records from the unmodified traceable Wine process. Preserve geometry, bounds, activation, all four decoded Borland Real48 response values, flags, contact state, score, layer mask, and render rectangle in a reviewable TSV. This extends the static function ledger with the runtime-built data table needed to replace inferred target, lock, gate, and mechanism logic in Rust. Test Plan: - generated `OBJECTS.tsv` contains one header plus 175 records - repeat dump compared byte-for-byte equal with `cmp` - `ruff check` on all original tracing tools -- passed - `git diff --cached --check` -- passed
139 lines
4.3 KiB
Python
Executable File
139 lines
4.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Dump the initialized 175-record collision/rule table from TDKPIN under Wine."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import csv
|
|
import math
|
|
import struct
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from trace_original_state import (
|
|
ProcessMemory,
|
|
find_winevdm_pid,
|
|
locate_data_segment,
|
|
verify_original,
|
|
)
|
|
|
|
OBJECT_COUNT = 175
|
|
OBJECT_STRIDE = 0x53
|
|
OBJECT_FIELD_BASE = 0x095B
|
|
DATA_SIZE = 0x4D3A
|
|
|
|
|
|
def decode_real48(data: bytes) -> float:
|
|
exponent = data[0]
|
|
if exponent == 0:
|
|
return 0.0
|
|
mantissa = int.from_bytes(data[1:], "little")
|
|
sign = -1.0 if mantissa & (1 << 39) else 1.0
|
|
fraction = mantissa & ((1 << 39) - 1)
|
|
return sign * math.ldexp(1.0 + fraction / (1 << 39), exponent - 129)
|
|
|
|
|
|
def real_text(value: float) -> str:
|
|
return format(value, ".12g")
|
|
|
|
|
|
def u16_at(data: bytes, offset: int) -> int:
|
|
return struct.unpack_from("<H", data, offset)[0]
|
|
|
|
|
|
def i32_at(data: bytes, offset: int) -> int:
|
|
return struct.unpack_from("<i", data, offset)[0]
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--pid", type=int, help="Linux PID of the TDKPIN winevdm.exe process")
|
|
parser.add_argument("--output", type=Path, help="TSV path; stdout when omitted")
|
|
return parser.parse_args()
|
|
|
|
|
|
FIELDS = [
|
|
"id",
|
|
"type",
|
|
"subtype",
|
|
"active",
|
|
"bounds_min_x_milli",
|
|
"bounds_min_y_milli",
|
|
"bounds_max_x_milli",
|
|
"bounds_max_y_milli",
|
|
"point1_x_milli",
|
|
"point1_y_milli",
|
|
"point2_x_milli",
|
|
"point2_y_milli",
|
|
"radius_milli",
|
|
"response_normal",
|
|
"response_tangent",
|
|
"response_auxiliary",
|
|
"response_kick",
|
|
"flags",
|
|
"contact_state",
|
|
"score_low",
|
|
"score_high",
|
|
"layer_mask",
|
|
"render_left",
|
|
"render_top",
|
|
"render_right",
|
|
"render_bottom",
|
|
]
|
|
|
|
|
|
def main() -> None:
|
|
args = parse_args()
|
|
verify_original()
|
|
pid = args.pid if args.pid is not None else find_winevdm_pid()
|
|
output = args.output.open("w", newline="", encoding="utf-8") if args.output else sys.stdout
|
|
try:
|
|
writer = csv.writer(output, delimiter="\t", lineterminator="\n")
|
|
writer.writerow(FIELDS)
|
|
with ProcessMemory(pid) as memory:
|
|
data_base = locate_data_segment(memory)
|
|
data = memory.read(data_base, DATA_SIZE)
|
|
for object_id in range(1, OBJECT_COUNT + 1):
|
|
base = object_id * OBJECT_STRIDE
|
|
writer.writerow(
|
|
(
|
|
object_id,
|
|
data[base + OBJECT_FIELD_BASE],
|
|
data[base + 0x095C],
|
|
data[base + 0x098F],
|
|
i32_at(data, base + 0x095D),
|
|
i32_at(data, base + 0x0961),
|
|
i32_at(data, base + 0x0965),
|
|
i32_at(data, base + 0x0969),
|
|
i32_at(data, base + 0x096D),
|
|
i32_at(data, base + 0x0971),
|
|
i32_at(data, base + 0x0975),
|
|
i32_at(data, base + 0x0979),
|
|
real_text(decode_real48(data[base + 0x097D : base + 0x0983])),
|
|
real_text(decode_real48(data[base + 0x0983 : base + 0x0989])),
|
|
real_text(decode_real48(data[base + 0x0989 : base + 0x098F])),
|
|
real_text(decode_real48(data[base + 0x0990 : base + 0x0996])),
|
|
real_text(decode_real48(data[base + 0x0996 : base + 0x099C])),
|
|
f"0x{u16_at(data, base + 0x099C):04x}",
|
|
u16_at(data, base + 0x099E),
|
|
u16_at(data, base + 0x09A0),
|
|
u16_at(data, base + 0x09A2),
|
|
f"0x{u16_at(data, base + 0x09A4):04x}",
|
|
u16_at(data, base + 0x09A6),
|
|
u16_at(data, base + 0x09A8),
|
|
u16_at(data, base + 0x09AA),
|
|
u16_at(data, base + 0x09AC),
|
|
)
|
|
)
|
|
print(f"dumped {OBJECT_COUNT} objects from TDKPIN pid={pid}", file=sys.stderr)
|
|
finally:
|
|
if output is not sys.stdout:
|
|
output.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except (OSError, RuntimeError) as error:
|
|
raise SystemExit(f"dump_original_objects.py: {error}") from error
|