/*
 * PSD Plugin version 3.0.13
 * This GIMP plug-in is designed to load Adobe Photoshop(tm) files (.PSD)
 *
 * Adam D. Moss <adam@gimp.org> <adam@foxbox.org>
 *
 *     If this plug-in fails to load a file which you think it should,
 *     please tell me what seemed to go wrong, and anything you know
 *     about the image you tried to load.  Please don't send big PSD
 *     files to me without asking first.
 *
 *          Copyright (C) 1997-2004 Adam D. Moss
 *          Copyright (C) 1996      Torsten Martinsen
 *
 * 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * Adobe and Adobe Photoshop are trademarks of Adobe Systems
 * Incorporated that may be registered in certain jurisdictions.
 */

/*
 * Revision history:
 *
 *  2004-01-06 / v3.0.13 / Adam D. Moss
 *       Disable one of the PanoTools fixes by default, since it causes
 *       regressions in some ordinary PSD file loading.
 *
 *  2004-01-06 / v3.0.12 / Adam D. Moss
 *       Try to avoid 0-sized drawables (including channels) in all
 *       circumstances under GIMP 2.
 *
 *  2004-01-01 / v3.0.11 / Daniel Rogers <dsrogers@phaseveloctiy.org>
 *       GIMP crashes on 0x0 layers, so we skip them.
 *       
 *  2003-11-27 / v3.0.10 / Adam D. Moss
 *       GIMP 1.3/2.0 needs its layer/channel names to be UTF8 or it
 *       fails wackily, so convert the strings from the PSD file to
 *       UTF8 instead of using them raw.
 *
 *  2003-10-05 / v3.0.9 / Morten Eriksen
 *       Fixed memory corruption bug: too little memory was allocated
 *       for the bitmap image buffer if (imgwidth % 8 != 0) for
 *       monocolor PSD images.
 *
 *  2003-08-31 / v3.0.8 / applied (modified) patch from Andy Wallis
 *       Fix for handling of layer masks. See bug #68538.
 *
 *  2003-06-16 / v3.0.7 / Adam D. Moss
 *       Avoid memory corruption when things get shot to hell in the
 *       image unpacking phase.  Major version bumped to distinguish
 *       GIMP 1.3 development thread.
 *
 *  2000-08-23 / v2.0.6 / Adam D. Moss
 *       Eliminate more debugging output (don't people have more
 *       substantial problems to report?  I'm poised at my keyboard).
 *
 *  1999-11-14 / v2.0.5 / Adam D. Moss
 *       Applied patch by Andy Hefner to load 1-bit images.
 *
 *  1999-08-13 / v2.0.4 / Adam D. Moss
 *       Allowed NULL layer names again, whee.  (Also fixed the time machine.)
 *
 *  1999-08-20 / v2.0.3 / Adam D. Moss
 *       Ensure that NULL name does not get passed to gimp_layer_new(),
 *       or it will fail to create the layer and cause problems down
 *       the line (only since April 1999).
 *
 *  1999-01-18 / v2.0.2 / Adam D. Moss
 *       Better guess at how PSD files store Guide position precision.
 *
 *  1999-01-10 / v2.0.1 / Adam D. Moss
 *       Greatly reduced memory requirements for layered image loading -
 *       we now do just-in-time channel unpacking.  Some little
 *       cleanups too.
 *
 *  1998-09-04 / v2.0.0 / Adam D. Moss
 *       Now recognises and loads the new Guides extensions written
 *       by Photoshop 4 and 5.
 *
 *  1998-07-31 / v1.9.9.9f / Adam D. Moss
 *       Use GIMP_OVERLAY_MODE if available.
 *
 *  1998-07-31 / v1.9.9.9e / Adam D. Moss
 *       Worked around some buggy PSD savers (suspect PS4 on Mac) - ugh.
 *       Fixed a bug when loading layer masks of certain dimensions.
 *
 *  1998-05-04 / v1.9.9.9b / Adam D. Moss
 *       Changed the Pascal-style string-reading stuff.  That fixed
 *       some file-padding problems.  Made all debugging output
 *       compile-time optional (please leave it enabled for now).
 *       Reduced memory requirements; still much room for improvement.
 *
 *  1998-04-28 / v1.9.9.9 / Adam D. Moss
 *       Fixed the correct channel interlacing of 'raw' flat images.
 *       Thanks to Christian Kirsch and Jay Cox for spotting this.
 *       Changed some of the I/O routines.
 *
 *  1998-04-26 / v1.9.9.8 / Adam D. Moss
 *       Implemented Aux-channels for layered files.  Got rid
 *       of <endian.h> nonsense.  Improved Layer Mask padding.
 *       Enforced num_layers/num_channels limit checks.
 *
 *  1998-04-23 / v1.9.9.5 / Adam D. Moss
 *       Got Layer Masks working, got Aux-channels working
 *       for unlayered files, fixed 'raw' channel loading, fixed
 *       some other mini-bugs, slightly better progress meters.
 *       Thanks to everyone who is helping with the testing!
 *
 *  1998-04-21 / v1.9.9.1 / Adam D. Moss
 *       A little cleanup.  Implemented Layer Masks but disabled
 *       them again - PS masks can be a different size to their
 *       owning layer, unlike those in GIMP.
 *
 *  1998-04-19 / v1.9.9.0 / Adam D. Moss
 *       Much happier now.
 *
 *  1997-03-13 / v1.9.0 / Adam D. Moss
 *       Layers, channels and masks, oh my.
 *       + Bugfixes & rearchitecturing.
 *
 *  1997-01-30 / v1.0.12 / Torsten Martinsen
 *       Flat PSD image loading.
 */

/*
 * TODO:
 *
 *      Crush 16bpp channels *
 *	CMYK -> RGB *
 *      * I don't think these should be done lossily -- wait for
 *        GIMP to be able to support them natively.
 *
 *      Read in the paths.
 *
 *      File saving (someone has an alpha plugin for this)
 */

/*
 * BUGS:
 *
 *      Sometimes creates a superfluous aux channel?  Harmless.
 */



/* *** USER DEFINES *** */

/* set to TRUE if you want debugging, FALSE otherwise */
#define PSD_DEBUG TRUE

/* the max number of channels that this plugin should let a layer have */
#define MAX_CHANNELS 30

/* set to TRUE to allow a fix for transparency in PSD files
   generated by PanoTools that unfortunately causes regressions
   in some other ordinary files saved by Photoshop. */
#define PANOTOOLS_FIX FALSE

/* *** END OF USER DEFINES *** */



#define IFDBG if (PSD_DEBUG)

#include "config.h"

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>

#include <glib.h>

#include <libgimp/gimp.h>

#include "libgimp/stdplugins-intl.h"


/* Local types etc
 */
typedef enum
{
  PSD_UNKNOWN_IMAGE,
  PSD_RGB_IMAGE,
  PSD_RGBA_IMAGE,
  PSD_GRAY_IMAGE,
  PSD_GRAYA_IMAGE,
  PSD_INDEXED_IMAGE,
  PSD_INDEXEDA_IMAGE,
  PSD_BITMAP_IMAGE
} psd_imagetype;


typedef struct PsdChannel
{
  gchar *name;
  guchar *data;
  gint type;

  guint32 compressedsize;

  fpos_t fpos; /* Remember where the data is in the file, so we can
		  come back to it! */

 /* We can't just assume that the channel's width and height are the
  * same as those of the layer that owns the channel, since this
  * channel may be a layer mask, which Photoshop allows to have a
  * different size from the layer which it applies to.
  */
  guint32 width;
  guint32 height;

} PSDchannel;

typedef struct PsdGuide
{
  gboolean horizontal; /* else vertical */
  gint position;
} PSDguide;

typedef struct PsdLayer
{
  gint num_channels;
  PSDchannel *channel;

  gint32 x;
  gint32 y;
  guint32 width;
  guint32 height;

  gchar blendkey[4];
  guchar opacity;
  gchar clipping;

  gboolean protecttrans;
  gboolean visible;

  gchar* name;

  gint32 lm_x;
  gint32 lm_y;
  gint32 lm_width;
  gint32 lm_height;

} PSDlayer;


typedef gint32 Fixed;

typedef struct
{
  Fixed hRes;
  gint16 hRes_unit;
  gint16 widthUnit;

  Fixed vRes;
  gint16 vRes_unit;
  gint16 heightUnit;

/* Res_unit :
	1 == Pixels per inch
        2 == Pixels per cm
*/

} PSDresolution;

typedef struct PsdImage
{
  gint num_layers;
  PSDlayer *layer;

  gboolean absolute_alpha;

  gint type;

  gulong colmaplen;
  guchar *colmapdata;

  guint num_aux_channels;
  PSDchannel aux_channel[MAX_CHANNELS];

  guint num_guides;
  PSDguide *guides;

  gchar *caption;

  guint active_layer_num;

  guint resolution_is_set;
  PSDresolution resolution;
} PSDimage;

/* Declare some local functions.
 */
static void   query      (void);
static void   run        (const gchar      *name,
                          gint              nparams,
                          const GimpParam  *param,
                          gint             *nreturn_vals,
                          GimpParam       **return_vals);

static GimpImageType  psd_type_to_gimp_type      (psd_imagetype  psdtype);
static GimpImageBaseType     psd_type_to_gimp_base_type (psd_imagetype  psdtype);
static GimpLayerModeEffects     psd_lmode_to_gimp_lmode    (gchar          modekey[4]);
static GimpUnit       psd_unit_to_gimp_unit      (gint           psdunit);

static gint32         load_image                 (const gchar  *filename);



/* Various local variables...
 */
GimpPlugInInfo PLUG_IN_INFO =
{
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
  query, /* query_proc */
  run,   /* run_proc   */
};


static PSDimage psd_image;


static struct
{
  gchar    signature[4];
  gushort  version;
  guchar   reserved[6];
  gushort  channels;
  gulong   rows;
  gulong   columns;
  gushort  bpp;
  gushort  mode;
  gulong   imgreslen;
  gulong   miscsizelen;
  gushort  compression;
  gushort *rowlength;
  long     imgdatalen;
} PSDheader;


static gchar * modename[] =
{
  "Bitmap",
  "Grayscale",
  "Indexed Colour",
  "RGB Colour",
  "CMYK Colour",
  "<invalid>",
  "<invalid>",
  "Multichannel",
  "Duotone",
  "Lab Colour",
  "<invalid>"
};


