/*
    main.c --
    OSD code for DOS platform.
*/

#include "osd.h"


/* Display timing data */
volatile int frame_skip      = 1;
volatile int frame_count     = 0;
volatile int frames_rendered = 0;
volatile int frame_rate      = 0;
volatile int tick_count      = 0;
volatile int old_tick_count  = 0;
volatile int skip            = 0;
int running                  = 1;
int state_slot               = 0;
int snap_count               = 0;
int use_mouse                = 0;

/* Blitter data */
BITMAP *sms_bmp = NULL;
PALETTE sms_pal;

/* Game file name */
char game_name[PATH_MAX];



int main (int argc, char **argv)
{
    /* Show version information if no arguments are specified */
    if(argc < 2)
    {
        printf("\n%s\n", APP_NAME);
        printf("Copyright (C) Charles MacDonald 1998-2004\n");
        printf("Version %s, build date: %s, %s\n", APP_VERSION, __DATE__, __TIME__);
        printf("Usage: sp <filename.ext> [-options]\n");
        printf("Type 'sp -help' for a summary of the available options\n");
        exit(1);
    }

    /* Show option list */
    if(stricmp(argv[1], "-info") == 0)
    {
        int version = AGetVersion();
        printf("Using library versions:\n");
        printf("* Allegro: `%s'\n", allegro_id);
        printf("* zlib:    %s\n", zlibVersion());
        printf("* SEAL:    %d.%d\n", (version >> 8) & 0xFF, version & 0xFF);
        exit(1);
    }

    /* Show option list */
    if(stricmp(argv[1], "-help") == 0)
    {
        printf("Options:\n");
        printf(" -vdriver <s> \t specify video driver.\n");
        printf(" -res <x> <y> \t set the display resolution.\n");
        printf(" -scale       \t scale display to full resolution. (slow)\n");
        printf(" -expand      \t force 512x384 or 400x300 zoomed display.\n");
        printf(" -nommx       \t disable use of MMX instructions.\n");
        printf(" -novga       \t disable use of VGA vertical scaling with '-expand'.\n");
        printf(" -depth <n>   \t specify color depth. (8, 16)\n");
        printf(" -blur        \t blur display. (16-bit color only)\n");
        printf(" -scanlines   \t use scanlines effect.\n");
        printf(" -tweak       \t force 256x192 or 160x144 tweaked display.\n");
        printf(" -vsync       \t wait for vertical sync before blitting.\n");
        printf(" -throttle    \t limit updates to 60 frames per second.\n");
        printf(" -sound       \t enable sound. (auto-throttle)\n");
        printf(" -sndrate <n> \t specify sound rate. (8000, 11025, 22050, 44100)\n");
        printf(" -sndcard <n> \t specify sound card. (0-7)\n");
        printf(" -joy <s>     \t specify joystick type.\n");
        printf(" -jp          \t use Japanese console type.\n");
        printf(" -fm          \t required to enable YM2413 sound.\n");
        printf(" -info        \t show library versions.\n");
        printf(" -codies      \t force Codemasters mapper.\n");
        exit(1);
    }

    /* Do configuration */
    do_config("sp.cfg");

    /* Make copy of game filename */
    strcpy(game_name, argv[1]);

    /* Attempt to load game off commandline */
    if(load_rom(game_name) == 0)
    {
        printf("Error loading `%s'.\n", game_name);
        exit(1);
    }

    /* Force Codemasters mapper */
    if(option.codies)
    {
        cart.mapper = MAPPER_CODIES;
        sms.territory = TERRITORY_EXPORT;
    }

    /* Set up video, audio, input, etc. */
    osd_init();

    snd.fm_which = SND_EMU2413;
    snd.fps = (sms.display == DISPLAY_NTSC) ? FPS_NTSC : FPS_PAL;
    snd.fm_clock = (sms.display == DISPLAY_NTSC) ? CLOCK_NTSC : CLOCK_PAL;
    snd.psg_clock = (sms.display == DISPLAY_NTSC) ? CLOCK_NTSC : CLOCK_PAL;
    snd.sample_rate = option.sound ? option.sndrate : 0;
    snd.mixer_callback = NULL;

    /* Initialize the virtual console emulation */
    system_init();
    sms.territory = option.country;
    sms.use_fm = option.fm_enable;

    system_poweron();

    pick_blitter_proc();

    if(blitter_proc == NULL)
    {
        set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
        printf("Illegal combination of parameters - not all options can be combined.\n");
        exit(1);
    }

    /* Main emulation loop */
    while(running)
    {
        /* Bump frame count */
        frame_count++;
        frames_rendered++;
        skip = (frame_count % frame_skip == 0) ? 0 : 1;

        /* Get current input */
        osd_update_inputs();
        check_ui_keys();

        /* Run the system emulation for a frame */
        system_frame(skip);

        /* Update the display */
        if(skip == 0)
        {
            if(option.sound)
            {
                osd_play_streamed_sample_16(0, snd.output[0], snd.buffer_size, option.sndrate, 60, -100);
                osd_play_streamed_sample_16(1, snd.output[1], snd.buffer_size, option.sndrate, 60,  100);
            }

            osd_update_video();
        }

        /* Speed throttling */
        if(option.throttle)
        {
            while(tick_count == old_tick_count);
            old_tick_count = tick_count;
        }
    }

    system_poweroff();
    system_shutdown();
    osd_shutdown();
    return 0;
}


