fix(switch): write nx-asset-probe.log on every NX Play

Capture resolve paths and newImage open results for Yellow/Blue art
triage without enabling switch-debug.txt.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Andrew Quenehen
2026-08-03 15:01:58 -03:00
parent f944507520
commit f099593136
4 changed files with 129 additions and 0 deletions
+2
View File
@@ -432,6 +432,8 @@ Community mod zip install smoke (MODS inbox + Play): NXMOD-12 in [switch-hardwar
**Opt-in diagnostics:** create an empty `switch-debug.txt` in the save directory; events flush to `switch.log` at ≤1 Hz with build identity (no ROM/save bytes).
**NX asset probe (always on Play):** every Switch Play writes `nx-asset-probe.log` in the save directory (`pokemon-love2d/`). It lists whether `assets/generated/…` vs `yellow|blue/assets/generated/…` exist, what `Assets.resolve` returns, and whether `newImage` / `newImageData` open — for Yellow/Blue blank-sprite triage. No ROM bytes.
**Hardware re-test:** T16 **pass** @ `2699c9a` (naming A=confirm / B=cancel). T19 **pass** (quit/reopen, suspend×10, reboot) — operator 2026-08-01.
**Suspend/resume audio:** after resume, chip music is stopped to avoid duplicate streams; confirm on hardware during P0-09/10 (T19).
+5
View File
@@ -188,6 +188,11 @@ local function bootGame(version)
-- (Blue/Yellow caches live under blue/ / yellow/).
CacheFs.prefix = GameVersion.cachePrefix()
CacheFs.mountVersion(GameVersion.get())
-- NX: always write nx-asset-probe.log so Yellow/Blue art failures are
-- diagnosable from the SD without enabling switch-debug.txt.
pcall(function()
require("src.debug.SwitchDiagnostics").probeAssets(GameVersion.get())
end)
if love.window and love.window.setTitle then
local Version = require("src.core.Version")
love.window.setTitle(Version.title(
+87
View File
@@ -171,4 +171,91 @@ function SwitchDiagnostics.maybeFlush(force, now)
filesystem.write(LOG_FILE, table.concat(lines, "\n") .. "\n")
end
-- One-shot NX asset probe written on every Play. No ROM/save bytes — only
-- paths, sizes, resolve results, and whether newImage/newImageData open.
-- Pull sdmc:.../pokemon-love2d/nx-asset-probe.log after a Yellow boot.
local PROBE_LOG = "nx-asset-probe.log"
local function probeInfo(filesystem, path)
local info = filesystem.getInfo(path)
if not info then return "missing" end
local size = info.size
if size == nil then
local bytes = filesystem.read(path)
size = type(bytes) == "string" and #bytes or -1
end
return ("type=%s size=%s"):format(tostring(info.type), tostring(size))
end
local function probeOpen(kind, path)
if kind == "image" then
local ok, err = pcall(love.graphics.newImage, path)
return ok and "ok" or ("FAIL " .. tostring(err):gsub("%s+", " "):sub(1, 160))
end
if not (love.image and love.image.newImageData) then return "skip-no-imageData" end
local ok, err = pcall(love.image.newImageData, path)
return ok and "ok" or ("FAIL " .. tostring(err):gsub("%s+", " "):sub(1, 160))
end
function SwitchDiagnostics.probeAssets(version)
local Platform = require("src.core.Platform")
if not Platform.isNX() then return end
local filesystem = fs()
if not filesystem then return end
local GameVersion = require("src.core.GameVersion")
local Assets = require("src.render.Assets")
local prefix = GameVersion.cachePrefix(version or GameVersion.get())
local lines = {
SwitchDiagnostics.identityOverlay(),
"probe=nx-asset",
"version=" .. tostring(version or GameVersion.get()),
"cachePrefix=" .. tostring(prefix),
"isNX=" .. tostring(Platform.isNX()),
"saveDir=" .. tostring(filesystem.getSaveDirectory and filesystem.getSaveDirectory() or "?"),
}
local samples = {
"assets/generated/fonts/font.png",
"assets/generated/tilesets/reds_house.png",
"assets/generated/sprites/red.png",
"assets/generated/sprites/monster.png",
}
for _, path in ipairs(samples) do
local versioned = prefix ~= "" and (prefix .. path) or path
local resolved = Assets.resolve(path)
lines[#lines + 1] = ("--- %s"):format(path)
lines[#lines + 1] = "unprefixed=" .. probeInfo(filesystem, path)
if prefix ~= "" then
lines[#lines + 1] = "versioned=" .. probeInfo(filesystem, versioned)
end
lines[#lines + 1] = "resolve=" .. tostring(resolved)
lines[#lines + 1] = "newImage=" .. probeOpen("image", resolved)
lines[#lines + 1] = "newImageData=" .. probeOpen("imageData", resolved)
if prefix ~= "" and resolved ~= versioned then
lines[#lines + 1] = "newImage_versioned=" .. probeOpen("image", versioned)
lines[#lines + 1] = "newImageData_versioned=" .. probeOpen("imageData", versioned)
end
end
-- Shallow listing so we can see if the extract tree exists at all.
local roots = { "yellow", "blue", "assets", "yellow/assets/generated",
"yellow/assets/generated/sprites", "blue/assets/generated/sprites" }
for _, dir in ipairs(roots) do
local info = filesystem.getInfo(dir)
if info and info.type == "directory" and filesystem.getDirectoryItems then
local items = filesystem.getDirectoryItems(dir) or {}
local n = math.min(8, #items)
local head = {}
for i = 1, n do head[i] = items[i] end
lines[#lines + 1] = ("list %s count=%d head=%s"):format(
dir, #items, table.concat(head, ","))
else
lines[#lines + 1] = ("list %s %s"):format(dir, info and info.type or "missing")
end
end
filesystem.write(PROBE_LOG, table.concat(lines, "\n") .. "\n")
end
return SwitchDiagnostics
+35
View File
@@ -70,4 +70,39 @@ check(errLog:find("missing module", 1, true) ~= nil,
check(errLog:find("Import again", 1, true) ~= nil,
"lua-error.log preserves lines after newline")
-- NX asset probe: always writes nx-asset-probe.log on Play (Switch only).
local Platform = require("src.core.Platform")
local GameVersion = require("src.core.GameVersion")
local savedSystem = love.system
love.system = { getOS = function() return "NX" end }
Platform._resetForTests()
GameVersion.set("yellow")
love.filesystem.write("yellow/assets/generated/fonts/font.png", "font-bytes")
love.filesystem.write("yellow/assets/generated/tilesets/reds_house.png", "house-bytes")
love.filesystem.write("yellow/assets/generated/sprites/red.png", "red-bytes")
SwitchDiagnostics.probeAssets("yellow")
local probe = love.filesystem.read("nx-asset-probe.log") or ""
check(probe:find("probe=nx-asset", 1, true) ~= nil, "probe log writes header")
check(probe:find("cachePrefix=yellow/", 1, true) ~= nil, "probe records yellow prefix")
check(probe:find("resolve=yellow/assets/generated/fonts/font.png", 1, true) ~= nil
or probe:find("versioned=type=file", 1, true) ~= nil,
"probe records versioned font path visibility")
check(not probe:find(string.char(0xEA, 0x9B), 1, true),
"probe log contains no ROM-like binary")
love.system = { getOS = function() return "OS X" end }
Platform._resetForTests()
love.filesystem.remove("nx-asset-probe.log")
SwitchDiagnostics.probeAssets("yellow")
check(love.filesystem.read("nx-asset-probe.log") == nil,
"probe is a no-op off NX")
love.system = savedSystem
Platform._resetForTests()
GameVersion.set("red")
love.filesystem.remove("nx-asset-probe.log")
love.filesystem.remove("yellow/assets/generated/fonts/font.png")
love.filesystem.remove("yellow/assets/generated/tilesets/reds_house.png")
love.filesystem.remove("yellow/assets/generated/sprites/red.png")
T.finish()