changeset 51:276ea4c98d33

Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net> * exif.[ch]: Use glib provided data types and byte order functions for consistency with rest of application. Made several more functions available in the header. Use MakerNote parsing from format_raw.c. * format_canon.[ch]: Changes to match exif.h and format_raw.h. * format_fuji.[ch]: Add support for Fuji EXIF MakerNote. * format_nikon.[ch]: New files, add support for Nikon EXIF MakerNote. * format_raw.[ch]: Add EXIF MakerNote parser functions to gather all camera formats here (similar to existing raw format list). * src/Makefile.am: Add format_nikon.[ch]. ##### Note: GQview CVS on sourceforge is not always up to date, please use ##### ##### an offical release when making enhancements and translation updates. #####
author gqview
date Sun, 05 Jun 2005 02:48:54 +0000
parents 3b83fb81afc4
children a210a19f26da
files ChangeLog TODO src/Makefile.am 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
diffstat 13 files changed, 682 insertions(+), 170 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat Jun 04 08:06:47 2005 +0000
+++ b/ChangeLog	Sun Jun 05 02:48:54 2005 +0000
@@ -1,3 +1,15 @@
+Sat Jun  4 22:24:00 2005  John Ellis  <johne@verizon.net>
+
+	* exif.[ch]: Use glib provided data types and byte order functions for
+	consistency with rest of application. Made several more functions
+	available in the header. Use MakerNote parsing from format_raw.c.
+	* format_canon.[ch]: Changes to match exif.h and format_raw.h.
+	* format_fuji.[ch]: Add support for Fuji EXIF MakerNote.
+	* format_nikon.[ch]: New files, add support for Nikon EXIF MakerNote.
+	* format_raw.[ch]: Add EXIF MakerNote parser functions to gather all
+	camera formats here (similar to existing raw format list).
+	* src/Makefile.am: Add format_nikon.[ch].
+
 Sat Jun  4 04:02:04 2005  John Ellis  <johne@verizon.net>
 
 	* README: Update credits.
--- a/TODO	Sat Jun 04 08:06:47 2005 +0000
+++ b/TODO	Sun Jun 05 02:48:54 2005 +0000
@@ -88,7 +88,7 @@
    > collection window
    > search window
 
