Updated interpreters
[projects/chimara/chimara.git] / interpreters / frotz / text.c
index 8145cfea29d6307c3969d0eb246c741e540eeaff..9cccc2c1cb0be5813b07872fa80095f8eabbb50a 100644 (file)
@@ -25,9 +25,11 @@ enum string_type {
 };
 
 extern zword object_name (zword);
+extern zword get_window_font (zword);
 
-static zchar decoded[10];
-static zword encoded[3];
+zchar* decoded;
+zchar* encoded;
+static int resolution;
 
 /* 
  * According to Matteo De Luigi <matteo.de.luigi@libero.it>, 
@@ -35,21 +37,36 @@ static zword encoded[3];
  *   Sat Apr 21, 2001
  */
 static zchar zscii_to_latin1[] = {
-    0xe4, 0xf6, 0xfc, 0xc4, 0xd6, 0xdc, 0xdf, 0xbb,
-    0xab, 0xeb, 0xef, 0xff, 0xcb, 0xcf, 0xe1, 0xe9,
-    0xed, 0xf3, 0xfa, 0xfd, 0xc1, 0xc9, 0xcd, 0xd3,
-    0xda, 0xdd, 0xe0, 0xe8, 0xec, 0xf2, 0xf9, 0xc0,
-    0xc8, 0xcc, 0xd2, 0xd9, 0xe2, 0xea, 0xee, 0xf4,
-    0xfb, 0xc2, 0xca, 0xce, 0xd4, 0xdb, 0xe5, 0xc5,
-    0xf8, 0xd8, 0xe3, 0xf1, 0xf5, 0xc3, 0xd1, 0xd5,
-    0xe6, 0xc6, 0xe7, 0xc7, 0xfe, 0xf0, 0xde, 0xd0,
-    0xa3, 0x00, 0x00, 0xa1, 0xbf
+    0x0e4, 0x0f6, 0x0fc, 0x0c4, 0x0d6, 0x0dc, 0x0df, 0x0bb,
+    0x0ab, 0x0eb, 0x0ef, 0x0ff, 0x0cb, 0x0cf, 0x0e1, 0x0e9,
+    0x0ed, 0x0f3, 0x0fa, 0x0fd, 0x0c1, 0x0c9, 0x0cd, 0x0d3,
+    0x0da, 0x0dd, 0x0e0, 0x0e8, 0x0ec, 0x0f2, 0x0f9, 0x0c0,
+    0x0c8, 0x0cc, 0x0d2, 0x0d9, 0x0e2, 0x0ea, 0x0ee, 0x0f4,
+    0x0fb, 0x0c2, 0x0ca, 0x0ce, 0x0d4, 0x0db, 0x0e5, 0x0c5,
+    0x0f8, 0x0d8, 0x0e3, 0x0f1, 0x0f5, 0x0c3, 0x0d1, 0x0d5,
+    0x0e6, 0x0c6, 0x0e7, 0x0c7, 0x0fe, 0x0f0, 0x0de, 0x0d0,
+    0x0a3, 0x153, 0x152, 0x0a1, 0x0bf
 };
 
+/*
+ * init_text
+ *
+ * Initialize text variables.
+ *
+ */
+
+void init_text (void)
+{
+    decoded = NULL;
+    encoded = NULL;
+
+    resolution = 0;
+}
+
 /*
  * translate_from_zscii
  *
- * Map a ZSCII character onto the ISO Latin-1 alphabet.
+ * Map a ZSCII character into Unicode.
  *
  */
 
@@ -78,7 +95,10 @@ zchar translate_from_zscii (zbyte c)
 
                LOW_WORD (addr, unicode)
 
-               return (unicode < 0x100) ? (zchar) unicode : '?';
+               if (unicode < 0x20)
+                       return '?';
+
+               return unicode;
 
            } else return '?';
 
