1 /* Nitfol - z-machine interpreter using Glk for output.
2 Copyright (C) 1999 Evin Robertson
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
18 The author can be reached at nitfol@deja.com
24 /* string.c - decode and encode strings */
28 #define CACHE_SIZE 4096
31 static offset recent[4];
32 static unsigned uses[4];
37 int getstring(zword packedaddress)
39 return decodezscii(UNPACKS(packedaddress), output_char);
43 /* Returns character for given alphabet, letter pair */
44 static unsigned char alphabetsoup(unsigned spoon, unsigned char letter)
49 return 32; /* space */
51 if(letter < 6 || letter > 31)
52 return 32; /* gurgle */
55 alphabet = "abcdefghijklmnopqrstuvwxyz"
56 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
57 " 0123456789.,!?_#'\"/\\<-:()";
59 return 13; /* newline */
61 alphabet = "abcdefghijklmnopqrstuvwxyz"
62 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
63 " ^0123456789.,!?_#'\"/\\-:()";
65 if(spoon == 2 && letter == 7)
66 return 13; /* newline */
68 if(zversion >= 5) { /* v5 can use its own bowl of soup */
69 zword t = LOWORD(HD_ALPHABET);
71 return LOBYTE(t + spoon*26 + (letter-6)); /* alphabet = z_memory+t */
75 return alphabet[spoon * 26 + (letter-6)];
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.
87 static char untriplet(offset *location, int *shift_amt)
91 if(*shift_amt == END) {
93 n_show_error(E_STRING, "attempt to read past end of string", *location);
97 triplet = HISTRWORD(*location);
98 result = (triplet >> *shift_amt) & b00011111;
100 *shift_amt -= 5; /* next character is 5 bits to the right */
102 if(*shift_amt < 0) { /* Reached end of triplet; go on to next */
106 if(triplet & 0x8000) /* High bit set - reached the end of the string */
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))
118 const int alphaup[3] = { 1, 2, 0 };
119 const int alphadn[3] = { 2, 0, 1 };
125 offset startzscii = zscii;
126 static int depth = 0;
129 if(depth > 2) { /* Nested abbreviations */
130 if(!testing_string) {
131 int remdepth = depth;
133 n_show_error(E_STRING, "nested abbreviations", zscii);
144 if(zscii > total_size) {
146 n_show_error(E_STRING, "attempt to print string beyond end of story", zscii);
152 z = untriplet(&zscii, &shift_amt);
154 alphacurrent = alphashift;
155 alphashift = alphalock;
160 case 0: putcharfunc(32); break; /* space */
163 putcharfunc(13); /* newline */
164 } else { /* abbreviation */
165 x = untriplet(&zscii, &shift_amt);
166 decodezscii(((offset) HIWORD(z_synonymtable + x*ZWORD_SIZE)) * 2,
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;
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),
185 case 4: alphashift = alphaup[alphashift]; break;
186 case 5: alphashift = alphadn[alphashift]; break;
191 if(alphacurrent == 2 && z == 6) {
196 multibyte = untriplet(&zscii, &shift_amt) << 5;
200 multibyte |= untriplet(&zscii, &shift_amt);
202 putcharfunc(multibyte);
204 putcharfunc(alphabetsoup(alphacurrent, z));
207 } while(shift_amt != END);
210 return zscii - startzscii;
214 static void tripletize(zbyte **location, unsigned *triplet, int *count,
215 char value, BOOL isend)
217 if(*location == NULL)
218 return; /* stop doing stuff if we're already done. */
220 *triplet = ((*triplet) << 5) | value;
225 *triplet = ((*triplet) << 5) | 5; /* 5 is the official pad char */
228 *triplet |= 0x8000; /* end bit */
232 (*location)[0] = *triplet >> 8; /* high byte first */
233 (*location)[1] = *triplet & 255; /* then lower */
244 static BOOL search_soup(zbyte c, int *rspoon, int *rletter)
247 for(spoon = 0; spoon < 3; spoon++)
248 for(letter = 0; letter < 32; letter++)
249 if(c == alphabetsoup(spoon, letter)) {
258 int encodezscii(zbyte *dest, int mindestlen, int maxdestlen,
259 const char *source, int sourcelen)
265 unsigned triplet = 0; int count = 0;
268 alphachanger[1] = 2; /* Shift up */
269 alphachanger[2] = 3; /* Shift down */
271 alphachanger[1] = 4; /* Shift up */
272 alphachanger[2] = 5; /* Shift down */
274 mindestlen *= 3; maxdestlen *= 3; /* Change byte sizes to zscii sizes */
275 mindestlen /= 2; maxdestlen /= 2;
277 for(i = 0; i < sourcelen && !done && dest != NULL; i++) {
279 if(search_soup(source[i], &spoon, &letter)) {
280 if(spoon != 0) { /* switch alphabet if necessary */
282 tripletize(&dest, &triplet, &count,
283 alphachanger[spoon], destlen >= maxdestlen);
287 done = ((destlen >= maxdestlen) || (i == sourcelen - 1)) &&
288 (destlen >= mindestlen);
290 tripletize(&dest, &triplet, &count, letter, done);
291 } else { /* The character wasn't found, so use multibyte encoding */
293 tripletize(&dest, &triplet, &count, alphachanger[2],destlen>=maxdestlen);
295 tripletize(&dest, &triplet, &count, 6, destlen >= maxdestlen);
297 /* Upper 5 bits (really 3) */
298 tripletize(&dest, &triplet, &count, source[i] >> 5, destlen>=maxdestlen);
301 done = ((destlen >= maxdestlen) || (i == sourcelen - 1)) &&
302 (destlen >= mindestlen);
304 tripletize(&dest, &triplet, &count, source[i] & b00011111, done);
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 */
314 tripletize(&dest, &triplet, &count, 5, TRUE); /* uh huh, done */
319 void op_encode_text(void)
321 encodezscii(z_memory + operand[3], 6, 6,
322 (char *) z_memory + operand[0] + operand[2], operand[1]);