#include <stdio.h>
#include <allegro.h>
#include <allegro/aintern.h>
extern int _packfile_type;

#define N            4096           /* 4k buffers for LZ compression */
#define F            18             /* upper limit for LZ match length */
typedef struct UNPACK_DATA          /* for reading LZ files */
{
   int state;                       /* where have we got to? */
   int i, j, k, r, c;
   int flags;
   unsigned char text_buf[N+F-1];   /* ring buffer, with F-1 extra bytes for string comparison */
} UNPACK_DATA;

/* my_load_datafile:
 * Loads an entire data file into memory, and returns a pointer to it. 
 * On error, sets errno and returns NULL.
 * The following was divined by perusing the runes in the allegro library datafile.c and file.c 
 * It allows loading from an open FILE * rather than a filename.  FILE *f assumed to be positioned at the
 * appended data upon entry, it is still open on exit.
 * If future versions of allegro change the load_datafile routine, this will have to be modified to
 * account for it - good luck. 
 */
void *my_load_datafile(FILE *fp,long len)
{
   DATAFILE *dat=NULL;
   int type;
   PACKFILE *f;
   _packfile_type = DAT_MAGIC;
   errno = *allegro_errno = 0;
   if ((f = malloc(sizeof(PACKFILE))) == NULL) {
      *allegro_errno = ENOMEM;
      return NULL;
   }
   f->buf_pos = f->buf;
   f->flags = 0;
   f->flags &= ~PACKFILE_FLAG_WRITE;
   f->buf_size = 0;
   f->filename = NULL;
   f->passdata = NULL;
   f->passpos = NULL;
   f->parent = NULL;
   f->pack_data = NULL;
   f->passpos = f->passdata;
   f->todo=len;
   f->hndl=fileno(fp); // _al_open (somehow ends up) same as open() so we can use the file number
   /* that is essentially what pack_fopen does for a readonly file */

   lseek(f->hndl,ftell(fp),SEEK_SET); 
   /* ensures the unbuffered file position is set correctly so allegro internal buffering works */

   /* dlxinsd simply appends standard allegro data files onto the file after its marker,
      we have to figure out the first bit ourselves because this is what read_datafile does prior to 
      calling load_file_object()
   */
   type = pack_mgetl(f);

   if (type == F_PACK_MAGIC) {  /* read a packed file */
      PACKFILE *f1;
      UNPACK_DATA *dat1 = malloc(sizeof(UNPACK_DATA));
      int c;
      if (!dat1) {
            *allegro_errno = ENOMEM;
            free(f);
            return NULL;
      }
      if ((f1 = malloc(sizeof(PACKFILE))) == NULL) {
         *allegro_errno = ENOMEM;
         free(f);
         return NULL;
      }
      f1->buf_pos = f1->buf;
      f1->flags = 0;
      f1->flags &= ~PACKFILE_FLAG_WRITE;
      f1->buf_size = 0;
      f1->filename = NULL;
      f1->passdata = NULL;
      f1->passpos = NULL;
      f1->parent = f;
      f1->pack_data = NULL;
      f1->passpos = f->passdata;
      f1->hndl=fileno(fp); // _al_open (somehow ends up) same as open() so we can use the file number
      for (c=0; c < N - F; c++)
               dat1->text_buf[c] = 0; 
      dat1->state = 0;
      f1->todo = LONG_MAX;
      f1->pack_data = (char *)dat1;
      f1->flags |= PACKFILE_FLAG_PACK;
      type = pack_mgetl(f1);
      if ( type != DAT_MAGIC ) {
            if (*allegro_errno == 0)
               *allegro_errno = EDOM;
            free(f1);free(f);
            return NULL;
      }
      dat = load_file_object(f1, LONG_MAX); 
      if (f1->passdata)
	  free(f1->passdata);
      if (f1->pack_data)
	  free(f1->pack_data);
      free(f1);
   } else {
      type = pack_mgetl(f);
      if ( type != DAT_MAGIC ) {
            if (*allegro_errno == 0)
               *allegro_errno = EDOM;
            return NULL;
      } /* we expect this next */
      /* load_file_object might use a callback (its address is internal to file.c sadly).
       It should be null when we call it though because it gets reset after use. 
      */
      dat = load_file_object(f, len); 
   }
   // Now clean up the way pack_fclose does  - without closing the file though.
   if (f->passdata)
	 free(f->passdata);
   if (f->pack_data)
	 free(f->pack_data);
   free(f);
   return dat; 
}
