Files
TeslaSuite/410console/battletech-port/BATTLETECH-PORT-SPEC.md
T
CydandClaude Fable 5 deaade4f72 Track the 410console hand-off docs; ignore the raw Mac archive
The BattleTech port spec, the Console 4.10 decompilation notes, the golden
reference eggs, and the PEF/rsrc analysis tools come into the repo. The .sit
archive and its extraction (Mac binaries, __MACOSX/.finf metadata) stay
untracked for now.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 22:43:32 -05:00

15 KiB
Raw Blame History

BattleTech → TeslaConsole port spec

Hand-off package for the TeslaSuite repo. Everything learned in the TeslaRel410 repo about the Mac 4.10 operator console and the path to add BattleTech support to the modernized TeslaConsole. Implement this over in TeslaSuite (that repo's Claude has the console's own memories and can build it). This folder is self-contained — the reference/ files travel with it.

Sibling doc: 410console/CONSOLE-4.10-DECOMP.md (full decompilation of the Mac Console 4.10 PowerPC binary). This spec assumes it.


0. The situation in one paragraph

The Mac Console 4.10 (decompiled — see the sibling doc) and the Windows TeslaConsole.exe (4.11.x, which TeslaSuite/Console/TeslaConsole is a .NET reconstruction of) are two generations of the same operator console. The Mac one drove both games — BattleTech and Red Planet. The modernized TeslaConsole implements only Red Planet (TeslaConsole.RedPlanet), and its shell is even hardcoded to it (TeslaConsoleForm: AppID != 0 → "Pod Not Running RP", status strings "RP Initalizing"…). The task: add a TeslaConsole.BattleTech game that mirrors TeslaConsole.RedPlanet, and de-hardcode the shell to pick the game by ApplicationID. The transport layer is game-agnostic and reused unchanged.


1. The shared spine (reused verbatim by BattleTech — do NOT re-implement)

