• src/doors/syncconquer/CMa

    From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Wed Jul 8 01:33:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/f5578ea57f856e06816444ad
    Added Files:
    src/doors/syncconquer/CMakeLists.txt DEFERRED.md DESIGN.md build.sh deploy.sh src/doors/syncconquer/door/door.h door_core.c door_input.c door_input.h door_io.c door_io.h door_node.c door_node.h soundio_termgfx.cpp video_termgfx.cpp wwkeyboard_termgfx.cpp src/doors/syncconquer/plans/2026-07-06-m1-vendor-headless.md 2026-07-07-m2-phase1-door.md src/doors/syncconquer/plans/m2-proto/FACTS.md keystone.patch menu-headless.png video_capture_extra.cpp src/doors/syncconquer/resources src/doors/termgfx/CLAUDE.md xtrn/syncalert/.gitignore fetch-assets.sh install-xtrn.ini
    Modified Files:
    src/doors/syncconquer/PROVENANCE.md src/doors/syncconquer/vanilla/common/soundio_common.cpp video_null.cpp src/doors/syncconquer/vanilla/redalert/conquer.cpp globals.cpp init.cpp src/doors/termgfx/README.md audio.c geometry.c geometry.h
    Log Message:
    syncalert: Red Alert (Command & Conquer) door for Synchronet

    A terminal-playable Red Alert for Synchronet BBS -- the "SyncConquer"
    door family's first title (syncalert = Red Alert; syncdawn = Tiberian
    Dawn, deferred). Renders the running Vanilla Conquer engine to a sixel/JPEG-XL/PPM/text-block frame over the wire, maps terminal keyboard
    and mouse to the engine, and streams the game audio as SyncTERM audio
    APCs. Design and deferred-scope notes in DESIGN.md/DEFERRED.md; every vendored-engine edit is tracked in PROVENANCE.md.

    Build & assets: build.sh drives the headless CMake build (the top-level CMakeLists folds the termgfx-backed video/keyboard/audio shims into the VanillaRA target and defines NEW_VIDEO_BUILD); fetch-assets.sh pulls the
    RA95 freeware CD from archive.org (checksum-pinned) and extracts the MIX archives -- nothing EA-copyrighted is redistributed.

    Rendering: a shared termgfx pipeline (sixel/JXL/text encoders, the APC
    image transport, cap-probe, geometry). Tier auto-selection from the DA1/CTDA/Q;JXL probes with an F4 manual cycle (JXL > sixel > PPM > half/quadrant/sextant); DSR-ACK pacing with AIMD depth; whole-frame
    de-dupe; OS-pointer-as-RTS-cursor.

    Input: CSI/SS3/kitty/evdev keys and SGR mouse into the engine's
    WWKeyboard; real held modifiers on evdev/kitty, plus SGR-report modifier
    bits and SGR-Pixels (DEC 1016, auto-detected) so modifier-clicks and a pixel-granular cursor work where the terminal allows.

    Audio: engine-decoded AUD/ADPCM PCM shipped through termgfx's audio_mgr
    as upload-once SFX and a looping music track (Ogg/Opus, door-side disk
    cache, C;L client-cache skip).

    Synchronet integration: install-xtrn.ini, per-user UserPath via a
    per-home REDALERT.INI + argv[0] override (multi-node safe), DOOR32.SYS, pre-engine asset validation, SIGTERM drain/teardown, argv sanitization,
    node status / Ctrl-U / Ctrl-P, per-node logging, deploy.sh, and client-charset-aware block tiers (terminal.ini).

    Vendored-engine local patches (PROVENANCE.md): fail-loud Bootstrap
    mixfile caching, streamed-score whole-file decode (the ring path
    desynced the IMA-ADPCM decoder), bNoMovies, and the video/keyboard/audio
    seams. termgfx gained the audio_mgr music path, an Ogg/Opus resample, geom_fit_ex, and audio gain-staging / decode documentation
    (README/CLAUDE).

    Includes the live-test hardening pass: asset permissions, audio routing
    and the score-decode fix, F4 under evdev, mouse/cell-size and sixel-
    scroll geometry, screen-clear on tier change, per-tier bandwidth stats,
    and the charset/tier gating above.

    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Windows@VERT to Git commit to main/sbbs/m on Fri Jul 10 03:28:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/37de0f10359177f53b344452
    Modified Files:
    src/doors/syncconquer/CMakeLists.txt PROVENANCE.md src/doors/syncconquer/door/door_io.c src/doors/syncconquer/vanilla/common/utfargs.h src/doors/syncconquer/vanilla/redalert/startup.cpp
    Log Message:
    syncalert: fix four Windows-only defects that broke the door end to end

    The door's first-ever Windows build surfaced four bugs, each invisible on
    *nix. Two are in our code, two in the vendored engine's
    Windows-without-SDL path -- which upstream never compiles, because its
    Windows build always defines SDL_BUILD. All four date from f5578ea57f (foam-43-clip), where the door landed and was only ever built for *nix.

    1. setvbuf(stderr, NULL, _IOLBF, 0) fast-failed the process before it
    could log a thing: MSVC's CRT requires 2 <= size <= INT_MAX for
    _IOLBF/_IOFBF, so size 0 is an invalid parameter and, with no handler
    installed, __fastfail()s (c0000409, FAST_FAIL_INVALID_ARG, straight
    through ucrtbase!_invoke_watson). glibc ignores the size. Launching
    the door dumped the caller back to the BBS with a zero-byte node log.
    Use _IONBF on Windows: valid with size 0, and it flushes every write
    -- the crash-durability the comment promises, which _IOLBF would not
    have delivered there anyway (that CRT silently treats it as _IOFBF).
    syncduke fixed the identical fast-fail in 705a840fe9 (panel-11-mind);
    syncconquer had copied the pre-fix idiom.

    2. Winsock was only ever initialized from door_early_write(), which runs
    on failure paths (the missing-assets splash). syncduke gets away with
    the same placement because its constructor calls its splash
    unconditionally. Here, a healthy launch never called WSAStartup() and
    died on its first recv() with WSANOTINITIALISED (10093), which the
    pump reads as a client hangup -- again dropping the user back to the
    BBS. Hoist it into an idempotent door_wsa_ensure() called from the
    pre-main constructor. xpdev's sockwrap does not self-init Winsock.

    3. redalert/startup.cpp's `UtfArgs args(argc, argv)` replaces ArgV with a
    fresh CommandLineToArgvU(GetCommandLineW()) parse, discarding the argv
    door_sanitize_argv() rewrote in the constructor. So -NOMOVIES never
    reached the engine (the intro and mission FMV played, with music but
    no dialog audio -- vqaaudio_null.cpp stubs the VQA player's audio
    hooks), and the door's own -s<fd>/-home tokens reached
    Parse_Command_Line(), whose strstr() substring scan over every token
    can mis-set an engine option from an unrelated path. utfargs.h now
    honors SYNCALERT_KEEP_CRT_ARGV, defined for VanillaRA only.
    UNICODE/_UNICODE stay on, so common/'s UTF-8 path handling is
    untouched; only the argv re-derivation is skipped.

    4. "Exit Game" hung forever. main() sets ReadyToQuit = 1, posts
    WM_DESTROY to MainWindow, then spins on Keyboard->Check() until
    winstub.cpp's window procedure advances it. The headless door has no
    window (MainWindow is NULL) and no message pump, so nothing ever did:
    the process stayed alive burning CPU in door_io_pump()'s recv(), the
    terminal kept looping its music, and no frames were drawn.
    Emergency_Exit()'s ReadyToQuit == 3 spin would hang the same way. Both
    are now gated on !NEW_VIDEO_BUILD, which upstream sets for exactly the
    backends (SDL1/SDL2) that have no legacy WndProc -- a no-op for every
    upstream configuration.

    Also close the door's own console window on Windows. Synchronet spawns a
    native door with CREATE_NEW_CONSOLE, so a console pops up on the BBS
    machine each session; the door draws to the client's terminal and logs to data/syncalert/syncalert_n<node>.log, so it is pure noise. Detach only
    when we own the console exclusively (GetConsoleProcessList() == 1) -- a developer running the door from a shell shares that shell's console and
    must not lose stdout -- and only with a door socket. FreeConsole()
    invalidates all three std streams even when stderr already points at a
    file, so it runs BEFORE the node log is opened and re-points every stream
    at NUL (the engine still printf()s on some paths).

    Vendored patches 10 and 11 recorded in PROVENANCE.md.

    Verified on Windows (VS2022, Win32/Release) and by Rob against the live
    BBS: the door launches, plays with audio, shows no movies, and exits
    cleanly. The engine-argv fix confirmed under cdb (argv[1] "-s999" -> "-NOMOVIES"; bNoMovies 00 -> 01 across Parse_Command_Line); the console
    detach confirmed against a CREATE_NEW_CONSOLE / CREATE_NO_WINDOW
    launcher, with the node log intact in both. Not compiled by GCC/Clang.

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Tue Jul 28 17:19:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/da8a5a83c388aeff18edbc5a
    Modified Files:
    src/doors/syncconquer/CMakeLists.txt src/doors/syncmoo1/tools/fakterm.py xtrn/syncalert/.gitignore xtrn/syncdawn/.gitignore xtrn/syncdoom/get-binary.js xtrn/syncduke/get-binary.js xtrn/syncmoo1/getdata.js
    Log Message:
    doors: stop telling sysops to run deploy.sh, which no longer exists

    71f0836093 (none-19-ends, 2026-07-11) replaced each door's deploy.sh + deploy.bat pair with one deploy.js run under jsexec, but the text a sysop actually reads was left behind. Two installers abort with instructions to run
    a script that is not in the tree, and a third says the same in a note; the
    rest are comments naming it as the deploy step.

    The two that a sysop hits on a failed install now name the real command
    (jsexec src/doors/<door>/deploy.js) rather than a path they would go looking for and not find.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net