/*
 * osdepend.c
 *
 * All non screen specific operating system dependent routines.
 *
 * Olaf Barthel 28-Jul-1992
 *
 */

#include "ztypes.h"
#include "arguments.h"
#include "version.h"

/* File names will be O/S dependent */

#if defined(AMIGA)
#define SAVE_NAME   "Story.Save"   /* Default save name */
#define SCRIPT_NAME "PRT:"         /* Default script name */
#define RECORD_NAME "Story.Record" /* Default record name */
#else /* defined(AMIGA) */
#define SAVE_NAME   "story.sav"   /* Default save name */
#define SCRIPT_NAME "story.lis"   /* Default script name */
#define RECORD_NAME "record.lis"  /* Default record name */
#endif /* defined(AMIGA) */

static optionname optionnamelist[] = {
    {"bindings", OPT_BINDINGS, TRUE},
    {"strictz", OPT_STRICTZ, TRUE},
    {"spec", OPT_SPEC, TRUE},
    {"geometry", OPT_GEOMETRY, TRUE},
    {"statgeometry", OPT_STATGEOMETRY, TRUE},
    {"inputstyle", OPT_INPUTSTYLE, TRUE},
    {"marginx", OPT_MARGINX, TRUE},
    {"leading", OPT_LEADING, TRUE},
    {"justify", OPT_JUSTIFY, TRUE},
    {"autoresize", OPT_AUTORESIZE, TRUE},
    {"resizeupward", OPT_RESIZEUPWARD, TRUE},
    {"autoclear", OPT_AUTOCLEAR, TRUE},
    {"history", OPT_HISTORY, TRUE},
    {"buffer", OPT_BUFFER, TRUE},
    {"background", OPT_BACKGROUND, TRUE},
    {"foreground", OPT_FOREGROUND, TRUE},
    {"bg", OPT_BACKGROUND, FALSE},
    {"fg", OPT_FOREGROUND, FALSE},
    {"greycolor", OPT_GREYCOLOR, TRUE},

    {"n-color", OPT_STYLECOLOR+0, TRUE},
    {"r-color", OPT_STYLECOLOR+1, TRUE},
    {"b-color", OPT_STYLECOLOR+2, TRUE},
    {"rb-color", OPT_STYLECOLOR+3, TRUE},
    {"i-color", OPT_STYLECOLOR+4, TRUE},
    {"ri-color", OPT_STYLECOLOR+5, TRUE},
    {"bi-color", OPT_STYLECOLOR+6, TRUE},
    {"rbi-color", OPT_STYLECOLOR+7, TRUE},
    {"f-color", OPT_STYLECOLOR+8, TRUE},
    {"rf-color", OPT_STYLECOLOR+9, TRUE},
    {"bf-color", OPT_STYLECOLOR+10, TRUE},
    {"rbf-color", OPT_STYLECOLOR+11, TRUE},
    {"if-color", OPT_STYLECOLOR+12, TRUE},
    {"rif-color", OPT_STYLECOLOR+13, TRUE},
    {"bif-color", OPT_STYLECOLOR+14, TRUE},
    {"rbif-color", OPT_STYLECOLOR+15, TRUE},

    {"n-font", OPT_STYLEFONT+0, TRUE},
    {"r-font", OPT_STYLEFONT+1, TRUE},
    {"b-font", OPT_STYLEFONT+2, TRUE},
    {"rb-font", OPT_STYLEFONT+3, TRUE},
    {"i-font", OPT_STYLEFONT+4, TRUE},
    {"ri-font", OPT_STYLEFONT+5, TRUE},
    {"bi-font", OPT_STYLEFONT+6, TRUE},
    {"rbi-font", OPT_STYLEFONT+7, TRUE},
    {"f-font", OPT_STYLEFONT+8, TRUE},
    {"rf-font", OPT_STYLEFONT+9, TRUE},
    {"bf-font", OPT_STYLEFONT+10, TRUE},
    {"rbf-font", OPT_STYLEFONT+11, TRUE},
    {"if-font", OPT_STYLEFONT+12, TRUE},
    {"rif-font", OPT_STYLEFONT+13, TRUE},
    {"bif-font", OPT_STYLEFONT+14, TRUE},
    {"rbif-font", OPT_STYLEFONT+15, TRUE},

    {"help", OPT_HELP, TRUE},

    {NULL, 0, FALSE}
};

unixoption unixoptionlist[NUMOPTIONS];

int strictz_declare_spec;

