THE BROKEN SEAL - BUILDING THE DESKTOP EDITION (LINUX AND WINDOWS)

Build host and tested platform: Kubuntu/Ubuntu amd64, Free Pascal 3.2.2,
Lazarus 4.4 LCL, GTK 2 widget set. Native Wayland sessions need XWayland for
this GTK 2 build. Other Linux distributions can build from source.

Development packages (Debian/Ubuntu package names):
  fp-compiler fp-units-fcl fp-units-gtk2 lcl-units lcl-gtk2
  libgtk2.0-dev make python3 binutils dpkg-dev
GUI tests additionally use xvfb, xdotool and ImageMagick's import utility.

  make                  build bin/brokenseal
  make test             scenario tests and a 10,000-turn simulation
  make gui-test         exercise the real GUI in an isolated Xvfb display
  make deb              build, test, and produce dist/*.deb plus source archive

Override LAZARUS if the installed Lazarus root differs:
  make LAZARUS=/usr/lib/lazarus/4.4
The current Makefile targets x86_64-linux, GTK 2. Other architectures require
corresponding unit paths and a matching package architecture.

Windows (cross-compiled on Linux):
  tools/package.py      first: the source archive goes into the installer
  tools/build-win64.sh  build/win64/brokenseal.exe and dist/BrokenSeal-*-x64.msi
It needs Free Pascal 3.2.2 with the x86_64-win64 RTL and packages (WINRTL,
default ~/fpc-win64/lib/fpc/3.2.2/units/x86_64-win64), the Lazarus 4.4
sources (LAZARUS; the LCL is compiled from source for the win32 widget set),
x86_64-w64-mingw32-windres (mingw-w64) for the icon, manifest and version
resource, and wixl (msitools) for the MSI. The script will not overwrite an
existing MSI. The Windows build keeps saves in %APPDATA%\BrokenSeal and
takes its single-instance lock with an exclusive handle on desktop.lock.

On a system with GTK runtime libraries but missing development symlinks,
tools/prepare-link.py creates build-local symlinks to the installed GTK 2
shared libraries. It does not change system libraries or install packages.

Architecture
  bsdata/bsrules/bsai/bswar/bscivil/bsturn: deterministic simulation.
  bsstore: explicit little-endian world serialisation and validation.
  bshex: hex battles - the field, units, orders and the computer's play
    (no screen code); bswar hands it a battle through BattleHook.
  bshexscr/bsbattleui: the battle screen and its window.
  bsdesktopgame: campaign saves, turn commitments, reports and orders.
  bsmapgen.inc (in bsdata): the realm map generator; a fresh 32-province map
    for each campaign from the campaign seed.
  bsretro/bsfont.inc: the 640x400 16-colour screen and its bitmap font.
  bsretroui/bsmainscr/bsface: setup screens, the main screen, portraits.
  bsretroctl: the retro window controls used by every game window.
  bsmusic: the background music, made note by note at start-up in a
    background thread and played in a loop (Linux: pw-play, paplay or
    aplay; Windows: winmm PlaySound). BROKENSEAL_MUTE=1 silences it (the
    GUI test sets it).
  bsmap: the old vector map, kept only to hold the selected province.
  bsdesktopui: Lazarus forms, management screens, keyboard controls.

A .bseal is a desktop container, not a BBS WORLD.DAT file. The inner world
format is 7: format 2 (0.3.0) kept gold per province, war
chests and gold shipments; format 3 added five computer levels; format 4
(0.4.0) stores the province count and the realm map (run-length coded by
row); format 5 (0.4.4) adds officers' stamina and each province's town
(recruits, market, winter roads); format 6 (0.4.5) adds the outlaw camp's
training; format 7 marks provinces taken but not yet given a governor.
Worlds of format 1 to 6 still load; 1 to 3 on the old 24-province grid. Header BSDESK02
(8 bytes), payload FNV-1a checksum (u32 little-endian), then payload:
  world length (u32), complete BSEL world bytes (with their own CRC-32),
  active human faction (u32), then for factions 1..6: report string, mail
  string; finally the chronicle string. Strings are u32 byte length + UTF-8
  bytes. The inner world still uses bounded short strings and ASCII names.
See ENGINE-FORMAT.txt for the inner world fields. Old unused BBS metadata
fields are retained in that inner format for serialisation compatibility,
but have no desktop scheduling or identity role.

Save/load checks outer checksum, inner CRC and world bounds before replacing
the current playable state. Saves use a completed .new file and atomic rename,
retain a .bak, and fsync (FlushFileBuffers on Windows) the new main file.
On Windows the replacing rename is MoveFileEx with MOVEFILE_REPLACE_EXISTING. This is application crash recovery;
filesystem or device failure still requires external backups.

The app takes an advisory process lock in its per-user data directory. Use a
separate BROKENSEAL_DATA_DIR for independent test instances.
