/*  Sarien - A Sierra AGI resource interpreter engine
 *  Copyright (C) 1999-2001 Stuart George and Claudio Matsuoka
 *
 *  $Id: agi_v3.c,v 1.36 2001/09/11 06:14:51 jpenner 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"
#include "lzw.h"

static int agi_v3_init (void);
static int agi_v3_deinit (void);
static int agi_v3_detect_game (char *);
static int agi_v3_load_resource (int, int);
static int agi_v3_unload_resource (int, int);
static int agi_v3_load_objects(char *);
static int agi_v3_load_words(char *);

struct agi_loader agi_v3 = {
        3,
        0,
        agi_v3_init,
        agi_v3_deinit,
        agi_v3_detect_game,
        agi_v3_load_resource,
        agi_v3_unload_resource,
        agi_v3_load_objects,
        agi_v3_load_words
};


int agi_v3_detect_game (char *gn)
{
        int ec = err_Unk;
        char x[MAX_PATH], *xname, *path;
        int l;


        _D ("(\"%s\")", gn);
        strncpy (game.dir, gn, MAX_PATH);

        strcpy (x, "*vol.0");
        path = fixpath (NO_GAMEDIR, x);

        _D (_D_WARN "path = %s", path);
        if (file_isthere (path)) {
                _D(_D_WARN "getting xname for path = %s", path);
                xname = file_name (path);
                /* remove VOL.0 and VMS version number from xname */
                l = strlen (xname);
#ifdef OPENVMS
                /* OpenVMS: Strip version number first (e.g., KQ4VOL.0;1 -> KQ4VOL.0) */
                /* Only strip if we find ;N pattern (VMS version), not .0 (part of filename) */
                {
                        int orig_l = l;
                        while (l > 0 && xname[l-1] >= '0' && xname[l-1] <= '9')
                                l--;
                        /* Only strip if we found a semicolon (version number) */
                        if (l > 0 && l < orig_l && xname[l-1] == ';') {
                                l--;  /* Remove semicolon */
                                /* Also remove trailing dot if present (OBJECT.;1 -> OBJECT) */
                                if (l > 0 && xname[l-1] == '.')
                                        l--;
                                xname[l] = 0;
                        } else {
                                /* No version number found, restore original length */
                                l = orig_l;
                        }
                }
                /* Now strip VOL.0 part */
                l = strlen(xname);
#endif
                if (l >= 5)
                        l -= 5;
                xname[l] = 0;
                strncpy (game.name, xname, 8);
                _D (_D_WARN "game.name = %s", game.name);


                agi_v3.int_version = 0x3149;    /* setup for 3.002.149 */
                ec = v3id_game();

        } else {
                _D (_D_CRIT "not found");
                ec = err_InvalidAGIFile;
        }

        return ec;
}


static int agi_v3_load_dir (struct agi_dir *agid, FILE *fp, UINT32 offs, UINT32 len)
{
        int ec = err_OK;
        UINT8 *mem;
        unsigned int i;

        fseek (fp, offs, SEEK_SET);
        if ((mem = malloc (len + 32)) != NULL) {
                fread(mem, 1, len, 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 < len; i += 3) {
                        agid[i / 3].volume = hilo_getbyte (mem + i) >> 4;
                        agid[i / 3].offset = hilo_getpword (mem+i) & (UINT32)_EMPTY;
                }

                free(mem);
        } else {
                ec = err_NotEnoughMemory;
        }

        return ec;
}


struct agi3vol {
        UINT32 sddr;
        UINT32 len;
};

int agi_v3_init (void)
{
        int ec = err_OK;
        struct agi3vol agi_vol3[4];
        int i;
        UINT16 xd[4];
        FILE *fp;
        char *path;

        path = fixpath (GAMEDIR, DIR_);

        if ((fp = fopen(path, "rb")) == NULL) {
                printf ("Failed to open \"%s\"\n", path);
                return err_BadFileOpen;
        }
        /* build offset table for v3 directory format */
        fread (&xd, 1, 8, fp);
        fseek (fp, 0, SEEK_END);

        for(i = 0; i < 4; i++)
                agi_vol3[i].sddr = lohi_getword((UINT8 *)&xd[i]);

        agi_vol3[0].len = agi_vol3[1].sddr - agi_vol3[0].sddr;
        agi_vol3[1].len = agi_vol3[2].sddr - agi_vol3[1].sddr;
        agi_vol3[2].len = agi_vol3[3].sddr - agi_vol3[2].sddr;
        agi_vol3[3].len = ftell(fp) - agi_vol3[3].sddr;

        if (agi_vol3[3].len > 256 * 3)
                agi_vol3[3].len = 256 * 3;

        fseek(fp, 0, SEEK_SET);


        /* read in directory files */
        ec = agi_v3_load_dir (game.dir_logic, fp, agi_vol3[0].sddr,
                agi_vol3[0].len);

        if(ec == err_OK) {
                ec = agi_v3_load_dir (game.dir_pic, fp, agi_vol3[1].sddr,
                        agi_vol3[1].len);
        }

        if(ec == err_OK) {
                ec = agi_v3_load_dir (game.dir_view, fp, agi_vol3[2].sddr,
                        agi_vol3[2].len);
        }

        if(ec == err_OK) {
                ec = agi_v3_load_dir (game.dir_sound, fp, agi_vol3[3].sddr,
                        agi_vol3[3].len);
        }

        fclose(fp);
        return ec;
}