#ifdef STRICTZ

/* Define stuff for stricter Z-code error checking, for the generic
   Unix/DOS/etc terminal-window interface. Feel free to change the way
   player prefs are specified, or replace report_zstrict_error() 
   completely if you want to change the way errors are reported. */

/* There are four error reporting modes: never report errors;
  report only the first time a given error type occurs; report
  every time an error occurs; or treat all errors as fatal
  errors, killing the interpreter. I strongly recommend
  "report once" as the default. But you can compile in a
  different default by changing the definition of
  STRICTZ_DEFAULT_REPORT_MODE. In any case, the player can
  specify a report mode on the command line by typing "-s 0"
  through "-s 3". */

int strictz_report_mode;
static int strictz_error_count[STRICTZ_NUM_ERRORS];

#endif /* STRICTZ */


#if !defined(AMIGA)

/*
 * process_arguments
 *
 * Do any argument preprocessing necessary before the game is
 * started. This may include selecting a specific game file or
 * setting interface-specific options.
 *
 */

#ifdef __STDC__
void process_arguments (int argc, char *argv[])
#else
void process_arguments (argc, argv)
int argc;
char *argv[];
#endif
{
    int c, errflg = 0;
    int num;
    char *gamename = NULL;

    {
      zword_t zw;
      int little_endian=0;

      /* Find out if we are big-endian */
      zw=0x0001;
      if ( *(zbyte_t *)&zw ) {
	little_endian=1;
      }

#if defined(BIG_END_MODE) || defined(LITTLE_END_MODE)

#ifdef BIG_END_MODE
      if (little_endian) {
	fprintf (stderr, "%s: On this machine, this program must be compiled with LITTLE_END_MODE defined! Adjust the Makefile and recompile.\n", PROGRAMNAME);
	exit(EXIT_FAILURE);
      }
#else
      if (!little_endian) {
	fprintf (stderr, "%s: On this machine, this program must be compiled with BIG_END_MODE defined! Adjust the Makefile and recompile.\n", PROGRAMNAME);
	exit(EXIT_FAILURE);
      }
#endif

#else
      fprintf (stderr, "%s: This program must be compiled with either BIG_END_MODE or LITTLE_END_MODE defined! Adjust the Makefile and recompile.\n", PROGRAMNAME);
      exit(EXIT_FAILURE);

#endif
    }

#ifdef STRICTZ
    /* Initialize the STRICTZ variables. */
    strictz_report_mode = STRICTZ_DEFAULT_REPORT_MODE;
    for (num=0; num<STRICTZ_NUM_ERRORS; num++) {
        strictz_error_count[num] = 0;
    }
#endif /* STRICTZ */
 
    /* Parse the options */
    /* this is considerably changed --zarf */

    num = 0;
    for (c=0; c<NUMOPTIONS; c++) {
	unixoptionlist[c].val = NULL;
	unixoptionlist[c].name = NULL;
    }
    for (c=0; optionnamelist[c].name; c++) {
	if (optionnamelist[c].canonical) {
	    unixoptionlist[optionnamelist[c].key].name = optionnamelist[c].name;
	}
    }

    errflg = 0;
    for (c=1; !errflg && c<argc; c++) {
	if (argv[c][0]=='-') {
	    /* ### if I were clever, this would be a binary search */
	    for (num=0; optionnamelist[num].name; num++) {
		if (!strcmp(optionnamelist[num].name, argv[c]+1)) {
		    break;
		}
	    }
	    if (optionnamelist[num].name) {
		num = optionnamelist[num].key;
		if (num==OPT_HELP) {
		    errflg = 2;
		}
		else {
		    if (c+1 >= argc) {
			fprintf(stderr, "%s: option %s requires a value to follow it\n", PROGRAMNAME, argv[c]);
			errflg = 1;
		    }
		    else {
			c++;
			unixoptionlist[num].val = argv[c];
		    }
		}
	    }
	    else {
		fprintf(stderr, "%s: unknown option: %s\n", PROGRAMNAME, argv[c]);
		errflg = 1;
	    }
	}
	else {
	    if (!gamename)
		gamename = argv[c];
	    else {
		fprintf(stderr, "%s: unknown option after story-file: %s\n", PROGRAMNAME, argv[c]);
		errflg = 1;
	    }
	}
    }

    /* Display usage */

    if (errflg || !gamename) {
        fprintf (stderr, "usage: %s [options...] story-file\n\n", argv[0]);
	fprintf (stderr, "XZip - a Z-machine interpreter.\nVersion %s by Andrew Plotkin (erkyrath@eblong.com).\n", XZIPVERSION);
        fprintf (stderr, "Based on ZIP Version 2.0 by Mark Howell.\n");
        fprintf (stderr, "Plays Z-machine files of versions 1-5 and version 8.\n\n");
	fprintf (stderr, "\t-help: display full list of options\n");
	if (errflg==2) {
	    fprintf(stderr, "\n");
	    for (c=0; c<NUM_SIMPLE_OPTIONS; c++) {
		if (unixoptionlist[c].name)
		    fprintf(stderr, "\t-%s\n", unixoptionlist[c].name);
	    }
	    /* sue me, this is hardwired */
	    fprintf(stderr, "\t-%s\n", "XXX-color");
	    fprintf(stderr, "\t-%s\n", "XXX-font");
	    fprintf(stderr, "\t(for XXX in n, r, b, rb, i, ri, bi, rbi, f, rf, bf, rbf, if, rif, bif, rbif)\n");
	}
	exit (EXIT_FAILURE);
    }

    /* Open the story file */

    open_story (gamename);

}/* process_arguments */