static const gchar *prog_name = "PSD";


static void unpack_pb_channel(FILE *fd, guchar *dst, gint32 unpackedlen,
				  guint32 *offset);
static void decode(long clen, long uclen, guchar *src, guchar *dst, int step);
static void packbitsdecode(long *clenp, long uclen,
			   guchar *src, guchar *dst, int step);
static void cmyk2rgb(guchar *src, guchar *destp,
		     long width, long height, int alpha);
static void cmykp2rgb(guchar *src, guchar *destp,
		      long width, long height, int alpha);
static void bitmap2gray(guchar *src,guchar *dest,long w,long h);
static guchar getguchar(FILE *fd, gchar *why);
static gshort getgshort(FILE *fd, gchar *why);
static glong getglong(FILE *fd, gchar *why);
static void xfread(FILE *fd, void *buf, long len, gchar *why);
static void xfread_interlaced(FILE *fd, guchar *buf, long len, gchar *why,
			      gint step);
static void read_whole_file(FILE *fd);
static void reshuffle_cmap(guchar *map256);
static gchar* getpascalstring(FILE *fd, gchar *why);
static gchar* getstring(size_t n, FILE * fd, gchar *why);
static void throwchunk(size_t n, FILE * fd, gchar *why);
static void dumpchunk(size_t n, FILE * fd, gchar *why);
static void seek_to_and_unpack_pixeldata(FILE* fd, gint layeri, gint channeli);


MAIN ()


