2310 lines · 83.2 KB · C
RawDownload
/* ------------------------------------------------------------------------- *//*   "text" : Text translation, the abbreviations optimiser, the dictionary  *//*                                                                           *//*   Part of Inform 6.33                                                     *//*   copyright (c) Graham Nelson 1993 - 2014                                 *//*                                                                           *//* ------------------------------------------------------------------------- */ #include "header.h" uchar *low_strings, *low_strings_top;  /* Start and next free byte in the low                                          strings pool */ int32 static_strings_extent;           /* Number of bytes of static strings                                          made so far */memory_block static_strings_area;      /* Used if (!temporary_files_switch) to                                          hold the static strings area so far */ static uchar *strings_holding_area;    /* Area holding translated strings                                          until they are moved into either                                          a temporary file, or the                                          static_strings_area below */ char *all_text, *all_text_top;         /* Start and next byte free in (large)                                          text buffer holding the entire text                                          of the game, when it is being                                          recorded                           */int put_strings_in_low_memory,         /* When TRUE, put static strings in                                          the low strings pool at 0x100 rather                                          than in the static strings area    */    is_abbreviation,                   /* When TRUE, the string being trans                                          is itself an abbreviation string                                          so can't make use of abbreviations */    abbrevs_lookup_table_made,         /* The abbreviations lookup table is                                          constructed when the first non-                                          abbreviation string is translated:                                          this flag is TRUE after that       */    abbrevs_lookup[256];               /* Once this has been constructed,                                          abbrevs_lookup[n] = the smallest                                          number of any abbreviation beginning                                          with ASCII character n, or -1                                          if none of the abbreviations do    */int no_abbreviations;                  /* No of abbreviations defined so far */uchar *abbreviations_at;                 /* Memory to hold the text of any                                          abbreviation strings declared      *//* ------------------------------------------------------------------------- *//*   Glulx string compression storage                                        *//* ------------------------------------------------------------------------- */ int no_strings;                        /* No of strings in static strings                                          area.                              */int no_dynamic_strings;                /* No. of @.. string escapes used                                          (actually, the highest value used                                          plus one)                          */int no_unicode_chars;                  /* Number of distinct Unicode chars                                          used. (Beyond 0xFF.)               */ static int MAX_CHARACTER_SET;          /* Number of possible entities */huffentity_t *huff_entities;           /* The list of entities (characters,                                          abbreviations, @.. escapes, and                                           the terminator)                    */static huffentity_t **hufflist;        /* Copy of the list, for sorting      */ int no_huff_entities;                  /* The number of entities in the list */int huff_unicode_start;                /* Position in the list where Unicode                                          chars begin.                       */int huff_abbrev_start;                 /* Position in the list where string                                          abbreviations begin.               */int huff_dynam_start;                  /* Position in the list where @..                                          entities begin.                    */int huff_entity_root;                  /* The position in the list of the root                                          entry (when considering the table                                          as a tree).                        */ int done_compression;                  /* Has the game text been compressed? */int32 compression_table_size;          /* Length of the Huffman table, in                                           bytes                              */int32 compression_string_size;         /* Length of the compressed string                                          data, in bytes                     */int32 *compressed_offsets;             /* The beginning of every string in                                          the game, relative to the beginning                                          of the Huffman table. (So entry 0                                          is equal to compression_table_size)*/ #define UNICODE_HASH_BUCKETS (64)unicode_usage_t *unicode_usage_entries;static unicode_usage_t *unicode_usage_hash[UNICODE_HASH_BUCKETS]; static int unicode_entity_index(int32 unicode); /* ------------------------------------------------------------------------- *//*   Abbreviation arrays                                                     *//* ------------------------------------------------------------------------- */ int *abbrev_values;int *abbrev_quality;int *abbrev_freqs; /* ------------------------------------------------------------------------- */ int32 total_chars_trans,               /* Number of ASCII chars of text in   */      total_bytes_trans,               /* Number of bytes of Z-code text out */      zchars_trans_in_last_string;     /* Number of Z-chars in last string:                                          needed only for abbrev efficiency                                          calculation in "directs.c"         */static int32 total_zchars_trans,       /* Number of Z-chars of text out                                          (only used to calculate the above) */      no_chars_transcribed;            /* Number of ASCII chars written to                                          the text transcription area (used                                          for the -r and -u switches)        */ static int zchars_out_buffer[3],       /* During text translation, a buffer of                                          3 Z-chars at a time: when it's full                                          these are written as a 2-byte word */           zob_index;                  /* Index (0 to 2) into it             */ static unsigned char *text_out_pc;     /* The "program counter" during text                                          translation: the next address to                                          write Z-coded text output to       */ static unsigned char *text_out_limit;  /* The upper limit of text_out_pc                                          during text translation            */ static int text_out_overflow;          /* During text translation, becomes                                          true if text_out_pc tries to pass                                          text_out_limit                     */ /* ------------------------------------------------------------------------- *//*   For variables/arrays used by the dictionary manager, see below          *//* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- *//*   Prepare the abbreviations lookup table (used to speed up abbreviation   *//*   detection in text translation).  We first bubble-sort the abbrevs into  *//*   alphabetical order (this is necessary for the detection algorithm to    *//*   to work).  Since the table is only prepared once, and for a table       *//*   of size at most 96, there's no point using an efficient sort algorithm. *//* ------------------------------------------------------------------------- */ static void make_abbrevs_lookup(void){   int bubble_sort, j, k, l; char p[MAX_ABBREV_LENGTH]; char *p1, *p2;    do    {   bubble_sort = FALSE;        for (j=0; j<no_abbreviations; j++)            for (k=j+1; k<no_abbreviations; k++)            {   p1=(char *)abbreviations_at+j*MAX_ABBREV_LENGTH;                p2=(char *)abbreviations_at+k*MAX_ABBREV_LENGTH;                if (strcmp(p1,p2)<0)                {   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;                    bubble_sort = TRUE;                }            }    } while (bubble_sort);     for (j=no_abbreviations-1; j>=0; j--)    {   p1=(char *)abbreviations_at+j*MAX_ABBREV_LENGTH;        abbrevs_lookup[(uchar)p1[0]]=j;        abbrev_freqs[j]=0;    }    abbrevs_lookup_table_made = TRUE;} /* ------------------------------------------------------------------------- *//*   Search the abbreviations lookup table (a routine which must be fast).   *//*   The source text to compare is text[i], text[i+1], ... and this routine  *//*   is only called if text[i] is indeed the first character of at least one *//*   abbreviation, "from" begin the least index into the abbreviations table *//*   of an abbreviation for which text[i] is the first character.  Recall    *//*   that the abbrevs table is in alphabetical order.                        *//*                                                                           *//*   The return value is -1 if there is no match.  If there is a match, the  *//*   text to be abbreviated out is over-written by a string of null chars    *//*   with "ASCII" value 1, and the abbreviation number is returned.          *//*                                                                           *//*   In Glulx, we *do not* do this overwriting with 1's.                     *//* ------------------------------------------------------------------------- */ static int try_abbreviations_from(unsigned char *text, int i, int from){   int j, k; uchar *p, c;    c=text[i];    for (j=from, p=(uchar *)abbreviations_at+from*MAX_ABBREV_LENGTH;         (j<no_abbreviations)&&(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;            if (!glulx_mode) {                for (k=0; p[k]!=0; k++) text[i+k]=1;            }            abbrev_freqs[j]++;            return(j);            NotMatched: ;        }    }    return(-1);} extern void make_abbreviation(char *text){    strcpy((char *)abbreviations_at            + no_abbreviations*MAX_ABBREV_LENGTH, text);     is_abbreviation = TRUE;    abbrev_values[no_abbreviations] = compile_string(text, TRUE, TRUE);    is_abbreviation = FALSE;     /*   The quality is the number of Z-chars saved by using this            */    /*   abbreviation: note that it takes 2 Z-chars to print it.             */     abbrev_quality[no_abbreviations++] = zchars_trans_in_last_string - 2;} /* ------------------------------------------------------------------------- *//*   The front end routine for text translation                              *//* ------------------------------------------------------------------------- */ extern int32 compile_string(char *b, int in_low_memory, int is_abbrev){   int i, j; uchar *c;     is_abbreviation = is_abbrev;     /* Put into the low memory pool (at 0x100 in the Z-machine) of strings   */    /* which may be wanted as possible entries in the abbreviations table    */     if (!glulx_mode && in_low_memory)    {   j=subtract_pointers(low_strings_top,low_strings);        low_strings_top=translate_text(low_strings_top, low_strings+MAX_LOW_STRINGS, b);        if (!low_strings_top)            memoryerror("MAX_LOW_STRINGS", MAX_LOW_STRINGS);        is_abbreviation = FALSE;        return(0x21+(j/2));    }     if (glulx_mode && done_compression)        compiler_error("Tried to add a string after compression was done.");     c = translate_text(strings_holding_area, strings_holding_area+MAX_STATIC_STRINGS, b);    if (!c)        memoryerror("MAX_STATIC_STRINGS",MAX_STATIC_STRINGS);     i = subtract_pointers(c, strings_holding_area);     /* Insert null bytes as needed to ensure that the next static string */    /* also occurs at an address expressible as a packed address         */     if (!glulx_mode) {        int textalign;        if (oddeven_packing_switch)             textalign = scale_factor*2;        else            textalign = scale_factor;        while ((i%textalign)!=0)        {            if (i+2 > MAX_STATIC_STRINGS)                memoryerror("MAX_STATIC_STRINGS",MAX_STATIC_STRINGS);            i+=2; *c++ = 0; *c++ = 0;        }    }     j = static_strings_extent;     if (temporary_files_switch)        for (c=strings_holding_area; c<strings_holding_area+i;             c++, static_strings_extent++)            fputc(*c,Temp1_fp);    else        for (c=strings_holding_area; c<strings_holding_area+i;             c++, static_strings_extent++)            write_byte_to_memory_block(&static_strings_area,                static_strings_extent, *c);     is_abbreviation = FALSE;     if (!glulx_mode) {        return(j/scale_factor);    }    else {        /* The marker value is a one-based string number. (We reserve zero           to mean "not a string at all". */        return (++no_strings);    }} /* ------------------------------------------------------------------------- *//*   Output a single Z-character into the buffer, and flush it if full       *//* ------------------------------------------------------------------------- */ static void write_z_char_z(int i){   uint32 j;    ASSERT_ZCODE();    total_zchars_trans++;    zchars_out_buffer[zob_index++]=(i%32);    if (zob_index!=3) return;    zob_index=0;    j= zchars_out_buffer[0]*0x0400 + zchars_out_buffer[1]*0x0020       + zchars_out_buffer[2];    if (text_out_pc+2 > text_out_limit) {        text_out_overflow = TRUE;        return;    }    text_out_pc[0] = j/256; text_out_pc[1] = j%256; text_out_pc+=2;    total_bytes_trans+=2;} static void write_zscii(int zsc){    int lookup_value, in_alphabet;     if (zsc==' ')    {   write_z_char_z(0);        return;    }     if (zsc < 0x100) lookup_value = zscii_to_alphabet_grid[zsc];     else lookup_value = -1;     if (lookup_value >= 0)    {   alphabet_used[lookup_value] = 'Y';        in_alphabet = lookup_value/26;        if (in_alphabet==1) write_z_char_z(4);  /* SHIFT to A1 */        if (in_alphabet==2) write_z_char_z(5);  /* SHIFT to A2 */        write_z_char_z(lookup_value%26 + 6);    }    else    {   write_z_char_z(5); write_z_char_z(6);        write_z_char_z(zsc/32); write_z_char_z(zsc%32);    }} /* ------------------------------------------------------------------------- *//*   Finish a Z-coded string, padding out with Z-char 5s if necessary and    *//*   setting the "end" bit on the final 2-byte word                          *//* ------------------------------------------------------------------------- */ static void end_z_chars(void){   unsigned char *p;    zchars_trans_in_last_string=total_zchars_trans-zchars_trans_in_last_string;    while (zob_index!=0) write_z_char_z(5);    p=(unsigned char *) text_out_pc;    *(p-2)= *(p-2)+128;} /* Glulx handles this much more simply -- compression is done elsewhere. */static void write_z_char_g(int i){  ASSERT_GLULX();  if (text_out_pc+1 > text_out_limit) {      text_out_overflow = TRUE;      return;  }  total_zchars_trans++;  text_out_pc[0] = i;  text_out_pc++;  total_bytes_trans++;  } /* ------------------------------------------------------------------------- *//*   The main routine "text.c" provides to the rest of Inform: the text      *//*   translator. p is the address to write output to, s_text the source text *//*   and the return value is the next free address to write output to.       *//*   The return value will not exceed p_limit. If the translation tries to   *//*   overflow this boundary, the return value will be NULL (and you should   *//*   display an error).                                                      *//*   Note that the source text may be corrupted by this routine.             *//* ------------------------------------------------------------------------- */ extern uchar *translate_text(uchar *p, uchar *p_limit, char *s_text){   int i, j, k, in_alphabet, lookup_value;    int32 unicode; int zscii;    unsigned char *text_in;     /*  Cast the input and output streams to unsigned char: text_out_pc will        advance as bytes of Z-coded text are written, but text_in doesn't    */     text_in     = (unsigned char *) s_text;    text_out_pc = (unsigned char *) p;    text_out_limit = (unsigned char *) p_limit;    text_out_overflow = FALSE;     /*  Remember the Z-chars total so that later we can subtract to find the        number of Z-chars translated on this string                          */     zchars_trans_in_last_string = total_zchars_trans;     /*  Start with the Z-characters output buffer empty                      */     zob_index=0;     /*  If this is the first text translated since the abbreviations were        declared, and if some were declared, then it's time to make the        lookup table for abbreviations         (Except: we don't if the text being translated is itself        the text of an abbreviation currently being defined)                 */     if ((!abbrevs_lookup_table_made) && (no_abbreviations > 0)        && (!is_abbreviation))        make_abbrevs_lookup();     /*  If we're storing the whole game text to memory, then add this text   */     if ((!is_abbreviation) && (store_the_text))    {   no_chars_transcribed += strlen(s_text)+2;        if (no_chars_transcribed >= MAX_TRANSCRIPT_SIZE)            memoryerror("MAX_TRANSCRIPT_SIZE", MAX_TRANSCRIPT_SIZE);        sprintf(all_text_top, "%s\n\n", s_text);        all_text_top += strlen(all_text_top);    }     if (transcript_switch && (!veneer_mode))        write_to_transcript_file(s_text);   if (!glulx_mode) {     /*  The empty string of Z-text is illegal, since it can't carry an end        bit: so we translate an empty string of ASCII text to just the        pad character 5.  Printing this causes nothing to appear on screen.  */     if (text_in[0]==0) write_z_char_z(5);     /*  Loop through the characters of the null-terminated input text: note        that if 1 is written over a character in the input text, it is        afterwards ignored                                                   */     for (i=0; text_in[i]!=0; i++)    {   total_chars_trans++;         /*  Contract ".  " into ". " if double-space-removing switch set:            likewise "?  " and "!  " if the setting is high enough           */         if ((double_space_setting >= 1)            && (text_in[i+1]==' ') && (text_in[i+2]==' '))        {   if (text_in[i]=='.') text_in[i+2]=1;            if (double_space_setting >= 2)            {   if (text_in[i]=='?') text_in[i+2]=1;                if (text_in[i]=='!') text_in[i+2]=1;            }        }         /*  Try abbreviations if the economy switch set                      */         if ((economy_switch) && (!is_abbreviation)            && ((k=abbrevs_lookup[text_in[i]])!=-1))        {   if ((j=try_abbreviations_from(text_in, i, k))!=-1)            {   if (j<32) { write_z_char_z(2); write_z_char_z(j); }                else { write_z_char_z(3); write_z_char_z(j-32); }            }        }         /* If Unicode switch set, use text_to_unicode to perform UTF-8           decoding */        if (character_set_unicode && (text_in[i] & 0x80))        {   unicode = text_to_unicode((char *) (text_in+i));            zscii = unicode_to_zscii(unicode);            if (zscii != 5) write_zscii(zscii);            else            {   unicode_char_error(                    "Character can only be used if declared in \advance as part of 'Zcharacter table':", unicode);            }            i += textual_form_length - 1;            continue;        }         /*  '@' is the escape character in Inform string notation: the various            possibilities are:                 (printing only)                @@decimalnumber  :  write this ZSCII char (0 to 1023)                @twodigits       :  write the abbreviation string with this                                    decimal number                 (any string context)                @accentcode      :  this accented character: e.g.,                                        for @'e write an E-acute                @{...}           :  this Unicode char (in hex)              */         if (text_in[i]=='@')        {   if (text_in[i+1]=='@')            {                /*   @@...   */                 i+=2; j=atoi((char *) (text_in+i));                switch(j)                {   /* Prevent ~ and ^ from being translated to double-quote                       and new-line, as they ordinarily would be */                     case 94:   write_z_char_z(5); write_z_char_z(6);                               write_z_char_z(94/32); write_z_char_z(94%32);                               break;                    case 126:  write_z_char_z(5); write_z_char_z(6);                               write_z_char_z(126/32); write_z_char_z(126%32);                               break;                     default:   write_zscii(j); break;                }

Lines 1–500 of 2310.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t