#endif /* !defined(AMIGA) */

#if !defined(AMIGA)

/*
 * file_cleanup
 *
 * Perform actions when a file is successfully closed. Flag can be one of:
 * GAME_SAVE, GAME_RESTORE, GAME_SCRIPT.
 *
 */

#ifdef __STDC__
void file_cleanup (const char *file_name, int flag)
#else
void file_cleanup (file_name, flag)
const char *file_name;
int flag;
#endif
{

}/* file_cleanup */

#endif /* !defined(AMIGA) */

#if !defined(AMIGA)

/*
 * sound
 *
 * Play a sound file or a note.
 *
 * argc = 1: argv[0] = note# (range 1 - 3)
 *
 *           Play note.
 *
 * argc = 2: argv[0] = 0
 *           argv[1] = 3
 *
 *           Stop playing current sound.
 *
 * argc = 2: argv[0] = 0
 *           argv[1] = 4
 *
 *           Free allocated resources.
 *
 * argc = 3: argv[0] = ID# of sound file to replay.
 *           argv[1] = 2
 *           argv[2] = Volume to replay sound with, this value
 *                     can range between 1 and 8.
 *
 * argc = 4: argv[0] = ID# of sound file to replay.
 *           argv[1] = 2
 *           argv[2] = Control information
 *           argv[3] = Volume information
 *
 *           Volume information:
 *
 *               0x34FB -> Fade sound in
 *               0x3507 -> Fade sound out
 *               other  -> Replay sound at maximum volume
 *
 *           Control information:
 *
 *               This word is divided into two bytes,
 *               the upper byte determines the number of
 *               cycles to play the sound (e.g. how many
 *               times a clock chimes or a dog barks).
 *               The meaning of the lower byte is yet to
 *               be discovered :)
 *
 */

#ifdef __STDC__
void sound (int argc, zword_t *argv)
#else
void sound (argc, argv)
int argc;
zword_t *argv;
#endif
{

    /* Supply default parameters */

    if (argc < 4)
        argv[3] = 0;
    if (argc < 3)
        argv[2] = 0xff;
    if (argc < 2)
        argv[1] = 2;

    /* Generic bell sounder */

    if (argc == 1 || argv[1] == 2) {
	xio_bell();    /* --zarf */
	/* display_char ('\007'); */
    }

}/* sound */

#endif /* !defined(AMIGA) */

/*
 * get_file_name
 *
 * Return the name of a file. Flag can be one of:
 *    GAME_SAVE    - Save file (write only)
 *    GAME_RESTORE - Save file (read only)
 *    GAME_SCRIPT  - Script file (write only)
 *    GAME_RECORD  - Keystroke record file (write only)
 *    GAME_PLABACK - Keystroke record file (read only)
 *
 */

