Submitted By:            Joe Locash <jlocash at gmail dot com>
Date:                    2026-02-19
Initial Package Version: 3.0.6
Upstream Status:         Committed
Origin:                  Upstream commits:
                         - d6dba93bb5b6eea4216ac5b34f30d2a90799361b
                         - 8cf2772f5631719ae0e4e701bd7ef793b1f59cfa
                         - 02886e626df5e4c5f73f838a64fd3f21809dda09
                         - f876094768501f1d55f392117846efac465e9599
                         - d9d0f5b4e642dd5b101e70728042027d568bb01d
                         - 058ada8f3ffc0a42b7dd1561a8817c8cc83b7d2a
Description:             Fixes 5 security vulnerabilites, 3 of which have been
                         assigned: CVE-2026-2239, CVE-2026-2271, and
                         CVE-2026-2272.

diff -Nuar gimp-3.0.6.orig/plug-ins/common/file-psp.c gimp-3.0.6/plug-ins/common/file-psp.c
--- gimp-3.0.6.orig/plug-ins/common/file-psp.c	2025-10-05 13:14:02.000000000 -0400
+++ gimp-3.0.6/plug-ins/common/file-psp.c	2026-02-19 13:51:17.386225232 -0500
@@ -1121,7 +1121,17 @@
         }
       keyword = GUINT16_FROM_LE (keyword);
       length = GUINT32_FROM_LE (length);
-      switch (keyword)
+
+      if ((goffset) ftell (f) + length > (goffset) data_start + total_len)
+        {
+          /* FIXME: After string freeze is over, we should consider changing
+           * this error message to be a bit more descriptive. */
+          g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+                        _("Error reading creator keyword data"));
+          return -1;
+        }
+
+        switch (keyword)
         {
         case PSP_CRTR_FLD_TITLE:
         case PSP_CRTR_FLD_ARTIST:
diff -Nuar gimp-3.0.6.orig/plug-ins/file-ico/ico-load.c gimp-3.0.6/plug-ins/file-ico/ico-load.c
--- gimp-3.0.6.orig/plug-ins/file-ico/ico-load.c	2025-10-05 13:14:02.000000000 -0400
+++ gimp-3.0.6/plug-ins/file-ico/ico-load.c	2026-02-19 13:51:38.462022365 -0500
@@ -426,6 +426,7 @@
                gint    *height)
 {
   IcoFileDataHeader   data;
+  gsize               data_size;
   gint                length;
   gint                x, y, w, h;
   guchar             *xor_map, *and_map;
@@ -471,7 +472,9 @@
       return FALSE;
     }
 
