Files
TeslaRel410/restoration/source410/build410.sh
T
CydandClaude Fable 5 5b35eb973c 4.10 reconstruction: BTL4OPT.EXE links clean and BOOTS to the first staged brick
The reconstructed tree now produces a runnable binary with the authentic
1995 toolchain (BC4.52 / tlink32 / DPMI32):

- BTL4.CPP main TU reconstructed from the 4.11 Ghidra decomp (FUN_0040109c)
  + the 4.10 binary's own string pool + surviving RPL4TOOL.CPP house style;
  probe_main.cpp scaffold retired (BTL4.NOTES.md documents every decoded call)
- L4NET.CPP staged: L4NetworkManager ctor/dtor + 10 vtable-pulled virtuals
  (standalone-benign ones no-op, network ones Fail loudly) + NetNub client
  globals (Net_Common_Ptr=NULL routes L4File to its plain-DOS path)
- build410.sh: libs now built in the AUTHENTIC makefile member order
  (MUNGA.MAK / mungal4.mak / BT.MAK / BTL4.MAK). Order is load-bearing:
  tlink emits static-init records in module pull order, and alphabetical
  order booted into a null-vptr crash (IcomManager::ClassDerivations
  constructing before parent NetworkClient::ClassDerivations). Also fixed
  stage_link to the proven 32-bit lib set (SOSDBXC+SOSMBXC, no WATTCPLG)
- BOXTREE.HPP MemoryBlock unify, BTL4GRND notify stubs, remaining engine
  backfills (audio/gauge/resource/stream TUs) that closed the deep ledger

Smoke test (DOSBox-X + 32RTM, copy of the pod BT tree, our exe swapped in):
  BattleTech v4.10
  BTL4Application::BTL4Application
  l4net.cpp(22): L4NetworkManager -- l4net.cpp not yet reconstructed
Static init, main, -egg parse, BTL4.RES load (version 1.0.6 check passes),
ApplicationManager and the BTL4Application ctor chain all execute real
reconstructed code; boot halts at the first staged Fail() as designed.
Next brick: the real l4net.cpp body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 19:05:53 -05:00

173 lines
9.2 KiB
Bash

