From: Galen Hazelwood Date: Fri, 17 Oct 1997 20:11:33 +0000 (+0000) Subject: Released UnixFrotz232R2Std10.tar.gz X-Git-Url: https://scope-eye.net/git/?a=commitdiff_plain;h=bb56b28408fdf1a30ca323b89169ff29c5ca4d95;p=liskon_frotz.git Released UnixFrotz232R2Std10.tar.gz --- diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..21d055f --- /dev/null +++ b/Makefile @@ -0,0 +1,73 @@ + +# Define your c compiler. I recommend gcc if you've got it. +#CC = cc +CC = gcc + +# Define your optimization flags. Most compilers understand -O and -O2, +# Debugging (don't use) +#OPTS = -Wall -g +# Standard +OPTS = -O2 +# Pentium with gcc 2.7.0 or better +#OPTS = -O2 -fomit-frame-pointer -malign-functions=2 -malign-loops=2 \ +-malign-jumps=2 + +# There are a few defines which may be necessary to make frotz compile on +# your system. Most of these are fairly straightforward. +# -DNO_MEMMOVE: Use this if your not-so-standard c library lacks the +# memmove(3) function. (SunOS, other old BSDish systems) +# +# You may need to define one of these to get the getopt prototypes: +# -DUSE_UNISTD_H: Use this if your getopt prototypes are in unistd.h. +# (Solaris?) +# -DUSE_GETOPT_H: Use this if you have a separate /usr/include/getopt.h +# file containing the getopt prototypes. (Linux, IRIX 5?) +# -DUSE_NOTHING: I've heard reports of some systems defining getopt in +# stdlib.h, which unix.c automatically includes. +# (*BSD, Solaris?) +# +# If none of the above are defined, frotz will define the getopt prototypes +# itself. +# +# -DUSE_NCURSES_H: Use this if you want to include ncurses.h rather +# than curses.h. +# +# These defines add various cosmetic features to the interpreter: +# -DCOLOR_SUPPORT: If the terminal you're using has color support, frotz +# will use the curses color routines...if your curses +# library supports color. +# -DEMACS_EDITING: This enables some of the standard emacs editing keys +# (Ctrl-B,-F,-P,-N, etc.) to be used when entering +# input. I can't see any reason why you wouldn't want +# to define it--it can't hurt anything--but you do +# have that option. +# +#DEFS = -DUSE_GETOPT_H -DCOLOR_SUPPORT -DEMACS_EDITING +DEFS = + +# This should point to the location of your curses or ncurses include file +# if it's in a non-standard place. +#INCL = -I/usr/local/include +#INCL = -I/5usr/include +INCL = + +# This should define the location and name of whatever curses library you're +# linking with. +#LIB = -L/usr/local/lib +#CURSES = -lncurses +#LIB = -L/5usr/lib +LIB = +CURSES = -lcurses + +# Nothing under this line should need to be changed. + +OBJECTS = buffer.o fastmem.o files.o hotkey.o input.o main.o math.o object.o \ + process.o random.o redirect.o screen.o sound.o stream.o table.o \ + text.o ux_init.o ux_input.o ux_pic.o ux_screen.o ux_sample.o \ + ux_text.o variable.o + +CFLAGS = $(OPTS) $(DEFS) $(INCL) + +frotz: $(OBJECTS) + $(CC) -o frotz $(OBJECTS) $(LIB) $(CURSES) + diff --git a/Readme.unix b/Readme.unix new file mode 100644 index 0000000..972b59e --- /dev/null +++ b/Readme.unix @@ -0,0 +1,318 @@ +(This Readme file was adapted from the one released with the MS-DOS binary.) + +FROTZ V2.32 - an interpreter for all Infocom games. Complies with standard +1.0 of Graham Nelson's specification. Written by Stefan Jokisch in 1995-7 + + This program once started as a re-make of Mark Howell's Zip, but + has grown into an utterly new interpreter. + + Frotz is freeware: It may be used and distributed freely provided + no commercial profit is involved. (c) 1995-1997 Stefan Jokisch. + + Please report bugs to s.jokisch@avu.de + Please report unix bugs to galenh@micron.net + + This is the unix port of Stefan Jokisch's nifty-neato-cool Z-machine +interpreter. There are only two things you need to compile Frotz: + + * Some variant of Unix with an ANSI C compiler (gcc works fine) + * A reasonably good SYSV derived curses library + + Note that the second requirement is _very_ important. I had several people +send me complaints about Frotz not working, and found out they were unaware +that their curses library was obsolete. If your system curses won't work, +pick up and compile the ncurses library from ftp://prep.ai.mit.edu/pub/gnu +or any GNU mirror site. + +Changes since Release 1: + + * Fixed problems with backspace + * Now supports "delete" key for editing + * Wired in tab completion support (which I stupidly missed) + * Added emacs editing key support (-DEMACS_EDITING) + +How to Compile: + + This source distribution comes with a unsophisticated but functional +Makefile. Edit it to reflect your system; the comments are fairly self- +explanatory. Once that's done, just type "make". + + Color support is available by putting -DCOLOR_SUPPORT in the designated +spot. I've added code in ux_init.c for sound hooks, but as no sound code is +actually written, defining SOUND_SUPPORT will merely cause compilation to fail. + + Thanks for giving this a try. Any bug reports, success stories, or +constructive criticism will be greatly appreciated. + + -- Galen Hazelwood + +Syntax: frotz [options] story-file + + -d disable color + + If frotz discovers that your terminal has color support, it will + enable it by default. This switch overrides that behavior. + + -f # set the foreground colour + -b # set the background colour + + The color numbers are the Z-machine colors, which are as follows: + 2 = black 3 = red 4 = green 5 = yellow + 6 = blue 7 = magenta 8 = cyan 9 = white + + If color support is disabled in the binary, or not available on + your terminal, these options do nothing. + + -i ignore runtime errors + + Set this switch and Frotz no longer worries about anything the + game tries to do. This can help you to get around fatal errors. + + -w # set the screen width + -h # set the screen height + -l # set the left margin + -r # set the right margin + + Setting the screen format can be useful if you are running Frotz + under Microsoft Windows, or if you want to test a game on a more + narrow screen. Setting the margins is a matter of taste; Infocom + interpreters usually set a right margin of one character (-r1). + + -S # set the width of the transscript file + + By default your transscript files are formatted to a width of 80 + columns per line -- regardless of the current screen width. This + switch allows you to change this setting. In particular, use -S0 + to deactivate automatic line splitting in transscript files. + + -c # set the number of context lines + + When the game prints several pages of text in a row, Frotz stops + for a more prompt after each page. The first prompt appears when + your input reaches the top of the window. Further prompts appear + when the previous page has been scrolled off the window. You can + use this switch to make the latter more prompts appear earlier. + + -u # set the number of undo slots for multiple undo + + Frotz tries to allocate as much conventional memory as possible + for multiple UNDO. If this strategy causes some kind of problem, + use this switch to set a tighter limit. In particular, you might + want to turn off the UNDO feature altogether by typing -u0. + + -s # set the random number seed + + The given seed value is used as the initial seed value on every + restart. This is helpful for testing games like 'Curses' which + make random decisions before the first input (such that the hot + key Alt-S does not really help). The meaning of seed values is + explained in the next section. + + -x expand abbreviations (g, x, z ==> again, examine, wait) + + This switch was made for old Infocom games that lack the common + abbreviations introduced in later games. Use it with caution: A + few games might use "g", "x" or "z" for different purposes. + + -o watch object movement + -O watch object locating + -a watch attribute assignment + -A watch attribute testing + + Although these switches may be of assistance while debugging new + games, they are are actually meant to be cheat functions. The -o + switch, for example, helps to locate the thief in 'Zork 1' and + the cat in 'Curses'. The other switches produce a lot of obscure + messages during the game; but some of these messages might give + you important clues if you watch carefully. + + -t set the Tandy bit + + Some old Infocom games were sold by the Tandy Corporation. These + games behave slightly different when you use this option. For + example, 'The Witness' gets censored: bastards turn into idiots, + private dicks into private eyes and so on. + + -p plain ascii output only + + This inhibits the output of accented letters and other characters + from the Latin-1 character set, replacing them with reasonable + alternatives. This may be necessary on devices lacking these + characters. + + -P alter behaviour of piracy opcode + + The piracy opcode was never used by Infocom, and this switch is + only useful for those who like to toy around with Z-code. + +Special keys: + + Alt-D - toggle debugging options (-a, -A, -o, -O) + Alt-H - help on hot keys + Alt-N - new game (restart) + Alt-P - turn on input line playback + Alt-R - toggle input line recording on/off + Alt-S - set the random number seed + Alt-U - multiple undo, works even for old V1 to V4 games + Alt-X - exit game + + When testing a text adventure it can be difficult to reproduce a + specific bug. To avoid this problem you should use the Alt-R key + to record all your inputs in a command file. Later you can press + Alt-P to feed the command file back into Frotz. In many cases, + however, you will find that the result is different because most + games contain random events. Luckily, Frotz provides a hot key + to control these events. Type Alt-S and you are asked for a seed + value, i.e. a value in the range from 1 to 32767. Normally, you + would choose a number >= 1000. Smaller values generate a special + sequence of random numbers as proposed by Nelson. (For instance, + the seed value 4 generates 1, 2, 3, 4, 1, 2, 3, 4, 1...). In any + case, random events become predictable until the next restart. + + See also the command line option -s above. + + cursor left/Ctrl-B - move one character to the left + cursor right/Ctrl-F - move one character to the right + home/Ctrl-A - move to beginning of line + end/Ctrl-E - move to end of line + backspace - delete character to the left + delete/Ctrl-D - delete character below cursor + insert - toggle overwrite mode on/off + escape/Ctrl-U - delete whole input line + cursor up/Ctrl-P - get previous command + cursor down/Ctrl-N - get next command + page up - scroll status window up ('Beyond Zork') + page down - scroll status window down ('Beyond Zork') + tab - word completion (like "tcsh" under Unix) + + Note that the various Control aliases only function if emacs editing + support has been compiled into your binary. See the Makefile for + more information. + + When you need to type an unpleasantly long word, try to type the + first three or four letters then press the tabulator key. If you + are lucky, Frotz fills in some or all of the missing letters. A + high beep noise indicates that the word is ambiguous; a low beep + indicates that it does not exist. Apart from that, you can also + use the history feature to get to previous input lines. Type the + beginning of the input line you are looking for, then use cursor + up/down to scroll through all input lines matching that prefix. + +Questions and answers: + + Q: What is Frotz? + A: Frotz runs text adventures which come in so-called story files: + ZORK1.DAT, TRINITY.DAT, CURSES.Z5, JIGSAW.Z8, ARTHUR.ZIP etc. + + Q: Where can I find story files to use with Frotz? + A: First, you can use the files from your original Infocom games. It + is possible to play Atari ST, Amiga or Macintosh games on your PC + once you manage to transfer the story files. Some people even + extracted story files from old Atari 800, Apple II and C-64 disks + (ask your local 8bit guru). Second, there is an increasing number + of new games available on the Internet. Check the if-archive at + ftp.gmd.de. + + Q: Why does Frotz stop with an error message? + A: It might have detected a bug in the story file other interpreters + overlooked. Use the -i switch to run your story file anyway. It's + also possible that the story file is corrupt; be sure to download + story files in binary mode, especially when you use a WWW browser. + + Q: Is there a way to exit Frotz in emergency situations? + A: Try Ctrl-break, Ctrl-C, or Ctrl-Z. The last one suspends your process + and returns you to the shell, allowing you to kill it. + + Q: What do I need to play graphic games? + A: A different port of Frotz. Try the MS-DOS version if you can use + it, or wait for the X Windows port. You can run some of the V6 games + without graphics--Arthur and Journey seem to work, but not Shogun or + Zork Zero. (Shogun dies because I can't backscroll; Zork 0 just + sputters and dies rather amusingly.) + + Q: What do I need for sound? + A: A different port of Frotz, or a later version of this one. Sound + support will first be available on Linux, and any other system which + can use the Open Sound System (or OSS/Lite). + + Q: How can I send transscription to the printer? + A: Type PRN as file name. + + Q: Why do I get weird characters instead of accented letters? + A: Because your display/terminal/window doesn't support the Latin-1 + character set properly. Use the -p flag, which uses plain ASCII + output and converts accented letters appropriately. + + Q: Why don't the number pad keys work in 'Beyond Zork'? + A: Because curses dosen't seem to know how to handle them properly. + I might add a hack to support them later. + + Q: How can I scroll the status window in 'Beyond Zork'? + A: Use page up/down instead of cursor up/down. + + Q: Why are my default color selections so weird? + A: You're used to using the standard PC color values. The unix port + wants color numbers in Z-machine form. Quick reference: + 2 = black 3 = red 4 = green 5 = yellow + 6 = blue 7 = magenta 8 = cyan 9 = white + + +List of fatal errors: + + - "Bad stack frame" + - "Byte swapped story file" + - "Call to illegal address" + - "Call to non-routine" [1] + - "Cannot open story file" + - "Division by zero" + - "Error reading save file" + - "Illegal attribute" + - "Illegal object" [2] + - "Illegal window" + - "Illegal window property" + - "Jump to illegal address" + - "Nesting stream #3 too deep" + - "No such property" + - "Out of memory" + - "Print at illegal address" + - "Stack overflow" [3] + - "Stack underflow" [4] + - "Store out of dynamic memory" + - "Story file read error" + - "Text buffer overflow" + - "Unknown opcode" + - "Unknown Z-code version" + - "Can't Happen (os_scroll_area)" [5] + + [1] The first byte of a routine must be less than 16. + [2] In V4 and above, object numbers > 2000 are considered illegal. + [3] Checked on every call instruction. + [4] Checked on every return from a subroutine. + [5] Unix-specific. Should only happen in Shogun. + +Acknowledgements: + + Many thanks to Paul D. Doherty for his continuing support of this + project. Thanks to everyone who sent bug reports, contributions or + helpful hints (in alphabetical order): + + Thomas Biskup, Ian Carpenter, Graeme Cree, Jason Dyer, + Carl Edman, Julian Eggebrecht, Bernhard Fuchs, Joe Hachem, + John Kennedy, Kirk Klobe, Marnix Klooster, John Mackin, + Paul O'Brian, Magnus Olsson, Barry Prescott, L. Ross Raszewski, + Ambat Sasi Nair, Alan Sherrod, Linards Ticmanis and Paolo Vece. + + Last but not least, thanks to the porters: + + David Kinder (Amiga), Rich Lawrence (Windows 95/NT), + Andrew Holdsworth (RiscOS), Christos Dimitrakakis (HP-UX), + Christopher J. Madsen (OS/2), Galen Hazelwood (Unix curses library), + Ian Dean (Windows CE). + + Executables are available from ftp.gmd.de and from + + http://www.geocities.com/SiliconValley/Heights/3222/frotz.html + + which is the Frotz home page maintained by Chris Madsen. + + diff --git a/Todo b/Todo new file mode 100644 index 0000000..b189a4e --- /dev/null +++ b/Todo @@ -0,0 +1,12 @@ +Things to do and bugs to investigate +------------------------------------ + +* Write sound support. This is on hold until frotz supports the new "Blorb" + format. + +* Border Zone has a strange status line feature, mixing bright and dull + whites. Not sure what's going on here. + +* Add support for GNU readline/history libraries instead of the kludgy + editing routines I wrote. If I can manage this, I'll probably release + it as a patch rather than part of a release. diff --git a/bcfrotz.h b/bcfrotz.h deleted file mode 100644 index 70c99a1..0000000 --- a/bcfrotz.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * "BCfrotz.h" - * - * Borland C interface, declarations - * - */ - -#define byte0(v) ((byte *)&v)[0] -#define byte1(v) ((byte *)&v)[1] -#define byte2(v) ((byte *)&v)[2] -#define byte3(v) ((byte *)&v)[3] -#define word0(v) ((word *)&v)[0] -#define word1(v) ((word *)&v)[1] - -#ifndef HISTORY_MIN_ENTRY -#define HISTORY_MIN_ENTRY 1 -#endif - -#define SPECIAL_KEY_MIN 256 -#define SPECIAL_KEY_HOME 256 -#define SPECIAL_KEY_END 257 -#define SPECIAL_KEY_WORD_LEFT 258 -#define SPECIAL_KEY_WORD_RIGHT 259 -#define SPECIAL_KEY_DELETE 260 -#define SPECIAL_KEY_INSERT 261 -#define SPECIAL_KEY_PAGE_UP 262 -#define SPECIAL_KEY_PAGE_DOWN 263 -#define SPECIAL_KEY_TAB 264 -#define SPECIAL_KEY_MAX 264 - -#define _MONO_ 0 -#define _TEXT_ 1 -#define _CGA_ 2 -#define _MCGA_ 3 -#define _EGA_ 4 -#define _AMIGA_ 5 - -typedef unsigned char byte; -typedef unsigned short word; - -extern display; - -extern cursor_x; -extern cursor_y; - -extern char latin1_to_ibm[]; -extern char latin1_to_ascii[]; - -extern byte text_bg; -extern byte text_fg; - -extern byte scrn_attr; - -extern user_background; -extern user_foreground; -extern user_emphasis; -extern user_reverse_bg; -extern user_reverse_fg; -extern user_screen_height; -extern user_screen_width; -extern user_tandy_bit; -extern user_bold_typing; -extern user_random_seed; -extern user_font; - -extern char stripped_story_name[]; -extern char *prog_name; - -extern current_bg; -extern current_fg; -extern current_style; -extern current_font; - -extern scaler; - -/* BCinit */ int dectoi (const char *); -/* BCinit */ int hextoi (const char *); -/* BCmouse */ bool detect_mouse (void); -/* BCmouse */ int read_mouse (void); -/* BCpic */ bool init_pictures (void); -/* BCpic */ void reset_pictures (void); -/* BCsmpl */ bool init_sound (void); -/* BCsmpl */ void reset_sound (void); -/* BCtext */ void switch_scrn_attr (bool); -/* BCtext */ void load_fonts (const char *); diff --git a/bcinit.c b/bcinit.c deleted file mode 100644 index 3fba0d9..0000000 --- a/bcinit.c +++ /dev/null @@ -1,755 +0,0 @@ -/* - * file "BCinit.c" - * - * Borland C front end, initialisation - * - */ - -#include -#include -#include -#include -#include -#include "frotz.h" -#include "BCfrotz.h" - -#define INFORMATION "\ -\n\ -FROTZ V2.32 - interpreter for all Infocom games. Complies with standard\n\ -1.0 of Graham Nelson's specification. Written by Stefan Jokisch in 1995-7\n\ -\n\ -Syntax: frotz [options] story-file\n\ -\n\ - -a watch attribute setting \t -l # left margin\n\ - -A watch attribute testing \t -o watch object movement\n\ - -b # background colour \t -O watch object locating\n\ - -B # reverse background colour\t -p alter piracy opcode\n\ - -c # context lines \t -r # right margin\n\ - -d # display mode (see below) \t -s # random number seed value\n\ - -e # emphasis colour [mode 1] \t -S # transscript width\n\ - -f # foreground colour \t -t set Tandy bit\n\ - -F # reverse foreground colour\t -T bold typing [modes 2+4+5]\n\ - -g # font [mode 5] (see below)\t -u # slots for multiple undo\n\ - -h # screen height \t -w # screen width\n\ - -i ignore runtime errors \t -x expand abbreviations g/x/z\n\ -\n\ -Fonts are 0 (fixed), 1 (sans serif), 2 (comic), 3 (times), 4 (serif).\n\ -\n\ -Display modes are 0 (mono), 1 (text), 2 (CGA), 3 (MCGA), 4 (EGA), 5 (Amiga)." - -extern unsigned cdecl _heaplen = 0x800 + 4 * BUFSIZ; -extern unsigned cdecl _stklen = 0x800; - -extern const char *optarg; -extern int optind; - -int cdecl getopt (int, char *[], const char *); - -static const char *progname = NULL; - -extern char script_name[]; -extern char command_name[]; -extern char save_name[]; -extern char auxilary_name[]; - -char stripped_story_name[10]; - -int display = -1; - -int user_background = -1; -int user_foreground = -1; -int user_emphasis = -1; -int user_bold_typing = -1; -int user_reverse_bg = -1; -int user_reverse_fg = -1; -int user_screen_height = -1; -int user_screen_width = -1; -int user_tandy_bit = -1; -int user_random_seed = -1; -int user_font = 1; - -static byte old_video_mode = 0; - -static void interrupt (*oldvect) () = NULL; - -/* - * dectoi - * - * Convert a string containing a decimal number to integer. The string may - * be NULL, but it must not be empty. - * - */ - -int dectoi (const char *s) -{ - int n = 0; - - if (s != NULL) - - do { - - n = 10 * n + (*s & 15); - - } while (*++s > ' '); - - return n; - -}/* dectoi */ - -/* - * hextoi - * - * Convert a string containing a hex number to integer. The string may be - * NULL, but it must not be empty. - * - */ - -int hextoi (const char *s) -{ - int n = 0; - - if (s != NULL) - - do { - - n = 16 * n + (*s & 15); - - if (*s > '9') - n += 9; - - } while (*++s > ' '); - - return n; - -}/* hextoi */ - -/* - * cleanup - * - * Shut down the IO interface: free memory, close files, restore - * interrupt pointers and return to the previous video mode. - * - */ - -static void cleanup (void) -{ - - reset_sound (); - reset_pictures (); - - asm mov ah,0 - asm mov al,old_video_mode - asm int 0x10 - - setvect (0x1b, oldvect); - -}/* cleanup */ - -/* - * fast_exit - * - * Handler routine to be called when the crtl-break key is pressed. - * - */ - -static void interrupt fast_exit () -{ - - cleanup (); exit (EXIT_FAILURE); - -}/* fast_exit */ - -/* - * os_fatal - * - * Display error message and exit program. - * - */ - -void os_fatal (const char *s) -{ - - if (h_interpreter_number) - os_reset_screen (); - - /* Display error message */ - - fputs ("\nFatal error: ", stderr); - fputs (s, stderr); - fputs ("\n", stderr); - - /* Abort program */ - - exit (EXIT_FAILURE); - -}/* os_fatal */ - -/* - * parse_options - * - * Parse program options and set global flags accordingly. - * - */ - -static void parse_options (int argc, char **argv) -{ - int c; - - do { - - int num = 0; - - c = getopt (argc, argv, "aAb:B:c:d:e:f:F:g:h:il:oOpr:s:S:tTu:w:x"); - - if (optarg != NULL) - num = dectoi (optarg); - - if (c == 'a') - option_attribute_assignment = 1; - if (c == 'A') - option_attribute_testing = 1; - if (c == 'b') - user_background = num; - if (c == 'B') - user_reverse_bg = num; - if (c == 'c') - option_context_lines = num; - if (c == 'd') - display = optarg[0] | 32; - if (c == 'e') - user_emphasis = num; - if (c == 'T') - user_bold_typing = 1; - if (c == 'f') - user_foreground = num; - if (c == 'F') - user_reverse_fg = num; - if (c == 'g') - user_font = num; - if (c == 'h') - user_screen_height = num; - if (c == 'i') - option_ignore_errors = 1; - if (c == 'l') - option_left_margin = num; - if (c == 'o') - option_object_movement = 1; - if (c == 'O') - option_object_locating = 1; - if (c == 'p') - option_piracy = 1; - if (c == 'r') - option_right_margin = num; - if (c == 's') - user_random_seed = num; - if (c == 'S') - option_script_cols = num; - if (c == 't') - user_tandy_bit = 1; - if (c == 'u') - option_undo_slots = num; - if (c == 'w') - user_screen_width = num; - if (c == 'x') - option_expand_abbreviations = 1; - - } while (c != EOF); - -}/* parse_options */ - -/* - * os_process_arguments - * - * Handle command line switches. Some variables may be set to activate - * special features of Frotz: - * - * option_attribute_assignment - * option_attribute_testing - * option_context_lines - * option_object_locating - * option_object_movement - * option_left_margin - * option_right_margin - * option_ignore_errors - * option_piracy - * option_undo_slots - * option_expand_abbreviations - * option_script_cols - * - * The global pointer "story_name" is set to the story file name. - * - */ - -void os_process_arguments (int argc, char *argv[]) -{ - const char *p; - int i; - - /* Parse command line options */ - - parse_options (argc, argv); - - if (optind != argc - 1) { - puts (INFORMATION); - exit (EXIT_FAILURE); - } - - /* Set the story file name */ - - story_name = argv[optind]; - - /* Strip path and extension off the story file name */ - - p = story_name; - - for (i = 0; story_name[i] != 0; i++) - if (story_name[i] == '\\' || story_name[i] == ':') - p = story_name + i + 1; - - for (i = 0; p[i] != 0 && p[i] != '.' && i < 8; i++) - stripped_story_name[i] = p[i]; - - stripped_story_name[i] = 0; - - /* Create nice default file names */ - - strcpy (script_name, stripped_story_name); - strcpy (command_name, stripped_story_name); - strcpy (save_name, stripped_story_name); - strcpy (auxilary_name, stripped_story_name); - - strcat (script_name, ".scr"); - strcat (command_name, ".rec"); - strcat (save_name, ".sav"); - strcat (auxilary_name, ".aux"); - - /* Save the executable file name */ - - progname = argv[0]; - -}/* os_process_arguments */ - -/* - * standard_palette - * - * Set palette registers to EGA default values and call VGA BIOS to - * use DAC registers #0 to #63. - * - */ - -static void standard_palette (void) -{ - - static byte palette[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, - 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - 0x00 /* last one is the overscan register */ - }; - - if (display == _AMIGA_) { - asm mov ax,0x1002 - asm lea dx,palette - asm push ds - asm pop es - asm int 0x10 - asm mov ax,0x1013 - asm mov bx,0x0001 - asm int 0x10 - } - -}/* standard_palette */ - -/* - * special_palette - * - * Set palette register #i to value i and call VGA BIOS to use DAC - * registers #64 to #127. - * - */ - -static void special_palette (void) -{ - - static byte palette[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x00 /* last one is the overscan register */ - }; - - if (display == _AMIGA_) { - asm mov ax,0x1002 - asm mov dx,offset palette - asm push ds - asm pop es - asm int 0x10 - asm mov ax,0x1013 - asm mov bx,0x0101 - asm int 0x10 - } - -}/* special_palette */ - -/* - * os_init_screen - * - * Initialise the IO interface. Prepare the screen and other devices - * (mouse, sound board). Set various OS depending story file header - * entries: - * - * h_config (aka flags 1) - * h_flags (aka flags 2) - * h_screen_cols (aka screen width in characters) - * h_screen_rows (aka screen height in lines) - * h_screen_width - * h_screen_height - * h_font_height (defaults to 1) - * h_font_width (defaults to 1) - * h_default_foreground - * h_default_background - * h_interpreter_number - * h_interpreter_version - * h_user_name (optional; not used by any game) - * - * Finally, set reserve_mem to the amount of memory (in bytes) that - * should not be used for multiple undo and reserved for later use. - * - */ - -void os_init_screen (void) -{ - static byte zcolour[] = { - BLACK_COLOUR, - BLUE_COLOUR, - GREEN_COLOUR, - CYAN_COLOUR, - RED_COLOUR, - MAGENTA_COLOUR, - BROWN + 16, - LIGHTGRAY + 16, - GREY_COLOUR, - LIGHTBLUE + 16, - LIGHTGREEN + 16, - LIGHTCYAN + 16, - LIGHTRED + 16, - LIGHTMAGENTA + 16, - YELLOW_COLOUR, - WHITE_COLOUR - }; - - static struct { /* information on modes 0 to 5 */ - byte vmode; - word width; - word height; - byte font_width; - byte font_height; - byte fg; - byte bg; - } info[] = { - { 0x07, 80, 25, 1, 1, LIGHTGRAY + 16, BLACK_COLOUR }, /* MONO */ - { 0x03, 80, 25, 1, 1, LIGHTGRAY + 16, BLUE_COLOUR }, /* TEXT */ - { 0x06, 640, 200, 8, 8, WHITE_COLOUR, BLACK_COLOUR }, /* CGA */ - { 0x13, 320, 200, 5, 8, WHITE_COLOUR, GREY_COLOUR }, /* MCGA */ - { 0x0e, 640, 200, 8, 8, WHITE_COLOUR, BLUE_COLOUR }, /* EGA */ - { 0x12, 640, 400, 8, 16, WHITE_COLOUR, BLACK_COLOUR } /* AMIGA */ - }; - - static struct { /* information on modes A to E */ - word vesamode; - word width; - word height; - } subinfo[] = { - { 0x001, 40, 25 }, - { 0x109, 132, 25 }, - { 0x10b, 132, 50 }, - { 0x108, 80, 60 }, - { 0x10c, 132, 60 } - }; - - int subdisplay; - - /* Get the current video mode. This video mode will be selected - when the program terminates. It's also useful to auto-detect - monochrome boards. */ - - asm mov ah,15 - asm int 0x10 - asm mov old_video_mode,al - - /* If the display mode has not already been set by the user then see - if this is a monochrome board. If so, set the display mode to 0. - Otherwise check the graphics flag of the story. Select a graphic - mode if it is set or if this is a V6 game. Select text mode if it - is not. */ - - if (display == -1) - - if (old_video_mode == 7) - display = '0'; - else if (h_version == V6 || (h_flags & GRAPHICS_FLAG)) - display = '5'; - else - display = '1'; - - /* Activate the desired display mode. All VESA text modes are very - similar to the standard text mode; in fact, only here we need to - know which VESA mode is used. */ - - if (display >= '0' && display <= '5') { - subdisplay = -1; - display -= '0'; - _AL = info[display].vmode; - _AH = 0; - } else if (display == 'a') { - subdisplay = 0; - display = 1; - _AL = 0x01; - _AH = 0; - } else if (display >= 'b' && display <= 'e') { - subdisplay = display - 'a'; - display = 1; - _BX = subinfo[subdisplay].vesamode; - _AX = 0x4f02; - } - - geninterrupt (0x10); - - /* Make various preparations */ - - if (display <= _TEXT_) { - - /* Enable bright background colours */ - - asm mov ax,0x1003 - asm mov bl,0 - asm int 0x10 - - /* Turn off hardware cursor */ - - asm mov ah,1 - asm mov cx,0xffff - asm int 0x10 - - } else { - - load_fonts (progname); - - if (display == _AMIGA_) { - - scaler = 2; - - /* Use resolution 640 x 400 instead of 640 x 480. BIOS doesn't - help us here since this is not a standard resolution. */ - - outportb (0x03c2, 0x63); - - outport (0x03d4, 0x0e11); - outport (0x03d4, 0xbf06); - outport (0x03d4, 0x1f07); - outport (0x03d4, 0x9c10); - outport (0x03d4, 0x8f12); - outport (0x03d4, 0x9615); - outport (0x03d4, 0xb916); - - } - - } - -#if !defined(__SMALL__) && !defined (__TINY__) && !defined (__MEDIUM__) - - /* Set the amount of memory to reserve for later use. It takes - some memory to open command, script and game files. If Frotz - is compiled in a small memory model then memory for opening - files is allocated on the "near heap" while other allocations - are made on the "far heap", i.e. we need not reserve memory - in this case. */ - - reserve_mem = 4 * BUFSIZ; - -#endif - - /* Amiga emulation under V6 needs special preparation. */ - - if (display == _AMIGA_ && h_version == V6) { - - user_reverse_fg = -1; - user_reverse_bg = -1; - zcolour[LIGHTGRAY] = LIGHTGREY_COLOUR; - zcolour[DARKGRAY] = DARKGREY_COLOUR; - - special_palette (); - - } - - /* Set various bits in the configuration byte. These bits tell - the game which features are supported by the interpreter. */ - - if (h_version == V3 && user_tandy_bit != -1) - h_config |= CONFIG_TANDY; - if (h_version == V3) - h_config |= CONFIG_SPLITSCREEN; - if (h_version == V3 && (display == _MCGA_ || (display == _AMIGA_ && user_font != 0))) - h_config |= CONFIG_PROPORTIONAL; - if (h_version >= V4 && display != _MCGA_ && (user_bold_typing != -1 || display <= _TEXT_)) - h_config |= CONFIG_BOLDFACE; - if (h_version >= V4) - h_config |= CONFIG_EMPHASIS | CONFIG_FIXED | CONFIG_TIMEDINPUT; - if (h_version >= V5 && display != _MONO_ && display != _CGA_) - h_config |= CONFIG_COLOUR; - if (h_version >= V5 && display >= _CGA_ && init_pictures ()) - h_config |= CONFIG_PICTURES; - - /* Handle various game flags. These flags are set if the game wants - to use certain features. The flags must be cleared if the feature - is not available. */ - - if (h_flags & GRAPHICS_FLAG) - if (display <= _TEXT_) - h_flags &= ~GRAPHICS_FLAG; - if (h_version == V3 && (h_flags & OLD_SOUND_FLAG)) - if (!init_sound ()) - h_flags &= ~OLD_SOUND_FLAG; - if (h_flags & SOUND_FLAG) - if (!init_sound ()) - h_flags &= ~SOUND_FLAG; - if (h_version >= V5 && (h_flags & UNDO_FLAG)) - if (!option_undo_slots) - h_flags &= ~UNDO_FLAG; - if (h_flags & MOUSE_FLAG) - if (subdisplay != -1 || !detect_mouse ()) - h_flags &= ~MOUSE_FLAG; - if (h_flags & COLOUR_FLAG) - if (display == _MONO_ || display == _CGA_) - h_flags &= ~COLOUR_FLAG; - h_flags &= ~MENU_FLAG; - - /* Set the screen dimensions, font size and default colour */ - - h_screen_width = info[display].width; - h_screen_height = info[display].height; - h_font_height = info[display].font_height; - h_font_width = info[display].font_width; - h_default_foreground = info[display].fg; - h_default_background = info[display].bg; - - if (subdisplay != -1) { - h_screen_width = subinfo[subdisplay].width; - h_screen_height = subinfo[subdisplay].height; - } - - if (user_screen_width != -1) - h_screen_width = user_screen_width; - if (user_screen_height != -1) - h_screen_height = user_screen_height; - - h_screen_cols = h_screen_width / h_font_width; - h_screen_rows = h_screen_height / h_font_height; - - if (user_foreground != -1) - h_default_foreground = zcolour[user_foreground]; - if (user_background != -1) - h_default_background = zcolour[user_background]; - - /* Set the interpreter number (a constant telling the game which - operating system it runs on) and the interpreter version. The - interpreter number has effect on V6 games and "Beyond Zork". */ - - h_interpreter_number = INTERP_MSDOS; - h_interpreter_version = 'F'; - - if (display == _AMIGA_) - h_interpreter_number = INTERP_AMIGA; - - /* Install the fast_exit routine to handle the ctrl-break key */ - - oldvect = getvect (0x1b); setvect (0x1b, fast_exit); - -}/* os_init_screen */ - -/* - * os_reset_screen - * - * Reset the screen before the program stops. - * - */ - -void os_reset_screen (void) -{ - - os_set_font (TEXT_FONT); - os_set_text_style (0); - os_display_string ((zchar *) "[Hit any key to exit.]"); - os_read_key (0, TRUE); - - cleanup (); - -}/* os_reset_screen */ - -/* - * os_restart_game - * - * This routine allows the interface to interfere with the process of - * restarting a game at various stages: - * - * RESTART_BEGIN - restart has just begun - * RESTART_WPROP_SET - window properties have been initialised - * RESTART_END - restart is complete - * - */ - -void os_restart_game (int stage) -{ - int x, y; - - if (story_id == BEYOND_ZORK) - - if (stage == RESTART_BEGIN) - - if ((display == _MCGA_ || display == _AMIGA_) && os_picture_data (1, &x, &y)) { - - special_palette (); - - asm mov ax,0x1010 - asm mov bx,64 - asm mov dh,0 - asm mov ch,0 - asm mov cl,0 - asm int 0x10 - asm mov ax,0x1010 - asm mov bx,79 - asm mov dh,0xff - asm mov ch,0xff - asm mov cl,0xff - asm int 0x10 - - os_draw_picture (1, 1, 1); - os_read_key (0, FALSE); - - standard_palette (); - - } - -}/* os_restart_game */ - -/* - * os_random_seed - * - * Return an appropriate random seed value in the range from 0 to - * 32767, possibly by using the current system time. - * - */ - -int os_random_seed (void) -{ - - if (user_random_seed == -1) { - - /* Use the time of day as seed value */ - - asm mov ah,0 - asm int 0x1a - - return _DX & 0x7fff; - - } else return user_random_seed; - -}/* os_random_seed */ diff --git a/bcinput.c b/bcinput.c deleted file mode 100644 index 04c6e0f..0000000 --- a/bcinput.c +++ /dev/null @@ -1,989 +0,0 @@ -/* - * file "BCinput.c" - * - * Borland C front end, input functions - * - */ - -#include -#include -#include -#include "frotz.h" -#include "BCfrotz.h" - -#ifndef HISTORY_BUFSIZE -#define HISTORY_BUFSIZE 500 -#endif - -extern bool is_terminator (zchar); - -extern bool read_yes_or_no (const char *); -extern void read_string (int, zchar *); - -extern int completion (const zchar *, zchar *); - -static long limit = 0; - -static struct { - zchar buffer[HISTORY_BUFSIZE]; - int latest; - int current; - int prefix_len; -} history; - -static struct { - zchar *buffer; - int pos; - int length; - int max_length; - int width; - int max_width; -} input; - -static bool overwrite = FALSE; - -/* - * swap_colours - * - * This is a small helper function for switch_cursor. It swaps the - * current background and foreground colours. - * - */ - -static void swap_colours (void) -{ - - _AL = text_fg; - _AH = text_bg; - text_fg = _AH; - text_bg = _AL; - -}/* swap_colours */ - -/* - * switch_cursor - * - * Turn cursor on/off. If there is mouse support then turn the mouse - * pointer on/off as well. The cursor should not be moved and the - * contents of the screen should not be changed while the cursor is - * visible (because of the primitive cursor emulation we use here). - * - */ - -static void switch_cursor (bool cursor) -{ - - if (display <= _TEXT_) { - - /* Use hardware cursor in text mode */ - - if (display == _MONO_) - _CX = overwrite ? 0x080f : 0x0a0b; - else - _CX = overwrite ? 0x0408 : 0x0506; - - if (!cursor) - _CX = 0xffff; - - asm mov ah,2 - asm mov bh,0 - asm mov dh,byte ptr cursor_y - asm mov dl,byte ptr cursor_x - asm int 0x10 - asm mov ah,1 - asm int 0x10 - - } else { - - int saved_x = cursor_x; - - if (cursor) - swap_colours (); - - if (input.pos < input.length) - os_display_char (input.buffer[input.pos]); - else - os_display_char (' '); - - if (cursor) - swap_colours (); - - cursor_x = saved_x; - - } - -}/* switch_cursor */ - -/* - * get_current_time - * - * Return the current system time in 1/10 seconds. - * - */ - -static long get_current_time (void) -{ - long time; - - /* Get the current time of day measured in - 65536 / 1,193,180 = 0.054925493 - seconds. Multiply this value with - 959 / 1746 = 0.54925544 - to get the current time in 0.1 seconds. */ - - asm mov ah,0 - asm int 0x1a - asm mov word ptr time,dx - asm mov word ptr time + 2,cx - - return time * 959 / 1746; - -}/* get_current_time */ - -/* - * set_timer - * - * Set a time limit of timeout/10 seconds if timeout is not zero; - * otherwise clear the time limit. - * - */ - -static void set_timer (int timeout) -{ - - limit = (timeout != 0) ? get_current_time () + timeout : 0; - -}/* set_timer */ - -/* - * time_limit_hit - * - * Return true if a previously set time limit has been exceeded. - * - */ - -static bool out_of_time (void) -{ - - if (limit != 0) { - - long now = get_current_time (); - - if (now < 1L * 3600 * 10 && limit > 23L * 3600 * 10) - now += 24L * 3600 * 10; - - return now >= limit; - - } else return FALSE; - -}/* out_of_time */ - -/* - * get_key - * - * Read a keypress or a mouse click. Returns... - * - * ZC_TIME_OUT = time limit exceeded, - * ZC_BACKSPACE = the backspace key, - * ZC_RETURN = the return key, - * ZC_HKEY_MIN...ZC_HKEY_MAX = a hot key, - * ZC_ESCAPE = the escape key, - * ZC_ASCII_MIN...ZC_ASCII_MAX = ASCII character, - * ZC_ARROW_MIN...ZC_ARROW_MAX = an arrow key, - * ZC_FKEY_MIN...ZC_FKEY_MAX = a function key, - * ZC_NUMPAD_MIN...ZC_NUMPAD_MAX = a number pad key, - * ZC_SINGLE_CLICK = single mouse click, - * ZC_DOUBLE_CLICK = double mouse click, - * ZC_LATIN1_MIN+1...ZC_LATIN1_MAX = ISO Latin-1 character, - * SPECIAL_KEY_MIN...SPECIAL_KEY_MAX = a special editing key. - * - */ - -static int get_key (bool cursor) -{ - static byte arrow_key_map[] = { - 0x48, 0x50, 0x4b, 0x4d - }; - static byte special_key_map[] = { - 0x47, 0x4f, 0x73, 0x74, 0x53, 0x52, 0x49, 0x51, 0x0f - }; - static byte hot_key_map[] = { - 0x13, 0x19, 0x1f, 0x16, 0x31, 0x2d, 0x20, 0x23 - }; - - int key; - - /* Loop until a key was pressed */ - - if (cursor) - switch_cursor (TRUE); - - if (h_flags & MOUSE_FLAG) { - asm mov ax,1 - asm int 0x33 - } - - do { - - if (_bios_keybrd (_KEYBRD_READY)) { - - word code = _bios_keybrd (_KEYBRD_READ); - - if (byte0 (code) != 0 && byte0 (code) != 9) { - - for (key = ZC_NUMPAD_MIN; key <= ZC_NUMPAD_MAX; key++) - if (byte0 (code) == key - ZC_NUMPAD_MIN + '0' && byte1 (code) >= 0x10) - goto exit_loop; - - for (key = ZC_LATIN1_MIN + 1; key <= ZC_LATIN1_MAX; key++) - if (byte0 (code) == latin1_to_ibm[key - ZC_LATIN1_MIN]) - goto exit_loop; - - key = byte0 (code); - - if (key == ZC_BACKSPACE) - goto exit_loop; - if (key == ZC_RETURN) - goto exit_loop; - if (key == ZC_ESCAPE) - goto exit_loop; - if (key >= ZC_ASCII_MIN && key <= ZC_ASCII_MAX) - goto exit_loop; - - } else { - - for (key = ZC_ARROW_MIN; key <= ZC_ARROW_MAX; key++) - if (byte1 (code) == arrow_key_map[key - ZC_ARROW_MIN]) - goto exit_loop; - - for (key = ZC_FKEY_MIN; key <= ZC_FKEY_MAX - 2; key++) - if (byte1 (code) == key - ZC_FKEY_MIN + 0x3b) - goto exit_loop; - - for (key = ZC_HKEY_MIN; key <= ZC_HKEY_MAX; key++) - if (byte1 (code) == hot_key_map[key - ZC_HKEY_MIN]) - goto exit_loop; - - for (key = SPECIAL_KEY_MIN; key <= SPECIAL_KEY_MAX; key++) - if (byte1 (code) == special_key_map[key - SPECIAL_KEY_MIN]) - goto exit_loop; - - } - - } else { - - int clicks = read_mouse (); - - if (clicks == 1) - { key = ZC_SINGLE_CLICK; goto exit_loop; } - if (clicks == 2) - { key = ZC_DOUBLE_CLICK; goto exit_loop; } - - } - - key = ZC_TIME_OUT; - - } while (!out_of_time ()); - -exit_loop: - - if (h_flags & MOUSE_FLAG) { - asm mov ax,2 - asm int 0x33 - } - - if (cursor) - switch_cursor (FALSE); - - return key; - -}/* get_key */ - -/* - * cursor_left - * - * Move the cursor one character to the left. - * - */ - -static void cursor_left (void) -{ - - if (input.pos > 0) - cursor_x -= os_char_width (input.buffer[--input.pos]); - -}/* cursor_left */ - -/* - * cursor_right - * - * Move the cursor one character to the right. - * - */ - -static void cursor_right (void) -{ - - if (input.pos < input.length) - cursor_x += os_char_width (input.buffer[input.pos++]); - -}/* cursor_right */ - -/* - * first_char - * - * Move the cursor to the beginning of the input line. - * - */ - -static void first_char (void) -{ - - while (input.pos > 0) - cursor_left (); - -}/* first_char */ - -/* - * last_char - * - * Move the cursor to the end of the input line. - * - */ - -static void last_char (void) -{ - - while (input.pos < input.length) - cursor_right (); - -}/* last_char */ - -/* - * prev_word - * - * Move the cursor to the start of the previous word. - * - */ - -static void prev_word (void) -{ - - do { - - cursor_left (); - - if (input.pos == 0) - return; - - } while (input.buffer[input.pos] == ' ' || input.buffer[input.pos - 1] != ' '); - -}/* prev_word */ - -/* - * next_word - * - * Move the cursor to the start of the next word. - * - */ - -static void next_word (void) -{ - - do { - - cursor_right (); - - if (input.pos == input.length) - return; - - } while (input.buffer[input.pos] == ' ' || input.buffer[input.pos - 1] != ' '); - -}/* next_word */ - -/* - * input_move - * - * Helper function to move parts of the input buffer: - * - * newc != 0, oldc == 0: INSERT - * newc != 0, oldc != 0: OVERWRITE - * newc == 0, oldc != 0: DELETE - * newc == 0, oldc == 0: NO OPERATION - * - */ - -#define H(x) (x ? 1 : 0) - -static void input_move (zchar newc, zchar oldc) -{ - int newwidth = (newc != 0) ? os_char_width (newc) : 0; - int oldwidth = (oldc != 0) ? os_char_width (oldc) : 0; - - zchar *p = input.buffer + input.pos; - - int saved_x = cursor_x; - - int updated_width = input.width + newwidth - oldwidth; - int updated_length = input.length + H (newc) - H (oldc); - - if (updated_width > input.max_width) - return; - if (updated_length > input.max_length) - return; - - input.width = updated_width; - input.length = updated_length; - - if (oldc != 0 && newc == 0) - memmove (p, p + 1, updated_length - input.pos + 1); - if (newc != 0 && oldc == 0) - memmove (p + 1, p, updated_length - input.pos); - - if (newc != 0) - *p = newc; - - os_display_string (p); - - switch_scrn_attr (TRUE); - - if (oldwidth > newwidth) - - os_erase_area ( - cursor_y + 1, - cursor_x + 1, - cursor_y + h_font_height, - cursor_x + oldwidth - newwidth); - - switch_scrn_attr (FALSE); - - cursor_x = saved_x; - - if (newc != 0) - cursor_right (); - -}/* input_move */ - -#undef H(x) - -/* - * delete_char - * - * Delete the character below the cursor. - * - */ - -static void delete_char (void) -{ - - input_move (0, input.buffer[input.pos]); - -}/* delete_char */ - -/* - * delete_left - * - * Delete the character to the left of the cursor. - * - */ - -static void delete_left (void) -{ - - if (input.pos > 0) { - cursor_left (); - delete_char (); - } - -}/* delete_left */ - -/* - * truncate_line - * - * Truncate the input line to n characters. - * - */ - -static void truncate_line (int n) -{ - - last_char (); - - while (input.length > n) - delete_left (); - -}/* truncate_line */ - -/* - * insert_char - * - * Insert a character into the input buffer. - * - */ - -static void insert_char (zchar newc) -{ - zchar oldc = 0; - - if (overwrite) - oldc = input.buffer[input.pos]; - - input_move (newc, oldc); - -}/* insert_char */ - -/* - * insert_string - * - * Add a string of characters to the input line. - * - */ - -static void insert_string (const zchar *s) -{ - - while (*s != 0) { - - if (input.length + 1 > input.max_length) - break; - if (input.width + os_char_width (*s) > input.max_width) - break; - - insert_char (*s++); - - } - -}/* insert_string */ - -/* - * tabulator_key - * - * Complete the word at the end of the input line, if possible. - * - */ - -static void tabulator_key (void) -{ - int status; - - if (input.pos == input.length) { - - zchar extension[10]; - - status = completion (input.buffer, extension); - insert_string (extension); - - } else status = 2; - - /* Beep if the completion was impossible or ambiguous */ - - if (status != 0) - os_beep (status); - -}/* tabulator_key */ - -/* - * store_input - * - * Copy the current input line to the history buffer. - * - */ - -static void store_input (void) -{ - - if (input.length >= HISTORY_MIN_ENTRY) { - - const zchar *ptr = input.buffer; - - do { - - if (history.latest++ == HISTORY_BUFSIZE - 1) - history.latest = 0; - - history.buffer[history.latest] = *ptr; - - } while (*ptr++ != 0); - - } - -}/* store_input */ - -/* - * fetch_entry - * - * Copy the current history entry to the input buffer and check if it - * matches the prefix in the input buffer. - * - */ - -static bool fetch_entry (zchar *buf, int entry) -{ - int i = 0; - - zchar c; - - do { - - if (entry++ == HISTORY_BUFSIZE - 1) - entry = 0; - - c = history.buffer[entry]; - - if (i < history.prefix_len && input.buffer[i] != c) - return FALSE; - - buf[i++] = c; - - } while (c != 0); - - return (i > history.prefix_len) && (i > 1); - -}/* fetch_entry */ - -/* - * get_prev_entry - * - * Copy the previous history entry to the input buffer. - * - */ - -static void get_prev_entry (void) -{ - zchar buf[INPUT_BUFFER_SIZE]; - - int i = history.current; - - do { - - do { - - if (i-- == 0) - i = HISTORY_BUFSIZE - 1; - - if (i == history.latest) - return; - - } while (history.buffer[i] != 0); - - } while (!fetch_entry (buf, i)); - - truncate_line (history.prefix_len); - - insert_string (buf + history.prefix_len); - - history.current = i; - -}/* get_prev_entry */ - -/* - * get_next_entry - * - * Copy the next history entry to the input buffer. - * - */ - -static void get_next_entry (void) -{ - zchar buf[INPUT_BUFFER_SIZE]; - - int i = history.current; - - truncate_line (history.prefix_len); - - do { - - do { - - if (i == history.latest) - return; - - if (i++ == HISTORY_BUFSIZE - 1) - i = 0; - - } while (history.buffer[i] != 0); - - if (i == history.latest) - goto no_further; - - } while (!fetch_entry (buf, i)); - - insert_string (buf + history.prefix_len); - -no_further: - - history.current = i; - -}/* get_next_entry */ - -/* - * os_read_line - * - * Read a line of input from the keyboard into a buffer. The buffer - * may already be primed with some text. In this case, the "initial" - * text is already displayed on the screen. After the input action - * is complete, the function returns with the terminating key value. - * The length of the input should not exceed "max" characters plus - * an extra 0 terminator. - * - * Terminating keys are the return key (13) and all function keys - * (see the Specification of the Z-machine) which are accepted by - * the is_terminator function. Mouse clicks behave like function - * keys except that the mouse position is stored in global variables - * "mouse_x" and "mouse_y" (top left coordinates are (1,1)). - * - * Furthermore, Frotz introduces some special terminating keys: - * - * ZC_HKEY_PLAYBACK (Alt-P) - * ZC_HKEY_RECORD (Alt-R) - * ZC_HKEY_SEED (Alt-S) - * ZC_HKEY_UNDO (Alt-U) - * ZC_HKEY_RESTART (Alt-N, "new game") - * ZC_HKEY_QUIT (Alt-X, "exit game") - * ZC_HKEY_DEBUGGING (Alt-D) - * ZC_HKEY_HELP (Alt-H) - * - * If the timeout argument is not zero, the input gets interrupted - * after timeout/10 seconds (and the return value is 0). - * - * The complete input line including the cursor must fit in "width" - * screen units. - * - * The function may be called once again to continue after timeouts, - * misplaced mouse clicks or hot keys. In this case the "continued" - * flag will be set. This information can be useful if the interface - * implements input line history. - * - * The screen is not scrolled after the return key was pressed. The - * cursor is at the end of the input line when the function returns. - * - * Since Inform 2.2 the helper function "completion" can be called - * to implement word completion (similar to tcsh under Unix). - * - */ - -#define new_history_search() \ - { history.prefix_len = input.pos; history.current = history.latest; } - -zchar os_read_line (int max, zchar *buf, int timeout, int width, int continued) -{ - int key = continued ? 9999 : 0; - - /* Initialise input variables */ - - input.buffer = buf; - input.pos = strlen ((char *) buf); - input.length = strlen ((char *) buf); - input.max_length = max; - input.width = os_string_width (buf); - input.max_width = width - os_char_width (' '); - - /* Calculate time limit */ - - set_timer (timeout); - - /* Loop until a terminator is found */ - - do { - - if (key != 9999) - new_history_search (); - - /* Get next key from mouse or keyboard */ - - key = get_key (TRUE); - - if (key < ZC_ASCII_MIN || key > ZC_ASCII_MAX && key < ZC_LATIN1_MIN || key > ZC_LATIN1_MAX) { - - /* Ignore time-outs if the cursor is not at end of the line */ - - if (key == ZC_TIME_OUT && input.pos < input.length) - key = 9999; - - /* Backspace, return and escape keys */ - - if (key == ZC_BACKSPACE) - delete_left (); - if (key == ZC_RETURN) - store_input (); - if (key == ZC_ESCAPE) - truncate_line (0); - - /* Editing keys */ - - if (cwin == 0) { - - if (key == ZC_ARROW_UP) - get_prev_entry (); - if (key == ZC_ARROW_DOWN) - get_next_entry (); - if (key == ZC_ARROW_LEFT) - cursor_left (); - if (key == ZC_ARROW_RIGHT) - cursor_right (); - - if (key >= ZC_ARROW_MIN && key <= ZC_ARROW_MAX) - key = 9999; - - if (key == SPECIAL_KEY_HOME) - first_char (); - if (key == SPECIAL_KEY_END) - last_char (); - if (key == SPECIAL_KEY_WORD_LEFT) - prev_word (); - if (key == SPECIAL_KEY_WORD_RIGHT) - next_word (); - if (key == SPECIAL_KEY_DELETE) - delete_char (); - if (key == SPECIAL_KEY_INSERT) - overwrite = !overwrite; - if (key == SPECIAL_KEY_TAB) - tabulator_key (); - - } - - if (key == SPECIAL_KEY_PAGE_UP) - key = ZC_ARROW_UP; - if (key == SPECIAL_KEY_PAGE_DOWN) - key = ZC_ARROW_DOWN; - - } else insert_char (key); - - } while (key > 0xff || !is_terminator (key)); - - last_char (); - - overwrite = FALSE; - - /* Return terminating key */ - - return key; - -}/* os_read_line */ - -#undef new_history_search() - -/* - * os_read_key - * - * Read a single character from the keyboard (or a mouse click) and - * return it. Input aborts after timeout/10 seconds. - * - */ - -zchar os_read_key (int timeout, bool cursor) -{ - int key; - - set_timer (timeout); - - do { - - key = get_key (cursor); - - } while (key > 0xff); - - return key; - -}/* os_read_key */ - -/* - * os_read_file_name - * - * Return the name of a file. Flag can be one of: - * - * FILE_SAVE - Save game file - * FILE_RESTORE - Restore game file - * FILE_SCRIPT - Transscript file - * FILE_RECORD - Command file for recording - * FILE_PLAYBACK - Command file for playback - * FILE_SAVE_AUX - Save auxilary ("preferred settings") file - * FILE_LOAD_AUX - Load auxilary ("preferred settings") file - * - * The length of the file name is limited by MAX_FILE_NAME. Ideally - * an interpreter should open a file requester to ask for the file - * name. If it is unable to do that then this function should call - * print_string and read_string to ask for a file name. - * - */ - -int os_read_file_name (char *file_name, const char *default_name, int flag) -{ - char *extension; - FILE *fp; - bool terminal; - bool result; - - bool saved_replay = istream_replay; - bool saved_record = ostream_record; - - /* Turn off playback and recording temporarily */ - - istream_replay = FALSE; - ostream_record = FALSE; - - /* Select appropriate extension */ - - extension = ".aux"; - - if (flag == FILE_SAVE || flag == FILE_RESTORE) - extension = ".sav"; - if (flag == FILE_SCRIPT) - extension = ".scr"; - if (flag == FILE_RECORD || flag == FILE_PLAYBACK) - extension = ".rec"; - - /* Input file name (reserve four bytes for a file name extension) */ - - print_string ("Enter file name (\""); - print_string (extension); - print_string ("\" will be added).\nDefault is \""); - print_string (default_name); - print_string ("\": "); - - read_string (MAX_FILE_NAME - 4, (zchar *) file_name); - - /* Use the default name if nothing was typed */ - - if (file_name[0] == 0) - strcpy (file_name, default_name); - if (strchr (file_name, '.') == NULL) - strcat (file_name, extension); - - /* Make sure it is safe to use this file name */ - - result = TRUE; - - /* OK if the file is opened for reading */ - - if (flag != FILE_SAVE && flag != FILE_SAVE_AUX && flag != FILE_RECORD) - goto finished; - - /* OK if the file does not exist */ - - if ((fp = fopen (file_name, "rb")) == NULL) - goto finished; - - /* OK if this is a pseudo-file (like PRN, CON, NUL) */ - - terminal = fp->flags & _F_TERM; - - fclose (fp); - - if (terminal) - goto finished; - - /* OK if user wants to overwrite */ - - result = read_yes_or_no ("Overwrite existing file"); - -finished: - - /* Restore state of playback and recording */ - - istream_replay = saved_replay; - ostream_record = saved_record; - - return result; - -}/* os_read_file_name */ diff --git a/bcmouse.c b/bcmouse.c deleted file mode 100644 index ea8444b..0000000 --- a/bcmouse.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * file "BCmouse.c" - * - * Borland C front end, mouse support - * - */ - -#include -#include "frotz.h" -#include "BCfrotz.h" - -/* - * detect_mouse - * - * Return true if a mouse driver is present. - * - */ - -bool detect_mouse (void) -{ - - asm xor ax,ax - asm int 0x33 - - return _AX; - -}/* detect_mouse */ - -/* - * read_mouse - * - * Report any mouse clicks. Return 2 for a double click, 1 for a single - * click or 0 if there was no mouse activity at all. - * - */ - -int read_mouse (void) -{ - int click; - - /* Read the current mouse status */ - - for (click = 0; click < 2; click++) { - - if (click == 1) - delay (222); - - asm mov ax,6 - asm xor bx,bx - asm int 0x33 - - if (_BX == 0) - break; - - mouse_x = _CX; - mouse_y = _DX; - - if (display <= _TEXT_) { - mouse_x /= 8; - mouse_y /= 8; - } - - if (display == _MCGA_) - mouse_x /= 2; - - mouse_x++; - mouse_y++; - - } - - /* Return single or double click */ - - return click; - -}/* read_mouse */ diff --git a/bcpic.c b/bcpic.c deleted file mode 100644 index 59115a9..0000000 --- a/bcpic.c +++ /dev/null @@ -1,677 +0,0 @@ -/* - * "BCpic.c" - * - * Borland C front end, picture functions - * - */ - -#include -#include -#include -#include -#include "frotz.h" -#include "BCfrotz.h" - -#define PIC_NUMBER 0 -#define PIC_WIDTH 2 -#define PIC_HEIGHT 4 -#define PIC_FLAGS 6 -#define PIC_DATA 8 -#define PIC_COLOUR 11 - -#define READ_BYTE(v,p,o) v = *(byte far *)(p+o) -#define READ_WORD(v,p,o) v = *(word far *)(p+o) - -extern byte far *get_scrnptr (int); - -static struct { - byte fileno; - byte flags; - word unused1; - word images; - word link; - byte entry_size; - byte padding; - word checksum; - word unused2; - word version; -} gheader; - -int scaler = 1; - -static word pic_width = 0; -static word pic_height = 0; -static word pic_flags = 0; -static long pic_data = 0; -static long pic_colour = 0; - -static byte far *table_val = NULL; -static word far *table_ref = NULL; - -static FILE *file = NULL; -static byte far *info = NULL; - -/* - * open_graphics_file - * - * Open a graphics file. EGA pictures may be stored in two separate - * graphics files. - * - */ - -static bool open_graphics_file (int number) -{ - char fname[MAX_FILE_NAME + 1]; - char extension[4 + 1]; - - /* Build graphics file name */ - - extension[0] = '.'; - extension[1] = "cmem"[display - 2]; - extension[2] = 'g'; - extension[3] = '0' + number; - extension[4] = 0; - - strcpy (fname, stripped_story_name); - strcat (fname, extension); - - /* Open file, load header, allocate memory, load picture directory */ - - if ((file = fopen (fname, "rb")) == NULL) - goto failure1; - if (fread (&gheader, sizeof (gheader), 1, file) != 1) - goto failure2; - if ((info = farmalloc (gheader.images * gheader.entry_size)) == NULL) - goto failure2; - if (fread (info, gheader.entry_size, gheader.images, file) != gheader.images) - goto failure3; - return TRUE; - -failure3: - farfree (info); info = NULL; -failure2: - fclose (file); file = NULL; -failure1: - return FALSE; - -}/* open_graphics_file */ - -/* - * close_graphics_file - * - * Free resources allocated for pictures. - * - */ - -static void close_graphics_file (void) -{ - - if (file != NULL) - { fclose (file); file = NULL; } - if (info != NULL) - { farfree (info); info = NULL; } - -}/* close_graphics_file */ - -/* - * init_pictures - * - * Prepare to draw pictures. Return true if pictures are available. - * - */ - -bool init_pictures (void) -{ - - /* Allocate memory for decompression */ - - table_val = (byte far *) farmalloc (3 * 3840); - table_ref = (word far *) (table_val + 3840); - - if (table_val == NULL) - return FALSE; - - /* Open the [first of two] graphics file[s] */ - - return open_graphics_file (1); - -}/* init_pictures */ - -/* - * reset_pictures - * - * Free resources allocated for decompression of pictures. - * - */ - -void reset_pictures (void) -{ - - if (table_val != NULL) - { farfree (table_val); table_val = NULL; } - if (file != NULL) - { fclose (file); file = NULL; } - if (info != NULL) - { farfree (info); info = NULL; } - -}/* reset_pictures */ - -/* - * load_picture_info - * - * Helper function for os_picture_data. Load all information about - * the given picture from the graphics file and store it in global - * variables. - * - */ - -static bool load_picture_info (int picture) -{ - byte far *ptr; - byte fileno; - - fileno = gheader.fileno; - - do { - - int i; - - /* Abort if there is a problem with the graphics file */ - - if (file == NULL) - return FALSE; - - /* Scan the directory of the current graphics file */ - - ptr = info; - - for (i = 0; i < gheader.images; i++) { - - if (picture == * (int far *) ptr) { - - READ_WORD (pic_width, ptr, PIC_WIDTH); - READ_WORD (pic_height, ptr, PIC_HEIGHT); - READ_WORD (pic_flags, ptr, PIC_FLAGS); - - pic_height *= scaler; - pic_width *= scaler; - - READ_BYTE (byte0 (pic_data), ptr, PIC_DATA + 2); - READ_BYTE (byte1 (pic_data), ptr, PIC_DATA + 1); - READ_BYTE (byte2 (pic_data), ptr, PIC_DATA); - - if (gheader.entry_size > PIC_COLOUR + 2) { - - READ_BYTE (byte0 (pic_colour), ptr, PIC_COLOUR + 2); - READ_BYTE (byte1 (pic_colour), ptr, PIC_COLOUR + 1); - READ_BYTE (byte2 (pic_colour), ptr, PIC_COLOUR); - - } else pic_colour = 0; - - return TRUE; - - } - - ptr += gheader.entry_size; - - } - - /* Close current graphics file */ - - close_graphics_file (); - - /* Open next graphics file */ - - open_graphics_file ((gheader.link != 0) ? gheader.fileno + 1 : 1); - - } while (fileno != gheader.fileno); - - return FALSE; - -}/* load_picture_info */ - -/* - * load_colour_map - * - * Helper function for os_draw_picture. Load a colour map from the - * graphics file then copy it to the palette registers. - * - */ - -static void load_colour_map (int first_colour) -{ - byte rgb[42]; - int n, i; - - fseek (file, pic_colour, SEEK_SET); - - /* Some pictures from Arthur mistakenly claim to have 16 colours */ - - if ((n = fgetc (file)) == 16) - n = 14; - - /* Each colour is stored in three bytes R-G-B */ - - fread (rgb, 3, n, file); - - /* MCGA boards can only handle R-G-B values from 0 to 63 */ - - for (i = 0; i < 42; i++) - rgb[i] = (rgb[i] * 63 + 128) / 255; - - /* Synchronise with vertical retrace */ - - while ((inportb (0x03da) & 8) == 0); - while ((inportb (0x03da) & 8) == 8); - - /* Copy colours to palette registers */ - - asm mov ax,0x1012 - asm mov bx,first_colour - asm mov cx,n - asm lea dx,rgb - asm push ss - asm pop es - asm int 0x10 - -}/* load_colour_map */ - -/* - * draw_picture - * - * Helper function for os_draw_picture. The real work of drawing a - * picture happens here. - * - */ - -#pragma warn -def - -static void pascal draw_picture (int y, int x) -{ - static int raise_bits[4] = { - 0x0100, 0x0300, 0x0700, 0x0000 - }; - - byte buf[512]; - byte far *screen; - byte transparent; - byte colour_shift; - int first_colour; - int code, prev_code; - int next_entry; - int bits_per_code; - int bits_shift; - int bits; - int current_y; - int current_x; - int bufpos; - int pixels; - int i; - - bufpos = 0; - - /* When the given picture provides a colour map then activate it. - This is only used for MCGA pictures; the colour map affects - every picture on the screen. The first colour to be defined is - colour 2. Every map defines up to 14 colours (colour 2 to 15). - These colours are not related to the standard Z-machine colour - scheme which remains unchanged. (This is based on the Amiga - interpreter which had to work with 16 colours. Colours 0 and 1 - were used for text; changing the text colours actually changed - palette entries 0 and 1. This interface uses the same trick in - Amiga mode.) */ - - if (display == _CGA_) - colour_shift = -2; - if (display == _EGA_) - colour_shift = 0; - if (display == _MCGA_) - { colour_shift = 32; first_colour = 34; } - if (display == _AMIGA_) - { colour_shift = -1; first_colour = 65; } - - if (pic_colour != 0) - load_colour_map (first_colour); - - fseek (file, pic_data, SEEK_SET); - - /* Bit 0 of "flags" indicates that the picture uses a transparent - colour, the top four bits tell us which colour it is. For CGA - and MCGA pictures this is always 0; for EGA pictures it can be - any colour between 0 and 15. */ - - transparent = 0xff; - - if (pic_flags & 1) - transparent = pic_flags >> 12; - - /* Prepare EGA hardware for setting pixels */ - - if (display >= _EGA_) { - outport (0x03ce, 0x0205); - outport (0x03ce, 0xff08); - } - - /* The uncompressed picture is a long sequence of bytes. Every - byte holds the colour of a pixel, starting at the top left, - stopping at the bottom right. We keep track of our position - in the current line. (There is a special case: CGA pictures - with no transparent colour are stored as bit patterns, i.e. - every byte holds the pattern for eight pixels. A pixel must - be white if the corresponding bit is set, otherwise it must - be black.) */ - - current_x = x + pic_width; - current_y = y - 1; - - /* The compressed picture is a stream of bits. We read the file - byte-wise, storing the current byte in the variable "bits". - Several bits make one code; the variable "bits_shift" helps - us to build the next code. */ - - bits_shift = 0; - bits = 0; - -reset_table: - - /* Clear the table. We use a table of 3840 entries. Each entry - consists of both a value and a reference to another table - entry. Following these references we get a sequence of - values. At the start of decompression all table entries are - undefined. Later we see how entries are set and used. */ - - next_entry = 1; - - /* At the start of decompression 9 bits make one code; during - the process this can rise to 12 bits per code. 9 bits are - sufficient to address both 256 literal values and 256 table - entries; 12 bits are sufficient to address both 256 literal - values and all 3840 table entries. The number of bits per - code rises with the number of table entries. When the table - is cleared, the number of bits per code drops back to 9. */ - - bits_per_code = 9; - -next_code: - - /* Read the next code from the graphics file. This requires - some confusing bit operations. Note that low bits always - come first. Usually there are a few bits left over from - the previous code; these bits must be used before further - bits are read from the graphics file. */ - - code = bits >> (8 - bits_shift); - - do { - - bits = fgetc (file); - - code |= bits << bits_shift; - - bits_shift += 8; - - } while (bits_shift < bits_per_code); - - bits_shift -= bits_per_code; - - code &= 0xfff >> (12 - bits_per_code); - - /* There are two codes with a special meaning. The first one - is 256 which clears the table and sets the number of bits - per code to 9. (This is necessary when the table is full.) - The second one is 257 which marks the end of the picture. - For the sake of efficiency, we drecement the code by 256. */ - - byte1 (code) --; - - if (code == 0) - goto reset_table; - if (code == 1) - return; - - /* Codes from 0 to 255 are literals, i.e. they represent a - plain byte value. Codes from 258 onwards are references - to table entries, i.e. they represent a sequence of byte - values (see the remarks on the table above). This means - that for each code one or several byte values are added - to the decompressed picture. But there is yet more work - to do: Every time we read a code one table entry is set. - As we said above, a table entry consist of both a value - and a reference to another table entry. If the current - code is a literal, then the value has to be set to this - literal; but if the code refers to a sequence of byte - values, then the value has to be set to the last byte of - this sequence. In any case, the reference is set to the - previous code. Finally, one should be aware that a code - may legally refer to the table entry which is currently - being set. This requires some extra care. */ - - table_ref[next_entry] = prev_code; - - prev_code = code; - - while (code >= 0) { - buf[bufpos++] = table_val[code]; - code = (short) table_ref[code]; - } - - if (next_entry == prev_code) - buf[0] = code; - - table_val[next_entry] = code; - - /* The number of bits per code is incremented when the current - number of bits no longer suffices to address all defined - table entries; but in any case the number of bits may never - be greater than 12. */ - - next_entry++; - - if (next_entry == raise_bits[bits_per_code - 9]) - bits_per_code++; - -reverse_buffer: - - /* Append the sequence of byte values (pixels) to the picture. - The order of the sequence must be reversed. (This is why we - have stored the sequence in a buffer; experiments show that - a buffer of 512 bytes suffices.) The sequence of values may - spread over several lines of the picture, so we must take - care to start a new line when we reach the right border of - the picture. */ - - if (current_x == x + pic_width) { - - screen = get_scrnptr (current_y); - - current_x -= pic_width; - current_y += scaler; - - } - - /* Either add a single pixel or a pattern of eight bits (b/w - CGA pictures without a transparent colour) to the current - line. Increment our position by 1 or 8 respectively. The - pixel may have to be painted several times if the scaling - factor is greater than one. */ - - if (display == _CGA_ && transparent == 0xff) { - - pixels = x + pic_width - current_x; - - if (pixels > 8) - pixels = 8; - - asm les bx,screen - asm mov dx,current_x - asm dec dx - asm push dx - asm mov cl,3 - asm shr dx,cl - asm add bx,dx - asm mov ax,es:[bx] - asm mov dx,0xffff - asm mov cl,byte ptr pixels - asm shr dl,cl - asm pop cx - asm and cl,7 - asm ror dx,cl - asm and ax,dx - asm mov dx,code - asm inc dh - asm ror dx,cl - asm or ax,dx - asm mov es:[bx],ax - - current_x += pixels; - - } else for (i = 0; i < scaler; i++) { - - _AH = code; - - if (_AH != transparent) { - - asm add ah,colour_shift - asm les bx,screen - asm mov dx,current_x - asm dec dx - - if (display != _MCGA_) { - - asm push dx - asm mov cl,3 - asm shr dx,cl - asm pop cx - asm and cl,7 - asm add bx,dx - asm mov al,es:[bx] - - if (display == _CGA_) { - asm mov dl,0x7f - asm ror dl,cl - asm and al,dl - asm xor ah,1 - asm ror ah,1 - asm shr ah,cl - asm or ah,al - } else { - asm mov al,0x80 - asm shr al,cl - asm mov dx,0x03cf - asm out dx,al - } - - } else asm add bx,dx - - asm mov es:[bx],ah - - if (display == _AMIGA_) { - asm add bx,80 - asm mov al,es:[bx] - asm mov es:[bx],ah - } - - } - - current_x++; - - } - - /* If there are no more values in the buffer then read the - next code from the file. Otherwise fetch the next byte - value from the buffer and continue painting the picture. */ - - if (bufpos == 0) - goto next_code; - - byte0 (code) = buf[--bufpos]; - - goto reverse_buffer; - -}/* draw_picture */ - -#pragma warn +def - -/* - * os_draw_picture - * - * Display a picture at the given coordinates. Top left is (1,1). - * - */ - -void os_draw_picture (int picture, int y, int x) -{ - - if (load_picture_info (picture)) - draw_picture (y, x); - -}/* os_draw_picture */ - -/* - * os_peek_colour - * - * Return the colour of the pixel below the cursor. This is used - * by V6 games to print text on top of pictures. The coulor need - * not be in the standard set of Z-machine colours. To handle - * this situation, Frotz extends the colour scheme: Values above - * 15 (and below 256) may be used by the interface to refer to - * non-standard colours. Of course, os_set_colour must be able to - * deal with these colours. Interfaces which refer to characters - * instead of pixels might return the current background colour - * instead. - * - */ - -int os_peek_colour (void) -{ - - if (display >= _CGA_) { - - asm mov ah,13 - asm mov bh,0 - asm mov cx,cursor_x - asm mov dx,cursor_y - asm int 0x10 - asm mov ah,0 - - return _AX + 16; - - } else return current_bg; - -}/* os_peek_colour */ - -/* - * os_picture_data - * - * Return true if the given picture is available. If so, write the - * width and height of the picture into the appropriate variables. - * Only when picture 0 is asked for, write the number of available - * pictures and the release number instead. - * - */ - -bool os_picture_data (int picture, int *height, int *width) -{ - bool avail; - - if (picture == 0) { - - avail = FALSE; - - /* This is the special case mentioned above. In practice, only - the release number is used; and even this is only used by - the DOS version of "Zork Zero". Infocom's Amiga interpreter - could not handle this feature, and the Amiga version of the - story file does not use it. */ - - pic_height = gheader.images; - pic_width = gheader.version; - - } else avail = load_picture_info (picture); - - *height = pic_height; - *width = pic_width; - - return avail; - -}/* os_picture_data */ diff --git a/bcsample.c b/bcsample.c deleted file mode 100644 index bb50b23..0000000 --- a/bcsample.c +++ /dev/null @@ -1,414 +0,0 @@ -/* - * file "BCsample.c" - * - * Borland C front end, sound support - * - */ - -#include -#include -#include -#include -#include -#include "frotz.h" -#include "BCfrotz.h" - -#define SWAP_BYTES(v) {_AX=v;asm xchg al,ah;v=_AX;} - -#define READ_DSP(v) {while(!inportb(sound_adr+14)&0x80);v=inportb(sound_adr+10);} -#define WRITE_DSP(v) {while(inportb(sound_adr+12)&0x80);outportb(sound_adr+12,v);} - -extern void end_of_sound (void); - -static struct { - word prefix; - byte repeats; - byte base_note; - word frequency; - word unused; - word length; -} sheader; - -static current_sample = 0; - -static void interrupt (*vect) (void) = NULL; - -static play_part = 0; -static play_count = 0; - -static word sound_adr = 0; -static word sound_irq = 0; -static word sound_dma = 0; -static word sound_int = 0; -static word sound_ver = 0; - -static byte far *sample_data = NULL; - -static long sample_adr1 = 0; -static long sample_adr2 = 0; -static word sample_len1 = 0; -static word sample_len2 = 0; - -/* - * start_of_dma - * - * Start the DMA transfer to the sound board. - * - */ - -static void start_of_dma (long address, unsigned length) -{ - static unsigned dma_page_port[] = { - 0x87, 0x83, 0x81, 0x82 - }; - - length--; - - /* Set up DMA chip */ - - outportb (0x0a, 0x04 | sound_dma); - outportb (0x0c, 0x00); - outportb (0x0b, 0x48 | sound_dma); - outportb (2 * sound_dma, byte0 (address)); - outportb (2 * sound_dma, byte1 (address)); - outportb (dma_page_port[sound_dma], byte2 (address)); - outportb (2 * sound_dma + 1, byte0 (length)); - outportb (2 * sound_dma + 1, byte1 (length)); - outportb (0x0a, sound_dma); - - /* Play 8-bit mono sample */ - - WRITE_DSP (0x14) - WRITE_DSP (byte0 (length)) - WRITE_DSP (byte1 (length)) - -}/* start_of_dma */ - -/* - * end_of_dma - * - * This function is called when a hardware interrupt signals the - * end of the current sound. We may have to play the second half - * of the sound effect, or we may have to repeat it, or call the - * end_of_sound function when we are finished. - * - */ - -static void interrupt end_of_dma (void) -{ - - /* Play the second half, play another cycle or finish */ - - if (play_part == 1 && sample_len2 != 0) { - play_part = 2; - start_of_dma (sample_adr2, sample_len2); - } else if (play_count == 255 || --play_count != 0) { - play_part = 1; - start_of_dma (sample_adr1, sample_len1); - } else { - play_part = 0; - end_of_sound (); - } - - /* Tell interrupt controller(s) + sound board we are done */ - - outportb (0x20, 0x20); - - if (sound_irq >= 8) - outportb (0xa0, 0x20); - - inportb (sound_adr + 14); - -}/* end_of_dma */ - -/* - * init_sound - * - * Initialise the sound board and various sound related variables. - * - */ - -bool init_sound (void) -{ - const char *settings = getenv ("BLASTER"); - word irc_mask_port; - - /* Read the IRQ, port address, DMA channel and SB version */ - - if ((settings = getenv ("BLASTER")) == NULL) - return FALSE; - - sound_irq = dectoi (strchr (settings, 'I') + 1); - sound_adr = hextoi (strchr (settings, 'A') + 1); - sound_dma = dectoi (strchr (settings, 'D') + 1); - sound_ver = dectoi (strchr (settings, 'T') + 1); - - /* Reset mixer chip and DSP */ - - outportb (sound_adr + 4, 0); - outportb (sound_adr + 5, 0); - - outportb (sound_adr + 6, 1); - inportb (sound_adr + 6); - inportb (sound_adr + 6); - inportb (sound_adr + 6); - outportb (sound_adr + 6, 0); - - /* Turn on speakers */ - - WRITE_DSP (0xd1) - - /* Install the end_of_dma interrupt */ - - if (sound_irq < 8) { - irc_mask_port = 0x21; - sound_int = 0x08 + sound_irq; - } else { - irc_mask_port = 0xa1; - sound_int = 0x68 + sound_irq; - } - - vect = getvect (sound_int); setvect (sound_int, end_of_dma); - - /* Allocate 64KB RAM for sample data */ - - if ((sample_data = (byte far *) farmalloc (0x10000L)) == NULL) - return FALSE; - - word0 (sample_adr1) = FP_OFF (sample_data) | (FP_SEG (sample_data) << 4); - word1 (sample_adr1) = FP_SEG (sample_data) >> 12; - word0 (sample_adr2) = 0; - word1 (sample_adr2) = word1 (sample_adr1) + 1; - - /* Enable the end_of_dma interrupt */ - - outportb (0x20, 0x20); - - if (sound_irq >= 8) - outportb (0xa0, 0x20); - - outportb (irc_mask_port, inportb (irc_mask_port) & ~(1 << (sound_irq & 7))); - - /* Indicate success */ - - return TRUE; - -}/* init_sound */ - -/* - * reset_sound - * - * Free resources allocated for playing samples. - * - */ - -void reset_sound (void) -{ - - os_stop_sample (); - - if (sample_data != NULL) - { farfree (sample_data); sample_data = NULL; } - if (sound_adr != 0) - { setvect (sound_int, vect); sound_adr = 0; } - -}/* reset_sound */ - -/* - * os_beep - * - * Play a beep sound. Ideally, the sound should be high- (number == 1) - * or low-pitched (number == 2). - * - */ - -void os_beep (int number) -{ - word T = 888 * number; - - outportb (0x43, 0xb6); - outportb (0x42, lo (T)); - outportb (0x42, hi (T)); - outportb (0x61, inportb (0x61) | 3); - - delay (75); - - outportb (0x61, inportb (0x61) & ~3); - -}/* os_beep */ - -/* - * os_prepare_sample - * - * Load the sample from the disk. - * - */ - -void os_prepare_sample (int number) -{ - - os_stop_sample (); - - /* Exit if the sound board isn't set up properly */ - - if (sample_data == NULL) - return; - if (sound_adr == 0) - return; - - /* Continue only if the desired sample is not already present */ - - if (current_sample != number) { - - char sample_name[MAX_FILE_NAME + 1]; - char numstr[2]; - FILE *fp; - - /* Build sample file name */ - - strcpy (sample_name, "sound\\"); - - numstr[0] = '0' + number / 10; - numstr[1] = '0' + number % 10; - - strncat (sample_name, stripped_story_name, 6); - strncat (sample_name, numstr, 2); - strncat (sample_name, ".snd", 4); - - /* Open sample file */ - - if ((fp = fopen (sample_name, "rb")) == NULL) - return; - - /* Load header and sample data */ - - fread (&sheader, sizeof (sheader), 1, fp); - - SWAP_BYTES (sheader.frequency) - SWAP_BYTES (sheader.length) - - fread (sample_data, 1, sheader.length, fp); - - sample_len1 = -word0 (sample_adr1); - - if (sample_len1 > sheader.length || sample_len1 == 0) - sample_len1 = sheader.length; - - sample_len2 = sheader.length - sample_len1; - - WRITE_DSP (0x40) - WRITE_DSP (256 - 1000000L / sheader.frequency) - - current_sample = number; - - /* Close sample file */ - - fclose (fp); - - } - -}/* os_prepare_sample */ - -/* - * os_start_sample - * - * Play the given sample at the given volume (ranging from 1 to 8 and - * 255 meaning a default volume). The sound is played once or several - * times in the background (255 meaning forever). The end_of_sound - * function is called as soon as the sound finishes. - * - */ - -void os_start_sample (int number, int volume, int repeats) -{ - - os_stop_sample (); - - /* Exit if the sound board isn't set up properly */ - - if (sample_data == NULL) - return; - if (sound_adr == 0) - return; - - /* Load new sample */ - - os_prepare_sample (number); - - /* Continue only if the sample's in memory now */ - - if (current_sample == number) { - - play_count = repeats; - - if (sound_ver < 6) { /* Set up SB pro mixer chip */ - - volume = (volume != 255) ? 7 + volume : 15; - - outportb (sound_adr + 4, 0x04); - outportb (sound_adr + 5, (volume << 4) | volume); - outportb (sound_adr + 4, 0x22); - outportb (sound_adr + 5, 0xff); - - } else { /* Set up SB16 mixer chip */ - - /* Many thanks to Linards Ticmanis for writing this part! */ - - volume = (volume != 255) ? 127 + 16 * volume : 255; - - outportb (sound_adr + 4, 0x32); - outportb (sound_adr + 5, volume); - outportb (sound_adr + 4, 0x33); - outportb (sound_adr + 5, volume); - outportb (sound_adr + 4, 0x30); - outportb (sound_adr + 5, 0xff); - outportb (sound_adr + 4, 0x31); - outportb (sound_adr + 5, 0xff); - - } - - play_part = 1; - start_of_dma (sample_adr1, sample_len1); - - } - -}/* os_start_sample */ - -/* - * os_stop_sample - * - * Turn off the current sample. - * - */ - -void os_stop_sample (void) -{ - - play_part = 0; - - /* Exit if the sound board isn't set up properly */ - - if (sample_data == NULL) - return; - if (sound_adr == 0) - return; - - /* Tell DSP to stop the current sample */ - - WRITE_DSP (0xd0) - -}/* os_stop_sample */ - -/* - * os_finish_with_sample - * - * Remove the current sample from memory (if any). - * - */ - -void os_finish_with_sample (void) -{ - - os_stop_sample (); /* we keep 64KB allocated all the time */ - -}/* os_finish_with_sample */ diff --git a/bcscreen.c b/bcscreen.c deleted file mode 100644 index ed42e2e..0000000 --- a/bcscreen.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * file "BCscreen.c" - * - * Borland C front end, screen manipulation - * - */ - -#include -#include -#include "frotz.h" -#include "BCfrotz.h" - -/* - * get_scrnptr - * - * Return a pointer to the given line in video RAM. - * - */ - -byte far *get_scrnptr (int y) -{ - - if (display == _CGA_) - return MK_FP ((y & 1) ? 0xba00 : 0xb800, 40 * (y & ~1)); - else if (display == _MCGA_) - return MK_FP (0xa000, 320 * y); - else - return MK_FP (0xa000, 80 * y); - -}/* get_scrnptr */ - -/* - * clear_byte - * - * Helper function for clear_line. - * - */ - -static void clear_byte (byte far *scrn, word mask) -{ - - if (display == _CGA_) - - if (scrn_attr == 0) - *scrn &= ~mask; - else - *scrn |= mask; - - else { - - outport (0x03ce, 0x0205); - - outportb (0x03ce, 0x08); - outportb (0x03cf, mask); - - asm les bx,scrn - asm mov al,es:[bx] - asm mov al,scrn_attr - asm mov es:[bx],al - - } - -}/* clear_byte */ - -/* - * clear_line - * - * Helper function for os_erase_area. - * - */ - -static void clear_line (int y, int left, int right) -{ - byte far *scrn = get_scrnptr (y); - - if (display == _MCGA_) - - _fmemset (scrn + left, scrn_attr, right - left + 1); - - else { - - word mask1 = 0x00ff >> (left & 7); - word mask2 = 0xff80 >> (right & 7); - - int x = right / 8 - left / 8; - - scrn += left / 8; - - if (x == 0) { - mask1 &= mask2; - mask2 = 0; - } - - /* Clear first byte */ - - clear_byte (scrn++, mask1); - - /* Clear middle bytes */ - - if (display >= _EGA_) - outport (0x03ce, 0xff08); - - while (--x > 0) - *scrn++ = scrn_attr; - - /* Clear last byte */ - - clear_byte (scrn, mask2); - - } - -}/* clear_line */ - -/* - * os_erase_area - * - * Fill a rectangular area of the screen with the current background - * colour. Top left coordinates are (1,1). The cursor does not move. - * - */ - -void os_erase_area (int top, int left, int bottom, int right) -{ - int y; - - top--; - left--; - bottom--; - right--; - - if (display <= _TEXT_) { - - asm mov ax,0x0600 - asm mov ch,byte ptr top - asm mov cl,byte ptr left - asm mov dh,byte ptr bottom - asm mov dl,byte ptr right - asm mov bh,scrn_attr - asm int 0x10 - - } else - - for (y = top; y <= bottom; y++) - clear_line (y, left, right); - -}/* os_erase_area */ - -/* - * copy_byte - * - * Helper function for copy_line. - * - */ - -static void copy_byte (byte far *scrn1, byte far *scrn2, byte mask) -{ - int i; - - if (display == _CGA_) - - *scrn1 = (*scrn1 & ~mask) | (*scrn2 & mask); - - else { - - outport (0x03ce, 0x0005); - - outportb (0x03ce, 0x08); - outportb (0x03cf, mask); - - outportb (0x03ce, 0x04); - outportb (0x03c4, 0x02); - - for (i = 0; i < 4; i++) { - - outportb (0x03cf, i); - outportb (0x03c5, 1 << i); - - asm les bx,scrn2 - asm mov ah,es:[bx] - asm les bx,scrn1 - asm mov al,es:[bx] - asm mov es:[bx],ah - - } - - outportb (0x03c5, 0x0f); - - } - -}/* copy_byte */ - -/* - * copy_line - * - * Helper function for os_scroll_area. - * - */ - -static void copy_line (int y1, int y2, int left, int right) -{ - byte far *scrn1 = get_scrnptr (y1); - byte far *scrn2 = get_scrnptr (y2); - - if (display == _MCGA_) - - _fmemcpy (scrn1 + left, scrn2 + left, right - left + 1); - - else { - - word mask1 = 0x00ff >> (left & 7); - word mask2 = 0xff80 >> (right & 7); - - int x = right / 8 - left / 8; - - scrn1 += left / 8; - scrn2 += left / 8; - - if (x == 0) { - mask1 &= mask2; - mask2 = 0; - } - - /* Copy first byte */ - - copy_byte (scrn1++, scrn2++, mask1); - - /* Copy middle bytes */ - - if (display >= _EGA_) - outport (0x03ce, 0x0105); - - while (--x > 0) - *scrn1++ = *scrn2++; - - /* Copy last byte */ - - copy_byte (scrn1, scrn2, mask2); - - } - -}/* copy_line */ - -/* - * os_scroll_area - * - * Scroll a rectangular area of the screen up (units > 0) or down - * (units < 0) and fill the empty space with the current background - * colour. Top left coordinates are (1,1). The cursor stays put. - * - */ - -void os_scroll_area (int top, int left, int bottom, int right, int units) -{ - int y; - - top--; - left--; - bottom--; - right--; - - if (display <= _TEXT_) { - - asm mov ah,6 - asm mov bx,units - asm cmp bx,0 - asm jg scroll - asm mov ah,7 - asm neg bx - scroll: - asm mov al,bl - asm mov ch,byte ptr top - asm mov cl,byte ptr left - asm mov dh,byte ptr bottom - asm mov dl,byte ptr right - asm mov bh,scrn_attr - asm int 0x10 - - } else - - if (units > 0) - - for (y = top; y <= bottom; y++) - - if (y <= bottom - units) - copy_line (y, y + units, left, right); - else - clear_line (y, left, right); - - else - - for (y = bottom; y >= top; y--) - - if (y >= top - units) - copy_line (y, y + units, left, right); - else - clear_line (y, left, right); - -}/* os_scroll_area */ diff --git a/bctext.c b/bctext.c deleted file mode 100644 index 4ab9100..0000000 --- a/bctext.c +++ /dev/null @@ -1,842 +0,0 @@ -/* - * file "BCtext.c" - * - * Borland C front end, text functions - * - */ - -#include -#include -#include -#include -#include -#include "frotz.h" -#include "BCfrotz.h" - -extern byte far *get_scrnptr (int); - -int current_bg = 0; -int current_fg = 0; -int current_style = 0; -int current_font = 0; - -byte text_bg = 0; -byte text_fg = 0; -byte bg = 0; -byte fg = 0; -byte scrn_attr = 0; - -int cursor_x = 0; -int cursor_y = 0; - -char latin1_to_ascii[] = - " ! c L >o> 1/41/23/4? " - "A A A A Ae A AE C E E E E I I I I " - "Th N O O O O Oe * O U U U Ue Y Th ss " - "a a a a ae a ae c e e e e i i i i " - "th n o o o o oe : o u u u ue y th y "; - -char latin1_to_ibm[] = { - 0x20, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, - 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, - 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, - 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, - 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, - 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, - 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, - 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, - 0x85, 0xa0, 0x83, 0xc6, 0x84, 0x86, 0x91, 0x87, - 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, - 0xd0, 0xa4, 0x95, 0xa2, 0x93, 0xe4, 0x94, 0xf6, - 0x9b, 0x97, 0xa3, 0x96, 0x81, 0xec, 0xe7, 0x98 -}; - -static byte far *graphics_font = NULL; -static byte far *mcga_font = NULL; -static byte far *mcga_width = NULL; -static word far *serif_font = NULL; -static byte far *serif_width = NULL; - -/* - * get_font - * - * Helper function for load_fonts. - * - */ - -static void far *get_chunk (FILE *fp, int num) -{ - static chunk_size[] = { - 0x0300, - 0x0360, - 0x18c0, - 0x18c0, - 0x18c0, - 0x18c0 }; - - void far *ptr; - long offs = 0; - int i; - - ptr = farmalloc (chunk_size[num]); - - for (i = 0; i <= num; i++) - offs -= chunk_size[i]; - - fseek (fp, offs, SEEK_END); - - if (ptr == NULL || !fread (ptr, chunk_size[num], 1, fp)) - os_fatal ("Cannot load fonts"); - - return ptr; - -}/* get_chunk */ - -/* - * load_fonts - * - * Load the proportional and graphics fonts. In the release version all - * font data is appended to the end of the executable. - * - */ - -void load_fonts (const char *progname) -{ - FILE *fp; - - if ((fp = fopen (progname, "rb")) == NULL) - os_fatal ("Cannot load fonts"); - - if (display == _MCGA_) { - - mcga_font = get_chunk (fp, 1); - mcga_width = (byte far *) mcga_font + 0x300; - - } else graphics_font = get_chunk (fp, 0); - - if (display == _AMIGA_ && user_font != 0) { - - serif_font = get_chunk (fp, 1 + user_font); - serif_width = (byte far *) serif_font + 0x1800; - - } - - fclose (fp); - -}/* load_fonts */ - -/* - * os_font_data - * - * Return true if the given font is available. The font can be - * - * TEXT_FONT - * PICTURE_FONT - * GRAPHICS_FONT - * FIXED_WIDTH_FONT - * - * The font size should be stored in "height" and "width". If the given - * font is unavailable then these values must _not_ be changed. - * - */ - -int os_font_data (int font, int *height, int *width) -{ - - /* All fonts of this interface have the same size */ - - *height = h_font_height; - *width = h_font_width; - - /* Not every font is available in every mode */ - - if (font == TEXT_FONT) - return TRUE; - if (font == GRAPHICS_FONT && (display == _CGA_ || display >= _EGA_)) - return TRUE; - if (font == FIXED_WIDTH_FONT) - return TRUE; - - /* Unavailable font */ - - return FALSE; - -}/* os_font_data */ - -/* - * switch_scrn_attr - * - * Parts of the screen are usually erased to background colour. However, - * for deleting text in the input routine it can be useful to erase to - * the current text background colour. The two colours can be different, - * for example when the reverse text style is used. This helper function - * toggles between the two possible behaviours. - * - */ - -void switch_scrn_attr (bool flag) -{ - byte scrn_bg; - byte scrn_fg; - - if (flag) { - scrn_bg = text_bg; - scrn_fg = text_fg; - } else { - scrn_bg = bg; - scrn_fg = fg; - } - - if (display <= _TEXT_) - scrn_attr = (scrn_bg << 4) | scrn_fg; - else if (display == _CGA_) - scrn_attr = (scrn_bg != BLACK) ? 0xff : 0x00; - else - scrn_attr = scrn_bg; - -}/* switch_scrn_attr */ - -/* - * adjust_style - * - * Set the current colours. This combines the current colour selection - * and the current text style. - * - */ - -static void adjust_style (void) -{ - static byte amiga_palette[][3] = { - { 0x00, 0x00, 0x00 }, - { 0x2a, 0x00, 0x00 }, - { 0x00, 0x2a, 0x00 }, - { 0x3f, 0x3f, 0x15 }, - { 0x00, 0x00, 0x2a }, - { 0x2a, 0x00, 0x2a }, - { 0x00, 0x2a, 0x2a }, - { 0x3f, 0x3f, 0x3f }, - { 0x30, 0x30, 0x30 }, - { 0x20, 0x20, 0x20 }, - { 0x10, 0x10, 0x10 }, - }; - - static byte pc_colour[] = { - BLACK, - RED, - GREEN, - YELLOW, - BLUE, - MAGENTA, - CYAN, - WHITE, - DARKGRAY - }; - - static byte palette_bg = 0xff; - static byte palette_fg = 0xff; - - fg = current_fg; - bg = current_bg; - - /* V6 game, Amiga mode: Alter the palette registers if the colours - of window 0 have changed. DAC register #79 holds the foreground, - DAC register #64 the background colour. */ - - if (display == _AMIGA_ && h_version == V6 && cwin == 0) { - - if (fg < 16 && fg != palette_fg) { - - byte R = amiga_palette[fg - 2][0]; - byte G = amiga_palette[fg - 2][1]; - byte B = amiga_palette[fg - 2][2]; - - asm mov ax,0x1010 - asm mov bx,79 - asm mov dh,R - asm mov ch,G - asm mov cl,B - asm int 0x10 - - palette_fg = fg; - - } - - if (bg < 16 && bg != palette_bg) { - - byte R = amiga_palette[bg - 2][0]; - byte G = amiga_palette[bg - 2][1]; - byte B = amiga_palette[bg - 2][2]; - - asm mov ax,0x1010 - asm mov bx,64 - asm mov dh,R - asm mov ch,G - asm mov cl,B - asm int 0x10 - - palette_bg = bg; - - } - - } - - /* Handle colours */ - - if (fg < 16) - - if (display == _MONO_) - fg = (fg == WHITE_COLOUR) ? LIGHTGRAY : BLACK; - else if (h_version == V6 && display == _AMIGA_) - fg = (palette_fg == fg) ? 15 : 0; - else - fg = pc_colour[fg - 2]; - - else fg -= 16; - - if (bg < 16) - - if (display == _MONO_) - bg = (bg == WHITE_COLOUR) ? LIGHTGRAY : BLACK; - else if (h_version == V6 && display == _AMIGA_) - bg = (palette_bg == bg) ? 0 : 15; - else - bg = pc_colour[bg - 2]; - - else bg -= 16; - - /* Handle reverse text style */ - - if (current_style & REVERSE_STYLE) { - text_fg = (user_reverse_fg != -1) ? user_reverse_fg : bg; - text_bg = (user_reverse_bg != -1) ? user_reverse_bg : fg; - } else { - text_fg = fg; - text_bg = bg; - } - - /* Handle emphasis style */ - - if (current_style & EMPHASIS_STYLE) { - - if (display == _MONO_ && text_bg == BLACK) - text_fg = BLUE; /* blue in monochrome mode is underlined */ - if (display == _TEXT_) - text_fg = (user_emphasis != -1) ? user_emphasis : YELLOW; - - } - - /* Handle boldface style */ - - if (current_style & BOLDFACE_STYLE) { - - if (display == _MONO_) - text_fg = WHITE; - if (display == _TEXT_) - text_fg ^= 8; - - } - - /* Set the screen attribute for scrolling and erasing */ - - switch_scrn_attr (FALSE); - -}/* adjust_style */ - -/* - * os_set_colour - * - * Set the foreground and background colours which can be: - * - * DEFAULT_COLOUR - * BLACK_COLOUR - * RED_COLOUR - * GREEN_COLOUR - * YELLOW_COLOUR - * BLUE_COLOUR - * MAGENTA_COLOUR - * CYAN_COLOUR - * WHITE_COLOUR - * - * MS-DOS 320 columns MCGA mode only: - * - * GREY_COLOUR - * - * Amiga only: - * - * LIGHTGREY_COLOUR - * MEDIUMGREY_COLOUR - * DARKGREY_COLOUR - * - * There may be more colours in the range from 16 to 255; see the remarks - * on os_peek_colour. - * - */ - -void os_set_colour (int new_foreground, int new_background) -{ - - current_fg = new_foreground; - current_bg = new_background; - - /* Apply changes */ - - adjust_style (); - -}/* os_set_colour */ - -/* - * os_set_text_style - * - * Set the current text style. Following flags can be set: - * - * REVERSE_STYLE - * BOLDFACE_STYLE - * EMPHASIS_STYLE (aka underline aka italics) - * FIXED_WIDTH_STYLE - * - */ - -void os_set_text_style (int new_style) -{ - - current_style = new_style; - - /* Apply changes */ - - adjust_style (); - -}/* os_set_text_style */ - -/* - * os_set_font - * - * Set the font for text output. The interpreter takes care not to - * choose fonts which aren't supported by the interface. - * - */ - -void os_set_font (int new_font) -{ - - current_font = new_font; - -}/* os_set_font */ - -/* - * write_pattern - * - * Helper function for drawing characters in EGA and Amiga mode. - * - */ - -void write_pattern (byte far *screen, byte val, byte mask) -{ - - if (mask != 0) { - - if (display == _CGA_) { - - if (text_bg == BLACK) - *screen &= ~mask; - if (text_bg == WHITE) - *screen |= mask; - if (text_fg != text_bg) - *screen ^= val; - - } else if (display == _MCGA_) { - - byte i; - - for (i = 0x80; (mask & i) != 0; i >>= 1) - *screen++ = (val & i) ? text_fg : text_bg; - - } else { - - asm mov dx,0x03cf - asm mov al,mask - asm out dx,al - asm les bx,screen - asm mov ch,text_bg - asm mov al,es:[bx] - asm mov es:[bx],ch - asm mov al,val - asm out dx,al - asm mov ch,text_fg - asm mov al,es:[bx] - asm mov es:[bx],ch - - } - - } - -}/* write_pattern */ - -/* - * os_display_char - * - * Display a character of the current font using the current colours and - * text style. The cursor moves to the next position. Printable codes are - * all ASCII values from 32 to 126, ISO Latin-1 characters from 160 to - * 255, ZC_GAP (gap between two sentences) and ZC_INDENT (paragraph - * indentation). The screen should not be scrolled after printing to the - * bottom right corner. - * - */ - -void os_display_char (zchar c) -{ - int width = os_char_width (c); - - /* Handle accented characters */ - - if (c >= ZC_LATIN1_MIN && (story_id != BEYOND_ZORK || (h_flags & GRAPHICS_FLAG))) - - if (display == _CGA_ || display == _MCGA_) { - - char *ptr = latin1_to_ascii + 3 * (c - ZC_LATIN1_MIN); - - char c1 = *ptr++; - char c2 = *ptr++; - char c3 = *ptr++; - - os_display_char (c1); - - if (c2 != ' ') - os_display_char (c2); - if (c3 != ' ') - os_display_char (c3); - - return; - - } else if (display == _AMIGA_ && current_font == TEXT_FONT && !(current_style & FIXED_WIDTH_STYLE) && user_font != 0) { - - if (c >= ZC_LATIN1_MIN) - c -= 32; - - } else c = latin1_to_ibm[c - ZC_LATIN1_MIN]; - - /* Handle special indentations */ - - if (c == ZC_INDENT) - { os_display_char (' '); os_display_char (' '); os_display_char (' '); return; } - if (c == ZC_GAP) - { os_display_char (' '); os_display_char (' '); return; } - - /* Display character */ - - if (display <= _TEXT_) { - - asm mov ah,2 - asm mov bh,0 - asm mov dh,byte ptr cursor_y - asm mov dl,byte ptr cursor_x - asm int 0x10 - asm mov ah,9 - asm mov bh,0 - asm mov bl,byte ptr text_bg - asm mov cl,4 - asm shl bl,cl - asm or bl,byte ptr text_fg - asm mov cx,1 - asm mov al,byte ptr c - asm int 0x10 - - } else { - - void far *table; - word mask; - word val; - byte mask0; - byte mask1; - int align; - int underline; - int boldface; - int type; - - int shift = (display != _MCGA_) ? cursor_x % 8 : 0; - int offset = (display != _MCGA_) ? cursor_x / 8 : cursor_x; - - int i; - - if (current_font == GRAPHICS_FONT) { - table = graphics_font + 8 * (c - 32); - mask = 0xff; - underline = -1; - boldface = -1; - align = 0; - type = 1; - } else if (display == _AMIGA_ && current_font == TEXT_FONT && !(current_style & FIXED_WIDTH_STYLE) && user_font != 0) { - table = serif_font + 16 * (c - 32); - mask = 0xffff << (16 - width); - underline = 14; - boldface = 1; - align = 0; - type = 2; - } else if (display == _CGA_) { - table = (byte far *) MK_FP (0xf000, 0xfa6e) + 8 * c; - mask = 0xff; - underline = 7; - boldface = (user_bold_typing != -1) ? 1 : -1; - align = 0; - type = 3; - } else if (display >= _EGA_) { - table = (byte far *) getvect (0x43) + h_font_height * c; - mask = 0xff; - underline = h_font_height - 1; - boldface = (user_bold_typing != -1) ? 1 : -1; - align = 0; - type = 3; - } else { - table = mcga_font + 8 * (c - 32); - mask = 0xff & (0xff << (8 - width)); - underline = 7; - boldface = -1; - align = (width + 1 - mcga_width[c - 32]) / 2; - type = 3; - } - - mask0 = mask >> shift; - mask1 = mask << (8 - shift); - - if (!(current_style & BOLDFACE_STYLE)) - boldface = -1; - if (!(current_style & EMPHASIS_STYLE)) - underline = -1; - - if (display >= _EGA_) { - outport (0x03ce, 0x0205); - outport (0x03ce, 0xff08); - } - - for (i = 0; i < h_font_height; i++) { - - byte far *screen = get_scrnptr (cursor_y + i) + offset; - - if (type == 1) - val = *((byte far *) table + 8 * i / h_font_height); - if (type == 2) - val = *((word far *) table + i); - if (type == 3) - val = *((byte far *) table + i); - - if (align != 0) - val >>= align; - - if (boldface == 1) - val |= val >> 1; - if (underline == i) - val ^= mask; - - if (type == 2) - write_pattern (screen++, val >> (8 + shift), mask >> (8 + shift)); - - write_pattern (screen + 0, val >> shift, mask0); - write_pattern (screen + 1, val << (8 - shift), mask1); - - } - - } - - /* Move cursor to next position */ - - cursor_x += width; - -}/* os_display_char */ - -/* - * os_display_string - * - * Pass a string of characters to os_display_char. - * - */ - -void os_display_string (const zchar *s) -{ - - zchar c; - - while ((c = *s++) != 0) - - if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE) { - - int arg = *s++; - - if (c == ZC_NEW_FONT) - os_set_font (arg); - if (c == ZC_NEW_STYLE) - os_set_text_style (arg); - - } else os_display_char (c); - -}/* os_display_string */ - -/* - * os_char_width - * - * Return the width of the character in screen units. - * - */ - -int os_char_width (zchar c) -{ - - /* Handle accented characters */ - - if (c >= ZC_LATIN1_MIN && (story_id != BEYOND_ZORK || (h_flags & GRAPHICS_FLAG))) - - if (display == _CGA_ || display == _MCGA_) { - - const char *ptr = latin1_to_ascii + 3 * (c - ZC_LATIN1_MIN); - - int width = 0; - - char c1 = *ptr++; - char c2 = *ptr++; - char c3 = *ptr++; - - width = os_char_width (c1); - - if (c2 != ' ') - width += os_char_width (c2); - if (c3 != ' ') - width += os_char_width (c3); - - return width; - - } else if (display == _AMIGA_ && current_font == TEXT_FONT && !(current_style & FIXED_WIDTH_STYLE) && user_font != 0) - - if (c >= ZC_LATIN1_MIN) - c -= 32; - - /* Handle special indentations */ - - if (c == ZC_INDENT) - return 3 * os_char_width (' '); - if (c == ZC_GAP) - return 2 * os_char_width (' '); - - /* Calculate width */ - - if (display <= _TEXT_) - return 1; - if (display == _CGA_) - return 8; - if (display == _EGA_) - return 8; - - if (current_font == GRAPHICS_FONT) - return 8; - if (current_font == FIXED_WIDTH_FONT || (current_style & FIXED_WIDTH_STYLE) || (display == _AMIGA_ && user_font == 0)) - return (display == _AMIGA_) ? 8 : 5; - - if (display == _MCGA_) - return mcga_width[c - 32]; - if (display == _AMIGA_) - return serif_width[c - 32] + ((current_style & BOLDFACE_STYLE) ? 1 : 0); - - return 0; - -}/* os_char_width */ - -/* - * os_string_width - * - * Calculate the length of a word in screen units. Apart from letters, - * the word may contain special codes: - * - * ZC_NEW_STYLE - next character is a new text style - * ZC_NEW_FONT - next character is a new font - * - */ - -int os_string_width (const zchar *s) -{ - int width = 0; - - int saved_font = current_font; - int saved_style = current_style; - - zchar c; - - while ((c = *s++) != 0) - - if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT) { - - int arg = *s++; - - if (c == ZC_NEW_FONT) - current_font = arg; - if (c == ZC_NEW_STYLE) - current_style = arg; - - } else width += os_char_width (c); - - current_font = saved_font; - current_style = saved_style; - - return width; - -}/* os_string_width */ - -/* - * os_set_cursor - * - * Place the text cursor at the given coordinates. Top left is (1,1). - * - */ - -void os_set_cursor (int y, int x) -{ - - cursor_y = y - 1; - cursor_x = x - 1; - -}/* os_set_cursor */ - -/* - * os_more_prompt - * - * Display a MORE prompt, wait for a keypress and remove the MORE - * prompt from the screen. - * - */ - -void os_more_prompt (void) -{ - int saved_x; - - /* Save text font and style */ - - int saved_font = current_font; - int saved_style = current_style; - - /* Choose plain text style */ - - current_font = TEXT_FONT; - current_style = 0; - - adjust_style (); - - /* Wait until the user presses a key */ - - saved_x = cursor_x; - - os_display_string ((zchar *) "[MORE]"); - os_read_key (0, TRUE); - - os_erase_area (cursor_y + 1, - saved_x + 1, - cursor_y + h_font_height, - cursor_x + 1); - - cursor_x = saved_x; - - /* Restore text font and style */ - - current_font = saved_font; - current_style = saved_style; - - adjust_style (); - -}/* os_more_prompt */ diff --git a/fastmem.c b/fastmem.c index d121f1b..ea9b001 100644 --- a/fastmem.c +++ b/fastmem.c @@ -207,8 +207,26 @@ void init_memory (void) /* Open story file */ - if ((story_fp = fopen (story_name, "rb")) == NULL) - os_fatal ("Cannot open story file"); + if ((story_fp = fopen (story_name, "rb")) == NULL) { + + char *path, *p, tmp[256]; + + /* Story file may be on INFOCOM_PATH, rather than current directory */ + if ((path = getenv("INFOCOM_PATH")) == NULL) + os_fatal ("Cannot open story file"); + + p=strtok(path,":"); + while (p != NULL) { + sprintf(tmp, "%s/%s", p, story_name); + if ((story_fp = fopen (tmp, "rb")) == NULL) + p = strtok(NULL, ":"); + else + p = NULL; + } + + if (story_fp == NULL) + os_fatal ("Cannot open story file"); + } /* Allocate memory for story header */ diff --git a/font.dat b/font.dat deleted file mode 100644 index fe9799c..0000000 Binary files a/font.dat and /dev/null differ diff --git a/frotz.6 b/frotz.6 new file mode 100644 index 0000000..f5dd8fb --- /dev/null +++ b/frotz.6 @@ -0,0 +1,131 @@ +.\" -*- nroff -*- +.TH FROTZ 6 2.32 +.SH NAME +frotz \- interpreter for all Infocom games +.SH SYNOPSIS +.B frotz +.RI [ options "] " file +.SH DESCRIPTION +.B Frotz +is a Z-machine interpreter. The Z-machine was a virtual machine designed +by Infocom to run all of their text adventures. It went through multiple +revisions during the lifetime of the company, and two further revisions +(V7 and V8) were created by Graham Nelson after the company's demise. +The specification is now quite well documented; this version of frotz +supports version 1.0. +.SH OPTIONS +.TP +.B \-a +Watch attribute setting. Setting and clearing of attributes on objects +will be noted in debugging messages. +.TP +.B \-A +Watch attribute testing. Every time the z-machine test an attribute value, +the test and the result will be reported. +.TP +.B \-b N +Sets the default background color. N corresponds to one of the Z-machine +colors, which are as follows: +.br +2 = black 3 = red 4 = green 5 = yellow +.br +6 = blue 7 = magenta 8 = cyan 9 = white +.br +If color support is disabled in the binary, or not +available on your terminal, this option does nothing. +.TP +.B \-c N +Sets the number of context lines used. By default, after a ``[MORE]'' +prompt, and assuming there is enough output pending, frotz will allow all +the currently visible lines to scroll off the screen before prompting +again. This switch specifies how many lines of text frotz will hold +over and display at the top of the next screen. +.TP +.B \-d +Disables color. If frotz discovers that your terminal has color support, +it will enable it by default. This switch overrides that behavior. +.TP +.B \-f N +Sets the default foreground color. N is a number corresponding to one of +the Z-machine colors, as listed above. If color support is disabled in +the binary, or not available on your terminal, this option does nothing. +.TP +.B \-h N +Manually sets the screen height. Though most curses libraries are intelligent +enough to determine the current width from the terminal, it may sometimes +be necessary to use this option to override the default. +.TP +.B \-i +Ignore runtime errors. Set this switch and Frotz no longer worries about +anything the game tries to do. This can help you to get around fatal errors. +.TP +.B \-l N +Sets the left margin, for those who might have specific formatting needs. +.TP +.B \-o +Watch object movement. This option enables debugging messages from the +interpreter which detail the moving of objects in the object tree. +.TP +.B \-O +Watch object location. These debugging messages detail the locations of +objects in the object tree. +.TP +.B \-p +Plain ASCII output only. This inhibits the output of accented letters +and other characters from the Latin-1 character set, replacing them with +reasonable alternatives. This may be necessary on devices lacking these +characters. +.TP +.B \-P +Alter the piracy opcode. The piracy opcode was never used by Infocom, and +this switch is only useful for those who like to toy around with Z-code. +.TP +.B \-r N +Sets the right margin. +.TP +.B \-s N +Set the random number seed value. The given seed value is used as the initial +seed value on every restart. This is helpful for testing games like +.B Curses +which make random decisions before the first input (such that the hot +key Alt\-S does not really help). +.TP +.B \-S N +Set the transcript width. By default your transscript files are formatted +to a width of 80 columns per line, regardless of the current screen width. +This switch allows you to change this setting. In particular, use \-S 0 +to deactivate automatic line splitting in transscript files. +.TP +.B \-t +Sets the z-machine's +.I Tandy bit, +which may affect the behavior of certain Infocom games. For example, +Zork I pretends not to have sequals, and Witness has it's language +toned down. +.TP +.B \-u N +Sets the number of slots available for frotz's multiple undo hotkey (see +below). This defaults to twenty, which should be sufficient for most +purposes. Setting too high a number here may be dangerous on machines +with limited memory. +.TP +.B \-w N +Manually sets the screen width. Again, this should not be necessary +except in special circumstances. +.TP +.B \-x +Expand the abbreviations "g","x", and "z" to "again", "examine", and "wait". +This switch was made for old Infocom games that lack the common +abbreviations introduced in later games. Use it with caution: A +few games might use "g", "x" or "z" for different purposes. +.SH ENVIRONMENT +If the INFOCOM_PATH environment variable is defined, frotz will search +those paths for game files. +.SH "SEE ALSO" +.SH CAVEATS +.SH BUGS +This program has no bugs. no bugs. no bugs. no *WHAP* thank you. +.SH AUTHOR +.B Frotz +was written by Stefan Jokisch in 1995-7. The unix port (and this man +page) were done by Galen Hazelwood. diff --git a/frotz.h b/frotz.h index 93a2c96..b62bc83 100644 --- a/frotz.h +++ b/frotz.h @@ -5,10 +5,16 @@ * */ +/* Unfortunately, frotz's bool definition conflicts with that of curses. + But since no os_* function uses it, it's safe to let the frotz core see + this definition, but have the unix port see the curses version. */ + +#ifndef __UNIX_PORT_FILE typedef int bool; #define TRUE 1 #define FALSE 0 +#endif typedef unsigned char zbyte; typedef unsigned short zword; diff --git a/ux_frotz.h b/ux_frotz.h new file mode 100644 index 0000000..dc23873 --- /dev/null +++ b/ux_frotz.h @@ -0,0 +1,10 @@ +/* + * ux_frotz.h + * + * Unix interface, declarations + * + */ + +extern int current_text_style; /* ux_init */ +extern char unix_plain_ascii; /* ux_init */ +extern int current_color; /* ux_text */ diff --git a/ux_init.c b/ux_init.c new file mode 100644 index 0000000..b2f528e --- /dev/null +++ b/ux_init.c @@ -0,0 +1,385 @@ +/* + * ux_init.c + * + * Unix interface, initialisation + * + */ + +#define __UNIX_PORT_FILE + +#include +#include +#include + +#include + +/* Prototypes for getopt */ +#ifdef USE_UNISTD_H +#include +#elif USE_GETOPT_H +#include +#else +#ifndef USE_NOTHING +extern int getopt(int, char **, char *); +extern int optind, opterr; +extern char *optarg; +#endif +#endif + +#ifdef USE_NCURSES_H +#include +#else +#include +#endif + +#include "frotz.h" +#include "ux_frotz.h" + +#define INFORMATION "\ +\n\ +FROTZ V2.32 - interpreter for all Infocom games. Complies with standard\n\ +1.0 of Graham Nelson's specification. Written by Stefan Jokisch in 1995-7\n\ +\n\ +Syntax: frotz [options] story-file\n\ +\n\ + -a watch attribute setting \t -l # left margin\n\ + -A watch attribute testing \t -o watch object movement\n\ + -b # background colour \t -O watch object locating\n\ + \t -p plain ASCII output only\n\ + \t -P alter piracy opcode\n\ + -c # context lines \t -r # right margin\n\ + -d disable color \t -s # random number seed value\n\ + \t -S # transscript width\n\ + -f # foreground colour \t -t set Tandy bit\n\ + \t -u # slots for multiple undo\n\ + -h # screen height \t -w # screen width\n\ + -i ignore runtime errors \t -x expand abbreviations g/x/z\n" + +static int user_disable_color = 0; +static int user_foreground_color = -1; +static int user_background_color = -1; +static int user_screen_width = -1; +static int user_screen_height = -1; +static int user_random_seed = -1; +static int user_tandy_bit = 0; + +static int curses_active = 0; /* true if os_init_screen has been run */ +int current_text_style = 0; /* Since I can't use attr_get, which + would make things easier, I need to + use the same hack the MS-DOS port + does...keep the current style in a + global variable. */ +char unix_plain_ascii = 0; /* true if user wants to disable latin-1 */ + +/* + * os_fatal + * + * Display error message and exit program. + * + */ + +void os_fatal (const char *s) +{ + + if (curses_active) { + os_display_string("\n\n"); + beep(); + os_set_text_style(BOLDFACE_STYLE); + os_display_string("Fatal error: "); + os_set_text_style(0); + os_display_string(s); + os_display_string("\n"); + os_reset_screen(); + exit(1); + } + + fputs ("\nFatal error: ", stderr); + fputs (s, stderr); + fputs ("\n\n", stderr); + + exit (1); + +}/* os_fatal */ + +extern char script_name[]; +extern char command_name[]; +extern char save_name[]; +extern char auxilary_name[]; + +/* + * os_process_arguments + * + * Handle command line switches. Some variables may be set to activate + * special features of Frotz: + * + * option_attribute_assignment + * option_attribute_testing + * option_context_lines + * option_object_locating + * option_object_movement + * option_left_margin + * option_right_margin + * option_ignore_errors + * option_piracy + * option_undo_slots + * option_expand_abbreviations + * option_script_cols + * + * The global pointer "story_name" is set to the story file name. + * + */ + +void os_process_arguments (int argc, char *argv[]) +{ + int c, i; + char stripped_story_name[MAX_FILE_NAME]; + char *p = NULL; + + /* Parse the options */ + + do { + + c = getopt(argc, argv, "aAb:c:df:h:il:oOpPr:s:S:tu:w:x"); + + switch(c) { + case 'a': option_attribute_assignment = 1; break; + case 'A': option_attribute_testing = 1; break; + case 'b': user_background_color = atoi(optarg); + if ((user_background_color < 2) || + (user_background_color > 9)) + user_background_color = -1; + break; + case 'c': option_context_lines = atoi(optarg); break; + case 'd': user_disable_color = 1; break; + case 'f': user_foreground_color = atoi(optarg); + if ((user_foreground_color < 2) || + (user_foreground_color > 9)) + user_foreground_color = -1; + break; + case 'h': user_screen_height = atoi(optarg); break; + case 'i': option_ignore_errors = 1; break; + case 'l': option_left_margin = atoi(optarg); break; + case 'o': option_object_movement = 1; break; + case 'O': option_object_locating = 1; break; + case 'p': unix_plain_ascii = 1; break; + case 'P': option_piracy = 1; break; + case 'r': option_right_margin = atoi(optarg); break; + case 's': user_random_seed = atoi(optarg); break; + case 'S': option_script_cols = atoi(optarg); break; + case 't': user_tandy_bit = 1; break; + case 'u': option_undo_slots = atoi(optarg); break; + case 'w': user_screen_width = atoi(optarg); break; + case 'x': option_expand_abbreviations = 1; break; + } + + } while (c != EOF); + + if (optind != argc - 1) { + puts (INFORMATION); + exit (1); + } + + /* Save the story file name */ + + story_name = argv[optind]; + + /* Strip path off the story file name */ + + p = story_name; + + for (i = 0; story_name[i] != 0; i++) + if (story_name[i] == '/') + p = story_name + i + 1; + + for (i = 0; p[i] != '\0' && p[i] != '.'; i++) + stripped_story_name[i] = p[i]; + stripped_story_name[i] = '\0'; + + /* Create nice default file names */ + + strcpy (script_name, stripped_story_name); + strcpy (command_name, stripped_story_name); + strcpy (save_name, stripped_story_name); + strcpy (auxilary_name, stripped_story_name); + + /* Don't forget the extensions */ + + strcat (script_name, ".scr"); + strcat (command_name, ".rec"); + strcat (save_name, ".sav"); + strcat (auxilary_name, ".aux"); + +}/* os_process_arguments */ + +/* + * os_init_screen + * + * Initialise the IO interface. Prepare the screen and other devices + * (mouse, sound board). Set various OS depending story file header + * entries: + * + * h_config (aka flags 1) + * h_flags (aka flags 2) + * h_screen_cols (aka screen width in characters) + * h_screen_rows (aka screen height in lines) + * h_screen_width + * h_screen_height + * h_font_height (defaults to 1) + * h_font_width (defaults to 1) + * h_default_foreground + * h_default_background + * h_interpreter_number + * h_interpreter_version + * h_user_name (optional; not used by any game) + * + * Finally, set reserve_mem to the amount of memory (in bytes) that + * should not be used for multiple undo and reserved for later use. + * + * (Unix has a non brain-damaged memory model which dosen't require such + * ugly hacks, neener neener neener. --GH :) + * + */ + +void os_init_screen (void) +{ + + /*trace(TRACE_CALLS);*/ + curses_active = 1; /* Let os_fatal know curses is running */ + initscr(); /* Set up curses */ + cbreak(); /* Raw input mode, no line processing */ + noecho(); /* No input echo */ + nonl(); /* No newline translation */ + intrflush(stdscr, TRUE); /* Flush output on interrupt */ + keypad(stdscr, TRUE); /* Enable the keypad and function keys */ + scrollok(stdscr, FALSE); /* No scrolling unless explicitly asked for */ + + if (h_version == V3 && user_tandy_bit != 0) + h_config |= CONFIG_TANDY; + + if (h_version == V3) + h_config |= CONFIG_SPLITSCREEN; + + if (h_version >= V4) + h_config |= CONFIG_BOLDFACE | CONFIG_EMPHASIS | CONFIG_FIXED | CONFIG_TIMEDINPUT; + +#ifndef SOUND_SUPPORT + if (h_version >= V5) + h_flags &= ~(GRAPHICS_FLAG | SOUND_FLAG | MOUSE_FLAG | MENU_FLAG); + + if (h_version == V3) + h_flags &= ~OLD_SOUND_FLAG; +#else + if (h_version >= V5) + h_flags &= ~(GRAPHICS_FLAG | MOUSE_FLAG | MENU_FLAG); + + if ((h_version >= 5) && (h_flags & SOUND_FLAG)) { + if (unix_init_sound()) + h_flags |= SOUND_FLAG; + else + h_flags &= ~SOUND_FLAG; + } + if ((h_version == 3) && (h_flags & OLD_SOUND_FLAG)) { + if (unix_init_sound()) + h_flags |= OLD_SOUND_FLAG; + else + h_flags &= ~OLD_SOUND_FLAG; + } +#endif + + if (h_version >= V5 && (h_flags & UNDO_FLAG)) + if (option_undo_slots == 0) + h_flags &= ~UNDO_FLAG; + + getmaxyx(stdscr, h_screen_rows, h_screen_cols); + + if (user_screen_height != -1) + h_screen_rows = user_screen_height; + if (user_screen_width != -1) + h_screen_cols = user_screen_width; + + h_screen_width = h_screen_cols; + h_screen_height = h_screen_rows; + + h_font_width = 1; + h_font_height = 1; + + h_interpreter_number = INTERP_DEC_20; /* We is a DECsystem-20 :) */ + h_interpreter_version = 'F'; + +#ifdef COLOR_SUPPORT + if (has_colors() && !user_disable_color) { + h_config |= CONFIG_COLOUR; + h_flags |= COLOUR_FLAG; /* FIXME: beyond zork handling? */ + start_color(); + if (user_foreground_color != -1) + h_default_foreground = user_foreground_color; + else + h_default_foreground = WHITE_COLOUR; + if (user_background_color != -1) + h_default_background = user_background_color; + else + h_default_background = BLUE_COLOUR; + os_set_colour(h_default_foreground, h_default_background); + bkgdset(current_color); + erase(); + /*os_erase_area(1, 1, h_screen_rows, h_screen_cols);*/ + } + else + if (h_flags & COLOUR_FLAG) h_flags &= ~COLOUR_FLAG; +#endif + + refresh(); + +}/* os_init_screen */ + +/* + * os_reset_screen + * + * Reset the screen before the program stops. + * + */ + +void os_reset_screen (void) +{ + + os_set_text_style(0); + os_display_string((zchar *) "[Hit any key to exit.]"); + os_read_key(0, FALSE); + scrollok(stdscr, TRUE); scroll(stdscr); + refresh(); endwin(); + +}/* os_reset_screen */ + +/* + * os_restart_game + * + * This routine allows the interface to interfere with the process of + * restarting a game at various stages: + * + * RESTART_BEGIN - restart has just begun + * RESTART_WPROP_SET - window properties have been initialised + * RESTART_END - restart is complete + * + */ + +void os_restart_game (int stage) +{ +} + +/* + * os_random_seed + * + * Return an appropriate random seed value in the range from 0 to + * 32767, possibly by using the current system time. + * + */ + +int os_random_seed (void) +{ + + if (user_random_seed == -1) + /* Use the epoch as seed value */ + return (time(0) & 0x7fff); + else return user_random_seed; + +}/* os_random_seed */ diff --git a/ux_input.c b/ux_input.c new file mode 100644 index 0000000..3676162 --- /dev/null +++ b/ux_input.c @@ -0,0 +1,489 @@ +/* + * ux_input.c + * + * Unix interface, input functions + * + */ + +#define __UNIX_PORT_FILE + +#include +#include +#include + +#include + +#ifdef USE_NCURSES_H +#include +#else +#include +#endif + +#include "frotz.h" +#include "ux_frotz.h" + +static struct timeval global_timeout; + +#define MAX_HISTORY 20 +static char *history_buffer[MAX_HISTORY]; +static short history_pointer = 0; /* Pointer to next available slot */ +static short history_frame = 0; /* Pointer to what the user's looking at */ + +extern bool is_terminator (zchar); +extern void read_string (int, zchar *); +extern int completion (const zchar *, zchar *); + +/* + * unix_set_global_timeout + * + * This sets up a time structure to determine when unix_read_char should + * return zero (representing input timeout). When current system time + * equals global_timeout, boom. + * + */ + +static void unix_set_global_timeout(int timeout) +{ + if (!timeout) global_timeout.tv_sec = 0; + else { + gettimeofday(&global_timeout, NULL); + global_timeout.tv_sec += (timeout/10); + global_timeout.tv_usec += ((timeout%10)*100000); + if (global_timeout.tv_usec > 999999) { + global_timeout.tv_sec++; + global_timeout.tv_usec -= 1000000; + } + } +} + +/* + * unix_read_char + * + * This uses the curses getch() routine to get the next character typed, + * and returns it unless the global timeout is reached. It returns values + * which the standard considers to be legal input, and also returns editing + * and frotz hot keys. If called with a non-zero flag, it will also return + * line-editing keys like INSERT, etc, + * + */ + +static int unix_read_char(int flag) +{ + int c; + struct timeval ctime; + + while(1) { + /* Timed keyboard input. Crude but functional. */ + if (global_timeout.tv_sec) { + nodelay(stdscr, TRUE); + do { + c = getch(); + if (c == ERR) { + gettimeofday(&ctime, NULL); + if ((ctime.tv_sec >= global_timeout.tv_sec) && + (ctime.tv_usec >= global_timeout.tv_usec)) { + nodelay(stdscr, FALSE); + return 0; + } + } + } while (c == ERR); + nodelay(stdscr, FALSE); + } + /* The easy way. */ + else c = getch(); + + /* Catch 98% of all input right here... */ + if ( ((c >= 32) && (c <= 126)) || (c == 13) || (c == 8)) + return c; + + /* ...and the other 2% makes up 98% of the code. :( */ + switch(c) { + /* Lucian P. Smith reports KEY_ENTER on Irix 5.3. 10 has never + been reported, but I'm leaving it in just in case. */ + case 10: case KEY_ENTER: return 13; + /* I've seen KEY_BACKSPACE returned on some terminals. */ + case KEY_BACKSPACE: return 8; + /* On seven-bit connections, "Alt-Foo" is returned as an escape + followed by the ASCII value of the letter. We have to decide + here whether to return a single escape or a frotz hot key. */ + case 27: nodelay(stdscr, TRUE); c = getch(); nodelay(stdscr, FALSE); + if (c == ERR) return 27; + switch(c) { + case 112: return ZC_HKEY_PLAYBACK; /* Alt-P */ + case 114: return ZC_HKEY_RECORD; /* Alt-R */ + case 115: return ZC_HKEY_SEED; /* Alt-S */ + case 117: return ZC_HKEY_UNDO; /* Alt-U */ + case 110: return ZC_HKEY_RESTART; /* Alt-N */ + case 120: return ZC_HKEY_QUIT; /* Alt-X */ + case 100: return ZC_HKEY_DEBUG; /* Alt-D */ + case 104: return ZC_HKEY_HELP; /* Alt-H */ + default: return 27; + } + break; + /* The standard function key block. */ + case KEY_UP: return 129; + case KEY_DOWN: return 130; + case KEY_LEFT: return 131; + case KEY_RIGHT: return 132; + case KEY_F(1): return 133; case KEY_F(2): return 134; + case KEY_F(3): return 135; case KEY_F(4): return 136; + case KEY_F(5): return 137; case KEY_F(6): return 138; + case KEY_F(7): return 139; case KEY_F(8): return 140; + case KEY_F(9): return 141; case KEY_F(10): return 142; + case KEY_F(11): return 143; case KEY_F(12): return 144; + /* Curses can't differentiate keypad numbers from cursor keys. Damn. */ + /* This will catch the alt-keys on 8-bit clean input streams... */ + case 240: return ZC_HKEY_PLAYBACK; /* Alt-P */ + case 242: return ZC_HKEY_RECORD; /* Alt-R */ + case 243: return ZC_HKEY_SEED; /* Alt-S */ + case 245: return ZC_HKEY_UNDO; /* Alt-U */ + case 238: return ZC_HKEY_RESTART; /* Alt-N */ + case 248: return ZC_HKEY_QUIT; /* Alt-X */ + case 228: return ZC_HKEY_DEBUG; /* Alt-D */ + case 232: return ZC_HKEY_HELP; /* Alt-H */ +#ifdef EMACS_EDITING + case 21: return 27; /* Ctrl-U, erase line */ + case 2: return 131; /* Ctrl-B, left arrow */ + case 6: return 132; /* Ctrl-F, right arrow */ + case 16: return 129; /* Ctrl-P, up arrow */ + case 14: return 130; /* Ctrl-N, down arrow */ + case 1: c = KEY_HOME; break; /* Ctrl-A */ + case 4: c = 127; break; /* Ctrl-D */ + case 5: c = KEY_END; break; /* Ctrl-E */ +#endif + default: break; /* Who knows? */ + } + + /* Finally, if we're in full line mode (os_read_line), we might return + codes which aren't legal Z-machine keys but are used by the editor. */ + if (flag) return c; + } +} + + +/* + * unix_add_to_history + * + * Add the given string to the next available history buffer slot. Commandeer + * that slot if necessary using realloc. + * + */ + +static void unix_add_to_history(zchar *str) +{ + + if (history_buffer[history_pointer] == NULL) + history_buffer[history_pointer] = (char *) malloc(strlen(str) + 1); + else + history_buffer[history_pointer] = + (char *) realloc(history_buffer[history_pointer], strlen(str) + 1); + strcpy(history_buffer[history_pointer], str); + history_pointer = ((history_pointer + 1) % MAX_HISTORY); + history_frame = history_pointer; /* Reset user frame after each line */ +} + +/* + * unix_history_back + * + * Copy last available string to str, if possible. Return 1 if successful. + * + */ + +static int unix_history_back(zchar *str) +{ + + history_frame--; if (history_frame==-1) history_frame = (MAX_HISTORY - 1); + if ((history_frame == history_pointer) || + (history_buffer[history_frame] == NULL)) { + beep(); history_frame = (history_frame + 1) % MAX_HISTORY; + return 0; + } + strcpy(str, history_buffer[history_frame]); + return 1; + +} + +/* + * unix_history_forward + * + * Opposite of unix_history_back, and works in the same way. + * + */ + +static int unix_history_forward(zchar *str) +{ + + history_frame = (history_frame + 1) % MAX_HISTORY; + if ((history_frame == history_pointer) || + (history_buffer[history_frame] == NULL)) { + beep(); history_frame--; if (history_frame == -1) history_frame = + (MAX_HISTORY - 1); + return 0; + } + strcpy(str, history_buffer[history_frame]); + return 1; + +} + +/* + * os_read_line + * + * Read a line of input from the keyboard into a buffer. The buffer + * may already be primed with some text. In this case, the "initial" + * text is already displayed on the screen. After the input action + * is complete, the function returns with the terminating key value. + * The length of the input should not exceed "max" characters plus + * an extra 0 terminator. + * + * Terminating keys are the return key (13) and all function keys + * (see the Specification of the Z-machine) which are accepted by + * the is_terminator function. Mouse clicks behave like function + * keys except that the mouse position is stored in global variables + * "mouse_x" and "mouse_y" (top left coordinates are (1,1)). + * + * Furthermore, Frotz introduces some special terminating keys: + * + * ZC_HKEY_KEY_PLAYBACK (Alt-P) + * ZC_HKEY_RECORD (Alt-R) + * ZC_HKEY_SEED (Alt-S) + * ZC_HKEY_UNDO (Alt-U) + * ZC_HKEY_RESTART (Alt-N, "new game") + * ZC_HKEY_QUIT (Alt-X, "exit game") + * ZC_HKEY_DEBUGGING (Alt-D) + * ZC_HKEY_HELP (Alt-H) + * + * If the timeout argument is not zero, the input gets interrupted + * after timeout/10 seconds (and the return value is 0). + * + * The complete input line including the cursor must fit in "width" + * screen units. + * + * The function may be called once again to continue after timeouts, + * misplaced mouse clicks or hot keys. In this case the "continued" + * flag will be set. This information can be useful if the interface + * implements input line history. + * + * The screen is not scrolled after the return key was pressed. The + * cursor is at the end of the input line when the function returns. + * + * Since Inform 2.2 the helper function "completion" can be called + * to implement word completion (similar to tcsh under Unix). + * + */ + +zchar os_read_line (int max, zchar *buf, int timeout, int width, int continued) +{ + int ch, scrpos, pos, y, x; + char insert_flag = 1; /* Insert mode is now default */ + + scrpos = pos = strlen((char *) buf); + + if (!continued) + history_frame = history_pointer; /* Reset user's history view */ + + unix_set_global_timeout(timeout); + + getyx(stdscr, y, x); + + do { + refresh(); /* Shouldn't be necessary, but is, to print spaces */ + + ch = unix_read_char(1); + + /* Backspace */ + if ((ch == 8) && (scrpos)) { + mvdelch(y, --x); + pos--; scrpos--; + if (scrpos != pos) + memmove(&(buf[scrpos]), &(buf[scrpos+1]), pos-scrpos); + } + + /* Delete */ + if (((ch == 127) || (ch == KEY_DC)) && (scrpos < pos)) { + delch(); pos--; + memmove(&(buf[scrpos]), &(buf[scrpos+1]), pos-scrpos); + } + + /* Left key */ + if ((ch == 131) && (scrpos)) { + scrpos--; + move(y, --x); + } + /* Right key */ + if ((ch == 132) && (scrpos < pos)) { + scrpos++; + move(y, ++x); + } + + /* Home */ + if (ch == KEY_HOME) { + x -= scrpos; scrpos = 0; + move(y, x); + } + /* End */ + if (ch == KEY_END) { + x += (pos - scrpos); scrpos = pos; + move(y,x); + } + + /* Insert */ + if (ch == KEY_IC) + insert_flag = (insert_flag ? 0 : 1); + + /* Up and down keys */ + if (ch == 129) { + if (unix_history_back(buf)) { + x -= scrpos; + move(y, x); + while (scrpos) {addch(' '); scrpos--;} + move(y, x); + addstr(buf); + scrpos = pos = strlen(buf); + x += scrpos; + } + } + if (ch == 130) { + if (unix_history_forward(buf)) { + x -= scrpos; + move(y, x); + while(scrpos) {addch(' '); scrpos--;} + move(y, x); + addstr(buf); + scrpos = pos = strlen(buf); + x += scrpos; + } + } + + /* Page up/down (passthrough as up/down arrows for beyond zork) */ + if (ch == KEY_PPAGE) ch = 129; + if (ch == KEY_NPAGE) ch = 130; + + /* Escape */ + if (ch == 27) { + /* Move cursor to end of buffer first */ + x += (pos - scrpos); scrpos = pos; move(y,x); + x -= scrpos; + move(y, x); + while (scrpos) {addch(' '); scrpos--;} + move(y, x); pos = 0; + } + + /* Tab */ + if ((ch == 9) && (scrpos == pos)) { + int status; + zchar extension[10]; + + status = completion(buf, extension); + if (status != 2) { + addstr(extension); + strcpy(&buf[pos], extension); + pos += strlen(extension); scrpos += strlen(extension); + } + if (status) beep(); + } + + /* ASCII printable */ + if ((ch >= 32) && (ch <= 126)) { + if (pos == scrpos) { + /* Append to end of buffer */ + if ((pos < max) && (pos < width)) { + buf[pos++] = (char) ch; + addch(ch); + scrpos++; x++; + } else beep(); + } + else { + /* Insert/overwrite in middle of buffer */ + if (insert_flag) { + memmove(&buf[scrpos+1], &buf[scrpos], pos-scrpos); + buf[scrpos++] = (char) ch; + insch(ch); + pos++; x++; move(y, x); + } else { + buf[scrpos++] = (char) ch; + addch(ch); + x++; + } + } + } + } while (!is_terminator(ch)); + + buf[pos] = '\0'; + if (ch == 13) unix_add_to_history(buf); + return ch; + +}/* os_read_line */ + +/* + * os_read_key + * + * Read a single character from the keyboard (or a mouse click) and + * return it. Input aborts after timeout/10 seconds. + * + */ + +zchar os_read_key (int timeout, int cursor) +{ + zchar c; + + refresh(); + if (!cursor) curs_set(0); + + unix_set_global_timeout(timeout); + c = (zchar) unix_read_char(0); + + if (!cursor) curs_set(1); + return c; + +}/* os_read_key */ + +/* + * os_read_file_name + * + * Return the name of a file. Flag can be one of: + * + * FILE_SAVE - Save game file + * FILE_RESTORE - Restore game file + * FILE_SCRIPT - Transscript file + * FILE_RECORD - Command file for recording + * FILE_PLAYBACK - Command file for playback + * FILE_SAVE_AUX - Save auxilary ("preferred settings") file + * FILE_LOAD_AUX - Load auxilary ("preferred settings") file + * + * The length of the file name is limited by MAX_FILE_NAME. Ideally + * an interpreter should open a file requester to ask for the file + * name. If it is unable to do that then this function should call + * print_string and read_string to ask for a file name. + * + */ + +int os_read_file_name (char *file_name, const char *default_name, int flag) +{ + + int saved_replay = istream_replay; + int saved_record = ostream_record; + + /* Turn off playback and recording temporarily */ + + istream_replay = 0; + ostream_record = 0; + + print_string ("Enter a file name.\nDefault is \""); + print_string (default_name); + print_string ("\": "); + + read_string (MAX_FILE_NAME, file_name); + + /* Use the default name if nothing was typed */ + + if (file_name[0] == 0) + strcpy (file_name, default_name); + + /* Restore state of playback and recording */ + + istream_replay = saved_replay; + ostream_record = saved_record; + + return 1; + +} /* os_read_file_name */ diff --git a/ux_pic.c b/ux_pic.c new file mode 100644 index 0000000..39e343b --- /dev/null +++ b/ux_pic.c @@ -0,0 +1,61 @@ +/* + * ux_pic.c + * + * Unix interface, stubs for picture functions + * + */ + +/* + * os_draw_picture + * + * Display a picture at the given coordinates. Top left is (1,1). + * + */ + +void os_draw_picture (int picture, int y, int x) +{ + + /* Not Implemented */ + +}/* os_draw_picture */ + +/* + * os_peek_colour + * + * Return the colour of the pixel below the cursor. This is used + * by V6 games to print text on top of pictures. The coulor need + * not be in the standard set of Z-machine colours. To handle + * this situation, Frotz extends the colour scheme: Values above + * 15 (and below 256) may be used by the interface to refer to + * non-standard colours. Of course, os_set_colour must be able to + * deal with these colours. Interfaces which refer to characters + * instead of pixels might return the current background colour + * instead. + * + */ + +int os_peek_colour (void) +{ + + /* Not Implemented */ + return 0; + +}/* os_peek_colour */ + +/* + * os_picture_data + * + * Return true if the given picture is available. If so, write the + * width and height of the picture into the appropriate variables. + * Only when picture 0 is asked for, write the number of available + * pictures and the release number instead. + * + */ + +int os_picture_data (int picture, int *height, int *width) +{ + + /* Not implemented */ + return 0; + +}/* os_picture_data */ diff --git a/ux_sample.c b/ux_sample.c new file mode 100644 index 0000000..1fa1871 --- /dev/null +++ b/ux_sample.c @@ -0,0 +1,107 @@ +/* + * ux_sample.c + * + * Unix interface, sound support + * + */ + +#define __UNIX_PORT_FILE + +#ifdef USE_NCURSES_H +#include +#else +#include +#endif + +#include "frotz.h" +#include "ux_frotz.h" + +/* + * os_beep + * + * Play a beep sound. Ideally, the sound should be high- (number == 1) + * or low-pitched (number == 2). + * + */ + +void os_beep (int number) +{ + + beep(); + +}/* os_beep */ + +/* + * os_prepare_sample + * + * Load the sample from the disk. + * + */ + +void os_prepare_sample (int number) +{ + + /* Not implemented */ + +}/* os_prepare_sample */ + +/* + * os_start_sample + * + * Play the given sample at the given volume (ranging from 1 to 8 and + * 255 meaning a default volume). The sound is played once or several + * times in the background (255 meaning forever). In Z-code 3 the + * repeats value is always 0 and the number of repeats is taken from + * the sound file itself. The end_of_sound function is called as soon + * as the sound finishes. + * + */ + +void os_start_sample (int number, int volume, int repeats) +{ + + /* Not implemented */ + +}/* os_start_sample */ + +/* + * os_stop_sample + * + * Turn off the current sample. + * + */ + +void os_stop_sample (void) +{ + + /* Not implemented */ + +}/* os_stop_sample */ + +/* + * os_finish_with_sample + * + * Remove the current sample from memory (if any). + * + */ + +void os_finish_with_sample (void) +{ + + /* Not implemented */ + +}/* os_finish_with_sample */ + +/* + * os_wait_sample + * + * Stop repeating the current sample and wait until it finishes. + * + */ + +void os_wait_sample (void) +{ + + /* Not implemented */ + +}/* os_wait_sample */ diff --git a/ux_screen.c b/ux_screen.c new file mode 100644 index 0000000..879cd28 --- /dev/null +++ b/ux_screen.c @@ -0,0 +1,101 @@ +/* + * ux_screen.c + * + * Unix interface, screen manipulation + * + */ + +#define __UNIX_PORT_FILE + +#include +#include +#include + +#ifdef USE_NCURSES_H +#include +#else +#include +#endif + +#include "frotz.h" +#include "ux_frotz.h" + +/* + * os_erase_area + * + * Fill a rectangular area of the screen with the current background + * colour. Top left coordinates are (1,1). The cursor does not move. + * + */ + +void os_erase_area (int top, int left, int bottom, int right) +{ + int y, x, i, j; + int saved_style; + + /* Catch the most common situation and do things the easy way */ + if ((top == 1) && (bottom == h_screen_rows) && + (left == 1) && (right == h_screen_cols)) + erase(); + else { + /* Sigh... */ + getyx(stdscr, y, x); + saved_style = current_text_style; + os_set_text_style(0); + top--; left--; bottom--; right--; + for (i = top; i <= bottom; i++) { + move(i, left); + for (j = left; j <= right; j++) + addch(' '); + } + + os_set_text_style(saved_style); + move(y, x); + } + + refresh(); + +}/* os_erase_area */ + +/* + * os_scroll_area + * + * Scroll a rectangular area of the screen up (units > 0) or down + * (units < 0) and fill the empty space with the current background + * colour. Top left coordinates are (1,1). The cursor stays put. + * + */ + +static int old_scroll_top = 0; +static int old_scroll_bottom = 0; + +void os_scroll_area (int top, int left, int bottom, int right, int units) +{ +#ifdef COLOR_SUPPORT + int y, x, i; + int saved_style; +#endif + + if (units != 1) os_fatal("Can't Happen (os_scroll_area)"); /* FIXME */ + + if (!((old_scroll_top == top) && (old_scroll_bottom == bottom))) { + old_scroll_top = top; old_scroll_bottom = bottom; + setscrreg(--top, --bottom); + } + scrollok(stdscr, TRUE); + scroll(stdscr); + scrollok(stdscr, FALSE); + +#ifdef COLOR_SUPPORT + if (h_flags & COLOUR_FLAG) { + getyx(stdscr, y, x); + move(old_scroll_bottom, 0); + saved_style = current_text_style; + os_set_text_style(0); + for (i = 0; i <= right; i++) addch(' '); + os_set_text_style(saved_style); + move(y, x); + } +#endif + +}/* os_scroll_area */ diff --git a/ux_text.c b/ux_text.c new file mode 100644 index 0000000..ccb933c --- /dev/null +++ b/ux_text.c @@ -0,0 +1,354 @@ +/* + * ux_text.c + * + * Unix interface, text functions + * + */ + +#define __UNIX_PORT_FILE + +#include +#include +#include + +#ifdef USE_NCURSES_H +#include +#else +#include +#endif + +#include "frotz.h" +#include "ux_frotz.h" + +#ifdef COLOR_SUPPORT +int current_color = 0; +#endif + +static char latin1_to_ascii[] = + " ! c L >o> 1/41/23/4? " + "A A A A Ae A AE C E E E E I I I I " + "Th N O O O O Oe * O U U U Ue Y Th ss " + "a a a a ae a ae c e e e e i i i i " + "th n o o o o oe : o u u u ue y th y "; +/* + * os_font_data + * + * Return true if the given font is available. The font can be + * + * TEXT_FONT + * PICTURE_FONT + * GRAPHICS_FONT + * FIXED_WIDTH_FONT + * + * The font size should be stored in "height" and "width". If + * the given font is unavailable then these values must _not_ + * be changed. + * + */ + +int os_font_data (int font, int *height, int *width) +{ + + if (font == TEXT_FONT) { + *height = 1; *width = 1; return 1; /* Truth in advertising */ + } + return 0; + +}/* os_font_data */ + +#ifdef COLOR_SUPPORT +/* + * unix_convert + * + * Converts frotz's (and Infocom's) color values to ncurses color values. + * + */ + +static int unix_convert(int color) +{ + switch(color) { + case BLACK_COLOUR: return COLOR_BLACK; + case RED_COLOUR: return COLOR_RED; + case GREEN_COLOUR: return COLOR_GREEN; + case YELLOW_COLOUR: return COLOR_YELLOW; + case BLUE_COLOUR: return COLOR_BLUE; + case MAGENTA_COLOUR: return COLOR_MAGENTA; + case CYAN_COLOUR: return COLOR_CYAN; + case WHITE_COLOUR: return COLOR_WHITE; + } + return 0; +} +#endif + +/* + * os_set_colour + * + * Set the foreground and background colours which can be: + * + * DEFAULT_COLOUR + * BLACK_COLOUR + * RED_COLOUR + * GREEN_COLOUR + * YELLOW_COLOUR + * BLUE_COLOUR + * MAGENTA_COLOUR + * CYAN_COLOUR + * WHITE_COLOUR + * + * MS-DOS 320 columns MCGA mode only: + * + * GREY_COLOUR + * + * Amiga only: + * + * LIGHTGREY_COLOUR + * MEDIUMGREY_COLOUR + * DARKGREY_COLOUR + * + * There may be more colours in the range from 16 to 255; see the + * remarks on os_peek_colour. + * + */ + +#ifdef COLOR_SUPPORT +static int colorspace[10][10]; +static int count = 0; +#endif + +void os_set_colour (int new_foreground, int new_background) +{ +#ifdef COLOR_SUPPORT + int saved_style; + + saved_style = current_text_style; + if (new_foreground == 1) new_foreground = h_default_foreground; + if (new_background == 1) new_background = h_default_background; + if (!colorspace[new_foreground][new_background]) { + init_pair(++count, unix_convert(new_foreground), unix_convert(new_background)); + colorspace[new_foreground][new_background] = count; + } + current_color = COLOR_PAIR(colorspace[new_foreground][new_background]); + os_set_text_style(saved_style); + +#endif +}/* os_set_colour */ + +/* + * os_set_text_style + * + * Set the current text style. Following flags can be set: + * + * REVERSE_STYLE + * BOLDFACE_STYLE + * EMPHASIS_STYLE (aka underline aka italics) + * FIXED_WIDTH_STYLE + * + */ + +void os_set_text_style (int new_style) +{ + int temp = 0; + + current_text_style = new_style; + if (new_style & REVERSE_STYLE) temp |= A_REVERSE; + if (new_style & BOLDFACE_STYLE) temp |= A_BOLD; + if (new_style & EMPHASIS_STYLE) temp |= A_UNDERLINE; +#ifdef COLOR_SUPPORT + attrset(temp | current_color); +#else + attrset(temp); +#endif + +}/* os_set_text_style */ + +/* + * os_set_font + * + * Set the font for text output. The interpreter takes care not to + * choose fonts which aren't supported by the interface. + * + */ + +void os_set_font (int new_font) +{ + + /* Not implemented */ + +}/* os_set_font */ + +/* + * os_display_char + * + * Display a character of the current font using the current colours and + * text style. The cursor moves to the next position. Printable codes are + * all ASCII values from 32 to 126, ISO Latin-1 characters from 160 to + * 255, ZC_GAP (gap between two sentences) and ZC_INDENT (paragraph + * indentation). The screen should not be scrolled after printing to the + * bottom right corner. + * + */ + +void os_display_char (zchar c) +{ + + if (c >= ZC_LATIN1_MIN && c <= ZC_LATIN1_MAX) { + if (unix_plain_ascii) { + + char *ptr = latin1_to_ascii + 3 * (c - ZC_LATIN1_MIN); + char c1 = *ptr++; + char c2 = *ptr++; + char c3 = *ptr++; + + addch(c1); + + if (c2 != ' ') + addch(c2); + if (c3 != ' ') + addch(c3); + + } else + addch(c); + return; + } + if (c >= 32 && c <= 126) { + addch(c); + return; + } + if (c == ZC_INDENT) { + addch(' '); addch(' '); addch(' '); + return; + } + if (c == ZC_GAP) { + addch(' '); addch(' '); + return; + } + +}/* os_display_char */ + +/* + * os_display_string + * + * Pass a string of characters to os_display_char. + * + */ + +void os_display_string (const zchar *s) +{ + + zchar c; + + while ((c = (unsigned char) *s++) != 0) + + if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE) { + + int arg = (unsigned char) *s++; + + if (c == ZC_NEW_FONT) + os_set_font (arg); + if (c == ZC_NEW_STYLE) + os_set_text_style (arg); + + } else os_display_char (c); + +}/* os_display_string */ + +/* + * os_char_width + * + * Return the width of the character in screen units. + * + */ + +int os_char_width (zchar c) +{ + + if (c >= ZC_LATIN1_MIN && c <= ZC_LATIN1_MAX && unix_plain_ascii) { + + int width = 0; + const char *ptr = latin1_to_ascii + 3 * (c - ZC_LATIN1_MIN); + char c1 = *ptr++; + char c2 = *ptr++; + char c3 = *ptr++; + + width++; + if (c2 != ' ') + width++; + if (c3 != ' ') + width++; + return width; + } + return 1; + +}/* os_char_width*/ + +/* + * os_string_width + * + * Calculate the length of a word in screen units. Apart from letters, + * the word may contain special codes: + * + * NEW_STYLE - next character is a new text style + * NEW_FONT - next character is a new font + * + */ + +int os_string_width (const zchar *s) +{ + int width = 0; + zchar c; + + while ((c = *s++) != 0) + + if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT) { + + s++; + /* No effect */ + + } else width += os_char_width(c); + + return width; + +}/* os_string_width */ + +/* + * os_set_cursor + * + * Place the text cursor at the given coordinates. Top left is (1,1). + * + */ + +void os_set_cursor (int y, int x) +{ + + /* Curses thinks the top left is (0,0) */ + move(--y, --x); + +}/* os_set_cursor */ + +/* + * os_more_prompt + * + * Display a MORE prompt, wait for a keypress and remove the MORE + * prompt from the screen. + * + */ + +void os_more_prompt (void) +{ + int saved_style, saved_x, saved_y; + + /* Save some useful information */ + saved_style = current_text_style; + getyx(stdscr, saved_y, saved_x); + + os_set_text_style(0); + addstr("[MORE]"); + os_read_key(0, TRUE); + + move(saved_y, saved_x); + addstr(" "); + move(saved_y, saved_x); + os_set_text_style(saved_style); + +}/* os_more_prompt */