On this page

A labor of love by Hunter Davis. The pack manifest is the JSON file that sits next to each compiled FG2 pack and describes what is inside it. The pack payload is the binary the PS1 reads at runtime; the manifest is what every other tool in the pipeline reads to reason about that binary. CD image builders read manifests to decide layout. The regtest harness reads them to check that the right packs are present. Pack-planning tools read them to estimate peak memory and pick pack granularity. Runtime sanity checks read a trimmed subset of them to assert “the pack on disc is the pack we built.”

It is intentionally a JSON manifest, not a binary on-disc format. From the source schema:

Define the minimum manifest contract for Phase 5 pack compilation work so that analyzer-derived planning tools emit a stable intermediate format, pack compiler work can start before the final binary pack layout is chosen, and transition and prefetch studies target the same field names.

The schema is on schema_version: 1, draft. It is producible from analyzer output plus post-processing tools and consumable without scraping text reports. Pack granularity is still open — one manifest can cover one ADS tag, one scene cluster, or a hybrid pack with a shared bank.

If you paid for this, you were cheated. Open source and free.

Required top-level fields

Every manifest must carry these keys:

Field Type Notes
schema_version integer Currently 1.
manifest_kind string "scene_pack_manifest".
pack_id string Stable identifier for this pack across rebuilds.
pack_strategy string enum "ads_cluster", "scene_tag", or "hybrid".
scene_indices array of integer Story-order scene indices this pack covers.
ads_names array of string Source ADS file names. Often a single entry.
resources object See resources below.
runtime_requirements object See runtime requirements below.
transition_hints object See transition hints below.
prefetch_hints object See prefetch hints below.

pack_strategy is the planner’s intent — why these scenes were grouped into one pack. ads_cluster means “all scenes that share an ADS family landed here.” scene_tag means “this is one tag’s worth of scenes.” hybrid means “this pack plus a separately-loaded shared bank covers the working set.”

resources

The resources section describes what the pack needs available for deterministic runtime use. Each sub-list is the set of asset names the pack depends on — the manifest does not embed the assets, only references them.

"resources": {
  "bmps": ["GJDIVE.BMP", "MJDIVE.BMP", "JOHNWALK.BMP"],
  "scrs": ["ISLETEMP.SCR"],
  "ttms": ["GJDIVE.TTM", "MJDIVE.TTM"],
  "ads":  ["ACTIVITY.ADS"],
  "shared_candidates": ["JOHNWALK.BMP"]
}
Key Meaning
bmps BMP names the pack references.
scrs SCR (background) names.
ttms TTM (animation script) names.
ads ADS (scene controller script) names.
shared_candidates Resources the planner thinks should move into a shared bank later.

shared_candidates is purely advisory. If a BMP appears in many packs (e.g. JOHNWALK.BMP, which is on screen in nearly every walking scene), the manifest tags it for later deduplication. Today nothing actually deduplicates them — the source schema notes “no shared-bank deduplication is applied across packs yet.”

runtime_requirements

The runtime envelope the pack must satisfy. If the pack covers multiple scenes, these values represent the envelope for the whole pack, not a single scene.

"runtime_requirements": {
  "max_concurrent_threads": 20,
  "max_sprite_frames": 160,
  "peak_memory_bytes": 436592,
  "memory_components": {
    "bmp_indexed_bytes": 228598,
    "ttm_bytes": 7288,
    "ads_bytes": 2528,
    "scr_bytes": 112038,
    "sprite_pointer_bytes": 640,
    "ttm_slot_overhead_bytes": 49152
  }
}
Field Meaning
max_concurrent_threads Peak active TTM thread count this pack ever needs.
max_sprite_frames Peak frame count across all sprite slots.
peak_memory_bytes Top-line worst-case memory for the pack.
memory_components.* Breakdown by source — which subsystem owns what fraction of peak.

The pack-planning tools use peak_memory_bytes to decide whether a candidate pack fits the PS1’s working budget. The runtime does not allocate against these numbers directly — it allocates against fixed buffers — but if a pack reports peak_memory_bytes larger than the fixed buffer, that is a sign the pack will not fit and the planner should split it.

transition_hints

Pack-boundary and load-time planning data. Generated post-analysis from the story-order graph and resource churn between scenes.

"transition_hints": {
  "likely_next_pack_ids": ["ads-cluster-building"],
  "high_churn_transitions": [
    { "from_scene_index": 9, "to_scene_index": 10, "total_resource_churn": 12 }
  ],
  "transition_budget_notes": "Derived from analyzer post-processing; not yet runtime-validated."
}
Field Meaning
likely_next_pack_ids Pack IDs the runtime is likely to load next. Feeds prefetch.
high_churn_transitions Scene-to-scene transitions where many resources change at once.
transition_budget_notes Optional freeform note about heuristics in use.