@@ -86,36 +106,26 @@ zchar translate_from_zscii (zbyte c)
 
            if (c <= 0xdf) {
 
-               if (c == 0xdc || c == 0xdd)     /* Oe and oe ligatures */
-                   return '?';                 /* are not ISO-Latin 1 */
-
                return zscii_to_latin1[c - 0x9b];
 
            } else return '?';
     }
 
-    return c;
+    return (zchar) c;
 
 }/* translate_from_zscii */
 
 /*
- * translate_to_zscii
+ * unicode_to_zscii
  *
- * Map an ISO Latin-1 character onto the ZSCII alphabet.
+ * Convert a Unicode character to ZSCII, returning 0 on failure.
  *
  */
 
-zbyte translate_to_zscii (zchar c)
+zbyte unicode_to_zscii (zchar c)
 {
     int i;
 
-    if (c == ZC_SINGLE_CLICK)
-       return 0xfe;
-    if (c == ZC_DOUBLE_CLICK)
-       return 0xfd;
-    if (c == ZC_MENU_CLICK)
-       return 0xfc;
-
     if (c >= ZC_LATIN1_MIN) {
 
        if (hx_unicode_table != 0) {    /* game has its own Unicode table */
@@ -137,7 +147,7 @@ zbyte translate_to_zscii (zchar c)
 
            }
 
-           return '?';
+           return 0;
 
        } else {                        /* game uses standard set */
 
@@ -145,16 +155,39 @@ zbyte translate_to_zscii (zchar c)
                if (c == zscii_to_latin1[i - 0x9b])
                    return (zbyte) i;
 
-           return '?';
+           return 0;
 
        }
     }
 
-    if (c == 0)                /* Safety thing from David Kinder */
-       c = '?';        /* regarding his Unicode patches */
-                       /* Sept 15, 2002 */
+    return (zbyte) c;
 
-    return c;
+}/* unicode_to_zscii */
+
+/*
+ * translate_to_zscii
+ *
+ * Map a Unicode character onto the ZSCII alphabet.
+ *
+ */
+
+zbyte translate_to_zscii (zchar c)
+{
+
+    if (c == ZC_SINGLE_CLICK)
+       return 0xfe;
+    if (c == ZC_DOUBLE_CLICK)
+       return 0xfd;
+    if (c == ZC_MENU_CLICK)
+       return 0xfc;
+    if (c == 0)
+       return 0;
+
+    c = unicode_to_zscii (c);
+    if (c == 0)
+       c = '?';
+
+    return (zbyte) c;
 
 }/* translate_to_zscii */
 
@@ -167,6 +200,8 @@ zbyte translate_to_zscii (zchar c)
 
 static zchar alphabet (int set, int index)
 {
+    if (h_version > V1 && set == 2 && index == 1)
+       return 0x0D;            /* always newline */
 
     if (h_alphabet != 0) {     /* game uses its own alphabet */
 
@@ -190,6 +225,66 @@ static zchar alphabet (int set, int index)
 
 }/* alphabet */
 
+/*
+ * find_resolution
+ *
+ * Find the number of bytes used for dictionary resolution.
+ *
+ */
+
+static void find_resolution (void)
+{
+    zword dct = h_dictionary;
+    zword entry_count;
+    zbyte sep_count;
+    zbyte entry_len;
+
+    LOW_BYTE (dct, sep_count)
+    dct += 1 + sep_count;  /* skip word separators */
+    LOW_BYTE (dct, entry_len)
+    dct += 1;              /* skip entry length */
+    LOW_WORD (dct, entry_count)
+    dct += 2;              /* get number of entries */
+
+    if (h_version < V9) {
+
+       resolution = (h_version <= V3) ? 2 : 3;
+
+    } else {
+
+       zword addr = dct;
+       zword code;
+
+       if (entry_count == 0) {
+
+           runtime_error (ERR_DICT_LEN);
+
+       }
+
+       /* check the first word in the dictionary */
+
+       do {
+
+           LOW_WORD (addr, code)
+           addr += 2;
+
+       } while (!(code & 0x8000) && (addr - dct < entry_len + 1));
+
+       resolution = (addr - dct) / 2;
+
+    }
+
+    if (2 * resolution > entry_len) {
+
+       runtime_error (ERR_DICT_LEN);
+
+    }
+
+    decoded = (zchar *)malloc (sizeof (zchar) * (3 * resolution) + 1);
+    encoded = (zchar *)malloc (sizeof (zchar) * resolution);
+
+}/* find_resolution */
+
 /*
  * load_string
  *
@@ -199,9 +294,10 @@ static zchar alphabet (int set, int index)
 
 static void load_string (zword addr, zword length)
 {
-    int resolution = (h_version <= V3) ? 2 : 3;
     int i = 0;
 
+    if (resolution == 0) find_resolution();
+
     while (i < 3 * resolution)
 
        if (i < length) {
@@ -223,29 +319,34 @@ static void load_string (zword addr, zword length)
  * Encode the Unicode text in the global "decoded" string then write
  * the result to the global "encoded" array. (This is used to look up
  * words in the dictionary.) Up to V3 the vocabulary resolution is
- * two, since V4 it is three words. Because each word contains three
- * Z-characters, that makes six or nine Z-characters respectively.
- * Longer words are chopped to the proper size, shorter words are are
- * padded out with 5's. For word completion we pad with 0s and 31s,
- * the minimum and maximum Z-characters.
+ * two, from V4 it is three, and from V9 it is any number of words.
+ * Because each word contains three Z-characters, that makes six or
+ * nine Z-characters respectively. Longer words are chopped to the
+ * proper size, shorter words are are padded out with 5's. For word
+ * completion we pad with 0s and 31s, the minimum and maximum
+ * Z-characters.
  *
  */
 
 static void encode_text (int padding)
 {
-    static zchar again[] = { 'a', 'g', 'a', 'i', 'n', 0 };
-    static zchar examine[] = { 'e', 'x', 'a', 'm', 'i', 'n', 'e', 0 };
-    static zchar wait[] = { 'w', 'a', 'i', 't', 0 };
+    static zchar again[] = { 'a', 'g', 'a', 'i', 'n', 0, 0, 0, 0 };
+    static zchar examine[] = { 'e', 'x', 'a', 'm', 'i', 'n', 'e', 0, 0 };
+    static zchar wait[] = { 'w', 'a', 'i', 't', 0, 0, 0, 0, 0 };
 
-    zbyte zchars[12];
-    const zchar *ptr = decoded;
+    zbyte *zchars;
+    const zchar *ptr;
     zchar c;
-    int resolution = (h_version <= V3) ? 2 : 3;
     int i = 0;
 
+    if (resolution == 0) find_resolution();
+
+    zchars = (zbyte *)malloc (sizeof (zbyte) * 3 * (resolution + 1));
+    ptr = decoded;
+
     /* Expand abbreviations that some old Infocom games lack */
 
-    if (f_setup.expand_abbreviations)
+    if (option_expand_abbreviations && (h_version <= V8))
 
        if (padding == 0x05 && decoded[1] == 0)
 
@@ -264,6 +365,14 @@ static void encode_text (int padding)
            int index, set;
            zbyte c2;
 
+           if (c == 32) {
+
+               zchars[i++] = 0;
+
+               continue;
+
+           }
+
            /* Search character in the alphabet */
 
            for (set = 0; set < 3; set++)
@@ -304,10 +413,12 @@ static void encode_text (int padding)
 
     encoded[resolution - 1] |= 0x8000;
 
+    free (zchars);
+
 }/* encode_text */
 
 /*
- * z_check_unicode, test if a unicode character can be read and printed.
+ * z_check_unicode, test if a unicode character can be printed (bit 0) and read (bit 1).
  *
  *     zargs[0] = Unicode
  *
@@ -316,15 +427,19 @@ static void encode_text (int padding)
 void z_check_unicode (void)
 {
     zword c = zargs[0];
+    zword result = 0;
 
-    if (c >= 0x20 && c <= 0x7e)
-       store (3);
-    else if (c == 0xa0)
-       store (1);
-    else if (c >= 0xa1 && c <= 0xff)
-       store (3);
+    if (c <= 0x1f)
+    {
+       if ((c == 0x08) || (c == 0x0d) || (c == 0x1b))
+           result = 2;
+    }
+    else if (c <= 0x7e)
+       result = 3;
     else
-       store (0);
+       result = 1; // we support unicode
+
+    store (result);
 
 }/* z_check_unicode */
 
@@ -349,7 +464,7 @@ void z_encode_text (void)
 
     encode_text (0x05);
 
-    for (i = 0; i < 3; i++)
+    for (i = 0; i < resolution; i++)
        storew ((zword) (zargs[3] + 2 * i), encoded[i]);
 
 }/* z_encode_text */
