/*
 *    This program demonstrates how to load and display a LBM file
 *    using load_lbm() function and Allegro 2.1x. It was tested
 *    with Allegro 2.1
 *
 *    Written by AsH / DEVOTiON (Adrian Oboroc) at 01/26/97
 *
 *    Modification log:
 *    01/28/97 - rudimental lines removed, better file handling
 *    01/29/97 - a bug in bytes per line calculation removed
 *
 *    Send your notes to ash@cinf.usm.md
 */

#include <stdlib.h>
#include <stdio.h>

#include "allegro.h"

/* Uncoment following line to use 9 extra x-modes (unstable) */
/* #define USE_UNSTABLE_MODE_X */

#define BANNER "\nLBMVIEW v1.0b (" __DATE__ ")\t"\
               "Written by AsH\t" \
               "(C)opyright 1997 DEVOTiON\n"

/* ------------------ IFF ILBM/PBM loading routine ------------------ */

#define IFF_FORM        0x4D524F46    /* 'FORM' - IFF FORM structure  */
#define IFF_ILBM        0x4D424C49    /* 'ILBM' - interleaved bitmap  */
#define IFF_PBM         0x204D4250    /* 'PBM ' - new DP2e format     */
#define IFF_BMHD        0x44484D42    /* 'BMHD' - bitmap header       */
#define IFF_CMAP        0x50414D43    /* 'CMAP' - color map (palette) */
#define IFF_BODY        0x59444F42    /* 'BODY' - bitmap data         */

/* BSWAPL, BSWAPW
 *  Byte swapping macros for convertion between
 *  Intel and Motorolla byte ordering.
 */

#define BSWAPL(x)   ((((x) & 0x000000ff) << 24) + \
                     (((x) & 0x0000ff00) <<  8) + \
                     (((x) & 0x00ff0000) >>  8) + \
                     (((x) & 0xff000000) >> 24))

#define BSWAPW(x)    (((x) & 0x00ff) << 8) + (((x) & 0xff00) >> 8)

/* load_lbm:
 *  Loads IFF ILBM/PBM files with up to 8 bits per pixel, returning
 *  a bitmap structure and storing the palette data in the specified
 *  palette (this should be an array of at least 256 RGB structures).
 */

BITMAP *load_lbm(char *filename, RGB *pal)
{
  PACKFILE *f;
  BITMAP *b;
  int w, h, i, x, y, bpl, ppl, pbm_mode;
  char ch, cmp_type, bit_plane, color_depth;
  unsigned char uc, check_flags, bit_mask, *line_buf;
  long id, len, l;

  f = pack_fopen(filename, F_READ);
  if (!f)
    return NULL;

  id = pack_igetl(f);              /* read file header    */
  if (id != IFF_FORM)              /* check for 'FORM' id */
  {
    pack_fclose(f);
    return NULL;
  }

  pack_igetl(f);                   /* skip FORM length    */

  id = pack_igetl(f);              /* read id             */

  /* check image type ('ILBM' or 'PBM ') */

  if ((id != IFF_ILBM) && (id != IFF_PBM))
  {
    pack_fclose(f);
    return NULL;
  }

  pbm_mode = id == IFF_PBM;

  id = pack_igetl(f);              /* read id               */
  if (id != IFF_BMHD)              /* check for header      */
  {
    pack_fclose(f);
    return NULL;
  }

  len = pack_igetl(f);             /* read header length    */
  if (len != BSWAPL(20))           /* check, if it is right */
  {
    pack_fclose(f);
    return NULL;
  }

  i = pack_igetw(f);               /* read screen width  */
  w = BSWAPW(i);

  i = pack_igetw(f);               /* read screen height */
  h = BSWAPW(i);

  pack_igetw(f);                   /* skip initial x position  */
  pack_igetw(f);                   /* skip initial y position  */

  color_depth = pack_getc(f);      /* get image depth   */
  if (color_depth > 8)
  {
    pack_fclose(f);
    return NULL;
  }

  pack_getc(f);                    /* skip masking type */

  cmp_type = pack_getc(f);         /* get compression type */
  if ((cmp_type != 0) && (cmp_type != 1))
  {
    pack_fclose(f);
    return NULL;
  }

  pack_getc(f);                    /* skip unused field        */
  pack_igetw(f);                   /* skip transparent color   */
  pack_getc(f);                    /* skip x aspect ratio      */
  pack_getc(f);                    /* skip y aspect ratio      */
  pack_igetw(f);                   /* skip default page width  */
  pack_igetw(f);                   /* skip default page height */

  check_flags = 0;

  do  /* We'll use cycle to skip possible junk      */
  {   /*  chunks: ANNO, CAMG, GRAB, DEST, TEXT etc. */
    id = pack_igetl(f);
    switch(id)
    {
      case IFF_CMAP:
        memset(pal, 0, 256 * 3);
        l = pack_igetl(f);
        len = BSWAPL(l) / 3;
        for (i=0; i<len; i++)
        {
          pal[i].r = pack_getc(f) >> 2;
          pal[i].g = pack_getc(f) >> 2;
          pal[i].b = pack_getc(f) >> 2;
        }
        check_flags |= 1;          /* flag "palette readed" */
        break;

      case IFF_BODY:
        pack_igetl(f);             /* skip BODY size */
        b = create_bitmap(w, h);
        if (!b)
        {
          pack_fclose(f);
          return NULL;
        }

        memset(b->dat, 0, w * h);

        if (pbm_mode)
          bpl = w;
        else
        {
          bpl = w >> 3;            /* calc bytes per line  */
          if (w & 7)               /* for finish bits      */
            bpl++;
        }
        if (bpl & 1)               /* alignment            */
          bpl++;
        line_buf = malloc(bpl);
        if (!line_buf)
        {
          pack_fclose(f);
          return NULL;
        }

        if (pbm_mode)
        {
          for (y = 0; y < h; y++)
          {
            if (cmp_type)
            {
              i = 0;
              while (i < bpl)
              {
                uc = pack_getc(f);
                if (uc < 128)
                {
                  uc++;
                  pack_fread(&line_buf[i], uc, f);
                  i += uc;
                }
                else
                if (uc > 128)
                {
                  uc = 257 - uc;
                  ch = pack_getc(f);
                  memset(&line_buf[i], ch, uc);
                  i += uc;
                }
                /* 128 (0x80) means NOP - no operation  */
              }
            } else      /* pure theoretical situation */
              pack_fread(line_buf, bpl, f);

            memcpy(b->line[y], line_buf, bpl);
          }
        } else
        {
          for (y = 0; y < h; y++)
          {
            for (bit_plane = 0; bit_plane < color_depth; bit_plane++)
            {
              if (cmp_type)
              {
                i = 0;
                while (i < bpl)
                {
                  uc = pack_getc(f);
                  if (uc < 128)
                  {
                    uc++;
                    pack_fread(&line_buf[i], uc, f);
                    i += uc;
                  } else
                    if (uc > 128)
                    {
                      uc = 257 - uc;
                      ch = pack_getc(f);
                      memset(&line_buf[i], ch, uc);
                      i += uc;
                    }
                    /* 128 (0x80) means NOP - no operation  */
                }
              }
              else
                pack_fread(line_buf, bpl, f);
              bit_mask = 1 << bit_plane;
              ppl = bpl;                        /* for all pixel blocks */
              if (w & 7)                        /*  may be, except the  */
                ppl--;                          /*  the last            */
              for (x = 0; x < ppl; x++)
              {
                if (line_buf[x] & 128)
                  b->line[y][x * 8]     |= bit_mask;
                if (line_buf[x] & 64)
                  b->line[y][x * 8 + 1] |= bit_mask;
                if (line_buf[x] & 32)
                  b->line[y][x * 8 + 2] |= bit_mask;
                if (line_buf[x] & 16)
                  b->line[y][x * 8 + 3] |= bit_mask;
                if (line_buf[x] & 8)
                  b->line[y][x * 8 + 4] |= bit_mask;
                if (line_buf[x] & 4)
                  b->line[y][x * 8 + 5] |= bit_mask;
                if (line_buf[x] & 2)
                  b->line[y][x * 8 + 6] |= bit_mask;
                if (line_buf[x] & 1)
                  b->line[y][x * 8 + 7] |= bit_mask;
              }

              /* last pixel block */
              if (w & 7)
              {
                x = bpl - 1;

                /* no necessary to check if (w & 7) > 0 in */
                /* first condition, because (w & 7) != 0   */
                if (line_buf[x] & 128)
                  b->line[y][x * 8]     |= bit_mask;
                if ((line_buf[x] & 64) && ((w & 7) > 1))
                  b->line[y][x * 8 + 1] |= bit_mask;
                if ((line_buf[x] & 32) && ((w & 7) > 2))
                  b->line[y][x * 8 + 2] |= bit_mask;
                if ((line_buf[x] & 16) && ((w & 7) > 3))
                  b->line[y][x * 8 + 3] |= bit_mask;
                if ((line_buf[x] & 8)  && ((w & 7) > 4))
                  b->line[y][x * 8 + 4] |= bit_mask;
                if ((line_buf[x] & 4)  && ((w & 7) > 5))
                  b->line[y][x * 8 + 5] |= bit_mask;
                if ((line_buf[x] & 2)  && ((w & 7) > 6))
                  b->line[y][x * 8 + 6] |= bit_mask;
                if ((line_buf[x] & 1)  && ((w & 7) > 7))
                  b->line[y][x * 8 + 7] |= bit_mask;
              }
            }
          }
        }
        free(line_buf);
        check_flags |= 2;       /* flag "bitmap readed" */
        break;

      default:                  /* skip useless chunks  */
        l = pack_igetl(f);
        len = BSWAPL(l);
        if (len & 1)
          len++;
        for (l=0; l < (len >> 1); l++)
          pack_igetw(f);
    }

  /* Exit from loop if we are at the end of file, */
  /* or if we loaded both bitmap and palette      */

  } while ((check_flags != 3) && (!pack_feof(f)));

  pack_fclose(f);

  if (check_flags != 3)
  {
    if (check_flags & 2)
      destroy_bitmap(b);
    return FALSE;
  }

  if (errno)
  {
    destroy_bitmap(b);
    return FALSE;
  }

  return b;
}

/* ------------------------------------------------------------------ */


void main(int argc, char *argv[])
{
  BITMAP *the_image;
  PALLETE the_palette;
  char key;
  int i, mode, old_mode, param_count;

  /* table with all Allegro resolutions */
  int res[][2] = {{256, 224}, {256, 240}, {320, 200}, {320, 240},
                  {320, 400}, {320, 480}, {360, 200}, {360, 240},
                  {360, 360}, {360, 400}, {360, 480},
#ifdef USE_UNSTABLE_MODE_X
                  {256, 200}, {256, 256}, {360, 270}, {376, 282},
                  {400, 300}, {376, 308}, {376, 564}, {400, 150},
                  {400, 600},
#endif
                  {640, 400}, {640, 480}, {800, 600}, {1024, 768}};

  #define RES_CNT (sizeof(res) / (sizeof(res[0])))

  if (argc < 2)
  {
    printf(BANNER);
    printf("\nUsage:\t'lbmview file01.lbm file02.iff ...', or\n"
           "\t'lbmview *.iff *.lbm *.bbm ...', or\n"
           "\t'lbmview @filelist.ext'\n"
           "\n Keys:\tAny key except Esc - next picture, Esc - exit\n");
    exit(1);
  }

  allegro_init();
  install_keyboard();

  param_count = 0;
  old_mode = -1;

  while (++param_count < argc)
  {

    /* read in the LBM file */
    the_image = load_lbm(argv[param_count], the_palette);
    if (!the_image)
    {
      set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
      printf("Error reading LBM file '%s'\n", argv[1]);
      exit(1);
    }

    mode = RES_CNT - 1;
    for (i = 0; i < RES_CNT; i++)
    {
      if ((res[i][0] == the_image->w) && (res[i][1] == the_image->h))
      {
        mode = i;
        break;
      }
      if ((the_image->w < res[i][0]) && (the_image->w < res[i][1]) &&
          (res[i][0] < res[mode][0]) && (res[i][1] < res[mode][1]))
        mode = i;
    }

    if (old_mode != mode)
    {
      if (!set_gfx_mode(GFX_AUTODETECT, res[mode][0], res[mode][1], 0, 0))
        old_mode = mode;
      else
      {
        set_gfx_mode(GFX_VGA, res[0][0], res[0][1], 0, 0);
        old_mode = 0;
      }
    }

    /* select the palette from the LBM file */
    set_pallete(the_palette);

    /* blit the image onto the screen */
    blit(the_image, screen, 0, 0, (SCREEN_W-the_image->w)/2,
         (SCREEN_H-the_image->h)/2, the_image->w, the_image->h);

    /* destroy the LBM bitmap */
    destroy_bitmap(the_image);
    if ((readkey() & 0xff) == 27)
      break;
  }

  set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
  printf(BANNER);
  exit(0);
}