- > clean up exif.c to be portable (don't assume sizeof(short)==2 and sizeof(long)==4)
+d> clean up exif.c to be portable (don't assume sizeof(short)==2 and sizeof(long)==4)
 
 
 Wishlist?:
--- a/src/Makefile.am	Sat Jun 04 08:06:47 2005 +0000
+++ b/src/Makefile.am	Sun Jun 05 02:48:54 2005 +0000
@@ -88,6 +88,8 @@
 	format_canon.h	\
 	format_fuji.c	\
 	format_fuji.h	\
+	format_nikon.c	\
+	format_nikon.h	\
 	format_raw.c	\
 	format_raw.h	\
 	fullscreen.c	\
--- a/src/exif.c	Sat Jun 04 08:06:47 2005 +0000
+++ b/src/exif.c	Sun Jun 05 02:48:54 2005 +0000
@@ -73,8 +73,6 @@
 #include "format_raw.h"
 #include "ui_fileops.h"
 
-/* makernote parsers */
-#include "format_canon.h"
 
 /*
  *-----------------------------------------------------------------------------
@@ -436,10 +434,6 @@
  *-----------------------------------------------------------------------------
  */
 
-#define BYTE_ORDER_INTEL	1
-#define BYTE_ORDER_MOTOROLA	2
-
-
 #define MARKER_UNKNOWN		0x00
 #define MARKER_SOI		0xD8
 #define MARKER_APP1		0xE1
@@ -461,7 +455,7 @@
 } IFDEntry;
 
 
-static const ExifMarker *exif_marker_from_tag(uint16_t tag, const ExifMarker *list);
+static const ExifMarker *exif_marker_from_tag(guint16 tag, const ExifMarker *list);
 
 /*
  *-----------------------------------------------------------------------------
@@ -469,8 +463,8 @@
  *-----------------------------------------------------------------------------
  */
 
-ExifItem *exif_item_new(ExifFormatType format, unsigned int tag,
-			unsigned int elements, const ExifMarker *marker)
+ExifItem *exif_item_new(ExifFormatType format, guint tag,
+			guint elements, const ExifMarker *marker)
 {
 	ExifItem *item;
 
@@ -495,10 +489,10 @@
 			item->data_len = sizeof(char) * elements;
 			break;
 		case EXIF_FORMAT_SHORT_UNSIGNED:
-			item->data_len = sizeof(unsigned short int) * elements;
+			item->data_len = sizeof(guint16) * elements;
 			break;
 		case EXIF_FORMAT_LONG_UNSIGNED:
-			item->data_len = sizeof(unsigned long int) * elements;
+			item->data_len = sizeof(guint32) * elements;
 			break;
 		case EXIF_FORMAT_RATIONAL_UNSIGNED:
 			item->data_len = sizeof(ExifRational) * elements;
@@ -510,10 +504,10 @@
 			item->data_len = sizeof(char) * elements;
 			break;
 		case EXIF_FORMAT_SHORT:
-			item->data_len = sizeof(short int) * elements;
+			item->data_len = sizeof(gint16) * elements;
 			break;
 		case EXIF_FORMAT_LONG:
-			item->data_len = sizeof(long int) * elements;
+			item->data_len = sizeof(gint32) * elements;
 			break;
 		case EXIF_FORMAT_RATIONAL:
 			item->data_len = sizeof(ExifRational) * elements;
@@ -596,7 +590,7 @@
 	return string;
 }
 
-static gchar *text_list_find_value(ExifTextList *list, gint value)
+static gchar *text_list_find_value(ExifTextList *list, guint value)
 {
 	gchar *result = NULL;
 	gint i;
@@ -618,45 +612,42 @@
  *-------------------------------------------------------------------
  */
 
-static uint16_t get_int16(unsigned char *f, int bo)
+guint16 exif_byte_get_int16(unsigned char *f, ExifByteOrder bo)
 {
-	if (bo == BYTE_ORDER_INTEL)
-		return *f + (*(f+1)<<8);
+	if (bo == EXIF_BYTE_ORDER_INTEL)
+		return GUINT16_FROM_LE(*(guint16*)f);
 	else
-		return ((*f)<<8) + *(f+1);
+		return GUINT16_FROM_BE(*(guint16*)f);
 }
 
-#if 0
-/* not used ? */
-static uint32_t get_int32(unsigned char *f, int bo)
+guint32 exif_byte_get_int32(unsigned char *f, ExifByteOrder bo)
 {
-	if (bo == BYTE_ORDER_INTEL)
-		return get_int16(f, BYTE_ORDER_INTEL) + (get_int16(f+2, BYTE_ORDER_INTEL)<<16);
+	if (bo == EXIF_BYTE_ORDER_INTEL)
+		return GUINT32_FROM_LE(*(guint32*)f);
 	else
-		return (get_int16(f, BYTE_ORDER_MOTOROLA)<<16) + get_int16(f+2, BYTE_ORDER_MOTOROLA);
+		return GUINT32_FROM_BE(*(guint32*)f);
 }
+
+guint16 exif_byte_swab_int16(guint16 n, ExifByteOrder bo)
+{
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+	if (bo == EXIF_BYTE_ORDER_MOTOROLA)
+#else
+	if (bo == EXIF_BYTE_ORDER_INTEL)
 #endif
-
-static uint16_t swab_int16(uint16_t n, int bo)
-{
-#if BYTE_ORDER == LITTLE_ENDIAN
-	if (bo == BYTE_ORDER_MOTOROLA)
-#else
-	if (bo == BYTE_ORDER_INTEL)
-#endif
-		return n>>8 | n<<8 ;
+		return GUINT16_SWAP_LE_BE(n);
 	else
 		return n;
 }
 
-static uint32_t swab_int32(uint32_t n, int bo)
+guint32 exif_byte_swab_int32(guint32 n, ExifByteOrder bo)
 {
-#if BYTE_ORDER == LITTLE_ENDIAN
-	if (bo == BYTE_ORDER_MOTOROLA)
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+	if (bo == EXIF_BYTE_ORDER_MOTOROLA)
 #else
-	if (bo == BYTE_ORDER_INTEL)
+	if (bo == EXIF_BYTE_ORDER_INTEL)
 #endif
-		return n<<24 | n>>24 | (n & 0xFF0000)>>8 | (n & 0xFF00)<<8;
+		return GUINT32_SWAP_LE_BE(n);
 	else
 		return n;
 }
@@ -670,7 +661,7 @@
 static int get_marker_size(unsigned char *f)
 {
 	/* Size is always in Motorola byte order */
-	return get_int16(f+2, BYTE_ORDER_MOTOROLA);
+	return exif_byte_get_int16(f+2, EXIF_BYTE_ORDER_MOTOROLA);
 }
 
 static int goto_next_marker(unsigned char **f, int *size, int *marker)
@@ -713,9 +704,9 @@
  *-------------------------------------------------------------------
  */
 
-static const ExifMarker *exif_marker_from_tag(uint16_t tag, const ExifMarker *list)
+static const ExifMarker *exif_marker_from_tag(guint16 tag, const ExifMarker *list)
 {
-	int i = 0;
+	gint i = 0;
 
 	if (!list) return NULL;
 
@@ -727,18 +718,19 @@
 	return (list[i].tag == 0 ? NULL : &list[i]);
 }
 
-static void rational_from_data(ExifRational *r, void *src, int byte_order)
+static void rational_from_data(ExifRational *r, void *src, ExifByteOrder byte_order)
 {
-	r->num = swab_int32(*(uint32_t*)src, byte_order);
-	r->den = swab_int32(*(uint32_t*)(src + sizeof(uint32_t)), byte_order);
+	r->num = exif_byte_swab_int32(*(guint32*)src, byte_order);
+	r->den = exif_byte_swab_int32(*(guint32*)(src + sizeof(guint32)), byte_order);
 }
 
-void exif_item_copy_data(ExifItem *item, void *src, int len, ExifFormatType src_format, int byte_order)
+void exif_item_copy_data(ExifItem *item, void *src, guint len,
+			 ExifFormatType src_format, ExifByteOrder byte_order)
 {
-	int bs;
-	int ne;
+	gint bs;
+	gint ne;
 	gpointer dest;
-	int i;
+	gint i;
 
 	bs = ExifFormatList[item->format].size;
 	ne = item->elements;
@@ -768,7 +760,7 @@
 		case EXIF_FORMAT_SHORT:
 			for (i = 0; i < ne; i++)
 				{
-				((short *)dest)[i] = swab_int16(*(uint16_t*)(src + i * bs), byte_order);
+				((guint16 *)dest)[i] = exif_byte_swab_int16(*(guint16*)(src + i * bs), byte_order);
 				}
 			break;
 		case EXIF_FORMAT_LONG_UNSIGNED:
@@ -782,14 +774,16 @@
 				ss = ExifFormatList[src_format].size;
 				for (i = 0; i < ne; i++)
 					{
-					((long *)dest)[i] = (long)swab_int16(*(uint16_t*)(src + i * ss), byte_order);
+					((gint32 *)dest)[i] =
+						(gint32)exif_byte_swab_int16(*(guint16*)(src + i * ss), byte_order);
 					}
 				}
 			else
 				{
 				for (i = 0; i < ne; i++)
 					{
-					((long *)dest)[i] = swab_int32(*(uint32_t*)(src + i * bs), byte_order);
+					((gint32 *)dest)[i] =
+						exif_byte_swab_int32(*(guint32*)(src + i * bs), byte_order);
 					}
 				}
 			break;
@@ -803,7 +797,7 @@
 		case EXIF_FORMAT_FLOAT:
 			for (i = 0; i < ne; i++)
 				{
-				((float *)dest)[i] = swab_int32(*(uint32_t*)(src + i * bs), byte_order);
+				((float *)dest)[i] = exif_byte_swab_int32(*(guint32*)(src + i * bs), byte_order);
 				}
 			break;
 		case EXIF_FORMAT_DOUBLE:
@@ -818,21 +812,21 @@
 		}
 }
 
-static int exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, int offset,
-				int size, int byte_order,
-				const ExifMarker *list)
+static gint exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, guint offset,
+				 guint size, ExifByteOrder byte_order,
+				 const ExifMarker *list)
 {
 	IFDEntry *ent = (IFDEntry*)(tiff+offset);
-	uint32_t swabed_data;
+	guint32 swabed_data;
 	void *data;
-	int data_len;
+	guint data_len;
 	const ExifMarker *marker;
 	ExifItem *item;
 
-	ent->tag = swab_int16(ent->tag, byte_order);
-	ent->format = swab_int16(ent->format, byte_order);
-	ent->nb = swab_int32(ent->nb, byte_order);
-	swabed_data = swab_int32(ent->data, byte_order);
+	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);
 
 	/* 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.
@@ -866,12 +860,14 @@
 			if (ent->format <= EXIF_FORMAT_DOUBLE)
 				{
 				printf("warning: exif tag %s format mismatch, found %s exif spec requests %s\n",
-					marker->key, ExifFormatList[ent->format].short_name, ExifFormatList[marker->format].short_name);
+					marker->key, ExifFormatList[ent->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, ExifFormatList[marker->format].short_name);
+					marker->key, ent->format, marker->format,
+					ExifFormatList[marker->format].short_name);
 				}
 			return 0;
 			}
@@ -881,7 +877,8 @@
 	 */
 	if (marker->components > 0 && marker->components != ent->nb)
 		{
-		printf("warning: exif tag %s has %d elements, exif spec requests %d\n", marker->key, ent->nb, marker->components);
+		printf("warning: exif tag %s has %d elements, exif spec requests %d\n",
+			marker->key, ent->nb, marker->components);
 		}
 	data_len = ExifFormatList[marker->format].size * ent->nb;
 	if (data_len > sizeof(ent->data))
