Two commands, run from the afkj-parser folder: ./afkj-extract reads the game install
into out/raw/, and ./afkj-parse turns that into the feed at out/api/. Both
are small launchers for the Python packages in src/, so they need no install beyond the libraries in
section 2.
| Task | Command |
|---|---|
| After a game update (game closed) | ./afkj-extract --locales-from cdn, then ./afkj-parse build |
| What changed since the last import | printed by build; details in out/build/diff.md, or ./afkj-parse diff |
| One hero's changed texts | ./afkj-parse diff --hero karma (--locale zh for another language) |
| After stargazer imports the feed | ./afkj-parse accept: the feed becomes the next diff's base |
| Which heroes are in the feed, and why | ./afkj-parse roster |
| Season art (portraits, artifacts, phantimals) | ./afkj-extract --only portraits --only artifacts --only phantimals |
| Help | ./afkj-parse --help, ./afkj-parse build --help, ./afkj-extract --help |
The whole routine, with stargazer's side, is in Integrations §2.1.
| How | Where it works |
|---|---|
| ./afkj-parse …, ./afkj-extract … | The documented way. Shell launchers for macOS and Linux that always run the code in src/. |
python3 -m afkj_parse … with PYTHONPATH=src | Anywhere Python runs, Windows included; the same program the launchers start. |
| pip install -e ., then afkj-parse … | Anywhere; installs both commands on the PATH (needs a recent pip). |
| make feed, make clean, … | Shortcuts for macOS and Linux, listed in section 10.1; each runs the commands above and adds nothing of its own. |
afkj-parse build prints one line per gate (source, model, feed, diff), the findings that need
attention under it, where it wrote, and a last line that is either PASSED or
FAILED with what to do next:
afkj-parse build extract 1.6.11, build 14368
source out/raw (read-only)
✓ source passed
✓ model passed 1 warning, 4 notes
! M3 heroes missing a core skill slot: general: skill2, skill3
✓ feed passed 1 note
✓ diff passed no changes since the baseline
feed out/api 129 heroes · 18 artifacts · 5 phantimals · 19 charms · 16 locales
diff out/build/diff.md
report out/build/report.md
model out/build/model
PASSED in 4.8s
| Mark | Meaning |
|---|---|
✓ / ✗ | a gate passed / failed; a failed gate stops the build and the feed is not written |
✗ F12 | an error, with its check code; → under it is the suggested fix |
! M3 | a warning: worth reading, does not fail the build (unless --strict) |
· M17 | a note: counted by default, listed with -v; report.md always has them all |
– | a gate that did not run, because an earlier one failed |
-q prints only errors and the result line, --json prints report.json. Output is
coloured on a terminal and plain when piped or redirected; NO_COLOR=1 turns colour off and
FORCE_COLOR=1 on. Fatal errors (a missing source, a bad config.json) print one
error: line and exit 2.
Install the libraries once, then use the bundled ./afkj-extract launcher — no package install, and it
always runs the current code in ./src.
# one-time: install dependencies
python3 -m pip install --user pycryptodome lz4 lupa
# + for the textures stage: Pillow texture2ddecoder
# run (from the project folder)
cd afkj-parser
./afkj-extract [args]
Requires Python ≥ 3.9 and a local AFK Journey install whose "Complete" resources have finished downloading.
A pip install -e . (to get a bare afkj-extract on PATH) needs a recent pip/setuptools; the
launcher avoids that. No stage reads the Unity bundles; the research/atlas_sprites.py sprite tool needs
UnityPy (the [bundles] extra).
afkj-extract … for brevity. Run it as ./afkj-extract … (the
launcher, simplest) or PYTHONPATH=src python3 -m afkj_extract …, from the afkj-parser
folder. For a friendlier, fully-spelled-out walkthrough see the How-to.
afkj-extract [--source DIR] [--out DIR] [--only STAGE ...] [--tables NAMES]
[--textures GLOB] [--hero NAMES] [--linear] [--reference DIR] [--limit N]
[--list | --dry-run] [--verify REF] [-q]
With no arguments, afkj-extract auto-discovers the game container, runs the data stages
(localization + tables), and writes to ./out. The textures, portraits,
artifacts stages are opt-in (via --only) and need the
[textures] extra. Each run first prints what it will touch:
afkj-extract game 1.6.11
source ~/Library/Containers/<uuid>/Data/Documents/data (read-only)
output out (write)
✓ loc 17 files · … records
✓ tables 924 files · … records
DONE in 38.2s → out
afkj-extract extracts all skill partitions present in the archive. For quick
iteration use --only to pick a stage, --tables to pick tables, or --limit to cap
the number of skill files.
| Stage | State | Produces |
|---|---|---|
loc | DONE | out/raw/locales/<code>.json — localization key → text, from the install or, with --locales-from cdn, all 17 from the CDN |
tables | DONE | out/raw/tables/*.json — hero/skill tables with resolved arrays and exact defaults, plus HeroScripts.json, the integer settings of each hero battle script, read from its bytecode (Raw data §10.5) |
textures | DONE (opt-in) | out/raw/textures/**.png — loose AST/ASTC art decoded to PNG. Needs the [textures] extra. |
portraits | DONE (opt-in) | out/images/heroes/ — every hero's roster portrait named by the feed's slug (<slug>.png, as stargazer names them), skin alternates in alt/, and manifest.csv. Full runs replace the folder; --hero updates selected heroes and preserves others. Art this
install has not downloaded is warned about and listed in the manifest, except under --hero, which
requests it explicitly and so fails. See Architecture → Portrait export. |
artifacts | DONE (opt-in) | out/images/artifacts/ — the permanent artifacts and the current season's (<slug>.png), told apart by manifest.csv's set column. Rebuilt on each run. See Architecture → Artifact & icon export. |
phantimals | DONE (opt-in) | out/images/phantimals/: the current season's Phantimal portraits (the CampPet units, sub-units included), named by the feed's slug (<slug>.png), and manifest.csv. Rebuilt on each run. See Architecture → Phantimal export. |
Without --only, the data stages (loc + tables) run.
textures, portraits, artifacts and phantimals run only when named with --only.
manifest.json describes the last extraction invocation, including selections, status and statistics.
Dry runs and preflight failures do not replace it; unselected output may be older.
| Flag | Meaning |
|---|---|
--source DIR | Game data directory to read. Default: auto-discover the sandbox container by bundle id. Point at a manual copy to extract off-machine. |
--out DIR | Output directory. Default ./out. Refused if it overlaps a game source tree in either direction or uses unsafe output links. |
--only STAGE | Run only this stage; repeatable (--only loc --only tables). One of loc, tables, textures, portraits, artifacts, phantimals. |
--tables NAMES | Comma-separated table names (whitespace is trimmed; empty and unknown values fail) for the tables stage (e.g. Unit,SkillBuff,TavernPool). Default: the curated core set and HeroScripts. Selecting specific tables skips skill partitions. |
--textures GLOB | Filter the textures stage by a path/filename glob (e.g. *herohead*). Default: all ~12k loose AST images under ui/. |
--locales-from install|cdn | For the loc stage. install (default) exports the languages downloaded in-game; cdn downloads all 17 from the game's public CDN (hash-verified, cached in out/raw/cdn/<build>/), after checking that the CDN's file table is the installed game's, so text and tables are one build. If the CDN is ahead: open the game, let it update, close it, re-run. See Integrations → CDN. |
--hero NAMES | Limit the textures or portraits stage to these heroes. Comma-separated; each is an English name (Karma), internal codename (doublehead), or Unit ID (132), case-insensitive. Matches playable heroes only, and prints what each name resolved to. Combines with --textures (both must match). See Architecture → Finding a hero's art. |
--linear | Write images' stored linear color values untouched. By default the image stages (textures, portraits, artifacts, phantimals) sRGB-encode them, the same conversion the game applies on screen, so PNGs match in-game colors. Use this only when feeding a linear-workflow tool. The default is checked against in-game screenshots; see Architecture → Color. |
--reference DIR | For the portraits stage: an existing portrait folder, such as stargazer's src/assets/images/character. The manifest's in_reference column says which portraits it has (yes/no); the summary lists the heroes it lacks and the reference files the export lacks. |
--limit N | Positive integer cap on skill partitions or textures. Limited runs preserve unselected output. |
--list, --dry-run | Validate selections and required dependencies, print the source, version and plan, then exit without writes. This does not decode every selected asset. |
--verify REF | After extracting, semantically diff out/raw/locales/* against a reference export directory (e.g. ../AFKJourneyData) and report missing, extra and changed keys. Any mismatch returns failure. |
-q, --quiet | Only warnings and errors. |
--version | Print the tool version and exit. |
Exit codes: 0 success; 1 extraction or verification failure; 2
argument, preflight, dependency or safety failure. Errors remain visible with --quiet.
# See what would happen — no writes
afkj-extract --list
# All data stages, to ./out (including all skill partitions)
afkj-extract
# Just localization, and check it against the reference export
afkj-extract --only loc --verify ../AFKJourneyData
# A few specific tables, fast
afkj-extract --only tables --tables Unit,SkillBuff,SkillBuffUI,TavernPool
# Core tables + only the first 20 skill files (dev loop)
afkj-extract --only tables --limit 20 --out /tmp/afkjout
# Extract from a manual copy of the data dir instead of the live container
afkj-extract --source /Volumes/dump/afkj/data --out ./out
# Textures (opt-in). Needs: pip install --user Pillow texture2ddecoder
afkj-extract --only textures --textures "*herohead*" # just hero-head icons
afkj-extract --only textures --hero Karma # one hero's art: portrait, ult, tavern art, skins, icons
afkj-extract --only textures --hero "Karma,Ravion,132" # several heroes (names, codenames, IDs)
afkj-extract --only textures # ALL ~12k images (slow)
# Hero portraits for stargazer (and skin alternates for chaldea)
afkj-extract --only portraits --reference ../stargazer/src/assets/images/character
cp out/images/heroes/*.png ../stargazer/src/assets/images/character/ # bulk replace, after the checks below
afkj-extract --only portraits --hero "Karma,Igor" # a few heroes
# Artifact art: the permanent six (stargazer) + the current season's set (chaldea), one folder
afkj-extract --only artifacts
# The current season's Phantimal portraits for chaldea
afkj-extract --only phantimals
research/portrait_audit.py (what changes), portrait_webp_check.mjs (what
the build ships), and portrait_import_check.py (match-screenshot import).
--textures "*herohead_132*" also matches heroes 1322–1325. For one hero prefer
--hero; with a glob, pin the boundary: "*herohead_132[._]*". --hero and
--textures only filter the image stages; without one of them in --only the CLI prints a
note and ignores them.
out/
manifest.json latest invocation: schemaVersion, versions, source, scope,
status, options, stats, optional error
raw/locales/en.json { "<key>": "<text>" }
raw/locales/_stage.json format version, game version, file-table hash, install or CDN build
raw/tables/Unit.json { fields:[...], arrayFields:[...], rows: { "<key>": {…} } }
raw/tables/Skill/Skill_0.json per-skill partitions
raw/tables/HeroScripts.json {Script, Name, Value}: hero battle script settings
raw/tables/_stage.json format version, game version, file-table hash, complete
raw/cdn/<build>/… the CDN download cache (--locales-from cdn)
raw/textures/**/*.png decoded art (only when the textures stage is run)
images/heroes/<slug>.png hero roster portraits (portraits stage)
images/heroes/alt/<slug>_alt.png skin portraits
images/heroes/manifest.csv slug, English name, unit ID, source files, in_reference
images/artifacts/<slug>.png permanent and current-season artifacts (artifacts stage)
images/artifacts/manifest.csv slug, English name, Fairy ID, set, season, source file
images/phantimals/<slug>.png the current season's Phantimals (phantimals stage)
tools/ outputs of tools/
…
Table rows are full objects keyed by primary key (grouped tables use JSON-encoded pairs such as "[1,1]"), with defaults applied and $arrayRef arrays
resolved inline (see Architecture → Output). Localization is a flat
{key: text} dict per language — the loc stage emits every language present
on the source (a language file appears only after you've selected it in-game; --list shows which),
or with --locales-from cdn all 17 from the game's CDN, after checking the CDN serves the installed build.
AFKJourney process): every read, listing or path check first confirms the game is closed, and refuses otherwise. There is no override, and it applies to --list / --dry-run previews and the tools too. If processes cannot be inspected, it refuses as well.| Symptom | Cause / fix |
|---|---|
error: no sandbox container for com.farlightgames.igame.ios | Game not installed, never run, or discovery metadata is inaccessible. Pass an explicit --source /path/to/data if known. Otherwise launch it once and let "Complete" resources finish downloading. Or pass --source to a copy of the data dir. |
error: AFK Journey is running: game files are never read while it is open | Close the game and re-run. There is no override. |
error: cannot check whether the game is running | Process inspection failed, so game files stay untouched. Fix the check (libproc or pgrep); there is no override. |
error: output … overlaps a game source tree | Choose an --out outside the container / app bundle. |
ModuleNotFoundError: lupa (tables stage) | pip install lupa (or pip install -e .). Localization needs only pycryptodome + lz4. |
No module named 'texture2ddecoder' or PIL (textures stage) | python3 -m pip install --user Pillow texture2ddecoder |
| Images look darker / more contrasty than in-game | They were extracted with --linear or by an older version, which wrote the stored linear values. Re-run without --linear. There's no HDR or color profile to add; the default output already has the game's sRGB encode. |
error: no playable hero matches '…' | Check the spelling. The error suggests the closest names. Use the name shown in-game, the codename, or the Unit ID. --hero covers playable heroes only, not NPCs or monsters. |
| Extraction is slow | A full run processes all available skill files. Use --only, --tables, or --limit for quick loops. |
Localization diffs in --verify (<0.1%) | Check whether the reference and install are the same game version. Small differences can be version skew, but still return a failure status until investigated. |
./afkj-parse turns raw tables into the feed stargazer imports. It reads either this repo's extract
output or an AFKJourneyData checkout, and never writes to either. What it builds is described in
Model & feed; the raw data in Raw data.
./afkj-parse build [--source DIR] [--source-kind auto|extract|dump] [--config FILE]
[--work DIR] [--out DIR] [--locales en,zh] [--baseline DIR] [--no-diff]
[--limit N] [--strict] [-v] [-q] [--json]
./afkj-parse check [--out DIR] [--strict] [-v] [-q]
./afkj-parse diff [--out DIR] [--base DIR] [--hero SLUG] [--locale CODE] [--config FILE] [--work DIR]
./afkj-parse accept [--out DIR] [--work DIR] [--baseline DIR]
./afkj-parse roster [--source DIR] [--source-kind auto|extract|dump] [--config FILE]
| Flag | Meaning |
|---|---|
--source | Raw data: out/raw/ by default (or $AFKJ_SOURCE); ../AFKJourneyData is the fallback when the extract fails its gate, or when there is no install to extract from (a --depth 1 clone is enough: Integrations §2.3). The layout is detected; --source-kind forces it. |
--config | Hero lists (blocklist, awakeningLv2Blocklist, forceParse), locales.skip and expectations (including diff.attentionShare); config.json by default. An unknown or mistyped setting stops the run (exit 2) and names it: counts are whole numbers from 0, attentionShare a number from 0 to 1. |
--work | The report (report.json, report.md), the diff report (diff.md) and the model dump (model/); out/build/ by default. diff and accept read the last report from it. |
--out | The feed; out/api/ by default, with out/internal/ beside it. Each replaced as a whole, and only when every gate passes. |
--locales | Emit only these locales. en must be among them; locales.skip still applies. |
--baseline, --base | The diff's base: out/baseline/ by default. diff --base also takes any feed directory. |
--no-diff | Skip the diff gate and diff.md. |
--hero, --locale | diff only: print every changed text of one hero, old over new, in one locale (en by default). |
--limit | Parse only the first N heroes, for development. The model gate runs; the feed is not written. |
--strict | Warnings fail a gate too. |
-v, -q, --json | List notes too; print only errors and the result; print report.json instead (section 1.2). |
| Command | Does |
|---|---|
build | Runs the source gate, the model, the model gate, the feed, the feed gate and the diff
against the baseline, in that order, stopping at the first gate that fails. Writes out/build/diff.md
and publishes out/api/ and out/internal/ only if every gate passes. See Model & feed §1.1. |
check | Runs the feed gate alone on a feed on disk, including the check for files the feed
does not list; the internal/ beside it is checked when there. |
diff | Prints what changed from --base to --out: flagged heroes
first, heroes whose English or Chinese text changed one per line, heroes changed only in other languages on one
line, then artifacts, phantimals, charms and buff records. With --hero, it prints one hero's changed
texts, old above new, with the changed words marked (coloured on a terminal, [-old-] and
{+new+} when piped). out/build/diff.md is the same report as Markdown. List changes are attributed from the last build report's lists
against _accepted.json's. See Model & feed §7. |
accept | Makes the feed stargazer imported the next diff's base: copies its bulk files into
out/baseline/api/, the internal files into out/baseline/internal/, and writes out/baseline/_accepted.json. Refuses (exit 1) without a
passing build report for this very feed, byte for byte. See Model & feed §7.4. |
roster | Prints every playable hero (ID, codename, name, status, hidden levels), the
status counts, and M6 and M12's suggestions for config.json. Status is feed,
feed (forced), hidden: blocklist, hidden: blocklist (permanent),
hidden: the game disables it or
no skill file; hidden levels read e.g. Awakening L2 (IsShow) or
Awakening L2 (list). |
Exit status: 0 passed, 1 a gate failed (or accept refused), 2 bad input (a missing source or feed, a
bad config, or --source, --out, --work and --baseline that are the
same folder or nested in one another; the default layout keeps them side by side under out/).
On build 14368, roster shows 129 heroes as feed, three as
hidden: blocklist (permanent) (Hogan, Guywin and Joey, not in the public game), and six as
hidden: the game disables it (Lorsan, Baden, Thane, Tephesia, Grezhul and a dev
placeholder); IsShow hides the Awakening L2 of Ulmus, Mikola, Sinbad and Harak.
| Target | Runs |
|---|---|
make feed | ./afkj-extract --locales-from cdn, then ./afkj-parse build. The game must be closed. |
make images | ./afkj-extract --only portraits --only artifacts --only phantimals, at a season start or when art changes |
make clean | Deletes everything under out/ except baseline/. It never promotes a feed. |
make check | Ruff and the offline test suite |
These are shortcuts for macOS and Linux; each runs the commands in its row and nothing else, so the commands
themselves work the same without make. The update routine is in
Integrations §2.1.