Both games and both console generations use one command/control path:

  • Munga over TCP 1501, one MungaSocket per pod (Munga Net.dll, vendored in Console/lib/). Console 200.0.0.1, pods 200.0.0.11….
  • MungaGame.cs (in TeslaConsole, game-agnostic): connect pod:1501, poll StateQueryMessageStateResponseMessage(ApplicationID, ApplicationState) every ~1 s, and on ApplicationState.WaitingForEgg stream the egg as 1000-byte EggFileMessage[] chunks, await AcknowledgeEggFileMessage, then run.
  • ApplicationState (the named "cockpit states"): CreatingMission, LaunchingMission, LoadingMission, ResumingMission, RunningMission, SuspendingMission, WaitingForLaunch, WaitingForEgg, …. In the Mac 4.10 console these were the numeric Cockpit States 012 (decomp §6.2).
  • HostType (TeslaConsole/HostType.cs): GameMachineHostType=0, MissionReviewHostType=2 (camera/spectator), ConsoleHostType=3.
  • Reused unchanged by BT: MungaGame, Pod, PodManager, Site, SitePanel, PlasmaBitmaps, PlasmaFontTool, the ordinals blocks, and the whole provisioning/launch path (that's the separate RPC channel on TCP 53290; game control is Munga 1501).

Only the egg content and the per-game UI/data differ between RP and BT.


2. The BattleTech egg format (authoritative)

Reconstructed from the console-generated reference/cavern.egg (freeforall, map cavern, night; pilot cyd in a madcat, veteran) and reference/TESTARN.EGG. This is what BTMission.ToEggString() must produce.

Structure (section by section)

[mission] adventure=BattleTech map=cavern scenario=freeforall time=night
          weather=clear temperature=27 length=120
[ordinals]                       ← 1st4th place plasma bitmaps, 128×32, IDENTICAL to RP
   bitmap=Ordinal::BitMap::1 … [Ordinal::BitMap::1] …32 rows… x=128 y=32 width=8
   (…::2 ::3 ::4)
[pilots]
   pilot=200.0.0.113             ← one per player + one per camera
[200.0.0.113]                    ← one participant block per pilot line
   hostType=0 advancedDamage=1 loadzones=1 name=cyd bitmapindex=1
   experience=veteran vehicle=madcat vehicleValue=0 badge=VGL
   dropzone=one color=Grey patch=Red role=Role::Default
[largebitmap]
   bitmap=BitMap::Large::cyd
[BitMap::Large::cyd] …32 rows… x=128 y=32 width=8   ← pilot-name plasma bitmap
[smallbitmap]
   bitmap=BitMap::Small::cyd
[BitMap::Small::cyd] …16 rows… x=64  y=16 width=4
[Role::Default] model=dfltrole   ← one block per referenced role

BattleTech vs Red Planet — the field diff

RPMission.ToEggString() / RPPlayer.AppendParticipantSpecificData() is the template. BT differs as follows:

Field Red Planet BattleTech
[mission] adventure= Red Planet BattleTech
[mission] temperature= 0 nonzero (e.g. 27)
[mission] compression= present (0/1) absent
participant advancedDamage= present (1) advanced damage model
participant experience= present — pilot skill (novice/veteran/expert)
participant vehicleValue= present (0)
participant patch= present (e.g. Red)
participant role= presentRole::<key>, needs a [Role::<key>] block
participant vehicle= RP racer (quark/bug…) a mech (madcat/…)
[Role::*] blocks presentmodel=<rolemodel> (Defaultdfltrole; TESTARN.EGG also has NoReturn)
ordinals, largebitmap, smallbitmap present identical — reuse as-is

Shared participant fields (same as RP): hostType, loadzones, name, bitmapindex, dropzone, color, badge.

Wire encoding (mirror RP exactly)

RPMission builds the string with \n between every key=value, then ToEggFileMessages() does Replace("\r\n","\0").Replace('\n','\0'), ASCII- encodes, and chunks into 1000-byte EggFileMessage(index, totalLen, thisLen, buf). So on the wire the egg is NUL-delimited. cavern.egg on disk is the same NUL-delimited blob (renders as run-together fields). BT reuses this verbatim — only ToEggString() content changes.

Section-order caveat (don't chase a false byte-match)

cavern.egg was written by the original console; its section order (ordinals early, bitmap defs inlined) differs from the modernized RPMission.ToEggString order (bitmaps listed then defined, ordinals last). The pod parses the egg INI-style by section name, so order is almost certainly non-critical. Match RP's builder structure (it's known-good against real pods) rather than byte-matching cavern.egg; use cavern.egg as the field/value reference and verify a generated egg on a real BT pod. (If a byte-exact golden test is wanted, generate from BT classes and pin that, like the existing DiffTests do.)


3. The RedPlanet template → BattleTech counterparts

Mirror Console/TeslaConsole.RedPlanet/ into Console/TeslaConsole.BattleTech/. The .csproj is SDK-style (globs **/*.cs), so new files under a new folder are picked up automatically; add Content Include lines for the BT XML (see §5).

RedPlanet file BattleTech counterpart Notes
RPParticipant.cs (abstract) BTParticipant [podIP] + hostType; abstract AppendParticipantSpecificData
RPPlayer.cs (abstract) BTPlayer adds the BT participant fields (§2 diff); HostType.GameMachine
RPRacePlayer / RPFootballPlayer BTPlayer concretes per scenario e.g. BTFreeForAllPlayer, BTTeamPlayer
RPCamera.cs BTCamera HostType.MissionReview, vehicle=camera
RPMission.cs (abstract) BTMission ToEggString() per §2; ToEggFileMessages() reusable as-is; emit [Role::*] blocks for referenced roles
RPRaceMission / RPFootballMission BTFreeForAllMission / BTTeamMission AppendMissionSpecificData for scenario extras
Scenario.cs + FootballScenario/DeathRaceScenario Scenario + BT scenarios loaded from BTConfig.xml; <invalid> filters valid maps/vehicles
RPConfig.cs + RedPlanet/RPConfig.xml BTConfig.cs + BattleTech/BTConfig.xml the data catalog (§5)
RPDefaults.cs / RPDefaultsDialog.cs BTDefaults / BTDefaultsDialog operator defaults UI
RPMap.cs / RPVehicle.cs BTMap (arena) / BTVehicle (mech) key/name/image; images under `images/battletech maps
RPTeam.cs BTTeam (if BT team scenario)
MissionEvent + Scored/Killed/Damaged/Boost/ScoreUpdate BT MissionEvent + Killed/Damaged/… BT has DamageMatrix + KillMarker (decomp §5 string lists); RP-only Boost/football events drop; confirm BT's set against the pod's Munga event messages
RPMissionResults.cs / RPMissionRecorder.cs / RPPrintDocument.cs BT* .rpm-style saved results + printout
RPGame.cs (the DockContent pane, ~66 KB) BTGame the big one: mission-config UI + the run/stop state loop driving each pod's MungaGame
TeslaConsole/RPStrings.cs + RedPlanet/RPStrings.xml BTStrings localized strings

RPGame's engine (worth copying closely): a per-pod GameStateData {EggMessageSent, RunMessageSent, AbortMessageSent, StopMessageSent, PodAppearsReset}, an RPGameState machine, sStopToIdleStates (the ApplicationStates that mean "busy, must stop first"), mEggFileMessages, mMissionRecorder, mMissionPlayers (index→player, to decode incoming events), mLastMissionResults. BT reuses this shape unchanged.


4. Shell de-hardcoding (TeslaConsole)

The shell currently assumes RP. To support both games:

  • TeslaConsoleForm.cs ~line 139: if (pod.MungaGame.AppID.Value != 0) → "Pod Not Running RP". ApplicationID 0 = Red Planet. Generalize: map the pod's reported ApplicationID to a game (RP vs BT) and pick the right game module + status strings ("RP Initalizing" → per-game). OPEN: the ApplicationID value for BattleTech — see §6.
  • Game selection where a game pane is created (Site/SitePanel/SiteManagement — how RPGame is instantiated today): allow creating a BTGame for BT pods.
  • Per-game status string table instead of the inline "RP …" literals.

5. The BattleTech catalog (BTConfig.xml) — seed + sources

Mirror reference/RPConfig.xml.template: a root element with per-scenario blocks carrying scenario-specific option lists, plus top-level <map>/<vehicle>/ <weather>/<time> catalogs and <invalid> exclusions. For BT:

<BattleTech>
  <freeforall name="Free For All">
     <experience key="novice"  name="Novice"/>
     <experience key="veteran" name="Veteran"/>
     <experience key="expert"  name="Expert"/>
     <color  key="Grey" name="Grey"/><badge  key="VGL"  name="VGL"/><patch  key="Red"  name="Red"/><role   key="Default"  model="dfltrole"/>
     <role   key="NoReturn" model="…"/>
  </freeforall>
  <!-- team/other scenarios from the Console.ini BT tree -->
  <map     key="cavern" name="Cavern" image="images/battletech maps/Cavern.bmp"/>
  <map     key="arena1" name="…"/>
  <vehicle key="madcat" name="Mad Cat" image="images/battletech vehicles/Mad Cat.bmp"/>
  <vehicle key="loki"   name="Loki"/><time    key="day"  name="Day"/> <time key="night" name="Night"/>
  <weather key="clear" name="Clear"/></BattleTech>

Known values (from this repo's eggs + reversing this session):

  • scenario: freeforall (confirmed). More in the Console.ini BT tree.
  • map (arena): cavern, arena1 (confirmed). Full list: Console.ini BT LocationList + game maps.
  • vehicle (mech): madcat (Mad Cat). Others seen in this repo's mech data: loki, thor, vulture, bhk1 (Black Hawk); OWN/PGN skeletons exist too. (Torso-twist limits found this session: MadCat ±130°, Loki/Thor 110°, Vulture 90°; some skeletons — BLH/OWN/PGN — have no torso joint.)
  • experience: veteran (confirmed); novice/expert are the other BTL4 pilot levels.
  • color: Grey (confirmed). badge: VGL. patch: Red.
  • role: Default (model=dfltrole), NoReturn (from TESTARN.EGG).
  • time: day, night. weather: clear. temperature: numeric (27). length: seconds (120).

Where to get the exhaustive catalog (in this repo):

  • 410console/4_10-console-extracted/Console.ini — the Mac console's full [Adventure::…::BattleTech] → LocationList → scenario/vehicle tree (minified; parse by ::-namespaced sections; decomp §8.2 shows the RP tree shape).
  • ALPHA_1/REL410/BT/ — the game itself: GAUGE/L4GAUGE.CFG, *.RES resources, *.SKL skeletons (mech list), and the .egg files.
  • CODE/BT/ — BattleTech game source (mech/weapon/damage classes; the DamageMatrix/KillMarker semantics for the event model).

6. Open questions — resolve in the TeslaSuite repo

  1. ApplicationID for BattleTech. RP is 0. A BT pod (running BTL4OPT) reports its own value in StateResponseMessage.Application. Get it by decompiling Console/lib/Munga Net.dll's ApplicationID enum, or by reading a live BT pod's state response. Needed for §4 shell game-selection.
  2. Full BT scenario list + per-scenario <invalid> maps/vehicles — from the Console.ini BT tree.
  3. BT in-match event messages. RP receives Scored/Killed/Damaged/Boost/ ScoreUpdate MungaMessages during a game. Confirm BT's set (BT models DamageMatrix + KillMarker) against Munga Net.dll / a live BT match.
  4. Image assets for BT mechs/arenas (RP ships images/red planet …; BT needs images/battletech …). Sourceable from the game .RES/gauge art or recreated.
  5. Whether BT reuses RP's exact plasma bitmaps/ordinalscavern.egg shows YES (identical [ordinals] + [BitMap::Large|Small::*] mechanism, same 128×32 / 64×16). Reuse PlasmaBitmaps/PlasmaFontTool unchanged.

7. Verification

  • Golden-egg check: construct the cavern.egg scenario in BT classes (freeforall/cavern/night/clear/27/120, pilot cyd/madcat/veteran/VGL/ Grey/Red/Role::Default) and diff ToEggString() field-by-field against reference/cavern.egg. Values must match; section order may not (see §2 caveat).
  • Live check: feed a generated egg to a real BT pod over Munga 1501 and drive the state machine WaitingForEgg → … → RunningMission.
  • Add BT cases to Console/tests/TeslaConsole.DiffTests alongside the RP ones.

8. reference/ contents (travels with this spec)

  • cavern.egg — console-generated BattleTech egg (the golden field reference).
  • TESTARN.EGG — 2nd BT egg (shows multiple [Role::*] blocks).
  • RPConfig.xml.template — the RP catalog XML to mirror as BTConfig.xml.
  • L4PLASMA.HPP — the pod plasma-display class (128×32) the egg bitmaps target.

9. The plasma tie-in (context)

The console authors the pod's plasma-scoreboard graphics: the egg's [BitMap::Large::<pilot>] (128×32) / [BitMap::Small::<pilot>] (64×16) and the [ordinals] 1st4th place bitmaps are rendered on the pod's plasma display (L4PLASMA.HPP: plasmaWidth=128, plasmaHeight=32, serial out on DOS COM2). So console → egg → pod plasma panel. PlasmaBitmaps/PlasmaFontTool in TeslaConsole generate them; BT reuses both. (In TeslaRel410 the pod side of this is emulated — see that repo's plasma-display notes.)