/* ------------------------------------------------------------------------- */
/*   "zcode" : Z-opcodes, text translation and abbreviation routines         */
/*                                                                           */
/*   Part of Inform release 5                                                */
/*                                                                           */
/*   On non-ASCII machines, translate_to_ascii must be altered               */
/* ------------------------------------------------------------------------- */

#include "header.h"

int32 total_chars_trans, total_bytes_trans, trans_length;
int chars_lookup[256];
int abbrevs_lookup[256], almade_flag=0;

extern int translate_to_ascii(char c)
{   return((int) c);
}

const char *alphabet[3] = {
   "abcdefghijklmnopqrstuvwxyz",
   "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
   " ^0123456789.,!?_#'~/\\-:()"
};

extern void make_lookup(void)
{   int i, j, k;
   for (j=0; j<256; j++)
   {   chars_lookup[j]=127; abbrevs_lookup[j]= -1; }
   for (j=0; j<3; j++)
       for (k=0; k<26; k++)
       {   i=(int) ((alphabet[j])[k]);
           chars_lookup[i]=k+j*26;
       }
}

extern void make_abbrevs_lookup(void)
{   int i, j, k, l; char p[MAX_ABBREV_LENGTH]; char *p1, *p2;
   do
   { for (i=0, j=0; j<no_abbrevs; j++)
       for (k=j+1; k<no_abbrevs; k++)
       {   p1=(char *)abbreviations_at+j*MAX_ABBREV_LENGTH;
           p2=(char *)abbreviations_at+k*MAX_ABBREV_LENGTH;
           if (strcmp(p1,p2)<0)
           {   i=1; strcpy(p,p1); strcpy(p1,p2); strcpy(p2,p);
               l=abbrev_values[j]; abbrev_values[j]=abbrev_values[k];
               abbrev_values[k]=l;
               l=abbrev_quality[j]; abbrev_quality[j]=abbrev_quality[k];
               abbrev_quality[k]=l;
           }
       }
   } while (i==1);
   for (j=no_abbrevs-1; j>=0; j--)
   {   p1=(char *)abbreviations_at+j*MAX_ABBREV_LENGTH;
       abbrevs_lookup[p1[0]]=j;
       abbrev_freqs[j]=0;
   }
   almade_flag=1;
}

static int z_chars[3], uptothree;
static int total_zchars_trans;
static unsigned char *text_pc;

static void write_z_char(int i)
{   uint32 j;
   total_zchars_trans++;
   z_chars[uptothree++]=(i%32);
   if (uptothree!=3) return;
   j= z_chars[0]*0x0400 + z_chars[1]*0x0020 + z_chars[2];
   text_pc[0] = j/256;
   text_pc[1] = j%256;
   uptothree=0; text_pc+=2;
   total_bytes_trans+=2;
}

static void end_z_chars(void)
{   unsigned char *p;
   trans_length=total_zchars_trans-trans_length;
   while (uptothree!=0) write_z_char(5);
   p=(unsigned char *) text_pc;
   *(p-2)= *(p-2)+128;
}


static int try_abbreviations_from(unsigned char *text, int i, int from)
{   int j, k; char *p, c;
   c=text[i];
   for (j=from, p=(char *)abbreviations_at+from*MAX_ABBREV_LENGTH;
        (j<no_abbrevs)&&(c==p[0]); j++, p+=MAX_ABBREV_LENGTH)
   {   if (text[i+1]==p[1])
       {   for (k=2; p[k]!=0; k++)
               if (text[i+k]!=p[k]) goto NotMatched;
           for (k=0; p[k]!=0; k++) text[i+k]=1;
           abbrev_freqs[j]++;
           return(j);
           NotMatched: ;
       }
   }
   return(-1);
}

static int32 text_transcribed = 0;

extern zip *translate_text(zip *p, char *s_text)
{   int i, j, k, newa, cc, value, value2;
   unsigned char *text;

   if (s_text[0]==0) error("The empty string \"\" is illegal");

   trans_length=total_zchars_trans;

   if ((almade_flag==0)&&(no_abbrevs!=0)&&(abbrev_mode!=0))
       make_abbrevs_lookup();

   if ((abbrev_mode!=0)&&(store_the_text==1))
   {   if (pass_number==1)
       {   text_transcribed += strlen(s_text)+2;
           if (text_transcribed >= MAX_TRANSCRIPT_SIZE)
               memoryerror("MAX_TRANSCRIPT_SIZE", MAX_TRANSCRIPT_SIZE);
       }
       else
       {   sprintf(all_text_p, "%s\n\n", s_text);
           all_text_p+=strlen(all_text_p);
       }
   }

   text=(unsigned char *) s_text;

   uptothree=0; text_pc=(unsigned char *) p;
   for (i=0; text[i]!=0; i++)
   {   total_chars_trans++;
       if (double_spaced==1)
       {   if ((text[i]=='.')&&(text[i+1]==' ')&&(text[i+2]==' '))
               text[i+2]=1;
       }
       if ((economy_mode==1)&&(abbrev_mode!=0)
           &&((k=abbrevs_lookup[text[i]])!=-1))
       {   if ((j=try_abbreviations_from(text, i, k))!=-1)
           {   if (j<32) { write_z_char(2); write_z_char(j); }
               else { write_z_char(3); write_z_char(j-32); }
           }
       }
       if (text[i]=='@')
       {   if (text[i+1]=='@')
           {   i+=2;
               value=atoi((char *) (text+i));
               write_z_char(5); write_z_char(6);
               write_z_char(value/32); write_z_char(value%32);
               while (isdigit(text[i])) i++; i--;
           }
           else
           {   value= -1;
               switch(text[i+1])
               {   case '0': value=0; break;
                   case '1': value=1; break;
                   case '2': value=2; break;
                   case '3': value=3; break;
                   case '4': value=4; break;
                   case '5': value=5; break;
                   case '6': value=6; break;
                   case '7': value=7; break;
                   case '8': value=8; break;
                   case '9': value=9; break;
               }
               value2= -1;
               switch(text[i+2])
               {   case '0': value2=0; break;
                   case '1': value2=1; break;
                   case '2': value2=2; break;
                   case '3': value2=3; break;
                   case '4': value2=4; break;
                   case '5': value2=5; break;
                   case '6': value2=6; break;
                   case '7': value2=7; break;
                   case '8': value2=8; break;
                   case '9': value2=9; break;
               }
               if ((value!=-1)&&(value2!=-1))
               {   i++; i++;
                   write_z_char(1); write_z_char(value*10+value2);
               }
           }
       }
       else
       {   if (text[i]!=1)
           {   if (text[i]==' ') write_z_char(0);
               else
               {   cc=chars_lookup[(int) (text[i])];
                   if (cc==127)
                   {   write_z_char(5); write_z_char(6);
                       j=translate_to_ascii(text[i]);
                       write_z_char(j/32); write_z_char(j%32);
                   }
                   else
                   {   newa=cc/26; value=cc%26;
                       if (newa==1) write_z_char(4);
                       if (newa==2) write_z_char(5);
                       write_z_char(value+6);
                   }
               }
           }
       }
   }
   end_z_chars();
   return((zip *) text_pc);
}

/* ------------------------------------------------------------------------- */
/*   The (static) Z-code database (using a table adapted from that in the    */
/*   InfoToolkit disassembler "txd")                                         */
/* ------------------------------------------------------------------------- */

static opcode the_opcode(int i,char *s,int k,int l,int m)
{   opcode op; op.name=(zip *)s; op.code=i; op.type1=k; op.type2=l; op.no=m;
   return(op);
}

extern opcode opcs(int32 i)
{
  switch(i)
   {
   case  0: return(the_opcode(0x01, "je",              BRANCH,   NONE, TWO));
   case  1: return(the_opcode(0x02, "jl",              BRANCH,   NONE, TWO));
   case  2: return(the_opcode(0x03, "jg",              BRANCH,   NONE, TWO));
   case  3: return(the_opcode(0x04, "dec_chk",         BRANCH,    VAR, TWO));
   case  4: return(the_opcode(0x05, "inc_chk",         BRANCH,    VAR, TWO));
   case  5: return(the_opcode(0x06, "jin",             BRANCH,   NONE, TWO));
   case  6: return(the_opcode(0x07, "test",            BRANCH,   NONE, TWO));
   case  7: return(the_opcode(0x08, "or",               STORE,   NONE, TWO));
   case  8: return(the_opcode(0x09, "and",              STORE,   NONE, TWO));
   case  9: return(the_opcode(0x0A, "test_attr",       BRANCH,   NONE, TWO));
   case 10: return(the_opcode(0x0B, "set_attr",          NONE,   NONE, TWO));
   case 11: return(the_opcode(0x0C, "clear_attr",        NONE,   NONE, TWO));
   case 12: return(the_opcode(0x0D, "store",             NONE,    VAR, TWO));
   case 13: return(the_opcode(0x0D, "store",             NONE,    VAR, VARI));
   case 14: return(the_opcode(0x0E, "insert_obj",        NONE,   NONE, TWO));
   case 15: return(the_opcode(0x0F, "loadw",            STORE,   NONE, TWO));
   case 16: return(the_opcode(0x10, "loadb",            STORE,   NONE, TWO));
   case 17: return(the_opcode(0x11, "get_prop",         STORE,   NONE, TWO));
   case 18: return(the_opcode(0x12, "get_prop_addr",    STORE,   NONE, TWO));
   case 19: return(the_opcode(0x13, "get_next_prop",    STORE,   NONE, TWO));
   case 20: return(the_opcode(0x14, "add",              STORE,   NONE, TWO));
   case 21: return(the_opcode(0x15, "sub",              STORE,   NONE, TWO));
   case 22: return(the_opcode(0x16, "mul",              STORE,   NONE, TWO));
   case 23: return(the_opcode(0x17, "div",              STORE,   NONE, TWO));
   case 24: return(the_opcode(0x18, "mod",              STORE,   NONE, TWO));

   case 25: return(the_opcode(0x01, "je",              BRANCH,   NONE, VARI));

   case 26: return(the_opcode(0x20, "call",              CALL,   NONE, VARI));
   case 27: return(the_opcode(0x20, "call",             STORE,   NONE, VARI));
   case 28: return(the_opcode(0x21, "storew",            NONE,   NONE, VARI));
   case 29: return(the_opcode(0x22, "storeb",            NONE,   NONE, VARI));
   case 30: return(the_opcode(0x23, "put_prop",          NONE,   NONE, VARI));
   case 32: return(the_opcode(0x25, "print_char",       PCHAR,   NONE, VARI));
   case 33: return(the_opcode(0x26, "print_num",         NONE,   NONE, VARI));
   case 34: return(the_opcode(0x27, "random",           STORE,   NONE, VARI));
   case 35: return(the_opcode(0x28, "push",              NONE,   NONE, VARI));
   case 36: return(the_opcode(0x29, "pull",              NONE,    VAR, VARI));
   case 37: return(the_opcode(0x2A, "split_window",      NONE,   NONE, VARI));
   case 38: return(the_opcode(0x2B, "set_window",        NONE,   NONE, VARI));
   case 39: return(the_opcode(0x33, "output_stream",     NONE,   NONE, VARI));
   case 40: return(the_opcode(0x34, "input_stream",      NONE,   NONE, VARI));
   case 41: return(the_opcode(0x35, "sound_effect",      NONE,   NONE, VARI));

   case 42: return(the_opcode(0x00, "jz",              BRANCH,   NONE, ONE));
   case 43: return(the_opcode(0x01, "get_sibling",      STORE, OBJECT, ONE));
   case 44: return(the_opcode(0x02, "get_child",        STORE, OBJECT, ONE));
   case 45: return(the_opcode(0x03, "get_parent",       STORE,   NONE, ONE));
   case 46: return(the_opcode(0x04, "get_prop_len",     STORE,   NONE, ONE));
   case 47: return(the_opcode(0x05, "inc",               NONE,    VAR, ONE));
   case 48: return(the_opcode(0x06, "dec",               NONE,    VAR, ONE));
   case 49: return(the_opcode(0x07, "print_addr",        NONE,   NONE, ONE));

   case 50: return(the_opcode(0x09, "remove_obj",        NONE,   NONE, ONE));
   case 51: return(the_opcode(0x0A, "print_obj",         NONE,   NONE, ONE));
   case 52: return(the_opcode(0x0B, "ret",             RETURN,   NONE, ONE));
   case 53: return(the_opcode(0x0C, "jump",              JUMP,   NONE, ONE));
   case 54: return(the_opcode(0x0D, "print_paddr",       NONE,   NONE, ONE));
   case 55: return(the_opcode(0x0E, "load",             STORE,    VAR, ONE));

   case 57: return(the_opcode(0x00, "rtrue",           RETURN,   NONE, ZERO));
   case 58: return(the_opcode(0x01, "rfalse",          RETURN,   NONE, ZERO));
   case 59: return(the_opcode(0x02, "print",             NONE,   TEXT, ZERO));
   case 60: return(the_opcode(0x03, "print_ret",       RETURN,   TEXT, ZERO));
   case 63: return(the_opcode(0x07, "restart",           NONE,   NONE, ZERO));
   case 64: return(the_opcode(0x08, "ret_popped",      RETURN,   NONE, ZERO));
   case 65: return(the_opcode(0x09, "pop",               NONE,   NONE, ZERO));
   case 66: return(the_opcode(0x0A, "quit",              NONE,   NONE, ZERO));
   case 67: return(the_opcode(0x0B, "new_line",          NONE,   NONE, ZERO));
   case 69: return(the_opcode(0x0D, "verify",          BRANCH,   NONE, ZERO));
 }

 if ((version_number==3) && (i==68))
            return(the_opcode(0x0C, "show_status",       NONE,   NONE, ZERO));

 if ((version_number==3) || (version_number==4))
 { switch(i)
   {
   case 31: return(the_opcode(0x24, "read",              NONE,   NONE, VARI));
   case 56: return(the_opcode(0x0F, "not",              STORE,   NONE, ONE));
   case 61: return(the_opcode(0x05, "save",            BRANCH,   NONE, ZERO));
   case 62: return(the_opcode(0x06, "restore",         BRANCH,   NONE, ZERO));
   }
 }

 if (version_number>=4)
 { switch(i)
   {
   case 70: return(the_opcode(0x19, "call_2s",           CALL,   NONE, TWO));

   case 72: return(the_opcode(0x20, "call_vs",           CALL,   NONE, VARI));
   case 89: return(the_opcode(0x2C, "call_vs2",          CALL,   NONE, MANY));
   case 75: return(the_opcode(0x2D, "erase_window",      NONE,   NONE, VARI));
   case 76: return(the_opcode(0x2E, "erase_line",        NONE,   NONE, VARI));
   case 77: return(the_opcode(0x2F, "set_cursor",        NONE,   NONE, VARI));
   case 78: return(the_opcode(0x31, "set_text_style",    NONE,   NONE, VARI));
   case 79: return(the_opcode(0x32, "buffer_mode",       NONE,   NONE, VARI));
   case 96: return(the_opcode(0x35, "sound_effect",      NONE,   NONE, VARI));
   case 80: return(the_opcode(0x36, "read_char",        STORE,   NONE, VARI));
   case 81: return(the_opcode(0x37, "scan_table",       STORE, OBJECT, VARI));

   case 98: return(the_opcode(0x04, "nop",               NONE,   NONE, ZERO));

   case 82: return(the_opcode(0x08, "call_1s",           CALL,   NONE, ONE));
   }
 }

 if (version_number>=5)
 { switch(i)
   {
   case 71: return(the_opcode(0x1a, "call_2n",          NCALL,   NONE, TWO));
   case 99: return(the_opcode(0x1b, "set_colour",        NONE,   NONE, TWO));
  case 100: return(the_opcode(0x1c, "throw",             NONE,   NONE, TWO));
   case 90: return(the_opcode(0x24, "read",             STORE,   NONE, VARI));

   case 61: return(the_opcode(0x00, "save",             STORE,   NONE, EXTD));
   case 62: return(the_opcode(0x01, "restore",          STORE,   NONE, EXTD));
   case 85: return(the_opcode(0x02, "log_shift",        STORE,   NONE, EXTD));
   case 91: return(the_opcode(0x03, "art_shift",        STORE,   NONE, EXTD));
   case 86: return(the_opcode(0x04, "set_font",         STORE,   NONE, EXTD));
   case 87: return(the_opcode(0x09, "save_undo",        STORE,   NONE, EXTD));
   case 88: return(the_opcode(0x0A, "restore_undo",     STORE,   NONE, EXTD));

   case 56: return(the_opcode(0x38, "not",               NONE,   NONE, VARI));
   case 73: return(the_opcode(0x39, "call_vn",          NCALL,   NONE, VARI));
   case 74: return(the_opcode(0x3a, "call_vn2",         NCALL,   NONE, MANY));
   case 97: return(the_opcode(0x3b, "tokenise",          NONE,   NONE, VARI));
  case 101: return(the_opcode(0x3c, "encode_text",       NONE,   NONE, VARI));
  case 102: return(the_opcode(0x3d, "copy_table",        NONE,   NONE, VARI));
  case 103: return(the_opcode(0x3e, "print_table",       NONE,   NONE, VARI));
   case 84: return(the_opcode(0x3f, "check_arg_count", BRANCH,   NONE, VARI));

   case 83: return(the_opcode(0x0F, "call_1n",          NCALL,   NONE, ONE));

  case 104: return(the_opcode(0x09, "catch",            STORE,   NONE, ZERO));
  case 105: return(the_opcode(0x0F, "piracy",          BRANCH,   NONE, ZERO));
   }
 }

 if (version_number==6)
 { switch(i)
   {
   case 92: return(the_opcode(0x05, "draw_picture",      NONE,   NONE, EXTD));
   case 93: return(the_opcode(0x06, "picture_data",    BRANCH,   NONE, EXTD));
   case 94: return(the_opcode(0x07, "erase_picture",     NONE,   NONE, EXTD));
   case 106: return(the_opcode(0x08, "set_margins",       NONE,   NONE, EXTD));

   case 107: return(the_opcode(0x10, "move_window",       NONE,   NONE, EXTD));
   case 108: return(the_opcode(0x11, "window_size",       NONE,   NONE, EXTD));
   case 109: return(the_opcode(0x12, "window_style",      NONE,   NONE, EXTD));
   case 110: return(the_opcode(0x13, "get_wind_prop",    STORE,   NONE, EXTD));
   case 111: return(the_opcode(0x14, "scroll_window",     NONE,   NONE, EXTD));
   case 112: return(the_opcode(0x15, "pop_stack",         NONE,   NONE, EXTD));
   case 113: return(the_opcode(0x16, "read_mouse",        NONE,   NONE, EXTD));
   case 114: return(the_opcode(0x17, "mouse_window",      NONE,   NONE, EXTD));
   case 115: return(the_opcode(0x18, "push_stack",      BRANCH,   NONE, EXTD));
   case 116: return(the_opcode(0x19, "put_wind_prop",     NONE,   NONE, EXTD));
   case 117: return(the_opcode(0x1a, "print_form",        NONE,   NONE, EXTD));
   case 118: return(the_opcode(0x1b, "make_menu",       BRANCH,   NONE, EXTD));
   case 119: return(the_opcode(0x1c, "picture_table",     NONE,   NONE, EXTD));
   }
 }

 return(the_opcode(0xff,"???",INVALID,NONE,ZERO));
}

