Added Nitfol and Frotz source code.
[projects/chimara/chimara.git] / interpreters / nitfol / zscii.c
diff --git a/interpreters/nitfol/zscii.c b/interpreters/nitfol/zscii.c
new file mode 100644 (file)
index 0000000..f7c12d4
--- /dev/null
@@ -0,0 +1,323 @@
+/*  Nitfol - z-machine interpreter using Glk for output.
+    Copyright (C) 1999  Evin Robertson
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+
+    The author can be reached at nitfol@deja.com
+*/
+#include "nitfol.h"
+#include <limits.h>
+#include <stdio.h>
+
+/* string.c - decode and encode strings */
+
+
+#define NUM_CACHE 4
+#define CACHE_SIZE 4096
+
+/*
+static offset recent[4];
+static unsigned uses[4];
+
+static int is_cached;
+*/
+
+int getstring(zword packedaddress)
+{
+  return decodezscii(UNPACKS(packedaddress), output_char);
+}
+
+
+/* Returns character for given alphabet, letter pair */
+static unsigned char alphabetsoup(unsigned spoon, unsigned char letter)
+{
+  const char *alphabet;
+
+  if(letter == 0)
+    return 32;     /* space */
+
+  if(letter < 6 || letter > 31)
+    return 32;     /* gurgle */
+
+  if(zversion == 1) {
+    alphabet = "abcdefghijklmnopqrstuvwxyz"
+               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+               " 0123456789.,!?_#'\"/\\<-:()";
+    if(letter == 1)
+      return 13;   /* newline */
+  } else {
+    alphabet = "abcdefghijklmnopqrstuvwxyz"
+               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+               " ^0123456789.,!?_#'\"/\\-:()";
+
+    if(spoon == 2 && letter == 7)
+      return 13;   /* newline */
+
+    if(zversion >= 5) { /* v5 can use its own bowl of soup */
+      zword t = LOWORD(HD_ALPHABET);
+      if(t != 0)
+       return LOBYTE(t + spoon*26 + (letter-6));   /* alphabet = z_memory+t */
+    }
+  }
+
+  return alphabet[spoon * 26 + (letter-6)];
+}
+
+
+#define END -1
+
+
+/* Text is arranged in triplets - this function takes a reference to location,
+ * and to shift_amt which says how many bits are left at that location. Each
+ * letter in 5 bits, shift_amt is decremented by this each time.  When a
+ * location runs out, we move to another location.
+ */
+static char untriplet(offset *location, int *shift_amt)
+{
+  unsigned triplet;
+  unsigned char result;
+  if(*shift_amt == END) {
+    if(!testing_string)
+      n_show_error(E_STRING, "attempt to read past end of string", *location);
+    string_bad = TRUE;
+    return 0;
+  }
+  triplet = HISTRWORD(*location);
+  result = (triplet >> *shift_amt) & b00011111;
+
+  *shift_amt -= 5;       /* next character is 5 bits to the right */
+
+  if(*shift_amt < 0) {   /* Reached end of triplet; go on to next */
+    *shift_amt = 10;
+    *location += 2;
+
+    if(triplet & 0x8000) /* High bit set - reached the end of the string */
+      *shift_amt = END;
+  }
+  return result;
+}
+
+
+/* Decodes a zscii string at address 'zscii', sending the decoded characters
+   into putcharfunc.  Returns number of zscii characters it ate until it
+   reached the end of the string. */
+int decodezscii(offset zscii, void (*putcharfunc)(int))
+{
+  const int alphaup[3] = { 1, 2, 0 };
+  const int alphadn[3] = { 2, 0, 1 };
+
+  int shift_amt = 10;
+  int alphalock = 0;
+  int alphashift = 0;
+  int alphacurrent;
+  offset startzscii = zscii;
+  static int depth = 0;
+  depth++;
+
+  if(depth > 2) { /* Nested abbreviations */
+    if(!testing_string) {
+      int remdepth = depth;
+      depth = 0;
+      n_show_error(E_STRING, "nested abbreviations", zscii);
+      depth = remdepth;
+    }
+    string_bad = TRUE;
+    depth--;
+    return 0;
+  }
+
+  do {
+    unsigned char z, x;
+
+    if(zscii > total_size) {
+      if(!testing_string)
+       n_show_error(E_STRING, "attempt to print string beyond end of story", zscii);
+      string_bad = TRUE;
+      depth--;
+      return 0;
+    }
+
+    z = untriplet(&zscii, &shift_amt);
+
+    alphacurrent = alphashift;
+    alphashift = alphalock;
+
+    if(z < 6) {
+      if(zversion <= 2) {
+       switch(z) {
+       case 0: putcharfunc(32); break;                 /* space */
+       case 1:
+         if(zversion == 1) {
+           putcharfunc(13);                            /* newline */
+         } else {                                      /* abbreviation */
+           x = untriplet(&zscii, &shift_amt);
+           decodezscii(((offset) HIWORD(z_synonymtable + x*ZWORD_SIZE)) * 2,
+                       putcharfunc);
+         }
+         break;
+       case 2: alphashift = alphaup[alphashift]; break;
+       case 3: alphashift = alphadn[alphashift]; break;
+       case 4: alphalock = alphashift = alphaup[alphalock]; break;
+       case 5: alphalock = alphashift = alphadn[alphalock]; break;
+       }
+      } else {
+       switch(z) {
+       case 0: putcharfunc(32); break;       /* space */
+       case 1: case 2: case 3:                         /* abbreviations */
+         x = untriplet(&zscii, &shift_amt);
+         decodezscii((offset) 2 * HIWORD(z_synonymtable +
+                                         (32*(z-1) + x) * ZWORD_SIZE),
+                     putcharfunc);
+
+         break;
+       case 4: alphashift = alphaup[alphashift]; break;
+       case 5: alphashift = alphadn[alphashift]; break;
+       }
+      }
+    } else {
+
+      if(alphacurrent == 2 && z == 6) {
+       int multibyte;
+       if(shift_amt == END)
+         break;
+
+       multibyte = untriplet(&zscii, &shift_amt) << 5;
+
+       if(shift_amt == END)
+         break;
+       multibyte |= untriplet(&zscii, &shift_amt);
+
+       putcharfunc(multibyte);
+      } else {
+       putcharfunc(alphabetsoup(alphacurrent, z));
+      }
+    }
+  } while(shift_amt != END);
+  
+  depth--;
+  return zscii - startzscii;
+}
+
+
+static void tripletize(zbyte **location, unsigned *triplet, int *count,
+                      char value, BOOL isend)
+{
+  if(*location == NULL)
+    return;                  /* stop doing stuff if we're already done. */
+
+  *triplet = ((*triplet) << 5) | value;
+  *count += 1;
+
+  if(isend) {
+    while(*count < 3) {
+      *triplet = ((*triplet) << 5) | 5;   /* 5 is the official pad char */
+      *count += 1;
+    }
+    *triplet |= 0x8000;                   /* end bit */
+  }
+
+  if(*count == 3) {
+    (*location)[0] = *triplet >> 8;        /* high byte first */
+    (*location)[1] = *triplet & 255;       /* then lower */
+    *triplet = 0;
+    *count = 0;
+    *location += 2;
+
+    if(isend)
+      *location = NULL;
+  }
+}
+
+
+static BOOL search_soup(zbyte c, int *rspoon, int *rletter)
+{
+  int spoon, letter;
+  for(spoon = 0; spoon < 3; spoon++)
+    for(letter = 0; letter < 32; letter++)
+      if(c == alphabetsoup(spoon, letter)) {
+       *rspoon = spoon;
+       *rletter = letter;
+       return TRUE;
+      }
+  return FALSE;
+}
+
+
+int encodezscii(zbyte *dest, int mindestlen, int maxdestlen,
+               const char *source, int sourcelen)
+{
+  int alphachanger[3];
+  int i;
+  int destlen = 0;
+  int done = FALSE;
+  unsigned triplet = 0; int count = 0;
+
+  if(zversion <= 2) {
+    alphachanger[1] = 2;   /* Shift up */
+    alphachanger[2] = 3;   /* Shift down */
+  } else {
+    alphachanger[1] = 4;   /* Shift up */
+    alphachanger[2] = 5;   /* Shift down */
+  }
+  mindestlen *= 3; maxdestlen *= 3; /* Change byte sizes to zscii sizes */
+  mindestlen /= 2; maxdestlen /= 2;
+
+  for(i = 0; i < sourcelen && !done && dest != NULL; i++) {
+    int spoon, letter;
+    if(search_soup(source[i], &spoon, &letter)) {
+      if(spoon != 0) {   /* switch alphabet if necessary */
+       destlen++;
+       tripletize(&dest, &triplet, &count,
+                  alphachanger[spoon], destlen >= maxdestlen);
+      }
+
+      destlen++;
+      done = ((destlen >= maxdestlen) || (i == sourcelen - 1)) &&
+       (destlen >= mindestlen);
+
+      tripletize(&dest, &triplet, &count, letter, done);
+    } else {    /* The character wasn't found, so use multibyte encoding */
+      destlen++;
+      tripletize(&dest, &triplet, &count, alphachanger[2],destlen>=maxdestlen);
+      destlen++;
+      tripletize(&dest, &triplet, &count, 6, destlen >= maxdestlen);
+      destlen++;
+                                 /* Upper 5 bits (really 3) */
+      tripletize(&dest, &triplet, &count, source[i] >> 5, destlen>=maxdestlen);
+      
+      destlen++;
+      done = ((destlen >= maxdestlen) || (i == sourcelen - 1)) &&
+       (destlen >= mindestlen);
+                                           /* Lower 5 bits */
+      tripletize(&dest, &triplet, &count, source[i] & b00011111, done);
+    }
+
+  }
+
+  if(!done) {                                     /* come back here */
+    while(destlen < mindestlen - 1) {              /* oh yeah you pad me out */
+      tripletize(&dest, &triplet, &count, 5, FALSE);/* uh huh */
+      destlen++;                                    /* uh yup */
+    }                                              /* uh */
+    tripletize(&dest, &triplet, &count, 5, TRUE); /* uh huh, done */
+  }
+  return i;
+}
+
+void op_encode_text(void)
+{
+  encodezscii(z_memory + operand[3], 6, 6,
+             (char *) z_memory + operand[0] + operand[2], operand[1]);
+}