Skip to content

Latest commit

 

History

History
96 lines (65 loc) · 20.1 KB

runtime-module.md

File metadata and controls

96 lines (65 loc) · 20.1 KB

Runtime configuration and preparation

The runtime module is the source of game interpretation for both review and play. The generated support catalog lists the current platforms, product cores, Provider Targets and bindings. Catalog membership and product acceptance evidence are recorded separately.

RuntimeConfiguration contains content and optional cores. Content identifies a single file, arcade archive, DOS bundle and optional internal executable, or a project. RPG Maker projects select the actual one of seven engine implementations. Per-core rules contain declared Target options, arcade Parent logical paths and an optional romSet short name from that core’s paired DAT. cores[coreId].romSet stays inside the existing game JSON and cannot be borrowed by another core. BIOS file IDs, installation state, import batches, deployment revisions and authorization credentials never enter this JSON.

configureRuntime constructs these rules from immutable logical path/size/SHA facts and bounded evidence. RPG Maker uses its real JS marker, Game.ini Scripts field or LCF system identifier. Multiple MV/MZ projects of the same engine default to a unique root index.html; all valid project entries remain selectable. Mixed engines or multiple projects without a unique root entry require an explicit choice. DOS selects an actual executable member when one is explicit or uniquely detected. A package with several programs omits entryPath and starts the core's program menu; an explicit entryPath must be nonempty and identify a real archive member. Empty strings and null are invalid configuration states. ONS, KiriKiri and Cave Story choose their ordinary script/archive/executable entries. ScummVM runs the pinned native detector and returns explicit candidates; a blocked or ambiguous candidate requires an operator choice rather than a host engine guess. Every stored core option is validated against its actual Target schema before returning a configuration.

ZIP member names use explicit UTF-8 or a CRC-verified Info-ZIP Unicode Path field first. Unmarked names are decoded as strict UTF-8, then GB18030 for legacy Chinese archives, with CP437 as the remaining byte fallback. DOS candidates, configuration and preparation share this decoding and retain the readable canonical member path. DOSBox cannot address Unicode names directly, so its bounded archive view assigns non-ASCII path segments deterministic, collision-checked ASCII 8.3 aliases and maps an explicit program choice to the same native path. Existing ASCII segments remain unchanged. Only the virtual central directory and optional AUTOBOOT.DBP are appended; original compressed payloads, CAS bytes and game content identity remain unchanged. The default program menu uses the same native aliases.

prepareRuntime selects the declared implementation using directory platform, selected core and game content. It freezes coreId, providerId, targetId, coreFingerprint and romHash, then returns selected options, checkpoint semantics, BIOS requirements, actual arcade dependencies and generic resource plans. The host projects these plans into authorized resource URLs. paths maps logical file keys to virtual paths; encoding: "ZIP" requests an ordinary deterministic STORE archive. MKXP receives the complete project ZIP, DOS receives its ZIP as game.zip, file-tree cores receive paths relative to the project root, and a single CUE receives its verified referenced track files. Each Parent configured for the selected core is delivered as an independent original ZIP with its canonical virtual filename. The runtime mounts those ZIP bytes directly for arcade driver lookup; neither the host nor adapter merges, repackages or expands their members.

ROM identity is calculated from the entire Game active file set, including Parent files needed by any configured core. A single non-project ROM uses its raw SHA-256. Projects, including singleton trees, and multi-file content use the canonical sorted logical path, byte size and SHA-256 tree encoding. Media rows remain outside this set. Selecting another core does not change the Game content identity.

BIOS requirements have stable explicit keys, known size and digest facts, delivery paths and activation options. Requirements share a file only where declarations explicitly share the same key. A scan matches known digests or an unambiguous declared name/path when no digest is known; hosts skip ambiguous missing requirements. Arcade DAT facts inspect actual ZIP members, accept merged game content and identify missing Parent sets even if the game rules omit them. Required BIOS and ROM-bearing device firmware are always delivered independently according to the selected core's DAT, even when matching firmware members are embedded in the game or Parent archive. The compressed fact table retains each pinned source digest in src/runtime/arcade-provenance.json; it contains metadata, never ROM or firmware bytes.