static void
query (void)
{
  static GimpParamDef load_args[] =
  {
    { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
    { GIMP_PDB_STRING, "filename", "The name of the file to load" },
    { GIMP_PDB_STRING, "raw_filename", "The name of the file to load" }
  };
  static GimpParamDef load_return_vals[] =
  {
    { GIMP_PDB_IMAGE, "image", "Output image" }
  };

  gimp_install_procedure ("file_psd_load",
                          "loads files of the Photoshop(tm) PSD file format",
                          "This filter loads files of Adobe Photoshop(tm) native PSD format.  These files may be of any image type supported by GIMP, with or without layers, layer masks, aux channels and guides.",
                          "Adam D. Moss & Torsten Martinsen",
                          "Adam D. Moss & Torsten Martinsen",
                          "1996-1998",
                          "<Load>/PSD",
			  NULL,
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (load_args),
                          G_N_ELEMENTS (load_return_vals),
                          load_args, load_return_vals);

  gimp_register_magic_load_handler ("file_psd_load",
				    "psd",
				    "",
				    "0,string,8BPS");
}


static void
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
{
  static GimpParam values[2];
  GimpRunMode run_mode;
  /*  GimpPDBStatusType status = GIMP_PDB_SUCCESS;*/
  gint32 image_ID;

  run_mode = param[0].data.d_int32;

  *nreturn_vals = 1;
  *return_vals = values;
  values[0].type = GIMP_PDB_STATUS;
  values[0].data.d_status = GIMP_PDB_CALLING_ERROR;

  if (strcmp (name, "file_psd_load") == 0)
    {
      image_ID = load_image (param[1].data.d_string);

      if (image_ID != -1)
	{
	  *nreturn_vals = 2;
	  values[0].data.d_status = GIMP_PDB_SUCCESS;
	  values[1].type = GIMP_PDB_IMAGE;
	  values[1].data.d_image = image_ID;
	}
      else
	{
	  values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
	}
    }
}


static char *
sanitise_string(char *old_name)
{
  if (old_name) {
    char *rtn = gimp_any_to_utf8(old_name, -1,
				 _("Invalid UTF-8 string in PSD file"));
    g_free(old_name);
    return rtn;
  } else {
    return NULL;
  }
}

static GimpImageType
psd_type_to_gimp_type (psd_imagetype psdtype)
{
  switch(psdtype)
    {
    case PSD_RGBA_IMAGE: return(GIMP_RGBA_IMAGE);
    case PSD_RGB_IMAGE: return(GIMP_RGB_IMAGE);
    case PSD_GRAYA_IMAGE: return(GIMP_GRAYA_IMAGE);
    case PSD_GRAY_IMAGE: return(GIMP_GRAY_IMAGE);
    case PSD_INDEXEDA_IMAGE: return(GIMP_INDEXEDA_IMAGE);
    case PSD_INDEXED_IMAGE: return(GIMP_INDEXED_IMAGE);
    case PSD_BITMAP_IMAGE: return(GIMP_GRAY_IMAGE);
    default: return(GIMP_RGB_IMAGE);
    }
}

static GimpLayerModeEffects
psd_lmode_to_gimp_lmode (gchar modekey[4])
{
  if (strncmp(modekey, "norm", 4)==0) return(GIMP_NORMAL_MODE);
  if (strncmp(modekey, "dark", 4)==0) return(GIMP_DARKEN_ONLY_MODE);
  if (strncmp(modekey, "lite", 4)==0) return(GIMP_LIGHTEN_ONLY_MODE);
  if (strncmp(modekey, "hue ", 4)==0) return(GIMP_HUE_MODE);
  if (strncmp(modekey, "sat ", 4)==0) return(GIMP_SATURATION_MODE);
  if (strncmp(modekey, "colr", 4)==0) return(GIMP_COLOR_MODE);
  if (strncmp(modekey, "mul ", 4)==0) return(GIMP_MULTIPLY_MODE);
  if (strncmp(modekey, "scrn", 4)==0) return(GIMP_SCREEN_MODE);
  if (strncmp(modekey, "diss", 4)==0) return(GIMP_DISSOLVE_MODE);
  if (strncmp(modekey, "diff", 4)==0) return(GIMP_DIFFERENCE_MODE);
  if (strncmp(modekey, "lum ", 4)==0) return(GIMP_VALUE_MODE);

#if (GIMP_MAJOR_VERSION > 0) && (GIMP_MINOR_VERSION > 0)
  if (strncmp(modekey, "over", 4)==0) return(GIMP_OVERLAY_MODE);

  printf("PSD: Warning - unsupported layer-blend mode '%c%c%c%c', using "
	 "'overlay' mode\n", modekey[0], modekey[1], modekey[2], modekey[3]);
  if (strncmp(modekey, "hLit", 4)==0) return(/**/GIMP_OVERLAY_MODE);
  if (strncmp(modekey, "sLit", 4)==0) return(/**/GIMP_OVERLAY_MODE);
#else
  printf("PSD: Warning - unsupported layer-blend mode '%c%c%c%c', using "
	 "'addition' mode\n", modekey[0], modekey[1], modekey[2], modekey[3]);
  if (strncmp(modekey, "over", 4)==0) return(GIMP_ADDITION_MODE); /* ? */
  if (strncmp(modekey, "hLit", 4)==0) return(/**/GIMP_ADDITION_MODE);
  if (strncmp(modekey, "sLit", 4)==0) return(/**/GIMP_ADDITION_MODE);
#endif

  printf("PSD: Warning - UNKNOWN layer-blend mode, reverting to 'normal'\n");

  return(GIMP_NORMAL_MODE);
}

static GimpImageBaseType
psd_type_to_gimp_base_type (psd_imagetype psdtype)
{
  switch(psdtype)
    {
    case PSD_RGBA_IMAGE:
    case PSD_RGB_IMAGE: return(GIMP_RGB);
    case PSD_BITMAP_IMAGE:
    case PSD_GRAYA_IMAGE:
    case PSD_GRAY_IMAGE: return(GIMP_GRAY);
    case PSD_INDEXEDA_IMAGE:
    case PSD_INDEXED_IMAGE: return(GIMP_INDEXED);
    default:
      g_message ("Error: Can't convert PSD imagetype to GIMP imagetype");
      gimp_quit();
      return(GIMP_RGB);
    }
}

static GimpUnit
psd_unit_to_gimp_unit (int psdunit)
{
  switch (psdunit)
    {
    case 1:
      return GIMP_UNIT_INCH;
    case 2: /* this means cm to PS, but MM is as close as we have */
      return GIMP_UNIT_MM;
    case 3:
      return GIMP_UNIT_POINT;
    case 4:
      return GIMP_UNIT_PICA;
    case 5: /* 5 == Columns, but what the heck is a column? */
    default:
      IFDBG printf("Warning: unable to convert psd unit %d to gimp unit\n", psdunit);
      return GIMP_UNIT_PIXEL;
    }
}

static GimpImageBaseType
psd_mode_to_gimp_base_type (gushort psdtype)
{
  switch(psdtype)
    {
    case 1: return GIMP_GRAY;
    case 2: return GIMP_INDEXED;
    case 3: return GIMP_RGB;
    default:
      g_message ("Error: Can't convert PSD mode to GIMP base imagetype");
      gimp_quit();
      return GIMP_RGB;
    }
}

static void
reshuffle_cmap (guchar *map256)
{
  guchar *tmpmap;
  int i;

  tmpmap = g_malloc(3 * 256);

  for (i = 0; i < 256; i++)
    {
      tmpmap[i*3  ] = map256[i];
      tmpmap[i*3+1] = map256[i+256];
      tmpmap[i*3+2] = map256[i+512];
    }

  memcpy(map256, tmpmap, 3 * 256);
  g_free(tmpmap);
}

static void
dispatch_resID(guint ID, FILE *fd, guint32 *offset, guint32 Size)
{
  if ( (ID < 0x0bb6) && (ID >0x07d0) )
    {
      IFDBG printf ("\t\tPath data is irrelevant to GIMP at this time.\n");
      throwchunk(Size, fd, "dispatch_res path throw");
      (*offset) += Size;
    }
  else
    switch (ID)
      {
      case 0x03ee:
	{
	  gint32 remaining = Size;

	  IFDBG printf ("\t\tALPHA CHANNEL NAMES:\n");
	  if (Size > 0)
	    {
	      do
		{
		  guchar slen;
		  gchar* sname;

		  slen = getguchar (fd, "alpha channel name length");
		  (*offset)++;
		  remaining--;

		  /* Check for (Mac?) Photoshop (4?) file-writing bug */
		  if (slen > remaining)
		    {
		      IFDBG {printf("\nYay, a file bug.  "
				    "Yuck.  Photoshop 4/Mac?  "
				    "I'll work around you.\n");fflush(stdout);}
		      break;
		    }

		  if (slen)
		    {
		      sname = getstring(slen, fd, "alpha channel name");

		      sname = sanitise_string(sname);

		      psd_image.aux_channel[psd_image.num_aux_channels].name =
			sname;
		    }
		  else
		    {
		      psd_image.aux_channel[psd_image.num_aux_channels].name =
			NULL;
		    }

		  if (psd_image.aux_channel[psd_image.num_aux_channels].name
		      == NULL)
		    {
		      IFDBG
			{printf("\t\t\tNull channel name %d.\n",
				psd_image.num_aux_channels);fflush(stdout);}
		    }

		  if (psd_image.aux_channel[psd_image.num_aux_channels].name)
		    {
		      guint32 alpha_name_len;

		      alpha_name_len =
			strlen(psd_image.aux_channel[psd_image.num_aux_channels].name);

		      IFDBG printf("\t\t\tname: \"%s\"\n",
				   psd_image.aux_channel[psd_image.num_aux_channels].name);

		      (*offset) += alpha_name_len;
		      remaining -= alpha_name_len;
		    }

		  psd_image.num_aux_channels++;

		  if (psd_image.num_aux_channels > MAX_CHANNELS)
		    {
		      printf("\nPSD: Sorry - this image has too many "
			     "aux channels.  Tell Adam!\n");
		      gimp_quit();
		    }
		}
	      while (remaining > 0);
	    }

	  if (remaining)
	    {
	      IFDBG
		dumpchunk(remaining, fd, "alphaname padding 0 throw");
              else
		throwchunk(remaining, fd, "alphaname padding 0 throw");
	      (*offset) += remaining;
	      remaining = 0;
	    }
	}
	break;
      case 0x03ef:
	IFDBG printf("\t\tDISPLAYINFO STRUCTURE: unhandled\n");
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;
      case 0x03f0: /* FIXME: untested */
	{
	  psd_image.caption = getpascalstring(fd, "caption string");
	  (*offset)++;

	  if (psd_image.caption)
	    {
	      IFDBG printf("\t\t\tcontent: \"%s\"\n",psd_image.caption);
	      (*offset) += strlen(psd_image.caption);
	    }
	}
	break;
      case 0x03f2:
	IFDBG printf("\t\tBACKGROUND COLOR: unhandled\n");
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;
      case 0x03f4:
	IFDBG printf("\t\tGREY/MULTICHANNEL HALFTONING INFO: unhandled\n");
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;
      case 0x03f5:
	IFDBG printf("\t\tCOLOUR HALFTONING INFO: unhandled\n");
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;
      case 0x03f6:
	IFDBG printf("\t\tDUOTONE HALFTONING INFO: unhandled\n");
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;
      case 0x03f7:
	IFDBG printf("\t\tGREYSCALE/MULTICHANNEL TRANSFER FUNCTION: unhandled\n");
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;
      case 0x03f8:
	IFDBG printf("\t\tCOLOUR TRANSFER FUNCTION: unhandled\n");
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;
      case 0x03f9:
	IFDBG printf("\t\tDUOTONE TRANSFER FUNCTION: unhandled\n");
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;
      case 0x03fa:
	IFDBG printf("\t\tDUOTONE IMAGE INFO: unhandled\n");
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;
      case 0x03fb:
	IFDBG printf("\t\tEFFECTIVE BLACK/WHITE VALUES: unhandled\n");
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;
      case 0x03fe:
	IFDBG printf("\t\tQUICK MASK INFO: unhandled\n");
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;
      case 0x0400:
	{
	  IFDBG printf("\t\tLAYER STATE INFO:\n");
	  psd_image.active_layer_num = getgshort(fd, "ID target_layer_num");

	  IFDBG printf("\t\t\ttarget: %d\n",(gint)psd_image.active_layer_num);
	  (*offset) += 2;
	}
	break;
      case 0x0402:
	IFDBG printf("\t\tLAYER GROUP INFO: unhandled\n");
	IFDBG printf("\t\t\t(Inferred number of layers: %d)\n",(gint)(Size/2));
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;
      case 0x0405:
	IFDBG printf ("\t\tIMAGE MODE FOR RAW FORMAT: unhandled\n");
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;
      case 0x0408:
	{
	  gint32 remaining = Size;
	  int i;
	  IFDBG printf ("\t\tGUIDE INFORMATION:\n");

	  if (Size > 0)
	    {
	      gshort magic1, magic2, magic3, magic4, magic5, magic6;
	      glong num_guides;
	      PSDguide *guide;

	      magic1 = getgshort(fd, "guide"); (*offset) += 2;
	      magic2 = getgshort(fd, "guide"); (*offset) += 2;
	      magic3 = getgshort(fd, "guide"); (*offset) += 2;
	      magic4 = getgshort(fd, "guide"); (*offset) += 2;
	      magic5 = getgshort(fd, "guide"); (*offset) += 2;
	      magic6 = getgshort(fd, "guide"); (*offset) += 2;
	      remaining -= 12;

	      IFDBG printf("\t\t\tMagic: %d %d %d %d %d %d\n",
			   magic1, magic2, magic3, magic4, magic5, magic6);
	      IFDBG printf("\t\t\tMagic: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n",
			   magic1, magic2, magic3, magic4, magic5, magic6);

	      num_guides = getglong(fd, "guide");
	      (*offset) += 4; remaining -= 4;

	      if (remaining != num_guides*5)
		{
		  IFDBG printf ("** FUNNY AMOUNT OF GUIDE DATA (%d)\n",
				remaining);
		  goto funny_amount_of_guide_data;
		}

	      IFDBG printf("\t\t\tNumber of guides is %ld\n", num_guides);
	      psd_image.num_guides = num_guides;
	      psd_image.guides = g_new(PSDguide, num_guides);
	      guide = psd_image.guides;

	      for (i = 0; i < num_guides; i++, guide++)
		{
		  guide->position = getglong(fd, "guide");

		  guide->horizontal = (1 == getguchar(fd, "guide"));
		  (*offset) += 5; remaining -= 5;

		  if (guide->horizontal)
		    {
		      guide->position =
			RINT((double)(guide->position * (magic4>>8))
			     /(double)(magic4&255));
		    }
		  else
		    {
		      guide->position =
			RINT((double)(guide->position * (magic6>>8))
			     /(double)(magic6&255));
		    }

		  IFDBG printf("\t\t\tGuide %d at %d, %s\n", i+1,
			       guide->position,
			       guide->horizontal ? "horizontal" : "vertical");
		}
	    }

	funny_amount_of_guide_data:

	  if (remaining)
	    {
	      IFDBG
		{
		  printf ("** GUIDE INFORMATION DROSS: ");
		  dumpchunk(remaining, fd, "dispatch_res");
		}
	      else
		{
		  throwchunk(remaining, fd, "dispatch_res");
		}

	      (*offset) += remaining;
	    }
	}
	break;
      case 0x03ed:
	{
	  IFDBG printf ("\t\tResolution Info:\n");
	  psd_image.resolution_is_set = 1;

	  psd_image.resolution.hRes = getglong(fd, "hRes");
	  psd_image.resolution.hRes_unit = getgshort(fd, "hRes_unit");
	  psd_image.resolution.widthUnit = getgshort(fd, "WidthUnit");
	  psd_image.resolution.vRes = getglong(fd, "vRes");
	  psd_image.resolution.vRes_unit = getgshort(fd, "vRes_unit");
	  psd_image.resolution.heightUnit = getgshort(fd, "HeightUnit");
	  (*offset) += Size;
	  IFDBG  printf("\t\t\tres = %f, %f\n",
			psd_image.resolution.hRes / 65536.0,
			psd_image.resolution.vRes / 65536.0);
	} break;

      case 0x03e9:
      case 0x03f1:
      case 0x03f3:
      case 0x03fd:
      case 0x0401:
      case 0x0404:
      case 0x0406:
      case 0x0bb7:
      case 0x2710:
	IFDBG printf ("\t\t<Field is irrelevant to GIMP at this time.>\n");
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;

      case 0x03e8:
      case 0x03eb:
	IFDBG printf ("\t\t<Obsolete Photoshop 2.0 field.>\n");
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;

      case 0x03fc:
      case 0x03ff:
      case 0x0403:
	IFDBG printf ("\t\t<Obsolete field.>\n");
	throwchunk(Size, fd, "dispatch_res");
	(*offset) += Size;
	break;

      default:
	IFDBG
	  {
	    printf ("\t\t<Undocumented field.>\n");
	    dumpchunk(Size, fd, "dispatch_res");
	  }
	else
	  throwchunk(Size, fd, "dispatch_res");

	(*offset) += Size;
	break;
      }
}

static void
do_layer_record(FILE *fd, guint32 *offset, gint layernum)
{
  PSDlayer *layer;
  gint32 top, left, bottom, right;
  guint32 extradatasize, layermaskdatasize, layerrangesdatasize;
  guint32 totaloff;
  gchar sig[4];
  guchar flags;
  gint i;

  IFDBG printf("\t\t\tLAYER RECORD (layer %d)\n", (int)layernum);

  layer = psd_image.layer + layernum;

  /* table 11-12 */
  top = getglong (fd, "layer top");
  (*offset)+=4;
  left = getglong (fd, "layer left");
  (*offset)+=4;
  bottom = getglong (fd, "layer bottom");
  (*offset)+=4;
  right = getglong (fd, "layer right");
  (*offset)+=4;

  layer->x = left;
  layer->y = top;
  layer->width = right - left;
  layer->height = bottom - top;

  IFDBG printf("\t\t\t\tLayer extents: (%d,%d) -> (%d,%d)\n",
	       left,top,right,bottom);

  layer->num_channels = getgshort (fd, "layer num_channels");
  (*offset)+=2;

  IFDBG printf("\t\t\t\tNumber of channels: %d\n",
	       (int)layer->num_channels);

  if (layer->num_channels)
    {
      layer->channel = g_new(PSDchannel, layer->num_channels);
      
      for (i = 0; i < layer->num_channels; i++)
	{
	  PSDchannel *channel = layer->channel + i;
	  
	  /* table 11-13 */
	  IFDBG printf("\t\t\t\tCHANNEL LENGTH INFO (%d)\n", i);
	  
	  channel->type = getgshort(fd, "channel id");
	  (*offset)+=2;
	  IFDBG printf("\t\t\t\t\tChannel TYPE: %d\n", channel->type);
	  
	  channel->compressedsize = getglong(fd, "channeldatalength");
	  (*offset)+=4;
	  IFDBG printf("\t\t\t\t\tChannel Data Length: %d\n",
		       channel->compressedsize);
	}
    } else {
      IFDBG printf("\t\t\t\tOo-er, layer has no channels.  Hmm.\n");
    }

  xfread(fd, sig, 4, "layer blend sig");
  (*offset)+=4;

  if (strncmp(sig, "8BIM", 4)!=0)
    {
      IFDBG printf("\t\t\t(layer blend signature '%c%c%c%c' is incorrect: quitting)\n", sig[0], sig[1], sig[2], sig[3]);
      g_message ("Error: layer blend signature is incorrect. :-(");
      gimp_quit();
    }

  xfread(fd, layer->blendkey, 4, "layer blend key");
  (*offset)+=4;

  IFDBG printf("\t\t\t\tBlend type: PSD(\"%c%c%c%c\") = GIMP(%d)\n",
	       layer->blendkey[0],
	       layer->blendkey[1],
	       layer->blendkey[2],
	       layer->blendkey[3],
	       psd_lmode_to_gimp_lmode(layer->blendkey));

  layer->opacity = getguchar(fd, "layer opacity");
  (*offset)++;

  IFDBG printf("\t\t\t\tLayer Opacity: %d\n", layer->opacity);

  layer->clipping = getguchar(fd, "layer clipping");
  (*offset)++;

  IFDBG printf("\t\t\t\tLayer Clipping: %d (%s)\n",
	       layer->clipping,
	       layer->clipping == 0 ? "base" : "non-base");

  flags = getguchar(fd, "layer flags");
  (*offset)++;

  IFDBG printf("\t\t\t\tLayer Flags: %d (%s, %s)\n", flags,
	 flags&1?"preserve transparency":"don't preserve transparency",
	 flags&2?"visible":"not visible");

  layer->protecttrans = (flags & 1) ? TRUE : FALSE;
  layer->visible = (flags & 2) ? FALSE : TRUE;

  getguchar(fd, "layer record filler");
  (*offset)++;

  extradatasize = getglong(fd, "layer extra data size");
  (*offset)+=4;
  IFDBG printf("\t\t\t\tEXTRA DATA SIZE: %d\n",extradatasize);

  /* FIXME: should do something with this data */
  /*throwchunk(extradatasize, fd, "layer extradata throw");
  (*offset) += extradatasize;*/

  totaloff = (*offset) + extradatasize;

  /* table 11-14 */
  layermaskdatasize = getglong(fd, "layer mask data size");
  (*offset)+=4;
  IFDBG printf("\t\t\t\t\tLAYER MASK DATA SIZE: %d\n", layermaskdatasize);

  if (layermaskdatasize)
    {
      top    = getglong(fd, "lmask top");
      (*offset) += 4;
      left   = getglong(fd, "lmask left");
      (*offset) += 4;
      bottom = getglong(fd, "lmask bottom");
      (*offset) += 4;
      right  = getglong(fd, "lmask right");
      (*offset) += 4;

      layer->lm_x = left;
      layer->lm_y = top;
      layer->lm_width = right - left;
      layer->lm_height = bottom - top;

      getglong(fd, "lmask data throw");
      (*offset) += 4;

      /*      throwchunk(layermaskdatasize, fd, "layer mask data throw");
      (*offset) += layermaskdatasize;*/
    }

  layerrangesdatasize = getglong(fd, "layer ranges data size");
  (*offset)+=4;
  IFDBG printf("\t\t\t\t\t\tLAYER RANGES DATA SIZE: %d\n",layermaskdatasize);

  if (layerrangesdatasize)
    {
      throwchunk(layerrangesdatasize, fd, "layer ranges data throw");
      (*offset) += layerrangesdatasize;
    }

  layer->name = getpascalstring(fd, "layer name");
  layer->name = sanitise_string(layer->name);
  (*offset)++;

  if (layer->name)
    {
      (*offset) += strlen(layer->name);
      IFDBG printf("\t\t\t\t\t\tLAYER NAME: '%s'\n", layer->name);
    }
  else
    {
      IFDBG printf("\t\t\t\t\t\tNULL LAYER NAME\n");
    }
  /* If no layermask data - set offset and size from layer data */
  if (! layermaskdatasize)
    {
      IFDBG
        fprintf(stderr, "Setting layer mask data layer\n");

      psd_image.layer[layernum].lm_x = psd_image.layer[layernum].x;
      psd_image.layer[layernum].lm_y = psd_image.layer[layernum].y;
      psd_image.layer[layernum].lm_width =  psd_image.layer[layernum].width;
      psd_image.layer[layernum].lm_height = psd_image.layer[layernum].height;
    }

  if (totaloff-(*offset) > 0)
    {
      IFDBG
	{
	  printf("Warning: layer record dross: ");
	  dumpchunk(totaloff-(*offset), fd, "layer record dross throw");
	}
      else
	{
	  throwchunk(totaloff-(*offset), fd, "layer record dross throw");
	}
      (*offset) = totaloff;
    }
}

static void
do_layer_struct(FILE *fd, guint32 *offset)
{
  gint i;

  IFDBG printf("\t\tLAYER STRUCTURE SECTION\n");

  psd_image.num_layers = getgshort(fd, "layer struct numlayers");
  (*offset)+=2;

  IFDBG printf("\t\t\tCanonical number of layers: %d%s\n",
	 psd_image.num_layers>0?
	 (int)psd_image.num_layers:abs(psd_image.num_layers),
	 psd_image.num_layers>0?"":" (absolute/alpha)");

  if (psd_image.num_layers < 0)
    {
      psd_image.num_layers = -psd_image.num_layers;
      psd_image.absolute_alpha = TRUE;
    }
  else
    {
      psd_image.absolute_alpha = FALSE;
    }

  psd_image.layer = g_new(PSDlayer, psd_image.num_layers);

  for (i = 0; i < psd_image.num_layers; i++)
    {
      do_layer_record(fd, offset, i);
    }
}

static void
do_layer_pixeldata(FILE *fd, guint32 *offset)
{
  gint layeri, channeli;

  for (layeri = 0; layeri < psd_image.num_layers; layeri++)
    {
      PSDlayer *layer = psd_image.layer + layeri;

      for (channeli = 0; channeli < layer->num_channels; channeli++)
	{
	  PSDchannel *channel = layer->channel + channeli;

	  if (channel->type == -2)
	    {
	      channel->width = layer->lm_width;
	      channel->height = layer->lm_height;
	    }
	  else
	    {
	      channel->width = layer->width;
	      channel->height = layer->height;
	    }

	  fgetpos(fd, &channel->fpos);

	  throwchunk(channel->compressedsize, fd, "channel data skip");
	  (*offset) += channel->compressedsize;
	}
    }
}

static void
seek_to_and_unpack_pixeldata(FILE* fd, gint layeri, gint channeli)
{
  int width, height;
  guchar *tmpline;
  gint compression;
  guint32 offset = 0;
  PSDchannel *channel = &psd_image.layer[layeri].channel[channeli];

  fsetpos(fd, &channel->fpos);

  compression = getgshort(fd, "layer channel compression type");
  offset+=2;

  width = channel->width;
  height = channel->height;

  IFDBG
    {
      printf("\t\t\tLayer (%d) Channel (%d:%d) Compression: %d (%s)\n",
	     layeri,
	     channeli,
	     channel->type,
	     compression,
	     compression==0?"raw":(compression==1?"RLE":"*UNKNOWN!*"));

      fflush(stdout);
    }

  channel->data = g_malloc (width * height);

  tmpline = g_malloc(width + 1);

  switch (compression)
    {
    case 0: /* raw data */
      {
	gint linei;

	for (linei = 0; linei < height; linei++)
	  {
	    xfread(fd, channel->data + linei * width, width,
		   "raw channel line");
	    offset += width;
	  }

#if 0
	/* Pad raw data to multiple of 2? */
	if ((height * width) & 1)
	  {
	    getguchar(fd, "raw channel padding");
	    offset++;
	  }
#endif
      }
      break;
    case 1: /* RLE, one row at a time, padded to an even width */
      {
	gint linei;
	gint blockread;

	/* we throw this away because in theory we can trust the
	   data to unpack to the right length... hmm... */
	throwchunk(height * 2, fd, "widthlist");
	offset += height * 2;

	blockread = offset;

	/*IFDBG {printf("\nHere comes the guitar solo...\n");
	  fflush(stdout);}*/

	for (linei=0; linei<height; linei++)
	  {
	    /*printf(" %d ", *offset);*/
	    unpack_pb_channel(fd, tmpline,
			      width
			      /*+ (width&1)*/,
			      &offset);
	    memcpy(channel->data + linei * width, tmpline, width);
	  }

	IFDBG {printf("\t\t\t\t\tActual compressed size was %d bytes\n"
		      , offset-blockread);fflush(stdout);}
      }
      break;
    default: /* *unknown* */
      IFDBG {printf("\nEEP!\n");fflush(stdout);}
      g_message ("*** Unknown compression type in channel.");
      gimp_quit();
      break;
    }
  g_free(tmpline);
}

static void
do_layers(FILE *fd, guint32 *offset)
{
  guint32 section_length;

  section_length = getglong(fd, "layerinfo sectionlength");
  (*offset)+=4;

  IFDBG printf("\tLAYER INFO SECTION\n");
  IFDBG printf("\t\tSECTION LENGTH: %u\n",section_length);

  do_layer_struct(fd, offset);

  do_layer_pixeldata(fd, offset);
}

static void
do_layer_and_mask(FILE *fd)
{
  guint32 offset = 0;
  guint32 Size = PSDheader.miscsizelen;

  guint32 offset_now = ftell(fd);

  IFDBG printf("LAYER AND MASK INFO\n");
  IFDBG printf("\tSECTION LENGTH: %u\n",Size);

  if (Size == 0) return;

  do_layers(fd, &offset);

  IFDBG {printf("And...?\n");fflush(stdout);}

  if (offset < Size)
    {
      IFDBG
	{
	  printf("PSD: Supposedly there are %d bytes of mask info left.\n",
		 Size-offset);
	  if ((Size-offset == 4) || (Size-offset == 24))
	    printf("     That sounds good to me.\n");
	  else
	    printf("     That sounds strange to me.\n");
	}


      /*      if ((getguchar(fd, "mask info throw")!=0) ||
	  (getguchar(fd, "mask info throw")!=0) ||
	  (getguchar(fd, "mask info throw")!=0) ||
	  (getguchar(fd, "mask info throw")!=0))
	{
	  printf("*** This mask info block looks pretty bogus.\n");
	}*/
    }
  else
    printf("PSD: Stern warning - no mask info.\n");


  /* If 'offset' wasn't being buggily updated, we wouldn't need this. (!?) */
  fseek(fd, Size+offset_now, SEEK_SET);
}

static void
do_image_resources(FILE *fd)
{
  guint16 ID;
  gchar *Name;
  guint32 Size;

  guint32 offset = 0;

  IFDBG printf("IMAGE RESOURCE BLOCK:\n");

  psd_image.resolution_is_set = 0;

  /* FIXME: too trusting that the file isn't corrupt */
  while (offset < PSDheader.imgreslen-1)
    {
      gchar sig[4];

      xfread(fd, sig, 4, "imageresources signature");

      if (strncmp(sig, "8BIM", 4) != 0)
	{
	  printf("PSD: Error - imageresources block has invalid signature.\n");
	  gimp_quit();
	}
      offset += 4;

      /* generic information about a block ID */


      ID = getgshort(fd, "ID num");
      offset += 2;
      IFDBG printf("\tID: 0x%04x / ",ID);

      Name = getpascalstring(fd, "ID name");
      offset++;

      if (Name)
	{
	  IFDBG printf("\"%s\" ", Name);
	  offset += strlen(Name);

	  if (!(strlen(Name)&1))
	    {
	      throwchunk(1, fd, "ID name throw");
	      offset ++;
	    }
	  g_free(Name);
	}
      else
	{
	  throwchunk(1, fd, "ID name throw2");
	  offset++;
	}

      Size = getglong(fd, "ID Size");
      offset += 4;
      IFDBG printf("Size: %d\n", Size);

      dispatch_resID(ID, fd, &offset, Size);

      if (Size&1)
	{
	  IFDBG printf("+1");
	  throwchunk(1, fd, "ID content throw");
	  offset ++;
	}
    }

  /*  if (offset != PSDheader.imgreslen)
    {
      printf("\tSucking imageres byte...\n");
      throwchunk(1, fd, "imageres suck");
      offset ++;
    }*/

}


#if PANOTOOLS_FIX
/* Convert RGB data to RGBA data */
static guchar*
RGB_to_RGBA (const guchar* rgb_data, gint numpix)
{
  guchar* rtn;
  int i,j;

  if (!rgb_data)
    {
      printf("NULL rgb data - eep!");
      return NULL;
    }

  rtn = g_malloc(numpix * 4);

  j = 0;
  for (i=0; i<numpix; i++)
    {
      rtn[j++] = rgb_data[i*3];
      rtn[j++] = rgb_data[i*3+1];
      rtn[j++] = rgb_data[i*3+2];
      rtn[j++] = 255;
    }

  return rtn;
}
#endif /* PANOTOOLS_FIX */


static guchar*
chans_to_GRAYA (const guchar* grey,
                const guchar* alpha,
                gint numpix)
{
  guchar* rtn;
  int i;

  if (!grey || !alpha)
    {
      printf("NULL channel - eep!");
      return NULL;
    }

  rtn = g_malloc(numpix * 2);

  for (i = 0; i < numpix; i++)
    {
      rtn[i*2  ] = grey[i];
      rtn[i*2+1] = alpha[i];
    }

  return rtn;
}

static guchar*
chans_to_RGB (const guchar* red,
              const guchar* green,
              const guchar* blue,
              gint numpix)
{
  guchar* rtn;
  int i;

  if (!red || !green || !blue)
    {
      printf("NULL channel - eep!");
      return NULL;
    }

  rtn = g_malloc(numpix * 3);

  for (i=0; i<numpix; i++)
    {
      rtn[i*3  ] = red[i];
      rtn[i*3+1] = green[i];
      rtn[i*3+2] = blue[i];
    }

  return rtn;
}

static guchar*
chans_to_RGBA (const guchar* red,
               const guchar* green,
               const guchar* blue,
               const guchar* alpha,
               gint numpix)
{
  guchar* rtn;
  int i;
  gboolean careful = FALSE;

  if (!red || !green || !blue || !alpha)
    {
      printf("chans_to_RGBA : NULL channel - eep!");
      careful = TRUE;
    }

  rtn = g_malloc(numpix * 4);

  if (!careful)
    {
      for (i=0; i<numpix; i++)
	{
	  rtn[i*4  ] = red[i];
	  rtn[i*4+1] = green[i];
	  rtn[i*4+2] = blue[i];
	  rtn[i*4+3] = alpha[i];
	}
    }
  else
    {
      for (i=0; i<numpix; i++)
	{
	  rtn[i*4  ] = (red==NULL) ? 0 : red[i];
	  rtn[i*4+1] = (green==NULL) ? 0 : green[i];
	  rtn[i*4+2] = (blue==NULL) ? 0 : blue[i];
	  rtn[i*4+3] = (alpha==NULL) ? 0 : alpha[i];
	}
    }

  return rtn;
}

static
gboolean psd_layer_has_alpha(PSDlayer* layer)
{
  int i;

  for (i = 0; i < layer->num_channels; i++)
    {
      if (layer->channel[i].type == -1)
	{
	  return TRUE;
	}
    }
  return FALSE;
}

static
void extract_data_and_channels(guchar* src, gint gimpstep, gint psstep,
			       gint32 image_ID, GimpDrawable* drawable,
			       gint width, gint height)
{
  guchar* primary_data;
  guchar* aux_data;
  GimpPixelRgn pixel_rgn;

  IFDBG printf("Extracting primary channel data (%d channels)\n"
	 "\tand %d auxiliary channels.\n", gimpstep, psstep-gimpstep);

  /* gimp doesn't like 0 width/height drawables. */
  if ((width == 0) || (height == 0))
    {
      IFDBG printf("(bad channel dimensions -- skipping)");
      return;
    }

  primary_data = g_malloc(width * height * gimpstep);
  {
    int pix,chan;

    for (pix = 0; pix < width * height; pix++)
      {
	for (chan = 0; chan < gimpstep; chan++)
	  {
	    primary_data[pix * gimpstep + chan] = src[pix * psstep + chan];
	  }
      }
    gimp_pixel_rgn_init (&pixel_rgn, drawable,
			 0, 0, drawable->width, drawable->height,
			 TRUE, FALSE);
    gimp_pixel_rgn_set_rect (&pixel_rgn, primary_data,
			     0, 0, drawable->width, drawable->height);

    gimp_drawable_flush (drawable);
    gimp_drawable_detach (drawable);
  }
  g_free (primary_data);

  aux_data = g_malloc (width * height);
  {
    int pix, chan;
    gint32 channel_ID;
    GimpDrawable* chdrawable;
    GimpRGB colour;

    gimp_rgb_set (&colour, 0.0, 0.0, 0.0);

    for (chan=gimpstep; chan<psstep; chan++)
      {
	for (pix = 0; pix < width * height; pix++)
	  {
	    aux_data [pix] = src [pix * psstep + chan];
	  }
	channel_ID = gimp_channel_new (image_ID,
                                       psd_image.aux_channel[chan-gimpstep].name,
                                       width, height,
                                       100.0, &colour);
	gimp_image_add_channel (image_ID, channel_ID, 0);
	gimp_drawable_set_visible (channel_ID, FALSE);

	chdrawable = gimp_drawable_get (channel_ID);

	gimp_pixel_rgn_init (&pixel_rgn, chdrawable,
			     0, 0, chdrawable->width, chdrawable->height,
			     TRUE, FALSE);
	gimp_pixel_rgn_set_rect (&pixel_rgn, aux_data,
				 0, 0, chdrawable->width, chdrawable->height);

	gimp_drawable_flush (chdrawable);
	gimp_drawable_detach (chdrawable);
      }
  }
  g_free(aux_data);

  IFDBG printf("Done with that.\n\n");
}

static void
extract_channels(guchar* src, gint num_wanted, gint psstep,
		 gint32 image_ID,
		 gint width, gint height)
{
  guchar* aux_data;
  GimpPixelRgn pixel_rgn;

  IFDBG printf("Extracting %d/%d auxiliary channels.\n", num_wanted, psstep);

  /* gimp doesn't like 0 width/height drawables. */
  if ((width == 0) || (height == 0))
    {
      IFDBG printf("(bad channel dimensions -- skipping)");
      return;
    }

  aux_data = g_malloc(width * height);
  {
    int pix, chan;
    gint32 channel_ID;
    GimpDrawable* chdrawable;
    GimpRGB colour;

    gimp_rgb_set (&colour, 0.0, 0.0, 0.0);

    for (chan=psstep-num_wanted; chan<psstep; chan++)
      {
	for (pix = 0; pix < width * height; pix++)
	  {
	    aux_data [pix] = src [pix * psstep + chan];
	  }

	channel_ID = gimp_channel_new (image_ID,
                                       psd_image.aux_channel[chan-(psstep-num_wanted)].name,
                                       width, height,
                                       100.0, &colour);
	gimp_image_add_channel (image_ID, channel_ID, 0);
	gimp_drawable_set_visible (channel_ID, FALSE);

	chdrawable = gimp_drawable_get (channel_ID);

	gimp_pixel_rgn_init (&pixel_rgn, chdrawable,
			     0, 0, chdrawable->width, chdrawable->height,
			     TRUE, FALSE);
	gimp_pixel_rgn_set_rect (&pixel_rgn, aux_data,
				 0, 0, chdrawable->width, chdrawable->height);

	gimp_drawable_flush (chdrawable);
	gimp_drawable_detach (chdrawable);
      }
  }
  g_free(aux_data);

  IFDBG printf("Done with that.\n\n");
}

static void
resize_mask(guchar* src, guchar* dest,
	    gint32 src_x, gint32 src_y,
	    gint32 src_w, gint32 src_h,
	    gint32 dest_w, gint32 dest_h)
{
  int x,y;

  IFDBG printf("--> %p %p : %d %d . %d %d . %d %d\n",
	 src, dest,
	 src_x, src_y,
	 src_w, src_h,
	 dest_w, dest_h);

  for (y=0; y<dest_h; y++)
    {
      for (x=0; x<dest_w; x++)
	{
	  /* Avoid a 1-pixel border top-left */
	  if ((x>=src_x) && (x<src_x+src_w) &&
	      (y>=src_y) && (y<src_y+src_h))
	    {
	      dest[dest_w * y + x] =
		src[src_w * (y-src_y) + (x-src_x)];
	    }
	  else
	    {
	      dest[dest_w * y + x] = 255;
	    }
	}
    }
}

static gint32
load_image (const gchar *name)
{
  FILE *fd;
  gboolean want_aux;
  char *name_buf;
  guchar *cmykbuf;
  guchar *dest, *temp;
  long channels, nguchars;
  psd_imagetype imagetype;
  gboolean cmyk = FALSE;
  gint step = 1;
  gint32 image_ID = -1;
  gint32 layer_ID = -1;
  GimpDrawable *drawable = NULL;
  GimpPixelRgn pixel_rgn;
  gint32 iter;
  fpos_t tmpfpos;
  int red_chan, grn_chan, blu_chan, alpha_chan, ichan;

  IFDBG printf("------- %s ---------------------------------\n",name);

  fd = fopen (name, "rb");
  if (! fd)
    {
      g_message (_("Could not open '%s' for reading: %s"),
                 name, g_strerror (errno));
      return -1;
    }

  name_buf = g_strdup_printf (_("Opening '%s'..."), name);
  gimp_progress_init (name_buf);
  g_free (name_buf);

  read_whole_file (fd);

  if (psd_image.num_layers > 0) /* PS3-style */
    {
      int lnum;
      GimpImageBaseType gimagetype;

      gimagetype = psd_mode_to_gimp_base_type(PSDheader.mode);
      image_ID =
	gimp_image_new (PSDheader.columns, PSDheader.rows, gimagetype);
      gimp_image_set_filename (image_ID, name);

      if (psd_image.resolution_is_set)
	{
	  gimp_image_set_resolution(image_ID,
				    psd_image.resolution.hRes / 65536.0,
				    psd_image.resolution.vRes / 65536.0);
	  /* currently can only set one unit for the image so we use the
	     horizontal unit from the psd image */
	  gimp_image_set_unit(image_ID,
                              psd_unit_to_gimp_unit (psd_image.resolution.widthUnit));
	}

      fgetpos (fd, &tmpfpos);

      for (lnum = 0; lnum < psd_image.num_layers; lnum++)
	{
	  gint numc;
	  guchar* merged_data = NULL;
	  PSDlayer *layer = psd_image.layer + lnum;
	  
	  /*
	   * since ps supports sloppy bounding boxes it is possible to
	   * have a 0x0 or Xx0 or 0xY layer.  Gimp doesn't support a
	   * 0x0 layer so we will just skip these.  We might be able
	   * to do something better here.
	   */
	  if ((layer->width == 0) || (layer->height == 0))
	    {
	      IFDBG printf("(bad layer dimensions -- skipping)");
	      continue;
	    }
	  numc = layer->num_channels;
	  
	  IFDBG printf("Hey, it's a LAYER with %d channels!\n", numc);

	  switch (gimagetype)
	    {
	    case GIMP_GRAY:
	      {
		IFDBG printf("It's GRAY.\n");
		if (!psd_layer_has_alpha(layer))
		  {
		    merged_data = g_malloc(layer->width * layer->height);
		    seek_to_and_unpack_pixeldata(fd, lnum, 0);
		    memcpy(merged_data, layer->channel[0].data,
			   layer->width * layer->height);

		    g_free(layer->channel[0].data);
		  }
		else
		  {
		    seek_to_and_unpack_pixeldata(fd, lnum, 0);
		    seek_to_and_unpack_pixeldata(fd, lnum, 1);
		    merged_data =
		      chans_to_GRAYA (layer->channel[1].data,
				      layer->channel[0].data,
				      layer->width * layer->height);

		    g_free(layer->channel[0].data);
		    g_free(layer->channel[1].data);
		  }

		layer_ID = gimp_layer_new (image_ID,
					   layer->name,
					   layer->width,
					   layer->height,
					   (numc==1) ? GIMP_GRAY_IMAGE : GIMP_GRAYA_IMAGE,
					   (100.0 * layer->opacity) / 255.0,
					   psd_lmode_to_gimp_lmode(layer->blendkey));

	      }; break; /* case GIMP_GRAY */

	    case GIMP_RGB:
	      {
		IFDBG printf("It's RGB, %dx%d.\n", layer->width,
			     layer->height);
		if (!psd_layer_has_alpha(layer))
		  {
		    seek_to_and_unpack_pixeldata(fd, lnum, 0);
		    seek_to_and_unpack_pixeldata(fd, lnum, 1);
		    seek_to_and_unpack_pixeldata(fd, lnum, 2);
		    merged_data =
		      chans_to_RGB (layer->channel[0].data,
				    layer->channel[1].data,
				    layer->channel[2].data,
				    layer->width *
				    layer->height);

		    g_free(layer->channel[0].data);
		    g_free(layer->channel[1].data);
		    g_free(layer->channel[2].data);

		    IFDBG
		      fprintf(stderr, "YAH0a\n");
		  }
		else
		  {
		    seek_to_and_unpack_pixeldata(fd, lnum, 0);
		    seek_to_and_unpack_pixeldata(fd, lnum, 1);
		    seek_to_and_unpack_pixeldata(fd, lnum, 2);
		    seek_to_and_unpack_pixeldata(fd, lnum, 3);

		    /* Fix for unexpected layer data order for files
                     * from PS files created by PanoTools. Rather
		     * than assuming an order, we find the actual order.
		     */

		    red_chan = grn_chan = blu_chan = alpha_chan = -1;

		    for (ichan=0; ichan<numc; ichan++)
                      {
                        switch(psd_image.layer[lnum].channel[ichan].type)
                          {
                          case 0:    red_chan = ichan; break;
                          case 1:    grn_chan = ichan; break;
                          case 2:    blu_chan = ichan; break;
                          case -1: alpha_chan = ichan; break;
		      }
		    }

		    if ((red_chan < 0) ||
                        (grn_chan < 0) ||
                        (blu_chan < 0) ||
                        (alpha_chan < 0))
                      {
                        g_message ("Error: Cannot identify required RGBA channels");
                        gimp_quit();
                        break;
                      }

		    merged_data =
		      chans_to_RGBA (psd_image.layer[lnum].channel[red_chan].data,
				     psd_image.layer[lnum].channel[grn_chan].data,
				     psd_image.layer[lnum].channel[blu_chan].data,
				     psd_image.layer[lnum].channel[alpha_chan].data,
				     psd_image.layer[lnum].width *
				     psd_image.layer[lnum].height);

		    g_free(layer->channel[0].data);
		    g_free(layer->channel[1].data);
		    g_free(layer->channel[2].data);
		    g_free(layer->channel[3].data);

		    IFDBG
		      fprintf(stderr, "YAH0b\n");
		  }

		IFDBG
		  fprintf(stderr, "YAH1\n");

		layer_ID = gimp_layer_new (image_ID,
					   psd_image.layer[lnum].name,
					   psd_image.layer[lnum].width,
					   psd_image.layer[lnum].height,
					   psd_layer_has_alpha(&psd_image.layer[lnum]) ?
                                           GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE,
					   (100.0 * (double)psd_image.layer[lnum].opacity) / 255.0,
					   psd_lmode_to_gimp_lmode(psd_image.layer[lnum].blendkey));

		IFDBG
		  fprintf(stderr, "YAH2\n");

	      }; break; /* case GIMP_RGB */

	    default:
	      {
		g_message ("Error: Sorry, can't deal with a layered image of this type.\n");
		gimp_quit();
	      }; break; /* default */
	    }

	  gimp_image_add_layer (image_ID, layer_ID, 0);

	  IFDBG
	    fprintf(stderr, "YAH3\n");

	  /* Do a layer mask if it exists */
	  for (iter = 0; iter < layer->num_channels; iter++)
	    {
	      if (layer->channel[iter].type == -2) /* is mask */
		{
		  gint32 mask_id;
		  guchar* lm_data;

		  IFDBG
		    fprintf(stderr, "YAH3m\n");

		  lm_data = g_malloc(layer->width * layer->height);
		  {
                    guchar *tmp;

		    seek_to_and_unpack_pixeldata(fd, lnum, iter);
		    /* PS layer masks can be a different size to
		       their owning layer, so we have to resize them. */
		    resize_mask(layer->channel[iter].data,
				lm_data,
				layer->lm_x - layer->x,
				layer->lm_y - layer->y,
				layer->lm_width, layer->lm_height,
				layer->width, layer->height);

		    /* won't be needing the original data any more */
		    g_free(layer->channel[iter].data);

		    /* give it to GIMP */
		    mask_id = gimp_layer_create_mask(layer_ID, 0);

#if PANOTOOLS_FIX
 		    /* Convert the layer RGB data (not the mask) to RGBA */
                    tmp = merged_data;
 		    merged_data = RGB_to_RGBA (tmp,
                                               psd_image.layer[lnum].width *
                                               psd_image.layer[lnum].height);
                    g_free (tmp);

 		    /* Add alpha - otherwise cannot add layer mask */
 		    gimp_layer_add_alpha (layer_ID);

 		    /* Add layer mask */
		    gimp_layer_add_mask (layer_ID, mask_id);
#endif /* PANOTOOLS_FIX */
		    drawable = gimp_drawable_get (mask_id);

		    gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0,
					 layer->width, layer->height,
					 TRUE, FALSE);
		    gimp_pixel_rgn_set_rect (&pixel_rgn, lm_data, 0, 0,
					     layer->width, layer->height);
		  }
		  g_free(lm_data);

		  gimp_drawable_flush (drawable);
		  gimp_drawable_detach (drawable);
		}
	    }

	  IFDBG
	    fprintf(stderr, "YAH4\n");

	  gimp_layer_translate (layer_ID, layer->x, layer->y);

	  gimp_layer_set_preserve_trans (layer_ID, layer->protecttrans);
	  gimp_drawable_set_visible (layer_ID, layer->visible);

	  drawable = gimp_drawable_get (layer_ID);

	  IFDBG
	    fprintf(stderr, "YAH5 - merged_data=%p, drawable=%p, drawdim=%dx%dx%d\n",
		    merged_data,
		    drawable,
		    drawable->width,
		    drawable->height,
		    drawable->bpp);

	  gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0,
			       layer->width, layer->height,
			       TRUE, FALSE);
	  gimp_pixel_rgn_set_rect (&pixel_rgn, merged_data, 0, 0,
				   layer->width, layer->height);

	  IFDBG
	    fprintf(stderr, "YAH6\n");

	  gimp_drawable_flush (drawable);
	  gimp_drawable_detach (drawable);
	  drawable = NULL;

	  g_free (merged_data);

	  gimp_progress_update ((double)(lnum+1.0) /
				(double)psd_image.num_layers);
	}
      fsetpos (fd, &tmpfpos);
    }

  if ((psd_image.num_aux_channels > 0) &&
      (psd_image.num_layers > 0))
    {
      want_aux = TRUE;
      IFDBG printf("::::::::::: WANT AUX :::::::::::::::::::::::::::::::::::::::\n");
    }
  else
    {
      want_aux = FALSE;
    }


  if (want_aux || (psd_image.num_layers==0)) /* Photoshop2-style: NO LAYERS. */
    {

      IFDBG printf("Image data %ld chars\n", PSDheader.imgdatalen);

      step = PSDheader.channels;

      imagetype = PSD_UNKNOWN_IMAGE;
     switch (PSDheader.mode)
	{
	case 0:		/* Bitmap */
	  imagetype = PSD_BITMAP_IMAGE;
	  break;
	case 1:		/* Grayscale */
	  imagetype = PSD_GRAY_IMAGE;
	  break;
	case 2:		/* Indexed Colour */
	  imagetype = PSD_INDEXED_IMAGE;
	  break;
	case 3:		/* RGB Colour */
	  imagetype = PSD_RGB_IMAGE;
	  break;
	case 4:		/* CMYK Colour */
	  cmyk = TRUE;
	  switch (PSDheader.channels)
	    {
	    case 4:
	      imagetype = PSD_RGB_IMAGE;
	      break;
	    case 5:
	      imagetype = PSD_RGBA_IMAGE;
	      break;
	    default:
	      printf("%s: cannot handle CMYK with more than 5 channels\n",
		     prog_name);
	      return(-1);
	      break;
	    }
	  break;
	case 7:		/* Multichannel (?) */
	case 8:		/* Duotone */
	case 9:		/* Lab Colour */
	default:
	  break;
	}


      if (imagetype == PSD_UNKNOWN_IMAGE)
	{
	  printf("%s: Image type %d (%s) is not supported in this data format\n",
		 prog_name, PSDheader.mode,
		 (PSDheader.mode > 10) ?
		 "<out of range>" : modename[PSDheader.mode]);

	  return(-1);
	}

      if ((PSDheader.bpp != 8) && (PSDheader.bpp != 1))
	{
	  printf("%s: The GIMP only supports 8-bit or 1-bit deep PSD images "
		 "at this time.\n",
		 prog_name);
	  return(-1);
	}

      IFDBG printf(
	     "psd:%d gimp:%d gimpbase:%d\n",
	     imagetype,
	     psd_type_to_gimp_type(imagetype),
	     psd_type_to_gimp_base_type(imagetype)
	     );


      if (!want_aux)
	{
	  /* gimp doesn't like 0 width/height drawables. */
	  if ((PSDheader.columns == 0) || (PSDheader.rows == 0))
	    {
	      IFDBG printf("(bad psd2-style image dimensions -- skipping)");
	      image_ID = -1;
	      goto finish_up;
	    }

	  image_ID = gimp_image_new (PSDheader.columns, PSDheader.rows,
				     psd_type_to_gimp_base_type(imagetype));
	  gimp_image_set_filename (image_ID, name);
	  if (psd_type_to_gimp_base_type(imagetype) == GIMP_INDEXED)
	    {
	      if ((psd_image.colmaplen%3)!=0)
		printf("PSD: Colourmap looks screwed! Aiee!\n");
	      if (psd_image.colmaplen==0)
		printf("PSD: Indexed image has no colourmap!\n");
	      if (psd_image.colmaplen!=768)
		printf("PSD: Warning: Indexed image is %ld!=256 colours.\n",
		       psd_image.colmaplen/3);
	      if (psd_image.colmaplen==768)
		{
		  reshuffle_cmap(psd_image.colmapdata);
		  gimp_image_set_cmap (image_ID,
				       psd_image.colmapdata,
				       256);
		}
	    }

	  layer_ID = gimp_layer_new (image_ID, _("Background"),
				     PSDheader.columns, PSDheader.rows,
				     psd_type_to_gimp_type(imagetype),
				     100, GIMP_NORMAL_MODE);

	  gimp_image_add_layer (image_ID, layer_ID, 0);
	  drawable = gimp_drawable_get (layer_ID);

	}


      if (want_aux)
	{
	  switch (PSDheader.mode)
	    {
	    case 1:		/* Grayscale */
	      channels = 1;
	      break;
	    case 2:		/* Indexed Colour */
	      channels = 1;
	      break;
	    case 3:		/* RGB Colour */
	      channels = 3;
	      break;
	    default:
	      printf("aux? Aieeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee!!!!!!!!!\n");
	      channels = 1;
	      break;
	    }

	  channels = PSDheader.channels - channels;

	  if (psd_image.absolute_alpha)
	    {
	      channels--;
	    }
	}
      else
	{
	  channels = gimp_drawable_bpp(drawable->drawable_id);
	}


      dest = g_malloc( step * PSDheader.columns * PSDheader.rows );

      if (PSDheader.compression == 1)
	{
	  nguchars = PSDheader.columns * PSDheader.rows;
	  temp = g_malloc (PSDheader.imgdatalen);
	  xfread (fd, temp, PSDheader.imgdatalen, "image data");
	  if (!cmyk)
	    {
	      gimp_progress_update ((double)1.00);

	      if(imagetype==PSD_BITMAP_IMAGE) /* convert bitmap to grayscale */
		{
		  guchar *monobuf;

		  monobuf =
                    g_malloc (((PSDheader.columns + 7) >> 3) * PSDheader.rows);

		  decode (PSDheader.imgdatalen,
                          nguchars >> 3, temp,monobuf, step);
		  bitmap2gray (monobuf, dest,
                               PSDheader.columns, PSDheader.rows);

		  g_free (monobuf);
		}
	      else
		{
		  decode(PSDheader.imgdatalen, nguchars, temp, dest, step);
		}
	    }
	  else
	    {
	      gimp_progress_update (1.0);

	      cmykbuf = g_malloc (step * nguchars);
	      decode (PSDheader.imgdatalen, nguchars, temp, cmykbuf, step);

	      cmyk2rgb (cmykbuf, dest, PSDheader.columns, PSDheader.rows,
                        step > 4);
	      g_free (cmykbuf);
	    }

	  g_free (temp);
	}
      else
	{
	  if (!cmyk)
	    {
	      gimp_progress_update ((double)1.00);

	      xfread_interlaced(fd, dest, PSDheader.imgdatalen,
				"raw image data", step);
	    }
	  else
	    {
	      gimp_progress_update ((double)1.00);

	      cmykbuf = g_malloc(PSDheader.imgdatalen);
	      xfread_interlaced(fd, cmykbuf, PSDheader.imgdatalen,
				"raw cmyk image data", step);

	      cmykp2rgb(cmykbuf, dest,
			PSDheader.columns, PSDheader.rows, step > 4);
	      g_free(cmykbuf);
	    }
	}


      if (want_aux) /* want_aux */
	{
	  extract_channels(dest, channels, step,
			   image_ID,
			   PSDheader.columns, PSDheader.rows);

	  goto finish_up; /* Haha!  Look!  A goto! */
	}
      else
	{
	  gimp_progress_update ((double)1.00);

	  if (channels == step) /* gimp bpp == psd bpp */
	    {

	      if (psd_type_to_gimp_type(imagetype)==GIMP_INDEXEDA_IMAGE)
		{
		  printf("@@@@ Didn't know that this could happen...\n");
		  for (iter=0; iter<drawable->width*drawable->height; iter++)
		    {
		      dest[iter*2+1] = 255;
		    }
		}

	      gimp_pixel_rgn_init (&pixel_rgn, drawable,
				   0, 0, drawable->width, drawable->height,
				   TRUE, FALSE);
	      gimp_pixel_rgn_set_rect (&pixel_rgn, dest,
				       0, 0, drawable->width, drawable->height);

	      gimp_drawable_flush (drawable);
	      gimp_drawable_detach (drawable);
	    }
	  else
	    {
	      IFDBG printf("Uhhh... uhm... extra channels... heavy...\n");

	      extract_data_and_channels(dest, channels, step,
					image_ID, drawable,
					drawable->width, drawable->height);
	    }
	}



    finish_up:

      g_free (dest);

      if (psd_image.colmaplen > 0)
	g_free(psd_image.colmapdata);
    }

  if (psd_image.num_guides > 0)
    {
      PSDguide *guide = psd_image.guides;
      int i;

      IFDBG printf("--- Adding %d Guides\n", psd_image.num_guides);

      for (i = 0; i < psd_image.num_guides; i++, guide++)
	{
	  if (guide->horizontal)
	    gimp_image_add_hguide (image_ID, guide->position);
	  else
	    gimp_image_add_vguide (image_ID, guide->position);
	}
    }

  gimp_displays_flush();

  IFDBG printf("--- %d layers : pos %ld : a-alph %d ---\n",
	 psd_image.num_layers, (long int)ftell(fd),
	 psd_image.absolute_alpha);

  return image_ID;
}

