Thu May 26 13:57:19 2005 John Ellis <johne@verizon.net>
authorJohn Ellis <johne@verizon.net>
Thu, 26 May 2005 18:10:52 +0000 (18:10 +0000)
committerJohn Ellis <johne@verizon.net>
Thu, 26 May 2005 18:10:52 +0000 (18:10 +0000)
        * format_raw.[ch]: Move camera specific code to manufacturer specific
        format_*.c files. Change code so that file descripter version is now a
        separate functions that wraps the standard parser by using mmap.
        * format_canon.[ch]: Moved Canon specific raw support here, removed
        file descriptor versions of parser. This Canon raw file parser written
        by Daniel M. German.
        * format_fuji.[ch]: Move Fuji specific raw support here, parser written
        by Lars Ellenberg.
        * exif.c: Update for change to format_raw_img_exif_offsets.
        * filelist.c: Add cr2 extension to Canon raw format list.
        * image-load.c: Fixes for changes to format_raw_img_exif_offset_fd so
        that buffer is refilled using new offset of file descriptor.
        * src/Makefile.am: Add format_canon.[ch], format_fuji.[ch] to build.

##### Note: GQview CVS on sourceforge is not always up to date, please use #####
##### an offical release when making enhancements and translation updates. #####

12 files changed:
ChangeLog
THIS_CVS_IS_NOT_UP_TO_DATE [deleted file]
src/Makefile.am
src/exif.c
src/filelist.c
src/format_canon.c [new file with mode: 0644]
src/format_canon.h [new file with mode: 0644]
src/format_fuji.c [new file with mode: 0644]
src/format_fuji.h [new file with mode: 0644]
src/format_raw.c
src/format_raw.h
src/image-load.c

index 4efde9d..0e4d3d0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+Thu May 26 13:57:19 2005  John Ellis  <johne@verizon.net>
+
+       * format_raw.[ch]: Move camera specific code to manufacturer specific
+       format_*.c files. Change code so that file descripter version is now a
+       separate functions that wraps the standard parser by using mmap.
+       * format_canon.[ch]: Moved Canon specific raw support here, removed
+       file descriptor versions of parser. This Canon raw file parser written
+       by Daniel M. German.
+       * format_fuji.[ch]: Move Fuji specific raw support here, parser written
+       by Lars Ellenberg.
+       * exif.c: Update for change to format_raw_img_exif_offsets.
+       * filelist.c: Add cr2 extension to Canon raw format list.
+       * image-load.c: Fixes for changes to format_raw_img_exif_offset_fd so
+       that buffer is refilled using new offset of file descriptor.
+       * src/Makefile.am: Add format_canon.[ch], format_fuji.[ch] to build.
+
 Wed May 18 19:36:49 2005  John Ellis  <johne@verizon.net>
 
        * utilops.[ch] (file_util_rename_dir): New utility to rename a folder,
diff --git a/THIS_CVS_IS_NOT_UP_TO_DATE b/THIS_CVS_IS_NOT_UP_TO_DATE
deleted file mode 100644 (file)
index a6f504e..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-The CVS on sourceforge is far behind the latest version,
-this CVS is not used for GQview development.
-
-There is no public CVS!
-
-(This CVS was set up as a test, and is here in the event GQview development CVS does go public)
index 54e03d4..a1722f4 100644 (file)
@@ -84,6 +84,10 @@ gqview_SOURCES = \
        exif.h          \
        filelist.c      \
        filelist.h      \
+       format_canon.c  \
+       format_canon.h  \
+       format_fuji.c   \
+       format_fuji.h   \
        format_raw.c    \
        format_raw.h    \
        fullscreen.c    \
