+Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
+
+ * exif.[ch]: A lot of code clean up, add generic tiff header parser,
+ remove use of packed structures to interpret tiff file format,
+ fix possible endless loops in tiff parser with corrupt IFD tables,
+ and fix possible overflow in jpeg exif parser.
+ * format_canon.[ch]: Add additional makernote values, plus a few
+ spelling fixes. Header update.
+ * format_fuji.[ch]: Header update.
+ * format_nikon.[ch]: Updates to use new tiff parsing utils in exif.c,
+ code cleanup. Header update.
+ * format_raw.[ch]: Add pathname argument to file descriptor version of
+ raw parser to quickly rule out non-raw files based on file extension.
+ Add raw header match type to check for tiff "make" field value.
+ * image-load.c (image_loader_begin): Add image filename for raw parser.
+
Tue Jun 7 03:47:03 2005 John Ellis <johne@verizon.net>
* filelist.c (filter_add_defaults): Add Nikon file extension for nef.
>raw + exif formats:
- > rethink raw format header parser, apparently canon and nikon both use the TIFF file format,
- so it is possible that the same magic header can be in both formats - it only works now
- because Canon header list ignores Motorola alignment, which is what Nikon uses. Additionally
- matching CRW format uses a magic "HEADCCDR" offset 6 bytes into the file, first two bytes are
- similar to tiff for specifying byte alignment (II or MM), so the current code will also pick
- up tiff files. Whatever happens here, we want to avoid mmap'ing the file until we are sure.
-
- > make a generic tiff header and directory parser from the nikon parser for use by all raw
+ > all thats left to do is possibly mmap the file so we have all the tiff data available when
+ looking for the make TIFF tag (0x10f) as it may not always fit within data available from
+ the first read() in image-load.c.
+
+ d> make a generic tiff header and directory parser from the nikon parser for use by all raw
parsers that involve tiff.
- > clean up canon parser (canon_read_int can be substituted with, or wrap exif_get_int16/32).
+ > clean up canon parser (there are now many convenience utils to simplify tiff header, etc.):
+ > canon_read_int can be substituted with, or wrap exif_get_int16/32.
+ > CR2 tiff code can now use exif_tiff_directory_offset.
> support olympus MakerNote, investigate RAW
> support konica / minolta MakerNote, investigate RAW
#endif
#include <stdio.h>
-#include <inttypes.h> /* stdint.h is not available on all systems... */
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
};
-/*
- *-----------------------------------------------------------------------------
- * misc
- *-----------------------------------------------------------------------------
- */
-
-#define MARKER_UNKNOWN 0x00
-#define MARKER_SOI 0xD8
-#define MARKER_APP1 0xE1
-
-/* These data structs are packed to make sure the
- * byte alignment matches the on-disk data format.
- */
-typedef struct __attribute__((packed)) {
- char byte_order[2];
- uint16_t magic;
- uint32_t IFD_offset;
-} TIFFHeader;
-
-typedef struct __attribute__((packed)) {
- uint16_t tag;
- uint16_t format;
- uint32_t nb;
- uint32_t data;
-} IFDEntry;
-
-
static const ExifMarker *exif_marker_from_tag(guint16 tag, const ExifMarker *list);
/*
return n;
}
-/*
- *-------------------------------------------------------------------
- * marker utils
- *-------------------------------------------------------------------
- */
-
-static int get_marker_size(unsigned char *f)
-{
- /* Size is always in Motorola byte order */
- return exif_byte_get_int16(f+2, EXIF_BYTE_ORDER_MOTOROLA);
-}
-
-static int goto_next_marker(unsigned char **f, int *size, int *marker)
-{
- int marker_size = 2;
-
- *marker = MARKER_UNKNOWN;
-
- /* It is safe to access the marker and its size since we have checked
- * the SOI and this function guaranties the whole next marker is
- * available
- */
- if (*(*f+1) != MARKER_SOI)
- {
- marker_size += get_marker_size(*f);
- }
-
- *size -= marker_size;
-
- /* size should be at least 4, so we can read the marker and its size
- * and check data are actually available
- */
- if (*size < 4) return -1;
-
- /* Jump to the next marker and be sure it begins with 0xFF
- */
- *f += marker_size;
- if (**f != 0xFF) return -1;
-
- if (get_marker_size(*f)+2 > *size) return -1;
-
- *marker = *(*f+1);
-
- return 0;
-}
-
/*
*-------------------------------------------------------------------
* IFD utils
return (list[i].tag == 0 ? NULL : &list[i]);
}
-static void rational_from_data(ExifRational *r, void *src, ExifByteOrder byte_order)
+static void rational_from_data(ExifRational *r, void *src, ExifByteOrder bo)
{
- r->num = exif_byte_swab_int32(*(guint32*)src, byte_order);
- r->den = exif_byte_swab_int32(*(guint32*)(src + sizeof(guint32)), byte_order);
+ r->num = exif_byte_get_int32(src, bo);
+ r->den = exif_byte_get_int32(src + sizeof(guint32), bo);
}
+/* src_format and item->format must be compatible
+ * and not overrun src or item->data.
+ */
void exif_item_copy_data(ExifItem *item, void *src, guint len,
- ExifFormatType src_format, ExifByteOrder byte_order)
+ ExifFormatType src_format, ExifByteOrder bo)
{
gint bs;
gint ne;
ne = item->elements;
dest = item->data;
- if (!dest || len > item->data_len)
+ if (!dest ||
+ ExifFormatList[src_format].size * ne > len)
{
printf("exif tag %s data size mismatch\n", exif_item_get_tag_name(item));
return;
case EXIF_FORMAT_SHORT:
for (i = 0; i < ne; i++)
{
- ((guint16 *)dest)[i] = exif_byte_swab_int16(*(guint16*)(src + i * bs), byte_order);
+ ((guint16 *)dest)[i] = exif_byte_get_int16(src + i * bs, bo);
}
break;
case EXIF_FORMAT_LONG_UNSIGNED:
src_format == EXIF_FORMAT_SHORT)
{
/* a short fits into a long, so allow it */
- int ss;
+ gint ss;
ss = ExifFormatList[src_format].size;
for (i = 0; i < ne; i++)
{
((gint32 *)dest)[i] =
- (gint32)exif_byte_swab_int16(*(guint16*)(src + i * ss), byte_order);
+ (gint32)exif_byte_get_int16(src + i * ss, bo);
}
}
else
for (i = 0; i < ne; i++)
{
((gint32 *)dest)[i] =
- exif_byte_swab_int32(*(guint32*)(src + i * bs), byte_order);
+ exif_byte_get_int32(src + i * bs, bo);
}
}
break;
case EXIF_FORMAT_RATIONAL:
for (i = 0; i < ne; i++)
{
- rational_from_data(&((ExifRational *)dest)[i], src + i * bs, byte_order);
+ rational_from_data(&((ExifRational *)dest)[i], src + i * bs, bo);
}
break;
case EXIF_FORMAT_FLOAT:
for (i = 0; i < ne; i++)
{
- ((float *)dest)[i] = exif_byte_swab_int32(*(guint32*)(src + i * bs), byte_order);
+ ((float *)dest)[i] = exif_byte_get_int32(src + i * bs, bo);
}
break;
case EXIF_FORMAT_DOUBLE:
{
ExifRational r;
- rational_from_data(&r, src + i * bs, byte_order);
+ rational_from_data(&r, src + i * bs, bo);
if (r.den) ((double *)dest)[i] = (double)r.num / r.den;
}
break;
}
static gint exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, guint offset,
- guint size, ExifByteOrder byte_order,
+ guint size, ExifByteOrder bo,
+ gint level,
const ExifMarker *list)
{
- IFDEntry *ent = (IFDEntry*)(tiff+offset);
- guint32 swabed_data;
- void *data;
- guint data_len;
+ guint tag;
+ guint format;
+ guint count;
+ guint data_val;
+ guint data_offset;
+ guint data_length;
const ExifMarker *marker;
ExifItem *item;
- ent->tag = exif_byte_swab_int16(ent->tag, byte_order);
- ent->format = exif_byte_swab_int16(ent->format, byte_order);
- ent->nb = exif_byte_swab_int32(ent->nb, byte_order);
- swabed_data = exif_byte_swab_int32(ent->data, byte_order);
+ tag = exif_byte_get_int16(tiff + offset + EXIF_TIFD_OFFSET_TAG, bo);
+ format = exif_byte_get_int16(tiff + offset + EXIF_TIFD_OFFSET_FORMAT, bo);
+ count = exif_byte_get_int32(tiff + offset + EXIF_TIFD_OFFSET_COUNT, bo);
+ data_val = exif_byte_get_int32(tiff + offset + EXIF_TIFD_OFFSET_DATA, bo);
/* Check tag type. If it does not match, either the format is wrong,
* either it is a unknown tag; so it is not really an error.
*/
- marker = exif_marker_from_tag(ent->tag, list);
+ marker = exif_marker_from_tag(tag, list);
if (!marker)
{
- if (ent->format > EXIF_FORMAT_DOUBLE)
+ if (format >= EXIF_FORMAT_COUNT)
{
- printf("warning: exif tag 0x%4x has invalid format %d\n", ent->tag, ent->format);
+ printf("warning: exif tag 0x%4x has invalid format %d\n", tag, format);
return 0;
}
/* allow non recognized tags to be displayed */
- marker = &ExifUnknownMarkersList[ent->format];
+ marker = &ExifUnknownMarkersList[format];
}
- if (marker->format != ent->format)
+ if (marker->format != format)
{
/* Some cameras got mixed up signed/unsigned_rational
* eg KODAK DC4800 on object_distance tag
* FIXME: what exactly is this test trying to do?
* ok, so this test is to allow the case of swapped signed/unsigned mismatch to leak through?
*/
- if ( !(marker->format == EXIF_FORMAT_RATIONAL_UNSIGNED && ent->format == EXIF_FORMAT_RATIONAL) &&
- !(marker->format == EXIF_FORMAT_RATIONAL && ent->format == EXIF_FORMAT_RATIONAL_UNSIGNED) &&
+ if (!(marker->format == EXIF_FORMAT_RATIONAL_UNSIGNED && format == EXIF_FORMAT_RATIONAL) &&
+ !(marker->format == EXIF_FORMAT_RATIONAL && format == EXIF_FORMAT_RATIONAL_UNSIGNED) &&
/* short fits into a long so allow this mismatch
* as well (some tags allowed to be unsigned short _or_ unsigned long)
*/
- !(marker->format == EXIF_FORMAT_LONG_UNSIGNED && ent->format == EXIF_FORMAT_SHORT_UNSIGNED) )
+ !(marker->format == EXIF_FORMAT_LONG_UNSIGNED && format == EXIF_FORMAT_SHORT_UNSIGNED) )
{
- if (ent->format <= EXIF_FORMAT_DOUBLE)
+ if (format < EXIF_FORMAT_COUNT)
{
printf("warning: exif tag %s format mismatch, found %s exif spec requests %s\n",
- marker->key, ExifFormatList[ent->format].short_name,
+ marker->key, ExifFormatList[format].short_name,
ExifFormatList[marker->format].short_name);
}
else
{
printf("warning: exif tag %s format mismatch, found unknown id %d exif spec requests %d (%s)\n",
- marker->key, ent->format, marker->format,
+ marker->key, format, marker->format,
ExifFormatList[marker->format].short_name);
}
return 0;
/* Where is the data, is it available?
*/
- if (marker->components > 0 && marker->components != ent->nb)
+ if (marker->components > 0 && marker->components != count)
{
printf("warning: exif tag %s has %d elements, exif spec requests %d\n",
- marker->key, ent->nb, marker->components);
+ marker->key, count, marker->components);
}
- data_len = ExifFormatList[marker->format].size * ent->nb;
- if (data_len > sizeof(ent->data))
+
+ data_length = ExifFormatList[marker->format].size * count;
+ if (data_length > 4)
{
- if (size < swabed_data+data_len)
+ data_offset = data_val;
+ if (size < data_offset + data_length)
{
- printf("warning: exif tag %s will overrun IFD segment, ignored.\n", marker->key);
+ printf("warning: exif tag %s data will overrun end of file, ignored.\n", marker->key);
return -1;
}
- data = (void*)tiff + swabed_data;
}
else
{
- data = (void*)(&(ent->data));
+ data_offset = offset + EXIF_TIFD_OFFSET_DATA;
}
- item = exif_item_new(marker->format, ent->tag, ent->nb, marker);
- exif_item_copy_data(item, data, data_len, ent->format, byte_order);
+ item = exif_item_new(marker->format, tag, count, marker);
+ exif_item_copy_data(item, tiff + data_offset, data_length, format, bo);
exif->items = g_list_prepend(exif->items, item);
if (list == ExifKnownMarkersList)
switch (item->tag)
{
case TAG_EXIFOFFSET:
- exif_parse_IFD_table(exif, tiff, swabed_data, size, byte_order, list);
+ exif_parse_IFD_table(exif, tiff, data_val, size, bo, level + 1, list);
break;
case TAG_EXIFMAKERNOTE:
- format_exif_makernote_parse(exif, tiff, swabed_data, size, byte_order);
+ format_exif_makernote_parse(exif, tiff, data_val, size, bo);
break;
}
}
gint exif_parse_IFD_table(ExifData *exif,
unsigned char *tiff, guint offset,
- guint size, ExifByteOrder byte_order,
+ guint size, ExifByteOrder bo,
+ gint level,
const ExifMarker *list)
{
- gint i, nb_entries;
+ guint count;
+ guint i;
+
+ /* limit damage from infinite loops */
+ if (level > EXIF_TIFF_MAX_LEVELS) return -1;
/* We should be able to read number of entries in IFD0) */
- if (size < offset+2) return -1;
+ if (size < offset + 2) return -1;
- nb_entries = exif_byte_get_int16(tiff+offset, byte_order);
+ count = exif_byte_get_int16(tiff + offset, bo);
/* Entries and next IFD offset must be readable */
- if (size < offset+nb_entries*12+4) return -1;
+ if (size < offset + count * EXIF_TIFD_SIZE + 4) return -1;
+ offset += 2;
- for (i=0; i<nb_entries; ++i)
+ for (i = 0; i < count; i++)
{
- exif_parse_IFD_entry(exif, tiff, offset+2+i*sizeof(IFDEntry), size, byte_order, list);
+ exif_parse_IFD_entry(exif, tiff, offset + i * EXIF_TIFD_SIZE, size, bo, level, list);
}
return 0;
*-------------------------------------------------------------------
*/
-gint exif_parse_TIFF(ExifData *exif, unsigned char *tiff, guint size, ExifMarker *list)
+gint exif_tiff_directory_offset(unsigned char *data, const guint len,
+ guint *offset, ExifByteOrder *bo)
{
- ExifByteOrder byte_order;
- guint offset=0;
+ if (len < 8) return FALSE;
- if (size < sizeof(TIFFHeader))
+ if (memcmp(data, "II", 2) == 0)
{
- return -1;
+ *bo = EXIF_BYTE_ORDER_INTEL;
}
-
- if (strncmp(((TIFFHeader*)tiff)->byte_order, "II", 2) == 0)
+ else if (memcmp(data, "MM", 2) == 0)
{
- byte_order = EXIF_BYTE_ORDER_INTEL;
+ *bo = EXIF_BYTE_ORDER_MOTOROLA;
}
- else if (strncmp(((TIFFHeader*)tiff)->byte_order, "MM", 2) == 0)
+ else
{
- byte_order = EXIF_BYTE_ORDER_MOTOROLA;
+ return FALSE;
}
- else
+
+ if (exif_byte_get_int16(data + 2, *bo) != 0x002A)
{
- return -1;
+ return FALSE;
}
- if (exif_byte_swab_int16(((TIFFHeader*)tiff)->magic, byte_order) != 0x002A)
+ *offset = exif_byte_get_int32(data + 4, *bo);
+
+ return (*offset < len);
+}
+
+gint exif_tiff_parse(ExifData *exif, unsigned char *tiff, guint size, ExifMarker *list)
+{
+ ExifByteOrder bo;
+ guint offset;
+
+ if (!exif_tiff_directory_offset(tiff, size, &offset, &bo)) return -1;
+
+ return exif_parse_IFD_table(exif, tiff, offset, size, bo, 0, list);
+}
+
+/*
+ *-------------------------------------------------------------------
+ * jpeg marker utils
+ *-------------------------------------------------------------------
+ */
+
+#define MARKER_UNKNOWN 0x00
+#define MARKER_SOI 0xD8
+#define MARKER_APP1 0xE1
+
+static gint jpeg_get_marker_size(unsigned char *data)
+{
+ /* Size is always in Motorola byte order */
+ return exif_byte_get_int16(data + 2, EXIF_BYTE_ORDER_MOTOROLA);
+}
+
+static gint jpeg_goto_next_marker(unsigned char **data, gint *size, gint *marker)
+{
+ gint marker_size = 2;
+
+ *marker = MARKER_UNKNOWN;
+
+ /* It is safe to access the marker and its size since we have checked
+ * the SOI and this function guaranties the whole next marker is
+ * available
+ */
+ if (*(*data + 1) != MARKER_SOI)
{
- return -1;
+ marker_size += jpeg_get_marker_size(*data);
}
- offset = exif_byte_swab_int32(((TIFFHeader*)tiff)->IFD_offset, byte_order);
+ *size -= marker_size;
+
+ /* size should be at least 4, so we can read the marker and its size
+ * and check data are actually available
+ */
+ if (*size < 4) return -1;
+
+ /* Jump to the next marker and be sure it begins with 0xFF
+ */
+ *data += marker_size;
+ if (**data != 0xFF) return -1;
+
+ if (jpeg_get_marker_size(*data) + 2 > *size) return -1;
- return exif_parse_IFD_table(exif, tiff, offset, size, byte_order, list);
+ *marker = *(*data + 1);
+
+ return 0;
}
-static gint exif_parse_JPEG(ExifData *exif, unsigned char *f, guint size, ExifMarker *list)
+
+static gint exif_parse_JPEG(ExifData *exif, unsigned char *data, guint size, ExifMarker *list)
{
- guint marker, marker_size;
+ guint marker;
+ guint marker_size;
- if (size<2 || *f!=0xFF || *(f+1)!=MARKER_SOI)
+ if (size < 4 || *data != 0xFF || *(data + 1) != MARKER_SOI)
{
return -2;
}
do {
- if (goto_next_marker(&f, &size, &marker) == -1)
+ if (jpeg_goto_next_marker(&data, &size, &marker) == -1)
{
break;
}
return -2;
}
- marker_size = get_marker_size(f)-2;
+ marker_size = jpeg_get_marker_size(data) - 2;
- if (marker_size<6 || strncmp((char*)f+4, "Exif\0\0", 6)!=0)
+ if (marker_size < 6 || strncmp((char*)data + 4, "Exif\0\0", 6) != 0)
{
return -2;
}
- return exif_parse_TIFF(exif, f+10, marker_size-6, list);
+ return exif_tiff_parse(exif, data + 10, marker_size - 6, list);
}
+/*
+ *-------------------------------------------------------------------
+ * misc
+ *-------------------------------------------------------------------
+ */
+
static gint map_file(const gchar *path, void **mapping, int *size)
{
int fd;
if ((res = exif_parse_JPEG(exif, (unsigned char *)f, size, ExifKnownMarkersList)) == -2)
{
- res = exif_parse_TIFF(exif, (unsigned char *)f, size, ExifKnownMarkersList);
+ res = exif_tiff_parse(exif, (unsigned char *)f, size, ExifKnownMarkersList);
}
if (res != 0)
if (format_raw_img_exif_offsets(f, size, NULL, &offset))
{
- res = exif_parse_TIFF(exif, (unsigned char*)f + offset, size - offset, ExifKnownMarkersList);
+ res = exif_tiff_parse(exif, (unsigned char*)f + offset, size - offset, ExifKnownMarkersList);
}
}
*-----------------------------------------------------------------------------
*/
+#define EXIF_FORMAT_COUNT 13
+
typedef enum {
EXIF_FORMAT_UNKNOWN = 0,
EXIF_FORMAT_BYTE_UNSIGNED = 1,
-/* These funcs for use by makernote parsers only */
+/* These funcs for use by makernote/tiff parsers only */
+
+#define EXIF_TIFF_MAX_LEVELS 4
+
+#define EXIF_TIFD_OFFSET_TAG 0
+#define EXIF_TIFD_OFFSET_FORMAT 2
+#define EXIF_TIFD_OFFSET_COUNT 4
+#define EXIF_TIFD_OFFSET_DATA 8
+#define EXIF_TIFD_SIZE 12
guint16 exif_byte_get_int16(unsigned char *f, ExifByteOrder bo);
ExifItem *exif_item_new(ExifFormatType format, guint tag,
guint elements, const ExifMarker *marker);
void exif_item_copy_data(ExifItem *item, void *src, guint len,
- ExifFormatType src_format, ExifByteOrder byte_order);
+ ExifFormatType src_format, ExifByteOrder bo);
gint exif_parse_IFD_table(ExifData *exif,
unsigned char *tiff, guint offset,
- guint size, ExifByteOrder byte_order,
+ guint size, ExifByteOrder bo,
+ gint level,
const ExifMarker *list);
-gint exif_parse_TIFF(ExifData *exif, unsigned char *tiff, guint size, ExifMarker *list);
+gint exif_tiff_directory_offset(unsigned char *data, const guint len,
+ guint *offset, ExifByteOrder *bo);
+gint exif_tiff_parse(ExifData *exif, unsigned char *tiff, guint size, ExifMarker *list);
#endif
}
-gint format_canon_raw(const void *data, const guint len,
+gint format_canon_raw(unsigned char *data, const guint len,
guint *image_offset, guint *exif_offset)
{
static ExifTextList CanonSet1Quality[] = {
{ 2, "normal" },
{ 3, "fine" },
+ { 4, "raw" },
{ 5, "superfine" },
EXIF_TEXT_LIST_END
};
{ 0, "flash not fired" },
{ 1, "auto" },
{ 2, "on" },
- { 3, "red eye reduction" },
- { 4, "slow synchro" },
- { 5, "auto with red eye reduction" },
- { 6, "on with red eye reduction" },
+ { 3, "red-eye reduction" },
+ { 4, "slow sync" },
+ { 5, "red-eye reduction (auto)" },
+ { 6, "red-eye reduction (on)" },
{ 16, "external flash" },
EXIF_TEXT_LIST_END
};
static ExifTextList CanonSet1DriveMode[] = {
- { 0, "single or timer" },
+ { 0, "single" },
{ 1, "continuous" },
EXIF_TEXT_LIST_END
};
static ExifTextList CanonSet1FocusMode[] = {
- { 0, "one-shot" },
- { 1, "AI servo" },
- { 2, "AI focus" },
+ { 0, "one-shot AF" },
+ { 1, "AI servo AF" },
+ { 2, "AI focus AF" },
{ 3, "manual" },
{ 4, "single" },
{ 5, "continuous" },
{ 0, "none" },
{ 1, "2x" },
{ 2, "4x" },
+ { 3, "other" },
EXIF_TEXT_LIST_END
};
};
static ExifTextList CanonSet1MeteringMode[] = {
+ { 0, "default" },
+ { 1, "spot" },
{ 3, "evaluative" },
{ 4, "partial" },
{ 5, "center-weighted" },
static ExifTextList CanonSet1FocusType[] = {
{ 0, "manual" },
{ 1, "auto" },
+ { 2, "auto" },
{ 3, "macro" },
+ { 7, "infinity" },
{ 8, "locked" },
EXIF_TEXT_LIST_END
};
static ExifTextList CanonSet1AutoFocusPoint[] = {
- { 12288, "manual focus" },
- { 12289, "auto" },
- { 12290, "right" },
- { 12291, "center" },
- { 12292, "left" },
+ { 0x2005, "manual AF point selection" },
+ { 0x3000, "manual focus" },
+ { 0x3001, "auto" },
+ { 0x3002, "right" },
+ { 0x3003, "center" },
+ { 0x3004, "left" },
+ { 0x4001, "auto AF point selection" },
EXIF_TEXT_LIST_END
};
static ExifTextList CanonSet2WhiteBalance[] = {
{ 0, "auto" },
- { 1, "sunny" },
+ { 1, "daylight" },
{ 2, "cloudy" },
{ 3, "tungsten" },
- { 4, "flourescent" },
+ { 4, "fluorescent" },
{ 5, "flash" },
{ 6, "custom" },
+ { 7, "black and white" },
+ { 8, "shade" },
+ { 9, "manual" },
+ { 14, "daylight fluorescent" },
+ { 17, "underwater" },
EXIF_TEXT_LIST_END
};
};
static void canon_mknote_parse_settings(ExifData *exif,
- guint16 *data, guint32 len, ExifByteOrder byte_order,
+ guint16 *data, guint32 len, ExifByteOrder bo,
ExifMarker *list)
{
gint i;
ExifItem *item;
item = exif_item_new(EXIF_FORMAT_SHORT_UNSIGNED, list[i].tag, 1, &list[i]);
- exif_item_copy_data(item, &data[list[i].tag], 1, EXIF_FORMAT_SHORT_UNSIGNED, byte_order);
+ exif_item_copy_data(item, &data[list[i].tag], 2, EXIF_FORMAT_SHORT_UNSIGNED, bo);
exif->items = g_list_prepend(exif->items, item);
}
#endif
gint format_canon_makernote(ExifData *exif, unsigned char *tiff, guint offset,
- guint size, ExifByteOrder byte_order)
+ guint size, ExifByteOrder bo)
{
ExifItem *item;
- if (exif_parse_IFD_table(exif, tiff, offset, size, byte_order, CanonExifMarkersList) != 0)
+ if (exif_parse_IFD_table(exif, tiff, offset, size, bo, 0, CanonExifMarkersList) != 0)
{
return FALSE;
}
item = exif_get_item(exif, "MkN.Canon.Settings1");
if (item)
{
- canon_mknote_parse_settings(exif, item->data, item->data_len, byte_order, CanonSet1);
+ canon_mknote_parse_settings(exif, item->data, item->data_len, bo, CanonSet1);
}
item = exif_get_item(exif, "MkN.Canon.Settings2");
if (item)
{
- canon_mknote_parse_settings(exif, item->data, item->data_len, byte_order, CanonSet2);
+ canon_mknote_parse_settings(exif, item->data, item->data_len, bo, CanonSet2);
}
#if 0
#include "exif.h"
-gint format_canon_raw(const void *data, const guint len,
+gint format_canon_raw(unsigned char *data, const guint len,
guint *image_offset, guint *exif_offset);
-#define FORMAT_RAW_CANON { "II", 2, "Canon crw format", format_canon_raw }, \
- { "\x49\x49\x2a\00", 4, "Canon cr2 format", format_canon_raw }
+#define FORMAT_RAW_CANON { "crw", \
+ FORMAT_RAW_MATCH_MAGIC, 6, "HEAPCCDR", 8, \
+ "Canon crw", format_canon_raw }, \
+ { "cr2", \
+ FORMAT_RAW_MATCH_TIFF_MAKE, 0, "Canon", 5, \
+ "Canon cr2", format_canon_raw }
gint format_canon_makernote(ExifData *exif, unsigned char *tiff, guint offset,
- guint size, ExifByteOrder byte_order);
+ guint size, ExifByteOrder bo);
#define FORMAT_EXIF_CANON { FORMAT_EXIF_MATCH_MAKE, "Canon", 5, "Canon", format_canon_makernote }
#endif
+
*/
-gint format_fuji_raw(const void *data, const guint len,
+gint format_fuji_raw(unsigned char *data, const guint len,
guint *image_offset, guint *exif_offset)
{
guint io;
gint format_fuji_makernote(ExifData *exif, unsigned char *tiff, guint offset,
- guint size, ExifByteOrder byte_order)
+ guint size, ExifByteOrder bo)
{
unsigned char *data;
guint ifdstart;
if (offset + 8 + 4 >= size) return FALSE;
data = tiff + offset;
+
+ /* Fuji tag format starts with "FUJIFILM",
+ * followed by 4 bytes indicating offset to IFD directory using Fuji tags,
+ * byte order is always little endian (II).
+ */
if (memcmp(data, "FUJIFILM", 8) != 0) return FALSE;
ifdstart = exif_byte_get_int32(data + 8, EXIF_BYTE_ORDER_INTEL);
if (offset + ifdstart >= size) return FALSE;
if (exif_parse_IFD_table(exif, tiff + offset, ifdstart, size - offset,
- EXIF_BYTE_ORDER_INTEL, FujiExifMarkersList) != 0)
+ EXIF_BYTE_ORDER_INTEL, 0, FujiExifMarkersList) != 0)
{
return FALSE;
}
#include "exif.h"
-gint format_fuji_raw(const void *data, const guint len,
+gint format_fuji_raw(unsigned char *data, const guint len,
guint *image_offset, guint *exif_offset);
-#define FORMAT_RAW_FUJI { "FUJIFILM", 8, "Fuji raw format", format_fuji_raw }
+#define FORMAT_RAW_FUJI { "raf", \
+ FORMAT_RAW_MATCH_MAGIC, 0, "FUJIFILM", 8, \
+ "Fuji raw", format_fuji_raw }
gint format_fuji_makernote(ExifData *exif, unsigned char *tiff, guint offset,
- guint size, ExifByteOrder byte_order);
+ guint size, ExifByteOrder bo);
#define FORMAT_EXIF_FUJI { FORMAT_EXIF_MATCH_MAKERNOTE, "FUJIFILM", 8, "Fujifilm", format_fuji_makernote }
*-----------------------------------------------------------------------------
*/
-static guint tiff_table(unsigned char *data, const guint len, guint offset, ExifByteOrder byte_order,
- guint *image_offset, guint *jpeg_len);
+static guint nikon_tiff_table(unsigned char *data, const guint len, guint offset, ExifByteOrder bo,
+ gint level,
+ guint *image_offset, guint *jpeg_len);
-static void tiff_entry(unsigned char *data, const guint len, guint offset, ExifByteOrder byte_order,
- guint *image_offset, guint *image_length, guint *jpeg_start, guint *jpeg_len)
+static void nikon_tiff_entry(unsigned char *data, const guint len, guint offset, ExifByteOrder bo,
+ gint level,
+ guint *image_offset, guint *image_length, guint *jpeg_start, guint *jpeg_len)
{
- static gint size[] = { 1,1,1,2,4,8,1,1,2,4,8,4,8 };
guint tag;
guint type;
guint count;
guint segment;
+ guint seg_len;
- tag = exif_byte_get_int16(data + offset, byte_order);
- type = exif_byte_get_int16(data + offset + 2, byte_order);
- count = exif_byte_get_int32(data + offset + 4, byte_order);
+ tag = exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_TAG, bo);
+ type = exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_FORMAT, bo);
+ count = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_COUNT, bo);
- if (type > 12) return;
- if (count * size[type] > 4)
+ /* so far, we only care about tags with type long */
+ if (type != EXIF_FORMAT_LONG_UNSIGNED && type != EXIF_FORMAT_LONG) return;
+
+ seg_len = ExifFormatList[type].size * count;
+ if (seg_len > 4)
{
- segment = exif_byte_get_int32(data + offset + 8, byte_order);
- if (len < segment + count * size[type]) return;
+ segment = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_DATA, bo);
+ if (segment + seg_len > len) return;
}
else
{
- segment = offset + 8;
+ segment = offset + EXIF_TIFD_OFFSET_DATA;
}
- if (tag == 0x14a &&
- type == EXIF_FORMAT_LONG_UNSIGNED)
+ if (tag == 0x14a)
{
/* sub IFD table */
gint i;
{
guint subset;
- subset = exif_byte_get_int32(data + segment + i * 4, byte_order);
- tiff_table(data, len, subset, byte_order, image_offset, image_length);
+ subset = exif_byte_get_int32(data + segment + i * 4, bo);
+ nikon_tiff_table(data, len, subset, bo, level + 1, image_offset, image_length);
}
}
else if (tag == 0x201)
{
/* jpeg data start offset */
- *jpeg_start = exif_byte_get_int32(data + segment, byte_order);
+ *jpeg_start = exif_byte_get_int32(data + segment, bo);
}
else if (tag == 0x202)
{
/* jpeg data length */
- *jpeg_len = exif_byte_get_int32(data + segment, byte_order);
+ *jpeg_len = exif_byte_get_int32(data + segment, bo);
}
}
-static guint tiff_table(unsigned char *data, const guint len, guint offset, ExifByteOrder byte_order,
- guint *image_offset, guint *image_length)
+static guint nikon_tiff_table(unsigned char *data, const guint len, guint offset, ExifByteOrder bo,
+ gint level,
+ guint *image_offset, guint *image_length)
{
guint count;
guint i;
guint jpeg_start = 0;
guint jpeg_len = 0;
+ /* limit damage from infinite loops */
+ if (level > EXIF_TIFF_MAX_LEVELS) return 0;
+
if (len < offset + 2) return FALSE;
- count = exif_byte_get_int16((unsigned char *)data + offset, byte_order);
+ count = exif_byte_get_int16(data + offset, bo);
- if (len < offset + count * 12 + 4) return 0;
+ if (len < offset + count * EXIF_TIFD_SIZE + 4) return 0;
offset += 2;
for (i = 0; i < count; i++)
{
- tiff_entry(data, len, offset + i * 12, byte_order,
- image_offset, image_length, &jpeg_start, &jpeg_len);
+ nikon_tiff_entry(data, len, offset + i * EXIF_TIFD_SIZE, bo, level,
+ image_offset, image_length, &jpeg_start, &jpeg_len);
}
if (jpeg_start > 0 &&
*image_length = jpeg_len;
}
- return exif_byte_get_int32((unsigned char *)data + offset + count * 12, byte_order);
+ return exif_byte_get_int32(data + offset + count * EXIF_TIFD_SIZE, bo);
}
-/*
- * Walk the first TIFF IFD table and check for existence of a "make" tag (0x10f) that
- * identifies NIKON CORPORATION, so that we can abort quickly if it is not a raw NEF.
- */
-static gint tiff_nikon_verify(unsigned char *data, const guint len, guint offset, ExifByteOrder byte_order)
-{
- guint nb_entries;
- guint i;
-
- if (len < offset + 2) return FALSE;
-
- nb_entries = exif_byte_get_int16(data + offset, byte_order);
- offset += 2;
- if (len < offset + nb_entries * 12 + 4) return FALSE;
-
- for (i = 0; i < nb_entries; i++)
- {
- guint segment;
-
- segment = offset + i * 12;
- if (exif_byte_get_int16(data + segment, byte_order) == 0x10f &&
- exif_byte_get_int16(data + segment + 2, byte_order) == EXIF_FORMAT_STRING)
- {
- guint count;
- guint make_text;
-
- count = exif_byte_get_int32(data + segment + 4, byte_order);
- make_text = exif_byte_get_int32(data + segment + 8, byte_order);
-
- if (count >= 17 &&
- memcmp(data + make_text, "NIKON CORPORATION", 17) == 0)
- {
- return TRUE;
- }
-
- return FALSE;
- }
- }
-
- return FALSE;
-}
-
-gint format_nikon_raw(const void *data, const guint len,
+gint format_nikon_raw(unsigned char *data, const guint len,
guint *image_offset, guint *exif_offset)
{
guint i_off = 0;
guint i_len = 0;
- ExifByteOrder byte_order;
+ ExifByteOrder bo;
guint offset;
+ gint level;
- if (len < 8) return FALSE;
+ if (!exif_tiff_directory_offset(data, len, &offset, &bo)) return FALSE;
- if (memcmp(data, "II", 2) == 0)
- {
- byte_order = EXIF_BYTE_ORDER_INTEL;
- }
- else if (memcmp(data, "MM", 2) == 0)
+ level = 0;
+ while (offset && level < EXIF_TIFF_MAX_LEVELS)
{
- byte_order = EXIF_BYTE_ORDER_MOTOROLA;
- }
- else
- {
- return FALSE;
- }
-
- if (exif_byte_get_int16((unsigned char *)data + 2, byte_order) != 0x002A)
- {
- return FALSE;
- }
-
- offset = exif_byte_get_int32((unsigned char *)data + 4, byte_order);
- if (!tiff_nikon_verify((unsigned char *)data, len, offset, byte_order)) return FALSE;
-
- while (offset != 0)
- {
- guint next_offset = 0;
- tiff_table((unsigned char *)data, len, offset, byte_order, &i_off, &i_len);
- offset = next_offset;
+ offset = nikon_tiff_table(data, len, offset, bo, 0, &i_off, &i_len);
+ level++;
}
if (i_off != 0)
gint format_nikon_makernote(ExifData *exif, unsigned char *tiff, guint offset,
- guint size, ExifByteOrder byte_order)
+ guint size, ExifByteOrder bo)
{
unsigned char *data;
if (offset + 8 + 4 >= size) return FALSE;
data = tiff + offset;
+
+ /* Nikon tag format 1 */
if (memcmp(data, "Nikon\x00\x01\x00", 8) == 0)
{
if (exif_parse_IFD_table(exif, tiff, offset + 8, size,
- byte_order, NikonExifMarkersList1) != 0)
+ bo, 0, NikonExifMarkersList1) != 0)
{
return FALSE;
}
return TRUE;
}
+ /* Nikon tag format 2 uses Embedded tiff header */
if (memcmp(data, "Nikon\x00\x02\x00\x00\x00", 10) == 0 ||
memcmp(data, "Nikon\x00\x02\x10\x00\x00", 10) == 0)
{
guint tiff_header;
tiff_header = offset + 10;
- if (exif_parse_TIFF(exif, tiff + tiff_header, size - tiff_header,
+ if (exif_tiff_parse(exif, tiff + tiff_header, size - tiff_header,
NikonExifMarkersList2) != 0)
{
return FALSE;
return TRUE;
}
- /* fixme: support E990 and D1 */
+ /* Nikon tag format 3 uses format 2 tags without "Nikon" and tiff header */
if (exif_parse_IFD_table(exif, tiff, offset, size,
- byte_order, NikonExifMarkersList2) != 0)
+ bo, 0, NikonExifMarkersList2) != 0)
{
return FALSE;
}
#include "exif.h"
-gint format_nikon_raw(const void *data, const guint len,
+gint format_nikon_raw(unsigned char *data, const guint len,
guint *image_offset, guint *exif_offset);
-#define FORMAT_RAW_NIKON { "II\x2a\x00", 4, "Nikon tiff raw", format_nikon_raw }, \
- { "MM\x00\x2a", 4, "Nikon tiff raw", format_nikon_raw }
+#define FORMAT_RAW_NIKON { "nef", \
+ FORMAT_RAW_MATCH_TIFF_MAKE, 0, "NIKON CORPORATION", 17, \
+ "Nikon raw", format_nikon_raw }
gint format_nikon_makernote(ExifData *exif, unsigned char *tiff, guint offset,
- guint size, ExifByteOrder byte_order);
+ guint size, ExifByteOrder bo);
#define FORMAT_EXIF_NIKON { FORMAT_EXIF_MATCH_MAKERNOTE, "Nikon\x00", 6, "Nikon", format_nikon_makernote }, \
{ FORMAT_EXIF_MATCH_MAKE, "NIKON", 5, "Nikon", format_nikon_makernote }
typedef struct _FormatRawEntry FormatRawEntry;
struct _FormatRawEntry {
- const void *header_pattern;
- const guint header_length;
+ const gchar *extension;
+ FormatRawMatchType magic_type;
+ const guint magic_offset;
+ const void *magic_pattern;
+ const guint magic_length;
const gchar *description;
FormatRawParseFunc func_parse;
};
FORMAT_RAW_CANON,
FORMAT_RAW_FUJI,
FORMAT_RAW_NIKON,
- { NULL, 0, NULL, NULL }
+ { NULL, 0, 0, NULL, 0, NULL, NULL }
};
};
-static FormatRawEntry *format_raw_find(const void *data, const guint len)
+static guint tiff_table(unsigned char *data, const guint len, guint offset, ExifByteOrder bo,
+ guint tag, ExifFormatType type,
+ guint *result_offset, guint *result_count)
+{
+ guint count;
+ guint i;
+
+ if (len < offset + 2) return 0;
+ if (type < 0 || type > EXIF_FORMAT_COUNT) return 0;
+
+ count = exif_byte_get_int16(data + offset, bo);
+ offset += 2;
+ if (len < offset + count * 12 + 4) return 0;
+
+ for (i = 0; i < count; i++)
+ {
+ guint segment;
+
+ segment = offset + i * 12;
+ if (exif_byte_get_int16(data + segment, bo) == tag &&
+ exif_byte_get_int16(data + segment + 2, bo) == type)
+ {
+ guint chunk_count;
+ guint chunk_offset;
+ guint chunk_length;
+
+ chunk_count = exif_byte_get_int32(data + segment + 4, bo);
+ chunk_length = ExifFormatList[type].size * chunk_count;
+
+ if (chunk_length > 4)
+ {
+ chunk_offset = exif_byte_get_int32(data + segment + 8, bo);
+ }
+ else
+ {
+ chunk_offset = segment + 8;
+ }
+
+ if (chunk_offset + chunk_length <= len)
+ {
+ *result_offset = chunk_offset;
+ *result_count = chunk_count;
+ }
+
+ return 0;
+ }
+ }
+
+ return exif_byte_get_int32(data + offset + count * 12, bo);
+}
+
+static gint format_tiff_find_tag_data(unsigned char *data, const guint len,
+ guint tag, ExifFormatType type,
+ guint *result_offset, guint *result_count)
+{
+ ExifByteOrder bo;
+ guint offset;
+
+ if (len < 8) return FALSE;
+
+ if (memcmp(data, "II", 2) == 0)
+ {
+ bo = EXIF_BYTE_ORDER_INTEL;
+ }
+ else if (memcmp(data, "MM", 2) == 0)
+ {
+ bo = EXIF_BYTE_ORDER_MOTOROLA;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ if (exif_byte_get_int16(data + 2, bo) != 0x002A)
+ {
+ return FALSE;
+ }
+
+ offset = exif_byte_get_int32(data + 4, bo);
+
+ while (offset != 0)
+ {
+ guint ro = 0;
+ guint rc = 0;
+
+ offset = tiff_table(data, len, offset, bo, tag, type, &ro, &rc);
+ if (ro != 0)
+ {
+ *result_offset = ro;
+ *result_count = rc;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static FormatRawEntry *format_raw_find(unsigned char *data, const guint len)
{
gint n;
+ gint tiff;
+ guint make_count = 0;
+ guint make_offset = 0;
+
+ tiff = (len > 8 &&
+ (memcmp(data, "II\x2a\x00", 4) == 0 ||
+ memcmp(data, "MM\x00\x2a", 4) == 0));
n = 0;
- while (format_raw_list[n].header_pattern)
+ while (format_raw_list[n].magic_pattern)
{
- if (format_raw_list[n].header_length <= len &&
- memcmp(data, format_raw_list[n].header_pattern, format_raw_list[n].header_length) == 0)
+ FormatRawEntry *entry = &format_raw_list[n];
+
+ switch (entry->magic_type)
{
- return &format_raw_list[n];
+ case FORMAT_RAW_MATCH_MAGIC:
+ if (entry->magic_length + entry->magic_offset <= len &&
+ memcmp(data + entry->magic_offset,
+ entry->magic_pattern, entry->magic_length) == 0)
+ {
+ return entry;
+ }
+ break;
+ case FORMAT_RAW_MATCH_TIFF_MAKE:
+ if (tiff &&
+ make_offset == 0 &&
+ !format_tiff_find_tag_data(data, len, 0x10f, EXIF_FORMAT_STRING,
+ &make_offset, &make_count))
+ {
+ tiff = FALSE;
+ }
+ if (make_offset != 0 &&
+ make_count >= entry->magic_offset + entry->magic_length &&
+ memcmp(entry->magic_pattern,
+ data + make_offset + entry->magic_offset, entry->magic_length) == 0)
+ {
+ return entry;
+ }
+ break;
+ default:
+ break;
}
n++;
}
}
static gint format_raw_parse(FormatRawEntry *entry,
- const void *data, const guint len,
+ unsigned char *data, const guint len,
guint *image_offset, guint *exif_offset)
{
guint io = 0;
return TRUE;
}
-gint format_raw_img_exif_offsets(const void *data, const guint len,
+gint format_raw_img_exif_offsets(unsigned char *data, const guint len,
guint *image_offset, guint *exif_offset)
{
FormatRawEntry *entry;
}
-gint format_raw_img_exif_offsets_fd(int fd, const void *header_data, const guint header_len,
+gint format_raw_img_exif_offsets_fd(int fd, const gchar *path,
+ unsigned char *header_data, const guint header_len,
guint *image_offset, guint *exif_offset)
{
FormatRawEntry *entry;
if (!header_data || fd < 0) return FALSE;
+ /* given image pathname, first do simple (and fast) file extension test */
+ if (path)
+ {
+ const gchar *ext;
+ gint match = FALSE;
+ gint i;
+
+ ext = strrchr(path, '.');
+ if (!ext) return FALSE;
+ ext++;
+
+ i = 0;
+ while (!match && format_raw_list[i].magic_pattern)
+ {
+ if (format_raw_list[i].extension &&
+ strcasecmp(format_raw_list[i].extension, ext) == 0)
+ {
+ match = TRUE;
+ }
+ i++;
+ }
+
+ if (!match) return FALSE;
+
+ if (debug) printf("RAW file parser extension match\n");
+ }
+
entry = format_raw_find(header_data, header_len);
if (!entry || !entry->func_parse) return FALSE;
}
gint format_exif_makernote_parse(ExifData *exif, unsigned char *tiff, guint offset,
- guint size, ExifByteOrder byte_order)
+ guint size, ExifByteOrder bo)
{
FormatExifEntry *entry;
if (debug) printf("EXIF using makernote parser for %s\n", entry->description);
- return entry->func_parse(exif, tiff, offset, size, byte_order);
+ return entry->func_parse(exif, tiff, offset, size, bo);
}
#include "exif.h"
-typedef gint (* FormatRawParseFunc)(const void *data, const guint len,
+typedef enum {
+ FORMAT_RAW_MATCH_MAGIC,
+ FORMAT_RAW_MATCH_TIFF_MAKE
+} FormatRawMatchType;
+
+typedef gint (* FormatRawParseFunc)(unsigned char *data, const guint len,
guint *image_offset, guint *exif_offset);
-gint format_raw_img_exif_offsets(const void *data, const guint len,
+gint format_raw_img_exif_offsets(unsigned char *data, const guint len,
guint *image_offset, guint *exif_offset);
-gint format_raw_img_exif_offsets_fd(int fd, const void *header_data, const guint header_len,
+gint format_raw_img_exif_offsets_fd(int fd, const gchar *path,
+ unsigned char *header_data, const guint header_len,
guint *image_offset, guint *exif_offset);
} FormatExifMatchType;
typedef gint (* FormatExifParseFunc)(ExifData *exif, unsigned char *tiff, guint offset,
- guint size, ExifByteOrder byte_order);
+ guint size, ExifByteOrder bo);
gint format_exif_makernote_parse(ExifData *exif, unsigned char *tiff, guint offset,
- guint size, ExifByteOrder byte_order);
+ guint size, ExifByteOrder bo);
#endif
b = read(il->load_fd, &buf, sizeof(buf));
if (b > 1 &&
- format_raw_img_exif_offsets_fd(il->load_fd, buf, b, &offset, NULL))
+ format_raw_img_exif_offsets_fd(il->load_fd, il->path, buf, b, &offset, NULL))
{
if (debug) printf("Raw file %s contains embedded image\n", il->path);