A labor of love by Hunter Davis. The pause menu is the PS1 build’s in-game control room. It opens with Start, dims the framebuffer with a translucent quad, draws a compact panel, and renders text using the embedded 8x8 ASCII font that also powers closed captions.

v0.5.0-ps1 made the menu more important because it is now the front door for Freeplay and debug mode. The design rule is simple: no submenu should need more than a handful of rows on the PS1 screen. Anything that grows into a catalog becomes a selector page with a title, description, source asset, frame count, and memory note.

On this page

Controller mapping

Button Action
Start Open the pause menu. Inside the menu, resume or back out depending on depth.
D-pad / left analog Move the cursor, change selector values, or edit numeric fields.
Cross Select / apply.
Circle Back everywhere. This is the global cancel rule.

Navigation debounces through the menu’s own prevButtons cache so a long-press does not accidentally rip through a selector.

State machine

enum PauseMenuState lives in src/pause_menu/pause_menu.h:

enum PauseMenuState {
    PAUSE_MENU_MAIN,
    PAUSE_MENU_FREEPLAY_OPTIONS,
    PAUSE_MENU_FREEPLAY_GAGS,
    PAUSE_MENU_FREEPLAY_VISITORS,
    PAUSE_MENU_OPTIONS,     /* world options */
    PAUSE_MENU_HOLIDAYS,
    PAUSE_MENU_ACCESSIBILITY,
    PAUSE_MENU_SOUND_TEST,
    PAUSE_MENU_SYSTEM,
    PAUSE_MENU_SCENE_INFO,
    PAUSE_MENU_CONTROLS,
    PAUSE_MENU_SET_TIME,
    PAUSE_MENU_ISLAND_POS,
    PAUSE_MENU_SET_SEED,
    PAUSE_MENU_CREDITS,
};

Main screen

The main screen is intentionally short. It points to small sub-screens instead of trying to carry every toggle in one tall list:

Entry Purpose
Resume Close the menu and continue.
Scene Set Constrain the screensaver-loop’s random pool to a category — All Scenes (default), Fishing Only, Johnny Stories, Mary Visits, Visitors, Activities, or Misc & Suzy. Left/Right previews; Cross or Start commits and fires a frog-clock loading transition. Drives the scene picker policy.
Scene Explorer Open the chapter-select grid — every FG2 pack on the disc, with on-PS1-captured thumbnails. Cross plays the selected scene once; Triangle loops it. Bypasses the scene picker and dispatches through fgpilot.
Freeplay ON/OFF Enter freeplay from normal mode, or exit freeplay back to the screensaver loop.
Freeplay Options Gags, visitors, controls, and clear-screen tools.
World Options Day/night, tide, raft, holiday, date/time, island position.
Accessibility Captions and sound/accessibility toggles.
System Save settings, set time/date, set RNG seed, perf log, reset current scene, next scene.

The Freeplay ON/OFF row is a mode switch, not a cosmetic toggle. Starting freeplay tears down the current scene, shows the meanwhile frog clock, builds the freeplay island, and hands control to scene_freeplay.c. Exiting freeplay does the same in reverse before the screensaver loop resumes.

The player-facing screenshot guide lives at Menu help guide. It is generated by the scripted controller-input harness, so the documentation doubles as a regression test for Start, D-pad navigation, Cross selection, Circle back, and the major menu states.

Scene Explorer

Scene Explorer is the chapter-select sub-screen — a single-cursor walkthrough of every FG2 pack on the disc that lets the player jump straight to one. It bypasses the screensaver-loop’s scene picker and the Scene Set pool entirely; whatever the cursor is on dispatches through fgpilot.

The layout is a full-screen preview of one scene at a time: the selected scene’s 320×240 RGB555 thumbnail fills the centered region of the framebuffer, with a top chrome band carrying the page title, position counter (N/63), per-scene validation marker (* validated or ? pending from gSceneExplorer[].validated), display name, and family / frame-count, and a bottom chrome band carrying the FG2 pack path and control hints. The cursor walks the 63-scene list sequentially.

Control Action
D-pad Left/Right Move to the previous / next scene in the list. Wraps at the ends.
L1 / R1 Jump to the start of the previous / next ADS family group (Fishing → Johnny → Mary → Visitors → Activities → Misc & Suzy → Standing → Walking → Building).
Cross Pin the selected scene for one-shot play. The screensaver loop runs that scene once, then resumes random selection.
Triangle Pin the selected scene for loop play. The selected scene replays until the user opens the menu and pins something else.
Circle / Start Back to the pause menu main screen without pinning anything.

Thumbnails load lazily on cursor change from SCR\SX<abbrev><tag>.SCR — the <abbrev> table (fishingFI, johnnyJO, etc.) is shared between scripts/build-scene-explorer-thumbnails.py and grLoadSceneExplorerThumbnail in src/graphics_ps1/graphics_ps1.c. The runtime header src/pause_menu/scene_explorer_data.h is regenerated by scripts/build-scene-explorer-data.py from docs/ps1/scene-status.md and the per-scene site/scenes/<slug>/index.md titles, so the in-game menu strings stay in sync with the website ledger.