These are heuristic. The schema doc is explicit that scene ordering is the analyzer’s story order, not an instrumented runtime trace. When the project gains validated transition telemetry the hints should be regenerated from real data.

prefetch_hints

Early streaming policy work. Drives the transition prefetch schema.

"prefetch_hints": {
  "candidate_scene_indices": [3, 4, 5],
  "additional_bmps": ["FISHPOLE.BMP"],
  "additional_scrs": [],
  "additional_ttms": ["GJNAT3.TTM"],
  "heuristic": "ads_family_union",
  "confidence": "low"
}
Field Meaning
candidate_scene_indices Scene indices likely to follow the current pack.
additional_bmps / _scrs / _ttms Resources to consider warming.
heuristic Name of the rule that produced this hint.
confidence low, medium, or high.

Current expectation per the schema: “all initial prefetch hints should be treated as heuristic; confidence should stay low until we have validated transition data.” Today every hint is low. Promoting hints to medium or high requires runtime telemetry that does not exist yet.

Example manifest

A complete manifest for an ADS-cluster pack:

{
  "schema_version": 1,
  "manifest_kind": "scene_pack_manifest",
  "pack_id": "ads-cluster-activity",
  "pack_strategy": "ads_cluster",
  "scene_indices": [0, 1, 2],
  "ads_names": ["ACTIVITY.ADS"],
  "resources": {
    "bmps": ["GJDIVE.BMP", "MJDIVE.BMP", "JOHNWALK.BMP"],
    "scrs": ["ISLETEMP.SCR"],
    "ttms": ["GJDIVE.TTM", "MJDIVE.TTM"],
    "ads": ["ACTIVITY.ADS"],
    "shared_candidates": ["JOHNWALK.BMP"]
  },
  "runtime_requirements": {
    "max_concurrent_threads": 20,
    "max_sprite_frames": 160,
    "peak_memory_bytes": 436592,
    "memory_components": {
      "bmp_indexed_bytes": 228598,
      "ttm_bytes": 7288,
      "ads_bytes": 2528,
      "scr_bytes": 112038,
      "sprite_pointer_bytes": 640,
      "ttm_slot_overhead_bytes": 49152
    }
  },
  "transition_hints": {
    "likely_next_pack_ids": ["ads-cluster-building"],
    "high_churn_transitions": [
      { "from_scene_index": 9, "to_scene_index": 10, "total_resource_churn": 12 }
    ],
    "transition_budget_notes": "Derived from analyzer post-processing; not yet runtime-validated."
  },
  "prefetch_hints": {
    "candidate_scene_indices": [3, 4, 5],
    "additional_bmps": ["FISHPOLE.BMP"],
    "additional_scrs": [],
    "additional_ttms": ["GJNAT3.TTM"],
    "heuristic": "ads_family_union",
    "confidence": "low"
  }
}

The pack compiler emits this alongside pack_payload.bin. The bytes described in the manifest’s resources lists end up referenced from pack_index.json — see the pack payload reference for the relationship between the binary and its index.

How the manifest is consumed

Three readers, three jobs.

CD image build. mkpsxiso does not read the manifest itself, but the cd_layout.xml generator does. It walks the manifest set, picks which FG\*.FG2 payloads to stamp onto the disc, and orders sectors so that packs the manifest marks as likely_next_pack_ids of each other land near each other on the spiral. This is best-effort — the layout file ships hand-curated for the pilot scenes — but the manifest is the source of truth when the layout gets regenerated.

Regtest harness. The regtest tooling reads each manifest at startup to know which scene indices a pack is supposed to cover. When a regtest run captures a screenshot for scene N, the harness looks up the manifest that owns N and asserts the pack actually loaded. If the pack loaded but the dirty-rect bookkeeping disagrees with the manifest’s runtime_requirements, the run is flagged for review.

Runtime sanity checks. The PS1 build does not parse JSON. It consumes a trimmed binary index (pack_index.json written into a fixed-shape sidecar) that carries only the fields the runtime cares about: pack ID, scene indices, magic + version of the corresponding FG2 payload. A mismatch between disc and index is logged to TTY and the scene falls back to the legacy ADS path.

Known limitations

Direct from the schema doc:

  • the research sidecar index is still JSON
  • no shared-bank deduplication is applied across packs yet
  • no checksum validation is performed on-console yet

The runtime does not currently verify SHA256 of the pack payload against the sha256 field that pack_index.json carries per entry. That check exists only in the regtest harness today.

Source on GitHub