index 4642127..ff229f4 100644 (file)
@@ -1088,7 +1088,7 @@ ExifData *exif_read(const gchar *path)
                {
                guint32 offset = 0;
                
-               if (format_raw_img_exif_offsets(-1, f, size, NULL, &offset))
+               if (format_raw_img_exif_offsets(f, size, NULL, &offset))
                        {
                        res = parse_TIFF(exif, (unsigned char*)f + offset, size - offset);
                        }
index 714a2f5..762cd9a 100644 (file)
@@ -211,7 +211,8 @@ void filter_add_defaults(void)
        /* These are the raw camera formats with embedded jpeg/exif.
         * (see format_raw.c)
         */
-       filter_add_if_missing("raf", "Fujifilm raw camera format", ".raf", TRUE);
+       filter_add_if_missing("raf", "Fujifilm raw format", ".raf", TRUE);
+       filter_add_if_missing("crw", "Canon raw format", ".crw;.cr2", TRUE);
 }
 
 static GList *filter_to_list(const gchar *extensions)
diff --git a/src/format_canon.c b/src/format_canon.c
new file mode 100644 (file)
index 0000000..f6ff838
--- /dev/null
@@ -0,0 +1,473 @@
+/*
+ *  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!
+ *
+ *
+ * Code to add support for Canon CR2 and CRW files, version 0.2
+ *
+ * Developed by Daniel M. German, dmgerman at uvic.ca 
+ *
+ * you can find the sources for this patch at http://turingmachine.org/~dmg/libdcraw/gqview/
+ *
+ */
+
+#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_canon.h"
+#include "format_raw.h"
+
+
+#if 0
+  #define CANON_DEBUG
+#endif
+
+#ifdef CANON_DEBUG
+int canonEnableDebug = 0;
+/* This should be really a stack, but I am too lazy to implement */
+#define DEBUG_ENABLE (canonEnableDebug = 0)
+#define DEBUG_DISABLE (canonEnableDebug = 1)
+/* It would be nice if these functions indented according to depth in the stack, but I am too lazy to implement */
+
+#define DEBUG_ENTRY(a) (canonEnableDebug || fprintf(stderr, "Entering function: %s [%s:%d]\n", a, __FILE__, __LINE__))
+#define DEBUG_EXIT(a) (canonEnableDebug || fprintf(stderr, "Exiting function: %s [%s:%d]\n", a, __FILE__, __LINE__))
+#define DEBUG_1(a) (canonEnableDebug || fprintf(stderr, a " [%s:%d]\n", __FILE__, __LINE__))
+#define DEBUG_2(a,b) (canonEnableDebug || fprintf(stderr, a " [%s:%d]\n",b,  __FILE__, __LINE__))
+#define DEBUG_3(a,b,c) (canonEnableDebug || fprintf(stderr, a " [%s:%d]\n",b, c,  __FILE__, __LINE__))
+
+#else
+#define DEBUG_ENABLE
+#define DEBUG_DISABLE 
+#define DEBUG_ENTRY(a)
+#define DEBUG_EXIT(a)
+
+#define DEBUG_1(a) 
+#define DEBUG_2(a,b)
+#define DEBUG_3(a,b,c)
+#endif
+
+
+/* canon_read_int4 
+
+
+The problem with gqview is that sometimes the data is to be read from
+a file, and sometimes it is in memory. This function tries to isolate
+the rest of the code from having to deal with both cases
+
+This function reads a 4 byte unsigned integer, and fixes its endianism.
+
+If fd >= 0 then the value is read from the corresponding file descriptor
+   
+   in that case, if offset is > 0, then the value is read from that offset
+
+   otherwise it is read from the current file pointer 
+
+if fd < 0 then the value is read from the memory pointed by data + offset
+
+
+offset is a pointer to the actual offset of the file.
+
+sizeInt can be 2 or 4 (it is the number of bytes to read)
+
+RETURNS true is no error, false if it can't read the value
+
+
+*/
+static int canon_read_int(unsigned int *offset, const void *data, int sizeInt, unsigned int *value )
+{
+  DEBUG_DISABLE;
+
+  DEBUG_ENTRY("canon_read_int");
+  /* Verify values before we do anything */
+  if (sizeInt != 2 && sizeInt != 4) return FALSE;
+  if (offset == NULL) return FALSE;
+  if (*offset <= 0) return FALSE;
+  if (data == NULL) return FALSE;
+  if (value == NULL) return FALSE;
+
+  if (sizeInt == 4) {
+    *value = GUINT32_FROM_LE(*(guint32*)(data + *offset));      
+    *offset +=4;
+    DEBUG_3("Read 4 bytes %d %x", *value, *value);
+  } else {
+    *value = GUINT16_FROM_LE(*(guint32*)(data + *offset));
+    *offset +=2;
+    DEBUG_3("Read 2 bytes %d %x", *value, *value);
+  }
+
+  DEBUG_EXIT("canon_read_int");
+
+  DEBUG_ENABLE;
+  return TRUE;
+}
+
+#define CANON_HEADER_SIZE                   26
+
+/*
+
+ The CR2 format is really a TIFF format. It is nicely documented in the TIFF V 6.0 document available from adobe.
+
+  The CR2 file contains two thumbnails, one tiny and one decent sized. The record Id of the latter is 0x0111.
+
+  The photo info is also available, in EXIF, and it looks like I don't need to do anything! Yeah!
+
+*/
+
+static int canon_cr2_process_directory(void *data, int offsetIFD, guint *jpegLocation, guint *exifLocation) 
+{
+  unsigned int offset;
+  int returnValue = FALSE;
+
+  DEBUG_ENTRY("canon_cr2_process_directory");
+
+  /* The directory is a link list, after an array of records, the next 4 byptes point to the offset of the next directory.
+
+  All offsets are absolution within the file (in CRWs the offsets are relative ).
+
+  */
+
+  while (offsetIFD != 0 && offsetIFD != 0xFFFF) {
+    int countEntries=0;
+    int i;
+    /* Read directory, we start by reading number of entries in the directory */
+
+    offset = offsetIFD;
+    if (!canon_read_int(&offset, data, 2, &countEntries)) {
+      goto return_only;
+    }
+    DEBUG_2("Number of entries: %d\n", countEntries);
+
+    for (i=0;i<countEntries;i++) {
+      /* read each entry */
+
+      int recordId;
+#if 0
+      int format;
+      int size;
+#endif
+
+      /* read record type */
+      if (!canon_read_int(&offset, data, 2, &recordId)) {
+       goto return_only;
+      }
+
+      /* Did we find the JPEG */
+      if (recordId == 0x0111) { 
+       DEBUG_1("This is the record to find**********************\n");
+       offset +=6;
+       if (!canon_read_int(&offset, data, 4, jpegLocation)) {
+         goto return_only;
+       }
+       DEBUG_3("JPEG Location %d 0x%x\n", *jpegLocation, *jpegLocation);
+       /* We don't want to keep reading, because there is another
+          0x0111 record at the end that contains the raw data */
+       returnValue = TRUE;
+       goto return_only;
+      } else {
+       /* advance pointer by skipping rest of record */
+       offset += 10;
+      }
+    }
+    /* The next 4 bytes are the offset of next directory, if zero we are done
+       
+     */
+    if (!canon_read_int(&offset, data, 4, &offsetIFD)) {
+      goto return_only;
+    }
+    DEBUG_3("Value of NEXT offsetIFD: %d 0x%x\n", offsetIFD, offsetIFD);
+  }
+
+  returnValue = TRUE;
+  DEBUG_1("Going to return true");
+
+ return_only:
+  DEBUG_EXIT("canon_cr2_process_directory");
+
+  return TRUE;
+
+
+}
+
+
+static int format_raw_test_canon_cr2(void *data, const guint len,
+                                    guint *image_offset, guint *exif_offset)
+{
+#if 0
+  char signature[4];
+  unsigned int offset = 4;
+#endif
+  int offsetIFD;
+  int returnValue = FALSE;
+  void *jpgInDataOffset;
+
+  DEBUG_ENTRY("format_raw_test_canon_cr2");
+
+  /* Verify signature */
+  if (memcmp(data, "\x49\x49\x2a\00", 4) != 0) {
+    DEBUG_1("This is not a CR2");
+    goto return_only;
+  }
+
+  /* Get address of first directory */
+  offsetIFD = GUINT32_FROM_LE(*(guint32*)(data + 4));
+
+
+  DEBUG_2("Value of offsetIFD: %d\n", offsetIFD);
+
+  returnValue = canon_cr2_process_directory(data, offsetIFD, image_offset, exif_offset);
+
+  if (returnValue) {
+    jpgInDataOffset = data + *image_offset;
+
+    /* Make sure we really got a JPEG */
+
+    if (memcmp(jpgInDataOffset, "\xff\xd8",2) != 0) {
+      /* It is not at the JPEG! */
+      DEBUG_2("THis is not a jpeg after all: there are the first 4 bytes 0x%x ", (int)jpgInDataOffset);
+      returnValue = FALSE;
+    }
+  }
+
+return_only:
+  DEBUG_EXIT("format_raw_test_canon_cr2");
+
+  return returnValue;
+}
+
+
+gint format_raw_test_canon(const void *data, const guint len,
+                          guint *image_offset, guint *exif_offset)
+{
+
+
+  /* There are at least 2 types of Canon raw files. CRW and CR2 
+
+  CRW files have a proprietary format. 
+
+  HEADER
+  Heap
+    RAW   data
+    JPEG  data
+    PHoto data
+
+  HEADER_LENGTH            32  bytes
+   int2     byteOrder; Always II (MM Motorola ---big endian, II Intel --little endian)
+   int4     length;    Should be 26 
+   char     identifier[8];type HEAP, subtype heap  CCDR
+   int2     version;
+   int2     subversion;
+   char     unused[14]; 
+  */
+
+  int returnValue = FALSE;
+  int heapHeaderOffset = 0;
+  int heapRecordsCount = 0;
+#if 0
+  guint32 rawInt4;
+  guint16 rawInt2;
+#endif
+  int i;
+  unsigned int currentOffset;
+  /* File has to be little endian, first two bytes II */
+
+  if (len < 100) 
+    return FALSE;
+
+  if (format_raw_test_canon_cr2((void *)data, len, image_offset, exif_offset)) {
+    return TRUE;
+  }
+
+  if (memcmp("II", data, 2) != 0) {
+    return FALSE;
+  }
+  /* NO DEBUG BEFORE THIS POINT, we want to debug only Canon */
+  
+  DEBUG_ENTRY("format_raw_test_canon");
+
+  DEBUG_2("Length of buffer read %u", len);
+
+  DEBUG_2("CRW header length Data %d", GUINT32_FROM_LE(*(guint32*)(data + 2)));
+
+  /* the length has to be CANON_HEADER_SIZE  */
+  if (GUINT32_FROM_LE(*(guint32*)(data + 2)) != CANON_HEADER_SIZE) {
+    DEBUG_1("It is not the right size");
+    goto return_only;
+  }
+  
+  if (!memcmp("HEAPCCDR", data+6, 8) == 0) {
+    DEBUG_1("This file is not a Canon CRW raw photo");
+    goto return_only;
+
+   }
+   
+  /* Ok, so now we know that this is a CRW file */
+
+  /* The heap is a strange data structure. It is recursive, so a record
+    can contain a heap itself. That is indeed the case for the photo information
+    reecord. Luckily the first heap contains the jpeg, so we don't need to do
+    any recursive processing. 
+
+    Its "header" is a the end. The header is a sequence of records,
+     and the data of each record is at the beginning of the heap
+
+   +-----------------+
+   | data raw        |
+   +-----------------+
+   | data jpeg       |
+   +-----------------+
+   | data photo info |
+   +-----------------+
+   |header of heap   |
+   | # records       |   it should be 3
+   |      raw info   |
+   |      jpeg info  |
+   |      photo info |
+   +-----------------+
+
+   The header contains 
+      number of records: 2 bytes
+      for each record (10 bytes long)
+          type:    2 bytes
+          length:  4 bytes 
+          offset:  4 bytes 
+         
+     In some records the length and offset are actually data,
+     but none for the ones in the first heap.
+     
+     the offset is with respect to the beginning of the heap, not the
+     beginning of the file. That allows heaps to be "movable"
+
+   For the purpose of finding the JPEG, all we need is to scan the fist heap,
+   which contains the following record types:
+
+    0x2005 Record RAW data
+    0x2007 Record JPEG data
+    0x300a Record with photo info
+
+  */
+
+
+  if (len < 0x10000) {
+    DEBUG_2("We have a problem, the length is too small %d ", len);
+    goto return_only;
+  }
+  currentOffset = len-4;
+
+
+  /* The last 4 bytes have the offset of the header of the heap */
+  if (!canon_read_int(&currentOffset, data, 4, &heapHeaderOffset)) 
+    goto return_only;
+  
+  /* The heapoffset has to be adjusted to the actual file size, the header is CANON_HEADER_SIZE bytes long */
+  heapHeaderOffset += CANON_HEADER_SIZE;
+  DEBUG_2("heap header Offset %d ", heapHeaderOffset);
+  
+  /* Just check, it does not hurt, we don't want to crash */
+  if (heapHeaderOffset > len) 
+    goto return_only;
+
+  currentOffset =   heapHeaderOffset;
+  /* Let us read the number of records in the heap */
+  if (!canon_read_int(&currentOffset, data, 2, &heapRecordsCount))
+    goto return_only;
+  
+  DEBUG_2("heap record count %d ", heapRecordsCount);
+    
+  if (heapRecordsCount != 3) {
+    /* In all the cameras I have seen, this is always 3
+       if not, something is wrong, so just quit */
+    goto return_only;
+  }
+    
+  for (i=0;i<3;i++) {
+    int recordType;
+    int recordOffset;
+    int recordLength;
+    const void *jpgInDataOffset;
+    /* Read each record, to find jpg, it should be second */
+    
+    if (!canon_read_int(&currentOffset, data, 2, &recordType))
+      goto return_only;
+    
+    DEBUG_2("record type 0x%x ", recordType);
+    
+    if (recordType != 0x2007) {
+      /* Go to the next record, don't waste time, 
+        but first, eat 8 bytes from header */
+      currentOffset += 8;
+      continue; /* Nah, wrong record, go to next */
+    }
+    /* Bingo, we are at the JPEG record */
+    
+    /* Read length */
+    if (!canon_read_int(&currentOffset, data, 4, &recordLength))
+      goto return_only;
+    
+    DEBUG_2("record length %d ", recordLength);
+    
+    /* Read offset */
+    
+    if (!canon_read_int(&currentOffset, data, 4, &recordOffset))
+      goto return_only;
+    
+    DEBUG_2("record offset 0x%d ", recordOffset);
+    
+    /* Great, we now know where the JPEG is! 
+       it is CANON_HEADER_SIZE (size of CRW header) + recordOffset 
+    */
+    
+    *image_offset =  CANON_HEADER_SIZE + recordOffset;
+    DEBUG_2("image offset %d ", *image_offset);
+    
+    /* keep checking for potential errors */
+    if (*image_offset > len) {
+      goto return_only;
+    }
+    /* Get the JPEG is */
+    
+    jpgInDataOffset = data + *image_offset;
+
+    if (memcmp(jpgInDataOffset, "\xff\xd8\xff\xdb",4) != 0) {
+      /* It is not at the JPEG! */
+      DEBUG_2("THis is not a jpeg after all: there are the first 4 bytes 0x%x ", (int)jpgInDataOffset);
+      goto return_only;
+    }
+    returnValue = TRUE;
+    goto return_only;
+  }
+ /* undo whatever we need in case of an error*/
+  DEBUG_1("We scan all records, but nothing was found!!!!!!!!!!!!!!!!!!");
+
+
+  /* At this point we are returning */
+return_only:
+  if (returnValue) {
+    DEBUG_1("****We got an embedded  JPEG for a canon CRW");
+
+  }
+
+  DEBUG_EXIT("format_raw_test_canon");
+  return returnValue;
+
+#undef DEBUG_2
+#undef DEBUG
+#undef DEBUG_ENTRY
+#undef DEBUG_EXIT
+
+}
+
+
diff --git a/src/format_canon.h b/src/format_canon.h
new file mode 100644 (file)
index 0000000..ffbdd26
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *  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!
+ *
+ *
+ * Code to add support for Canon CR2 and CRW files, version 0.2
+ *
+ * Developed by Daniel M. German, dmgerman at uvic.ca 
+ *
+ * you can find the sources for this patch at http://turingmachine.org/~dmg/libdcraw/gqview/
+ *
+ */
+
+#ifndef __FORMAT_RAW_CANON_H
+#define __FORMAT_RAW_CANON_H
+
+
+gint format_raw_test_canon(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 }
+
+
+#endif
+
diff --git a/src/format_fuji.c b/src/format_fuji.c
new file mode 100644 (file)
index 0000000..5395f11
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *  GQView
+ *  (C) 2005 John Ellis
+ *
+ *  Authors:
+ *    Original version 2005 Lars Ellenberg, base on dcraw by David coffin.
+ *
+ * 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_fuji.h"
+#include "format_raw.h"
+
+
+gint format_raw_test_fuji(const void *data, const guint len,
+                         guint *image_offset, guint *exif_offset)
+{
+       guint io;
+       guint eo;
+
+       if (len < 128 ||
+           memcmp(data, "FUJIFILM", 8) != 0)
+               {
+               return FALSE;
+               }
+
+       io = GUINT32_FROM_BE(*(guint32*)(data + 84));
+       eo = *image_offset + 12;
+
+       /* verify jpeg marker */
+       if (memcmp(data + io, "\xff\xd8\xff\xe1", 4) != 0)
+               {
+               return FALSE;
+               }
+
+       if (image_offset) *image_offset = io;
+       if (exif_offset) *exif_offset = eo;
+
+       printf("raw Fuji format file\n");
+
+       return TRUE;
+}
+
+
diff --git a/src/format_fuji.h b/src/format_fuji.h
new file mode 100644 (file)
index 0000000..d4c7e4a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *  GQView
+ *  (C) 2005 John Ellis
+ *
+ *  Authors:
+ *    Original version 2005 Lars Ellenberg, base on dcraw by David coffin.
+ *
+ * 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_RAW_FUJI_H
+#define __FORMAT_RAW_FUJI_H
+
+
+gint format_raw_test_fuji(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 }
+
+
+#endif
+
index c805f19..a63e8bd 100644 (file)
 #  include "config.h"
 #endif
 
