Debug and test switches
- Source
docs/executable/debug-switches.md- SHA-256
29a668b4…3562ce68
Summary · config.ini · auto_test · The shop's debug sequence · The @debug font · The debug heaps · Leads
What is left in the executable of the developers' debug and test switches, and whether any of it can be reached in the shipped game: the auto_test switch, the shop's debug sequence, the @debug font and the debug memory heaps. Also the settings file config.ini, the one outside file the game reads switches from. None of it gives a debug menu: each piece is either compiled out, never offered, or ordinary code with a debug name. The work it serves is R14, testing edits in the game.
| Based on | The unpacked executable, SHA-256 7b0230a11498f40396c9048345e7cfe0ef442cb7ca8faffc6f197920d2669599; menu/menu_data.dat entries #7 and #20, for the shop command texts. |
| Checked | 2026-10-11 |
| Method | Static analysis with Ghidra 12.1.4: the decompiled code in local/exe/decomp/, the indexes exe.calls and exe.strings (The indexes), objdump -d of the call sites, and xxd of the executable's key table and string pool. Every fact here is at most likely. |
Summary
| Switch | What it does | Can it be reached? |
|---|---|---|
config.ini |
The display settings: screen mode, monitor, adapter, resolution, window placement (config.ini). | Yes, by editing the file. |
auto_test |
When set, the game never writes config.ini (auto_test). |
No: the check always answers "not set". |
Shop DebugSequence |
Shop commands 8 and 9: enter the hero's name again, or the birthday (The shop's debug sequence). | No: no shop lists commands 8 or 9. Only a code patch could add them. |
@debug font |
The name given to a font's first face when it's loaded (The @debug font). | Not a switch: ordinary font loading. |
DEBUG, DEBUG_MENU, debug heaps |
Memory heaps registered at startup (The debug heaps). | No code allocates from them. |
config.ini
The game keeps its display settings in config.ini, a text file of Key=Value lines in the user's save folder. It isn't a game data file and isn't in Data/; the game writes it, so a mod never ships one.
- likely: it's in the save folder:
<Documents>\My Games\Tactics Ogre Reborn\Steam\<SteamID64>\when Steam is running, elseSaveData\beside the executable. The platform setup (0x140180c80) builds the Steam path fromFOLDERID_Documents(the GUID at0x1406AEF60); the framework setup (0x14015d120) falls back toSaveData\and stores the folder in the file manager at+0x48(0x14015bf60);0x14032d3c0appendsconfig.inito it. - likely: the game reads it at startup: the SystemInitData object is made with defaults (
0x14032ce60), then the file is loaded (0x14032d070), both from0x1402c2110. App init (0x140034d30) reads it once before that, withfopen, only to find theGraphicDeviceId=line and pick the graphics adapter before the renderer starts. - likely: the game writes it (
0x14032d2f0) at shutdown (0x1402c3470) and when the player quits from the menu (0x140314600), from the display in use, once per run unless forced, and never when auto_test is set.
Keys
Eleven keys, in this order, which is also the order the game writes them. Their names and types are a table of 0x24-byte records at 0x140687050 (a 32-byte name, then a type: 0 text, 1 integer, 2 decimal); the values live from 0x140F22490, 0x88 bytes each (127 characters of text, then an s32 and an f32). Source: xxd of both; the struct the loader fills (0x14032d070) and the code that applies it (0x14032c610).
| Key | Type | Default | What it sets |
|---|---|---|---|
ScreenMode |
text | FullScreen; Window when a debugger is attached (0x14032ce60) |
Window, FullScreen (exclusive full screen) or FullScreenVirtual (a borderless window the size of the monitor), matched ignoring case (0x14032cbc0). Anything else is Window. |
Monitor |
integer | 1 |
The monitor, counting from 1. Out of range is the first monitor. |
Adapter |
integer | 1 |
Nothing: the loader replaces it with the adapter in use (0x14032d070). The adapter is picked by GraphicDeviceId. |
MonitorDeviceId |
text | empty | A monitor's device name; when it names a connected monitor, it picks the monitor instead of Monitor (0x14032c610). |
GraphicDeviceId |
text | empty | The graphics adapter, read at startup by App init (0x140034d30). |
ScreenWidth |
integer | 0 |
In Window mode, the window's width, at least 800; 0 or less is the display's current width. In FullScreen, the display mode to use, with ScreenHeight: an exact match from the monitor's list of modes, else the list's last mode. Not used in FullScreenVirtual. |
ScreenHeight |
integer | 0 |
The height, as ScreenWidth; at least 450 in Window mode. |
WindowPosX |
integer | 0 |
The window's position, passed to the renderer with the mode. |
WindowPosY |
integer | 0 |
|
WindowWidth |
integer | 0 |
The window's size, passed the same way. |
WindowHeight |
integer | 0 |
How the game reads it
- likely: each line is
Key=Value. The key ends at a space or=and is matched ignoring case against the key table, by the length of the key in the file, so a shortened key matches the first key it begins (ScreenisScreenMode). A line with an unknown key, or one starting//, is skipped. The value runs from after the=to a tab, carriage return or line feed, at most 127 characters, and is kept as text; it's also read as a number, a decimal (atof) if it has a., else an integer (atoi). Source:0x1404458f0. - likely: the game writes every key as
Key=Valueand a line feed, text as is and integers in decimal (0x1404460b0), so the file always has all eleven keys.
For in-game tests, ScreenMode=Window with ScreenWidth and ScreenHeight runs the game in a window.
auto_test
likely: the switch only stops the game writing config.ini, and it can't be turned on. The save (0x14032d2f0) first asks the framework (the object at 0x140F6C778) whether an option named auto_test is set, by a direct call to 0x1400349d0, and writes the file only when it isn't. 0x1400349d0 is three bytes, xor al, al; ret: it always answers false. Source: objdump -d of both.
- likely:
0x1400349d0is a shared stub for every function that only returns false, folded together by the linker: script natives 272 and 273 call it too (What the natives do), as does game setup (0x1402c1970) twice, with no arguments. Ghidra names itSQEX::CDev::Engine::Sd::Driver::IStreamReader::vfunction3from one of the classes that share it. - likely:
auto_testis the only option name the code asks the framework about, fromexe.stringsand the stub's callers inexe.calls.
The shop's debug sequence
likely: the shop has a ninth mode, Taco::Application::DebugSequence, that no shop offers. Every shop builds all nine of its mode objects (0x14030d0c0, ShopMenu::vfunction4), and the top menu (0x14040e430, TopMenuSequence::vfunction6) maps the chosen command to a mode:
| Command | Mode | Constructor |
|---|---|---|
| 0 | 1, buy | BuyListMenuSequence |
| 1 | 2, sell | SellMenuSequence |
| 2 | 3, hire | EmployMenuSequence |
| 3 | 4, auction | AuctionSequence |
| 4 | 5, craft | ItemBlendSequence |
| 5 | 6, upgrade | ItemUpgradeSequence |
| 6 | 7, organize | OrganizeSequence |
| 7 | exit | — |
| 8, 9 | 8, debug | DebugSequence |
- likely: the debug mode opens the hero's name entry (
MenuNameInput) for command 8 (0x1402fdf20) or the title screen's birthday entry,Title::BirthDayInputMenu, for command 9 (0x1404a1040), waits for it to close, then returns to the top menu. Source:DebugSequence::vfunction2(0x140408d40) andvfunction6(0x14040b290). - likely: commands 8 and 9 are never in the menu. The top menu's list is built by
0x140409850, which adds commands 0 to 7 (4 and 5 only when a game-state check allows them,0x1402cdd10and0x1402cdd30), through0x14030c1e0; those are its only 8 calls (exe.calls). Reaching the debug mode would take a patch to the executable that adds a command, which the project doesn't do (rule 5).
The @debug font
likely: not a debug feature. While the language's font package is read (0x1402ac010), every face of every outline font gets a slot and is registered under its family name; the first face of the first font whose debug_font field is set is also registered, in slot 0, as @debug font, with the value 52 (0x34) set on that text font (font.md). Nothing else refers to the name.
The debug heaps
likely: the memory system (0x140036e10) registers 28 named heaps, among them DEBUG (ID 0x14), DEBUG_MENU (0x15) and debug (0x1C), but no allocation in the code names any of the three: the allocator 0x140198c90 is called only with other heap IDs (MENU, 8, for the shop windows; SAVELOAD, 0x12, for the system settings), or with a heap chosen at run time. Source: the heap table in 0x140036e10 and its name strings at 0x1406AF5A8; every constant heap ID passed to 0x140198c90 in the decompiled code.
Leads
- Whether an in-game test confirms
config.ini's location and keys: running the game once and reading the file it writes would raise every fact in config.ini to confirmed. - What the framework setup does with the command line WinMain passes it (
0x1400362c0, then0x14015d120): no code was seen looking for an option in it, but it wasn't followed to the end. 0x140314600: the names list calls itQuitGame_Update(guess) and this doc reads it as quitting from the menu, while overview calls the same code "a system error or quit prompt". It shows messageDAT_140ece60c(0x815 or 0x81c, set by0x1403145c0from four callers) with rows 6001/6002, then on yes fades, savesconfig.iniand calls a framework method; decodingmenu_data#2and#6would give the texts. If it is the exit prompt, the game's word is "Exit" (COMMAND_LC_COMMAND_NAME_EXIT_TEXT).- Shop command 7 is called "exit" by the shop's
COMMAND_LC_COMMAND_NAME_SHOP11_NAME"Exit"; the mapping from command numbers tomenu_data#20rows wasn't checked. - Command 8: mode 0 of
0x1402fdf20takes its name from0x14031de20on the party object, unread, so that it edits the hero's name is likely, not confirmed. - The heap IDs passed at run time (the
0xFFFFFFFFand0xFFFFFFFEcalls to0x140198c90, and the other allocator0x140037650) weren't followed, so a debug heap could still be picked by one of them.