Skip to content

Commit 82ef36c

Browse files
authored
Merge pull request #51 from eddmann/claude/save-load-game-cli-012uFPKHAYhaBhC8SLQPXqbL
2 parents 18b5be0 + d120d23 commit 82ef36c

2 files changed

Lines changed: 56 additions & 3 deletions

File tree

bin/phpboy.php

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ function showHelp(): void
7070
--playback=<path> Playback TAS input from JSON file
7171
--help Show this help message
7272
73+
Controls (during gameplay):
74+
Arrow Keys / WASD: D-pad
75+
Z: A button
76+
X: B button
77+
Enter: Start
78+
Space: Select
79+
Ctrl+S: Save state (creates timestamped save file)
80+
Ctrl+C: Exit
81+
7382
Examples:
7483
php bin/phpboy.php tetris.gb
7584
php bin/phpboy.php --rom=tetris.gb --speed=2.0
@@ -312,6 +321,22 @@ function parseArguments(array $argv): array
312321
if (!$options['headless']) {
313322
$input = new CliInput();
314323
$emulator->setInput($input);
324+
325+
// Set up Ctrl+S save callback
326+
$saveCounter = 0;
327+
$romBaseName = pathinfo($options['rom'], PATHINFO_FILENAME);
328+
$input->onSave(function () use ($emulator, &$saveCounter, $romBaseName) {
329+
$saveCounter++;
330+
$timestamp = date('Y-m-d_H-i-s');
331+
$filename = "{$romBaseName}_save_{$saveCounter}_{$timestamp}.state";
332+
333+
try {
334+
$emulator->saveState($filename);
335+
echo "\n[Saved state to: {$filename}]\n";
336+
} catch (\Throwable $e) {
337+
echo "\n[Error saving state: {$e->getMessage()}]\n";
338+
}
339+
});
315340
}
316341

317342
// Set up renderer
@@ -508,7 +533,9 @@ function parseArguments(array $argv): array
508533
echo " Z: A button\n";
509534
echo " X: B button\n";
510535
echo " Enter: Start\n";
511-
echo " Space: Select\n\n";
536+
echo " Space: Select\n";
537+
echo " Ctrl+S: Save state\n";
538+
echo " Ctrl+C: Exit\n\n";
512539

513540
// Set up signal handler for graceful shutdown
514541
if (function_exists('pcntl_signal')) {

src/Frontend/Cli/CliInput.php

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
* CLI keyboard input handler for PHPBoy.
1212
*
1313
* Maps keyboard keys to Game Boy buttons:
14-
* - Arrow keys → D-pad (Up, Down, Left, Right)
14+
* - Arrow keys / WASD → D-pad (Up, Down, Left, Right)
1515
* - Z → A button
1616
* - X → B button
1717
* - Enter → Start
18-
* - Right Shift → Select
18+
* - Space → Select
19+
* - Ctrl+S → Save state (via callback)
20+
* - Ctrl+C → Exit emulation
1921
*
2022
* Note: Non-blocking keyboard input in PHP CLI is limited.
2123
* This implementation uses stream_select for non-blocking reads
@@ -29,6 +31,9 @@ final class CliInput implements InputInterface
2931
/** Control character for Ctrl+C (ASCII 3) */
3032
private const CTRL_C = "\x03";
3133

34+
/** Control character for Ctrl+S (ASCII 19) */
35+
private const CTRL_S = "\x13";
36+
3237
/** @var array<string, Button> Keyboard key to button mapping */
3338
private const KEY_MAP = [
3439
// Arrow keys (ANSI escape sequences)
@@ -73,12 +78,25 @@ final class CliInput implements InputInterface
7378
/** @var bool Whether terminal mode has been set */
7479
private bool $terminalModeSet = false;
7580

81+
/** @var callable|null Callback to invoke when Ctrl+S is pressed */
82+
private $onSaveCallback = null;
83+
7684
public function __construct()
7785
{
7886
$this->stdin = STDIN;
7987
$this->setupTerminal();
8088
}
8189

90+
/**
91+
* Set callback to invoke when Ctrl+S is pressed.
92+
*
93+
* @param callable $callback Function to call when user presses Ctrl+S for save
94+
*/
95+
public function onSave(callable $callback): void
96+
{
97+
$this->onSaveCallback = $callback;
98+
}
99+
82100
public function __destruct()
83101
{
84102
$this->restoreTerminal();
@@ -201,6 +219,14 @@ private function parseInput(string $input): void
201219
exit(0);
202220
}
203221

222+
// Check for Ctrl+S (in raw mode, this comes through as ASCII 19)
223+
if (str_contains($input, self::CTRL_S)) {
224+
if ($this->onSaveCallback !== null) {
225+
($this->onSaveCallback)();
226+
}
227+
// Don't return here - continue processing other input in the buffer
228+
}
229+
204230
// Check for arrow key escape sequences (3 characters)
205231
if (strlen($input) >= 3 && $input[0] === "\033" && $input[1] === '[') {
206232
$sequence = substr($input, 0, 3);

0 commit comments

Comments
 (0)