Skip to content
eriscorpPublic

About

DALib realized in typescript

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

@eriscorp/dalib-ts

TypeScript library for parsing and manipulating Dark Ages (DOOMVAS v1) game client files.

Translated from eriscorp/dalib, the original C# implementation, under the MIT license.


Installation

npm install @eriscorp/dalib-ts

Supported Formats

Format Class(es) Description
.dat DataArchive, DataArchiveEntry Game data archives
.map MapFile Map layout files
.meta MetaFile, MetaFileEntry Metadata files
.hpf HpfFile Tile graphics
.spf SpfFile, SpfFrame Sprite frames
.epf EpfFile, EpfFrame Entity / mob sprites
.mpf MpfFile, MpfFrame Map sprites
.efa EfaFile, EfaFrame Effect animations
.lft LftFile Bitmap fonts (the format the client actually renders text with)
.fnt / .hea FntFile, HeaFile Dormant fixed-cell fonts / light masks
sotp.dat SotpFile Static-tile collision & render flags
.tbl PaletteTable, ColorTable, TileAnimationTable, EffectTable Lookup tables
.control ControlFile, Control UI layout files

Usage

Reading a data archive (Node.js)

import { DataArchive } from '@eriscorp/dalib-ts';

const archive = DataArchive.fromFile('path/to/archive.dat');

// List all .spf entries
const sprites = archive.getEntriesByExtension('.spf');

// Get a specific entry's raw bytes
const entry = archive.get('sprite001.spf');
if (entry) {
  const buffer = archive.getEntryBuffer(entry);
}

Reading a data archive (browser)

import { DataArchive } from '@eriscorp/dalib-ts';

const response = await fetch('archive.dat');
const buffer = await response.arrayBuffer();
const archive = DataArchive.fromBuffer(buffer);

Parsing a sprite file

import { DataArchive, SpfFile, Palette } from '@eriscorp/dalib-ts';

const archive = DataArchive.fromFile('seo.dat');

const spfEntry = archive.get('sprite001.spf')!;
const palEntry = archive.get('sprite001.pal')!;

const spf = SpfFile.fromBuffer(archive.getEntryBuffer(spfEntry));
const palette = Palette.fromBuffer(archive.getEntryBuffer(palEntry));

Resolving the palette for a legacy entry

Legacy sprites do not carry their own colors. PaletteResolver applies the client's rules to find the correct palette for an entry:

import { DataArchive, PaletteResolver } from '@eriscorp/dalib-ts';

const archive = DataArchive.fromFile('legend.dat');
// The provider supplies sibling archives; return null when unavailable.
const resolver = new PaletteResolver('legend.dat', archive, name => null);

const resolved = resolver.resolve(archive.get('item001.epf')!, 0);
if (resolved) {
  // resolved.palette, resolved.paletteNumber, resolved.ruleId
  // resolved.luminanceBlended → render with straight (non-premultiplied) alpha
} else {
  // No rule matched, or data is missing — fall back to a manual picker.
}

Rendering to ImageData

import { renderSpfPalettized } from '@eriscorp/dalib-ts';

const frame = spf.frames[0];
const rgbaFrame = renderSpfPalettized(frame, palette);

// rgbaFrame.data is a Uint8ClampedArray — pass directly to ImageData
const imageData = new ImageData(rgbaFrame.data, rgbaFrame.width, rgbaFrame.height);
ctx.putImageData(imageData, 0, 0);

API Overview

Binary I/O

  • SpanReader — low-level binary reader (LE/BE integers, fixed-length ASCII, byte spans)
  • SpanWriter — low-level binary writer
  • compressHpf / decompressHpf — HPF tile compression

Cryptography

  • crc16(data) — CRC-16 checksum
  • crc32(data) — CRC-32 checksum

Utilities

  • decodeRgb555 / encodeRgb555 — RGB555 color codec
  • decodeRgb565 / encodeRgb565 — RGB565 color codec
  • quantizeFrames — palette quantization for image export
  • cropTransparentPixels — trim transparent borders from frames

Palette resolution

  • PaletteResolver — resolves the palette for entries of one open archive; returns ResolvedPalette (palette, paletteNumber, luminanceBlended, kind, ruleId) or null
  • matchPaletteRule(archiveName, entryName) — the pure rule-match stage, for tests and hosts that only need the ruleId
  • PaletteLookup.getResolvedPaletteForId(id, override?) — like getPaletteForId, and reports the real palette number plus the luminance-blending flag

Virtualized (lazy) Views

For large archives, avoid decoding everything upfront:

  • SpfView, EpfView, MpfView, EfaView, TilesetView

These decode individual frames on demand rather than parsing the entire file.


Testing against a real client

A handful of suites assert against the archives a real Dark Ages install ships rather than against synthetic fixtures. They gate themselves on the files being present, so they skip on CI and on any machine without a client — no binary assets are committed here.

Point DALIB_CLIENT_DIR at an install to run them:

DALIB_CLIENT_DIR="/path/to/dark ages" npm test

If you have no install, tools/extract-wise-installer.mjs unpacks one straight out of the official installer without running it (it is a Wise self-extractor; 7z and innoextract cannot read it, and no wine is needed):

node tools/extract-wise-installer.mjs ~/Downloads/DarkAges741single.exe "/path/to/dark ages"

Every extracted file is checked against both the size and the CRC32 the installer records for it, so a clean exit means the payload is intact. --dry-run verifies without writing.

One thing to know on a case-sensitive filesystem: the installer spells the archive Legend.dat, while tests/paletteResolver.test.ts looks it up as legend.dat, so that suite stays skipped. Either extract with --lowercase, or add the alias:

ln -s Legend.dat "/path/to/dark ages/legend.dat"

License

MIT

About

DALib realized in typescript

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages