Skip to content

Asure/midway-loadimg

Repository files navigation

loadimg — Williams/Midway Arcade Image Loader

A modern replacement for the MS-DOS LOAD2.EXE / LOADW.EXE (Williams Electronics Games Inc., 1993–1995) toolchain. Reads .lod script files and .img container files from arcade hardware and outputs .tbl/.asm/.glo/.irw files targeting the TMS34020 GSP DMA2 graphics co-processor.

Supported games: Mortal Kombat 2, NBA Jam, NBA Jam Tournament Edition, Hangtime, Trog (/OLD mode), Narc (/OLD mode).

Smash TV / Total Carnage (ROBOY.LOD): The loadsmash branch has ROM-verified dedup that makes SMASH TBLs pass. Switch to it with git checkout loadsmash, build, and use /OLD /H flags.


Table of Contents


Version History

v0.98

  • 15 pass, 7 fail — stable baseline. All modern (LOADW) tests pass except pre-existing MK7, BB7.
  • GLO file rework — Single g.glo_fp handle replaces dual glo_fp+main_glo_fp (fixes interleaved-write corruption — killed the S_SZ line). Palette globals follow the current GLO (IMGTBL.GLO by default, redirected by GLO>), with no duplicate entries. ASM> directive resets GLO back to IMGTBL.GLO. FRM entries no longer emit .globl (game code uses TBL directly, no palette data). ENDMARKER removed from modern-mode GLO output. All MK IMGTBL.GLO/IMGPAL.ASM/IMGTBL.ASM now match refs and included in test suite.
  • PAL> directive added — redirects palette color data output, symmetrical with GLO> and ASM>. Opens new PAL file, writes header if empty. Hangtime worked around the lack of this with batch-file ren commands per-LOD.
  • /A append mode fixesg.asm_fp opens with "a" (was "w", always truncated). g.pal_fp opens with "a+" (was "w+", truncated). IMGTBL.ASM wrapper opens with "a" and skips header. Correctly appends to existing TBL/PAL/GLO/ASM files across multiple LOD runs.
  • /OLD2 CARN 10/13 — Total Carnage: ORCUS, JET, SHAWN, RACKUP, HBASE, JEEP, TITLE, WARP, FINGRNT, PLYR, THROW, AKHBBOSS, MUTEXP all pass. JET/TEXT/TITLE have cosmetic SIZX diffs.
  • memcmp-verified dedup — Byte-for-byte verification catches false checksum collisions. Per-mode: modern (checksum-only), /OLD1 (off by default), /OLD2 (ANIX-only check with memcmp).
  • IRW header format standardized0x00640194 magic at 0x20, data-only size at 0x30, uint16 LE checksum at 0x34 (trailing odd byte discarded), 0x00000004 at 0x38. See irw-header.md.
  • Consistent trailer.TEXT + 0x1a for modern and /OLD2; .TEXT only for /OLD1 (matches original LOAD.EXE 4.50 behavior).
  • Smash TV — Separate loadsmash branch with ROM-verified dedup (VHIT1R dedup against VHIT1 confirmed by u105+u89 ROM de-interleave). Switch: git checkout loadsmash.
  • BIGSREC.EXE compatibility — IRW header now accepted by BIGSREC tool (handles COFF-like header format).
  • Full regression testingbash test.sh after every change ensures 15 pass baseline.
  • Trog: 12/15 — IMGTBL.GLO and BGNDTBL.GLO now pass. Motorola hex for BGNDTBL/BGNDPAL/BLKS sections. Tab separators (always \t.word\t not /OLD-specific spaces). Removed GLO blank lines. Skip .TEXT trailer for ASM/GLO in /OLD mode. Insert .include BGNDTBL.GLO in IMGTBL.ASM at BBB handler start. BLKS hdr_idx uses LOADW word-slot indexing (di * 2).
  • NARC: 20/21 — NARCLOAF fixed. Duplicate IMG records: LOADW uses LAST match. NARCMUGS ref was generated by buggy LOAD.EXE (while run > 0), accepted deviation.
  • POF>-based SAG+PAL combined/separate detection/OLD mode uses POF> presence in LOD to select combined SAG+PAL (NARC) or separate lines (Trog).

