extract-vassal-images
Extract unit counter images from VASSAL game modules (.vmod files) for roster generation. Use when adding counter images to a game, setting up image mappings for units, or troubleshooting missing/mismatched unit images. Handles both individual pre-rendered images and template-based composite images with automatic counter type detection.
Installation
Suivez les instructions du dépôt pour votre agent. Consultez les fichiers et dépendances avant l’installation.
Voir la source ↗GuidesInstructions
Extract VASSAL Module Images
Overview
VASSAL modules are ZIP files containing game piece definitions and images. This workflow covers:
- Automatically detecting the counter image system used (
detect_counter_type.py) - Understanding module structure
- Extracting and mapping images to parsed unit data
- Generating composite images when needed
VASSAL Module Structure
A .vmod file is a ZIP archive containing:
module.vmod/
├── buildFile.xml # Piece definitions, prototypes, image references
├── moduledata # Module metadata
├── images/ # All image files (jpg, gif, png)
│ ├── counters...
│ ├── markers...
│ └── maps...
└── *.vsav # Saved game files for scenarios
buildFile.xml
This XML file defines all game pieces. Key elements:
<!-- PieceSlot defines a placeable piece -->
<VASSAL.build.widget.PieceSlot entryName="UnitName" gpid="123">
+/null/prototype;USA Infantry Division\
piece;;;ImageFile.jpg;UnitName/
</VASSAL.build.widget.PieceSlot>
<!-- The piece definition format: -->
<!-- piece;;;IMAGE_FILE;PIECE_NAME/ -->
Counter Image Systems
Type 1: INDIVIDUAL (RTG2-style)
Each unit has a dedicated pre-rendered image with the unit name already on it:
-
C_Lee.jpg- Confederate unit "Lee" -
U_Meade.jpg- Union unit "Meade" -
CL_Stuart.jpg- Confederate leader -
UL_Hooker.jpg- Union leader
Extraction: Use extract_images.py with the game code.
Type 2: TEMPLATE_COMPOSITE (HCR/OTR2/GTC2-style)
Units use generic background images based on corps/type, with unit names overlaid via VASSAL's label mechanism:
-
Background: Corps/wing identifier + quality + strength (e.g.,
I-P-2-4.jpg,UII_2_3.jpg,USA_I_24.jpg) - Text label: Unit name rendered dynamically by VASSAL
Only leaders have dedicated images. Brigade/division counters are composited.
Background image naming patterns:
- Union corps:
I-P-2-4.jpg,UII_2_3.jpg,UV_2_2.jpg - Confederate:
J-3-4.jpg(Jackson's Wing),CIII_3_4.jpg - OTR2-style:
USA_I_24.jpg,CSA_M_20.jpg
Extraction: Use the unified generator generate_counters.py with the appropriate game_id:
- Parses buildFile.xml for unit→background mappings
- Loads parsed game data for actual unit names
- Copies leader images directly
- Composites brigade/division images with unit name text overlay
Important: For HYBRID systems where VASSAL adds text labels dynamically (detected by detect_counter_type.py), use --no-text flag to avoid double text overlay:
cd parser && uv run python image_extraction/generate_counters.py tom ~/Documents/vasl/gcacw/TOM_3_17.vmod --no-text
Quick Start
Environment Variables: Configure VASSAL module paths in parser/.env:
# Example from parser/.env
GTC2_VASSAL_PATH=~/Documents/vasl/gcacw/GTC2_3_10.vmod
OTR2_VASSAL_PATH=~/Documents/vasl/gcacw/OTR2_3_11.vmod
VMOD Files Location: ~/Documents/vasl/gcacw/ (store all GCACW .vmod files here)
For a streamlined extraction workflow, see:
-
Quick Reference:
parser/EXTRACT_IMAGES_QUICKSTART.md- 30-second workflow cheat sheet -
Integration Script:
parser/image_extraction/integrate_game_images.py- Automates post-extraction setup
Extraction Workflow
Step 1: Detect Counter Type (CRITICAL - Always Start Here)
Use detect_counter_type.py to automatically determine the counter image system:
cd parser && uv run python image_extraction/detect_counter_type.py /path/to/GAME.vmod
This analyzes buildFile.xml and image naming patterns to determine:
- INDIVIDUAL: Each unit has a dedicated pre-rendered image (e.g., RTG2)
- TEMPLATE_COMPOSITE: Units use generic backgrounds with text overlays (e.g., HCR, OTR2, GTC2, HSN)
- HYBRID: Mixed approach (usually leaders are individual, units are templates)
To analyze all modules in a directory:
cd parser && uv run python image_extraction/detect_counter_type.py --all /path/to/vmods/
Step 2: Extract the VMOD (if needed for manual inspection)
mkdir /tmp/game_vmod
unzip "/path/to/GAME.vmod" -d /tmp/game_vmod
Step 3: Explore the Images
# List all images
ls /tmp/game_vmod/images/
# Find unit counters (filter out maps, charts, markers)
ls /tmp/game_vmod/images/ | grep -vE "Map-|Chart|VP|Turn|Control"
Step 4: Examine buildFile.xml
# Find piece definitions
grep -o 'entryName="[^"]*".*piece;;;[^;]*;[^/]*/' buildFile.xml | head -50
# Look for prototype definitions (counter composition)
grep "PrototypeDefinition" buildFile.xml | head -20
Step 5: Run Appropriate Extractor
For INDIVIDUAL (RTG2-style) games:
cd parser
uv run python image_extraction/extract_images.py GAME /path/to/GAME.vmod
For TEMPLATE_COMPOSITE games:
# HCR
cd parser && uv run python image_extraction/generate_counters.py hcr ~/Documents/vasl/gcacw/HCR.vmod
# OTR2
cd parser && uv run python image_extraction/generate_counters.py otr2 ~/Documents/vasl/gcacw/OTR2.vmod
# GTC2
cd parser && uv run python image_extraction/generate_counters.py gtc2 ~/Documents/vasl/gcacw/GTC2.vmod
# TOM (HYBRID - uses --no-text because VASSAL adds labels)
cd parser && uv run python image_extraction/generate_counters.py tom ~/Documents/vasl/gcacw/TOM_3_17.vmod --no-text
For new games, add game-specific configuration to image_extraction/generate_counters.py.
Step 6: Integrate into Web App
Use the integration helper script to automate the post-extraction setup:
cd parser && uv run python image_extraction/integrate_game_images.py GAME
This automatically:
- Copies
image_mappings/game_images.jsontoweb/src/data/ - Updates
web/src/data/imageMap.tswith necessary imports and registrations - Validates the setup
Then verify the build:
make build
Name Matching Challenges
Unit names often differ between VASSAL and parsed data:
| VASSAL | Parsed | Issue |
|---|---|---|
Heitzelman |
Heintzelman |
Typo |
A.P.Hill |
A.P. Hill |
Spacing |
Rodes |
Rodes-A |
Suffix |
Wilcox |
Willcox-A |
Spelling + suffix |
D'Utassy |
D'Utassy |
Curly apostrophe |
The extractors include name normalization to handle these variations.
Output Files
Image Mapping JSON
Location: parser/image_mappings/{game}_images.json
{
"game": "hcr",
"matched": {
"C:Jackson": "Jackson",
"U:Meade": "U_Meade"
},
"matched_with_ext": {
"C:Jackson": "Jackson.jpg",
"U:Meade": "U_Meade.jpg"
},
"unmatched": ["Union (Cav): IL", ...],
"unused_images": [...]
}
TypeScript Image Map
Location: web/src/data/imageMap.ts
This file is auto-generated and maps unit keys to image filenames for the web app.
Counter Images
Location: web/public/images/counters/{game}/
Adding a New Game's Images
-
Detect the counter system using
detect_counter_type.py -
For INDIVIDUAL: Add game patterns to
extract_images.py -
For TEMPLATE_COMPOSITE: Add game-specific configuration to
generate_counters.py - Run the extractor
- Check unmatched units and add name normalization as needed
-
Update
imageMap.tsif not auto-generated
Troubleshooting
"Missing background" errors
The VASSAL module may not have all referenced images. Check if images exist:
ls /tmp/game_vmod/images/ | grep -i "imagename"
High unmatched count
Usually indicates:
- Wrong counter system identification
- Name normalization issues
- Parsing artifacts in source data (e.g., turn numbers mixed into unit names)
Composite images look wrong
Check the Pillow font loading - the script uses system fonts with fallback to default.
Integration script fails with "No such file or directory"
If integrate_game_images.py or the extraction creates files in the wrong location (e.g., parser/web/ instead of project root web/), the path calculations in the scripts may be incorrect. After adding a new game configuration:
-
Check output directory: Images should go to
web/public/images/counters/{game}/at the project root -
Verify paths: Scripts in
parser/image_extraction/need.parent.parent.parentto reach project root (up fromparser/image_extraction/→parser/→ project root) -
Manual integration: If the integration script's regex fails, manually update
web/src/data/imageMap.ts:- Add import:
import {game}Data from "./{game}_images.json"; - Add to
imageMap:{game}: {game}Data.matched_with_ext, - Add to
counterTypeMap:{game}: ({game}Data as {{ counterType?: CounterType }}).counterType ?? 'template',
- Add import:
Related Files
-
parser/image_extraction/detect_counter_type.py- Automatic counter type detection -
parser/image_extraction/extract_images.py- INDIVIDUAL (RTG2-style) extractor -
parser/image_extraction/generate_counters.py- Unified TEMPLATE_COMPOSITE generator (HCR, OTR2, GTC2, HSN) -
parser/image_extraction/integrate_game_images.py- Post-extraction integration automation -
parser/image_mappings/*.json- Generated mappings -
parser/EXTRACT_IMAGES_QUICKSTART.md- Quick reference cheat sheet -
web/src/data/imageMap.ts- TypeScript image map
Exemples
Consultez les exemples du dépôt source.
Voir la source ↗Historique source
- Date d’indexation
- 2026-01-20
- Mise à jour de l’index
- 2026-01-26
- Branch
- main
Les dates concernent ce répertoire. Consultez le dépôt pour l’historique du code.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!