Since v0.8.7-ps1 the loader streams each thumbnail through a static 16-row chunk buffer instead of allocating the full 153 KB image at once. The paused heap is thin (the runtime never freed pre-pause state), so the prior malloc could fail after long sessions and leave the explorer text-only. The streaming path is allocation-free and robust to that fragmentation.

Pinning persists in two int flags read by the screensaver loop at the top of each iteration: pauseMenuRequestPlayScene (one-shot, cleared after the scene runs) and pauseMenuRequestLoopScene (loop, persists until cleared by another pin or by leaving Scene Explorer with Circle). The flags are direct indices into gSceneExplorer[] (0..62, -1 = idle).

If a thumbnail SCR is missing from the disc (which v0.8.4-ps1 ruled out by adding 21 missing manifest entries — see the chapter-select grind), the loader returns 0 and the panel falls back to its text-only layout. The scene still plays correctly when pinned; only the preview image is absent.

Freeplay pages

Page What it does
Freeplay Options Entry page for Gags, Visitors, Controls, and Clear.
Freeplay Gags Selector for Johnny one-shot actions. Each row shows a name, description, BMP, frame count, and rough memory cost.
Freeplay Visitors Selector for outside-world events, with the same metadata.
Controls On-device reminder for the final freeplay controls.
Clear Cancels transient actions, shows the frog clock, rebuilds the clean island backdrop, and returns Johnny to idle.

Gags and visitors are fail-soft. If an optional BMP cannot load, freeplay shows the skip text and keeps running.

World Options

World Options is the home for visual state:

Setting Behavior
Day/Night Force day or night. In freeplay this applies immediately after a frog-clock rebuild.
Tide Force high or low tide.
Raft Cycle raft stage 0..5.
Holidays Open the holiday selector: Auto Date, None, Original 4, Expanded, then selected holiday inside the active set.
Set Time/Date Edit the software date used by the holiday/date system.
Set Island Pos Nudge the island anchor for placement testing.

The live freeplay shortcuts are the same state changes: R1 + Up toggles day/night, R1 + Down toggles tide, R1 + Left cycles raft, and R1 + Right cycles holiday. Menu edits and live shortcuts share the same backdrop-rebuild path so the visible island changes immediately.

Sound Test

The Sound Test page is a selector for the PS1 SPU effects. It exists because audio bugs are easier to chase when a tester can play a specific sample on command instead of waiting for one scene to reach one frame. It uses the same soundPlay() path as scene playback and freeplay actions.

Accessibility

The Accessibility page owns player-facing readability/audio controls:

  • closed captions on/off;
  • sound/accessibility toggles used by freeplay;
  • the shared text path that uses the pause-menu font atlas.

Captions call pauseMenuEnsureFontUploaded() before drawing, so subtitles work even if the player has never opened the menu in the current boot.

System pages

System keeps the less frequent operations away from the high-use screens:

Page Purpose
Save Settings Writes supported options to memcard.
Reset Current Scene Restart the current story loop.
Next Scene Ask the screensaver loop to pick another scene.
Set RNG Seed Pin the random sequence for repeatable testing.

The PAUSE_MENU_SCENE_INFO and PAUSE_MENU_CREDITS states still exist in the source tree from the previous menu generation. They are not surfaced from the compact v0.5.0 System page; scene diagnostics moved to telemetry and the public credits/legal text lives on the website.

Render pipeline

The pause menu does not modify the background tiles. It draws a dimming POLY_F4, a panel quad, then text. On resume, the scene renderer restores from its clean background and continues.

FntFlush was empirically unreliable in the scene-runtime context, so the menu uses a custom embedded 8x8 font atlas in VRAM. The same atlas is shared with captions and other small text overlays.

  • Freeplay mode — the direct-control Johnny mode reached from the pause menu’s Freeplay sub-screen.
  • Closed captions — the menu owns the shared 8x8 font atlas and the Captions: ON / OFF toggle.
  • Holidays — the World Options sub-screen’s Holiday: <name> cycler walks gHolidays[] via holidayNextId / holidayPrevId.
  • Audio pipeline — the Sound Test sub-screen drives the same SPU voice rotation the scene runtime uses.
  • Scripted input harness — how the auto-builder for /help/menu/ walks the menu routes deterministically.
  • Help: menu screenshots — captured screens of every sub-screen, generated by the scripted input harness.
  • Lab: the chapter-select grind — the v0.8.4-ps1 retrospective on the loop that produced the Scene Explorer’s custom on-PS1-captured thumbnails for all 63 scenes, plus the caption-mismap reconciliation that fell out of walking every pack on hardware.
  • Lab: the two-day SPI bug — war story for the controller-poll fix the menu’s Start / D-pad / Cross input depends on.

Source