Skip to content

@seer-project/pipeline

Node-only utilities for the offline data extraction pipeline.

Pre-1.0 — expect breaking changes. Seer is at 0.x, and under semver that means no compatibility promise: a minor bump may rename exports or change signatures. Pin an exact version if you need reproducible builds, and read the changelog before upgrading. Details: https://seer.shaid.net/start-here/project-status/.

Handles the conversion of original game data files (executables, ROMs, resource archives) into web-native formats (JSON + PNG) via a configurable two-stage pipeline. Node.js only — never imported by browser-bundled code.

Terminal window
npm install @seer-project/pipeline

Requires pngjs ^7.0.0 as a peer dependency (for PNG writing).

Terminal window
npx seer <command> [options]
Command Description
extract [--game <id>] [--platform <id>] [--data-dir <path>] Run the offline extraction pipeline
hex-dump <file> [offset] [length] Inspect binary file contents as hex + ASCII
doctor [--data-dir <path>] Sanity-check the resolved config against disk
--help, -h Show usage

The CLI reads seer.config.ts (or .js / .mjs) from the project root. The config file exports a GameConfig[] array defining games and their platforms:

import { defineGameConfig } from '@seer-project/pipeline';
export default defineGameConfig([{
id: 'mygame',
displayName: 'My Game',
platforms: [{
platform: 'amiga',
dataDirs: ['mygame/amiga'],
executable: 'mygame',
expectedFiles: ['mygame'],
supported: true,
assetDir: 'mygame',
exportGameData: async (cfg, dataDir) => { /* ... */ },
buildAssets: async (cfg, dataDir) => { /* ... */ },
}],
}]);
  1. Stage 1 — exportGameData: Parse game executables/data tables and write raw JSON to data/extracted/<game>/.
  2. Stage 2 — buildAssets: Decode resource files into web-native PNG + JSON assets in public/assets/<game>/<platform>/.
import { runPipeline } from '@seer-project/pipeline';
import { defineGameConfig } from '@seer-project/pipeline';
const configs = defineGameConfig([/* ... */]);
const results = await runPipeline(configs, { game: 'mygame', platform: 'amiga' });
Function Description
defineGameConfig(config) Typed wrapper for GameConfig[]
flattenConfigs(configs) Flatten nested GameConfig[] to PlatformConfig[]
resolveDataDir(platformConfig, dataDir?) Find the data directory on disk
findFileCI(dir, name) Case-insensitive filename lookup
resType(platformConfig, logical) Map a logical resource type (e.g. 'imag') to its platform-specific code via platformConfig.typeCodes, uppercased fallback if unmapped
Function Description
readBinary(path) Read a file as Uint8Array
writeJson(path, data, pretty?) Write data as formatted JSON
writePNG(path, rgba, width, height) Write an RGBA PNG image
writeIndexedPNG(path, indices, width, height, opts?) Write a palette-indexed PNG (index value in the R channel — not resolved colors). opts.transparentIndex (default 0) picks which index renders transparent; pass null to make every index opaque
writeWav(path, channels, opts) Write PCM samples as a RIFF/WAVE file. channels is one array per channel ([mono] or [left, right]); opts.bits: 8 expects raw Uint8Array samples copied byte-for-byte, opts.bits: 16 (default) expects normalized Float32Array samples in [-1, 1], quantized to 16-bit PCM
resolveDataFile(dataDir, candidates) First matching filename from a list of casing candidates, or the first candidate if none exist
scanFilesByExtension(dir, ext) Find files by extension, case-insensitive, sorted
Function Description
decompressLZEXE(input) Decompress an LZEXE v0.91-compressed DOS executable, returning { data, bodyOffset } — a faithful port of the classic unlzexe.c. Common on DOS-era executables; throws if input isn’t v0.91-shaped.
import { decompressLZEXE } from '@seer-project/pipeline';
const compressed = readBinary('GAME.EXE');
const { data, bodyOffset } = decompressLZEXE(compressed);
Terminal window
npm test
npm run lint

Seer exists to reverse-engineer other people’s work, and that is only possible because the preservation and romhacking communities published what they found instead of keeping it. The licence is chosen so that keeps happening: build on Seer and your work stays open too, so the next person gets the same head start.

  • AGPL-3.0-or-later — free for personal, educational and open-source use. Note that the AGPL extends copyleft to network use: run a public web app or hosted service on this and you must publish your application’s source under the AGPL.
  • Commercial licence — waives that requirement so a proprietary or closed-source product can keep its codebase private. Flat-fee and subscription terms are available, and custom terms are negotiable.

If the copyleft doesn’t fit what you’re building, we would much rather have the conversation than have you walk away — email dr.shaid@gmail.com with the subject [Commercial License Request - Project Name].

Full details: https://seer.shaid.net/start-here/licensing/.