# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

HC Launcher is a Lazarus/FPC LCL Windows application that provides a GUI for configuring and launching the Harpoon Classic (HC) Game Engine (GE). It builds complex command-line argument strings from UI controls and executes the GE executable via shell.

Published at https://harpgamer.com/harpforum/files/file/445-hc-launcher/ with support topic at https://harpgamer.com/harpforum/topic/6098-file-hc-launcher/. Author: TonyE. First released September 2009, actively maintained since. Current version: 2025.024.

The launcher is described as "early attempts at a launcher program, currently mainly to test command line options" — it is a developer/power-user tool for exercising GE command-line flags, not a polished end-user launcher.

## Development Practices

- **Platform targeting**: Windows-first (HC is Win32) but always include `{$IFDEF MSWINDOWS}`/`{$IFDEF LINUX}` guards for future Linux ports
- **Documentation**: Use XMLDoc format (`/// <summary>`, `/// <param>`, `/// <returns>`) on new/modified code
- **Design**: Use interfaces and generics wherever it makes sense
- **Testing**: Automated tests at unit, integration, and (where feasible) UAT levels. Create and update tests before each check-in. Use FPCUnit for FPC code (see `../shared/pascal/tests/` for patterns).
- **Licensing**: Use libraries that allow a proprietary license (MIT, BSD, Apache 2.0, MPL OK). Discuss with the team before introducing GPL/LGPL dependencies.
- **Version control**: SVN (Git migration planned)

## Build

- **IDE**: Lazarus — open `Launcher.lpi`
- **Command-line (Release)**: `lazbuild Launcher.lpi`
- **Command-line (Debug)**: `lazbuild --build-mode=Debug Launcher.lpi`
- **Target**: Win32 (x86), outputs `Launcher.exe`
- **Tests**: `lazbuild tests/HCLauncherTests.lpi` then `tests/HCLauncherTests.exe --all --format=plain`
- **lazbuild location on this system**: `C:\Prog\lazarus\32\lazarus\lazbuild.exe`

> The project was ported from Delphi/RAD Studio to Lazarus/FPC in 2026. This is a one-way migration — Delphi is no longer a build target. Legacy Delphi files (`Launcher.dpr`, `Launcher.dproj`, `uMain1.dfm`, `FmxUtils.pas`) exist in SVN history for reference.

## Source Files

- `Launcher.lpr` — Program entry point (replaces `Launcher.dpr`)
- `Launcher.lpi` — Lazarus project file (replaces `Launcher.dproj`)
- `uMain1.pas` / `uMain1.lfm` — Main form (LCL, replaces TMS VCL `.dfm`): UI logic, settings I/O, coordinates command-line assembly
- `uHCLCmdLineBuilder.pas` — Extracted command-line builder (interface + class, zero UI dependency; independently testable)
- `uHCLProcessUtils.pas` — Process launch utility (clean-room replacement for `FmxUtils.pas`)
- `../shared/pascal/uCICVersionInformation.pas` — Extracts version strings from executables (shared across sibling projects, FPC-compatible)
- `tests/` — FPCUnit test suite (`test_CmdLineBuilder.pas`, `test_Settings.pas`)

## Architecture

Single-form application. All logic lives in `TfrmMain1` (uMain1.pas):

1. **Startup** (`FormCreate`): loads settings from `Launcher.ini`, detects GE directory (INI → registry fallback → app dir), reads GE version via `CICGetAppVersionStr`, shows/hides version-gated UI panels.
2. **UI → Command Line** (`BuildCommandLineGE`): every control change event calls this procedure, which calls `SnapshotInputs` to populate a `THCGECmdLineInputs` record, then delegates to `IHCLCmdLineBuilder.BuildCommandLine` to assemble the `-f`, `-l`, `-b`, `-c`, `-g`, etc. flags into a preview string. The builder is independently testable with no UI dependency.
3. **Launch** (`btnLaunchGEClick`): runs the GE executable with the assembled command line via `uHCLProcessUtils`.
4. **Shutdown** (`FormDestroy`): persists all control states back to `Launcher.ini`.

## Control Naming Conventions

- `ckb*` — TCheckBox (LCL)
- `cbx*` — TComboBox (LCL)
- `edt*` — TEdit / TSpinEdit (LCL)
- `pnl*` — TPanel (LCL)
- `btn*` — TButton (LCL)
- `lbl*` — TLabel (LCL)
- Suffix `GE` — Game Engine tab controls

## Settings Persistence

INI file (`Launcher.ini`) in the application directory, sections:
- `[Main]` — HC directory, GE executable name
- `[GECLO]` — Command-line option states (checkboxes, file paths, numeric values)
- `[GECLOGO]` — Game option combobox selections (side, nuke release, weather, etc.)
- `[GECLOLO]` — Logging option states and debug flags

## Version-Gated UI

