Logo Search packages:      
Sourcecode: sarien version File versions  Download package

agi_v4.c

/*  Sarien - A Sierra AGI resource interpreter engine
 *  Copyright (C) 1999-2001 Stuart George and Claudio Matsuoka
 *
 *  $Id: agi_v4.c,v 1.8 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.
 */

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

#if defined PALMOS

static int agi_v4_init (void);
static int agi_v4_deinit (void);
static int agi_v4_detect_game (char *);
static int agi_v4_load_resource (int, int);
static int agi_v4_unload_resource (int, int);
static int agi_v4_load_objects(char *);
static int agi_v4_load_words(char *);

UINT32 pdb_read_dword(UINT8 *mem);
UINT16 pdb_read_word(UINT8 *mem);
int load_v2_dir(struct agi_dir *agid, int offset, UINT32 flen, FILE *fp);
int load_v3_dir(struct agi_dir *agid, int offs, UINT32 len, FILE *fp);

extern struct agi_picture pictures[];
extern struct agi_logic logics[];
extern struct agi_view views[];
extern struct agi_sound sounds[];

struct agi_loader agi_v4 = {
      4,
      0,
      agi_v4_init,
      agi_v4_deinit,
      agi_v4_detect_game,
      agi_v4_load_resource,
      agi_v4_unload_resource,
      agi_v4_load_objects,
      agi_v4_load_words
};


/* this must be kept synchronous with the AGI2PDB headers */
#define     PDB_OPT_AGDS            0x0001
#define PDB_OPT_AMIGA         0x0002

#ifdef __WATCOMC__
#pragma pack(1)
#endif

struct pdb_pdbheader
{
      UINT8 name[32];
                        /*
                           String - This is the name of the database
                            on the PalmPilot device.
                            It need not match the name of the PDB file
                            in the environment in which it is created.
                        */
      UINT16 fileAttributes;
                        /*
                           Attributes of the pdb file.
                            0x0002 Read-Only
                            0x0004 Dirty AppInfoArea
                            0x0008 Backup this database
                                   (i.e. no conduit exists)
                            0x0010 Okay to install newer over existing
                                   copy, if present on PalmPilot
                            0x0020 Force the PalmPilot to reset after
                                   this database is installed
                        */
      UINT16 version;
                        /*
                           Defined by the application.
                        */
      UINT32 creationDate;
                        /*
                           Expressed as the number of seconds since
                            January 1, 1904. The database will not
                            install if this value is zero. (PalmOS
                            1.0.6)
                        */
      UINT32 modificationDate;
                        /*
                           Expressed as the number of seconds since
                            January 1, 1904.
                            The database will not install if this
                            value is zero. (PalmOS 1.0.6)
                        */
      UINT32 lastBackupDate;
                        /*
                           Expressed as the number of seconds since
                            January 1, 1904. This can be left at zero
                            and the database will install.
                        */
      UINT32 modificationNumber;
                        /*
                           Set to zero.
                        */
      UINT32 appInfoArea;
                        /*
                           The byte number in the PDB file (counting
                            from zero) at which the AppInfoArea is
                            located. This must be the first entry
                            in the Data portion of the PDB file.
                            If this database does not have an
                            AppInfoArea, set this value to zero.
                        */
      UINT32 sortInfoArea;
                        /*
                           The byte number in the PDB file (counting
                            from zero) at which the SortInfoArea is
                            located. This must be placed immediately
                            after the AppInfoArea, if one exists,
                            within the Data portion of the PDB file.
                            If this database does not have a
                            SortInfoArea, set this value to zero.
                            Do not use this. See Note C below for
                            further details.
                        */
      UINT8 databaseType[4];
                        /*
                           String - Set this to the desired value.
                            Generally it should match the Database Type
                            used by the corresponding application.
                            This is 4 characters long and does not have
                            a terminating null.
                        */
      UINT8 creatorID[4];
                        /*
                           String - Set this to the desired value.
                            Generally it should match the Creator ID
                            used by the corresponding application. In
                            all cases, you should always register your
                            Creator ID before using it. This is 4
                            characters long and does not have a
                            terminating null.
                        */
      UINT32 uniqueIDSeed;
                        /*
                           This is used to generate the Unique ID
                            number of subsequent records. Generally,
                            this should be set to zero.
                        */
      UINT32 nextRecordListID;
                        /*
                           Set this to zero. (This does not appear
                            to be used, but that has not been
                            verified by a third party.)
                        */
      UINT16 numberOfRecords;
                        /*
                           This contains the number of records
                        */
}
#ifdef __GNUC__
__attribute__((packed))
#endif
;

struct pdb_recheader{
      UINT32 recordDataOffset;
                        /*
                           The byte number in the PDB file (counting
                            from zero) at which the record is located.
                        */
      UINT8 recordAttributes;
                        /*
                           The records attributes.
                            0x10 Secret record bit.
                            0x20 Record in use (busy bit).
                            0x40 Dirty record bit.
                            0x80 Delete record on next HotSync.
                            The least significant four bits are used
                             to represent the category values.
                        */
      UINT8 uniqueID[3];
                        /*
                           Set this to zero and do not try to
                            second-guess what PalmOS will do with
                            this value.
                        */
}
#ifdef __GNUC__
__attribute__((packed))
#endif
;

struct agi3vol {
      UINT32 sddr;
      UINT32 len;
};

/* a agi v2.xxx game format */
struct PalmOSAGI2
{
      UINT32      logdir[2];
      UINT32      picdir[2];
      UINT32      viewdir[2];
      UINT32      snddir[2];

      UINT32      object[2];
      UINT32      words[2];

      UINT32      vol[16*2];        /* 0=offs, 1=len */
};

/* an agi v3.xx.yyy game format */
struct PalmOSAGI3
{
      UINT32      dir[2];
      UINT32      object[2];
      UINT32      words[2];
      UINT32      vol[16*2];        /* 0=offs, 1=len */
};

union PalmOSAGI4
{
      struct PalmOSAGI2 v2;
      struct PalmOSAGI3 v3;
};

/* PalmOS AGI Header */
struct PalmOSAGI
{
      char  name[32];
      UINT16      version;
      UINT16      emulation;
      UINT32      options;
      UINT32      crc;

      union PalmOSAGI4 v4;
};

#ifdef __WATCOMC__
#pragma pack()
#endif

struct PalmOSAGI palmagi;
struct pdb_pdbheader PDBHeader;

/* our PDB file that contains everything */
char  pdb_file[MAX_PATH];
UINT32      pdb_start;

static int agi_v4_detect_game (char *gn)
{
      FILE *fp;
      int ec= err_InvalidAGIFile;
      int offset;
      struct pdb_recheader rh;

      /* gn is filename for a .pdb! */

      memset(&PDBHeader, 0x0, sizeof(struct pdb_pdbheader));
      memset(&palmagi, 0x0, sizeof(struct PalmOSAGI));
      strcpy(pdb_file, gn);

      fp = fopen (pdb_file, "rb");
      if(fp!=NULL)
      {
            fread(&PDBHeader, 1, sizeof(struct pdb_pdbheader), fp);

            if(PDBHeader.creatorID[0]=='F' && PDBHeader.creatorID[1]=='A' &&
               PDBHeader.creatorID[2]=='G' && PDBHeader.creatorID[3]=='I')
            {
                  /* valid palm database with Sarien Creator ID */
                  /* now skip record headers, read in v4 Header */

                  fread(&rh, 1, sizeof(struct pdb_recheader), fp);

                  offset=(int)pdb_read_dword((UINT8*)&rh.recordDataOffset);

                  /* our data all starts from here. add this to all offsets */
                  pdb_start=offset;

                  fseek(fp, offset, SEEK_SET);
                  fread(&palmagi, 1, sizeof(struct PalmOSAGI), fp);

                  palmagi.crc=pdb_read_dword((UINT8*)&palmagi.crc);
                  palmagi.options=pdb_read_word((UINT8*)&palmagi.options);
                  palmagi.emulation=pdb_read_word((UINT8*)&palmagi.version);
                  palmagi.version=pdb_read_word((UINT8*)&palmagi.version);

                  /* setup our options before we call setup game! */
                  if((palmagi.options&PDB_OPT_AGDS))
                        opt.agds=1;

                  if((palmagi.options&PDB_OPT_AMIGA))
                        opt.amiga=1;

                  v4id_game(palmagi.crc);

                  /* if the game is a v2 */
                  
                  /* use agi_get_release instead of palmagi.emulation */                  
                  if((agi_get_release()>>12)==0x2)
                        agi_v4.int_version = 0x2917;        /* setup for 2.917 */
                  else
                        agi_v4.int_version = 0x3149;        /* setup for 3.002.149 */

                  ec=err_OK;
            }

            fclose(fp);
      }
      
      return ec;
}


static int agi_v4_load_dir (struct agi_dir *agid, int intDirType)
{
      int ec=err_OK;

#ifndef PALMOS
      FILE *fp;
      struct agi3vol agi_vol3[4];
      UINT32      offs, flen;
      UINT16 xd[4];
      int   i;

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

      if((palmagi.emulation>>12)==2)
      {
            /* load singledir v2 style */
            switch(intDirType)
            {
                  case rLOGIC: ec=load_v2_dir(agid, pdb_start + palmagi.v4.v2.logdir[0], palmagi.v4.v2.logdir[1], fp); break;
                  case rPICTURE: ec=load_v2_dir(agid, pdb_start + palmagi.v4.v2.picdir[0], palmagi.v4.v2.picdir[1], fp); break;
                  case rSOUND: ec=load_v2_dir(agid, pdb_start + palmagi.v4.v2.snddir[0], palmagi.v4.v2.snddir[1], fp); break;
                  case rVIEW: ec=load_v2_dir(agid, pdb_start + palmagi.v4.v2.viewdir[0], palmagi.v4.v2.viewdir[1], fp); break;
            }
      }
      else
      {
            /* load v3 gamedir style */
            offs=pdb_start + palmagi.v4.v3.dir[0];
            flen=palmagi.v4.v3.dir[1];

            fseek(fp, offs, SEEK_SET);

            /* FIXME: Not Endian Aware */
            fread (&xd, 1, 8, fp);

            for(i = 0; i < 4; i++)
                  agi_vol3[i].sddr = offs + 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;

            /* read in all directory file info */
            ec=load_v3_dir(game.dir_logic, agi_vol3[0].sddr, agi_vol3[0].len, fp);
            if(ec==err_OK)
                  ec=load_v3_dir(game.dir_pic, agi_vol3[1].sddr, agi_vol3[1].len, fp);
            if(ec==err_OK)
                  ec=load_v3_dir(game.dir_view, agi_vol3[2].sddr, agi_vol3[2].len, fp);
            if(ec==err_OK)
                  ec=load_v3_dir(game.dir_sound, agi_vol3[3].sddr, agi_vol3[3].len, fp);
      }

      fclose(fp);
#endif

      return ec;
}

int load_v2_dir(struct agi_dir *agid, int offset, UINT32 flen, FILE *fp)
{
      UINT8 *mem;
      int i;

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

      fseek (fp, offset, SEEK_SET);
      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) & _EMPTY;
      }

      free (mem);

      return err_OK;
}

int load_v3_dir(struct agi_dir *agid, int offs, UINT32 len, FILE *fp)
{
      int ec = err_OK;
      UINT8 *mem;
      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) & _EMPTY;
            }

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

      return ec;
}


static int agi_v4_init ()
{
      int ec = err_OK;

      /* load directory files */
      switch((palmagi.emulation>>12))
      {
            case 2:
                  ec = agi_v4_load_dir (game.dir_logic, rLOGIC);
                  if (ec == err_OK)
                        ec = agi_v4_load_dir (game.dir_pic, rPICTURE);
                  if (ec == err_OK)
                        ec = agi_v4_load_dir (game.dir_view, rVIEW);
                  if (ec == err_OK)
                        ec = agi_v4_load_dir (game.dir_sound, rSOUND);
                  break;
            case 3:
                  /* a v3 game load will load all DIR files,
                     so below is just dummy code */
                  ec=agi_v4_load_dir(game.dir_logic, rLOGIC);
                  break;
      }

      return ec;
}