@@ -910,7 +907,7 @@
 				exif_parse_IFD_table(exif, tiff, swabed_data, size, byte_order, list);
 				break;
 			case TAG_EXIFMAKERNOTE:
-				format_exif_makernote_canon_parse(exif, tiff, swabed_data, size, byte_order);
+				format_exif_makernote_parse(exif, tiff, swabed_data, size, byte_order);
 				break;
 			}
 		}
@@ -918,17 +915,17 @@
 	return 0;
 }
 
-int exif_parse_IFD_table(ExifData *exif,
-			 unsigned char *tiff, int offset,
-			 int size, int byte_order,
-			 const ExifMarker *list)
+gint exif_parse_IFD_table(ExifData *exif,
+			  unsigned char *tiff, guint offset,
+			  guint size, ExifByteOrder byte_order,
+			  const ExifMarker *list)
 {
-	int i, nb_entries;
+	gint i, nb_entries;
 
 	/* We should be able to read number of entries in IFD0) */
 	if (size < offset+2) return -1;
 
-	nb_entries = get_int16(tiff+offset, byte_order);
+	nb_entries = exif_byte_get_int16(tiff+offset, byte_order);
 
 	/* Entries and next IFD offset must be readable */
 	if (size < offset+nb_entries*12+4) return -1;
@@ -947,9 +944,10 @@
  *-------------------------------------------------------------------
  */
 
-static int parse_TIFF(ExifData *exif, unsigned char *tiff, int size)
+gint exif_parse_TIFF(ExifData *exif, unsigned char *tiff, guint size, ExifMarker *list)
 {
-	int byte_order, offset=0;
+	ExifByteOrder byte_order;
+	guint offset=0;
 
 	if (size < sizeof(TIFFHeader))
 		{
@@ -958,30 +956,30 @@
 
 	if (strncmp(((TIFFHeader*)tiff)->byte_order, "II", 2) == 0)
 		{
-		byte_order = BYTE_ORDER_INTEL;
+		byte_order = EXIF_BYTE_ORDER_INTEL;
 		}
 	else if (strncmp(((TIFFHeader*)tiff)->byte_order, "MM", 2) == 0)
 		{
-		byte_order = BYTE_ORDER_MOTOROLA;
+		byte_order = EXIF_BYTE_ORDER_MOTOROLA;
 		}
 	else
 		{
 		return -1;
 		}
 
-	if (swab_int16(((TIFFHeader*)tiff)->magic, byte_order) != 0x002A)
+	if (exif_byte_swab_int16(((TIFFHeader*)tiff)->magic, byte_order) != 0x002A)
 		{
 		return -1;
 		}
 
-	offset = swab_int32(((TIFFHeader*)tiff)->IFD_offset, byte_order);
+	offset = exif_byte_swab_int32(((TIFFHeader*)tiff)->IFD_offset, byte_order);
 
-	return exif_parse_IFD_table(exif, tiff, offset, size, byte_order, ExifKnownMarkersList);
+	return exif_parse_IFD_table(exif, tiff, offset, size, byte_order, list);
 }
 
-static int parse_JPEG(ExifData *exif, unsigned char *f, int size)
+static gint exif_parse_JPEG(ExifData *exif, unsigned char *f, guint size, ExifMarker *list)
 {
-	int marker, marker_size;
+	guint marker, marker_size;
 
 	if (size<2 || *f!=0xFF || *(f+1)!=MARKER_SOI)
 		{
@@ -1007,7 +1005,7 @@
 		return -2;
 		}
 
-	return parse_TIFF(exif, f+10, marker_size-6);
+	return exif_parse_TIFF(exif, f+10, marker_size-6, list);
 }
 
 static gint map_file(const gchar *path, void **mapping, int *size)
@@ -1090,9 +1088,9 @@
 	exif = g_new0(ExifData, 1);
 	exif->items = NULL;
 
-	if ((res = parse_JPEG(exif, (unsigned char *)f, size)) == -2)
+	if ((res = exif_parse_JPEG(exif, (unsigned char *)f, size, ExifKnownMarkersList)) == -2)
 		{
-		res = parse_TIFF(exif, (unsigned char *)f, size);
+		res = exif_parse_TIFF(exif, (unsigned char *)f, size, ExifKnownMarkersList);
 		}
 
 	if (res != 0)
@@ -1101,7 +1099,7 @@
 		
 		if (format_raw_img_exif_offsets(f, size, NULL, &offset))
 			{
-			res = parse_TIFF(exif, (unsigned char*)f + offset, size - offset);
+			res = exif_parse_TIFF(exif, (unsigned char*)f + offset, size - offset, ExifKnownMarkersList);
 			}
 		}
 
@@ -1180,7 +1178,7 @@
 					val = (unsigned char)(((signed char *)data)[0]);
 					}
 
-				result = text_list_find_value(marker->list, (unsigned short)val);
+				result = text_list_find_value(marker->list, (guint)val);
 				string = g_string_append(string, result);
 				g_free(result);
 				}
--- a/src/exif.h	Sat Jun 04 08:06:47 2005 +0000
+++ b/src/exif.h	Sun Jun 05 02:48:54 2005 +0000
@@ -49,13 +49,18 @@
 	EXIF_FORMAT_DOUBLE		= 12
 } ExifFormatType;
 
+typedef enum {
+	EXIF_BYTE_ORDER_INTEL,
+	EXIF_BYTE_ORDER_MOTOROLA
+} ExifByteOrder;
+
 typedef struct _ExifFormatAttrib ExifFormatAttrib;
 struct _ExifFormatAttrib
 {
 	ExifFormatType type;
-	int size;
-	const char *short_name;
-	const char *description;
+	guint size;
+	const gchar *short_name;
+	const gchar *description;
 };
 
 /* the list of known tag data formats */
@@ -77,8 +82,8 @@
 typedef struct _ExifRational ExifRational;
 struct _ExifRational
 {
-	unsigned long int num;
-	unsigned long int den;
+	guint32 num;
+	guint32 den;
 };
 
 