-  if (data.width * data.height * 2 > maxsize)
+  if (! g_size_checked_mul (&data_size, data.width, data.height) ||
+      ! g_size_checked_mul (&data_size, data_size, 2)            ||
+      data_size > maxsize)
     {
       D(("skipping image: too large\n"));
       return FALSE;
@@ -726,7 +729,14 @@
   image = gimp_image_new (max_width, max_height, GIMP_RGB);
 
   maxsize = max_width * max_height * 4;
-  buf = g_new (guchar, max_width * max_height * 4);
+  buf     = g_try_new (guchar, maxsize);
+  if (! buf)
+    {
+      g_free (info);
+      fclose (fp);
+      return NULL;
+    }
+
   for (i = 0; i < icon_count; i++)
     {
       GimpLayer *layer;
diff -Nuar gimp-3.0.6.orig/plug-ins/file-ico/ico-load.c.orig gimp-3.0.6/plug-ins/file-ico/ico-load.c.orig
--- gimp-3.0.6.orig/plug-ins/file-ico/ico-load.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ gimp-3.0.6/plug-ins/file-ico/ico-load.c.orig	2025-10-05 13:14:02.000000000 -0400
@@ -0,0 +1,1101 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * GIMP Plug-in for Windows Icon files.
+ * Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
+ *
+ * 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 3 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include <png.h>
+
+/* #define ICO_DBG */
+
+#include "ico.h"
+#include "ico-load.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define A_VAL(p) ((guchar *)(p))[3]
+#define R_VAL(p) ((guchar *)(p))[2]
+#define G_VAL(p) ((guchar *)(p))[1]
+#define B_VAL(p) ((guchar *)(p))[0]
+
+#define A_VAL_GIMP(p) ((guchar *)(p))[3]
+#define R_VAL_GIMP(p) ((guchar *)(p))[0]
+#define G_VAL_GIMP(p) ((guchar *)(p))[1]
+#define B_VAL_GIMP(p) ((guchar *)(p))[2]
+
+
+static gint       ico_read_int8  (FILE        *fp,
+                                  guint8      *data,
+                                  gint         count);
+static gint       ico_read_int16 (FILE        *fp,
+                                  guint16     *data,
+                                  gint         count);
+static gint       ico_read_int32 (FILE        *fp,
+                                  guint32     *data,
+                                  gint         count);
+
+static gint
+ico_read_int32 (FILE    *fp,
+                guint32 *data,
+                gint     count)
+{
+  gint i, total;
+
+  total = count;
+  if (count > 0)
+    {
+      ico_read_int8 (fp, (guint8 *) data, count * 4);
+      for (i = 0; i < count; i++)
+        data[i] = GUINT32_FROM_LE (data[i]);
+    }
+
+  return total * 4;
+}
+
+
+static gint
+ico_read_int16 (FILE    *fp,
+                guint16 *data,
+                gint     count)
+{
+  gint i, total;
+
+  total = count;
+  if (count > 0)
+    {
+      ico_read_int8 (fp, (guint8 *) data, count * 2);
+      for (i = 0; i < count; i++)
+        data[i] = GUINT16_FROM_LE (data[i]);
+    }
+
+  return total * 2;
+}
+
+
+static gint
+ico_read_int8 (FILE   *fp,
+               guint8 *data,
+               gint    count)
+{
+  gint total;
+  gint bytes;
+
+  total = count;
+  while (count > 0)
+    {
+      bytes = fread ((gchar *) data, sizeof (gchar), count, fp);
+      if (bytes <= 0) /* something bad happened */
+        break;
+
+      count -= bytes;
+      data += bytes;
+    }
+
+  return total;
+}
+
+
+static IcoFileHeader
+ico_read_init (FILE *fp)
+{
+  IcoFileHeader header;
+
+  /* read and check file header */
+  if (! ico_read_int16 (fp, &header.reserved, 1)      ||
+      ! ico_read_int16 (fp, &header.resource_type, 1) ||
+      ! ico_read_int16 (fp, &header.icon_count, 1)    ||
+      header.reserved != 0 ||
+      (header.resource_type != 1 && header.resource_type != 2))
+    {
+      header.icon_count = 0;
+      return header;
+    }
+
+  return header;
+}
+
+
+static gboolean
+ico_read_size (FILE        *fp,
+               gint32       file_offset,
+               IcoLoadInfo *info)
+{
+  png_structp png_ptr;
+  png_infop   info_ptr;
+  png_uint_32 w, h;
+  gint32      bpp;
+  gint32      color_type;
+  guint32     magic;
+
+  if (fseek (fp, info->offset + file_offset, SEEK_SET) < 0)
+    return FALSE;
+
+  ico_read_int32 (fp, &magic, 1);
+
+  if (magic == ICO_PNG_MAGIC)
+    {
+      png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL,
+                                        NULL);
+      if (! png_ptr)
+        return FALSE;
+
+      info_ptr = png_create_info_struct (png_ptr);
+      if (! info_ptr)
+        {
+          png_destroy_read_struct (&png_ptr, NULL, NULL);
+          return FALSE;
+        }
+
+      if (setjmp (png_jmpbuf (png_ptr)))
+        {
+          png_destroy_read_struct (&png_ptr, NULL, NULL);
+          return FALSE;
+        }
+      png_init_io (png_ptr, fp);
+      png_set_sig_bytes (png_ptr, 4);
+      png_read_info (png_ptr, info_ptr);
+      png_get_IHDR (png_ptr, info_ptr, &w, &h, &bpp, &color_type,
+                    NULL, NULL, NULL);
+      png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
+      info->width = w;
+      info->height = h;
+      D(("ico_read_size: PNG: %ix%i\n", info->width, info->height));
+      return TRUE;
+    }
+  else if (magic == 40)
+    {
+      if (ico_read_int32 (fp, &info->width, 1) &&
+          ico_read_int32 (fp, &info->height, 1))
+        {
+          info->height /= 2;
+          D(("ico_read_size: ICO: %ix%i\n", info->width, info->height));
+          return TRUE;
+        }
+      else
+        {
+          info->width = 0;
+          info->height = 0;
+          return FALSE;
+        }
+    }
+  return FALSE;
+}
+
+static IcoLoadInfo*
+ico_read_info (FILE    *fp,
+               gint     icon_count,
+               gint32   file_offset,
+               GError **error)
+{
+  gint            i;
+  IcoFileEntry   *entries;
+  IcoLoadInfo    *info;
+
+  /* read icon entries */
+  entries = g_new (IcoFileEntry, icon_count);
+  if (fread (entries, sizeof (IcoFileEntry), icon_count, fp) <= 0)
+    {
+      g_set_error (error, G_FILE_ERROR, 0,
+                   _("Could not read '%lu' bytes"),
+                   (long unsigned int) sizeof (IcoFileEntry));
+      g_free (entries);
+      return NULL;
+    }
+
+  info = g_new (IcoLoadInfo, icon_count);
+  for (i = 0; i < icon_count; i++)
+    {
+      info[i].width  = entries[i].width;
+      info[i].height = entries[i].height;
+      info[i].planes = entries[i].planes;
+      info[i].bpp    = GUINT16_FROM_LE (entries[i].bpp);
+      info[i].size   = GUINT32_FROM_LE (entries[i].size);
+      info[i].offset = GUINT32_FROM_LE (entries[i].offset);
+
+      if (info[i].width == 0 || info[i].height == 0)
+        {
+          ico_read_size (fp, file_offset, info + i);
+        }
+
+      D(("ico_read_info: %ix%i (%i bits, size: %i, offset: %i)\n",
+         info[i].width, info[i].height, info[i].bpp,
+         info[i].size, info[i].offset));
+
+      if (info[i].width == 0 || info[i].height == 0)
+        {
+          g_set_error (error, G_FILE_ERROR, 0,
+                       _("Icon #%d has zero width or height"), i);
+          g_free (info);
+          g_free (entries);
+          return NULL;
+        }
+    }
+
+  g_free (entries);
+
+  return info;
+}
+
+static gboolean
+ico_read_png (FILE    *fp,
+              guint32  header,
+              guchar  *buf,
+              gint     maxsize,
+              gint    *width,
+              gint    *height)
+{
+  png_structp   png_ptr;
+  png_infop     info;
+  png_uint_32   w;
+  png_uint_32   h;
+  gint32        bit_depth;
+  gint32        color_type;
+  guint32     **rows;
+  gint          i;
+
+  png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+  if (! png_ptr)
+    return FALSE;
+  info = png_create_info_struct (png_ptr);
+  if (! info)
+    {
+      png_destroy_read_struct (&png_ptr, NULL, NULL);
+      return FALSE;
+    }
+
+  if (setjmp (png_jmpbuf (png_ptr)))
+    {
+      png_destroy_read_struct (&png_ptr, &info, NULL);
+      return FALSE;
+    }
+
+  png_init_io (png_ptr, fp);
+  png_set_sig_bytes (png_ptr, 4);
+  png_read_info (png_ptr, info);
+  png_get_IHDR (png_ptr, info, &w, &h, &bit_depth, &color_type,
+                NULL, NULL, NULL);
+  /* Check for overflow */
+  if ((w * h * 4) < w       ||
+      (w * h * 4) < h       ||
+      (w * h * 4) < (w * h) ||
+      (w * h * 4) > maxsize)
+    {
+      png_destroy_read_struct (&png_ptr, &info, NULL);
+      return FALSE;
+    }
+  D(("ico_read_png: %ix%i, %i bits, %i type\n", (gint)w, (gint)h,
+     bit_depth, color_type));
+  switch (color_type)
+    {
+    case PNG_COLOR_TYPE_GRAY:
+      png_set_expand_gray_1_2_4_to_8 (png_ptr);
+      if (bit_depth == 16)
+        png_set_strip_16 (png_ptr);
+      png_set_gray_to_rgb (png_ptr);
+      png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER);
+      break;
+    case PNG_COLOR_TYPE_GRAY_ALPHA:
+      png_set_expand_gray_1_2_4_to_8 (png_ptr);
+      if (bit_depth == 16)
+        png_set_strip_16 (png_ptr);
+      png_set_gray_to_rgb (png_ptr);
+      break;
+    case PNG_COLOR_TYPE_PALETTE:
+      png_set_palette_to_rgb (png_ptr);
+      if (png_get_valid (png_ptr, info, PNG_INFO_tRNS))
+        png_set_tRNS_to_alpha (png_ptr);
+      else
+        png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER);
+      break;
+    case PNG_COLOR_TYPE_RGB:
+      if (bit_depth == 16)
+        png_set_strip_16 (png_ptr);
+      png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER);
+      break;
+    case PNG_COLOR_TYPE_RGB_ALPHA:
+      if (bit_depth == 16)
+        png_set_strip_16 (png_ptr);
+      break;
+    }
+
+  *width = w;
+  *height = h;
+  rows = g_new (guint32*, h);
+  rows[0] = (guint32*) buf;
+  for (i = 1; i < h; i++)
+    rows[i] = rows[i-1] + w;
+  png_read_image (png_ptr, (png_bytepp) rows);
+  png_destroy_read_struct (&png_ptr, &info, NULL);
+  g_free (rows);
+  return TRUE;
+}
+
+gint
+ico_get_bit_from_data (const guint8 *data,
+                       gint          line_width,
+                       gint          bit)
+{
+  gint line;
+  gint width32;
+  gint offset;
+  gint result;
+
+  /* width per line in multiples of 32 bits */
+  width32 = (line_width % 32 == 0 ? line_width/32 : line_width/32 + 1);
+  line = bit / line_width;
+  offset = bit % line_width;
+
+  result = (data[line * width32 * 4 + offset/8] & (1 << (7 - (offset % 8))));
+
+  return (result ? 1 : 0);
+}
+
+
+gint
+ico_get_nibble_from_data (const guint8 *data,
+                          gint          line_width,
+                          gint          nibble)
+{
+  gint line;
+  gint width32;
+  gint offset;
+  gint result;
+
+  /* width per line in multiples of 32 bits */
+  width32 = (line_width % 8 == 0 ? line_width/8 : line_width/8 + 1);
+  line = nibble / line_width;
+  offset = nibble % line_width;
+
+  result =
+    (data[line * width32 * 4 + offset/2] & (0x0F << (4 * (1 - offset % 2))));
+
+  if (offset % 2 == 0)
+    result = result >> 4;
+
+  return result;
+}
+
+gint
+ico_get_byte_from_data (const guint8 *data,
+                        gint          line_width,
+                        gint          byte)
+{
+  gint line;
+  gint width32;
+  gint offset;
+
+  /* width per line in multiples of 32 bits */
+  width32 = (line_width % 4 == 0 ? line_width / 4 : line_width / 4 + 1);
+  line = byte / line_width;
+  offset = byte % line_width;
+
+  return data[line * width32 * 4 + offset];
+}
+
+static gboolean
+ico_read_icon (FILE    *fp,
+               guint32  header_size,
+               guchar  *buf,
+               gint     maxsize,
+               gint    *width,
+               gint    *height)
+{
+  IcoFileDataHeader   data;
+  gint                length;
+  gint                x, y, w, h;
+  guchar             *xor_map, *and_map;
+  guint32            *palette;
+  guint32            *dest_vec;
+  guchar             *row;
+  gint                rowstride;
+
+  palette = NULL;
+
+  data.header_size = header_size;
+  ico_read_int32 (fp, &data.width, 1);
+  ico_read_int32 (fp, &data.height, 1);
+  ico_read_int16 (fp, &data.planes, 1);
+  ico_read_int16 (fp, &data.bpp, 1);
+  ico_read_int32 (fp, &data.compression, 1);
+  ico_read_int32 (fp, &data.image_size, 1);
+  ico_read_int32 (fp, &data.x_res, 1);
+  ico_read_int32 (fp, &data.y_res, 1);
+  ico_read_int32 (fp, &data.used_clrs, 1);
+  ico_read_int32 (fp, &data.important_clrs, 1);
+
+  D(("  header size %i, "
+     "w %i, h %i, planes %i, size %i, bpp %i, used %i, imp %i.\n",
+     data.header_size, data.width, data.height,
+     data.planes, data.image_size, data.bpp,
+     data.used_clrs, data.important_clrs));
+
+  if (data.planes      != 1 ||
+      data.compression != 0)
+    {
+      D(("skipping image: invalid header\n"));
+      return FALSE;
+    }
+
+  if (data.bpp != 1  &&
+      data.bpp != 4  &&
+      data.bpp != 8  &&
+      data.bpp != 24 &&
+      data.bpp != 32)
+    {
+      D(("skipping image: invalid depth: %i\n", data.bpp));
+      return FALSE;
+    }
+
+  if (data.width * data.height * 2 > maxsize)
+    {
+      D(("skipping image: too large\n"));
+      return FALSE;
+    }
+
+  w = data.width;
+  h = data.height / 2;
+
+  if (data.bpp <= 8)
+    {
+      if (data.used_clrs == 0)
+        data.used_clrs = (1 << data.bpp);
+
+      D(("  allocating a %i-slot palette for %i bpp.\n",
+         data.used_clrs, data.bpp));
+
+      palette = g_new0 (guint32, data.used_clrs);
+      ico_read_int8 (fp, (guint8 *) palette, data.used_clrs * 4);
+    }
+
+  xor_map = ico_alloc_map (w, h, data.bpp, &length);
+  ico_read_int8 (fp, xor_map, length);
+  D(("  length of xor_map: %i\n", length));
+
+  /* Read in and_map. It's padded out to 32 bits per line: */
+  and_map = ico_alloc_map (w, h, 1, &length);
+  ico_read_int8 (fp, and_map, length);
+  D(("  length of and_map: %i\n", length));
+
+  dest_vec = (guint32 *) buf;
+  switch (data.bpp)
+    {
+    case 1:
+      for (y = 0; y < h; y++)
+        for (x = 0; x < w; x++)
+          {
+            guint32 color = palette[ico_get_bit_from_data (xor_map,
+                                                           w, y * w + x)];
+            guint32 *dest = dest_vec + (h - 1 - y) * w + x;
+
+            R_VAL_GIMP (dest) = R_VAL (&color);
+            G_VAL_GIMP (dest) = G_VAL (&color);
+            B_VAL_GIMP (dest) = B_VAL (&color);
+
+            if (ico_get_bit_from_data (and_map, w, y * w + x))
+              A_VAL_GIMP (dest) = 0;
+            else
+              A_VAL_GIMP (dest) = 255;
+          }
+      break;
+
+    case 4:
+      for (y = 0; y < h; y++)
+        for (x = 0; x < w; x++)
+          {
+            guint32 color = palette[ico_get_nibble_from_data (xor_map,
+                                                              w, y * w + x)];
+            guint32 *dest = dest_vec + (h - 1 - y) * w + x;
+
+            R_VAL_GIMP (dest) = R_VAL (&color);
+            G_VAL_GIMP (dest) = G_VAL (&color);
+            B_VAL_GIMP (dest) = B_VAL (&color);
+
+            if (ico_get_bit_from_data (and_map, w, y * w + x))
+              A_VAL_GIMP (dest) = 0;
+            else
+              A_VAL_GIMP (dest) = 255;
+          }
+      break;
+
+    case 8:
+      for (y = 0; y < h; y++)
+        for (x = 0; x < w; x++)
+          {
+            guint32 color = palette[ico_get_byte_from_data (xor_map,
+                                                            w, y * w + x)];
+            guint32 *dest = dest_vec + (h - 1 - y) * w + x;
+
+            R_VAL_GIMP (dest) = R_VAL (&color);
+            G_VAL_GIMP (dest) = G_VAL (&color);
+            B_VAL_GIMP (dest) = B_VAL (&color);
+
+            if (ico_get_bit_from_data (and_map, w, y * w + x))
+              A_VAL_GIMP (dest) = 0;
+            else
+              A_VAL_GIMP (dest) = 255;
+          }
+      break;
+
+    default:
+      {
+        gint bytespp = data.bpp / 8;
+
+        rowstride = ico_rowstride (w, data.bpp);
+
+        for (y = 0; y < h; y++)
+          {
+            row = xor_map + rowstride * y;
+
+            for (x = 0; x < w; x++)
+              {
+                guint32 *dest = dest_vec + (h - 1 - y) * w + x;
+
+                B_VAL_GIMP (dest) = row[0];
+                G_VAL_GIMP (dest) = row[1];
+                R_VAL_GIMP (dest) = row[2];
+
+                if (data.bpp < 32)
+                  {
+                    if (ico_get_bit_from_data (and_map, w, y * w + x))
+                      A_VAL_GIMP (dest) = 0;
+                    else
+                      A_VAL_GIMP (dest) = 255;
+                  }
+                else
+                  {
+                    A_VAL_GIMP (dest) = row[3];
+                  }
+
+                row += bytespp;
+              }
+          }
+      }
+    }
+  if (palette)
+    g_free (palette);
+  g_free (xor_map);
+  g_free (and_map);
+  *width = w;
+  *height = h;
+  return TRUE;
+}
+
+static GimpLayer *
+ico_load_layer (FILE        *fp,
+                GimpImage   *image,
+                gint32       icon_num,
+                guchar      *buf,
+                gint         maxsize,
+                gint32       file_offset,
+                gchar       *layer_prefix,
+                IcoLoadInfo *info)
+{
+  gint        width, height;
+  GimpLayer  *layer;
+  guint32     first_bytes;
+  GeglBuffer *buffer;
+
+  if (fseek (fp, info->offset + file_offset, SEEK_SET) < 0 ||
+      ! ico_read_int32 (fp, &first_bytes, 1))
+    return NULL;
+
+  if (first_bytes == ICO_PNG_MAGIC)
+    {
+      if (!ico_read_png (fp, first_bytes, buf, maxsize, &width, &height))
+        return NULL;
+    }
+  else if (first_bytes == 40)
+    {
+      if (!ico_read_icon (fp, first_bytes, buf, maxsize, &width, &height))
+        return NULL;
+    }
+  else
+    {
+      return NULL;
+    }
+
+  /* read successfully. add to image */
+  layer = gimp_layer_new (image, layer_prefix, width, height,
+                          GIMP_RGBA_IMAGE, 100,
+                          gimp_image_get_default_new_layer_mode (image));
+  gimp_image_insert_layer (image, layer, NULL, icon_num);
+
+  buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
+
+  gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
+                   NULL, buf, GEGL_AUTO_ROWSTRIDE);
+
+  g_object_unref (buffer);
+
+  return layer;
+}
+
+
+GimpImage *
+ico_load_image (GFile        *file,
+                gint32       *file_offset,
+                gint          frame_num,
+                GError      **error)
+{
+  FILE          *fp;
+  IcoFileHeader  header;
+  IcoLoadInfo   *info;
+  gint           max_width, max_height;
+  gint           i;
+  GimpImage     *image;
+  guchar        *buf;
+  guint          icon_count;
+  gint           maxsize;
+  gchar         *str;
+
+  if (! file_offset)
+    gimp_progress_init_printf (_("Opening '%s'"),
+                               gimp_file_get_utf8_name (file));
+
+  fp = g_fopen (g_file_peek_path (file), "rb");
+
+  if (! fp)
+    {
+      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+                   _("Could not open '%s' for reading: %s"),
+                   gimp_file_get_utf8_name (file), g_strerror (errno));
+      return NULL;
+    }
+
+  if (file_offset)
+    fseek (fp, *file_offset, SEEK_SET);
+
+  header = ico_read_init (fp);
+  icon_count = header.icon_count;
+  if (!icon_count)
+    {
+      fclose (fp);
+      return NULL;
+    }
+
+  info = ico_read_info (fp, icon_count, file_offset ? *file_offset : 0, error);
+  if (! info)
+    {
+      fclose (fp);
+      return NULL;
+    }
+
+  /* find width and height of image */
+  max_width = 0;
+  max_height = 0;
+  for (i = 0; i < icon_count; i++)
+    {
+      if (info[i].width > max_width)
+        max_width = info[i].width;
+      if (info[i].height > max_height)
+        max_height = info[i].height;
+    }
+  if (max_width <= 0 || max_height <= 0)
+    {
+      g_free (info);
+      fclose (fp);
+      return NULL;
+    }
+  D(("image size: %ix%i\n", max_width, max_height));
+
+  image = gimp_image_new (max_width, max_height, GIMP_RGB);
+
+  maxsize = max_width * max_height * 4;
+  buf = g_new (guchar, max_width * max_height * 4);
+  for (i = 0; i < icon_count; i++)
+    {
+      GimpLayer *layer;
+      gchar     *layer_prefix;
+      gchar     *icon_metadata;
+
+      if (info[i].bpp)
+        icon_metadata = g_strdup_printf ("(%dx%d, %dbpp)", info[i].width,
+                                         info[i].height, info[i].bpp);
+      else
+        icon_metadata = g_strdup_printf ("(%dx%d)", info[i].width,
+                                         info[i].height);
+
+      if (frame_num > -1)
+        {
+          layer_prefix = g_strdup_printf ("Cursor %s Frame #%i", icon_metadata,
+                                          frame_num);
+        }
+      else
+        {
+          if (header.resource_type == 1)
+            layer_prefix = g_strdup_printf ("Icon #%i %s ", i + 1,
+                                            icon_metadata);
+          else
+            layer_prefix = g_strdup_printf ("Cursor #%i %s ", i + 1,
+                                            icon_metadata);
+        }
+
+      layer = ico_load_layer (fp, image, i + 1, buf, maxsize,
+                              file_offset ? *file_offset : 0,
+                              layer_prefix, info + i);
+      g_free (icon_metadata);
+      g_free (layer_prefix);
+
+      /* Save CUR hot spot information */
+      if (header.resource_type == 2)
+        {
+          GimpParasite *parasite;
+
+          str = g_strdup_printf ("%d %d", info[i].planes, info[i].bpp);
+          parasite = gimp_parasite_new ("cur-hot-spot",
+                                        GIMP_PARASITE_PERSISTENT,
+                                        strlen (str) + 1, (gpointer) str);
+          g_free (str);
+          gimp_item_attach_parasite (GIMP_ITEM (layer), parasite);
+          gimp_parasite_free (parasite);
+        }
+    }
+
+  if (file_offset)
+    *file_offset = ftell (fp);
+
+  g_free (buf);
+  g_free (info);
+  fclose (fp);
+
+  /* Don't update progress here if .ani file */
+  if (! file_offset)
+    gimp_progress_update (1.0);
+
+  return image;
+}
+
+/* Ported from James Huang's ani.c code, under the GPL license, version 3
+ * or any later version of the license */
+GimpImage *
+ani_load_image (GFile   *file,
+                gboolean load_thumb,
+                gint    *width,
+                gint    *height,
+                GError **error)
+{
+  FILE         *fp;
+  GimpImage    *image = NULL;
+  GimpParasite *parasite;
+  gchar         id[4];
+  guint32       size;
+  guint8        padding;
+  gint32        file_offset;
+  guint         frame = 1;
+  AniFileHeader header;
+  gchar        *inam  = NULL;
+  gchar        *iart  = NULL;
+  gchar        *str;
+
+  gimp_progress_init_printf (_("Opening '%s'"),
+                             gimp_file_get_utf8_name (file));
+
+  fp = g_fopen (g_file_peek_path (file), "rb");
+
+  if (! fp)
+    {
+      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+                   _("Could not open '%s' for reading: %s"),
+                   gimp_file_get_utf8_name (file), g_strerror (errno));
+      return NULL;
+    }
+
+  while (fread (id, 1, 4, fp) == 4)
+    {
+      if (memcmp (id, "RIFF", 4) == 0 )
+        {
+          fread (&size, sizeof (size), 1, fp);
+        }
+      else if (memcmp (id, "anih", 4) == 0)
+        {
+          fread (&size, sizeof (size), 1, fp);
+          fread (&header, sizeof (header), 1, fp);
+        }
+      else if (memcmp (id, "rate", 4) == 0)
+        {
+          fread (&size, sizeof (size), 1, fp);
+          fseek (fp, size, SEEK_CUR);
+        }
+      else if (memcmp (id, "seq ", 4) == 0)
+        {
+          fread (&size, sizeof (size), 1, fp);
+          fseek (fp, size, SEEK_CUR);
+        }
+      else if (memcmp (id, "LIST", 4) == 0)
+        {
+          fread (&size, sizeof (size), 1, fp);
+        }
+      else if (memcmp (id, "INAM", 4) == 0)
+        {
+          gint n_read = -1;
+
+          fread (&size, sizeof (size), 1, fp);
+          if (size > 0)
+            {
+              if (inam)
+                g_free (inam);
+
+              inam = g_new0 (gchar, size + 1);
+              n_read = fread (inam, sizeof (gchar), size, fp);
+              inam[size] = '\0';
+            }
+
+          if (n_read < 1 || (inam && ! g_utf8_validate (inam, -1, NULL)))
+            {
+              fclose (fp);
+              g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+                           _("Invalid ANI metadata"));
+              return NULL;
+            }
+
+          /* Metadata length must be even. If data itself is odd,
+           * then an extra 0x00 is added for padding. We read in
+           * that extra byte to keep loading properly.
+           * See discussion in #8562.
+           */
+          if (size % 2 != 0)
+            fread (&padding, sizeof (padding), 1, fp);
+        }
+      else if (memcmp (id, "IART", 4) == 0)
+        {
+          gint n_read = -1;
+
+          fread (&size, sizeof (size), 1, fp);
+          if (size > 0)
+            {
+              if (iart)
+                g_free (iart);
+
+              iart = g_new0 (gchar, size + 1);
+              n_read = fread (iart, sizeof (gchar), size, fp);
+              iart[size] = '\0';
+            }
+
+          if (n_read < 1 || (iart && ! g_utf8_validate (iart, -1, NULL)))
+            {
+              fclose (fp);
+              g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+                           _("Invalid ANI metadata"));
+              return NULL;
+            }
+
+          if (size % 2 != 0)
+            fread (&padding, sizeof (padding), 1, fp);
+        }
+      else if (memcmp (id, "icon", 4) == 0)
+        {
+          fread (&size, sizeof (size), 1, fp);
+          file_offset = ftell (fp);
+          if (load_thumb)
+            {
+              image = ico_load_thumbnail_image (file, width, height, file_offset, error);
+              break;
+            }
+           else
+             {
+               if (! image)
+                 {
+                   image = ico_load_image (file, &file_offset, 1, error);
+                 }
+               else
+                 {
+                   GimpImage    *temp_image = NULL;
+                   GimpLayer   **layers;
+                   GimpLayer    *new_layer;
+
+                   temp_image = ico_load_image (file, &file_offset, frame + 1,
+                                                error);
+                   layers = gimp_image_get_layers (temp_image);
+                   if (layers)
+                     {
+                       for (gint i = 0; layers[i]; i++)
+                         {
+                           new_layer = gimp_layer_new_from_drawable (GIMP_DRAWABLE (layers[i]),
+                                                                     image);
+                           gimp_image_insert_layer (image, new_layer, NULL, frame);
+                           frame++;
+                         }
+                       g_free (layers);
+                     }
+                   gimp_image_delete (temp_image);
+                 }
+
+               /* Update position after reading icon data */
+               fseek (fp, file_offset, SEEK_SET);
+               if (header.frames > 0)
+                 gimp_progress_update ((gdouble) frame /
+                                       (gdouble) header.frames);
+             }
+        }
+    }
+  fclose (fp);
+
+  /* Saving header metadata */
+  str = g_strdup_printf ("%d", header.jif_rate);
+  parasite = gimp_parasite_new ("ani-header",
+                                GIMP_PARASITE_PERSISTENT,
+                                strlen (str) + 1, (gpointer) str);
+  g_free (str);
+  gimp_image_attach_parasite (image, parasite);
+  gimp_parasite_free (parasite);
+
+  /* Saving INFO block */
+  if (inam && strlen (inam) > 0)
+    {
+      str = g_strdup_printf ("%s", inam);
+      parasite = gimp_parasite_new ("ani-info-inam",
+                                    GIMP_PARASITE_PERSISTENT,
+                                    strlen (str) + 1, (gpointer) str);
+      g_free (str);
+      g_free (inam);
+      gimp_image_attach_parasite (image, parasite);
+      gimp_parasite_free (parasite);
+    }
+
+  if (iart && strlen (iart) > 0)
+    {
+      str = g_strdup_printf ("%s", iart);
+      parasite = gimp_parasite_new ("ani-info-iart",
+                                    GIMP_PARASITE_PERSISTENT,
+                                    strlen (str) + 1, (gpointer) str);
+      g_free (str);
+      g_free (iart);
+      gimp_image_attach_parasite (image, parasite);
+      gimp_parasite_free (parasite);
+    }
+
+  gimp_progress_update (1.0);
+
+  return image;
+}
+
+GimpImage *
+ico_load_thumbnail_image (GFile   *file,
+                          gint    *width,
+                          gint    *height,
+                          gint32   file_offset,
+                          GError **error)
+{
+  FILE          *fp;
+  IcoLoadInfo   *info;
+  IcoFileHeader  header;
+  GimpImage     *image;
+  gint           max_width;
+  gint           max_height;
+  gint           w     = 0;
+  gint           h     = 0;
+  gint           bpp   = 0;
+  gint           match = 0;
+  gint           i, icon_count;
+  guchar        *buf;
+
+  gimp_progress_init_printf (_("Opening thumbnail for '%s'"),
+                             gimp_file_get_utf8_name (file));
+
+  fp = g_fopen (g_file_peek_path (file), "rb");
+
+  if (! fp)
+    {
+      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+                   _("Could not open '%s' for reading: %s"),
+                   gimp_file_get_utf8_name (file), g_strerror (errno));
+      return NULL;
+    }
+
+  if (file_offset > 0)
+    fseek (fp, file_offset, SEEK_SET);
+
+  header = ico_read_init (fp);
+  icon_count = header.icon_count;
+  if (! icon_count)
+    {
+      fclose (fp);
+      return NULL;
+    }
+
+  D(("*** %s: Microsoft icon file, containing %i icon(s)\n",
+     gimp_file_get_utf8_name (file), icon_count));
+
+  info = ico_read_info (fp, icon_count, file_offset, error);
+  if (! info)
+    {
+      fclose (fp);
+      return NULL;
+    }
+
+  max_width  = 0;
+  max_height = 0;
+
+  /* Do a quick scan of the icons in the file to find the best match */
+  for (i = 0; i < icon_count; i++)
+    {
+      if (info[i].width > max_width)
+        max_width = info[i].width;
+      if (info[i].height > max_height)
+        max_height = info[i].height;
+
+      if ((info[i].width  > w && w < *width) ||
+          (info[i].height > h && h < *height))
+        {
+          w = info[i].width;
+          h = info[i].height;
+          bpp = info[i].bpp;
+
+          match = i;
+        }
+      else if (w == info[i].width  &&
+               h == info[i].height &&
+               info[i].bpp > bpp)
+        {
+          /* better quality */
+          bpp = info[i].bpp;
+          match = i;
+        }
+    }
+
+  if (w <= 0 || h <= 0)
+    return NULL;
+
+  image = gimp_image_new (w, h, GIMP_RGB);
+  buf = g_new (guchar, w*h*4);
+  ico_load_layer (fp, image, match, buf, w*h*4, file_offset,
+                  "Thumbnail", info + match);
+  g_free (buf);
+
+  *width  = max_width;
+  *height = max_height;
+
+  D(("*** thumbnail successfully loaded.\n\n"));
+
+  gimp_progress_update (1.0);
+
+  g_free (info);
+  fclose (fp);
+
+  return image;
+}
diff -Nuar gimp-3.0.6.orig/plug-ins/file-psd/psd-image-res-load.c gimp-3.0.6/plug-ins/file-psd/psd-image-res-load.c
--- gimp-3.0.6.orig/plug-ins/file-psd/psd-image-res-load.c	2025-10-05 13:14:02.000000000 -0400
+++ gimp-3.0.6/plug-ins/file-psd/psd-image-res-load.c	2026-02-19 13:50:21.170338855 -0500
@@ -650,8 +650,6 @@
       return 0;
     }
 
