wormz-a / docs /re-notes /frontend-main-navigation-loop.md
paavohtl's picture
Claude Opus 4.6 (1M context)
feat: test runner crash detection, ERRORLOG capture, and dialog suppression
a8f286b
|
Raw History Blame Contribute Delete
3.72 kB

Frontend__MainNavigationLoop (0x4E6700)

Ghidra address: 0x004E6700 Calling convention: __fastcall (ECX = CWinApp*) Size: ~3500 lines of decompiled C

This is the main entry point / event loop of WA.exe. It handles:

  • Command-line argument parsing
  • Crash handling (/handlecrash, /silentcrash, /disablecrashhandler)
  • Replay playback (/play, /playat, /getlog, /getmap, /getscheme, /getvideo, /getlogs)
  • Sanitize/repair modes (/sanitise, /repair)
  • Network hosting (/host, wa: protocol)
  • WormKit module loading (/wk, /nowk, /wkargs)
  • Frontend screen navigation loop (main menu, multiplayer, campaigns, etc.)
  • Renderer selection (DirectDraw, OpenGL, D3D9, etc.)

Crash Handler Mechanism

WA.exe's crash handler works by relaunching itself with /handlecrash:

  1. Original WA.exe crashes (C++ exception via __CxxThrowException)
  2. The crash handler (likely SEH-based) writes ERRORLOG.TXT and relaunches WA.exe with /handlecrash
  3. The new WA.exe instance processes /handlecrash in this function
  4. It checks Options__GetCrashReportURL() β€” if crash reporting is available, shows a dialog offering to send the report
  5. It checks DAT_0088e410 (WormKit modules loaded flag) β€” if set, shows the "Fatal error" dialog with Yes/No asking to disable WormKit modules

Key flags:

  • /silentcrash β†’ sets DAT_007a0f10+1 = 1 β€” suppresses the crash dialog
  • /disablecrashhandler β†’ sets DAT_007a0f10+0 = 1 β€” disables the crash handler entirely
  • /crash β†’ intentionally crashes via __CxxThrowException("Goodbye, cruel world!")

Crash dialog code (at ~0x4E6730):

// Check for crash report URL
iVar11 = Options__GetCrashReportURL();
if (iVar11 != 0) {
    // Show "send bug report?" dialog (MB_YESNO | MB_ICONQUESTION = 0x14)
    iVar10 = MessageBoxA(NULL,
        "Worms Armageddon has encountered an error...\nWould you like to send the bug report?",
        "Fatal error", 0x14);
    if (iVar10 == IDYES) FUN_005a6470(); // send crash report
}

// Check if WormKit modules were loaded
if (DAT_0088e410 == 0) {
    // No WormKit β€” just show error (MB_ICONERROR = 0x10)
    MessageBoxA(NULL, error_msg, "Fatal error", 0x10);
} else {
    // WormKit loaded β€” ask to disable (MB_YESNO | MB_ICONQUESTION = 0x14)
    FUN_00444d30(); // builds error message mentioning WormKit
    iVar11 = MessageBoxA(NULL, error_msg, "Fatal error", 0x14);
    if (iVar11 == IDYES) {
        DAT_0088e410 = 0;
        FUN_004c8c70(); // disable WormKit in registry
        MessageBoxA(NULL, "WormKit modules have been disabled.", "Worms Armageddon", 0x40);
    }
}

ERRORLOG.TXT

String at 0x00677FE0: "ERRORLOG.TXT" String at 0x00678484: "Can't open ERRORLOG.TXT" Written by the crash handler before relaunching (separate from this function).

/getlog Mode

The /getlog flag sets:

  • DAT_0088cd4c = 1 (headless replay mode)
  • local_31 = 1 (getlog flag)
  • Copies replay path to DAT_0088af58

Later in the function, /getlog mode opens the replay for writing the game log output.

WormKit Module Loading

Around line 1200 of decompilation:

  • Searches for wk*.dll files in the game directory
  • Loads each via LoadLibraryA
  • If loading fails, shows a MessageBox with the error
  • Flag at DAT_0088e410 tracks whether WormKit modules are loaded

Key Addresses

Address Purpose
0x007A0F10+0 /disablecrashhandler flag
0x007A0F10+1 /silentcrash flag
0x0088E410 WormKit modules loaded flag
0x0088AF58 Replay path buffer
0x0088CD4C Headless replay mode flag
0x0088E17D Current directory buffer
0x006B1FEC argc
0x006B1FF0 argv