Skip to content

VID: the "null block" is a trailing RLE frame terminator — confirmed across ARENA2 (84/84) #2806

Description

@Alexbeav

Assets/Scripts/API/VidFile.cs:110-113 documents a case it handles without an
explanation:

/// Occasionally there is a null block which must be rejected.

I think I can explain it, and the check reproduces cleanly against ARENA2.

The finding

When a frame's RLE data fills the frame buffer exactly, the decode loop exits on the
pixel count without consuming the terminator byte — but the encoder wrote one anyway.

That leftover byte lands where the next block type is read, and reads as block type 0.
It was never a block.

The prediction this makes is falsifiable: every 0x00 at a block boundary should be
immediately preceded by a video block whose RLE ended exactly at buffer-full, and never
by anything else. Results:

Corpus Files Null blocks Preceded by buffer-full frame Violations
Daggerfall ARENA2 17 84 84 0
Terminator: Future Shock + SkyNET 23 1,245 1,245 0

In ARENA2 the null blocks are confined to two files — DAG2.VID (83) and ANIM0009.VID
(1) — while the other 15 have none at all, which is presumably why they read as
"occasional."

Why this is not a bug report

Rejecting 0x00 as a skippable block is byte-identical in effect to consuming it as a
stray terminator, so the current handling is correct and needs no change. The only
actionable detail is for anyone writing a stricter parser later:

Terminator emission is inconsistent. In ARENA2, 84 of the 101 video frames that end
exactly at buffer-full are followed by 0x00 — the other 17 are not. (Future Shock and
SkyNET: 1,245 of 1,294.) So a parser can neither require the trailing byte nor assume
it is absent; both forms are valid output from the original encoder.

Incidental corpus data on the Unknown header fields

I ran the same parse over both games' files. Offered as data points, not claims:

Field VidFile.cs ARENA2 (17 files) Future Shock / SkyNET (23 files)
Unknown1 // Always 512 512 in all 17 512 in all 23 — never varies anywhere, so undecidable from data
Unknown2 undocumented 14 in all 17 10 or 14. 10 correlates exactly with use of type-0x01 incremental blocks; 14 with row-offset-only encoding. Both occur within Future Shock alone, so not a version marker
PlaybackRate hard throw unless 166 (VidFile.cs:250-251) 166 in all 17 166 in all 23 — the assumption holds across both eras
Frame size expects 256x200 or 320x200 320x200 (16 files), 256x200 (DAG2.VID) also 320x186 in one game — only relevant if the decoder is ever pointed at those files

The Unknown2 split is the one that might be worth something: whatever encoder feature
it tracks appears to have gone unused by the time Daggerfall shipped, since ARENA2 holds
14 uniformly.

Provenance

This came out of documenting the VID format as it shipped in The Terminator: Future
Shock
(Dec 1995) and SkyNET (1996), which use the same format Daggerfall shipped with
roughly nine months later — same 15-byte header, same block types
(0x00/0x01/0x02/0x03/0x04/0x14/0x7C/0x7D), same RLE opcodes, same 6-bit palette, same
rate byte. VidFile.cs was my starting reference, and every assumption in it that I
could test against those earlier files held up.

As a cross-check in the other direction: the parser I built from that earlier corpus
reads all 17 ARENA2 VIDs with no desync, and decoded frame count equals header
FrameCount in every file.

Happy to share the verification script (it just instruments the RLE exit condition and
walks the block stream) if anyone wants to reproduce the table.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions