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