#ifdef __STDC__
int get_file_name (char *file_name, char *default_name, int flag)
#else
int get_file_name (file_name, default_name, flag)
char *file_name;
char *default_name;
int flag;
#endif
{
    char buffer[127 + 2]; /* 127 is the biggest positive char */
    int status = 0;

    /* If no default file name then supply the standard name */

    if (default_name[0] == '\0') {
        if (flag == GAME_SCRIPT)
            strcpy (default_name, SCRIPT_NAME);
        else if (flag == GAME_RECORD || flag == GAME_PLAYBACK)
            strcpy (default_name, RECORD_NAME);
        else /* (flag == GAME_SAVE || flag == GAME_RESTORE) */
            strcpy (default_name, SAVE_NAME);
    }

    /* Prompt for the file name */

    output_line ("Enter a file name.");
    output_string ("(Default is \"");
    output_string (default_name);
    output_string ("\"): ");

    buffer[0] = 127;
    buffer[1] = 0;
    (void) get_line (buffer, 0, 0);

    /* Copy file name from the input buffer */

    if (h_type > V4) {
        unsigned char len = buffer[1];
        memcpy (file_name, &buffer[2], len);
        file_name[len] = '\0';
    } else
        strcpy (file_name, &buffer[1]);

    /* If nothing typed then use the default name */

    if (file_name[0] == '\0')
        strcpy (file_name, default_name);

#if !defined(VMS) /* VMS has file version numbers, so cannot overwrite */

    /* Check if we are going to overwrite the file */

    if (flag == GAME_SAVE || flag == GAME_SCRIPT || flag == GAME_RECORD) {
        FILE *tfp;
        char c;

        /* Try to access the file */

        tfp = fopen (file_name, "r");
        if (tfp != NULL) {
            fclose (tfp);

            /* If it succeeded then prompt to overwrite */

            output_line ("You are about to write over an existing file.");
            output_string ("Proceed? (Y/N) ");

            do {
                c = (char) input_character (0);
                c = (char) toupper (c);
            } while (c != 'Y' && c != 'N');

            output_char (c);
            output_new_line ();

            /* If no overwrite then fail the routine */

            if (c == 'N')
                status = 1;
        }
    }
#endif /* !defined(VMS) */

    /* Record the file name if it was OK */

    if (status == 0)
        record_line (file_name, strlen(file_name));

    return (status);

}/* get_file_name */

#if !defined(AMIGA)

/*
 * fatal
 *
 * Display message and stop interpreter.
 *
 */

#ifdef __STDC__
void fatal (const char *s)
#else
void fatal (s)
const char *s;
#endif
{

    reset_screen ();
    printf ("\nFatal error: %s (PC = %lx)\n", s, pc);
    exit (1);

}/* fatal */

#endif /* !defined(AMIGA) */

#if !defined(AMIGA)

/*
 * report_strictz_error
 *
 * This handles Z-code error conditions which ought to be fatal errors,
 * but which players might want to ignore for the sake of finishing the
 * game.
 *
 * The error is provided as both a numeric code and a string. This allows
 * us to print a warning the first time a particular error occurs, and
 * ignore it thereafter.
 *
 * errnum : Numeric code for error (0 to STRICTZ_NUM_ERRORS-1)
 * errstr : Text description of error
 *
 */

#ifdef STRICTZ

#ifdef __STDC__
void report_strictz_error (int errnum, const char *errstr)
#else /* __STDC__ */ 
void report_strictz_error (errnum, errstr)
int errnum;
const char *errstr;
#endif /* __STDC__ */ 
{
    int wasfirst;
    
    if (errnum <= 0 || errnum >= STRICTZ_NUM_ERRORS)
        return;
    
    if (strictz_report_mode == STRICTZ_REPORT_FATAL) {
        fatal(errstr);
        return;
    }

    wasfirst = (strictz_error_count[errnum] == 0);
    strictz_error_count[errnum]++;
    
    if ((strictz_report_mode == STRICTZ_REPORT_ALWAYS)
        || (strictz_report_mode == STRICTZ_REPORT_ONCE && wasfirst)) {
        char buf[256];
        sprintf(buf, "Warning: %s (PC = %lx)", errstr, pc);
        write_string(buf);
        
        if (strictz_report_mode == STRICTZ_REPORT_ONCE) {
            write_string(" (will ignore further occurrences)");
        }
        else {
            sprintf(buf, " (occurrence %d)", strictz_error_count[errnum]);
            write_string(buf);
        }
        new_line();
    }

} /* report_strictz_error */

#endif /* STRICTZ */

#endif /* !defined(AMIGA) */

#if !defined(AMIGA)

/*
 * fit_line
 *
 * This routine determines whether a line of text will still fit
 * on the screen.
 *
 * line : Line of text to test.
 * pos  : Length of text line (in characters).
 * max  : Maximum number of characters to fit on the screen.
 *
 */