/* ------------------------------------------------------------------------- */
/*   Creating reserved words                                                 */
/* ------------------------------------------------------------------------- */

#define CreateD_(x,y) prim_new_symbol(x,y,14,6)
#define CreateC_(x,y) prim_new_symbol(x,y,15,6)
#define CreateB_(x,y,z) prim_new_symbol(x,z+y*100,16,6)
#define CreateA_(x,y) prim_new_symbol(x,y,17,6)

extern void stockup_symbols(void)
{   char *r1="RET#TRUE", *r2="RET#FALSE";

   new_symbol("nothing",0,9);

   new_symbol("sp",0,4);    new_symbol("ret#true",1,4);
   new_symbol("rtrue",1,4); new_symbol("ret#false",2,4);
   new_symbol("rfalse",2,4);

   new_symbol("=",1,10);    new_symbol("==",1,10);
   new_symbol(">",2,10);    new_symbol("<",3,10);
   new_symbol("has",4,10);
   new_symbol("near",5,10);
   new_symbol("~=",6,10);   new_symbol("<=",7,10);
   new_symbol(">=",8,10);
   new_symbol("hasnt",9,10);  new_symbol("far",10,10);

   new_symbol("name",1,12);

   new_symbol("temp_global",239,2);
   new_symbol("temp_global2",238,2);
   new_symbol("temp_global3",237,2);
   new_symbol("sys_glob0",0,2);
   new_symbol("sys_glob1",1,2);
   new_symbol("sys_glob2",2,2);

   CreateD_("ABBREVIATE", ABBREVIATE_CODE);
   CreateD_("ATTRIBUTE",  ATTRIBUTE_CODE);
   CreateD_("CONSTANT",   CONSTANT_CODE);
   CreateD_("DICTIONARY", DICTIONARY_CODE);
   CreateD_("END",        END_CODE);
   CreateD_("INCLUDE",    INCLUDE_CODE);
   CreateD_("GLOBAL",     GLOBAL_CODE);
   CreateD_("OBJECT",     OBJECT_CODE);
   CreateD_("PROPERTY",   PROPERTY_CODE);
   CreateD_("RELEASE",    RELEASE_CODE);
   CreateD_("SWITCHES",   SWITCHES_CODE);
   CreateD_("STATUSLINE", STATUSLINE_CODE);
   CreateD_("VERB",       VERB_CODE);
   CreateD_("TRACE",      TRACE_CODE);
   CreateD_("NOTRACE",    NOTRACE_CODE);
   CreateD_("ETRACE",     ETRACE_CODE);
   CreateD_("NOETRACE",   NOETRACE_CODE);
   CreateD_("BTRACE",     BTRACE_CODE);
   CreateD_("NOBTRACE",   NOBTRACE_CODE);
   CreateD_("LTRACE",     LTRACE_CODE);
   CreateD_("NOLTRACE",   NOLTRACE_CODE);
   CreateD_("ATRACE",     ATRACE_CODE);
   CreateD_("NOATRACE",   NOATRACE_CODE);
   CreateD_("LISTSYMBOLS", LISTSYMBOLS_CODE);
   CreateD_("LISTOBJECTS", LISTOBJECTS_CODE);
   CreateD_("LISTVERBS",  LISTVERBS_CODE);
   CreateD_("LISTDICT",   LISTDICT_CODE);
   CreateD_("[",          OPENBLOCK_CODE);
   CreateD_("]",          CLOSEBLOCK_CODE);
   CreateD_("SERIAL",     SERIAL_CODE);
   CreateD_("DEFAULT",    DEFAULT_CODE);
   CreateD_("STUB",       STUB_CODE);
   CreateD_("VERSION",    VERSION_CODE);
   CreateD_("IFV3",       IFV3_CODE);
   CreateD_("IFV5",       IFV5_CODE);
   CreateD_("IFDEF",      IFDEF_CODE);
   CreateD_("IFNDEF",     IFNDEF_CODE);
   CreateD_("ENDIF",      ENDIF_CODE);
   CreateD_("IFNOT",      IFNOT_CODE);
   CreateD_("LOWSTRING",  LOWSTRING_CODE);
   CreateD_("CLASS",      CLASS_CODE);
   CreateD_("FAKE_ACTION", FAKE_ACTION_CODE);
   CreateD_("NEARBY",     NEARBY_CODE);
   CreateD_("SYSTEM_FILE", SYSTEM_CODE);
   CreateD_("REPLACE",    REPLACE_CODE);
   CreateD_("EXTEND",     EXTEND_CODE);
   CreateD_("ARRAY",      ARRAY_CODE);

   CreateB_("PRINT_ADDR", PRINT_ADDR_CODE,   49);
   CreateB_("PRINT_CHAR", PRINT_CHAR_CODE,   32);
   CreateB_("PRINT_PADDR", PRINT_PADDR_CODE, 54);
   CreateB_("PRINT_OBJ",  PRINT_OBJ_CODE,    51);
   CreateB_("PRINT_NUM",  PRINT_NUM_CODE,    33);
   CreateB_("RESTORE",    RESTORE_CODE,      62);
   CreateB_("SAVE",       SAVE_CODE,         61);
   CreateB_("PRINT",      PRINT_CODE,        59);
   CreateB_("PRINT_RET",  PRINT_RET_CODE,    60);
   CreateB_("JUMP",       JUMP_CODE,         53);

   CreateC_("REMOVE",     REMOVE_CODE);
   CreateC_("RETURN",     RETURN_CODE);
   CreateC_("DO",         DO_CODE);
   CreateC_("FOR",        FOR_CODE);
   CreateC_("IF",         IF_CODE);
   CreateC_("OBJECTLOOP", OBJECTLOOP_CODE);
   CreateC_("UNTIL",      UNTIL_CODE);
   CreateC_("WHILE",      WHILE_CODE);
   CreateC_("BREAK",      BREAK_CODE);
   CreateC_("ELSE",       ELSE_CODE);
   CreateC_("GIVE",       GIVE_CODE);
   CreateC_("INVERSION",  INVERSION_CODE);
   CreateC_("MOVE",       MOVE_CODE);
   CreateC_("PUT",        PUT_CODE);
   CreateC_("WRITE",      WRITE_CODE);
   CreateC_("STRING",     STRING_CODE);
   CreateC_("FONT",       FONT_CODE);
   CreateC_("READ",       READ_CODE);
   CreateC_("STYLE",      STYLE_CODE);
   CreateC_("SPACES",     SPACES_CODE);
   CreateC_("BOX",        BOX_CODE);
   CreateC_("SWITCH",     SWITCH_CODE);

   CreateA_("JE",0);
   CreateA_("JL",1);
   CreateA_("JG",2);
   CreateA_("JZ",42);
   CreateA_("SREAD",31);
   CreateA_("RANDOM",34);
   CreateA_("RET",52);
   CreateA_(r1,57);
   CreateA_(r2,58);
   CreateA_("RTRUE",57);
   CreateA_("RFALSE",58);
   CreateA_("RESTART",63);
   CreateA_("RETSP",64);
   CreateA_("REMOVE_OBJ",50);
   CreateA_("PUT_PROP",30);
   CreateA_("PUSH",35);
   CreateA_("PULL",36);
   CreateA_("POP",65);
   CreateA_("GET_SIBLING",43);
   CreateA_("GET_CHILD",44);
   CreateA_("GET_PARENT",45);
   CreateA_("GET_PROP_LEN",46);
   CreateA_("GET_PROP",17);
   CreateA_("GET_PROP_ADDR",18);
   CreateA_("GET_NEXT_PROP",19);
   CreateA_("SET_ATTR",10);
   CreateA_("STORE",12);
   CreateA_("SUB",21);
   CreateA_("STOREW",28);
   CreateA_("STOREB",29);
   CreateA_("SPLIT_WINDOW",37);
   CreateA_("SET_WINDOW",38);
   CreateA_("OUTPUT_STREAM",39);
   CreateA_("SOUND_EFFECT",41);
   CreateA_("SHOW_SCORE",68);
   CreateA_("DEC_CHK",3);
   CreateA_("INC_CHK",4);
   CreateA_("COMPARE_POBJ",5);
   CreateA_("TEST",6);
   CreateA_("OR",7);
   CreateA_("AND",8);
   CreateA_("TEST_ATTR",9);
   CreateA_("CLEAR_ATTR",11);
   CreateA_("LSTORE",13);
   CreateA_("INSERT_OBJ",14);
   CreateA_("LOADW",15);
   CreateA_("LOADB",16);
   CreateA_("ADD",20);
   CreateA_("MUL",22);
   CreateA_("DIV",23);
   CreateA_("MOD",24);
   CreateA_("VJE",25);
   CreateA_("CALL",26);
   CreateA_("ICALL",27);
   CreateA_("INPUT_STREAM",40);
   CreateA_("INC",47);
   CreateA_("DEC",48);
   CreateA_("LOAD",55);
   CreateA_("NOT",56);
   CreateA_("QUIT",66);
   CreateA_("NEW_LINE",67);
   CreateA_("VERIFY",69);

   CreateA_("CALL_2S",70);
   CreateA_("CALL_2N",71);
   CreateA_("CALL_VS",72);
   CreateA_("CALL_VN",73);
   CreateA_("CALL_VN2",74);
   CreateA_("ERASE_WINDOW",75);
   CreateA_("ERASE_LINE",76);
   CreateA_("SET_CURSOR",77);
   CreateA_("SET_TEXT_STYLE",78);
   CreateA_("BUFFER_MODE",79);
   CreateA_("READ_CHAR",80);
   CreateA_("SCANW",81);
   CreateA_("CALL_1S",82);
   CreateA_("CALL_1N",83);
   CreateA_("CHECK_NO_ARGS",84);

   CreateA_("LOG_SHIFT",85);
   CreateA_("SET_FONT",86);
   CreateA_("SAVE_UNDO",87);
   CreateA_("RESTORE_UNDO",88);

   CreateA_("CALL_VS2",89);

   CreateA_("AREAD",90);
   CreateA_("ART_SHIFT",91);
   CreateA_("DRAW_PICTURE",92);
   CreateA_("PICTURE_DATA",93);
   CreateA_("ERASE_PICTURE",94);
   CreateA_("INPUT_STREAM",95);
   CreateA_("BEEP",96);
   CreateA_("APARSE",97);

   CreateA_("NOP",98);
   CreateA_("COLOUR",99);
   CreateA_("THROW",100);
   CreateA_("ENCRYPT",101);
   CreateA_("COPY_TABLE",102);
   CreateA_("PRINT_TABLE",103);
   CreateA_("CATCH",104);
   CreateA_("PIRACY",105);
   CreateA_("SET_MARGINS",106);
   CreateA_("MOVE_WINDOW",107);
   CreateA_("WINDOW_SIZE",108);
   CreateA_("WINDOW_STYLE",109);
   CreateA_("GET_WIND_PROP",110);
   CreateA_("SCROLL_WINDOW",111);
   CreateA_("POP_STACK",112);
   CreateA_("READ_MOUSE",113);
   CreateA_("MOUSE_WINDOW",114);
   CreateA_("PUSH_STACK",115);
   CreateA_("PUT_WIND_PROP",116);
   CreateA_("PRINT_FORM",117);
   CreateA_("MAKE_MENU",118);
   CreateA_("PICTURE_TABLE",119);

   CreateA_("IO_BUFFER_MODE",79);  /* Alias for BUFFER_MODE */
   CreateA_("JIN",5);              /* for COMPARE_POBJ */
   CreateA_("SET_COLOUR",99);      /* for COLOUR */
   CreateA_("RET_POPPED",64);      /* for RETSP */
   CreateA_("SHOW_STATUS",68);     /* for SHOW_SCORE */
   CreateA_("SCAN_TABLE",81);      /* for SCANW */
   CreateA_("TOKENISE",97);        /* for APARSE */
   CreateA_("ENCODE_TEXT",101);    /* for ENCRYPT */
   CreateA_("CHECK_ARG_COUNT",84); /* for CHECK_NO_ARGS */

}

