/*  Sarien - A Sierra AGI resource interpreter engine
 *  Copyright (C) 1999-2001 Stuart George and Claudio Matsuoka
 *
 *  $Id: agi_v2.c,v 1.38 2001/09/06 18:52:14 cmatsuoka Exp $
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; see docs/COPYING for further details.
 */

#ifndef PALMOS

#include <stdio.h>
#include <string.h>
#include "sarien.h"
#include "agi.h"

static int agi_v2_init (void);
static int agi_v2_deinit (void);
static int agi_v2_detect_game (char *);
static int agi_v2_load_resource (int, int);
static int agi_v2_unload_resource (int, int);
static int agi_v2_load_objects(char *);
static int agi_v2_load_words(char *);

struct agi_loader agi_v2 = {
        2,
        0,
        agi_v2_init,
        agi_v2_deinit,
        agi_v2_detect_game,
        agi_v2_load_resource,
        agi_v2_unload_resource,
        agi_v2_load_objects,
        agi_v2_load_words
};


static int agi_v2_detect_game (char *gn)
{
        strncpy (game.dir, gn, MAX_PATH);
        _D (_D_WARN "game.dir = %s", game.dir);

        if (    !file_isthere (fixpath (NO_GAMEDIR, LOGDIR)) ||
                !file_isthere (fixpath (NO_GAMEDIR, PICDIR)) ||
                !file_isthere (fixpath (NO_GAMEDIR, SNDDIR)) ||
                !file_isthere (fixpath (NO_GAMEDIR, VIEWDIR)))
        {
                return err_InvalidAGIFile;
        }

        agi_v2.int_version = 0x2917;            /* setup for 2.917 */
#ifdef __MPW__
        return err_OK;
#else
        return v2id_game ();
#endif
}


static int agi_v2_load_dir (struct agi_dir *agid, char *fname)
{
        FILE *fp;
        UINT8 *mem;
        UINT32 flen;
        unsigned int i;
        char *path;

        path = fixpath (NO_GAMEDIR, fname);
        report ("Loading directory: %s\n", path);

        if ((fp = fopen (path, "rb")) == NULL) {
                return err_BadFileOpen;
        }

        fseek (fp, 0, SEEK_END);
        flen = ftell (fp);
        fseek (fp, 0, SEEK_SET);

        if ((mem = malloc (flen + 32)) == NULL) {
                fclose (fp);
                return err_NotEnoughMemory;
        }

        fread (mem, 1, flen, fp);

        /* set all directory resources to gone */
        for (i = 0; i < MAX_DIRS; i++) {
                agid[i].volume = 0xff;
                agid[i].offset = _EMPTY;
        }

        /* build directory entries */
        for (i = 0; i < flen; i += 3) {
                agid[i / 3].volume = hilo_getbyte (mem + i) >> 4;
                agid[i / 3].offset = hilo_getpword (mem + i) & (UINT32)_EMPTY;
        }

        free (mem);
        fclose (fp);

        return err_OK;
}


static int agi_v2_init ()
{
        int ec = err_OK;

        /* load directory files */
        ec = agi_v2_load_dir (game.dir_logic, LOGDIR);
        if (ec == err_OK)
                ec = agi_v2_load_dir (game.dir_pic, PICDIR);
        if (ec == err_OK)
                ec = agi_v2_load_dir (game.dir_view, VIEWDIR);
        if (ec == err_OK)
                ec = agi_v2_load_dir (game.dir_sound, SNDDIR);

        return ec;
}


static int agi_v2_deinit ()
{
        int ec = err_OK;

#if 0
        /* unload words */
        agi_v2_unload_words ();

        /* unload objects */
        agi_v2_unload_objects ();
#endif

        return ec;
}


#if 0
static int agi_v2_load_objects (UINT8 *fname)
{
        return load_objects (fname);
}


static int agi_v2_load_words (UINT8* fname)
{
        return load_words (fname);
}


static int agi_v2_unload_objects ()
{
        unload_objects ();
        return err_OK;
}


static int agi_v2_unload_words ()
{
        unload_words ();
        return err_OK;
}
#endif

static int agi_v2_unload_resource (int t, int n)
{
        /* _D ("unload resource"); */

        switch (t) {
        case rLOGIC:
                unload_logic (n);
                break;
        case rPICTURE:
                unload_picture (n);
                break;
        case rVIEW:
                unload_view (n);
                break;
        case rSOUND:
                unload_sound (n);
                break;
        }

        return err_OK;
}


/*
 * This function does noting but load a raw resource into memory,
 * if further decoding is required, it must be done by another
 * routine. NULL is returned if unsucsessfull.
 */

static UINT8* agi_v2_load_vol_res (struct agi_dir *agid)
{
        UINT8 *data = NULL;
        char x[MAX_PATH], *path;
        FILE *fp;
        unsigned int sig;

        sprintf (x, "vol.%i", agid->volume);
        path = fixpath (NO_GAMEDIR, x);
        _D ("path = %s", path);

        if (agid->offset != _EMPTY && (fp = fopen (path, "rb")) != NULL) {
                _D ("loading resource at offset %d", agid->offset);
                if (fseek (fp, agid->offset, SEEK_SET) < 0) {
                        agid->offset = _EMPTY;
                        return NULL;
                }
                fread (&x, 1, 5, fp);
                if ((sig = hilo_getword ((UINT8*)x)) == 0x1234) {
                        agid->len = lohi_getword ((UINT8*)x + 3);
                        data = calloc (1, agid->len + 32);
                        if (data != NULL) {
                                fread (data, 1, agid->len, fp);
                        } else {
                                abort ();
                        }
                } else {
#if 0
                        /* FIXME: call some panic handler instead of
                         *        deiniting directly
                         */
                        deinit_video_mode ();
#endif
                        report ("Error: bad signature %04x\n", sig);
                        /* fprintf (stderr, "ACK! BAD RESOURCE!!!\n"); */
                        return 0;
                }
                fclose (fp);
        } else {
                /* we have a bad volume resource */
                /* set that resource to NA */
                agid->offset = _EMPTY;
        }

        return data;
}


/*
 * Loads a resource into memory, a raw resource is loaded in
 * with above routine, then further decoded here.
 */
int agi_v2_load_resource (int t, int n)
{
        int ec = err_OK;
        UINT8 *data = NULL;

        _D (_D_WARN "(t = %d, n = %d)", t, n);
        if (n > MAX_DIRS)
                return err_BadResource;

        switch (t) {
        case rLOGIC:
                if (~game.dir_logic[n].flags & RES_LOADED) {
                        /*report ("load logic #%d\n", n);*/
                        _D (_D_WARN "loading logic resource %d", n);
                        agi_v2.unload_resource (rLOGIC, n);
                        /* load raw resource into data */
                        data = agi_v2_load_vol_res (&game.dir_logic[n]);

                        game.logics[n].data = data;
                        ec = data ? decode_logic (n) : err_BadResource;

                        game.logics[n].sIP = 2;
                }

                /* if logic was cached, we get here */
                /* reset code pointers incase it was cached */

                game.logics[n].cIP = game.logics[n].sIP;
                break;
        case rPICTURE:
                /* if picture is currently NOT loaded *OR* cacheing is off,
                 * unload the resource (caching == off) and reload it
                 */

                _D (_D_WARN "loading picture resource %d", n);
                if (game.dir_pic[n].flags & RES_LOADED)
                        break;

                /* if loaded but not cached, unload it */
                /* if cached but not loaded, etc */
                agi_v2.unload_resource (rPICTURE, n);
                data = agi_v2_load_vol_res (&game.dir_pic[n]);

                if (data != NULL) {
                        game.pictures[n].rdata = data;
                        game.dir_pic[n].flags |= RES_LOADED;
                } else {
                        ec = err_BadResource;
                }
                break;
        case rSOUND:
                _D (_D_WARN "loading sound resource %d", n);
                if (game.dir_sound[n].flags & RES_LOADED)
                        break;

                data = agi_v2_load_vol_res (&game.dir_sound[n]);

                if (data != NULL) {
                        game.sounds[n].rdata = data;
                        game.dir_sound[n].flags |= RES_LOADED;
                        decode_sound (n);
                } else {
                        ec = err_BadResource;
                }
                break;
        case rVIEW:
                /* Load a VIEW resource into memory...
                 * Since VIEWS alter the view table ALL the time
                 * can we cache the view? or must we reload it all
                 * the time?
                 */
                if (game.dir_view[n].flags & RES_LOADED)
                        break;

                _D (_D_WARN "loading view resource %d", n);
                agi_v2.unload_resource (rVIEW, n);
                data = agi_v2_load_vol_res (&game.dir_view[n]);
                if (data) {
                        game.views[n].rdata = data;
                        game.dir_view[n].flags |= RES_LOADED;
                        ec = decode_view (n);
                } else {
                        ec=err_BadResource;
                }
                break;
        default:
                ec = err_BadResource;
                break;
        }

        return ec;
}

static int agi_v2_load_objects(char *fname)
{
        return load_objects(fname);
}

static int agi_v2_load_words(char *fname)
{
        return load_words(fname);
}

#endif /* PALMOS */