python3 scripts/arcade-facts.py --refresh --dat CORE=PATH regenerates required ROM facts and searchable labels from the exact DAT digest pinned in src/runtime/arcade-provenance.json. MAME and other XML DAT generators share optional/default BIOS/NO_DUMP semantics; refresh never imports DAT into a database. FBNeo's paired required-only DAT excludes BRF_OPT at export.

When adding native MAME drivers, run scripts/mame-facts.py against the clean PFB core candidate. It verifies the generated DAT and build metadata, updates only the MAME catalog/provenance, and derives the Arcade asset family list from that same candidate so launch assets and Host facts cover the same machines. The generated MAME JavaScript loader remains digest-verified and bounded to 4 MiB in candidate packaging and browser loading.

For Beetle PSX HW, playable preparation reads only the disc system area (at most 11,760 bytes) from the managed locator. Its sector-four license selects the declared Japanese, US or European firmware requirement; the region never becomes stored game JSON. Unknown compressed-disc evidence leaves all declared regional files optional for the core to select from the available installation, so it does not invent a Japanese default. The stable firmware names, sizes and MD5s follow the core's official firmware declaration.

EasyRPG converts the authorized public file-tree index into the pinned engine's private index and scoped XHR aliases. The canonical host index URL remains unchanged; every game payload still goes through shared Content IO. Tyrano's isolated bridge redirects desktop file-storage calls to the engine's own browser-storage functions before initialization. This preserves its missing-value and serialization rules without editing the managed project or adding an engine branch to the host.

The Target coreFingerprint hashes the Provider/Target identity and the sorted content digests of its explicitly declared coreAssetPaths. Runtime adapter source, public bridges, frontend/input/display code, release labels, deployment paths and Bundle/Module digests do not enter save identity. Paths remain in the diagnostic inputs but are not hashed. Packaged core bytes must exist in the verified asset index; changing a core asset affects only Targets declaring it. RPG Maker MV/MZ and TyranoScript explicitly declare an empty packaged core set because the complete game content hash already covers their engine. Existing Bundle/Module integrity checks still cover all deployed bytes.

Checkpoint format and restore semantics are maintained explicitly: an incompatible codec or restore change must update the affected Target's checkpoint format even if core files are unchanged. A compatible runtime update keeps the format. No old fingerprint aliases, format readers or data migration are provided. Provider bundles and the PFB watcher use the same generator and publish the resulting map atomically.

Restore requires exact frozen core/provider/Target, core-file fingerprint, complete content hash and current checkpoint format. prepare accepts an optional savedContext with the eight frozen Save extinfo fields. It first checks the current directory permits the saved core, the core bytes and whole active content still match, and the format is readable. It then validates and prepares the frozen content rules and runtime options rather than the game's edited options. A raw single-file identity permits a filename change: its original entry is mapped to the sole current file after its SHA matches. Project trees remain path-sensitive. DOS program selection and ScummVM detector options use this same boundary; the host supplies facts and does not interpret the engine.

Theodore exposes optional thomsonModel with its native model choices. Omitting the option, or selecting Auto, uses the pinned core's original filename detection order. Preparation resolves that to a concrete model in runtimeOptions (the native no-match default is TO8), without adding an inferred model to Game JSON. The source is Theodore autodetect_model. The editable game title never selects a computer model. Saved options retain the original model even after the game configuration changes.

ONS host capture is available only at a stable native dialogue wait with fully drawn text/background, outside menus, transitions, skip and automatic playback. Its native save entry atomically rechecks that condition and serializes the current wait, page, UTF-8 text offset, font cursor and textgosub continuation rather than an earlier user-slot savepoint. Restoring the same instance or a new one continues at that wait; ordinary in-game slots keep their original semantics and serialization buffer. Busy capture returns the existing public BUSY reason. The current core must export _onsyuri_host_checkpoint_ready; the adapter does not supply a default for missing ABI. Only the current public single-gzip checkpoint format is read. Runtime URLs are deployment facts and do not participate in save compatibility. A server losing a short-lived run context must never reconstruct a prior draft with a current hash; the host preserves the original immutable save context and checkpoint bytes until durable storage confirms the matching save.