UI elements are conditionally shown based on detected GE version:
- `>= 2014.001` — combobox side selector replaces checklist (single side only in newer GE)
- `>= 2022.0.1.5` — debug options panel visible
- `>= 2022.0.2.5` — message log and iterative save filename controls visible

Note: The `-S` iterative save option requires GE >= 2009.074.

## Release History (key milestones)

- **2009.042** — Initial release
- **2010.006** — Settings persistence via INI, improved HC directory detection, file filters
- **2010.011** — Fixed nuclear release options
- **2011.011** — Introduced `-S` iterative saves
- **2013.001** — Fixed spacing bug in `-S` command formatting
- **2014.001** — Limited player side selection to one for newer GE versions
- **2017.001** — Added unit log option
- **2022.016** — Added debugging and logging command-line options
- **2022.027** — Base filename for saves; MessageLog control
- **2025.024** — Added `-h` window hide option; Malacca Strait BS #32 in dropdown

## Third-Party Components

Standard LCL controls only. No third-party component dependencies.

## Key Dependencies Outside This Directory

- `../shared/pascal/uCICVersionInformation.pas` — shared unit, changes affect sibling projects (HC, CIC)

## Broader Ecosystem

HC Launcher is part of the Stratsims/StratsimsOSS family of Delphi/Pascal projects for the Harpoon Classic (HC) game. Key sibling projects and shared infrastructure:

### TGlobe5 Component (`C:\Prog\Projects\Stratsims\Delphi\Components\TGlobe5`)

