# Tools Everything in this repository that you can run, what it is for and where its code and notes are. None of the tools contain game data; they all work on your own copy of the game. ## LlamaMod (Java app) The main product: a mod manager with a launcher and a 3D level editor, for Windows and Linux. Java 25 + Swing, sources in [`src/main/java/com/eliasmueller/llamamod/`](../src/main/java/com/eliasmueller/llamamod/). ![The LlamaMod launcher](img/launcher.jpg) | Part | What it does | Docs | |---|---|---| | launcher | first-start wizard (copies the game from CD/ISO), mod list with options, save-game editor, starts the game (Wine on Linux) | [manual: Installing](manual/02-installing.md), [manual: Mods](manual/18-mods.md), [notes/llamamod-app.md](../notes/llamamod-app.md) | | level editor | 3D editor for new and original levels: brushes, tiles, meshes, textures, objects, waypoints, cameras, zones, lights, visibility, strat scripts, play-test, glTF export | [editor manual](manual/index.md) | | mods | Test World, Alligator Arena, level projects, Boot to level, Intro skip, Mirror mode, Practice tools, Randomizer, Draw distance, Built-in cheats, Debug wireframe, Free camera | [manual: LlamaMod mods](manual/18-mods.md), `notes/mod-*.md` | ### Command line ```text llamamod start the GUI llamamod --version show the version and build id llamamod --install set up the game data llamamod --mods list available mods and their options llamamod --apply [id,id,...] [--opt id.key=value ...] apply mods (none = original); options are saved llamamod --launch [--wait] start the game llamamod --registry show the registry values llamamod --saves show save games llamamod --unlock [--cursor] llamamod --editor [project.llamalevel | t1l1m001] level editor llamamod --editor-build [--cd ] llamamod --editor-import [--cd ] llamamod --editor-new [base level] [--name ] [--cd ] llamamod --import-level (into the level folder, shown as a mod) llamamod --remove-level move a level mod's file to backups/levels/ ``` Data folder: `-Dllamamod.data=` or `LLAMAMOD_DATA` (default `~/.local/share/LlamaMod`, Windows `%LOCALAPPDATA%\LlamaMod`). Source: [`Main.java`](../src/main/java/com/eliasmueller/llamamod/Main.java). ### Debug view The *Debug wireframe* mod ([`mods/debug/`](../src/main/java/com/eliasmueller/llamamod/mods/debug/)) adds visible geometry to every level: collision as coloured frames (invisible walls filled red, the exit floor violet, death planes magenta) and markers for invisible objects (start, exit, checkpoints, cameras, triggers, zones). Gameplay is unchanged. Its Python ancestor is `tools/debugviz.py`; colour legend and details: [notes/debugviz.md](../notes/debugviz.md). ![Debug view in Village 3](img/debug-view.jpg) ## Python kit (`tools/`) The reverse-engineering toolkit the project started with. Python 3.8+ with `numpy` and `Pillow` (`capstone` for `disx86.py`). All parsers rebuild the original files byte for byte. | Tool | Purpose | |---|---| | [`wadtool.py`](../tools/wadtool.py) | list, extract and replace WAD chunks | | [`geomlib.py`](../tools/geomlib.py) | library: WAD, TRAK, STPC models, MAP tile part, TEXT, collision | | [`geom_export.py`](../tools/geom_export.py) | level → OBJ/MTL with textures and vertex colours, `--collision` (collision.obj by material), `--objects`, preview renders | | [`geom_import.py`](../tools/geom_import.py) | OBJ → new world geometry in a copy of a level | | [`geom_demo.py`](../tools/geom_demo.py) | example geometry for `geom_import.py` | | [`mapchunk.py`](../tools/mapchunk.py) | MAP parser/writer | | [`map_dump.py`](../tools/map_dump.py) | print a level's tiles, strats, lights | | [`strats.py`](../tools/strats.py) | object types of a level: names, instance counts, code offsets, type fields | | [`strat_table.py`](../tools/strat_table.py) | type × level table → [notes/strat_types_table.md](../notes/strat_types_table.md) | | [`stpcwalk.py`](../tools/stpcwalk.py) | rough walk through STPC (models, animations, code) | | [`textchunk.py`](../tools/textchunk.py), [`text_export.py`](../tools/text_export.py) | TEXT chunk; export pages to PNG and import them back | | [`texsheet.py`](../tools/texsheet.py) | contact sheet of a level's textures with indices | | [`levelbuild.py`](../tools/levelbuild.py) | level builder from a Python script ([guide](guide/levels-in-code.md)) | | [`make_testlevel.py`](../tools/make_testlevel.py) | early experiment: one object per type on a floor plate | | [`debugviz.py`](../tools/debugviz.py), [`debugviz_check.py`](../tools/debugviz_check.py) | debug-view WADs for all levels and an offline check | | [`cor2png.py`](../tools/cor2png.py), [`png2cor.py`](../tools/png2cor.py) | loading pictures (COR) ↔ PNG | | [`asf2wav.py`](../tools/asf2wav.py) | music (ASF) → WAV, exact decoder | | [`disx86.py`](../tools/disx86.py) | disassemble `groove.exe` at a virtual address | | [`vorschau/`](../tools/vorschau/) | Blender (Eevee) preview renders of a level with labels | | [`lama-mod`](../tools/lama-mod) | shell script: CD overlay of symlinks for mods in a hand-made Wine setup (pre-LlamaMod) | Level scripts in [`levels/`](../levels/): `testwelt.py` (the original test world), `sprunglabor.py` (jump lab used to measure the llama), `testwelt_parkour.seq` (scripted run). Several scripts have **hard-coded paths** to the original author's Wine setup at the top (see [Levels from scripts](guide/levels-in-code.md#a-python-level)). ## Testing in the game | Tool | What it does | Notes | |---|---|---| | [`tools/apptest.py`](../tools/apptest.py) | runs the game **through LlamaMod** in an isolated session: own data folder, Wine prefix, invisible X display (Xvfb), muted private PulseAudio; applies mods, unlocks a level, navigates the menus, presses keys, takes screenshots, detects crashes | [notes/llamamod-app.md](../notes/llamamod-app.md#testing-mods-in-game-toolsapptestpy) | | [`tools/lamatest.py`](../tools/lamatest.py) | the older harness for a hand-made Wine setup: loads a WAD as TestLevel and runs a key sequence | [notes/testharness.md](../notes/testharness.md) | Linux only; needs Wine, Xvfb, xdotool, ImageMagick, tesseract (German data, for reading menu texts) and PulseAudio. `apptest.py` defaults to a session root inside the author's job folder – set `APPTEST_ROOT`. ```bash ./gradlew fatJar tools/apptest.py run demo --display 7 --mods arena --level 9 49 \ --seq "Up:1.5 shot:walk z shot:kick" --out /tmp/demo ``` ## Reverse engineering * `re/ghidra_scripts/DumpAll.java` – headless Ghidra script that decompiles every function of `groove.exe` into one C file. The exe, the decompile and (currently) the script itself are excluded from the repository (`.gitignore`). Run `analyzeHeadless groove -import groove.exe -scriptPath re/ghidra_scripts -postScript DumpAll.java groove_decomp.c` (Ghidra 12.x needs JDK 25). * Key functions: MAP loader `0x42ac50`, TRAK `0x42aac0`, STPC `0x42ab50`, strat VM `0x54d180`, chunk dispatcher `0x558960`. ## Other folders | Folder | Content | |---|---| | [`examples/`](../examples/) | `testwelt.llamalevel`: the original test world as an editor project | | [`packaging/`](../packaging/) | icons, Windows (launch4j) and Linux packaging files | | [`deb-build/`](../deb-build/) | older script (German) that builds a Debian package of the game itself from your ISO, with the soundtrack converted to MP3 |