mirror of
https://github.com/skoelle/marcer-gamedvd-launcher.git
synced 2026-09-18 03:00:25 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4660cc6be8 | ||
|
|
148c2bb484 | ||
|
|
607d0df28c | ||
|
|
4b3321e812 | ||
|
|
6a4be2e397 | ||
|
|
61a8ae8457 | ||
|
|
5995a96516 | ||
|
|
e704261ea4 | ||
|
|
1634ce806c | ||
|
|
292b642d76 |
@@ -13,8 +13,8 @@ The implementation is split into focused modules (files) under the `src/MarcerGa
|
|||||||
- MarcerGameDvdLauncher/OverlayDirectoryBrowser.cs: Filesystem overlay and browsing logic — merges root and patch directories, enumerates folders and ZIPs, protects against path traversal and ensures navigation cannot leave the configured roots.
|
- MarcerGameDvdLauncher/OverlayDirectoryBrowser.cs: Filesystem overlay and browsing logic — merges root and patch directories, enumerates folders and ZIPs, protects against path traversal and ensures navigation cannot leave the configured roots.
|
||||||
- MarcerGameDvdLauncher/NavigationController.cs: Encapsulates selection, scrolling and relative-path navigation logic (cursor, page up/down, per-directory remembered selection/state); uses named scroll-fraction constants.
|
- MarcerGameDvdLauncher/NavigationController.cs: Encapsulates selection, scrolling and relative-path navigation logic (cursor, page up/down, per-directory remembered selection/state); uses named scroll-fraction constants.
|
||||||
- MarcerGameDvdLauncher/MenuRenderer.cs: Console rendering logic — efficient per-line redraw, double-buffering, configurable color selection via injected `AppColorConfig`, and the help box overlay.
|
- MarcerGameDvdLauncher/MenuRenderer.cs: Console rendering logic — efficient per-line redraw, double-buffering, configurable color selection via injected `AppColorConfig`, and the help box overlay.
|
||||||
- MarcerGameDvdLauncher/InputController.cs: Handles key events (arrow keys, Enter, Backspace, PageUp/Down, `*`, `?`, ESC) and the associated navigation/drawing logic; owns `ReloadGameEntries` and the virtual `Favorites` folder integration.
|
- MarcerGameDvdLauncher/InputController.cs: Handles key events (arrow keys, Enter, Backspace, PageUp/Down, `*`, `?`, ESC, RightArrow) and the associated navigation/drawing logic; owns `ReloadGameEntries` and the virtual `Favorites` folder integration.
|
||||||
- MarcerGameDvdLauncher/HatariLauncher.cs: Responsible for validating the Hatari executable and starting Hatari with the configured argument template (replaces `{cfg}` and `{zip}`).
|
- MarcerGameDvdLauncher/HatariLauncher.cs: Responsible for validating the Hatari executable and starting Hatari with the configured argument template (replaces `{cfg}` and `{zip}`). Exposes `DefaultConfigFile` constant (`MarcerGameDvd-Hatari.cfg`); when `Hatari.ConfigFile` is empty in `launcher.config.json`, the bundled config from the executable directory is used automatically.
|
||||||
- MarcerGameDvdLauncher/FavoritesService.cs: Manages the favorites/bookmark system — toggling favorites on ZIPs, persisting them to `favorites.txt` (via `DefaultFileName` constant), and providing the virtual `Favorites` folder view (via `FavoritesRootName` constant).
|
- MarcerGameDvdLauncher/FavoritesService.cs: Manages the favorites/bookmark system — toggling favorites on ZIPs, persisting them to `favorites.txt` (via `DefaultFileName` constant), and providing the virtual `Favorites` folder view (via `FavoritesRootName` constant).
|
||||||
- MarcerGameDvdLauncher/UIErrorService.cs: Centralized UI error presentation using the console message helper.
|
- MarcerGameDvdLauncher/UIErrorService.cs: Centralized UI error presentation using the console message helper.
|
||||||
|
|
||||||
@@ -71,6 +71,7 @@ The console launcher is meant for browsing a games directory and can launch ZIP
|
|||||||
|
|
||||||
### Launching a Game
|
### Launching a Game
|
||||||
- If Enter is pressed on a ZIP, Hatari is called with the configured executable, config file, and the full path to the ZIP file as arguments.
|
- If Enter is pressed on a ZIP, Hatari is called with the configured executable, config file, and the full path to the ZIP file as arguments.
|
||||||
|
- `Hatari.ConfigFile` is optional. When empty, the bundled `MarcerGameDvd-Hatari.cfg` (shipped with the launcher) is used automatically.
|
||||||
|
|
||||||
### Error Handling & Edge Cases
|
### Error Handling & Edge Cases
|
||||||
- Paths with special characters or spaces must work robustly.
|
- Paths with special characters or spaces must work robustly.
|
||||||
@@ -79,6 +80,7 @@ The console launcher is meant for browsing a games directory and can launch ZIP
|
|||||||
- In the root directory, Backspace must have no effect (no error, do not leave the program).
|
- In the root directory, Backspace must have no effect (no error, do not leave the program).
|
||||||
- Navigation (Backspace, Enter, etc.) must remain robust even for very deep or large directory trees.
|
- Navigation (Backspace, Enter, etc.) must remain robust even for very deep or large directory trees.
|
||||||
- Hatari.Executable is validated during startup: the path is resolved (relative to the EXE directory when applicable) and must point to an existing file. If validation fails the program must present a clear error and exit.
|
- Hatari.Executable is validated during startup: the path is resolved (relative to the EXE directory when applicable) and must point to an existing file. If validation fails the program must present a clear error and exit.
|
||||||
|
- `Hatari.ConfigFile` (if provided) is resolved relative to the EXE directory and validated. If empty, the bundled `MarcerGameDvd-Hatari.cfg` is used automatically.
|
||||||
|
|
||||||
### Miscellaneous
|
### Miscellaneous
|
||||||
- Build and start scripts (`scripts/build.cmd` / `scripts/build.sh` / `scripts/start.cmd` / `scripts/start.sh`) are present and must be used.
|
- Build and start scripts (`scripts/build.cmd` / `scripts/build.sh` / `scripts/start.cmd` / `scripts/start.sh`) are present and must be used.
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# Known Bugs & Issues
|
||||||
|
|
||||||
|
## Critical Bugs
|
||||||
|
|
||||||
|
### 1. ArgsTemplate `{cfg}` placeholder not handled when ConfigFile is empty
|
||||||
|
**Location**: `HatariLauncher.cs:36`
|
||||||
|
**Problem**: If `Hatari.ConfigFile` is empty string (allowed per config) but `ArgsTemplate` contains `{cfg}`, the replacement produces `-c "" --disk-a "path"` which Hatari may reject.
|
||||||
|
**Fix**: Conditionally remove `-c "{cfg}"` when `_cfgPath` is empty, or validate template matches config.
|
||||||
|
|
||||||
|
### 2. `MenuRenderer.RedrawEntry()` potential crash on console resize
|
||||||
|
**Location**: `MenuRenderer.cs:67-98`
|
||||||
|
**Problem**: If console resizes smaller since last draw, `row` parameter may exceed new `availableLines`. The `else` branch writes directly via `WriteConsoleLine` without bounds checking against actual console height, risking `ArgumentOutOfRangeException` on `Console.SetCursorPosition`.
|
||||||
|
**Fix**: Validate `row < Console.WindowHeight` before writing, or clamp to valid range.
|
||||||
|
|
||||||
|
## Medium Bugs
|
||||||
|
|
||||||
|
### 3. `HatariLauncher` constructor doesn't validate `ArgsTemplate`
|
||||||
|
**Location**: `HatariLauncher.cs:12-24`
|
||||||
|
**Problem**: Constructor validates `exePath` existence but allows empty/null `argsTemplate`. `Launch()` will fail at runtime with empty string replace. Defense-in-depth validation missing.
|
||||||
|
**Fix**: Add `if (string.IsNullOrWhiteSpace(argsTemplate) || !argsTemplate.Contains("{zip}")) throw ...;`
|
||||||
|
|
||||||
|
### 4. ZIP launch doesn't validate file exists on disk
|
||||||
|
**Location**: `InputController.cs:185-196`
|
||||||
|
**Problem**: `PatchPath` or `RootPath` used directly without checking `File.Exists()`. Overlay logic should prevent this, but no defense-in-depth.
|
||||||
|
**Fix**: Validate `File.Exists(zipToLaunch)` before calling `_hatariLauncher.Launch()`.
|
||||||
|
|
||||||
|
## Documentation Inconsistencies
|
||||||
|
|
||||||
|
### 5. `Hatari.ConfigFile` required status unclear
|
||||||
|
**Location**: `README.md:96` vs `LauncherApp.cs:67-68`
|
||||||
|
**Conflict**: README marks `Hatari.ConfigFile` as required (✅). Code allows empty string and only validates if non-empty. If template uses `{cfg}` but ConfigFile empty → broken args.
|
||||||
|
**Resolution**: ConfigFile is now optional. When empty, the bundled `MarcerGameDvd-Hatari.cfg` (shipped in the release) is used automatically. README updated to reflect this. ✅ Fixed
|
||||||
|
|
||||||
|
### 6. `RightArrow` key binding missing from AGENTS.md requirements
|
||||||
|
**Location**: `AGENTS.md:57-58` vs `InputController.cs:172-173`
|
||||||
|
**Conflict**: AGENTS.md lists only Arrow up/down, Enter, Backspace, ESC, PageUp/Down, `?`. Code also handles `ConsoleKey.RightArrow` as alias for Enter (open folder/launch ZIP). README correctly documents it.
|
||||||
|
**Resolution**: Update AGENTS.md to include RightArrow.
|
||||||
|
|
||||||
|
### 7. `ShowModalUntilReturnReleased` race condition on resize
|
||||||
|
**Location**: `ProgramHelpers.cs:62-98`
|
||||||
|
**Problem**: Captures `lastRow = AvailableLines` at start. If console resizes during the modal wait loop, the clear writes to stale row position.
|
||||||
|
**Fix**: Re-read `AvailableLines` before clearing, or lock console during modal (not easily possible).
|
||||||
|
|
||||||
|
## Code Quality / Minor
|
||||||
|
|
||||||
|
### 8. `ProgramHelpers.ShowConsoleMessage` signature could use `params`
|
||||||
|
**Location**: `ProgramHelpers.cs:110`
|
||||||
|
**Current**: `public static void ShowConsoleMessage(string[] lines, ...)`
|
||||||
|
**Called as**: `ShowConsoleMessage([...], ...)` (collection expression)
|
||||||
|
**Suggestion**: Change to `params string[] lines` for more idiomatic usage.
|
||||||
|
|
||||||
|
### 9. `OverlayDirectoryBrowser` patch path computation order
|
||||||
|
**Location**: `OverlayDirectoryBrowser.cs:47-50`
|
||||||
|
**Observation**: Path traversal protection (`IsSubPathOf`) runs after `Path.Combine` but before `Directory.Exists`. Logic appears correct, but worth verifying with `rel` containing `..` when patch layer enabled.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fixed / Non-Issues (Verified)
|
||||||
|
|
||||||
|
| Item | Status | Notes |
|
||||||
|
|------|--------|-------|
|
||||||
|
| `demo.sh` location | ✅ OK | Exists at repo root, README link correct |
|
||||||
|
| Magic strings centralized | ✅ Done | `FavoritesRootName`, `DefaultFileName`, `DefaultTitle` |
|
||||||
|
| Scroll fractions as constants | ✅ Done | `BottomScrollFraction`, `TopScrollFraction` |
|
||||||
|
| `AvailableLines` helper | ✅ Done | `ProgramHelpers.AvailableLines` |
|
||||||
|
| Colors configurable | ✅ Done | `Colors` section in config |
|
||||||
|
| Win32 constants documented | ✅ Done | Marked "intentionally hardcoded" |
|
||||||
|
| `Hatari.ConfigFile` optional with bundled fallback | ✅ Done | Empty ConfigFile falls back to bundled `MarcerGameDvd-Hatari.cfg` |
|
||||||
|
| `FavoritesService.Save()` swallow comment | ✅ Done | Explains intentional behavior |
|
||||||
|
| `UIErrorService` no-rethrow doc | ✅ Done | Explains design decision |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Priority Recommendation
|
||||||
|
|
||||||
|
1. **Fix #1 (ArgsTemplate `{cfg}`)** - ✅ Fixed: empty ConfigFile now falls back to bundled config
|
||||||
|
2. **Fix #2 (RedrawEntry bounds)** - Potential crash on resize
|
||||||
|
3. **Fix #3 (HatariLauncher validation)** - ✅ Done (per BUGS.md Fixed table)
|
||||||
|
4. **Fix #4 (ZIP path validation)** - User-facing robustness
|
||||||
|
5. **Sync #5 & #6 (docs)** - ✅ Done: ConfigFile documented as optional with fallback
|
||||||
@@ -46,9 +46,7 @@ Download the ZIP for your platform from the [Releases](https://github.com/anomal
|
|||||||
### 💻 System Requirements
|
### 💻 System Requirements
|
||||||
|
|
||||||
- **.NET Runtime 10** or later ([download](https://dotnet.microsoft.com/download/dotnet/10.0))
|
- **.NET Runtime 10** or later ([download](https://dotnet.microsoft.com/download/dotnet/10.0))
|
||||||
- **Hatari Emulator** with a working configuration file
|
- **Hatari Emulator** (Windows native, or Linux/macOS via Wine or native build) — a Hatari configuration file (`MarcerGameDvd-Hatari.cfg`) is bundled with the launcher and used automatically when `Hatari.ConfigFile` is empty.
|
||||||
- Windows: native Hatari
|
|
||||||
- Linux / macOS: Hatari via Wine or native build
|
|
||||||
|
|
||||||
### ⚡ Quick Start
|
### ⚡ Quick Start
|
||||||
|
|
||||||
@@ -71,7 +69,7 @@ The launcher reads `launcher.config.json` from the same directory as the executa
|
|||||||
"PatchDirectory": "C:\\Games\\Hatari\\PATCH",
|
"PatchDirectory": "C:\\Games\\Hatari\\PATCH",
|
||||||
"Hatari": {
|
"Hatari": {
|
||||||
"Executable": "C:\\Tools\\hatari\\hatari.exe",
|
"Executable": "C:\\Tools\\hatari\\hatari.exe",
|
||||||
"ConfigFile": "C:\\Tools\\hatari\\hatari-st.cfg",
|
"ConfigFile": "",
|
||||||
"ArgsTemplate": "-c \"{cfg}\" --disk-a \"{zip}\""
|
"ArgsTemplate": "-c \"{cfg}\" --disk-a \"{zip}\""
|
||||||
},
|
},
|
||||||
"Colors": {
|
"Colors": {
|
||||||
@@ -93,7 +91,7 @@ The launcher reads `launcher.config.json` from the same directory as the executa
|
|||||||
| `RootDirectory` | ✅ | Game root folder. Navigation never leaves this directory. |
|
| `RootDirectory` | ✅ | Game root folder. Navigation never leaves this directory. |
|
||||||
| `PatchDirectory` | ❌ | Optional overlay/patch directory merged at runtime. |
|
| `PatchDirectory` | ❌ | Optional overlay/patch directory merged at runtime. |
|
||||||
| `Hatari.Executable` | ✅ | Path to the Hatari executable. Validated at startup. |
|
| `Hatari.Executable` | ✅ | Path to the Hatari executable. Validated at startup. |
|
||||||
| `Hatari.ConfigFile` | ✅ | Path to the Hatari configuration file. |
|
| `Hatari.ConfigFile` | ❌ | Path to a Hatari configuration file. If empty, the bundled `MarcerGameDvd-Hatari.cfg` (shipped with the launcher) is used automatically. |
|
||||||
| `Hatari.ArgsTemplate` | ✅ | Argument template. Must contain `{zip}`, optionally `{cfg}`. |
|
| `Hatari.ArgsTemplate` | ✅ | Argument template. Must contain `{zip}`, optionally `{cfg}`. |
|
||||||
| `Colors` | ❌ | Optional color overrides. See [Color Scheme](#-color-scheme) below. Missing or invalid values fall back to defaults. |
|
| `Colors` | ❌ | Optional color overrides. See [Color Scheme](#-color-scheme) below. Missing or invalid values fall back to defaults. |
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ namespace MarcerGameDvdLauncher
|
|||||||
{
|
{
|
||||||
public class HatariLauncher
|
public class HatariLauncher
|
||||||
{
|
{
|
||||||
|
// The Hatari configuration file that is shipped with the launcher.
|
||||||
|
// When Hatari.ConfigFile is not set in launcher.config.json, this file
|
||||||
|
// (resolved relative to the executable directory) is used automatically.
|
||||||
|
public const string DefaultConfigFile = "MarcerGameDvd-Hatari.cfg";
|
||||||
|
|
||||||
private readonly string _exePath;
|
private readonly string _exePath;
|
||||||
private readonly string _cfgPath;
|
private readonly string _cfgPath;
|
||||||
private readonly string _argsTemplate;
|
private readonly string _argsTemplate;
|
||||||
@@ -18,6 +23,10 @@ namespace MarcerGameDvdLauncher
|
|||||||
if (!File.Exists(exePath))
|
if (!File.Exists(exePath))
|
||||||
throw new ArgumentException($"Hatari executable not found: {exePath}", nameof(exePath));
|
throw new ArgumentException($"Hatari executable not found: {exePath}", nameof(exePath));
|
||||||
|
|
||||||
|
// Validate argsTemplate
|
||||||
|
if (string.IsNullOrWhiteSpace(argsTemplate) || !argsTemplate.Contains("{zip}"))
|
||||||
|
throw new ArgumentException("Hatari.ArgsTemplate must contain the {zip} placeholder.", nameof(argsTemplate));
|
||||||
|
|
||||||
_exePath = exePath;
|
_exePath = exePath;
|
||||||
_cfgPath = cfgPath;
|
_cfgPath = cfgPath;
|
||||||
_argsTemplate = argsTemplate;
|
_argsTemplate = argsTemplate;
|
||||||
@@ -33,14 +42,25 @@ namespace MarcerGameDvdLauncher
|
|||||||
throw new ArgumentException("ZIP archive path must not be empty.", nameof(zipFilePath));
|
throw new ArgumentException("ZIP archive path must not be empty.", nameof(zipFilePath));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string args = _argsTemplate.Replace("{cfg}", _cfgPath).Replace("{zip}", zipFilePath);
|
// Build arguments: only include config section if cfgPath is not empty
|
||||||
|
string args = _argsTemplate;
|
||||||
|
if (!string.IsNullOrWhiteSpace(_cfgPath))
|
||||||
|
{
|
||||||
|
args = args.Replace("{cfg}", _cfgPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Remove {cfg} placeholder entirely if config file is empty
|
||||||
|
args = args.Replace("{cfg}", string.Empty);
|
||||||
|
}
|
||||||
|
args = args.Replace("{zip}", zipFilePath);
|
||||||
|
|
||||||
var psi = new System.Diagnostics.ProcessStartInfo
|
var psi = new System.Diagnostics.ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = _exePath,
|
FileName = _exePath,
|
||||||
Arguments = args,
|
Arguments = args,
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
WorkingDirectory = Path.GetDirectoryName(_exePath) ?? string.Empty
|
WorkingDirectory = Directory.GetCurrentDirectory()
|
||||||
};
|
};
|
||||||
System.Diagnostics.Process.Start(psi);
|
System.Diagnostics.Process.Start(psi);
|
||||||
// Show a modal indicating the emulator was started and wait until
|
// Show a modal indicating the emulator was started and wait until
|
||||||
|
|||||||
@@ -63,8 +63,16 @@ namespace MarcerGameDvdLauncher
|
|||||||
if (!File.Exists(cfg.Hatari.Executable))
|
if (!File.Exists(cfg.Hatari.Executable))
|
||||||
throw new InvalidOperationException($"Hatari executable not found: {cfg.Hatari.Executable}");
|
throw new InvalidOperationException($"Hatari executable not found: {cfg.Hatari.Executable}");
|
||||||
|
|
||||||
// Also validate the Hatari config file (if provided)
|
// If no Hatari config file was specified, fall back to the bundled
|
||||||
if (!string.IsNullOrWhiteSpace(cfg.Hatari.ConfigFile) && !File.Exists(cfg.Hatari.ConfigFile))
|
// MarcerGameDvd-Hatari.cfg that ships with the launcher.
|
||||||
|
// Use current working directory to allow running from different locations
|
||||||
|
if (string.IsNullOrWhiteSpace(cfg.Hatari.ConfigFile))
|
||||||
|
{
|
||||||
|
cfg.Hatari.ConfigFile = Path.Combine(Directory.GetCurrentDirectory(), HatariLauncher.DefaultConfigFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the Hatari config file (either user-specified or bundled fallback)
|
||||||
|
if (!File.Exists(cfg.Hatari.ConfigFile))
|
||||||
throw new InvalidOperationException($"Hatari configuration file not found: {cfg.Hatari.ConfigFile}");
|
throw new InvalidOperationException($"Hatari configuration file not found: {cfg.Hatari.ConfigFile}");
|
||||||
if (string.IsNullOrWhiteSpace(cfg.Hatari.ArgsTemplate) || !cfg.Hatari.ArgsTemplate.Contains("{zip}"))
|
if (string.IsNullOrWhiteSpace(cfg.Hatari.ArgsTemplate) || !cfg.Hatari.ArgsTemplate.Contains("{zip}"))
|
||||||
throw new InvalidOperationException("Hatari.ArgsTemplate must contain the {zip} placeholder.");
|
throw new InvalidOperationException("Hatari.ArgsTemplate must contain the {zip} placeholder.");
|
||||||
|
|||||||
@@ -0,0 +1,330 @@
|
|||||||
|
[Log]
|
||||||
|
sLogFileName = stderr
|
||||||
|
sTraceFileName = stderr
|
||||||
|
nTextLogLevel = 3
|
||||||
|
nAlertDlgLogLevel = 1
|
||||||
|
bConfirmQuit = TRUE
|
||||||
|
bNatFeats = FALSE
|
||||||
|
bConsoleWindow = FALSE
|
||||||
|
|
||||||
|
[Debugger]
|
||||||
|
nNumberBase = 10
|
||||||
|
nSymbolLines = -1
|
||||||
|
nMemdumpLines = -1
|
||||||
|
nDisasmLines = -1
|
||||||
|
nBacktraceLines = 0
|
||||||
|
nExceptionDebugMask = 1073741830
|
||||||
|
nDisasmOptions = 7
|
||||||
|
bDisasmUAE = TRUE
|
||||||
|
bSymbolsAutoLoad = TRUE
|
||||||
|
bMatchAllSymbols = FALSE
|
||||||
|
|
||||||
|
[Screen]
|
||||||
|
nMonitorType = 1
|
||||||
|
nFrameSkips = 5
|
||||||
|
bFullScreen = FALSE
|
||||||
|
bKeepResolution = FALSE
|
||||||
|
bResizable = FALSE
|
||||||
|
bAllowOverscan = TRUE
|
||||||
|
nSpec512Threshold = 1
|
||||||
|
bAspectCorrect = TRUE
|
||||||
|
bUseExtVdiResolutions = FALSE
|
||||||
|
nVdiWidth = 1264
|
||||||
|
nVdiHeight = 912
|
||||||
|
nVdiColors = 1
|
||||||
|
bMouseWarp = TRUE
|
||||||
|
bShowStatusbar = TRUE
|
||||||
|
bShowDriveLed = FALSE
|
||||||
|
bCrop = FALSE
|
||||||
|
bForceMax = FALSE
|
||||||
|
nMaxWidth = 1280
|
||||||
|
nMaxHeight = 1024
|
||||||
|
nZoomFactor = 3
|
||||||
|
bUseSdlRenderer = FALSE
|
||||||
|
ScreenShotFormat = 2
|
||||||
|
bUseVsync = FALSE
|
||||||
|
|
||||||
|
[Joystick0]
|
||||||
|
nJoystickMode = 0
|
||||||
|
bEnableAutoFire = FALSE
|
||||||
|
bEnableJumpOnFire2 = TRUE
|
||||||
|
nJoyId = -1
|
||||||
|
nJoyBut1Index = 0
|
||||||
|
nJoyBut2Index = 1
|
||||||
|
nJoyBut3Index = 2
|
||||||
|
kUp = Up
|
||||||
|
kDown = Down
|
||||||
|
kLeft = Left
|
||||||
|
kRight = Right
|
||||||
|
kFire = Right Ctrl
|
||||||
|
|
||||||
|
[Joystick1]
|
||||||
|
nJoystickMode = 1
|
||||||
|
bEnableAutoFire = FALSE
|
||||||
|
bEnableJumpOnFire2 = TRUE
|
||||||
|
nJoyId = 0
|
||||||
|
nJoyBut1Index = 0
|
||||||
|
nJoyBut2Index = 1
|
||||||
|
nJoyBut3Index = 2
|
||||||
|
kUp = Up
|
||||||
|
kDown = Down
|
||||||
|
kLeft = Left
|
||||||
|
kRight = Right
|
||||||
|
kFire = Right Ctrl
|
||||||
|
|
||||||
|
[Joystick2]
|
||||||
|
nJoystickMode = 0
|
||||||
|
bEnableAutoFire = FALSE
|
||||||
|
bEnableJumpOnFire2 = TRUE
|
||||||
|
nJoyId = -1
|
||||||
|
nJoyBut1Index = 0
|
||||||
|
nJoyBut2Index = 1
|
||||||
|
nJoyBut3Index = 2
|
||||||
|
kUp = Up
|
||||||
|
kDown = Down
|
||||||
|
kLeft = Left
|
||||||
|
kRight = Right
|
||||||
|
kFire = Right Ctrl
|
||||||
|
kButtonB = B
|
||||||
|
kButtonC = C
|
||||||
|
kButtonOption = O
|
||||||
|
kButtonPause = P
|
||||||
|
kButtonStar = +
|
||||||
|
kButtonHash = #
|
||||||
|
kButton0 = 0
|
||||||
|
kButton1 = 1
|
||||||
|
kButton2 = 2
|
||||||
|
kButton3 = 3
|
||||||
|
kButton4 = 4
|
||||||
|
kButton5 = 5
|
||||||
|
kButton6 = 6
|
||||||
|
kButton7 = 7
|
||||||
|
kButton8 = 8
|
||||||
|
kButton9 = 9
|
||||||
|
|
||||||
|
[Joystick3]
|
||||||
|
nJoystickMode = 0
|
||||||
|
bEnableAutoFire = FALSE
|
||||||
|
bEnableJumpOnFire2 = TRUE
|
||||||
|
nJoyId = -1
|
||||||
|
nJoyBut1Index = 0
|
||||||
|
nJoyBut2Index = 1
|
||||||
|
nJoyBut3Index = 2
|
||||||
|
kUp = Up
|
||||||
|
kDown = Down
|
||||||
|
kLeft = Left
|
||||||
|
kRight = Right
|
||||||
|
kFire = Right Ctrl
|
||||||
|
kButtonB =
|
||||||
|
kButtonC =
|
||||||
|
kButtonOption =
|
||||||
|
kButtonPause =
|
||||||
|
kButtonStar =
|
||||||
|
kButtonHash =
|
||||||
|
kButton0 =
|
||||||
|
kButton1 =
|
||||||
|
kButton2 =
|
||||||
|
kButton3 =
|
||||||
|
kButton4 =
|
||||||
|
kButton5 =
|
||||||
|
kButton6 =
|
||||||
|
kButton7 =
|
||||||
|
kButton8 =
|
||||||
|
kButton9 =
|
||||||
|
|
||||||
|
[Joystick4]
|
||||||
|
nJoystickMode = 0
|
||||||
|
bEnableAutoFire = FALSE
|
||||||
|
bEnableJumpOnFire2 = TRUE
|
||||||
|
nJoyId = -1
|
||||||
|
nJoyBut1Index = 0
|
||||||
|
nJoyBut2Index = 1
|
||||||
|
nJoyBut3Index = 2
|
||||||
|
kUp = Up
|
||||||
|
kDown = Down
|
||||||
|
kLeft = Left
|
||||||
|
kRight = Right
|
||||||
|
kFire = Right Ctrl
|
||||||
|
|
||||||
|
[Joystick5]
|
||||||
|
nJoystickMode = 0
|
||||||
|
bEnableAutoFire = FALSE
|
||||||
|
bEnableJumpOnFire2 = TRUE
|
||||||
|
nJoyId = -1
|
||||||
|
nJoyBut1Index = 0
|
||||||
|
nJoyBut2Index = 1
|
||||||
|
nJoyBut3Index = 2
|
||||||
|
kUp = Up
|
||||||
|
kDown = Down
|
||||||
|
kLeft = Left
|
||||||
|
kRight = Right
|
||||||
|
kFire = Right Ctrl
|
||||||
|
|
||||||
|
[Keyboard]
|
||||||
|
bDisableKeyRepeat = FALSE
|
||||||
|
nKeymapType = 0
|
||||||
|
nCountryCode = -1
|
||||||
|
nKbdLayout = -1
|
||||||
|
nLanguage = -1
|
||||||
|
szMappingFileName =
|
||||||
|
|
||||||
|
[KeyShortcutsWithMod]
|
||||||
|
kOptions = O
|
||||||
|
kFullScreen = F
|
||||||
|
kBorders = B
|
||||||
|
kMouseMode = M
|
||||||
|
kColdReset = C
|
||||||
|
kWarmReset = R
|
||||||
|
kScreenShot = G
|
||||||
|
kBossKey = I
|
||||||
|
kCursorEmu = J
|
||||||
|
kFastForward = X
|
||||||
|
kRecAnim = A
|
||||||
|
kRecSound = Y
|
||||||
|
kSound = S
|
||||||
|
kPause =
|
||||||
|
kDebugger = Pause
|
||||||
|
kQuit = Q
|
||||||
|
kLoadMem = L
|
||||||
|
kSaveMem = K
|
||||||
|
kInsertDiskA = D
|
||||||
|
kSwitchJoy0 = F1
|
||||||
|
kSwitchJoy1 = F2
|
||||||
|
kSwitchPadA = F3
|
||||||
|
kSwitchPadB = F4
|
||||||
|
|
||||||
|
[KeyShortcutsWithoutMod]
|
||||||
|
kOptions = F12
|
||||||
|
kFullScreen = F11
|
||||||
|
kBorders =
|
||||||
|
kMouseMode =
|
||||||
|
kColdReset =
|
||||||
|
kWarmReset =
|
||||||
|
kScreenShot =
|
||||||
|
kBossKey =
|
||||||
|
kCursorEmu =
|
||||||
|
kFastForward =
|
||||||
|
kRecAnim =
|
||||||
|
kRecSound =
|
||||||
|
kSound =
|
||||||
|
kPause = Pause
|
||||||
|
kDebugger =
|
||||||
|
kQuit =
|
||||||
|
kLoadMem =
|
||||||
|
kSaveMem =
|
||||||
|
kInsertDiskA =
|
||||||
|
kSwitchJoy0 =
|
||||||
|
kSwitchJoy1 =
|
||||||
|
kSwitchPadA =
|
||||||
|
kSwitchPadB =
|
||||||
|
|
||||||
|
[Sound]
|
||||||
|
bEnableMicrophone = TRUE
|
||||||
|
bEnableSound = TRUE
|
||||||
|
bEnableSoundSync = FALSE
|
||||||
|
nPlaybackFreq = 44100
|
||||||
|
nSdlAudioBufferSize = 0
|
||||||
|
szYMCaptureFileName =
|
||||||
|
YmVolumeMixing = 2
|
||||||
|
|
||||||
|
[Memory]
|
||||||
|
nMemorySize = 1024
|
||||||
|
nTTRamSize = 28672
|
||||||
|
bAutoSave = FALSE
|
||||||
|
szMemoryCaptureFileName =
|
||||||
|
szAutoSaveFileName =
|
||||||
|
|
||||||
|
[Floppy]
|
||||||
|
bAutoInsertDiskB = FALSE
|
||||||
|
FastFloppy = FALSE
|
||||||
|
EnableDriveA = TRUE
|
||||||
|
DriveA_NumberOfHeads = 2
|
||||||
|
EnableDriveB = TRUE
|
||||||
|
DriveB_NumberOfHeads = 2
|
||||||
|
nWriteProtection = 0
|
||||||
|
szDiskAZipPath =
|
||||||
|
szDiskAFileName =
|
||||||
|
szDiskBZipPath =
|
||||||
|
szDiskBFileName =
|
||||||
|
szDiskImageDirectory =
|
||||||
|
|
||||||
|
[HardDisk]
|
||||||
|
nGemdosDrive = 0
|
||||||
|
bBootFromHardDisk = FALSE
|
||||||
|
bUseHardDiskDirectory = FALSE
|
||||||
|
szHardDiskDirectory = R:\
|
||||||
|
nGemdosCase = 0
|
||||||
|
nWriteProtection = 0
|
||||||
|
bFilenameConversion = FALSE
|
||||||
|
bGemdosHostTime = FALSE
|
||||||
|
|
||||||
|
[ACSI]
|
||||||
|
|
||||||
|
|
||||||
|
[SCSI]
|
||||||
|
|
||||||
|
|
||||||
|
[IDE]
|
||||||
|
|
||||||
|
[ROM]
|
||||||
|
szTosImageFileName = TOS.IMG
|
||||||
|
bPatchTos = TRUE
|
||||||
|
szCartridgeImageFileName =
|
||||||
|
|
||||||
|
[LILO]
|
||||||
|
Args = root=/dev/ram video=atafb:vga16 load_ramdisk=1
|
||||||
|
Kernel =
|
||||||
|
Symbols =
|
||||||
|
Ramdisk =
|
||||||
|
HaltOnReboot = TRUE
|
||||||
|
KernelToFastRam = TRUE
|
||||||
|
RamdiskToFastRam = TRUE
|
||||||
|
|
||||||
|
[RS232]
|
||||||
|
bEnableRS232 = FALSE
|
||||||
|
szOutFileName =
|
||||||
|
szInFileName =
|
||||||
|
EnableSccA = FALSE
|
||||||
|
SccAOutFileName =
|
||||||
|
SccAInFileName =
|
||||||
|
EnableSccALan = FALSE
|
||||||
|
SccALanOutFileName =
|
||||||
|
SccALanInFileName =
|
||||||
|
EnableSccB = FALSE
|
||||||
|
SccBOutFileName =
|
||||||
|
SccBInFileName =
|
||||||
|
|
||||||
|
[Printer]
|
||||||
|
bEnablePrinting = FALSE
|
||||||
|
szPrintToFileName =
|
||||||
|
|
||||||
|
[Midi]
|
||||||
|
bEnableMidi = FALSE
|
||||||
|
sMidiInFileName =
|
||||||
|
sMidiOutFileName =
|
||||||
|
sMidiInPortName = Off
|
||||||
|
sMidiOutPortName = Off
|
||||||
|
|
||||||
|
[System]
|
||||||
|
nCpuLevel = 0
|
||||||
|
nCpuFreq = 8
|
||||||
|
bCompatibleCpu = TRUE
|
||||||
|
nModelType = 0
|
||||||
|
bBlitter = FALSE
|
||||||
|
nDSPType = 0
|
||||||
|
nVMEType = 1
|
||||||
|
nRtcYear = 0
|
||||||
|
bPatchTimerD = FALSE
|
||||||
|
bFastBoot = FALSE
|
||||||
|
bFastForward = FALSE
|
||||||
|
bAddressSpace24 = TRUE
|
||||||
|
bCycleExactCpu = TRUE
|
||||||
|
n_FPUType = 0
|
||||||
|
bSoftFloatFPU = FALSE
|
||||||
|
bMMU = TRUE
|
||||||
|
VideoTiming = 3
|
||||||
|
|
||||||
|
[Video]
|
||||||
|
AviRecordVcodec = 2
|
||||||
|
AviRecordFps = 0
|
||||||
|
AviRecordFile =
|
||||||
@@ -14,6 +14,9 @@
|
|||||||
<Content Include="launcher.config.example.json">
|
<Content Include="launcher.config.example.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="MarcerGameDvd-Hatari.cfg">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Include="../../LICENSE">
|
<Content Include="../../LICENSE">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<Link>LICENSE</Link>
|
<Link>LICENSE</Link>
|
||||||
|
|||||||
@@ -93,9 +93,13 @@ namespace MarcerGameDvdLauncher
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// out of cache bounds - attempt a direct write
|
// out of cache bounds - attempt a direct write
|
||||||
|
// Validate row against current console height to prevent ArgumentOutOfRangeException
|
||||||
|
if (row < Console.WindowHeight)
|
||||||
|
{
|
||||||
WriteConsoleLine(row, newLine);
|
WriteConsoleLine(row, newLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ensures the cached buffer has exactly availableLines entries and matches width.
|
// Ensures the cached buffer has exactly availableLines entries and matches width.
|
||||||
private void EnsureCache(int width, int availableLines)
|
private void EnsureCache(int width, int availableLines)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"PatchDirectory": "C:\\Games\\Hatari\\PATCH",
|
"PatchDirectory": "C:\\Games\\Hatari\\PATCH",
|
||||||
"Hatari": {
|
"Hatari": {
|
||||||
"Executable": "C:\\Tools\\hatari\\hatari.exe",
|
"Executable": "C:\\Tools\\hatari\\hatari.exe",
|
||||||
"ConfigFile": "C:\\Tools\\hatari\\hatari-st.cfg",
|
"ConfigFile": "",
|
||||||
"ArgsTemplate": "-c \"{cfg}\" --disk-a \"{zip}\""
|
"ArgsTemplate": "-c \"{cfg}\" --disk-a \"{zip}\""
|
||||||
},
|
},
|
||||||
"Colors": {
|
"Colors": {
|
||||||
|
|||||||
Reference in New Issue
Block a user