@@ -391,6 +506,8 @@ static void decode_text (enum string_type st, zword addr)
     ptr = NULL;                /* makes compilers shut up */
     byte_addr = 0;
 
+    if (resolution == 0) find_resolution();
+
     /* Calculate the byte address if necessary */
 
     if (st == ABBREVIATION)
@@ -405,8 +522,12 @@ static void decode_text (enum string_type st, zword addr)
            byte_addr = (long) addr << 2;
        else if (h_version <= V7)
            byte_addr = ((long) addr << 2) + ((long) h_strings_offset << 3);
-       else /* h_version == V8 */
+       else if (h_version <= V8)
            byte_addr = (long) addr << 3;
+       else /* h_version == V9 */ {
+           long indirect = (long) addr << 2;
+           HIGH_LONG(indirect, byte_addr);
+       }
 
        if (byte_addr >= story_size)
            runtime_error (ERR_ILL_PRINT_ADDR);
@@ -439,6 +560,7 @@ static void decode_text (enum string_type st, zword addr)
 
            zword abbr_addr;
            zword ptr_addr;
+           zchar zc;
 
            c = (code >> i) & 0x1f;
 
@@ -499,8 +621,30 @@ static void decode_text (enum string_type st, zword addr)
 
            case 3:     /* ZSCII character - second part */
 
-               c2 = translate_from_zscii ((prev_c << 5) | c);
-               outchar (c2);
+               zc = (prev_c << 5) | c;
+
+               if (zc > 767) { /* Unicode escape */
+
+                   while (zc-- > 767) {
+
+                       if (st == LOW_STRING || st == VOCABULARY) {
+                           LOW_WORD (addr, c2)
+                           addr += 2;
+                       } else if (st == HIGH_STRING || st == ABBREVIATION) {
+                           HIGH_WORD (byte_addr, c2)
+                           byte_addr += 2;
+                       } else
+                           CODE_WORD (c2)
+
+                       outchar (c2 ^ 0xFFFF);
+                   }
+
+               } else {
+
+                   c2 = translate_from_zscii (zc);
+                   outchar (c2);
+
+               }
 
                status = 0;
                break;
@@ -760,7 +904,10 @@ void print_string (const char *s)
 void z_print_unicode (void)
 {
 
-    print_char ((zargs[0] <= 0xff) ? zargs[0] : '?');
+    if (zargs[0] < 0x20)
+       print_char ('?');
+    else
+       print_char (zargs[0]);
 
 }/* z_print_unicode */
 
@@ -786,12 +933,13 @@ static zword lookup_text (int padding, zword dct)
     zword addr;
     zbyte entry_len;
     zbyte sep_count;
-    int resolution = (h_version <= V3) ? 2 : 3;
     int entry_number;
     int lower, upper;
     int i;
     bool sorted;
 
+    if (resolution == 0) find_resolution();
+
     encode_text (padding);
 
     LOW_BYTE (dct, sep_count)          /* skip word separators */
@@ -1061,6 +1209,8 @@ int completion (const zchar *buffer, zchar *result)
 
     *result = 0;
 
+    if (resolution == 0) find_resolution();
+
     /* Copy last word to "decoded" string */
 
     len = 0;
@@ -1069,7 +1219,7 @@ int completion (const zchar *buffer, zchar *result)
 
        if (c != ' ') {
 
-           if (len < 9)
+           if (len < 3 * resolution)
                decoded[len++] = c;
 
        } else len = 0;
@@ -1107,3 +1257,74 @@ int completion (const zchar *buffer, zchar *result)
     return (minaddr == maxaddr) ? 0 : 1;
 
 }/* completion */
+
+/*
+ * unicode_tolower
+ *
+ * Convert a Unicode character to lowercase.
+ * Taken from Zip2000 by Kevin Bracey.
+ *
+ */
+
+zchar unicode_tolower (zchar c)
+{
+    const static unsigned char tolower_basic_latin[0x100] = {
+       0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
+       0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+       0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
+       0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+       0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+       0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x5B,0x5C,0x5D,0x5E,0x5F,
+       0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+       0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
+       0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
+       0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,
+       0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,
+       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+       0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+       0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xD7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xDF,
+       0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+       0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
+    };
+    const static unsigned char tolower_latin_extended_a[0x80] = {
+       0x01,0x01,0x03,0x03,0x05,0x05,0x07,0x07,0x09,0x09,0x0B,0x0B,0x0D,0x0D,0x0F,0x0F,
+       0x11,0x11,0x13,0x13,0x15,0x15,0x17,0x17,0x19,0x19,0x1B,0x1B,0x1D,0x1D,0x1F,0x1F,
+       0x21,0x21,0x23,0x23,0x25,0x25,0x27,0x27,0x29,0x29,0x2B,0x2B,0x2D,0x2D,0x2F,0x2F,
+       0x00,0x31,0x33,0x33,0x35,0x35,0x37,0x37,0x38,0x3A,0x3A,0x3C,0x3C,0x3E,0x3E,0x40,
+       0x40,0x42,0x42,0x44,0x44,0x46,0x46,0x48,0x48,0x49,0x4B,0x4B,0x4D,0x4D,0x4F,0x4F,
+       0x51,0x51,0x53,0x53,0x55,0x55,0x57,0x57,0x59,0x59,0x5B,0x5B,0x5D,0x5D,0x5F,0x5F,
+       0x61,0x61,0x63,0x63,0x65,0x65,0x67,0x67,0x69,0x69,0x6B,0x6B,0x6D,0x6D,0x6F,0x6F,
+       0x71,0x71,0x73,0x73,0x75,0x75,0x77,0x77,0x00,0x7A,0x7A,0x7C,0x7C,0x7E,0x7E,0x7F
+    };
+    const static unsigned char tolower_greek[0x50] = {
+       0x80,0x81,0x82,0x83,0x84,0x85,0xAC,0x87,0xAD,0xAE,0xAF,0x8B,0xCC,0x8D,0xCD,0xCE,
+       0x90,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+       0xC0,0xC1,0xA2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xAC,0xAD,0xAE,0xAF,
+       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF
+    };
+    const static unsigned char tolower_cyrillic[0x60] = {
+       0x00,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,
+       0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+       0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
+       0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+       0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
+       0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F
+    };
+
+    if (c < 0x0100)
+       c = tolower_basic_latin[c];
+    else if (c == 0x0130)
+       c = 0x0069;     /* Capital I with dot -> lower case i */
+    else if (c == 0x0178)
+       c = 0x00FF;     /* Capital Y diaeresis -> lower case y diaeresis */
+    else if (c < 0x0180)
+       c = tolower_latin_extended_a[c-0x100] + 0x100;
+    else if (c >= 0x380 && c < 0x3D0)
+       c = tolower_greek[c-0x380] + 0x300;
+    else if (c >= 0x400 && c < 0x460)
+       c = tolower_cyrillic[c-0x400] + 0x400;
+
+    return c;
+}
+