/* ------------------------------------------------------------------------- */
/*   The abbreviations optimiser                                             */
/* ------------------------------------------------------------------------- */

typedef struct tlb_s
{   char text[4];
   int32 intab, occurrences;
} tlb;
static tlb *tlbtab;
static int32 no_occs;

static int32 *grandtable;
static int32 *grandflags;
typedef struct optab_s
{   int32  length;
   int32  popularity;
   int32  score;
   int32  location;
   char text[64];
} optab;
static optab *bestyet, *bestyet2;

static int pass_no=0;

static void optimise_pass(void)
{   int32 i; int t1, t2;
   int32 j, j2, k, nl, matches, noflags, score, min, minat, x, scrabble, c;
   for (i=0; i<256; i++) bestyet[i].length=0;
   for (i=0; i<no_occs; i++)
   {   if ((i!=(int) '\n')&&(tlbtab[i].occurrences!=0))
       {   printf("Pass %d, %4ld/%ld '%s' (%ld occurrences) ",
               pass_no, (long int) i, (long int) no_occs, tlbtab[i].text,
               (long int) tlbtab[i].occurrences);
           t1=(int) (time(0));
           for (j=0; j<tlbtab[i].occurrences; j++)
           {   for (j2=0; j2<tlbtab[i].occurrences; j2++) grandflags[j2]=1;
               nl=2; noflags=tlbtab[i].occurrences;
               while ((noflags>=2)&&(nl<=62))
               {   nl++;
                   for (j2=0; j2<nl; j2++)
                       if (all_text[grandtable[tlbtab[i].intab+j]+j2]=='\n')
                           goto FinishEarly;
                   matches=0;
                   for (j2=j; j2<tlbtab[i].occurrences; j2++)
                   {   if (grandflags[j2]==1)
                       {   x=grandtable[tlbtab[i].intab+j2]
                             - grandtable[tlbtab[i].intab+j];
                        if (((x>-nl)&&(x<nl))
                           || (memcmp(all_text+grandtable[tlbtab[i].intab+j],
                                      all_text+grandtable[tlbtab[i].intab+j2],
                                      nl)!=0))
                           {   grandflags[j2]=0; noflags--; }
                           else matches++;
                       }
                   }
                   scrabble=0;
                   for (k=0; k<nl; k++)
                   {   scrabble++;
                       c=all_text[grandtable[tlbtab[i].intab+j+k]];
                       if (c!=(int) ' ')
                       {   if (chars_lookup[c]==127)
                               scrabble+=2;
                           else
                               if (chars_lookup[c]>=26)
                                   scrabble++;
                       }
                   }
                   score=(matches-1)*(scrabble-2);
                   min=score;
                   for (j2=0; j2<256; j2++)
                   {   if ((nl==bestyet[j2].length)
                               && (memcmp(all_text+bestyet[j2].location,
                                      all_text+grandtable[tlbtab[i].intab+j],
                                      nl)==0))
                       {   j2=256; min=score; }
                       else
                       {   if (bestyet[j2].score<min)
                           {   min=bestyet[j2].score; minat=j2;
                           }
                       }
                   }
                   if (min!=score)
                   {   bestyet[minat].score=score;
                       bestyet[minat].length=nl;
                       bestyet[minat].location=grandtable[tlbtab[i].intab+j];
                       bestyet[minat].popularity=matches;
                       for (j2=0; j2<nl; j2++) sub_buffer[j2]=
                           all_text[bestyet[minat].location+j2];
                       sub_buffer[nl]=0;
                   }
               }
               FinishEarly: ;
           }
           t2=((int) time(0)) - t1;
           printf(" (%d seconds)\n",t2);
       }
   }
}

