Skip to content

Latest commit

 

History

History
271 lines (211 loc) · 12 KB

File metadata and controls

271 lines (211 loc) · 12 KB

05 — Compression, and the band

Order of work as the checklist sets it: census first, then magic scan validated by running the decompressor, then nesting, then the band.

Reproduce with python3 tools/packscan2.py _work/iso --depth 3notes/packscan.txt and python3 tools/band.py _work/iso --per-filenotes/band.txt.

Census first

python3 tools/census.py _work/isonotes/census.txt. First-longword histogram, 221 files:

magic count what
ATN! 96 Imploder
\0\0\3\xf3 24 AmigaDOS hunk
FORM 20 IFF
PaCK 9 not one of the eight known crunchers
\xe3\x10 8 Workbench icon
CrM! 2 CrunchMania
#--- 5 text (the .Man scripts)
cd0: 3 the three launcher scripts
everything else 54 raw data, one file each

The prediction

P3, written before any magic was read:

After three Core Design discs the only surviving same-label regularity is the cruncher. The Speris Legacy — the other Team 17 disc — uses the Imploder (IMP!), 35 of 47 files, 52.8 %. Prediction: Superfrog packs with the Imploder.

Correct, and by a wider margin than Speris. 96 of 221 files begin ATN!, and the Imploder's decruncher — transcribed from The Speris Legacy's own shipped routine, tools/speris_imp.py, reused unmodified — decodes every one of them with ok=True, meaning the in-place pointer landed exactly on zero.

ATN! rather than Speris's IMP! is the same packer: the two magics are the Imploder's data-file and executable forms and the same 350-byte routine reads both. The tail layout is identical — 38 bytes carrying the initial literal run, the initial bit buffer, eight offset bases, twelve bit widths and a checksum — and every file on this disc carries the Imploder's default tables.

