Commit Graph
4 Commits
Author SHA1 Message Date
Cyd 9f0a77cc16 The tracks were never built from one model
A map record is an Entity::MakeMessage (MUNGA/ENTITY3.h): classToCreate,
owningPlayerID, resourceID, instanceFlags, localOrigin. The origin ends
the 76-byte case, which puts classToCreate at +28, resourceID at +40 and
instanceFlags at +44.

I had been reading +44. That is instanceFlags, and it is 524 on every
scenery record - and 524 happens to be cn3's GaugeImage. So every track
resolved to cn3 repeated a few hundred times, consistently and wrongly,
and every conclusion drawn from that followed: the "one wall bar with a
gate", the ticks, the claim that the LOD machinery is never exercised.

The id at +40 varies per placement. The tracks use cn1, cn3, cn4, cn5,
cn7, br1, br3, ft1, cq1, cq2, md3, md4, the pits, and the score zones
sc50/sc50a/sc500a that gave the amber boxes at each end. A record names
the model's Model List; the GaugeImage is filed under the same model
name, so the name is the join - and models with no gauge image (oao,
snAwork, pz1) are skipped here exactly as DrawStatic skips them.

Found by following the map loader: InterestManager::LoadMapStream reads
the stream as MakeMessages and names the map entity classes, one of which
is 95 in these records - CulturalIconClassID.
2026-08-07 14:45:20 -05:00
Cyd 86bf6a934b Map instance records carry their own length
They are not a fixed 76 bytes. The first int of each record is its length,
and while most placements are 76, every track also has eight of 140, two
of 80 and one of 336 - 560 on Paingod's. Striding a fixed 76 landed
mid-record on those, and hunting forward for the next plausible
quaternion then locked onto arbitrary bytes: that is where the impossible
class ids came from, and the "resource id" 1065353216, which is
0x3F800000 - float 1.0.

Reading the length instead, all eighteen tracks parse to exactly their
declared instance count with no bytes left over. That is the check that
was missing before.

It does not change what gets drawn, because the extra records were never
drawable anyway. It does mean the parse is no longer guessing.
2026-08-07 13:23:37 -05:00
Cyd 4979193528 The viewer uses the engine's projection, not the console's
It was drawing the map mirrored. The X flip came from the setup console's
picture of Brewer's Bane, which is an illustration and not a screenshot -
the game does not draw it that way round.

What the game does: L4GaugeImagePrimitive::Draw plots
MoveToAbsolute(dest->x, dest->z), so the screen axes are view-space X and
Z, and the graphics view's origin is bottom left with Y increasing upward
- BackgroundLine draws endpoints.bottomLeft to endpoints.topRight, and
the port's zero-degree blit is documented with the origin in the bottom
left corner. So +X runs right and +Z runs up.

Also added the heading the real display has. NavDisplay centres on the
vehicle and turns with it, inverting the viewer's transform and taking
yaw only unless rockAndRoll is set; the viewer defaults to heading 0,
which is the north-up case, and Q/E/R turn it. Panning and dragging now
work in what you see rather than in world axes, so up stays up when the
map is turned.

TRACKS.html is left following the console pictures on purpose: nine of
those cards are the console pictures, so the reconstructed nine have to
sit beside them consistently. The two disagree by a mirror and each is
right for what it is, which both READMEs now say.
2026-08-07 13:12:57 -05:00
Cyd b1b82d5da1 A map viewer that draws tracks the way the map screen does
Pan with the arrows, zoom with plus and minus, [ and ] for the next
track. Self-contained HTML with the track data and palette embedded;
nothing here ships, and pack-dist.ps1 does not look at it.

It follows the engine rather than approximating it. NavDisplay derives
metersPerPixel from the zoom and sets LODIndex to it; L4GaugeImage::Draw
takes the first LOD whose scale is at least that value and draws nothing
once the value runs past the largest, so objects vanish rather than
simplify. Both map gauges are here because they disagree - nav is the
448x416 radar screen with LOD following zoom, gps the 125x203 panel whose
config pins LOD at 1.0. The HUD reports what is dropped, and is honest
that this content barely exercises it: every placement in every track is
cn3 with one LOD at scale 1000.

The map is not a phosphor screen. Primitives carry palette indices and
the palette is whichever the port was configured with - for the pod's
secondary port, configure(0,sec,270,0x00ff,clut0,rgb,secpal.pcc). PCC is
PCX, so the palette is the last 769 bytes. Walls are grey because index
51 is #4b4b4b; background is index 0, black; a primitive with colour 0
keeps the display's staticColor, 0x3C. Per-file palettes, not a global
one - 39 of 40 gauge PCCs differ - so the port's configured palette is
the one that counts.
2026-08-07 12:40:52 -05:00