@@ -89,20 +94,20 @@
 struct _ExifItem
 {
 	ExifFormatType format;
-	int tag;
+	guint tag;
 	const ExifMarker *marker;
-	int elements;
+	guint elements;
 	gpointer data;
-	int data_len;
+	guint data_len;
 };
 
 struct _ExifMarker
 {
-	int		tag;
+	guint		tag;
 	ExifFormatType	format;
-	int		components;
-	char		*key;
-	char		*description;
+	gint		components;
+	gchar		*key;
+	gchar		*description;
 	ExifTextList	*list;
 };
 
@@ -110,8 +115,8 @@
 
 struct _ExifTextList
 {
-	int value;
-	const char* description;
+	gint value;
+	const gchar* description;
 };
 
 #define EXIF_TEXT_LIST_END { -1, NULL }
@@ -120,8 +125,8 @@
 typedef struct _ExifFormattedText ExifFormattedText;
 struct _ExifFormattedText
 {
-	const char *key;
-	const char *description;
+	const gchar *key;
+	const gchar *description;
 };
 
 
@@ -195,15 +200,24 @@
 
 /* These funcs for use by makernote parsers only */
 
-ExifItem *exif_item_new(ExifFormatType format, unsigned int tag,
-			unsigned int elements, const ExifMarker *marker);
-void exif_item_copy_data(ExifItem *item, void *src, int len, ExifFormatType src_format, int byte_order);
+
+guint16 exif_byte_get_int16(unsigned char *f, ExifByteOrder bo);
+guint32 exif_byte_get_int32(unsigned char *f, ExifByteOrder bo);
+guint16 exif_byte_swab_int16(guint16 n, ExifByteOrder bo);
+guint32 exif_byte_swab_int32(guint32 n, 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);
 
 gint exif_parse_IFD_table(ExifData *exif,
-			  unsigned char *tiff, int offset,
-			  int size, int byte_order,
+			  unsigned char *tiff, guint offset,
+			  guint size, ExifByteOrder byte_order,
 			  const ExifMarker *list);
 
+gint exif_parse_TIFF(ExifData *exif, unsigned char *tiff, guint size, ExifMarker *list);
 
 
 #endif
+
--- a/src/format_canon.c	Sat Jun 04 08:06:47 2005 +0000
+++ b/src/format_canon.c	Sun Jun 05 02:48:54 2005 +0000
@@ -251,8 +251,8 @@
 }
 
 