So "the cruncher follows the label" goes from 3-of-3 inside Core Design to two independent labels, with Team 17 at 2 of 2 across two studios (Superfrog is Team 17's own; Super Stardust is Bloodhouse) and three years.

The streams

158 validated Imploder streams. Validation is not structural: every one was decoded, and a hit only counts if the output length equals the declared length.

depth streams packed unpacked factor
1 137 3,285,886 11,988,480 3.65×
2 21 207,228 782,656 3.78×

P13 is wrong. The prediction was depth 0 — no stream inside a stream. There are 21 second-level streams, all of them inside the output of a first-level stream, and they are not incidental: they are the intro and the particle engine's payloads.

96 files begin with a stream; 137 streams exist at the first level; so 41 streams sit at non-zero offsets inside files, which is the concatenation form.

The three container forms, all present

The checklist names three and this disc has all three.

A file that is a stream. 96 of them. SFData/L1BM is ATN!, 63,744 bytes on disc, 134,400 out, and nothing else.

A file that is a concatenation of streams with raw gaps between them. SuperFrog/SFIntro, 751,852 bytes, holds 27 streams. SFData/S3BO holds 6, SABO 4, SuperFrogCD 6.

A file that is a stream whose output is a concatenation. SFData/INTROF is one stream at offset 0; decompress it and the 268,956-byte result contains 15 more streams. SFData/PARTF is the same shape with 6. This is the form that a single-level unpack misses entirely, and it is where 21 of the 158 streams live.

The gaps are not padding. 32 raw runs totalling 1,080,250 bytes sit between and around the streams — 6.2 % of the resident corpus. They are not alignment: the largest single gap is 209,504 bytes and it is the code hunk of the packed engine inside PARTF. Counted rather than assumed, as Gunship's 241 single-zero-byte gaps were.

A ninth container: PaCK

Nine files in /SuperStardust/CRM begin PaCK, which is none of the eight crunchers the set has catalogued. The header was deduced from the files, not from a format description, and it validates on all nine:

+0   char[4]  'PaCK'
+4   UWORD    0            (zero on all nine)
+6   ULONG    unpacked size
+10  ULONG    packed size
+14  ...      packed bytes, exactly `packed` of them

14 + packed == filesize on 9 of 9. And the disc supplies its own control: four of the nine have a same-named sibling in RAW/, and the declared unpacked size equals that sibling's byte count exactly in all four cases.

file on disc declared unpacked RAW/ sibling match
Cargo.bob 1,626 2,868 2,868 yes
Enemy02.bob 2,700 3,570 3,570 yes
NEMESIS.bob 950 2,208 2,208 yes
tausta1.frames(64x488) 9,458 11,712 11,712 yes
TOKENpuff 526 1,176
TOKENpuffM 216 392
megablast0.bob 534 852
megablast1.bob 402 708
megablast2.bob 208 480

9 files, 16,620 bytes on disc, 23,966 declared, 1.44×.

The codec is not decoded — the header is proven, the stream is not. That is open question 3, and the honest statement is that PaCK is a ninth container here, and a candidate ninth cruncher only once someone reads the stream.

The CRM/ and AGACRM/ directory names mean crunched, and RAW/ and AGARAW/ are the same assets unpacked: the demo ships both forms of four assets side by side.

CrunchMania, and an inherited tool that lies

Two files in /SuperStardust/AGACRM begin CrM!. The CrunchMania header documented on the Gloom disc parses both exactly:

                    leeway  unpacked  packed   14+packed  filesize
MAPMONITOR             0      61,440   18,020    18,034    18,034  ✓
MAPMONITORSCREENS      0     102,400   16,480    16,494    16,494  ✓

34,528 bytes on disc declaring 163,840 out, 4.75×.

And the inherited decoder returns a wrong negative. cd32-gloom-doc's crm.py implements CrM2 and its CrM! branch is the literal line

raise NotImplementedError("CrM! stream: no file on the disc uses it")

— a correct statement about Gloom and a false one here. Run it on this disc and it reports 0 decrunched, 2 failed. That is the fourth time an inherited tool with an assumption inside has produced a clean negative on the wrong disc (after akiko.py on Universe and containers.py and audiotracks.py on Gunship), and the checklist's rule — verify the assumption inside the tool before writing the negative — is what caught it. The two streams remain undecoded: open question 2.

Note what would have happened without the rule: the inherited containers.py and unpackall.py decode only RNC method 1, and this disc has no RNC at all, so those two report zero streams on a disc with 158. They were not used. packscan2.py is codec-agnostic and was written for this disc.

The band

Three figures, every one stated against a named denominator.

Files On disc Resident Used Expansion Slack
Superfrog 122 4,102,748 13,336,690 12,853,499 3.25× 3.62 %
Super Stardust (demo) 64 3,221,772 ≥3,358,430 3,218,518 ≥1.04× 0.10 %
Arcade Pool (demo) 14 601,518 645,598 645,582 1.07× 0.002 %
system 21 200,527 200,527 200,518 1.00× 0.004 %
whole disc 221 8,126,565 17,404,587 16,918,117 2.14× 2.80 %

Depth of unpacking: 2. Fully exhausted — a third pass finds nothing. Super Stardust's resident figure is a lower bound because the two CrM! and nine PaCK streams are declared and not decoded; adding their declared sizes gives ≥3,358,430. Superfrog's figure has no such caveat: every container in its tree decoded.

The ceiling breaks, on one of the two readings

P5 predicted the band would hold, by route (a) "the disc barely compresses" or (c) "the expansion factor is at the bottom of the scale". Both are wrong. Superfrog expands 3.25×, the second-highest factor in the set after Dragonstone's 3.78×, and well above the 1.58× that the arithmetic said was the break-even.

What saves the on-disc reading is route (b), the one the prediction ranked last: 49.5 % of this disc is not Superfrog. Strip the two demos and the system files and the game is 4.10 MB on disc, comfortably inside the 2.25–13.3 band.

The resident reading does not survive:

Superfrog resident        13,336,690
Marvin, previous maximum  13,251,697
excess                    +84,993 bytes = +0.64 %

So the resident band's ceiling is broken, by 0.64 %, and the on-disc band's is not. The two readings now return different verdicts about the same disc.

That is the honest way to state it, and it matters more than picking the louder half. The checklist already says the band is coherent only as an on-disc measurement, because Banshee and Dragonstone are 6 % apart on disc and 80 % apart unpacked. Superfrog is the first disc where following that rule changes the answer rather than merely tidying it: on-disc says "inside", resident says "new maximum", and both are correctly computed. Keep them separate, quote the denominator, and do not average them.

A caveat that belongs with the number rather than in a footnote: "resident" means bytes after every container is expanded, which is a property of the corpus, not of peak RAM. SFIntro alone accounts for 3,290,454 resident bytes and the game plainly does not hold all of them at once. The figure is comparable across discs because it is defined the same way for all of them; it is not a memory-footprint claim.

Slack

Resident minus used, where "used" is bytes up to the last non-zero byte of each resident piece. The checklist asks for the number, for the per-file view, and for no third hypothesis. All three:

Whole disc 2.80 %. Superfrog alone 3.62 %. That puts it between Banshee (3.9 %) and Universe (0.5 %) and makes it the sixth point on the scale, which now runs Gunship 0.004 %, Fire & Ice 0.3 %, Universe 0.5 %, Superfrog 3.6 %, Banshee 3.9 %, Dragonstone 9.1 %.

Per file, the slack is not spread evenly — it is concentrated, as it was on Universe:

file resident used slack %
SFData/LWMS 26,880 10,222 16,658 61.97 %
SFData/L4MS 26,880 13,426 13,454 50.05 %
SFData/LBMA 53,588 31,988 21,600 40.31 %
SFData/S2BO 204,800 167,034 37,766 18.44 %
SFData/LWMA1 53,588 47,149 6,439 12.02 %
SFData/L3MS 26,880 23,664 3,216 11.96 %
SFData/S1BO 204,800 182,011 22,789 11.13 %
SFIntro 3,290,454 3,095,105 195,349 5.94 %
SFData/S3BO 1,229,178 1,171,408 57,770 4.70 %

And the reason the per-file view is worth taking is visible in it directly: every MS file unpacks to exactly 26,880 bytes and every MA file to exactly 53,588, regardless of content. Seven and twenty-four files respectively, all landing on the same two numbers to the byte. These are fixed-size buffers, and the slack is the unused tail of a fixed allocation — which is the checklist's "expect fixed sizes, packed or not" observation showing up as the dominant term in the slack figure rather than as a side note.

No third hypothesis is offered. The number is 3.62 % for the game, it is concentrated in fixed-size level buffers, and that is the finding.

The near-duplicate inside two containers

SFData/INTROC unpacks to 101,528 bytes. SFIntro has a raw gap at offset 650,256 that is 101,596 bytes. Align them and the second contains the first starting 56 bytes in, with 99.9576 % of 101,528 bytes identical — 43 bytes differ.

So roughly 101 KB of intro payload exists twice on the disc, once as a standalone packed file and once inside the intro executable, in two builds that differ in 43 bytes. Byte-identical duplication is zero on this disc (01); near-identical duplication inside containers is not, and it is only visible after unpacking.