/* ------------------------------------------------------------------------- *//* "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. Load more
/* ------------------------------------------------------------------------- */ /* "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; }