SPEC · FORGE · v1.0

Relics Specification v1.0

The Sigil Forge ROM container format — self-contained, capability-secured, per-system typed.

Overview

A Relic is a single binary file containing a complete playable game: the ROM, save state, BIOS data (if required), and metadata. The name fits — a Relic preserves a whole piece of gaming history in one self-contained, verifiable artifact. Relics are the native game container format for sigilOS RetroPie, assembled by the Forge toolchain.

Three Sigil tools form the Forge toolchain:

Key properties:


File extensions

Each supported system maps to a dedicated extension. The extension is the authoritative system selector — it is not advisory.

Extension System Notes
.ness Nintendo Entertainment System iNES / NES 2.0 ROM format; BIOS section not required
.sfcs Super Nintendo Entertainment System SFC / SNES ROM; headered or headerless
.zips ZX Spectrum .z80 / .tap snapshot in ROM section

Additional per-system extensions follow the same pattern when new systems are registered in the RetroPie 73-system catalog. The extension suffix is always lower-case and 4–5 characters. New extensions must be registered in sys_launch.sg alongside their system ID.


Binary structure

Header (16 bytes, fixed)

Offset  Size  Field           Value / Notes
------  ----  -----           -------------
0x00    4B    FORG_MAGIC   'F' 'O' 'R' 'G'  (0x46 0x4F 0x52 0x47, big-endian ASCII)
0x04    1B    VERSION         0x01  (v1; this document)
0x05    2B    SYS_ID          LE u16; matches RetroPie 73-system catalog ID
0x07    1B    SECTION_COUNT   number of sections; valid range 1–16
0x08    4B    TOTAL_SIZE      total file size in bytes including this header (LE u32)
0x0C    4B    RESERVED        must be 0x00000000; rejected by forgery_verify.sg if non-zero

The header is always exactly 16 bytes. No padding follows it; the section table begins at offset 0x10.

Section table

Immediately follows the header at offset 0x10.
Each entry is 12 bytes. SECTION_COUNT entries are present.

Offset within entry  Size  Field        Notes
--------------------  ----  -----        -----
0x00                  1B    SECT_TYPE    section type byte (see Section Types below)
0x01                  3B    RESERVED     must be zero
0x04                  4B    SECT_OFFSET  byte offset from file start to this section's data (LE u32)
0x08                  4B    SECT_SIZE    byte size of section data (LE u32)

Section data follows the section table, ordered by SECT_OFFSET ascending. No alignment requirement — sections may be packed immediately after the table. Gaps between sections are permitted but not produced by forgery_pack.sg. Sections must not overlap; forgery_verify.sg rejects any Relic where a section's byte range intersects another's.

Diagram

+------------------+  0x00
|   FORG_MAGIC  |  4B  'F''O''R''G'
|   VERSION        |  1B  0x01
|   SYS_ID         |  2B  LE u16
|   SECTION_COUNT  |  1B  1–16
|   TOTAL_SIZE     |  4B  LE u32
|   RESERVED       |  4B  zero
+------------------+  0x10   ← header end (16 bytes)
|   SECT entry 0   |  12B
|   SECT entry 1   |  12B
|   ...            |
|   SECT entry N-1 |  12B
+------------------+  0x10 + (SECTION_COUNT × 12)   ← table end
|   [section data] |  packed, in SECT_OFFSET order
|   ...            |
+------------------+  TOTAL_SIZE

Section types

Four section types are defined in v1. A section of type 0x01 (ROM) is mandatory; all others are optional. Section type bytes 0x050xFF are reserved for future versions; forgery_verify.sg emits FORG_WARN_UNKNOWN_SECT but does not reject on unknown types.

0x01 ROM
Required. The raw ROM image. A read-only sub-range capability is derived from this section's byte range and handed to the emulator core via rp_launch. The core cannot read outside this section — it has no cap to META, BIOS, or SAVE. For .ness: iNES file content (header + PRG + CHR). For .sfcs: bare SFC image (with or without SMC header — forgery_pack.sg strips 512-byte headers). For .zips: the .z80 snapshot verbatim.
0x02 BIOS
Optional. System BIOS image. Required by some systems (e.g. PlayStation, Saturn, Famicom Disk System). Stored as a separate section so forgery_verify.sg can hash-check it independently from the ROM section. The BIOS cap is issued to the emulator core only for systems that declare bios_required = 1 in their core manifest.
0x03 SAVE
Optional. Embedded save state. Produced when forgery_pack.sg --embed-save is passed a save file. Loaded by the emulator via core_load_state() immediately after ROM load. The SAVE section is written by the save path through forgery_pack.sg — the core never receives a write cap to the Relic file directly.
0x04 META
Required. UTF-8 key=value pairs, one per line, \n-terminated. Required keys: title, sys, forge_version. Optional keys: region, crc32, hash_sha256, year. The crc32 value is the CRC32 (IEEE 802.3 polynomial) of the ROM section data; forgery_verify.sg recomputes and compares it. Unknown keys are ignored by v1 tooling.