-  img_a->alpha_names = g_ptr_array_new ();
-
   block_rem = res_a->data_len;
   while (block_rem > 1)
     {
@@ -661,6 +659,8 @@
       IFDBG(3) g_debug ("String: %s, %d, %d", str, read_len, write_len);
       if (write_len >= 0)
         {
+          if (! img_a->alpha_names)
+            img_a->alpha_names = g_ptr_array_new ();
           g_ptr_array_add (img_a->alpha_names, (gpointer) str);
         }
       block_rem -= read_len;
diff -Nuar gimp-3.0.6.orig/plug-ins/file-psd/psd-image-res-load.c.orig gimp-3.0.6/plug-ins/file-psd/psd-image-res-load.c.orig
--- gimp-3.0.6.orig/plug-ins/file-psd/psd-image-res-load.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ gimp-3.0.6/plug-ins/file-psd/psd-image-res-load.c.orig	2025-10-05 13:14:02.000000000 -0400
@@ -0,0 +1,1786 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP PSD Plug-in
+ * Copyright 2007 by John Marshall
+ *
+ * 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 3 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, see <https://www.gnu.org/licenses/>.
+ */
+
+/* ----- Known Image Resource Block Types -----
+  All image resources not otherwise handled, including unknown types
+  are added as image parasites.
+  The data is attached as-is from the file (i.e. in big endian order).
+
+  PSD_PS2_IMAGE_INFO    = 1000,    Dropped    * 0x03e8 - Obsolete - ps 2.0 image info *
+  PSD_MAC_PRINT_INFO    = 1001,    PS Only    * 0x03e9 - Optional - Mac print manager print info record *
+  PSD_PS2_COLOR_TAB     = 1003,    Dropped    * 0x03eb - Obsolete - ps 2.0 indexed color table *
+  PSD_RESN_INFO         = 1005,    Loaded     * 0x03ed - ResolutionInfo structure *
+  PSD_ALPHA_NAMES       = 1006,    Loaded     * 0x03ee - Alpha channel names *
+  PSD_DISPLAY_INFO      = 1007,    Loaded     * 0x03ef - DisplayInfo structure *
+  PSD_CAPTION           = 1008,    Loaded     * 0x03f0 - Optional - Caption string *
+  PSD_BORDER_INFO       = 1009,               * 0x03f1 - Border info *
+  PSD_BACKGROUND_COL    = 1010,               * 0x03f2 - Background color *
+  PSD_PRINT_FLAGS       = 1011,               * 0x03f3 - Print flags *
+  PSD_GREY_HALFTONE     = 1012,               * 0x03f4 - Greyscale and multichannel halftoning info *
+  PSD_COLOR_HALFTONE    = 1013,               * 0x03f5 - Color halftoning info *
+  PSD_DUOTONE_HALFTONE  = 1014,               * 0x03f6 - Duotone halftoning info *
+  PSD_GREY_XFER         = 1015,               * 0x03f7 - Greyscale and multichannel transfer functions *
+  PSD_COLOR_XFER        = 1016,               * 0x03f8 - Color transfer functions *
+  PSD_DUOTONE_XFER      = 1017,               * 0x03f9 - Duotone transfer functions *
+  PSD_DUOTONE_INFO      = 1018,               * 0x03fa - Duotone image information *
+  PSD_EFFECTIVE_BW      = 1019,               * 0x03fb - Effective black & white values for dot range *
+  PSD_OBSOLETE_01       = 1020,    Dropped    * 0x03fc - Obsolete *
+  PSD_EPS_OPT           = 1021,               * 0x03fd - EPS options *
+  PSD_QUICK_MASK        = 1022,    Loaded     * 0x03fe - Quick mask info *
+  PSD_OBSOLETE_02       = 1023,    Dropped    * 0x03ff - Obsolete *
+  PSD_LAYER_STATE       = 1024,    Loaded     * 0x0400 - Layer state info *
+  PSD_WORKING_PATH      = 1025,               * 0x0401 - Working path (not saved) *
+  PSD_LAYER_GROUP       = 1026,               * 0x0402 - Layers group info *
+  PSD_OBSOLETE_03       = 1027,    Dropped    * 0x0403 - Obsolete *
+  PSD_IPTC_NAA_DATA     = 1028,    Loaded     * 0x0404 - IPTC-NAA record (IMV4.pdf) *
+  PSD_IMAGE_MODE_RAW    = 1029,               * 0x0405 - Image mode for raw format files *
+  PSD_JPEG_QUAL         = 1030,    PS Only    * 0x0406 - JPEG quality *
+  PSD_GRID_GUIDE        = 1032,    Loaded     * 0x0408 - Grid & guide info *
+  PSD_THUMB_RES         = 1033,    Special    * 0x0409 - Thumbnail resource *
+  PSD_COPYRIGHT_FLG     = 1034,               * 0x040a - Copyright flag *
+  PSD_URL               = 1035,               * 0x040b - URL string *
+  PSD_THUMB_RES2        = 1036,    Special    * 0x040c - Thumbnail resource *
+  PSD_GLOBAL_ANGLE      = 1037,               * 0x040d - Global angle *
+  PSD_COLOR_SAMPLER     = 1038,               * 0x040e - Color samplers resource *
+  PSD_ICC_PROFILE       = 1039,    Loaded     * 0x040f - ICC Profile *
+  PSD_WATERMARK         = 1040,               * 0x0410 - Watermark *
+  PSD_ICC_UNTAGGED      = 1041,               * 0x0411 - Do not use ICC profile flag *
+  PSD_EFFECTS_VISIBLE   = 1042,               * 0x0412 - Show   hide all effects layers *
+  PSD_SPOT_HALFTONE     = 1043,               * 0x0413 - Spot halftone *
+  PSD_DOC_IDS           = 1044,               * 0x0414 - Document specific IDs *
+  PSD_ALPHA_NAMES_UNI   = 1045,    Loaded     * 0x0415 - Unicode alpha names *
+  PSD_IDX_COL_TAB_CNT   = 1046,    Loaded     * 0x0416 - Indexed color table count *
+  PSD_IDX_TRANSPARENT   = 1047,               * 0x0417 - Index of transparent color (if any) *
+  PSD_GLOBAL_ALT        = 1049,               * 0x0419 - Global altitude *
+  PSD_SLICES            = 1050,               * 0x041a - Slices *
+  PSD_WORKFLOW_URL_UNI  = 1051,               * 0x041b - Workflow URL - Unicode string *
+  PSD_JUMP_TO_XPEP      = 1052,               * 0x041c - Jump to XPEP (?) *
+  PSD_ALPHA_ID          = 1053,    Loaded     * 0x041d - Alpha IDs *
+  PSD_URL_LIST_UNI      = 1054,               * 0x041e - URL list - unicode *
+  PSD_VERSION_INFO      = 1057,               * 0x0421 - Version info *
+  PSD_EXIF_DATA         = 1058,    Loaded     * 0x0422 - Exif data block 1 *
+  PSD_EXIF_DATA_3       = 1059                * 0X0423 - Exif data block 3 (?) *
+  PSD_XMP_DATA          = 1060,    Loaded     * 0x0424 - XMP data block *
+  PSD_CAPTION_DIGEST    = 1061,               * 0x0425 - Caption digest *
+  PSD_PRINT_SCALE       = 1062,               * 0x0426 - Print scale *
+  PSD_PIXEL_AR          = 1064,               * 0x0428 - Pixel aspect ratio *
+  PSD_LAYER_COMPS       = 1065,               * 0x0429 - Layer comps *
+  PSD_ALT_DUOTONE_COLOR = 1066,               * 0x042A - Alternative Duotone colors *
+  PSD_ALT_SPOT_COLOR    = 1067,               * 0x042B - Alternative Spot colors *
+  PSD_LAYER_SELECT_ID   = 1069,               * 0x042D - Layer selection ID *
+  PSD_HDR_TONING_INFO   = 1070,               * 0x042E - HDR toning information *
+  PSD_PRINT_INFO_SCALE  = 1071,               * 0x042F - Print scale *
+  PSD_LAYER_GROUP_E_ID  = 1072,               * 0x0430 - Layer group(s) enabled ID *
+  PSD_COLOR_SAMPLER_NEW = 1073,               * 0x0431 - Color sampler resource for ps CS3 and higher PSD files *
+  PSD_MEASURE_SCALE     = 1074,               * 0x0432 - Measurement scale *
+  PSD_TIMELINE_INFO     = 1075,               * 0x0433 - Timeline information *
+  PSD_SHEET_DISCLOSE    = 1076,               * 0x0434 - Sheet discloser *
+  PSD_DISPLAY_INFO_NEW  = 1077,    Loaded     * 0x0435 - DisplayInfo structure for ps CS3 and higher PSD files *
+  PSD_ONION_SKINS       = 1078,               * 0x0436 - Onion skins *
+  PSD_COUNT_INFO        = 1080,               * 0x0438 - Count information*
+  PSD_PRINT_INFO        = 1082,               * 0x043A - Print information added in ps CS5*
+  PSD_PRINT_STYLE       = 1083,               * 0x043B - Print style *
+  PSD_MAC_NSPRINTINFO   = 1084,               * 0x043C - Mac NSPrintInfo*
+  PSD_WIN_DEVMODE       = 1085,               * 0x043D - Windows DEVMODE *
+  PSD_AUTO_SAVE_PATH    = 1086,               * 0x043E - Auto save file path *
+  PSD_AUTO_SAVE_FORMAT  = 1087,               * 0x043F - Auto save format *
+  PSD_PATH_INFO_FIRST   = 2000,    Loaded     * 0x07d0 - First path info block *
+  PSD_PATH_INFO_LAST    = 2998,    Loaded     * 0x0bb6 - Last path info block *
+  PSD_CLIPPING_PATH     = 2999,               * 0x0bb7 - Name of clipping path *
+  PSD_PLUGIN_R_FIRST    = 4000,               * 0x0FA0 - First plugin resource *
+  PSD_PLUGIN_R_LAST     = 4999,               * 0x1387 - Last plugin resource *
+  PSD_IMAGEREADY_VARS   = 7000,    PS Only    * 0x1B58 - Imageready variables *
+  PSD_IMAGEREADY_DATA   = 7001,    PS Only    * 0x1B59 - Imageready data sets *
+  PSD_LIGHTROOM_WORK    = 8000,    PS Only    * 0x1F40 - Lightroom workflow *
+  PSD_PRINT_FLAGS_2     = 10000               * 0x2710 - Print flags *
+*/
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+#include <libgimp/gimp.h>
+
+#include <jpeglib.h>
+#include <jerror.h>
+
+#ifdef HAVE_IPTCDATA
+#include <libiptcdata/iptc-data.h>
+#endif /* HAVE_IPTCDATA */
+
+#include "psd.h"
+#include "psd-util.h"
+#include "psd-image-res-load.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+#define EXIF_HEADER_SIZE 8
+
+/*  Local function prototypes  */
+static gint     load_resource_unknown  (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_ps_only  (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_1005     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_1006     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        PSDimage              *img_a,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_1007     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        PSDimage              *img_a,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_1008     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_1022     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        PSDimage              *img_a,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_1024     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        PSDimage              *img_a,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_1028     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_1032     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+/* 1033 - Thumbnail needs special handling since it calls the jpeg library
+ *        which needs a classic FILE. */
+static gint     load_resource_1033     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        GFile                 *file,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_1039     (const PSDimageres     *res_a,
+                                        PSDimage              *img_a,
+                                        GimpImage             *image,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_1045     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        PSDimage              *img_a,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_1046     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_1053     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        PSDimage              *img_a,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_1058     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_1069     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        PSDimage              *img_a,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_1077     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        PSDimage              *img_a,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_2000     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+static gint     load_resource_2999     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        GInputStream          *input,
+                                        GError               **error);
+
+/* Public Functions */
+gint
+get_image_resource_header (PSDimageres   *res_a,
+                           GInputStream  *input,
+                           GError       **error)
+{
+  gint32        read_len;
+  gint32        write_len;
+  gchar        *name;
+
+  if (psd_read (input, &res_a->type, 4, error) < 4 ||
+      psd_read (input, &res_a->id,   2, error) < 2)
+    {
+      psd_set_error (error);
+      return -1;
+    }
+  res_a->id = GUINT16_FROM_BE (res_a->id);
+  name = fread_pascal_string (&read_len, &write_len, 2, input, error);
+  if (*error)
+    return -1;
+  if (name != NULL)
+    g_strlcpy (res_a->name, name, write_len + 1);
+  else
+    res_a->name[0] = 0x0;
+  g_free (name);
+  if (psd_read (input, &res_a->data_len, 4, error) < 4)
+    {
+      psd_set_error (error);
+      return -1;
+    }
+  res_a->data_len = GUINT32_FROM_BE (res_a->data_len);
+  res_a->data_start = g_seekable_tell (G_SEEKABLE (input));
+
+  IFDBG(2) g_debug ("Type: %.4s, id: %d, start: %" G_GOFFSET_FORMAT ", len: %" G_GSIZE_FORMAT,
+                    res_a->type, res_a->id, res_a->data_start, res_a->data_len);
+
+  return 0;
+}
+
+gint
+load_image_resource (PSDimageres  *res_a,
+                     GimpImage    *image,
+                     PSDimage     *img_a,
+                     GInputStream  *input,
+                     gboolean     *resolution_loaded,
+                     gboolean     *profile_loaded,
+                     GError      **error)
+{
+  gint  pad;
+
+  /* Set file position to start of image resource data block */
+  if (! psd_seek (input, res_a->data_start, G_SEEK_SET, error))
+    {
+      psd_set_error (error);
+      return -1;
+    }
+
+  /* Process image resource blocks */
+  if (memcmp (res_a->type, "8BIM", 4) != 0 &&
+      memcmp (res_a->type, "MeSa", 4) !=0)
+    {
+      IFDBG(1) g_debug ("Unknown image resource type signature %.4s",
+                        res_a->type);
+    }
+  else
+    {
+      switch (res_a->id)
+        {
+          case PSD_PS2_IMAGE_INFO:
+          case PSD_PS2_COLOR_TAB:
+          case PSD_OBSOLETE_01:
+          case PSD_OBSOLETE_02:
+          case PSD_OBSOLETE_03:
+            /* Drop obsolete image resource blocks */
+            IFDBG(2) g_debug ("Obsolete image resource block: %d",
+                               res_a->id);
+            break;
+
+          case PSD_THUMB_RES:
+          case PSD_THUMB_RES2:
+            /* Drop thumbnails from standard file load */
+            IFDBG(2) g_debug ("Thumbnail resource block: %d",
+                               res_a->id);
+            break;
+
+          case PSD_MAC_PRINT_INFO:
+          case PSD_JPEG_QUAL:
+            /* Save photoshop resources with no meaning for GIMP
+              as image parasites */
+            load_resource_ps_only (res_a, image, input, error);
+            break;
+
+          case PSD_RESN_INFO:
+            if (! load_resource_1005 (res_a, image, input, error))
+              *resolution_loaded = TRUE;
+            break;
+
+          case PSD_ALPHA_NAMES:
+            load_resource_1006 (res_a, image, img_a, input, error);
+            break;
+
+          case PSD_DISPLAY_INFO:
+            load_resource_1007 (res_a, image, img_a, input, error);
+            break;
+
+          case PSD_CAPTION:
+            load_resource_1008 (res_a, image, input, error);
+            break;
+
+          case PSD_QUICK_MASK:
+            if (! img_a->merged_image_only)
+              load_resource_1022 (res_a, image, img_a, input, error);
+            break;
+
+          case PSD_LAYER_STATE:
+            if (! img_a->merged_image_only)
+              load_resource_1024 (res_a, image, img_a, input, error);
+            break;
+
+          case PSD_WORKING_PATH:
+            if (! img_a->merged_image_only)
+              load_resource_2000 (res_a, image, input, error);
+            break;
+
+          case PSD_IPTC_NAA_DATA:
+            load_resource_1028 (res_a, image, input, error);
+            break;
+
+          case PSD_GRID_GUIDE:
+            if (! img_a->merged_image_only)
+              load_resource_1032 (res_a, image, input, error);
+            break;
+
+          case PSD_ICC_PROFILE:
+            if (! load_resource_1039 (res_a, img_a, image, input, error))
+              *profile_loaded = TRUE;
+            break;
+
+          case PSD_ALPHA_NAMES_UNI:
+            load_resource_1045 (res_a, image, img_a, input, error);
+            break;
+
+          case PSD_IDX_COL_TAB_CNT:
+            load_resource_1046 (res_a, image, input, error);
+            break;
+
+          case PSD_ALPHA_ID:
+            if (! img_a->merged_image_only)
+              load_resource_1053 (res_a, image, img_a, input, error);
+            break;
+
+          case PSD_EXIF_DATA:
+            load_resource_1058 (res_a, image, input, error);
+            break;
+
+          case PSD_LAYER_SELECT_ID:
+            if (! img_a->merged_image_only)
+              load_resource_1069 (res_a, image, img_a, input, error);
+            break;
+
+          case PSD_XMP_DATA:
+            break;
+
+          case PSD_DISPLAY_INFO_NEW:
+            load_resource_1077 (res_a, image, img_a, input, error);
+            break;
+
+          case PSD_CLIPPING_PATH:
+            load_resource_2999 (res_a, image, input, error);
+            break;
+
+          case PSD_LAYER_COMPS:
+            img_a->unsupported_features->layer_comp = TRUE;
+            img_a->unsupported_features->show_gui = TRUE;
+            load_resource_unknown (res_a, image, input, error);
+            break;
+
+          default:
+            if (res_a->id >= 2000 &&
+                res_a->id <  2999)
+              load_resource_2000 (res_a, image, input, error);
+            else
+              load_resource_unknown (res_a, image, input, error);
+        }
+    }
+
+  /* Image blocks are null padded to even length */
+  if (res_a->data_len % 2 == 0)
+    pad = 0;
+  else
+    pad = 1;
+
+  /* Set file position to end of image resource block */
+  if (! psd_seek (input, res_a->data_start + res_a->data_len + pad, G_SEEK_SET, error))
+    {
+      psd_set_error (error);
+      return -1;
+    }
+
+  return 0;
+}
+
+gint
+load_thumbnail_resource (PSDimageres   *res_a,
+                         GimpImage     *image,
+                         GFile         *file,
+                         GInputStream  *input,
+                         GError       **error)
+{
+  gint  rtn = 0;
+  gint  pad;
+
+  /* Set file position to start of image resource data block */
+  if (! psd_seek (input, res_a->data_start, G_SEEK_SET, error))
+    {
+      psd_set_error (error);
+      return -1;
+    }
+
+  /* Process image resource blocks */
+  if (res_a->id == PSD_THUMB_RES
+      || res_a->id == PSD_THUMB_RES2)
+    {
+      /* Load thumbnails from standard file load */
+      load_resource_1033 (res_a, image, file, input, error);
+      rtn = 1;
+    }
+
+  /* Image blocks are null padded to even length */
+  if (res_a->data_len % 2 == 0)
+    pad = 0;
+  else
+    pad = 1;
+
+  /* Set file position to end of image resource block */
+  if (psd_seek (input, res_a->data_start + res_a->data_len + pad, G_SEEK_SET, error))
+    {
+      psd_set_error (error);
+      return -1;
+    }
+
+  return rtn;
+}
+
+/* Private Functions */
+
+static gint
+load_resource_unknown (const PSDimageres  *res_a,
+                       GimpImage          *image,
+                       GInputStream       *input,
+                       GError            **error)
+{
+  /* Unknown image resources attached as parasites to re-save later */
+  GimpParasite  *parasite;
+  gchar         *data;
+  gchar         *name;
+
+  IFDBG(2) g_debug ("Process unknown image resource block: %d", res_a->id);
+
+  data = g_malloc (res_a->data_len);
+  if (res_a->data_len > 0 && psd_read (input, data, res_a->data_len, error) < res_a->data_len)
+    {
+      psd_set_error (error);
+      g_free (data);
+      return -1;
+    }
+
+  name = g_strdup_printf ("psd-image-resource-%.4s-%.4x",
+                           res_a->type, res_a->id);
+  IFDBG(2) g_debug ("Parasite name: %s", name);
+
+  parasite = gimp_parasite_new (name, 0, res_a->data_len, data);
+  gimp_image_attach_parasite (image, parasite);
+  gimp_parasite_free (parasite);
+  g_free (data);
+  g_free (name);
+
+  return 0;
+}
+
+static gint
+load_resource_ps_only (const PSDimageres  *res_a,
+                       GimpImage          *image,
+                       GInputStream       *input,
+                       GError            **error)
+{
+  /* Save photoshop resources with no meaning for GIMP as image parasites
+     to re-save later */
+  GimpParasite  *parasite;
+  gchar         *data;
+  gchar         *name;
+
+  IFDBG(3) g_debug ("Process image resource block: %d", res_a->id);
+
+  data = g_malloc (res_a->data_len);
+  if (psd_read (input, data, res_a->data_len, error) < res_a->data_len)
+    {
+      psd_set_error (error);
+      g_free (data);
+      return -1;
+    }
+
+  name = g_strdup_printf ("psd-image-resource-%.4s-%.4x",
+                           res_a->type, res_a->id);
+  IFDBG(2) g_debug ("Parasite name: %s", name);
+
+  parasite = gimp_parasite_new (name, 0, res_a->data_len, data);
+  gimp_image_attach_parasite (image, parasite);
+  gimp_parasite_free (parasite);
+  g_free (data);
+  g_free (name);
+
+  return 0;
+}
+
+static gint
+load_resource_1005 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  /* Load image resolution and unit of measure */
+
+  /* FIXME  width unit and height unit unused at present */
+
+  ResolutionInfo  res_info;
+  GimpUnit       *image_unit;
+
+  IFDBG(2) g_debug ("Process image resource block 1005: Resolution Info");
+
+  if (psd_read (input, &res_info.hRes,       4, error) < 4 ||
+      psd_read (input, &res_info.hResUnit,   2, error) < 2 ||
+      psd_read (input, &res_info.widthUnit,  2, error) < 2 ||
+      psd_read (input, &res_info.vRes,       4, error) < 4 ||
+      psd_read (input, &res_info.vResUnit,   2, error) < 2 ||
+      psd_read (input, &res_info.heightUnit, 2, error) < 2)
+    {
+      psd_set_error (error);
+      return -1;
+    }
+  res_info.hRes = GINT32_FROM_BE (res_info.hRes);
+  res_info.hResUnit = GINT16_FROM_BE (res_info.hResUnit);
+  res_info.widthUnit = GINT16_FROM_BE (res_info.widthUnit);
+  res_info.vRes = GINT32_FROM_BE (res_info.vRes);
+  res_info.vResUnit = GINT16_FROM_BE (res_info.vResUnit);
+  res_info.heightUnit = GINT16_FROM_BE (res_info.heightUnit);
+
+  IFDBG(3) g_debug ("Resolution: %d, %d, %d, %d, %d, %d",
+                     res_info.hRes,
+                     res_info.hResUnit,
+                     res_info.widthUnit,
+                     res_info.vRes,
+                     res_info.vResUnit,
+                     res_info.heightUnit);
+
+  /* Resolution always recorded as pixels / inch in a fixed point implied
+     decimal int32 with 16 bits before point and 16 after (i.e. cast as
+     double and divide resolution by 2^16 */
+  gimp_image_set_resolution (image,
+                             res_info.hRes / 65536.0, res_info.vRes / 65536.0);
+
+  /* GIMP only has one display unit so use ps horizontal resolution unit */
+  switch (res_info.hResUnit)
+    {
+    case PSD_RES_INCH:
+      image_unit = gimp_unit_inch ();
+      break;
+    case PSD_RES_CM:
+      image_unit = gimp_unit_mm ();
+      break;
+    default:
+      image_unit = gimp_unit_inch ();
+    }
+
+  gimp_image_set_unit (image, image_unit);
+
+  return 0;
+}
+
+static gint
+load_resource_1006 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    PSDimage           *img_a,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  /* Load alpha channel names stored as a series of pascal strings
+     unpadded between strings */
+
+  gchar        *str;
+  gint32        block_rem;
+  gint32        read_len;
+  gint32        write_len;
+
+  IFDBG(2) g_debug ("Process image resource block 1006: Alpha Channel Names");
+
+  if (img_a->alpha_names)
+    {
+      IFDBG(3) g_debug ("Alpha names loaded from unicode resource block");
+      return 0;
+    }
+
+  img_a->alpha_names = g_ptr_array_new ();
+
+  block_rem = res_a->data_len;
+  while (block_rem > 1)
+    {
+      str = fread_pascal_string (&read_len, &write_len, 1, input, error);
+      if (*error)
+        return -1;
+      IFDBG(3) g_debug ("String: %s, %d, %d", str, read_len, write_len);
+      if (write_len >= 0)
+        {
+          g_ptr_array_add (img_a->alpha_names, (gpointer) str);
+        }
+      block_rem -= read_len;
+    }
+
+  return 0;
+}
+
+static gint
+load_resource_1007 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    PSDimage           *img_a,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  /* Load alpha channel display info */
+
+  DisplayInfo       dsp_info;
+  CMColor           ps_color;
+  gint16            tot_rec;
+  gint              cidx;
+
+  IFDBG(2) g_debug ("Process image resource block 1007: Display Info");
+  tot_rec = res_a->data_len / 14;
+  if (tot_rec == 0)
+    return 0;
+
+  img_a->alpha_display_info = g_new (PSDchanneldata *, tot_rec);
+  img_a->alpha_display_count = tot_rec;
+  for (cidx = 0; cidx < tot_rec; ++cidx)
+    {
+      GeglColor *color;
+
+      if (psd_read (input, &dsp_info.colorSpace, 2, error) < 2 ||
+          psd_read (input, &dsp_info.color,      8, error) < 8 ||
+          psd_read (input, &dsp_info.opacity,    2, error) < 2 ||
+          psd_read (input, &dsp_info.kind,       1, error) < 1 ||
+          psd_read (input, &dsp_info.padding,    1, error) < 1)
+        {
+          psd_set_error (error);
+          return -1;
+        }
+      dsp_info.colorSpace = GINT16_FROM_BE (dsp_info.colorSpace);
+      ps_color.cmyk.cyan = GUINT16_FROM_BE (dsp_info.color[0]);
+      ps_color.cmyk.magenta = GUINT16_FROM_BE (dsp_info.color[1]);
+      ps_color.cmyk.yellow = GUINT16_FROM_BE (dsp_info.color[2]);
+      ps_color.cmyk.black = GUINT16_FROM_BE (dsp_info.color[3]);
+      dsp_info.opacity = GINT16_FROM_BE (dsp_info.opacity);
+
+      color = gegl_color_new ("red");
+
+      switch (dsp_info.colorSpace)
+        {
+          case PSD_CS_RGB:
+            /* TODO: which space should we use? */
+            gegl_color_set_rgba_with_space (color,
+                                            ps_color.rgb.red / 65535.0,
+                                            ps_color.rgb.green / 65535.0,
+                                            ps_color.rgb.blue / 65535.0,
+                                            1.0, NULL);
+            break;
+
+          case PSD_CS_HSB:
+            {
+              gfloat hsv[3] =
+                {
+                  ps_color.hsv.hue / 65535.0f,
+                  ps_color.hsv.saturation / 65535.0f,
+                  ps_color.hsv.value / 65535.0f
+                };
+
+              gegl_color_set_pixel (color, babl_format ("HSV float"), hsv);
+            }
+          break;
+
+          case PSD_CS_CMYK:
+            /* TODO: again, which space? */
+            gegl_color_set_cmyk (color,
+                                 1.0 - ps_color.cmyk.cyan / 65535.0,
+                                 1.0 - ps_color.cmyk.magenta / 65535.0,
+                                 1.0 - ps_color.cmyk.yellow / 65535.0,
+                                 1.0 - ps_color.cmyk.black / 65535.0,
+                                 1.0, NULL);
+            break;
+
+          case PSD_CS_GRAYSCALE:
+            {
+              gdouble gray = ps_color.gray.gray / 10000.0;
+
+              /* TODO: which space? */
+              gegl_color_set_pixel (color, babl_format_with_space ("Y' double", NULL), &gray);
+            }
+            break;
+
+          case PSD_CS_FOCOLTONE:
+          case PSD_CS_TRUMATCH:
+          case PSD_CS_HKS:
+          case PSD_CS_LAB:
+          case PSD_CS_PANTONE:
+          case PSD_CS_TOYO:
+          case PSD_CS_DIC:
+          case PSD_CS_ANPA:
+          default:
+            if (CONVERSION_WARNINGS)
+              g_message ("Unsupported color space: %d",
+                         dsp_info.colorSpace);
+        }
+
+      IFDBG(2) g_debug ("PS cSpace: %d, col: %d %d %d %d, opacity: %d, kind: %d",
+                        dsp_info.colorSpace, ps_color.cmyk.cyan, ps_color.cmyk.magenta,
+                        ps_color.cmyk.yellow, ps_color.cmyk.black, dsp_info.opacity,
+                        dsp_info.kind);
+
+      IFDBG(2) g_debug ("cSpace: %d, opacity: %d, kind: %d",
+                        dsp_info.colorSpace, dsp_info.opacity, dsp_info.kind);
+
+      img_a->alpha_display_info[cidx] = g_malloc0 (sizeof (PSDchanneldata));
+      img_a->alpha_display_info[cidx]->gimp_color = color;
+      img_a->alpha_display_info[cidx]->opacity = dsp_info.opacity;
+      img_a->alpha_display_info[cidx]->ps_kind = dsp_info.kind;
+      img_a->alpha_display_info[cidx]->ps_cspace = dsp_info.colorSpace;
+      img_a->alpha_display_info[cidx]->ps_color = ps_color;
+    }
+
+  return 0;
+}
+
+static gint
+load_resource_1008 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  /* Load image caption */
+  GimpParasite  *parasite;
+  gchar         *caption;
+  gint32         read_len;
+  gint32         write_len;
+
+  IFDBG(2) g_debug ("Process image resource block: 1008: Caption");
+  caption = fread_pascal_string (&read_len, &write_len, 1, input, error);
+  if (*error)
+    return -1;
+
+  IFDBG(3) g_debug ("Caption: %s", caption);
+  parasite = gimp_parasite_new (GIMP_PARASITE_COMMENT, GIMP_PARASITE_PERSISTENT,
+                                write_len, caption);
+  gimp_image_attach_parasite (image, parasite);
+  gimp_parasite_free (parasite);
+  g_free (caption);
+
+  return 0;
+}
+
+static gint
+load_resource_1022 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    PSDimage           *img_a,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  /* Load quick mask info */
+  gboolean quick_mask_empty = TRUE; /* Quick mask initially empty */
+
+  IFDBG(2) g_debug ("Process image resource block: 1022: Quick Mask");
+
+  if (psd_read (input, &img_a->quick_mask_id, 2, error) < 2 ||
+      psd_read (input, &quick_mask_empty,     1, error) < 1)
+    {
+      psd_set_error (error);
+      return -1;
+    }
+  img_a->quick_mask_id = GUINT16_FROM_BE (img_a->quick_mask_id);
+
+  IFDBG(3) g_debug ("Quick mask channel: %d, empty: %d",
+                    img_a->quick_mask_id,
+                    quick_mask_empty);
+
+  return 0;
+}
+
+static gint
+load_resource_1024 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    PSDimage           *img_a,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  /* Load image layer state - current active layer counting from bottom up */
+  IFDBG(2) g_debug ("Process image resource block: 1024: Layer State");
+
+  if (psd_read (input, &img_a->layer_state, 2, error) < 2)
+    {
+      psd_set_error (error);
+      return -1;
+    }
+  img_a->layer_state = GUINT16_FROM_BE (img_a->layer_state);
+
+  return 0;
+}
+
+static gint
+load_resource_1028 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  /* Load IPTC data block */
+
+#ifdef HAVE_IPTCDATA
+  IptcData     *iptc_data;
+  guchar       *iptc_buf;
+  guint         iptc_buf_len;
+#else
+  gchar        *name;
+#endif /* HAVE_IPTCDATA */
+
+  GimpParasite *parasite;
+  gchar        *res_data;
+
+  IFDBG(2) g_debug ("Process image resource block: 1028: IPTC data");
+
+  res_data = g_malloc (res_a->data_len);
+  if (psd_read (input, res_data, res_a->data_len, error) < res_a->data_len)
+    {
+      psd_set_error (error);
+      g_free (res_data);
+      return -1;
+    }
+
+#ifdef HAVE_IPTCDATA
+  /* Load IPTC data structure */
+  iptc_data = iptc_data_new_from_data (res_data, res_a->data_len);
+  IFDBG (3) iptc_data_dump (iptc_data, 0);
+
+  /* Store resource data as a GIMP IPTC parasite */
+  IFDBG (2) g_debug ("Processing IPTC data as GIMP IPTC parasite");
+  /* Serialize IPTC data */
+  iptc_data_save (iptc_data, &iptc_buf, &iptc_buf_len);
+  if (iptc_buf_len > 0)
+    {
+      parasite = gimp_parasite_new (GIMP_PARASITE_IPTC,
+                                    GIMP_PARASITE_PERSISTENT,
+                                    iptc_buf_len, iptc_buf);
+      gimp_image_attach_parasite (image, parasite);
+      gimp_parasite_free (parasite);
+    }
+
+  iptc_data_unref (iptc_data);
+  g_free (iptc_buf);
+
+#else
+  /* Store resource data as a standard psd parasite */
+  IFDBG (2) g_debug ("Processing IPTC data as psd parasite");
+  name = g_strdup_printf ("psd-image-resource-%.4s-%.4x",
+                           res_a->type, res_a->id);
+  IFDBG(3) g_debug ("Parasite name: %s", name);
+
+  parasite = gimp_parasite_new (name, 0, res_a->data_len, res_data);
+  gimp_image_attach_parasite (image, parasite);
+  gimp_parasite_free (parasite);
+  g_free (name);
+
+#endif /* HAVE_IPTCDATA */
+
+  g_free (res_data);
+  return 0;
+}
+
+static gint
+load_resource_1032 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  /* Load grid and guides */
+
+  /* Grid info is not used (CS2 or earlier) */
+
+  GuideHeader           hdr;
+  GuideResource         guide;
+  gint                  i;
+
+  IFDBG(2) g_debug ("Process image resource block 1032: Grid and Guide Info");
+
+  if (psd_read (input, &hdr.fVersion,    4, error) < 4 ||
+      psd_read (input, &hdr.fGridCycleV, 4, error) < 4 ||
+      psd_read (input, &hdr.fGridCycleH, 4, error) < 4 ||
+      psd_read (input, &hdr.fGuideCount, 4, error) < 4)
+    {
+      psd_set_error (error);
+      return -1;
+    }
+  hdr.fVersion = GUINT32_FROM_BE (hdr.fVersion);
+  hdr.fGridCycleV = GUINT32_FROM_BE (hdr.fGridCycleV);
+  hdr.fGridCycleH = GUINT32_FROM_BE (hdr.fGridCycleH);
+  hdr.fGuideCount = GUINT32_FROM_BE (hdr.fGuideCount);
+
+  IFDBG(3) g_debug ("Grids & Guides: %d, %d, %d, %d",
+                     hdr.fVersion,
+                     hdr.fGridCycleV,
+                     hdr.fGridCycleH,
+                     hdr.fGuideCount);
+
+  for (i = 0; i < hdr.fGuideCount; ++i)
+    {
+      if (psd_read (input, &guide.fLocation,  4, error) < 4 ||
+          psd_read (input, &guide.fDirection, 1, error) < 1)
+        {
+          psd_set_error (error);
+          return -1;
+        }
+      guide.fLocation = GUINT32_FROM_BE (guide.fLocation);
+      guide.fLocation /= 32;
+
+      IFDBG(3) g_debug ("Guide: %d px, %d",
+                         guide.fLocation,
+                         guide.fDirection);
+
+      if (guide.fDirection == PSD_VERTICAL)
+        gimp_image_add_vguide (image, guide.fLocation);
+      else
+        gimp_image_add_hguide (image, guide.fLocation);
+    }
+
+  return 0;
+}
+
+static gint
+load_resource_1033 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    GFile              *file,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  /* Load thumbnail image */
+
+  struct jpeg_decompress_struct cinfo;
+  struct jpeg_error_mgr         jerr;
+
+  FILE                 *f;
+  ThumbnailInfo         thumb_info;
+  GeglBuffer           *buffer;
+  const Babl           *format;
+  GimpLayer            *layer;
+  guchar               *buf;
+  guchar               *rgb_buf;
+  guchar              **rowbuf;
+  gint                  i;
+
+  IFDBG(2) g_debug ("Process image resource block %d: Thumbnail Image", res_a->id);
+
+  /* Read thumbnail resource header info */
+  if (psd_read (input, &thumb_info.format,         4, error) < 4 ||
+      psd_read (input, &thumb_info.width,          4, error) < 4 ||
+      psd_read (input, &thumb_info.height,         4, error) < 4 ||
+      psd_read (input, &thumb_info.widthbytes,     4, error) < 4 ||
+      psd_read (input, &thumb_info.size,           4, error) < 4 ||
+      psd_read (input, &thumb_info.compressedsize, 4, error) < 4 ||
+      psd_read (input, &thumb_info.bitspixel,      2, error) < 2 ||
+      psd_read (input, &thumb_info.planes,         2, error) < 2)
+    {
+      psd_set_error (error);
+      return -1;
+    }
+  thumb_info.format = GINT32_FROM_BE (thumb_info.format);
+  thumb_info.width = GINT32_FROM_BE (thumb_info.width);
+  thumb_info.height = GINT32_FROM_BE (thumb_info.height);
+  thumb_info.widthbytes = GINT32_FROM_BE (thumb_info.widthbytes);
+  thumb_info.size = GINT32_FROM_BE (thumb_info.size);
+  thumb_info.compressedsize = GINT32_FROM_BE (thumb_info.compressedsize);
+  thumb_info.bitspixel = GINT16_FROM_BE (thumb_info.bitspixel);
+  thumb_info.planes = GINT16_FROM_BE (thumb_info.planes);
+
+  IFDBG(2) g_debug ("\nThumbnail:\n"
+                    "\tFormat: %d\n"
+                    "\tDimensions: %d x %d\n",
+                     thumb_info.format,
+                     thumb_info.width,
+                     thumb_info.height);
+
+  if (thumb_info.format != 1)
+    {
+      IFDBG(1) g_debug ("Unknown thumbnail format %d", thumb_info.format);
+      return -1;
+    }
+
+  /* Load Jpeg RGB thumbnail info */
+
+  /* Open input also as a FILE. */
+  f = g_fopen (g_file_peek_path (file), "rb");
+
+  if (! f)
+    return -1;
+
+  /* Now seek to the same position as we have in input. */
+  fseek(f, g_seekable_tell (G_SEEKABLE (input)), SEEK_SET);
+
+  /* Step 1: Allocate and initialize JPEG decompression object */
+  cinfo.err = jpeg_std_error (&jerr);
+  jpeg_create_decompress (&cinfo);
+
+  /* Step 2: specify data source (eg, a file) */
+  jpeg_stdio_src(&cinfo, f);
+
+  /* Step 3: read file parameters with jpeg_read_header() */
+  jpeg_read_header (&cinfo, TRUE);
+
+  /* Step 4: set parameters for decompression */
+
+
+  /* Step 5: Start decompressor */
+  jpeg_start_decompress (&cinfo);
+
+  /* temporary buffers */
+  buf = g_new (guchar, cinfo.output_height * cinfo.output_width
+               * cinfo.output_components);
+  if (res_a->id == PSD_THUMB_RES)
+    rgb_buf = g_new (guchar, cinfo.output_height * cinfo.output_width
+                     * cinfo.output_components);
+  else
+    rgb_buf = NULL;
+  rowbuf = g_new (guchar *, cinfo.output_height);
+
+  for (i = 0; i < cinfo.output_height; ++i)
+    rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i;
+
+  /* Create image layer */
+  gimp_image_resize (image, cinfo.output_width, cinfo.output_height, 0, 0);
+  layer = gimp_layer_new (image, _("Background"),
+                          cinfo.output_width,
+                          cinfo.output_height,
+                          GIMP_RGB_IMAGE,
+                          100,
+                          gimp_image_get_default_new_layer_mode (image));
+  buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
+  format = babl_format ("R'G'B' u8");
+
+  /* Step 6: while (scan lines remain to be read) */
+  /*           jpeg_read_scanlines(...); */
+  while (cinfo.output_scanline < cinfo.output_height)
+    {
+      jpeg_read_scanlines (&cinfo,
+                           (JSAMPARRAY) &rowbuf[cinfo.output_scanline], 1);
+    }
+
+  if (res_a->id == PSD_THUMB_RES)   /* Order is BGR for resource 1033 */
+    {
+      guchar *dst = rgb_buf;
+      guchar *src = buf;
+
+      for (i = 0; i < gegl_buffer_get_width (buffer) * gegl_buffer_get_height (buffer); ++i)
+        {
+          guchar r, g, b;
+
+          r = *(src++);
+          g = *(src++);
+          b = *(src++);
+          *(dst++) = b;
+          *(dst++) = g;
+          *(dst++) = r;
+        }
+    }
+
+  gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0,
+                                           gegl_buffer_get_width (buffer),
+                                           gegl_buffer_get_height (buffer)),
+                   0, format, rgb_buf ? rgb_buf : buf, GEGL_AUTO_ROWSTRIDE);
+
+  /* Step 7: Finish decompression */
+  jpeg_finish_decompress (&cinfo);
+  /* We can ignore the return value since suspension is not possible
+   * with the stdio data source.
+   */
+
+  /* Step 8: Release JPEG decompression object */
+  jpeg_destroy_decompress (&cinfo);
+
+  /* free up the temporary buffers */
+  g_free (rowbuf);
+  g_free (buf);
+  g_free (rgb_buf);
+
+  /* Close FILE */
+  fclose (f);
+
+  /* At this point you may want to check to see whether any
+   * corrupt-data warnings occurred (test whether
+   * jerr.num_warnings is nonzero).
+   */
+  gimp_image_insert_layer (image, layer, NULL, 0);
+  g_object_unref (buffer);
+
+  return 0;
+}
+
+static gint
+load_resource_1039 (const PSDimageres  *res_a,
+                    PSDimage           *img_a,
+                    GimpImage          *image,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  /* Load ICC profile */
+  GimpColorProfile *profile;
+  gchar            *icc_profile;
+
+  IFDBG(2) g_debug ("Process image resource block: 1039: ICC Profile");
+
+  icc_profile = g_malloc (res_a->data_len);
+  if (psd_read (input, icc_profile, res_a->data_len, error) < res_a->data_len)
+    {
+      psd_set_error (error);
+      g_free (icc_profile);
+      return -1;
+    }
+
+  profile = gimp_color_profile_new_from_icc_profile ((guint8 *) icc_profile,
+                                                     res_a->data_len,
+                                                     NULL);
+  if (profile)
+    {
+      if (img_a->color_mode == PSD_CMYK &&
+          gimp_color_profile_is_cmyk (profile))
+        {
+          img_a->cmyk_profile = profile;
+          /* Store CMYK profile in GimpImage if attached */
+          gimp_image_set_simulation_profile (image, img_a->cmyk_profile);
+        }
+      else if (img_a->color_mode == PSD_LAB)
+        {
+          g_debug ("LAB color profile ignored.");
+          g_object_unref (profile);
+        }
+      else
+        {
+          gimp_image_set_color_profile (image, profile);
+          g_object_unref (profile);
+        }
+    }
+
+  g_free (icc_profile);
+
+  return 0;
+}
+
+static gint
+load_resource_1045 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    PSDimage           *img_a,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  /* Load alpha channel names stored as a series of unicode strings
+     in a GPtrArray */
+
+  gchar        *str;
+  gint32        block_rem;
+  gint32        read_len;
+  gint32        write_len;
+
+  IFDBG(2) g_debug ("Process image resource block 1045: Unicode Alpha Channel Names");
+
+  if (img_a->alpha_names)
+    {
+      gint      i;
+      IFDBG(3) g_debug ("Deleting localised alpha channel names");
+      for (i = 0; i < img_a->alpha_names->len; ++i)
+        {
+          str = g_ptr_array_index (img_a->alpha_names, i);
+          g_free (str);
+        }
+      g_ptr_array_free (img_a->alpha_names, TRUE);
+    }
+
+  img_a->alpha_names = g_ptr_array_new ();
+
+  block_rem = res_a->data_len;
+  while (block_rem > 1)
+    {
+      str = fread_unicode_string (&read_len, &write_len, 1, FALSE, input,
+                                  error);
+      if (*error)
+        return -1;
+
+      IFDBG(3) g_debug ("String: %s, %d, %d", str, read_len, write_len);
+      if (write_len >= 0)
+        {
+          g_ptr_array_add (img_a->alpha_names, (gpointer) str);
+        }
+      block_rem -= read_len;
+    }
+
+  return 0;
+}
+
+static gint
+load_resource_1046 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  /* Load indexed color table count */
+  gint16 index_count = 0;
+
+  IFDBG(2) g_debug ("Process image resource block: 1046: Indexed Color Table Count");
+
+  if (psd_read (input, &index_count, 2, error) < 2)
+    {
+      psd_set_error (error);
+      return -1;
+    }
+  index_count = GINT16_FROM_BE (index_count);
+
+  IFDBG(3) g_debug ("Indexed color table count: %d", index_count);
+  /* FIXME - check that we have indexed image */
+  if (index_count && index_count < 256)
+    {
+      GimpPalette *palette;
+
+      palette = gimp_image_get_palette (image);
+      if (palette)
+        while (index_count < gimp_palette_get_color_count (palette))
+          gimp_palette_delete_entry (palette, index_count);
+    }
+  return 0;
+}
+
+static gint
+load_resource_1053 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    PSDimage           *img_a,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  /* Load image alpha channel ids (tattoos) */
+  gint16        tot_rec;
+  gint16        cidx;
+
+  IFDBG(2) g_debug ("Process image resource block: 1053: Channel ID");
+
+  tot_rec = res_a->data_len / 4;
+  if (tot_rec ==0)
+    return 0;
+
+  img_a->alpha_id = g_malloc (sizeof (img_a->alpha_id) * tot_rec);
+  img_a->alpha_id_count = tot_rec;
+  for (cidx = 0; cidx < tot_rec; ++cidx)
+    {
+      if (psd_read (input, &img_a->alpha_id[cidx], 4, error) < 4)
+        {
+          psd_set_error (error);
+          return -1;
+        }
+      img_a->alpha_id[cidx] = GUINT32_FROM_BE (img_a->alpha_id[cidx]);
+
+      IFDBG(3) g_debug ("Channel id: %d", img_a->alpha_id[cidx]);
+    }
+
+  return 0;
+}
+
+static gint
+load_resource_1058 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  gchar        *name;
+
+  GimpParasite *parasite;
+  gchar        *res_data;
+
+  IFDBG(2) g_debug ("Process image resource block: 1058: Exif data");
+
+  res_data = g_malloc (res_a->data_len);
+  if (psd_read (input, res_data, res_a->data_len, error) < res_a->data_len)
+    {
+      psd_set_error (error);
+      g_free (res_data);
+      return -1;
+    }
+
+  /* Store resource data as a standard psd parasite */
+  IFDBG (2) g_debug ("Processing exif data as psd parasite");
+  name = g_strdup_printf ("psd-image-resource-%.4s-%.4x",
+                           res_a->type, res_a->id);
+  IFDBG(3) g_debug ("Parasite name: %s", name);
+
+  parasite = gimp_parasite_new (name, 0, res_a->data_len, res_data);
+  gimp_image_attach_parasite (image, parasite);
+  gimp_parasite_free (parasite);
+  g_free (name);
+
+  g_free (res_data);
+  return 0;
+}
+
+static gint
+load_resource_1069 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    PSDimage           *img_a,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  guint16 layer_count = 0;
+  gint    i;
+
+  IFDBG(2) g_debug ("Process image resource block: 1069: Layer Selection ID(s)");
+
+  if (psd_read (input, &layer_count, 2, error) < 2)
+    {
+      psd_set_error (error);
+      return -1;
+    }
+  layer_count = GUINT16_FROM_BE (layer_count);
+
+  /* This should probably not happen, but just in case the block is
+   * duplicated, let's just free the previous selection.
+   */
+  g_list_free (img_a->layer_selection);
+  img_a->layer_selection = NULL;
+
+  for (i = 0; i < layer_count; i++)
+    {
+      guint32 layer_id = 0;
+
+      if (psd_read (input, &layer_id, 4, error) < 4)
+        {
+          psd_set_error (error);
+          return -1;
+        }
+      layer_id = GUINT32_FROM_BE (layer_id);
+      img_a->layer_selection = g_list_prepend (img_a->layer_selection, GINT_TO_POINTER (layer_id));
+    }
+
+  return 0;
+}
+
+static gint
+load_resource_1077 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    PSDimage           *img_a,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  /* Load alpha channel display info */
+
+  DisplayInfoNew    dsp_info;
+  CMColor           ps_color;
+  gint16            tot_rec;
+  gint              cidx;
+
+  IFDBG(2) g_debug ("Process image resource block 1077: Display Info New");
+
+  /* For now, skip first 4 bytes since intention is unclear. Seems to be
+     a version number that is always one, but who knows. */
+  if (! psd_seek (input, 4, G_SEEK_CUR, error))
+    return -1;
+
+  tot_rec = res_a->data_len / 13;
+  if (tot_rec == 0)
+    return 0;
+
+  img_a->alpha_display_info = g_new (PSDchanneldata *, tot_rec);
+  img_a->alpha_display_count = tot_rec;
+  for (cidx = 0; cidx < tot_rec; ++cidx)
+    {
+      GeglColor *color;
+
+      if (psd_read (input, &dsp_info.colorSpace, 2, error) < 2 ||
+          psd_read (input, &dsp_info.color,      8, error) < 8 ||
+          psd_read (input, &dsp_info.opacity,    2, error) < 2 ||
+          psd_read (input, &dsp_info.mode,       1, error) < 1)
+        {
+          psd_set_error (error);
+          return -1;
+        }
+      dsp_info.colorSpace = GINT16_FROM_BE (dsp_info.colorSpace);
+      ps_color.cmyk.cyan = GUINT16_FROM_BE (dsp_info.color[0]);
+      ps_color.cmyk.magenta = GUINT16_FROM_BE (dsp_info.color[1]);
+      ps_color.cmyk.yellow = GUINT16_FROM_BE (dsp_info.color[2]);
+      ps_color.cmyk.black = GUINT16_FROM_BE (dsp_info.color[3]);
+      dsp_info.opacity = GINT16_FROM_BE (dsp_info.opacity);
+
+      color = gegl_color_new ("red");
+
+      switch (dsp_info.colorSpace)
+        {
+          case PSD_CS_RGB:
+            /* TODO: which space? */
+            gegl_color_set_rgba_with_space (color,
+                                            ps_color.rgb.red / 65535.0,
+                                            ps_color.rgb.green / 65535.0,
+                                            ps_color.rgb.blue / 65535.0,
+                                            1.0, NULL);
+            break;
+
+          case PSD_CS_HSB:
+            {
+              gfloat hsv[3] =
+                {
+                  ps_color.hsv.hue / 65535.0f,
+                  ps_color.hsv.saturation / 65535.0f,
+                  ps_color.hsv.value / 65535.0f
+                };
+
+              gegl_color_set_pixel (color, babl_format ("HSV float"), hsv);
+            }
+            break;
+
+          case PSD_CS_CMYK:
+            /* TODO: which space? */
+            gegl_color_set_cmyk (color,
+                                 1.0 - ps_color.cmyk.cyan / 65535.0,
+                                 1.0 - ps_color.cmyk.magenta / 65535.0,
+                                 1.0 - ps_color.cmyk.yellow / 65535.0,
+                                 1.0 - ps_color.cmyk.black / 65535.0,
+                                 1.0, NULL);
+            break;
+
+          case PSD_CS_GRAYSCALE:
+            {
+              gdouble gray = ps_color.gray.gray / 10000.0;
+
+              /* TODO: which space? */
+              gegl_color_set_pixel (color, babl_format_with_space ("Y' double", NULL), &gray);
+            }
+            break;
+
+          case PSD_CS_FOCOLTONE:
+          case PSD_CS_TRUMATCH:
+          case PSD_CS_HKS:
+          case PSD_CS_LAB:
+          case PSD_CS_PANTONE:
+          case PSD_CS_TOYO:
+          case PSD_CS_DIC:
+          case PSD_CS_ANPA:
+          default:
+            if (CONVERSION_WARNINGS)
+              g_message ("Unsupported color space: %d",
+                          dsp_info.colorSpace);
+        }
+
+      IFDBG(2) g_debug ("PS cSpace: %d, col: %d %d %d %d, opacity: %d, mode: %d",
+                        dsp_info.colorSpace, ps_color.cmyk.cyan, ps_color.cmyk.magenta,
+                        ps_color.cmyk.yellow, ps_color.cmyk.black, dsp_info.opacity,
+                        dsp_info.mode);
+
+      IFDBG(2) g_debug ("cSpace: %d, opacity: %d, mode: %d",
+                        dsp_info.colorSpace, dsp_info.opacity, dsp_info.mode);
+
+      img_a->alpha_display_info[cidx] = g_malloc0 (sizeof (PSDchanneldata));
+      img_a->alpha_display_info[cidx]->gimp_color = color;
+      img_a->alpha_display_info[cidx]->opacity = dsp_info.opacity;
+      img_a->alpha_display_info[cidx]->ps_mode = dsp_info.mode;
+      img_a->alpha_display_info[cidx]->ps_cspace = dsp_info.colorSpace;
+      img_a->alpha_display_info[cidx]->ps_color = ps_color;
+    }
+
+  return 0;
+}
+
+static gint
+load_resource_2000 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  gdouble   *controlpoints;
+  gint32     x[3];
+  gint32     y[3];
+  GimpPath  *path = NULL;
+  gint16     type = 0;
+  gint16     init_fill;
+  gint16     num_rec;
+  gint16     path_rec;
+  gint16     cntr;
+  gint       image_width;
+  gint       image_height;
+  gint       i;
+  gboolean   closed;
+
+  /* Load path data from image resources 2000-2998 */
+
+  IFDBG(2) g_debug ("Process image resource block: %d :Path data", res_a->id);
+  path_rec = res_a->data_len / 26;
+  if (path_rec ==0)
+    return 0;
+
+  if (psd_read (input, &type, 2, error) < 2)
+    {
+      psd_set_error (error);
+      return -1;
+    }
+  type = GINT16_FROM_BE (type);
+  if (type != PSD_PATH_FILL_RULE)
+    {
+      IFDBG(1) g_debug ("Unexpected path record type: %d", type);
+      return -1;
+    }
+
+  if (! psd_seek (input, 24, G_SEEK_CUR, error))
+    {
+      psd_set_error (error);
+      return -1;
+    }
+
+  path_rec--;
+  if (path_rec ==0)
+    return 0;
+
+  image_width = gimp_image_get_width (image);
+  image_height = gimp_image_get_height (image);
+
+  /* Create path */
+  if (res_a->id == PSD_WORKING_PATH)
+    {
+      /* use "Working Path" for the path name to match the Photoshop display */
+      path = gimp_path_new (image, "Working Path");
+    }
+  else
+    {
+      /* Use the name stored in the PSD to name the path */
+      path = gimp_path_new (image, res_a->name);
+    }
+
+  gimp_image_insert_path (image, path, NULL, -1);
+
+  while (path_rec > 0)
+    {
+      if (psd_read (input, &type, 2, error) < 2)
+        {
+          psd_set_error (error);
+          return -1;
+        }
+      type = GINT16_FROM_BE (type);
+      IFDBG(3) g_debug ("Path record type %d", type);
+
+      if (type == PSD_PATH_FILL_RULE)
+        {
+          if (! psd_seek (input, 24, G_SEEK_CUR, error))
+            {
+              psd_set_error (error);
+              return -1;
+            }
+        }
+
+      else if (type == PSD_PATH_FILL_INIT)
+        {
+          if (psd_read (input, &init_fill, 2, error) < 2)
+            {
+              psd_set_error (error);
+              return -1;
+            }
+
+          if (! psd_seek (input, 22, G_SEEK_CUR, error))
+            {
+              psd_set_error (error);
+              return -1;
+            }
+        }
+
+      else if (type == PSD_PATH_CL_LEN
+               || type == PSD_PATH_OP_LEN)
+        {
+          if (psd_read (input, &num_rec, 2, error) < 2)
+            {
+              psd_set_error (error);
+              return -1;
+            }
+          num_rec = GINT16_FROM_BE (num_rec);
+          IFDBG(3) g_debug ("Num path records %d", num_rec);
+          if (num_rec > path_rec)
+            {
+              psd_set_error (error);
+              return - 1;
+            }
+
+          if (type == PSD_PATH_CL_LEN)
+            closed = TRUE;
+          else
+            closed = FALSE;
+          cntr = 0;
+          controlpoints = g_malloc (sizeof (gdouble) * num_rec * 6);
+          if (! psd_seek (input, 22, G_SEEK_CUR, error))
+            {
+              psd_set_error (error);
+              g_free (controlpoints);
+              return -1;
+            }
+
+          while (num_rec > 0)
+            {
+              if (psd_read (input, &type, 2, error) < 2)
+                {
+                  psd_set_error (error);
+                  return -1;
+                }
+              type = GINT16_FROM_BE (type);
+              IFDBG(3) g_debug ("Path record type %d", type);
+
+              if (type == PSD_PATH_CL_LNK
+                  || type == PSD_PATH_CL_UNLNK
+                  || type == PSD_PATH_OP_LNK
+                  || type == PSD_PATH_OP_UNLNK)
+                {
+                  if (psd_read (input, &y[0], 4, error) < 4 ||
+                      psd_read (input, &x[0], 4, error) < 4 ||
+                      psd_read (input, &y[1], 4, error) < 4 ||
+                      psd_read (input, &x[1], 4, error) < 4 ||
+                      psd_read (input, &y[2], 4, error) < 4 ||
+                      psd_read (input, &x[2], 4, error) < 4)
+                    {
+                      psd_set_error (error);
+                      return -1;
+                    }
+                  for (i = 0; i < 3; ++i)
+                    {
+                      x[i] = GINT32_FROM_BE (x[i]);
+                      controlpoints[cntr] = x[i] / 16777216.0 * image_width;
+                      cntr++;
+                      y[i] = GINT32_FROM_BE (y[i]);
+                      controlpoints[cntr] = y[i] / 16777216.0 * image_height;
+                      cntr++;
+                    }
+                  IFDBG(3) g_debug ("Path points (%d,%d), (%d,%d), (%d,%d)",
+                                    x[0], y[0], x[1], y[1], x[2], y[2]);
+                }
+              else
+                {
+                  IFDBG(1) g_debug ("Unexpected path type record %d", type);
+                  if (! psd_seek (input, 24, G_SEEK_CUR, error))
+                    {
+                      psd_set_error (error);
+                      return -1;
+                    }
+                }
+              path_rec--;
+              num_rec--;
+            }
+          /* Add sub-path */
+          gimp_path_stroke_new_from_points (path,
+                                            GIMP_PATH_STROKE_TYPE_BEZIER,
+                                            cntr, controlpoints, closed);
+          g_free (controlpoints);
+        }
+
+      else
+        {
+          if (! psd_seek (input, 24, G_SEEK_CUR, error))
+            {
+              psd_set_error (error);
+              return -1;
+            }
+        }
+
+      path_rec--;
+    }
+
+  return 0;
+}
+
+static gint
+load_resource_2999 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    GInputStream       *input,
+                    GError            **error)
+{
+  gchar        *path_name;
+  gint16        path_flatness_int   = 0;
+  gint16        path_flatness_fixed = 0;
+  gfloat        path_flatness;
+  GimpParasite *parasite;
+  gint32        read_len;
+  gint32        write_len;
+
+  path_name = fread_pascal_string (&read_len, &write_len, 2, input, error);
+  if (*error || ! path_name)
+    {
+      g_printerr ("psd-load: Unable to read clipping path name.");
+      return -1;
+    }
+
+  /* Convert from fixed to floating point */
+  if (psd_read (input, &path_flatness_int, 2, error) < 2 ||
+      psd_read (input, &path_flatness_fixed, 2, error) < 2)
+    {
+      psd_set_error (error);
+      return -1;
+    }
+  path_flatness_fixed = GINT16_FROM_BE (path_flatness_fixed);
+  path_flatness_int   = GINT16_FROM_BE (path_flatness_int);
+
+  /* Converting from Adobe fixed point value to float */
+  path_flatness = (path_flatness_fixed - 0.5f) / 65536.0;
+  path_flatness += path_flatness_int;
+
+  /* Adobe path flatness range is 0.2 to 100.0 */
+  path_flatness = CLAMP (path_flatness, 0.2f, 100.0f);
+
+  /* Save to image parasite */
+  parasite = gimp_parasite_new (PSD_PARASITE_CLIPPING_PATH, 0,
+                                read_len, path_name);
+  gimp_image_attach_parasite (image, parasite);
+  gimp_parasite_free (parasite);
+
+  parasite = gimp_parasite_new (PSD_PARASITE_PATH_FLATNESS, 0,
+                                sizeof (gfloat), &path_flatness);
+  gimp_image_attach_parasite (image, parasite);
+  gimp_parasite_free (parasite);
+
+  /* Adobe says they ignore the last two bytes, the fill rule */
+  if (psd_read (input, &path_flatness_fixed, 2, error) < 2)
+    {
+      psd_set_error (error);
+      return -1;
+    }
+
+  g_free (path_name);
+
+  return 0;
+}
diff -Nuar gimp-3.0.6.orig/plug-ins/file-psd/psd-load.c gimp-3.0.6/plug-ins/file-psd/psd-load.c
--- gimp-3.0.6.orig/plug-ins/file-psd/psd-load.c	2025-10-05 13:14:02.000000000 -0400
+++ gimp-3.0.6/plug-ins/file-psd/psd-load.c	2026-02-19 13:50:59.614131781 -0500
@@ -796,7 +796,19 @@
   if (img_a->num_layers < 0)
     {
       img_a->transparency = TRUE;
-      img_a->num_layers = -img_a->num_layers;
+      if (img_a->num_layers == G_MININT16)
+        {
+          /* FIXME After string freeze should be set translatable */
+          g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+                       "Invalid value for number of layers: %d.",
+                       img_a->num_layers);
+          img_a->num_layers = -1;
+          return NULL;
+        }
+      else
+        {
+          img_a->num_layers = abs (img_a->num_layers);
+        }
     }
 
   if (! img_a->merged_image_only && img_a->num_layers)
diff -Nuar gimp-3.0.6.orig/plug-ins/file-psd/psd-util.c gimp-3.0.6/plug-ins/file-psd/psd-util.c
--- gimp-3.0.6.orig/plug-ins/file-psd/psd-util.c	2025-10-05 13:14:02.000000000 -0400
+++ gimp-3.0.6/plug-ins/file-psd/psd-util.c	2026-02-19 13:50:11.791807551 -0500
@@ -274,7 +274,8 @@
       return NULL;
     }
 
-  str = g_malloc (len);
+  str = g_malloc (len + 1);
+  str[len] = '\0';
   if (psd_read (input, str, len, error) < len)
     {
       psd_set_error (error);
@@ -435,7 +436,13 @@
       return NULL;
     }
 
-  utf16_str = g_malloc (len * 2);
+  utf16_str = g_try_malloc (len * 2);
+  if (! utf16_str)
+    {
+      psd_set_error (error);
+      return NULL;
+    }
+
   for (i = 0; i < len; ++i)
     {
       if (psd_read (input, &utf16_str[i], 2, error) < 2)
