changeset 54:b58cac75ad12

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.
author gqview
date Fri, 10 Jun 2005 02:44:36 +0000
parents 00843150f7c8
children 1e21f094e0be
files ChangeLog TODO src/exif.c src/exif.h src/format_canon.c src/format_canon.h src/format_fuji.c src/format_fuji.h src/format_nikon.c src/format_nikon.h src/format_raw.c src/format_raw.h src/image-load.c
diffstat 13 files changed, 493 insertions(+), 320 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Jun 07 07:55:00 2005 +0000
+++ b/ChangeLog	Fri Jun 10 02:44:36 2005 +0000
@@ -1,3 +1,19 @@
+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.
--- a/TODO	Tue Jun 07 07:55:00 2005 +0000
+++ b/TODO	Fri Jun 10 02:44:36 2005 +0000
@@ -22,17 +22,16 @@
 
  >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.
+   > 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.
 
-   > make a generic tiff header and directory parser from the nikon parser for use by all raw
+  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
--- a/src/exif.c	Tue Jun 07 07:55:00 2005 +0000
+++ b/src/exif.c	Fri Jun 10 02:44:36 2005 +0000
@@ -55,7 +55,6 @@
 #endif
 
 #include <stdio.h>
-#include <inttypes.h>   /* stdint.h is not available on all systems... */
 #include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
@@ -428,33 +427,6 @@
 };
 
 
-/*
- *-----------------------------------------------------------------------------
- * 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);
 
 /*
@@ -654,52 +626,6 @@
 
 /*
  *-------------------------------------------------------------------
- * 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
  *-------------------------------------------------------------------
  */
@@ -718,14 +644,17 @@
 	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;
@@ -736,7 +665,8 @@
 	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;
@@ -760,7 +690,7 @@
 		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:
@@ -769,13 +699,13 @@
 			    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
@@ -783,7 +713,7 @@
 				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;
@@ -791,13 +721,13 @@
 		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:
@@ -805,7 +735,7 @@
 				{
 				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;
@@ -813,36 +743,39 @@
 }
 
 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
@@ -850,23 +783,23 @@
 		 * 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;
@@ -875,28 +808,29 @@
 
 	/* 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)
@@ -904,10 +838,10 @@
 		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;
 			}
 		}
@@ -917,22 +851,28 @@
 
 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;
@@ -944,50 +884,107 @@
  *-------------------------------------------------------------------
  */
 
-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;
-		}
-	else if (strncmp(((TIFFHeader*)tiff)->byte_order, "MM", 2) == 0)
-		{
-		byte_order = EXIF_BYTE_ORDER_MOTOROLA;
+		*bo = EXIF_BYTE_ORDER_MOTOROLA;
 		}
 	else
 		{
-		return -1;
+		return FALSE;
 		}
 
-	if (exif_byte_swab_int16(((TIFFHeader*)tiff)->magic, byte_order) != 0x002A)
+	if (exif_byte_get_int16(data + 2, *bo) != 0x002A)
 		{
-		return -1;
+		return FALSE;
 		}
 
-	offset = exif_byte_swab_int32(((TIFFHeader*)tiff)->IFD_offset, byte_order);
+	*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);
+}
 