+
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
 
 #include <glib.h>
 
 
 #include "format_raw.h"
 
-static gint format_raw_test_canon(int fd, const void *data, const guint len,
-                                 guint *image_offset, guint *exif_offset)
+#include "format_canon.h"
+#include "format_fuji.h"
+
+
+typedef struct _FormatEntry FormatEntry;
+struct _FormatEntry {
+       const void *header_pattern;
+       const guint header_length;
+       const gchar *description;
+       FormatRawParseFunc func_parse;
+};
+
+
+static FormatEntry format_list[] = {
+       FORMAT_RAW_CANON,
+       FORMAT_RAW_FUJI,
+       { NULL, 0, NULL, NULL }
+};
+
+
+static FormatEntry *format_raw_find(const void *data, const guint len)
 {
-       return FALSE;
+       gint n;
+
+       n = 0;
+       while (format_list[n].header_pattern)
+               {
+               if (format_list[n].header_length <= len &&
+                   memcmp(data, format_list[n].header_pattern, format_list[n].header_length) == 0)
+                       {
+                       return &format_list[n];
+                       }
+               n++;
+               }
+
+       return NULL;
 }
 
-static gint format_raw_test_fuji(int fd, const void *data, const guint len,
-                                 guint *image_offset, guint *exif_offset)
+static gint format_raw_parse(FormatEntry *entry,
+                            const void *data, const guint len,
+                            guint *image_offset, guint *exif_offset)
 {
-       if (len < 128 ||
-           memcmp(data, "FUJIFILM", 8) != 0)
+       gint io = 0;
+       gint eo = 0;
+       gint found;
+
+       if (!entry || !entry->func_parse) return FALSE;
+
+       found = entry->func_parse(data, len, &io, &eo);
+
+       if (!found ||
+           io >= len - 4 ||
+           eo >= len)
                {
                return FALSE;
                }
 
-       *image_offset = GUINT32_FROM_BE(*(guint32*)(data + 84));
-       *exif_offset = *image_offset + 12;
-printf("found a raw fuji file!\n");
+       if (image_offset) *image_offset = io;
+       if (exif_offset) *exif_offset = eo;
+
        return TRUE;
 }
 
