From fee2b1ef1007e3863ae3bc39761d0ec6b416aca8 Mon Sep 17 00:00:00 2001 From: Anachronaut Date: Sat, 5 Sep 2026 22:09:18 -0400 Subject: [PATCH] One missing patch says one thing A patch file that could not be read left the name unregistered, so every #Voice naming it failed as well, and then the check that a voice has an instrument failed for each of those. One wrong path produced seven messages and only the first was worth reading. A patch that cannot be read is still a patch that was NAMED. It is registered either way now, with its bytes marked missing, so everything below resolves the name and says nothing. Nothing is written regardless - one problem is enough to stop that - so a patch with no bytes never reaches a file. The damage from the old behaviour was not the extra lines. It is that a compiler which says one thing seven ways teaches people to read the last line, which is the one that matters least. Checked by counting: one missing patch, three voices using it, and the count of messages mentioning it has to be one. break.sh confirms it by putting the old behaviour back on the failure path alone - the first attempt at that break stopped every tune compiling and the disk build failed before any test ran, which is break.sh being right about a break that proved nothing. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW --- Source/Tune/TuneC.c | 28 +++++++++++++++++++++++----- Tests/sound.sh | 16 ++++++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/Source/Tune/TuneC.c b/Source/Tune/TuneC.c index 13a9ec5..bf82390 100644 --- a/Source/Tune/TuneC.c +++ b/Source/Tune/TuneC.c @@ -62,6 +62,7 @@ typedef struct { unsigned char bytes[512]; int length; int line; + int bad; // Named, but its bytes could not be had. See doPatch. } Patch; typedef struct { @@ -215,26 +216,41 @@ static void doPatch(void) { complain(name->line, "that patch is named twice", name->text); return; } + // ---- A PATCH THAT CANNOT BE READ IS STILL A PATCH THAT WAS NAMED ---- + // + // It is registered either way, with its bytes marked missing. Nothing will be written - + // one problem is enough to stop that - but everything below can now resolve the name, so + // a single missing file says one thing instead of seven. + // + // It used to return here, and the damage was out of all proportion: the name went + // unregistered, so every #Voice naming it failed as well, and then the check that a voice + // has an instrument failed for each of those. One wrong path, seven messages, and only + // the first of them worth reading. A compiler that says one thing seven ways teaches + // people to read the last line, which is the one that matters least. + Patch *p = &patches[patchCount]; + snprintf(p->name, MAX_NAME, "%s", name->text); + p->line = name->line; + p->length = 0; + p->bad = 0; + patchCount++; + char where[1024]; FILE *in = openPatch(file->text, where, sizeof(where)); if (!in) { complain(file->line, "cannot find that patch beside the tune or on the -I path", file->text); + p->bad = 1; return; } - Patch *p = &patches[patchCount]; - snprintf(p->name, MAX_NAME, "%s", name->text); p->length = (int)fread(p->bytes, 1, sizeof(p->bytes), in); - p->line = name->line; fclose(in); // A patch is a count and that many pairs. Anything else is a file that is not one, and // embedding it would make a tune that writes rubbish at the sound device. if (p->length < 1 || p->length != 1 + p->bytes[0] * 2) { complain(file->line, "that is not a patch: SoundPatch --blob writes one", file->text); - return; + p->bad = 1; } - patchCount++; } static void doVoice(void) { @@ -269,6 +285,8 @@ static void doSequence(void) { return; } if (findSequence(name->text) >= 0) { + // The first one keeps the name, so an order list naming it still resolves and the + // duplicate is reported once rather than once here and again at every use. complain(name->line, "that sequence is named twice", name->text); return; } diff --git a/Tests/sound.sh b/Tests/sound.sh index 6fc75bc..8aac3a7 100755 --- a/Tests/sound.sh +++ b/Tests/sound.sh @@ -862,6 +862,22 @@ PY2 refuses "a duration is from" \ "#Tick 0d125000" "#Patch A low.patch" "#Voice 0d0 A" "#Sequence S" "0d60 0d0" \ "#Order 0d0" "S" + # ---- AND IT SAYS IT ONCE ---- + # + # One missing patch used to be seven messages: the name went unregistered, so every #Voice + # naming it failed too, and then the check that a voice has an instrument failed for each + # of those. Only the first was worth reading. A compiler that says one thing seven ways + # teaches people to read the last line, which is the one that matters least. + printf '%s\n' "#Tick 0d125000" "#Patch A nosuch.patch" \ + "#Voice 0d0 A" "#Voice 0d1 A" "#Voice 0d2 A" \ + "#Sequence S" "0d60 0d4" \ + "#Order 0d0" "S" "#Order 0d1" "S" "#Order 0d2" "S" > "$BUILD/cascade.tune.txt" + SAID="$("$ROOT/TuneC" "$BUILD/cascade.tune.txt" "$BUILD/cascade.tune" 2>&1 \ + | grep -c "cannot find\|no patch of that name\|no instrument")" + [ "$SAID" = "1" ] \ + && result ok "TuneC says a missing patch once" "one message, not one for every use of it" \ + || result no "TuneC says a missing patch once" "$SAID messages for one missing file" + refuses "no sequence of that name" \ "#Tick 0d125000" "#Patch A low.patch" "#Voice 0d0 A" "#Sequence S" "0d60 0d4" \ "#Order 0d0" "Nope"