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>
555 lines
28 KiB
Markdown
555 lines
28 KiB
Markdown
# Console 4.10 — Decompilation & Documentation
|
||
|
||
Reverse-engineering notes for the classic Mac OS **operator console** that
|
||
commanded the networked Virtual World Entertainment (VWE) BattleTech / Red
|
||
Planet pods. Part of the VWE Tesla restoration project.
|
||
|
||
- **Target dir:** `c:\VWE\TeslaRel410\410console\4_10-console-extracted\`
|
||
- **Binary:** `Console 4.10` (728,064 bytes) — classic Mac OS **fat application**
|
||
(68K `CODE` resources **+** PowerPC **PEF/CFM** fragment in the data fork).
|
||
- **Internal identity:** type `APPL`, creator **`vwe4`**, version resource
|
||
**`2.4.0b3` / "Console 2.4.0b3", © 1995–1997 FASA Interactive, Inc.**
|
||
(The "4.10" in the folder name is the site/config release, not the app's own
|
||
`vers`.)
|
||
- **Analysis method:** custom Python PEF/PPC + resource-fork tooling (no Ghidra
|
||
on this machine); capstone in PPC big-endian mode. See `410console/tools/`.
|
||
- **Status:** read-only analysis. Nothing in the target was modified.
|
||
|
||
---
|
||
|
||
## 0. TL;DR — what the console does
|
||
|
||
The console is a **document-based Mac app** (Metrowerks **PowerPlant** framework,
|
||
CodeWarrior C++). Each open document is a **Mission**. The operator picks an
|
||
*Adventure* (Solo/Team × BattleTech/Red Planet), a *Location* (arena) and a
|
||
*Scenario*, assigns *pilots/vehicles*, then drives a fleet of **cockpit pods**
|
||
through a mission lifecycle. It talks to two networks:
|
||
|
||
1. **Pod command/control** — one **MacTCP TCP** stream per pod on **port 1501**
|
||
(console `200.0.0.1`, pods `200.0.0.11`–`200.0.0.120`). This is the protocol
|
||
that matters for the emulator. Commands (Load / Translocate / Stop / End /
|
||
Reset) go out as small binary packets; pods stream back a one-byte
|
||
**Cockpit State** (0–12) that the console records in `Console Log`.
|
||
2. **4th Dimension (4D) database** — a second client link to a 4D server for the
|
||
**pilot roster and mission scheduling** ("Get Next Departure", Add/Delete/
|
||
Lookup Pilot, scoring). Evidenced by the `_4D_Select4DServer` /
|
||
`_4D_Find4DServer` error strings; the wire protocol for 4D was **not**
|
||
reverse-engineered (it is ACI's proprietary 4D client library).
|
||
|
||
The rest of this document backs every claim above with offsets, string refs,
|
||
resource dumps and log excerpts.
|
||
|
||
---
|
||
|
||
## 1. File inventory & formats
|
||
|
||
| File | Size | Format / role |
|
||
|---|---|---|
|
||
| `Console 4.10` | 728 KB | The application. Fat binary: PEF/PPC in data fork, 68K `CODE` + all UI resources in the resource fork (see §5). |
|
||
| `Console Log` | 5.3 MB | Runtime log, CR-terminated records. Ground truth for the pod protocol (§7). |
|
||
| `Console.ini` | 92 KB | Live config: 586 `::`-namespaced sections — pods, adventures, locations, scenarios, vehicles (§8). |
|
||
| `Console.ini copy` | 91 KB | Prior copy of the above. |
|
||
| `Stationary.ini` | 98 KB | The **stationery/template** master config (Mac "stationery" = template document) used to seed a fresh `Console.ini`. |
|
||
| `ini Folder/DB<City>/` | — | Per-venue config overrides: Atlanta, Chicago, Cincinnati, Denver, Detroit, Houston, Miami, Ontario, Pittsburgh, San Jose, Toronto, LaZerPark. |
|
||
| `ConsoleSorter/` + `Console Log Sorter`, `Console Log.rpl` | 0 B here | A companion log-sorter tool (data forks empty in this extraction; only `Instructions` survived). |
|
||
| `FRC.rsrc`, `KR.rsrc`, `US.rsrc` | 225 B each | Tiny locale resource stubs (French / Korean / US). |
|
||
| `Fonts/` (`Killmark`, `Stencil`) | 0 B | Bitmap/suitcase fonts for kill-marker/stencil display (data forks empty here). |
|
||
| `PlainText16 Folder/` | 173 KB | `PlainText` support (text traits). |
|
||
| `.finf/` | 32 B each | Mac Finder-info sidecars from extraction (type/creator). |
|
||
| `__MACOSX/._*` | — | **AppleDouble sidecars — these carry the resource forks.** `._Console 4.10` is 3.38 MB and holds the app's entire resource fork (§5). |
|
||
|
||
**Resource-fork location:** the app's resource fork was preserved inside
|
||
`__MACOSX/._Console 4.10` (AppleDouble magic `0x00051607`, entry id 2 =
|
||
resource fork at file offset `0x52`, length `0x3391CA`). Finder info at offset
|
||
`0x32` reads `APPLvwe4` → **type `APPL`, creator `vwe4`**.
|
||
|
||
---
|
||
|
||
## 2. PEF container structure
|
||
|
||
First 16 bytes: `4A6F7921 70656666 70777063 00000001` = `Joy! peff pwpc` v1
|
||
(classic Mac OS CFM PowerPC container, big-endian).
|
||
|
||
**Container header (40 bytes):**
|
||
|
||
| Field | Value |
|
||
|---|---|
|
||
| tag1 / tag2 / arch | `Joy!` / `peff` / `pwpc` |
|
||
| formatVersion | 1 |
|
||
| dateTimeStamp | `0xAF4A5504` |
|
||
| currentVersion | 0 |
|
||
| sectionCount | 3 |
|
||
| instSectionCount | 2 |
|
||
|
||
**Section table** (each header 28 bytes; bodies follow the name table):
|
||
|
||
| # | Kind | default | total | unpacked | packed | file off | notes |
|
||
|---|---|---|---|---|---|---|---|
|
||
| 0 | Code (0) | 0 | 625,536 | 625,536 | 625,536 | `0x003840` | PPC text (`0x98B80` bytes). |
|
||
| 1 | PatternInitData (2) | 0 | 127,204 | 122,866 | 88,128 | `0x09C3C0` | RLE-packed data/TOC/strings; unpacks to `0x1DFF2`. |
|
||
| 2 | Loader (4) | 0 | 0 | 0 | 14,272 | `0x000080` | Imports/exports/relocs/entry. |
|
||
|
||
**Entry points (from the Loader header):**
|
||
|
||
- **main** = data-section transition vector at offset `0x5154` → `[code 0x00082E5C, TOC 0x00008000]`.
|
||
So `main` executes at **code `0x82E5C`** and the runtime **TOC/`r2` = data offset `0x8000`**.
|
||
(This TOC value is the key that lets us resolve all `r2`-relative constant loads — see §6.)
|
||
- **init** = none (`-1`), **term** = none (`-1`).
|
||
- **exported symbols = 0** (nothing is exported; this is a leaf application, not a shared library).
|
||
- imported libraries = 3, imported symbols = 448, reloc sections = 1.
|
||
|
||
Tool: `py -3.13 tools/pefparse.py "Console 4.10" header|loader|strings|dump|dis`.
|
||
|
||
---
|
||
|
||
## 3. CFM imports (448 symbols across 3 libraries) — interpreted
|
||
|
||
Only three shared libraries are imported. **Notably absent: OpenTransport,
|
||
AppleTalk, and any TCP shared library** — so the console does *not* use Open
|
||
Transport. Its IP networking is done the older way, through the **MacTCP `.IPP`
|
||
driver via the Device Manager** (see the `PBControlAsync/Sync` + `PBOpenSync`
|
||
imports and the `CTCPDriver`/`.IPP` strings in §6).
|
||
|
||
### MathLib (6)
|
||
`num2dec, dec2num, str2dec, dec2str` (SANE-style decimal ↔ string) and
|
||
`fetestexcept, feclearexcept` (FP exception flags). Used for numeric formatting
|
||
of scores/stats.
|
||
|
||
### InterfaceLib (436) — the Mac Toolbox surface
|
||
Grouped by subsystem (representative, not exhaustive):
|
||
|
||
- **QuickDraw / GWorld / PICT:** `InitGraf, OpenCPort, NewGWorld*(via UGWorld),
|
||
LockPixels, CopyBits(implied), OpenCPicture, DrawPicture(implied), PlotCIcon,
|
||
PlotIconID, RGBBackColor, FrameRoundRect, InvertRgn, ClipRect …` — draws the
|
||
console windows, pod photos (PICT), mech/vehicle art, kill markers.
|
||
- **Window Manager:** `NewCWindow, GetNewCWindow, ZoomWindow, SelectWindow,
|
||
HiliteWindow, FindWindow, BeginUpdate/EndUpdate, DragWindow(implied) …`
|
||
- **Menu Manager:** `NewMenu, InsertMenu(implied), DeleteMenu, DrawMenuBar,
|
||
MenuKey, HiliteMenu, EnableItem, CountMItems, GetItemMark …`
|
||
- **Control Manager:** `NewControl, TrackControl, FindControl, Draw1Control,
|
||
SetControlValue/Min/Max, HiliteControl, SetControlAction …` (scrollers,
|
||
buttons, the toolbar).
|
||
- **List / Dialog / TextEdit:** `LGetSelect, LSetSelect, LSetCell, LGetCell`
|
||
(List Manager — player/network tables), `CautionAlert, ParamText`,
|
||
`TEInsert, TEActivate, TEGetHeight, TECut, TEUpdate` (mission notes / fields).
|
||
- **File Manager:** `FSpOpenDF, FSpCreate, FSpDelete, FSRead, FSWrite, FSClose,
|
||
PBOpenSync, PBReadSync, PBWriteSync, PBHCreateSync, PBGetEOFSync,
|
||
PBSetEOFSync, FSpCreateResFile, FSpOpenResFile, HOpenResFile …` — reads
|
||
`Console.ini`, writes `Console Log`, loads/saves mission **egg** files.
|
||
- **Device Manager (→ MacTCP):** **`PBOpenSync`, `PBControlAsync`,
|
||
`PBControlSync`** — open the `.IPP` driver and issue TCP control calls
|
||
(TCPCreate/ActiveOpen/Send/Rcv/Close) — the pod network layer.
|
||
- **Printing Manager:** `PrOpen, PrOpenDoc, PrOpenPage, PrCloseDoc, PrClose` —
|
||
prints score sheets / mission highlights.
|
||
- **Apple Event Manager:** `AEInstallEventHandler, AEProcessAppleEvent,
|
||
AECreateAppleEvent, AESend, AEGetParamDesc, AEGetNthDesc, AEPutParamDesc,
|
||
AECreateList, AEGetAttributePtr, AEResolve(+ObjectSupportLib) …` (see §9).
|
||
- **Misc:** `Random, BlockMove, GetScrap, SysEnvirons, ExitToShell,
|
||
NewRoutineDescriptor/NewFatRoutineDescriptor` (68K↔PPC mixed-mode glue).
|
||
|
||
### ObjectSupportLib (6) — Apple Event object model
|
||
`AEResolve, AESetObjectCallbacks, AEInstallObjectAccessor, CreateObjSpecifier,
|
||
AEObjectInit, CreateOffsetDescriptor` — the app is an AppleScript object-model
|
||
server over its window/document objects (standard PowerPlant `UAppleEventsMgr`).
|
||
|
||
---
|
||
|
||
## 4. Code organization (from the 27 named `CODE` segments)
|
||
|
||
Because this is a fat binary, the resource fork still carries the CodeWarrior
|
||
**68K segment names**, which are a clean module map of the whole program
|
||
(the PPC side is the same code, differently packaged):
|
||
|
||
```
|
||
1 Main 10 Utilities 19 RP Panes
|
||
2 Commanders 11 Libraries 20 Data Objects
|
||
3 Connection 12 Debug 21 BT Data Objects
|
||
4 Panes 13 TCP/IP 22 RP Data Objects
|
||
5 Table 14 MUNGA 23 Printing
|
||
6 Files 15 Console Menus 24 RP Printing
|
||
7 Apple Events 16 Console Windows 25 BT Printing
|
||
8 Array 17 Console Panes 26 Console Features
|
||
9 Features 18 BT Panes
|
||
```
|
||
|
||
- **Connection / TCP/IP** = the pod networking (`CTCPDriver`, `CTCPStream`,
|
||
`CTCPEndpoint`, `CTCPAsyncCall`, `CTCPResolverCall`, `NetworkEndpoint`).
|
||
- **MUNGA** = data marshalling (the mission/egg (de)serializer — "munge").
|
||
- **BT / RP** split = per-game code (BattleTech vs Red Planet) for panes, data
|
||
objects, and printing.
|
||
|
||
`.cp` source-module names recovered from the binary (the CodeWarrior debug
|
||
string table) include, among ~140 files:
|
||
`ConsoleApp, ConsoleDoc, ConsoleWindow, PrefsDoc, NetworkEndpoint,
|
||
NetworkEndpointList, NetworkTable, NetworkWindow, CTCPDriver, CTCPStream,
|
||
CTCPEndpoint, CTCPAsyncCall, CTCPResolverCall, MissionObject, MissionEvent,
|
||
MissionEventQueue, MissionView, MissionGraphView, MissionGraphWindow,
|
||
MissionClockView, Participant, TeamList, TeamPrtcpntList, PlayerTable,
|
||
BTScenario, BTVehicle, BTLocation, RPScenario, RPVehicle, RPLocation, Vehicle,
|
||
Weather, Experience, Adventure, ScoreSheetView, DamageMatrix, BTKillMarker` and
|
||
the PowerPlant framework classes (`LApplication, LDocument, LWindow, LView,
|
||
LPane, LTableView, LCommander, LListener/LBroadcaster(implied), UAppleEventsMgr,
|
||
UGWorld, UMemoryMgr, UReanimator, UModalDialogs`).
|
||
|
||
---
|
||
|
||
## 5. Resource fork (550 resources, 39 types)
|
||
|
||
Parsed from `__MACOSX/._Console 4.10` with `tools/rsrcparse.py`. Highlights:
|
||
|
||
- **`cfrg` (1)** — the Code Fragment resource that points the CFM loader at the
|
||
PEF in the data fork (confirms the fat-binary layout).
|
||
- **`vers` (2)** — `2.4.0b3`, "Console 2.4.0b3", © 1995–1997 **FASA Interactive, Inc.**
|
||
- **`SIZE` / `BNDL` / `FREF` / `ICN#`…** — standard app packaging; `icm#/ics#`
|
||
application icons.
|
||
- **`ºMWC` (32000)** — Metrowerks CodeWarrior project stamp (confirms toolchain).
|
||
|
||
### Operator commands & UI
|
||
- **Toolbar buttons** (`ICN#`/`icl4`/`icl8`, ids 1000–1011):
|
||
**Get Next Departure, Translocate, Load Mission, Add Pilot, Delete Pilot, Lookup.**
|
||
- **`MENU`/`Mcmd` (10)**: Apple, File (New / Close / Save / Save a Copy… / Page
|
||
Setup… / Print / Quit), Edit, **Configuration**, **Adventure**, **BattleTech**,
|
||
**Red Planet** + scenario menus (`BTScenario`, `RPScenario`).
|
||
- **`WIND`/`PPob` (windows)**: **`RPMR ConsoleWindow`, `RPMF ConsoleWindow`,
|
||
`BTFA ConsoleWindow`, `BTSB ConsoleWindow`** (the four game-mode console
|
||
layouts), **Mission Graph**, **Toolbar**, **Network Diagnostic**, **Lookup**.
|
||
- **`ALRT`/`DITL`**: About Box, Save-before-closing/quitting, Confirm Revert,
|
||
Low Memory Warning, **TCP Warning**, Landscape/Portrait mode.
|
||
|
||
### Data/game resources
|
||
- **`Mech`/`MWpn`/`MDgZ` (×24/×24/×22)** — BattleTech mechs + weapons + damage
|
||
zones: *BlackHawk, Loki, MadCat, Owens, Thor, Vulture, Avatar, Sunder,
|
||
TimberWolf, Satyr, Hellbringer, Summoner, …*
|
||
- **`PICT` (×98)** — pod photos (*Carpe Diem, Calamity Jane, Man O' War,…*) and
|
||
faction crests (*Davion, Kurita, Steiner, Liao, Marik, Comstar, VGL*). These
|
||
match `pictID=` in `Console.ini`.
|
||
- **`Txtr` (×57)** — named text traits (fonts/styles for each field, plasma
|
||
screen, mission clock).
|
||
|
||
### String lists (`STR#`) — the semantic vocabulary
|
||
61 `STR#` resources. The protocol-relevant ones:
|
||
|
||
- **`STR# 131 "Mission State"`** — the console-side (document) mission state
|
||
descriptions, index 0..6:
|
||
1. `Mission data is incomplete.`
|
||
2. `The mission is ready to load.`
|
||
3. `The mission is being loaded.`
|
||
4. `The mission is ready for translocation.`
|
||
5. `The mission is being translocated.`
|
||
6. `The mission is in progress.`
|
||
7. `The mission has been completed.`
|
||
- **`STR# 130 "Network Endpoints"`** — the 50-name roster: cockpits (*Frequent
|
||
Flyer, Privateer, Man O' War, Divine Wind, Carpe Diem, Gypsy, Puck, Icarus,
|
||
Calamity Jane, Valkyrie, Pegasus,…*), cameras (*Alpha/Beta/Gamma/Delta/Epsilon
|
||
Camera*), *Mission Review* stations, and PC/test targets (*BrownCartPC,
|
||
GaugeCartPC, ZabswareTarget*). Index 0 = `Bogon Alert!` (invalid),
|
||
`<Available>` = a free slot.
|
||
- **`STR# 1002/1003 "Network Errors"`** — explicitly a **4D database** client:
|
||
`_4D_Select4DServer`, `_4D_Find4DServer`, "connection to the database",
|
||
"asynchronous mode query already in progress", "invalid connection handle".
|
||
- **`STR# 1001 Database Errors`, `1004 Database Tasks`, `1005 General Errors`,
|
||
`128 General Strings`** (`"%s Mission %s"`, `"Mission Time & Date: %s, %s"`,
|
||
Show/Hide Toolbar, Show/Hide Network), **`300 Adventure Strings`**
|
||
(`BattleTech`, `Red Planet`), plus large game tables (scenarios, drop zones,
|
||
roles, kill/death/damage strings, textures, football positions, …).
|
||
- **`eTbl` (3)** — error tables named *Database Errors*, *Network Errors*,
|
||
*4D Open Errors* (again confirming the 4D backend).
|
||
|
||
---
|
||
|
||
## 6. Pod command/control protocol (MacTCP, port 1501)
|
||
|
||
This is the highest-value result. Evidence chain: binary strings →
|
||
disassembly (TOC-resolved) → `Console Log` → `Console.ini`.
|
||
|
||
### 6.1 Transport
|
||
- Custom C++ classes **`CTCPDriver` / `CTCPStream` / `CTCPEndpoint` /
|
||
`CTCPAsyncCall` / `CTCPResolverCall`** wrap **MacTCP**. The literal driver
|
||
name **`.IPP`** appears at data `0x1053F` right next to `CTCPDriver` — this is
|
||
the classic MacTCP IP driver opened with `PBOpenSync`, then driven with
|
||
`PBControlAsync/Sync`.
|
||
- One TCP connection per pod. From `Console.ini` each
|
||
`[NetworkEndpoint::Cockpit::*]` sets `classID=Ptcp`, `defaultPort=1501`,
|
||
`localHostPort=1501`, and the pod's `addressIP` (`200.0.0.11`…`200.0.0.117`,
|
||
etc.). Console host = `200.0.0.1`; pods `200.0.0.11`–`200.0.0.120` (from the
|
||
log). The many `Ptcp` sub-keys (`openTimeout, listenTimeout, delay,
|
||
throughput, reliability, precedence, fragment, timeToLive, security,
|
||
queryTimeout`) are **MacTCP TCP/IP ULP parameters** passed to TCPActiveOpen /
|
||
the IP header — further proof this is MacTCP, not OT.
|
||
|
||
### 6.2 Receive path — pod → console "Cockpit State"
|
||
Message dispatch lives at **code `0x63B3C`** (inside the participant's
|
||
message handler). The received message's low byte is the type:
|
||
|
||
```
|
||
0x63B3C clrlwi r0, r31, 0x18 ; r0 = msg & 0xFF (message type)
|
||
0x63B40 cmplwi r0, 0xC ; 0..12 valid
|
||
0x63B44 bgt 0x63BEC ; >12 → default → "Unknown message type!!!"
|
||
0x63B48 lwz r3, -0x6CA4(r2) ; TOC[data 0x135C] = jump table @ data 0x17B1C
|
||
0x63B4C slwi r0, r0, 2
|
||
0x63B50 lwzx r3, r3, r0 ; handler = jumptable[type]
|
||
0x63B54 mtctr r3 ; bctr
|
||
```
|
||
|
||
Every case calls **`SetCockpitState(participant+0x30C, type)` at `0x62720`**:
|
||
|
||
```
|
||
0x62720 SetCockpitState:
|
||
stb r4, 0x289(r6) ; participant->cockpitState = type (field at +0x289)
|
||
li r4, 0xBBC ; bl BroadcastMessage ; notify the UI panes/graph
|
||
```
|
||
(`0x62778` is the matching getter: `lbz r3, 0x289(r3)`.) The console then logs
|
||
`"<IP> Cockpit State: <type>"`. So **the pod's message-type byte is literally
|
||
the Cockpit State number, 0–12**; type > 12 logs `"Unknown message type!!!"`
|
||
(seen twice in the 5.3 MB log). The jump table at data `0x17B1C` (13 code
|
||
pointers, default `0x63BEC`) is the physical proof of the 0–12 range.
|
||
|
||
### 6.3 Send path — console → pod commands
|
||
Commands are transmitted by **`NetworkEndpoint::Send` at `0x629EC`**, called as
|
||
`Send(endpoint, &msgHeaderWord, payloadObj, payloadLen)`. It frames a packet =
|
||
**16-byte header + payload**:
|
||
|
||
```
|
||
buf = alloc(len + 0x10)
|
||
memcpy(buf+0x10, payloadObj, len) ; body
|
||
buf[0..3] = *msgHeaderWord ; message-class word (observed 0x04000000)
|
||
buf[4..7] = 0
|
||
buf[8..11] = value from 0x3CB38 ; sequence / timestamp
|
||
CTCPStream::Write(endpoint, buf, len+0x10) ; via 0x39748 → 0x3BD28
|
||
```
|
||
|
||
The **payload object** is a small serialized struct whose **byte[0] = its size**
|
||
and **byte[4] = the command code**. Two confirmed by disassembling the object
|
||
constructors:
|
||
|
||
| Command | Ctor | payload size | **command code (byte[4])** | Verb logged | Sender |
|
||
|---|---|---|---|---|---|
|
||
| Translocate mission | `0x3EC74` | `0x0C` | **5** | `Translocating Mission…` | `0x67438` |
|
||
| Stop mission | `0x3ECC8` | `0x10` | **6** | `Stopping Mission…` | `0x6755C` |
|
||
|
||
Other senders (same shape, codes not yet pinned): **Loading Mission**
|
||
(`0x64xxx`/`Received egg file` `0x648C0`), **Ending Mission** `0x66464` (uses a
|
||
variant send `0x5E474` that packs the destination IP from a 4-byte struct field
|
||
`+0x10..0x13`), **Reset Mission** `0x6769C`. Command codes for these are
|
||
recoverable with the same method (find the `li r0,N; stb r0,-0x10(r1)` that
|
||
seeds object byte[4]) — see Open Questions.
|
||
|
||
### 6.4 Internal PowerPlant command/broadcast IDs
|
||
The app's own command constants cluster in **`0xBBA`–`0xBDF`** (decimal
|
||
3002–3039). Confirmed meanings:
|
||
`0xBBC` = cockpit-state-changed broadcast, `0xBC5` = "received egg file",
|
||
`0xBCB` = ending mission, `0xBBA/0xBBB` = handled in the participant
|
||
`ListenToMessage` at `0x628D8`. (`0xBC1` is the most frequent; others 0xBCE–
|
||
0xBDF map to menu/toolbar commands.)
|
||
|
||
### 6.5 Observed mission lifecycle (from the log)
|
||
A full run for one pod, with the two state machines interleaved
|
||
(**Cockpit State N** = pod→console; **verbs** = console actions):
|
||
|
||
```
|
||
Cockpit State 2 (present/connected)
|
||
Cockpit State 1 (ready)
|
||
Loading Mission… → console loads the egg
|
||
Cockpit State 6
|
||
Cockpit State 7
|
||
Received egg file → egg in hand; "Ready to translocate"
|
||
Cockpit State 8
|
||
Translocating Mission… → console sends egg/mission to pod (cmd code 5)
|
||
Cockpit State 9
|
||
Cockpit State 10 (in progress / running)
|
||
Stopping Mission… (cmd code 6)
|
||
Cockpit State 11
|
||
Ending Mission…
|
||
Cockpit State 12
|
||
Cockpit at <IP> initiated close!!! → pod closed its TCP stream
|
||
Cockpit State 3 (closed/disconnected)
|
||
Reset Mission… → console rearms for the next departure
|
||
Cockpit State 2 …
|
||
```
|
||
|
||
Cockpit-state frequencies in the sample log: `1:3997 2:13258 3:12809
|
||
6:3098 7:3093 8:3262 9:2852 10:2852 11:2990 12:2890` (states 0, 4, 5
|
||
never appear in this log though the dispatch table has slots for them — likely
|
||
transient/internal). Verb counts: `initiated close 24702, Loading 3129,
|
||
Received egg 3124, Stopping 2996, Ending 2905, Translocating 2882, Reset 953`.
|
||
|
||
---
|
||
|
||
## 7. Console Log format
|
||
|
||
- **Encoding:** Mac Roman. **Records are CR-terminated** (`0x0D`; classic Mac,
|
||
**no LF** — 98,910 CRs for ~91,607 records in the sample). `0xC9` = "…"
|
||
ellipsis inside the mission verbs (13,415 occurrences).
|
||
- **Record grammar:**
|
||
`M/D/YYYY` `HH:MM:SS`␠`AM|PM`␠`<message>` `CR`
|
||
where `<message>` is usually `<pod-IP> <text>` (state/verb lines) or the
|
||
IP-embedded form `Cockpit at <pod-IP> initiated close!!!`.
|
||
- The timestamps in this capture read year **1956** — the logging Mac's clock
|
||
was unset (default RTC), not a real date. Times of day are valid.
|
||
- **72 distinct message templates**; the load-bearing ones:
|
||
`"<IP> Cockpit State: <N>"`, `"Cockpit at <IP> initiated close!!!"`,
|
||
`"<IP> Loading|Translocating|Stopping|Ending|Reset Mission…"`,
|
||
`"<IP> Received egg file"`, `"<IP> Unknown message type!!!"`.
|
||
- **Log writer:** each line is assembled by a string-append helper (`0x85324`,
|
||
repeatedly) and flushed by `0x851C4`; the leading date/time comes from the
|
||
global at TOC `-0x77CC(r2)` (data `0x834`). Field separator string is at data
|
||
`0x17B8`.
|
||
|
||
---
|
||
|
||
## 8. Console.ini schema
|
||
|
||
INI-style but with **no newlines** — sections are `[Name]` headers and
|
||
`key=value` pairs run together; parse by splitting on `[`. Section names use a
|
||
`::` **hierarchical namespace**. 586 sections total in the live file.
|
||
|
||
**Section families (by count):** `RP` 342, `BT` 196, `rpl4` 34,
|
||
`NetworkEndpoint` 10, `Adventure` 3, `NetworkEndpointList` 1.
|
||
|
||
**Every object carries a 4-char `classID`** (an OSType tag naming its C++
|
||
class). Confirmed: `Ptcp` (TCP endpoint), `Advn` (adventure), `RPLn` (Red
|
||
Planet location); BT/RP scenario & vehicle classes follow the same pattern.
|
||
|
||
### 8.1 Network endpoints (the pods)
|
||
```
|
||
[NetworkEndpointList]
|
||
networkEndpoint = NetworkEndpoint::Cockpit::Frequent Flyer
|
||
networkEndpoint = NetworkEndpoint::Cockpit::Privateer
|
||
… (10 active endpoints)
|
||
|
||
[NetworkEndpoint::Cockpit::Frequent Flyer]
|
||
classID=Ptcp hostType=0 state=0 addressUse=0
|
||
addressIP=200.0.0.11
|
||
defaultPort=1501 localHostPort=1501 openInterval=40 openTimeout=10
|
||
listenTimeout=0
|
||
resourceID=130 resourceIndex=2 pictID=308 ; STR# 130 name + PICT photo
|
||
; MacTCP TCP/IP ULP parameters:
|
||
delay=1 throughput=1 reliability=1 precedence=0 fragment=0
|
||
timeToLive=60 security=0 queryTimeout=60
|
||
loadQueryInterval=1 inProgressQueryInterval=10
|
||
```
|
||
`resourceID=130` + `resourceIndex` selects the pod's display name from
|
||
`STR# 130`; `pictID` selects its photo PICT.
|
||
|
||
### 8.2 Adventure → Location → Scenario tree (populates the menus)
|
||
```
|
||
[Adventure::Solo::Red Planet] classID=Advn adventureType=2 resourceID=300 resourceIndex=2 list=RP::Solo::LocationList
|
||
[RP::Solo::LocationList] location=rpl4::Solo::Location::Yip's Yahoorama …
|
||
[rpl4::Solo::Location::Wiseguy's Wake] classID=RPLn resourceID=4000 resourceIndex=3 tag=wise pictID=601 list=RP::Wiseguy's Wake::Solo::ScenarioList
|
||
[BT::Cavern::ScenarioList] scenario=BT::Cavern::NoReturn scenario=BT::Cavern::FreeForAll
|
||
[BT::Solo::LocationList] location=BT::Solo::Ravines / Arena1 / Polar4 / Cavern …
|
||
```
|
||
Four top-level adventures exist in the binary:
|
||
`Adventure::{Solo,Team}::{BattleTech,Red Planet}` — matching the four console
|
||
windows `BTFA/BTSB/RPMF/RPMR ConsoleWindow`.
|
||
|
||
### 8.3 Key inventory
|
||
`classID, resourceID, resourceIndex, pictID, tag, list, scenario, location,
|
||
default, value, max, time` (structural) and the game-object keys
|
||
`vehicle/vehicel(sic)/vehicleClass, mechID, camo, color, texture, emblem,
|
||
killMarker, experience, role, position, dropZone, weather, temperature,
|
||
adventureType, language, scriptCode` plus the full `Ptcp` network key set.
|
||
|
||
`Stationary.ini` is the same schema used as a **template** to regenerate a
|
||
site's `Console.ini`; the per-city `ini Folder/DB<City>` files are venue
|
||
overrides.
|
||
|
||
---
|
||
|
||
## 9. Apple Events
|
||
|
||
The app installs standard AE handlers (`AEInstallEventHandler`) and is a full
|
||
AppleScript **object-model server** (`ObjectSupportLib` + `UAppleEventsMgr.cp`).
|
||
Its `aete` (id 0, "English") declares only the **standard suites** — Required
|
||
(`reqd`: run/open/print/quit), Standard/Core (`CoRe`: close/get/set/make/save/
|
||
data size), and Miscellaneous Standards (`misc`: revert/select) — over the
|
||
generic `application`/`window`/`document` classes. `aedt` (3) = *Required Suite,
|
||
Core Suite, Misc Standards*.
|
||
|
||
**Conclusion:** Apple Events are used for **OS/Finder integration**
|
||
(open-documents = open mission/ini files, print score sheets, quit), **not** for
|
||
commanding pods. Pod command is exclusively the MacTCP protocol in §6. (No
|
||
custom application scripting suite is defined.)
|
||
|
||
---
|
||
|
||
## 10. Tooling written (`410console/tools/`)
|
||
|
||
Reusable, dependency-light (Python 3.13 + capstone):
|
||
|
||
- **`pefparse.py`** — PEF container parser + PIDATA (RLE) unpacker + loader
|
||
(imports/exports/entry) + string extractor + capstone PPC disassembler.
|
||
`header | loader | strings [minlen] | dump <sec> <off> <len> | dis <sec> <off> <count>`.
|
||
- **`ppcxref.py`** — TOC-aware annotator (`r2` = data `0x8000`): resolves
|
||
`r2`-relative constant loads to data addresses/strings and builds a
|
||
string→code cross-reference. `annotate <start> <count> | xref <substr> | toc <off>`.
|
||
- **`rsrcparse.py`** — classic Mac resource-fork parser (reads the AppleDouble
|
||
`._` sidecar). `map | names <TYPE> | strn [id] | dump <TYPE> [id]`.
|
||
|
||
Example: `py -3.13 tools/rsrcparse.py "4_10-console-extracted/__MACOSX/._Console 4.10" strn 131`.
|
||
|
||
---
|
||
|
||
## 11. Confidence & open questions
|
||
|
||
**High confidence (multiple corroborating sources):**
|
||
- Fat PEF/PPC app, PowerPlant/CodeWarrior, FASA Interactive, creator `vwe4`,
|
||
vers 2.4.0b3. *(PEF header + resource fork + strings.)*
|
||
- MacTCP `.IPP`, port 1501, per-pod TCP, addressing scheme. *(imports +
|
||
`CTCPDriver`/`.IPP` string + ini + log.)*
|
||
- Cockpit State machine 0–12, pod→console, dispatch at `0x63B3C`, stored at
|
||
Participant `+0x289`, logged verbatim. *(disasm + jump table + log stats.)*
|
||
- Send framing = 16-byte header + payload; command payload byte[0]=size,
|
||
byte[4]=code; Translocate=5, Stop=6. *(disasm of `0x629EC` + two ctors.)*
|
||
- Mission lifecycle ordering. *(log, thousands of runs.)*
|
||
- ini schema and 4D-database backend. *(ini + `STR#`/`eTbl` + error strings.)*
|
||
|
||
**Open questions / next steps:**
|
||
1. **Finish the command-code table.** Pin byte[4] for Load, End, Reset,
|
||
Get-Next-Departure (method: disassemble each sender's object ctor near
|
||
`0x3EBxx–0x3EFxx` for `li r0,N; stb r0,-0x10(r1)`). Also decode the
|
||
header word-2 (`0x3CB38`) — sequence counter vs. timestamp.
|
||
2. **Header semantics.** Confirm whether the pod reads packet byte[0] (class=4)
|
||
or a payload byte as its "message type", and map the console's **command**
|
||
codes to the resulting **Cockpit State** transitions (5→9, 6→11 observed).
|
||
3. **Egg transfer mechanism.** Determine whether the `.egg` mission bundle
|
||
(note the `Egg.ini` string at data `0x98E9`, and the **MUNGA** segment)
|
||
travels to the pod over the same 1501 stream during "Translocate", or via a
|
||
separate transfer. `Received egg file` is logged **console-side** — clarify
|
||
direction (console reads egg from disk vs. receives from DB/pod).
|
||
4. **States 0/4/5.** Never seen in this log; identify their handlers
|
||
(`jumptable[0]=0x63BEC` default, `[4]/[5]=0x63B78`) and meaning.
|
||
5. **4D protocol.** Out of scope here (proprietary ACI 4D client); the console↔
|
||
4D link governs scheduling ("Get Next Departure") and the pilot roster.
|
||
6. **Relocations.** `pefparse` does not yet apply the Loader relocation opcode
|
||
stream (`reloc_instr_offset`); constant addresses were resolved via the TOC
|
||
value instead, which was sufficient. A full reloc pass would make every data
|
||
pointer absolute for deeper xref.
|
||
|
||
---
|
||
|
||
### Appendix A — key addresses (code section, base 0)
|
||
| Addr | Role |
|
||
|---|---|
|
||
| `0x82E5C` | program `main` (via TVector @ data `0x5154`) |
|
||
| `0x63B3C` | received-message dispatch (msg&0xFF → jumptable @ data `0x17B1C`) |
|
||
| `0x62720` | `SetCockpitState(participant, state)` → Participant `+0x289`, broadcast `0xBBC` |
|
||
| `0x62778` | `GetCockpitState` (`lbz +0x289`) |
|
||
| `0x629EC` | `NetworkEndpoint::Send` (16-byte header + payload) |
|
||
| `0x39748`→`0x3BD28` | `CTCPStream::Write` |
|
||
| `0x3EC74` / `0x3ECC8` | Translocate (code 5) / Stop (code 6) command-object ctors |
|
||
| `0x66464` / `0x67438` / `0x6755C` / `0x6769C` / `0x648C0` | senders: Ending / Translocating / Stopping / Reset / Received-egg |
|
||
| `0x85324` / `0x851C4` | log-line append / flush |
|
||
| data `0x8000` | TOC base (`r2`) |
|
||
| data `0x17B1C` | 13-entry cockpit-message jump table |
|
||
|
||
### Appendix B — reproduce
|
||
```
|
||
cd c:\VWE\TeslaRel410\410console
|
||
py -3.13 tools\pefparse.py "4_10-console-extracted\Console 4.10" loader
|
||
py -3.13 tools\ppcxref.py xref "Mission"
|
||
py -3.13 tools\ppcxref.py annotate 0x629ec 0x60
|
||
py -3.13 tools\rsrcparse.py "4_10-console-extracted\__MACOSX\._Console 4.10" map
|
||
py -3.13 tools\rsrcparse.py "4_10-console-extracted\__MACOSX\._Console 4.10" strn 131
|
||
```
|