-static gint format_raw_test_nikon(int fd, 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)
 {
-       return FALSE;
+       FormatEntry *entry;
+
+       if (!data || len < 1) return FALSE;
+
+       entry = format_raw_find(data, len);
+
+       if (!entry || !entry->func_parse) return FALSE;
+
+       return format_raw_parse(entry, data, len, image_offset, exif_offset);
 }
 
 
-gint format_raw_img_exif_offsets(int fd, 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)
 {
-       guint32 io = 0;
-       guint32 eo = 0;
-       gint found;
+       FormatEntry *entry;
+       void *map_data = NULL;
+       size_t map_len = 0;
+       struct stat st;
+       gint success;
 
-       if (fd < 0 && !data) return FALSE;
-#if 0
-       if (len < 512) return FALSE;
-#endif
+       if (!header_data || fd < 0) return FALSE;
 
-       found = format_raw_test_canon(fd, data, len, &io, &eo) ||
-               format_raw_test_fuji (fd, data, len, &io, &eo) ||
-               format_raw_test_nikon(fd, data, len, &io, &eo);
+       entry = format_raw_find(header_data, header_len);
 
-       if (!found ||
-           io >= len - 4 ||
-           eo >= len ||
-           memcmp(data + io, "\xff\xd8\xff\xe1", 4) != 0)      /* jpeg marker */
+       if (!entry || !entry->func_parse) return FALSE;
+
+       if (fstat(fd, &st) == -1)
+               {
+               printf("Failed to stat file %d\n", fd);
+               return FALSE;
+               }
+       map_len = st.st_size;
+       map_data = mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (map_data == MAP_FAILED)
                {
+               printf("Failed to mmap of file %d\n", fd);
                return FALSE;
                }
 
-       if (image_offset) *image_offset = io;
-       if (exif_offset) *exif_offset = eo;
+       success = format_raw_parse(entry, map_data, map_len, image_offset, exif_offset);
 
-       return TRUE;
-}
+       if (munmap(map_data, map_len) == -1)
+               {
+               printf("Failed to unmap file %d\n", fd);
+               }
 
