Skip to content

Fix crashes and improve asset handling in PC port - #15

Merged
MatheoVignaud merged 194 commits into
CI-Testfrom
master
May 16, 2026
Merged

Fix crashes and improve asset handling in PC port#15
MatheoVignaud merged 194 commits into
CI-Testfrom
master

Conversation

@MatheoVignaud

Copy link
Copy Markdown
Owner

No description provided.

999sian and others added 30 commits May 2, 2026 16:43
build.py now copies assets/sounds.json into dist/<version>/ so the m4a
backend's exe-dir probe finds it in release tarballs (the audio fix in
9c6b036 expected the file to ship there but nothing was producing it).

Forward-port viruappu-mosaic.patch onto current upstream ViruaPPU: drop
the affine-BG hunk (renderer was rewritten upstream and no longer has
that path) and add the missing <stdlib.h> include so getenv() compiles.
Re-enable the patch in xmake.lua now that it applies cleanly again.
Two related issues. (1) sub_0804AFB0 / GetRoomProperty on PC only
checked the room-callback function table for property indices 4..7,
returning NULL when a room actually stores DATA there (e.g. Minish
Forest lily pads index a rail array via type2 in 4..7 in some rooms).
Fall back to a packed ROM read when no callback is registered. (2)
build.py packed assets from build/pc/assets/, but asset_extractor
writes to build/<version>/assets/, so dist/<version>/ tarballs were
missing whole subtrees (including data_080D5360/ which holds the
lily-pad rail data). Point build.py at the right source path.

