/* * A Z-Machine * Copyright (C) 2000 Andrew Hunter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Convert ZSCII strings to ASCII */ #include #include "zmachine.h" #include "zscii.h" static unsigned int *buf = NULL; static int maxlen = 0; /* Default tables */ static unsigned int alpha_a[32] = { 0,0,0,0,0,0, 97, 98, 99,100,101,102,103,104,105,106,107,108,109, 110,111,112,113,114,115,116,117,118,119,120,121,122 }; static unsigned int alpha_b[32] = { 0,0,0,0,0,0, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90 }; static unsigned int alpha_c[32] = { 0,0,0,0,0,0, 0, 10, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 46, 44, 33, 63, 95, 35, 39, 34, 47, 92, 45, 58, 40, 41 }; static unsigned int* convert_table[3] = { alpha_a, alpha_b, alpha_c }; /* Table that maps alphabet + character to ZSCII character */ static unsigned int** convert = convert_table; int zscii_unicode_table[256] = { 0x3f,0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f,0x3f, /* 000-007 */ 0x3f,0x09,0x0a,0x20, 0x3f,0x0a,0x3f,0x3f, /* 008-015 */ 0x3f,0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f,0x3f, /* 016-023 */ 0x3f,0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f,0x3f, /* 024-031 */ 0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27, /* 032-039 */ 0x28,0x29,0x2a,0x2b, 0x2c,0x2d,0x2e,0x2f, /* 040-047 */ 0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37, /* 048-055 */ 0x38,0x39,0x3a,0x3b, 0x3c,0x3d,0x3e,0x3f, /* 056-063 */ 0x40,0x41,0x42,0x43, 0x44,0x45,0x46,0x47, /* 064-071 */ 0x48,0x49,0x4a,0x4b, 0x4c,0x4d,0x4e,0x4f, /* 072-079 */ 0x50,0x51,0x52,0x53, 0x54,0x55,0x56,0x57, /* 080-087 */ 0x58,0x59,0x5a,0x5b, 0x5c,0x5d,0x5e,0x5f, /* 088-095 */ 0x60,0x61,0x62,0x63, 0x64,0x65,0x66,0x67, /* 096-103 */ 0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f, /* 104-111 */ 0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77, /* 112-119 */ 0x78,0x79,0x7a,0x7b, 0x7c,0x7d,0x7e,0x7f, /* 120-127 */ 0x3f,0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f,0x3f, /* 128-135 */ 0x3f,0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f,0x3f, /* 136-143 */ 0x3f,0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f,0x3f, /* 144-151 */ 0x3f,0x3f,0x3f,0xe4, 0xf6,0xfc,0xc4,0xd6, /* 152-159 */ 0xdc,0xdf,0xbb,0xab, 0xeb,0xef,0xff,0xcb, /* 160-167 */ 0xcf,0xe1,0xe9,0xed, 0xf3,0xfa,0xfd,0xc1, /* 168-175 */ 0xc9,0xcd,0xd3,0xda, 0xdd,0xe0,0xe8,0xec, /* 176-183 */ 0xf2,0xf9,0xc0,0xc8, 0xcc,0xd2,0xd9,0xe2, /* 184-191 */ 0xea,0xee,0xf4,0xfb, 0xc2,0xca,0xce,0xd4, /* 192-199 */ 0xdb,0xe5,0xc5,0xf8, 0xd8,0xe3,0xf1,0xf5, /* 200-207 */ 0xc3,0xd1,0xd5,0xe6, 0xc6,0xe7,0xc7,0xfe, /* 208-215 */ 0xf0,0xde,0xd0,0xa3, 0x153,0x152,0xa1,0xbf, /* 216-223 */ 0x3f,0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f,0x3f, /* 224-231 */ 0x3f,0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f,0x3f, /* 232-239 */ 0x3f,0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f,0x3f, /* 240-247 */ 0x3f,0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f,0x3f /* 248-255 */ }; /* Table that maps (8-bit) ZSCII to unicode */ int* zscii_unicode = zscii_unicode_table; #ifdef DEBUG char* zscii_to_ascii(ZByte* string, int* len) { static char* cbuf = NULL; int x; zscii_to_unicode(string, len); cbuf = realloc(cbuf, 2); for (x=0; buf[x] != 0; x++) { cbuf = realloc(cbuf, (x+2)); cbuf[x] = zscii_get_char(buf[x]); } cbuf[x] = 0; return cbuf; } #endif /* * Convert a ZSCII string (packed) to Unicode (unpacked) */ unsigned int* zscii_to_unicode(ZByte* string, int* len) { int abet = 0; int x = 0; int y = 0; int zlen, z; ZWord zchar = 0; int fin = 0; zlen = 0; if (maxlen <= 0) { maxlen += 512; buf = realloc(buf, sizeof(int)*maxlen); } while (!fin) { ZUWord word; fin = (string[x]&0x80) != 0; word = ((unsigned)string[x]<<8)|(unsigned)string[x+1]; for (z=0; z<3; z++) { int c; c = (word&0x7c00)>>10; if ((y+8) > maxlen) { maxlen += 1024; buf = realloc(buf, sizeof(int)*maxlen); } switch (abet) { /* Standard alphabets */ case 2: if (c == 6) { /* Next 2 chars make up a Z-Character */ abet=4; break; } case 1: case 0: if (c >= 6) { if (convert[abet][c] < 256) buf[y++] = zscii_unicode[convert[abet][c]]; else buf[y++] = convert[abet][c]; if (buf[y-1] == 9) { y--; buf[y++] = ' '; buf[y++] = ' '; buf[y++] = ' '; } if (buf[y-1] == 11) { y--; buf[y++] = ' '; buf[y++] = ' '; } abet=0; } else { switch (c) { case 0: /* Space */ buf[y++] = ' '; break; case 1: /* Next char is an abbreviation */ case 2: case 3: zchar=(c-1)<<5; abet=3; break; case 4: /* Shift to alphabet 1 */ abet=1; break; case 5: /* Shift to alphabet 2 */ abet=2; break; default: /* Ignore */ break; } } break; case 3: /* Abbreviation */ { int z; int* abbrev; int addr; ZByte* table; zchar |= c; /* * Annoyingly, some games seem to rewrite the abbreviation * table at runtime. This may cause weird things to happen * if a game is sick enough to use abbreviations in * abbreviations, too. */ table = machine.memory + GetWord(machine.header, ZH_abbrevs); addr = ((table[zchar*2]<<9)|(table[zchar*2+1]<<1)); if (machine.abbrev_addr[zchar] != addr) { if (addr >= 0 && addr < machine.story_length) { /* * Hack, this function was never designed to be called * recursively */ unsigned int* oldbuf; int oldmaxlen; int ablen; oldbuf = buf; oldmaxlen = maxlen; maxlen = 0; buf = NULL; zlen = y; abbrev = zscii_to_unicode((ZByte*)machine.memory + addr, &ablen); buf = oldbuf; maxlen = oldmaxlen; for (z=0; abbrev[z]!=0; z++) zlen++; while ((zlen+2) > maxlen) { maxlen += 1024; buf = realloc(buf, sizeof(int)*(maxlen)); } for (z=0; abbrev[z] != 0; z++) { buf[y++] = abbrev[z]; } free(abbrev); } else { zmachine_fatal("Found a bad entry in the abbreviation table for entry %i", zchar); } } else if (machine.abbrev[zchar]) { abbrev = machine.abbrev[zchar]; for (z=0; abbrev[z]!=0; z++) zlen++; while ((zlen+2) > maxlen) { maxlen+=1024; buf = realloc(buf, sizeof(int)*(maxlen)); } for (z=0; abbrev[z] != 0; z++) { buf[y++] = abbrev[z]; } } else { zmachine_fatal("Found a bad entry in the abbreviation table for entry %i", zchar); } } abet = 0; break; case 4: /* First byte of a Z-Char */ zchar = c<<5; abet = 5; break; case 5: /* Second byte of a Z-Char */ zchar |= c; abet = 0; if (zchar < 256) { switch(zchar) { default: buf[y++] = zscii_unicode[zchar]; if (buf[y-1] == 9) { y--; buf[y++] = ' '; buf[y++] = ' '; buf[y++] = ' '; } } } else { #ifdef SPEC_11 if (zchar > 767) { /* Unicode character, this is a bit of a PITA */ int ulen; int i; ulen = zchar - 767; x += 2; for (i=0; i maxlen) { maxlen += 1024; buf = realloc(buf, sizeof(int)*maxlen); } buf[y++] = (~((((unsigned)string[x])<<8)|((unsigned)string[x+1])))&0xffff; x += 2; } if (z == 2) goto onward; /* Blech */ } #else buf[y++] = zchar; #endif } break; } word <<= 5; } x += 2; onward: ; /* Stupid ANSI standard, or is this an ISO thing? */ } *len = x; buf[y] = 0; return buf; } /* * Pack a ZSCII string, suitable for comparing to a dictionary item * * A packlen of 6 gives us v3 format, and 9 gives us v5 */ static unsigned char zscii_table[256] = { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 8 */ 0x00,0x00,0xc7,0x00, 0x00,0x00,0x00,0x00, /* 16 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 24 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 32 */ 0x00,0xd4,0xd9,0xd7, 0x00,0x00,0x00,0xd8, /* 40 */ 0xde,0xdf,0x00,0x00, 0xd3,0xdc,0xd2,0xda, /* 48 */ 0xc8,0xc9,0xca,0xcb, 0xcc,0xcd,0xce,0xcf, /* 56 */ 0xd0,0xd1,0xdd,0x00, 0x00,0x00,0x00,0xd5, /* 64 */ 0x00,0x86,0x87,0x88, 0x89,0x8a,0x8b,0x8c, /* 72 */ 0x8d,0x8e,0x8f,0x90, 0x91,0x92,0x93,0x94, /* 80 */ 0x95,0x96,0x97,0x98, 0x99,0x9a,0x9b,0x9c, /* 88 */ 0x9d,0x9e,0x9f,0x00, 0xdb,0x00,0x00,0xd6, /* 96 */ 0x00,0x46,0x47,0x48, 0x49,0x4a,0x4b,0x4c, /* 104 */ 0x4d,0x4e,0x4f,0x50, 0x51,0x52,0x53,0x54, /* 112 */ 0x55,0x56,0x57,0x58, 0x59,0x5a,0x5b,0x5c, /* 120 */ 0x5d,0x5e,0x5f,0x00, 0x00,0x00,0x00,0x00, /* 128 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 8 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 16 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 24 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 32 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 40 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 48 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 56 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 64 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 72 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 80 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 88 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 96 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 104 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 112 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 120 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 /* 128 */ }; /* Table that maps 8-bit characters to packed characters. Lower 6 bits are the characters, the other bits are the alphabet */ static unsigned char* zscii = zscii_table; void pack_zscii(unsigned int* string, int strlen, ZByte* packed, int packlen) { int zpos, byte; int strpos; int wordlen; char zchr[40]; strpos = 0; for (zpos=0; zpos= strlen) { /* Add padding */ zchr[zpos++] = 5; } else { unsigned char zchar; int alphabet, chr; /* Convert the character to ZSCII */ zchar = zscii_get_char(string[strpos]); /* Encoding using an alphabet/character encoding if available */ alphabet = zscii[zchar]>>6; chr = zscii[zchar]&0x1f; switch (alphabet) { case 0: /* Encode directly */ zchr[zpos++] = 5; zchr[zpos++] = 6; zchr[zpos++] = zchar>>5; zchr[zpos++] = zchar&0x1f; break; case 1: zchr[zpos++]= chr; break; case 2: zchr[zpos++] = 4; zchr[zpos++] = chr; break; case 3: zchr[zpos++] = 5; zchr[zpos++] = chr; break; /* Other cases are impossible. IMPOSSIBLE I SAY. There is no spoon. */ } /* * (In spec 1.1, it's possible to encode an arbitrary unicode character, but not * sensible unless two-character commands are demanded by the story, so we don't * bother) */ } /* Next character in the string */ strpos++; } /* Pack the Z-characters */ wordlen = packlen/3; for (byte=0; byte>3); packed[(byte<<1)+1] = (zchr[byte*3+1]<<5)|zchr[byte*3+2]; } packed[wordlen*2-2] |= 0x80; } /* * Works out the length (in characters) of a packed Z-string */ int zstrlen(ZByte* string) { int x = 0; /* * ObRant: The new Unicode encoding is poorly thought out... * it *MASSIVELY* complicates string decoding, because suddenly * the top bit of a packed word doesn't always indicate the end * of a string. Well, massively is relative, o'course. Practically * any increase is massive over two lines of code :-/. HUGE PITA. */ #ifdef ZSPEC_11 int pos = 0; int buf[3] = { 0,0,0 }; while ((string[x]&0x80) == 0) { /* ARRRGH! */ ZUWord word; int y, a, b; word = (string[x]<<8)|string[x]; a = 1; b = 2; for (y=0; y<3; y++) { a++; if (a == 3) a = 0; b++; if (b == 3) b = 0; buf[y] = (word&0x1f); word >>= 5; if (buf[z] == 6) { int ulen; ulen = (buf[a]<<5)|buf[b]; } } x += 2; } #else /* Sniff. Easy to debug. Easy to write. */ while ((string[x]&0x80) == 0) x+=2; #endif return x*3+3; } /* * Installs the alphabet table associated with the currently loaded story */ void zscii_install_alphabet(void) { if (ReadByte(0)>=5) { ZUWord table; table = Word(ZH_alphatable); if (table) { static unsigned int** conv = NULL; static unsigned char* zsc = NULL; ZByte* alpha; int x, y; int alphabet, character; alpha = Address(table); if (conv == NULL) { conv = malloc(sizeof(int*)*3); for (x=0; x<3; x++) { conv[x] = malloc(sizeof(int)*32); for (y=0; y<32; y++) conv[x][y] = 0; } } zsc = realloc(zsc, sizeof(char)*256); for (x=0; x<256; x++) zsc[x] = 0x0; for (alphabet=0; alphabet<3; alphabet++) { /* First 6 characters are always 0 */ for (character=0; character<6; character++) conv[alphabet][character] = 0; /* Conversion mapping for the alphabet itself */ for (character=0; character<26; character++) { conv[alphabet][character+6] = *(alpha++); zsc[conv[alphabet][character+6]] = (character+6)|((alphabet+1)<<6); } } /* These characters are always newline/space */ zsc[conv[2][7]] = 0; zsc[conv[2][6]] = 0; conv[2][7] = 10; conv[2][6] = 32; zsc[10] = 7|(3<<6); zsc[32] = 6|(3<<6); convert = conv; zscii = zsc; } else { convert = convert_table; zscii = zscii_table; } } else { convert = convert_table; zscii = zscii_table; } }