Thu Jun 9 22:23:18 2005 John Ellis <johne@verizon.net>
authorJohn Ellis <johne@verizon.net>
Fri, 10 Jun 2005 02:44:36 +0000 (02:44 +0000)
committerJohn Ellis <johne@verizon.net>
Fri, 10 Jun 2005 02:44:36 +0000 (02:44 +0000)
        * 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.

13 files changed:
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

index a1ef439..94a175e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
diff --git a/TODO b/TODO
index 5e9d934..b2f726d 100644 (file)
--- a/TODO
+++ b/TODO
@@ -22,17 +22,16 @@ Major:
 
  >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
index 9eef4e9..4cc3e57 100644 (file)
@@ -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 @@ ExifFormattedText ExifFormattedList[] = {
 };
 
 
-/*
- *-----------------------------------------------------------------------------
- * 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);
 
 /*
@@ -652,52 +624,6 @@ guint32 exif_byte_swab_int32(guint32 n, ExifByteOrder bo)
                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
@@ -718,14 +644,17 @@ static const ExifMarker *exif_marker_from_tag(guint16 tag, const ExifMarker *lis
        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 @@ void exif_item_copy_data(ExifItem *item, void *src, guint len,
        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 @@ void exif_item_copy_data(ExifItem *item, void *src, guint len,
                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 @@ void exif_item_copy_data(ExifItem *item, void *src, guint len,
                            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 @@ void exif_item_copy_data(ExifItem *item, void *src, guint len,
                                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 @@ void exif_item_copy_data(ExifItem *item, void *src, guint len,
                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 @@ void exif_item_copy_data(ExifItem *item, void *src, guint len,
                                {
                                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 @@ void exif_item_copy_data(ExifItem *item, void *src, guint len,
 }
 
 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 @@ static gint exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, guint offs
                 * 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 @@ static gint exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, guint offs
 
        /* 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 @@ static gint exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, guint offs
                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 @@ static gint exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, guint offs
 
 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_IFD_table(ExifData *exif,
  *-------------------------------------------------------------------
  */
 
-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;
                        }
@@ -998,16 +995,22 @@ static gint exif_parse_JPEG(ExifData *exif, unsigned char *f, guint size, ExifMa
                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 @@ ExifData *exif_read(const gchar *path)
 
        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 @@ ExifData *exif_read(const gchar *path)
                
                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);
                        }
                }
 
index f11ea10..3f313b6 100644 (file)
@@ -33,6 +33,8 @@
  *-----------------------------------------------------------------------------
  */
 
+#define EXIF_FORMAT_COUNT 13
+
 typedef enum {
        EXIF_FORMAT_UNKNOWN             = 0,
        EXIF_FORMAT_BYTE_UNSIGNED       = 1,
@@ -198,7 +200,15 @@ void exif_write_data_list(ExifData *exif, FILE *f, gint human_readable_list);
 
 
 
-/* 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 @@ 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);
+                        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
index 4193106..e4ac637 100644 (file)
@@ -258,7 +258,7 @@ return_only:
 }
 
 
-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 CanonSet1MacroMode[] = {
 static ExifTextList CanonSet1Quality[] = {
        { 2,    "normal" },
        { 3,    "fine" },
+       { 4,    "raw" },
        { 5,    "superfine" },
        EXIF_TEXT_LIST_END
 };
@@ -502,24 +503,24 @@ static ExifTextList CanonSet1FlashMode[] = {
        { 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 @@ static ExifTextList CanonSet1DigitalZoom[] = {
        { 0,    "none" },
        { 1,    "2x" },
        { 2,    "4x" },
+       { 3,    "other" },
        EXIF_TEXT_LIST_END
 };
 
@@ -577,6 +579,8 @@ static ExifTextList CanonSet1ISOSpeed[] = {
 };
 
 static ExifTextList CanonSet1MeteringMode[] = {
+       { 0,    "default" },
+       { 1,    "spot" },
        { 3,    "evaluative" },
        { 4,    "partial" },
        { 5,    "center-weighted" },
@@ -586,17 +590,21 @@ static ExifTextList CanonSet1MeteringMode[] = {
 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 @@ EXIF_MARKER_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
 };
 
@@ -766,7 +779,7 @@ static ExifMarker CanonExifMarkersList[] = {
 };
 
 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 @@ static void canon_mknote_parse_settings(ExifData *exif,
                        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 @@ static void canon_mknote_parse_convert(ExifData *exif)
 #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 @@ gint format_canon_makernote(ExifData *exif, unsigned char *tiff, guint offset,
        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
index faaefc9..d9da01c 100644 (file)
 #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
 
+
index 5545d99..53e9fe4 100644 (file)
@@ -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 @@ EXIF_MARKER_LIST_END
 
 
 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 @@ gint format_fuji_makernote(ExifData *exif, unsigned char *tiff, guint offset,
        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;
                }
index c643f3e..29ff565 100644 (file)
 #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 }
 
index 8cc2632..f5fcce6 100644 (file)
  *-----------------------------------------------------------------------------
  */
 
-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 @@ static void tiff_entry(unsigned char *data, const guint len, guint offset, ExifB
                        {
                        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 @@ static guint tiff_table(unsigned char *data, const guint len, guint offset, Exif
                *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)
@@ -391,30 +337,33 @@ EXIF_MARKER_LIST_END
 
 
 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 @@ gint format_nikon_makernote(ExifData *exif, unsigned char *tiff, guint offset,
                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;
                }
index 4d543d4..c089cf6 100644 (file)
 
 #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 }
index 37ae03c..150f88c 100644 (file)
@@ -39,8 +39,11 @@ extern gint debug;
 
 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 @@ static FormatRawEntry format_raw_list[] = {
        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 FormatExifEntry format_exif_list[] = {
 };
 
 
-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 FormatRawEntry *format_raw_find(const void *data, const guint len)
 }
 
 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 @@ static gint format_raw_parse(FormatRawEntry *entry,
        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(const void *data, const guint len,
 }
 
 
-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 @@ gint format_raw_img_exif_offsets_fd(int fd, const void *header_data, const guint
 
        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 @@ static FormatExifEntry *format_exif_makernote_find(ExifData *exif, unsigned char
 }
 
 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 @@ gint format_exif_makernote_parse(ExifData *exif, unsigned char *tiff, guint offs
 
        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);
 }
 
 
index 5c6c963..8531e01 100644 (file)
 #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 @@ typedef enum {
 } 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
index df9c03f..86d098d 100644 (file)
@@ -218,7 +218,7 @@ static gint image_loader_begin(ImageLoader *il)
        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);