/****************************************************************************/

/* Timer handler, runs at 60Hz */
void tick_handler(void)
{
    tick_count++;
    if(msg_enable) --msg_enable;
    if(tick_count % FRAMES_PER_SECOND == 0)
    {
        frame_rate = frames_rendered;
        frames_rendered = 0;
    }
}
END_OF_FUNCTION(tick_handler);


/* Initialize machine dependant stuff */
void osd_init(void)
{
    /* Set up sound hardware */
    if(option.sound)
    {
        /* Initialize sound and update sample rate */
        int ret = msdos_init_sound(&option.sndrate, option.sndcard);

        /* Error - disable sound */
        if(ret != 0) option.sound = 0;
    }

    /* Set up the Allegro library */
    allegro_init();

    /* Install mouse handler */
    if(install_mouse() != -1)
        use_mouse = 1;

    /* Install input devices */
    install_keyboard();
    install_joystick(option.joy_driver);

    /* Install tick handler */
    install_timer();
    LOCK_FUNCTION(tick_handler);
    LOCK_VARIABLE(tick_count);
    LOCK_VARIABLE(frame_rate);
    LOCK_VARIABLE(frames_rendered);
    LOCK_VARIABLE(msg_enable);
    install_int_ex(tick_handler, BPS_TO_TIMER(FRAMES_PER_SECOND));


    dos_video_init();

    /* Allocate memory bitmap for blitting */
    sms_bmp = create_bitmap_ex(option.video_depth, 256, 256);
    clear(sms_bmp);

    /* Set up bitmap structure */
    memset(&bitmap, 0, sizeof(bitmap_t));
    bitmap.width  = sms_bmp->w;
    bitmap.height = sms_bmp->h;
    bitmap.depth  = option.video_depth;
    bitmap.granularity = (bitmap.depth >> 3);
    bitmap.pitch  = bitmap.width * bitmap.granularity;
    bitmap.data   = (uint8 *)&sms_bmp->line[0][0];
    bitmap.viewport.x = 0;
    bitmap.viewport.y = 0;
    bitmap.viewport.w = 256;
    bitmap.viewport.h = 192;

    fg = (option.video_depth == 8) ? 0xFE : makecol(0xFF, 0xFF, 0xFF);
    bg = (option.video_depth == 8) ? 0xFF : makecol(0x00, 0x00, 0x00);
    
}


/* Free system resources */
void osd_shutdown(void)
{
    if(option.sound) msdos_shutdown_sound();
    dos_video_shutdown();
}

/*--------------------------------------------------------------------------*/

/* Load system state */
int load_state(void)
{
    char name[PATH_MAX];
    FILE *fd = NULL;
    strcpy(name, game_name);
    sprintf(strrchr(name, '.'), ".st%d", state_slot);
    fd = fopen(name, "rb");
    if(!fd) return 0;
    system_load_state(fd);
    fclose(fd);
    return 1;
}

/* Save system state */
int save_state(void)
{
    char name[PATH_MAX];
    FILE *fd = NULL;
    strcpy(name, game_name);
    sprintf(strrchr(name, '.'), ".st%d", state_slot);
    fd = fopen(name, "wb");
    if(!fd) return 0;
    system_save_state(fd);
    fclose(fd);
    return 1;
}

/* Save or load SRAM */
void system_manage_sram(uint8 *sram, int slot, int mode)
{
    char name[PATH_MAX];
    FILE *fd;
    strcpy(name, game_name);
    strcpy(strrchr(name, '.'), ".sav");

    switch(mode)
    {
        case SRAM_SAVE:
            if(sms.save)
            {
                fd = fopen(name, "wb");
                if(fd)
                {
                    fwrite(sram, 0x8000, 1, fd);
                    fclose(fd);
                }
            }
            break;

        case SRAM_LOAD:
            fd = fopen(name, "rb");
            if(fd)
            {
                sms.save = 1;
                fread(sram, 0x8000, 1, fd);
                fclose(fd);
            }
            else
            {
                /* No SRAM file, so initialize memory */
                memset(sram, 0x00, 0x8000);
            }
            break;
    }
}

/* Dump RAM to disk */
void dump_wram(void)
{
    static int count = 0;
    char path[PATH_MAX];
    FILE *fd;
    sprintf(path, "wram.%03d", count);
    fd = fopen(path, "wb");
    if(!fd)
        add_msg("Error saving WRAM to `%s'", path);
    else
    {
        add_msg("WRAM saved to `%s'", path);
        fwrite(sms.wram, sizeof(sms.wram), 1, fd);
        fclose(fd);
        ++count;
    }
}

/* Dump VRAM to disk */
void dump_vram(void)
{
    static int count = 0;
    char path[PATH_MAX];
    FILE *fd;
    sprintf(path, "vram.%03d", count);
    fd = fopen(path, "wb");
    if(!fd)
        add_msg("Error saving VRAM to `%s'", path);
    else
    {
        add_msg("VRAM saved to `%s'", path);
        fwrite(vdp.vram, sizeof(vdp.vram), 1, fd);
        fclose(fd);
        ++count;
    }
}