#!/bin/bash
# build410.sh -- assemble BTL4OPT.EXE from the reconstructed 4.10 tree.
#
# Stages:
# 1. mass-compile the merged engine source (CODE/BT over CODE/RP over
# source410 backfills) into build410/obj/{munga,mungal4}/
# 2. compile the BT game TUs (CODE originals + source410) into
# build410/obj/{bt,btl4}/
# 3. tlib the four libs; prebuilt-1995 .obj fallbacks fill source gaps
# 4. tlink32 with the authentic OPT.MAK order; unresolved externals ->
# build410/UNRESOLVED.txt (the canonical remaining-work ledger)
#
# Usage: ./build410.sh [engine|bt|libs|link|all|status]
T=/c/VWE/TeslaRel410
TW=c:/VWE/TeslaRel410
export PATH="$T/BORLAND/BC45/BIN:$PATH"
C=$TW/CODE/BT
R=$TW/CODE/RP
S=$TW/restoration/source410
B=$T/restoration/build410
Bw=$TW/restoration/build410
INC="$S/override;$C/BT_L4;$C/BT;$C/MUNGA;$C/MUNGA_L4;$C/MUNGA_L4/SOS/BC4;$S/MUNGA;$S/MUNGA_L4;$R/MUNGA;$R/MUNGA_L4;$R/MUNGA_L4/NetNub;$R/MUNGA_L4/sos/bc4;$C/MUNGA_L4/LIBDPL;$C/MUNGA_L4/NETNUB;$C/MUNGA_L4/NETNUB/INCLUDE;$S/MUNGA_L4;$R/MUNGA_L4/libDPL;$R/MUNGA_L4/libDPL/dpl;$S/shim;$S/BT;$S/BT_L4;$TW/BORLAND/BC45/INCLUDE"
FLAGS="-c -DLBE4 -DDEBUG_LEVEL=0 -DDEBUG_STREAM=cout \
-b -r -ff -AT -k- -N- -v- -5 -a4 -V -Jg -x- -RT- -O2 -w-"
mkdir -p "$B"/obj/{munga,mungal4,bt,btl4} "$B"/lib
# compile one TU into a bucket; skip if the obj is newer than the source
cc() { # cc <bucket> <src>
local out="$B/obj/$1/$(basename "${2%.*}" | tr 'A-Z' 'a-z').obj"
[ -f "$out" ] && [ "$out" -nt "$2" ] && return 0
if bcc32 $FLAGS -I"$INC" -o"$(cygpath -w "$out" 2>/dev/null || echo $out)" "$2" \
> "$B/obj/$1/$(basename "${2%.*}").log" 2>&1; then
return 0
else
rm -f "$out"; return 1
fi
}
# merged engine source view: BT tree wins, RP fills, source410 backfills last
merged() { # merged <BTdir> <RPdir> [S410dir]
{ ls "$1"/*.CPP "$1"/*.cpp 2>/dev/null | sed 's/.*[\\/]//'
ls "$2"/*.CPP "$2"/*.cpp 2>/dev/null | sed 's/.*[\\/]//'
[ -n "$3" ] && ls "$3"/*.CPP 2>/dev/null | sed 's/.*[\\/]//'
} | tr 'A-Z' 'a-z' | sort -u
}
srcfor() { # srcfor <name.cpp> <BTdir> <RPdir> [S410dir]
case "$1" in
filestrm.cpp) echo "$3/FILESTRM.CPP"; return;; # BT pair = post-4.10 drift
l4vidper.cpp|l4tsdpl.cpp) return;; # not in mungal4.lib
filestub.cpp) return;; # stub.lib member (prebuilt obj)
esac
for d in "$4" "$2" "$3"; do # source410 overrides, then BT, then RP
[ -z "$d" ] && continue
for f in "$d"/*.CPP "$d"/*.cpp; do
[ -f "$f" ] || continue
[ "$(basename "$f" | tr 'A-Z' 'a-z')" = "$1" ] && { echo "$f"; return; }
done
done
}
stage_engine() {
local ok=0 fail=0 failed=""
for lib in munga mungal4; do
local bd rd sd
if [ $lib = munga ]; then bd=$C/MUNGA; rd=$R/MUNGA; sd=$S/MUNGA; else bd=$C/MUNGA_L4; rd=$R/MUNGA_L4; sd=$S/MUNGA_L4; fi
for n in $(merged "$bd" "$rd" "$sd"); do
src=$(srcfor "$n" "$bd" "$rd" "$sd")
[ -z "$src" ] && continue
if cc $lib "$src"; then ok=$((ok+1)); else fail=$((fail+1)); failed="$failed $lib/$n"; fi
done
done
echo "ENGINE: $ok ok, $fail fail"
for f in $failed; do echo " FAIL $f"; done | head -60
for f in $failed; do echo "$f"; done > "$B/ENGINE-FAILURES.txt"
}
stage_bt() {
local ok=0 fail=0
for n in $(merged "$C/BT" /dev/null "$S/BT"); do
src=$(srcfor "$n" "$C/BT" /dev/null "$S/BT")
[ -z "$src" ] && continue
if cc bt "$src"; then ok=$((ok+1)); else fail=$((fail+1)); echo " FAIL bt/$n"; fi
done
for n in $(merged "$C/BT_L4" /dev/null "$S/BT_L4"); do
src=$(srcfor "$n" "$C/BT_L4" /dev/null "$S/BT_L4")
[ -z "$src" ] && continue
if cc btl4 "$src"; then ok=$((ok+1)); else fail=$((fail+1)); echo " FAIL btl4/$n"; fi
done
echo "BT: $ok ok, $fail fail"
}
# Authentic 1995 lib member ORDER (MUNGA.MAK / mungal4.mak / BT.MAK / BTL4.MAK).
# Order is LOAD-BEARING: tlink emits static-init records in module pull order,
# and base-class statics (e.g. NetworkClient::ClassDerivations, network.obj)
# must construct before derived ones (IcomManager, icom.obj). Alphabetical
# order boots into a null-vptr crash in Derivation::Derivation.
ORDER_munga="heap cstr memblock memreg memstrm filestrm verify namelist notation fileutil color resource scalar rect2d random angle vector3d vector4d point3d unitvec rotation motion origin affnmtrx linmtrx matrix mtrxstk spline ray line sphere normal plane extntbox bndgbox boxtree boxlist boxsolid boxsort boxsphr boxcone boxramp boxiramp boxwedge boxdisks boxtile time vdata iterator link plug objstrm socket node slot chain sfeskt srtskt schain vchain tree table hash trace receiver event evtstat cmpnnt simulate subsystm joint network damage entity entityid nttmgr entity2 envirnmt mover segment jmover watcher host hostmgr console scnrole team exptbl caminst cammgr camship cammppr eyecandy player director dropzone explode terrain cultural doorfram door registry lattice intorgn collorgn interest collasst update reticle rndorgn renderer vidrend audio audent audtime audwgt audlvl audloc audcmp audsrc audmidi audseq audwthr audrend wrhous gaugmap gauge lamp gaugrend gaugalrm graph2d mode controls mission apptask app appmgr appmsg spooler icom maptool modtool animtool audtools tool"
ORDER_mungal4="l4trace l4file l4time netshare l4serial l4svga16 joystick pcspak l4host l4net l4dplmem l4vidrnd l4video startdpl sosmawe l4audhdw l4audio l4audlvl l4audwtr l4audres l4audrnd l4wrhous l4vb8 l4vb16 l4gauima l4grend l4gauge l4lamp l4plasma l4ctrl l4keybd l4mouse l4rio l4mppr l4app l4splr l4icom l4vidtul l4audtul l4gautul l4ctltul"
ORDER_bt="btmssn messmgr mechdmg dmgtable mech mech2 mech3 mech4 mechsub mechtech heat mechmppr powersub sensor gnrator gyro torso hud myomers mechweap emitter ppc projweap mislanch ammobin gauss projtile misthrst seeker missile btplayer btteam btdirect btreg btcnsl bttool"
ORDER_btl4="btl4mode btl4rdr btl4gaug btl4gau2 btl4gau3 btl4grnd btl4galm btl4vid btl4mppr btl4arnd btl4mssn btl4app btl4pb"
stage_libs() {
cd "$B/lib"
for lib in munga mungal4 bt btl4; do
rm -f $lib.lib
ls "$B/obj/$lib"/*.obj >/dev/null 2>&1 || continue
: > $lib.rsp
eval "order=\$ORDER_$lib"
added=" "
# authentic makefile order first: compiled obj, else prebuilt-1995 obj
for n in $order; do
if [ -f "$B/obj/$lib/$n.obj" ]; then
printf '+..\obj\%s\%s.obj &
' "$lib" "$n" >> $lib.rsp
added="$added$n "
else
pre=$(find $R/MUNGA/opt $R/MUNGA_L4 -iname "$n.obj" 2>/dev/null | head -1)
if [ -n "$pre" ]; then
printf '+%s &
' "$(cygpath -w "$pre")" >> $lib.rsp
added="$added$n "
fi
fi
done
# any extra compiled objs not in the 1995 list go last
for o in "$B/obj/$lib"/*.obj; do
n=$(basename "${o%.*}")
# btl4.obj (main) is linked bare, never a lib member (BTL4.MAK:70)
[ "$lib" = btl4 ] && [ "$n" = btl4 ] && continue
# l4tstall = the engine TEST main -- its own exe in 1995, never a lib member
[ "$n" = l4tstall ] && continue
case "$added" in *" $n "*) continue;; esac
printf '+..\obj\%s\%s.obj &
' "$lib" "$n" >> $lib.rsp
done
sed -i '$ s/ &$//' $lib.rsp
MSYS2_ARG_CONV_EXCL='*' tlib $lib.lib /P32 @$lib.rsp > $lib.tlib.log 2>&1
echo "$lib.lib: $(grep -c '^+' $lib.rsp) members ($(ls -la $lib.lib 2>/dev/null | awk '{print $5}') bytes)"
done
}
stage_link() {
cd "$B"
if [ -f obj/btl4/btl4.obj ]; then
MAIN=obj/btl4/btl4.obj
elif [ -f probe_main.cpp ]; then
bcc32 $FLAGS -I"$INC" -oprobe_main.obj probe_main.cpp > probe_main.log 2>&1 || { echo "probe main FAILED (probe_main.log)"; return 1; }
MAIN=probe_main.obj
fi
cp -f "$T/CODE/BT/32STUB.EXE" 32stub.exe 2>/dev/null
# single-line sections; SOSDBXC+SOSMBXC = the 32-bit Borland SOS pair
# (SOSMW* is 16-bit); WATTCPLG is the 16-bit NetNub TSR side -- excluded
cat > link.rsp <<EOF2
$(cygpath -w $C/C0X32.OBJ) $(cygpath -w "$B/$MAIN"), btl4opt.exe, btl4opt.map, lib\\munga.lib lib\\mungal4.lib $(cygpath -w $R/MUNGA_L4/libDPL/LIBDPL.LIB) $(cygpath -w $R/MUNGA_L4/sos/SOSDBXC.LIB) $(cygpath -w $R/MUNGA_L4/sos/SOSMBXC.LIB) lib\\bt.lib lib\\btl4.lib $(cygpath -w $R/DPMI32.LIB) $(cygpath -w $R/CW32.LIB)
EOF2
MSYS2_ARG_CONV_EXCL='*' tlink32 -Tpe -ax -m -L"$(cygpath -w $TW/BORLAND/BC45/LIB)" @link.rsp > link.log 2>&1
grep -i "unresolved" link.log | sed "s/^.*Error: //" | sort -u > UNRESOLVED.txt
echo "link exit: $? ; unresolved: $(wc -l < UNRESOLVED.txt) (build410/UNRESOLVED.txt); main=$MAIN"
ls -la btl4opt.exe 2>/dev/null
}
case "${1:-all}" in
engine) stage_engine;;
bt) stage_bt;;
libs) stage_libs;;
link) stage_link;;
status) for d in munga mungal4 bt btl4; do echo "$d: $(ls "$B/obj/$d"/*.obj 2>/dev/null | wc -l) objs"; done;;
all) stage_engine; stage_bt; stage_libs; stage_link;;
esac