v0.95

  • Trog: 9/9 TBLs + IMGPAL.ASM pass — BBB compression fixed (8bpp, no CMP, consistent sizx_a for 2nd pass), OUT_STRIDE minimum 1 for /OLD, file headers in IMGTBL.ASM, separate SAG/PAL .long lines.
  • /OLD2 flag for LOAD.EXE 4.65 (Total Carnage) with "4.65 9/3/91" IRW header.
  • NARC1: 18/21 — RLC encoder improvements.
  • Fixed /OLD mode IRW header format: stores ***> base address at offset 0x2C.
  • Added section_base_bit tracking for continuous IRW across multiple ***> sections (fixes Trog SAG with two address ranges).
  • Made +META suffix cache global (static) matching LOAD.EXE behavior; stores final TBL SAG for correct cross-section cache hits.
  • Fixed PTTBL shared-entry pointer arithmetic (pttblnum-2/pttblnum-3): uses signed ptrdiff_t offset to avoid UB. Resolves BB5 CMP=1 cascade (3/7→6/7).
  • Added cascade.md documenting remaining encoder issues, sentinel.md for PT0X investigation.

v0.91

  • Fixed old-format IMG palette offset computation (42-byte → 50-byte conversion was computing pal_ofs = old_oset + n*92 instead of old_oset + n*42). Fixes palette lookups in WWF WrestleMania font files (TROGF15.IMG, TROGF7.IMG).
  • Made write_image_tbl fully dynamic: IHDR> directive now controls field order, grouping, and formatting exactly. Removed hardcoded special case for CTRL that broke .word line grouping when PAL:L was skipped via POF>.
  • Added WWF test data (MAIN.LOD, MISC.LOD) with LOADW reference outputs and verbose logs.

