136 lines
8.4 KiB
Markdown
136 lines
8.4 KiB
Markdown
# 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/).
|
||
|
||

|
||
|
||
| 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 <iso|folder> 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 <slot 1-3> <world> <chapter> [--cursor]
|
||
llamamod --editor [project.llamalevel | t1l1m001] level editor
|
||
llamamod --editor-build <project.llamalevel> <out.wad> [--cd <dir>]
|
||
llamamod --editor-import <level.wad|t1l1m001> <project.llamalevel> [--cd <dir>]
|
||
llamamod --editor-new <project.llamalevel> [base level] [--name <name>] [--cd <dir>]
|
||
llamamod --import-level <project.llamalevel> (into the level folder, shown as a mod)
|
||
llamamod --remove-level <id|file> move a level mod's file to backups/levels/
|
||
```
|
||
|
||
Data folder: `-Dllamamod.data=<folder>` 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).
|
||
|
||

|
||
|
||
## 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).
|
||
|
||
### Paths
|
||
|
||
The tools find the original game files through these environment variables
|
||
([`tools/lamapaths.py`](../tools/lamapaths.py)); the defaults match a hand-made Wine setup.
|
||
Command-line options (`--base`, `--src`, `--root`, `--iso`) win over them.
|
||
|
||
| Variable | Used by | Default |
|
||
|---|---|---|
|
||
| `LLAMAMOD_CD` | `levelbuild.py`, `debugviz.py`, `debugviz_check.py`, Gradle tests (`-Dllamamod.cd`) | `$LAMA_WINEPREFIX/iso-dk/Setup/Eng/Cd` – the CD folder (`wads/`, `music/`, `*.cor`) |
|
||
| `LAMA_WINEPREFIX` | `lamatest.py`, `levelbuild.py --install` | `~/.wine-lama` |
|
||
| `LAMA_MOD` | `lamatest.py` | `~/.local/bin/lama-mod`, else `tools/lama-mod` |
|
||
| `APPTEST_ROOT` | `apptest.py` (session folders) | `$CLAUDE_JOB_DIR/tmp` if set, else `~/.cache/llamamod-apptest` |
|
||
| `APPTEST_ISO` | `apptest.py` (first install) | `~/Downloads/GROOVE_DK.iso` |
|
||
| `LAMATEST_DIR` | `lamatest.py` (state, logs) | `/tmp/lamatest-$USER` |
|
||
|
||
A missing folder stops the tool with a message naming the variable to set.
|
||
|
||
## 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. Session folders and the ISO: `APPTEST_ROOT`, `APPTEST_ISO` (see
|
||
[Paths](#paths)).
|
||
|
||
```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 <projdir> 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 |
|