hoi4treesnap generates Hearts of Iron IV focus tree screenshots.
The tool itself does not contain any textures and picks them up from the HOI4 base game or a mod that contains selected focus trees. That includes all focus tree graphics: focus icons, focus tree plaques, focus tree lines and fonts. nationalfocusview.gui is being parsed to pick on your changes to it, so the output image looks quite similar to what you see in the game, even a modded one.
- Parallel multi-file rendering — folder selections now render in a bounded worker pool (
runtime.NumCPU()workers) via a hidden env-based worker subprocess mode inparallel_generation.go, keeping the UI responsive and maximising CPU utilisation while avoiding shared-global races. - In-run cancellation — during rendering the Quit button is joined by a Cancel button; cancellation is propagated through shared-context prep, scan workers, render workers, and per-focus steps, returning cleanly with a "Generation cancelled" message rather than an error.
- Settings window — a dedicated Settings panel (accessible via button below the focus controls) lets users choose from a wide palette of HOI4-themed colour presets and pick or reset a custom background image. Theme and custom background path are persisted to
config/settings.jsonunder the output folder and restored on relaunch. - Background rendering — an optional "Render with background" toggle tiles
parch_bg_adjustable.dds(or a user-supplied image/.webmfirst frame extracted via ffmpeg) behind the focus tree canvas. Falls back through a chain of DDS tile candidates and finally a solid opaque colour so output is never left transparent. - Structured application logging — all diagnostic output is written via
log/slogto a rotating log underlogs/hoi4treesnap.lognext to the output folder. Per-run error diagnostics go to a timestamped file underlogs/DD-MM-YYYY/HH-MM-SS-...-error.log; the stale single-file approach has been removed. - Loading animation — an embedded
hoi4 loading.gifis shown in the progress area during active generation and hidden at idle, giving clear visual feedback that the tool has not frozen. - Native file/folder dialogs — file and directory pickers now use
github.com/sqweek/dialog(resizable native dialogs) instead of Fyne's built-in choosers, for game folder, mod folder, and focus file selection. - Focus picker rework — the focus file picker supports a file/folder mode toggle, optional recursive folder scan, focus-tree content filtering, inline empty-folder feedback, and per-entry removal that recomputes
focusTreePathslive.
- Grammar expanded to cover modern and base-game PDX syntax:
|,[ ... ],?, and broader comparison operators (>=,<=,!=,==); symbolic lvalues like2.ITA_*now parse correctly. - Input is normalised centrally before every parse: UTF-8 BOM stripped, numeric percentage suffixes (
35%→35) rewritten, and unmatched top-level closing braces salvaged with a retry pass (stripUnmatchedClosingBraces). - Malformed focus files are skipped instead of aborting the whole run; parsing errors are logged and a summary dialog lists skipped files at the end. Generation only hard-errors if no files parse successfully.
- Missing focus icon textures no longer abort rendering;
renderFocuslogs a warning and falls back to theGFX_goal_unknownicon. - Sprite texture fallback resolution normalises leading separators before probing mod and game paths, improving cross-mod robustness.
getFramehardened forNoOfFrames <= 0, out-of-bounds frame indices, and invalid frame dimensions to prevent divide-by-zero / nil-frame panics.renderLinesnow checks eachreadTextureAndGetFramescall individually; previously only the last error was inspected, which could leave nil images and cause nil-pointer panics.- Nil images from
FocusLine.Get()are guarded at the render callsite.
- Large
.ymllocalisation files are now parsed with a fast line-scanner (parseLocFile) instead of the PEG parser, fixing severe slowdown and missing text on large mod localisation sets. - Fallback focus labels (used when localisation is absent or the value is itself an unresolved key) are humanised: leading 2–4 character country-tag prefixes (
AUS_,mac_, etc.) are stripped and the remainder is title-cased (e.g.MAC_reform_army→Reform Army).
- Progress display shows current task name and overall percentage (e.g.
Parsing GFX: ... (18.0%)), with file/folder-aware task text during scan, shared-context, and render phases. - Shared-context prep (the 0–25 % phase) parallelises focus requirement discovery via a
scan-focusworker mode, reducing the pre-render bottleneck on multi-core machines. - Worker stderr is sanitised before surfacing errors (GTK theme warning noise filtered) and malformed worker messages are replaced with canonical
Skipping malformed focus file …lines. - End-of-run malformed-file dialog shows a short summary only; full details are in
logs/error.log. - Action row layout uses
container.NewBorderto anchor Quit / Cancel buttons at the bottom edge; idle state removes progress widgets from layout and syncs window to content min-size, eliminating residual blank space after cancel or run completion. - Theme selection offers a broad palette covering charcoal, warm utility, parchment (light/aged/event), brassworks, research/naval blues, military olive, and alert/command reds — applied app-wide at runtime.
- Output PNGs are exported to a
Renderssubfolder adjacent to the binary (or adjacent to$APPIMAGEwhen running as an AppImage); the UI shows the resolved export path.
- Background tile loading now prefers
tiled_bg.dds, skips fully-transparent tiles (alpha = 0), and composites withdraw.Overon an opaque base so the background is never invisible. - Fixed a potential UI deadlock in the file-picker by avoiding blocking waits inside selection confirmation; the add-file flow chains via async confirm callbacks.
- Removed duplicate malformed-focus diagnostics: skipped-file details log only to
logs/error.log; the legacylogs/malformed_focus_files.logfile is deleted on startup to avoid stale duplicates.
hoi4treesnap builds and runs natively on Debian Linux amd64 with Fyne v2.
Install the required development packages before building:
sudo apt-get update
sudo apt-get install -y \
pkg-config \
libgtk-3-dev \
libgl1-mesa-dev \
libx11-dev \
libxrandr-dev \
libxxf86vm-dev \
libxi-dev \
libxcursor-dev \
libxinerama-devBuild the portable Linux binary with:
make build-linuxThis produces dist/hoi4treesnap-linux-amd64.
The Linux binary remains dynamically linked (fully static linking is not practical for the Fyne/GLFW/OpenGL stack on glibc-based systems). At runtime it relies on libGL.so.1, libX11.so.6, libXrandr.so.2, libXxf86vm.so.1, libXi.so.6, libXcursor.so.1, libXinerama.so.1, and glibc.
The saved HOI4 path cache is stored under os.UserCacheDir(): typically ~/.cache/hoi4treesnap/hoi4treesnapGamePath.txt on Linux and %LocalAppData%\hoi4treesnap\hoi4treesnapGamePath.txt on Windows.
Linux builds auto-detect the base game at these locations before falling back to the saved cache:
~/.steam/steam/steamapps/common/Hearts of Iron IV~/.var/app/com.valvesoftware.Steam/data/Steam/steamapps/common/Hearts of Iron IV
All release binaries are available on the releases page.
| Platform | File | Notes |
|---|---|---|
| Linux (raw binary) | hoi4treesnap-linux-amd64 |
Mark executable with chmod +x then run directly |
| Linux (Debian/Ubuntu) | hoi4treesnap_<version>_amd64.deb |
Install with sudo dpkg -i hoi4treesnap_<version>_amd64.deb; binary placed at /usr/bin/hoi4treesnap |
| Windows | hoi4treesnap-windows-amd64.exe |
Run directly; no installation required |
- Download the appropriate binary for your platform from the releases page (see Downloads table above).
- Select one or more focus tree files, or a folder containing them, from
/common/national_focus. - Select the Hearts of Iron IV game folder. It will be saved for later use after the first time if auto-detection did not find it.
- If you need other mods (dependencies, for example), select those too.
- If you want non-English localisation, press
Select localisation language. - Optionally open Settings to choose a theme or custom background.
- Press
Generate image. Output PNGs are saved to theRendersfolder next to the binary.
The DDS decoder is backed by github.com/xypwn/filediver/dds, which registers with the standard Go image package and handles:
- DXT1 and DXT5 textures even when
pitchOrLinearSizeis zero or incorrect. - Mipmapped textures (only the base image is decoded for rendering).
- DX10 extended headers.
- Uncompressed 32-bit ARGB8 textures with correct BGRA → RGBA channel handling.
The test suite includes synthetic DDS fixtures covering DXT1, DXT5 with alpha, uncompressed ARGB8, and a DX10 mipmapped texture.
GitHub Actions builds Linux and Windows binaries on pushes to master and on version tags matching v*. Tag builds publish both binaries as release assets. The Windows build runs on a native windows-latest runner because the Fyne/go-gl stack is not a reliable Linux-to-Windows cross-compilation target.
- The file parser is stricter than the PDX one, so you might need to fix reported errors manually.
- You can't generate a single image for shared focus trees. You'll have to combine them from separate images.
- There is no country name in the image. Might be added later either through parsing of the files or by asking the user to input the name.
- If a focus title uses scripted localisation, it will be rendered as a scripted localisation string instead of the appropriate name.




