From 16b6b98ede75c167359a5a3e003c01f6ac43597f Mon Sep 17 00:00:00 2001 From: Andrew Quenehen Date: Mon, 3 Aug 2026 15:26:37 -0300 Subject: [PATCH] fix(switch): pass NX cache prefix to the chip-audio worker Yellow music was still silent because the background worker thread loads ChipSynth.lua in a fresh Lua state with no GameVersion/Platform context. The main thread's prefix never reached it. ChipAudio.slimAudio now resolves the versioned cache prefix on the main thread and includes it in the audio payload as `programPrefix`. ChipSynth.loadBanks prefers `audio.programPrefix` when present, falling back to its own NX detection for the sync path. Blue and Yellow are handled the same way. Tests cover the worker prefix hand-off and Blue's programs.bin path. Co-authored-by: Cursor --- src/core/ChipAudio.lua | 13 +++++++ src/core/ChipSynth.lua | 17 +++++---- tests/engine/assets_version_fallback_test.lua | 36 +++++++++++++++++++ 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/core/ChipAudio.lua b/src/core/ChipAudio.lua index 91bcea8b..520961ed 100644 --- a/src/core/ChipAudio.lua +++ b/src/core/ChipAudio.lua @@ -100,14 +100,27 @@ end -- play so a hot-reloaded dataset (or a mod's audio) always reaches the worker local function slimAudio(data) local audio = data.audio or {} + -- NX-only: resolve the versioned cache prefix on the main thread and hand + -- it to the worker, which runs in a fresh Lua state without GameVersion. + local programPrefix + if require("src.core.Platform").isNX() then + local prefix = require("src.core.GameVersion").cachePrefix() + if prefix ~= "" then programPrefix = prefix end + end return { programFile = audio.programFile, + programPrefix = programPrefix, bankOrder = audio.bankOrder, waveBanks = audio.waveBanks, noiseHeaders = audio.noiseHeaders, } end +-- test-only: expose slimAudio so the NX prefix hand-off is verifiable +function ChipAudio._slimAudioForTest(data) + return slimAudio(data) +end + -- If the worker died (a malformed def that errors mid-synth), fall back to the -- synchronous path for the rest of the session instead of going silent. local function workerAlive() diff --git a/src/core/ChipSynth.lua b/src/core/ChipSynth.lua index 1115cc36..0eceaabb 100644 --- a/src/core/ChipSynth.lua +++ b/src/core/ChipSynth.lua @@ -124,13 +124,16 @@ local function loadBanks(data) return cachedBanks end local raw, readError - -- NX-only: Blue/Yellow live under a versioned save-dir prefix; desktop - -- relies on mountVersion overlay. Read the prefixed path when present. - if require("src.core.Platform").isNX() then - local prefix = require("src.core.GameVersion").cachePrefix() - if prefix ~= "" then - raw, readError = love.filesystem.read(prefix .. audio.programFile) - end + -- NX-only: Blue/Yellow live under a versioned save-dir prefix. The main + -- thread resolves it before sending audio to the worker; the sync path + -- resolves it here so desktop keeps the mountVersion overlay behavior. + local prefix = audio.programPrefix + if not prefix and require("src.core.Platform").isNX() then + local gv = require("src.core.GameVersion").cachePrefix() + if gv ~= "" then prefix = gv end + end + if prefix and prefix ~= "" then + raw, readError = love.filesystem.read(prefix .. audio.programFile) end if not raw then raw, readError = love.filesystem.read(audio.programFile) diff --git a/tests/engine/assets_version_fallback_test.lua b/tests/engine/assets_version_fallback_test.lua index 6f777205..acab7914 100644 --- a/tests/engine/assets_version_fallback_test.lua +++ b/tests/engine/assets_version_fallback_test.lua @@ -111,6 +111,42 @@ if okB and banks then eq(banks[1], PROG_BYTES:sub(1, 0x4000), "loadBanks returns the bank 1 bytes") end +-- ChipSynth honors an explicit programPrefix (worker path; worker has no +-- GameVersion state, so the prefix must arrive via the audio payload) +ChipSynth.invalidateBanks() +local workerData = { audio = { + programFile = PROG, + programPrefix = "yellow/", + bankOrder = { 1, 2 }, +} } +local okW, wbanks = pcall(ChipSynth._loadBanksForTest, workerData) +check(okW and wbanks ~= nil, "loadBanks uses audio.programPrefix when set") +if okW and wbanks then + eq(wbanks[1], PROG_BYTES:sub(1, 0x4000), + "programPrefix loads the same bank 1 bytes") +end + +-- Blue gets the same treatment +ChipSynth.invalidateBanks() +GameVersion.set("blue") +love.filesystem.write("blue/" .. PROG, PROG_BYTES) +clearPath("yellow/" .. PROG) +local okBl, bbanks = pcall(ChipSynth._loadBanksForTest, progData) +check(okBl and bbanks ~= nil, "NX loadBanks reads blue/programs.bin") +clearPath("blue/" .. PROG) +GameVersion.set("yellow") +love.filesystem.write("yellow/" .. PROG, PROG_BYTES) + +-- ChipAudio.slimAudio hands the NX prefix to the worker +local ChipAudio = require("src.core.ChipAudio") +local slim = ChipAudio._slimAudioForTest + and ChipAudio._slimAudioForTest(progData) + or nil +if slim then + eq(slim.programPrefix, "yellow/", + "slimAudio passes the NX cache prefix to the worker") +end + -- Sound.playPikaCry: NX rewrites the pika-cry path before newSource setOS("NX") GameVersion.set("yellow")