With both fixes Minish Forest lily pads move correctly when shrunk;
this is a progression unblocker.
Real GBA's MPlayStart was effectively a no-op when the engine re-issued
the same song on the same player; agbplay's m4aMPlayStart restarts
playback unconditionally and that's audible as the BGM resetting on
every room transition (Link's house was the regression report).

Track the currently-playing song per player and skip the restart when
the same BGM (songId 1..99) is requested. Keep SFX (>=100) on the
unconditional path — they legitimately re-trigger the same id.
StopPlayer clears the tracking so the next start always restarts.
The USA GBA original queues BGM_BEANSTALK ("Climbing the Beanstalk") in
sub_StateChange_CastleGarden_Main and sub_StateChange_HyruleField_OutsideCastle
during the prologue. EU was patched to queue BGM_FESTIVAL_APPROACH —
the toned-down Picori Festival theme — which is the canonical music
for that sequence. Follow EU on PC_PORT.
The PC port had stripped the GBA-original 'gMessage.state & MESSAGE_ACTIVE'
check from EnterRoomTextboxManager_Main, so the location-name banner
created on room enter would linger after a script raised a fresh dialog
(e.g. Zelda calling Link from off-screen as you leave the house). The
result was a stray boxed name centered on screen for ~2 seconds before
the real dialog took over off to the side.

Restore the original kill condition. Also drop the chatty per-frame
[MANAGER] dispatch and [AREA] textbox prints — they were diagnostics
left in from earlier debugging and dominated stderr.
GBA-original animates the "becoming big" cutscene as giant → normal
(player affine sx=1152 → 0x100, sprite displays 4.5x → 1x). User
expectation for the port is the conventional small → big direction —
matches what's intuitive for emerging from minish form. PC_PORT path
now starts unk_80/unk_84 at 0x80 (sprite displays 0.5x — small) and
grows by 5/frame up to 0x100 (1x), with a clamp so we don't overshoot
into giant. GBA build keeps the original direction.

Code paths: src/player.c sub_08073F4C (init scale) and sub_08074018
(per-frame grow).
After Festari/Gentari shows the local map and the player dismisses it,
the room behind the dialog was rendering as solid black on the port.

Two issues in the Subtask_FadeOut → RestoreGameTask → sub_0801AE44 path:

1. sub_0807C4F8 (scroll_flags&1 alternate path) only iterated ROM-packed
   12-byte MapDataDefinition entries, but on the asset-loader path
   (gArea.pCurrentRoomInfo)->map points to native 24-byte structs whose
   dest is a void* holding a GBA address. The matcher missed them and
   gMapDataBottomSpecial stayed zeroed, so UpdateScrollVram filled the
   BG buffer with zeros.

2. Even with the buffer correctly filled, no one raised bg.updated=1
   after RestoreGameTask, so sub_08016CA8 never copied the buffer to
   VRAM and the screen base kept the stale map tilemap. Force the
   buffer→VRAM copy in RestoreGameTask.
After the Festari/Gentari fix taught the codebase to detect packed-ROM
vs native-heap MapDataDefinition format, the same hazard exists in three
other places that iterate room property data: each assumes GBA-packed
layout and silently misreads native 24-byte (or 16-byte) structs that
the asset loader provides.

- houseDoorExterior: 12-byte packed → 16-byte native unk_DoorProperty
  (low half of 8-byte u8* unk8 holds the GBA script address)
- lavaPlatform:      16-byte packed → 24-byte native LavaPlatformEntry
  (low half of 8-byte Entity* unk_78 holds the GBA rail address)
- delayedEntityLoadManager: 16-byte packed → 24-byte native NPCStruct
  (low half of 8-byte u16* script holds the GBA script address)

Each site now detects packed-ROM via gRomData range check and falls
back to native-struct iteration on the asset-loader path. The script
pointer in each native struct is treated as a real heap pointer when
≥ 0x10000000, and as a sign-extended GBA address otherwise.
The GBA-original HouseDoorExterior spawner sets entity->context only
when it has a non-NULL script pointer that resolves successfully:

    if (p_unk8) {
        u16* script = (u16*)Port_ResolveRomData(p_unk8);
        if (script) entity->context = StartCutscene(...);
    }

On real hardware the script address always resolved, so Type3 (a
fully-scripted door) could safely call ExecuteScript on context. On
the port, Port_ResolveRomData can return NULL for door scripts whose
GBA address isn't backed by the loaded ROM image, leaving the spawned
Type3 entity with context=NULL. The next frame then segfaults inside
ExecuteScript dereferencing the NULL context.

Reproduced entering Hyrule Field room 5 (Lon Lon Ranch). Skip the
script invocation under PC_PORT instead of crashing.
Fresh checkouts crashed when running the asset processor in extract mode
because directory_iterator threw on a missing assets/ folder. Create it
first so the iteration yields zero entries on a clean tree.
…ssets

Two gaps in the extract→runtime pipeline meant fresh release tarballs
were missing whole asset subtrees (most user-visibly data_080D5360/),
which manifests as silent feature breakage: lily pads stationary, town
doors not spawning their cutscene entities, etc.

1. assets_extractor.hpp: extract_area_tables skipped property indices
   4..7 entirely because they're usually room callback functions. But
   some rooms (lily pads, doorway data tables) put data pointers there.
   Now treat them as data when the ROM offset matches an indexed asset
   entry, and add a final sweep that extracts every entry the index
   knows about which the specialized passes didn't already produce.

2. port_asset_pipeline.cpp: CopyRuntimePassthroughAssets had a fixed
   directory whitelist that excluded all data_* subtrees. Add the 12
   data_<addr>/ directories explicitly so BuildRuntimeAssets carries
   them from assets_src/ to assets/.

Verified by extracting from a clean tree: data_080D5360/ now contains
all 260 files byte-identical to the previously-curated dist/USA/assets/
tree.
Bumps version to 0.1.2 and documents the fixes that landed since 0.1.1:
- Doorway crash in HyruleField/LonLonRanch (HouseDoorExterior_Type3
  NULL-context segfault)
- Post-map-hint black BG (sub_0807C4F8 native-struct iteration +
  RestoreGameTask buffer→VRAM force-flush)
- BGM resets per room, prologue BGM mismatch, stray location textbox
  during Zelda call, magic-stump grow direction
- Asset extraction now produces data_*/ subtrees that release tarballs
  were silently missing — caused lily pads / town doors / etc. to break

Known issues section enumerates what's still deferred (rolling-barrel
affine-DMA, festival facades, doorway glitches, Minish Woods fog).
Reported in issue #2 (Kwagsyre, Kubuntu 25.10): user launched tmc_pc
via a custom dynamic loader so they could supply their own glibc 2.43
(\$HOME/glibc-2.43/lib/ld-linux-x86-64.so.2 ./tmc_pc). In that scenario
/proc/self/exe resolves to the dynamic loader, not tmc_pc, so the
asset-loader's exe-dir lookup hunts \$HOME/glibc-2.43/lib/assets/...
finds nothing, and the first gfx-group load aborts.

