diff --git a/screen.js b/screen.js index bd9025c..f8f123b 100644 --- a/screen.js +++ b/screen.js @@ -264,14 +264,28 @@ function _ndSyncMlGate(token, wantsMl, audio) { } catch (_) { /* downlevel / transient — leave state so a later sync retries */ } } +// Lowercase — compared against the lowercased filename. Core renamed the +// seeded pack to feedBack-… (#900); keep the legacy name for older libraries. const _ND_DIAGNOSTIC_FILENAME_MARKERS = [ + 'feedback-diagnostic-basic-guitar.sloppak', 'slopsmith-diagnostic-basic-guitar.sloppak', ]; +// True when a lowercased (already URI-decoded) filename's basename is exactly +// one of the track's pack names. filenameIncludes is a string or a list. +function _ndTrackMatchesFilename(track, lowerFn) { + if (!track || !track.filenameIncludes || !lowerFn) return false; + const base = String(lowerFn).split(/[\\/]/).pop(); + return [].concat(track.filenameIncludes) + .some((m) => base === String(m).toLowerCase()); +} + +// Same exact-basename rule as the catalog match, so the playSong wrapper and +// _getDiagnosticTrackForSession agree on what counts as the diagnostic pack. function _ndFilenameLooksDiagnostic(fn) { - const lower = String(fn || '').toLowerCase(); - if (!lower) return false; - return _ND_DIAGNOSTIC_FILENAME_MARKERS.some((m) => lower.includes(m)); + return _ndTrackMatchesFilename( + { filenameIncludes: _ND_DIAGNOSTIC_FILENAME_MARKERS }, + String(fn || '').toLowerCase()); } // Escape untrusted text before interpolating into an innerHTML string. @@ -16777,11 +16791,13 @@ function createNoteDetector(options = {}) { const _DIAGNOSTIC_TRACK_CATALOG = [ { id: 'basic-guitar-6', - title: 'Slopsmith Diagnostic — Basic Guitar', - artist: 'Slopsmith', + // Core seeds this as feedBack-diagnostic-… (#900; formerly + // slopsmith-diagnostic-…). Match exactly those two pack names. + title: 'FeedBack Diagnostic — Basic Guitar', + artist: 'FeedBack', arrangement: 'Diagnostic Guitar', - filenameIncludes: 'slopsmith-diagnostic-basic-guitar.sloppak', - dlcRelativePath: 'diagnostics-builtin/slopsmith-diagnostic-basic-guitar.sloppak', + filenameIncludes: _ND_DIAGNOSTIC_FILENAME_MARKERS, + dlcRelativePath: 'diagnostics-builtin/feedBack-diagnostic-basic-guitar.sloppak', instrument: 'guitar', stringCount: 6, reportProfile: 'basic-guitar-v1', @@ -16834,8 +16850,7 @@ function createNoteDetector(options = {}) { if (!diagFn) return; const prevLower = prevFn.toLowerCase(); if (prevLower === diagFn.toLowerCase()) return; - if (track.filenameIncludes - && prevLower.includes(String(track.filenameIncludes).toLowerCase())) { + if (_ndTrackMatchesFilename(track, prevLower)) { return; } const hw = resolveHw(); @@ -16999,8 +17014,7 @@ function createNoteDetector(options = {}) { const currentHw = resolveHw(); const info = (currentHw && currentHw.getSongInfo) ? currentHw.getSongInfo() : {}; for (const track of _DIAGNOSTIC_TRACK_CATALOG) { - if (track.filenameIncludes - && fn.includes(String(track.filenameIncludes).toLowerCase())) { + if (_ndTrackMatchesFilename(track, fn)) { return track; } } diff --git a/test/_loader.js b/test/_loader.js index 660c9f3..090bc6e 100644 --- a/test/_loader.js +++ b/test/_loader.js @@ -253,6 +253,8 @@ function loadDetectionCore({ sandboxBeforeRun } = {}) { // do. Expose the raw functions; tests pass state in directly. return { yinDetect: rewrapYin(sandbox._ndYinDetect), + filenameLooksDiagnostic: sandbox._ndFilenameLooksDiagnostic, + trackMatchesFilename: sandbox._ndTrackMatchesFilename, hpsDetect: rewrapYin(sandbox._ndHpsDetect), freqToMidi: sandbox._ndFreqToMidi, midiFromStringFret: midiFromStringFretWrapped, diff --git a/test/diagnostic_filename.test.js b/test/diagnostic_filename.test.js new file mode 100644 index 0000000..4d76ece --- /dev/null +++ b/test/diagnostic_filename.test.js @@ -0,0 +1,56 @@ +// The built-in calibration pack was renamed in core from +// slopsmith-diagnostic-basic-guitar.sloppak to feedBack-diagnostic-… (core +// #900). Both names must still be recognised as the diagnostic track, with the +// case-insensitive match the library's filenames need. +const { test } = require('node:test'); +const assert = require('node:assert'); +const fs = require('node:fs'); +const path = require('node:path'); +const { loadDetectionCore } = require('./_loader'); + +const { filenameLooksDiagnostic, trackMatchesFilename } = loadDetectionCore(); + +test('current core name (feedBack-…) is a diagnostic track', () => { + assert.strictEqual( + filenameLooksDiagnostic('diagnostics-builtin/feedBack-diagnostic-basic-guitar.sloppak'), true); +}); + +test('legacy name (slopsmith-…) is still a diagnostic track', () => { + assert.strictEqual( + filenameLooksDiagnostic('diagnostics-builtin/slopsmith-diagnostic-basic-guitar.sloppak'), true); +}); + +test('ordinary songs are not diagnostic tracks', () => { + assert.strictEqual(filenameLooksDiagnostic('Uriah Heep - Shadows Of Grief.feedpak'), false); + assert.strictEqual(filenameLooksDiagnostic(''), false); + assert.strictEqual(filenameLooksDiagnostic(null), false); + // Look-alikes are not the diagnostic pack (same rule as the catalog match). + assert.strictEqual(filenameLooksDiagnostic('my-feedBack-diagnostic-basic-guitar.sloppak'), false); + assert.strictEqual(filenameLooksDiagnostic('feedBack-diagnostic-basic-guitar.sloppak.bak'), false); +}); + +test('the launcher points at the pack core seeds today', () => { + const src = fs.readFileSync(path.join(__dirname, '..', 'screen.js'), 'utf8'); + assert.match(src, + /dlcRelativePath: 'diagnostics-builtin\/feedBack-diagnostic-basic-guitar\.sloppak'/); +}); + +test('the catalog matches only the two real pack names, not any similar suffix', () => { + const track = { filenameIncludes: ['feedback-diagnostic-basic-guitar.sloppak', + 'slopsmith-diagnostic-basic-guitar.sloppak'] }; + assert.strictEqual(trackMatchesFilename(track, + 'diagnostics-builtin/feedback-diagnostic-basic-guitar.sloppak'), true); + assert.strictEqual(trackMatchesFilename(track, + 'diagnostics-builtin/slopsmith-diagnostic-basic-guitar.sloppak'), true); + assert.strictEqual(trackMatchesFilename(track, 'my-own-diagnostic-basic-guitar.sloppak'), false); + assert.strictEqual(trackMatchesFilename(track, 'my-feedback-diagnostic-basic-guitar.sloppak'), false); + assert.strictEqual(trackMatchesFilename(track, 'feedback-diagnostic-basic-guitar.sloppak'), true); + assert.strictEqual(trackMatchesFilename(track, 'sub\\dir\\slopsmith-diagnostic-basic-guitar.sloppak'), true); + assert.strictEqual(trackMatchesFilename(track, ''), false); + assert.strictEqual(trackMatchesFilename({ filenameIncludes: 'x.sloppak' }, 'a/x.sloppak'), true); +}); + +test('the catalog entry reuses the two filename markers', () => { + const src = fs.readFileSync(path.join(__dirname, '..', 'screen.js'), 'utf8'); + assert.match(src, /filenameIncludes: _ND_DIAGNOSTIC_FILENAME_MARKERS,/); +});