static gint read_image_resource_block (PSDimage *img_a, FILE *f, GError **error) { guint32 block_len; guint32 block_end; if (fread (&block_len, 4, 1, f) < 1) { psd_set_error (feof (f), errno, error); return -1; } img_a->image_res_len = GUINT32_FROM_BE (block_len); IFDBG(1) g_debug ("Image resource block size = %d", (int)img_a->image_res_len); img_a->image_res_start = ftell (f); block_end = img_a->image_res_start + img_a->image_res_len; if (fseek (f, block_end, SEEK_SET) < 0) { psd_set_error (feof (f), errno, error); return -1; } return 0; }
ioctl_tree * ioctl_tree_execute(ioctl_tree * tree, ioctl_tree * last, unsigned long id, void *arg, int *ret) { const ioctl_type *t; ioctl_tree *i; int r, handled; DBG("ioctl_tree_execute ioctl %lX\n", id); /* check if it's a hardware independent stateless ioctl */ t = ioctl_type_get_by_id(id); if (t != NULL && t->insertion_parent == NULL) { DBG(" ioctl_tree_execute: stateless\n"); if (t->execute(NULL, id, arg, &r)) *ret = r; else *ret = -1; return last; } if (tree == NULL) return NULL; i = ioctl_tree_next_wrap(tree, last); /* start at the previously executed node to maintain original order of * ioctls as much as possible (i. e. maintain it while the requests come in * at the same order as originally recorded) */ for (;;) { DBG(" ioctl_tree_execute: checking node %s(%lX, base id %lX) ", i->type->name, i->id, i->type->id); IFDBG(i->type->write(i, stdout)); DBG("\n"); handled = i->type->execute(i, id, arg, &r); if (handled) { DBG(" -> match, ret %i, adv: %i\n", r, handled); *ret = r; if (handled == 1) return i; else return last; } if (last != NULL && i == last) { /* we did a full circle */ DBG(" -> full iteration, not found\n"); break; } i = ioctl_tree_next_wrap(tree, i); if (last == NULL && i == tree) { /* we did a full circle */ DBG(" -> full iteration with last == NULL, not found\n"); break; } } /* not found */ return NULL; }
static gint read_header_block (PSDimage *img_a, FILE *f, GError **error) { guint16 version; gchar sig[4]; gchar buf[6]; if (fread (sig, 4, 1, f) < 1 || fread (&version, 2, 1, f) < 1 || fread (buf, 6, 1, f) < 1 || fread (&img_a->channels, 2, 1, f) < 1 || fread (&img_a->rows, 4, 1, f) < 1 || fread (&img_a->columns, 4, 1, f) < 1 || fread (&img_a->bps, 2, 1, f) < 1 || fread (&img_a->color_mode, 2, 1, f) < 1) { psd_set_error (feof (f), errno, error); return -1; } version = GUINT16_FROM_BE (version); img_a->channels = GUINT16_FROM_BE (img_a->channels); img_a->rows = GUINT32_FROM_BE (img_a->rows); img_a->columns = GUINT32_FROM_BE (img_a->columns); img_a->bps = GUINT16_FROM_BE (img_a->bps); img_a->color_mode = GUINT16_FROM_BE (img_a->color_mode); IFDBG(1) g_debug ("\n\n\tSig: %.4s\n\tVer: %d\n\tChannels: " "%d\n\tSize: %dx%d\n\tBPS: %d\n\tMode: %d\n", sig, version, img_a->channels, img_a->columns, img_a->rows, img_a->bps, img_a->color_mode); if (memcmp (sig, "8BPS", 4) != 0) return -1; if (version != 1) return -1; if (img_a->channels > MAX_CHANNELS) return -1; if (img_a->rows < 1 || img_a->rows > GIMP_MAX_IMAGE_SIZE) return -1; if (img_a->columns < 1 || img_a->columns > GIMP_MAX_IMAGE_SIZE) return -1; return 0; }
static gint32 create_gimp_image (PSDimage *img_a, const gchar *filename) { gint32 image_id = -1; img_a->base_type = GIMP_RGB; /* Create gimp image */ IFDBG(2) g_debug ("Create image"); image_id = gimp_image_new (img_a->columns, img_a->rows, img_a->base_type); gimp_image_set_filename (image_id, filename); gimp_image_undo_disable (image_id); return image_id; }
static void run (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[4]; GimpRunMode run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; gint32 image_ID; GError *error = NULL; run_mode = param[0].data.d_int32; INIT_I18N (); gegl_init (NULL, NULL); *nreturn_vals = 1; *return_vals = values; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR; if (strcmp (name, LOAD_PROC) == 0) { gboolean resolution_loaded = FALSE; gboolean interactive; switch (run_mode) { case GIMP_RUN_INTERACTIVE: case GIMP_RUN_WITH_LAST_VALS: gimp_ui_init (PLUG_IN_BINARY, FALSE); interactive = TRUE; break; default: interactive = FALSE; break; } image_ID = load_image (param[1].data.d_string, &resolution_loaded, &error); if (image_ID != -1) { GFile *file = g_file_new_for_path (param[1].data.d_string); GimpMetadata *metadata; metadata = gimp_image_metadata_load_prepare (image_ID, "image/x-psd", file, NULL); if (metadata) { GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL; if (resolution_loaded) flags &= ~GIMP_METADATA_LOAD_RESOLUTION; gimp_image_metadata_load_finish (image_ID, "image/x-psd", metadata, flags, interactive); g_object_unref (metadata); } g_object_unref (file); *nreturn_vals = 2; values[1].type = GIMP_PDB_IMAGE; values[1].data.d_image = image_ID; } else { status = GIMP_PDB_EXECUTION_ERROR; } } else if (strcmp (name, LOAD_THUMB_PROC) == 0) { if (nparams < 2) { status = GIMP_PDB_CALLING_ERROR; } else { const gchar *filename = param[0].data.d_string; gint width = 0; gint height = 0; image_ID = load_thumbnail_image (filename, &width, &height, &error); if (image_ID != -1) { *nreturn_vals = 4; values[1].type = GIMP_PDB_IMAGE; values[1].data.d_image = image_ID; values[2].type = GIMP_PDB_INT32; values[2].data.d_int32 = width; values[3].type = GIMP_PDB_INT32; values[3].data.d_int32 = height; } else { status = GIMP_PDB_EXECUTION_ERROR; } } } else if (strcmp (name, SAVE_PROC) == 0) { gint32 drawable_id; GimpMetadata *metadata; GimpMetadataSaveFlags metadata_flags; GimpExportReturn export = GIMP_EXPORT_IGNORE; IFDBG(2) g_debug ("\n---------------- %s ----------------\n", param[3].data.d_string); image_ID = param[1].data.d_int32; drawable_id = param[2].data.d_int32; switch (run_mode) { case GIMP_RUN_INTERACTIVE: case GIMP_RUN_WITH_LAST_VALS: gimp_ui_init (PLUG_IN_BINARY, FALSE); export = gimp_export_image (&image_ID, &drawable_id, "PSD", GIMP_EXPORT_CAN_HANDLE_RGB | GIMP_EXPORT_CAN_HANDLE_GRAY | GIMP_EXPORT_CAN_HANDLE_INDEXED | GIMP_EXPORT_CAN_HANDLE_ALPHA | GIMP_EXPORT_CAN_HANDLE_LAYERS | GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS); if (export == GIMP_EXPORT_CANCEL) { values[0].data.d_status = GIMP_PDB_CANCEL; return; } break; default: break; }
/* Main file load function */ gint32 load_thumbnail_image (const gchar *filename, gint *width, gint *height, GError **load_error) { FILE *f; struct stat st; PSDimage img_a; gint32 image_id = -1; GError *error = NULL; /* ----- Open PSD file ----- */ if (g_stat (filename, &st) == -1) return -1; IFDBG(1) g_debug ("Open file %s", gimp_filename_to_utf8 (filename)); f = g_fopen (filename, "rb"); if (f == NULL) { g_set_error (load_error, G_FILE_ERROR, g_file_error_from_errno (errno), _("Could not open '%s' for reading: %s"), gimp_filename_to_utf8 (filename), g_strerror (errno)); return -1; } gimp_progress_init_printf (_("Opening thumbnail for '%s'"), gimp_filename_to_utf8 (filename)); /* ----- Read the PSD file Header block ----- */ IFDBG(2) g_debug ("Read header block"); if (read_header_block (&img_a, f, &error) < 0) goto load_error; gimp_progress_update (0.2); /* ----- Read the PSD file Colour Mode block ----- */ IFDBG(2) g_debug ("Read colour mode block"); if (read_color_mode_block (&img_a, f, &error) < 0) goto load_error; gimp_progress_update (0.4); /* ----- Read the PSD file Image Resource block ----- */ IFDBG(2) g_debug ("Read image resource block"); if (read_image_resource_block (&img_a, f, &error) < 0) goto load_error; gimp_progress_update (0.6); /* ----- Create GIMP image ----- */ IFDBG(2) g_debug ("Create GIMP image"); image_id = create_gimp_image (&img_a, filename); if (image_id < 0) goto load_error; /* ----- Add image resources ----- */ IFDBG(2) g_debug ("Add image resources"); if (add_image_resources (image_id, &img_a, f, &error) < 1) goto load_error; gimp_progress_update (1.0); gimp_image_clean_all (image_id); gimp_image_undo_enable (image_id); fclose (f); *width = img_a.columns; *height = img_a.rows; return image_id; /* ----- Process load errors ----- */ load_error: if (error) { g_set_error (load_error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Error loading PSD file: %s"), error->message); g_error_free (error); } /* Delete partially loaded image */ if (image_id > 0) gimp_image_delete (image_id); /* Close file if Open */ if (! (f == NULL)) fclose (f); return -1; }
static int usbdevfs_reapurb_execute(const ioctl_tree * node, unsigned long id, void *arg, int *ret) { /* set in SUBMIT, cleared in REAP */ static const ioctl_tree *submit_node = NULL; static struct usbdevfs_urb *submit_urb = NULL; if (id == USBDEVFS_SUBMITURB) { const struct usbdevfs_urb *n_urb = node->data; struct usbdevfs_urb *a_urb = arg; assert(submit_node == NULL); if (n_urb->type != a_urb->type || n_urb->endpoint != a_urb->endpoint || n_urb->flags != a_urb->flags || n_urb->buffer_length != a_urb->buffer_length) return 0; DBG(" usbdevfs_reapurb_execute: handling SUBMITURB, metadata match\n"); /* for an output URB we also require the buffer contents to match; for * an input URB it can be uninitialized */ if ((n_urb->endpoint & 0x80) == 0 && memcmp(n_urb->buffer, a_urb->buffer, n_urb->buffer_length) != 0) { DBG(" usbdevfs_reapurb_execute: handling SUBMITURB, buffer mismatch, rejecting\n"); return 0; } DBG(" usbdevfs_reapurb_execute: handling SUBMITURB, buffer match, remembering\n"); /* remember the node for the next REAP */ submit_node = node; submit_urb = a_urb; *ret = 0; return 1; } if (id == node->type->id) { struct usbdevfs_urb *orig_node_urb; if (submit_node == NULL) { DBG(" usbdevfs_reapurb_execute: handling %s, but no submit node -> EAGAIN\n", node->type->name); *ret = -1; errno = EAGAIN; return 2; } orig_node_urb = submit_node->data; submit_urb->actual_length = orig_node_urb->actual_length; submit_urb->error_count = orig_node_urb->error_count; /* for an input EP we need to copy the buffer data */ if (orig_node_urb->endpoint & 0x80) { memcpy(submit_urb->buffer, orig_node_urb->buffer, orig_node_urb->actual_length); } submit_urb->status = orig_node_urb->status; *((struct usbdevfs_urb **)arg) = submit_urb; DBG(" usbdevfs_reapurb_execute: handling %s %u %u %i %u %i %i %i ", node->type->name, (unsigned)submit_urb->type, (unsigned)submit_urb->endpoint, submit_urb->status, (unsigned)submit_urb->flags, submit_urb->buffer_length, submit_urb->actual_length, submit_urb->error_count); IFDBG(write_hex(stdout, submit_urb->buffer, submit_urb->endpoint & 0x80 ? submit_urb->actual_length : submit_urb->buffer_length)); submit_urb = NULL; submit_node = NULL; *ret = 0; return 2; } return 0; }
gchar * fread_pascal_string (gint32 *bytes_read, gint32 *bytes_written, const guint16 mod_len, FILE *f, GError **error) { /* * Reads a pascal string from the file padded to a multiple of mod_len * and returns a utf-8 string. */ gchar *str; gchar *utf8_str; guchar len; gint32 padded_len; *bytes_read = 0; *bytes_written = -1; if (fread (&len, 1, 1, f) < 1) { psd_set_error (feof (f), errno, error); return NULL; } (*bytes_read)++; IFDBG(3) g_debug ("Pascal string length %d", len); if (len == 0) { if (fseek (f, mod_len - 1, SEEK_CUR) < 0) { psd_set_error (feof (f), errno, error); return NULL; } *bytes_read += (mod_len - 1); *bytes_written = 0; return NULL; } str = g_malloc (len); if (fread (str, len, 1, f) < 1) { psd_set_error (feof (f), errno, error); return NULL; } *bytes_read += len; if (mod_len > 0) { padded_len = len + 1; while (padded_len % mod_len != 0) { if (fseek (f, 1, SEEK_CUR) < 0) { psd_set_error (feof (f), errno, error); return NULL; } (*bytes_read)++; padded_len++; } } utf8_str = gimp_any_to_utf8 (str, len, NULL); *bytes_written = strlen (utf8_str); g_free (str); IFDBG(3) g_debug ("Pascal string: %s, bytes_read: %d, bytes_written: %d", utf8_str, *bytes_read, *bytes_written); return utf8_str; }
gchar * encode_packbits (const gchar *src, const guint32 unpacked_len, guint16 *packed_len) { /* * Encode a PackBits chunk. */ GString *dst_str; /* destination string */ gint curr_char; /* current character */ gchar char_buff[128]; /* buffer of already read characters */ guchar run_len; /* number of characters in a run */ gint32 unpack_left = unpacked_len; IFDBG(2) g_debug ("Encode packbits"); /* Initialise destination string */ dst_str = g_string_sized_new (unpacked_len); /* prime the read loop */ curr_char = *src; src++; run_len = 0; /* read input until there's nothing left */ while (unpack_left > 0) { char_buff[run_len] = (gchar) curr_char; IFDBG(2) g_debug ("buff %x, run len %d, curr char %x", char_buff[run_len], run_len, (gchar) curr_char); run_len++; if (run_len >= MIN_RUN) { gint i; /* check for run */ for (i = 2; i <= MIN_RUN; ++i) { if (curr_char != char_buff[run_len - i]) { /* no run */ i = 0; break; } } if (i != 0) { /* we have a run write out buffer before run*/ gint next_char; if (run_len > MIN_RUN) { /* block size - 1 followed by contents */ g_string_append_c (dst_str, (run_len - MIN_RUN - 1)); g_string_append_len (dst_str, char_buff, (run_len - MIN_RUN)); IFDBG(2) g_debug ("(1) Number of chars: %d, run length tag: %d", run_len - MIN_RUN, run_len - MIN_RUN - 1); } /* determine run length (MIN_RUN so far) */ run_len = MIN_RUN; /* get next character */ next_char = *src; src++; unpack_left--; while (next_char == curr_char) { run_len++; if (run_len == 128) { /* run is at max length */ break; } next_char = *src; src++; unpack_left--; } /* write out encoded run length and run symbol */ g_string_append_c (dst_str, (gchar)(1 - (gint)(run_len))); g_string_append_c (dst_str, curr_char); IFDBG(2) g_debug ("(2) Number of chars: %d, run length tag: %d", run_len, (1 - (gint)(run_len))); if (unpack_left > 0) { /* make run breaker start of next buffer */ char_buff[0] = next_char; run_len = 1; } else { /* file ends in a run */ run_len = 0; } } } if (run_len == 128) { /* write out buffer */ g_string_append_c (dst_str, 127); g_string_append_len (dst_str, char_buff, 128); IFDBG(2) g_debug ("(3) Number of chars: 128, run length tag: 127"); /* start a new buffer */ run_len = 0; } curr_char = *src; src++; unpack_left--; } /* write out last buffer */ if (run_len != 0) { if (run_len <= 128) { /* write out entire copy buffer */ g_string_append_c (dst_str, run_len - 1); g_string_append_len (dst_str, char_buff, run_len); IFDBG(2) g_debug ("(4) Number of chars: %d, run length tag: %d", run_len, run_len - 1); } else { IFDBG(2) g_debug ("(5) Very bad - should not be here"); } } *packed_len = dst_str->len; IFDBG(2) g_debug ("Packed len %d, unpacked %d", *packed_len, unpacked_len); return g_string_free (dst_str, FALSE); }
gint decode_packbits (const gchar *src, gchar *dst, guint16 packed_len, guint32 unpacked_len) { /* * Decode a PackBits chunk. */ gint n; gchar dat; gint32 unpack_left = unpacked_len; gint32 pack_left = packed_len; gint32 error_code = 0; gint32 return_val = 0; while (unpack_left > 0 && pack_left > 0) { n = *src; src++; pack_left--; if (n == 128) /* nop */ continue; else if (n > 128) n -= 256; if (n < 0) /* replicate next gchar |n|+ 1 times */ { n = 1 - n; if (! pack_left) { IFDBG(2) g_debug ("Input buffer exhausted in replicate"); error_code = 1; break; } if (n > unpack_left) { IFDBG(2) g_debug ("Overrun in packbits replicate of %d chars", n - unpack_left); error_code = 2; } dat = *src; for (; n > 0; --n) { if (! unpack_left) break; *dst = dat; dst++; unpack_left--; } if (unpack_left) { src++; pack_left--; } } else /* copy next n+1 gchars literally */ { n++; for (; n > 0; --n) { if (! pack_left) { IFDBG(2) g_debug ("Input buffer exhausted in copy"); error_code = 3; break; } if (! unpack_left) { IFDBG(2) g_debug ("Output buffer exhausted in copy"); error_code = 4; break; } *dst = *src; dst++; unpack_left--; src++; pack_left--; } } } if (unpack_left > 0) { /* Pad with zeros to end of output buffer */ for (n = 0; n < pack_left; ++n) { *dst = 0; dst++; } } if (unpack_left) { IFDBG(2) g_debug ("Packbits decode - unpack left %d", unpack_left); return_val -= unpack_left; } if (pack_left) { /* Some images seem to have a pad byte at the end of the packed data */ if (error_code || pack_left != 1) { IFDBG(2) g_debug ("Packbits decode - pack left %d", pack_left); return_val = pack_left; } } IFDBG(2) if (error_code) g_debug ("Error code %d", error_code); return return_val; }
gint32 fwrite_unicode_string (const gchar *src, const guint16 mod_len, FILE *f, GError **error) { /* * Converts utf-8 string to utf-16 and writes 4 byte length * then string padding to multiple of mod_len. */ gunichar2 *utf16_str; gchar null_str = 0x0; gint32 utf16_len = 0; gint32 bytes_written = 0; gint i; glong len; if (src == NULL) { /* Write null string as four byte 0 int32 */ if (fwrite (&utf16_len, 4, 1, f) < 1) { psd_set_error (feof (f), errno, error); return -1; } bytes_written += 4; } else { utf16_str = g_utf8_to_utf16 (src, -1, NULL, &len, NULL); /* Byte swap as required */ utf16_len = len; for (i = 0; i < utf16_len; ++i) utf16_str[i] = GINT16_TO_BE (utf16_str[i]); utf16_len = GINT32_TO_BE (utf16_len); if (fwrite (&utf16_len, 4, 1, f) < 1 || fwrite (utf16_str, 2, utf16_len + 1, f) < utf16_len + 1) { psd_set_error (feof (f), errno, error); return -1; } bytes_written += (4 + 2 * utf16_len + 2); IFDBG(2) g_debug ("Unicode string: %s, bytes_written: %d", src, bytes_written); } /* Pad with nulls */ if (mod_len > 0) { while (bytes_written % mod_len != 0) { if (fwrite (&null_str, 1, 1, f) < 1) { psd_set_error (feof (f), errno, error); return -1; } bytes_written++; } } return bytes_written; }
gchar * fread_unicode_string (gint32 *bytes_read, gint32 *bytes_written, const guint16 mod_len, FILE *f, GError **error) { /* * Reads a utf-16 string from the file padded to a multiple of mod_len * and returns a utf-8 string. */ gchar *utf8_str; gunichar2 *utf16_str; gint32 len; gint32 i; gint32 padded_len; glong utf8_str_len; *bytes_read = 0; *bytes_written = -1; if (fread (&len, 4, 1, f) < 1) { psd_set_error (feof (f), errno, error); return NULL; } *bytes_read += 4; len = GINT32_FROM_BE (len); IFDBG(3) g_debug ("Unicode string length %d", len); if (len == 0) { if (fseek (f, mod_len - 1, SEEK_CUR) < 0) { psd_set_error (feof (f), errno, error); return NULL; } *bytes_read += (mod_len - 1); *bytes_written = 0; return NULL; } utf16_str = g_malloc (len * 2); for (i = 0; i < len; ++i) { if (fread (&utf16_str[i], 2, 1, f) < 1) { psd_set_error (feof (f), errno, error); return NULL; } *bytes_read += 2; utf16_str[i] = GINT16_FROM_BE (utf16_str[i]); } if (mod_len > 0) { padded_len = len + 1; while (padded_len % mod_len != 0) { if (fseek (f, 1, SEEK_CUR) < 0) { psd_set_error (feof (f), errno, error); return NULL; } (*bytes_read)++; padded_len++; } } utf8_str = g_utf16_to_utf8 (utf16_str, len, NULL, &utf8_str_len, NULL); *bytes_written = utf8_str_len; g_free (utf16_str); IFDBG(3) g_debug ("Unicode string: %s, bytes_read: %d, bytes_written: %d", utf8_str, *bytes_read, *bytes_written); return utf8_str; }
gint32 fwrite_pascal_string (const gchar *src, const guint16 mod_len, FILE *f, GError **error) { /* * Converts utf-8 string to current locale and writes as pascal * string with padding to a multiple of mod_len. */ gchar *str; gchar *pascal_str; gchar null_str = 0x0; guchar pascal_len; gint32 bytes_written = 0; gsize len; if (src == NULL) { /* Write null string as two null bytes (0x0) */ if (fwrite (&null_str, 1, 1, f) < 1 || fwrite (&null_str, 1, 1, f) < 1) { psd_set_error (feof (f), errno, error); return -1; } bytes_written += 2; } else { str = g_locale_from_utf8 (src, -1, NULL, &len, NULL); if (len > 255) pascal_len = 255; else pascal_len = len; pascal_str = g_strndup (str, pascal_len); g_free (str); if (fwrite (&pascal_len, 1, 1, f) < 1 || fwrite (pascal_str, pascal_len, 1, f) < 1) { psd_set_error (feof (f), errno, error); return -1; } bytes_written++; bytes_written += pascal_len; IFDBG(2) g_debug ("Pascal string: %s, bytes_written: %d", pascal_str, bytes_written); } /* Pad with nulls */ if (mod_len > 0) { while (bytes_written % mod_len != 0) { if (fwrite (&null_str, 1, 1, f) < 1) { psd_set_error (feof (f), errno, error); return -1; } bytes_written++; } } return bytes_written; }