Sat Jun 4 22:24:00 2005 John Ellis <johne@verizon.net>
[geeqie.git] / src / format_canon.c
1 /*
2  *  GQView
3  *  (C) 2005 John Ellis
4  *
5  * This software is released under the GNU General Public License (GNU GPL).
6  * Please read the included file COPYING for more information.
7  * This software comes with no warranty of any kind, use at your own risk!
8  *
9  *
10  * Code to add support for Canon CR2 and CRW files, version 0.2
11  *
12  * Developed by Daniel M. German, dmgerman at uvic.ca 
13  *
14  * you can find the sources for this patch at http://turingmachine.org/~dmg/libdcraw/gqview/
15  *
16  */
17
18 #ifdef HAVE_CONFIG_H
19 #  include "config.h"
20 #endif
21
22
23 #include <stdio.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include <glib.h>
28
29 #include "intl.h"
30
31 #include "format_canon.h"
32 #include "format_raw.h"
33
34 #include "exif.h"
35
36
37 #if 0
38   #define CANON_DEBUG
39 #endif
40
41 #ifdef CANON_DEBUG
42 int canonEnableDebug = 0;
43 /* This should be really a stack, but I am too lazy to implement */
44 #define DEBUG_ENABLE (canonEnableDebug = 0)
45 #define DEBUG_DISABLE (canonEnableDebug = 1)
46 /* It would be nice if these functions indented according to depth in the stack, but I am too lazy to implement */
47
48 #define DEBUG_ENTRY(a) (canonEnableDebug || fprintf(stderr, "Entering function: %s [%s:%d]\n", a, __FILE__, __LINE__))
49 #define DEBUG_EXIT(a) (canonEnableDebug || fprintf(stderr, "Exiting function: %s [%s:%d]\n", a, __FILE__, __LINE__))
50 #define DEBUG_1(a) (canonEnableDebug || fprintf(stderr, a " [%s:%d]\n", __FILE__, __LINE__))
51 #define DEBUG_2(a,b) (canonEnableDebug || fprintf(stderr, a " [%s:%d]\n",b,  __FILE__, __LINE__))
52 #define DEBUG_3(a,b,c) (canonEnableDebug || fprintf(stderr, a " [%s:%d]\n",b, c,  __FILE__, __LINE__))
53
54 #else
55 #define DEBUG_ENABLE
56 #define DEBUG_DISABLE 
57 #define DEBUG_ENTRY(a)
58 #define DEBUG_EXIT(a)
59
60 #define DEBUG_1(a) 
61 #define DEBUG_2(a,b)
62 #define DEBUG_3(a,b,c)
63 #endif
64
65
66 /* canon_read_int4 
67
68
69 The problem with gqview is that sometimes the data is to be read from
70 a file, and sometimes it is in memory. This function tries to isolate
71 the rest of the code from having to deal with both cases
72
73 This function reads a 4 byte unsigned integer, and fixes its endianism.
74
75 If fd >= 0 then the value is read from the corresponding file descriptor
76    
77    in that case, if offset is > 0, then the value is read from that offset
78
79    otherwise it is read from the current file pointer 
80
81 if fd < 0 then the value is read from the memory pointed by data + offset
82
83
84 offset is a pointer to the actual offset of the file.
85
86 sizeInt can be 2 or 4 (it is the number of bytes to read)
87
88 RETURNS true is no error, false if it can't read the value
89
90
91 */
92 static int canon_read_int(unsigned int *offset, const void *data, int sizeInt, unsigned int *value )
93 {
94   DEBUG_DISABLE;
95
96   DEBUG_ENTRY("canon_read_int");
97   /* Verify values before we do anything */
98   if (sizeInt != 2 && sizeInt != 4) return FALSE;
99   if (offset == NULL) return FALSE;
100   if (*offset <= 0) return FALSE;
101   if (data == NULL) return FALSE;
102   if (value == NULL) return FALSE;
103
104   if (sizeInt == 4) {
105     *value = GUINT32_FROM_LE(*(guint32*)(data + *offset));      
106     *offset +=4;
107     DEBUG_3("Read 4 bytes %d %x", *value, *value);
108   } else {
109     *value = GUINT16_FROM_LE(*(guint16*)(data + *offset));
110     *offset +=2;
111     DEBUG_3("Read 2 bytes %d %x", *value, *value);
112   }
113
114   DEBUG_EXIT("canon_read_int");
115
116   DEBUG_ENABLE;
117   return TRUE;
118 }
119
120 #define CANON_HEADER_SIZE                   26
121
122 /*
123
124  The CR2 format is really a TIFF format. It is nicely documented in the TIFF V 6.0 document available from adobe.
125
126   The CR2 file contains two thumbnails, one tiny and one decent sized. The record Id of the latter is 0x0111.
127
128   The photo info is also available, in EXIF, and it looks like I don't need to do anything! Yeah!
129
130 */
131
132 static int canon_cr2_process_directory(void *data, int offsetIFD, guint *jpegLocation, guint *exifLocation) 
133 {
134   unsigned int offset;
135   int returnValue = FALSE;
136
137   DEBUG_ENTRY("canon_cr2_process_directory");
138
139   /* The directory is a link list, after an array of records, the next 4 byptes point to the offset of the next directory.
140
141   All offsets are absolution within the file (in CRWs the offsets are relative ).
142
143   */
144
145   while (offsetIFD != 0 && offsetIFD != 0xFFFF) {
146     int countEntries=0;
147     int i;
148     /* Read directory, we start by reading number of entries in the directory */
149
150     offset = offsetIFD;
151     if (!canon_read_int(&offset, data, 2, &countEntries)) {
152       goto return_only;
153     }
154     DEBUG_2("Number of entries: %d\n", countEntries);
155
156     for (i=0;i<countEntries;i++) {
157       /* read each entry */
158
159       int recordId;
160 #if 0
161       int format;
162       int size;
163 #endif
164
165       /* read record type */
166       if (!canon_read_int(&offset, data, 2, &recordId)) {
167         goto return_only;
168       }
169
170       /* Did we find the JPEG */
171       if (recordId == 0x0111) { 
172         DEBUG_1("This is the record to find**********************\n");
173         offset +=6;
174         if (!canon_read_int(&offset, data, 4, jpegLocation)) {
175           goto return_only;
176         }
177         DEBUG_3("JPEG Location %d 0x%x\n", *jpegLocation, *jpegLocation);
178         /* We don't want to keep reading, because there is another
179            0x0111 record at the end that contains the raw data */
180         returnValue = TRUE;
181         goto return_only;
182       } else {
183         /* advance pointer by skipping rest of record */
184         offset += 10;
185       }
186     }
187     /* The next 4 bytes are the offset of next directory, if zero we are done
188        
189      */
190     if (!canon_read_int(&offset, data, 4, &offsetIFD)) {
191       goto return_only;
192     }
193     DEBUG_3("Value of NEXT offsetIFD: %d 0x%x\n", offsetIFD, offsetIFD);
194   }
195
196   returnValue = TRUE;
197   DEBUG_1("Going to return true");
198
199  return_only:
200   DEBUG_EXIT("canon_cr2_process_directory");
201
202   return TRUE;
203
204
205 }
206
207
208 static int format_raw_test_canon_cr2(void *data, const guint len,
209                                      guint *image_offset, guint *exif_offset)
210 {
211 #if 0
212   char signature[4];
213   unsigned int offset = 4;
214 #endif
215   int offsetIFD;
216   int returnValue = FALSE;
217   void *jpgInDataOffset;
218
219   DEBUG_ENTRY("format_raw_test_canon_cr2");
220
221   /* Verify signature */
222   if (memcmp(data, "\x49\x49\x2a\00", 4) != 0) {
223     DEBUG_1("This is not a CR2");
224     goto return_only;
225   }
226
227   /* Get address of first directory */
228   offsetIFD = GUINT32_FROM_LE(*(guint32*)(data + 4));
229
230
231   DEBUG_2("Value of offsetIFD: %d\n", offsetIFD);
232
233   returnValue = canon_cr2_process_directory(data, offsetIFD, image_offset, exif_offset);
234
235   if (returnValue) {
236     jpgInDataOffset = data + *image_offset;
237
238     /* Make sure we really got a JPEG */
239
240     if (memcmp(jpgInDataOffset, "\xff\xd8",2) != 0) {
241       /* It is not at the JPEG! */
242       DEBUG_2("THis is not a jpeg after all: there are the first 4 bytes 0x%x ", (int)jpgInDataOffset);
243       returnValue = FALSE;
244     }
245   }
246
247 return_only:
248   DEBUG_EXIT("format_raw_test_canon_cr2");
249
250   return returnValue;
251 }
252
253
254 gint format_canon_raw(const void *data, const guint len,
255                       guint *image_offset, guint *exif_offset)
256 {
257
258
259   /* There are at least 2 types of Canon raw files. CRW and CR2 
260
261   CRW files have a proprietary format. 
262
263   HEADER
264   Heap
265     RAW   data
266     JPEG  data
267     PHoto data
268
269   HEADER_LENGTH            32  bytes
270    int2     byteOrder; Always II (MM Motorola ---big endian, II Intel --little endian)
271    int4     length;    Should be 26 
272    char     identifier[8];type HEAP, subtype heap  CCDR
273    int2     version;
274    int2     subversion;
275    char     unused[14]; 
276   */
277
278   int returnValue = FALSE;
279   int heapHeaderOffset = 0;
280   int heapRecordsCount = 0;
281 #if 0
282   guint32 rawInt4;
283   guint16 rawInt2;
284 #endif
285   int i;
286   unsigned int currentOffset;
287   /* File has to be little endian, first two bytes II */
288
289   if (len < 100) 
290     return FALSE;
291
292   if (format_raw_test_canon_cr2((void *)data, len, image_offset, exif_offset)) {
293     return TRUE;
294   }
295
296   if (memcmp("II", data, 2) != 0) {
297     return FALSE;
298   }
299   /* NO DEBUG BEFORE THIS POINT, we want to debug only Canon */
300   
301   DEBUG_ENTRY("format_raw_test_canon");
302
303   DEBUG_2("Length of buffer read %u", len);
304
305   DEBUG_2("CRW header length Data %d", GUINT32_FROM_LE(*(guint32*)(data + 2)));
306
307   /* the length has to be CANON_HEADER_SIZE  */
308   if (GUINT32_FROM_LE(*(guint32*)(data + 2)) != CANON_HEADER_SIZE) {
309     DEBUG_1("It is not the right size");
310     goto return_only;
311   }
312   
313   if (!memcmp("HEAPCCDR", data+6, 8) == 0) {
314     DEBUG_1("This file is not a Canon CRW raw photo");
315     goto return_only;
316
317    }
318    
319   /* Ok, so now we know that this is a CRW file */
320
321   /* The heap is a strange data structure. It is recursive, so a record
322     can contain a heap itself. That is indeed the case for the photo information
323     reecord. Luckily the first heap contains the jpeg, so we don't need to do
324     any recursive processing. 
325
326     Its "header" is a the end. The header is a sequence of records,
327      and the data of each record is at the beginning of the heap
328
329    +-----------------+
330    | data raw        |
331    +-----------------+
332    | data jpeg       |
333    +-----------------+
334    | data photo info |
335    +-----------------+
336    |header of heap   |
337    | # records       |   it should be 3
338    |      raw info   |
339    |      jpeg info  |
340    |      photo info |
341    +-----------------+
342
343    The header contains 
344       number of records: 2 bytes
345       for each record (10 bytes long)
346           type:    2 bytes
347           length:  4 bytes 
348           offset:  4 bytes 
349           
350      In some records the length and offset are actually data,
351      but none for the ones in the first heap.
352      
353      the offset is with respect to the beginning of the heap, not the
354      beginning of the file. That allows heaps to be "movable"
355
356    For the purpose of finding the JPEG, all we need is to scan the fist heap,
357    which contains the following record types:
358
359     0x2005 Record RAW data
360     0x2007 Record JPEG data
361     0x300a Record with photo info
362
363   */
364
365
366   if (len < 0x10000) {
367     DEBUG_2("We have a problem, the length is too small %d ", len);
368     goto return_only;
369   }
370   currentOffset = len-4;
371
372
373   /* The last 4 bytes have the offset of the header of the heap */
374   if (!canon_read_int(&currentOffset, data, 4, &heapHeaderOffset)) 
375     goto return_only;
376   
377   /* The heapoffset has to be adjusted to the actual file size, the header is CANON_HEADER_SIZE bytes long */
378   heapHeaderOffset += CANON_HEADER_SIZE;
379   DEBUG_2("heap header Offset %d ", heapHeaderOffset);
380   
381   /* Just check, it does not hurt, we don't want to crash */
382   if (heapHeaderOffset > len) 
383     goto return_only;
384
385   currentOffset =   heapHeaderOffset;
386   /* Let us read the number of records in the heap */
387   if (!canon_read_int(&currentOffset, data, 2, &heapRecordsCount))
388     goto return_only;
389   
390   DEBUG_2("heap record count %d ", heapRecordsCount);
391     
392   if (heapRecordsCount != 3) {
393     /* In all the cameras I have seen, this is always 3
394        if not, something is wrong, so just quit */
395     goto return_only;
396   }
397     
398   for (i=0;i<3;i++) {
399     int recordType;
400     int recordOffset;
401     int recordLength;
402     const void *jpgInDataOffset;
403     /* Read each record, to find jpg, it should be second */
404     
405     if (!canon_read_int(&currentOffset, data, 2, &recordType))
406       goto return_only;
407     
408     DEBUG_2("record type 0x%x ", recordType);
409     
410     if (recordType != 0x2007) {
411       /* Go to the next record, don't waste time, 
412          but first, eat 8 bytes from header */
413       currentOffset += 8;
414       continue; /* Nah, wrong record, go to next */
415     }
416     /* Bingo, we are at the JPEG record */
417     
418     /* Read length */
419     if (!canon_read_int(&currentOffset, data, 4, &recordLength))
420       goto return_only;
421     
422     DEBUG_2("record length %d ", recordLength);
423     
424     /* Read offset */
425     
426     if (!canon_read_int(&currentOffset, data, 4, &recordOffset))
427       goto return_only;
428     
429     DEBUG_2("record offset 0x%d ", recordOffset);
430     
431     /* Great, we now know where the JPEG is! 
432        it is CANON_HEADER_SIZE (size of CRW header) + recordOffset 
433     */
434     
435     *image_offset =  CANON_HEADER_SIZE + recordOffset;
436     DEBUG_2("image offset %d ", *image_offset);
437     
438     /* keep checking for potential errors */
439     if (*image_offset > len) {
440       goto return_only;
441     }
442     /* Get the JPEG is */
443     
444     jpgInDataOffset = data + *image_offset;
445
446     if (memcmp(jpgInDataOffset, "\xff\xd8\xff\xdb",4) != 0) {
447       /* It is not at the JPEG! */
448       DEBUG_2("THis is not a jpeg after all: there are the first 4 bytes 0x%x ", (int)jpgInDataOffset);
449       goto return_only;
450     }
451     returnValue = TRUE;
452     goto return_only;
453   }
454  /* undo whatever we need in case of an error*/
455   DEBUG_1("We scan all records, but nothing was found!!!!!!!!!!!!!!!!!!");
456
457
458   /* At this point we are returning */
459 return_only:
460   if (returnValue) {
461     DEBUG_1("****We got an embedded  JPEG for a canon CRW");
462
463   }
464
465   DEBUG_EXIT("format_raw_test_canon");
466   return returnValue;
467
468 #undef DEBUG_2
469 #undef DEBUG
470 #undef DEBUG_ENTRY
471 #undef DEBUG_EXIT
472
473 }
474
475 /*
476  *-----------------------------------------------------------------------------
477  * EXIF Makernote for Canon
478  *-----------------------------------------------------------------------------
479  */
480
481 static ExifTextList CanonSet1MacroMode[] = {
482         { 1,    "macro" },
483         { 2,    "normal" },
484         EXIF_TEXT_LIST_END
485 };
486
487 static ExifTextList CanonSet1Quality[] = {
488         { 2,    "normal" },
489         { 3,    "fine" },
490         { 5,    "superfine" },
491         EXIF_TEXT_LIST_END
492 };
493
494 static ExifTextList CanonSet1FlashMode[] = {
495         { 0,    "flash not fired" },
496         { 1,    "auto" },
497         { 2,    "on" },
498         { 3,    "red eye reduction" },
499         { 4,    "slow synchro" },
500         { 5,    "auto with red eye reduction" },
501         { 6,    "on with red eye reduction" },
502         { 16,   "external flash" },
503         EXIF_TEXT_LIST_END
504 };
505
506 static ExifTextList CanonSet1DriveMode[] = {
507         { 0,    "single or timer" },
508         { 1,    "continuous" },
509         EXIF_TEXT_LIST_END
510 };
511
512 static ExifTextList CanonSet1FocusMode[] = {
513         { 0,    "one-shot" },
514         { 1,    "AI servo" },
515         { 2,    "AI focus" },
516         { 3,    "manual" },
517         { 4,    "single" },
518         { 5,    "continuous" },
519         { 6,    "manual" },
520         EXIF_TEXT_LIST_END
521 };
522
523 static ExifTextList CanonSet1ImageSize[] = {
524         { 0,    "large" },
525         { 1,    "medium" },
526         { 2,    "small" },
527         /* where (or) does Medium 1/2 fit in here ? */
528         EXIF_TEXT_LIST_END
529 };
530
531 static ExifTextList CanonSet1ShootingMode[] = {
532         { 0,    "auto" },
533         { 1,    "manual" },
534         { 2,    "landscape" },
535         { 3,    "fast shutter" },
536         { 4,    "slow shutter" },
537         { 5,    "night" },
538         { 6,    "black and white" },
539         { 7,    "sepia" },
540         { 8,    "portrait" },
541         { 9,    "sports" },
542         { 10,   "macro" },
543         { 11,   "panoramic focus" },
544         EXIF_TEXT_LIST_END
545 };
546
547 /* Don't think this is interpreted correctly/completely, A60 at 2.5x Digital sets value of 3 */
548 static ExifTextList CanonSet1DigitalZoom[] = {
549         { 0,    "none" },
550         { 1,    "2x" },
551         { 2,    "4x" },
552         EXIF_TEXT_LIST_END
553 };
554
555 static ExifTextList CanonSet1ConSatSharp[] = {
556         { 0,    "normal" },
557         { 1,    "high" },
558         { 65535,"low" },
559         EXIF_TEXT_LIST_END
560 };
561
562 static ExifTextList CanonSet1ISOSpeed[] = {
563 /*      { 0,    "not set/see EXIF tag" }, */
564         { 15,   "auto" },
565         { 16,   "50" },
566         { 17,   "100" },
567         { 18,   "200" },
568         { 19,   "400" },
569         EXIF_TEXT_LIST_END
570 };
571
572 static ExifTextList CanonSet1MeteringMode[] = {
573         { 3,    "evaluative" },
574         { 4,    "partial" },
575         { 5,    "center-weighted" },
576         EXIF_TEXT_LIST_END
577 };
578
579 static ExifTextList CanonSet1FocusType[] = {
580         { 0,    "manual" },
581         { 1,    "auto" },
582         { 3,    "macro" },
583         { 8,    "locked" },
584         EXIF_TEXT_LIST_END
585 };
586
587 static ExifTextList CanonSet1AutoFocusPoint[] = {
588         { 12288,        "manual focus" },
589         { 12289,        "auto" },
590         { 12290,        "right" },
591         { 12291,        "center" },
592         { 12292,        "left" },
593         EXIF_TEXT_LIST_END
594 };
595
596 static ExifTextList CanonSet1ExposureMode[] = {
597         { 0,    "auto" },
598         { 1,    "program" },
599         { 2,    "Tv priority" },
600         { 3,    "Av priority" },
601         { 4,    "manual" },
602         { 5,    "A-DEP" },
603         EXIF_TEXT_LIST_END
604 };
605
606 static ExifTextList CanonSet1FlashFired[] = {
607         { 0,    "no" },
608         { 1,    "yes" },
609         EXIF_TEXT_LIST_END
610 };
611
612 static ExifTextList CanonSet1FocusCont[] = {
613         { 0,    "no (single)" },
614         { 1,    "yes" },
615         EXIF_TEXT_LIST_END
616 };
617
618 static ExifMarker CanonSet1[] = {
619 /* 0 is length of array in bytes (2 x array size) */
620 { 1,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.MacroMode",   "Macro mode",           CanonSet1MacroMode },
621 { 2,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SelfTimer",   "Self timer (10ths of second)", NULL },
622 { 3,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Quality",     "Quality",              CanonSet1Quality },
623 { 4,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashMode",   "Flash mode",           CanonSet1FlashMode },
624 { 5,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.DriveMode",   "Drive mode",           CanonSet1DriveMode },
625 { 7,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocusMode",   "Focus mode",           CanonSet1FocusMode },
626 { 10,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ImageSize",   "Image size",           CanonSet1ImageSize },
627 { 11,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ShootingMode","Shooting mode",        CanonSet1ShootingMode },
628  { 11,  EXIF_FORMAT_SHORT_UNSIGNED, 1, "ExposureProgram",       "ExposureProgram",      CanonSet1ShootingMode },
629 { 12,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.DigitalZoom", "Digital zoom",         CanonSet1DigitalZoom },
630 { 13,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Contrast",    "Contrast",             CanonSet1ConSatSharp },
631 { 14,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Saturation",  "Saturation",           CanonSet1ConSatSharp },
632 { 15,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Sharpness",   "Sharpness",            CanonSet1ConSatSharp },
633 { 16,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ISOSpeed",    "ISO speed",            CanonSet1ISOSpeed },
634  { 16,  EXIF_FORMAT_SHORT_UNSIGNED, 1, "ISOSpeedRatings",       "ISO speed",            CanonSet1ISOSpeed },
635 { 17,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.MeteringMode","Metering mode",        CanonSet1MeteringMode },
636 { 18,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocusType",   "Focus type",           CanonSet1FocusType },
637 { 19,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.AutoFocus",   "AutoFocus point",      CanonSet1AutoFocusPoint },
638 { 20,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ExposureMode","Exposure mode",        CanonSet1ExposureMode },
639  { 20,  EXIF_FORMAT_SHORT_UNSIGNED, 1, "ExposureMode",          "Exposure mode",        CanonSet1ExposureMode },
640 { 23,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocalLengthLong","Long focal length", NULL },
641 { 24,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocalLengthShort","Short focal length", NULL },
642 { 25,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocalLengthUnits","Focal units per mm", NULL },
643 { 28,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashFired",  "Flash fired",          CanonSet1FlashFired },
644 { 29,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashDetails","Flash details",        NULL },
645 { 32,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ContinuousFocus","Continuous focus",  CanonSet1FocusCont },
646 EXIF_MARKER_LIST_END
647 };
648
649 static ExifTextList CanonSet2WhiteBalance[] = {
650         { 0,    "auto" },
651         { 1,    "sunny" },
652         { 2,    "cloudy" },
653         { 3,    "tungsten" },
654         { 4,    "flourescent" },
655         { 5,    "flash" },
656         { 6,    "custom" },
657         EXIF_TEXT_LIST_END
658 };
659
660 static ExifTextList CanonSet2FlashBias[] = {
661         { 0x0000,       "0" },
662         { 0x000c,       "0.33" },
663         { 0x0010,       "0.5" },
664         { 0x0014,       "0.67" },
665         { 0x0020,       "1" },
666         { 0x002c,       "1.33" },
667         { 0x0030,       "1.5" },
668         { 0x0034,       "1.67" },
669         { 0x0040,       "2" },
670         { 0xffc0,       "-2" },
671         { 0xffcc,       "-1.67" },
672         { 0xffd0,       "-1.5" },
673         { 0xffd4,       "-1.33" },
674         { 0xffe0,       "-1" },
675         { 0xffec,       "-0.67" },
676         { 0xfff0,       "-0.5" },
677         { 0xfff4,       "-0.33" },
678         EXIF_TEXT_LIST_END
679 };
680
681 static ExifMarker CanonSet2[] = {
682 /* 0 is length of array in bytes (2 x array size) */
683 { 7,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.WhiteBalance","White balance",        CanonSet2WhiteBalance },
684  { 7,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "LightSource",           "White balance",        CanonSet2WhiteBalance },
685 { 9,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SequenceNumber","Sequence number",    NULL },
686 { 15,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashBias",   "Flash bias",           CanonSet2FlashBias },
687 /* distance needs more than just this (metric) value */
688 { 19,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SubjectDistance",     "Subject Distance", NULL },
689 EXIF_MARKER_LIST_END
690 };
691
692 #if 0
693
694 static ExifTextList CanonCustomEnable[] = {
695         { 0,    "off" },
696         { 1,    "on" },
697         EXIF_TEXT_LIST_END
698 };
699
700 static ExifTextList CanonCustomEnableInvert[] = {
701         { 0,    "on" },
702         { 1,    "off" },
703         EXIF_TEXT_LIST_END
704 };
705
706 static ExifTextList CanonCustomExposureLevel[] = {
707         { 0,    "1/2 stop" },
708         { 1,    "1/3 stop" },
709         EXIF_TEXT_LIST_END
710 };
711
712 static ExifTextList CanonCustomAVShutterSpeed[] = {
713         { 0,    "auto" },
714         { 1,    "1/200 (fixed)" },
715         EXIF_TEXT_LIST_END
716 };
717
718 static ExifTextList CanonCustomShutterCurtainSync[] = {
719         { 0,    "1st" },
720         { 1,    "2nd" },
721         EXIF_TEXT_LIST_END
722 };
723
724 static ExifMarker CanonCustom[] = {
725 { 1,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.NoiseReduction", "Noise reduction",  CanonCustomEnable },
726 /*{ 2,  EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncShutter",
727                                                 "Shutter/Auto exposure button function",CanonCustomBTNShutter }, */
728 { 3,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.MirrorLockup", "Mirror lockup",      CanonCustomEnable },
729 { 4,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.TvAvExposureLevel",
730                                                         "Tv/Av and exposure level",     CanonCustomExposureLevel },
731 { 5,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.AFAssistLight", "AF assist light",   CanonCustomEnableInvert },
732 { 6,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.AvShutterSpeed",
733                                                         "Shutter speed in Av mode",     CanonCustomAVShutterSpeed },
734 /*{ 7,  EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.AutoBracket",
735                                 "Auto-Exposure bracketting sequence/auto cancellation", CanonCustom }, */
736 { 8,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.ShutterSync", "Shutter sync",        CanonCustomShutterCurtainSync },
737 /* { 9, EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncAF",  "AF button function",   CanonCustom }, */
738 { 10,   EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.FillFlashReduction",
739                                                         "Fill flash auto reduction",    CanonCustomEnableInvert },
740 /*{ 11, EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncMenu",
741                                                         "Menu button function",         CanonCustom }, */
742 /*{ 12, EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncSet", "Set button function",  CanonCustom }, */
743 { 13,   EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.SensorCleaning", "Sensor cleaning",  CanonCustomEnable },
744 EXIF_MARKER_LIST_END
745 };
746
747 #endif
748
749 static ExifMarker CanonExifMarkersList[] = {
750         { 1,    EXIF_FORMAT_SHORT_UNSIGNED, -1, "MkN.Canon.Settings1",          NULL, NULL },
751         { 4,    EXIF_FORMAT_SHORT_UNSIGNED, -1, "MkN.Canon.Settings2",          NULL, NULL },
752         { 6,    EXIF_FORMAT_STRING, -1,         "MkN.Canon.ImageType",          "Image type", NULL },
753         { 7,    EXIF_FORMAT_STRING, -1,         "MkN.Canon.FirmwareVersion",    "Firmware version", NULL },
754         { 8,    EXIF_FORMAT_LONG_UNSIGNED, 1,   "MkN.Canon.ImageNumber",        "Image number", NULL },
755         { 9,    EXIF_FORMAT_STRING, -1,         "MkN.Canon.OwnerName",          "Owner name", NULL },
756         { 12,   EXIF_FORMAT_LONG_UNSIGNED, -1,  "MkN.Canon.SerialNumber",       "Camera serial number", NULL },
757         { 15,   EXIF_FORMAT_SHORT_UNSIGNED, -1, "MkN.Canon.CustomFunctions",    NULL, NULL },
758         EXIF_MARKER_LIST_END
759 };
760
761 static void canon_mknote_parse_settings(ExifData *exif,
762                                         guint16 *data, guint32 len, ExifByteOrder byte_order,
763                                         ExifMarker *list)
764 {
765         gint i;
766
767         i = 0;
768         while (list[i].tag != 0)
769                 {
770                 if (list[i].tag < len)
771                         {
772                         ExifItem *item;
773
774                         item = exif_item_new(EXIF_FORMAT_SHORT_UNSIGNED, list[i].tag, 1, &list[i]);
775                         exif_item_copy_data(item, &data[list[i].tag], 1, EXIF_FORMAT_SHORT_UNSIGNED, byte_order);
776                         exif->items = g_list_prepend(exif->items, item);
777                         }
778
779                 i++;
780                 }
781 }
782
783 #if 0
784 static void canon_mknote_parse_convert(ExifData *exif)
785 {
786         gint value;
787         ExifItem *result;
788
789         /* seems we need more than only this value for distance */
790         if (exif_get_integer(exif, "MkN.Canon.SubjectDistance", &value))
791                 {
792                 static ExifMarker marker= { 0x9206, EXIF_FORMAT_RATIONAL_UNSIGNED, 1,
793                                             "SubjectDistance", "Subject distance", NULL };
794                 ExifItem *item;
795                 ExifRational *rational;
796
797                 item = exif_item_new(marker.format, marker.tag, 1, &marker);
798                 rational = item->data;
799                 rational->num = value;
800                 rational->den = 100;
801
802                 exif->items = g_list_prepend(exif->items, item);
803                 }
804
805         result = exif_get_item(exif, "MkN.Canon.SerialNumber");
806         if (result && result->format == EXIF_FORMAT_LONG_UNSIGNED && result->data_len == 4)
807                 {
808                 static ExifMarker marker= { 12, EXIF_FORMAT_STRING, -1,
809                                             "SerialNumber", "Camera serial number", NULL };
810                 ExifItem *item;
811                 gchar *text;
812                 gint l;
813                 guint32 n;
814
815                 n = (guint32)((guint32 *)(result->data))[0];
816                 text = g_strdup_printf("%04X%05d", n & 0xffff0000 >> 8, n & 0x0000ffff);
817                 l = strlen(text);
818                 item = exif_item_new(marker.format, marker.tag, l, &marker);
819                 memcpy(item->data, text, l);
820                 g_free(text);
821
822                 exif->items = g_list_prepend(exif->items, item);
823                 }
824 }
825 #endif
826
827 gint format_canon_makernote(ExifData *exif, unsigned char *tiff, guint offset,
828                             guint size, ExifByteOrder byte_order)
829 {
830         ExifItem *item;
831
832         if (exif_parse_IFD_table(exif, tiff, offset, size, byte_order, CanonExifMarkersList) != 0)
833                 {
834                 return FALSE;
835                 }
836
837         item = exif_get_item(exif, "MkN.Canon.Settings1");
838         if (item)
839                 {
840                 canon_mknote_parse_settings(exif, item->data, item->data_len, byte_order, CanonSet1);
841                 }
842
843         item = exif_get_item(exif, "MkN.Canon.Settings2");
844         if (item)
845                 {
846                 canon_mknote_parse_settings(exif, item->data, item->data_len, byte_order, CanonSet2);
847                 }
848
849 #if 0
850         canon_mknote_parse_convert(exif);
851 #endif
852
853         return TRUE;
854 }
855
856