static int any_overlap(char *s1, char *s2)
{   int a, b, i, j, flag;
   a=strlen(s1); b=strlen(s2);
   for (i=1-b; i<a; i++)
   {   flag=0;
       for (j=0; j<b; j++)
           if ((0<=i+j)&&(i+j<=a-1))
               if (s1[i+j]!=s2[j]) flag=1;
       if (flag==0) return(1);
   }
   return(0);
}

#define MAX_TLBS 8000

extern void optimise_abbreviations(void)
{   int32 i, j, t, max=0, MAX_GTABLE;
   int32 j2, selected, available, maxat, nl;
   tlb test;

   printf("Beginning calculation of optimal abbreviations...\n");

   tlbtab=my_calloc(sizeof(tlb), MAX_TLBS, "tlb table"); no_occs=0;
   sub_buffer=my_calloc(sizeof(char), BUFFER_LENGTH, "sub_buffer");
   for (i=0; i<MAX_TLBS; i++) tlbtab[i].occurrences=0;

   bestyet=my_calloc(sizeof(optab), 256, "bestyet");
   bestyet2=my_calloc(sizeof(optab), 64, "bestyet2");

           bestyet2[0].text[0]='.';
           bestyet2[0].text[1]=' ';
           bestyet2[0].text[2]=' ';
           bestyet2[0].text[3]=0;

           bestyet2[1].text[0]=',';
           bestyet2[1].text[1]=' ';
           bestyet2[1].text[2]=0;

   for (i=0, t=0; all_text+i<all_text_p; i++)
   {
       if ((all_text[i]=='.') && (all_text[i+1]==' ') && (all_text[i+2]==' '))
       {   all_text[i]='\n'; all_text[i+1]='\n'; all_text[i+2]='\n';
           bestyet2[0].popularity++;
       }

       if ((all_text[i]=='.') && (all_text[i+1]==' '))
       {   all_text[i]='\n'; all_text[i+1]='\n';
           bestyet2[0].popularity++;
       }

       if ((all_text[i]==',') && (all_text[i+1]==' '))
       {   all_text[i]='\n'; all_text[i+1]='\n';
           bestyet2[1].popularity++;
       }
   }

   MAX_GTABLE=subtract_pointers(all_text_p,all_text)+1;
   grandtable=my_calloc(4*sizeof(int32), MAX_GTABLE/4, "grandtable");

   for (i=0, t=0; all_text+i<all_text_p; i++)
   {   test.text[0]=all_text[i];
       test.text[1]=all_text[i+1];
       test.text[2]=all_text[i+2];
       test.text[3]=0;
       if ((test.text[0]=='\n')||(test.text[1]=='\n')||(test.text[2]=='\n'))
           goto DontKeep;
       for (j=0; j<no_occs; j++)
           if (strcmp(test.text,tlbtab[j].text)==0)
               goto DontKeep;
       test.occurrences=0;
       for (j=i+3; all_text+j<all_text_p; j++)
       {   if ((all_text[i]==all_text[j])
                && (all_text[i+1]==all_text[j+1])
                && (all_text[i+2]==all_text[j+2]))
                {   grandtable[t+test.occurrences]=j;
                    test.occurrences++;
                    if (t+test.occurrences==MAX_GTABLE)
                    {   printf("All %ld cross-references used\n",
                            (long int) MAX_GTABLE);
                        goto Built;
                    }
                }
       }
       if (test.occurrences>=2)
       {   tlbtab[no_occs]=test;
           tlbtab[no_occs].intab=t; t+=tlbtab[no_occs].occurrences;
           if (max<tlbtab[no_occs].occurrences)
               max=tlbtab[no_occs].occurrences;
           no_occs++;
           if (no_occs==MAX_TLBS)
           {   printf("All %d three-letter-blocks used\n",
                   MAX_TLBS);
               goto Built;
           }
       }
       DontKeep: ;
   }

   Built:
   grandflags=my_calloc(sizeof(int), max, "grandflags");


   printf("Cross-reference table (%ld entries) built...\n",
       (long int) no_occs);
   /*  for (i=0; i<no_occs; i++)
           printf("%4d %4d '%s' %d\n",i,tlbtab[i].intab,tlbtab[i].text,
               tlbtab[i].occurrences);
   */

   for (i=0; i<64; i++) bestyet2[i].length=0; selected=2;
   available=256;
   while ((available>0)&&(selected<64))
   {   printf("Pass %d\n", ++pass_no);

       optimise_pass();
       available=0;
       for (i=0; i<256; i++)
           if (bestyet[i].score!=0)
           {   available++;
               nl=bestyet[i].length;
               for (j2=0; j2<nl; j2++) bestyet[i].text[j2]=
                   all_text[bestyet[i].location+j2];
               bestyet[i].text[nl]=0;
           }

/*      printf("End of pass results:\n");
       printf("\nno   score  freq   string\n");
       for (i=0; i<256; i++)
           if (bestyet[i].score>0)
               printf("%02d:  %4d   %4d   '%s'\n", i, bestyet[i].score,
                   bestyet[i].popularity, bestyet[i].text);
*/

       do
       {   max=0;
           for (i=0; i<256; i++)
               if (max<bestyet[i].score)
               {   max=bestyet[i].score;
                   maxat=i;
               }

           if (max>0)
           {   bestyet2[selected++]=bestyet[maxat];

               printf("Selection %2ld: '%s' (repeated %ld times, scoring %ld)\n",
                   (long int) selected,bestyet[maxat].text,
                   (long int) bestyet[maxat].popularity,
                   (long int) bestyet[maxat].score);

               test.text[0]=bestyet[maxat].text[0];
               test.text[1]=bestyet[maxat].text[1];
               test.text[2]=bestyet[maxat].text[2];
               test.text[3]=0;

               for (i=0; i<no_occs; i++)
                   if (strcmp(test.text,tlbtab[i].text)==0)
                       break;

               for (j=0; j<tlbtab[i].occurrences; j++)
               {   if (memcmp(bestyet[maxat].text,
                              all_text+grandtable[tlbtab[i].intab+j],
                              bestyet[maxat].length)==0)
                   {   for (j2=0; j2<bestyet[maxat].length; j2++)
                           all_text[grandtable[tlbtab[i].intab+j]+j2]='\n';
                   }
               }

               for (i=0; i<256; i++)
                   if ((bestyet[i].score>0)&&
                       (any_overlap(bestyet[maxat].text,bestyet[i].text)==1))
                   {   bestyet[i].score=0;
                      /* printf("Discarding '%s' as overlapping\n",
                           bestyet[i].text); */
                   }
           }
       } while ((max>0)&&(available>0)&&(selected<64));
   }

   printf("\nChosen abbreviations (in Inform syntax):\n\n");
   for (i=0; i<selected; i++)
       printf("Abbreviate \"%s\";\n", bestyet2[i].text);

   zcode_free_arrays();
}

extern void zcode_free_arrays(void)
{   my_free (&tlbtab,"tlb table");
   my_free (&sub_buffer,"sub_buffer");
   my_free (&bestyet,"bestyet");
   my_free (&bestyet2,"bestyet2");
   my_free (&grandtable,"grandtable");
   my_free (&grandflags,"grandflags");
}

extern void init_zcode_vars(void)
{   almade_flag = 0;
   pass_no = 0;
   bestyet = NULL;
   bestyet2 = NULL;
   tlbtab = NULL;
   grandtable = NULL;
   grandflags = NULL;
   text_transcribed = 0;
}