v0.9

  • Fixed BBMUG 16-bit checksum collisions causing false deduplication matches by adding a sum2 byte-sum dual-hash mechanism.
  • Fixed BB6 CHEER bpp selection by properly scanning the stride-width pixel buffer (matching LOADW's handling of padding artifacts).
  • Fixed BB7 cascade failures by gating the PTTBL seq/scr offset skip to < v0x654 and enforcing a minimum SIZX threshold of 10.
  • Added -v and --version CLI flags.

What It Does

The Midway arcade toolchain stored game sprites and backgrounds in IMG container files — a format with palette records, image headers (position, size, animation anchors), and 8-bit indexed pixel data. A LOD script tells the loader which images to extract, how to compress them (zero-run, uncompressed, or raw movie footage), and where to place them in the GSP's memory space.

loadimg reads these inputs and produces:

File Content
.TBL Assembly image tables — per-image SIZX, SIZY, SAG (bit offset), CTRL (DMA2 control word), palette reference, animation points
IMGTBL.ASM Master table assembly (modern: .include-ing all per-game TBL files; /OLD: direct image entries)
IMGPAL.ASM Colour palette data as .word arrays (RGB565) with .globl declarations in current GLO
IMGTBL.GLO Global symbol declarations — palette globals (modern) or all image globals (/OLD mode). Redirected by GLO> directive
.IRW Bit-packed compressed (or raw) image binary ready for DMA transfer
BGNDTBL.ASM / BGNDPAL.ASM / BGNDEQU.H Background layer tables (from BBB> directive, background data, stages, levels.)

The output is assembled by the TI TMS34010 toolchain (gspa / gsplnk) and GFX are burned into EPROMs for the arcade hardware.


Build Instructions

Dependencies: C99 compiler, CMake ≥ 3.10. No external libraries (only libm on Linux).

Linux

mkdir build && cd build
cmake ../src
make

Windows (MinGW cross-compile)

A toolchain file is provided:

mkdir build_win && cd build_win
cmake ../src -DCMAKE_TOOLCHAIN_FILE=../src/toolchain-mingw.cmake
make

Native Windows builds with MSVC also work (set CMAKE_C_STANDARD 99).


Usage

loadimg <lod_file> [flags]
Flag Effect
/R[=dir] Headerless raw IRW — no 0x44-byte header (optionally to directory)
/T[=dir] Build TBL/ASM/GLO table files (optionally to directory)
/F[=dir] Build IRW binary (optionally to directory)
/X Skip IRW generation (TBL only)
/I[=dir] Source image directory for .IMG files
/D[=dir] LOD file directory
/V Verbose output
/E Dual-banked image memory (ED adjustment)
/P Pad images to 4-bit boundary (stride alignment)
/L Align images to 16-bit boundaries
/B Derive bpp from pixel data, not palette size
/3 Limit LOD scales to 3 (full, half, quarter, no eighth)
/A Append mode — open existing GLO/TBL/ASM/PAL files and append new entries instead of truncating
/OLD Legacy LOAD.EXE 4.50 mode — old-style output for Narc, Trog (and Smash TV /ROBOY). No CTRL field, SAG+PAL on same .long line, RLC> run-length encoding. Use /H for Intel hex format.
/OLD2 Legacy LOAD.EXE 4.65 mode — Total Carnage. Same as /OLD but uses IMG_STRIDE(w) for SIZX in TBL. Use /H for Intel hex format.
/H Intel hex format (0XXXXH) instead of Motorola hex (>XXXX). Required for CARN and SMASH reference matching.

Output filenames derive from the LOD base name (uppercased):

loadimg MK2MIL.LOD /P /T
  → MK2MILIMG.TBL, IMGTBL.ASM, IMGTBL.GLO, IMGPAL.ASM
loadimg MK2MIL.LOD /F
  → MK2MIL.IRW

Output Files

TBL Format (Assembly)

        .DATA
        .word   2               ; scale count
IMAGE_NAME:
        .word   SIZX
        .word   SIZY
        .long   0<SAG>H         ; bit offset into IRW data section
        .word   0<CTRL>H        ; DMA2 control word
        .long   PALNAME         ; palette label
        .long   0<SAG_S1>H      ; scale 1 SAG
        .word   0<CTRL_S1>H     ; scale 1 CTRL

IRW Format

Offset Size Field
0x00 8 ASCII date string, NUL-padded (03/14/95)
0x08 24 Reserved (zero-filled)
0x20 4 Magic — always 0x00640194 (LE)
0x24 8 Reserved (zero-filled)
0x2C 4 ROM address (LE)
0x30 4 Data size — payload byte count, header-exclusive (LE)
0x34 4 Checksum — sum of uint16 LE words in payload, truncated 32-bit; trailing odd byte discarded
0x38 4 Constant — always 0x00000004
0x3C 2 Type/flags
0x3E 6 Reserved (zero-filled)
0x44 Bit-packed image data (LSB-first)

SAG values in the TBL are bit offsets from the start of the IRW data section (plus the base address from the ***> directive).

DMA2 CTRL Word

Bits [15:12]  PIX — pixel size (0=8bpp, 1-7=bpp)
Bits [11:10]  TM  — trailing-zero multiplier (0=×1, 1=×2, 2=×4, 3=×8)
Bits [9:8]    LM  — leading-zero multiplier (0=×1, 1=×2, 2=×4, 3=×8)
Bit  [7]      CMP — 1=ZON compression, 0=ZOF uncompressed

Each compressed row: 1 header byte [trail_n:4][lead_n:4] + stored pixels at bpp bits each, LSB-first. The hardware skips lead_n × lm_mult leading zeros and trail_n × tm_mult trailing zeros per row.


The LOD/IMG Pipeline

  1. LOD Parsing — Text script with directives (ZON>, ZOF>, CON>, COF>, PPP>, XON>, ASM>, GLO>, PAL>, ***>, FRM>, BBB>, --->) controlling compression mode, dedup, bpp, and image selection.

  2. IMG Loading — Binary container with LIB_HDR (28 bytes), IMG_REC records (50 bytes each, 42 for old format), PAL_REC records (26 bytes), optional SEQ/SCR blocks, and PTTBL (point table) entries (40 bytes each). Pixel data is 8-bit indexed, row-stride aligned to 4 bytes.

  3. Two-Pass Encoding:

    • Pass 1 (scan_bpp): scans all ---> image lists to determine global bpp (max pixel value), respecting PPP> override.
    • Pass 2 (process_lod): for each image, analyzes the compression window (SIZX from PTTBL or image width), selects LM/TM multipliers via error-minimizing analysis (reverse-engineered from FUN_1000_6f20), encodes rows, writes TBL entries and palette data, and handles dedup (CON>/COF>) and FRM>/BBB> directives.
  4. IRW Writing — Flushes the bit-packed header and data to disk.

Asset Size Limits

Dimensions observed across the full dataset (sprites + backgrounds), excluding VDA development artifacts and !-prefixed special records:

Category Max Width Max Height Example
Sprite (IMG) 414px 255px TRANSBAR.IMG (transition bar)
BDD background tile 212px 221px TEAMSTAT.BDD (team stats)
BDB scrolling world 2000px 1500px Playfield coordinate system

Test Suite

Run make test from the build directory. Compares all TBL/ASM/GLO/IRW output against LOADW reference files.

cd build && make test

Current Results: 15 pass, 7 fail

Test Mode Result Notes
MK2MIL ZON + ZOF PASS (8/8) + IMGTBL.ASM, IMGPAL.ASM, IMGTBL.GLO
MK3MIL ZOF PASS (8/8) + globals
MK4MIL ZON PASS (9/9) + globals
MK5MIL ZON PASS (10/10) + globals
MK6MIL ZON/ZOF PASS (20/20) + globals
MK7MIL Mixed FAIL (13/14) All globals pass; pre-existing TBL failure
MK8MIL FRM PASS (4/4) + globals
BB ZOF+XON PASS (2/2)
BB2 ZOF+XON PASS (3/3)
BB3 ZOF+PT PASS (2/2)
BB4 ZOF+XON PASS (1/1)
BB5 Mixed PASS (7/7)
BB6 Mixed PASS (6/6)
BB7 Mixed FAIL (15/16) OUTDOOR LEAF8 dedup (unused image, inconsequential)
CARN /OLD2 PASS (13/13) All TBLs pass with memcmp-verified dedup
ROBOY /OLD FAIL (0/6) Smash TV — font atlas encoding order differs from reference
BB8 XON PASS (3/3)
BBMUG ZOF+XON PASS (2/2)
BBVDA VDA PASS (1/1)
TROG /OLD FAIL (12/15) 9 TBLs + IMGPAL.ASM + IMGTBL.GLO + BGNDTBL.GLO pass; BBB handler remaining
NARC1 /OLD FAIL (20/21) NARCMUGS: NARCMUGS compresses better. (see RLC.md)
CARN /OLD2 FAIL (10/13) 10 TBLs pass. JET/TEXT/TITLE cosmetic SIZX diffs.
MISC Dual-bank FAIL (18/21) 18 TBLs pass; global files differ from ref
ROBOY /OLD FAIL (0/6) Smash TV — use loadsmash branch for ROM-verified dedup

Format: loadimg <LOD> /P /T for LOADW tests, loadimg ... /OLD /T for LOAD.EXE. Reference files can be regenerated via make regen (requires DOSBox).


Known Issues & Quirks

Issue Scope Root Cause
IMGTBL.ASM TROGMOUTH/MOUTHBACK Trog Entries not in any source file
BGNDTBL.ASM HDRS SAG values Trog BDD images at different IRW positions vs LOADW
BGNDPAL.ASM missing FACEPALS Trog BBB handler missing .long palette aliases
BB7 OUTDOOR LEAF8 dedup BB7 LEAF8 unused image; ref has wrong dedup
NARC NARCMUGS SAG offset Narc Ref generated by buggy LOAD.EXE (while run > 0 loop). Accepted. Our output is slightly better compressed and accepted by the game.
ROBOY font atlas order Smash Font images encoded in LOD order but reference uses different order. Affects all 6 TBLs.
MISC global files differ Hangtime IMGTBL.ASM/IMGPAL.ASM/IMGTBL.GLO differ from ref (ref has inline data, tool generates wrapped includes)

LOAD Tool History

The Williams/Midway arcade toolchain evolved through several tool versions, each with different features and output formats. This project supports all of them.

LOAD.EXE 4.50 (4/27/90) — /OLD mode

The original tool, used for Narc (1988) and Trog (1990). Pre-DMA2, targets DMA1 hardware.

Feature Behavior
Compression None (raw 8bpp, stride-aligned rows)
Sprite dedup Off (no CON>/COF> support)
TBL format SAG+PAL on same .long line, no CTRL field
SIZX in TBL Raw rec->w (not stride-aligned)
RLC encoding RLC> directive for 2bpp run-length (DMA1 4-color images)
RLC bug while run > 0 emits malformed runs for remainders 1-3 (see RLC.md)
IRW magic [0x20] 0x0064024B
Trailer .TEXT only (no 0x1a EOF byte)
IRW version string "4.50 4/27/90"

Games: Narc (/OLD), Trog (/OLD)

LOAD.EXE 4.50 variant (Smash TV)

A later build of 4.50 used for Smash TV / Total Carnage prototypes. Same base tool but with sprite checksum dedup added.

Feature Behavior
Sprite dedup Checksum-based (ROM-confirmed: VHIT1R dedup against VHIT1)
Anix/aniy in dedup Not checked — dedup purely by pixel content
SIZX in TBL IMG_STRIDE(w) (stride-aligned)
IRW magic [0x20] 0x0064024B

Games: Smash TV (/OLD, loadsmash branch)

LOAD.EXE 4.65 (9/3/91) — /OLD2 mode

Updated for Total Carnage. Adds checksum dedup with ANIX comparison.

Feature Behavior
Sprite dedup Checksum + memcmp + ANIX-only check
SIZX in TBL IMG_STRIDE(w) (stride-aligned)
IRW magic [0x20] 0x00640194
Trailer .TEXT + 0x1a\x0d\x0a\x1a
IRW version string "4.65 9/3/91"

Games: Total Carnage (/OLD2)

Unknown tool (11/13/92, 11/11/93)

Two intermediate builds between LOAD.EXE 4.65 and LOADW. IRW files with these dates exist for NBA Jam / NBA Jam TE, but the original EXEs have not been found. Feature set likely transitional — probably DMA2-era with compression.

LOADW.EXE (5/25/94) — Modern mode

The final known tool, used for Mortal Kombat 2. Full DMA2 compression, CTRL words, point tables, and checksum dedup. This is the only modern-mode EXE we have (290 KB, Borland C++ 4.5), decompiled with Ghidra. The IRW date string "5/25/94" matches the binary timestamp.

Feature Behavior
Compression ZON (zero-run) / ZOF (uncompressed), DMA2 hardware decode
Sprite dedup Checksum-only (CON>/COF> toggles), no anix/aniy check
TBL format Full: SIZX, SIZY, ANIX, ANIY, SAG, CTRL, PAL, PWRD1-3, PT3Y
Scales Multi-scale encoding (full, ½, ¼, ⅛) via *N suffix
SIZX in TBL OUT_STRIDE(w) (depends on /P flag)
IRW magic [0x20] 0x00640194
Trailer .TEXT + 0x1a (single byte)
IRW version string "03/14/95"

Games: MK2, NBA Jam, NBA Jam TE, Hangtime

Feature Matrix

Feature LOAD 4.50 LOAD 4.50v LOAD 4.65 LOADW
CLI flag /OLD /OLD /OLD2 (none)
Build dates 4/27/90 ~1990 9/3/91 11/13/92, 11/11/93, 5/25/94
Games Narc, Trog Smash TV Total Carnage NBA Jam, MK2
EXE we have 5/25/94 (LOADW.EXE)
Sprite dedup ✓ (pixel) ✓ (ANIX) ✓ (checksum)
RLC encoding
ZON compression
CTRL/PWRD fields
SIZX stride raw w IMG_STRIDE IMG_STRIDE OUT_STRIDE
IRW magic 0x024B 0x024B 0x0194 0x0194
IRW trailer .TEXT .TEXT .TEXT+0x1a×2 .TEXT+0x1a
Test status 20/21 0/6 (loadsmash) 10/13 ~63/66

Reverse Engineering

Key Decompiled Functions

Function Address Purpose
_packbits 1000:5b64 Main compression — lead/trail analysis, row encoding
FUN_1000_6f20 1000:6f20 Error-minimizing LM/TM multiplier selector
_do_zcom 100a:fa02 Zero-compression row writer
_load_bits 1000:737c Bitstream I/O
_compute_bpp 1000:35a1 Bits-per-pixel calculation
_create_point_table 1000:75e1 PT pair output for TBL
FUN_1854_35fc 1854:35fc Checksum computation (dedup)
FUN_1854_1a49 1854:1a49 BBB background processor

COFF Debug Symbols

Extracted via strings from the binary:

  • _packbits, _do_zcom, _load_bits, _compute_bpp, _create_point_table
  • _write_palette, _write_row, _write_tbl
  • _do_sclpad, _do_pad, _dataword, _do_table, _ctrl_word
  • _zero_pad, _do_align0, _do_cksum, _do_superbpp, _color_average

See ghidra.md for the full analysis guide and compression.md for the complete algorithm reference.

Hardware Spec

The DMA2 compression format is documented in DMA2.DOC (Keep Enterprises, January 1992, Rev 1.5) — the official hardware specification for the Williams/Midway TMS34010 DMA co-processor.

Format References

  • wmpstruc.inc — Midway assembly struct definitions for IMG/PTTBL/SEQSCR
  • agents.md — Full IMG container format specification, TBL output, PT pair computation, implementation status, and all known bug fixes
  • bbb.md / bdd.md — BLIMP background format (BDB/BDD) specification
  • irw-header.md — IRW binary file header specification
  • compression.md — DMA2 ZON/ZOF compression algorithm, verified against Ghidra decompilation and LOADW verbose output

Credits

The original LOADIMG was created by Warren B. Davis around 1988 for Y-Unit hardware. Possibly other developers updated it for Wolf Unit/DMA2. Thank you! You've made our lives better with the tools that helped build games that excited and shaped our life during the 80's and 90's. Without you, this would not exist!

  • Based on reverse-engineering of LOADW.EXE (Williams Electronics Games Inc., 1993–1995, Borland C++ 4.5)
  • DMA2 hardware documentation: Keep Enterprises, Jan 1992
  • Original struct definitions: Midway Manufacturing (wmpstruc.inc, itimg.asm)
  • Test data extracted from Mortal Kombat 2, NBA Jam, NBA Jam Tournament Edition, and Hangtime arcade ROMs

About

Williams/Midway arcade image loader replacement for the MS-DOS LOAD2/LOADW tool

Resources

Stars

3 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages