[Base64::] Base64. To produce base64-encoded story files ready for in-browser play by a Javascript-based interpreter such as Parchment. @ The Base64 encoding scheme is defined by the Internet standard RFC 1113. Broadly, the idea is to take a binary stream of bytes, break it into threes, and then convert this into a sequence of four emailable characters. To encode 24 bits in four characters, we need six bits per character, so we need $2^6 = 64$ characters in all. Since $64 = 26 + 26 + 10 + 2$, we can nearly get there with alphanumeric characters alone, adding just two others -- conventionally, plus and forward-slash. That's more or less the whole thing, except that we use an equals sign to indicate incompleteness of the final triplet (which might have only 1 or 2 bytes in it). RFC 1113 permits white space to be used freely, including in particular line breaks, but we don't avail ourselves. = char *RFC1113_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; @ The encoding routine is as follows. = void Base64::encode(filename *in_filename, filename *out_filename, text_stream *top, text_stream *tail) { FILE *IN = BinaryFiles::open_for_reading(in_filename); text_stream TO_struct; text_stream *TO = &TO_struct; if (STREAM_OPEN_TO_FILE(TO, out_filename, UTF8_ENC) == FALSE) BlorbErrors::fatal_fs("can't open base-64 encoded story file for output", out_filename); Base64::encode_inner(TO, IN, top, tail); BinaryFiles::close(IN); STREAM_CLOSE(TO); } void Base64::encode_inner(OUTPUT_STREAM, FILE *IN, text_stream *top, text_stream *tail) { if (top) WRITE("%S", top); while (TRUE) { int triplet[3], triplet_size = 0; @; if (triplet_size == 0) break; int quartet[4]; @; int i; for (i=0; i<4; i++) PUT(RFC1113_table[quartet[i]]); if (triplet_size < 3) break; } if (tail) WRITE("%S", tail); } @ If the file ends in mid-triplet, we pad out with zeros. @ = triplet[0] = fgetc(IN); if (triplet[0] != EOF) { triplet_size++; triplet[1] = fgetc(IN); if (triplet[1] != EOF) { triplet_size++; triplet[2] = fgetc(IN); if (triplet[2] != EOF) triplet_size++; } } int i; for (i=triplet_size; i<3; i++) triplet[i] = 0; @ = int i; for (i=0; i<4; i++) quartet[i] = 0; quartet[0] += (triplet[0] & 0xFC) >> 2; quartet[1] += (triplet[0] & 0x03) << 4; quartet[1] += (triplet[1] & 0xF0) >> 4; quartet[2] += (triplet[1] & 0x0F) << 2; quartet[2] += (triplet[2] & 0xC0) >> 6; quartet[3] += (triplet[2] & 0x3F) << 0; switch (triplet_size) { case 1: quartet[2] = 64; quartet[3] = 64; break; case 2: quartet[3] = 64; break; }