int agi_v3_deinit ()
{
        int ec=err_OK;

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

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

        return ec;
}


int agi_v3_unload_resource (int t, int n)
{
        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 unsucsessful.
 */

UINT8* agi_v3_load_vol_res (struct agi_dir *agid)
{
        char x[MAX_PATH], *path;
        UINT8 *data = NULL, *comp_buffer;
        FILE *fp;

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


        if (agid->offset != _EMPTY && (fp = fopen((char*)path, "rb")) != NULL) {
                fseek (fp, agid->offset, SEEK_SET);
                fread (&x, 1, 7, fp);

                if (hilo_getword((UINT8 *)x) != 0x1234) {
#if 0
                        /* FIXME */
                        deinit_video_mode();
#endif
                        printf("ACK! BAD RESOURCE!!!\n");
                        exit(0);
                }

                agid->len = lohi_getword((UINT8 *)x + 3);/* uncompressed size */
                agid->clen = lohi_getword((UINT8 *)x + 5);/* compressed len */

                comp_buffer = calloc (1, agid->clen + 32);
                fread (comp_buffer, 1, agid->clen, fp);

                if (x[2] & 0x80 || agid->len == agid->clen) {
                        /* do not decompress */
                        data = comp_buffer;

#if 0
                        /* CM: added to avoid problems in
                         *     convert_v2_v3_pic() when clen > len
                         *     e.g. Sierra demo 4, first picture
                         *     (Tue Mar 16 13:13:43 EST 1999)
                         */
                        agid->len = agid->clen;

                        /* Now removed to fix Gold Rush! in demo4 */
#endif
                } else {
                        /* it is compressed */
                        data = calloc (1, agid->len + 32);
                        LZW_expand (comp_buffer, data, agid->len);
                        free (comp_buffer);
                        agid->flags |= RES_COMPRESSED;
                }

                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_v3_load_resource (int t, int n)
{
        int ec = err_OK;
        UINT8 *data = NULL;

        if (n > MAX_DIRS)
                return err_BadResource;

        switch (t) {
        case rLOGIC:
                /* load resource into memory, decrypt messages at the end
                 * and build the message list (if logic is in memory)
                 */
                if (~game.dir_logic[n].flags & RES_LOADED) {
                        /* if logic is already in memory, unload it */
                        agi_v3.unload_resource (rLOGIC, n);

                        /* load raw resource into data */
                        data = agi_v3_load_vol_res (&game.dir_logic[n]);
                        game.logics[n].data=data;

                        /* uncompressed logic files need to be decrypted */
                        if (data != NULL) {
                                /* resloaded flag gets set by decode logic */
                                /* needed to build string table */
                                ec = decode_logic(n);
                                game.logics[n].sIP=2;
                        } else {
                                ec=err_BadResource;
                        }

                        /*logics[n].sIP=2;*/    /* saved IP = 2 */
                        /*logics[n].cIP=2;*/    /* current IP = 2 */

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

                /* 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
                 */
                if (~game.dir_pic[n].flags & RES_LOADED) {
                        agi_v3.unload_resource (rPICTURE, n);
                        data = agi_v3_load_vol_res (&game.dir_pic[n]);
                        if (data != NULL) {
                                data = convert_v3_pic (data,
                                        game.dir_pic[n].len);
                                game.pictures[n].rdata = data;
                                game.dir_pic[n].flags |= RES_LOADED;
                        } else {
                                ec=err_BadResource;
                        }
                }
                break;
        case rSOUND:
                if (game.dir_sound[n].flags & RES_LOADED)
                        break;

                if ((data = agi_v3_load_vol_res (&game.dir_sound[n])) != 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?
                 */
                /* load a raw view from a VOL file into data */
                if (game.dir_view[n].flags & RES_LOADED)
                        break;

                agi_v3.unload_resource (rVIEW, n);
                if ((data = agi_v3_load_vol_res (&game.dir_view[n])) != NULL) {
                        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_v3_load_objects(char *fname)
{
        return load_objects(fname);
}

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

#endif /* PALMOS */

