diff --git a/2020/otterness/.entry.json b/2020/otterness/.entry.json
index 71b6d2aef..1aaab4556 100644
--- a/2020/otterness/.entry.json
+++ b/2020/otterness/.entry.json
@@ -33,6 +33,14 @@
"display_via_github" : true,
"entry_text" : "original source code"
},
+ {
+ "file_path" : "try.alt.sh",
+ "inventory_order" : 100,
+ "OK_to_edit" : true,
+ "display_as" : "shellscript",
+ "display_via_github" : true,
+ "entry_text" : "script to try alternate code"
+ },
{
"file_path" : "try.sh",
"inventory_order" : 100,
@@ -41,6 +49,14 @@
"display_via_github" : true,
"entry_text" : "script to try entry"
},
+ {
+ "file_path" : "prog.alt.c",
+ "inventory_order" : 120,
+ "OK_to_edit" : true,
+ "display_as" : "c",
+ "display_via_github" : true,
+ "entry_text" : "unobfuscated source code"
+ },
{
"file_path" : "cvikl.mid",
"inventory_order" : 150,
diff --git a/2020/otterness/.gitignore b/2020/otterness/.gitignore
index b89f0ad1b..454f4b5d8 100644
--- a/2020/otterness/.gitignore
+++ b/2020/otterness/.gitignore
@@ -6,6 +6,7 @@ indent
indent.c
indent.o
prog
+prog.alt
prog.orig
!cvikl.mid
!entertainer.mid
diff --git a/2020/otterness/Makefile b/2020/otterness/Makefile
index 1a89cefc6..9cc95acc2 100644
--- a/2020/otterness/Makefile
+++ b/2020/otterness/Makefile
@@ -115,8 +115,8 @@ OBJ= ${PROG}.o
DATA=
TARGET= ${PROG}
#
-ALT_OBJ=
-ALT_TARGET=
+ALT_OBJ= ${PROG}.alt.o
+ALT_TARGET= ${PROG}.alt
#################
@@ -139,6 +139,9 @@ ${PROG}: ${PROG}.c
alt: data ${ALT_TARGET}
@${TRUE}
+${PROG}.alt: ${PROG}.alt.c
+ ${CC} ${CFLAGS} $< -o $@ ${LDFLAGS}
+
# data files
#
data: ${DATA}
diff --git a/2020/otterness/README.md b/2020/otterness/README.md
index 2ab1f5144..986a5ca00 100644
--- a/2020/otterness/README.md
+++ b/2020/otterness/README.md
@@ -4,6 +4,9 @@
make
```
+There is an unobfuscated version for those who wish to see the original final
+prior to obfuscation. See [Alternate code](#alternate-code) below.
+
### Bugs and (Mis)features:
@@ -33,6 +36,37 @@ Now play `output.mid` and `output2.mid` in an audio player that can play MIDI
files.
+## Alternate code:
+
+### NOTICE to those who wish for a greater challenge:
+
+**If you want a greater challenge, don't check this version**:
+just try to understand the program via the entry source.
+
+If you get stuck, come back and take a look at the alternate, unobfuscated code,
+described by the author as:
+
+> If you're curious about the un-obfuscated program that I originally wrote,
+this was the final working version. I tried to keep the functions names and
+comments understandable, but it will make a lot more sense to readers familiar
+with the MIDI "SMF" file format.
+
+... at their [web page about the
+entry](https://www.nathanotterness.com/2021/04/obfuscated_c_2020.html).
+
+
+## Alternate use:
+
+Use `prog.alt` as you would `prog`.
+
+
+### Alternate try:
+
+```
+ ./try.alt.sh
+```
+
+
## Judges' remarks:
In an entry that boasts not using literals, one would expect to see
@@ -105,6 +139,13 @@ especially true for "live performance" .mid files.
correctly parse, but I have rarely seen this in practice. The largest
cause of program failures, in my experience, has been Limitation 2.
+### NOTICE to those who wish for a greater challenge:
+
+**If you want a greater challenge, don't read any further**:
+just try to understand the program via the source.
+
+If you get stuck, come back and read below for additional hints and information.
+
#### Obfuscation notes
diff --git a/2020/otterness/index.html b/2020/otterness/index.html
index ecdf516f2..bce40cad3 100644
--- a/2020/otterness/index.html
+++ b/2020/otterness/index.html
@@ -409,6 +409,8 @@ Location: US - United States of America
To build:
make
+There is an unobfuscated version for those who wish to see the original final
+prior to obfuscation. See Alternate code below.
Bugs and (Mis)features:
The current status of this entry is:
STATUS: INABIAF - please DO NOT fix
@@ -419,6 +421,24 @@ Location: US - United States of America
./try.sh
Now play output.mid and output2.mid in an audio player that can play MIDI
files.
+Alternate code:
+NOTICE to those who wish for a greater challenge:
+If you want a greater challenge, don’t check this version:
+just try to understand the program via the entry source.
+If you get stuck, come back and take a look at the alternate, unobfuscated code,
+described by the author as:
+
+If you’re curious about the un-obfuscated program that I originally wrote,
+this was the final working version. I tried to keep the functions names and
+comments understandable, but it will make a lot more sense to readers familiar
+with the MIDI “SMF” file format.
+
+… at their web page about the
+entry.
+Alternate use:
+Use prog.alt as you would prog.
+Alternate try:
+ ./try.alt.sh
In an entry that boasts not using literals, one would expect to see
straightforward ways to produce small integers like !(x^x)<<!(x^x), etc. but
@@ -537,7 +557,9 @@ very thoroughly).
prog.c - entry source code
Makefile - entry Makefile
prog.orig.c - original source code
+try.alt.sh - script to try alternate code
try.sh - script to try entry
+prog.alt.c - unobfuscated source code
cvikl.mid - sample MIDI file
entertainer.mid - sample MIDI file
diff --git a/2020/otterness/prog.alt.c b/2020/otterness/prog.alt.c
new file mode 100644
index 000000000..f1a520cb4
--- /dev/null
+++ b/2020/otterness/prog.alt.c
@@ -0,0 +1,284 @@
+#include
+#include
+#include
+#include
+
+uint16_t ReadBigEndian16(uint8_t *c) {
+ return *(c + 1) | (uint16_t) (*c) << 8;
+}
+
+void WriteBigEndian16(uint8_t *c, uint16_t v) {
+ c[1] = v;
+ c[0] = v >> 8;
+}
+
+uint32_t ReadBigEndian32(uint8_t *c) {
+ return (uint32_t) ReadBigEndian16(c) << 16 | ReadBigEndian16(c + 2);
+}
+
+void WriteBigEndian32(uint8_t *c, uint32_t v) {
+ WriteBigEndian16(c + 2, v);
+ WriteBigEndian16(c, v >> 16);
+}
+
+// Fills in the *value. Returns the length in bytes.
+int ReadVarInt(uint8_t *content, uint32_t *value) {
+ int i;
+ uint8_t b;
+ *value = 0;
+ for (i = 0; i < 4; i++) {
+ b = content[i];
+ *value |= b & 0x7f;
+ if (!(b & 0x80)) return i + 1;
+ *value = *value << 7;
+ }
+ fprintf(stderr, "Bad variable int.\n");
+ exit(1);
+}
+
+uint8_t HighestBit(uint32_t n) {
+ uint8_t r = 1;
+ if (!n) return 0;
+ while (n >>= 1) r++;
+ return r;
+}
+
+uint8_t VarIntBytes(uint32_t n) {
+ uint8_t bits = HighestBit(n);
+ return bits / 7 + !!(bits % 7);
+}
+
+int WriteVarInt(uint32_t n, uint8_t *c) {
+ uint8_t bytes_needed = VarIntBytes(n);
+ *c = 0;
+ if (!n) return bytes_needed;
+ if (bytes_needed > 4) {
+ fprintf(stderr, "Int too big for MIDI.\n");
+ exit(1);
+ }
+ c += bytes_needed - 1;
+ while (n != 0) {
+ *c = n & 0x7f;
+ n = n >> 7;
+ c--;
+ }
+ return bytes_needed;
+}
+
+// Advances past the next MIDI message, returning a pointer after it, and
+// updating running status if necessary.
+uint8_t* AdvancePastMessage(uint8_t *c, uint8_t *running_status) {
+ uint32_t tmp;
+ uint8_t b;
+ switch (*c) {
+ case 0xf0:
+ case 0xf7:
+ // Skip past system exclusive messages
+ *running_status = 0;
+ c++;
+ c += ReadVarInt(c, &tmp);
+ return c + tmp;
+ case 0xff:
+ // Skip past meta events
+ *running_status = 0;
+ c++;
+ c++;
+ c += ReadVarInt(c, &tmp);
+ return c + tmp;
+ }
+ // We're assuming this is a channel voice message now.
+ if (*c & 0x80) {
+ // Advance past the new status byte.
+ b = *c;
+ c++;
+ *running_status = b;
+ } else {
+ b = *running_status;
+ }
+ // Just test the different channel voice message types.
+ switch (b >> 4) {
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 14:
+ return c + 2;
+ case 12:
+ case 13:
+ return c + 1;
+ }
+ fprintf(stderr, "Bad message status byte: %x\n", b);
+ exit(1);
+ return c;
+}
+
+// Assumes we're pointing at the start of a MTrk. Returns a pointer to the
+// start of the next track.
+uint8_t* GetTrackTicks(uint8_t *c, uint32_t *ticks) {
+ uint32_t length, tmp;
+ uint8_t *end;
+ uint8_t running_status = 0;
+ *ticks = 0;
+ if (c[3] != 'k') {
+ fprintf(stderr, "Not at a track start.\n");
+ exit(1);
+ }
+ length = ReadBigEndian32(c + 4);
+ c += 8;
+ end = c + length;
+ while (c < end) {
+ c += ReadVarInt(c, &tmp);
+ *ticks += tmp;
+ c = AdvancePastMessage(c, &running_status);
+ }
+ return c;
+}
+
+// Requires a pointer to the start of the first track, and the total number of
+// tracks.
+uint32_t GetHighestTrackTicks(uint8_t *c, int track_count) {
+ uint32_t tmp, max = 0;
+ int i;
+ for (i = 0; i < track_count; i++) {
+ c = GetTrackTicks(c, &tmp);
+ fprintf(stderr, "Track %d: %u ticks\n", i, (unsigned) tmp);
+ if (tmp > max) max = tmp;
+ }
+ return max;
+}
+
+void DumpHex(uint8_t *data, int size) {
+ int i;
+ for (i = 0; i < size; i++) {
+ fprintf(stderr, "%p + %d: 0x%02x\n", data, i, data[i]);
+ }
+}
+
+uint8_t *beat = NULL;
+uint8_t *note_off_note = NULL;
+// Writes a beat at the given offset. Requires the beat size.
+uint8_t* WriteBeat(uint8_t *c, int i, int bs) {
+ uint8_t n = 42, v = 80;
+ if ((i % 4) == 0) {
+ n = 36;
+ v = 120;
+ } else if ((i % 4) == 2) {
+ n = 40;
+ v = 100;
+ }
+ beat[1] = n;
+ beat[2] = v;
+ *note_off_note = n;
+ memcpy(c, beat, bs);
+ return c + bs;
+}
+
+void WriteBootsAndCats(uint8_t *c, uint32_t length) {
+ uint16_t ticks_per_beat, track_count;
+ uint32_t beats_to_generate, tmp, data_size, beat_size;
+ uint8_t *cur, *data = NULL;
+ track_count = ReadBigEndian16(c + 10);
+ ticks_per_beat = ReadBigEndian16(c + 12) >> 1;
+ if (ticks_per_beat >> 15) {
+ fprintf(stderr, "SMPTE timing isn't supported\n");
+ exit(1);
+ }
+ beats_to_generate = GetHighestTrackTicks(c + 14, track_count) / ticks_per_beat;
+ // A "beat" is simply a note-on followed by a note-off event. The tricky part
+ // is the delta time may be more than one byte. We'll assume running status.
+ // Note on = 00
+ // Note off = 00
+ beat_size = 5 + VarIntBytes(ticks_per_beat);
+ beat = malloc(beat_size);
+ if (!beat) {
+ fprintf(stderr, "Failed allocating beat.\n");
+ exit(1);
+ }
+ // This requires 8 bytes in the track header + 4 bytes for end-of-track +
+ // the track data, plus an additional byte to set up running status in the
+ // first beat.
+ data_size = beats_to_generate * beat_size + 13;
+ data = malloc(data_size);
+ if (!data) {
+ fprintf(stderr, "Failed allocating data.\n");
+ exit(1);
+ }
+ fprintf(stderr, "Data size: %d (0x%x), beat size %d, # beats %d\n",
+ (int) data_size, data_size, beat_size, beats_to_generate);
+ memcpy(data, "MTrk", 4);
+ WriteBigEndian32(data + 4, data_size - 8);
+ fprintf(stderr, "Data size: %d\n", (int) data_size);
+
+ // Build the "beat" data
+ cur = beat;
+ *cur++ = 0;
+ // Initial note number and velocity.
+ *cur++ = 36;
+ *cur++ = 120;
+ cur += WriteVarInt(ticks_per_beat, cur);
+ // Keep a pointer to where to write the note to turn off
+ note_off_note = cur;
+ *cur++ = 36;
+ // The note-off velocity is 0.
+ *cur = 0;
+
+ cur = data + 8;
+ // Write the first note-on and off:
+ *cur++ = 0;
+ *cur++ = 0x99;
+ memcpy(cur, beat + 1, beat_size - 1);
+ cur += beat_size - 1;
+ for (tmp = 1; tmp < beats_to_generate; tmp++) {
+ cur = WriteBeat(cur, tmp, beat_size);
+ }
+ *cur++ = 0;
+ *cur++ = 0xff;
+ *cur++ = 0x2f;
+ *cur = 0;
+ // Update the original MIDI header to increment the track count and make sure
+ // the format is 1.
+ c[9] = 1;
+ WriteBigEndian16(c + 10, track_count + 1);
+ fwrite(c, length, 1, stdout);
+ fwrite(data, data_size, 1, stdout);
+}
+
+int main(int argc, char **argv) {
+ FILE *f = NULL;
+ uint8_t *content = NULL;
+ long size = 0;
+ int result;
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s \n", argv[0]);
+ return 1;
+ }
+ f = fopen(argv[1], "rb");
+ if (!f) {
+ fprintf(stderr, "Coudn't open %s\n", argv[1]);
+ return 1;
+ }
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ content = malloc(size);
+ if (!content) {
+ fprintf(stderr, "Couldn't allocate content buffer.\n");
+ fclose(f);
+ return 1;
+ }
+ result = fread(content, size, 1, f);
+ fclose(f);
+ if (result != 1) {
+ fprintf(stderr, "Couldn't read %s.\n", argv[1]);
+ free(content);
+ return 1;
+ }
+ if ((content[3] != 'd') || (content[7] != 6)) {
+ fprintf(stderr, "Bad MIDI file.\n");
+ free(content);
+ return 1;
+ }
+ WriteBootsAndCats(content, size);
+ free(content);
+ return 0;
+}
diff --git a/2020/otterness/try.alt.sh b/2020/otterness/try.alt.sh
new file mode 100755
index 000000000..5adc123c4
--- /dev/null
+++ b/2020/otterness/try.alt.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+#
+# try.sh - demonstrate IOCCC entry 2020/otterness alt code
+#
+
+# make sure CC is set so that when we do make CC="$CC" it isn't empty. Doing it
+# this way allows us to have the user specify a different compiler in an easy
+# way.
+[[ -z "$CC" || ! -f "$CC" || ! -x "$CC" ]] && CC="cc"
+
+make CC="$CC" alt >/dev/null || exit 1
+
+# clear screen after compilation so that only the entry is shown
+clear
+
+read -r -n 1 -p "Press any key to run: ./prog.alt < cvikl.mid > output.mid: "
+echo 1>&2
+./prog.alt < cvikl.mid > output.mid
+echo "Now play output.wav in an audio player that can play MIDI files." 1>&2
+echo 1>&2
+
+read -r -n 1 -p "Press any key to run: ./prog.alt < entertainer.mid > output2.mid: "
+echo 1>&2
+./prog.alt < entertainer.mid > output2.mid
+echo "Now play output2.wav in an audio player that can play MIDI files." 1>&2
+echo 1>&2
diff --git a/thanks-for-help.html b/thanks-for-help.html
index e219a1aee..a048be0d9 100644
--- a/thanks-for-help.html
+++ b/thanks-for-help.html
@@ -4659,7 +4659,10 @@ Tolkien.
entertainer.mid from the URLs we suggested so
that one need not download them and to make sure they can always be obtained
even if the domain or link goes dead.
-Cody also added the try.sh script.
+Cody also added the try.sh script and the
+try.alt.sh script for the author’s
+unobfuscated version that Cody added as
+prog.alt.c.
Winning entry source code: prog.c
diff --git a/thanks-for-help.md b/thanks-for-help.md
index 37691468b..549e44779 100644
--- a/thanks-for-help.md
+++ b/thanks-for-help.md
@@ -6038,7 +6038,10 @@ Tolkien](https://www.tolkienestate.com/life/biography/).
that one need not download them and to make sure they can always be obtained
even if the domain or link goes dead.
-Cody also added the [try.sh](%%REPO_URL%%/2020/otterness/try.sh) script.
+Cody also added the [try.sh](%%REPO_URL%%/2020/otterness/try.sh) script and the
+[try.alt.sh](%%REPO_URL%%/2020/otterness/try.alt.sh) script for the author's
+unobfuscated version that Cody added as
+[prog.alt.c](%%REPO_URL%%/2020/otterness/prog.alt.c).
diff --git a/tmp/manifest.csv b/tmp/manifest.csv
index b2f75ff4f..301307f23 100644
--- a/tmp/manifest.csv
+++ b/tmp/manifest.csv
@@ -4319,7 +4319,9 @@
2020,otterness,prog.c,20,true,c,true,entry source code
2020,otterness,Makefile,30,true,makefile,true,entry Makefile
2020,otterness,prog.orig.c,50,false,c,true,original source code
+2020,otterness,try.alt.sh,100,true,shellscript,true,script to try alternate code
2020,otterness,try.sh,100,true,shellscript,true,script to try entry
+2020,otterness,prog.alt.c,120,true,c,true,unobfuscated source code
2020,otterness,cvikl.mid,150,true,midi,false,sample MIDI file
2020,otterness,entertainer.mid,159,true,midi,false,sample MIDI file
2020,otterness,2020_otterness.tar.bz2,1415926535,false,tbz2,false,download entry tarball
diff --git a/tmp/manifest.numbers b/tmp/manifest.numbers
index 374da2847..5889f6f1a 100644
Binary files a/tmp/manifest.numbers and b/tmp/manifest.numbers differ
diff --git a/tmp/path_list.found.txt b/tmp/path_list.found.txt
index ac46688af..908d93634 100644
--- a/tmp/path_list.found.txt
+++ b/tmp/path_list.found.txt
@@ -4324,8 +4324,10 @@
2020/otterness/cvikl.mid
2020/otterness/entertainer.mid
2020/otterness/index.html
+2020/otterness/prog.alt.c
2020/otterness/prog.c
2020/otterness/prog.orig.c
+2020/otterness/try.alt.sh
2020/otterness/try.sh
2020/tsoj/.entry.json
2020/tsoj/.gitignore
diff --git a/tmp/path_list.manifest.txt b/tmp/path_list.manifest.txt
index ac46688af..908d93634 100644
--- a/tmp/path_list.manifest.txt
+++ b/tmp/path_list.manifest.txt
@@ -4324,8 +4324,10 @@
2020/otterness/cvikl.mid
2020/otterness/entertainer.mid
2020/otterness/index.html
+2020/otterness/prog.alt.c
2020/otterness/prog.c
2020/otterness/prog.orig.c
+2020/otterness/try.alt.sh
2020/otterness/try.sh
2020/tsoj/.entry.json
2020/tsoj/.gitignore
diff --git a/tmp/path_list.required.txt b/tmp/path_list.required.txt
index 9a9c51f54..1526a78d6 100644
--- a/tmp/path_list.required.txt
+++ b/tmp/path_list.required.txt
@@ -3091,7 +3091,9 @@
2020/otterness/README.md
2020/otterness/cvikl.mid
2020/otterness/entertainer.mid
+2020/otterness/prog.alt.c
2020/otterness/prog.c
+2020/otterness/try.alt.sh
2020/otterness/try.sh
2020/tsoj/.entry.json
2020/tsoj/.gitignore