static int agi_v4_deinit ()
{
      int ec = err_OK;
      return ec;
}



static int agi_v4_unload_resource (int restype, int resnum)
{
      switch (restype) {
      case rLOGIC:
            unload_logic (resnum);
            break;
      case rPICTURE:
            unload_picture (resnum);
            break;
      case rVIEW:
            unload_view (resnum);
            break;
      case rSOUND:
            unload_sound (resnum);
            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.
 */

UINT8* agi_v4v2_load_vol_res (struct agi_dir *agid)
{
#ifndef PALMOS
      UINT8 *data = NULL;
      FILE *fp;
      UINT32      offs;
      char  x[16];

      /* fill offs with volume offset*/

      offs = pdb_start + palmagi.v4.v2.vol[(2*agid->volume)];

      /* loading a bad resource */
      if(offs==pdb_start)
            agid->offset=_EMPTY;

      _D ("(agi_dir = [offset:%ld, len:%ld])", agid->offset, agid->len);

      if (agid->offset != _EMPTY && (fp = fopen (pdb_file, "rb")) != NULL) {
            _D ("loading resource");
            fseek (fp, offs + agid->offset, SEEK_SET);
            fread (&x, 1, 5, fp);
            if (hilo_getword (x) == 0x1234) {
                  agid->len = lohi_getword (x + 3);
                  data = calloc (1, agid->len + 32);
                  if (data != NULL)
                        fread (data, 1, agid->len, fp);
            } else {
                  /* FIXME: call some panic handler instead of
                   *        deiniting directly
                   */
                  deinit_video_mode ();
                  fprintf (stderr, "ACK! BAD RESOURCE!!!\n");
                  exit (0);
            }
            fclose (fp);
      } else {
            /* we have a bad volume resource */
            /* set that resource to NA */
            agid->offset = _EMPTY;
      }

      return data;
#endif
}

UINT8* agi_v4v3_load_vol_res (struct agi_dir *agid)
{
#ifndef PALMOS
      UINT8 x[MAX_PATH], *data = NULL, *comp_buffer;
      FILE *fp;
      UINT32 offs;

      _D ("(%p)", agid);

      offs = pdb_start + palmagi.v4.v3.vol[2*agid->volume];
      /* loading a bad resource */
      if(offs==pdb_start)
            agid->offset=_EMPTY;

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

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

            agid->len = lohi_getword (x + 3);   /* uncompressed size */
            agid->clen = lohi_getword (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;
            } 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;
#endif
}



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


      _D (_D_WARN "(restype = %d, resnum = %d)", restype, resnum);
      if (resnum > MAX_DIRS)
            return err_BadResource;

      switch (restype) {
      case rLOGIC:
            if (~game.dir_logic[resnum].flags & RES_LOADED) {
                  _D (_D_WARN "loading logic resource %d", resnum);
                  agi_v4.unload_resource (rLOGIC, resnum);
                  /* load raw resource into data */
                  if((palmagi.emulation>>12)==2)
                        data = agi_v4v2_load_vol_res (&game.dir_logic[resnum]);
                  else
                        data = agi_v4v3_load_vol_res(&game.dir_logic[resnum]);

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

                  logics[resnum].sIP = 2;
            }

            logics[resnum].cIP = logics[resnum].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", resnum);
            if (game.dir_pic[resnum].flags & RES_LOADED)
                  break;

            /* if loaded but not cached, unload it */
            /* if cached but not loaded, etc */
            agi_v4.unload_resource (rPICTURE, resnum);

            if((palmagi.emulation>>12)==2)
            {
                  data = agi_v4v2_load_vol_res (&game.dir_pic[resnum]);
            if (data != NULL) {
                  pictures[resnum].rdata = data;
                  game.dir_pic[resnum].flags |= RES_LOADED;
            } else {
                  ec = err_BadResource;
            }
            }
            else
            {
                  data = agi_v4v3_load_vol_res(&game.dir_pic[resnum]);
                  if (data != NULL) {
                        data = convert_v3_pic (data,
                              game.dir_pic[resnum].len);
                        pictures[resnum].rdata = data;
                        game.dir_pic[resnum].flags |= RES_LOADED;
                  } else {
                        ec=err_BadResource;
                  }
            }

            break;
      case rSOUND:
            _D (_D_WARN "loading sound resource %d", resnum);
            if (game.dir_sound[resnum].flags & RES_LOADED)
                  break;

            if((palmagi.emulation>>12)==2)
                  data = agi_v4v2_load_vol_res (&game.dir_sound[resnum]);
            else
                  data = agi_v4v3_load_vol_res(&game.dir_sound[resnum]);

            if (data != NULL) {
                  sounds[resnum].rdata = data;
                  game.dir_sound[resnum].flags |= RES_LOADED;
                  decode_sound (resnum);
            } 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[resnum].flags & RES_LOADED)
                  break;

            _D (_D_WARN "loading view resource %d", resnum);
            agi_v4.unload_resource (rVIEW, resnum);

            if((palmagi.emulation>>12)==2)
            {
                  if ((data = agi_v4v2_load_vol_res (&game.dir_view[resnum]))) {
                        views[resnum].rdata = data;
                        game.dir_view[resnum].flags |= RES_LOADED;
                        ec = decode_view (resnum);
                  } else {
                        ec=err_BadResource;
                        }
            }
            else
            {
                  if ((data = agi_v4v3_load_vol_res (&game.dir_view[resnum]))) {
                        views[resnum].rdata = data;
                        game.dir_view[resnum].flags |= RES_LOADED;
                        ec = decode_view (resnum);
                  } else {
                        ec=err_BadResource;
                        }
            }
            break;
      default:
            ec = err_BadResource;
            break;
      }

      return ec;
}


UINT16 pdb_read_word(UINT8 *mem)
{
      UINT16      w;

      w = (UINT16)mem[1];
      w += (UINT16)mem[0]<<8;

      return w;
}

UINT32 pdb_read_dword(UINT8 *mem)
{
      UINT32 w;

      w = (UINT32)mem[3];
      w += (UINT32)mem[2]<<8;
      w += (UINT32)mem[1]<<16;
      w += (UINT32)mem[0]<<24;
      
      return w;
}

static int agi_v4_load_objects(char *fname)
{
      int ec=err_OK;
#ifndef PALMOS
      FILE *fp;
      UINT8 *mem;
      UINT32      len;
      UINT32      offs;

      game.num_objects=0;
      fp=fopen(pdb_file, "rb");
      if(fp!=NULL)
      {
            if((palmagi.emulation>>12)==2)
            {
                  offs=palmagi.v4.v2.object[0];
                  len=palmagi.v4.v2.object[1];
            }
            else
            {
                  offs=palmagi.v4.v3.object[0];
                  len=palmagi.v4.v3.object[1];
            }

            fseek(fp, pdb_start + offs, SEEK_SET);
            mem=(UINT8*)malloc(32+len);
            fread(mem, 1, len, fp);
            ec=decode_objects(mem, len);

            free(mem);
            fclose(fp);
      }
#endif
      return ec;
}

static int agi_v4_load_words(char *fname)
{
      int ec=err_OK;
#ifndef PALMOS
      FILE *fp;
      UINT8 *mem;
      UINT32      len;
      UINT32      offs;

      fp=fopen(pdb_file, "rb");
      if(fp!=NULL)
      {
            if((palmagi.emulation>>12)==2)
            {
                  offs=palmagi.v4.v2.words[0];
                  len=palmagi.v4.v2.words[1];
            }
            else
            {
                  offs=palmagi.v4.v3.words[0];
                  len=palmagi.v4.v3.words[1];
            }

            fseek(fp, pdb_start + offs, SEEK_SET);
            mem=(UINT8*)malloc(32+len);
            fread(mem, 1, len, fp);
            ec=decode_words(mem, len);
            free(mem);
            fclose(fp);
      }
#endif
      return ec;
}

#endif /* PALMOS */

Generated by  Doxygen 1.6.0   Back to index