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"