Wolfenstein Modloader Buy me a coffeeKaffee spendieren

Wiki / Latest research roundsNeueste Forschungsrunden

R4 Research Report: The Console Command Gate (TNC)

Partly solvedTeilweise gelöst Original page in the GitHub wikiOriginalseite im GitHub-Wiki

In plain wordsIn einfachen Worten

This report studied, by reading the program code of Wolfenstein II: The New Colossus, why its console only accepts 34 commands. It found that one shared number decides whether the short or the full list (234 commands, 3,256 settings) is searched, and that resourceExec lowers this lock while running a file. Everything here is static analysis; the game was not run for this report (the in-game proof came later on a separate page).

  • The lock is one number: 0 means all commands are reachable, 1 means only the 34 exposed ones.
  • Six entry points (console typing, startup arguments, exec, and others) set the lock to 1 briefly and always restore it.
  • Hidden commands like map, devmap, teleport, killMonsters and God are registered and reachable through resourceExec.
  • Correction: SpawnAI, like give, spawn, noclip and lowercase god, does not exist in this retail build.
  • The DevGUI debug menu offers 41 working hidden buttons once switched on; 18 of its buttons point to commands that do not exist.

Dieser Bericht hat durch Lesen des Programmcodes von Wolfenstein II: The New Colossus untersucht, warum die Konsole nur 34 Befehle annimmt. Ergebnis: Eine gemeinsame Zahl entscheidet, ob die kurze oder die volle Liste (234 Befehle, 3.256 Einstellungen) durchsucht wird, und resourceExec senkt diese Sperre, während es eine Datei ausführt. Alles hier ist statische Analyse; für diesen Bericht lief das Spiel nicht (der Beweis im Spiel kam später auf einer eigenen Seite).

  • Die Sperre ist eine einzige Zahl: 0 heißt, alle Befehle sind erreichbar, 1 heißt, nur die 34 freigegebenen.
  • Sechs Einstiegspunkte (Konsoleneingabe, Startargumente, exec und andere) setzen die Sperre kurz auf 1 und stellen sie immer wieder her.
  • Versteckte Befehle wie map, devmap, teleport, killMonsters und God sind registriert und über resourceExec erreichbar.
  • Korrektur: SpawnAI gibt es in dieser Verkaufsversion ebenso wenig wie give, spawn, noclip und kleingeschriebenes god.
  • Das DevGUI-Debugmenü bietet nach dem Einschalten 41 funktionierende versteckte Schaltflächen; 18 seiner Schaltflächen verweisen auf nicht vorhandene Befehle.

Technical detailsTechnische Details

Der technische Teil ist auf Englisch – so, wie er im Wiki steht. Unterstrichene Begriffe haben eine Erklärung auf Deutsch.

Static disassembly of a read-only snapshot of the running NewColossus_x64vk.exe process (no debugger attached, no game launched by this track). Builds on the command-table and cvar-table research already on the wiki.

Summary

The question was whether the retail console's ~34-command limit can be bypassed to reach commands like map, devmap, teleport, killMonsters, give, spawn, noclip and god without patching the executable. The gate turned out to be a single process-wide integer shared by the command and cvar systems: at rest it is 0 (unrestricted, all 234 commands / 3,256 cvars searchable), and only narrow, self-restoring windows — one console keystroke, one exec, one startup line — force it to 1 (the restricted, 34-command list) and then put it back. The already-proven command resourceExec <file> -s is the one built-in command that deliberately drops the level to 0 for the duration of the file it runs, making every registered command and cvar reachable with no code patch. All of this is static analysis of the executable image; nothing below was confirmed with the game actually running. Correction applied to this report's own earlier claim: SpawnAI is not a registered command in this retail build, the same as give, spawn, noclip and lowercase god — none of these five names appear anywhere in the 234-entry command table, under any flag.

1. The shared gate

idCmdSystemLocal (RVA 0x4104420) holds two command lists — "all" (234 entries) and "exposed" (34 entries) — plus a restriction-level field at +0x200b8 (RVA 0x41244d8). idCVarSystemLocal shares the identical global for its own 3,256-entry / 304-exposed split. The dispatcher (0x1265150) picks which list to search with one cmp/cmove against that global:

level == 0  -> search all 234 commands / 3,256 cvars
level != 0  -> search only the 34 exposed commands / 304 exposed cvars

A command not in the active list prints Unknown command '%s' — this is the entire reason map/devmap/teleport/killMonsters fail from a normal console line even though they are registered.

2. What sets the level, and that it always comes back

Six built-in entry points write 1 to the level: both console-input handlers, the +-prefixed startup-argument parser, the startup-config exec, the exec command itself, and the one-time engine Init() call. All six were disassembled in full: every one of them saves the old value and restores it before returning. Nothing in the binary ever leaves the level changed after it returns, and the constructor default is 0 — so for nearly the entire session the level simply sits at 0, unrestricted, except for the handful of instructions it takes to process one keystroke or one config line.

resourceExec (one of the 34 already-exposed commands, callback 0xa7a040) is the one path that deliberately sets the level to 0 while a resource-archive file it reads runs, then restores it afterward — this is the mechanism the existing custom-map/decl-edit route already relies on.