The browser consumes one createRuntime(envelope, host) and PlayerRuntimeV1. Existing login state authorizes HTTP resources. Run IDs identify resource scope; message nonces correlate RPC. Isolated web projects load their explicit entry through the host shell and bounded STAT/READ bridge, including ordinary relative script/import/Worker/media paths. There are no derived tickets, tokens or runtime cookies. Review can restore an explicit LOCAL descriptor whose bytes are returned by the host; ordinary saves use HTTP descriptors.

Offline host tool

The npm package layout contains scripts/runtime-cli.mjs, its host helpers, dist/runtime/**, immutable JSON facts and assets/facts/**. With production dependencies installed and Node 24, it runs offline without a Git checkout, esbuild or a separate service. Release builds also produce retrom-runtime-host-tool-<tag-version>.tar.gz with that complete layout, production dependency closure and source digest. The host must publish the complete layout as an immutable tool snapshot; running requests must not read a dist directory that an active build cleans. Its catalog and selected Provider manifests come from the same build.

Commands read one bounded JSON request on stdin and write one JSON result to stdout:

  • catalog: Provider manifests, product bindings, named platforms and static BIOS facts; read once at host startup.
  • configure: platformId, coreIds, files, optional evidence/archiveMembers/selection; returns RuntimeConfiguration.
  • discover-content: immutable files and host-only locators; returns {files:[{logicalKey,relativeTo,relativePath}]} for bounded, verified single-disc CUE FILE references, including references already present in the facts. The host skips existing logical keys and authorizes missing files under its source root relative to the original CUE source directory before copying their real bytes. Archive members have no external source mapping; missing members fail. Discovery never supplies authorization or changes the source file.
  • normalize-content: platformId, immutable files, locators and a fresh outputRoot; retained Electron ASAR and NW executable ZIP containers become ordinary web project files through bounded streaming extraction. It never executes native bytes. Returned path is host staging state, never game JSON; the host imports those exact new facts before configuration. Ordinary content returns its unchanged file facts.
  • prepare: directory, config, files, selected core, fingerprint map, optional savedContext and host-only locators; returns identity and resource plans. Arcade inspection reads bounded ZIP central directories.
  • rom-sets: directory, configuration, immutable files and selected core; returns bounded searchable playable DAT entries, the filename match and the selected entry independently of pagination. Optional draft entryFile/romSet affect only this projection. BIOS, device and non-runnable entries are excluded. Labels and versions come from digest-verified original DAT during the build.
  • reconcile-config: validates per-core DAT choices and prunes Parent references that the selected entry no longer needs. It returns removed logical keys only after comparing all cores’ references with the previous configuration; the host uses its existing transactional retirement and delayed reference-safe cleanup. This is configuration validation, not launch preparation or mandatory CRC acceptance.
  • detect-scummvm: verified providerRoot and a bounded treeRoot; returns candidates and optional automaticSelection.
  • assemble-resource: files, locators, optional virtual paths and a fresh outputPath; streams a deterministic STORE ZIP, verifies every file SHA and removes incomplete output on failure.
  • Parent dependencies use EXTERNAL_FILE_SET: each original archive has its own size, SHA and canonical virtual filename. Adapters mount these bytes directly; the host never merges or repackages Parent ZIPs.
  • batch-identity / batch-restorable: process up to 10,000 items in one invocation; list identity does not assemble BIOS, Parent or playable resources.
  • bios-requirements / bios-identify: resolve declarations or identify exact immutable bytes and archive members.
  • batch-content-bios-requirements: inspect up to 100 ordered items with directory, config, files, host-owned locators, optional installed biosFiles, and an optional allowed coreId (directory default otherwise). Each result has {biosRequirements, missingRomMembers, crcWarnings, error}; arrays are always present and errors are stable codes.
  • dos-entry-candidates: accepts config, immutable files and host-owned locators, then returns {entries: string[]} from the selected DOS ZIP/DOSZ archive. Entries retain their complete safe paths, include .exe, .com and .bat without case restrictions, and are deduplicated and sorted by UTF-8 path. The query ignores any existing entryPath and does not prepare a Run, so a replaced archive can offer new choices even when its previous program is absent.

A checkpoint availability with requiredAction: "SELECT_PROGRAM" tells the host to show program selection guidance. Provider-private Target options are interpreted only inside the Provider.

The same executable accepts --serve for one shared persistent host process. Each newline-delimited request is exactly {id, command, input}; id is a nonnegative safe integer or 1–128 printable ASCII characters without spaces, and input is an object. Each reply is {id, result} or {id, error} and may arrive out of request order. The Go host serializes stdin writes and dispatches stdout replies by unique ID, admitting at most 32 concurrent calls including queue time in deadlines. The CLI runs at most four tasks and accepts at most 32 running/queued requests, with 64 MiB individual message and aggregate queued input/output bounds. Its single stdout writer respects backpressure. {cancel: id} cancels only that request; late replies are ignored by the host. Domain errors preserve other requests. Oversized input or a broken transport ends the process, fails pending calls, and a subsequent call starts a fresh process without replay. EOF waits for final queued work. DAT first loads use shared Promises and retain one parsed catalog per process. Parent delivery performs no archive assembly in the CLI. The process owns no sessions, durable queues, authorization or network endpoint.

All DAT-capable arcade targets use one selection and dependency projection. Their declaration chooses either the original ZIP resource’s virtual basename (FBNeo/MAME2003/Plus) or the native machine option (Current MAME). The ROM, Parent and BIOS stored bytes remain intact. Required BIOS is still delivered independently. The selected romSet is frozen in the existing save-context JSON so restoring a save uses its original driver selection. CRC differences remain review warnings and require manual approval; trial execution stays optional.

Provider release builders and the PFB watcher call fingerprintProvider from scripts/runtime-fingerprints.mjs with the current declarations and verified core asset index. The offline host tool reads the integrity-covered result from its selected Provider. Hosts cache startup facts and bound repeated preparation work; they do not launch one Node process per save.

Unit and build gates validate this contract and all retained adapters. Product acceptance remains a separate per-binding exercise: real review and play, frame/input evidence, declared checkpoint semantics, a different run's restore and post-restore input. Product evidence must match the current Provider Bundle and Module digests as well as the core fingerprint; save compatibility alone cannot carry an old runtime's PASS into a new build. Isolation URL loading and loss-of-context draft persistence need actual browser and host fault evidence. A component test, missing-material annotation or declaration count is never a product pass.

Arcade review CRC warnings

The batch review query reads entry/selected Parent ZIP directories and installed firmware evidence against the paired DAT. Required game/Parent/BIOS gaps or wrong sizes prevent publication; named, correctly sized CRC differences remain warnings for manual approval and optional trial play. optional and NO_DUMP entries are excluded; BAD_DUMP with a known CRC remains required. Firmware ownership is separate, unused ancestor ROMs are not required, and each core uses only its own saved romSet.

These completeness facts are audit-only: prepareRuntime retains its existing launch checks. Review never assembles resources, rewrites original archives, or requires a browser trial. Failed inspection is unknown, never an empty successful result.

prepareRuntime and contentBIOSRequirements compute crcWarnings from the current game, explicitly bound Parent and BIOS archives. Named members with the required size remain usable when their CRC differs from the core catalog. Each warning identifies ROM, Parent or BIOS, the archive/member, and expected/actual CRC32. Hosts show these warnings before manual approval and exclude affected games from quick approval. A test launch is optional. Missing members, wrong sizes and unsupported media remain structural errors. No warning is persisted in a game record or database column. Ordinary Parent archives supply game ROMs; firmware is resolved separately from declared BIOS/device ancestry.

Flycast cartridge ZIPs use the selected Provider's SHA-bound compiled requirements catalog. Their contents remain opaque during import and are read by the native ZIP reader. Dreamcast CDI retains its extension and native entry without an automatically generated CUE. CDI selects Flycast's HLE bootstrap because the WASM real-BIOS MIL-CD boot path traps after loading the disc; CHD and arcade keep their existing BIOS options. CDI metadata and track FILE handles use bounded reads with separate seek cursors. Core load receipts reject failed native loads; a successful receipt alone does not establish gameplay compatibility.