static void
decode(long clen, long uclen, guchar *src, guchar* dst, int step)
{
    gint i, j;
    gint32 l;
    gushort * w;

    l = clen;
    for (i = 0; i < PSDheader.rows*PSDheader.channels; ++i)
      {
	l -= PSDheader.rowlength[i];
      }
    if (l)
      {
	g_warning("decode: %ld should be zero\n", (long)l);
      }

    w = PSDheader.rowlength;

    packbitsdecode(&clen, uclen, src, dst++, step);

    for (j = 0; j < step-1; ++j)
      {
	for (i = 0; i < PSDheader.rows; ++i)
	  {
	    src += *w++;
	  }
	packbitsdecode(&clen, uclen, src, dst++, step);
      }

    IFDBG printf("clen %ld\n", clen);
}

/*
 * Decode a PackBits data stream.
 */
static void
packbitsdecode(long * clenp, long uclen, guchar *src, guchar *dst, int step)
{
    gint n, b;
    gint32 clen = *clenp;

    while ((clen > 0) && (uclen > 0)) {
	n = (int) *src++;
	if (n >= 128)
	    n -= 256;
	if (n < 0) {		/* replicate next guchar -n+1 times */
	    clen -= 2;
	    if (n == -128)	/* nop */
		continue;
	    n = -n + 1;
	    uclen -= n;
	    for (b = *src++; n > 0; --n) {
		*dst = b;
		dst += step;
	    }
	} else {		/* copy next n+1 guchars literally */
	    for (b = ++n; b > 0; --b) {
		*dst = *src++;
		dst += step;
	    }
	    uclen -= n;
	    clen -= n+1;
	}
    }
    if (uclen > 0) {
	printf("%s: unexpected EOF while reading image data\n", prog_name);
	gimp_quit();
    }
    *clenp = clen;
}

