Phase 3f: lighting from wire normals; defuse shipped-in debug crash
Lighting: stride-8/9 vertices carry a normal (floats 3-5, uv at 6-7). The backend transforms normals by the instance rotation and lights with a single directional sun (GL_LIGHT0, smooth, two-sided, GL_COLOR_MATERIAL). Terrain and hulls shade instead of reading flat. LOD: the lod flush has no switch distances -- the host keeps the active LOD at the child-list head and re-orders over the wire, so children[0] is right and LOD changes come through as the sim runs. The crash that halted the game once the RIO drove the sim was not ours: fault at 0047E1D1 writing 0xFFFFFFFF is the RIO driver's DISABLE_AND_DIE debug macro (PCSPAK.ASM, DIE_ON_ERROR=1), which deliberately faults on a serial-tx anomaly -- error 3 = body char >0x7F during a RIO packet, i.e. a glitch on a board reset. Release build (DIE_ON_ERROR=0) compiles it out and recovers (and al,07Fh). patch_btl4opt.py NOPs all 12 sites by signature (with .orig backup) = release behavior; game then survives RIO resets (1200+ frames). (ALPHA_1 is git-ignored, so the tool ships, not the patched binary.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Neutralize the shipped-in debug crash macros in BTL4OPT.EXE.
|
||||
|
||||
The Rel 4.10 BattleTech binary was built with the RIO serial driver's
|
||||
`DIE_ON_ERROR equ 1` diagnostic enabled (CODE/RP/MUNGA_L4/PCSPAK.ASM). Its
|
||||
`DISABLE_AND_DIE` macro deliberately faults ("crash loudly to the debugger")
|
||||
on any serial-transmit anomaly:
|
||||
|
||||
push eax; push edx
|
||||
mov edx, 0FFFFFFFFh ; crash loudly
|
||||
mov eax, <error-code>
|
||||
mov [edx], eax ; write to 0xFFFFFFFF -> Exception 0E
|
||||
|
||||
On the real pod a clean cockpit harness never tripped these; through the
|
||||
DOSBox-X serial passthrough a RIO glitch (e.g. during a board reset) hits
|
||||
error 3 (PCSPAK.ASM:1630, "body char > 0x7F") and halts the whole game. The
|
||||
release build (`DIE_ON_ERROR equ 0`) compiles the macro to nothing and lets
|
||||
the normal recovery path (`and al,07Fh`) continue.
|
||||
|
||||
This tool finds every DISABLE_AND_DIE site by its exact 14-byte signature and
|
||||
replaces it with NOPs -- exactly the release-build behavior -- writing a
|
||||
<name>.orig backup first. Idempotent.
|
||||
|
||||
Usage: patch_btl4opt.py <path-to-BTL4OPT.EXE>
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
|
||||
SIG = re.compile(
|
||||
rb'\x50\x52\xBA\xFF\xFF\xFF\xFF\xB8(.)\x00\x00\x00\x89\x02', re.DOTALL)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
raise SystemExit(__doc__)
|
||||
path = sys.argv[1]
|
||||
data = bytearray(open(path, 'rb').read())
|
||||
sites = list(SIG.finditer(bytes(data)))
|
||||
if not sites:
|
||||
print("no DISABLE_AND_DIE sites found (already patched?)")
|
||||
return
|
||||
import os
|
||||
bak = path + '.orig'
|
||||
if not os.path.exists(bak):
|
||||
open(bak, 'wb').write(data)
|
||||
print(f"backup written: {bak}")
|
||||
for m in sites:
|
||||
code = m.group(1)[0]
|
||||
va = 0x401000 + m.start() - 0xe00 # CODE VA 0x401000 @ file 0xe00
|
||||
print(f" NOP VA 0x{va:06x} (error code {code})")
|
||||
data[m.start():m.end()] = b'\x90' * (m.end() - m.start())
|
||||
open(path, 'wb').write(data)
|
||||
print(f"patched {len(sites)} sites")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user