FindEditableAssetsRoot / FindRuntimeAssetsRoot now consult an ordered
list (exe-dir first, then cwd) so a working assets/ next to where the
user invoked tmc_pc still resolves. Filename-only ROM lookups already
fell back to cwd in port_rom.c.

Also: replace the cryptic "ROM fallback is disabled for gfx groups on
PC" abort message with a clear "run ./asset_extractor first" hint so
users who haven't extracted yet know what to do.
port_hdma_step_line conflated DEST_FIXED (don't increment, don't
reload) with DEST_RELOAD (do increment within each transfer, reload
between transfers). For 8×u16 affine-matrix HBlank-DMA used by the
rolling-barrel scene, this meant all 8 values overwrote BG2PA on each
scanline — only the last word stuck and BG2PB..Y_H stayed at whatever
the immediate-DMA prologue left them. Result: flat affine matrix per
scanline, brown-bands render in Deepwood Shrine rolling barrel.

Split DEST_FIXED / DEST_INC / DEST_RELOAD into three explicit modes:
- FIXED:  no increment, no reload
- INC:    increment within transfer, keep advanced address after
- RELOAD: increment within transfer, rewind to dest_orig after

Verified on the rolling-barrel scene: cylindrical interior renders
correctly. Applies to any scene that registers HBlank-DMA with
DEST_RELOAD on a multi-register window (BG2PA..Y_H, BG3PA..Y_H,
WIN0H..1V).
LoadGfxGroup entries that target GBA EWRAM addresses (e.g. gfx group 30
→ 0x02002F00 for gMapDataTopSpecial, used by Minish Paths to populate
the foreground vegetation BG3 tilemap) were going through MemCopy's
port_resolve_addr, which translates GBA EWRAM addresses to gEwram[]
slots. But the port keeps several large GBA-EWRAM globals as
heap-allocated stand-in arrays *outside* gEwram (gMapDataTopSpecial,
gMapDataBottomSpecial, gMapTop, gMapBottom). Result: bytes landed in
gEwram[0x2F00] but the game read from the heap-allocated symbol, which
stayed empty — so BG3 in Minish Village entrance had no tilemap data
and the green leafy vegetation didn't render.

Port_ResolveEwramPtr already knows about these special addresses (it's
used by LoadMapData / sub_0807C4F8). Route EWRAM-destined gfx-group
copies through it so they land in the actual symbols the game reads.

Reported in issue #3 (Minish Village entrance — Faulty Textures).
Two stacked bugs caused "You got X items!" textboxes (and rupee
counts, kinstone counts, minigame timers, every dialog with a number
variable) to render either zero or garbage on the port:

1. DecToHex (src/common.c): the GBA-original abuses Div (SWI 0x06)
   which returns the quotient in r0 and the remainder in r1 as a side
   effect, then reads the global register `r1` between successive Div
   calls to extract subsequent digits. On the port, Div is just plain
   `num / denom` with no register side effect, so `r1` stayed
   uninitialized — every digit past the most-significant was garbage.
   Replaced with a clean BCD encoder under PC_PORT.

2. gUnk_08107BE0 buf-source array (src/message.c): on GBA, gTextRender
   lives at EWRAM 0x02022780, so the variable-substitution buffer
   pointers gUnk_020227E8 / F0 / F8 / 0x02022800 happen to alias the
   rupees / field_0x14 / field_0x18 / field_0x1c byte-buffer slots
   inside gTextRender's _66/_77 fields (which sub_08056FBC populates).
   On the port, gTextRender is heap-allocated, so those raw GBA
   addresses point at unrelated gEwram[] bytes that nothing writes.
   Variable substitution read uninitialized bytes → no number
   displayed. Initialize the array at runtime to point inside
   gTextRender.

