No description
  • Shell 53.9%
  • Python 44.8%
  • B (Formal Method) 1.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Kion 72610023a9 gdb-headless: --savestate passed an option melonDS has never had
--savestate appended melonDS's own --loadstate. No melonDS build defines
that option: src/frontend/qt_sdl/CLI.cpp declares only boot, fullscreen,
archive-file and archive-file-gba, in the patched tree and in melonDS-ss13
alike. Qt's parser rejects an unknown option and the emulator exits, so the
flag could never have loaded anything.

That matters because the script's own header prescribed --savestate as the
recipe for a major-13 Retroid savestate -- which is the exact case the ss13
build exists to open. Anyone following the documented path got an emulator
that quit instead of a loaded state.

It is a synonym for --state now, and the header says what --state actually
does: it INSTALLS the file as slot 8. Loading it is a separate step,
send-input.sh --load-state 8, because melonDS decides which F-key slots are
enabled when the ROM loads.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 18:15:43 -05:00
cheats cheats: importable PSZ cheat DB for melonDS-android + download route 2026-07-28 22:45:39 -05:00
docs PSO-style modal: shrink to 66% so the top screen stays readable around it 2026-07-31 01:10:09 -05:00
patches PSO-style modal: shrink to 66% so the top screen stays readable around it 2026-07-31 01:10:09 -05:00
tools gdb-headless: --savestate passed an option melonDS has never had 2026-07-31 18:15:43 -05:00
.gitignore gitignore: glob the upstream clones and sandboxes 2026-07-31 08:09:01 -05:00
BAKE.md Add 2D layer isolation for single-screen HUD R&D (engine B) 2026-07-27 19:50:44 -05:00
build.sh Build on macOS; note observed title/file-select scenes 2026-07-27 23:38:08 -05:00
README.md tools: headless GDB-stub harness + input driver 2026-07-28 18:36:08 -05:00

psz-melonmix — melonDS fork for Phantasy Star Zero

A melonDS fork carrying a Phantasy Star Zero plugin, modelled on KHMelonMix.

Two goals, deliberately ordered:

  1. Instrumentation — make the original game emit machine-readable ground truth about its own combat, so psz-godot can be tuned against measurements instead of memory.
  2. Single-screen presentation — composite the dual-screen layout into one widescreen view, so the original is pleasant to play and to capture reference footage from.

Instrumentation comes first, and that ordering is the whole point of this repo. See below.


Why instrumentation before pretty

The original framing of this project was "a PSZ version of KHMelonMix" — get the game onto one screen to make it easier to compare against the remake.

That is a presentation feature. It makes the game nicer to look at. It does not, by itself, tell you a single number.