-gint format_raw_test_canon(const void *data, const guint len,
-			   guint *image_offset, guint *exif_offset)
+gint format_canon_raw(const void *data, const guint len,
+		      guint *image_offset, guint *exif_offset)
 {
 
 
@@ -478,13 +478,6 @@
  *-----------------------------------------------------------------------------
  */
 
-typedef struct _CanonTag CanonTag;
-struct _CanonTag {
-	guint id;
-	const gchar *description;
-	ExifTextList *list;
-};
-
 static ExifTextList CanonSet1MacroMode[] = {
 	{ 1,	"macro" },
 	{ 2,	"normal" },
@@ -728,21 +721,27 @@
 	EXIF_TEXT_LIST_END
 };
 
-static CanonTag CanonCustom[] = {
-	{ 1,	"Noise reduction",		CanonCustomEnable },
-/*	{ 2,	"Shutter/Auto Exposure Button Function", CanonCustomBTNShutter }, */
-	{ 3,	"Mirror lockup",		CanonCustomEnable },
-	{ 4,	"Tv/Av and exposure level",	CanonCustomExposureLevel },
-	{ 5,	"AF assist light",		CanonCustomEnableInvert },
-	{ 6,	"Shutter speed in Av mode",	CanonCustomAVShutterSpeed },
-/*	{ 7,	"Auto-Exposure bracketting sequence/auto cancellation",	CanonCustom }, */
-	{ 8,	"Shutter sync",			CanonCustomShutterCurtainSync },
-/*	{ 9,	"AF button function",		CanonCustom }, */
-	{ 10,	"Fill flash auto reduction",	CanonCustomEnableInvert },
-/*	{ 11,	"Menu button function",		CanonCustom }, */
-/*	{ 12,	"Set button function",		CanonCustom }, */
-	{ 13,	"Sensor cleaning",		CanonCustomEnable },
-	{ 0,    NULL, NULL }
+static ExifMarker CanonCustom[] = {
+{ 1,	EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Canon.NoiseReduction", "Noise reduction",	CanonCustomEnable },
+/*{ 2,	EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncShutter",
+						"Shutter/Auto exposure button function",CanonCustomBTNShutter }, */
+{ 3,	EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.MirrorLockup", "Mirror lockup",	CanonCustomEnable },
+{ 4,	EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.TvAvExposureLevel",
+							"Tv/Av and exposure level",	CanonCustomExposureLevel },
+{ 5,	EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.AFAssistLight", "AF assist light",	CanonCustomEnableInvert },
+{ 6,	EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.AvShutterSpeed",
+							"Shutter speed in Av mode",	CanonCustomAVShutterSpeed },
+/*{ 7,	EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.AutoBracket",
+				"Auto-Exposure bracketting sequence/auto cancellation",	CanonCustom }, */
+{ 8,	EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.ShutterSync", "Shutter sync",	CanonCustomShutterCurtainSync },
+/* { 9,	EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncAF",	"AF button function",	CanonCustom }, */
+{ 10,	EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.FillFlashReduction",
+							"Fill flash auto reduction",	CanonCustomEnableInvert },
+/*{ 11,	EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncMenu",
+							"Menu button function",		CanonCustom }, */
+/*{ 12,	EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncSet", "Set button function",	CanonCustom }, */
+{ 13,	EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.SensorCleaning", "Sensor cleaning",	CanonCustomEnable },
+EXIF_MARKER_LIST_END
 };
 
 #endif
@@ -760,7 +759,7 @@
 };
 
 static void canon_mknote_parse_settings(ExifData *exif,
-					guint16 *data, guint32 len, int byte_order,
+					guint16 *data, guint32 len, ExifByteOrder byte_order,
 					ExifMarker *list)
 {
 	gint i;
@@ -825,19 +824,12 @@
 }
 #endif
 
-gint format_exif_makernote_canon_parse(ExifData *exif, unsigned char *tiff, int offset,
-				       int size, int byte_order)
+gint format_canon_makernote(ExifData *exif, unsigned char *tiff, guint offset,
+			    guint size, ExifByteOrder byte_order)
 {
 	ExifItem *item;
-	gchar *text;
-	gint found;
 
-	text = exif_get_data_as_text(exif, "Make");
-	found = (text && strncasecmp(text, "Canon", 5) == 0);
-	g_free(text);
-
-	if (!found ||
-	    exif_parse_IFD_table(exif, tiff, offset, size, byte_order, CanonExifMarkersList) != 0)
+	if (exif_parse_IFD_table(exif, tiff, offset, size, byte_order, CanonExifMarkersList) != 0)
 		{
 		return FALSE;
 		}
--- a/src/format_canon.h	Sat Jun 04 08:06:47 2005 +0000
+++ b/src/format_canon.h	Sun Jun 05 02:48:54 2005 +0000
@@ -15,23 +15,25 @@
  *
  */
 
-#ifndef __FORMAT_RAW_CANON_H
-#define __FORMAT_RAW_CANON_H
+#ifndef __FORMAT_CANON_H
+#define __FORMAT_CANON_H
 
 
 #include "exif.h"
 
 
-gint format_raw_test_canon(const void *data, const guint len,
-			   guint *image_offset, guint *exif_offset);
+gint format_canon_raw(const void *data, const guint len,
+		      guint *image_offset, guint *exif_offset);
 
 
-#define FORMAT_RAW_CANON { "II", 2, "Canon crw format", format_raw_test_canon }, \
-			 { "\x49\x49\x2a\00", 4, "Canon cr2 format", format_raw_test_canon }
+#define FORMAT_RAW_CANON { "II", 2, "Canon crw format", format_canon_raw }, \
+			 { "\x49\x49\x2a\00", 4, "Canon cr2 format", format_canon_raw }
 
 
-gint format_exif_makernote_canon_parse(ExifData *exif, unsigned char *tiff, int offset,
-				       int size, int byte_order);
+gint format_canon_makernote(ExifData *exif, unsigned char *tiff, guint offset,
+			    guint size, ExifByteOrder byte_order);
+
+#define FORMAT_EXIF_CANON { FORMAT_EXIF_MATCH_MAKE, "Canon", 5, format_canon_makernote }
 
 
 #endif
--- a/src/format_fuji.c	Sat Jun 04 08:06:47 2005 +0000
+++ b/src/format_fuji.c	Sun Jun 05 02:48:54 2005 +0000
@@ -26,9 +26,11 @@
 #include "format_fuji.h"
 #include "format_raw.h"
 
+#include "exif.h"
 
-gint format_raw_test_fuji(const void *data, const guint len,
-			  guint *image_offset, guint *exif_offset)
+
+gint format_fuji_raw(const void *data, const guint len,
+		     guint *image_offset, guint *exif_offset)
 {
 	guint io;
 	guint eo;
@@ -57,3 +59,130 @@
 }
 
 
+/*
+ *-----------------------------------------------------------------------------
+ * EXIF Makernote for Fujifilm
+ *-----------------------------------------------------------------------------
+ */
+
+static ExifTextList FujiTagSharpness[] = {
+	{ 1,	"soft" },
+	{ 2,	"soft" },
+	{ 3,	"normal" },
+	{ 4,	"hard" },
+	{ 5,	"hard" },
+	EXIF_TEXT_LIST_END
+};
+
+static ExifTextList FujiTagWhiteBalance[]= {
+	{ 0,	"auto" },
+	{ 256,	"daylight" },
+	{ 512,	"cloudy" },
+	{ 768,	"daylight color-fluorescence" },
+	{ 769,	"daywhite color-fluorescence" },
+	{ 770,	"white-fluorescence" },
+	{ 1024,	"incandescent" },
+	{ 3840,	"custom" },
+	EXIF_TEXT_LIST_END
+};
+
+static ExifTextList FujiTagColorTone[]= {
+	{ 0,	"normal" },
+	{ 256,	"high" },
+	{ 512,	"low" },
+	EXIF_TEXT_LIST_END
+};
+
+static ExifTextList FujiTagFlashMode[]= {
+	{ 0,	"auto" },
+	{ 1,	"on" },
+	{ 2,	"off" },
+	{ 3,	"red-eye reduction" },
+	EXIF_TEXT_LIST_END
+};
+
+static ExifTextList FujiTagOffOn[]= {
+	{ 0,	"off" },
+	{ 1,	"on" },
+	EXIF_TEXT_LIST_END
+};
+
+static ExifTextList FujiTagFocusMode[]= {
+	{ 0,	"auto" },
+	{ 1,	"manual" },
+	EXIF_TEXT_LIST_END
+};
+
+static ExifTextList FujiTagPictureMode[]= {
+	{ 0,	"auto" },
+	{ 1,	"portrait" },
+	{ 2,	"landscape" },
+	{ 4,	"sports" },
+	{ 5,	"night" },
+	{ 6,	"program AE" },
+	{ 256,	"aperture priority AE" },
+	{ 512,	"shutter priority AE" },
+	{ 768,	"manual" },
+	EXIF_TEXT_LIST_END
+};
+
+static ExifTextList FujiTagNoYes[]= {
+	{ 0,	"no" },
+	{ 1,	"yes" },
+	EXIF_TEXT_LIST_END
+};
+
+#if 0
+static ExifTextList FujiTag[]= {
+	{ ,	"" },
+	{ ,	"" },
+	EXIF_TEXT_LIST_END
+};
+#endif
+
+
+static ExifMarker FujiExifMarkersList[] = {
+{ 0x1000,	EXIF_FORMAT_STRING, 8,		"MkN.Fuji.Quality",	"Quality",	NULL },
+{ 0x1001,	EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Fuji.Sharpness",	"Sharpness",	FujiTagSharpness },
+{ 0x1002,	EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Fuji.WhiteBalance","White balance",FujiTagWhiteBalance },
+{ 0x1003,	EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Fuji.Color",	"Color",	FujiTagColorTone },
+{ 0x1004,	EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Fuji.Tone",	"Tone",		FujiTagColorTone },
+{ 0x1010,	EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Fuji.FlashMode",	"Flash mode",	FujiTagFlashMode },
+{ 0x1011,	EXIF_FORMAT_RATIONAL, 1,	"MkN.Fuji.FlashStrength", "Flash strength", NULL },
+{ 0x1020,	EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Fuji.Macro",	"Macro",	FujiTagOffOn },
+{ 0x1021,	EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Fuji.FocusMode",	"Focus mode",	FujiTagFocusMode },
+{ 0x1030,	EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Fuji.SlowSync",	"Slow synchro",	FujiTagOffOn },
+{ 0x1031,	EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Fuji.PictureMode",	"Picture mode",	FujiTagPictureMode },
+{ 0x1100,	EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Fuji.ContTake/Bracket",
+							"Continuous / Auto bracket",	FujiTagOffOn },
+{ 0x1300,	EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Fuji.BlurWarning",	"Blue warning",	FujiTagNoYes },
+{ 0x1301,	EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Fuji.FocusWarning","Focus warning",FujiTagNoYes },
+{ 0x1302,	EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Fuji.AEWarning",	"AE warning",	FujiTagNoYes },
+EXIF_MARKER_LIST_END
+};
+
+
+
+gint format_fuji_makernote(ExifData *exif, unsigned char *tiff, guint offset,
+			   guint size, ExifByteOrder byte_order)
+{
+	unsigned char *data;
+	guint ifdstart;
+
+	if (offset + 8 + 4 >= size) return FALSE;
+
+	data = tiff + offset;
+	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)
+		{
+		return FALSE;
+		}
+
+	return TRUE;
+}
+
--- a/src/format_fuji.h	Sat Jun 04 08:06:47 2005 +0000
+++ b/src/format_fuji.h	Sun Jun 05 02:48:54 2005 +0000
@@ -10,15 +10,25 @@
  * This software comes with no warranty of any kind, use at your own risk!
  */
 
-#ifndef __FORMAT_RAW_FUJI_H
-#define __FORMAT_RAW_FUJI_H
+#ifndef __FORMAT_FUJI_H
+#define __FORMAT_FUJI_H
+
+
+#include "exif.h"
 
 
-gint format_raw_test_fuji(const void *data, const guint len,
-			  guint *image_offset, guint *exif_offset);
+gint format_fuji_raw(const void *data, const guint len,
+		     guint *image_offset, guint *exif_offset);
 
 
-#define FORMAT_RAW_FUJI { "FUJIFILM", 8, "Fuji raw format", format_raw_test_fuji }
+#define FORMAT_RAW_FUJI { "FUJIFILM", 8, "Fuji raw format", format_fuji_raw }
+
+
+gint format_fuji_makernote(ExifData *exif, unsigned char *tiff, guint offset,
+			   guint size, ExifByteOrder byte_order);
+
+#define FORMAT_EXIF_FUJI { FORMAT_EXIF_MATCH_MAKERNOTE, "FUJIFILM", 8, format_fuji_makernote }
+
 
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/format_nikon.c	Sun Jun 05 02:48:54 2005 +0000
@@ -0,0 +1,248 @@
+/*
+ *  GQView
+ *  (C) 2005 John Ellis
+ *
+ * This software is released under the GNU General Public License (GNU GPL).
+ * Please read the included file COPYING for more information.
+ * This software comes with no warranty of any kind, use at your own risk!
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "intl.h"
+
+#include "format_nikon.h"
+
+#include "exif.h"
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * EXIF Makernote for Nikon
+ *-----------------------------------------------------------------------------
+ */
+
+static ExifTextList NikonTagQuality[]= {
+	{ 1,	"VGA basic" },
+	{ 2,	"VGA normal" },
+	{ 3,	"VGA fine" },
+	{ 4,	"SXGA basic" },
+	{ 5,	"SXGA normal" },
+	{ 6,	"SXGA fine" },
+	{ 7,	"XGA basic (?)" },
+	{ 8,	"XGA normal (?)" },
+	{ 9,	"XGA fine (?)" },
+	{ 10,	"UXGA basic" },
+	{ 11,	"UXGA normal" },
+	{ 12,	"UXGA fine" },
+	EXIF_TEXT_LIST_END
+};
+
+static ExifTextList NikonTagColorMode[]= {
+	{ 1,	"color" },
+	{ 2,	"monochrome" },
+	EXIF_TEXT_LIST_END
+};
+
+static ExifTextList NikonTagImgAdjust[]= {
+	{ 0,	"normal" },
+	{ 1,	"bright+" },
+	{ 2,	"bright-" },
+	{ 3,	"contrast+" },
+	{ 4,	"contrast-" },
+	EXIF_TEXT_LIST_END
+};
+
+static ExifTextList NikonTagISOSensitivity[]= {
+	{ 0,	"80" },
+	{ 2,	"160" },
+	{ 4,	"320" },
+	{ 5,	"100" },
+	EXIF_TEXT_LIST_END
+};
+
+static ExifTextList NikonTagWhiteBalance[]= {
+	{ 0,	"auto" },
+	{ 1,	"preset" },
+	{ 2,	"daylight" },
+	{ 3,	"incandescent" },
+	{ 4,	"fluorescence" },
+	{ 5,	"cloudy" },
+	{ 6,	"speedlight" },
+	EXIF_TEXT_LIST_END
+};
+
+static ExifTextList NikonTagConverter[]= {
+	{ 0,	"none" },
+	{ 1,	"Fisheye" },
+	EXIF_TEXT_LIST_END
+};
+
+#if 0
+static ExifTextList NikonTag[]= {
+	{ ,	"" },
+	{ ,	"" },
+	EXIF_TEXT_LIST_END
+};
+#endif
+
+static ExifMarker NikonExifMarkersList1[] = {
+{ 0x0002, EXIF_FORMAT_STRING, 6,		"MkN.Nikon.unknown",	NULL,		NULL },
+{ 0x0003, EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Nikon.Quality",	"Quality",	NikonTagQuality },
+{ 0x0004, EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Nikon.ColorMode",	"Color mode",	NikonTagColorMode },
+{ 0x0005, EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Nikon.ImageAdjustment",
+								"Image adjustment",	NikonTagImgAdjust },
+{ 0x0006, EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Nikon.ISOSensitivity",
+								"ISO sensitivity",	NikonTagISOSensitivity },
+{ 0x0007, EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Nikon.WhiteBalance",
+								"White balance",	NikonTagWhiteBalance },
+{ 0x0008, EXIF_FORMAT_RATIONAL_UNSIGNED, 1,	"MkN.Nikon.Focus",	"Focus",	NULL },
+{ 0x000a, EXIF_FORMAT_RATIONAL_UNSIGNED, 1,	"MkN.Nikon.DigitalZoom","Digital zoom", NULL },
+{ 0x000b, EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Nikon.Converter",	"Converter",	NikonTagConverter },
+EXIF_MARKER_LIST_END
+};
+
+static ExifTextList NikonTag2FlashComp[]= {
+	{ 0x06,	"+1.0 EV" },
+	{ 0x04,	"+0.7 EV" },
+	{ 0x03,	"+0.5 EV" },
+	{ 0x02,	"+0.3 EV" },
+	{ 0x00,	"0.0 EV" },
+	{ 0xfe,	"-0.3 EV" },
+	{ 0xfd,	"-0.5 EV" },
+	{ 0xfc,	"-0.7 EV" },
+	{ 0xfa,	"-1.0 EV" },
+	{ 0xf8,	"-1.3 EV" },
+	{ 0xf7,	"-1.5 EV" },
+	{ 0xf6,	"-1.7 EV" },
+	{ 0xf4,	"-2.0 EV" },
+	{ 0xf2,	"-2.3 EV" },
+	{ 0xf1,	"-2.5 EV" },
+	{ 0xf0,	"-2.7 EV" },
+	{ 0xee,	"-3.0 EV" },
+	EXIF_TEXT_LIST_END
+};
+
+static ExifTextList NikonTag2FlashUsed[]= {
+	{ 0,	"no" },
+	{ 9,	"yes" },
+	EXIF_TEXT_LIST_END
+};
+
+#if 0
+static ExifTextList NikonTagi2Saturation[]= {
+	{ -3,	"black and white" },
+	{ -2,	"-2" },
+	{ -1,	"-1" },
+	{ 0,	"normal" },
+	{ 1,	"+1" },
+	{ 2,	"+2" },
+	EXIF_TEXT_LIST_END
+};
+#endif
+
+static ExifMarker NikonExifMarkersList2[] = {
+{ 0x0002, EXIF_FORMAT_SHORT_UNSIGNED, 2,	"MkN.Nikon.ISOSpeed",	"ISO speed",	NULL },
+{ 0x0003, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.ColorMode",	"Color mode",	NULL },
+{ 0x0004, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.Quality",	"Quality",	NULL },
+{ 0x0005, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.WhiteBalance",
+								"White balance",	NULL },
+{ 0x0006, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.Sharpening",	"Sharpening",	NULL },
+{ 0x0007, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.FocusMode",	"Focus mode",	NULL },
+{ 0x0008, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.FlashSetting",
+								"Flash setting",	NULL },
+{ 0x0009, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.AutoFlashMode","Auto flash mode",NULL },
+{ 0x000b, EXIF_FORMAT_SHORT, 1,			"MkN.Nikon.WhiteBalanceBias",
+							"White balance bias value",	NULL },
+/* { 0x000c, EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Nikon.WhiteBalanceCoeff",
+						"White balance red/blue coefficents",	NULL }, */
+/* { 0x000f, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.ISOSelect",	"ISO selection",NULL }, */
+{ 0x0012, EXIF_FORMAT_UNDEFINED, 4,		"MkN.Nikon.FlashCompensation",
+								"Flash compensation",	NikonTag2FlashComp },
+{ 0x0013, EXIF_FORMAT_SHORT_UNSIGNED, 2,	"MkN.Nikon.ISOSpeedRequest",
+								"ISO speed requested",	NULL },
+{ 0x0016, EXIF_FORMAT_SHORT_UNSIGNED, 4,	"MkN.Nikon.CornerCoord",
+								"Corner coordinates",	NULL },
+{ 0x0018, EXIF_FORMAT_UNDEFINED, 4,		"MkN.Nikon.FlashBracketCompensation",
+							"Flash bracket compensation",	NikonTag2FlashComp },
+{ 0x0019, EXIF_FORMAT_RATIONAL, 1,		"MkN.Nikon.AEBracketCompensation",
+							"AE bracket compensation",	NULL },
+{ 0x0080, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.ImageAdjustment",
+								"Image adjustment",	NULL },
+{ 0x0081, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.Contrast",	"Contrast",	NULL },
+{ 0x0082, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.AuxLens","Aux lens adapter", NULL },
+{ 0x0083, EXIF_FORMAT_BYTE_UNSIGNED, -1,	"MkN.Nikon.LensType",	"Lens type",	NULL },
+{ 0x0084, EXIF_FORMAT_RATIONAL_UNSIGNED, -1,	"MkN.Nikon.LensFocalLength",
+							"Lens min/max focal length and aperture", NULL },
+{ 0x0085, EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Nikon.ManualFocusDistance",
+							"Manual focus distance", 	NULL },
+{ 0x0086, EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Nikon.DigitalZoomFactor",
+							"Digital zoom facotr",		NULL },
+{ 0x0087, EXIF_FORMAT_BYTE_UNSIGNED, 1,		"MkN.Nikon.FlashUsed",	"Flash used",	NikonTag2FlashUsed },
+{ 0x0088, EXIF_FORMAT_UNDEFINED, -1,		"MkN.Nikon.AutoFocusArea", NULL,	NULL },
+/* { 0x0089, EXIF_FORMAT_SHORT_UNSIGNED, -1,	"MkN.Nikon.Bracket/ShootingMode", NULL,	NULL }, */
+{ 0x008d, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.ColorMode",	"Color mode",	NULL },
+{ 0x008f, EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Nikon.SceneMode",	NULL,		NULL },
+{ 0x0090, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.LightingType", "Lighting type", NULL },
+{ 0x0092, EXIF_FORMAT_SHORT, 1,			"MkN.Nikon.HueAdjust",	"Hue adjustment", NULL },
+/* { 0x0094, EXIF_FORMAT_SHORT_UNSIGNED, 1,	"MkN.Nikon.Saturation",	"Saturation",	NikonTag2Saturation }, */
+{ 0x0095, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.NoiseReduction", "Noise reduction", NULL },
+{ 0x00a7, EXIF_FORMAT_LONG_UNSIGNED, 1,		"MkN.Nikon.ShutterCount", "Shutter release count", NULL },
+{ 0x00a9, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.ImageOptimization", "Image optimization", NULL },
+{ 0x00aa, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.Saturation", "Saturation",	NULL },
+{ 0x00ab, EXIF_FORMAT_STRING, -1,		"MkN.Nikon.DigitalVariProg", "Digital Vari-program", NULL },
+EXIF_MARKER_LIST_END
+};
+
+
+gint format_nikon_makernote(ExifData *exif, unsigned char *tiff, guint offset,
+			    guint size, ExifByteOrder byte_order)
+{
+	unsigned char *data;
+
+	if (offset + 8 + 4 >= size) return FALSE;
+
+	data = tiff + offset;
+	if (memcmp(data, "Nikon\x00\x01\x00", 8) == 0)
+		{
+		if (exif_parse_IFD_table(exif, tiff, offset + 8, size,
+					 byte_order, NikonExifMarkersList1) != 0)
+			{
+			return FALSE;
+			}
+		return TRUE;
+		}
+
+	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,
+		    NikonExifMarkersList2) != 0)
+			{
+			return FALSE;
+			}
+		return TRUE;
+		}
+
+	/* fixme: support E990 and D1 */
+	if (exif_parse_IFD_table(exif, tiff, offset, size,
+				 byte_order, NikonExifMarkersList2) != 0)
+		{
+		return FALSE;
+		}
+
+	return FALSE;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/format_nikon.h	Sun Jun 05 02:48:54 2005 +0000
@@ -0,0 +1,25 @@
+/*
+ *  GQView
+ *  (C) 2005 John Ellis
+ *
+ * This software is released under the GNU General Public License (GNU GPL).
+ * Please read the included file COPYING for more information.
+ * This software comes with no warranty of any kind, use at your own risk!
+ */
+
+#ifndef __FORMAT_NIKON_H
+#define __FORMAT_NIKON_H
+
+
+#include "exif.h"
+
+
+gint format_nikon_makernote(ExifData *exif, unsigned char *tiff, guint offset,
+			    guint size, ExifByteOrder byte_order);
+
+#define FORMAT_EXIF_NIKON { FORMAT_EXIF_MATCH_MAKERNOTE, "Nikon\x00", 6, format_nikon_makernote }, \
+			  { FORMAT_EXIF_MATCH_MAKE,      "NIKON",     5, format_nikon_makernote }
+
+
+#endif
+
--- a/src/format_raw.c	Sat Jun 04 08:06:47 2005 +0000
+++ b/src/format_raw.c	Sun Jun 05 02:48:54 2005 +0000
@@ -30,35 +30,51 @@
 
 #include "format_canon.h"
 #include "format_fuji.h"
+#include "format_nikon.h"
 
 
-typedef struct _FormatEntry FormatEntry;
-struct _FormatEntry {
+typedef struct _FormatRawEntry FormatRawEntry;
+struct _FormatRawEntry {
 	const void *header_pattern;
 	const guint header_length;
 	const gchar *description;
 	FormatRawParseFunc func_parse;
 };
 
-
-static FormatEntry format_list[] = {
+static FormatRawEntry format_raw_list[] = {
 	FORMAT_RAW_CANON,
 	FORMAT_RAW_FUJI,
 	{ NULL, 0, NULL, NULL }
 };
 
 
-static FormatEntry *format_raw_find(const void *data, const guint len)
+typedef struct _FormatExifEntry FormatExifEntry;
+struct _FormatExifEntry {
+	FormatExifMatchType header_type;
+	const void *header_pattern;
+	const guint header_length;
+	FormatExifParseFunc func_parse;
+};
+
+static FormatExifEntry format_exif_list[] = {
+	FORMAT_EXIF_CANON,
+	FORMAT_EXIF_FUJI,
+	FORMAT_EXIF_NIKON,
+	{ 0, NULL, 0, NULL }
+};
+
+
+static FormatRawEntry *format_raw_find(const void *data, const guint len)
 {
 	gint n;
 
 	n = 0;
-	while (format_list[n].header_pattern)
+	while (format_raw_list[n].header_pattern)
 		{
-		if (format_list[n].header_length <= len &&
-		    memcmp(data, format_list[n].header_pattern, format_list[n].header_length) == 0)
+		if (format_raw_list[n].header_length <= len &&
+		    memcmp(data, format_raw_list[n].header_pattern, format_raw_list[n].header_length) == 0)
 			{
-			return &format_list[n];
+			return &format_raw_list[n];
 			}
 		n++;
 		}
@@ -66,7 +82,7 @@
 	return NULL;
 }
 
-static gint format_raw_parse(FormatEntry *entry,
+static gint format_raw_parse(FormatRawEntry *entry,
 			     const void *data, const guint len,
 			     guint *image_offset, guint *exif_offset)
 {
@@ -94,7 +110,7 @@
 gint format_raw_img_exif_offsets(const void *data, const guint len,
 				 guint *image_offset, guint *exif_offset)
 {
-	FormatEntry *entry;
+	FormatRawEntry *entry;
 
 	if (!data || len < 1) return FALSE;
 
@@ -109,7 +125,7 @@
 gint format_raw_img_exif_offsets_fd(int fd, const void *header_data, const guint header_len,
 				    guint *image_offset, guint *exif_offset)
 {
-	FormatEntry *entry;
+	FormatRawEntry *entry;
 	void *map_data = NULL;
 	size_t map_len = 0;
 	struct stat st;
@@ -157,3 +173,53 @@
 }
 
 
+static FormatExifEntry *format_exif_makernote_find(ExifData *exif, unsigned char *tiff,
+						   guint offset, guint size)
+{
+	ExifItem *make;
+	gint n;
+
+	make = exif_get_item(exif, "Make");
+
+	n = 0;
+	while (format_exif_list[n].header_pattern)
+		{
+		switch (format_exif_list[n].header_type)
+			{
+			case FORMAT_EXIF_MATCH_MAKERNOTE:
+				if (format_exif_list[n].header_length + offset < size &&
+				    memcmp(tiff + offset, format_exif_list[n].header_pattern,
+							  format_exif_list[n].header_length) == 0)
+					{
+					return &format_exif_list[n];
+					}
+				break;
+			case FORMAT_EXIF_MATCH_MAKE:
+				if (make &&
+				    make->data_len >= format_exif_list[n].header_length &&
+				    memcmp(make->data, format_exif_list[n].header_pattern,
+						       format_exif_list[n].header_length) == 0)
+					{
+					return &format_exif_list[n];
+					}
+				break;
+			}
+		n++;
+		}
+
+	return FALSE;
+}
+
+gint format_exif_makernote_parse(ExifData *exif, unsigned char *tiff, guint offset,
+				 guint size, ExifByteOrder byte_order)
+{
+	FormatExifEntry *entry;
+
+	entry = format_exif_makernote_find(exif, tiff, offset, size);
+
+	if (!entry || !entry->func_parse) return FALSE;
+
+	return entry->func_parse(exif, tiff, offset, size, byte_order);
+}
+
+
--- a/src/format_raw.h	Sat Jun 04 08:06:47 2005 +0000
+++ b/src/format_raw.h	Sun Jun 05 02:48:54 2005 +0000
@@ -14,15 +14,29 @@
 #define __FORMAT_RAW_H
 
 
+#include "exif.h"
+
+
 typedef gint (* FormatRawParseFunc)(const void *data, const guint len,
 				    guint *image_offset, guint *exif_offset);
 
-
 gint format_raw_img_exif_offsets(const void *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,
 				    guint *image_offset, guint *exif_offset);
 
 
+typedef enum {
+	FORMAT_EXIF_MATCH_MAKE,
+	FORMAT_EXIF_MATCH_MAKERNOTE
+} FormatExifMatchType;
+
+typedef gint (* FormatExifParseFunc)(ExifData *exif, unsigned char *tiff, guint offset,
+				    guint size, ExifByteOrder byte_order);
+
+gint format_exif_makernote_parse(ExifData *exif, unsigned char *tiff, guint offset,
+				 guint size, ExifByteOrder byte_order);
+
+
 #endif