1146 lines
24 KiB
C
1146 lines
24 KiB
C
|
|
/* $Id: text.c,v 1.5 2000/10/10 14:46:22 jholder Exp $
|
|
* --------------------------------------------------------------------
|
|
* see doc/License.txt for License Information
|
|
* --------------------------------------------------------------------
|
|
*
|
|
* File name: $Id: text.c,v 1.5 2000/10/10 14:46:22 jholder Exp $
|
|
*
|
|
* Description:
|
|
*
|
|
* Modification history:
|
|
* $Log: text.c,v $
|
|
* Revision 1.5 2000/10/10 14:46:22 jholder
|
|
* Fixed text wrap bug when printing array w/ \r chars in it
|
|
*
|
|
* Revision 1.4 2000/10/04 23:07:57 jholder
|
|
* fixed redirect problem with isolatin1 range chars
|
|
*
|
|
* Revision 1.3 2000/07/05 15:20:34 jholder
|
|
* Updated code to remove warnings.
|
|
*
|
|
* Revision 1.2 2000/05/25 22:28:56 jholder
|
|
* changes routine names to reflect zmachine opcode names per spec 1.0
|
|
*
|
|
* Revision 1.1.1.1 2000/05/10 14:21:34 jholder
|
|
*
|
|
* imported
|
|
*
|
|
*
|
|
* --------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
* text.c
|
|
*
|
|
* Text manipulation routines
|
|
*
|
|
*/
|
|
|
|
#include "ztypes.h"
|
|
|
|
static int saved_formatting = ON;
|
|
static int story_buffer = 0;
|
|
static int story_pos = 0;
|
|
static int story_count = 0;
|
|
|
|
static int line_pos = 0;
|
|
static int char_count = 0;
|
|
|
|
/*
|
|
* decode_text
|
|
*
|
|
* Convert ZSCII encoded text to ASCII. Text is encoded by squeezing each character
|
|
* into 5 bits. 3 x 5 bit encoded characters can fit in one word with a spare
|
|
* bit left over. The spare bit is used to signal to end of a string. The 5 bit
|
|
* encoded characters can either be actual character codes or prefix codes that
|
|
* modifier the following code.
|
|
*
|
|
*/
|
|
|
|
void decode_text( unsigned long *address )
|
|
{
|
|
int i, synonym_flag, synonym = 0, zscii_flag, zscii = 0;
|
|
int data, code, shift_state, shift_lock;
|
|
unsigned long addr;
|
|
|
|
/* Set state variables */
|
|
|
|
shift_state = 0;
|
|
shift_lock = 0;
|
|
zscii_flag = 0;
|
|
synonym_flag = 0;
|
|
|
|
do
|
|
{
|
|
|
|
/*
|
|
* Read one 16 bit word. Each word contains three 5 bit codes. If the
|
|
* high bit is set then this is the last word in the string.
|
|
*/
|
|
|
|
data = read_data_word( address );
|
|
|
|
for ( i = 10; i >= 0; i -= 5 )
|
|
{
|
|
|
|
/* Get code, high bits first */
|
|
|
|
code = ( data >> i ) & 0x1f;
|
|
|
|
/* Synonym codes */
|
|
|
|
if ( synonym_flag )
|
|
{
|
|
|
|
synonym_flag = 0;
|
|
synonym = ( synonym - 1 ) * 64;
|
|
addr = ( unsigned long ) get_word( h_synonyms_offset + synonym + ( code * 2 ) ) * 2;
|
|
decode_text( &addr );
|
|
shift_state = shift_lock;
|
|
|
|
}
|
|
/* ZSCII codes */
|
|
else if ( zscii_flag )
|
|
{
|
|
|
|
/*
|
|
* If this is the first part ZSCII ten-bit code then remember it.
|
|
* Because the codes are only 5 bits you need two codes to make
|
|
* one eight bit ASCII character. The first code contains the
|
|
* top 5 bits (although only 3 bits are used at the moment).
|
|
* The second code contains the bottom 5 bits.
|
|
*/
|
|
|
|
if ( zscii_flag++ == 1 )
|
|
{
|
|
zscii = code << 5;
|
|
}
|
|
/*
|
|
* If this is the second part of a ten-bit ZSCII code then assemble the
|
|
* character from the two codes and output it.
|
|
*/
|
|
else
|
|
{
|
|
zscii_flag = 0;
|
|
write_zchar( ( unsigned char ) ( zscii | code ) );
|
|
}
|
|
|
|
}
|
|
|
|
/* Character codes */
|
|
else if ( code > 5 )
|
|
{
|
|
|
|
code -= 6;
|
|
|
|
/*
|
|
* If this is character 0 in the punctuation set then the next two
|
|
* codes make a ten-bit ZSCII character. (Std. Sec. 3.4)
|
|
*/
|
|
|
|
if ( shift_state == 2 && code == 0 )
|
|
{
|
|
zscii_flag = 1;
|
|
}
|
|
|
|
/*
|
|
* If this is character 1 in the punctuation set then this
|
|
* is a new line.
|
|
*/
|
|
|
|
else if ( shift_state == 2 && code == 1 && h_type > V1 )
|
|
{
|
|
z_new_line( );
|
|
}
|
|
/*
|
|
* This is a normal character so select it from the character
|
|
* table appropriate for the current shift state.
|
|
*/
|
|
|
|
else
|
|
{
|
|
write_zchar( lookup_table[shift_state][code] );
|
|
}
|
|
shift_state = shift_lock;
|
|
|
|
}
|
|
|
|
/* Special codes 0 to 5 */
|
|
else
|
|
{
|
|
|
|
/* Space: 0 Output a space character. */
|
|
|
|
if ( code == 0 )
|
|
{
|
|
write_zchar( ' ' );
|
|
}
|
|
else
|
|
{
|
|
/* The use of the synonym and shift codes is the only
|
|
* difference between the different versions.
|
|
*/
|
|
|
|
if ( h_type < V3 )
|
|
{
|
|
|
|
/* Newline or synonym: 1
|
|
* Output a newline character or set synonym flag.
|
|
*/
|
|
|
|
if ( code == 1 )
|
|
{
|
|
if ( h_type == V1 )
|
|
{
|
|
z_new_line( );
|
|
}
|
|
else
|
|
{
|
|
synonym_flag = 1;
|
|
synonym = code;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Shift keys: 2, 3, 4 or 5
|
|
*
|
|
* Shift keys 2 & 3 only shift the next character and can be used regardless of
|
|
* the state of the shift lock. Shift keys 4 & 5 lock the shift until reset.
|
|
*
|
|
* The following code implements the the shift code state transitions:
|
|
* +-------------+-------------+-------------+-------------+
|
|
* | Shift State | Lock State |
|
|
* +-------------+-------------+-------------+-------------+-------------+
|
|
* | Code | 2 | 3 | 4 | 5 |
|
|
* +-------------+-------------+-------------+-------------+-------------+
|
|
* | lowercase | uppercase | punctuation | uppercase | punctuation |
|
|
* | uppercase | punctuation | lowercase | punctuation | lowercase |
|
|
* | punctuation | lowercase | uppercase | lowercase | uppercase |
|
|
* +-------------+-------------+-------------+-------------+-------------+
|
|
*/
|
|
if ( code < 4 )
|
|
{
|
|
shift_state = ( shift_lock + code + 2 ) % 3;
|
|
}
|
|
else
|
|
{
|
|
shift_lock = shift_state = ( shift_lock + code ) % 3;
|
|
}
|
|
}
|
|
|
|
}
|
|
else /* not V3 */
|
|
{
|
|
|
|
/*
|
|
* Synonym table: 1, 2 or 3
|
|
*
|
|
* Selects which of three synonym tables the synonym
|
|
* code following in the next code is to use.
|
|
*/
|
|
if ( code < 4 )
|
|
{
|
|
synonym_flag = 1;
|
|
synonym = code;
|
|
}
|
|
/*
|
|
* Shift key: 4 or 5
|
|
*
|
|
* Selects the shift state for the next character,
|
|
* either uppercase (4) or punctuation (5). The shift
|
|
* state automatically gets reset back to lowercase for
|
|
* V3+ games after the next character is output.
|
|
*
|
|
*/
|
|
else
|
|
{
|
|
shift_state = code - 3;
|
|
shift_lock = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while ( ( data & 0x8000 ) == 0 );
|
|
|
|
} /* decode_text */
|
|
|
|
/*
|
|
* encode_text
|
|
*
|
|
* Pack a string into up to 9 codes or 3 words.
|
|
*
|
|
*/
|
|
|
|
void encode_text( int len, const char *s, ZINT16 * buffer )
|
|
{
|
|
int i, j, prev_table, table, next_table, shift_state, code, codes_count;
|
|
char codes[9];
|
|
|
|
/* Initialise codes count and prev_table number */
|
|
|
|
codes_count = 0;
|
|
prev_table = 0;
|
|
|
|
/* Scan do the string one character at a time */
|
|
|
|
while ( len-- )
|
|
{
|
|
|
|
/*
|
|
* Set the table and code to be the ASCII character inducer, then
|
|
* look for the character in the three lookup tables. If the
|
|
* character isn't found then it will be an ASCII character.
|
|
*/
|
|
|
|
table = 2;
|
|
code = 0;
|
|
for ( i = 0; i < 3; i++ )
|
|
{
|
|
for ( j = 0; j < 26; j++ )
|
|
{
|
|
if ( lookup_table[i][j] == *s )
|
|
{
|
|
table = i;
|
|
code = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Type 1 and 2 games differ on how the shift keys are used. Switch
|
|
* now depending on the game version.
|
|
*/
|
|
|
|
if ( h_type < V3 )
|
|
{
|
|
|
|
/*
|
|
* If the current table is the same as the previous table then
|
|
* just store the character code, otherwise switch tables.
|
|
*/
|
|
|
|
if ( table != prev_table )
|
|
{
|
|
|
|
/* Find the table for the next character */
|
|
|
|
next_table = 0;
|
|
if ( len )
|
|
{
|
|
next_table = 2;
|
|
for ( i = 0; i < 3; i++ )
|
|
{
|
|
for ( j = 0; j < 26; j++ )
|
|
{
|
|
if ( lookup_table[i][j] == s[1] )
|
|
next_table = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Calculate the shift key. This magic. See the description in
|
|
* decode_text for more information on version 1 and 2 shift
|
|
* key changes.
|
|
*/
|
|
|
|
shift_state = ( table + ( prev_table * 2 ) ) % 3;
|
|
|
|
/* Only store the shift key if there is a change in table */
|
|
|
|
if ( shift_state )
|
|
{
|
|
|
|
/*
|
|
* If the next character as the uses the same table as
|
|
* this character then change the shift from a single
|
|
* shift to a shift lock. Also remember the current
|
|
* table for the next iteration.
|
|
*/
|
|
|
|
if ( next_table == table )
|
|
{
|
|
shift_state += 2;
|
|
prev_table = table;
|
|
}
|
|
else
|
|
prev_table = 0;
|
|
|
|
/* Store the code in the codes buffer */
|
|
|
|
if ( codes_count < 9 )
|
|
codes[codes_count++] = ( char ) ( shift_state + 1 );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* For V3 games each uppercase or punctuation table is preceded
|
|
* by a separate shift key. If this is such a shift key then
|
|
* put it in the codes buffer.
|
|
*/
|
|
|
|
if ( table && codes_count < 9 )
|
|
codes[codes_count++] = ( char ) ( table + 3 );
|
|
}
|
|
|
|
/* Put the character code in the code buffer */
|
|
|
|
if ( codes_count < 9 )
|
|
codes[codes_count++] = ( char ) ( code + 6 );
|
|
|
|
/*
|
|
* Cannot find character in table so treat it as a literal ASCII
|
|
* code. The ASCII code inducer (code 0 in table 2) is followed by
|
|
* the high 3 bits of the ASCII character followed by the low 5
|
|
* bits to make 8 bits in total.
|
|
*/
|
|
|
|
if ( table == 2 && code == 0 )
|
|
{
|
|
if ( codes_count < 9 )
|
|
codes[codes_count++] = ( char ) ( ( *s >> 5 ) & 0x07 );
|
|
if ( codes_count < 9 )
|
|
codes[codes_count++] = ( char ) ( *s & 0x1f );
|
|
}
|
|
|
|
/* Advance to next character */
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
/* Pad out codes with shift 5's */
|
|
|
|
while ( codes_count < 9 )
|
|
codes[codes_count++] = 5;
|
|
|
|
/* Pack codes into buffer */
|
|
|
|
buffer[0] = (short)(( ( ZINT16 ) codes[0] << 10 ) | ( ( ZINT16 ) codes[1] << 5 ) | ( ZINT16 ) codes[2]);
|
|
buffer[1] = (short)(( ( ZINT16 ) codes[3] << 10 ) | ( ( ZINT16 ) codes[4] << 5 ) | ( ZINT16 ) codes[5]);
|
|
buffer[2] = (short)(( ( ZINT16 ) codes[6] << 10 ) | ( ( ZINT16 ) codes[7] << 5 ) | ( ZINT16 ) codes[8]);
|
|
|
|
/* Terminate buffer at 6 or 9 codes depending on the version */
|
|
|
|
if ( h_type < V4 )
|
|
buffer[1] |= 0x8000;
|
|
else
|
|
buffer[2] |= 0x8000;
|
|
|
|
} /* encode_text */
|
|
|
|
/*
|
|
* write_zchar
|
|
*
|
|
* High level Z-code character output routine. Translates Z-code characters to
|
|
* machine specific character(s) before output. If it cannot translate it then
|
|
* use the default translation. If the character is still unknown then display
|
|
* a '?'.
|
|
*
|
|
*/
|
|
|
|
void write_zchar( int c )
|
|
{
|
|
char xlat_buffer[MAX_TEXT_SIZE + 1];
|
|
int i;
|
|
|
|
c = ( unsigned int ) ( c & 0xff );
|
|
|
|
/* If character is not special character then just write it */
|
|
|
|
if ( c >= ' ' && c <= '~' )
|
|
{
|
|
write_char( c );
|
|
}
|
|
else if ( c == 13 )
|
|
{
|
|
write_char( '\r' );
|
|
}
|
|
else
|
|
{
|
|
/* Put default character in translation buffer */
|
|
xlat_buffer[0] = '?';
|
|
xlat_buffer[1] = '\0';
|
|
|
|
/* If translation fails then supply a default */
|
|
if ( codes_to_text( c, xlat_buffer ) )
|
|
{
|
|
if ( c > 23 && c < 28 )
|
|
{
|
|
/* Arrow keys - these must the keyboard keys used for input */
|
|
static char xlat[4] = { '\\', '/', '+', '-' };
|
|
|
|
xlat_buffer[0] = xlat[c - 24];
|
|
xlat_buffer[1] = '\0';
|
|
}
|
|
else if ( c == 0 )
|
|
{
|
|
/* Null - print nothing */
|
|
xlat_buffer[0] = '\0';
|
|
}
|
|
else if ( c < 32 )
|
|
{
|
|
/* Some other control character: print an octal escape. */
|
|
xlat_buffer[0] = '\\';
|
|
xlat_buffer[1] = ( char ) ( '0' + ( ( c >> 6 ) & 7 ) );
|
|
xlat_buffer[2] = ( char ) ( '0' + ( ( c >> 3 ) & 7 ) );
|
|
xlat_buffer[3] = ( char ) ( '0' + ( c & 7 ) );
|
|
xlat_buffer[4] = '\0';
|
|
}
|
|
else if ( c > 178 && c < 219 )
|
|
{
|
|
/* IBM line drawing characters to ASCII characters */
|
|
if ( c == 179 )
|
|
xlat_buffer[0] = '|';
|
|
else if ( c == 186 )
|
|
xlat_buffer[0] = '#';
|
|
else if ( c == 196 )
|
|
xlat_buffer[0] = '-';
|
|
else if ( c == 205 )
|
|
xlat_buffer[0] = '=';
|
|
else
|
|
xlat_buffer[0] = '+';
|
|
xlat_buffer[1] = '\0';
|
|
}
|
|
else if ( c > 154 && c < 164 )
|
|
{
|
|
/* German character replacements */
|
|
static char xlat[] = "aeoeueAeOeUess>><<";
|
|
|
|
xlat_buffer[0] = xlat[( ( c - 155 ) * 2 ) + 0];
|
|
xlat_buffer[1] = xlat[( ( c - 155 ) * 2 ) + 1];
|
|
xlat_buffer[2] = '\0';
|
|
}
|
|
}
|
|
|
|
/* Substitute translated characters */
|
|
|
|
for ( i = 0; xlat_buffer[i] != '\0'; i++ )
|
|
{
|
|
write_char( ( unsigned char ) xlat_buffer[i] );
|
|
}
|
|
|
|
}
|
|
} /* write_zchar */
|
|
|
|
/*
|
|
* translate_to_zscii
|
|
*
|
|
*
|
|
*/
|
|
zbyte_t translate_to_zscii(int c)
|
|
{
|
|
int i;
|
|
|
|
if( c>= 0xa0 )
|
|
{
|
|
if( h_unicode_table !=0 )
|
|
{
|
|
fprintf(stderr,"[[ Unicode support not enabled yet. ]]");
|
|
}
|
|
else
|
|
{
|
|
for (i = 0x9b; i <= 0xdf; i++)
|
|
{
|
|
if (c == zscii2latin1[i - 0x9b])
|
|
{
|
|
return (zbyte_t) i;
|
|
}
|
|
}
|
|
return '?';
|
|
}
|
|
}
|
|
return (zbyte_t) c;
|
|
}
|
|
|
|
/*
|
|
* write_char
|
|
*
|
|
* High level character output routine. The write_char routine is slightly
|
|
* complicated by the fact that the output can be limited by a fixed character
|
|
* count, as well as, filling up the buffer.
|
|
*
|
|
*/
|
|
void write_char( int c )
|
|
{
|
|
char *cp;
|
|
int right_len;
|
|
|
|
/* Only do if text formatting is turned on */
|
|
|
|
if ( redirect_depth )
|
|
{
|
|
/* If redirect is on then write the character to the status line
|
|
* for V1 to V3 games or into the writeable data area for V4+ games */
|
|
if ( h_type < V4 )
|
|
{
|
|
status_line[status_pos++] = ( char ) c;
|
|
}
|
|
else
|
|
{
|
|
set_byte( story_pos++, translate_to_zscii(c) );
|
|
story_count++;
|
|
}
|
|
}
|
|
else if ( formatting == ON && screen_window == TEXT_WINDOW )
|
|
{
|
|
if ( fit_line( line, line_pos, screen_cols - right_margin ) == 0 || char_count < 1 )
|
|
{
|
|
/* Null terminate the line */
|
|
line[line_pos] = '\0';
|
|
|
|
/* If the next character is a space then no wrap is neccessary */
|
|
if ( c == ' ' )
|
|
{
|
|
z_new_line( );
|
|
c = '\0';
|
|
}
|
|
else
|
|
{
|
|
/* Wrap the line. First find the last space */
|
|
cp = strrchr( line, ' ' );
|
|
|
|
/* If no spaces in the lines then cannot do wrap */
|
|
if ( cp == NULL )
|
|
{
|
|
/* Output the buffer and a new line */
|
|
z_new_line( );
|
|
}
|
|
|
|
if (cp != NULL)
|
|
{
|
|
/* Terminate the line at the last space */
|
|
*cp++ = '\0';
|
|
|
|
/* Calculate the text length after the last space */
|
|
right_len = &line[line_pos] - cp;
|
|
|
|
/* Output the buffer and a new line */
|
|
z_new_line( );
|
|
|
|
/* If any text to wrap then move it to the start of the line */
|
|
if ( right_len > 0 )
|
|
{
|
|
memmove( line, cp, right_len );
|
|
line_pos = right_len;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* Put the character into the buffer and count it.
|
|
* Decrement line width if the character is visible */
|
|
if ( c )
|
|
{
|
|
line[line_pos++] = ( char ) c;
|
|
|
|
/* Wrap the line when there is a newline in the stream. */
|
|
cp = strrchr( line, 13 );
|
|
if ( cp!= NULL )
|
|
{
|
|
/* Terminate the line at the last space */
|
|
*cp++ = '\0';
|
|
|
|
/* Calculate the text length after the last space */
|
|
right_len = &line[line_pos] - cp;
|
|
|
|
/* Output the buffer and a new line */
|
|
z_new_line( );
|
|
|
|
/* If any text to wrap then move it to the start of the line */
|
|
if ( right_len > 0 )
|
|
{
|
|
memmove( line, cp, right_len );
|
|
line_pos = right_len;
|
|
}
|
|
}
|
|
|
|
if ( isprint( c ) )
|
|
{
|
|
char_count--;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* No formatting or output redirection, so just output the character */
|
|
script_char( c );
|
|
output_char( c );
|
|
}
|
|
|
|
} /* write_char */
|
|
|
|
/*
|
|
* z_set_text_style
|
|
*
|
|
* Set a video attribute. Write the video mode, from 0 to 8, incremented.
|
|
* This is so the output routines don't confuse video attribute 0 as the
|
|
* end of the string.
|
|
*
|
|
*/
|
|
|
|
void z_set_text_style( zword_t mode )
|
|
{
|
|
//if ( mode >= MIN_ATTRIBUTE && mode <= MAX_ATTRIBUTE )
|
|
if (mode <= MAX_ATTRIBUTE )
|
|
{
|
|
set_attribute( mode );
|
|
}
|
|
else
|
|
{
|
|
fatal( "@set_text_style called with invalid mode." );
|
|
}
|
|
} /* z_set_text_style */
|
|
|
|
/*
|
|
* write_string
|
|
*
|
|
* Output a string
|
|
*
|
|
*/
|
|
|
|
void write_string( const char *s )
|
|
{
|
|
while ( *s )
|
|
write_char( *s++ );
|
|
|
|
} /* write_string */
|
|
|
|
/*
|
|
* flush_buffer
|
|
*
|
|
* Send output buffer to the screen.
|
|
*
|
|
*/
|
|
|
|
void flush_buffer( int flag )
|
|
{
|
|
/* Terminate the line */
|
|
line[line_pos] = '\0';
|
|
|
|
/* Send the line buffer to the printer */
|
|
script_string( line );
|
|
flush_script( );
|
|
|
|
/* Send the line buffer to the screen */
|
|
output_string( line );
|
|
|
|
/* Reset the character count only if a carriage return is expected */
|
|
if ( flag == TRUE )
|
|
{
|
|
char_count = screen_cols - right_margin;
|
|
}
|
|
|
|
/* Reset the buffer pointer */
|
|
line_pos = 0;
|
|
|
|
} /* flush_buffer */
|
|
|
|
/*
|
|
* z_buffer_mode
|
|
*
|
|
* Set the format mode flag. Formatting disables writing into the output buffer.
|
|
* If set to 1, text output in the lower window on stream one is buffered so that
|
|
* it can be word-wrapped properly. If set to 0, it isn't.
|
|
*
|
|
*/
|
|
|
|
void z_buffer_mode( zword_t flag )
|
|
{
|
|
/* Flush any current output */
|
|
flush_buffer( FALSE );
|
|
|
|
/* Set formatting depending on the flag */
|
|
if ( flag )
|
|
formatting = ON;
|
|
else
|
|
formatting = OFF;
|
|
|
|
} /* z_buffer_mode */
|
|
|
|
/*
|
|
* z_output_stream
|
|
*
|
|
* Set various printing modes. These can be: disabling output, scripting and
|
|
* redirecting output. Redirection is peculiar. I use it to format the status
|
|
* line for V1 to V3 games, otherwise it wasn't used. V4 games format the
|
|
* status line themselves in an internal buffer in the writeable data area.
|
|
* To use the normal text decoding routines they have to redirect output to
|
|
* the writeable data area. This is done by passing in a buffer pointer.
|
|
* The first word of the buffer will receive the number of characters
|
|
* written since the output was redirected. The remainder of the buffer
|
|
* will contain the redirected text.
|
|
*
|
|
*/
|
|
|
|
typedef struct redirect_stash_struct
|
|
{
|
|
zword_t count;
|
|
zword_t buffer;
|
|
zword_t pos;
|
|
}
|
|
redirect_stash_t;
|
|
|
|
void z_output_stream( zword_t type, zword_t option )
|
|
{
|
|
static int redirect_size = 0;
|
|
static redirect_stash_t *stash = NULL;
|
|
|
|
if ( ( ZINT16 ) type == 1 )
|
|
{
|
|
/* Turn on text output */
|
|
outputting = ON;
|
|
}
|
|
else if ( ( ZINT16 ) type == 2 )
|
|
{
|
|
/* Turn on scripting */
|
|
open_script( );
|
|
}
|
|
else if ( ( ZINT16 ) type == 3 )
|
|
{
|
|
/* Turn on output redirection */
|
|
if ( redirect_depth == 0 )
|
|
{
|
|
/* Disable text formatting during redirection */
|
|
saved_formatting = formatting;
|
|
formatting = OFF;
|
|
|
|
/* Enable text redirection */
|
|
redirect_depth = 1;
|
|
}
|
|
else
|
|
{
|
|
if ( redirect_size == 0 )
|
|
{
|
|
redirect_size = 4;
|
|
stash = ( redirect_stash_t * ) malloc( redirect_size * sizeof ( redirect_stash_t ) );
|
|
}
|
|
if ( redirect_depth > redirect_size )
|
|
{
|
|
redirect_size *= 2;
|
|
stash = ( redirect_stash_t * ) realloc( stash, redirect_size * sizeof ( redirect_stash_t ) );
|
|
}
|
|
|
|
if ( h_type < V4 )
|
|
{
|
|
stash[redirect_depth - 1].pos = status_pos;
|
|
}
|
|
else
|
|
{
|
|
stash[redirect_depth - 1].pos = story_pos;
|
|
stash[redirect_depth - 1].buffer = story_buffer;
|
|
stash[redirect_depth - 1].count = story_count;
|
|
}
|
|
|
|
redirect_depth++;
|
|
}
|
|
|
|
/* Set up the redirection pointers */
|
|
|
|
if ( h_type < V4 )
|
|
{
|
|
status_pos = 0;
|
|
}
|
|
else
|
|
{
|
|
story_count = 0;
|
|
story_buffer = option;
|
|
story_pos = option + 2;
|
|
}
|
|
|
|
}
|
|
else if ( ( ZINT16 ) type == 4 )
|
|
{
|
|
/* Turn on input recording */
|
|
open_record( );
|
|
}
|
|
else if ( ( ZINT16 ) type == -1 )
|
|
{
|
|
/* Turn off text output */
|
|
outputting = OFF;
|
|
}
|
|
else if ( ( ZINT16 ) type == -2 )
|
|
{
|
|
/* Turn off scripting */
|
|
close_script( );
|
|
}
|
|
else if ( ( ZINT16 ) type == -3 )
|
|
{
|
|
/* Turn off output redirection */
|
|
if ( redirect_depth )
|
|
{
|
|
if ( redirect_depth == 1 )
|
|
{
|
|
/* Restore the format mode and turn off redirection */
|
|
formatting = saved_formatting;
|
|
redirect_depth = 0;
|
|
|
|
/* Terminate the redirection buffer and store the count of
|
|
* character in the buffer into the first word of the buffer */
|
|
if ( h_type > V3 )
|
|
{
|
|
set_word( story_buffer, story_count );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( h_type > V3 )
|
|
{
|
|
set_word( story_buffer, story_count );
|
|
}
|
|
|
|
redirect_depth--;
|
|
|
|
if ( h_type < V4 )
|
|
{
|
|
status_pos = stash[redirect_depth - 1].pos;
|
|
}
|
|
else
|
|
{
|
|
story_pos = stash[redirect_depth - 1].pos;
|
|
story_buffer = stash[redirect_depth - 1].buffer;
|
|
story_count = stash[redirect_depth - 1].count;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
else if ( ( ZINT16 ) type == -4 )
|
|
{
|
|
/* Turn off input recording */
|
|
close_record( );
|
|
}
|
|
} /* z_output_stream */
|
|
|
|
/*
|
|
* z_print_char
|
|
*
|
|
* Write a character.
|
|
*
|
|
*/
|
|
|
|
void z_print_char( zword_t c )
|
|
{
|
|
write_zchar( ( char ) c );
|
|
} /* z_print_char */
|
|
|
|
/*
|
|
* z_print_num
|
|
*
|
|
* Write a signed number.
|
|
*
|
|
*/
|
|
|
|
void z_print_num( zword_t num )
|
|
{
|
|
int i, count;
|
|
char buffer[10];
|
|
|
|
i = ( ZINT16 ) num;
|
|
sprintf( buffer, "%d", i );
|
|
count = strlen( buffer );
|
|
for ( i = 0; i < count; i++ )
|
|
write_char( buffer[i] );
|
|
|
|
} /* z_print_num */
|
|
|
|
/*
|
|
* z_print_paddr
|
|
*
|
|
* Print using a packed address. Packed addresses are used to save space and
|
|
* reference addresses outside of the data region.
|
|
*
|
|
*/
|
|
|
|
void z_print_paddr( zword_t packed_address )
|
|
{
|
|
unsigned long address;
|
|
|
|
/* Convert packed address to real address */
|
|
address = ( unsigned long ) packed_address * story_scaler;
|
|
|
|
/* Decode and output text at address */
|
|
decode_text( &address );
|
|
|
|
} /* z_print_paddr */
|
|
|
|
/*
|
|
* z_print_addr
|
|
*
|
|
* Print using a real address. Real addresses are just offsets into the
|
|
* data region.
|
|
*
|
|
*/
|
|
|
|
void z_print_addr( zword_t offset )
|
|
{
|
|
unsigned long address;
|
|
|
|
address = offset;
|
|
|
|
/* Decode and output text at address */
|
|
decode_text( &address );
|
|
|
|
} /* z_print_addr */
|
|
|
|
/*
|
|
* z_print_obj
|
|
*
|
|
* Print an object description. Object descriptions are stored as ASCII
|
|
* strings at the front of the property list for the object.
|
|
*
|
|
*/
|
|
|
|
void z_print_obj( zword_t obj )
|
|
{
|
|
zword_t offset;
|
|
unsigned long address;
|
|
|
|
/* Check for NULL object */
|
|
if ( obj == 0 )
|
|
return;
|
|
|
|
/* Calculate address of property list */
|
|
offset = get_object_address( obj );
|
|
offset += ( h_type < V4 ) ? O3_PROPERTY_OFFSET : O4_PROPERTY_OFFSET;
|
|
|
|
/* Read the property list address and skip the count byte */
|
|
address = ( unsigned long ) get_word( offset ) + 1;
|
|
|
|
/* Decode and output text at address */
|
|
decode_text( &address );
|
|
|
|
} /* z_print_obj */
|
|
|
|
/*
|
|
* z_print
|
|
*
|
|
* Print the string embedded in the instruction stream at this point. All
|
|
* strings that do not need to be referenced by address are embedded in the
|
|
* instruction stream. All strings that can be refered to by address are placed
|
|
* at the end of the code region and referenced by packed address.
|
|
*
|
|
*/
|
|
|
|
void z_print( void )
|
|
{
|
|
|
|
/* Decode and output text at PC */
|
|
decode_text( &pc );
|
|
|
|
} /* z_print */
|
|
|
|
/*
|
|
* z_print_ret
|
|
*
|
|
* Print a string embedded in the instruction stream as with print_literal,
|
|
* except flush the output buffer and write a new line. After this return from
|
|
* the current subroutine with a status of true.
|
|
*
|
|
*/
|
|
|
|
void z_print_ret( void )
|
|
{
|
|
|
|
z_print( );
|
|
z_new_line( );
|
|
z_ret( TRUE );
|
|
|
|
} /* z_print_ret */
|
|
|
|
/*
|
|
* z_new_line
|
|
*
|
|
* Simply flush the current contents of the output buffer followed by a new
|
|
* line.
|
|
*
|
|
*/
|
|
|
|
void z_new_line( void )
|
|
{
|
|
|
|
/* Only flush buffer if story redirect is off */
|
|
if ( redirect_depth == 0 )
|
|
{
|
|
flush_buffer( TRUE );
|
|
script_new_line( );
|
|
output_new_line( );
|
|
}
|
|
else
|
|
{
|
|
write_char( '\r' );
|
|
}
|
|
|
|
} /* z_new_line */
|
|
|
|
/*
|
|
* print_time
|
|
*
|
|
* Print the time as HH:MM [am|pm]. This is a bit language dependent and can
|
|
* quite easily be changed. If you change the size of the time string output
|
|
* then adjust the status line position in display_status_line.
|
|
*
|
|
*/
|
|
|
|
void print_time( int hours, int minutes )
|
|
{
|
|
int pm_indicator;
|
|
|
|
/* Remember if time is pm */
|
|
pm_indicator = ( hours < 12 ) ? OFF : ON;
|
|
|
|
/* Convert 24 hour clock to 12 hour clock */
|
|
hours %= 12;
|
|
if ( hours == 0 )
|
|
hours = 12;
|
|
|
|
/* Write hour right justified */
|
|
if ( hours < 10 )
|
|
write_char( ' ' );
|
|
z_print_num( (zword_t)hours );
|
|
|
|
/* Write hours/minutes separator */
|
|
write_char( ':' );
|
|
|
|
/* Write minutes zero filled */
|
|
if ( minutes < 10 )
|
|
write_char( '0' );
|
|
z_print_num( (zword_t)minutes );
|
|
|
|
/* Write the am or pm string */
|
|
if ( pm_indicator == ON )
|
|
write_string( " pm" );
|
|
else
|
|
write_string( " am" );
|
|
|
|
} /* print_time */
|
|
|
|
/*
|
|
* z_encode
|
|
*
|
|
* Convert text to packed text.
|
|
*
|
|
*/
|
|
|
|
void z_encode( zword_t word_addr, zword_t word_length, zword_t word_offset, zword_t dest_addr )
|
|
{
|
|
ZINT16 word[3];
|
|
int i;
|
|
|
|
/* Encode the word */
|
|
|
|
encode_text( word_length, ( const char * ) &datap[word_addr + word_offset], word );
|
|
|
|
/* Move the encoded word, byte swapped, into the destination buffer */
|
|
|
|
for ( i = 0; i < 3; i++, dest_addr += 2 )
|
|
set_word( dest_addr, word[i] );
|
|
|
|
} /* z_encode */
|