This document explains what the launcher does end to end, how its modules are organized, and how it talks to a Garlemald server. Read it before diving into any single module.
To build and run the launcher, see dev-environment.md.
Scope. Garlemald Client is a launcher for FINAL FANTASY XIV v1.23b — the last patch of the original 2010 1.0 release, not A Realm Reborn. It is a single Rust crate. Its job ends the moment a patched game is running with a valid session; from there the game talks to the server. For the server side, see the Garlemald-Server architecture doc.
From cold start to in-game, the launcher runs this pipeline:
┌─────────────────────────── garlemald-client ───────────────────────────┐
│ │
┌──────────┐ ┌────────▼─────────┐ ┌──────────────┐ ┌───────────────┐ ┌──────────────────┐ │
│ detect │──▶│ check version │──▶│ patch │──▶│ WebView login │──▶│ launch the game │ │
│ install │ │ (game.ver vs │ │ download + │ │ ffxiv://login │ │ (+ PE patch / │ │
│ │ │ 2012.09.19.0001)│ │ ZiPatch apply│ │ _success? │ │ Wine on mac/ │ │
└──────────┘ └──────────────────┘ │ (CRC32) │ │ sessionId=…) │ │ linux) │ │
▲ └──────────────┘ └───────┬───────┘ └────────┬─────────┘ │
│ ▲ │ │ │
│ S3 patch host server loginUrl │ │
└──────────────────┼─────────────────────┼───────────────────┼───────────┘
│ │ │
ffxivpatches.s3.amazonaws.com web-server :54993 ffxivgame.exe
│
game connects to the server ▼
lobby :54994 · world :54992 · map :1989
- Detect an installed FFXIV 1.x client (per-OS; see Platform).
- Check version — read the install's
game.verand compare to the targetFFXIV_GAME_VERSION(2012.09.19.0001). If it's behind, patch. - Patch — download the needed ZiPatch files and apply them, taking the install
from
FFXIV_BOOT_VERSION(2010.09.18.0000) up to2012.09.19.0001, each file CRC32-verified. - Log in — open the selected server's
loginUrlin a WebView and capture thesessionIdfrom theffxiv://login_successredirect. - Launch — build the (Blowfish-encrypted) game arguments, apply the launch-time
PE patches (lobby hostname, etc.), and start
ffxivgame.exe— natively on Windows, or through the managed Wine runtime on macOS/Linux.
After step 5 the launcher is done: the game opens its own TCP connections to
the server's lobby (54994), world (54992), and map (1989) services.
Version constants live in
src/version.rs(FFXIV_BOOT_VERSION,FFXIV_GAME_VERSION).2012.09.19.0001is the build the wire protocol targets — patching the install to it is what makes the game compatible with the server.
Single crate, src/:
| Module | Responsibility |
|---|---|
app/ |
The eframe/egui GUI — server picker, game-location selector, patch progress, launch button |
servers/ |
The server registry: ServerDefinition { name, address, login_url } |
patcher/ |
Patch download (ureq) + the apply worker thread + the patch manifest |
patch_format/ |
The ZiPatch format itself — decompress + apply file deltas |
login/ |
The WebView login subprocess and the ffxiv://login_success handshake |
crypto/ |
Blowfish encryption of the game's launch arguments |
launcher/ |
Assembling the launch request, the PE patches, and starting the game |
platform/ |
Per-OS support: native Win32 on Windows, managed Wine on macOS/Linux |
config/ |
Config/data paths (directories) and preferences.toml |
version.rs |
The launcher version and the FFXIV boot/game version constants |
main.rs / lib.rs |
Entry point + the run() that wires the GUI; the --login-webview subcommand |
eframe/egui. app/launcher_window.rs hosts LauncherApp, a small state machine
(a main screen and a patcher screen). patcher_window.rs renders the
download/apply progress (it polls the patcher's shared atomics each frame, so the UI
never blocks). settings_window.rs is the game-location picker; developer_window.rs
is the developer settings modal (verbose Wine logging, winsock tracing).
A ServerDefinition is { name, address, login_url }. The selectable server list is
baked into the binary from src/servers/default_servers.toml (include_str! as
DEFAULT_SERVERS_TOML, parsed by ServerDefinitions::load_default — e.g. a
Localhost entry pointing at 127.0.0.1's web login). The selected server's
name/address is persisted in preferences.toml. (A servers.xml config path is
defined in config/paths.rs but is not yet wired up.)
patcher/manifest.rs holds the hard-coded patch manifest (each entry: path, byte
size, CRC32) and PATCH_URL_BASE — patches are fetched over HTTP from a fixed S3
host, http://ffxivpatches.s3.amazonaws.com/ (the launcher can also apply patches
from a user-selected local directory). patcher/worker.rs runs the download+apply on
a background thread, reporting progress through lock-free shared state. The apply
step calls into patch_format/ (the ZiPatch parser: zlib-decompress, apply file
deltas), and each file is CRC32-checked (crc32fast) before it counts as applied.
When everything applies, the launcher writes the install's game.ver/version files.
See Client ↔ server communication below.
launcher/game_launch.rs defines the GameLaunchRequest { game_dir, lobby_host, session_id, wine_debug_override, enable_winsock_proxy } and launch_game(), which
delegates to the current platform backend. launcher/pe_patch.rs defines the
launch-time PE patches applied to ffxivgame.exe — most importantly the lobby
hostname (so one client binary can target any server) and an encryption-time
constant; plus several stability patches used under Wine.
A Platform trait (platform/mod.rs) with detect_game_install,
is_valid_game_location, and launch_game; platform::current() returns the right
backend. See Platform support.
The launcher touches the server in three narrow places. Everything else is the game's job after launch.
The login flow runs in a separate subprocess so the WebView's native event loop stays isolated from the GUI:
- The launcher re-execs itself with
--login-webview <loginUrl>(main.rs→login::run_webview). That child opens the server'sloginUrlin a native WebView (taoevent loop +wry). - The child watches navigations for the custom scheme
ffxiv://login_success?sessionId=<56-hex-chars>(src/login/webview.rs). On a match it prints a sentinel line to stdout:SESSION_ID=<id>on success (SESSION_PREFIX),LOGIN_CANCELLEDif the user closes the window (CANCEL_SENTINEL),LOGIN_ERROR=<msg>on failure (ERROR_PREFIX).
- The parent reads the child's stdout line by line on a worker thread and turns the
sentinel into a
LoginOutcomedelivered to the GUI over anmpscchannel. The session id is a fixed 56 characters (SESSION_ID_LEN).
That sessionId is minted by the server's web auth (web-server), exactly as the
server's lobby will later expect — see the server architecture doc.
ZiPatch files are downloaded over HTTP via ureq from the fixed patch host
(PATCH_URL_BASE). This is independent of which game server you pick.
The launcher never opens a game socket itself. Instead it bakes the destination and session into the game it starts:
crypto/(build_launch_arguments) builds the game's launch argument string (language/region/server-UTC/session id), derives a Blowfish key from a tick count, encrypts the 8-byte blocks, base64-url-encodes the result, and wraps it assqex0002<base64>!////— the exact formatffxivgame.exedecodes.launcher/pe_patch.rswrites the selected server's lobby hostname into the PE.- The game then connects to that server's lobby (54994) → world (54992) → map
(1989) with the handed-off session. The hard client-version gate is satisfied
because the install was patched to
2012.09.19.0001.
platform/ abstracts the one genuinely OS-specific step — actually starting the
game:
| Windows | macOS | Linux | |
|---|---|---|---|
| Game runtime | Native Win32 (no Wine) | Bundled, auto-downloaded Wine runtime | System Wine (Wine 7+) |
| Wine prefix | n/a | managed under data_dir/prefix/ |
managed under data_dir/prefix/ |
| Detect install | Registry (HKLM uninstall key) | ffxivboot.exe in the managed install |
ffxivboot.exe in the install |
| Patch method | In-memory (CreateProcess suspended + WriteProcessMemory) |
On-disk copy ffxivgame.patched.exe |
On-disk copy |
| Notes | Builds 32-bit (i686) to read the game's thread context; caps CPU affinity (1.x crashes with ≥16 logical CPUs) |
Apple Silicon needs Rosetta 2 for the x86 Wine engine | needs the GTK3/WebKit2GTK stack |
The shared Wine plumbing lives in platform/wine.rs (WineRuntime, prefix init via
wineboot --init, the on-disk patch set, and the game launch that redirects Wine's
output to data_dir/logs/wine.log). macOS/Linux additionally support an optional
winsock-tracing proxy (ws2_32-proxy/, a separately-built DLL) gated behind a
developer preference — handy for inspecting the game's network traffic.
dev-environment.md— build/run, logging, launcher state, and running against a local Garlemald-Server.agents.md— running an AI coding agent against an issue.../CONTRIBUTING.md— pick up an issue and open a PR.- Garlemald-Server
docs/architecture.md— the server side of the handoff.