/*
 * Decode a PackBits channel from file.
 */
static void
unpack_pb_channel(FILE *fd, guchar *dst, gint32 unpackedlen, guint32 *offset)
{
    int n, b;
    gint32 upremain = unpackedlen;

    while (upremain > 0)
      {
	n = (int) getguchar(fd, "packbits1");
	(*offset)++;

	if (n >= 128)
	  n -= 256;
	if (n < 0)
	  {		/* replicate next guchar -n+1 times */
	    if (n == -128)	/* nop */
	      continue;
	    n = -n + 1;
	    /*	    upremain -= n;*/

	    b = getguchar(fd, "packbits2");
	    (*offset)++;
	    for (; n > 0; --n)
	      {
		if (upremain >= 0) {
		  *dst = b;
		  dst ++;
		}
		upremain--;
	      }
	  }
	else
	  {		/* copy next n+1 guchars literally */
	    for (b = ++n; b > 0; --b)
	      {
		const guchar c = getguchar(fd, "packbits3");;
		if (upremain >= 0) {
		  *dst = c;
		  dst ++;
		}
		(*offset)++;
		upremain--;
	      }
	    /*	    upremain -= n;*/
	  }
      }

    if (upremain < 0)
      {
	printf("*** Unpacking overshot destination (%d) buffer by %d bytes!\n",
	       unpackedlen,
	       -upremain);
      }
}

static void
cmyk2rgb(unsigned char * src, unsigned char * dst,
	 long width, long height, int alpha)
{
    int r, g, b, k;
    int i, j;


    for (i = 0; i < height; i++) {
	for (j = 0; j < width; j++) {
	    r = *src++;
	    g = *src++;
	    b = *src++;
	    k = *src++;

	    gimp_cmyk_to_rgb_int (&r, &g, &b, &k);

	    *dst++ = r;
	    *dst++ = g;
	    *dst++ = b;

	    if (alpha)
		*dst++ = *src++;
	}

	if ((i % 5) == 0)
          gimp_progress_update ((double)(2.0*(double)height)/
                                ((double)height+(double)i));
    }
}

/*
 * Decode planar CMYK(A) to RGB(A).
 */
static void
cmykp2rgb(unsigned char * src, unsigned char * dst,
	  long width, long height, int alpha)
{
    int r, g, b, k;
    int i, j;
    long n;
    guchar *rp, *gp, *bp, *kp, *ap;

    n = width * height;
    rp = src;
    gp = rp + n;
    bp = gp + n;
    kp = bp + n;
    ap = kp + n;

    for (i = 0; i < height; i++) {
	for (j = 0; j < width; j++) {
	    r = *rp++;
	    g = *gp++;
	    b = *bp++;
	    k = *kp++;

	    gimp_cmyk_to_rgb_int (&r, &g, &b, &k);

	    *dst++ = r;
	    *dst++ = g;
	    *dst++ = b;

	    if (alpha)
		*dst++ = *ap++;
	}
	if ((i % 5) == 0)
	  gimp_progress_update ((double)(2.0*(double)height)/
                                ((double)height+(double)i));
    }
}

