Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0cea191
feat(zaparoo): F2 toggle to switch the launcher between HDMI and CRT …
asturur May 9, 2026
6d2690b
fix(zaparoo): handle F2 toggle in user_io_kbd, not HandleUI
asturur May 9, 2026
390c141
Revert "fix(zaparoo): handle F2 toggle in user_io_kbd, not HandleUI"
asturur May 9, 2026
4220a82
Revert "feat(zaparoo): F2 toggle to switch the launcher between HDMI …
asturur May 9, 2026
7d0f6a4
feat(zaparoo): OSD menu entry to toggle CRT path
asturur May 9, 2026
4afaa67
feat(zaparoo): also surface CRT mode toggle in System Settings (MENU_…
asturur May 9, 2026
99f9829
feat(zaparoo): let F12 open the OSD on top of a running launcher
asturur May 9, 2026
155254e
feat(zaparoo): F12 on menu core opens System Settings directly, F12 c…
asturur May 9, 2026
9c831d0
fix(zaparoo): suppress fb_terminal nag and let System Settings render…
asturur May 9, 2026
54001f1
fix(zaparoo): F12/MENU open the OSD overlay over a running launcher
asturur May 9, 2026
00b7251
fix(zaparoo): allow OSD to close in CRT mode
asturur May 9, 2026
6dc8bb8
fix(zaparoo): scope the alt-launcher F-key exception to F12 only
asturur May 10, 2026
b9e3bea
feat(zaparoo): support custom MENU_RBF and consolidate alt-launcher F…
wizzomafizzo May 10, 2026
27019d8
fix(zaparoo): route joypad input to launcher in native CRT mode
wizzomafizzo May 10, 2026
08ccd5d
feat(zaparoo): persist alt-launcher CRT/HDMI mode across reboots
wizzomafizzo May 10, 2026
c7804d2
fix(zaparoo): clear FPGA CRT scan-out region before launcher spawn
wizzomafizzo May 10, 2026
892777c
refactor(zaparoo): trim alt-launcher OSD menu via dedicated helper
wizzomafizzo May 10, 2026
72037bc
refactor(zaparoo): drop ALT_LAUNCHER and MENU_RBF ini knobs
wizzomafizzo May 10, 2026
09a7467
fix(build): apply cumulative fork diff in stable build
wizzomafizzo May 10, 2026
54fbded
feat(zaparoo): restore exit-launcher escape-to-stock behavior
wizzomafizzo May 10, 2026
af81c62
feat(zaparoo): right-side Display Centering page with H/V offset
asturur May 10, 2026
02552bc
docs(zaparoo): add ZAPAROO_FORK.md change map and cleanup backlog
asturur May 10, 2026
7d6b40a
good
asturur May 10, 2026
3eeb116
removed dsstore
asturur May 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 179 additions & 0 deletions ZAPAROO_FORK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# Zaparoo Fork — Change Map and Cleanup Backlog

This document maps the Zaparoo-specific changes layered on top of MiSTer-devel
`Main_MiSTer`. It is meant to evolve as the fork iterates, so future
contributors don't have to re-derive the architecture from `git log`.

**Scope:** commits authored by `wizzomafizzo` (Callan Barrett) and `asturur`
(Andrea Bogazzi) between commit `a2eb35e` and the tip of
`feat/zaparoo-fb-toggle`. Upstream merges, upstream-author changes, and
fully-reverted experiments (F2 toggle, picker/notice commands, perf tweak,
non-blocking spawn) are intentionally omitted.

> **Living doc:** when you add a Zaparoo-specific behavior, append a row to
> the table below or revise the inconsistencies section. Stale entries are
> worse than no entries — please prune as you cleanup.

---

## 1. Change map

| # | Cluster | Purpose | Where it lives |
|---|---------|---------|----------------|
| 1 | **External launcher process management** | Spawn `zaparoo/launcher` via `agetty` on tty2; SIGTERM/SIGKILL on shutdown; bounded `waitpid`; respawn timer; 3-strike crash give-up | `support/zaparoo/alt_launcher.cpp` (`spawn`, `kill_launcher`, `alt_launcher_poll`, `return_to_normal_mode`) |
| 2 | **Process discovery & gating** | `alt_launcher_configured()` = file exists at `zaparoo/launcher` (cached, with sticky escape bit); `alt_launcher_active()` = process running | `support/zaparoo/alt_launcher.cpp:34-43,329-332` |
| 3 | **Custom menu RBF discovery** | `menu_rbf_name()` / `is_menu_rbf()` so a Zaparoo build can ship its own renamed menu RBF and the file_io / fpga_io / user_io paths still recognize it | `support/zaparoo/menu_rbf.cpp/.h`; consumers in `file_io.cpp`, `fpga_io.cpp`, `user_io.cpp` |
| 4 | **Forced cfg overrides** | `alt_launcher_cfg_apply()` forces `cfg.fb_terminal = 1; cfg.recents = 1` after INI parse. Original `ALT_LAUNCHER` / `MENU_RBF` INI knobs were dropped in favor of file-existence detection | `cfg.cpp:614`, `support/zaparoo/alt_launcher.cpp:24-30` |
| 5 | **Polling integration** | `alt_launcher_poll()` driven by main scheduler tick | `scheduler.cpp:36`, `support/zaparoo/alt_launcher.cpp:365` |
| 6 | **TTY / framebuffer hygiene** | Clear/reset tty2 around launcher lifecycle; toggle `video_fb_enable` and `video_chvt` only on respawn paths; don't touch them on plain shutdown | `support/zaparoo/alt_launcher.cpp` (`clear_launcher_tty`, `reset_launcher_tty`) |
| 7 | **Joypad routing into launcher** | `alt_launcher_fb_terminal_key()` translates `JOY_L2/R2/OSD` to `KEY_F1/BACKSPACE/MENU`; `joy_digital()` short-circuits to `uinp_send_key` when launcher active | `input.cpp:2475-2484`, `support/zaparoo/alt_launcher.cpp:45-62` |
| 8 | **Native CRT rendering path** | Launcher running in CRT mode: kernel framebuffer at 320×240 RGBA8888, FPGA scans separate region at `0x3A000000`, `status[9]=1` gates it; pre-spawn blank wipes the prior frame | `support/zaparoo/alt_launcher.cpp` (`enable_native_crt_path`, `disable_native_crt_path`, `blank_native_crt_fb`); paired with `Menu_MiSTer/rtl/native_video_*.sv` |
| 9 | **CRT mode persistence** | 1-byte `zaparoo_launcher_crt.bin` via `FileSaveConfig` / `FileLoadConfig`; loaded at menu init, applied on spawn | `support/zaparoo/alt_launcher.cpp:76-89,344,499` |
| 10 | **Native-core auto-init** | `zaparoo_is_native_core()` matches core name `"Zaparoo Launcher"`; `zaparoo_alt_launcher_init_for_core()` auto-spawns when the FPGA loads that core | `support/zaparoo/alt_launcher.cpp:480-495`, `user_io.cpp:1543` |
| 11 | **In-core "Launcher" OSD entry** | Adds row 31 (`ALT_LAUNCHER_MENUSUB`) to MENU_COMMON1 marked with `reboot_req` when activated | `menu.cpp:2831,2845-2849,3088-3091` |
| 12 | **OSD/F12 overlay over running launcher** | F12 / `KEY_MENU` reaches the OSD even with launcher running; on menu core opens System Settings directly (skip file picker); F1/F9 disabled when launcher active; `vga_nag` suppressed; auto-open suppressed in CRT mode | `menu.cpp:843-852,1289,1304-1311,1334,1583,1604-1611,6727,6739,6816,6901`, `user_io.cpp:4162-4171` |
| 13 | **Trimmed System Settings render** | `alt_launcher_render_system_menu()` overrides MENU_SYSTEM1 body for the alt-launcher path; `alt_launcher_translate_system_select()` maps trimmed menusub indices to upstream dispatch slots | `support/zaparoo/alt_launcher_menu.cpp`, `menu.cpp:6739-6745,6816-6821` |
| 14 | **Right-side Display Centering page** | New menu state hosting H/V offset adjustment + relocated CRT toggle; persisted via 2-byte `zaparoo_video_offsets.bin`; pushed via `user_io_status_set("[13:10]" / "[17:14]")` | `support/zaparoo/display_menu.cpp/.h` *(local rename in progress: `launcher_pages.cpp/.h`)*, `support/zaparoo/alt_launcher.cpp` (offset state + setters), `menu.cpp` `MENU_ZAPAROO_DISPLAY*` cases |
| 15 | **Escape-to-stock semantics** | Sticky `s_escaped` flag makes `alt_launcher_configured()` return `false` after a clean exit, so the rest of the session reverts to stock OSD; reboot resets it | `support/zaparoo/alt_launcher.cpp:32,38-43,229-241` |
| 16 | **CI / build infrastructure** | Docker container build; binary named `MiSTer_Zaparoo`; "Z"-suffixed version; release / unstable CI; sync-upstream workflow; deploy script | `docker-build.sh`, `stable-build.sh`, `unstable-build.sh`, `deploy-zaparoo.sh`, `.github/build_*.sh`, `.github/workflows/*.yml` |
| 17 | **Build-time defaults flipped** | `cfg.recents` and `LOG_FILE_ENTRY` default to enabled in Zaparoo builds | `cfg.cpp` (defaults) |

---

## 2. Inconsistencies and cleanup backlog

These are intentional starting points for follow-up work, ordered roughly from
"30-minute cleanup" to "needs a design pass."

### 2.1 Mixed namespace prefix in one module
`support/zaparoo/alt_launcher.h` uses two prefixes for the same conceptual thing:

```c
alt_launcher_init / _shutdown / _toggle_crt / _native_crt / _active / _configured ...
zaparoo_is_native_core
zaparoo_alt_launcher_init_for_core / _for_menu
```

The `zaparoo_*` wrappers are the only ones called from `user_io.cpp`. Pick one
prefix (`zap_` is shortest) or split into two headers along that boundary.

### 2.2 Three overlapping predicates
Gating across the codebase uses `_configured()` / `_active()` / `_native_crt()`
interchangeably without a documented rule. Current de-facto convention:

| Predicate | Meaning | Used to gate |
|-----------|---------|--------------|
| `_configured` | binary file exists, sticky off after escape | **render paths** (System Settings body, MENU_SYSTEM1 entry, vga_nag, file-picker entry, "Launcher" row visibility, right-arrow gate) |
| `_active` | PID alive | **input handling** (F1/F9 disable, joypad-to-launcher routing, OSD overlay, menu auto-open suppression) |
| `_native_crt` | PID alive AND CRT mode on | **internal video state machine** (`disable_native_crt_path`, status timer) |

The split is mostly principled but leaks at `MENU_SYSTEM1`, which uses
`_configured` for both the gate AND the body delegation while `MENU_NONE2`
auto-open uses `_active`. A user with the launcher binary present but not yet
running sees different OSD behavior than a user without the binary at all —
worth either a comment or a unified helper.

### 2.3 Two persistence files, no shared format
Current state:

```
zaparoo_launcher_crt.bin 1 byte (CRT mode flag)
zaparoo_video_offsets.bin 2 bytes (h_offset, v_offset)
```

Same dir, same `FileSaveConfig` API, but no unified struct. If a third setting
arrives this becomes a per-feature file pattern. A single `zaparoo_state.bin`
with a versioned struct would scale better:

```c
struct zaparoo_state_v1 {
uint8_t magic; // 'Z'
uint8_t version; // 1
uint8_t crt; // 0 / 1
int8_t h_offset; // -8..+7
int8_t v_offset; // -8..+7
uint8_t reserved[3];
};
```

Cost: a one-shot migration on existing installs (read legacy `crt.bin` if
new file is missing; never write the legacy file again).

### 2.4 Hardcoded paths scattered across modules
- Launcher path: `zaparoo/launcher` in `alt_launcher.cpp:22`
- Menu RBF name(s): hardcoded in `support/zaparoo/menu_rbf.cpp`
- Persistence files: hardcoded in `alt_launcher.cpp`

INI knobs were intentionally dropped (commit `72037bc`) in favor of
file-existence detection, but the resulting literals are now in three places.
A small `support/zaparoo/paths.h` (or a `paths.cpp` that resolves them at
startup) would centralize them without bringing INI knobs back.

### 2.5 Status-bit map exists only in code
`status[9]` (CRT gate), `status[13:10]` (h_offset), `status[17:14]` (v_offset)
are agreed upon between this fork and `Menu_MiSTer/feat/dual-mode-native-fb`,
but the agreement is enforced only by lining up `user_io_status_set("[13:10]", …)`
against the SystemVerilog `status[13:10]`. Neither side has a comment
referencing the other.

A short `STATUS.md` (or a header in `support/zaparoo/`) documenting the shared
register layout would prevent accidental conflicts when a future feature
allocates new bits.

### 2.6 F-key handling comment is archaeology
F2 toggle was added (`0cea191`), moved to `user_io_kbd` (`6d2690b`), and both
were reverted (`390c141`, `4220a82`). What remains is a multi-line comment
block in `user_io.cpp:4162-4171` that reads like commit-history narration.

It can shrink to one line:

```c
// F12/KEY_MENU bypasses alt_launcher_active() so the user can open
// the OSD on top of a running launcher.
```

### 2.7 Trimmed-menu dispatcher has a dead branch
`alt_launcher_translate_system_select()` returns `-1` to signal "consumed
inline." That existed for the CRT row, which has now moved to the right page.

The `if (dispatch < 0) { menustate = MENU_SYSTEM1; break; }` branch in
`MENU_SYSTEM2`'s switch is currently unreachable. Either drop it or note that
it's reserved for future inline-handled rows.

### 2.8 `alt_launcher.cpp` is doing too many jobs
~600 lines covering: process lifecycle + video state machine + tty handling
+ status-bit pushers + offset persistence + cfg overrides. Splitting into
`alt_launcher_proc.cpp`, `alt_launcher_video.cpp`, `alt_launcher_state.cpp`
would make each concern testable and easier to audit. Not urgent — the file
is still readable — but the next feature will tip it over.

### 2.9 Hard-override of user INI config is silent
`alt_launcher_cfg_apply()` quietly forces `fb_terminal=1` and `recents=1`
after `cfg_parse`. A user who set `fb_terminal=0` in `MiSTer.ini` gets no
warning that we're overriding them.

At minimum, log it once at startup:

```c
printf("alt_launcher: forcing fb_terminal=1, recents=1 (Zaparoo build)\n");
```

### 2.10 Naming drift in the new menu page
The right-side page was committed as `MENU_ZAPAROO_DISPLAY1/2` with files
`display_menu.{cpp,h}`. Local in-progress refactor renames the menu states
to `MENU_ZAPAROO_VIDEO*` / `MENU_ZAPAROO_LAUNCHER*` and the file to
`launcher_pages.{cpp,h}`. After the rename settles, double-check that the
file name and the symbols inside agree on what the page is called.

---

## 3. Boundary commit

The "fork divergence" reference point used for this analysis is:

```
a2eb35eacdd7789abe3411e4d03381e7bf55309f
```

Use that as the base in `git log a2eb35e..HEAD` if you want to refresh this
document programmatically.
4 changes: 2 additions & 2 deletions cfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ static const ini_var_t ini_vars[] =
{ "SCREENSHOT_IMAGE_FORMAT", (void *)(&(cfg.screenshot_image_format)), STRING, 0, sizeof(cfg.screenshot_image_format) - 1 },
{ "XBE2_SHIFT", (void*)(&(cfg.xbe2_shift)), UINT16, 0, 0x22F },
{ "SPD_QUIRK", (void*)(&(cfg.spd_quirk)), UINT8, 0, 3 },
{ "ALT_LAUNCHER", (void*)(&(cfg.alt_launcher)), STRING, 0, sizeof(cfg.alt_launcher) - 1 },
};

