@seer/core
Generic binary utilities for browser-based reverse-engineering projects.
Zero runtime dependencies. Browser-safe (no Node built-ins). This is the
foundation package that all other @seer/* packages depend on.
Installation
Section titled “Installation”npm install @seer/coreModules
Section titled “Modules”binary.ts — Low-level byte readers
Section titled “binary.ts — Low-level byte readers”Standalone functions for reading unsigned integers from Uint8Array with
configurable endianness. No format assumptions — safe for any binary target.
import { r8, r16, r24, r32 } from '@seer/core';
const value = r32(data, offset, 'be'); // uint32, big-endian| Function | Width | Notes |
|---|---|---|
r8(data, offset) |
8-bit | Endianness N/A |
r16(data, offset, endian) |
16-bit | 'be' or 'le' |
r24(data, offset, endian) |
24-bit | |
r32(data, offset, endian) |
32-bit | Returns unsigned (>>> 0) |
dataViewOf(data) |
— | Create a DataView over a Uint8Array |
binary-reader.ts — Sequential cursor reader
Section titled “binary-reader.ts — Sequential cursor reader”BinaryReader wraps an ArrayBuffer with a sequential read cursor.
Endianness is a constructor parameter (default big-endian).
import { BinaryReader } from '@seer/core';
const reader = new BinaryReader(buffer, 0, 'be');const magic = reader.readFourCC(); // "FORM"const size = reader.readUint32();const chunk = reader.readBytes(size);| Method | Returns | Description |
|---|---|---|
readUint8() / readInt8() |
number |
8-bit integer |
readUint16() / readInt16() |
number |
16-bit integer |
readUint32() / readInt32() |
number |
32-bit integer |
readFourCC() |
string |
4-byte ASCII chunk ID |
readBytes(n) |
Uint8Array |
Raw byte slice |
readCString(max?) |
string |
Null-terminated ASCII string |
readString(len) |
string |
Fixed-length ASCII string |
subReader(len) |
BinaryReader |
Sub-reader for a byte range |
seek(offset) / skip(bytes) |
void |
Move the cursor |
assets.ts — Runtime asset loader
Section titled “assets.ts — Runtime asset loader”Fetch preprocessed JSON (and text) assets produced by the offline pipeline.
Browser-safe — uses globalThis.fetch, no Node built-ins.
Lives in @seer/core (not @seer/pipeline) to prevent Node-only
dependencies from leaking into browser bundles.
import { loadAssets, type AtlasMeta } from '@seer/core';
interface MyAssets { atlas: AtlasMeta; map: { cols: number };}
const assets = await loadAssets<MyAssets>('/assets/mygame/amiga', { atlas: 'atlas.json', map: 'map.json',});Or use the factory for reusable loading:
import { createAssetLoader } from '@seer/core';
const load = createAssetLoader('/assets/mygame/amiga');const assets = await load<MyAssets>({ atlas: 'atlas.json', map: 'map.json' });atlas.ts — Shared texture-atlas metadata
Section titled “atlas.ts — Shared texture-atlas metadata”The one canonical AtlasMeta/AtlasFrame shape written by every offline
build-assets pipeline and read by both browser runtime and viewer tooling —
a shelf-packed atlas (arbitrarily positioned/sized frames), not a uniform
grid, since real extracted sprite art is essentially never uniformly sized.
import type { AtlasFrame, AtlasMeta } from '@seer/core';
const atlas: AtlasMeta = { width: 256, height: 256, frames: [{ name: 'hero_idle', x: 0, y: 0, w: 32, h: 48 }],};| Type | Description |
|---|---|
AtlasFrame |
{ name, x, y, w, h } — one packed sprite’s position/size |
AtlasMeta |
{ frames: AtlasFrame[], width, height } — one atlas image |
palette.ts — Palette-cycling utility
Section titled “palette.ts — Palette-cycling utility”cyclePalette(colors, start, end, direction) rotates a contiguous
sub-range of a color array by one step, wrapping within that range only —
the classic 8/16-bit-era “colour cycling” animation trick (VGA palette
rotation, Amiga copper-list swaps). Generic over T, no DOM/WebGL/canvas
dependency; the caller owns how the result gets drawn or uploaded.
import { cyclePalette } from '@seer/core';
// Rotate indices 10-13 forward by one step, e.g. once per animation frame.cyclePalette(paletteColors, 10, 13, 1);Testing
Section titled “Testing”npm testnpm run lint