summaryrefslogtreecommitdiffstats
path: root/kfile-plugins/raw/parse.c
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit47d455dd55be855e4cc691c32f687f723d9247ee (patch)
tree52e236aaa2576bdb3840ebede26619692fed6d7d /kfile-plugins/raw/parse.c
downloadtdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.tar.gz
tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegraphics@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kfile-plugins/raw/parse.c')
-rw-r--r--kfile-plugins/raw/parse.c1080
1 files changed, 1080 insertions, 0 deletions
diff --git a/kfile-plugins/raw/parse.c b/kfile-plugins/raw/parse.c
new file mode 100644
index 00000000..1abbbfce
--- /dev/null
+++ b/kfile-plugins/raw/parse.c
@@ -0,0 +1,1080 @@
+/*
+ Raw Photo Parser
+ Copyright 2004 by Dave Coffin, dcoffin a cybercom o net
+
+ This program extracts thumbnail images (preferably JPEGs)
+ from any raw digital camera formats that have them, and
+ shows table contents.
+
+ $Revision: 1.36 $
+ $Date: 2005/05/10 21:43:10 $
+ */
+
+/* Hacked for thumbnail extraction in KDE by
+ Steffen Hansen <[email protected]>
+
+ Based on parse.c and parts of dcraw.c by Dave Coffin
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/types.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+typedef __int64 INT64;
+#else
+#include <netinet/in.h>
+typedef long long INT64;
+#endif
+
+/*
+ TIFF and CIFF data blocks can be quite large.
+ Display only the first DLEN bytes.
+ */
+#ifndef DLEN
+#define DLEN 768
+#endif
+
+typedef unsigned char uchar;
+/*typedef unsigned short ushort;*/
+
+FILE *ifp;
+short order;
+char *fname;
+char make[128], model[128], model2[128], thumb_head[128];
+int width, height, offset, length, bps, is_dng;
+int thumb_offset, thumb_length, thumb_layers;
+float cam_mul[4], pre_mul[4], coeff[3][4];
+#define camera_red cam_mul[0]
+#define camera_blue cam_mul[2]
+/*float flash_used, canon_5814;*/
+time_t timestamp;
+/*int data_offset, meta_offset*/
+int raw_height, raw_width, top_margin, left_margin;
+static int flip = 0;
+
+struct decode {
+ struct decode *branch[2];
+ int leaf;
+} first_decode[640], *free_decode;
+
+#define CLASS
+
+#define FORC3 for (c=0; c < 3; c++)
+#define FORC4 for (c=0; c < 4; c++)
+#define FORCC for (c=0; c < colors; c++)
+
+/*
+ Get a 2-byte integer, making no assumptions about CPU byte order.
+ Nor should we assume that the compiler evaluates left-to-right.
+ */
+ushort get2()
+{
+ uchar a, b;
+
+ a = fgetc(ifp); b = fgetc(ifp);
+
+ if (order == 0x4949) /* "II" means little-endian */
+ return a | b << 8;
+ else /* "MM" means big-endian */
+ return a << 8 | b;
+}
+
+/*
+ Same for a 4-byte integer.
+ */
+int get4()
+{
+ uchar a, b, c, d;
+
+ a = fgetc(ifp); b = fgetc(ifp);
+ c = fgetc(ifp); d = fgetc(ifp);
+
+ if (order == 0x4949)
+ return a | b << 8 | c << 16 | d << 24;
+ else
+ return a << 24 | b << 16 | c << 8 | d;
+}
+
+void tiff_dump(int base, int tag, int type, int count, int level)
+{
+ int save, j, num, den;
+ uchar c;
+ int size[] = { 1,1,1,2,4,8,1,1,2,4,8,4,8 };
+
+ if (count * size[type < 13 ? type:0] > 4)
+ fseek (ifp, get4()+base, SEEK_SET);
+ save = ftell(ifp);
+ fseek (ifp, save, SEEK_SET);
+}
+
+void nikon_decrypt (uchar ci, uchar cj, int tag, int i, int size, uchar *buf)
+{
+}
+
+int parse_tiff_ifd (int base, int level);
+
+void nef_parse_makernote (base)
+{
+ int offset=0, entries, tag, type, count, val, save;
+ unsigned serial=0, key=0;
+ uchar buf91[630], buf97[608], buf98[31];
+ short sorder;
+ char buf[10];
+
+/*
+ The MakerNote might have its own TIFF header (possibly with
+ its own byte-order!), or it might just be a table.
+ */
+ sorder = order;
+ fread (buf, 1, 10, ifp);
+ if (!strcmp (buf,"Nikon")) { /* starts with "Nikon\0\2\0\0\0" ? */
+ base = ftell(ifp);
+ order = get2(); /* might differ from file-wide byteorder */
+ val = get2(); /* should be 42 decimal */
+ offset = get4();
+ fseek (ifp, offset-8, SEEK_CUR);
+ } else if (!strncmp (buf,"FUJIFILM",8) ||
+ !strcmp (buf,"Panasonic")) {
+ order = 0x4949;
+ fseek (ifp, 2, SEEK_CUR);
+ } else if (!strcmp (buf,"OLYMP") ||
+ !strcmp (buf,"LEICA") ||
+ !strcmp (buf,"EPSON"))
+ fseek (ifp, -2, SEEK_CUR);
+ else if (!strcmp (buf,"AOC"))
+ fseek (ifp, -4, SEEK_CUR);
+ else
+ fseek (ifp, -10, SEEK_CUR);
+
+ entries = get2();
+ if (entries > 100) return;
+ while (entries--) {
+ save = ftell(ifp);
+ tag = get2();
+ type = get2();
+ count= get4();
+ tiff_dump (base, tag, type, count, 2);
+ if (tag == 0x1d)
+ fscanf (ifp, "%d", &serial);
+ if (tag == 0x91)
+ fread (buf91, sizeof buf91, 1, ifp);
+ if (tag == 0x97)
+ fread (buf97, sizeof buf97, 1, ifp);
+ if (tag == 0x98)
+ fread (buf98, sizeof buf98, 1, ifp);
+ if (tag == 0xa7)
+ key = fgetc(ifp)^fgetc(ifp)^fgetc(ifp)^fgetc(ifp);
+
+ if (tag == 0x100 && type == 7 && !strncmp(make,"OLYMPUS",7)) {
+ thumb_offset = ftell(ifp);
+ thumb_length = count;
+ }
+ if (tag == 0x280 && type == 1) { /* EPSON */
+ strncpy (thumb_head, "\xff", sizeof(thumb_head) );
+ thumb_offset = ftell(ifp)+1;
+ thumb_length = count-1;
+ }
+ if (strstr(make,"Minolta") || strstr(make,"MINOLTA")) {
+ switch (tag) {
+ case 0x81:
+ thumb_offset = ftell(ifp);
+ thumb_length = count;
+ break;
+ case 0x88:
+ thumb_offset = get4() + base;
+ break;
+ case 0x89:
+ thumb_length = get4();
+ }
+ }
+ if (!strcmp (buf,"OLYMP") && tag >> 8 == 0x20)
+ parse_tiff_ifd (base, 3);
+ fseek (ifp, save+12, SEEK_SET);
+ }
+ nikon_decrypt (serial, key, 0x91, 4, sizeof buf91, buf91);
+ nikon_decrypt (serial, key, 0x97, 284, sizeof buf97, buf97);
+ nikon_decrypt (serial, key, 0x98, 4, sizeof buf98, buf98);
+ order = sorder;
+}
+
+void nef_parse_exif(int base)
+{
+ int entries, tag, type, count, save;
+
+ entries = get2();
+ while (entries--) {
+ save = ftell(ifp);
+ tag = get2();
+ type = get2();
+ count= get4();
+ tiff_dump (base, tag, type, count, 1);
+ if (tag == 0x927c)
+ nef_parse_makernote (base);
+ fseek (ifp, save+12, SEEK_SET);
+ }
+}
+
+int parse_tiff_ifd (int base, int level)
+{
+ int entries, tag, type, count, slen, save, save2, val, i;
+ int comp=0;
+ static const int flip_map[] = { 0,1,3,2,4,6,7,5 };
+
+ entries = get2();
+ if (entries > 255) return 1;
+ while (entries--) {
+ save = ftell(ifp);
+ tag = get2();
+ type = get2();
+ count= get4();
+ slen = count;
+ if (slen > 128) slen = 128;
+
+ tiff_dump (base, tag, type, count, level);
+
+ save2 = ftell(ifp);
+ if (type == 3) /* short int */
+ val = get2();
+ else
+ val = get4();
+ fseek (ifp, save2, SEEK_SET);
+
+ if (tag > 50700 && tag < 50800)
+ is_dng = 1;
+
+ if (level == 3) { /* Olympus E-1 and E-300 */
+ if (type == 4) {
+ if (tag == 0x101)
+ thumb_offset = val;
+ else if (tag == 0x102)
+ thumb_length = val;
+ }
+ goto cont;
+ }
+ switch (tag) {
+ case 0x100: /* ImageWidth */
+ if (!width) width = val;
+ break;
+ case 0x101: /* ImageHeight */
+ if (!height) height = val;
+ break;
+ case 0x102: /* Bits per sample */
+ if (bps) break;
+ bps = val;
+ if (count == 1)
+ thumb_layers = 1;
+ break;
+ case 0x103: /* Compression */
+ comp = val;
+ break;
+ case 0x10f: /* Make tag */
+ fgets (make, slen, ifp);
+ break;
+ case 0x110: /* Model tag */
+ fgets (model, slen, ifp);
+ break;
+ case 33405: /* Model2 tag */
+ fgets (model2, slen, ifp);
+ break;
+ case 0x111: /* StripOffset */
+ if (!offset || is_dng) offset = val;
+ break;
+ case 0x112: /* Orientation */
+ flip = flip_map[(val-1) & 7];
+ break;
+ case 0x117: /* StripByteCounts */
+ if (!length || is_dng) length = val;
+ if (offset > val && !strncmp(make,"KODAK",5) && !is_dng)
+ offset -= val;
+ break;
+ case 0x14a: /* SubIFD tag */
+ save2 = ftell(ifp);
+ for (i=0; i < count; i++) {
+ fseek (ifp, save2 + i*4, SEEK_SET);
+ fseek (ifp, get4()+base, SEEK_SET);
+ parse_tiff_ifd (base, level+1);
+ }
+ break;
+ case 0x201:
+ if (strncmp(make,"OLYMPUS",7) || !thumb_offset)
+ thumb_offset = val;
+ break;
+ case 0x202:
+ if (strncmp(make,"OLYMPUS",7) || !thumb_length)
+ thumb_length = val;
+ break;
+ case 34665:
+ fseek (ifp, get4()+base, SEEK_SET);
+ nef_parse_exif (base);
+ break;
+ case 50706:
+ is_dng = 1;
+ }
+cont:
+ fseek (ifp, save+12, SEEK_SET);
+ }
+ if ((comp == 6 && !strcmp(make,"Canon")) ||
+ (comp == 7 && is_dng)) {
+ thumb_offset = offset;
+ thumb_length = length;
+ }
+ return 0;
+}
+
+/*
+ Parse a TIFF file looking for camera model and decompress offsets.
+ */
+void parse_tiff (int base)
+{
+ int doff, spp=3, ifd=0;
+
+ width = height = offset = length = bps = is_dng = 0;
+ fseek (ifp, base, SEEK_SET);
+ order = get2();
+ if (order != 0x4949 && order != 0x4d4d) return;
+ get2();
+ while ((doff = get4())) {
+ fseek (ifp, doff+base, SEEK_SET);
+ printf ("IFD #%d:\n", ifd++);
+ if (parse_tiff_ifd (base, 0)) break;
+ }
+ if (is_dng) return;
+
+ if (strncmp(make,"KODAK",5))
+ thumb_layers = 0;
+ if (!strncmp(make,"Kodak",5)) {
+ fseek (ifp, 12+base, SEEK_SET);
+ puts ("\nSpecial Kodak image directory:");
+ parse_tiff_ifd (base, 0);
+ }
+ if (!strncmp(model,"DCS460A",7)) {
+ spp = 1;
+ thumb_layers = 0;
+ }
+ if (!thumb_length && offset) {
+ thumb_offset = offset;
+ sprintf (thumb_head, "P%d %d %d %d\n",
+ spp > 1 ? 6:5, width, height, (1 << bps) - 1);
+ thumb_length = width * height * spp * ((bps+7)/8);
+ }
+}
+
+void parse_minolta()
+{
+ int data_offset, save, tag, len;
+
+ fseek (ifp, 4, SEEK_SET);
+ data_offset = get4() + 8;
+ while ((save=ftell(ifp)) < data_offset) {
+ tag = get4();
+ len = get4();
+ printf ("Tag %c%c%c offset %06x length %06x\n",
+ tag>>16, tag>>8, tag, save, len);
+ switch (tag) {
+ case 0x545457: /* TTW */
+ parse_tiff (ftell(ifp));
+ }
+ fseek (ifp, save+len+8, SEEK_SET);
+ }
+ strncpy (thumb_head, "\xff", sizeof(thumb_head) );
+ thumb_offset++;
+ thumb_length--;
+}
+
+/*
+ Parse a CIFF file, better known as Canon CRW format.
+ */
+void parse_ciff (int offset, int length, int level /*unused*/)
+{
+ int tboff, nrecs, i, c, type, len, roff, aoff, save, wbi=-1;
+ static const int remap[] = { 1,2,3,4,5,1 };
+ static const int remap_10d[] = { 0,1,3,4,5,6,0,0,2,8 };
+ static const int remap_s70[] = { 0,1,2,9,4,3,6,7,8,9,10,0,0,0,7,0,0,8 };
+ ushort key[] = { 0x410, 0x45f3 };
+
+ if (strcmp(model,"Canon PowerShot G6") &&
+ strcmp(model,"Canon PowerShot S60") &&
+ strcmp(model,"Canon PowerShot S70") &&
+ strcmp(model,"Canon PowerShot Pro1"))
+ key[0] = key[1] = 0;
+ fseek (ifp, offset+length-4, SEEK_SET);
+ tboff = get4() + offset;
+ fseek (ifp, tboff, SEEK_SET);
+ nrecs = get2();
+ if (nrecs > 100) return;
+ for (i = 0; i < nrecs; i++) {
+ type = get2();
+ len = get4();
+ roff = get4();
+ aoff = offset + roff;
+ save = ftell(ifp);
+ if (type == 0x080a) { /* Get the camera make and model */
+ fseek (ifp, aoff, SEEK_SET);
+ fread (make, 64, 1, ifp);
+ fseek (ifp, aoff+strlen(make)+1, SEEK_SET);
+ fread (model, 64, 1, ifp);
+ }
+ if (type == 0x102a) { /* Find the White Balance index */
+ fseek (ifp, aoff+14, SEEK_SET); /* 0=auto, 1=daylight, 2=cloudy ... */
+ wbi = get2();
+ if (((!strcmp(model,"Canon EOS DIGITAL REBEL") ||
+ !strcmp(model,"Canon EOS 300D DIGITAL"))) && wbi == 6)
+ wbi++;
+ }
+ if (type == 0x102c) { /* Get white balance (G2) */
+ if (!strcmp(model,"Canon PowerShot G1") ||
+ !strcmp(model,"Canon PowerShot Pro90 IS")) {
+ fseek (ifp, aoff+120, SEEK_SET);
+ FORC4 cam_mul[c ^ 2] = get2();
+ } else {
+ fseek (ifp, aoff+100, SEEK_SET);
+ goto common;
+ }
+ }
+ if (type == 0x0032) { /* Get white balance (D30 & G3) */
+ if (!strcmp(model,"Canon EOS D30")) {
+ fseek (ifp, aoff+72, SEEK_SET);
+common:
+ camera_red = get2() ^ key[0];
+ camera_red =(get2() ^ key[1]) / camera_red;
+ camera_blue = get2() ^ key[0];
+ camera_blue /= get2() ^ key[1];
+ } else if (!strcmp(model,"Canon PowerShot G6") ||
+ !strcmp(model,"Canon PowerShot S60") ||
+ !strcmp(model,"Canon PowerShot S70")) {
+ fseek (ifp, aoff+96 + remap_s70[wbi]*8, SEEK_SET);
+ goto common;
+ } else if (!strcmp(model,"Canon PowerShot Pro1")) {
+ fseek (ifp, aoff+96 + wbi*8, SEEK_SET);
+ goto common;
+ } else {
+ fseek (ifp, aoff+80 + (wbi < 6 ? remap[wbi]*8 : 0), SEEK_SET);
+ if (!camera_red)
+ goto common;
+ }
+ }
+ if (type == 0x10a9) { /* Get white balance (D60) */
+ if (!strcmp(model,"Canon EOS 10D"))
+ wbi = remap_10d[wbi];
+ fseek (ifp, aoff+2 + wbi*8, SEEK_SET);
+ camera_red = get2();
+ camera_red /= get2();
+ camera_blue = get2();
+ camera_blue = get2() / camera_blue;
+ }
+ /* Skip this for now /steffen */
+#if 0
+ if (type == 0x1030 && (wbi == 6 || wbi == 15)) {
+ fseek (ifp, aoff, SEEK_SET); /* Get white sample */
+ ciff_block_1030();
+ }
+#endif
+ if (type == 0x1031) { /* Get the raw width and height */
+ fseek (ifp, aoff+2, SEEK_SET);
+ raw_width = get2();
+ raw_height = get2();
+ }
+ if (type == 0x180e) { /* Get the timestamp */
+ fseek (ifp, aoff, SEEK_SET);
+ timestamp = get4();
+ }
+ if (type == 0x580e)
+ timestamp = len;
+#if 0
+ if (type == 0x5813)
+ flash_used = *((float *) &len);
+ if (type == 0x5814)
+ canon_5814 = *((float *) &len);
+#endif
+ if (type == 0x1810) { /* Get the rotation */
+ fseek (ifp, aoff+12, SEEK_SET);
+ flip = get4();
+ }
+ /* Skip this for now /steffen */
+#if 0
+ if (type == 0x1835) { /* Get the decoder table */
+ fseek (ifp, aoff, SEEK_SET);
+ crw_init_tables (get4());
+ }
+#endif
+ if (type == 0x2007) { /* Found the JPEG thumbnail */
+ thumb_offset = aoff;
+ thumb_length = len;
+ }
+ if (type >> 8 == 0x28 || type >> 8 == 0x30) /* Get sub-tables */
+ parse_ciff(aoff, len, level+1);
+ fseek (ifp, save, SEEK_SET);
+ }
+ if (wbi == 0 && !strcmp(model,"Canon EOS D30"))
+ camera_red = -1; /* Use my auto WB for this photo */
+}
+
+
+void parse_mos(int level)
+{
+ uchar data[256];
+ int i, j, skip, save;
+ char *cp;
+
+ save = ftell(ifp);
+ while (1) {
+ fread (data, 1, 8, ifp);
+ if (strcmp(data,"PKTS")) break;
+ strcpy (model, "Valeo");
+ fread (data, 1, 40, ifp);
+ skip = get4();
+ if (!strcmp(data,"icc_camera_to_tone_matrix")) {
+ for (i=0; i < skip/4; i++) {
+ j = get4();
+ }
+ continue;
+ }
+ if (!strcmp(data,"JPEG_preview_data")) {
+ thumb_head[0] = 0;
+ thumb_offset = ftell(ifp);
+ thumb_length = skip;
+ }
+ fread (data, 1, sizeof data, ifp);
+ fseek (ifp, -sizeof data, SEEK_CUR);
+ data[sizeof data - 1] = 0;
+ while ((cp=index(data,'\n')))
+ *cp = ' ';
+ parse_mos(level+2);
+ fseek (ifp, skip, SEEK_CUR);
+ }
+ fseek (ifp, save, SEEK_SET);
+}
+
+void parse_rollei()
+{
+ char line[128], *val;
+
+ fseek (ifp, 0, SEEK_SET);
+ do {
+ fgets (line, 128, ifp);
+ fputs (line, stdout);
+ if ((val = strchr(line,'=')))
+ *val++ = 0;
+ else
+ val = line + strlen(line);
+ if (!strcmp(line,"HDR"))
+ thumb_offset = atoi(val);
+ if (!strcmp(line,"TX "))
+ width = atoi(val);
+ if (!strcmp(line,"TY "))
+ height = atoi(val);
+ } while (strncmp(line,"EOHD",4));
+ strcpy (make, "Rollei");
+ strcpy (model, "d530flex");
+ thumb_length = width*height*2;
+}
+
+void rollei_decode (FILE *tfp)
+{
+ ushort data;
+ int row, col;
+
+ fseek (ifp, thumb_offset, SEEK_SET);
+ fprintf (tfp, "P6\n%d %d\n255\n", width, height);
+ for (row=0; row < height; row++)
+ for (col=0; col < width; col++) {
+ fread (&data, 2, 1, ifp);
+ data = ntohs(data);
+ putc (data << 3, tfp);
+ putc (data >> 5 << 2, tfp);
+ putc (data >> 11 << 3, tfp);
+ }
+}
+
+void get_utf8 (int offset, char *buf, int len)
+{
+ ushort c;
+ char *cp;
+
+ fseek (ifp, offset, SEEK_SET);
+ for (cp=buf; (c = get2()) && cp+3 < buf+len; ) {
+ if (c < 0x80)
+ *cp++ = c;
+ else if (c < 0x800) {
+ *cp++ = 0xc0 + (c >> 6);
+ *cp++ = 0x80 + (c & 0x3f);
+ } else {
+ *cp++ = 0xe0 + (c >> 12);
+ *cp++ = 0x80 + (c >> 6 & 0x3f);
+ *cp++ = 0x80 + (c & 0x3f);
+ }
+ }
+ *cp = 0;
+}
+
+ushort sget2 (uchar *s)
+{
+ return s[0] + (s[1]<<8);
+}
+
+int sget4 (uchar *s)
+{
+ return s[0] + (s[1]<<8) + (s[2]<<16) + (s[3]<<24);
+}
+
+void parse_foveon()
+{
+ int entries, img=0, off, len, tag, save, i, j, k, pent, poff[256][2];
+ char name[128], value[128], camf[0x20000], *pos, *cp, *dp;
+ unsigned val, key, type, num, ndim, dim[3];
+
+ order = 0x4949; /* Little-endian */
+ fseek (ifp, -4, SEEK_END);
+ fseek (ifp, get4(), SEEK_SET);
+ if (get4() != 0x64434553) { /* SECd */
+ printf ("Bad Section identifier at %6x\n", (int)ftell(ifp)-4);
+ return;
+ }
+ get4();
+ entries = get4();
+ while (entries--) {
+ off = get4();
+ len = get4();
+ tag = get4();
+ save = ftell(ifp);
+ fseek (ifp, off, SEEK_SET);
+ if (get4() != (0x20434553 | (tag << 24))) {
+ printf ("Bad Section identifier at %6x\n", off);
+ goto next;
+ }
+ val = get4();
+ switch (tag) {
+ case 0x32414d49: /* IMA2 */
+ case 0x47414d49: /* IMAG */
+ if (++img == 2) { /* second image */
+ thumb_offset = off;
+ thumb_length = 1;
+ }
+ printf ("type %d, " , get4());
+ printf ("format %2d, " , get4());
+ printf ("columns %4d, " , get4());
+ printf ("rows %4d, " , get4());
+ printf ("rowsize %d\n" , get4());
+ break;
+ case 0x464d4143: /* CAMF */
+ printf ("type %d, ", get4());
+ get4();
+ for (i=0; i < 4; i++)
+ putchar(fgetc(ifp));
+ val = get4();
+ printf (" version %d.%d:\n",val >> 16, val & 0xffff);
+ key = get4();
+ if ((len -= 28) > 0x20000)
+ len = 0x20000;
+ fread (camf, 1, len, ifp);
+ for (i=0; i < len; i++) {
+ key = (key * 1597 + 51749) % 244944;
+ val = key * (INT64) 301593171 >> 24;
+ camf[i] ^= ((((key << 8) - val) >> 1) + val) >> 17;
+ }
+ for (pos=camf; (unsigned) (pos-camf) < len; pos += sget4(pos+8)) {
+ if (strncmp (pos, "CMb", 3)) {
+ printf("Bad CAMF tag \"%.4s\"\n", pos);
+ break;
+ }
+ val = sget4(pos+4);
+ printf (" %4.4s version %d.%d: ", pos, val >> 16, val & 0xffff);
+ switch (pos[3]) {
+ case 'M':
+ cp = pos + sget4(pos+16);
+ type = sget4(cp);
+ ndim = sget4(cp+4);
+ dim[0] = dim[1] = dim[2] = 1;
+ printf ("%d-dimensonal array %s of type %d:\n Key: (",
+ ndim, pos+sget4(pos+12), sget4(cp));
+ dp = pos + sget4(cp+8);
+ for (i=ndim; i--; ) {
+ cp += 12;
+ dim[i] = sget4(cp);
+ printf ("%s %d%s", pos+sget4(cp+4), dim[i], i ? ", ":")\n");
+ }
+ for (i=0; i < dim[2]; i++) {
+ for (j=0; j < dim[1]; j++) {
+ printf (" ");
+ for (k=0; k < dim[0]; k++)
+ switch (type) {
+ case 0:
+ case 6:
+ printf ("%7d", sget2(dp));
+ dp += 2;
+ break;
+ case 1:
+ case 2:
+ printf (" %d", sget4(dp));
+ dp += 4;
+ break;
+ case 3: {
+ union { int ival; float fval; } __t;
+ __t.ival = sget4(dp);
+ printf (" %9f", __t.fval);
+ dp += 4;
+ }
+ }
+ printf ("\n");
+ }
+ printf ("\n");
+ }
+ break;
+ case 'P':
+ val = sget4(pos+16);
+ num = sget4(pos+val);
+ printf ("%s, %d parameters:\n", pos+sget4(pos+12), num);
+ cp = pos+val+8 + num*8;
+ for (i=0; i < num; i++) {
+ val += 8;
+ printf (" %s = %s\n", cp+sget4(pos+val), cp+sget4(pos+val+4));
+ }
+ break;
+ case 'T':
+ cp = pos + sget4(pos+16);
+ printf ("%s = %.*s\n", pos+sget4(pos+12), sget4(cp), cp+4);
+ break;
+ default:
+ printf ("\n");
+ }
+ }
+ break;
+ case 0x504f5250: /* PROP */
+ printf ("entries %d, ", pent=get4());
+ printf ("charset %d, ", get4());
+ get4();
+ printf ("nchars %d\n", get4());
+ off += pent*8 + 24;
+ if (pent > 256) pent=256;
+ for (i=0; i < pent*2; i++)
+ poff[0][i] = off + get4()*2;
+ for (i=0; i < pent; i++) {
+ get_utf8 (poff[i][0], name, 128);
+ get_utf8 (poff[i][1], value, 128);
+ printf (" %s = %s\n", name, value);
+ if (!strcmp (name,"CAMMANUF"))
+ strncpy (make, value, sizeof(make));
+ if (!strcmp (name,"CAMMODEL"))
+ strncpy (model, value, sizeof(value));
+ }
+ }
+next:
+ fseek (ifp, save, SEEK_SET);
+ }
+}
+
+void foveon_tree (unsigned huff[1024], unsigned code)
+{
+ struct decode *cur;
+ int i, len;
+
+ cur = free_decode++;
+ if (code) {
+ for (i=0; i < 1024; i++)
+ if (huff[i] == code) {
+ cur->leaf = i;
+ return;
+ }
+ }
+ if ((len = code >> 27) > 26) return;
+ code = (len+1) << 27 | (code & 0x3ffffff) << 1;
+
+ cur->branch[0] = free_decode;
+ foveon_tree (huff, code);
+ cur->branch[1] = free_decode;
+ foveon_tree (huff, code+1);
+}
+
+void foveon_decode (FILE *tfp)
+{
+ int bwide, row, col, bit=-1, c, i;
+ char *buf;
+ struct decode *dindex;
+ short pred[3];
+ unsigned huff[1024], bitbuf=0;
+
+ fseek (ifp, thumb_offset+16, SEEK_SET);
+ width = get4();
+ height = get4();
+ bwide = get4();
+ fprintf (tfp, "P6\n%d %d\n255\n", width, height);
+ if (bwide > 0) {
+ buf = malloc(bwide);
+ for (row=0; row < height; row++) {
+ fread (buf, 1, bwide, ifp);
+ fwrite (buf, 3, width, tfp);
+ }
+ free (buf);
+ return;
+ }
+ for (i=0; i < 256; i++)
+ huff[i] = get4();
+ memset (first_decode, 0, sizeof first_decode);
+ free_decode = first_decode;
+ foveon_tree (huff, 0);
+
+ for (row=0; row < height; row++) {
+ memset (pred, 0, sizeof pred);
+ if (!bit) get4();
+ for (col=bit=0; col < width; col++) {
+ for (c=0; c < 3; c++) {
+ for (dindex=first_decode; dindex->branch[0]; ) {
+ if ((bit = (bit-1) & 31) == 31)
+ for (i=0; i < 4; i++)
+ bitbuf = (bitbuf << 8) + fgetc(ifp);
+ dindex = dindex->branch[bitbuf >> bit & 1];
+ }
+ pred[c] += dindex->leaf;
+ fputc (pred[c], tfp);
+ }
+ }
+ }
+}
+
+void kodak_yuv_decode (FILE *tfp)
+{
+ uchar c, blen[384];
+ unsigned row, col, len, bits=0;
+ INT64 bitbuf=0;
+ int i, li=0, si, diff, six[6], y[4], cb=0, cr=0, rgb[3];
+ ushort *out, *op;
+
+ fseek (ifp, thumb_offset, SEEK_SET);
+ width = (width+1) & -2;
+ height = (height+1) & -2;
+ fprintf (tfp, "P6\n%d %d\n65535\n", width, height);
+ out = malloc (width * 12);
+ if (!out) {
+ fprintf (stderr, "kodak_yuv_decode() malloc failed!\n");
+ exit(1);
+ }
+
+ for (row=0; row < height; row+=2) {
+ for (col=0; col < width; col+=2) {
+ if ((col & 127) == 0) {
+ len = (width - col + 1) * 3 & -4;
+ if (len > 384) len = 384;
+ for (i=0; i < len; ) {
+ c = fgetc(ifp);
+ blen[i++] = c & 15;
+ blen[i++] = c >> 4;
+ }
+ li = bitbuf = bits = y[1] = y[3] = cb = cr = 0;
+ if (len % 8 == 4) {
+ bitbuf = fgetc(ifp) << 8;
+ bitbuf += fgetc(ifp);
+ bits = 16;
+ }
+ }
+ for (si=0; si < 6; si++) {
+ len = blen[li++];
+ if (bits < len) {
+ for (i=0; i < 32; i+=8)
+ bitbuf += (INT64) fgetc(ifp) << (bits+(i^8));
+ bits += 32;
+ }
+ diff = bitbuf & (0xffff >> (16-len));
+ bitbuf >>= len;
+ bits -= len;
+ if ((diff & (1 << (len-1))) == 0)
+ diff -= (1 << len) - 1;
+ six[si] = diff;
+ }
+ y[0] = six[0] + y[1];
+ y[1] = six[1] + y[0];
+ y[2] = six[2] + y[3];
+ y[3] = six[3] + y[2];
+ cb += six[4];
+ cr += six[5];
+ for (i=0; i < 4; i++) {
+ op = out + ((i >> 1)*width + col+(i & 1)) * 3;
+ rgb[0] = y[i] + 1.40200/2 * cr;
+ rgb[1] = y[i] - 0.34414/2 * cb - 0.71414/2 * cr;
+ rgb[2] = y[i] + 1.77200/2 * cb;
+ for (c=0; c < 3; c++)
+ if (rgb[c] > 0) op[c] = htons(rgb[c]);
+ }
+ }
+ fwrite (out, sizeof *out, width*6, tfp);
+ }
+ free(out);
+}
+
+void parse_phase_one (int base)
+{
+ unsigned entries, tag, type, len, data, save;
+ char str[256];
+
+ fseek (ifp, base + 8, SEEK_SET);
+ fseek (ifp, base + get4(), SEEK_SET);
+ entries = get4();
+ get4();
+ while (entries--) {
+ tag = get4();
+ type = get4();
+ len = get4();
+ data = get4();
+ save = ftell(ifp);
+ printf ("Phase One tag=0x%x, type=%d, len=%2d, data = 0x%x\n",
+ tag, type, len, data);
+ if (type == 1 && len < 256) {
+ fseek (ifp, base + data, SEEK_SET);
+ fread (str, 256, 1, ifp);
+ puts (str);
+ }
+ if (tag == 0x110) {
+ thumb_offset = data + base;
+ thumb_length = len;
+ }
+ fseek (ifp, save, SEEK_SET);
+ }
+ strcpy (make, "Phase One");
+ strcpy (model, "unknown");
+}
+
+void parse_jpeg (int offset)
+{
+ int len, save, hlen;
+
+ fseek (ifp, offset, SEEK_SET);
+ if (fgetc(ifp) != 0xff || fgetc(ifp) != 0xd8) return;
+
+ while (fgetc(ifp) == 0xff && fgetc(ifp) >> 4 != 0xd) {
+ order = 0x4d4d;
+ len = get2() - 2;
+ save = ftell(ifp);
+ order = get2();
+ hlen = get4();
+ if (get4() == 0x48454150) /* "HEAP" */
+ parse_ciff (save+hlen, len-hlen, 0);
+ parse_tiff (save+6);
+ fseek (ifp, save+len, SEEK_SET);
+ }
+}
+
+char *raw_memmem (char *haystack, size_t haystacklen,
+ char *needle, size_t needlelen)
+{
+ char *c;
+ for (c = haystack; c <= haystack + haystacklen - needlelen; c++)
+ if (!memcmp (c, needle, needlelen))
+ return c;
+ return NULL;
+}
+
+/*
+ Identify which camera created this file, and set global variables
+ accordingly.
+ Return nonzero if the file cannot be decoded or no thumbnail is found
+ */
+int identify(FILE* tfp)
+{
+ char head[32], *thumb, *rgb, *cp;
+ unsigned hlen, fsize, toff, tlen, lsize, i;
+
+ make[0] = model[0] = model2[0] = is_dng = 0;
+ thumb_head[0] = thumb_offset = thumb_length = thumb_layers = 0;
+ order = get2();
+ hlen = get4();
+ fseek (ifp, 0, SEEK_SET);
+ fread (head, 1, 32, ifp);
+ fseek (ifp, 0, SEEK_END);
+ fsize = ftell(ifp);
+ if ((cp = raw_memmem (head, 32, "MMMMRawT", 8)) ||
+ (cp = raw_memmem (head, 32, "IIIITwaR", 8)))
+ parse_phase_one (cp - head);
+ else if (order == 0x4949 || order == 0x4d4d) {
+ if (!memcmp(head+6,"HEAPCCDR",8)) {
+ parse_ciff (hlen, fsize - hlen, 0);
+ fseek (ifp, hlen, SEEK_SET);
+ } else
+ parse_tiff (0);
+ } else if (!memcmp (head, "\0MRM", 4))
+ parse_minolta();
+ else if (!memcmp (head, "\xff\xd8\xff\xe1", 4) &&
+ !memcmp (head+6, "Exif", 4)) {
+ parse_tiff (12);
+ thumb_length = 0;
+ } else if (!memcmp (head, "FUJIFILM", 8)) {
+ fseek (ifp, 84, SEEK_SET);
+ toff = get4();
+ tlen = get4();
+ thumb_offset = toff;
+ thumb_length = tlen;
+ } else if (!memcmp (head, "DSC-Image", 9))
+ parse_rollei();
+ else if (!memcmp (head, "FOVb", 4))
+ parse_foveon();
+ fseek (ifp, 8, SEEK_SET);
+ parse_mos(0);
+ fseek (ifp, 3472, SEEK_SET);
+ parse_mos(0);
+ parse_jpeg(0);
+
+ if (!thumb_length) {
+ fprintf (stderr, "Thumbnail image not found\n");
+ return -1;
+ }
+
+ if (is_dng) goto dng_skip;
+ if (!strncmp(model,"DCS Pro",7)) {
+ kodak_yuv_decode (tfp);
+ goto done;
+ }
+ if (!strcmp(make,"Rollei")) {
+ rollei_decode (tfp);
+ goto done;
+ }
+ if (!strcmp(make,"SIGMA")) {
+ foveon_decode (tfp);
+ goto done;
+ }
+dng_skip:
+ thumb = (char *) malloc(thumb_length);
+ if (!thumb) {
+ fprintf (stderr, "Cannot allocate %d bytes!!\n", thumb_length);
+ exit(1);
+ }
+ fseek (ifp, thumb_offset, SEEK_SET);
+ fread (thumb, 1, thumb_length, ifp);
+ if (thumb_layers && !is_dng) {
+ rgb = (char *) malloc(thumb_length);
+ if (!rgb) {
+ fprintf (stderr, "Cannot allocate %d bytes!!\n", thumb_length);
+ return -1;
+ }
+ lsize = thumb_length/3;
+ for (i=0; i < thumb_length; i++)
+ rgb[(i%lsize)*3 + i/lsize] = thumb[i];
+ free(thumb);
+ thumb = rgb;
+ }
+ fputs (thumb_head, tfp);
+ fwrite(thumb, 1, thumb_length, tfp);
+ free (thumb);
+done:
+ fprintf (stderr, "Thumbnail image written, make=%s, model=%s\n",&(make[0]),&(model[0]));
+ return 0;
+}
+
+int extract_thumbnail( FILE* input, FILE* output, int* orientation )
+{
+ /* Coffin's code has different meaning for orientation
+ values than TIFF, so we map them to TIFF values */
+ static const int flip_map[] = { 0,1,3,2,4,7,5,6 };
+ int rc;
+ ifp = input;
+ rc = identify(output);
+ switch ((flip+3600) % 360) {
+ case 270: flip = 5; break;
+ case 180: flip = 3; break;
+ case 90: flip = 6;
+ }
+ if( orientation ) *orientation = flip_map[flip%7];
+ return rc;
+}