#ifdef __STDC__
int fit_line (const char *line_buffer, int pos, int max)
#else
int fit_line (line_buffer, pos, max)
const char *line_buffer;
int pos;
int max;
#endif
{

    return (pos < max);

}/* fit_line */

#endif /* !defined(AMIGA) */

#if !defined(AMIGA)

/*
 * print_status
 *
 * Print the status line (type 3 games only).
 *
 * argv[0] : Location name
 * argv[1] : Moves/Time
 * argv[2] : Score
 *
 * Depending on how many arguments are passed to this routine
 * it is to print the status line. The rendering attributes
 * and the status line window will be have been activated
 * when this routine is called. It is to return FALSE if it
 * cannot render the status line in which case the interpreter
 * will use display_char() to render it on its own.
 *
 * This routine has been provided in order to support
 * proportional-spaced fonts.
 *
 */

#ifdef __STDC__
int print_status (int argc, char *argv[])
#else
int print_status (argc, argv)
int argc;
char *argv[];
#endif
{

    return (FALSE);

}/* print_status */

#endif /* !defined(AMIGA) */

#if !defined(AMIGA)

/*
 * set_font
 *
 * Set a new character font. Font can be either be:
 *
 *    TEXT_FONT (1)     = normal text character font
 *    GRAPHICS_FONT (3) = graphical character font
 *
 * Returns TRUE if succeeded, FALSE if failed.
 */

#ifdef __STDC__
int set_font (int font_type)
#else
int set_font (font_type)
int font_type;
#endif
{
    if (font_type == 1)
        return TRUE;
    else
        return FALSE;
}/* set_font */

#endif /* !defined(AMIGA) */

#if !defined(MSDOS) && !defined(AMIGA)

/*
 * set_colours
 *
 * Sets screen foreground and background colours.
 *
 */

#ifdef __STDC__
void set_colours (int foreground, int background)
#else
void set_colours (foreground, background)
int foreground;
int background;
#endif
{

}/* set_colours */

#endif /* !defined(MSDOS) && !defined(AMIGA) */

#if !defined(VMS) && !defined(MSDOS)

/*
 * codes_to_text
 *
 * Translate Z-code characters to machine specific characters. These characters
 * include line drawing characters and international characters.
 *
 * The routine takes one of the Z-code characters from the following table and
 * writes the machine specific text replacement. The target replacement buffer
 * is defined by MAX_TEXT_SIZE in ztypes.h. The replacement text should be in a
 * normal C, zero terminated, string.
 *
 * Return 0 if a translation was available, otherwise 1.
 *
 *  Arrow characters (0x18 - 0x1b):
 *
 *  0x18 Up arrow
 *  0x19 Down arrow
 *  0x1a Right arrow
 *  0x1b Left arrow
 *
 *  International characters (0x9b - 0xa3):
 *
 *  0x9b a umlaut (ae)
 *  0x9c o umlaut (oe)
 *  0x9d u umlaut (ue)
 *  0x9e A umlaut (Ae)
 *  0x9f O umlaut (Oe)
 *  0xa0 U umlaut (Ue)
 *  0xa1 sz (ss)
 *  0xa2 open quote (>>)
 *  0xa3 close quota (<<)
 *
 *  Line drawing characters (0xb3 - 0xda):
 *
 *  0xb3 vertical line (|)
 *  0xba double vertical line (#)
 *  0xc4 horizontal line (-)
 *  0xcd double horizontal line (=)
 *  all other are corner pieces (+)
 *
 */

#ifdef __STDC__
int codes_to_text (int c, char *s)
#else
int codes_to_text (c, s)
int c;
char *s;
#endif
{
    if (c >= 220 && c <= 221) {
        if (c == 220) {
	    s[0] = 'o';
	    s[1] = 'e';
	}
	else {
	    s[0] = 'O';
	    s[1] = 'E';
	}
	s[2] = '\0';
	return 0;
    }

    if (c >= 155 && c <= 223) {
        static char xlat[] = "\344\366\374\304\326\334\337\273\253\353\357\377\313\317\341\351\355\363\372\375\301\311\315\323\332\335\340\350\354\362\371\300\310\314\322\331\342\352\356\364\373\302\312\316\324\333\345\305\370\330\343\361\365\303\321\325\346\306\347\307\376\360\336\320\243oO\241\277";
	s[0] = xlat[c - 155];
	s[1] = '\0';
	return 0;
    }

    return 1;

}/* codes_to_text */

#endif /* !defined(VMS) && !defined(MSDOS) */