Reported in issue #7 (Deepwood Shrine — Game Reports 0 Mysterious
Shells Obtained).
Issue #10. The GBA-original keeps a 4-pointer table at ROM 0x080B2BD8
mapping each shadow type to sprite-frame data. The pointers stored in
that table are IWRAM addresses (0x0300xxxx) into the runtime-copied
overlay region, populated by InitOverlays via MemCopy(sub_080B197C,
ram_sub_080B197C, RAMFUNCS_END - sub_080B197C).

The PC port deliberately skips that IWRAM overlay copy (see
src/main.c InitOverlays under PC_PORT), so the IWRAM pointers in the
table point at uninitialized gIwram[] — every deferred shadow draw
bailed at `if (frameData == NULL) continue;` and characters had no
shadow underneath them.

Load the table from ROM on first use and translate each IWRAM
pointer to its ROM source via the linker-derived delta
(0x080B2248 ↔ 0x03005FBC → 0x050AC28C, USA region). Direct-ROM
pointer fallback for safety.

EU/JP may need a different delta — flagged with a TODO; testers
should retry on those builds.
Three issues bundled because the boss test paths overlap.

1. Drop shadows (issue #10): ProcessDeferredList bailed because
   sShadowFrameTable was never loaded — the GBA-original populates
   it via the IWRAM overlay copy that the PC port deliberately
   skips. Load the table directly from ROM at first use, with an
   IWRAM↔ROM offset translation derived from the linker symbol
   ram_sub_080B2248 (0x03005FBC ↔ 0x080B2248, USA region).

2. Deepwood boss heart container + warp not spawning (issue #12):
   gUnk_additional_a_DeepwoodShrineBoss_Main was a 64-byte
   port_linked_stubs.c stub left zeroed. The C code calls
   LoadRoomEntityList(&...) on it after the boss dies, so without
   real data the rewards never appeared. Copy the 48 bytes from
   ROM 0xDF94C in Port_InitDataStubs.

3. Deepwood barrel cobweb fall-through (player blocker reported by
   tester): GBA-original gates the fall on barrel angle in a 13-
   step window (unk_20 in 0x118..0x124), which is too tight for
   PC controls — testers hold Down for several seconds and never
   land the angle past the 0xF0 rest stop. Drop the angle gate on
   PC; once the cobweb (LV1TARU_OPEN) is removed and the player
   stands in the hole position with z=0, fall.
- Add explicit linking for fmt 12 (header-only mode was breaking scaninc, asset_processor, etc.)
- Copy baserom.gba into build/pc/ so runtime asset_extractor succeeds
- Tested on Kubuntu 25.04 (glibc 2.41)
WarpPointEntity::flag and HeartContainerEntity::flag2 sat at GBA-style
offset 0x86 from struct base, which on x86-64 lands at 0xAE — but
RegisterRoomEntity writes the spritePtr high-half to GenericEntity's
field_0x86 at 0xB2 (the void*-aligned union forces a 4-byte gap before
0x84). The flag fields therefore read junk, so warps never armed and
heart containers never spawned.

Insert 4-byte PC-only padding before the 0x84 fields so they line up
with what RegisterRoomEntity writes. Drop the FourElements port
workaround that double-spawned the rewards now that the original flow
works.

Fixes the boss-reward chain in 999sian#12 properly (cd99dd4 only
populated the data table).
The previous commit added per-struct PC padding to WarpPointEntity and
HeartContainerEntity to dodge a void*-alignment mismatch. Same bug
hits any entity subclass whose `flag` (or other) field sits at GBA
offset 0x84/0x86 — GentariCurtain (Minish Village elder, #14), button,
bossDoor, lockedDoor, lightDoor, jailBars, mask, fireplace, ~25 more.

Fix once at the source: in RegisterRoomEntity, after writing the
spritePtr halves to GenericEntity::cutsceneBeh/field_0x86 (PC 0xB0/2),
also mirror them to PC 0xAC/AE — the natural-aligned position used by
subclass structs without the void* trick. Bytes 0xAC-0xAF live in
GenericEntity's pre-union padding so cutscene entities are unaffected
(StartCutscene's later 8-byte scriptContext write at 0xB0 still wins).

Skip ENEMY because Enemy.child grew from 4→8 bytes on x86-64, which
already shifts subclass fields up by +4 — they land at 0xB2 and match
GE_FIELD natively.

Reverts the per-struct padding from 293a211 since this catches it
universally.
…gnment sweep

Bug-fix pass driven by the GitHub issue tracker. Closes #2, #3, #6, #7,
#10, #12, #14. Carries forward known-issues #4, #5, #8, #11, #13, #15
plus the still-open renderer items from 0.1.2 (rolling-barrel affine,
festival facades, fog, etc.) for retest.
Previous workflow checked out a private assets repo to copy baserom.gba
into the workspace and ran 'python build.py --usa' which extracts
assets at build time. Forks without ASSETS_REPO/ASSETS_TOKEN secrets
fail at the checkout step (which is why every release run from this
fork shows red).

Release tarballs only ship tmc_pc + asset_extractor + sounds.json — the
user runs asset_extractor against their own baserom on their own
machine. So the build doesn't actually need a ROM, just 'xmake build
tmc_pc' and 'xmake build asset_extractor'. Switch to that and drop the
private-assets steps.

sounds.json is tracked in repo at assets/sounds.json so it's available
to the runner without extraction.
The 'Build tmc_pc' step needed assets/map_offsets.h and assets/gfx_offsets.h
which are generated by ROM extraction. CI checkout doesn't run extraction
(no ROM), so the build always failed at compile time.

The two headers are stable per-region per-game-version (they're just ROM
offset constants for asset blobs), so commit them under a .gitignore
exception. EU is dropped from the matrix because we don't ship an EU
tarball or have an EU ROM in CI to regenerate its offsets.
fix: build on Ubuntu 25.04+ with system fmt 12.x
999sian and others added 29 commits May 7, 2026 22:03
Rolls back the partial-widescreen attempt that exposed cols 240..255
of the engine's 32-tile BG buffer. Cols 30..31 are a scroll buffer
that the engine updates only during camera motion — on title,
file-select, and cutscene-transition screens they hold stale tile
data, plus engine-parked off-screen sprites live at OAM x>=240, so
the right-edge strip showed visible glitches (stale palettes,
duplicate sprites).

Now: ViruaPPU clips BG and OAM at col 240 unconditionally, and
port_ppu.cpp uniformly stretches the 240-px frame across any
widescreen_width. Default widescreen_width=240 (clean GBA-native).

Real widescreen requires a 64-tile (sa2-style BGCNT_TXT512x256) BG
buffer and engine-side tile-load extension — Phase 2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Once the m4a buffer-sizing fix (7d36792) stopped the audio init from
crashing early at startup, the synth thread started running its full
workload — and the previously-set SINC resampler (~10x cost of LINEAR
per voice) became a measurable per-frame CPU hit on top of the
DSP post-process chain (biquad LP + mid/side widen + tanh clip).

Drop to LINEAR. Audible difference vs SINC on chiptune material at
48 kHz output is small; CPU saving is large. Easy to flip back if a
high-end audio pass is wanted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…SINC

Splits virtuappu_mode1_render_frame into two passes:

  1. Sequential — for each of 160 scanlines, fire the HDMA pre-line
     callback (BG_VOFS water FX, BLDY fades, etc. mutate the live
     IO register file) and snapshot post-callback IO state into a
     per-line array.

  2. Parallel via OpenMP — each thread points its thread-local
     `virtuappu_mode1_io_thread_override` at its line's snapshot,
     then renders BG / OBJ / composite normally. The single-threaded
     read16 path falls through when the override is NULL, so all
     non-render IO reads behave as before.

OpenMP was already wired into the build (`-fopenmp`, USE_OPENMP),
so this just lights up the cores. On a 4-core CPU the per-frame
PPU cost drops to roughly the line render / NUM_THREADS plus the
sequential snapshot pass (~1 µs/line) — Amdahl-bounded by the
synth thread and the snapshot pass, but still a visible win.

Also restores the agbplay resampler to SINC: the LINEAR drop earlier
in the session was a stopgap before the parallel-render path
existed; with PPU off the main thread the synth's CPU share is
no longer the bottleneck.

Patch infrastructure: viruappu-widescreen.patch absorbs the
parallel-render hunks (same file, overlapping context) and the
xmake marker is bumped to `io_thread_override` so it reapplies
cleanly on a fresh submodule reset.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nd flag 0x400

All eight takeover helpers (King Daltus, Minister Potho, Vaati,
Zelda Stone, and six guards) end their per-NPC scripts with the same
two-instruction tail:

  WaitForSyncFlag 0x00000400
  DoPostScriptAction 0x0006   ; delete entity / destroy script ctx

so the cutscene's exit relies on someone broadcasting sync flag 0x400.
Neither the parent orchestrator (script_CutsceneOrchestratorTakeover)
nor the in-castle child orchestrator
(script_CutsceneOrchestratorTakeoverCutscene) ever issues SetSyncFlag
0x400. On GBA this presumably came from an implicit broadcast tied to
the subtask transition that ends the cutscene; the C decomp's
post-action handler at HandlePostScriptActions case 1 << 0x06 (delete
entity) doesn't include any flag broadcast, and grep across the codebase
turned up no SetSyncFlag 0x400 in the takeover script set.

Caught with a [sync] tripwire diag on JesterWizard's bug-report save:
King(id=36)/Vaati(id=39 type=1)/Zelda(id=40) all spinning forever on
WaitForSyncFlag flag=0x400 cur=0x20.

Set it at the takeover subtask's end-of-cutscene gate (sub_08053BBC,
when CheckRoomFlag(0) sees the orchestrator's SetRoomFlag 0). Every
waiting helper falls through to DoPostScriptAction 0x06 and exits
cleanly. Scoped under PC_PORT so the GBA build behaviour is untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merge 7c10b08 (-X theirs) left an unbalanced #ifdef PC_PORT block:
both sides' PC_PORT branches got included with a stray #else between
them, leaving the outer guard unterminated. Drop the duplicate inner
PC_PORT block; keep the single-branch resolution that uses
gUnk_08128190.dest for the EWRAM-resolved scan.
A migration block introduced via the matheo merge converted any
config.json frame_time_ns == 16666667 (the canonical 60-FPS lock)
back to 0 (uncapped) at every startup, with a SaveConfig() so the
overwrite persisted. Users who picked 60 FPS via the in-game port-
settings menu had it silently undone the next launch — visible as
the window-title FPS counter sitting well above 60 even with the
preset at "60 FPS lock", and as a perceived fast-forward feel.

Drop the migration. If users want uncapped, they can pick that in
the port-settings menu; the deadline-based pacer in port_bios.c
already honours frame_time_ns=0 as "no cap".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eo merge

The matheo "Sync PC port from 999sian release branch" (646762e) brought
stale snapshots of port_debug_menu.cpp, port_ppu.cpp/.h, port_bios.c,
port_linked_stubs.c, and data_stubs_autogen.c — the 3-way merge in
7c10b08 silently dropped fixes/integrations that were already on master.
Audit found 7 things missing in the post-merge tree:

* zeldaret#54 boomerang dizzy-stars FX never clears (b9ce2fb):
  GetNextFunction's alive-dispatch lost the `confusedTime != 0 -> 4`
  branch, so GenericConfused stopped running on stunned enemies, the
  FX_STARS parent stayed alive, and stars persisted forever.

* 14 per-room entity-list stubs (1212584):
  Re-add the PerRoomEntityListInit entries for TempleOfDroplets BigOcto,
  PalaceOfWinds GyorgTornado, Romio's Purple House, Borlov entrance,
  Percy, HouseInteriors1 Library, HyruleCastle, CoF boss, etc. These
  are LoadRoomEntityList sources that crash when read from zeroed BSS
  (covers reported issues zeldaret#55, zeldaret#64, zeldaret#68, zeldaret#70).

* F8 debug-menu pages (077fed9, 668a936):
  Restore "Extra equip slots" cycle page (X/Y/L2/R2 assignments) and
  the "CRT filter" cycle entry under Display settings. Rename the
  upscale-mode entry from "Filter" to "Upscale" to disambiguate.

* port_ppu.cpp present-pipeline losses:
  - Port_Filter_Apply call in raw/linear branches + auto-bump internal
    scale to >=4x when filter is active (CRT pattern needs >=3 px per
    phosphor cell to read).
  - virtuappu_mode1_render_affine_obj_overlay call in BuildScaledFrame
    so affine OAM (Vaati tornado, world-shrink, every spinning enemy)
    keeps sub-pixel accuracy at internal-render-scale.
  - Port_SoftSlots_RenderOverlay call after Port_DebugMenu_Render so
    the `\` config overlay during pause actually draws.
  - MODE1_GBA_WIDTH/HEIGHT consistency in BuildScaledFrame and
    EnsureScaledTexture (preserves widescreen Phase 1).

* port_ppu.h: re-export Port_PPU_CycleFilter / Port_PPU_FilterName.

* port_bios.c: also drop GBA input + tick the soft-slot pause counter
  while the soft-slot config overlay is open, not just for the F8 menu.

Verified all other named fixes (#5, #22, #24, #25, zeldaret#34-36, zeldaret#39, zeldaret#42/43,
zeldaret#46, zeldaret#51-53, zeldaret#57, zeldaret#61, zeldaret#65, zeldaret#67, zeldaret#69, zeldaret#72, zeldaret#76, zeldaret#78, zeldaret#91, zeldaret#93, plus
WarpPoint/HC alignment via the universal spritePtr mirror, talon
wrong-address, gleerok packed pointers, LikeLike/rupee-like soft-locks,
Vaati apparate, FastTravel, GetNextFunction h=0 dispatch) are still
present and were not regressed by the merge.

Skipped: stderr-ring buffer in bug-report bundles (354324f) and
addr2line shell-out (8739b84) — pure diagnostic features, not bug
fixes; can be restored in a follow-up if wanted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diagnostic fprintf hooks in ScriptCommand_SetSyncFlag /
ScriptCommand_ClearSyncFlag / ScriptCommand_WaitForSyncFlag /
ScriptCommand_WaitForSyncFlagAndClear (PC_PORT only) emit the flag
mask, current syncFlags, and the (kind, id, type) of the entity
running the script. WAIT/WAIT&CLR de-dup consecutive identical
log lines so a stuck cutscene doesn't spam stderr.

Used to chase the zeldaret#93 Vaati takeover softlock (already fixed in
e2427e6); leaving the tripwires in for the next time we hit a
cutscene-end gate that never opens.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ment watchdog

The earlier zeldaret#93 fix (e2427e6) just broadcast sync flag 0x400 from the
takeover-overlay end gate (sub_08053BBC). That helped the helpers
stuck on `WaitForSyncFlag 0x400` *if* the cutscene ever reached that
gate, but on JesterWizard's bug-report save the cutscene never gets
that far — it stalls almost immediately after the player triggers it.

Diagnosis. script_CutsceneOrchestratorTakeoverCutscene runs as a
CUTSCENE_ORCHESTRATOR object inside the takeover Aux-Cutscene
subtask. Its job is to broadcast a sequenced wave of sync flags
(0x010, 0x004, 0x010, ..., 0x040, 0x001, 0x004, 0x200, 0x004, then
SetRoomFlag 0) waking King / Vaati / Zelda / Minister / Guards in
order. On the PC port this entity disappears after running its first
two Wait commands (heartbeat hooks confirm the entity is no longer
called by ObjectUpdate; my UnlinkEntity hook never fires for it; the
post-stall snapshot shows it absent from gEntityLists). Cause is
still unclear — neither the priority-elevation done in the parent
takeover script (`Call sub_0807FBC4` -> RequestPriorityOverPlayer)
nor a DeleteEntity / DeleteAllEntities path explains the state we
see. Investigation continues.

Workaround. sub_08053BBC (the takeover overlay-2 dispatcher) now
runs a small state machine that broadcasts the same flag sequence
the orchestrator script would, paced ~30-60 frames per step so the
helpers' inter-step animations have time to play. Once the sequence
finishes, it sets RoomFlag 0 itself; the existing branch then
broadcasts 0x400 (belt-and-suspenders for any helper that reached
its final WaitForSyncFlag 0x400 before the watchdog got there) and
exits the subtask. Verified on JesterWizard's save: cutscene
completes, player ends up in Hyrule Field post-takeover with the
correct local flag (SOUGEN_08_TORITSUKI) set.

Side effect: the cutscene's between-step camera moves race a bit
faster than they would on hardware (~12s total vs. ~20s). That's a
visible cosmetic regression vs. the GBA, but it's miles better than
hanging forever. If the orchestrator entity ever stops disappearing,
the watchdog stays harmless — it only kicks in while CheckRoomFlag(0)
is still false, which is the same gate the original cutscene gates
on.

Also adds a NULL-pointer guard in CleanUpObjPalettes
(src/color.c:308) for the iteration that walks
gEntityLists[i].first->next->next->...; multiple helpers
self-deleting in the same frame can leave the list with a stale node
whose `next` is NULL before ClearAllDeletedEntities runs, and
dereferencing it segfaults at offset 0x10 (the kind field). The
guard skips the corrupted node; cleanup for the rest of the list
finishes normally on the next frame.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…dio flag

Two follow-ups to the orchestrator-replacement watchdog from c5dca19:

* Halve the per-step delays (60 -> 30 frames, 30 -> 15) so the cutscene
  finishes in ~6s instead of ~12s. The user testing reports were
  cutting out before the 12s sequence completed; 6s is enough time for
  helpers to unwind through their WAIT&CLR / SET pairs at PC speed
  while not keeping the user staring at a half-frozen screen.

  Known cosmetic issue not addressed here: the cutscene's camera moves
  and fades are driven by the orchestrator entity itself, not by the
  helpers — and that entity is the one that disappears, so the visuals
  during the sequence are glitchy and at one of the orchestrator's
  internal `SetFade5` (fade-to-black) gates the screen stays black
  until the watchdog forces RoomFlag 0. The cutscene COMPLETES
  (subtask exits, player ends in Hyrule Field with the right local
  flag set) but it doesn't *play* properly. Restoring proper visuals
  needs the underlying orchestrator-disappearance bug fixed.

* Restore `--no-audio` CLI flag handling that was already declared in
  port_main.c's argv loop but disconnected from the audio init call
  (probably lost in the matheo merge). Useful as a workaround for the
  pre-existing intermittent agbplay SequenceReader.cpp:49 SIGSEGV
  while testing other things.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Getting the sword from the Blacksmith plays the proper animation, shrink/grow is completely fixed, and the shield from Zelda being given from Hyrule no longer floats where Link once stood. Also, added debug scaffolding to try to fix a pre-existing cloud deform bug.
Hardcoded files, git + auth path, and other conflicts have been resolved so universal compilation is successful
- Removed the F9 key bug report capture functionality from the BIOS.
- Deleted the port_bugreport.cpp and port_bugreport_state.c files as they are no longer needed.
- Introduced Port_IsAreaTablePtrFromAssets function to validate area table pointers from assets.
- Updated Port_IsAreaTablePtrReadable to utilize the new validation function.
- Adjusted room property list retrieval to ensure valid area table pointers.
- Cleaned up script execution context checks to remove redundant corruption checks.
- Updated xmake.lua to remove references to the deleted bug report files and adjust build flags accordingly.
Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	libs/tmc-Android-Experimental
#	port/port_m4a_backend.cpp
#	xmake.lua
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- Introduced touch control handling in `port_touch_controls.cpp` with support for joystick and D-pad schemes.
- Added functions to manage touch events, rendering, and settings requests.
- Implemented a bootstrap launcher in `port_launcher_bootstrap.cpp` to initialize the launcher UI.
- Updated `port_runtime_config.h` to include new touch scheme configurations and related functions.
- Modified `xmake.lua` to include the new `guilite` dependency and launcher source files.
- Enhanced the touch control rendering logic to accommodate different input schemes and visual feedback.
@MatheoVignaud
MatheoVignaud merged commit b0f7109 into CI-Test May 16, 2026
2 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants