/*
 * ImageFX Example Loader.
 *
 * Portable BitMap image loading routines.
 *
 * Currently supports PGM (GreyMap) and PPM (PixMap) images.
 *
 * NOTE:  Does NOT support comments in the files!
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>

#include <scan/modall.h>
#include <scan/loadsave.h>


enum {
   B_LoadPpm = 0,
   B_LoadPgm,
   TXT_COUNT
};

#define BUFLEN      (65536L)

static BPTR          handle;
static UBYTE        *buffer;
static ULONG         index;
static ULONG         count;
static short         unchar;


/*
 * PBM_Open() - Open a file for buffered reading.
 */

static
BOOL PBM_Open (char *fname)
{
   SetError(0);

   buffer = AllocMem(BUFLEN, MEMF_PUBLIC|MEMF_CLEAR);
   if (buffer == NULL) ReturnError(ERR_Memory, FALSE);

   handle = Open(fname, MODE_OLDFILE);
   if (handle == NULL) {
      FreeMem(buffer, BUFLEN);
      ReturnError(ERR_Open, FALSE);
   }

   index = BUFLEN;
   count = 0;
   unchar = -1;
}

/*
 * PBM_Close() - Close a buffered file.
 */

static
void PBM_Close (void)
{
   Close (handle);
   FreeMem(buffer, BUFLEN);
}

/*
 * PBM_Getc() - Read next character from the buffered file.
 */

static
short PBM_Getc (void)
{
   UBYTE c;

   if (unchar != -1) {
      c = unchar;
      unchar = -1;
   }
   else {
      if (!count) {
         count = Read (handle, buffer, BUFLEN);
         if (count < 1) return(-1);
         index = 0;
      }
      c = buffer[index++];
      count--;
   }
   return ((short)c);
}

/*
 * PBM_Ungetc() - Push back last character read.
 */

static
void PBM_Ungetc (UBYTE c)
{
   unchar = c;
}

/*
 * PBM_Getw() - Read next white-space bounded word from the
 *               buffered file.
 */

static
BOOL PBM_Getw (char *buf, int max)
{
   int i = 0;
   short c;

   do {
      c = PBM_Getc();
      if (c < 0) return(FALSE);
   }
   while (isspace(c));

   buf[i++] = c;
   while (i < max - 1) {
      c = PBM_Getc();
      if (c < 0) break;
      if (isspace(c)) break;
      buf[i++] = c;
   }
   buf[i] = '\0';

   return(TRUE);
}



/*
 * LoadPGM() - Load a PGM (Portable GreyMap) file.
 */

struct Buffer *LoadPGM (char *fname)
{
   struct Buffer *buffer;
   char buf[16];
   int wid, ht, val;
   short i, j;
   UBYTE *grey;
   BOOL text;

   SetError(0);

   if (!PBM_Open(fname)) {
      return(NULL);
   }

   PBM_Getw (buf, 16);
   if (strcmp("P2",buf) && strcmp("P5",buf)) {
      PBM_Close();
      return(NULL);
   }
   text = (buf[1] == '2');

   PBM_Getw (buf, 16);
   sscanf (buf, "%d", &wid);
   PBM_Getw (buf, 16);
   sscanf (buf, "%d", &ht);
   PBM_Getw (buf, 16);

   buffer = AllocBuffer (NULL, wid, ht, 1, 8, 0);
   if (buffer == NULL) {
      PBM_Close();
      return(NULL);
   }

   strcpy (buffer->Type, "PGM");
   strcpy (buffer->Name, fname);

   ShowStatus(buffer);

   BeginBar(GetStr(B_LoadPgm, "Load PGM"), ht, TRUE);
   for (j = 0; j < ht; j++) {
      if (Bar(j)) {
         KillBuffer(buffer);
         buffer = NULL;
         SetError(ERR_UserCancel);
         break;
      }
      GetBufLine (buffer, &grey, NULL, NULL, j);
      for (i = 0; i < wid; i++) {
         if (text) {
            PBM_Getw(buf,16);
            sscanf (buf, "%d", &val);
            *grey++ = (UBYTE)val;
         }
         else {
            *grey++ = PBM_Getc();
         }
      }
   }
   EndBar(NULL);

   PBM_Close();

   buffer->OriginalDepth = 8;

   return(buffer);
}

/*
 * LoadPPM() - Load a PPM (Portable PixMap) file.
 */

