Reverse-Engineering Premier Manager 2’s VGA Graphics

Reverse-Engineering Premier Manager 2’s VGA Graphics
Page content

About once every five years I fire up Premier Manager 2 — THE soccer management game before Championship Manager monopolised the genre. (These days, you can even play PM2 in your browser.)

Before starting to play, this time I noticed an interesting mix of files in the root directory — thingslike contract.gnd, icons.vga, anim.bin, goal.voc, and others—and wondered how hard it would be to decode them with modern tooling. Surely the .gnd and .vga files were graphic assets, the .voc files audio, and so on. A little reverse‑engineering later, here’s what I found about the image formats…


The PM2 .vga Format

Every PM2 graphic needs two files:

  1. Image file (e.g. icons.vga, font16c.vga) – stores pixel indices.
  2. Palette file (paldata.vga for in‑game screens, paltitle.vga for the title screen) – stores the actual colours.

The PM2 palette is just a basic look‑up table: 256 entries x 3 bytes (red, green, blue) = 768 bytes that follow a 0x100‑byte header. Each pixel in the image is an index (0‑255) into that palette table, keeping files tiny and guaranteeing that every sprite, font, and icon shares the exact same colour set.

Why only 256 colours? Classic DOS VGA modes (320 × 200, 8‑bit), such as this masterpiece, were limited to a 256‑entry palette. SVGA later raised the bar on resolution and, eventually, colour depth, but PM2 shipped pretty much as SVGA was getting started.

File layout

0x000–0x0FF   File header (256 bytes)
0x100–0x3FF   Palette data (only in palette files: 256 × RGB)
0x400–EOF     Image blocks

Each image block starts with an 8‑byte header:

OffsetSize (Bytes)Purpose
04unused / reserved
42height (y_size)
62width (x_size)

Pixel data immediately follows and is exactly width x height bytes. Because of that regularity, you can iterate through a file like so:

block_size = x_size * y_size + 8
num_images = len(raw_data) // block_size

Tool 1 - Batch Image Extractor

I wrote two small Python scripts that validate all assets, walk through a .vga file, pulls out each bitmap, and saves it as a BMP. Grab the PM2 game files (the easiest source is My Abandonware), drop them in an assets/ folder, then run:

python3 verifyAssets.py     # validates file hashes
python3 vgaToBmp.py         # dumps every icon/font/sprite to BMP

…But I don’t want to run your janky script.

Fine… Here are the extracted assets images:

Fax screen (fax_merged.vga)

Font16c glyphs (font16c_merged.vga)

Font55 glyphs (font55_merged.vga)

Font57 glyphs (font57_merged.vga)

Font57b glyphs (font57b_merged.vga)

Font77 glyphs (font77_merged.vga)

Font77b glyphs (font77b_merged.vga)

Font77c glyphs (font77c_merged.vga)

FontF9 glyphs (fontf9_merged.vga)

Score background (gndscore_merged.vga)

Stadium seats (gndseats_merged.vga)

Ground index (groundix_merged.vga)

UI icons (icons_merged.vga)

Impulse bar (impslbar_merged.vga)

Match ball icon (matball_merged.vga)

Match buttons (matbtn_merged.vga)

Match speed bar (matspd_merged.vga)

Phone UI 1 (phone2_merged.vga)

Phone UI 2 (phonem_merged.vga)

Pitch sprites (pitch_merged.vga)

Pitch bitmap (pitchbit_merged.vga)

Position graph (posgraph_merged.vga)

Report screen (report_merged.vga)

Result screen (result_merged.vga)

Security screen 2 (sec2_merged.vga)

Shareholders screen (sh_merged.vga)

Sponsors screen (sponsors_merged.vga)

Ticket window (ticket_merged.vga)

Validation buttons (validbtn_merged.vga)


Tool 2 - Dynamic Font Display

With the fonts extracted, why not type in glorious mid‑90 s style? dynamicFontDisplay.py is a quick Pygame demo that does exactly that:

python3 dynamicFontDisplay.py assets/font16c.vga assets/paldata.vga --scale 4

Where --scale enlarges each glyph by an integer factor. Here’s a sample output:

Dynamic font demo


Next Steps …

The image background .gnd files are tougher: they appear to be compressed with a custom Lempel‑Ziv–Huffman variant. I’m pretty sure I’ll have to dive into the main executable to see how its compressed. Next time.