mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-27 05:52:11 +00:00
853 lines
No EOL
30 KiB
C#
853 lines
No EOL
30 KiB
C#
/* txio.c
|
|
*
|
|
* I/O routines for Z code disassembler and story file dumper.
|
|
*
|
|
* Mark Howell 26 August 1992 howell_ma@movies.enet.dec.com
|
|
*
|
|
*/
|
|
|
|
using zword_t = System.UInt16;
|
|
using zbyte_t = System.Byte;
|
|
|
|
using System;
|
|
using System.IO;
|
|
namespace ZTools
|
|
{
|
|
public static class txio
|
|
{
|
|
internal static tx_h.zheader_t header;
|
|
|
|
internal static ulong story_scaler;
|
|
internal static ulong story_shift;
|
|
internal static ulong code_scaler;
|
|
internal static ulong code_shift;
|
|
internal static int property_mask;
|
|
internal static int property_size_mask;
|
|
|
|
internal static bool option_inform = false;
|
|
|
|
internal static int file_size = 0;
|
|
|
|
internal static String[] v1_lookup_table;
|
|
internal static String[] v3_lookup_table;
|
|
|
|
static String[] euro_substitute;
|
|
|
|
static String[] inform_euro_substitute;
|
|
|
|
internal static int lookup_table_loaded = 0;
|
|
internal static char[,] lookup_table;
|
|
|
|
static txio()
|
|
{
|
|
lookup_table = new char[3, 26];
|
|
|
|
v1_lookup_table = new String[] {
|
|
"abcdefghijklmnopqrstuvwxyz",
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
" 0123456789.,!?_#'\"/\\<-:()"
|
|
};
|
|
|
|
v3_lookup_table = new string[] {
|
|
"abcdefghijklmnopqrstuvwxyz",
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
" \n0123456789.,!?_#'\"/\\-:()"
|
|
};
|
|
|
|
euro_substitute = new string[] {
|
|
"ae", "oe", "ue", "Ae", "Oe", "Ue", "ss", ">>", "<<", "e",
|
|
"i", "y", "E", "I", "a", "e", "i", "o", "u", "y",
|
|
"A", "E", "I", "O", "U", "Y", "a", "e", "i", "o",
|
|
"u", "A", "E", "I", "O", "U", "a", "e", "i", "o",
|
|
"u", "A", "E", "I", "O", "U", "a", "A", "o", "O",
|
|
"a", "n", "o", "A", "N", "O", "ae", "AE", "c", "C",
|
|
"th", "th", "Th", "Th", "L", "oe", "OE", "!", "?"
|
|
};
|
|
|
|
inform_euro_substitute = new String[] {
|
|
"ae", "oe", "ue", "AE", "OE", "UE", "ss", ">>", "<<", ":e",
|
|
":i", ":y", ":E", ":I", "'a", "'e", "'i", "'o", "'u", "'y",
|
|
"'A", "'E", "'I", "'O", "'U", "'Y", "`a", "`e", "`i", "`o",
|
|
"`u", "`A", "`E", "`I", "`O", "`U", "^a", "^e", "^i", "^o",
|
|
"^u", "^A", "^E", "^I", "^O", "^U", "oa", "oA", "\\o", "\\O",
|
|
"~a", "~n", "~o", "~A", "~N", "~O", "ae", "AE", "cc", "cC",
|
|
"th", "et", "Th", "Et", "LL", "oe", "OE", "!!", "??"
|
|
};
|
|
}
|
|
|
|
const int TX_SCREEN_COLS = 79;
|
|
|
|
static char[] tx_line = null;
|
|
internal static int tx_line_pos = 0;
|
|
internal static int tx_col = 1;
|
|
internal static int tx_margin = 0;
|
|
internal static int tx_do_margin = 1;
|
|
internal static int tx_screen_cols = TX_SCREEN_COLS;
|
|
|
|
//internal class cache_entry_t
|
|
//{
|
|
// cache_entry_t flink;
|
|
// // uint page_number;
|
|
// zbyte_t[] data = new zbyte_t[tx_h.PAGE_SIZE];
|
|
//}
|
|
|
|
static Stream gfp = null;
|
|
|
|
//static cache_entry_t *cache = NULL;
|
|
|
|
// static uint current_data_page = 0;
|
|
//static cache_entry_t *current_data_cachep = NULL;
|
|
|
|
// static uint data_size;
|
|
|
|
static zbyte_t[] buffer;
|
|
|
|
internal static void configure(int min_version, int max_version)
|
|
{
|
|
// buffer = new zbyte_t[tx_h.PAGE_SIZE];
|
|
int i;
|
|
|
|
buffer = new zbyte_t[gfp.Length];
|
|
gfp.Read(buffer, 0, buffer.Length);
|
|
|
|
//#if !defined(lint)
|
|
// assert (sizeof (zheader_t) == 64);
|
|
// assert (sizeof (zheader_t) <= PAGE_SIZE);
|
|
//#endif /* !defined(lint) */
|
|
|
|
// read_page(0, buffer);
|
|
tx_h.datap = buffer;
|
|
|
|
header = new tx_h.zheader_t();
|
|
|
|
header.version = tx_h.get_byte(tx_h.H_VERSION);
|
|
header.config = tx_h.get_byte(tx_h.H_CONFIG);
|
|
header.release = tx_h.get_word(tx_h.H_RELEASE);
|
|
header.resident_size = tx_h.get_word(tx_h.H_RESIDENT_SIZE);
|
|
header.start_pc = tx_h.get_word(tx_h.H_START_PC);
|
|
header.dictionary = tx_h.get_word(tx_h.H_DICTIONARY);
|
|
header.objects = tx_h.get_word(tx_h.H_OBJECTS);
|
|
header.globals = tx_h.get_word(tx_h.H_GLOBALS);
|
|
header.dynamic_size = tx_h.get_word(tx_h.H_DYNAMIC_SIZE);
|
|
header.flags = tx_h.get_word(tx_h.H_FLAGS);
|
|
for (i = 0; i < header.serial.Length; i++)
|
|
header.serial[i] = tx_h.get_byte(tx_h.H_SERIAL + i);
|
|
header.abbreviations = tx_h.get_word(tx_h.H_ABBREVIATIONS);
|
|
header.file_size = tx_h.get_word(tx_h.H_FILE_SIZE);
|
|
header.checksum = tx_h.get_word(tx_h.H_CHECKSUM);
|
|
header.interpreter_number = tx_h.get_byte(tx_h.H_INTERPRETER_NUMBER);
|
|
header.interpreter_version = tx_h.get_byte(tx_h.H_INTERPRETER_VERSION);
|
|
header.screen_rows = tx_h.get_byte(tx_h.H_SCREEN_ROWS);
|
|
header.screen_columns = tx_h.get_byte(tx_h.H_SCREEN_COLUMNS);
|
|
header.screen_width = tx_h.get_word(tx_h.H_SCREEN_WIDTH);
|
|
header.screen_height = tx_h.get_word(tx_h.H_SCREEN_HEIGHT);
|
|
if (header.version != tx_h.V6)
|
|
{
|
|
header.font_width = (byte)tx_h.get_word(tx_h.H_FONT_WIDTH);
|
|
header.font_height = tx_h.get_byte(tx_h.H_FONT_HEIGHT);
|
|
}
|
|
else
|
|
{
|
|
header.font_width = (byte)tx_h.get_word(tx_h.H_FONT_HEIGHT);
|
|
header.font_height = tx_h.get_byte(tx_h.H_FONT_WIDTH);
|
|
}
|
|
header.routines_offset = tx_h.get_word(tx_h.H_ROUTINES_OFFSET);
|
|
header.strings_offset = tx_h.get_word(tx_h.H_STRINGS_OFFSET);
|
|
header.default_background = tx_h.get_byte(tx_h.H_DEFAULT_BACKGROUND);
|
|
header.default_foreground = tx_h.get_byte(tx_h.H_DEFAULT_FOREGROUND);
|
|
header.terminating_keys = tx_h.get_word(tx_h.H_TERMINATING_KEYS);
|
|
header.line_width = tx_h.get_word(tx_h.H_LINE_WIDTH);
|
|
header.specification_hi = tx_h.get_byte(tx_h.H_SPECIFICATION_HI);
|
|
header.specification_lo = tx_h.get_byte(tx_h.H_SPECIFICATION_LO);
|
|
header.alphabet = tx_h.get_word(tx_h.H_ALPHABET);
|
|
header.mouse_table = tx_h.get_word(tx_h.H_MOUSE_TABLE);
|
|
for (i = 0; i < header.name.Length; i++)
|
|
header.name[i] = tx_h.get_byte(tx_h.H_NAME + i);
|
|
|
|
if ((uint)header.version < (uint)min_version ||
|
|
(uint)header.version > (uint)max_version ||
|
|
((uint)header.config & tx_h.CONFIG_BYTE_SWAPPED) != 0)
|
|
{
|
|
throw new ArgumentException("\nFatal: wrong game or version\n");
|
|
}
|
|
|
|
if ((uint)header.version < tx_h.V4)
|
|
{
|
|
story_scaler = 2;
|
|
story_shift = 1;
|
|
code_scaler = 2;
|
|
code_shift = 1;
|
|
property_mask = tx_h.P3_MAX_PROPERTIES - 1;
|
|
property_size_mask = 0xe0;
|
|
}
|
|
else if ((uint)header.version < tx_h.V6)
|
|
{
|
|
story_scaler = 4;
|
|
story_shift = 2;
|
|
code_scaler = 4;
|
|
code_shift = 2;
|
|
property_mask = tx_h.P4_MAX_PROPERTIES - 1;
|
|
property_size_mask = 0x3f;
|
|
}
|
|
else if ((uint)header.version < tx_h.V8)
|
|
{
|
|
story_scaler = 8;
|
|
story_shift = 3;
|
|
code_scaler = 4;
|
|
code_shift = 2;
|
|
property_mask = tx_h.P4_MAX_PROPERTIES - 1;
|
|
property_size_mask = 0x3f;
|
|
}
|
|
else
|
|
{
|
|
story_scaler = 8;
|
|
story_shift = 3;
|
|
code_scaler = 8;
|
|
code_shift = 3;
|
|
property_mask = tx_h.P4_MAX_PROPERTIES - 1;
|
|
property_size_mask = 0x3f;
|
|
}
|
|
|
|
/* Calculate the file size */
|
|
|
|
if ((uint)header.file_size == 0)
|
|
{
|
|
throw new ArgumentException("Can't handle files with no length. Giving up!");
|
|
// file_size = get_story_size();
|
|
}
|
|
else if ((uint)header.version <= tx_h.V3)
|
|
file_size = header.file_size * 2;
|
|
else if ((uint)header.version <= tx_h.V5)
|
|
file_size = header.file_size * 4;
|
|
else
|
|
file_size = header.file_size * 8;
|
|
|
|
}/* configure */
|
|
|
|
internal static void open_story(byte[] story) {
|
|
gfp = new MemoryStream(story);
|
|
}
|
|
|
|
internal static void open_story(string storyname)
|
|
{
|
|
gfp = new FileStream(storyname, FileMode.Open);
|
|
if (gfp == null)
|
|
{
|
|
Console.Error.WriteLine("Fatal: game file not found\n");
|
|
}
|
|
|
|
} /* open_story */
|
|
|
|
internal static void close_story()
|
|
{
|
|
if (gfp != null)
|
|
{
|
|
gfp.Close();
|
|
}
|
|
}/* close_story */
|
|
|
|
internal static void read_page(uint page, byte[] buffer)
|
|
{
|
|
int bytes_to_read;
|
|
|
|
if (file_size == 0)
|
|
bytes_to_read = 64;
|
|
else if (page != (uint)(file_size / tx_h.PAGE_SIZE))
|
|
bytes_to_read = tx_h.PAGE_SIZE;
|
|
else
|
|
bytes_to_read = (int)(file_size & tx_h.PAGE_MASK);
|
|
|
|
gfp.Position = page * tx_h.PAGE_SIZE;
|
|
gfp.Read(buffer, 0, bytes_to_read);
|
|
|
|
} /* read_page */
|
|
|
|
//internal static void load_cache()
|
|
//{
|
|
// // ulong file_size;
|
|
// // uint i, file_pages, data_pages;
|
|
// // cache_entry_t *cachep;
|
|
|
|
// // /* Must have at least one cache page for memory calculation */
|
|
|
|
// // cachep = (cache_entry_t *) malloc (sizeof (cache_entry_t));
|
|
// // if (cachep == NULL) {
|
|
// // (void) fprintf (stderr, "\nFatal: insufficient memory\n");
|
|
// // exit (EXIT_FAILURE);
|
|
// // }
|
|
// // cachep->flink = cache;
|
|
// // cachep->page_number = 0;
|
|
// // cache = cachep;
|
|
|
|
// // /* Calculate dynamic cache pages required */
|
|
|
|
// // data_pages = ((uint) header.resident_size + PAGE_MASK) >> PAGE_SHIFT;
|
|
// // data_size = data_pages * PAGE_SIZE;
|
|
// // file_size = (ulong) header.file_size * story_scaler;
|
|
// // file_pages = (uint) ((file_size + PAGE_MASK) >> PAGE_SHIFT);
|
|
|
|
// // /* Allocate static data area and initialise it */
|
|
|
|
// // datap = (zbyte_t *) malloc ((size_t) data_size);
|
|
// // if (datap == NULL) {
|
|
// // (void) fprintf (stderr, "\nFatal: insufficient memory\n");
|
|
// // exit (EXIT_FAILURE);
|
|
// // }
|
|
// // for (i = 0; i < data_pages; i++)
|
|
// // read_page (i, &datap[i * PAGE_SIZE]);
|
|
|
|
// // /* Allocate cache pages and initialise them */
|
|
|
|
// // for (i = data_pages; cachep != NULL && i < file_pages && i < data_pages + MAX_CACHE; i++) {
|
|
// // cachep = (cache_entry_t *) malloc (sizeof (cache_entry_t));
|
|
// // if (cachep != NULL) {
|
|
// // cachep->flink = cache;
|
|
// // cachep->page_number = i;
|
|
// // read_page (cachep->page_number, cachep->data);
|
|
// // cache = cachep;
|
|
// // }
|
|
// // }
|
|
|
|
//} /* load_cache */
|
|
|
|
internal static zword_t read_data_word(ref ulong addr)
|
|
{
|
|
uint w;
|
|
|
|
w = (uint)read_data_byte(ref addr) << 8;
|
|
w |= read_data_byte(ref addr);
|
|
|
|
return (zword_t)w;
|
|
|
|
}/* txio.read_data_word */
|
|
|
|
internal static zbyte_t read_data_byte(ref ulong addr)
|
|
{
|
|
// uint page_number, page_offset;
|
|
zbyte_t value;
|
|
|
|
//if (addr < (ulong)txio.data_size)
|
|
// value = buffer[addr];
|
|
//else
|
|
//{
|
|
// page_number = (uint)(addr >> tx_h.PAGE_SHIFT);
|
|
// page_offset = (uint)(addr & (ulong)tx_h.PAGE_MASK);
|
|
// if (page_number != current_data_page)
|
|
// {
|
|
// current_data_cachep = update_cache(page_number);
|
|
// current_data_page = page_number;
|
|
// }
|
|
// value = current_data_cachep->data[page_offset];
|
|
//}
|
|
|
|
value = buffer[addr];
|
|
addr++;
|
|
|
|
return (value);
|
|
}/* txio.read_data_byte */
|
|
|
|
internal static int decode_text(ref ulong address)
|
|
{
|
|
int i, j, char_count, synonym_flag, synonym = 0, ascii_flag, ascii = 0;
|
|
int data, code, shift_state, shift_lock;
|
|
ulong addr;
|
|
|
|
/*
|
|
* Load correct character translation table for this game.
|
|
*/
|
|
|
|
if (lookup_table_loaded == 0)
|
|
{
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
for (j = 0; j < 26; j++)
|
|
{
|
|
if ((uint)header.alphabet > 0)
|
|
{
|
|
lookup_table[i, j] = (char)tx_h.get_byte(txio.header.alphabet + (i * 26) + j);
|
|
}
|
|
else
|
|
{
|
|
if ((uint)txio.header.version == tx_h.V1)
|
|
lookup_table[i, j] = v1_lookup_table[i][j];
|
|
else
|
|
lookup_table[i, j] = v3_lookup_table[i][j];
|
|
}
|
|
|
|
if (option_inform && lookup_table[i, j] == '\"')
|
|
{
|
|
lookup_table[i, j] = '~';
|
|
}
|
|
}
|
|
lookup_table_loaded = 1;
|
|
}
|
|
}
|
|
|
|
/* Set state variables */
|
|
|
|
shift_state = 0;
|
|
shift_lock = 0;
|
|
char_count = 0;
|
|
ascii_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 = txio.read_data_word(ref address);
|
|
|
|
for (i = 10; i >= 0; i -= 5)
|
|
{
|
|
/* Get code, high bits first */
|
|
|
|
code = (data >> i) & 0x1f;
|
|
|
|
/* Synonym codes */
|
|
|
|
if (synonym_flag > 0)
|
|
{
|
|
|
|
synonym_flag = 0;
|
|
synonym = (synonym - 1) * 64;
|
|
addr = (ulong)tx_h.get_word(header.abbreviations + synonym + (code * 2)) * 2;
|
|
char_count += txio.decode_text(ref addr);
|
|
shift_state = shift_lock;
|
|
|
|
/* ASCII codes */
|
|
|
|
}
|
|
else if (ascii_flag > 0)
|
|
{
|
|
|
|
/*
|
|
* If this is the first part ASCII 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 3 bits. The second code contains the bottom 5 bits.
|
|
*/
|
|
|
|
if (ascii_flag++ == 1)
|
|
|
|
ascii = code << 5;
|
|
|
|
/*
|
|
* If this is the second part ASCII code then assemble the
|
|
* character from the two codes and output it.
|
|
*/
|
|
|
|
else
|
|
{
|
|
|
|
ascii_flag = 0;
|
|
txio.tx_printf("{0}", (char)(ascii | code));
|
|
char_count++;
|
|
|
|
}
|
|
|
|
/* Character codes */
|
|
|
|
}
|
|
else if (code > 5)
|
|
{
|
|
|
|
code -= 6;
|
|
|
|
/*
|
|
* If this is character 0 in the punctuation set then the next two
|
|
* codes make an ASCII character.
|
|
*/
|
|
|
|
if (shift_state == 2 && code == 0)
|
|
|
|
ascii_flag = 1;
|
|
|
|
/*
|
|
* If this is character 1 in the punctuation set then this
|
|
* is a new line.
|
|
*/
|
|
|
|
else if (shift_state == 2 && code == 1 && (uint)header.version > tx_h.V1)
|
|
|
|
txio.tx_printf("{0}", (option_inform) ? '^' : '\n');
|
|
|
|
/*
|
|
* This is a normal character so select it from the character
|
|
* table appropriate for the current shift state.
|
|
*/
|
|
|
|
else
|
|
{
|
|
|
|
txio.tx_printf("{0}", (char)lookup_table[shift_state, code]);
|
|
char_count++;
|
|
|
|
}
|
|
|
|
shift_state = shift_lock;
|
|
|
|
/* Special codes 0 to 5 */
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Space: 0
|
|
*
|
|
* Output a space character.
|
|
*
|
|
*/
|
|
|
|
if (code == 0)
|
|
{
|
|
|
|
txio.tx_printf(" ");
|
|
char_count++;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* The use of the synonym and shift codes is the only difference between
|
|
* the different versions.
|
|
*/
|
|
|
|
if ((uint)header.version < tx_h.V3)
|
|
{
|
|
|
|
/*
|
|
* Newline or synonym: 1
|
|
*
|
|
* Output a newline character or set synonym flag.
|
|
*
|
|
*/
|
|
|
|
if (code == 1)
|
|
{
|
|
|
|
if ((uint)header.version == tx_h.V1)
|
|
{
|
|
txio.tx_printf("{0}", (option_inform) ? '^' : '\n');
|
|
char_count++;
|
|
}
|
|
else
|
|
{
|
|
synonym_flag = 1;
|
|
synonym = code;
|
|
}
|
|
|
|
/*
|
|
* 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 |
|
|
* +-------------+-------------+-------------+-------------+-------------+
|
|
*
|
|
*/
|
|
|
|
}
|
|
else
|
|
{
|
|
if (code < 4)
|
|
shift_state = (shift_lock + code + 2) % 3;
|
|
else
|
|
shift_lock = shift_state = (shift_lock + code) % 3;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* 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);
|
|
|
|
return (char_count);
|
|
|
|
}/* txio.decode_text */
|
|
|
|
//#ifdef __STDC__
|
|
//static cache_entry_t *update_cache (uint page_number)
|
|
//#else
|
|
//static cache_entry_t *update_cache (page_number)
|
|
//uint page_number;
|
|
//#endif
|
|
//{
|
|
// cache_entry_t *cachep, *lastp;
|
|
|
|
// for (lastp = cache, cachep = cache;
|
|
// cachep->flink != NULL &&
|
|
// cachep->page_number &&
|
|
// cachep->page_number != page_number;
|
|
// lastp = cachep, cachep = cachep->flink)
|
|
// ;
|
|
// if (cachep->page_number != page_number) {
|
|
// if (cachep->flink == NULL && cachep->page_number) {
|
|
// if (current_data_page == (uint) cachep->page_number)
|
|
// current_data_page = 0;
|
|
// }
|
|
// cachep->page_number = page_number;
|
|
// read_page (page_number, cachep->data);
|
|
// }
|
|
// if (lastp != cache) {
|
|
// lastp->flink = cachep->flink;
|
|
// cachep->flink = cache;
|
|
// cache = cachep;
|
|
// }
|
|
|
|
// return (cachep);
|
|
|
|
//}/* update_cache */
|
|
|
|
///*
|
|
// * get_story_size
|
|
// *
|
|
// * Calculate the size of the game file. Only used for very old games that do not
|
|
// * have the game file size in the header.
|
|
// *
|
|
// */
|
|
|
|
//#ifdef __STDC__
|
|
//static ulong get_story_size (void)
|
|
//#else
|
|
//static ulong get_story_size ()
|
|
//#endif
|
|
//{
|
|
// ulong file_length;
|
|
|
|
// /* Read whole file to calculate file size */
|
|
|
|
// rewind (gfp);
|
|
// for (file_length = 0; fgetc (gfp) != EOF; file_length++)
|
|
// ;
|
|
// rewind (gfp);
|
|
|
|
// return (file_length);
|
|
|
|
//}/* get_story_size */
|
|
|
|
internal static void tx_printf(String format, params Object[] args)
|
|
{
|
|
int count, i;
|
|
|
|
if (tx_screen_cols != 0)
|
|
{
|
|
if (tx_line == null || tx_line.Length == 0)
|
|
{
|
|
tx_line = new char[TX_SCREEN_COLS];
|
|
}
|
|
|
|
|
|
String temp = String.Format(format, args);
|
|
count = temp.Length;
|
|
if (count > TX_SCREEN_COLS)
|
|
{
|
|
throw new ArgumentException("\nFatal: buffer space overflow\n");
|
|
}
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
tx_write_char(temp[i]);
|
|
}
|
|
}
|
|
else
|
|
sb.AppendFormat(format, args);
|
|
|
|
}/* txio.tx_printf */
|
|
|
|
private static void write_high_zscii(int c)
|
|
{
|
|
// static zword_t unicode_table[256];
|
|
// static int unicode_table_loaded;
|
|
// int unicode_table_addr;
|
|
// int length, i;
|
|
|
|
// if (!unicode_table_loaded) {
|
|
// if (header.mouse_table && (tx_h.get_word(header.mouse_table) > 2)) {
|
|
// unicode_table_addr = tx_h.get_word(header.mouse_table + 6);
|
|
// if (unicode_table_addr) {
|
|
// length = tx_h.get_byte(unicode_table_addr);
|
|
// for (i = 0; i < unicode_table_addr; i++)
|
|
// unicode_table[i + 155] = tx_h.get_word(unicode_table_addr + 1 + i*2);
|
|
// }
|
|
// }
|
|
// unicode_table_loaded = 1;
|
|
// }
|
|
|
|
// if ((c <= 0xdf) && !unicode_table[c]) {
|
|
// if (option_inform)
|
|
// txio.tx_printf("@%s", inform_euro_substitute[c - 0x9b]);
|
|
// else
|
|
// txio.tx_printf (euro_substitute[c - 0x9b]);
|
|
// }
|
|
// else /* no non-inform version of these. */
|
|
// txio.tx_printf("@{%x}", unicode_table[c]);
|
|
}
|
|
|
|
static void tx_write_char(int c)
|
|
{
|
|
int i;
|
|
int cp;
|
|
|
|
/* In V6 games a tab is a paragraph indent gap and a vertical tab is
|
|
an inter-sentence gap. Both can be set to a space for readability */
|
|
|
|
if (c == '\v' || c == '\t')
|
|
c = ' ';
|
|
|
|
// /* European characters should be substituted by their replacements. */
|
|
|
|
if (c >= 0x9b && c <= 0xfb)
|
|
{
|
|
write_high_zscii(c);
|
|
return;
|
|
}
|
|
|
|
if (tx_col == tx_screen_cols + 1 || c == '\n')
|
|
{
|
|
tx_do_margin = 1;
|
|
if (tx_line_pos < tx_line.Length) tx_line[tx_line_pos++] = '\0';
|
|
int eol = tx_line.ToString().IndexOf('\0');
|
|
if (eol == -1) eol = tx_line_pos;
|
|
String temp = new String(tx_line, 0, tx_line_pos);
|
|
cp = temp.LastIndexOf(' ');
|
|
if (c == ' ' || c == '\n' || cp == -1)
|
|
{
|
|
sb.Append(temp);
|
|
sb.Append('\n');
|
|
tx_line_pos = 0;
|
|
tx_col = 1;
|
|
|
|
clear_tx_line();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
tx_line[cp++] = '\0';
|
|
|
|
sb.Append(temp.Substring(0, cp));
|
|
sb.Append('\n');
|
|
tx_line_pos = 0;
|
|
tx_col = 1;
|
|
txio.tx_printf("{0}", temp.Substring(cp));
|
|
}
|
|
}
|
|
|
|
if (tx_do_margin > 0)
|
|
{
|
|
tx_do_margin = 0;
|
|
for (i = 1; i < tx_margin; i++)
|
|
tx_write_char(' ');
|
|
}
|
|
|
|
tx_line[tx_line_pos++] = (char)c;
|
|
tx_col++;
|
|
|
|
}/* tx_write_char */
|
|
|
|
private static void clear_tx_line()
|
|
{
|
|
for (int i = 0; i < tx_line.Length; i++)
|
|
{
|
|
tx_line[i] = ' ';
|
|
}
|
|
}
|
|
|
|
internal static void tx_fix_margin(int flag)
|
|
{
|
|
txio.tx_margin = (flag) > 0 ? txio.tx_col : 0;
|
|
|
|
}/* txio.tx_fix_margin */
|
|
|
|
internal static void tx_set_width(int width)
|
|
{
|
|
|
|
if (width > tx_screen_cols)
|
|
{
|
|
if (tx_line != null)
|
|
{
|
|
tx_line[tx_line_pos++] = '\0';
|
|
sb.Append(tx_line);
|
|
}
|
|
tx_line_pos = 0;
|
|
// free (tx_line);
|
|
// tx_line = NULL;
|
|
}
|
|
tx_screen_cols = width;
|
|
|
|
}/* tx_set_width */
|
|
|
|
internal static System.Text.StringBuilder sb;
|
|
internal static void startStringBuilder()
|
|
{
|
|
sb = new System.Text.StringBuilder();
|
|
}
|
|
|
|
internal static String getTextFromStringBuilder()
|
|
{
|
|
String text = sb.ToString();
|
|
sb.Length = 0;
|
|
|
|
foreach (char c in text)
|
|
{
|
|
if (c != '\0')
|
|
{
|
|
sb.Append(c);
|
|
}
|
|
}
|
|
|
|
text = sb.ToString();
|
|
sb.Length = 0;
|
|
|
|
return text;
|
|
}
|
|
|
|
}
|
|
} |