static void
bitmap2gray(guchar *src,guchar *dest,long w,long h)
{
  int i,j;
  for(i=0;i<h;i++)
    {
      int mask=0x80;
      for(j=0;j<w;j++)
	{
	  *dest++=(*src&mask)?0:255;
	  mask>>=1;
	  if(!mask)
	    {
	      src++;
	      mask=0x80;
	    }
	}
      if(mask!=0x80) src++;
    }
}

static void
dumpchunk(size_t n, FILE * fd, gchar *why)
{
  guint32 i;

  printf("\n");

  for (i=0;i<n;i++)
    {
      printf("%02x ",(int)getguchar(fd, why));
    }

  printf("\n");
}

static void
throwchunk(size_t n, FILE * fd, gchar *why)
{
#if 0
  guchar *tmpchunk;

  if (n==0)
    {
      return;
    }

  tmpchunk = g_malloc(n);
  xfread(fd, tmpchunk, n, why);
  g_free(tmpchunk);
#else
  if (n==0)
    {
      return;
    }

  if (fseek(fd, n, SEEK_CUR) != 0)
    {
      printf("%s: unable to seek forward while reading '%s' chunk\n",
	     prog_name, why);
      gimp_quit();
    }
#endif
}

static gchar *
getstring(size_t n, FILE * fd, gchar *why)
{
  gchar *tmpchunk;

  tmpchunk = g_malloc(n + 1);
  xfread(fd, tmpchunk, n, why);
  tmpchunk[n] = 0;

  return tmpchunk;  /* caller should free memory */
}

