/*
 * zip.c
 *
 * Z code interpreter main routine. Plays Infocom type 1-8 games.
 *
 * Disclaimer:
 *
 * You are expressly forbidden to use this program if in so doing you violate
 * the copyright notice supplied with the original Infocom game.
 *
 */

#include <stdio.h>
#include <string.h>
#include <setjmp.h>

#ifdef ALLOW_OPCOUNT
#include "opcodes.h"
#include <oslib/osfile.h>
#endif
#include <oslib/osfscontrol.h>

#include "options.h"
#include "ztypes.h"
#include "osdepend.h"
#include "memory.h"
#include "input.h"
#include "screenio.h"
#include "screen.h"
#include "interpre.h"
#include "fileio.h"
#include "text.h"
#include "control.h"
#include "roinput.h"
#include "v6.h"

extern int pauseexit;
//extern int __root_stack_size;
extern char StoryName[];

static void configure(void);

#ifdef PROFILING
extern void _fmapstore(char *filename);

static void dump_profile(void)
{
    _fmapstore("<Zip2000$Dir>.Profile");
}
#endif

#ifdef USING_GLOBAL_REGS
static struct zframe *jump_fp;
static unsigned *jump_sp;
static unsigned char *jump_pc;
#endif
static jmp_buf restorejb;

void restore_longjmp(void)
{
#ifdef USING_GLOBAL_REGS
    /* Oh boy. Because these are in global registers, they will be splatted by
     * the longjmp.
     */
    jump_fp = fp;
    jump_sp = sp;
    jump_pc = pc;
#endif
    roinput_abort();
    longjmp(restorejb, 1);
}

//int __root_stack_size = 2048;

/*
 * main
 *
 * Initialise environment, start interpreter, clean up.
 *
 */

int main(int argc, char *argv[])
{
    extern void pre_initialise_wimp(void);

    argc=argc;

    /* As these are in registers, need to zero them manual, like */
    pc = 0;
    fp = 0;
    sp = 0;

    #ifdef PROFILING
    atexit(dump_profile);
    #endif

    pre_initialise_wimp();

    osfscontrol_canonicalise_path(argv[1], StoryName, NULL, NULL, 1024);

    configure();

    initialize_screen();

    load_cache();

    load_extra_blorb();

    if (h_type==V6)
    	v6_prepare_gfx();

    z_restart();

    autostart_quetzal();

    if (setjmp(restorejb))
    {
#ifdef USING_GLOBAL_REGS
        fp = jump_fp;
        sp = jump_sp;
        pc = jump_pc;
#endif
    }

    interpret();

    if (pauseexit)
    {
    	z_new_line();
    	output_string(msgs_lookup_u("Press"));
    	input_character(0);
    }
    else
        /* Hack to refresh window(!) */
        z_set_window(STATUS_WINDOW);

#ifdef ALLOW_OPCOUNT
    {
        int i;
        int max, maxref;
        int t_0OP=0, t_1OP=0, t_2OP=0, t_VAR=0, t_EXT=0;
    	osfile_save_stamped("<Zip2000$Dir>.^.Opcount", osfile_TYPE_DATA,
    	    	    	    (byte *) opcount, (byte *) opcount + sizeof opcount);
    	z_erase_window(-1);
    	write_char(EMBED_FONT+4);

    	for (i=0; i<128; i++) t_2OP += opcount[i];
    	for (   ; i<176; i++) t_1OP += opcount[i];
    	for (   ; i<192; i++) t_0OP += opcount[i];
    	for (   ; i<256; i++) t_VAR += opcount[i];
    	for (   ; i<512; i++) t_EXT += opcount[i];

    	do {
        	max=maxref=0;
        	for (i=0; i<512; i++)
        	{
        	    if (opcount[i] > max)
        	    {
        	        maxref = i;
        	        max = opcount[i];
        	    }
        	}

    	        if (max)
    	        {
    	            i = maxref;
    	            if (opcount[i] < 10) write_zchar(' ');
    	            if (opcount[i] < 100) write_zchar(' ');
    	            if (opcount[i] < 1000) write_zchar(' ');
    	            if (opcount[i] < 10000) write_zchar(' ');
    	            if (opcount[i] < 100000) write_zchar(' ');
    	            if (opcount[i] < 1000000) write_zchar(' ');
    	            if (opcount[i] < 10000000) write_zchar(' ');
    	            if (opcount[i] < 100000000) write_zchar(' ');
    	            z_print_num_32(opcount[i]);
    	            write_string("  ");
    	            write_string(opclass(i));
    	            write_zchar(':');
    	            z_print_num(i);
    	            write_zchar(' ');
    	            if (i < 10) write_zchar(' ');
    	            if (i < 100) write_zchar(' ');
    	            if (opcodetbl[i])
    	    	        write_string(opcodetbl[i]);
    	    	    else
    	    	        write_string("???");
    	    	    z_new_line();
    	    	    opcount[i]=0;
    	        }
    	} while (max);
    	z_new_line();

    	write_string("2OP  "); z_print_num_32(t_2OP); z_new_line();
    	write_string("1OP  "); z_print_num_32(t_1OP); z_new_line();
    	write_string("0OP  "); z_print_num_32(t_0OP); z_new_line();
    	write_string("VAR  "); z_print_num_32(t_VAR); z_new_line();
    	write_string("EXT  "); z_print_num_32(t_EXT); z_new_line();

    	output_string(msgs_lookup_u("Press"));
    	input_character(0);
    }
#endif

    reset_screen ();

    return 0;

}/* main */

