Sun Jun 12 19:25:26 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 /*
38  *-----------------------------------------------------------------------------
39  * Raw (CR2, CRW) embedded jpeg extraction for Canon
40  *-----------------------------------------------------------------------------
41  */
42
43 static gint canon_cr2_tiff_entry(unsigned char *data, const guint len, guint offset, ExifByteOrder bo,
44                                  guint *image_offset, gint *jpeg_encoding)
45 {
46         guint tag;
47         guint type;
48         guint count;
49         guint jpeg_start;
50
51         /* the two (tiff compliant) tags we want are:
52          *  0x0103 image compression type (must be type 6 for jpeg)
53          *  0x0111 jpeg start offset
54          * only use the first segment that contains an actual jpeg - as there
55          * is a another that contains the raw data.
56          */
57         tag = exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_TAG, bo);
58         type = exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_FORMAT, bo);
59         count = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_COUNT, bo);
60
61         /* tag 0x0103 contains the compression type for this segment's image data */
62         if (tag == 0x0103)
63                 {
64                 if (ExifFormatList[type].size * count == 2 &&
65                     exif_byte_get_int16(data + offset + EXIF_TIFD_OFFSET_DATA, bo) == 6)
66                         {
67                         *jpeg_encoding = TRUE;
68                         }
69                 return FALSE;
70                 }
71
72         /* find and verify jpeg offset */
73         if (tag != 0x0111 ||
74             !jpeg_encoding) return FALSE;
75
76         /* make sure data segment contains 4 bytes */
77         if (ExifFormatList[type].size * count != 4) return FALSE;
78
79         jpeg_start = exif_byte_get_int32(data + offset + EXIF_TIFD_OFFSET_DATA, bo);
80
81         /* verify this is jpeg data */
82         if (len < jpeg_start + 4 ||
83             memcmp(data + jpeg_start, "\xff\xd8", 2) != 0)
84                 {
85                 return FALSE;
86                 }
87
88         *image_offset = jpeg_start;
89         return TRUE;
90 }
91
92 static gint canon_cr2_tiff_table(unsigned char *data, const guint len, guint offset, ExifByteOrder bo,
93                                  guint *image_offset)
94 {
95         gint jpeg_encoding = FALSE;
96         guint count;
97         guint i;
98
99         if (len < offset + 2) return 0;
100
101         count = exif_byte_get_int16(data + offset, bo);
102         offset += 2;
103         if (len < offset + count * EXIF_TIFD_SIZE + 4) return 0;
104
105         for (i = 0; i < count; i++)
106                 {
107                 if (canon_cr2_tiff_entry(data, len, offset + i * EXIF_TIFD_SIZE, bo,
108                                          image_offset, &jpeg_encoding))
109                         {
110                         return 0;
111                         }
112                 }
113
114         return exif_byte_get_int32(data + offset + count * EXIF_TIFD_SIZE, bo);
115 }
116
117 gint format_canon_raw_cr2(unsigned char *data, const guint len,
118                           guint *image_offset, guint *exif_offset)
119 {
120         guint jpeg_offset = 0;
121         ExifByteOrder bo;
122         guint offset;
123         gint level;
124
125         /* cr2 files are tiff files with a few canon specific directory tags
126          * they are (always ?) in little endian format
127          */
128         if (!exif_tiff_directory_offset(data, len, &offset, &bo)) return FALSE;
129
130         level = 0;
131         while (offset && level < EXIF_TIFF_MAX_LEVELS)
132                 {
133                 offset = canon_cr2_tiff_table(data, len, offset, bo, &jpeg_offset);
134                 level++;
135
136                 if (jpeg_offset != 0)
137                         {
138                         if (image_offset) *image_offset = jpeg_offset;
139                         return TRUE;
140                         }
141                 }
142
143         return FALSE;
144 }
145
146 #define CRW_BYTE_ORDER          EXIF_BYTE_ORDER_INTEL
147 #define CRW_HEADER_SIZE         26
148 #define CRW_DIR_ENTRY_SIZE      10
149
150 gint format_canon_raw_crw(unsigned char *data, const guint len,
151                           guint *image_offset, guint *exif_offset)
152 {
153         guint block_offset;
154         guint data_length;
155         guint offset;
156         guint count;
157         guint i;
158
159         /* CRW header starts with 2 bytes for byte order (always "II", little endian),
160          * 4 bytes for start of root block,
161          * and 8 bytes of magic for file type and format "HEAPCCDR"
162          * (also 4 bytes for file version, and 8 bytes reserved)
163          *
164          * CIFF specification in pdf format is available on some websites,
165          * search for "CIFFspecV1R03.pdf" or "CIFFspecV1R04.pdf"
166          */
167         if (len < CRW_HEADER_SIZE ||
168             memcmp(data, "II", 2) != 0 ||
169             memcmp(data + 6, "HEAPCCDR", 8) != 0)
170                 {
171                 return FALSE;
172                 }
173
174         block_offset = exif_byte_get_int32(data + 2, CRW_BYTE_ORDER);
175
176         /* the end of the root block equals end of file,
177          * the last 4 bytes of the root block contain the block's data size
178          */
179         offset = len - 4;
180         data_length = exif_byte_get_int32(data + offset, CRW_BYTE_ORDER);
181
182         offset = block_offset + data_length;
183         if (len < offset + 2) return FALSE;
184
185         /* number of directory entries for this block is in
186          * the next two bytes after the data for this block.
187          */
188         count = exif_byte_get_int16(data + offset, CRW_BYTE_ORDER);
189         offset += 2;
190         if (len < offset + count * CRW_DIR_ENTRY_SIZE + 4) return FALSE;
191
192         /* walk the directory entries looking for type jpeg (tag 0x2007),
193          * for reference, other tags are 0x2005 for raw and 0x300a for photo info:
194          */
195         for (i = 0; i < count ; i++)
196                 {
197                 guint entry_offset;
198                 guint record_type;
199                 guint record_offset;
200                 guint record_length;
201
202                 entry_offset = offset + i * CRW_DIR_ENTRY_SIZE;
203
204                 /* entry is 10 bytes (in order):
205                  *  2 for type
206                  *  4 for length of data
207                  *  4 for offset into data segment of this block
208                  */
209                 record_type = exif_byte_get_int16(data + entry_offset, CRW_BYTE_ORDER);
210                 record_length = exif_byte_get_int32(data + entry_offset + 2, CRW_BYTE_ORDER);
211                 record_offset = exif_byte_get_int32(data + entry_offset + 6, CRW_BYTE_ORDER);
212
213                 /* tag we want for jpeg data */
214                 if (record_type == 0x2007)
215                         {
216                         guint jpeg_offset;
217
218                         jpeg_offset = block_offset + record_offset;
219                         if (len < jpeg_offset + record_length ||
220                             record_length < 4 ||
221                             memcmp(data + jpeg_offset, "\xff\xd8\xff\xdb", 4) != 0)
222                                 {
223                                 return FALSE;
224                                 }
225
226                         /* we now know offset and verified jpeg */
227                         *image_offset = jpeg_offset;
228                         return TRUE;
229                         }
230                 }
231
232         return FALSE;
233 }
234
235
236 /*
237  *-----------------------------------------------------------------------------
238  * EXIF Makernote for Canon
239  *-----------------------------------------------------------------------------
240  */
241
242 static ExifTextList CanonSet1MacroMode[] = {
243         { 1,    "macro" },
244         { 2,    "normal" },
245         EXIF_TEXT_LIST_END
246 };
247
248 static ExifTextList CanonSet1Quality[] = {
249         { 2,    "normal" },
250         { 3,    "fine" },
251         { 4,    "raw" },
252         { 5,    "superfine" },
253         EXIF_TEXT_LIST_END
254 };
255
256 static ExifTextList CanonSet1FlashMode[] = {
257         { 0,    "flash not fired" },
258         { 1,    "auto" },
259         { 2,    "on" },
260         { 3,    "red-eye reduction" },
261         { 4,    "slow sync" },
262         { 5,    "auto + red-eye reduction" },
263         { 6,    "on + red-eye reduction" },
264         { 16,   "external flash" },
265         EXIF_TEXT_LIST_END
266 };
267
268 static ExifTextList CanonSet1DriveMode[] = {
269         { 0,    "single or timer" },
270         { 1,    "continuous" },
271         EXIF_TEXT_LIST_END
272 };
273
274 static ExifTextList CanonSet1FocusMode[] = {
275         { 0,    "one-shot AF" },
276         { 1,    "AI servo AF" },
277         { 2,    "AI focus AF" },
278         { 3,    "manual" },
279         { 4,    "single" },
280         { 5,    "continuous" },
281         { 6,    "manual" },
282         EXIF_TEXT_LIST_END
283 };
284
285 static ExifTextList CanonSet1ImageSize[] = {
286         { 0,    "large" },
287         { 1,    "medium" },
288         { 2,    "small" },
289         /* where (or) does Medium 1/2 fit in here ? */
290         EXIF_TEXT_LIST_END
291 };
292
293 static ExifTextList CanonSet1ShootingMode[] = {
294         { 0,    "auto" },
295         { 1,    "manual" },
296         { 2,    "landscape" },
297         { 3,    "fast shutter" },
298         { 4,    "slow shutter" },
299         { 5,    "night" },
300         { 6,    "black and white" },
301         { 7,    "sepia" },
302         { 8,    "portrait" },
303         { 9,    "sports" },
304         { 10,   "macro" },
305         { 11,   "pan focus" },
306         EXIF_TEXT_LIST_END
307 };
308
309 /* Don't think this is interpreted correctly/completely, A60 at 2.5x Digital sets value of 3 */
310 static ExifTextList CanonSet1DigitalZoom[] = {
311         { 0,    "none" },
312         { 1,    "2x" },
313         { 2,    "4x" },
314         { 3,    "other" },
315         EXIF_TEXT_LIST_END
316 };
317
318 static ExifTextList CanonSet1ConSatSharp[] = {
319         { 0,    "normal" },
320         { 1,    "high" },
321         { 65535,"low" },
322         EXIF_TEXT_LIST_END
323 };
324
325 static ExifTextList CanonSet1ISOSpeed[] = {
326 /*      { 0,    "not set/see EXIF tag" }, */
327         { 15,   "auto" },
328         { 16,   "50" },
329         { 17,   "100" },
330         { 18,   "200" },
331         { 19,   "400" },
332         EXIF_TEXT_LIST_END
333 };
334
335 static ExifTextList CanonSet1MeteringMode[] = {
336         { 0,    "default" },
337         { 1,    "spot" },
338         { 3,    "evaluative" },
339         { 4,    "partial" },
340         { 5,    "center-weighted" },
341         EXIF_TEXT_LIST_END
342 };
343
344 static ExifTextList CanonSet1FocusType[] = {
345         { 0,    "manual" },
346         { 1,    "auto" },
347         { 2,    "auto" },
348         { 3,    "macro" },
349         { 7,    "infinity" },
350         { 8,    "locked" },
351         EXIF_TEXT_LIST_END
352 };
353
354 static ExifTextList CanonSet1AutoFocusPoint[] = {
355         { 0x2005,       "manual AF point selection" },
356         { 0x3000,       "manual focus" },
357         { 0x3001,       "auto" },
358         { 0x3002,       "right" },
359         { 0x3003,       "center" },
360         { 0x3004,       "left" },
361         { 0x4001,       "auto AF point selection" },
362         EXIF_TEXT_LIST_END
363 };
364
365 static ExifTextList CanonSet1ExposureMode[] = {
366         { 0,    "auto" },
367         { 1,    "program" },
368         { 2,    "Tv priority" },
369         { 3,    "Av priority" },
370         { 4,    "manual" },
371         { 5,    "A-DEP" },
372         EXIF_TEXT_LIST_END
373 };
374
375 static ExifTextList CanonSet1FlashFired[] = {
376         { 0,    "no" },
377         { 1,    "yes" },
378         EXIF_TEXT_LIST_END
379 };
380
381 static ExifTextList CanonSet1FocusCont[] = {
382         { 0,    "no (single)" },
383         { 1,    "yes" },
384         EXIF_TEXT_LIST_END
385 };
386
387 static ExifMarker CanonSet1[] = {
388 /* 0 is length of array in bytes (2 x array size) */
389 { 1,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.MacroMode",   "Macro mode",           CanonSet1MacroMode },
390 { 2,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SelfTimer",   "Self timer (10ths of second)", NULL },
391 { 3,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Quality",     "Quality",              CanonSet1Quality },
392 { 4,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashMode",   "Flash mode",           CanonSet1FlashMode },
393 { 5,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.DriveMode",   "Drive mode",           CanonSet1DriveMode },
394 { 7,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocusMode",   "Focus mode",           CanonSet1FocusMode },
395 { 10,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ImageSize",   "Image size",           CanonSet1ImageSize },
396 { 11,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ShootingMode","Shooting mode",        CanonSet1ShootingMode },
397  { 11,  EXIF_FORMAT_SHORT_UNSIGNED, 1, "ExposureProgram",       "ExposureProgram",      CanonSet1ShootingMode },
398 { 12,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.DigitalZoom", "Digital zoom",         CanonSet1DigitalZoom },
399 { 13,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Contrast",    "Contrast",             CanonSet1ConSatSharp },
400 { 14,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Saturation",  "Saturation",           CanonSet1ConSatSharp },
401 { 15,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.Sharpness",   "Sharpness",            CanonSet1ConSatSharp },
402 { 16,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ISOSpeed",    "ISO speed",            CanonSet1ISOSpeed },
403  { 16,  EXIF_FORMAT_SHORT_UNSIGNED, 1, "ISOSpeedRatings",       "ISO speed",            CanonSet1ISOSpeed },
404 { 17,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.MeteringMode","Metering mode",        CanonSet1MeteringMode },
405 { 18,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocusType",   "Focus type",           CanonSet1FocusType },
406 { 19,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.AutoFocus",   "AutoFocus point",      CanonSet1AutoFocusPoint },
407 { 20,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ExposureMode","Exposure mode",        CanonSet1ExposureMode },
408  { 20,  EXIF_FORMAT_SHORT_UNSIGNED, 1, "ExposureMode",          "Exposure mode",        CanonSet1ExposureMode },
409 { 23,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocalLengthLong","Long focal length", NULL },
410 { 24,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocalLengthShort","Short focal length", NULL },
411 { 25,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FocalLengthUnits","Focal units per mm", NULL },
412 { 28,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashFired",  "Flash fired",          CanonSet1FlashFired },
413 { 29,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashDetails","Flash details",        NULL },
414 { 32,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.ContinuousFocus","Continuous focus",  CanonSet1FocusCont },
415 EXIF_MARKER_LIST_END
416 };
417
418 static ExifTextList CanonSet2WhiteBalance[] = {
419         { 0,    "auto" },
420         { 1,    "sunny" },
421         { 2,    "cloudy" },
422         { 3,    "tungsten" },
423         { 4,    "fluorescent" },
424         { 5,    "flash" },
425         { 6,    "custom" },
426         { 7,    "black and white" },
427         { 8,    "shade" },
428         { 9,    "manual" },
429         { 14,   "daylight fluorescent" },
430         { 17,   "underwater" },
431         EXIF_TEXT_LIST_END
432 };
433
434 static ExifTextList CanonSet2FlashBias[] = {
435         { 0x0000,       "0" },
436         { 0x000c,       "0.33" },
437         { 0x0010,       "0.5" },
438         { 0x0014,       "0.67" },
439         { 0x0020,       "1" },
440         { 0x002c,       "1.33" },
441         { 0x0030,       "1.5" },
442         { 0x0034,       "1.67" },
443         { 0x0040,       "2" },
444         { 0xffc0,       "-2" },
445         { 0xffcc,       "-1.67" },
446         { 0xffd0,       "-1.5" },
447         { 0xffd4,       "-1.33" },
448         { 0xffe0,       "-1" },
449         { 0xffec,       "-0.67" },
450         { 0xfff0,       "-0.5" },
451         { 0xfff4,       "-0.33" },
452         EXIF_TEXT_LIST_END
453 };
454
455 static ExifMarker CanonSet2[] = {
456 /* 0 is length of array in bytes (2 x array size) */
457 { 7,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.WhiteBalance","White balance",        CanonSet2WhiteBalance },
458  { 7,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "LightSource",           "White balance",        CanonSet2WhiteBalance },
459 { 9,    EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SequenceNumber","Sequence number",    NULL },
460 { 15,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.FlashBias",   "Flash bias",           CanonSet2FlashBias },
461 /* distance needs more than just this (metric) value */
462 { 19,   EXIF_FORMAT_SHORT_UNSIGNED, 1, "MkN.Canon.SubjectDistance",     "Subject Distance", NULL },
463 EXIF_MARKER_LIST_END
464 };
465
466 #if 0
467
468 static ExifTextList CanonCustomEnable[] = {
469         { 0,    "off" },
470         { 1,    "on" },
471         EXIF_TEXT_LIST_END
472 };
473
474 static ExifTextList CanonCustomEnableInvert[] = {
475         { 0,    "on" },
476         { 1,    "off" },
477         EXIF_TEXT_LIST_END
478 };
479
480 static ExifTextList CanonCustomExposureLevel[] = {
481         { 0,    "1/2 stop" },
482         { 1,    "1/3 stop" },
483         EXIF_TEXT_LIST_END
484 };
485
486 static ExifTextList CanonCustomAVShutterSpeed[] = {
487         { 0,    "auto" },
488         { 1,    "1/200 (fixed)" },
489         EXIF_TEXT_LIST_END
490 };
491
492 static ExifTextList CanonCustomShutterCurtainSync[] = {
493         { 0,    "1st" },
494         { 1,    "2nd" },
495         EXIF_TEXT_LIST_END
496 };
497
498 static ExifMarker CanonCustom[] = {
499 { 1,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.NoiseReduction", "Noise reduction",  CanonCustomEnable },
500 /*{ 2,  EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncShutter",
501                                                 "Shutter/Auto exposure button function",CanonCustomBTNShutter }, */
502 { 3,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.MirrorLockup", "Mirror lockup",      CanonCustomEnable },
503 { 4,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.TvAvExposureLevel",
504                                                         "Tv/Av and exposure level",     CanonCustomExposureLevel },
505 { 5,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.AFAssistLight", "AF assist light",   CanonCustomEnableInvert },
506 { 6,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.AvShutterSpeed",
507                                                         "Shutter speed in Av mode",     CanonCustomAVShutterSpeed },
508 /*{ 7,  EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.AutoBracket",
509                                 "Auto-Exposure bracketting sequence/auto cancellation", CanonCustom }, */
510 { 8,    EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.ShutterSync", "Shutter sync",        CanonCustomShutterCurtainSync },
511 /* { 9, EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncAF",  "AF button function",   CanonCustom }, */
512 { 10,   EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.FillFlashReduction",
513                                                         "Fill flash auto reduction",    CanonCustomEnableInvert },
514 /*{ 11, EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncMenu",
515                                                         "Menu button function",         CanonCustom }, */
516 /*{ 12, EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.BtnFuncSet", "Set button function",  CanonCustom }, */
517 { 13,   EXIF_FORMAT_SHORT_UNSIGNED, 1,  "MkN.Canon.SensorCleaning", "Sensor cleaning",  CanonCustomEnable },
518 EXIF_MARKER_LIST_END
519 };
520
521 #endif
522
523 static ExifMarker CanonExifMarkersList[] = {
524         { 1,    EXIF_FORMAT_SHORT_UNSIGNED, -1, "MkN.Canon.Settings1",          NULL, NULL },
525         { 4,    EXIF_FORMAT_SHORT_UNSIGNED, -1, "MkN.Canon.Settings2",          NULL, NULL },
526         { 6,    EXIF_FORMAT_STRING, -1,         "MkN.Canon.ImageType",          "Image type", NULL },
527         { 7,    EXIF_FORMAT_STRING, -1,         "MkN.Canon.FirmwareVersion",    "Firmware version", NULL },
528         { 8,    EXIF_FORMAT_LONG_UNSIGNED, 1,   "MkN.Canon.ImageNumber",        "Image number", NULL },
529         { 9,    EXIF_FORMAT_STRING, -1,         "MkN.Canon.OwnerName",          "Owner name", NULL },
530         { 12,   EXIF_FORMAT_LONG_UNSIGNED, -1,  "MkN.Canon.SerialNumber",       "Camera serial number", NULL },
531         { 15,   EXIF_FORMAT_SHORT_UNSIGNED, -1, "MkN.Canon.CustomFunctions",    NULL, NULL },
532         EXIF_MARKER_LIST_END
533 };
534
535 static void canon_mknote_parse_settings(ExifData *exif,
536                                         guint16 *data, guint32 len, ExifByteOrder bo,
537                                         ExifMarker *list)
538 {
539         gint i;
540
541         i = 0;
542         while (list[i].tag != 0)
543                 {
544                 if (list[i].tag < len)
545                         {
546                         ExifItem *item;
547
548                         item = exif_item_new(EXIF_FORMAT_SHORT_UNSIGNED, list[i].tag, 1, &list[i]);
549                         exif_item_copy_data(item, &data[list[i].tag], 2, EXIF_FORMAT_SHORT_UNSIGNED, bo);
550                         exif->items = g_list_prepend(exif->items, item);
551                         }
552
553                 i++;
554                 }
555 }
556
557 #if 0
558 static void canon_mknote_parse_convert(ExifData *exif)
559 {
560         gint value;
561         ExifItem *result;
562
563         /* seems we need more than only this value for distance */
564         if (exif_get_integer(exif, "MkN.Canon.SubjectDistance", &value))
565                 {
566                 static ExifMarker marker= { 0x9206, EXIF_FORMAT_RATIONAL_UNSIGNED, 1,
567                                             "SubjectDistance", "Subject distance", NULL };
568                 ExifItem *item;
569                 ExifRational *rational;
570
571                 item = exif_item_new(marker.format, marker.tag, 1, &marker);
572                 rational = item->data;
573                 rational->num = value;
574                 rational->den = 100;
575
576                 exif->items = g_list_prepend(exif->items, item);
577                 }
578
579         result = exif_get_item(exif, "MkN.Canon.SerialNumber");
580         if (result && result->format == EXIF_FORMAT_LONG_UNSIGNED && result->data_len == 4)
581                 {
582                 static ExifMarker marker= { 12, EXIF_FORMAT_STRING, -1,
583                                             "SerialNumber", "Camera serial number", NULL };
584                 ExifItem *item;
585                 gchar *text;
586                 gint l;
587                 guint32 n;
588
589                 n = (guint32)((guint32 *)(result->data))[0];
590                 text = g_strdup_printf("%04X%05d", n & 0xffff0000 >> 8, n & 0x0000ffff);
591                 l = strlen(text) + 1;
592                 item = exif_item_new(marker.format, marker.tag, l, &marker);
593                 memcpy(item->data, text, l);
594                 g_free(text);
595
596                 exif->items = g_list_prepend(exif->items, item);
597                 }
598 }
599 #endif
600
601 gint format_canon_makernote(ExifData *exif, unsigned char *tiff, guint offset,
602                             guint size, ExifByteOrder bo)
603 {
604         ExifItem *item;
605
606         if (exif_parse_IFD_table(exif, tiff, offset, size, bo, 0, CanonExifMarkersList) != 0)
607                 {
608                 return FALSE;
609                 }
610
611         item = exif_get_item(exif, "MkN.Canon.Settings1");
612         if (item)
613                 {
614                 canon_mknote_parse_settings(exif, item->data, item->data_len, bo, CanonSet1);
615                 }
616
617         item = exif_get_item(exif, "MkN.Canon.Settings2");
618         if (item)
619                 {
620                 canon_mknote_parse_settings(exif, item->data, item->data_len, bo, CanonSet2);
621                 }
622
623 #if 0
624         canon_mknote_parse_convert(exif);
625 #endif
626
627         return TRUE;
628 }
629
630