A Python tool for extracting and decoding asset archives (.agg files) from Heroes of Might and Magic II and its expansion The Price of Loyalty.
- Parses the HoMM II AGG binary format (file table, hash verification, filename table)
- Decodes ICN sprite sheets to individual PNG files, with a
spec.xmloffset manifest - Decodes TIL tileset files to individual PNG files
- Decodes BMP palette-indexed bitmaps to PNG
- Extracts raw files (
.82Msounds,.PALpalette,.XMImusic,.BIN, etc.) as-is - Supports palette sharing across archives — essential for the expansion (
HEROES2X.AGG), which contains no palette of its own - Processes multiple AGG files in a single invocation
- Python 3.8+
- Pillow (for image decoding)
pip install PillowWithout Pillow, the tool will still run and extract raw files, but ICN, TIL, and BMP files will not be decoded to PNG.
python homm2_extract.py <agg_file> [<agg_file> ...] [--palette <agg_file>]Extract the base game archive:
python homm2_extract.py HEROES2.AGGExtract the expansion archive, using the base game's palette:
python homm2_extract.py HEROES2X.AGG --palette HEROES2.AGGExtract both archives in one command (palette from HEROES2.AGG is automatically reused for HEROES2X.AGG):
python homm2_extract.py HEROES2.AGG HEROES2X.AGGTested on the HEROES2.AGG and HEROES2X.AGG file from the retail versions from good old games (GOG), and the HOMM 2 Demo version HEROES2.AGG files. The demo is included in the repsitory.
| Argument | Description |
|---|---|
agg_files |
One or more .AGG files to extract, processed in order |
--palette AGG |
Load the palette from this AGG when the target archive has none |
Each archive is extracted into a subdirectory named after the archive (e.g. HEROES2/, HEROES2X/).
| File type | Output |
|---|---|
.ICN |
A subdirectory per ICN file containing 0000.png, 0001.png, … and a spec.xml |
.TIL |
A subdirectory per TIL file containing one PNG per tile |
.BMP |
A single .png file |
.PAL |
Raw palette file, plus a palette_swatch.png for visual inspection |
.82M |
Raw audio file |
| All others | Extracted as-is |
Each decoded ICN directory contains a spec.xml describing the sprite sheet:
<icn count="6">
<sprite id="0" file="0000.png" offsetX="-16" offsetY="-20" width="32" height="40" animationFrames="0"/>
...
</icn>offsetX and offsetY are the hotspot offsets used by the game engine to position sprites on screen. They are preserved here for reference; they do not affect the PNG canvas dimensions.
animationFrames stores the lower 5 bits as the frame count for adventure map animations. Bit 6 (0x20) being set marks the sprite as monochromatic (black-and-white silhouette).
The HoMM II .agg format is a simple uncompressed archive:
- 2 bytes — little-endian count of files (
n) - 12n bytes — file table:
(u32 hash, u32 offset, u32 size)per file - variable — concatenated file contents at the offsets given above
- 15n bytes — filename table at the end of the file: 13-byte null-terminated DOS filename + 2 padding bytes per file, in the same order as the file table
File IDs are a 32-bit hash of the uppercase filename computed by iterating the characters in reverse and accumulating a rolling sum.
The ICN sprite format uses a run-length encoding scheme with distinct opcodes for transparent pixels, shadow pixels (semi-transparent black), literal colour runs, and RLE colour runs. Each sprite is terminated by a 0x80 byte.
Written by Andrew G. Stevens with assistance from Claude Sonnet 4.6.
AGG format documented by James Koppel, whose original Java extraction tools were a key reference and some notes derived from it here: https://thaddeus002.github.io/fheroes2-WoT/infos/informations.html . ICN format details sourced from community documentation and cross-referenced against the fheroes2 open-source reimplementation.
This code is released under the Apache License, Version 2.0.