-	return exif_parse_IFD_table(exif, tiff, offset, size, byte_order, 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 exif_parse_JPEG(ExifData *exif, unsigned char *f, guint size, ExifMarker *list)
+static gint jpeg_goto_next_marker(unsigned char **data, gint *size, gint *marker)
 {
-	guint marker, marker_size;
+	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)
+		{
+		marker_size += jpeg_get_marker_size(*data);
+		}
+
+	*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;
 
-	if (size<2 || *f!=0xFF || *(f+1)!=MARKER_SOI)
+	/* 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;
+
+	*marker = *(*data + 1);
+
+	return 0;
+}
+
+
+static gint exif_parse_JPEG(ExifData *exif, unsigned char *data, guint size, ExifMarker *list)
+{
+	guint marker;
+	guint marker_size;
+
+	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;
 			}
@@ -998,16 +995,22 @@
 		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;
@@ -1090,7 +1093,7 @@
 
 	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)
@@ -1099,7 +1102,7 @@
 		
 		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);
 			}
 		}
 
--- a/src/exif.h	Tue Jun 07 07:55:00 2005 +0000
+++ b/src/exif.h	Fri Jun 10 02:44:36 2005 +0000
@@ -33,6 +33,8 @@
  *-----------------------------------------------------------------------------
  */
 
+#define EXIF_FORMAT_COUNT 13
+
 typedef enum {
 	EXIF_FORMAT_UNKNOWN		= 0,
 	EXIF_FORMAT_BYTE_UNSIGNED	= 1,
@@ -198,7 +200,15 @@
 
 
 
-/* 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);
@@ -209,14 +219,17 @@
 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
--- a/src/format_canon.c	Tue Jun 07 07:55:00 2005 +0000
+++ b/src/format_canon.c	Fri Jun 10 02:44:36 2005 +0000
@@ -258,7 +258,7 @@
 }
 
 
-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)
 {
 
@@ -494,6 +494,7 @@
 static ExifTextList CanonSet1Quality[] = {
 	{ 2,	"normal" },
 	{ 3,	"fine" },
+	{ 4,	"raw" },
 	{ 5,	"superfine" },
 	EXIF_TEXT_LIST_END
 };
@@ -502,24 +503,24 @@
 	{ 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" },
@@ -556,6 +557,7 @@
 	{ 0,	"none" },
 	{ 1,	"2x" },
 	{ 2,	"4x" },
+	{ 3,	"other" },
 	EXIF_TEXT_LIST_END
 };
 
@@ -577,6 +579,8 @@
 };
 
 static ExifTextList CanonSet1MeteringMode[] = {
+	{ 0,	"default" },
+	{ 1,	"spot" },
 	{ 3,	"evaluative" },
 	{ 4,	"partial" },
 	{ 5,	"center-weighted" },
@@ -586,17 +590,21 @@
 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
 };
 
@@ -655,12 +663,17 @@
 
 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
 };
 
@@ -766,7 +779,7 @@
 };
 
 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;
@@ -779,7 +792,7 @@
 			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);
 			}
 
@@ -832,11 +845,11 @@
 #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;
 		}
@@ -844,13 +857,13 @@
 	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
--- a/src/format_canon.h	Tue Jun 07 07:55:00 2005 +0000
+++ b/src/format_canon.h	Fri Jun 10 02:44:36 2005 +0000
@@ -22,19 +22,24 @@
 #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
 
+
--- a/src/format_fuji.c	Tue Jun 07 07:55:00 2005 +0000
+++ b/src/format_fuji.c	Fri Jun 10 02:44:36 2005 +0000
@@ -36,7 +36,7 @@
  */
 
 
-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;
@@ -171,7 +171,7 @@
 
 
 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;
@@ -179,13 +179,18 @@
 	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;
 		}
--- a/src/format_fuji.h	Tue Jun 07 07:55:00 2005 +0000
+++ b/src/format_fuji.h	Fri Jun 10 02:44:36 2005 +0000
@@ -17,15 +17,17 @@
 #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 }
 
--- a/src/format_nikon.c	Tue Jun 07 07:55:00 2005 +0000
+++ b/src/format_nikon.c	Fri Jun 10 02:44:36 2005 +0000
@@ -35,36 +35,40 @@
  *-----------------------------------------------------------------------------
  */
 
-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;
@@ -73,42 +77,46 @@
 			{
 			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 &&
@@ -118,87 +126,25 @@
 		*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;
-
-	if (len < 8) return FALSE;
+	gint level;
 
-	if (memcmp(data, "II", 2) == 0)
-		{
-		byte_order = EXIF_BYTE_ORDER_INTEL;
-		}
-	else if (memcmp(data, "MM", 2) == 0)
-		{
-		byte_order = EXIF_BYTE_ORDER_MOTOROLA;
-		}
-	else
+	if (!exif_tiff_directory_offset(data, len, &offset, &bo)) return FALSE;
+
+	level = 0;
+	while (offset && level < EXIF_TIFF_MAX_LEVELS)
 		{
-		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)
@@ -391,30 +337,33 @@
 
 
 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;
@@ -422,9 +371,9 @@
 		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;
 		}
--- a/src/format_nikon.h	Tue Jun 07 07:55:00 2005 +0000
+++ b/src/format_nikon.h	Fri Jun 10 02:44:36 2005 +0000
@@ -13,15 +13,16 @@
 
 #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 }
--- a/src/format_raw.c	Tue Jun 07 07:55:00 2005 +0000
+++ b/src/format_raw.c	Fri Jun 10 02:44:36 2005 +0000
@@ -39,8 +39,11 @@
 
 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;
 };
@@ -49,7 +52,7 @@
 	FORMAT_RAW_CANON,
 	FORMAT_RAW_FUJI,
 	FORMAT_RAW_NIKON,
-	{ NULL, 0, NULL, NULL }
+	{ NULL, 0, 0, NULL, 0, NULL, NULL }
 };
 
 
@@ -70,17 +73,147 @@
 };
 
 
-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++;
 		}
@@ -89,7 +222,7 @@
 }
 
 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;
@@ -115,7 +248,7 @@
 	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;
@@ -130,7 +263,8 @@
 }
 
 
-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;
@@ -141,6 +275,33 @@
 
 	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;
@@ -219,7 +380,7 @@
 }
 
 gint format_exif_makernote_parse(ExifData *exif, unsigned char *tiff, guint offset,
-				 guint size, ExifByteOrder byte_order)
+				 guint size, ExifByteOrder bo)
 {
 	FormatExifEntry *entry;
 
@@ -229,7 +390,7 @@
 
 	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);
 }
 
 
--- a/src/format_raw.h	Tue Jun 07 07:55:00 2005 +0000
+++ b/src/format_raw.h	Fri Jun 10 02:44:36 2005 +0000
@@ -17,12 +17,18 @@
 #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);
 
 
@@ -32,10 +38,10 @@
 } 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
--- a/src/image-load.c	Tue Jun 07 07:55:00 2005 +0000
+++ b/src/image-load.c	Fri Jun 10 02:44:36 2005 +0000
@@ -218,7 +218,7 @@
 	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);