Completes the weapon-side half of the look commit: - MECHWEAP: real rearFiring/viewFireEnable members (binary @0x334/@0x3E0) + accessors. Ctor resolves REAR-FIRING authentically (@004b99a8 tail): the mount SEGMENT site name is tested for the 'b' (back) marker -- only the back gun ports (sitelbgunport/siterbgunport) carry it. Spawn = forward-armed. - MECH CommitLookState: re-arms every roster weapon per view (forward = non-rear, LOOK-BACK = rear-mounted, side/down = none) and flips the HUD reticle pip group with the authentic Reticle::Front/RearFiringWeaponsOn flags (BT411's raw "bits 1/2" resolved to their 1995 names). - MECHWEAP: defined MechWeapon::MessageHandlers (0-entry set inheriting the Subsystem chain) -- it was declared-but-undefined, and the surviving CODE PPC.CPP binds the inherited name into PPC::DefaultData; the zero-filled common block was a latent NULL deref (same trap as Emitter::AttributeIndex). Discovery: the TEST.EGG mech genuinely mounts TWO REAR LASERS (ERMLaser_2/3 on the b-ports). Verified on a full-clean baseline (engine 181 + bt 42, 0 fail): look-behind arms exactly the rear lasers, disarms the other five, pipMask=0xfffffffe; neutral run holds forward-armed; zero Fail. build410.sh HARDENED against the stale-obj layout-skew trap this wave exposed: cc() skipped recompiles on obj-vs-.cpp timestamps only, so the MECHWEAP.HPP member additions left emitter.obj on the OLD MechWeapon layout -- its chargeLevel=0.0f write landed exactly on the new rearFiring (a silent cross-TU struct skew, heisenbug-grade). cc() now honors header stamps: a source410 engine-header edit invalidates every bucket, a BT/BT_L4 header edit additionally invalidates the game buckets. CODE/ is immutable, no stamp needed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
196 lines
10 KiB
Bash
196 lines
10 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
|
|
|
|
# Header-dependency stamps: cc() has no per-TU include tracking, so a header
|
|
# edit must invalidate every obj that could embed its layout (a stale obj with
|
|
# an old struct layout SILENTLY corrupts adjacent members at runtime -- the
|
|
# 2026-07-21 MechWeapon::rearFiring stomp). CODE/ is the immutable archive;
|
|
# only source410/ headers change. Engine-level headers (MUNGA/shim/override)
|
|
# are included by everything; game headers (BT/BT_L4) only by the game buckets.
|
|
hstamp() { # hstamp <dir...> -> newest header mtime (epoch secs, 0 if none)
|
|
local t
|
|
t=$(find "$@" -maxdepth 1 \( -iname '*.hpp' -o -iname '*.h' \) -printf '%T@\n' 2>/dev/null \
|
|
| sort -n | tail -1)
|
|
echo "${t%%.*}"
|
|
}
|
|
HS_ENGINE=$(hstamp "$S/MUNGA" "$S/MUNGA_L4" "$S/shim" "$S/override")
|
|
HS_GAME=$(hstamp "$S/BT" "$S/BT_L4")
|
|
[ -z "$HS_ENGINE" ] && HS_ENGINE=0
|
|
[ -z "$HS_GAME" ] && HS_GAME=0
|
|
|
|
# compile one TU into a bucket; skip only if the obj is newer than the source
|
|
# AND the relevant header stamps
|
|
cc() { # cc <bucket> <src>
|
|
local out="$B/obj/$1/$(basename "${2%.*}" | tr 'A-Z' 'a-z').obj"
|
|
if [ -f "$out" ] && [ "$out" -nt "$2" ]; then
|
|
local omt=$(stat -c %Y "$out")
|
|
local need=$HS_ENGINE
|
|
case "$1" in bt|btl4) [ "$HS_GAME" -gt "$need" ] && need=$HS_GAME;; esac
|
|
[ "$omt" -ge "$need" ] && return 0
|
|
fi
|
|
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
|