Tools

forgery_pack.sg

Assembles a Relic from source files. Accepts a ROM path (required), optional BIOS path, optional save state path, and key=value metadata pairs on the command line. Execution order:

  1. Read and validate ROM file; strip SMC header if present (.sfcs)
  2. Compute CRC32 of ROM data; embed as crc32= in META
  3. Call forgery_header_write — write 16-byte header with correct SECTION_COUNT and TOTAL_SIZE
  4. Call section_table_write — write section table entries with computed offsets and sizes
  5. Call section_data_write — write ROM, then BIOS (if any), then SAVE (if any), then META

Output file path is derived from the input ROM path with the per-system extension substituted. The --out flag overrides the output path. forgery_pack.sg requires a write cap to the output directory and a read cap to each input file.

forgery_unpack.sg

Validates and extracts a Relic. Steps:

  1. Validate magic (FORG), version (0x01), reserved fields (zero)
  2. Validate TOTAL_SIZE against actual file size
  3. Validate section table: no overlaps, all offsets + sizes within TOTAL_SIZE
  4. Recompute CRC32 of ROM section; compare against crc32= in META
  5. Extract sections to a per-cap staging area
  6. Return ROM cap (read-only sub-range) to caller; BIOS cap if BIOS section present; META key-value map

The caller receives no file path — only capability handles. The staging area is an ephemeral mount visible only within the scope of the unpack call.

forgery_verify.sg

Validates a Relic without extracting any section data. Intended for use by the launcher immediately before rp_launch. Returns an integer error code; zero is FORG_OK. Verification is read-only: no staging area is created, no caps are issued. Requires only a read cap to the Relic file.

Checks performed (in order):


Capability model

Relic I/O is capability-gated at every stage. No ambient authority is assumed.

Relic read cap
Required by forgery_unpack.sg and forgery_verify.sg. Issued from the ROM library mount point — the caller receives a read cap scoped to the specific Relic file, not to the directory. Derived from CAP_FS_RD on the ROM library.
ROM section cap
Read-only sub-range cap derived from the Relic read cap; covers exactly [SECT_OFFSET, SECT_OFFSET + SECT_SIZE) of the ROM section. Handed to the emulator core via rp_launch. The core cannot address outside this range — it cannot read META, BIOS, or SAVE, and cannot read adjacent bytes in the Relic file.
BIOS section cap
Read-only sub-range cap issued to the emulator core only when the core's manifest declares bios_required = 1. Scoped to the BIOS section byte range. Not issued for NES or SNES (no BIOS section present or required).
Save write cap
Issued only when --embed-save is active. The EL0 save path is: core_save_state()forgery_pack.sg (re-packs the Relic with updated SAVE section) → write cap to the output path. The emulator core has no direct write cap to the Relic file at any point.

Verification test vectors

The following test vectors define the expected return values from forgery_verify.sg. All implementations must pass these cases.

Test Input condition Expected return
FVRF-1 Valid .ness Relic; CRC32 in META matches computed CRC32 of ROM section FORG_OK (0)
FVRF-2 Truncated file — TOTAL_SIZE in header exceeds actual file size on disk FORG_ERR_SIZE
FVRF-3 Magic bytes do not equal 'F''O''R''G' (e.g. first byte is 0x00) FORG_ERR_MAGIC
FVRF-4 CRC32 in META does not match CRC32 recomputed over ROM section bytes FORG_ERR_CRC
FVRF-5 Section overlap: SECT_OFFSET[1] < SECT_OFFSET[0] + SECT_SIZE[0] FORG_ERR_LAYOUT

Error codes are returned as signed 32-bit integers. FORG_OK is 0. All error codes are negative. The specific numeric assignments for error codes are defined in forgery_verify.sg and are not part of this spec — only the symbolic names above are normative.