What actually blocks psz-godot is measurement. Open issues in that repo include things like "melee hits resolve on a single frame, not a window — feels unreliable vs original PSZ" (#554) and "enemy animations should play slower on normal difficulty (0.8x)" (#555). Those are not opinions; they are empirical claims about the original game that nobody has measured. A single-screen view does not resolve them. A per-frame CSV of hitbox-active flags and animation timers does, permanently, and without anyone having to play anything.

So phase 1 dumps data. Phase 2 makes it pretty. The RE work needed for phase 1 (finding where the game keeps its state) substantially overlaps the RE work needed for phase 2 (finding which layers hold the HUD), so nothing is wasted.

Relationship to psz-re

This repo and psz-re attack the same problem from opposite ends.

  • psz-re reads the binary statically — what the code says it does.
  • psz-melonmix observes the game dynamically — what it actually does at runtime.

They validate each other. A damage formula recovered from Ghidra in psz-re should predict the damage numbers this emulator logs. When they disagree, one of them has a bug, and finding out which is far easier than trusting either alone. Neither is redundant.


How KHMelonMix works (and what we copy)

KHMelonMix extends the melonDS core with a plugin layer that applies game-specific modifications transparently during emulation. Each plugin implements three integration points:

  • detectGameScene() — identify what the game is currently showing (menu, field, cutscene, battle) by inspecting emulated memory
  • input processing — plugins may rewrite controller input before the core sees it
  • rendering enhancement — plugins may intercept and recompose the two screens' output

We reuse this architecture wholesale. Our PSZPlugin implements the same interface. Phase 1 only needs detectGameScene() plus a memory-read hook; phase 2 is where the rendering path gets used.

Upstream is GPL — this fork inherits that. Keep it.


Prior art

Resource Relevance
vitor251093/KHMelonMix The architecture we are copying. Read its plugin layer before writing ours.
melonDS Upstream emulator. Track it; don't drift further than necessary.
psz-re Struct offsets and RAM addresses discovered statically feed directly into our memory watches
DeadSkullzJr cheat DB (psz-re/ref/psz_cheats_usa.xml) 86 verified RAM addresses for the USA build — the fastest way to bootstrap memory watches without any RE at all

The cheat database is the shortest path to a first useful dump. It already gives known-good addresses for money, EXP and stats on the exact C24E build. Anchor the player struct from one of those and walk outward.


How an agent verifies its own work

This repo has unusually good oracles for an emulator project. Use them.

  1. It builds. Non-negotiable, and fully machine-checkable. CI must build the fork on Linux for every change.
  2. It boots. The fork loads the PSZ ROM and reaches the title screen without crashing, verified headlessly.
  3. Deterministic replay. melonDS supports savestates and input recording. Load state → play back a fixed input script → the emulator must produce byte-identical logs across runs. Any nondeterminism is a bug in our hooks, not in the game.
  4. Screenshot diffing. Run to frame N, dump a PNG, compare against a committed reference. This catches rendering regressions in phase 2 without a human looking at anything. An agent can also simply look at the PNG.
  5. Sanity ranges. Logged values must be physically plausible — HP within [0, max], animation frame indices within the clip's length. Violations mean the watch address is wrong.

A phase-1 change is done when a fixed input script produces a stable, sane CSV across two consecutive runs. No human required.


Work breakdown

Phase 0 — fork and build

  • Fork melonDS, get a clean Linux build, commit build instructions
  • Add headless/CI build target; GitHub-Actions-equivalent config for Forgejo
  • Verify the fork boots Phantasy Star 0 (USA).nds to title, headlessly (tools/gdb-headless.sh — Xvfb + screenshots; intro renders)
  • Port KHMelonMix's plugin scaffolding; register an empty PSZPlugin

Phase 1 — instrumentation

  • [~] Memory-watch framework: declarative table of (name, address, type) → per-frame sampling, CSV/JSONL out, configurable at runtime. Sampling works today via the GDB stub — tools/gdb-headless.sh plus psz-re/tools/watch.py gives pointer-following JSONL samples with screenshots, no fork changes at all. A native per-frame plugin is still worth building for high-rate logging (the stub halts the core per sample).
  • Bootstrap watches from the cheat DB (money, EXP, level) to prove the pipeline end-to-end against values visible on screen
  • Locate the player struct; log position, HP, TP, state, current animation id, animation frame
  • Locate the enemy array; log per-enemy id, HP, state, animation, position
  • Hook damage application — log attacker, target, raw damage, final damage. This is the single highest-value output of the repo.
  • Log hitbox activation windows: first and last frame a swing can connect (answers psz-godot #554 definitively)
  • Log animation playback rate per difficulty (answers psz-godot #555)
  • Input scripting: replay a recorded input sequence deterministically so captures are reproducible
  • detectGameScene() for PSZ so logs can be segmented by context

Phase 2 — presentation

  • Identify which BG/OBJ layers carry HUD elements per game scene
  • Composite bottom-screen HUD onto the 3D top screen
  • Widescreen / aspect handling
  • Controller mapping suited to a single-screen layout
  • Optional: high-res 3D rendering

Phase 3 — delivery

  • Publish captured reference data to psz-re / psz-godot as JSON
  • Reference capture mode: run a scripted scenario, emit both video and the matching data log, for side-by-side comparison against the remake

Status

Not started. This README is the specification.

No ROM or game asset is committed here. Contributors supply their own legally obtained copy. Inherits melonDS's GPL licence.