static gchar *
getpascalstring(FILE *fd, gchar *why)
{
  guchar *tmpchunk;
  guchar len;

  xfread(fd, &len, 1, why);

  if (len==0)
    {
      return NULL;
    }

  tmpchunk = g_malloc(len+1);

  xfread(fd, tmpchunk, len, why);
  tmpchunk[len]=0;

  return (gchar *) tmpchunk; /* caller should free memory */
}

static guchar
getguchar(FILE *fd, gchar *why)
{
  gint tmp;

  tmp = fgetc(fd);

  if (tmp == EOF)
    {
      printf("%s: unexpected EOF while reading '%s' chunk\n",
	     prog_name, why);
      gimp_quit();
    }
  return tmp;
}

static gshort
getgshort(FILE *fd, gchar *why)
{
  guchar b1, b2;

  b1 = getguchar(fd, why);
  b2 = getguchar(fd, why);

  return (gshort) ((b1 * 256) + b2);
}

static glong
getglong(FILE *fd, gchar *why)
{
  guchar s1, s2, s3, s4;

  s1 = getguchar(fd, why);
  s2 = getguchar(fd, why);
  s3 = getguchar(fd, why);
  s4 = getguchar(fd, why);

  return (glong) ((s1*256*256*256) + (s2*256*256) + (s3*256) + s4);
}

static void
xfread(FILE * fd, void * buf, long len, gchar *why)
{
  if (fread(buf, len, 1, fd) == 0)
    {
      printf("%s: unexpected EOF while reading '%s' chunk\n",
	     prog_name, why);
      gimp_quit();
    }
}

static void
xfread_interlaced(FILE* fd, guchar* buf, long len, gchar *why, gint step)
{
  guchar* dest;
  gint pix, pos, bpplane;

  bpplane = len/step;

  if (len%step != 0)
    {
      printf("PSD: Stern warning: data size is not a factor of step size!\n");
    }

  for (pix=0; pix<step; pix++)
    {
      dest = buf + pix;

      for (pos=0; pos<bpplane; pos++)
	{
	  *dest = getguchar(fd, why);
	  dest += step;
	}
    }
}

static void
read_whole_file(FILE * fd)
{
    guint16 w;
    gint32 pos;
    gchar dummy[6];
    gint i;

    xfread(fd, &PSDheader.signature, 4, "signature");
    PSDheader.version = getgshort(fd, "version");
    xfread(fd, &dummy, 6, "reserved");
    PSDheader.channels = getgshort(fd, "channels");
    PSDheader.rows = getglong(fd, "rows");
    PSDheader.columns = getglong(fd, "columns");
    PSDheader.bpp = getgshort(fd, "depth");
    PSDheader.mode = getgshort(fd, "mode");


    psd_image.num_layers = 0;
    psd_image.type = PSDheader.mode;
    psd_image.colmaplen = 0;
    psd_image.num_aux_channels = 0;
    psd_image.num_guides = 0;


    psd_image.colmaplen = getglong(fd, "color data length");

    if (psd_image.colmaplen > 0)
      {
	psd_image.colmapdata = g_malloc(psd_image.colmaplen);
	xfread(fd, psd_image.colmapdata, psd_image.colmaplen, "colormap");
      }


    PSDheader.imgreslen = getglong(fd, "image resource length");
    if (PSDheader.imgreslen > 0)
      {
	do_image_resources(fd);
      }


    PSDheader.miscsizelen = getglong(fd, "misc size data length");
    if (PSDheader.miscsizelen > 0)
      {
	do_layer_and_mask(fd);
      }


    PSDheader.compression = getgshort(fd, "compression");
    IFDBG printf("<<compr:%d>>", (int)PSDheader.compression);
    if (PSDheader.compression == 1) /* RLE */
      {
	PSDheader.rowlength = g_malloc(PSDheader.rows *
				       PSDheader.channels * sizeof(gushort));
	for (i = 0; i < PSDheader.rows*PSDheader.channels; ++i)
	  PSDheader.rowlength[i] = getgshort(fd, "x");
      }
    pos = ftell(fd);
    fseek(fd, 0, SEEK_END);
    PSDheader.imgdatalen = ftell(fd)-pos;
    fseek(fd, pos, SEEK_SET);


    if (strncmp(PSDheader.signature, "8BPS", 4) != 0)
      {
	printf("%s: not an Adobe Photoshop PSD file\n", prog_name);
	gimp_quit();
      }
    if (PSDheader.version != 1)
      {
	printf("%s: bad version number '%d', not 1\n",
	       prog_name, PSDheader.version);
	gimp_quit();
      }
    w = PSDheader.mode;
    IFDBG printf("HEAD:\n"
	   "\tChannels %d\n\tRows %ld\n\tColumns %ld\n\tDepth %d\n\tMode %d (%s)\n"
	   "\tColour data %ld guchars\n",
	   PSDheader.channels, PSDheader.rows,
	   PSDheader.columns, PSDheader.bpp,
	   w, modename[w < 10 ? w : 10],
	   psd_image.colmaplen);
    /*    printf("\tImage resource length: %lu\n", PSDheader.imgreslen);*/
    IFDBG printf("\tLayer/Mask Data length: %lu\n", PSDheader.miscsizelen);
    w = PSDheader.compression;
    IFDBG printf("\tCompression %d (%s)\n", w, w ? "RLE" : "raw");
}