3. DevGUI: a second route, with a caveat

The compiled-in ImGui debug menu (idDevGuiMainPage_*/idDevGuiChildPage_*) calls the dispatcher directly from 74 call sites across 28 functions, and — unlike the six entry points above — without its own save/restore bracket, so a DevGUI click runs at whatever level is currently ambient (which is 0 essentially always). Of those 74 calls: 12 already work from the plain console (exposed), 41 are real registered-but-non-exposed commands/cvar toggles that become clickable once DevGUI is drawing, and 18 — including the DevGUI's own "Give Ammo", "Give Grenades" and "spawn %s" buttons — reference names that are not registered at all; those buttons are dead code, consistent with give/spawn not existing as commands.

devgui is itself a non-exposed cvar (needs the same level-0 window once to turn on, e.g. devgui 4), but after that its buttons keep working for the rest of the session with no further resourceExec call needed.

4. devMode_enable is a real gate, but an unreachable one

The dispatcher has a second branch, after a found command executes, that checks devMode_enable: if 0, it prints "Dev Mode is not enabled..." and returns without executing the command; if 1, it warns and executes anyway. This branch only fires for a command found at level !=0 that lacks the exposed bit — a combination none of the documented entry points (console, +args, exec, resourceExec) or the DevGUI calls ever produce (at level 1 only exposed commands are found; at level 0 the dispatcher already took its execute-immediately exit before this branch). So the branch is real but practically dead on every reachable path. devMode_enable and com_gameMode are themselves exposed, retail-settable cvars — a live process snapshot showed both already set to non-default values (1 and 5/GAMEMODE_MOD) with no crash — but neither one affects the restriction level.

5. Verdict per name

Name Registered? Exposed? Reachable without a patch?
map, devmap, cmap, fastmap, and the other map-load variants yes no yes, via resourceExec level-0 window
teleport, setviewpos, killMonsters, killAI, killAllAI yes no yes, via resourceExec
getviewpos, listCmds, exec, vid_restart, BuildInfo yes yes already works in the plain console
God, InfiniteHealth, NoPlayerDeath, Notarget yes (flag 0x200000) no yes, via resourceExec; no internal gate in the callback itself beyond "is a game active"
god (lowercase), noclip, spawn, give/give all, SpawnAI no — does not exist in this build at all — a code patch would be required to add it

listCmds is a cheap, non-destructive way to confirm the model in game: typed into the plain console it must print exactly 34 names; called from inside a resourceExec'd file it should print all 234.

6. Cvar equivalents for "give all" / infinite ammo / noclip

The cvar system shares the same gate, so these non-exposed CVAR_CHEAT cvars are reachable the same way, from inside a resourceExec'd file:

Cvar Description
g_infiniteAmmo 1 = infinite ammo for player/AI/turrets, 2 = infinite backpack ammo
g_infiniteEnergy / g_infiniteThrowItems infinite energy / infinite grenades
g_demoAmmo fully replenishes weapon ammo on every pickup
snapEdit_devMode "Sets dev mode on match parms. This will give the player ALL THE THINGS" — closest literal match to "give all"; untested, inferred to need setting before the map loads
pm_noclipspeed / pm_noclipfriction confirm a noclip mode exists in the engine, but no command named noclip triggers it in this build — likely only reachable from a DevGUI page not individually traced

7. The decl route (already proven, bypasses this gate entirely)

Decls are a completely separate subsystem from the command/cvar restriction level — they are data the resource loader reads, not something idCmdSystemLocal touches. For give/spawn, which do not exist as commands under any flag, the decl route (starting-inventory/pickup decls) is the actual mechanism the game uses, not a workaround.

Recipe

  1. Add a small script file reachable through the already-proven dlc_0-precedence mod route, e.g. sandbox.cfg: devgui 4 devmap <map>
  2. From the normal, restricted console (which can still run resourceExec, it is one of the 34 exposed commands): resourceExec sandbox.cfg -s. Both lines run at level 0. devgui 4 is a one-time switch; afterward its ~50 confirmed-live buttons (g_infiniteAmmo, killAI/killAllAI, dozens of toggle <debug cvar> switches) work without any further resourceExec call.
  3. For anything DevGUI doesn't cover, or to batch several lines (God 1, Notarget 1, snapEdit_devMode 1, devmap <map>), put them in one file and call it the same way.
  4. For actually giving weapons/items (give/spawn do not exist as commands): use the decl/starting-inventory route instead — see the pickups report.

Open questions

  • Whether resourceExec resolves a brand-new filename from a mod archive the same way it resolves an overridden existing name — untested.
  • Where the noclip mode implied by pm_noclipspeed/pm_noclipfriction is actually toggled if not by a console command.
  • Whether snapEdit_devMode truly grants "ALL THE THINGS", and at what point it is consumed.
  • Whether idCmdSystemLocal::Find's name lookup is case-sensitive (would decide whether some DevGUI button strings with different casing than the registered name are themselves silently dead).
  • A generic ExecuteCommandTextAtLevel-style helper (RVA 0x1265800) is called from ~14 places across the engine beyond Init()/DevGUI; whether any of the untraced call sites passes level 0 with a mod-controllable string during normal gameplay — independent of resourceExec and DevGUI — was not resolved.