
extract-vassal-images
by Bestra
Printable sheets to help manage counters for GCACW
SKILL.md
name: extract-vassal-images description: 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.
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 leaderUL_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 detectionparser/image_extraction/extract_images.py- INDIVIDUAL (RTG2-style) extractorparser/image_extraction/generate_counters.py- Unified TEMPLATE_COMPOSITE generator (HCR, OTR2, GTC2, HSN)parser/image_extraction/integrate_game_images.py- Post-extraction integration automationparser/image_mappings/*.json- Generated mappingsparser/EXTRACT_IMAGES_QUICKSTART.md- Quick reference cheat sheetweb/src/data/imageMap.ts- TypeScript image map
スコア
総合スコア
リポジトリの品質指標に基づく評価
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
レビュー
レビュー機能は近日公開予定です