xcf_load_layer_props (XcfInfo    *info,
                      GimpImage  *image,
                      GimpLayer **layer,
                      GList     **item_path,
                      gboolean   *apply_mask,
                      gboolean   *edit_mask,
                      gboolean   *show_mask,
                      guint32    *text_layer_flags,
                      guint32    *group_layer_flags)
{
  PropType prop_type;
  guint32  prop_size;

  while (TRUE)
    {
      if (! xcf_load_prop (info, &prop_type, &prop_size))
        return FALSE;

      switch (prop_type)
        {
        case PROP_END:
          return TRUE;

        case PROP_ACTIVE_LAYER:
          info->selected_layers = g_list_prepend (info->selected_layers, *layer);
          break;

        case PROP_FLOATING_SELECTION:
          info->floating_sel = *layer;
          xcf_read_offset (info, &info->floating_sel_offset, 1);
          break;

        case PROP_OPACITY:
          {
            guint32 opacity;

            xcf_read_int32 (info, &opacity, 1);

            gimp_layer_set_opacity (*layer, (gdouble) opacity / 255.0, FALSE);
          }
          break;

        case PROP_FLOAT_OPACITY:
          {
            gfloat opacity;

            xcf_read_float (info, &opacity, 1);

            gimp_layer_set_opacity (*layer, opacity, FALSE);
          }
          break;

        case PROP_VISIBLE:
          {
            gboolean visible;

            xcf_read_int32 (info, (guint32 *) &visible, 1);

            gimp_item_set_visible (GIMP_ITEM (*layer), visible, FALSE);
          }
          break;

        case PROP_LINKED:
          {
            gboolean linked;

            xcf_read_int32 (info, (guint32 *) &linked, 1);

            if (linked)
              info->linked_layers = g_list_prepend (info->linked_layers, *layer);
          }
          break;

        case PROP_COLOR_TAG:
          {
            GimpColorTag color_tag;

            xcf_read_int32 (info, (guint32 *) &color_tag, 1);

            gimp_item_set_color_tag (GIMP_ITEM (*layer), color_tag, FALSE);
          }
          break;

        case PROP_LOCK_CONTENT:
          {
            gboolean lock_content;

            xcf_read_int32 (info, (guint32 *) &lock_content, 1);

            if (gimp_item_can_lock_content (GIMP_ITEM (*layer)))
              gimp_item_set_lock_content (GIMP_ITEM (*layer),
                                          lock_content, FALSE);
          }
          break;

        case PROP_LOCK_ALPHA:
          {
            gboolean lock_alpha;

            xcf_read_int32 (info, (guint32 *) &lock_alpha, 1);

            if (gimp_layer_can_lock_alpha (*layer))
              gimp_layer_set_lock_alpha (*layer, lock_alpha, FALSE);
          }
          break;

        case PROP_LOCK_POSITION:
          {
            gboolean lock_position;

            xcf_read_int32 (info, (guint32 *) &lock_position, 1);

            if (gimp_item_can_lock_position (GIMP_ITEM (*layer)))
              gimp_item_set_lock_position (GIMP_ITEM (*layer),
                                           lock_position, FALSE);
          }
          break;

        case PROP_LOCK_VISIBILITY:
          {
            gboolean lock_visibility;

            xcf_read_int32 (info, (guint32 *) &lock_visibility, 1);

            if (gimp_item_can_lock_visibility (GIMP_ITEM (*layer)))
              gimp_item_set_lock_visibility (GIMP_ITEM (*layer),
                                             lock_visibility, FALSE);
          }
          break;

        case PROP_APPLY_MASK:
          xcf_read_int32 (info, (guint32 *) apply_mask, 1);
          break;

        case PROP_EDIT_MASK:
          xcf_read_int32 (info, (guint32 *) edit_mask, 1);
          break;

        case PROP_SHOW_MASK:
          xcf_read_int32 (info, (guint32 *) show_mask, 1);
          break;

        case PROP_OFFSETS:
          {
            gint32 offset_x;
            gint32 offset_y;

            xcf_read_int32 (info, (guint32 *) &offset_x, 1);
            xcf_read_int32 (info, (guint32 *) &offset_y, 1);

            if (offset_x < -GIMP_MAX_IMAGE_SIZE ||
                offset_x > GIMP_MAX_IMAGE_SIZE)
              {
                g_printerr ("unexpected item offset_x (%d) in XCF, "
                            "setting to 0\n", offset_x);
                offset_x = 0;
              }

            if (offset_y < -GIMP_MAX_IMAGE_SIZE ||
                offset_y > GIMP_MAX_IMAGE_SIZE)
              {
                g_printerr ("unexpected item offset_y (%d) in XCF, "
                            "setting to 0\n", offset_y);
                offset_y = 0;
              }

            gimp_item_set_offset (GIMP_ITEM (*layer), offset_x, offset_y);
          }
          break;

        case PROP_MODE:
          {
            GimpLayerMode mode;

            xcf_read_int32 (info, (guint32 *) &mode, 1);

            if (mode == GIMP_LAYER_MODE_OVERLAY_LEGACY)
              mode = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;

            gimp_layer_set_mode (*layer, mode, FALSE);
          }
          break;

        case PROP_BLEND_SPACE:
          {
            gint32 blend_space;

            xcf_read_int32 (info, (guint32 *) &blend_space, 1);

            /* if blend_space < 0 it was originally AUTO, and its negative is
             * the actual value AUTO used to map to at the time the file was
             * saved.  if AUTO still maps to the same value, keep using AUTO
             * for the property; otherwise, use the concrete value.
             */
            if (blend_space < 0)
              {
                GimpLayerMode mode = gimp_layer_get_mode (*layer);

                blend_space = -blend_space;

                if (blend_space == gimp_layer_mode_get_blend_space (mode))
                  blend_space = GIMP_LAYER_COLOR_SPACE_AUTO;
                else
                  GIMP_LOG (XCF, "BLEND_SPACE: AUTO => %d", blend_space);
              }

            gimp_layer_set_blend_space (*layer, blend_space, FALSE);
          }
          break;

        case PROP_COMPOSITE_SPACE:
          {
            gint32 composite_space;

            xcf_read_int32 (info, (guint32 *) &composite_space, 1);

            /* if composite_space < 0 it was originally AUTO, and its negative
             * is the actual value AUTO used to map to at the time the file was
             * saved.  if AUTO still maps to the same value, keep using AUTO
             * for the property; otherwise, use the concrete value.
             */
            if (composite_space < 0)
              {
                GimpLayerMode mode = gimp_layer_get_mode (*layer);

                composite_space = -composite_space;

                if (composite_space == gimp_layer_mode_get_composite_space (mode))
                  composite_space = GIMP_LAYER_COLOR_SPACE_AUTO;
                else
                  GIMP_LOG (XCF, "COMPOSITE_SPACE: AUTO => %d", composite_space);
              }

            gimp_layer_set_composite_space (*layer, composite_space, FALSE);
          }
          break;

        case PROP_COMPOSITE_MODE:
          {
            gint32 composite_mode;

            xcf_read_int32 (info, (guint32 *) &composite_mode, 1);

            /* if composite_mode < 0 it was originally AUTO, and its negative
             * is the actual value AUTO used to map to at the time the file was
             * saved.  if AUTO still maps to the same value, keep using AUTO
             * for the property; otherwise, use the concrete value.
             */
            if (composite_mode < 0)
              {
                GimpLayerMode mode = gimp_layer_get_mode (*layer);

                composite_mode = -composite_mode;

                if (composite_mode == gimp_layer_mode_get_composite_mode (mode))
                  composite_mode = GIMP_LAYER_COMPOSITE_AUTO;
                else
                  GIMP_LOG (XCF, "COMPOSITE_MODE: AUTO => %d", composite_mode);
              }

            gimp_layer_set_composite_mode (*layer, composite_mode, FALSE);
          }
          break;

        case PROP_TATTOO:
          {
            GimpTattoo tattoo;

            xcf_read_int32 (info, (guint32 *) &tattoo, 1);

            gimp_item_set_tattoo (GIMP_ITEM (*layer), tattoo);
          }
          break;

        case PROP_PARASITES:
          {
            goffset base = info->cp;

            while (info->cp - base < prop_size)
              {
                GimpParasite *p     = xcf_load_parasite (info);
                GError       *error = NULL;

                if (! p)
                  return FALSE;

                if (! gimp_item_parasite_validate (GIMP_ITEM (*layer), p, &error))
                  {
                    gimp_message (info->gimp, G_OBJECT (info->progress),
                                  GIMP_MESSAGE_WARNING,
                                  "Warning, invalid layer parasite in XCF file: %s",
                                  error->message);
                    g_clear_error (&error);
                  }
                else
                  {
                    gimp_item_parasite_attach (GIMP_ITEM (*layer), p, FALSE);
                  }

                gimp_parasite_free (p);
              }

            if (info->cp - base != prop_size)
              gimp_message_literal (info->gimp, G_OBJECT (info->progress),
                                    GIMP_MESSAGE_WARNING,
                                    "Error while loading a layer's parasites");
          }
          break;

        case PROP_TEXT_LAYER_FLAGS:
          xcf_read_int32 (info, text_layer_flags, 1);
          break;

        case PROP_GROUP_ITEM:
          {
            GimpLayer *group;
            gboolean   is_selected_layer;

            /* We're going to delete *layer, Don't leave its pointers
             * in @info.  After that, we'll restore them back with the
             * new pointer. See bug #767873.
             */
            is_selected_layer = (g_list_find (info->selected_layers, *layer ) != NULL);
            if (is_selected_layer)
              info->selected_layers = g_list_remove (info->selected_layers, *layer);

            if (*layer == info->floating_sel)
              info->floating_sel = NULL;

            group = gimp_group_layer_new (image);

            gimp_object_set_name (GIMP_OBJECT (group),
                                  gimp_object_get_name (*layer));

            g_object_ref_sink (*layer);
            g_object_unref (*layer);
            *layer = group;

            if (is_selected_layer)
              info->selected_layers = g_list_prepend (info->selected_layers, *layer);

            /* Don't restore info->floating_sel because group layers
             * can't be floating selections
             */
          }
          break;

        case PROP_ITEM_PATH:
          {
            goffset  base = info->cp;
            GList   *path = NULL;

            while (info->cp - base < prop_size)
              {
                guint32 index;

                if (xcf_read_int32 (info, &index, 1) != 4)
                  {
                    g_list_free (path);
                    return FALSE;
                  }

                path = g_list_append (path, GUINT_TO_POINTER (index));
              }

            *item_path = path;
          }
          break;

        case PROP_GROUP_ITEM_FLAGS:
          xcf_read_int32 (info, group_layer_flags, 1);
          break;

        case PROP_ITEM_SET_ITEM:
            {
              GimpItemList *set;
              guint32       n;

              xcf_read_int32 (info, &n, 1);
              set = g_list_nth_data (info->layer_sets, n);
              if (set == NULL)
                g_printerr ("xcf: layer '%s' cannot be added to unknown layer set at index %d (skipping)\n",
                            gimp_object_get_name (*layer), n);
              else if (! g_type_is_a (G_TYPE_FROM_INSTANCE (*layer),
                                      gimp_item_list_get_item_type (set)))
                g_printerr ("xcf: layer '%s' cannot be added to item set '%s' with item type %s (skipping)\n",
                            gimp_object_get_name (*layer), gimp_object_get_name (set),
                            g_type_name (gimp_item_list_get_item_type (set)));
              else if (gimp_item_list_is_pattern (set, NULL))
                g_printerr ("xcf: layer '%s' cannot be added to pattern item set '%s' (skipping)\n",
                            gimp_object_get_name (*layer), gimp_object_get_name (set));
              else
                gimp_item_list_add (set, GIMP_ITEM (*layer));
            }
          break;

        default:
#ifdef GIMP_UNSTABLE
          g_printerr ("unexpected/unknown layer property: %d (skipping)\n",
                      prop_type);
#endif
          if (! xcf_skip_unknown_prop (info, prop_size))
            return FALSE;
          break;
        }
    }

  return FALSE;
}
xcf_load_layer_props (XcfInfo    *info,
                      GimpImage  *image,
                      GimpLayer **layer,
                      GList     **item_path,
                      gboolean   *apply_mask,
                      gboolean   *edit_mask,
                      gboolean   *show_mask,
                      guint32    *text_layer_flags,
                      guint32    *group_layer_flags)
{
  PropType prop_type;
  guint32  prop_size;

  while (TRUE)
    {
      if (! xcf_load_prop (info, &prop_type, &prop_size))
        return FALSE;

      switch (prop_type)
        {
        case PROP_END:
          return TRUE;

        case PROP_ACTIVE_LAYER:
          info->selected_layers = g_list_prepend (info->selected_layers, *layer);
          break;

        case PROP_FLOATING_SELECTION:
          info->floating_sel = *layer;
          xcf_read_offset (info, &info->floating_sel_offset, 1);
          break;

        case PROP_OPACITY:
          {
            guint32 opacity;

            xcf_read_int32 (info, &opacity, 1);

            gimp_layer_set_opacity (*layer, (gdouble) opacity / 255.0, FALSE);
          }
          break;

        case PROP_FLOAT_OPACITY:
          {
            gfloat opacity;

            xcf_read_float (info, &opacity, 1);

            gimp_layer_set_opacity (*layer, opacity, FALSE);
          }
          break;

        case PROP_VISIBLE:
          {
            gboolean visible;

            xcf_read_int32 (info, (guint32 *) &visible, 1);

            gimp_item_set_visible (GIMP_ITEM (*layer), visible, FALSE);
          }
          break;

        case PROP_LINKED:
          {
            gboolean linked;

            xcf_read_int32 (info, (guint32 *) &linked, 1);

            if (linked)
              info->linked_layers = g_list_prepend (info->linked_layers, *layer);
          }
          break;

        case PROP_COLOR_TAG:
          {
            GimpColorTag color_tag;

            xcf_read_int32 (info, (guint32 *) &color_tag, 1);

            gimp_item_set_color_tag (GIMP_ITEM (*layer), color_tag, FALSE);
          }
          break;

        case PROP_LOCK_CONTENT:
          {
            gboolean lock_content;

            xcf_read_int32 (info, (guint32 *) &lock_content, 1);

            if (gimp_item_can_lock_content (GIMP_ITEM (*layer)))
              gimp_item_set_lock_content (GIMP_ITEM (*layer),
                                          lock_content, FALSE);
          }
          break;

        case PROP_LOCK_ALPHA:
          {
            gboolean lock_alpha;

            xcf_read_int32 (info, (guint32 *) &lock_alpha, 1);

            if (gimp_layer_can_lock_alpha (*layer))
              gimp_layer_set_lock_alpha (*layer, lock_alpha, FALSE);
          }
          break;

        case PROP_LOCK_POSITION:
          {
            gboolean lock_position;

            xcf_read_int32 (info, (guint32 *) &lock_position, 1);

            if (gimp_item_can_lock_position (GIMP_ITEM (*layer)))
              gimp_item_set_lock_position (GIMP_ITEM (*layer),
                                           lock_position, FALSE);
          }
          break;

        case PROP_LOCK_VISIBILITY:
          {
            gboolean lock_visibility;

            xcf_read_int32 (info, (guint32 *) &lock_visibility, 1);

            if (gimp_item_can_lock_visibility (GIMP_ITEM (*layer)))
              gimp_item_set_lock_visibility (GIMP_ITEM (*layer),
                                             lock_visibility, FALSE);
          }
          break;

        case PROP_APPLY_MASK:
          xcf_read_int32 (info, (guint32 *) apply_mask, 1);
          break;

        case PROP_EDIT_MASK:
          xcf_read_int32 (info, (guint32 *) edit_mask, 1);
          break;

        case PROP_SHOW_MASK:
          xcf_read_int32 (info, (guint32 *) show_mask, 1);
          break;

        case PROP_OFFSETS:
          {
            gint32 offset_x;
            gint32 offset_y;

            xcf_read_int32 (info, (guint32 *) &offset_x, 1);
            xcf_read_int32 (info, (guint32 *) &offset_y, 1);

            if (offset_x < -GIMP_MAX_IMAGE_SIZE ||
                offset_x > GIMP_MAX_IMAGE_SIZE)
              {
                g_printerr ("unexpected item offset_x (%d) in XCF, "
                            "setting to 0\n", offset_x);
                offset_x = 0;
              }

            if (offset_y < -GIMP_MAX_IMAGE_SIZE ||
                offset_y > GIMP_MAX_IMAGE_SIZE)
              {
                g_printerr ("unexpected item offset_y (%d) in XCF, "
                            "setting to 0\n", offset_y);
                offset_y = 0;
              }

            gimp_item_set_offset (GIMP_ITEM (*layer), offset_x, offset_y);
          }
          break;

        case PROP_MODE:
          {
            GimpLayerMode mode;

            xcf_read_int32 (info, (guint32 *) &mode, 1);

            if (mode == GIMP_LAYER_MODE_OVERLAY_LEGACY)
              mode = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;

            gimp_layer_set_mode (*layer, mode, FALSE);
          }
          break;

        case PROP_BLEND_SPACE:
          {
            gint32 blend_space;

            xcf_read_int32 (info, (guint32 *) &blend_space, 1);

             
            if (blend_space < 0)
              {
                GimpLayerMode mode = gimp_layer_get_mode (*layer);

                blend_space = -blend_space;

                if (blend_space == gimp_layer_mode_get_blend_space (mode))
                  blend_space = GIMP_LAYER_COLOR_SPACE_AUTO;
                else
                  GIMP_LOG (XCF, "BLEND_SPACE: AUTO => %d", blend_space);
              }

            gimp_layer_set_blend_space (*layer, blend_space, FALSE);
          }
          break;

        case PROP_COMPOSITE_SPACE:
          {
            gint32 composite_space;

            xcf_read_int32 (info, (guint32 *) &composite_space, 1);

             
            if (composite_space < 0)
              {
                GimpLayerMode mode = gimp_layer_get_mode (*layer);

                composite_space = -composite_space;

                if (composite_space == gimp_layer_mode_get_composite_space (mode))
                  composite_space = GIMP_LAYER_COLOR_SPACE_AUTO;
                else
                  GIMP_LOG (XCF, "COMPOSITE_SPACE: AUTO => %d", composite_space);
              }

            gimp_layer_set_composite_space (*layer, composite_space, FALSE);
          }
          break;

        case PROP_COMPOSITE_MODE:
          {
            gint32 composite_mode;

            xcf_read_int32 (info, (guint32 *) &composite_mode, 1);

             
            if (composite_mode < 0)
              {
                GimpLayerMode mode = gimp_layer_get_mode (*layer);

                composite_mode = -composite_mode;

                if (composite_mode == gimp_layer_mode_get_composite_mode (mode))
                  composite_mode = GIMP_LAYER_COMPOSITE_AUTO;
                else
                  GIMP_LOG (XCF, "COMPOSITE_MODE: AUTO => %d", composite_mode);
              }

            gimp_layer_set_composite_mode (*layer, composite_mode, FALSE);
          }
          break;

        case PROP_TATTOO:
          {
            GimpTattoo tattoo;

            xcf_read_int32 (info, (guint32 *) &tattoo, 1);

            gimp_item_set_tattoo (GIMP_ITEM (*layer), tattoo);
          }
          break;

        case PROP_PARASITES:
          {
            goffset base = info->cp;

            while (info->cp - base < prop_size)
              {
                GimpParasite *p     = xcf_load_parasite (info);
                GError       *error = NULL;

                if (! p)
                  return FALSE;

                if (! gimp_item_parasite_validate (GIMP_ITEM (*layer), p, &error))
                  {
                    gimp_message (info->gimp, G_OBJECT (info->progress),
                                  GIMP_MESSAGE_WARNING,
                                  "Warning, invalid layer parasite in XCF file: %s",
                                  error->message);
                    g_clear_error (&error);
                  }
                else
                  {
                    gimp_item_parasite_attach (GIMP_ITEM (*layer), p, FALSE);
                  }

                gimp_parasite_free (p);
              }

            if (info->cp - base != prop_size)
              gimp_message_literal (info->gimp, G_OBJECT (info->progress),
                                    GIMP_MESSAGE_WARNING,
                                    "Error while loading a layer's parasites");
          }
          break;

        case PROP_TEXT_LAYER_FLAGS:
          xcf_read_int32 (info, text_layer_flags, 1);
          break;

        case PROP_GROUP_ITEM:
          {
            GimpLayer *group;
            gboolean   is_selected_layer;

             
            is_selected_layer = (g_list_find (info->selected_layers, *layer ) != NULL);
            if (is_selected_layer)
              info->selected_layers = g_list_remove (info->selected_layers, *layer);

            if (*layer == info->floating_sel)
              info->floating_sel = NULL;

            group = gimp_group_layer_new (image);

            gimp_object_set_name (GIMP_OBJECT (group),
                                  gimp_object_get_name (*layer));

            g_object_ref_sink (*layer);
            g_object_unref (*layer);
            *layer = group;

            if (is_selected_layer)
              info->selected_layers = g_list_prepend (info->selected_layers, *layer);

             
          }
          break;

        case PROP_ITEM_PATH:
          {
            goffset  base = info->cp;
            GList   *path = NULL;

            while (info->cp - base < prop_size)
              {
                guint32 index;

                if (xcf_read_int32 (info, &index, 1) != 4)
                  {
                    g_list_free (path);
                    return FALSE;
                  }

                path = g_list_append (path, GUINT_TO_POINTER (index));
              }

            *item_path = path;
          }
          break;

        case PROP_GROUP_ITEM_FLAGS:
          xcf_read_int32 (info, group_layer_flags, 1);
          break;

        case PROP_ITEM_SET_ITEM:
            {
              GimpItemList *set;
              guint32       n;

              xcf_read_int32 (info, &n, 1);
              set = g_list_nth_data (info->layer_sets, n);
              if (set == NULL)
                g_printerr ("xcf: layer '%s' cannot be added to unknown layer set at index %d (skipping)\n",
                            gimp_object_get_name (*layer), n);
              else if (! g_type_is_a (G_TYPE_FROM_INSTANCE (*layer),
                                      gimp_item_list_get_item_type (set)))
                g_printerr ("xcf: layer '%s' cannot be added to item set '%s' with item type %s (skipping)\n",
                            gimp_object_get_name (*layer), gimp_object_get_name (set),
                            g_type_name (gimp_item_list_get_item_type (set)));
              else if (gimp_item_list_is_pattern (set, NULL))
                g_printerr ("xcf: layer '%s' cannot be added to pattern item set '%s' (skipping)\n",
                            gimp_object_get_name (*layer), gimp_object_get_name (set));
              else
                gimp_item_list_add (set, GIMP_ITEM (*layer));
            }
          break;

        default:
#ifdef GIMP_UNSTABLE
          g_printerr ("unexpected/unknown layer property: %d (skipping)\n",
                      prop_type);
#endif
          if (! xcf_skip_unknown_prop (info, prop_size))
            return FALSE;
          break;
        }
    }

  return FALSE;
}