/*
 * configure
 *
 * Initialise global and type specific variables.
 *
 */

extern char StoryName[];

static void configure(void)
{
    zbyte_t datap[64];

    open_story(StoryName);
    read_from_story_file(datap, 0, sizeof datap);

    h_type = get_byte_priv(H_TYPE);

    if (h_type < V4)
    {
        property_mask = P3_MAX_PROPERTIES - 1;
        property_size_mask = 0xe0;
    }
    else
    {
        property_mask = P4_MAX_PROPERTIES - 1;
        property_size_mask = 0x3f;
    }

    if (h_type < V4)
    {
        story_shift = 1;
        size_shift = 1;
    }
    else if (h_type == V4 || h_type == V5)
    {
        story_shift = 2;
        size_shift = 2;
    }
    else if (h_type == V6 || h_type == V7)
    {
        story_shift=2;
        size_shift=3;
        h_routines_offset=get_word_priv(H_ROUTINES_OFFSET)*8;
        h_strings_offset=get_word_priv(H_STRINGS_OFFSET)*8;
    }
    else if (h_type == V8)
    {
        story_shift=3;
        size_shift=3;
    }

    /* We can treat V7 and V8 as V5 from now on */
    if (h_type == V7 || h_type == V8)
        h_type=V5;


    if (h_type < V1 || h_type > V6)
        fatal_lookup("BadVer");

    h_config = get_byte_priv(H_CONFIG);
    h_release = get_word_priv(H_RELEASE);
    //h_high_memory_offset = get_word_priv(H_HIGH_MEMORY_OFFSET);
    h_objects_offset = get_word_priv(H_OBJECTS_OFFSET);
    h_globals_offset = get_word_priv(H_GLOBALS_OFFSET);
    h_static_offset = get_word_priv(H_STATIC_OFFSET);
    h_flags = get_word_priv(H_FLAGS);
    h_synonyms_offset = get_word_priv(H_SYNONYMS_OFFSET);
    h_file_size = get_word_priv(H_FILE_SIZE);
    if (h_file_size == 0)
        h_file_size = get_story_size ();
    h_checksum = get_word_priv(H_CHECKSUM);
    h_interpreter_version = h_type < V6 ? 'Z' : 200;
    if (h_type >= V5)
    {
        h_terminating_keys = get_word_priv(H_FUNCTION_KEYS_OFFSET);
        h_extension_table = get_word_priv(H_EXTENSION_TABLE_OFFSET);
    }

    h_static_limit = (h_file_size << size_shift) < 0x10000 ?
                     (h_file_size << size_shift) : 0x10000;

    if (h_globals_offset + 240*2 > h_static_offset)
        fatal_lookup("BadGlobals");


}/* configure */
