- Extract InputController from AppHost (key handling + ReloadGameEntries) - Centralize hardcoded values as code constants (non-configurable): FavoritesRootName, DefaultFileName, DefaultTitle, scroll fractions, AvailableLines helper, removed redundant ArgsTemplate fallback - Make color scheme configurable via 'Colors' section in launcher.config.json with Enum.TryParse + default fallback; MenuRenderer uses constructor injection - Clean up MenuRenderer: remove dead maxRow logic, unify cache sizing via EnsureCache - Document intentional error swallowing in FavoritesService.Save/UIErrorService - Update README + AGENTS.md with configurable color docs - Remove redundant ArgsTemplate fallback (config is single source of truth)
Marcer GameDVD Launcher
A fast, keyboard-driven console launcher for the Hatari emulator. Browse your Atari ST game archive, navigate folders, and launch ZIPs — all from the terminal. Supports overlay/patch mode for comparing and merging game directories.
Built for the Marcer GameDVD community on Facebook.
🎮 Features
- Overlay/Patch Mode: Recursively merges a main game directory with an optional patch directory. If a file or folder exists in both, the patch version takes precedence.
- Layer Labels: Each entry shows its source —
[BOTH],[ROOT], or[PTCH]— with a matching color scheme. - Favorites: Press
*on any ZIP to bookmark it. Bookmarked games appear in a virtualFavoritesfolder at the top of the root listing. - Robust Navigation: Cursor position is remembered per directory. Scrolling and page jumps adapt dynamically to any console height.
- Minimal Redraw: Only changed lines are redrawn — no flicker, no
Console.Clear, smooth even in huge directory trees.
Color Scheme
Colors are configurable via the Colors section in launcher.config.json (see Configuration). Defaults are shown below:
| Entry | Label | Default Color | Meaning |
|---|---|---|---|
| Folder in both layers | [BOTH] |
Yellow | Exists in main + patch |
| Patch-only folder | [PTCH] |
DarkYellow | Only in patch layer |
| Main-only folder | [ROOT] |
Gray | Only in main layer |
| ZIP in both layers | [BOTH] |
Green | Exists in main + patch |
| Main-only ZIP | [ROOT] |
DarkGreen | Only in main layer |
| Patch-only ZIP | [PTCH] |
Magenta | Only in patch layer |
| Selected entry | — | Black on DarkCyan | Highlighted entry |
| Virtual entry (Favorites) | — | White | Pseudo-folder |
🕹️ End Users
📥 Download
Download the ZIP for your platform from the Releases page:
| Platform | Archive |
|---|---|
| Windows | *-win-x64.zip |
| Linux | *-linux-x64.zip |
| macOS | *-osx-x64.zip |
💻 System Requirements
- .NET Runtime 10 or later (download)
- Hatari Emulator with a working configuration file
- Windows: native Hatari
- Linux / macOS: Hatari via Wine or native build
⚡ Quick Start
- Extract the release ZIP to any folder.
- Copy
launcher.config.example.jsontolauncher.config.json. - Edit
launcher.config.json— set your game directory and Hatari paths (see Configuration below). - Run the launcher:
- Windows: Double-click
MarcerGameDvdLauncher.exeor run from a terminal. - Linux:
chmod +x MarcerGameDvdLauncher && ./MarcerGameDvdLauncher - macOS:
chmod +x MarcerGameDvdLauncher && ./MarcerGameDvdLauncher
- Windows: Double-click
- Browse and launch games with your keyboard.
⚙️ Configuration
The launcher reads launcher.config.json from the same directory as the executable. A template is included in the release — copy it and adjust:
{
"RootDirectory": "C:\\Games\\Hatari\\ROMS",
"PatchDirectory": "C:\\Games\\Hatari\\PATCH",
"Hatari": {
"Executable": "C:\\Tools\\hatari\\hatari.exe",
"ConfigFile": "C:\\Tools\\hatari\\hatari-st.cfg",
"ArgsTemplate": "-c \"{cfg}\" --disk-a \"{zip}\""
},
"Colors": {
"FolderBoth": "Yellow",
"FolderPatchOnly": "DarkYellow",
"FolderRootOnly": "Gray",
"ZipBoth": "Green",
"ZipRootOnly": "DarkGreen",
"ZipPatchOnly": "Magenta",
"SelectedForeground": "Black",
"SelectedBackground": "DarkCyan",
"VirtualEntry": "White"
}
}
| Field | Required | Description |
|---|---|---|
RootDirectory |
✅ | Game root folder. Navigation never leaves this directory. |
PatchDirectory |
❌ | Optional overlay/patch directory merged at runtime. |
Hatari.Executable |
✅ | Path to the Hatari executable. Validated at startup. |
Hatari.ConfigFile |
✅ | Path to the Hatari configuration file. |
Hatari.ArgsTemplate |
✅ | Argument template. Must contain {zip}, optionally {cfg}. |
Colors |
❌ | Optional color overrides. See Color Scheme below. Missing or invalid values fall back to defaults. |
Notes:
- Relative paths are resolved relative to the executable's directory.
- Include quotes around
{cfg}and{zip}in the template if your paths contain spaces. - After editing
launcher.config.json, restart the application.
🎨 Color Scheme
Colors are fully configurable via the Colors section of launcher.config.json. Each field accepts a ConsoleColor name (case-insensitive). Omitting the entire Colors section — or any individual field — falls back to the built-in defaults:
| Field | Default | Applies to |
|---|---|---|
FolderBoth |
Yellow | Folders in both layers |
FolderPatchOnly |
DarkYellow | Folders in patch only |
FolderRootOnly |
Gray | Folders in root only |
ZipBoth |
Green | ZIPs in both layers |
ZipRootOnly |
DarkGreen | ZIPs in root only |
ZipPatchOnly |
Magenta | ZIPs in patch only |
SelectedForeground |
Black | Foreground for the highlighted entry |
SelectedBackground |
DarkCyan | Background for the highlighted entry |
VirtualEntry |
White | Virtual entries (e.g. the Favorites pseudo-folder) |
Notes:
- Relative paths are resolved relative to the executable's directory.
- Include quotes around
{cfg}and{zip}in the template if your paths contain spaces. - After editing
launcher.config.json, restart the application.
⌨️ Controls
| Key | Action |
|---|---|
↑ / ↓ |
Move selection |
Enter / → |
Open folder or launch ZIP |
Backspace / ← |
Go up one directory level |
PageUp / PageDown |
Jump one page |
* |
Toggle favorite on selected ZIP |
? |
Show help overlay |
ESC / Q |
Exit |
Navigation rules:
- Backspace in the root directory has no effect — you can never leave it.
- Empty directories are displayed correctly.
- When a ZIP or folder exists in both layers, the patch version is always launched/opened.
🔄 Keeping Your Patch Directory Updated
The community uses ftp-sync to keep the patch directory in sync with Marcer's FTP server. This downloads only changed or new files — fast and bandwidth-friendly.
Setup:
-
Clone and install ftp-sync:
git clone https://github.com/slippyex/ftp-sync.git cd ftp-sync npm install -
Create a
config.jsonwith your paths and the FTP credentials from the community:{ "ftpConfig": { "host": "<ftp-host>", "user": "<username>", "password": "<password>", "port": 2121 }, "localDir": "C:\\Games\\MarcersGameDVD\\", "remoteDir": "/GameDVD", "patchDir": "C:\\Games\\MarcersGameDVD-Patch\\" }💡 Ask in the Facebook group for the current FTP credentials.
-
Run the sync:
npm run sync config.jsonPress
sto start syncing. Pressqto exit when done. -
Point the launcher's
PatchDirectoryinlauncher.config.jsonto thepatchDirfrom your ftp-sync config.
🛠️ Developers
📋 Prerequisites
- .NET SDK 10
- Windows:
build.cmd/start.cmd - Linux / macOS:
build.sh/start.sh(runchmod +x scripts/*.shfirst)
🔨 Build & Run
Windows:
scripts\build.cmd
scripts\start.cmd
Linux / macOS:
scripts/build.sh
scripts/start.sh
⚠️ Do not use
dotnet buildordotnet rundirectly — always use the platform build script to ensure consistent output.
🧪 Demo Mode
demo.sh creates a complete test environment with fake ZIPs and two layers (root + patch), then launches the launcher:
scripts/demo.sh
This builds the project, generates a structured .demo/ directory with sample folders and ZIPs, writes a matching launcher.config.json, and starts the launcher. Useful for quickly testing overlay behavior and navigation without setting up real game files.
⚠️ Windows is not supported for
demo.sh. Usestart.cmdwith your own game files instead.
🚀 Release Process
Releases are automated via GitHub Actions (.github/workflows/release.yml).
To create a release:
You can use the /create-release command (via opencode) for a fully automated flow:
/create-release 1.0.0
This command validates the version, checks for uncommitted changes, generates a release summary from recent commits, creates a release commit with the summary as the commit message, tags it, and pushes — all in one step. The GitHub Action then builds platform ZIPs and creates the GitHub Release.
Manual release:
- Ensure
README.mdandAGENTS.mdare up to date. - Commit all changes.
- Tag and push:
git tag v1.2.3 git push origin v1.2.3 - The workflow builds platform ZIPs, generates release notes, and creates a GitHub Release.
🗺️ Roadmap
| Version | Feature |
|---|---|
| 2.0 | ZIP database & metadata extraction |
| 2.0 | Quicksearch / filter over ZIPs |
| 2.0 | History of recently launched games |
| 3.0 | Overlay hot-swap at runtime |
Not planned: Configurable keybindings, screenshot/cover display, sound/music integration, persistent UI settings.
📝 Notes
- Full technical requirements and build rules are in
AGENTS.md. - After any functional change, both
README.mdandAGENTS.mdmust be updated. - Every release must include release notes listing all changes and bugfixes.
License
Licensed under the MIT License - Copyright (c) 2026 Stefan Koelle (https://stefankoelle.de)