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>
15 KiB
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 MacConsole 4.10PowerPC 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
MungaSocketper pod (Munga Net.dll, vendored inConsole/lib/). Console200.0.0.1, pods200.0.0.11…. MungaGame.cs(inTeslaConsole, game-agnostic): connect pod:1501, pollStateQueryMessage→StateResponseMessage(ApplicationID, ApplicationState)every ~1 s, and onApplicationState.WaitingForEggstream the egg as 1000-byteEggFileMessage[]chunks, awaitAcknowledgeEggFileMessage, 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 0–12 (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] ← 1st–4th 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= |
— | present — Role::<key>, needs a [Role::<key>] block |
participant vehicle= |
RP racer (quark/bug…) | a mech (madcat/…) |
[Role::*] blocks |
— | present — model=<rolemodel> (Default→dfltrole; 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 reportedApplicationIDto a game (RP vs BT) and pick the right game module + status strings ("RP Initalizing" → per-game). OPEN: theApplicationIDvalue for BattleTech — see §6.- Game selection where a game pane is created (Site/SitePanel/SiteManagement — how
RPGameis instantiated today): allow creating aBTGamefor 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/expertare 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,*.RESresources,*.SKLskeletons (mech list), and the.eggfiles.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
ApplicationIDfor BattleTech. RP is0. A BT pod (runningBTL4OPT) reports its own value inStateResponseMessage.Application. Get it by decompilingConsole/lib/Munga Net.dll'sApplicationIDenum, or by reading a live BT pod's state response. Needed for §4 shell game-selection.- Full BT scenario list + per-scenario
<invalid>maps/vehicles — from the Console.ini BT tree. - BT in-match event messages. RP receives
Scored/Killed/Damaged/Boost/ ScoreUpdateMungaMessages during a game. Confirm BT's set (BT models DamageMatrix + KillMarker) againstMunga Net.dll/ a live BT match. - Image assets for BT mechs/arenas (RP ships
images/red planet …; BT needsimages/battletech …). Sourceable from the game.RES/gauge art or recreated. - Whether BT reuses RP's exact plasma bitmaps/ordinals —
cavern.eggshows YES (identical[ordinals]+[BitMap::Large|Small::*]mechanism, same 128×32 / 64×16). ReusePlasmaBitmaps/PlasmaFontToolunchanged.
7. Verification
- Golden-egg check: construct the
cavern.eggscenario in BT classes (freeforall/cavern/night/clear/27/120, pilotcyd/madcat/veteran/VGL/Grey/Red/Role::Default) and diffToEggString()field-by-field againstreference/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.DiffTestsalongside 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 asBTConfig.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] 1st–4th 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.)