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:
forgery_pack.sg— assembles a Relic from source ROM + optional BIOS + optional save + metadataforgery_unpack.sg— validates and extracts sections, returning capability-bounded handles to the callerforgery_verify.sg— validates a Relic in place without extracting; called by the launcher beforerp_launch
Key properties:
- Self-contained — one file holds everything needed to load and play a game
- Typed per system — the file extension encodes the target system (
.nessfor NES,.sfcsfor SNES,.zipsfor ZX Spectrum); the launcher reads the extension to select the correct emulator core - Capability-secured —
forgery_verify.sghash-checks content before any capability is granted to the emulator core; the core receives a read-only sub-range cap to the ROM section only - Immutable layout — the header and section table are read-only at runtime; the emulator core has no write path to any section of the Relic file
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 0x05–0xFF are reserved for future versions; forgery_verify.sg emits FORG_WARN_UNKNOWN_SECT but does not reject on unknown types.
0x01 ROMrp_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 BIOSforgery_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 SAVEforgery_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 METAkey=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:
- Read and validate ROM file; strip SMC header if present (
.sfcs) - Compute CRC32 of ROM data; embed as
crc32=in META - Call
forgery_header_write— write 16-byte header with correctSECTION_COUNTandTOTAL_SIZE - Call
section_table_write— write section table entries with computed offsets and sizes - 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:
- Validate magic (
FORG), version (0x01), reserved fields (zero) - Validate
TOTAL_SIZEagainst actual file size - Validate section table: no overlaps, all offsets + sizes within
TOTAL_SIZE - Recompute CRC32 of ROM section; compare against
crc32=in META - Extract sections to a per-cap staging area
- 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):
- Magic bytes equal
FORG - Version byte is
0x01 - Reserved header bytes are zero
TOTAL_SIZEmatches actual file size- Section table coherence: no section extends past
TOTAL_SIZE; no two sections overlap - CRC32 of ROM section matches
crc32=value in META section
Capability model
Relic I/O is capability-gated at every stage. No ambient authority is assumed.
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.
[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_required = 1. Scoped to the BIOS section byte range. Not issued for NES or SNES (no BIOS section present or required).
--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.