+       if (success && image_offset)
+               {
+               if (lseek(fd, *image_offset, SEEK_SET) != *image_offset)
+                       {
+                       printf("Failed to seek to embedded image\n");
+
+                       *image_offset = 0;
+                       if (*exif_offset) *exif_offset = 0;
+                       success = FALSE;
+                       }
+               }
+
+       return success;
+}
 
 
index d76b4ee..2c1609c 100644 (file)
 #ifndef __FORMAT_RAW_H
 #define __FORMAT_RAW_H
 
-gint format_raw_img_exif_offsets(int fd, const void *data, const guint len,
+
+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);
+
 
 #endif
 
index 1daf78b..df9c03f 100644 (file)
@@ -217,17 +217,23 @@ 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))
+               {
+               if (debug) printf("Raw file %s contains embedded image\n", il->path);
+
+               b = read(il->load_fd, &buf, sizeof(buf));
+               }
+
        if (b < 1)
                {
                image_loader_stop(il);
                return FALSE;
                }
 
-       format_raw_img_exif_offsets(il->load_fd, buf, b, &offset, NULL);
-
-       if (gdk_pixbuf_loader_write(il->loader, buf + offset, b - offset, NULL))
+       if (gdk_pixbuf_loader_write(il->loader, buf, b, NULL))
                {
-               il->bytes_read += b;
+               il->bytes_read += b + offset;
 
                if (b < sizeof(buf))
                        {