struct Buffer *LoadPPM (char *fname)
{
   struct Buffer *buffer;
   char buf[16];
   int wid, ht, val;
   short i, j;
   UBYTE *red, *grn, *blu;
   BOOL text;

   SetError(0);

   if (!PBM_Open(fname)) {
      return(NULL);
   }

   PBM_Getw (buf, 16);
   if (strcmp("P3",buf) && strcmp("P6",buf)) {
      PBM_Close();
      return(NULL);
   }
   text = (buf[1] == '3');

   PBM_Getw (buf, 16);
   sscanf (buf, "%d", &wid);
   PBM_Getw (buf, 16);
   sscanf (buf, "%d", &ht);
   PBM_Getw (buf, 16);

   buffer = AllocBuffer (NULL, wid, ht, 3, 8, 0);
   if (buffer == NULL) {
      PBM_Close();
      return(NULL);
   }

   strcpy (buffer->Type, "PPM");
   strcpy (buffer->Name, fname);

   ShowStatus(buffer);

   BeginBar(GetStr(B_LoadPpm, "Load PPM"), ht, TRUE);
   for (j = 0; j < ht; j++) {
      if (Bar(j)) {
         KillBuffer(buffer);
         buffer = NULL;
         SetError(ERR_UserCancel);
         break;
      }
      GetBufLine (buffer, &red, &grn, &blu, j);
      for (i = 0; i < wid; i++) {
         if (text) {
            PBM_Getw(buf,16);
            sscanf (buf, "%d", &val);
            *red++ = (UBYTE)val;
            PBM_Getw(buf,16);
            sscanf (buf, "%d", &val);
            *grn++ = (UBYTE)val;
            PBM_Getw(buf,16);
            sscanf (buf, "%d", &val);
            *blu++ = (UBYTE)val;
         }
         else {
            *red++ = PBM_Getc();
            *grn++ = PBM_Getc();
            *blu++ = PBM_Getc();
         }
      }
   }
   EndBar(NULL);

   PBM_Close();

   buffer->OriginalDepth = 24;

   return(buffer);
}


/*
 * LM_Load() - Read the given file; we check the ID to see whether
 *             it is color or grey and call the right load function.
 *
 */
struct Buffer * __saveds __asm LM_Load (register __a0 char *fname,
                                        register __d0 int id,
                                        register __a1 LONG *args)
{
   if (id == 0)
      return (LoadPPM(fname));
   else
      return (LoadPGM(fname));
}


/*
 * LM_LoadPalette() - Load a palette from the given file.  We don't
 *                    support this, so we just return an error.
 *
 */
BOOL __saveds __asm LM_LoadPalette (register __a0 char *fname,
                                    register __a1 struct Palette *pal,
                                    register __d0 int id)
{
   ReturnError(ERR_NoPalette, FALSE);
}

/*
 * Table of formats we support.
 */
static
struct LoadFormat loadformats[] = {
   { "P3", 2, "PPM (Text)", 0 },   /* color */
   { "P6", 2, "PPM (Binary)", 0 },
   { "P2", 2, "PGM (Text)", 1 },   /* grey */
   { "P5", 2, "PGM (Binary)", 1 },
   { NULL }
};

/*
 * LM_Signatures() - Return the table of signature bytes we
 *                   recognize.
 */
struct LoadFormat * __saveds LM_Signatures (void)
{
   return (loadformats);
}

/*
 * LM_CheckFile() - Not needed for this loader.
 *
 */
BOOL __saveds __asm LM_CheckFile (register __a0 char *fname)
{
   return(FALSE);
}


/**********************************************************************\

               Library Functions and Initialization Stuff

\**********************************************************************/

ULONG FuncTable[] = {
   /* These four MUST be present */
   (ULONG) LibOpen,
   (ULONG) LibClose,
   (ULONG) LibExpunge,
   (ULONG) LibNull,

   (ULONG) LM_Load,
   (ULONG) LM_LoadPalette,
   (ULONG) LM_Signatures,
   (ULONG) LM_CheckFile,

   /* End with -1L */
   (ULONG) -1L
};

UBYTE LibraryID[]    = "$VER: PBM Loader 1.00.05 (29.9.92)";
UBYTE LibraryType    = NT_LOADER;

LONG __saveds __stdargs UserOpen (struct ModuleBase *modbase)
{
   modbase->Language = "Loader_PBM";
   modbase->LangCount = TXT_COUNT;
   return(TRUE);
}

LONG __saveds __stdargs UserClose (struct ModuleBase *modbase)
{
   return(TRUE);
}

