* Fix yaml-cpp inclusion for VS building.
* Fix double-finds for rom files in the binary directory.
* Fix archive generation targets.
* Port over Proxy's change on 2ship to old ROM deletion to search all applicable subdirectories.
A stored value with no entry in the combo map threw out of map::at while
drawing, which took the whole menu down as soon as a search matched the widget.
Fall back to the default and log the offender.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
The source globs did not use CONFIGURE_DEPENDS, so adding a file under
soh/ or src/ left it out of the build until cmake was re-run by hand,
silently and with no warning.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
fmt::format returns a std::string by value, so taking c_str() of the temporary
left BeginCombo reading freed memory whenever the melee weapon state fell
outside the known cases. Hold the text in a std::string instead.
Use spdlog::fmt_lib in the save editor
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Root cause of three identical field crashes (audio thread, opcode fetch
through a pointer with its low 32 bits overwritten, seconds after scene
transitions): AudioHeap_AllocPermanent writes permanentCache[index] with
index = permanentPool.count and no bound against the 32-entry array. In
SoH every soundfont sync-load is forced permanent, and custom sequences
whose SEQ.xml says CachePolicy="Temporary" ALSO allocate permanently
(the factory stores the LUS enum where CACHE_TEMPORARY == 0, while
AudioLoad_SyncLoad's switch reads 0 with the ROM convention
'permanent'). A pack with ~60 streamed customs plus vanilla fonts pushes
count past 32 within a session, after which each allocation sprays a
{ptr, size, tableType/id} triplet at 24-byte stride through
gAudioContext - entry[135]'s ptr field lands exactly on
seqPlayers[0].scriptState.pc and entry[156] on seqPlayers[1]'s (both
verified against the crash-dump registers).
- permanentCache raised 32 -> 512 (12 KB) and AllocPermanent refuses
allocations past the array instead of corrupting memory.
- Same unbounded-index disease fixed in the three sibling writers:
AllocCached's persistent path (16-entry array; CACHE_EITHER degrades
to temporary, hard persistent requests fail cleanly),
AllocPersistentSampleCacheEntry, AllocTemporarySampleCacheEntry.
- seqLoadStatus malloc sized for the full id space (sequenceMapSize +
0xF) matching sequenceMap; custom ids above sequenceMapSize previously
overflowed the allocation by up to 15 bytes.
Upstream SoH bugs, not branch-introduced - this branch's many-track
packs merely made the overflow reachable in normal play. Standalone
upstreamable fix.
gItemSlots and sExtraItemBases only cover the item IDs that live in the
inventory, but Item_Give and Item_CheckObtainability index them with any
item ID. Reading past gItemSlots lands on gUpgradeShifts, so
INV_CONTENT(ITEM_SKULL_TOKEN) resolved to the Bow slot and stored the
token there, permanently corrupting the save.
Route both through a bounds-checked Item_GetSlot that reports SLOT_NONE
for items that have no slot, and skip the inventory store in that case.
That read is also undefined behaviour, which link-time optimization is
free to exploit: with it present clang dropped the ITEM_SKULL_TOKEN
branch and the ITEM_MEDALLION_WATER horse fixup from Item_Give
entirely, so tokens were never counted either.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Custom SAF sequence IDs can exceed sequenceMapSize, causing out-of-bounds
reads/writes on the seqLoadStatus byte array (sized exactly sequenceMapSize).
This is the direct cause of intermittent battle music failure with BGM packs
(issue #5706): on the second encounter, AudioLoad_SyncLoadSeq reads a garbage
value from heap memory past seqLoadStatus[] and may find 1 (loading in
progress), causing it to return NULL early — the sequence player is never
initialized and no battle music plays.
Symmetric fix to the fontLoadStatus guards in PR #6916:
- AudioLoad_IsSeqLoadComplete: return true for OOB seqIds (custom SAF
sequences are resource-manager-backed, not in the async-load status table)
- AudioLoad_SetSeqLoadStatus: skip update for seqId >= sequenceMapSize
- AudioLoad_SyncLoadSeq: skip the in-progress check for OOB seqIds
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Using hover recoils, Adult can reach the symphony room lock without having access to Statue Room and thus the shared area of MQ spirit. This means that child logically needs an extra key to pass their early locks and get shared checks as adult can waste it, but it was not reflected in the current logic.
This change fixes the issue, requiring an extra key for the first 2 child locks while both hover boost tricks are on unless either the grate in 3 suns room (which gives a hover sliding adult statue room access) is both open and climbable, or Adult already has reverse access to the dungeon, either of which giving the shared key logic assumed Statue Room access.
The Save Editor's Inventory tab writes gSaveContext.inventory.items[]
directly, but a C/D-pad item button mirrors the slot it points at
(buttonItems[i] == items[cButtonSlots[i-1]]). That derived value was
never refreshed, so e.g. emptying a bottle assigned to a button left the
button showing its old contents -- a desync the game's equip/inventory
consistency then trips on at the next age swap or bottle update.
Re-sync any button pointing at an edited slot after the write. Equipment
assigned to a button stores an equipment-page index in cButtonSlots
(>= inventory size), so it never matches an inventory slot and is left
untouched.
Includes a default-on "Keep C/D-pad buttons in sync" checkbox
(mirroring "Restrict to valid items") so inventory edits can intentionally
leave a slot desynced from its button for RBA practice/debugging.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>