Update interpreters to latest Garglk codebase
[projects/chimara/chimara.git] / interpreters / nitfol / zscii.c
1 /*  Nitfol - z-machine interpreter using Glk for output.
2     Copyright (C) 1999  Evin Robertson
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18     The author can be reached at nitfol@deja.com
19 */
20 #include "nitfol.h"
21 #include <limits.h>
22 #include <stdio.h>
23
24 /* string.c - decode and encode strings */
25
26
27 #define NUM_CACHE 4
28 #define CACHE_SIZE 4096
29
30 /*
31 static offset recent[4];
32 static unsigned uses[4];
33
34 static int is_cached;
35 */
36
37 int getstring(zword packedaddress)
38 {
39   return decodezscii(UNPACKS(packedaddress), output_char);
40 }
41
42
43 /* Returns character for given alphabet, letter pair */
44 static unsigned char alphabetsoup(unsigned spoon, unsigned char letter)
45 {
46   const char *alphabet;
47
48   if(letter == 0)
49     return 32;     /* space */
50
51   if(letter < 6 || letter > 31)
52     return 32;     /* gurgle */
53
54   if(zversion == 1) {
55     alphabet = "abcdefghijklmnopqrstuvwxyz"
56                "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
57                " 0123456789.,!?_#'\"/\\<-:()";
58     if(letter == 1)
59       return 13;   /* newline */
60   } else {
61     alphabet = "abcdefghijklmnopqrstuvwxyz"
62                "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
63                " ^0123456789.,!?_#'\"/\\-:()";
64
65     if(spoon == 2 && letter == 7)
66       return 13;   /* newline */
67
68     if(zversion >= 5) { /* v5 can use its own bowl of soup */
69       zword t = LOWORD(HD_ALPHABET);
70       if(t != 0)
71         return LOBYTE(t + spoon*26 + (letter-6));   /* alphabet = z_memory+t */
72     }
73   }
74
75   return alphabet[spoon * 26 + (letter-6)];
76 }
77
78
79 #define END -1
80
81
82 /* Text is arranged in triplets - this function takes a reference to location,
83  * and to shift_amt which says how many bits are left at that location. Each
84  * letter in 5 bits, shift_amt is decremented by this each time.  When a
85  * location runs out, we move to another location.
86  */
87 static char untriplet(offset *location, int *shift_amt)
88 {
89   unsigned triplet;
90   unsigned char result;
91   if(*shift_amt == END) {
92     if(!testing_string)
93       n_show_error(E_STRING, "attempt to read past end of string", *location);
94     string_bad = TRUE;
95     return 0;
96   }
97   triplet = HISTRWORD(*location);
98   result = (triplet >> *shift_amt) & b00011111;
99
100   *shift_amt -= 5;       /* next character is 5 bits to the right */
101
102   if(*shift_amt < 0) {   /* Reached end of triplet; go on to next */
103     *shift_amt = 10;
104     *location += 2;
105
106     if(triplet & 0x8000) /* High bit set - reached the end of the string */
107       *shift_amt = END;
108   }
109   return result;
110 }
111
112
113 /* Decodes a zscii string at address 'zscii', sending the decoded characters
114    into putcharfunc.  Returns number of zscii characters it ate until it
115    reached the end of the string. */
116 int decodezscii(offset zscii, void (*putcharfunc)(int))
117 {
118   const int alphaup[3] = { 1, 2, 0 };
119   const int alphadn[3] = { 2, 0, 1 };
120
121   int shift_amt = 10;
122   int alphalock = 0;
123   int alphashift = 0;
124   int alphacurrent;
125   offset startzscii = zscii;
126   static int depth = 0;
127   depth++;
128
129   if(depth > 2) { /* Nested abbreviations */
130     if(!testing_string) {
131       int remdepth = depth;
132       depth = 0;
133       n_show_error(E_STRING, "nested abbreviations", zscii);
134       depth = remdepth;
135     }
136     string_bad = TRUE;
137     depth--;
138     return 0;
139   }
140
141   do {
142     unsigned char z, x;
143
144     if(zscii > total_size) {
145       if(!testing_string)
146         n_show_error(E_STRING, "attempt to print string beyond end of story", zscii);
147       string_bad = TRUE;
148       depth--;
149       return 0;
150     }
151
152     z = untriplet(&zscii, &shift_amt);
153
154     alphacurrent = alphashift;
155     alphashift = alphalock;
156
157     if(z < 6) {
158       if(zversion <= 2) {
159         switch(z) {
160         case 0: putcharfunc(32); break;                 /* space */
161         case 1:
162           if(zversion == 1) {
163             putcharfunc(13);                            /* newline */
164           } else {                                      /* abbreviation */
165             x = untriplet(&zscii, &shift_amt);
166             decodezscii(((offset) HIWORD(z_synonymtable + x*ZWORD_SIZE)) * 2,
167                         putcharfunc);
168           }
169           break;
170         case 2: alphashift = alphaup[alphashift]; break;
171         case 3: alphashift = alphadn[alphashift]; break;
172         case 4: alphalock = alphashift = alphaup[alphalock]; break;
173         case 5: alphalock = alphashift = alphadn[alphalock]; break;
174         }
175       } else {
176         switch(z) {
177         case 0: putcharfunc(32); break;       /* space */
178         case 1: case 2: case 3:                         /* abbreviations */
179           x = untriplet(&zscii, &shift_amt);
180           decodezscii((offset) 2 * HIWORD(z_synonymtable +
181                                           (32*(z-1) + x) * ZWORD_SIZE),
182                       putcharfunc);
183
184           break;
185         case 4: alphashift = alphaup[alphashift]; break;
186         case 5: alphashift = alphadn[alphashift]; break;
187         }
188       }
189     } else {
190
191       if(alphacurrent == 2 && z == 6) {
192         int multibyte;
193         if(shift_amt == END)
194           break;
195
196         multibyte = untriplet(&zscii, &shift_amt) << 5;
197
198         if(shift_amt == END)
199           break;
200         multibyte |= untriplet(&zscii, &shift_amt);
201
202         putcharfunc(multibyte);
203       } else {
204         putcharfunc(alphabetsoup(alphacurrent, z));
205       }
206     }
207   } while(shift_amt != END);
208   
209   depth--;
210   return zscii - startzscii;
211 }
212
213
214 static void tripletize(zbyte **location, unsigned *triplet, int *count,
215                        char value, BOOL isend)
216 {
217   if(*location == NULL)
218     return;                  /* stop doing stuff if we're already done. */
219
220   *triplet = ((*triplet) << 5) | value;
221   *count += 1;
222
223   if(isend) {
224     while(*count < 3) {
225       *triplet = ((*triplet) << 5) | 5;   /* 5 is the official pad char */
226       *count += 1;
227     }
228     *triplet |= 0x8000;                   /* end bit */
229   }
230
231   if(*count == 3) {
232     (*location)[0] = *triplet >> 8;        /* high byte first */
233     (*location)[1] = *triplet & 255;       /* then lower */
234     *triplet = 0;
235     *count = 0;
236     *location += 2;
237
238     if(isend)
239       *location = NULL;
240   }
241 }
242
243
244 static BOOL search_soup(zbyte c, int *rspoon, int *rletter)
245 {
246   int spoon, letter;
247   for(spoon = 0; spoon < 3; spoon++)
248     for(letter = 0; letter < 32; letter++)
249       if(c == alphabetsoup(spoon, letter)) {
250         *rspoon = spoon;
251         *rletter = letter;
252         return TRUE;
253       }
254   return FALSE;
255 }
256
257
258 int encodezscii(zbyte *dest, int mindestlen, int maxdestlen,
259                 const char *source, int sourcelen)
260 {
261   int alphachanger[3];
262   int i;
263   int destlen = 0;
264   int done = FALSE;
265   unsigned triplet = 0; int count = 0;
266
267   if(zversion <= 2) {
268     alphachanger[1] = 2;   /* Shift up */
269     alphachanger[2] = 3;   /* Shift down */
270   } else {
271     alphachanger[1] = 4;   /* Shift up */
272     alphachanger[2] = 5;   /* Shift down */
273   }
274   mindestlen *= 3; maxdestlen *= 3; /* Change byte sizes to zscii sizes */
275   mindestlen /= 2; maxdestlen /= 2;
276
277   for(i = 0; i < sourcelen && !done && dest != NULL; i++) {
278     int spoon, letter;
279     if(search_soup(source[i], &spoon, &letter)) {
280       if(spoon != 0) {   /* switch alphabet if necessary */
281         destlen++;
282         tripletize(&dest, &triplet, &count,
283                    alphachanger[spoon], destlen >= maxdestlen);
284       }
285
286       destlen++;
287       done = ((destlen >= maxdestlen) || (i == sourcelen - 1)) &&
288         (destlen >= mindestlen);
289
290       tripletize(&dest, &triplet, &count, letter, done);
291     } else {    /* The character wasn't found, so use multibyte encoding */
292       destlen++;
293       tripletize(&dest, &triplet, &count, alphachanger[2],destlen>=maxdestlen);
294       destlen++;
295       tripletize(&dest, &triplet, &count, 6, destlen >= maxdestlen);
296       destlen++;
297                                  /* Upper 5 bits (really 3) */
298       tripletize(&dest, &triplet, &count, source[i] >> 5, destlen>=maxdestlen);
299       
300       destlen++;
301       done = ((destlen >= maxdestlen) || (i == sourcelen - 1)) &&
302         (destlen >= mindestlen);
303                                            /* Lower 5 bits */
304       tripletize(&dest, &triplet, &count, source[i] & b00011111, done);
305     }
306
307   }
308
309   if(!done) {                                     /* come back here */
310     while(destlen < mindestlen - 1) {              /* oh yeah you pad me out */
311       tripletize(&dest, &triplet, &count, 5, FALSE);/* uh huh */
312       destlen++;                                    /* uh yup */
313     }                                              /* uh */
314     tripletize(&dest, &triplet, &count, 5, TRUE); /* uh huh, done */
315   }
316   return i;
317 }
318
319 void op_encode_text(void)
320 {
321   encodezscii(z_memory + operand[3], 6, 6,
322               (char *) z_memory + operand[0] + operand[2], operand[1]);
323 }