14 KiB
Built-in cheats (cheats, CHEATS)
mods/cheats/CheatsMod – one groove.exe hook, no data changes.
How the game does it
Main menu frame function 0x54B460 (state 2, table 0x579050): whenever a pad bit is held (0x5FCF08 ≠ 0) it calls FUN_0041a160(0), the sequence matcher:
for i in 0..5:
if newlyPressed(0x5FCF30) & seq[i][pos[i]]: pos[i] at 0x58483C + 4i
pos[i]++
if seq[i][pos[i]] == 0: sequence complete
if cheatFlags(0x584710) & (1<<i): clear bit (toggle off, no sound)
else: set bit, play sound (FUN_00546170(0,-127,8))
i == 2: next mode 0x584700 = 0x0A, 0x584638 = 4 (go to the credits)
pos[i] = 0
else pos[i] = 0 any other key resets this cheat
Sequence table 0x571FBC, 6 × 9 dwords (8 steps + 0). Menu key bits (FUN_0041a220, raw DIK state array 0x58226C + 4·DIK): ↑ 0x10, ↓ 0x40, ← 0x80, → 0x20; Return 0x4000, Space 0x8000, C 0x2000, A 0x400, S 0x800, Esc 0x1000, Tab 0x08, backslash 0x01. Typing a sequence works with arrows alone; no extra key has to be held (the "hold" is just that the matcher only runs while some key is down). The flag word is never saved or reset (only bit 2 is cleared after the credits), so cheats stay on until the game quits.
Decoded cheat table
| Bit | Sequence | Read at | Effect | Option |
|---|---|---|---|---|
| 0 | ← → → → ↑ → → ↑ | 0x54F5C9, VM opcode 0xC0 "hurt player" (FUN_0054f5c0) | energy (0x584E70) is not decreased → no damage, no lost lives | health (default on) |
| 1 | ↓ ← → ↓ ↓ ← → ↓ | strat property 0xEA (0x5524F4), read by the Llama strat at chapter start | property 0xFA (0x584E60, pink idol counter, chapter collectible) = 50 | collectibles |
| 2 | ← ← ← ← → → → → | FUN_0041a160, 0x54A5F6 | plays the credits (CREDITS.dat) right away, then back to the main menu, bit cleared | – (one-shot, not a setting) |
| 3 | → → ↓ ↓ ↓ ↑ ↓ ↓ | 0x54D6F9 (chapter finished) | chapter flags ` | = 3` (collectible + gold coin, KSV +0x08) |
| 4 | ↑ ↓ → ← ← ← ← ↓ | strat property 0xB2 (0x550EE1) | property 0xC0 (0x584FF4, purple fruit counter) = 500 instead of 0 | fruit |
| 5 | ↑ ← ↓ → ↓ ↓ ↑ ↑ | strat property 0x97 (0x55100C) | property 0xE8 (0x6DA304, charge bar) = 165 instead of 50 | charge |
Llama strat (t1l1m001 STPC 0x429536, same in all 23 level WADs) – VM short opcodes: 0x07 p = push property p, 0x14 n = pop, skip n words if zero, 0x16 n = skip n words, 0x40 p = pop into property p, 0x44 n = push n, long opcode 0x45 = push next word:
if prop[0x97]: prop[0xE8] = 0xA5000 else prop[0xE8] = 0x32000 ; 165 / 50 (20.12)
if prop[0xB2]: prop[0xC0] = 0x1F4000 else prop[0xC0] = 0 ; 500 / 0
if prop[0xEA]: prop[0xFA] = 0x32000 ; 50
Strat property switch: FUN_00550e60 (get, 0x10A cases, table 0x552684), FUN_005509f0 (address for store, index table 0x550D64 → 0x550C78).
Patch
call FUN_0041c050 at 0x41D3EA in WinMain (right after the command line is parsed, before
DirectX init) → cave: or dword [0x584710], mask (+ mov byte [0x5834D0], 1 for
diagnostics) → jmp 0x41C050. The cave writes only game globals, so it needs no data block.
Hidden debug features found
| Feature | State | In the mod |
|---|---|---|
-display_diagnostics (DAT_005834D0) |
works: writes <COMPUTER>.dispdiag (DDraw/D3D enumeration) into the game folder; FUN_0040f4f0 opens every display mode of every DirectDraw device × D3D device (FUN_00410550/FUN_00410700 = mode switch, FUN_00411000), logs caps and texture formats, closes it again, then returns and the game starts normally. On Windows with two monitors both screens flashed black for 30+ s (tester, 2026-11); invisible under Wine |
option diagnostics, label "Troubleshooting: write display diagnostics", off by default, warning in its description (tested: ELIASPC.dispdiag, 60 KB) |
c:\kingdom_dbg.txt |
dead: FUN_0040d2c0 (called from FUN_0043c220) only creates the file with "Logfile %s started now/today (write this code!)"; the 43 debug prints (FUN_00414600) go to OutputDebugStringA (0x6DC3E8), never to the file | no |
| FrameAdvanceDelay (FUN_0043cb40) | slide show In\001.bmp… + In\In.wav at start, Out\… at exit, delay from registry FrameAdvanceDelay (default 1.0 s); folders are empty |
no (data, not a cheat) |
| F4 in game | requests state 0x0C (world map) – the normal "back to map" | no |
| F7 in game | toggles 0x584708 → start row of the strat debug print (0x6D9DB8 = 0x20 / 0x48); nothing visible in retail strats | no |
| F6 | screenshot capture%d.tga |
no |
CheatTeleport, CheatCharge strats |
present in most level WADs; gated by property 258, see "Developer switch (property 258)" | no |
Level select / unlock-all is the save editor's job (--unlock), not duplicated here.
Verified in game (Wine 6, Xvfb :8, Village 1-1)
- All cheats on: chapter start shows the full charge bar, pink idol 50, purple fruit 500
(
img/mods/cheats_hud.jpg, original on the left). - Infinite health: running off the cliff into the sea three times – original loses an energy
segment per fall (HUD shows the red segment), with the cheat the energy HUD never appears, no
loss (
img/mods/cheats_fall.jpg). rewards(bit 3) decoded only, not played through to a chapter end.- Diagnostics: file written, game runs normally.
Frog moonjump (PS second controller)
Question from speedrunners: is the frog moonjump (PS: needs a controller in port 2) reachable on PC? Answer: the logic is in the PC data, but its input can never be set on PC without patching.
Strat side (all 34 level WADs disassembled, 2026-11-08). Property 88 padAux (DAT_005FCF58) is read
only by Llama (23 WADs), LlamaChase (t1l2m003), KuscoBunny (t1l7m004, t1l8m001, t1l8m002), KuscoFrog
(t1l4m003, t1l8m003) and CheatTeleport (26 WADs, also padAuxPressed). Everything except the frog's
air handler is additionally gated by property 258 (0x102), which the exe getter hard-wires to 0
(jump table 0x552684[0x102] → 0x55231B push 0); CheatTeleport removes itself when it is 0.
KuscoFrog (t1l4m003 unit 44), airborne handler L_136c – no prop-258 check:
if (self.padAux & 0.5) { ; bit 0x800
g22 = 0.055 ; vertical speed upward, every frame while the bit is set
v31 = 1.0
}
self.y = max(self.y + g22, v44)
Ground handler L_1d6c (take-off): (padPressed & 4.0 /*jump*/) | ((padAux & 0.5) && prop258) → the
aux bit alone cannot start a jump, it only keeps the frog rising once it is in the air (= moonjump:
jump normally, then hold the aux button). Llama/Bunny/LlamaChase have the same code (Llama: g22 = 0.0698)
but behind prop258, so they never moon-jump.
Exe side: 0x5FCF58 is referenced exactly 3× in groove.exe (all 4-byte immediates scanned): 0x425B93
mov dword [0x5FCF58], 0 (end of the pad poll FUN_004256C0, only if pad count 0x5FCF6C > 1, i.e. a
DirectInput joystick is present), 0x425C1E (edge detect → 0x5FCF60 padAuxPressed), 0x551403 (strat
getter, prop 0x58). It lives in .bss → always 0. No keyboard key, joystick (1st or 2nd), debug key or
demo (.dem) playback writes it. Under Wine without a joystick: pad count 1, padAux 0 (read in game).
Verified in game (apptest :25, t1l4m003 Jungle day ch. 3, teleported onto PotionPadFrog → FrogPotion
→ frog): set {int}0x5FCF58 = 0x800 via the winedbg gdb proxy. Frog on the ground: nothing happens.
Frog jumps (X): it keeps rising – y 13460 → 41617 (~2 s) → 59842 (~4 s), far above the level; clearing
the bit makes it fall back. Same write as the llama: normal jump, no effect (prop 258 = 0).
img/mods/frog_moonjump.jpg (top: normal frog jump, bottom: bit 0x800 set).
To enable on PC a patch is required, e.g. make the pad poll map a key to padAux bit 0x800. Which PS button of pad 2 feeds bit 0x800 is not known (no PS binary analysed) – hypothesis only.
Developer switch (property 258)
Strat property 258 (0x102) is a hard-wired developer flag: getter FUN_00550e60, jump table
0x552684[0x102] → 0x55231B mov eax,[esp+14h]; push 0; push eax; call 0x54BBD0 (the push 0 byte is
at 0x552320; the same handler also serves the unread ids 0x75, 0x9E, 0xC1, 0xEE). It is read
101× in the 34 level WADs (all units disassembled; pushProp only, no player/parent/... variants).
Every use is a logical test (and, not, jz), so any non-zero value enables it.
Keys (in-game padHeld 0x5FCF20, measured with gdb while holding the key): A 0x400, S 0x800
(camera left/right), Return 0x1000, Z 0x2000 (action), X 0x4000 (jump), Space 0x8000 (power),
P 0x08 (pause). No keyboard key gives 0x100/0x200. padAux (prop 88, 0x5FCF58) and
padAuxPressed (prop 90, 0x5FCF60 = edge of padAux, computed every frame at 0x425C1E) are never
set on PC (see above). Strat constants are 20.12: 0.25 = 0x400, 0.5 = 0x800, 0.75 = 0xC00,
10.0 = 0xA000, 256r = raw 0x100.
| Reader strat | Levels | Gate | Bits / keys | Effect | Status |
|---|---|---|---|---|---|
| CheatTeleport (+CheatCharge, same unit) | placed in 22 levels: 1-1..1-4, 2-1, 2-2, 4-1..4-3, 5-1..5-3, 6-1..6-3, 7-2, 7-4, 8-1..8-5 (code also in 3-1..3-4, not placed) | entry if (!prop258) remove at level start |
hold A+S (padHeld 0xC00) | teleports the llama to the next node of the strat's own path (hand-placed warp list, 1–18 nodes per level, often next to checkpoints, doors, puzzles; wraps around); sets g0 = 14 |
verified 1-1 (4 nodes, cycled twice) |
| CheatTeleport | same | prop258 | padAuxPressed 0x400 | +1 heart (if alive), +5 purple fruit, hudDigit = charge | verified (fruit 0 → 5, HUD shows it) |
| CheatTeleport | same | prop258 (+ prop 0 == 0) | padAux 0x100 held | +1 coin per frame (prop 190, coins of this level) | verified (HUD 307 coins after 0.5 s) |
| CheatTeleport | same | prop258 (+ prop 0 == 0) | padAux 0x200 held | +1 pink idol per frame, max 99 (prop 250) | verified (0 → 65 → 99) |
| CheatTeleport | same | prop258 | hold Space+Z (0xA000) | only print debug lists (player/camera state dump: "gFreezePlayer", "gCamAng", "teleported to location (" …) that the PC VM skips → nothing visible |
static |
| CheatTeleport | same | prop258 and prop 0 ≠ 0 | padAux 0x100 held = debug menu; then padPressed Space/Z = previous/next music track (spawns unit 48 = track display, musicPlay), X = lose heart, Return = +1 idol, A = +1 coin (+10 charge every 5), S = full charge, 0x100 = endLevel(g101,g102,g103) (finish level), 0x200 = +1 counterC; combo becomes 0xF00 |
unreachable even with prop258 = 1 (prop 0 is also hard-wired 0, handler 0x552192) | static |
| Llama | 23 levels (all with a llama) | prop258 | padAux 0x800 held | moonjump/fly: starts a jump on the ground without pressing X (no cooldown check) and sets vertical speed 0.0698/frame every frame in the air → rises until released | verified 1-1: y 8.25 → 20.6 in ~2 s without touching a key |
| LlamaChase | 2-3 (night chase) | prop258 | padAux 0x800 | same moonjump; also allows jumping anywhere (normally only on floor material 11) | static (chase intro did not start under load) |
| KuscoBunny | 7-4, 8-1, 8-2 (potion) | prop258 | padAux 0x800 | rabbit jumps/rises with speed 0.045 while held | static |
| KuscoFrog | 4-3, 8-3 (potion) | prop258 only for take-off | padAux 0x800 | air rise has no gate (moonjump agent, verified); with prop258 the bit also starts the jump | static |
| KuscoAndPachaTogetherOnATree (river log) | 3-1..3-4 | prop258 | hold Space (0x8000) | damage handling skipped (invincible), +1 heart every frame, collision flag 0x40000 off (passes through banks), horizontal movement ×8, no current/steering term | verified 3-1: with Space the log shot through the river bank out of the level |
| RiverWhirlpool | 3-2 (36×) | prop258 | hold Space | whirlpool does not pull the log | static |
| CondorManDoor | 5-2 | prop258 | padHeld 0x100 (no PC key) | a hit always takes the "straight hit" branch (v16 = 1, later otherPiece() on impact; hypothesis: the door breaks) instead of needing the angle within 96 |
static |
| EndGameTriggerBox | 8-6 (finale, 15×) | prop258 | hold Space+Z during the box's wait time | endLevel(1,1,1) at once (hypothesis: skips the rest of the finale) |
static |
How it was verified (apptest devflag-a, :25, German exe): set {char}0x552320 = 1 via the winedbg
gdb proxy at the main menu (CheatTeleport removes itself at level start, so the patch must be in
before the level loads), then set {int}0x5FCF58 = 0x100/0x200/0x400/0x800 one at a time.
img/mods/devflag_teleport.jpg (1-1 start → three A+S warps), devflag_aux_moonjump.jpg (before /
after padAux 0x400+0x100: fruit 5 and 307 coins on the HUD / bit 0x800: llama flying above the
village), devflag_river.jpg (3-1 log normal / after Space boost in the bank / outside the level).
Note: the exe run by LlamaMod relocates the per-level array 0x584F1C to 0x6DB940 (seen at 0x550BC2),
so levelRecord/coins are not at the address of re/groove.exe.
Property 0 is a second hard-wired 0 read by strats (131×: Camera, CameraSide, CheatTeleport, birds, butterflies, ladybirds, moths, Bunny, Panther, Yzma…). Ambient critters with a parameter set remove themselves when it is non-zero, the camera uses alternative heights/distances when it is 0, and CheatTeleport switches its combo to 0xF00 – hypothesis: a platform/hardware flag (PS button layout or low-memory mode), not a debug flag. Other constant getter cases: 0x5E/0x60 = 84, 0x5F/0x61 = 123, 0x82/0x83 = 42 (stick thresholds), and 0, 0x13, 0x15, 0x47, 0x75, 0x76, 0x77, 0x98, 0x9E, 0xC1, 0xD8, 0xED, 0xEE, 0x102 = 0; of the zero ids only 0 and 0x102 are read by any strat. No exe code writes padAux, and no other dev key handler was found beyond the table above (F4, F6, F7).
Suggested "Developer mode" option for the cheats mod (not implemented): patch byte 0x552320 to 1 (or point jump-table entry 0x552A8C at a cave that pushes 0x1000, leaving the shared handler alone), and map keys to padAux in the pad poll: at the end of FUN_004256C0 (0x425B88, where padAux is only cleared when >1 pad) jump to a cave that builds padAux from the DIK state array 0x58226C + 4·DIK, e.g. F1 → 0x800 (moonjump), F2 → 0x400 (heart + fruit), F3 → 0x200 (idols), F5 → 0x100 (coins). A+S teleport and Space (river) work with the normal keys once prop 258 is 1. Note that F4/F6/F7 are taken; the frog's ungated moonjump would also react to the 0x800 key.