static const int nvars = (int)(sizeof(ini_vars) / sizeof(ini_var_t));
Expand Down Expand Up @@ -580,7 +579,6 @@ const char* cfg_get_label(uint8_t alt)
void cfg_parse()
{
memset(&cfg, 0, sizeof(cfg));
alt_launcher_cfg_defaults();
cfg.csync = 1;
cfg.bootscreen = 1;
cfg.fb_terminal = 1;
Expand Down Expand Up @@ -613,6 +611,8 @@ void cfg_parse()
ini_parse(altcfg(), video_get_core_mode_name(0));
}

alt_launcher_cfg_apply();

if (strlen(cfg.vga_mode))
{
if (!strcasecmp(cfg.vga_mode, "rgb")) cfg.vga_mode_int = 0;
Expand Down
1 change: 0 additions & 1 deletion cfg.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ typedef struct {
char screenshot_image_format[16];
uint16_t xbe2_shift;
uint8_t spd_quirk;
char alt_launcher[1024];
} cfg_t;

extern cfg_t cfg;
Expand Down
5 changes: 3 additions & 2 deletions file_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "scheduler.h"
#include "video.h"
#include "support.h"
#include "support/zaparoo/menu_rbf.h"

#define MIN(a,b) (((a)<(b)) ? (a) : (b))

Expand Down Expand Up @@ -1127,7 +1128,7 @@ void setStorage(int dev)
{
device = 0;
FileSave(CONFIG_DIR"/device.bin", &dev, sizeof(int));
fpga_load_rbf("menu.rbf");
fpga_load_rbf(menu_rbf_name());
}

static int orig_device = 0;
Expand Down Expand Up @@ -1693,7 +1694,7 @@ int ScanDirectory(char* path, int mode, const char *extension, int options, cons
// skip hidden files
if (!strncasecmp(de->d_name, ".", 1)) continue;
//skip non-selectable files
if (!strcasecmp(de->d_name, "menu.rbf")) continue;
if (is_menu_rbf(de->d_name)) continue;
if (!strncasecmp(de->d_name, "menu_20", 7)) continue;
if (!strncasecmp(de->d_name, "boot", 4))
{
Expand Down
5 changes: 3 additions & 2 deletions fpga_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "shmem.h"
#include "offload.h"
#include "support/zaparoo/alt_launcher.h"
#include "support/zaparoo/menu_rbf.h"

#include "fpga_base_addr_ac5.h"
#include "fpga_manager.h"
Expand Down Expand Up @@ -441,7 +442,7 @@ int fpga_load_rbf(const char *name, const char *cfg, const char *xml)
printf("Loading RBF: %s\n", name);

if(name[0] == '/') strcpy(path, name);
else sprintf(path, "%s/%s", !strcasecmp(name, "menu.rbf") ? getStorageDir(0) : getRootDir(), name);
else sprintf(path, "%s/%s", is_menu_rbf(name) ? getStorageDir(0) : getRootDir(), name);

Comment on lines 444 to 446
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Bound path writes in fpga_load_rbf.

Line 445 writes into a fixed 1024-byte buffer with sprintf; a long name can overflow path and corrupt stack state. Please switch to bounded writes and reject truncation.

💡 Suggested fix
-	if(name[0] == '/') strcpy(path, name);
-	else sprintf(path, "%s/%s", is_menu_rbf(name) ? getStorageDir(0) : getRootDir(), name);
+	if (name[0] == '/')
+	{
+		if (snprintf(path, sizeof(path), "%s", name) >= (int)sizeof(path)) return -1;
+	}
+	else
+	{
+		if (snprintf(path, sizeof(path), "%s/%s",
+			is_menu_rbf(name) ? getStorageDir(0) : getRootDir(), name) >= (int)sizeof(path)) return -1;
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fpga_io.cpp` around lines 444 - 446, The write into the fixed char path[1024]
in fpga_load_rbf uses sprintf and can overflow; replace both
sprintf/sprintf-like usage with snprintf(path, sizeof(path), ...) and check the
return value for truncation (ret < 0 or ret >= sizeof(path)) and treat that as
an error path (reject the name and return/fail), and for the absolute case
(name[0]=='/') similarly use snprintf and check truncation; reference
is_menu_rbf, getStorageDir, getRootDir and the local buffer path when
implementing the bounded write and rejection on truncation.

int rbf = open(path, O_RDONLY);
if (rbf < 0)
Expand Down Expand Up @@ -504,7 +505,7 @@ int fpga_load_rbf(const char *name, const char *cfg, const char *xml)
}
close(rbf);

app_restart(!strcasecmp(name, "menu.rbf") ? "menu.rbf" : path, xml);
app_restart(is_menu_rbf(name) ? menu_rbf_name() : path, xml);
return ret;
}

Expand Down
2 changes: 1 addition & 1 deletion input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3564,7 +3564,7 @@ static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int
if (osd_event == 2) joy_digital(input[dev].num, 0, 0, 0, BTN_OSD);
}

if (user_io_osd_is_visible() || video_fb_state())
if (user_io_osd_is_visible() || video_fb_state() || alt_launcher_active())
{
if (ev->value <= 1)
{
Expand Down
Loading