Map rendering component used by HC utilities (e.g., BSBuilder at `C:\Prog\Projects\HC\Util\BSBuilder\`). Supports both Delphi and Lazarus/FPC:

- **Delphi**: Open the appropriate `Packages/TGlobe5_*.dpk` for your Delphi version (D5 through 13)
- **Lazarus**: `lazbuild --build-all Packages/TGlobe5_Lazarus.lpk` (or `--add-package` + `--build-ide=` to install)
- **Linux cross-compile**: `lazbuild --os=linux --cpu=x86_64 --build-all Packages/TGlobe5_Lazarus.lpk`
- **Conditional compilation**: `Source/Globe5.inc` auto-detects FPC vs Delphi, defines `TG_FPC`/`TG_DELPHI*`/`TG_MSWINDOWS`/`TG_LINUX`. All `.pas` files must `{$I Globe5.inc}` before the `unit` keyword (required for FPC `{$MODE DELPHI}`).
- **FPC/Delphi compat notes**: See `TGlobe5/Docs/fpc-lazarus.md` for unit-scoped names, `QueryInterface` signature differences (`constref` vs `const`), `RecreateWnd`, set-to-byte typecasts, pointer dereference patterns.

### General Shared Code (`../shared/pascal/`, prefix `uCIC*`)

MIT-licensed, SVN-managed Pascal units available to all Stratsims projects. Docs in `../shared/pascal/docs/`.

**Cross-compiler (FPC + Delphi) — OSM tile pipeline:**

| Unit | Purpose |
|------|---------|
| `uCICOSMTileCommon` | Constants/types: tile size (256px), lat limit, `TOSMTilePixels` BGRA array. `{$WEAKPACKAGEUNIT}` |
| `uCICOSMTileMath` | Pure functions: lat/lon ↔ Mercator ↔ tile indices, scale→zoom |
| `uCICOSMTileDecode` | PNG→BGRA decode with LCL/VCL platform fixes |
| `uCICOSMTileStore` | LRU tile cache (`TGOSMTileCache`), open-addressing hash, 262KB/slot |
| `uCICTileDownloader` | Async HTTP download queue (`TGTileFetcher`), atomic writes, blacklist, dedup |
| `uCICOSMTileManager` | Coordinator owning cache + downloader; used by TGlobe4/TGlobe5 |

**Cross-compiler — other:**

| Unit | Purpose |
|------|---------|
| `uCICOpenSSL3` | Runtime patch for FPC's openssl.pas to support OpenSSL 3.x on Linux. Must appear in `uses` before `opensslsockets`. No-op on Windows. |

**FPC/Lazarus only — MQTT:**

| Unit | Purpose |
|------|---------|
| `uCICMQTTProtocol` | Stateless MQTT 5.0/3.1.1 packet builder/parser, pure functions, no I/O |
| `uCICMQTTClient` | Full MQTT 5.0 client (`TCICMQTTClient`) with auto 3.1.1 fallback, TLS, keepalive, QoS 0/1, exponential backoff reconnect. Thread-safe. |
| `uCICG4Presenters` | Custom TGlobe4 point presenter with extended shapes and heading arrows |

**Delphi/VCL only:**

| Unit | Purpose |
|------|---------|
| `uCICVersionInformation` | Extract PE VERSIONINFO → version string (used by HC Launcher) |
| `uCICExecuteProgram` | `TCICExecuteProgram` — ShellExecuteEx wrapper with IsStillRunning polling |

**Tests** (`../shared/pascal/tests/`): FPCUnit suites for MQTT — `test_MQTTPackets` (pure function tests) and `test_MQTTIntegration` (broker tests, Docker localhost or test.mosquitto.org).

**Dependency flow:** `uCICOSMTileCommon` → `TileMath`/`TileDecode` → `TileStore` → `TileManager` ← `TileDownloader`. MQTT: `Protocol` → `Client`.

### TGlobe-Specific Shared Code

- `C:\Prog\Projects\Stratsims\SharedCode\` (prefix `uCICTGlobe*`): OSM tile renderer base class

### HC Project (`C:\Prog\Projects\HC\`)

The main Harpoon Classic game project. Uses TGlobe5 in utilities like BSBuilder for map visualization/editing. HC does not add custom defines to TGlobe5 — it uses the component as-is.

### HC Shared Units (`../HC/shared/pascal/`, prefix `uHC*`)

Shared Pascal units defining HC's data model, used by ExportDLLs and HC utilities:

| Unit | Purpose |
|------|---------|
| `uHCDefines.pas` | Constants — side IDs, marker types (MT_SHIP, MT_SUB, MT_AC...), event types (ATTACK_EVENT, LAUNCH_EVENT...), annex type flags (ANNEX_A1..ANNEX_Z2), sliver/distance/time constants |
| `uHCTypeDefs.pas` | Packed records — THCGroup (group linked list), THCUnit (platform), THCHarpoonEvent (game event), THCPathRec (waypoint), event-specific records (THCAttackEvent, THCSpeedAltEvent, etc.), scenario/victory data |
| `uHCAnnexDefs.pas` | Platform database records — disk and memory variants for each annex type (ships, subs, aircraft, sensors, weapons, torpedoes, missiles, guns, bombs, loadouts, magazines, land installations, satellites, sonobuoys, special weapons, mines) |
| `uHCAnnexHelpers.pas` | Type-safe annex access — IHCAnnexHelper/IHCAnnexMemHelper interfaces, lookup by annex ID, ref-counted lifetime |
| `uHCGCI.pas` | Geographic math — sliver↔degree conversion, great-circle position (HCNewPos), range/bearing (HCGetRangeBearing, HCFindRangeBearing), Vincenty direct, intercept angle, altitude band conversion |
| `uHCEventHelpers.pas` | Event utilities — type-to-string, event-to-JSON serialization, air patrol event lookup |
| `uHCDateTime.pas` | Date/time increment with month/year/leap-year boundary handling |
| `uHCUtils.pas` | Validation (HCIsValidGroup/Unit), marker type tests (HCMTIsAir/Ship/Sub...), display helpers, UUID↔pointer lookups, airborne endurance/bingo calculations |
| `uHCGlobals.pas` | Weather modifier tables (visibility, IR, ESM, radar) |

### HC ExportDLL Interface

Full documentation: `../HC/shared/docs/ExportDLL-Interface-Docs.md`

Win32 DLL plugin interface (cdecl) allowing external DLLs to receive game-state callbacks from the GE and SE. DLLs are loaded from `<HC_DIR>/ExportDLLs/` (common), `ExportDLLs/GE/` (GE-only), or `ExportDLLs/SE/` (SE-only). HC discovers exports by name via `GetProcAddress` — DLLs only export the callbacks they need.

**Target interface version: 45** (versions 4–33 disabled due to THCHarpoonEvent structure change at v34).

Key export categories (68 slots total):
- **Lifecycle**: `DLLInit`, `DLLFree`, `DLLVer`, `DLLDesc`
- **Scenario**: `ScenLoad`, `ScenLoaded`, `DBLoaded`, `SaveGame`, `SaveScen`
- **Game tick**: `GameTime`, `TimeFactor`
- **Group/Unit pointers**: `StartGroup`, `ActGroupPtr`, `ActUnitPtr`, `UnitTop`
- **Unit notifications**: `UnitLatLon`, `UnitShowing`, `UnitCourse`, `UnitSpeed`, `UnitAlt`, `UnitSink`, `UnitFree`, `UnitNew`
- **Events**: `HarpoonEventHead`, `HarpoonEventQueued`
- **Victory**: `VictoryConditionsList`, `VictoryConditionAchieved`, `VictoryConditionLevels`
- **Effects**: `Effect00`–`Effect22` (23 overridable game-logic hooks, GE only)

Best practice: use `QueueHarpoonEvent` callback for game-state changes; direct pointer writes only when no event equivalent exists.

**Coordinate system (slivers)**:
- 1 degree = 372,827 slivers (`SLIVER1`); 90 degrees = 33,554,432 (`SLIVER90`)
- Latitude: 0 = 90°N, SLIVER90 = equator, SLIVER180 = 90°S
- Longitude: 0 = Greenwich, positive = east, negative = west
- Distance: 6,211 distance units per nautical mile (`NM1`)
