[AD] Custom packfiles

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


Here's a patch that implements custom packfiles, i.e. PACKFILEs where the user supplies the methods for reading, writing, etc. No documentation yet. I think the changes are simple enough for 4.2.

The relevant API additions are summarised below:

AL_FUNC(PACKFILE *, pack_fopen_vtable, (AL_CONST PACKFILE_VTABLE *vtable, void *userdata));
AL_FUNC(void *, pack_get_userdata, (PACKFILE *f));

struct PACKFILE_VTABLE
{
  AL_METHOD(int, pf_getc, (void *userdata));
  AL_METHOD(int, pf_putc, (int c, void *userdata));
  AL_METHOD(long, pf_fread, (void *p, long n, void *userdata));
  AL_METHOD(long, pf_fwrite, (AL_CONST void *p, long n, void *userdata));
  AL_METHOD(int, pf_fseek, (void *userdata, int offset));
  AL_METHOD(int, pf_fclose, (void *userdata));
};

AL_FUNC(struct BITMAP *, load_bmp_pf, (PACKFILE *f, struct RGB *pal));
AL_FUNC(struct BITMAP *, load_pcx_pf, (PACKFILE *f, struct RGB *pal));
AL_FUNC(struct BITMAP *, load_tga_pf, (PACKFILE *f, struct RGB *pal));
AL_FUNC(int, save_bmp_pf, (PACKFILE *f, struct BITMAP *bmp, AL_CONST struct RGB *pal)); AL_FUNC(int, save_pcx_pf, (PACKFILE *f, struct BITMAP *bmp, AL_CONST struct RGB *pal)); AL_FUNC(int, save_tga_pf, (PACKFILE *f, struct BITMAP *bmp, AL_CONST struct RGB *pal));
AL_FUNC(SAMPLE *, load_wav_pf, (struct PACKFILE *f));
AL_FUNC(SAMPLE *, load_voc_pf, (struct PACKFILE *f));

There is no load_lbm_pf because: (1) I have no LBMs nor an LBM converter handy, (2) nobody uses LBMs, and (3) the LBM loader stops reading as soon as it has enough data, rather than reading to the end of the LBM file, leaving the file offset at an unspecified place when the loader finishes. This probably should be fixed if load_lbm_pf was to be added -- although load_wav_pf has a similar problem, as it only stops at the EOF (AFAIK it can't be helped).

It's not possible to use the Allegro's packfile compression with pack_fopen_vtable(), nor can you use pack_fopen_chunk() on vtable-packfiles. That also explains why there is no load_datafile_pf().

Basically this is a quick hack to allow people to make use of the image and sample loaders with data stored in memory, or within non-datafile "wad" formats (e.g. zip files). I'll leave it to you guys to decide if it's worth including for 4.2.

expackf.c is code I was testing with. It won't compile by itself but it shows how the API can be used.

Peter

PS. I'm changing ISPs in the next week or so. Please send any private messages to novalazy at gmail.com instead.

Attachment: packfile-vtable.diff.gz
Description: GNU Zip compressed data

#include <stdio.h>
#include <string.h>
#include "allegro.h"

/*----------------------------------------------------------------------*/
/*		Reading							*/
/*----------------------------------------------------------------------*/

typedef struct MEMREAD_INFO
{
   AL_CONST unsigned char *block;
   long length;
   long offset;
} MEMREAD_INFO;

int memread_getc(void *userdata)
{
   MEMREAD_INFO *info = userdata;
   ASSERT(info);
   ASSERT(info->offset <= info->length);

   if (info->offset == info->length)
      return EOF;
   else
      return info->block[info->offset++];
}  

int memread_putc(int c, void *userdata)
{
   return EOF;
}

long memread_fread(void *p, long n, void *userdata)
{
   MEMREAD_INFO *info = userdata;
   size_t actual;
   ASSERT(info);
   ASSERT(info->offset <= info->length);

   actual = MIN(n, info->length - info->offset);

   memcpy(p, info->block + info->offset, actual);
   info->offset += actual;

   ASSERT(info->offset <= info->length);

   return actual;
}

long memread_fwrite(AL_CONST void *p, long n, void *userdata)
{
   return 0;
}

int memread_seek(void *userdata, int offset)
{
   MEMREAD_INFO *info = userdata;
   long actual;
   ASSERT(info);
   ASSERT(info->offset <= info->length);

   actual = MIN(offset, info->length - info->offset);

   info->offset += actual;

   ASSERT(info->offset <= info->length);

   if (offset == actual)
      return 0;
   else
      return -1;
}

int memread_fclose(void *userdata)
{
   return 0;
}

PACKFILE_VTABLE memread_vtable =
{
   memread_getc,
   memread_putc,
   memread_fread,
   memread_fwrite,
   memread_seek,
   memread_fclose
};

#include "expackf.inc"

void memread_test(void)
{
   PACKFILE *f;
   MEMREAD_INFO memread_info;
   BITMAP *bmp;
   BITMAP *bmp2;
   PALETTE pal;
   PALETTE pal2;

   memread_info.block = tga_pcx_block;
   memread_info.length = sizeof tga_pcx_block;
   memread_info.offset = 0;
   f = pack_fopen_vtable(&memread_vtable, &memread_info);
   if (!f)
      return;

   bmp = load_tga_pf(f, pal);
   bmp2 = load_pcx_pf(f, pal2);

   pack_fclose(f);

   if (bmp) {
      set_palette(pal);
      blit(bmp, screen, 0, 0, 0, 0, bmp->w, bmp->h);
      readkey();
      set_palette(pal2);
      blit(bmp2, screen, 0, 0, 0, 0, bmp2->w, bmp2->h);
      readkey();
   }
}

/*----------------------------------------------------------------------*/
/*		stdio							*/
/*----------------------------------------------------------------------*/

static int stdio_getc(void *userdata)
{
   FILE *fp = userdata;
   return fgetc(fp);
}

static int stdio_putc(int c, void *userdata)
{
   FILE *fp = userdata;
   return fputc(c, fp);
}

static long stdio_fread(void *p, long n, void *userdata)
{
   FILE *fp = userdata;
   return fread(p, 1, n, fp);
}

static long stdio_fwrite(AL_CONST void *p, long n, void *userdata)
{
   FILE *fp = userdata;
   return fwrite(p, 1, n, fp);
}

static int stdio_seek(void *userdata, int n)
{
   FILE *fp = userdata;
   return fseek(fp, n, SEEK_CUR);
}

static int stdio_fclose(void *userdata)
{
   FILE *fp = userdata;
   return fclose(fp);
}

static PACKFILE_VTABLE stdio_vtable =
{
   stdio_getc,
   stdio_putc,
   stdio_fread,
   stdio_fwrite,
   stdio_seek,
   stdio_fclose
};

static void stdio_read_test(void)
{
   FILE *fp, *fp2;
   PACKFILE *f;
   PALETTE pal;
   BITMAP *bmp;

   fp = fopen("examples/allegro.pcx", "rb");
   f = pack_fopen_vtable(&stdio_vtable, fp);

   bmp = load_pcx_pf(f, pal);
   set_palette(pal);
   blit(bmp, screen, 0, 0, 0, 0, bmp->w, bmp->h);
   destroy_bitmap(bmp);
   readkey();

   fp2 = freopen("examples/mysha.pcx", "rb", fp);
   ASSERT(fp2 == fp);

   bmp = load_pcx_pf(f, pal);
   set_palette(pal);
   blit(bmp, screen, 0, 0, 0, 0, bmp->w, bmp->h);
   destroy_bitmap(bmp);
   readkey();

   pack_fclose(f);
}

static void stdio_seek_test(void)
{
   FILE *fp;
   PACKFILE *f;
   int c1, c2, c3, c4;
   int fail;

   fp = fopen("examples/seektest", "rb");
   ASSERT(fp);
   f = pack_fopen_vtable(&stdio_vtable, fp);
   ASSERT(f);

   c1 = pack_getc(f);
   pack_fseek(f, 3);
   c2 = pack_getc(f);
   pack_fseek(f, 3);
   c3 = pack_getc(f);
   pack_fseek(f, 3);
   c4 = pack_getc(f);
   fail = pack_fseek(f, 10);

   printf("%c%c%c%c == abcd\n", c1, c2, c3, c4);
   printf("fail: %d == -1\n", fail);

   pack_fclose(f);
}

static void stdio_write_test(void)
{
   FILE *fp;
   PACKFILE *f;
   BITMAP *bmp;
   PALETTE pal;

   fp = fopen(",,expackf.out", "wb");
   ASSERT(fp);
   f = pack_fopen_vtable(&stdio_vtable, fp);
   ASSERT(f);

   bmp = load_pcx("examples/allegro.pcx", pal);
   save_pcx_pf(f, bmp, pal);
   destroy_bitmap(bmp);

   bmp = load_pcx("examples/mysha.pcx", pal);
   save_bmp_pf(f, bmp, pal);
   destroy_bitmap(bmp);

   pack_fclose(f);
}

/*----------------------------------------------------------------------*/

int main(void)
{
   if (allegro_init() != 0)
      return 1;
   install_keyboard();
   if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 320, 200, 0, 0) != 0)
      return 1;

   /*
   memread_test();
   stdio_read_test();
   stdio_seek_test();
   */
   stdio_write_test();

   return 0;
}

END_OF_MAIN()


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/