about summary refs log tree commit diff
path: root/converter/other/cameratopam
diff options
context:
space:
mode:
Diffstat (limited to 'converter/other/cameratopam')
-rw-r--r--converter/other/cameratopam/COPYRIGHT35
-rw-r--r--converter/other/cameratopam/Makefile37
-rw-r--r--converter/other/cameratopam/bayer.h43
-rw-r--r--converter/other/cameratopam/camera.c1781
-rw-r--r--converter/other/cameratopam/camera.h131
-rw-r--r--converter/other/cameratopam/cameratopam.c906
-rw-r--r--converter/other/cameratopam/canon.c172
-rw-r--r--converter/other/cameratopam/canon.h13
-rw-r--r--converter/other/cameratopam/decode.c172
-rw-r--r--converter/other/cameratopam/decode.h22
-rw-r--r--converter/other/cameratopam/dng.c73
-rw-r--r--converter/other/cameratopam/dng.h2
-rw-r--r--converter/other/cameratopam/foveon.c790
-rw-r--r--converter/other/cameratopam/foveon.h14
-rw-r--r--converter/other/cameratopam/global_variables.h55
-rw-r--r--converter/other/cameratopam/identify.c1183
-rw-r--r--converter/other/cameratopam/identify.h11
-rw-r--r--converter/other/cameratopam/ljpeg.c141
-rw-r--r--converter/other/cameratopam/ljpeg.h17
-rw-r--r--converter/other/cameratopam/util.c83
-rw-r--r--converter/other/cameratopam/util.h16
21 files changed, 5697 insertions, 0 deletions
diff --git a/converter/other/cameratopam/COPYRIGHT b/converter/other/cameratopam/COPYRIGHT
new file mode 100644
index 00000000..efc77975
--- /dev/null
+++ b/converter/other/cameratopam/COPYRIGHT
@@ -0,0 +1,35 @@
+Cameratopam is derived from Dave Coffin's Dcraw program.
+
+Bryan Henderson derived it in April 2005.  Bryan mainly just split it up
+into manageable pieces and replaced the parts that generate Netpbm formats
+and the command line parser.
+
+Dcraw and therefore Cameratopam is Copyright 1997-2005 by Dave Coffin,
+dcoffin a cybercom o net, and Dave licenses it to the public freely,
+except that the Foveon code (in foveon.c in Netpbm) is copyrighted by
+someone else (I don't know who).
+
+Dave licenses his copyright to the public freely, with the words, "Any
+code not declared GPL is free for all uses."  Only the code in
+foveon.c in Netpbm was declared GPL in Dave's work.
+
+The foveon code is licensed to the public by its unknown copyright owner
+under GPL.
+
+The Netpbm maintainer distributes the entire Cameratopam package under
+GPL.
+
+Dave's distribution to Bryan may be a copyright violation, but
+strangely enough, Bryan's distribution to others is not.  I.e.  you DO
+have license from every copyright owner to do GPL-compatible things
+with this code.
+
+The problem with Dave's distribution is that he combined the Foveon
+code and his code into a single work and distributed it with GPL terms
+applying only to the Foveon code.  The combined work may be a
+derivative work of the Foveon code and therefore cannot be distributed
+with the permission of the Foveon copyright owner, who gives that
+permission only on the condition that the distributor license the
+entire derivative work under GPL.  Dave did not do that.  He did not,
+for example, prohibit Bryan from distributing Dave's part of the code
+in object code only format.
diff --git a/converter/other/cameratopam/Makefile b/converter/other/cameratopam/Makefile
new file mode 100644
index 00000000..c50c9176
--- /dev/null
+++ b/converter/other/cameratopam/Makefile
@@ -0,0 +1,37 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/other/cameratopam
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+ifneq ($(JPEGLIB),NONE)
+  ifneq ($(JPEGHDR_DIR)x,x)
+    INCLUDES += -I$(JPEGHDR_DIR)
+    CFLAGS += -DHAVE_JPEG
+  endif
+endif
+
+include $(BUILDDIR)/Makefile.config
+
+
+.PHONY: all
+all: cameratopam
+
+OBJECTS = util.o identify.o cameratopam.o camera.o foveon.o decode.o \
+	canon.o ljpeg.o dng.o
+
+MERGE_OBJECTS =
+
+BINARIES = cameratopam
+MERGEBINARIES = 
+SCRIPTS = 
+
+include $(SRCDIR)/Makefile.common
+
+cameratopam: $(OBJECTS) $(NETPBMLIB) $(LIBOPT)
+	$(LD) -o $@ $(LDFLAGS) \
+          $(OBJECTS) $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR)) \
+	  $(MATHLIB) $(LDLIBS) \
+	  $(RPATH) $(LADD)
+
diff --git a/converter/other/cameratopam/bayer.h b/converter/other/cameratopam/bayer.h
new file mode 100644
index 00000000..7ac06ec3
--- /dev/null
+++ b/converter/other/cameratopam/bayer.h
@@ -0,0 +1,43 @@
+/*
+   In order to inline this calculation, I make the risky
+   assumption that all filter patterns can be described
+   by a repeating pattern of eight rows and two columns
+
+   Return values are either 0/1/2/3 = G/M/C/Y or 0/1/2/3 = R/G1/B/G2
+ */
+#define FC(row,col) \
+    (filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)
+
+#define BAYER(row,col) \
+    image[((row) >> shrink)*iwidth + ((col) >> shrink)][FC(row,col)]
+
+/*
+    PowerShot 600   PowerShot A50   PowerShot Pro70 Pro90 & G1
+    0xe1e4e1e4: 0x1b4e4b1e: 0x1e4b4e1b: 0xb4b4b4b4:
+
+      0 1 2 3 4 5     0 1 2 3 4 5     0 1 2 3 4 5     0 1 2 3 4 5
+    0 G M G M G M   0 C Y C Y C Y   0 Y C Y C Y C   0 G M G M G M
+    1 C Y C Y C Y   1 M G M G M G   1 M G M G M G   1 Y C Y C Y C
+    2 M G M G M G   2 Y C Y C Y C   2 C Y C Y C Y
+    3 C Y C Y C Y   3 G M G M G M   3 G M G M G M
+            4 C Y C Y C Y   4 Y C Y C Y C
+    PowerShot A5    5 G M G M G M   5 G M G M G M
+    0x1e4e1e4e: 6 Y C Y C Y C   6 C Y C Y C Y
+            7 M G M G M G   7 M G M G M G
+      0 1 2 3 4 5
+    0 C Y C Y C Y
+    1 G M G M G M
+    2 C Y C Y C Y
+    3 M G M G M G
+
+   All RGB cameras use one of these Bayer grids:
+
+    0x16161616: 0x61616161: 0x49494949: 0x94949494:
+
+      0 1 2 3 4 5     0 1 2 3 4 5     0 1 2 3 4 5     0 1 2 3 4 5
+    0 B G B G B G   0 G R G R G R   0 G B G B G B   0 R G R G R G
+    1 G R G R G R   1 B G B G B G   1 R G R G R G   1 G B G B G B
+    2 B G B G B G   2 G R G R G R   2 G B G B G B   2 R G R G R G
+    3 G R G R G R   3 B G B G B G   3 R G R G R G   3 G B G B G B
+ */
+
diff --git a/converter/other/cameratopam/camera.c b/converter/other/cameratopam/camera.c
new file mode 100644
index 00000000..98d6d37a
--- /dev/null
+++ b/converter/other/cameratopam/camera.c
@@ -0,0 +1,1781 @@
+#define _BSD_SOURCE
+    /* Make sure strcasecmp is in string.h */
+#define _XOPEN_SOURCE
+    /* Make sure putenv is in stdlib.h */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+
+#ifdef HAVE_JPEG
+#include <jpeglib.h>
+#endif
+
+#include "pm.h"
+#include "mallocvar.h"
+
+#include "global_variables.h"
+#include "util.h"
+#include "decode.h"
+#include "bayer.h"
+#include "ljpeg.h"
+#include "dng.h"
+
+#include "camera.h"
+
+#if HAVE_INT64
+   typedef int64_t INT64;
+   static bool const have64BitArithmetic = true;
+#else
+   /* We define INT64 to something that lets the code compile, but we
+      should not execute any INT64 code, because it will get the wrong
+      result.
+   */
+   typedef int INT64;
+   static bool const have64BitArithmetic = false;
+#endif
+
+#ifndef LONG_BIT
+#define LONG_BIT (8 * sizeof (long))
+#endif
+
+#define FORC3 for (c=0; c < 3; c++)
+#define FORC4 for (c=0; c < colors; c++)
+
+static void 
+merror (const void *ptr, const char *where) 
+{
+    if (ptr == NULL)
+        pm_error ("Out of memory in %s", where);
+}
+
+
+
+
+static void  
+adobe_copy_pixel (int row, int col, unsigned short **rp, bool use_secondary)
+{
+  unsigned r=row, c=col;
+
+  if (fuji_secondary && use_secondary) (*rp)++;
+  if (filters) {
+    if (fuji_width) {
+      r = row + fuji_width - 1 - (col >> 1);
+      c = row + ((col+1) >> 1);
+    }
+    if (r < height && c < width)
+      BAYER(r,c) = **rp < 0x1000 ? curve[**rp] : **rp;
+    *rp += 1 + fuji_secondary;
+  } else
+    for (c=0; c < tiff_samples; c++) {
+      image[row*width+col][c] = **rp < 0x1000 ? curve[**rp] : **rp;
+      (*rp)++;
+    }
+  if (fuji_secondary && use_secondary) (*rp)--;
+}
+
+void
+adobe_dng_load_raw_lj()
+{
+  int save, twide, trow=0, tcol=0, jrow, jcol;
+  struct jhead jh;
+  unsigned short *rp;
+
+  while (1) {
+    save = ftell(ifp);
+    fseek (ifp, get4(ifp), SEEK_SET);
+    if (!ljpeg_start (ifp, &jh)) break;
+    if (trow >= raw_height) break;
+    if (jh.high > raw_height-trow)
+    jh.high = raw_height-trow;
+    twide = jh.wide;
+    if (filters) twide *= jh.clrs;
+    else         colors = jh.clrs;
+    if (fuji_secondary) twide /= 2;
+    if (twide > raw_width-tcol)
+    twide = raw_width-tcol;
+
+    for (jrow=0; jrow < jh.high; jrow++) {
+      ljpeg_row (&jh);
+      for (rp=jh.row, jcol=0; jcol < twide; jcol++)
+    adobe_copy_pixel (trow+jrow, tcol+jcol, &rp, use_secondary);
+    }
+    fseek (ifp, save+4, SEEK_SET);
+    if ((tcol += twide) >= raw_width) {
+      tcol = 0;
+      trow += jh.high;
+    }
+    free (jh.row);
+  }
+}
+
+
+
+void
+adobe_dng_load_raw_nc()
+{
+    unsigned short *pixel, *rp;
+    int row, col;
+
+    pixel = calloc (raw_width * tiff_samples, sizeof *pixel);
+    merror (pixel, "adobe_dng_load_raw_nc()");
+    for (row=0; row < raw_height; row++) {
+        read_shorts (ifp, pixel, raw_width * tiff_samples);
+        for (rp=pixel, col=0; col < raw_width; col++)
+            adobe_copy_pixel (row, col, &rp, use_secondary);
+    }
+    free (pixel);
+}
+
+
+
+static int nikon_curve_offset;
+
+void
+nikon_compressed_load_raw(void)
+{
+    static const unsigned char nikon_tree[] = {
+        0,1,5,1,1,1,1,1,1,2,0,0,0,0,0,0,
+        5,4,3,6,2,7,1,0,8,9,11,10,12
+    };
+    int csize, row, col, i, diff;
+    unsigned short vpred[4], hpred[2], *curve;
+
+    init_decoder();
+    make_decoder (nikon_tree, 0);
+
+    fseek (ifp, nikon_curve_offset, SEEK_SET);
+    read_shorts (ifp, vpred, 4);
+    csize = get2(ifp);
+    curve = calloc (csize, sizeof *curve);
+    merror (curve, "nikon_compressed_load_raw()");
+    read_shorts (ifp, curve, csize);
+
+    fseek (ifp, data_offset, SEEK_SET);
+    getbits(ifp, -1);
+
+    for (row=0; row < height; row++)
+        for (col=0; col < raw_width; col++)
+        {
+            diff = ljpeg_diff (first_decode);
+            if (col < 2) {
+                i = 2*(row & 1) + (col & 1);
+                vpred[i] += diff;
+                hpred[col] = vpred[i];
+            } else
+                hpred[col & 1] += diff;
+            if ((unsigned) (col-left_margin) >= width) continue;
+            diff = hpred[col & 1];
+            if (diff >= csize) diff = csize-1;
+            BAYER(row,col-left_margin) = curve[diff];
+        }
+    maximum = curve[csize-1];
+    free (curve);
+}
+
+void
+nikon_load_raw()
+{
+  int irow, row, col, i;
+
+  getbits(ifp, -1);
+  for (irow=0; irow < height; irow++) {
+    row = irow;
+    if (model[0] == 'E') {
+      row = irow * 2 % height + irow / (height/2);
+      if (row == 1 && atoi(model+1) < 5000) {
+    fseek (ifp, 0, SEEK_END);
+    fseek (ifp, ftell(ifp)/2, SEEK_SET);
+    getbits(ifp, -1);
+      }
+    }
+    for (col=0; col < raw_width; col++) {
+      i = getbits(ifp, 12);
+      if ((unsigned) (col-left_margin) < width)
+    BAYER(row,col-left_margin) = i;
+      if (tiff_data_compression == 34713 && (col % 10) == 9)
+    getbits(ifp, 8);
+    }
+  }
+}
+
+/*
+   Figure out if a NEF file is compressed.  These fancy heuristics
+   are only needed for the D100, thanks to a bug in some cameras
+   that tags all images as "compressed".
+ */
+int
+nikon_is_compressed()
+{
+  unsigned char test[256];
+  int i;
+
+  if (tiff_data_compression != 34713)
+    return 0;
+  if (strcmp(model,"D100"))
+    return 1;
+  fseek (ifp, data_offset, SEEK_SET);
+  fread (test, 1, 256, ifp);
+  for (i=15; i < 256; i+=16)
+    if (test[i]) return 1;
+  return 0;
+}
+
+/*
+   Returns 1 for a Coolpix 990, 0 for a Coolpix 995.
+ */
+int
+nikon_e990()
+{
+  int i, histo[256];
+  const unsigned char often[] = { 0x00, 0x55, 0xaa, 0xff };
+
+  memset (histo, 0, sizeof histo);
+  fseek (ifp, 2064*1540*3/4, SEEK_SET);
+  for (i=0; i < 2000; i++)
+    histo[fgetc(ifp)]++;
+  for (i=0; i < 4; i++)
+    if (histo[often[i]] > 400)
+      return 1;
+  return 0;
+}
+
+/*
+   Returns 1 for a Coolpix 2100, 0 for anything else.
+ */
+int
+nikon_e2100()
+{
+  unsigned char t[12];
+  int i;
+
+  fseek (ifp, 0, SEEK_SET);
+  for (i=0; i < 1024; i++) {
+    fread (t, 1, 12, ifp);
+    if (((t[2] & t[4] & t[7] & t[9]) >> 4
+    & t[1] & t[6] & t[8] & t[11] & 3) != 3)
+      return 0;
+  }
+  return 1;
+}
+
+/*
+   Separates a Pentax Optio 33WR from a Nikon E3700.
+ */
+int
+pentax_optio33()
+{
+  int i, sum[] = { 0, 0 };
+  unsigned char tail[952];
+
+  fseek (ifp, -sizeof tail, SEEK_END);
+  fread (tail, 1, sizeof tail, ifp);
+  for (i=0; i < sizeof tail; i++)
+    sum[(i>>2) & 1] += tail[i];
+  return sum[0] < sum[1]*4;
+}
+
+/*
+   Separates a Minolta DiMAGE Z2 from a Nikon E4300.
+ */
+int
+minolta_z2()
+{
+  int i;
+  char tail[424];
+
+  fseek (ifp, -sizeof tail, SEEK_END);
+  fread (tail, 1, sizeof tail, ifp);
+  for (i=0; i < sizeof tail; i++)
+    if (tail[i]) return 1;
+  return 0;
+}
+
+void
+nikon_e2100_load_raw()
+{
+  unsigned char   data[3432], *dp;
+  unsigned short pixel[2288], *pix;
+  int row, col;
+
+  for (row=0; row <= height; row+=2) {
+    if (row == height) {
+      fseek (ifp, ((width==1616) << 13) - (-ftell(ifp) & -2048), SEEK_SET);
+      row = 1;
+    }
+    fread (data, 1, width*3/2, ifp);
+    for (dp=data, pix=pixel; pix < pixel+width; dp+=12, pix+=8) {
+      pix[0] = (dp[2] >> 4) + (dp[ 3] << 4);
+      pix[1] = (dp[2] << 8) +  dp[ 1];
+      pix[2] = (dp[7] >> 4) + (dp[ 0] << 4);
+      pix[3] = (dp[7] << 8) +  dp[ 6];
+      pix[4] = (dp[4] >> 4) + (dp[ 5] << 4);
+      pix[5] = (dp[4] << 8) +  dp[11];
+      pix[6] = (dp[9] >> 4) + (dp[10] << 4);
+      pix[7] = (dp[9] << 8) +  dp[ 8];
+    }
+    for (col=0; col < width; col++)
+      BAYER(row,col) = (pixel[col] & 0xfff);
+  }
+}
+
+void
+nikon_e950_load_raw()
+{
+  int irow, row, col;
+
+  getbits(ifp, -1);
+  for (irow=0; irow < height; irow++) {
+    row = irow * 2 % height;
+    for (col=0; col < width; col++)
+      BAYER(row,col) = getbits(ifp, 10);
+    for (col=28; col--; )
+      getbits(ifp, 8);
+  }
+  maximum = 0x3dd;
+}
+
+/*
+   The Fuji Super CCD is just a Bayer grid rotated 45 degrees.
+ */
+void
+fuji_s2_load_raw()
+{
+  unsigned short pixel[2944];
+  int row, col, r, c;
+
+  fseek (ifp, (2944*24+32)*2, SEEK_CUR);
+  for (row=0; row < 2144; row++) {
+    read_shorts(ifp, pixel, 2944);
+    for (col=0; col < 2880; col++) {
+      r = row + ((col+1) >> 1);
+      c = 2143 - row + (col >> 1);
+      BAYER(r,c) = pixel[col];
+    }
+  }
+}
+
+void
+fuji_s3_load_raw()
+{
+  unsigned short pixel[4352];
+  int row, col, r, c;
+
+  fseek (ifp, (4352*2+32)*2, SEEK_CUR);
+  for (row=0; row < 1440; row++) {
+    read_shorts(ifp, pixel, 4352);
+    for (col=0; col < 4288; col++) {
+      r = 2143 + row - (col >> 1);
+      c = row + ((col+1) >> 1);
+      BAYER(r,c) = pixel[col];
+    }
+  }
+}
+
+static void  fuji_common_load_raw (int ncol, int icol, int nrow)
+{
+  unsigned short pixel[2048];
+  int row, col, r, c;
+
+  for (row=0; row < nrow; row++) {
+    read_shorts(ifp, pixel, ncol);
+    for (col=0; col <= icol; col++) {
+      r = icol - col + (row >> 1);
+      c = col + ((row+1) >> 1);
+      BAYER(r,c) = pixel[col];
+    }
+  }
+}
+
+void
+fuji_s5000_load_raw()
+{
+  fseek (ifp, (1472*4+24)*2, SEEK_CUR);
+  fuji_common_load_raw (1472, 1423, 2152);
+}
+
+void
+fuji_s7000_load_raw()
+{
+  fuji_common_load_raw (2048, 2047, 3080);
+}
+
+/*
+   The Fuji Super CCD SR has two photodiodes for each pixel.
+   The secondary has about 1/16 the sensitivity of the primary,
+   but this ratio may vary.
+ */
+void
+fuji_f700_load_raw()
+{
+  unsigned short pixel[2944];
+  int row, col, r, c, val;
+
+  for (row=0; row < 2168; row++) {
+    read_shorts(ifp, pixel, 2944);
+    for (col=0; col < 1440; col++) {
+      r = 1439 - col + (row >> 1);
+      c = col + ((row+1) >> 1);
+      val = pixel[col+16 + use_secondary*1472];
+      BAYER(r,c) = val;
+    }
+  }
+}
+
+void
+rollei_load_raw()
+{
+  unsigned char pixel[10];
+  unsigned iten=0, isix, i, buffer=0, row, col, todo[16];
+
+  isix = raw_width * raw_height * 5 / 8;
+  while (fread (pixel, 1, 10, ifp) == 10) {
+    for (i=0; i < 10; i+=2) {
+      todo[i]   = iten++;
+      todo[i+1] = pixel[i] << 8 | pixel[i+1];
+      buffer    = pixel[i] >> 2 | buffer << 6;
+    }
+    for (   ; i < 16; i+=2) {
+      todo[i]   = isix++;
+      todo[i+1] = buffer >> (14-i)*5;
+    }
+    for (i=0; i < 16; i+=2) {
+      row = todo[i] / raw_width - top_margin;
+      col = todo[i] % raw_width - left_margin;
+      if (row < height && col < width)
+    BAYER(row,col) = (todo[i+1] & 0x3ff);
+    }
+  }
+  maximum = 0x3ff;
+}
+
+void
+phase_one_load_raw()
+{
+  int row, col, a, b;
+  unsigned short *pixel, akey, bkey;
+
+  fseek (ifp, 8, SEEK_CUR);
+  fseek (ifp, get4(ifp) + 296, SEEK_CUR);
+  akey = get2(ifp);
+  bkey = get2(ifp);
+  fseek (ifp, data_offset + 12 + top_margin*raw_width*2, SEEK_SET);
+  pixel = calloc (raw_width, sizeof *pixel);
+  merror (pixel, "phase_one_load_raw()");
+  for (row=0; row < height; row++) {
+    read_shorts(ifp, pixel, raw_width);
+    for (col=0; col < raw_width; col+=2) {
+      a = pixel[col+0] ^ akey;
+      b = pixel[col+1] ^ bkey;
+      pixel[col+0] = (b & 0xaaaa) | (a & 0x5555);
+      pixel[col+1] = (a & 0xaaaa) | (b & 0x5555);
+    }
+    for (col=0; col < width; col++)
+      BAYER(row,col) = pixel[col+left_margin];
+  }
+  free (pixel);
+}
+
+void
+ixpress_load_raw()
+{
+  unsigned short pixel[4090];
+  int row, col;
+
+  order = 0x4949;
+  fseek (ifp, 304 + 6*2*4090, SEEK_SET);
+  for (row=height; --row >= 0; ) {
+    read_shorts(ifp, pixel, 4090);
+    for (col=0; col < width; col++)
+      BAYER(row,col) = pixel[width-1-col];
+  }
+}
+
+void
+leaf_load_raw()
+{
+  unsigned short *pixel;
+  int r, c, row, col;
+
+  pixel = calloc (raw_width, sizeof *pixel);
+  merror (pixel, "leaf_load_raw()");
+  for (r=0; r < height-32; r+=32)
+    FORC3 for (row=r; row < r+32; row++) {
+      read_shorts(ifp, pixel, raw_width);
+      for (col=0; col < width; col++)
+    image[row*width+col][c] = pixel[col];
+    }
+  free (pixel);
+}
+
+/*
+   For this function only, raw_width is in bytes, not pixels!
+ */
+void
+packed_12_load_raw()
+{
+  int row, col;
+
+  getbits(ifp, -1);
+  for (row=0; row < height; row++) {
+    for (col=0; col < width; col++)
+      BAYER(row,col) = getbits(ifp, 12);
+    for (col = width*3/2; col < raw_width; col++)
+      getbits(ifp, 8);
+  }
+}
+
+void
+unpacked_load_raw()
+{
+  unsigned short *pixel;
+  int row, col;
+
+  pixel = calloc (raw_width, sizeof *pixel);
+  merror (pixel, "unpacked_load_raw()");
+  for (row=0; row < height; row++) {
+    read_shorts(ifp, pixel, raw_width);
+    for (col=0; col < width; col++)
+      BAYER(row,col) = pixel[col];
+  }
+  free (pixel);
+}
+
+void
+olympus_e300_load_raw()
+{
+  unsigned char  *data,  *dp;
+  unsigned short *pixel, *pix;
+  int dwide, row, col;
+
+  dwide = raw_width * 16 / 10;
+  data = malloc (dwide + raw_width*2);
+  merror (data, "olympus_e300_load_raw()");
+  pixel = (unsigned short *) (data + dwide);
+  for (row=0; row < height; row++) {
+    fread (data, 1, dwide, ifp);
+    for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=3, pix+=2) {
+      if (((dp-data) & 15) == 15) dp++;
+      pix[0] = dp[1] << 8 | dp[0];
+      pix[1] = dp[2] << 4 | dp[1] >> 4;
+    }
+    for (col=0; col < width; col++)
+      BAYER(row,col) = (pixel[col] & 0xfff);
+  }
+  free (data);
+}
+
+void
+olympus_cseries_load_raw()
+{
+  int irow, row, col;
+
+  for (irow=0; irow < height; irow++) {
+    row = irow * 2 % height + irow / (height/2);
+    if (row < 2) {
+      fseek (ifp, data_offset - row*(-width*height*3/4 & -2048), SEEK_SET);
+      getbits(ifp, -1);
+    }
+    for (col=0; col < width; col++)
+      BAYER(row,col) = getbits(ifp, 12);
+  }
+}
+
+void
+eight_bit_load_raw()
+{
+  unsigned char *pixel;
+  int row, col;
+
+  pixel = calloc (raw_width, sizeof *pixel);
+  merror (pixel, "eight_bit_load_raw()");
+  for (row=0; row < height; row++) {
+    fread (pixel, 1, raw_width, ifp);
+    for (col=0; col < width; col++)
+      BAYER(row,col) = pixel[col];
+  }
+  free (pixel);
+  maximum = 0xff;
+}
+
+void
+casio_qv5700_load_raw()
+{
+  unsigned char  data[3232],  *dp;
+  unsigned short pixel[2576], *pix;
+  int row, col;
+
+  for (row=0; row < height; row++) {
+    fread (data, 1, 3232, ifp);
+    for (dp=data, pix=pixel; dp < data+3220; dp+=5, pix+=4) {
+      pix[0] = (dp[0] << 2) + (dp[1] >> 6);
+      pix[1] = (dp[1] << 4) + (dp[2] >> 4);
+      pix[2] = (dp[2] << 6) + (dp[3] >> 2);
+      pix[3] = (dp[3] << 8) + (dp[4]     );
+    }
+    for (col=0; col < width; col++)
+      BAYER(row,col) = (pixel[col] & 0x3ff);
+  }
+  maximum = 0x3fc;
+}
+
+void
+nucore_load_raw()
+{
+  unsigned short *pixel;
+  int irow, row, col;
+
+  pixel = calloc (width, 2);
+  merror (pixel, "nucore_load_raw()");
+  for (irow=0; irow < height; irow++) {
+    read_shorts(ifp, pixel, width);
+    row = irow/2 + height/2 * (irow & 1);
+    for (col=0; col < width; col++)
+      BAYER(row,col) = pixel[col];
+  }
+  free (pixel);
+}
+
+static int  radc_token (int tree)
+{
+  int t;
+  static struct decode *dstart[18], *dindex;
+  static const int *s, source[] = {
+    1,1, 2,3, 3,4, 4,2, 5,7, 6,5, 7,6, 7,8,
+    1,0, 2,1, 3,3, 4,4, 5,2, 6,7, 7,6, 8,5, 8,8,
+    2,1, 2,3, 3,0, 3,2, 3,4, 4,6, 5,5, 6,7, 6,8,
+    2,0, 2,1, 2,3, 3,2, 4,4, 5,6, 6,7, 7,5, 7,8,
+    2,1, 2,4, 3,0, 3,2, 3,3, 4,7, 5,5, 6,6, 6,8,
+    2,3, 3,1, 3,2, 3,4, 3,5, 3,6, 4,7, 5,0, 5,8,
+    2,3, 2,6, 3,0, 3,1, 4,4, 4,5, 4,7, 5,2, 5,8,
+    2,4, 2,7, 3,3, 3,6, 4,1, 4,2, 4,5, 5,0, 5,8,
+    2,6, 3,1, 3,3, 3,5, 3,7, 3,8, 4,0, 5,2, 5,4,
+    2,0, 2,1, 3,2, 3,3, 4,4, 4,5, 5,6, 5,7, 4,8,
+    1,0, 2,2, 2,-2,
+    1,-3, 1,3,
+    2,-17, 2,-5, 2,5, 2,17,
+    2,-7, 2,2, 2,9, 2,18,
+    2,-18, 2,-9, 2,-2, 2,7,
+    2,-28, 2,28, 3,-49, 3,-9, 3,9, 4,49, 5,-79, 5,79,
+    2,-1, 2,13, 2,26, 3,39, 4,-16, 5,55, 6,-37, 6,76,
+    2,-26, 2,-13, 2,1, 3,-39, 4,16, 5,-55, 6,-76, 6,37
+  };
+
+  if (free_decode == first_decode)
+    for (s=source, t=0; t < 18; t++) {
+      dstart[t] = free_decode;
+      s = make_decoder_int (s, 0);
+    }
+  if (tree == 18) {
+    if (model[2] == '4')
+      return (getbits(ifp, 5) << 3) + 4; /* DC40 */
+    else
+      return (getbits(ifp, 6) << 2) + 2; /* DC50 */
+  }
+  for (dindex = dstart[tree]; dindex->branch[0]; )
+    dindex = dindex->branch[getbits(ifp, 1)];
+  return dindex->leaf;
+}
+
+#define FORYX for (y=1; y < 3; y++) for (x=col+1; x >= col; x--)
+
+#define PREDICTOR (c ? (buf[c][y-1][x] + buf[c][y][x+1]) / 2 \
+: (buf[c][y-1][x+1] + 2*buf[c][y-1][x] + buf[c][y][x+1]) / 4)
+
+void
+kodak_radc_load_raw()
+{
+  int row, col, tree, nreps, rep, step, i, c, s, r, x, y, val;
+  short last[3] = { 16,16,16 }, mul[3], buf[3][3][386];
+
+  init_decoder();
+  getbits(ifp, -1);
+  for (i=0; i < sizeof(buf)/sizeof(short); i++)
+    buf[0][0][i] = 2048;
+  for (row=0; row < height; row+=4) {
+    for (i=0; i < 3; i++)
+      mul[i] = getbits(ifp, 6);
+    FORC3 {
+      val = ((0x1000000/last[c] + 0x7ff) >> 12) * mul[c];
+      s = val > 65564 ? 10:12;
+      x = ~(-1 << (s-1));
+      val <<= 12-s;
+      for (i=0; i < sizeof(buf[0])/sizeof(short); i++)
+    buf[c][0][i] = (buf[c][0][i] * val + x) >> s;
+      last[c] = mul[c];
+      for (r=0; r <= !c; r++) {
+    buf[c][1][width/2] = buf[c][2][width/2] = mul[c] << 7;
+    for (tree=1, col=width/2; col > 0; ) {
+      if ((tree = radc_token(tree))) {
+        col -= 2;
+        if (tree == 8)
+          FORYX buf[c][y][x] = radc_token(tree+10) * mul[c];
+        else
+          FORYX buf[c][y][x] = radc_token(tree+10) * 16 + PREDICTOR;
+      } else
+        do {
+          nreps = (col > 2) ? radc_token(9) + 1 : 1;
+          for (rep=0; rep < 8 && rep < nreps && col > 0; rep++) {
+        col -= 2;
+        FORYX buf[c][y][x] = PREDICTOR;
+        if (rep & 1) {
+          step = radc_token(10) << 4;
+          FORYX buf[c][y][x] += step;
+        }
+          }
+        } while (nreps == 9);
+    }
+    for (y=0; y < 2; y++)
+      for (x=0; x < width/2; x++) {
+        val = (buf[c][y+1][x] << 4) / mul[c];
+        if (val < 0) val = 0;
+        if (c)
+          BAYER(row+y*2+c-1,x*2+2-c) = val;
+        else
+          BAYER(row+r*2+y,x*2+y) = val;
+      }
+    memcpy (buf[c][0]+!c, buf[c][2], sizeof buf[c][0]-2*!c);
+      }
+    }
+    for (y=row; y < row+4; y++)
+      for (x=0; x < width; x++)
+    if ((x+y) & 1) {
+      val = (BAYER(y,x)-2048)*2 + (BAYER(y,x-1)+BAYER(y,x+1))/2;
+      if (val < 0) val = 0;
+      BAYER(y,x) = val;
+    }
+  }
+  maximum = 0x1fff;     /* wild guess */
+}
+
+#undef FORYX
+#undef PREDICTOR
+
+#ifndef HAVE_JPEG
+void kodak_jpeg_load_raw() {}
+#else
+
+static boolean
+fill_input_buffer (j_decompress_ptr cinfo)
+{
+  static char jpeg_buffer[4096];
+  size_t nbytes;
+
+  nbytes = fread (jpeg_buffer, 1, 4096, ifp);
+  swab (jpeg_buffer, jpeg_buffer, nbytes);
+  cinfo->src->next_input_byte = jpeg_buffer;
+  cinfo->src->bytes_in_buffer = nbytes;
+  return TRUE;
+}
+
+void
+kodak_jpeg_load_raw()
+{
+  struct jpeg_decompress_struct cinfo;
+  struct jpeg_error_mgr jerr;
+  JSAMPARRAY buf;
+  JSAMPLE (*pixel)[3];
+  int row, col;
+
+  cinfo.err = jpeg_std_error (&jerr);
+  jpeg_create_decompress (&cinfo);
+  jpeg_stdio_src (&cinfo, ifp);
+  cinfo.src->fill_input_buffer = fill_input_buffer;
+  jpeg_read_header (&cinfo, TRUE);
+  jpeg_start_decompress (&cinfo);
+  if ((cinfo.output_width      != width  ) ||
+      (cinfo.output_height*2   != height ) ||
+      (cinfo.output_components != 3      )) {
+    pm_error ("incorrect JPEG dimensions");
+  }
+  buf = (*cinfo.mem->alloc_sarray)
+        ((j_common_ptr) &cinfo, JPOOL_IMAGE, width*3, 1);
+
+  while (cinfo.output_scanline < cinfo.output_height) {
+    row = cinfo.output_scanline * 2;
+    jpeg_read_scanlines (&cinfo, buf, 1);
+    pixel = (void *) buf[0];
+    for (col=0; col < width; col+=2) {
+      BAYER(row+0,col+0) = pixel[col+0][1] << 1;
+      BAYER(row+1,col+1) = pixel[col+1][1] << 1;
+      BAYER(row+0,col+1) = pixel[col][0] + pixel[col+1][0];
+      BAYER(row+1,col+0) = pixel[col][2] + pixel[col+1][2];
+    }
+  }
+  jpeg_finish_decompress (&cinfo);
+  jpeg_destroy_decompress (&cinfo);
+  maximum = 0xff << 1;
+}
+
+#endif
+
+void
+kodak_dc120_load_raw()
+{
+  static const int mul[4] = { 162, 192, 187,  92 };
+  static const int add[4] = {   0, 636, 424, 212 };
+  unsigned char pixel[848];
+  int row, shift, col;
+
+  for (row=0; row < height; row++) {
+    fread (pixel, 848, 1, ifp);
+    shift = row * mul[row & 3] + add[row & 3];
+    for (col=0; col < width; col++)
+      BAYER(row,col) = (unsigned short) pixel[(col + shift) % 848];
+  }
+  maximum = 0xff;
+}
+
+void
+kodak_dc20_coeff (float const juice)
+{
+  static const float my_coeff[3][4] =
+  { {  2.25,  0.75, -1.75, -0.25 },
+    { -0.25,  0.75,  0.75, -0.25 },
+    { -0.25, -1.75,  0.75,  2.25 } };
+  static const float flat[3][4] =
+  { {  1, 0,   0,   0 },
+    {  0, 0.5, 0.5, 0 },
+    {  0, 0,   0,   1 } };
+  int r, g;
+
+  for (r=0; r < 3; r++)
+    for (g=0; g < 4; g++)
+      coeff[r][g] = my_coeff[r][g] * juice + flat[r][g] * (1-juice);
+  use_coeff = 1;
+}
+
+void
+kodak_easy_load_raw()
+{
+  unsigned char *pixel;
+  unsigned row, col, icol;
+
+  if (raw_width > width)
+    black = 0;
+  pixel = calloc (raw_width, sizeof *pixel);
+  merror (pixel, "kodak_easy_load_raw()");
+  for (row=0; row < height; row++) {
+    fread (pixel, 1, raw_width, ifp);
+    for (col=0; col < raw_width; col++) {
+      icol = col - left_margin;
+      if (icol < width)
+    BAYER(row,icol) = (unsigned short) curve[pixel[col]];
+      else
+    black += curve[pixel[col]];
+    }
+  }
+  free (pixel);
+  if (raw_width > width)
+    black /= (raw_width - width) * height;
+  if (!strncmp(model,"DC2",3))
+    black = 0;
+  maximum = curve[0xff];
+}
+
+void
+kodak_compressed_load_raw()
+{
+  unsigned char c, blen[256];
+  unsigned short raw[6];
+  unsigned row, col, len, save, i, israw=0, bits=0, pred[2];
+  INT64 bitbuf=0;
+  int diff;
+
+  assert(have64BitArithmetic);
+
+  for (row=0; row < height; row++)
+    for (col=0; col < width; col++)
+    {
+      if ((col & 255) == 0) {       /* Get the bit-lengths of the */
+    len = width - col;      /* next 256 pixel values      */
+    if (len > 256) len = 256;
+    save = ftell(ifp);
+    for (israw=i=0; i < len; i+=2) {
+      c = fgetc(ifp);
+      if ((blen[i+0] = c & 15) > 12 ||
+          (blen[i+1] = c >> 4) > 12 )
+        israw = 1;
+    }
+    bitbuf = bits = pred[0] = pred[1] = 0;
+    if (len % 8 == 4) {
+      bitbuf  = fgetc(ifp) << 8;
+      bitbuf += fgetc(ifp);
+      bits = 16;
+    }
+    if (israw)
+      fseek (ifp, save, SEEK_SET);
+      }
+      if (israw) {          /* If the data is not compressed */
+    switch (col & 7) {
+      case 0:
+        read_shorts(ifp, raw, 6);
+        diff = raw[0] >> 12 << 8 | raw[2] >> 12 << 4 | raw[4] >> 12;
+        break;
+      case 1:
+        diff = raw[1] >> 12 << 8 | raw[3] >> 12 << 4 | raw[5] >> 12;
+        break;
+      default:
+        diff = raw[(col & 7) - 2] & 0xfff;
+    }
+      } else {              /* If the data is compressed */
+    len = blen[col & 255];      /* Number of bits for this pixel */
+    if (bits < len) {       /* Got enough bits in the buffer? */
+      for (i=0; i < 32; i+=8)
+        bitbuf += (INT64) fgetc(ifp) << (bits+(i^8));
+      bits += 32;
+    }
+    diff = bitbuf & (0xffff >> (16-len));  /* Pull bits from buffer */
+    bitbuf >>= len;
+    bits -= len;
+    if ((diff & (1 << (len-1))) == 0)
+      diff -= (1 << len) - 1;
+    pred[col & 1] += diff;
+    diff = pred[col & 1];
+      }
+      BAYER(row,col) = curve[diff];
+    }
+}
+
+void
+kodak_yuv_load_raw()
+{
+  unsigned char 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];
+  unsigned short *ip;
+
+  assert(have64BitArithmetic);
+
+  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++) {
+    ip = image[(row+(i >> 1))*width + col+(i & 1)];
+    rgb[0] = y[i] + cr;
+    rgb[1] = y[i];
+    rgb[2] = y[i] + cb;
+    FORC3 if (rgb[c] > 0) ip[c] = curve[rgb[c]];
+      }
+    }
+  maximum = 0xe74;
+}
+
+static void  sony_decrypt (unsigned *data, int len, int start, int key)
+{
+  static uint32_t pad[128];
+  unsigned int p;
+
+  if (start) {
+    for (p=0; p < 4; p++)
+      pad[p] = key = key * 48828125 + 1;
+    pad[3] = pad[3] << 1 | (pad[0]^pad[2]) >> 31;
+    for (p=4; p < 127; p++)
+      pad[p] = (pad[p-4]^pad[p-2]) << 1 | (pad[p-3]^pad[p-1]) >> 31;
+
+    /* Convert to big-endian */
+
+    for (p=0; p < 127; p++) {
+        union {
+            unsigned char bytes[4];
+            uint32_t word;
+        } u;
+
+        u.bytes[0] = pad[p] >> 24;
+        u.bytes[1] = pad[p] >> 16;
+        u.bytes[2] = pad[p] >>  8;
+        u.bytes[3] = pad[p] >>  0;
+        
+        pad[p] = u.word;
+    }
+  }
+  while (len--)
+    *data++ ^= pad[p++ & 0x7f] = pad[(p+1) & 0x7f] ^ pad[(p+65) & 0x7f];
+}
+
+void
+sony_load_raw()
+{
+  unsigned char head[40];
+  struct pixel {
+      unsigned char bytes[2];
+  };
+  struct pixel * pixelrow;
+  unsigned i, key, row, col;
+
+  fseek (ifp, 200896, SEEK_SET);
+  fseek (ifp, (unsigned) fgetc(ifp)*4 - 1, SEEK_CUR);
+  order = 0x4d4d;
+  key = get4(ifp);
+  fseek (ifp, 164600, SEEK_SET);
+  fread (head, 1, 40, ifp);
+  sony_decrypt ((void *) head, 10, 1, key);
+  for (i=26; i-- > 22; )
+    key = key << 8 | head[i];
+  fseek (ifp, data_offset, SEEK_SET);
+  MALLOCARRAY(pixelrow, raw_width);
+  merror (pixelrow, "sony_load_raw()");
+  for (row=0; row < height; row++) {
+    fread (pixelrow, 2, raw_width, ifp);
+    sony_decrypt ((void *) pixelrow, raw_width/2, !row, key);
+    for (col=9; col < left_margin; col++)
+      black += pixelrow[col].bytes[0] * 256 + pixelrow[col].bytes[1];
+    for (col=0; col < width; col++)
+      BAYER(row,col) =
+          pixelrow[col+left_margin].bytes[0] * 256 +
+          pixelrow[col+left_margin].bytes[1];
+  }
+  free (pixelrow);
+  if (left_margin > 9)
+    black /= (left_margin-9) * height;
+  maximum = 0x3ff0;
+}
+
+void
+parse_minolta(FILE * const ifp)
+{
+  int save, tag, len, offset, high=0, wide=0;
+
+  fseek (ifp, 4, SEEK_SET);
+  offset = get4(ifp) + 8;
+  while ((save=ftell(ifp)) < offset) {
+    tag = get4(ifp);
+    len = get4(ifp);
+    switch (tag) {
+      case 0x505244:                /* PRD */
+    fseek (ifp, 8, SEEK_CUR);
+    high = get2(ifp);
+    wide = get2(ifp);
+    break;
+      case 0x574247:                /* WBG */
+    get4(ifp);
+    camera_red  = get2(ifp);
+    camera_red /= get2(ifp);
+    camera_blue = get2(ifp);
+    camera_blue = get2(ifp) / camera_blue;
+    break;
+      case 0x545457:                /* TTW */
+    parse_tiff(ifp, ftell(ifp));
+    }
+    fseek (ifp, save+len+8, SEEK_SET);
+  }
+  raw_height = high;
+  raw_width  = wide;
+  data_offset = offset;
+}
+
+/*
+   CIFF block 0x1030 contains an 8x8 white sample.
+   Load this into white[][] for use in scale_colors().
+ */
+static void  ciff_block_1030()
+{
+  static const unsigned short key[] = { 0x410, 0x45f3 };
+  int i, bpp, row, col, vbits=0;
+  unsigned long bitbuf=0;
+
+  get2(ifp);
+  if (get4(ifp) != 0x80008) return;
+  if (get4(ifp) == 0) return;
+  bpp = get2(ifp);
+  if (bpp != 10 && bpp != 12) return;
+  for (i=row=0; row < 8; row++)
+    for (col=0; col < 8; col++) {
+      if (vbits < bpp) {
+    bitbuf = bitbuf << 16 | (get2(ifp) ^ key[i++ & 1]);
+    vbits += 16;
+      }
+      white[row][col] =
+    bitbuf << (LONG_BIT - vbits) >> (LONG_BIT - bpp);
+      vbits -= bpp;
+    }
+}
+
+/*
+   Parse a CIFF file, better known as Canon CRW format.
+ */
+void 
+parse_ciff(FILE * const ifp, 
+           int    const offset, 
+           int    const length)
+{
+  int tboff, nrecs, i, 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 };
+  unsigned short key[] = { 0x410, 0x45f3 };
+
+  if (strcmp(model,"Canon PowerShot G6") &&
+      strcmp(model,"Canon PowerShot S70") &&
+      strcmp(model,"Canon PowerShot Pro1"))
+    key[0] = key[1] = 0;
+  fseek (ifp, offset+length-4, SEEK_SET);
+  tboff = get4(ifp) + offset;
+  fseek (ifp, tboff, SEEK_SET);
+  nrecs = get2(ifp);
+  for (i = 0; i < nrecs; i++) {
+    type = get2(ifp);
+    len  = get4(ifp);
+    roff = get4(ifp);
+    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(ifp);
+      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);
+    white[0][1] = get2(ifp);
+    white[0][0] = get2(ifp);
+    white[1][0] = get2(ifp);
+    white[1][1] = get2(ifp);
+      } 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(ifp) ^ key[0];
+    camera_red   =(get2(ifp) ^ key[1]) / camera_red;
+    camera_blue  = get2(ifp) ^ key[0];
+    camera_blue /= get2(ifp) ^ key[1];
+      } else if (!strcmp(model,"Canon PowerShot G6") ||
+         !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(ifp);
+      camera_red /= get2(ifp);
+      camera_blue = get2(ifp);
+      camera_blue = get2(ifp) / camera_blue;
+    }
+    if (type == 0x1030 && (wbi == 6 || wbi == 15)) {
+      fseek (ifp, aoff, SEEK_SET);  /* Get white sample */
+      ciff_block_1030();
+    }
+    if (type == 0x1031) {       /* Get the raw width and height */
+      fseek (ifp, aoff+2, SEEK_SET);
+      raw_width  = get2(ifp);
+      raw_height = get2(ifp);
+    }
+    if (type == 0x180e) {       /* Get the timestamp */
+      fseek (ifp, aoff, SEEK_SET);
+      timestamp = get4(ifp);
+    }
+    if (type == 0x580e)
+      timestamp = len;
+    if (type == 0x1810) {       /* Get the rotation */
+      fseek (ifp, aoff+12, SEEK_SET);
+      flip = get4(ifp);
+    }
+    if (type == 0x1835) {       /* Get the decoder table */
+      fseek (ifp, aoff, SEEK_SET);
+      crw_init_tables (get4(ifp));
+    }
+    if (type >> 8 == 0x28 || type >> 8 == 0x30) /* Get sub-tables */
+      parse_ciff(ifp, aoff, len);
+    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_rollei(FILE * const ifp)
+{
+  char line[128], *val;
+  int tx=0, ty=0;
+  struct tm t;
+  time_t ts;
+
+  fseek (ifp, 0, SEEK_SET);
+  do {
+    fgets (line, 128, ifp);
+    if ((val = strchr(line,'=')))
+      *val++ = 0;
+    else
+      val = line + strlen(line);
+    if (!strcmp(line,"DAT"))
+      sscanf (val, "%d.%d.%d", &t.tm_mday, &t.tm_mon, &t.tm_year);
+    if (!strcmp(line,"TIM"))
+      sscanf (val, "%d:%d:%d", &t.tm_hour, &t.tm_min, &t.tm_sec);
+    if (!strcmp(line,"HDR"))
+      data_offset = atoi(val);
+    if (!strcmp(line,"X  "))
+      raw_width = atoi(val);
+    if (!strcmp(line,"Y  "))
+      raw_height = atoi(val);
+    if (!strcmp(line,"TX "))
+      tx = atoi(val);
+    if (!strcmp(line,"TY "))
+      ty = atoi(val);
+  } while (strncmp(line,"EOHD",4));
+  t.tm_year -= 1900;
+  t.tm_mon -= 1;
+  putenv((char*)"TZ=");
+  if ((ts = mktime(&t)) > 0)
+    timestamp = ts;
+  data_offset += tx * ty * 2;
+  strcpy (make, "Rollei");
+  strcpy (model,"d530flex");
+}
+
+
+
+void
+parse_mos(FILE * const ifp, 
+          int    const offset)
+{
+    char data[40];
+    int skip, from, i, neut[4];
+
+    fseek (ifp, offset, SEEK_SET);
+    while (1) {
+        fread (data, 1, 8, ifp);
+        if (strcmp(data,"PKTS")) break;
+        fread (data, 1, 40, ifp);
+        skip = get4(ifp);
+        from = ftell(ifp);
+#ifdef USE_LCMS
+        if (!strcmp(data,"icc_camera_profile")) {
+            profile_length = skip;
+            profile_offset = from;
+        }
+#endif
+        if (!strcmp(data,"NeutObj_neutrals")) {
+            for (i=0; i < 4; i++)
+                fscanf (ifp, "%d", neut+i);
+            camera_red  = (float) neut[2] / neut[1];
+            camera_blue = (float) neut[2] / neut[3];
+        }
+        parse_mos(ifp, from);
+        fseek (ifp, skip+from, SEEK_SET);
+    }
+}
+
+void
+nikon_e950_coeff()
+{
+  int r, g;
+  static const float my_coeff[3][4] =
+  { { -1.936280,  1.800443, -1.448486,  2.584324 },
+    {  1.405365, -0.524955, -0.289090,  0.408680 },
+    { -1.204965,  1.082304,  2.941367, -1.818705 } };
+
+  for (r=0; r < 3; r++)
+    for (g=0; g < 4; g++)
+      coeff[r][g] = my_coeff[r][g];
+  use_coeff = 1;
+}
+
+
+
+static double getrat()
+{
+  double num = get4(ifp);
+  return num / get4(ifp);
+}
+
+
+
+static void 
+parse_makernote(FILE * const ifp)
+{
+  unsigned base=0, offset=0, entries, tag, type, len, save;
+  static const int size[] = { 1,1,1,2,4,8,1,1,2,4,8,4,8 };
+  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 (!strncmp (buf,"KC" ,2) ||     /* these aren't TIFF format */
+      !strncmp (buf,"MLY",3)) return;
+  if (!strcmp (buf,"Nikon")) {
+    base = ftell(ifp);
+    order = get2(ifp);
+    if (get2(ifp) != 42) goto quit;
+    offset = get4(ifp);
+    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") ||
+       !strcmp (buf,"QVC"))
+    fseek (ifp, -4, SEEK_CUR);
+  else fseek (ifp, -10, SEEK_CUR);
+
+  entries = get2(ifp);
+  while (entries--) {
+    tag  = get2(ifp);
+    type = get2(ifp);
+    len  = get4(ifp);
+    save = ftell(ifp);
+    if (len * size[type < 13 ? type:0] > 4)
+      fseek (ifp, get4(ifp)+base, SEEK_SET);
+
+    if (tag == 0xc && len == 4) {
+      camera_red  = getrat();
+      camera_blue = getrat();
+    }
+    if (tag == 0x14 && len == 2560 && type == 7) {
+      fseek (ifp, 1248, SEEK_CUR);
+      goto get2_256;
+    }
+    if (strstr(make,"PENTAX")) {
+      if (tag == 0x1b) tag = 0x1018;
+      if (tag == 0x1c) tag = 0x1017;
+    }
+    if (tag == 0x8c)
+      nikon_curve_offset = ftell(ifp) + 2112;
+    if (tag == 0x96)
+      nikon_curve_offset = ftell(ifp) + 2;
+    if (tag == 0x97) {
+      if (!strcmp(model,"NIKON D100 ")) {
+    fseek (ifp, 72, SEEK_CUR);
+    camera_red  = get2(ifp) / 256.0;
+    camera_blue = get2(ifp) / 256.0;
+      } else if (!strcmp(model,"NIKON D2H")) {
+    fseek (ifp, 10, SEEK_CUR);
+    goto get2_rggb;
+      } else if (!strcmp(model,"NIKON D70")) {
+    fseek (ifp, 20, SEEK_CUR);
+    camera_red  = get2(ifp);
+    camera_red /= get2(ifp);
+    camera_blue = get2(ifp);
+    camera_blue/= get2(ifp);
+      }
+    }
+    if (tag == 0xe0 && len == 17) {
+      get2(ifp);
+      raw_width  = get2(ifp);
+      raw_height = get2(ifp);
+    }
+    if (tag == 0x200 && len == 4)
+      black = (get2(ifp)+get2(ifp)+get2(ifp)+get2(ifp))/4;
+    if (tag == 0x201 && len == 4) {
+      camera_red  = get2(ifp);
+      camera_red /= get2(ifp);
+      camera_blue = get2(ifp);
+      camera_blue = get2(ifp) / camera_blue;
+    }
+    if (tag == 0x401 && len == 4) {
+      black = (get4(ifp)+get4(ifp)+get4(ifp)+get4(ifp))/4;
+    }
+    if (tag == 0xe80 && len == 256 && type == 7) {
+      fseek (ifp, 48, SEEK_CUR);
+      camera_red  = get2(ifp) * 508 * 1.078 / 0x10000;
+      camera_blue = get2(ifp) * 382 * 1.173 / 0x10000;
+    }
+    if (tag == 0xf00 && len == 614 && type == 7) {
+      fseek (ifp, 188, SEEK_CUR);
+      goto get2_256;
+    }
+    if (tag == 0x1017)
+      camera_red  = get2(ifp) / 256.0;
+    if (tag == 0x1018)
+      camera_blue = get2(ifp) / 256.0;
+    if (tag == 0x2011 && len == 2) {
+get2_256:
+      order = 0x4d4d;
+      camera_red  = get2(ifp) / 256.0;
+      camera_blue = get2(ifp) / 256.0;
+    }
+    if (tag == 0x4001) {
+      fseek (ifp, strstr(model,"EOS-1D") ? 68:50, SEEK_CUR);
+get2_rggb:
+      camera_red  = get2(ifp);
+      camera_red /= get2(ifp);
+      camera_blue = get2(ifp);
+      camera_blue = get2(ifp) / camera_blue;
+    }
+    fseek (ifp, save+4, SEEK_SET);
+  }
+quit:
+  order = sorder;
+}
+
+
+
+static void
+get_timestamp(FILE * const ifp)
+{
+/*
+   Since the TIFF DateTime string has no timezone information,
+   assume that the camera's clock was set to Universal Time.
+ */
+  struct tm t;
+  time_t ts;
+
+  if (fscanf (ifp, "%d:%d:%d %d:%d:%d", &t.tm_year, &t.tm_mon,
+    &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec) != 6)
+    return;
+  t.tm_year -= 1900;
+  t.tm_mon -= 1;
+  putenv((char*)"TZ=UTC");   /* Remove this to assume local time */
+  if ((ts = mktime(&t)) > 0)
+    timestamp = ts;
+}
+
+static void 
+parse_exif(FILE * const ifp, int base)
+{
+  int entries, tag, type, len, val, save;
+
+  entries = get2(ifp);
+  while (entries--) {
+    tag  = get2(ifp);
+    type = get2(ifp);
+    len  = get4(ifp);
+    val  = get4(ifp);
+    save = ftell(ifp);
+    fseek (ifp, base+val, SEEK_SET);
+    if (tag == 0x9003 || tag == 0x9004)
+      get_timestamp(ifp);
+    if (tag == 0x927c) {
+      if (!strncmp(make,"SONY",4))
+    data_offset = base+val+len;
+      else
+    parse_makernote(ifp);
+    }
+    fseek (ifp, save, SEEK_SET);
+  }
+}
+
+static int 
+parse_tiff_ifd(FILE * const ifp, int base, int level)
+{
+  unsigned entries, tag, type, len, plen=16, save;
+  int done=0, use_cm=0, cfa, i, j, c;
+  static const int size[] = { 1,1,1,2,4,8,1,1,2,4,8,4,8 };
+  char software[64];
+  static const int flip_map[] = { 0,1,3,2,4,6,7,5 };
+  unsigned char cfa_pat[16], cfa_pc[] = { 0,1,2,3 }, tab[256];
+  unsigned short scale[4];
+  double dblack, cc[4][4], cm[4][3];
+  double ab[]={ 1,1,1,1 }, asn[] = { 0,0,0,0 }, xyz[] = { 1,1,1 };
+
+  for (j=0; j < 4; j++)
+    for (i=0; i < 4; i++)
+      cc[j][i] = i == j;
+  entries = get2(ifp);
+  if (entries > 512) return 1;
+  while (entries--) {
+    tag  = get2(ifp);
+    type = get2(ifp);
+    len  = get4(ifp);
+    save = ftell(ifp);
+    if (tag > 50700 && tag < 50800) done = 1;
+    if (len * size[type < 13 ? type:0] > 4)
+      fseek (ifp, get4(ifp)+base, SEEK_SET);
+    switch (tag) {
+      case 0x11:
+    camera_red  = get4(ifp) / 256.0;
+    break;
+      case 0x12:
+    camera_blue = get4(ifp) / 256.0;
+    break;
+      case 0x100:           /* ImageWidth */
+    if (strcmp(make,"Canon") || level)
+      raw_width = type==3 ? get2(ifp) : get4(ifp);
+    break;
+      case 0x101:           /* ImageHeight */
+    if (strcmp(make,"Canon") || level)
+      raw_height = type==3 ? get2(ifp) : get4(ifp);
+    break;
+      case 0x102:           /* Bits per sample */
+    fuji_secondary = len == 2;
+    if (level) maximum = (1 << get2(ifp)) - 1;
+    break;
+      case 0x103:           /* Compression */
+    tiff_data_compression = get2(ifp);
+    break;
+      case 0x106:           /* Kodak color format */
+    kodak_data_compression = get2(ifp);
+    break;
+      case 0x10f:           /* Make */
+    fgets (make, 64, ifp);
+    break;
+      case 0x110:           /* Model */
+    fgets (model, 64, ifp);
+    break;
+      case 0x111:           /* StripOffset */
+    data_offset = get4(ifp);
+    break;
+      case 0x112:           /* Orientation */
+    flip = flip_map[(get2(ifp)-1) & 7];
+    break;
+      case 0x115:           /* SamplesPerPixel */
+    tiff_samples = get2(ifp);
+    break;
+      case 0x131:           /* Software tag */
+    fgets (software, 64, ifp);
+    if (!strncmp(software,"Adobe",5))
+      make[0] = 0;
+    break;
+      case 0x132:           /* DateTime tag */
+    get_timestamp(ifp);
+    break;
+      case 0x144:           /* TileOffsets */
+    if (level) {
+      data_offset = ftell(ifp);
+    } else {
+      strcpy (make, "Leaf");
+      data_offset = get4(ifp);
+    }
+    break;
+      case 0x14a:           /* SubIFD tag */
+    if (len > 2 && !is_dng && !strcmp(make,"Kodak"))
+        len = 2;
+    while (len--) {
+      i = ftell(ifp);
+      fseek (ifp, get4(ifp)+base, SEEK_SET);
+      if (parse_tiff_ifd(ifp, base, level+1)) break;
+      fseek (ifp, i+4, SEEK_SET);
+    }
+    break;
+      case 33405:           /* Model2 */
+    fgets (model2, 64, ifp);
+    break;
+      case 33422:           /* CFAPattern */
+    if ((plen=len) > 16) plen = 16;
+    fread (cfa_pat, 1, plen, ifp);
+    for (colors=cfa=i=0; i < plen; i++) {
+      colors += !(cfa & (1 << cfa_pat[i]));
+      cfa |= 1 << cfa_pat[i];
+    }
+    if (cfa == 070) memcpy (cfa_pc,"\003\004\005",3);   /* CMY */
+    if (cfa == 072) memcpy (cfa_pc,"\005\003\004\001",4);   /* GMCY */
+    goto guess_cfa_pc;
+      case 34665:           /* EXIF tag */
+    fseek (ifp, get4(ifp)+base, SEEK_SET);
+    parse_exif(ifp, base);
+    break;
+      case 50706:           /* DNGVersion */
+    is_dng = 1;
+    if (flip == 7) flip = 4;    /* Adobe didn't read the TIFF spec. */
+    break;
+      case 50710:           /* CFAPlaneColor */
+    if (len > 4) len = 4;
+    colors = len;
+    fread (cfa_pc, 1, colors, ifp);
+guess_cfa_pc:
+    FORC4 tab[cfa_pc[c]] = c;
+    for (i=16; i--; )
+      filters = filters << 2 | tab[cfa_pat[i % plen]];
+    break;
+      case 50711:           /* CFALayout */
+    if (get2(ifp) == 2) {
+      fuji_width = (raw_width+1)/2;
+      filters = 0x49494949;
+    }
+    break;
+      case 0x123:
+      case 0x90d:
+      case 50712:           /* LinearizationTable */
+    if (len > 0x1000)
+        len = 0x1000;
+    read_shorts(ifp, curve, len);
+    for (i=len; i < 0x1000; i++)
+      maximum = curve[i] = curve[i-1];
+    break;
+      case 50714:           /* BlackLevel */
+      case 50715:           /* BlackLevelDeltaH */
+      case 50716:           /* BlackLevelDeltaV */
+    for (dblack=i=0; i < len; i++)
+      dblack += getrat();
+    black += dblack/len + 0.5;
+    break;
+      case 50717:           /* WhiteLevel */
+    maximum = get2(ifp);
+    break;
+      case 50718:           /* DefaultScale */
+    for (i=0; i < 4; i++)
+      scale[i] = get4(ifp);
+    if (scale[1]*scale[2] == 2*scale[0]*scale[3]) ymag = 2;
+    break;
+      case 50721:           /* ColorMatrix1 */
+      case 50722:           /* ColorMatrix2 */
+    FORC4 for (j=0; j < 3; j++)
+      cm[c][j] = getrat();
+    use_cm = 1;
+    break;
+      case 50723:           /* CameraCalibration1 */
+      case 50724:           /* CameraCalibration2 */
+    for (i=0; i < colors; i++)
+      FORC4 cc[i][c] = getrat();    
+      case 50727:           /* AnalogBalance */
+    FORC4 ab[c] = getrat();
+    break;
+      case 50728:           /* AsShotNeutral */
+    FORC4 asn[c] = getrat();
+    break;
+      case 50729:           /* AsShotWhiteXY */
+    xyz[0] = getrat();
+    xyz[1] = getrat();
+    xyz[2] = 1 - xyz[0] - xyz[1];
+    }
+    fseek (ifp, save+4, SEEK_SET);
+  }
+  for (i=0; i < colors; i++)
+    FORC4 cc[i][c] *= ab[i];
+  if (use_cm)
+    dng_coeff (cc, cm, xyz);
+  if (asn[0])
+    FORC4 pre_mul[c] = 1 / asn[c];
+  if (!use_cm)
+    FORC4 pre_mul[c] /= cc[c][c];
+
+  if (is_dng || level) return done;
+
+  if ((raw_height & 1) && !strncmp (make,"OLYMPUS",7))
+       raw_height++;
+
+  if (make[0] == 0 && raw_width == 680 && raw_height == 680) {
+    strcpy (make, "Imacon");
+    strcpy (model,"Ixpress");
+  }
+  return done;
+}
+
+void
+parse_tiff(FILE * const ifp, int base)
+{
+  int doff;
+
+  fseek (ifp, base, SEEK_SET);
+  order = get2(ifp);
+  if (order != 0x4949 && order != 0x4d4d) return;
+  get2(ifp);
+  while ((doff = get4(ifp))) {
+    fseek (ifp, doff+base, SEEK_SET);
+    if (parse_tiff_ifd(ifp, base, 0)) break;
+  }
+  if (!is_dng && !strncmp(make,"Kodak",5)) {
+    fseek (ifp, 12+base, SEEK_SET);
+    parse_tiff_ifd(ifp, base, 2);
+  }
+}
+
+
+
+/*
+   Many cameras have a "debug mode" that writes JPEG and raw
+   at the same time.  The raw file has no header, so try to
+   to open the matching JPEG file and read its metadata.
+ */
+void
+parse_external_jpeg(const char * const ifname)
+{
+    const char *file, *ext;
+    char * jfile;
+    char * jext;
+    char * jname;
+
+    ext  = strrchr (ifname, '.');
+    file = strrchr (ifname, '/');
+    if (!file) file = strrchr (ifname, '\\');
+    if (!file) file = ifname-1;
+    file++;
+    if (strlen(ext) != 4 || ext-file != 8) return;
+    jname = malloc (strlen(ifname) + 1);
+    merror (jname, "parse_external()");
+    strcpy (jname, ifname);
+    jfile = jname + (file - ifname);
+    jext  = jname + (ext  - ifname);
+    if (strcasecmp (ext, ".jpg")) {
+        strcpy (jext, isupper(ext[1]) ? ".JPG":".jpg");
+        memcpy (jfile, file+4, 4);
+        memcpy (jfile+4, file, 4);
+    } else
+        while (isdigit(*--jext)) {
+            if (*jext != '9') {
+                (*jext)++;
+                break;
+            }
+            *jext = '0';
+        }
+    if (strcmp (jname, ifname)) {
+        FILE * ifP;
+        ifP = fopen (jname, "rb");
+        if (ifP) {
+            if (verbose)
+                pm_message ("Reading metadata from %s...", jname);
+            parse_tiff(ifP, 12);
+            fclose (ifP);
+        }
+    }
+    if (!timestamp)
+        pm_message ( "Failed to read metadata from %s", jname);
+    free (jname);
+}
+
diff --git a/converter/other/cameratopam/camera.h b/converter/other/cameratopam/camera.h
new file mode 100644
index 00000000..a1e884cf
--- /dev/null
+++ b/converter/other/cameratopam/camera.h
@@ -0,0 +1,131 @@
+#include <stdio.h>
+
+void 
+parse_ciff(FILE * const ifp,
+           int    const offset,
+           int    const length);
+
+void
+parse_external_jpeg(const char * const ifname);
+
+void
+parse_tiff(FILE * const ifp, int base);
+
+void
+parse_minolta(FILE * const ifp);
+
+void
+parse_rollei(FILE * const ifp);
+
+void
+parse_mos(FILE * const ifp,
+          int    const offset);
+
+void
+adobe_dng_load_raw_lj(void);
+
+void
+adobe_dng_load_raw_nc(void);
+
+int
+nikon_is_compressed(void);
+
+void
+nikon_compressed_load_raw(void);
+
+void
+nikon_e950_load_raw(void);
+
+void
+nikon_e950_coeff(void);
+
+int
+nikon_e990(void);
+
+int
+nikon_e2100(void);
+
+void
+nikon_e2100_load_raw(void);
+
+int
+minolta_z2(void);
+
+void
+fuji_s2_load_raw(void);
+
+void
+fuji_s3_load_raw(void);
+
+void
+fuji_s5000_load_raw(void);
+
+void
+unpacked_load_raw(void);
+
+void
+fuji_s7000_load_raw(void);
+
+void
+fuji_f700_load_raw(void);
+
+void
+packed_12_load_raw(void);
+
+void
+eight_bit_load_raw(void);
+
+void
+phase_one_load_raw(void);
+
+void
+ixpress_load_raw(void);
+
+void
+leaf_load_raw(void);
+
+void
+olympus_e300_load_raw(void);
+
+void
+olympus_cseries_load_raw(void);
+
+void
+sony_load_raw(void);
+
+void
+kodak_easy_load_raw(void);
+
+void
+kodak_compressed_load_raw(void);
+
+void
+kodak_yuv_load_raw(void);
+
+void
+kodak_dc20_coeff (float const juice);
+
+void
+kodak_radc_load_raw(void);
+
+void
+kodak_jpeg_load_raw(void);
+
+void
+kodak_dc120_load_raw(void);
+
+void
+rollei_load_raw(void);
+
+void
+casio_qv5700_load_raw(void);
+
+void
+nucore_load_raw(void);
+
+void
+nikon_load_raw(void);
+
+int
+pentax_optio33(void);
+
diff --git a/converter/other/cameratopam/cameratopam.c b/converter/other/cameratopam/cameratopam.c
new file mode 100644
index 00000000..92773c91
--- /dev/null
+++ b/converter/other/cameratopam/cameratopam.c
@@ -0,0 +1,906 @@
+/*
+   This is derived from Dave Coffin's raw photo decoder, dcraw.c,
+   Copyright 1997-2005 by Dave Coffin, dcoffin a cybercom o net.
+
+   See the COPYRIGHT file in the same directory as this file for
+   information on copyright and licensing.
+ */
+
+
+#define _BSD_SOURCE 1   /* Make sure string.h contains strcasecmp() */
+#define _XOPEN_SOURCE  /* Make sure unistd.h contains swab() */
+
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __CYGWIN__
+#include <io.h>
+#endif
+#ifdef WIN32
+  #include <winsock2.h>
+  #pragma comment(lib, "ws2_32.lib")
+  #define strcasecmp stricmp
+  typedef __int64 INT64;
+  static bool const have64BitArithmetic = true;
+#else
+  #include <unistd.h>
+#endif
+
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
+
+#include "global_variables.h"
+#include "util.h"
+#include "decode.h"
+#include "identify.h"
+#include "bayer.h"
+#include "foveon.h"
+#include "dng.h"
+
+#if HAVE_INT64
+   typedef int64_t INT64;
+   static bool const have64BitArithmetic = true;
+#else
+   /* We define INT64 to something that lets the code compile, but we
+      should not execute any INT64 code, because it will get the wrong
+      result.  */
+   typedef int INT64;
+   static bool const have64BitArithmetic = false;
+#endif
+
+/*
+   All global variables are defined here, and all functions that
+   access them are prefixed with "CLASS".  Note that a thread-safe
+   C++ class cannot have non-const static local variables.
+ */
+FILE * ifp;
+short order;
+char make[64], model[70], model2[64], *meta_data;
+time_t timestamp;
+int data_offset, meta_offset, meta_length;
+int tiff_data_compression, kodak_data_compression;
+int raw_height, raw_width, top_margin, left_margin;
+int height, width, fuji_width, colors, tiff_samples;
+int black, maximum, clip_max;
+int iheight, iwidth, shrink;
+int is_dng, is_canon, is_foveon, use_coeff, use_gamma;
+int trim, flip, xmag, ymag;
+int zero_after_ff;
+unsigned filters;
+unsigned short (*image)[4], white[8][8], curve[0x1000];
+int fuji_secondary;
+float cam_mul[4], pre_mul[4], coeff[3][4];
+int histogram[3][0x2000];
+jmp_buf failure;
+bool use_secondary;
+bool verbose;
+
+#ifdef USE_LCMS
+#include <lcms.h>
+int profile_offset, profile_length;
+#endif
+
+#define CLASS
+
+#define FORC3 for (c=0; c < 3; c++)
+#define FORC4 for (c=0; c < colors; c++)
+
+static void CLASS merror (const void *ptr, const char *where)
+{
+    if (ptr == NULL)
+        pm_error ("Out of memory in %s", where);
+}
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;  /* "-" means Standard Input */
+    float bright;
+    float red_scale;
+    float blue_scale;
+    const char * profile;
+    unsigned int identify_only;
+    unsigned int verbose;
+    unsigned int half_size;
+    unsigned int four_color_rgb;
+    unsigned int document_mode;
+    unsigned int quick_interpolate;
+    unsigned int use_auto_wb;
+    unsigned int use_camera_wb;
+    unsigned int use_camera_rgb;
+    unsigned int use_secondary;
+    unsigned int no_clip_color;
+    unsigned int linear;
+};
+
+
+static struct cmdlineInfo cmdline;
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo *cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that many of the strings that this function returns in the
+   *cmdlineP structure are actually in the supplied argv array.  And
+   sometimes, one of these strings is actually just a suffix of an entry
+   in argv!
+-----------------------------------------------------------------------------*/
+    optStruct3 opt;
+    optEntry *option_def;
+    unsigned int option_def_index;
+    unsigned int brightSpec, red_scaleSpec, blue_scaleSpec,
+        profileSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;
+    opt.allowNegNum = FALSE;
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "bright", 
+            OPT_FLOAT,   &cmdlineP->bright,     &brightSpec, 0);
+    OPTENT3(0, "red_scale", 
+            OPT_FLOAT,   &cmdlineP->red_scale,  &red_scaleSpec, 0);
+    OPTENT3(0, "blue_scale", 
+            OPT_FLOAT,   &cmdlineP->blue_scale, &blue_scaleSpec, 0);
+    OPTENT3(0, "profile", 
+            OPT_STRING,  &cmdlineP->profile,    &profileSpec, 0);
+    OPTENT3(0,   "identify_only",   
+            OPT_FLAG,    NULL, &cmdlineP->identify_only, 0);
+    OPTENT3(0,   "verbose",   
+            OPT_FLAG,    NULL, &cmdlineP->verbose, 0);
+    OPTENT3(0,   "half_size",   
+            OPT_FLAG,    NULL, &cmdlineP->half_size, 0);
+    OPTENT3(0,   "four_color_rgb",   
+            OPT_FLAG,    NULL, &cmdlineP->four_color_rgb, 0);
+    OPTENT3(0,   "document_mode",   
+            OPT_FLAG,    NULL, &cmdlineP->document_mode, 0);
+    OPTENT3(0,   "quick_interpolate",   
+            OPT_FLAG,    NULL, &cmdlineP->quick_interpolate, 0);
+    OPTENT3(0,   "balance_auto",   
+            OPT_FLAG,    NULL, &cmdlineP->use_auto_wb, 0);
+    OPTENT3(0,   "balance_camera",   
+            OPT_FLAG,    NULL, &cmdlineP->use_camera_wb, 0);
+    OPTENT3(0,   "use_secondary",   
+            OPT_FLAG,    NULL, &cmdlineP->use_secondary, 0);
+    OPTENT3(0,   "no_clip_color",   
+            OPT_FLAG,    NULL, &cmdlineP->no_clip_color, 0);
+    OPTENT3(0,   "rgb",   
+            OPT_FLAG,    NULL, &cmdlineP->use_camera_rgb, 0);
+    OPTENT3(0,   "linear",   
+            OPT_FLAG,    NULL, &cmdlineP->linear, 0);
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+
+    if (!brightSpec)
+        cmdlineP->bright = 1.0;
+    if (!red_scaleSpec)
+        cmdlineP->red_scale = 1.0;
+    if (!blue_scaleSpec)
+        cmdlineP->blue_scale = 1.0;
+    if (!profileSpec)
+        cmdlineP->profile = NULL;
+
+
+    if (argc - 1 == 0)
+        cmdlineP->inputFileName = strdup("-");  /* he wants stdin */
+    else if (argc - 1 == 1)
+        cmdlineP->inputFileName = strdup(argv[1]);
+    else 
+        pm_error("Too many arguments.  The only argument accepted "
+                 "is the input file name");
+}
+
+  
+/*
+   Seach from the current directory up to the root looking for
+   a ".badpixels" file, and fix those pixels now.
+ */
+static void CLASS bad_pixels()
+{
+  FILE *fp=NULL;
+  char *fname, *cp, line[128];
+  int len, time, row, col, r, c, rad, tot, n, fixed=0;
+
+  if (!filters) return;
+  for (len=16 ; ; len *= 2) {
+    fname = malloc (len);
+    if (!fname) return;
+    if (getcwd (fname, len-12)) break;
+    free (fname);
+    if (errno != ERANGE) return;
+  }
+#ifdef WIN32
+  if (fname[1] == ':')
+    memmove (fname, fname+2, len-2);
+  for (cp=fname; *cp; cp++)
+    if (*cp == '\\') *cp = '/';
+#endif
+  cp = fname + strlen(fname);
+  if (cp[-1] == '/') cp--;
+  while (*fname == '/') {
+    strcpy (cp, "/.badpixels");
+    if ((fp = fopen (fname, "r"))) break;
+    if (cp == fname) break;
+    while (*--cp != '/');
+  }
+  free (fname);
+  if (!fp) return;
+  while (fgets (line, 128, fp)) {
+    cp = strchr (line, '#');
+    if (cp) *cp = 0;
+    if (sscanf (line, "%d %d %d", &col, &row, &time) != 3) continue;
+    if ((unsigned) col >= width || (unsigned) row >= height) continue;
+    if (time > timestamp) continue;
+    for (tot=n=0, rad=1; rad < 3 && n==0; rad++)
+      for (r = row-rad; r <= row+rad; r++)
+    for (c = col-rad; c <= col+rad; c++)
+      if ((unsigned) r < height && (unsigned) c < width &&
+        (r != row || c != col) && FC(r,c) == FC(row,col)) {
+        tot += BAYER(r,c);
+        n++;
+      }
+    BAYER(row,col) = tot/n;
+    if (cmdline.verbose) {
+      if (!fixed++)
+          pm_message ("Fixed bad pixels at: %d,%d", col, row);
+    }
+  }
+  fclose (fp);
+}
+
+static void CLASS scale_colors()
+{
+  int row, col, c, val, shift=0;
+  int min[4], max[4], count[4];
+  double sum[4], dmin;
+
+  maximum -= black;
+  if (cmdline.use_auto_wb || (cmdline.use_camera_wb && camera_red == -1)) {
+    FORC4 min[c] = INT_MAX;
+    FORC4 max[c] = count[c] = sum[c] = 0;
+    for (row=0; row < height; row++)
+      for (col=0; col < width; col++)
+    FORC4 {
+      val = image[row*width+col][c];
+      if (!val) continue;
+      if (min[c] > val) min[c] = val;
+      if (max[c] < val) max[c] = val;
+      val -= black;
+      if (val > maximum-25) continue;
+      if (val < 0) val = 0;
+      sum[c] += val;
+      count[c]++;
+    }
+    FORC4 pre_mul[c] = count[c] / sum[c];
+  }
+  if (cmdline.use_camera_wb && camera_red != -1) {
+    FORC4 count[c] = sum[c] = 0;
+    for (row=0; row < 8; row++)
+      for (col=0; col < 8; col++) {
+    c = FC(row,col);
+    if ((val = white[row][col] - black) > 0)
+      sum[c] += val;
+    count[c]++;
+      }
+    val = 1;
+    FORC4 if (sum[c] == 0) val = 0;
+    if (val)
+      FORC4 pre_mul[c] = count[c] / sum[c];
+    else if (camera_red && camera_blue)
+      memcpy (pre_mul, cam_mul, sizeof pre_mul);
+    else
+      pm_message ("Cannot use camera white balance.");
+  }
+  if (!use_coeff) {
+    pre_mul[0] *= cmdline.red_scale;
+    pre_mul[2] *= cmdline.blue_scale;
+  }
+  dmin = DBL_MAX;
+  FORC4 if (dmin > pre_mul[c])
+        dmin = pre_mul[c];
+  FORC4 pre_mul[c] /= dmin;
+
+  while (maximum << shift < 0x8000) shift++;
+  FORC4 pre_mul[c] *= 1 << shift;
+  maximum <<= shift;
+
+  if (cmdline.linear || cmdline.bright < 1) {
+      maximum *= cmdline.bright;
+      if (maximum > 0xffff)
+          maximum = 0xffff;
+      FORC4 pre_mul[c] *= cmdline.bright;
+  }
+  if (cmdline.verbose) {
+    fprintf (stderr, "Scaling with black=%d, pre_mul[] =", black);
+    FORC4 fprintf (stderr, " %f", pre_mul[c]);
+    fputc ('\n', stderr);
+  }
+  clip_max = cmdline.no_clip_color ? 0xffff : maximum;
+  for (row=0; row < height; row++)
+    for (col=0; col < width; col++)
+      FORC4 {
+    val = image[row*width+col][c];
+    if (!val) continue;
+    val -= black;
+    val *= pre_mul[c];
+    if (val < 0) val = 0;
+    if (val > clip_max) val = clip_max;
+    image[row*width+col][c] = val;
+      }
+}
+
+/*
+   This algorithm is officially called:
+
+   "Interpolation using a Threshold-based variable number of gradients"
+
+   described in http://www-ise.stanford.edu/~tingchen/algodep/vargra.html
+
+   I've extended the basic idea to work with non-Bayer filter arrays.
+   Gradients are numbered clockwise from NW=0 to W=7.
+ */
+static void CLASS vng_interpolate()
+{
+  static const signed char *cp, terms[] = {
+    -2,-2,+0,-1,0,(char)0x01, -2,-2,+0,+0,1,(char)0x01, -2,-1,-1,+0,0,(char)0x01,
+    -2,-1,+0,-1,0,(char)0x02, -2,-1,+0,+0,0,(char)0x03, -2,-1,+0,+1,1,(char)0x01,
+    -2,+0,+0,-1,0,(char)0x06, -2,+0,+0,+0,1,(char)0x02, -2,+0,+0,+1,0,(char)0x03,
+    -2,+1,-1,+0,0,(char)0x04, -2,+1,+0,-1,1,(char)0x04, -2,+1,+0,+0,0,(char)0x06,
+    -2,+1,+0,+1,0,(char)0x02, -2,+2,+0,+0,1,(char)0x04, -2,+2,+0,+1,0,(char)0x04,
+    -1,-2,-1,+0,0,(char)0x80, -1,-2,+0,-1,0,(char)0x01, -1,-2,+1,-1,0,(char)0x01,
+    -1,-2,+1,+0,1,(char)0x01, -1,-1,-1,+1,0,(char)0x88, -1,-1,+1,-2,0,(char)0x40,
+    -1,-1,+1,-1,0,(char)0x22, -1,-1,+1,+0,0,(char)0x33, -1,-1,+1,+1,1,(char)0x11,
+    -1,+0,-1,+2,0,(char)0x08, -1,+0,+0,-1,0,(char)0x44, -1,+0,+0,+1,0,(char)0x11,
+    -1,+0,+1,-2,1,(char)0x40, -1,+0,+1,-1,0,(char)0x66, -1,+0,+1,+0,1,(char)0x22,
+    -1,+0,+1,+1,0,(char)0x33, -1,+0,+1,+2,1,(char)0x10, -1,+1,+1,-1,1,(char)0x44,
+    -1,+1,+1,+0,0,(char)0x66, -1,+1,+1,+1,0,(char)0x22, -1,+1,+1,+2,0,(char)0x10,
+    -1,+2,+0,+1,0,(char)0x04, -1,+2,+1,+0,1,(char)0x04, -1,+2,+1,+1,0,(char)0x04,
+    +0,-2,+0,+0,1,(char)0x80, +0,-1,+0,+1,1,(char)0x88, +0,-1,+1,-2,0,(char)0x40,
+    +0,-1,+1,+0,0,(char)0x11, +0,-1,+2,-2,0,(char)0x40, +0,-1,+2,-1,0,(char)0x20,
+    +0,-1,+2,+0,0,(char)0x30, +0,-1,+2,+1,1,(char)0x10, +0,+0,+0,+2,1,(char)0x08,
+    +0,+0,+2,-2,1,(char)0x40, +0,+0,+2,-1,0,(char)0x60, +0,+0,+2,+0,1,(char)0x20,
+    +0,+0,+2,+1,0,(char)0x30, +0,+0,+2,+2,1,(char)0x10, +0,+1,+1,+0,0,(char)0x44,
+    +0,+1,+1,+2,0,(char)0x10, +0,+1,+2,-1,1,(char)0x40, +0,+1,+2,+0,0,(char)0x60,
+    +0,+1,+2,+1,0,(char)0x20, +0,+1,+2,+2,0,(char)0x10, +1,-2,+1,+0,0,(char)0x80,
+    +1,-1,+1,+1,0,(char)0x88, +1,+0,+1,+2,0,(char)0x08, +1,+0,+2,-1,0,(char)0x40,
+    +1,+0,+2,+1,0,(char)0x10
+  }, chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 };
+  unsigned short (*brow[5])[4], *pix;
+  int code[8][2][320], *ip, gval[8], gmin, gmax, sum[4];
+  int row, col, shift, x, y, x1, x2, y1, y2, t, weight, grads, color, diag;
+  int g, diff, thold, num, c;
+
+  for (row=0; row < 8; row++) {     /* Precalculate for bilinear */
+    for (col=1; col < 3; col++) {
+      ip = code[row][col & 1];
+      memset (sum, 0, sizeof sum);
+      for (y=-1; y <= 1; y++)
+    for (x=-1; x <= 1; x++) {
+      shift = (y==0) + (x==0);
+      if (shift == 2) continue;
+      color = FC(row+y,col+x);
+      *ip++ = (width*y + x)*4 + color;
+      *ip++ = shift;
+      *ip++ = color;
+      sum[color] += 1 << shift;
+    }
+      FORC4
+    if (c != FC(row,col)) {
+      *ip++ = c;
+      *ip++ = sum[c];
+    }
+    }
+  }
+  for (row=1; row < height-1; row++) {  /* Do bilinear interpolation */
+    for (col=1; col < width-1; col++) {
+      pix = image[row*width+col];
+      ip = code[row & 7][col & 1];
+      memset (sum, 0, sizeof sum);
+      for (g=8; g--; ) {
+    diff = pix[*ip++];
+    diff <<= *ip++;
+    sum[*ip++] += diff;
+      }
+      for (g=colors; --g; ) {
+    c = *ip++;
+    pix[c] = sum[c] / *ip++;
+      }
+    }
+  }
+  if (cmdline.quick_interpolate)
+    return;
+
+  for (row=0; row < 8; row++) {     /* Precalculate for VNG */
+    for (col=0; col < 2; col++) {
+      ip = code[row][col];
+      for (cp=terms, t=0; t < 64; t++) {
+    y1 = *cp++;  x1 = *cp++;
+    y2 = *cp++;  x2 = *cp++;
+    weight = *cp++;
+    grads = *cp++;
+    color = FC(row+y1,col+x1);
+    if (FC(row+y2,col+x2) != color) continue;
+    diag = (FC(row,col+1) == color && FC(row+1,col) == color) ? 2:1;
+    if (abs(y1-y2) == diag && abs(x1-x2) == diag) continue;
+    *ip++ = (y1*width + x1)*4 + color;
+    *ip++ = (y2*width + x2)*4 + color;
+    *ip++ = weight;
+    for (g=0; g < 8; g++)
+      if (grads & 1<<g) *ip++ = g;
+    *ip++ = -1;
+      }
+      *ip++ = INT_MAX;
+      for (cp=chood, g=0; g < 8; g++) {
+    y = *cp++;  x = *cp++;
+    *ip++ = (y*width + x) * 4;
+    color = FC(row,col);
+    if (FC(row+y,col+x) != color && FC(row+y*2,col+x*2) == color)
+      *ip++ = (y*width + x) * 8 + color;
+    else
+      *ip++ = 0;
+      }
+    }
+  }
+  brow[4] = calloc (width*3, sizeof **brow);
+  merror (brow[4], "vng_interpolate()");
+  for (row=0; row < 3; row++)
+    brow[row] = brow[4] + row*width;
+  for (row=2; row < height-2; row++) {      /* Do VNG interpolation */
+    for (col=2; col < width-2; col++) {
+      pix = image[row*width+col];
+      ip = code[row & 7][col & 1];
+      memset (gval, 0, sizeof gval);
+      while ((g = ip[0]) != INT_MAX) {      /* Calculate gradients */
+    num = (diff = pix[g] - pix[ip[1]]) >> 31;
+    gval[ip[3]] += (diff = ((diff ^ num) - num) << ip[2]);
+    ip += 5;
+    if ((g = ip[-1]) == -1) continue;
+    gval[g] += diff;
+    while ((g = *ip++) != -1)
+      gval[g] += diff;
+      }
+      ip++;
+      gmin = gmax = gval[0];            /* Choose a threshold */
+      for (g=1; g < 8; g++) {
+    if (gmin > gval[g]) gmin = gval[g];
+    if (gmax < gval[g]) gmax = gval[g];
+      }
+      if (gmax == 0) {
+    memcpy (brow[2][col], pix, sizeof *image);
+    continue;
+      }
+      thold = gmin + (gmax >> 1);
+      memset (sum, 0, sizeof sum);
+      color = FC(row,col);
+      for (num=g=0; g < 8; g++,ip+=2) {     /* Average the neighbors */
+    if (gval[g] <= thold) {
+      FORC4
+        if (c == color && ip[1])
+          sum[c] += (pix[c] + pix[ip[1]]) >> 1;
+        else
+          sum[c] += pix[ip[0] + c];
+      num++;
+    }
+      }
+      FORC4 {                   /* Save to buffer */
+    t = pix[color];
+    if (c != color) {
+      t += (sum[c] - sum[color])/num;
+      if (t < 0) t = 0;
+      if (t > clip_max) t = clip_max;
+    }
+    brow[2][col][c] = t;
+      }
+    }
+    if (row > 3)                /* Write buffer to image */
+      memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image);
+    for (g=0; g < 4; g++)
+      brow[(g-1) & 3] = brow[g];
+  }
+  memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image);
+  memcpy (image[(row-1)*width+2], brow[1]+2, (width-4)*sizeof *image);
+  free (brow[4]);
+}
+
+#ifdef USE_LCMS
+static void 
+apply_profile(FILE *       const ifP,
+              const char * const pfname)
+{
+  char *prof;
+  cmsHPROFILE hInProfile=NULL, hOutProfile;
+  cmsHTRANSFORM hTransform;
+
+  if (pfname)
+    hInProfile = cmsOpenProfileFromFile (pfname, "r");
+  else if (profile_length) {
+    prof = malloc (profile_length);
+    merror (prof, "apply_profile()");
+    fseek (ifP, profile_offset, SEEK_SET);
+    fread (prof, 1, profile_length, ifP);
+    hInProfile = cmsOpenProfileFromMem (prof, profile_length);
+    free (prof);
+  }
+  if (!hInProfile) return;
+  if (cmdline.verbose)
+      pm_message( "Applying color profile...");
+  maximum = 0xffff;
+  use_gamma = use_coeff = 0;
+
+  hOutProfile = cmsCreate_sRGBProfile();
+  hTransform = cmsCreateTransform (hInProfile, TYPE_RGBA_16,
+    hOutProfile, TYPE_RGBA_16, INTENT_PERCEPTUAL, 0);
+  cmsDoTransform (hTransform, image, image, width*height);
+
+  cmsDeleteTransform (hTransform);
+  cmsCloseProfile (hInProfile);
+  cmsCloseProfile (hOutProfile);
+}
+#else
+static void 
+apply_profile(FILE *       const ifP,
+              const char * const pfname)
+{
+}
+#endif
+
+/*
+   Convert the entire image to RGB colorspace and build a histogram.
+ */
+static void CLASS convert_to_rgb()
+{
+  int row, col, r, g, c=0;
+  unsigned short *img;
+  float rgb[3];
+
+  if (cmdline.document_mode)
+    colors = 1;
+  memset (histogram, 0, sizeof histogram);
+  for (row = trim; row < height-trim; row++)
+    for (col = trim; col < width-trim; col++) {
+      img = image[row*width+col];
+      if (cmdline.document_mode)
+    c = FC(row,col);
+      if (colors == 4 && !use_coeff)    /* Recombine the greens */
+    img[1] = (img[1] + img[3]) >> 1;
+      if (colors == 1)          /* RGB from grayscale */
+    for (r=0; r < 3; r++)
+      rgb[r] = img[c];
+      else if (use_coeff) {     /* RGB via coeff[][] */
+    for (r=0; r < 3; r++)
+      for (rgb[r]=g=0; g < colors; g++)
+        rgb[r] += img[g] * coeff[r][g];
+      } else                /* RGB from RGB (easy) */
+    goto norgb;
+      for (r=0; r < 3; r++) {
+    if (rgb[r] < 0)        rgb[r] = 0;
+    if (rgb[r] > clip_max) rgb[r] = clip_max;
+    img[r] = rgb[r];
+      }
+norgb:
+      for (r=0; r < 3; r++)
+    histogram[r][img[r] >> 3]++;
+    }
+}
+
+static void CLASS fuji_rotate()
+{
+  int i, wide, high, row, col;
+  double step;
+  float r, c, fr, fc;
+  unsigned ur, uc;
+  unsigned short (*img)[4], (*pix)[4];
+
+  if (!fuji_width) return;
+  if (cmdline.verbose)
+    pm_message ("Rotating image 45 degrees...");
+  fuji_width = (fuji_width + shrink) >> shrink;
+  step = sqrt(0.5);
+  wide = fuji_width / step;
+  high = (height - fuji_width) / step;
+  img = calloc (wide*high, sizeof *img);
+  merror (img, "fuji_rotate()");
+
+  for (row=0; row < high; row++)
+    for (col=0; col < wide; col++) {
+      ur = r = fuji_width + (row-col)*step;
+      uc = c = (row+col)*step;
+      if (ur > height-2 || uc > width-2) continue;
+      fr = r - ur;
+      fc = c - uc;
+      pix = image + ur*width + uc;
+      for (i=0; i < colors; i++)
+    img[row*wide+col][i] =
+      (pix[    0][i]*(1-fc) + pix[      1][i]*fc) * (1-fr) +
+      (pix[width][i]*(1-fc) + pix[width+1][i]*fc) * fr;
+    }
+  free (image);
+  width  = wide;
+  height = high;
+  image  = img;
+  fuji_width = 0;
+}
+
+static void CLASS flip_image()
+{
+    unsigned *flag;
+    int size, base, dest, next, row, col, temp;
+
+    struct imageCell {
+        unsigned char contents[8];
+    };
+    struct imageCell * img;
+    struct imageCell hold;
+
+    switch ((flip+3600) % 360) {
+    case 270:  flip = 5;  break;
+    case 180:  flip = 3;  break;
+    case  90:  flip = 6;
+    }
+    img = (struct imageCell *) image;
+    size = height * width;
+    flag = calloc ((size+31) >> 5, sizeof *flag);
+    merror (flag, "flip_image()");
+    for (base = 0; base < size; base++) {
+        if (flag[base >> 5] & (1 << (base & 31)))
+            continue;
+        dest = base;
+        hold = img[base];
+        while (1) {
+            if (flip & 4) {
+                row = dest % height;
+                col = dest / height;
+            } else {
+                row = dest / width;
+                col = dest % width;
+            }
+            if (flip & 2)
+                row = height - 1 - row;
+            if (flip & 1)
+                col = width - 1 - col;
+            next = row * width + col;
+            if (next == base) break;
+            flag[next >> 5] |= 1 << (next & 31);
+            img[dest] = img[next];
+            dest = next;
+        }
+        img[dest] = hold;
+    }
+    free (flag);
+    if (flip & 4) {
+        temp = height;
+        height = width;
+        width = temp;
+        temp = ymag;
+        ymag = xmag;
+        xmag = temp;
+    }
+}
+
+/*
+   Write the image as an RGB PAM image
+ */
+static void CLASS write_pam_nonlinear (FILE *ofp)
+{
+  unsigned char lut[0x10000];
+  int perc, c, val, total, i, row, col;
+  float white=0, r;
+  struct pam pam;
+  tuple * tuplerow;
+
+  pam.size   = sizeof(pam);
+  pam.len    = PAM_STRUCT_SIZE(tuple_type);
+  pam.file   = ofp;
+  pam.width  = xmag*(width-trim*2);
+  pam.height = ymag*(height-trim*2);
+  pam.depth  = 3;
+  pam.format = PAM_FORMAT;
+  pam.maxval = 255;
+  strcpy(pam.tuple_type, "RGB");
+
+  pnm_writepaminit(&pam);
+
+  tuplerow = pnm_allocpamrow(&pam);
+
+  perc = width * height * 0.01;     /* 99th percentile white point */
+  if (fuji_width) perc /= 2;
+  FORC3 {
+    for (val=0x2000, total=0; --val > 32; )
+      if ((total += histogram[c][val]) > perc) break;
+    if (white < val) white = val;
+  }
+  white *= 8 / cmdline.bright;
+  for (i=0; i < 0x10000; i++) {
+    r = i / white;
+    val = 256 * ( !use_gamma ? r :
+    r <= 0.018 ? r*4.5 : pow(r,0.45)*1.099-0.099 );
+    if (val > 255) val = 255;
+    lut[i] = val;
+  }
+  for (row=trim; row < height-trim; row++) {
+      for (col=trim; col < width-trim; col++) {
+          unsigned int plane;
+          for (plane=0; plane < pam.depth; ++plane) {
+              unsigned int copy;
+              for (copy=0; copy < xmag; ++copy) {
+                  unsigned int const pamcol = xmag*(col-trim)+copy;
+                  tuplerow[pamcol][plane] = lut[image[row*width+col][plane]];
+              }
+          }
+      }
+      {
+          unsigned int copy;
+          for (copy=0; copy < ymag; ++copy)
+              pnm_writepamrow(&pam, tuplerow);
+      }
+  }
+  pnm_freepamrow(tuplerow);
+}
+
+/*
+   Write the image to a 16-bit PAM file with linear color space
+ */
+static void CLASS write_pam_linear (FILE *ofp)
+{
+  int row;
+
+  struct pam pam;
+  tuple * tuplerow;
+
+  if (maximum < 256) maximum = 256;
+
+  pam.size   = sizeof(pam);
+  pam.len    = PAM_STRUCT_SIZE(tuple_type);
+  pam.file   = ofp;
+  pam.width  = width-trim*2;
+  pam.height = height-trim*2;
+  pam.depth  = 3;
+  pam.format = PAM_FORMAT;
+  pam.maxval = MAX(maximum, 256);
+  strcpy(pam.tuple_type, "RGB");
+
+  pnm_writepaminit(&pam);
+
+  tuplerow = pnm_allocpamrow(&pam);
+
+  for (row = trim; row < height-trim; row++) {
+      unsigned int col;
+      for (col = trim; col < width-trim; col++) {
+          unsigned int const pamCol = col - trim;
+          unsigned int plane;
+          for (plane = 0; plane < 3; ++plane)
+              tuplerow[pamCol][plane] = image[row*width+col][plane];
+      }
+      pnm_writepamrow(&pam, tuplerow);
+  }
+  pnm_freepamrow(tuplerow);
+}
+
+
+
+static void CLASS
+writePam(FILE * const ofP,
+         bool   const linear) {
+
+    if (linear)
+        write_pam_linear(ofP);
+    else
+        write_pam_nonlinear(ofP);
+}
+
+
+
+
+static void CLASS
+convertIt(FILE *    const ifP,
+          FILE *    const ofP,
+          loadRawFn const load_raw) {
+
+    shrink = cmdline.half_size && filters;
+    iheight = (height + shrink) >> shrink;
+    iwidth  = (width  + shrink) >> shrink;
+    image = calloc (iheight*iwidth*sizeof *image + meta_length, 1);
+    merror (image, "main()");
+    meta_data = (char *) (image + iheight*iwidth);
+    if (cmdline.verbose)
+        pm_message ("Loading %s %s image ...", make, model);
+
+    use_secondary = cmdline.use_secondary;  /* for load_raw() */
+
+    ifp = ifP;  /* Set global variable for (*load_raw)() */
+
+    load_raw();
+    bad_pixels();
+    height = iheight;
+    width  = iwidth;
+    if (is_foveon) {
+        if (cmdline.verbose)
+            pm_message ("Foveon interpolation...");
+        foveon_interpolate(coeff);
+    } else {
+        scale_colors();
+    }
+    if (shrink) filters = 0;
+    trim = 0;
+    if (filters && !cmdline.document_mode) {
+        trim = 1;
+        if (cmdline.verbose)
+            pm_message ("%s interpolation...",
+                        cmdline.quick_interpolate ? "Bilinear":"VNG");
+        vng_interpolate();
+    }
+    fuji_rotate();
+    apply_profile(ifP, cmdline.profile);
+    if (cmdline.verbose)
+        pm_message ("Converting to RGB colorspace...");
+    convert_to_rgb();
+
+    if (flip) {
+        if (cmdline.verbose)
+            pm_message ("Flipping image %c:%c:%c...",
+                        flip & 1 ? 'H':'0', flip & 2 ? 'V':'0', 
+                        flip & 4 ? 'T':'0');
+        flip_image();
+    }
+    writePam(ofP, cmdline.linear);
+}
+
+
+int 
+main (int argc, char **argv) {
+
+    FILE * const ofP = stdout;
+
+    FILE * ifP;
+    int rc;
+    loadRawFn load_raw;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose;
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    image = NULL;
+    
+    rc = identify(ifP,
+                  cmdline.use_secondary, cmdline.use_camera_rgb,
+                  cmdline.red_scale, cmdline.blue_scale,
+                  cmdline.four_color_rgb, cmdline.inputFileName,
+                  &load_raw);
+    if (rc != 0)
+        pm_error("Unable to identify the format of the input image");
+    else {
+        if (cmdline.identify_only) {
+            pm_message ("Input is a %s %s image.", make, model);
+        } else {
+            if (cmdline.verbose)
+                pm_message ("Input is a %s %s image.", make, model);
+            convertIt(ifP, ofP, load_raw);
+        }
+    }
+    pm_close(ifP);
+    pm_close(ofP);
+    free(image);
+
+    return 0;
+}
+
+
+
diff --git a/converter/other/cameratopam/canon.c b/converter/other/cameratopam/canon.c
new file mode 100644
index 00000000..a34771d0
--- /dev/null
+++ b/converter/other/cameratopam/canon.c
@@ -0,0 +1,172 @@
+#include <string.h>
+#include "mallocvar.h"
+#include "pm.h"
+#include "global_variables.h"
+#include "util.h"
+#include "decode.h"
+#include "bayer.h"
+#include "canon.h"
+
+
+void 
+canon_600_load_raw(void) {
+    unsigned char  data[1120], *dp;
+    unsigned short pixel[896], *pix;
+    int irow, orow, col;
+
+    for (irow=orow=0; irow < height; irow++)
+    {
+        fread (data, 1120, 1, ifp);
+        for (dp=data, pix=pixel; dp < data+1120; dp+=10, pix+=8)
+        {
+            pix[0] = (dp[0] << 2) + (dp[1] >> 6    );
+            pix[1] = (dp[2] << 2) + (dp[1] >> 4 & 3);
+            pix[2] = (dp[3] << 2) + (dp[1] >> 2 & 3);
+            pix[3] = (dp[4] << 2) + (dp[1]      & 3);
+            pix[4] = (dp[5] << 2) + (dp[9]      & 3);
+            pix[5] = (dp[6] << 2) + (dp[9] >> 2 & 3);
+            pix[6] = (dp[7] << 2) + (dp[9] >> 4 & 3);
+            pix[7] = (dp[8] << 2) + (dp[9] >> 6    );
+        }
+        for (col=0; col < width; col++)
+            BAYER(orow,col) = pixel[col];
+        for (col=width; col < 896; col++)
+            black += pixel[col];
+        if ((orow+=2) > height)
+            orow = 1;
+    }
+    black /= (896 - width) * height;
+    maximum = 0x3ff;
+}
+
+
+
+void
+canon_a5_load_raw(void) {
+    unsigned char  data[1940], *dp;
+    unsigned short pixel[1552], *pix;
+    int row, col;
+
+    for (row=0; row < height; row++) {
+        fread (data, raw_width * 10 / 8, 1, ifp);
+        for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=10, pix+=8)
+        {
+            pix[0] = (dp[1] << 2) + (dp[0] >> 6);
+            pix[1] = (dp[0] << 4) + (dp[3] >> 4);
+            pix[2] = (dp[3] << 6) + (dp[2] >> 2);
+            pix[3] = (dp[2] << 8) + (dp[5]     );
+            pix[4] = (dp[4] << 2) + (dp[7] >> 6);
+            pix[5] = (dp[7] << 4) + (dp[6] >> 4);
+            pix[6] = (dp[6] << 6) + (dp[9] >> 2);
+            pix[7] = (dp[9] << 8) + (dp[8]     );
+        }
+        for (col=0; col < width; col++)
+            BAYER(row,col) = (pixel[col] & 0x3ff);
+        for (col=width; col < raw_width; col++)
+            black += pixel[col] & 0x3ff;
+    }
+    if (raw_width > width)
+        black /= (raw_width - width) * height;
+    maximum = 0x3ff;
+}
+
+
+
+/*
+   Return 0 if the image starts with compressed data,
+   1 if it starts with uncompressed low-order bits.
+
+   In Canon compressed data, 0xff is always followed by 0x00.
+ */
+static int
+canon_has_lowbits()
+{
+    unsigned char test[0x4000];
+    int ret=1, i;
+
+    fseek (ifp, 0, SEEK_SET);
+    fread (test, 1, sizeof test, ifp);
+    for (i=540; i < sizeof test - 1; i++)
+        if (test[i] == 0xff) {
+            if (test[i+1]) return 1;
+            ret=0;
+        }
+    return ret;
+}
+
+
+
+void 
+canon_compressed_load_raw(void) {
+    unsigned short *pixel, *prow;
+    int lowbits, i, row, r, col, save, val;
+    unsigned irow, icol;
+    struct decode *decode, *dindex;
+    int block, diffbuf[64], leaf, len, diff, carry=0, pnum=0, base[2];
+    unsigned char c;
+
+    MALLOCARRAY(pixel, raw_width*8);
+    if (pixel == NULL)
+        pm_error("Unable to allocate space for %u pixels", raw_width*8);
+    lowbits = canon_has_lowbits();
+    if (!lowbits) maximum = 0x3ff;
+    fseek (ifp, 540 + lowbits*raw_height*raw_width/4, SEEK_SET);
+    zero_after_ff = 1;
+    getbits(ifp, -1);
+    for (row = 0; row < raw_height; row += 8) {
+        for (block=0; block < raw_width >> 3; block++) {
+            memset (diffbuf, 0, sizeof diffbuf);
+            decode = first_decode;
+            for (i=0; i < 64; i++ ) {
+                for (dindex=decode; dindex->branch[0]; )
+                    dindex = dindex->branch[getbits(ifp, 1)];
+                leaf = dindex->leaf;
+                decode = second_decode;
+                if (leaf == 0 && i) break;
+                if (leaf == 0xff) continue;
+                i  += leaf >> 4;
+                len = leaf & 15;
+                if (len == 0) continue;
+                diff = getbits(ifp, len);
+                if ((diff & (1 << (len-1))) == 0)
+                    diff -= (1 << len) - 1;
+                if (i < 64) diffbuf[i] = diff;
+            }
+            diffbuf[0] += carry;
+            carry = diffbuf[0];
+            for (i=0; i < 64; i++ ) {
+                if (pnum++ % raw_width == 0)
+                    base[0] = base[1] = 512;
+                pixel[(block << 6) + i] = ( base[i & 1] += diffbuf[i] );
+            }
+        }
+        if (lowbits) {
+            save = ftell(ifp);
+            fseek (ifp, 26 + row*raw_width/4, SEEK_SET);
+            for (prow=pixel, i=0; i < raw_width*2; i++) {
+                c = fgetc(ifp);
+                for (r=0; r < 8; r+=2, prow++) {
+                    val = (*prow << 2) + ((c >> r) & 3);
+                    if (raw_width == 2672 && val < 512) val += 2;
+                    *prow = val;
+                }
+            }
+            fseek (ifp, save, SEEK_SET);
+        }
+        for (r=0; r < 8; r++) {
+            irow = row - top_margin + r;
+            if (irow >= height) continue;
+            for (col = 0; col < raw_width; col++) {
+                icol = col - left_margin;
+                if (icol < width)
+                    BAYER(irow,icol) = pixel[r*raw_width+col];
+                else
+                    black += pixel[r*raw_width+col];
+            }
+        }
+    }
+    free (pixel);
+    if (raw_width > width)
+        black /= (raw_width - width) * height;
+}
+
diff --git a/converter/other/cameratopam/canon.h b/converter/other/cameratopam/canon.h
new file mode 100644
index 00000000..041cdc4d
--- /dev/null
+++ b/converter/other/cameratopam/canon.h
@@ -0,0 +1,13 @@
+#ifndef CANON_H_INCLUDED
+#define CANON_H_INCLUDED
+
+void 
+canon_600_load_raw(void);
+
+void
+canon_a5_load_raw(void);
+
+void 
+canon_compressed_load_raw(void);
+
+#endif
diff --git a/converter/other/cameratopam/decode.c b/converter/other/cameratopam/decode.c
new file mode 100644
index 00000000..e3a62fab
--- /dev/null
+++ b/converter/other/cameratopam/decode.c
@@ -0,0 +1,172 @@
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "pm.h"
+
+#include "decode.h"
+
+struct decode first_decode[2048], *second_decode, *free_decode;
+
+
+
+void init_decoder() {
+  memset (first_decode, 0, sizeof first_decode);
+  free_decode = first_decode;
+}
+
+
+
+/*
+   Construct a decode tree according the specification in *source.
+   The first 16 bytes specify how many codes should be 1-bit, 2-bit
+   3-bit, etc.  Bytes after that are the leaf values.
+
+   For example, if the source is
+
+    { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0,
+      0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff  },
+
+   then the code is
+
+    00      0x04
+    010     0x03
+    011     0x05
+    100     0x06
+    101     0x02
+    1100        0x07
+    1101        0x01
+    11100       0x08
+    11101       0x09
+    11110       0x00
+    111110      0x0a
+    1111110     0x0b
+    1111111     0xff
+ */
+
+
+unsigned char * 
+make_decoder(const unsigned char * const source, 
+             int                   const level) {
+
+    struct decode *cur;
+    static int leaf;
+    int i, next;
+
+    if (level==0) 
+        leaf=0;
+    cur = free_decode++;
+    if (free_decode > first_decode+2048) {
+        pm_error ("decoder table overflow");
+    }
+    for (i=next=0; i <= leaf && next < 16; )
+        i += source[next++];
+    if (i > leaf) {
+        if (level < next) {
+            cur->branch[0] = free_decode;
+            make_decoder (source, level+1);
+            cur->branch[1] = free_decode;
+            make_decoder (source, level+1);
+        } else
+            cur->leaf = source[16 + leaf++];
+    }
+    return (unsigned char *) source + 16 + leaf;
+}
+
+
+
+const int * 
+make_decoder_int(const int * const source,
+                 int         const level) {
+
+    const int * p;
+    struct decode *cur;
+
+    p = source;  /* initial value */
+
+    cur = free_decode++;
+    if (level < p[0]) {
+        cur->branch[0] = free_decode;
+        p = make_decoder_int (p, level+1);
+        cur->branch[1] = free_decode;
+        p = make_decoder_int (p, level+1);
+    } else {
+        cur->leaf = p[1];
+        p += 2;
+    }
+    return p;
+}
+
+
+
+void 
+crw_init_tables(unsigned int const table) {
+
+  unsigned int const clippedTableNum = MIN(2, table);
+
+  static const unsigned char first_tree[3][29] = {
+    { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0,
+      0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff  },
+
+    { 0,2,2,3,1,1,1,1,2,0,0,0,0,0,0,0,
+      0x03,0x02,0x04,0x01,0x05,0x00,0x06,0x07,0x09,0x08,0x0a,0x0b,0xff  },
+
+    { 0,0,6,3,1,1,2,0,0,0,0,0,0,0,0,0,
+      0x06,0x05,0x07,0x04,0x08,0x03,0x09,0x02,0x00,0x0a,0x01,0x0b,0xff  },
+  };
+
+  static const unsigned char second_tree[3][180] = {
+    { 0,2,2,2,1,4,2,1,2,5,1,1,0,0,0,139,
+      0x03,0x04,0x02,0x05,0x01,0x06,0x07,0x08,
+      0x12,0x13,0x11,0x14,0x09,0x15,0x22,0x00,0x21,0x16,0x0a,0xf0,
+      0x23,0x17,0x24,0x31,0x32,0x18,0x19,0x33,0x25,0x41,0x34,0x42,
+      0x35,0x51,0x36,0x37,0x38,0x29,0x79,0x26,0x1a,0x39,0x56,0x57,
+      0x28,0x27,0x52,0x55,0x58,0x43,0x76,0x59,0x77,0x54,0x61,0xf9,
+      0x71,0x78,0x75,0x96,0x97,0x49,0xb7,0x53,0xd7,0x74,0xb6,0x98,
+      0x47,0x48,0x95,0x69,0x99,0x91,0xfa,0xb8,0x68,0xb5,0xb9,0xd6,
+      0xf7,0xd8,0x67,0x46,0x45,0x94,0x89,0xf8,0x81,0xd5,0xf6,0xb4,
+      0x88,0xb1,0x2a,0x44,0x72,0xd9,0x87,0x66,0xd4,0xf5,0x3a,0xa7,
+      0x73,0xa9,0xa8,0x86,0x62,0xc7,0x65,0xc8,0xc9,0xa1,0xf4,0xd1,
+      0xe9,0x5a,0x92,0x85,0xa6,0xe7,0x93,0xe8,0xc1,0xc6,0x7a,0x64,
+      0xe1,0x4a,0x6a,0xe6,0xb3,0xf1,0xd3,0xa5,0x8a,0xb2,0x9a,0xba,
+      0x84,0xa4,0x63,0xe5,0xc5,0xf3,0xd2,0xc4,0x82,0xaa,0xda,0xe4,
+      0xf2,0xca,0x83,0xa3,0xa2,0xc3,0xea,0xc2,0xe2,0xe3,0xff,0xff  },
+
+    { 0,2,2,1,4,1,4,1,3,3,1,0,0,0,0,140,
+      0x02,0x03,0x01,0x04,0x05,0x12,0x11,0x06,
+      0x13,0x07,0x08,0x14,0x22,0x09,0x21,0x00,0x23,0x15,0x31,0x32,
+      0x0a,0x16,0xf0,0x24,0x33,0x41,0x42,0x19,0x17,0x25,0x18,0x51,
+      0x34,0x43,0x52,0x29,0x35,0x61,0x39,0x71,0x62,0x36,0x53,0x26,
+      0x38,0x1a,0x37,0x81,0x27,0x91,0x79,0x55,0x45,0x28,0x72,0x59,
+      0xa1,0xb1,0x44,0x69,0x54,0x58,0xd1,0xfa,0x57,0xe1,0xf1,0xb9,
+      0x49,0x47,0x63,0x6a,0xf9,0x56,0x46,0xa8,0x2a,0x4a,0x78,0x99,
+      0x3a,0x75,0x74,0x86,0x65,0xc1,0x76,0xb6,0x96,0xd6,0x89,0x85,
+      0xc9,0xf5,0x95,0xb4,0xc7,0xf7,0x8a,0x97,0xb8,0x73,0xb7,0xd8,
+      0xd9,0x87,0xa7,0x7a,0x48,0x82,0x84,0xea,0xf4,0xa6,0xc5,0x5a,
+      0x94,0xa4,0xc6,0x92,0xc3,0x68,0xb5,0xc8,0xe4,0xe5,0xe6,0xe9,
+      0xa2,0xa3,0xe3,0xc2,0x66,0x67,0x93,0xaa,0xd4,0xd5,0xe7,0xf8,
+      0x88,0x9a,0xd7,0x77,0xc4,0x64,0xe2,0x98,0xa5,0xca,0xda,0xe8,
+      0xf3,0xf6,0xa9,0xb2,0xb3,0xf2,0xd2,0x83,0xba,0xd3,0xff,0xff  },
+
+    { 0,0,6,2,1,3,3,2,5,1,2,2,8,10,0,117,
+      0x04,0x05,0x03,0x06,0x02,0x07,0x01,0x08,
+      0x09,0x12,0x13,0x14,0x11,0x15,0x0a,0x16,0x17,0xf0,0x00,0x22,
+      0x21,0x18,0x23,0x19,0x24,0x32,0x31,0x25,0x33,0x38,0x37,0x34,
+      0x35,0x36,0x39,0x79,0x57,0x58,0x59,0x28,0x56,0x78,0x27,0x41,
+      0x29,0x77,0x26,0x42,0x76,0x99,0x1a,0x55,0x98,0x97,0xf9,0x48,
+      0x54,0x96,0x89,0x47,0xb7,0x49,0xfa,0x75,0x68,0xb6,0x67,0x69,
+      0xb9,0xb8,0xd8,0x52,0xd7,0x88,0xb5,0x74,0x51,0x46,0xd9,0xf8,
+      0x3a,0xd6,0x87,0x45,0x7a,0x95,0xd5,0xf6,0x86,0xb4,0xa9,0x94,
+      0x53,0x2a,0xa8,0x43,0xf5,0xf7,0xd4,0x66,0xa7,0x5a,0x44,0x8a,
+      0xc9,0xe8,0xc8,0xe7,0x9a,0x6a,0x73,0x4a,0x61,0xc7,0xf4,0xc6,
+      0x65,0xe9,0x72,0xe6,0x71,0x91,0x93,0xa6,0xda,0x92,0x85,0x62,
+      0xf3,0xc5,0xb2,0xa4,0x84,0xba,0x64,0xa5,0xb3,0xd2,0x81,0xe5,
+      0xd3,0xaa,0xc4,0xca,0xf2,0xb1,0xe4,0xd1,0x83,0x63,0xea,0xc3,
+      0xe2,0x82,0xf1,0xa3,0xc2,0xa1,0xc1,0xe3,0xa2,0xe1,0xff,0xff  }
+  };
+
+  init_decoder();
+  make_decoder ( first_tree[clippedTableNum], 0);
+  second_decode = free_decode;
+  make_decoder (second_tree[clippedTableNum], 0);
+}
+
diff --git a/converter/other/cameratopam/decode.h b/converter/other/cameratopam/decode.h
new file mode 100644
index 00000000..b0addc82
--- /dev/null
+++ b/converter/other/cameratopam/decode.h
@@ -0,0 +1,22 @@
+struct decode {
+  struct decode *branch[2];
+  int leaf;
+}; 
+
+extern struct decode * free_decode;
+extern struct decode first_decode[2048];
+extern struct decode * second_decode;
+
+void 
+init_decoder(void);
+
+void 
+crw_init_tables(unsigned int const table);
+
+const int * 
+make_decoder_int (const int * const source, 
+                  int         const level);
+
+unsigned char * 
+make_decoder(const unsigned char * const source, 
+             int                   const level);
diff --git a/converter/other/cameratopam/dng.c b/converter/other/cameratopam/dng.c
new file mode 100644
index 00000000..44d45b02
--- /dev/null
+++ b/converter/other/cameratopam/dng.c
@@ -0,0 +1,73 @@
+#include <string.h>
+#include "global_variables.h"
+
+#include "dng.h"
+
+void 
+dng_coeff (double cc[4][4], double cm[4][3], double xyz[3])
+{
+  static const double rgb_xyz[3][3] = {     /* RGB from XYZ */
+    {  3.240479, -1.537150, -0.498535 },
+    { -0.969256,  1.875992,  0.041556 },
+    {  0.055648, -0.204043,  1.057311 } };
+#if 0
+  static const double xyz_rgb[3][3] = {     /* XYZ from RGB */
+    { 0.412453, 0.357580, 0.180423 },
+    { 0.212671, 0.715160, 0.072169 },
+    { 0.019334, 0.119193, 0.950227 } };
+#endif
+  double cam_xyz[4][3], xyz_cam[3][4], invert[3][6], num;
+  int i, j, k;
+
+  memset (cam_xyz, 0, sizeof cam_xyz);
+  for (i=0; i < colors; i++)
+    for (j=0; j < 3; j++)
+      for (k=0; k < colors; k++)
+    cam_xyz[i][j] += cc[i][k] * cm[k][j] * xyz[j];
+
+  for (i=0; i < colors; i++) {
+    for (num=j=0; j < 3; j++)
+      num += cam_xyz[i][j];
+    for (j=0; j < 3; j++)
+      cam_xyz[i][j] /= num;
+    pre_mul[i] = 1 / num;
+  }
+  for (i=0; i < 3; i++) {
+    for (j=0; j < 6; j++)
+      invert[i][j] = j == i+3;
+    for (j=0; j < 3; j++)
+      for (k=0; k < colors; k++)
+    invert[i][j] += cam_xyz[k][i] * cam_xyz[k][j];
+  }
+  for (i=0; i < 3; i++) {
+    num = invert[i][i];
+    for (j=0; j < 6; j++)       /* Normalize row i */
+      invert[i][j] /= num;
+    for (k=0; k < 3; k++) {     /* Subtract it from other rows */
+      if (k==i) continue;
+      num = invert[k][i];
+      for (j=0; j < 6; j++)
+    invert[k][j] -= invert[i][j] * num;
+    }
+  }
+  memset (xyz_cam, 0, sizeof xyz_cam);
+  for (i=0; i < 3; i++)
+    for (j=0; j < colors; j++)
+      for (k=0; k < 3; k++)
+    xyz_cam[i][j] += invert[i][k+3] * cam_xyz[j][k];
+
+  memset (coeff, 0, sizeof coeff);
+  for (i=0; i < 3; i++)
+    for (j=0; j < colors; j++)
+      for (k=0; k < 3; k++)
+    coeff[i][j] += rgb_xyz[i][k] * xyz_cam[k][j];
+
+  for (num=j=0; j < colors; j++)
+    num += coeff[1][j];
+  for (i=0; i < 3; i++) {
+    for (j=0; j < colors; j++)
+      coeff[i][j] /= num;
+  }
+  use_coeff = 1;
+}
+
diff --git a/converter/other/cameratopam/dng.h b/converter/other/cameratopam/dng.h
new file mode 100644
index 00000000..01e7e0a5
--- /dev/null
+++ b/converter/other/cameratopam/dng.h
@@ -0,0 +1,2 @@
+void 
+dng_coeff (double cc[4][4], double cm[4][3], double xyz[3]);
diff --git a/converter/other/cameratopam/foveon.c b/converter/other/cameratopam/foveon.c
new file mode 100644
index 00000000..0198940c
--- /dev/null
+++ b/converter/other/cameratopam/foveon.c
@@ -0,0 +1,790 @@
+/* This code is licensed to the public by its copyright owners under GPL. */
+
+#define _XOPEN_SOURCE   /* get M_PI */
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <float.h>
+#include <math.h>
+
+#include "mallocvar.h"
+#include "pm.h"
+#include "global_variables.h"
+#include "decode.h"
+#include "foveon.h"
+
+#if HAVE_INT64
+   typedef int64_t INT64;
+   static bool const have64BitArithmetic = true;
+#else
+   /* We define INT64 to something that lets the code compile, but we
+      should not execute any INT64 code, because it will get the wrong
+      result.
+   */
+   typedef int INT64;
+   static bool const have64BitArithmetic = false;
+#endif
+
+
+#define FORC3 for (c=0; c < 3; c++)
+#define FORC4 for (c=0; c < colors; c++)
+
+
+
+static char *  
+foveon_gets(int    const offset, 
+            char * const str, 
+            int    const len) {
+
+    unsigned int i;
+    fseek (ifp, offset, SEEK_SET);
+    for (i=0; i < len-1; ++i) {
+        /* It certains seems wrong that we're reading a 16 bit integer
+           and assigning it to char, but that's what Dcraw does.
+        */
+        unsigned short val;
+        pm_readlittleshortu(ifp, &val);
+        str[i] = val;
+        if (str[i] == 0)
+            break;
+    }
+    str[i] = 0;
+    return str;
+}
+
+
+
+void 
+parse_foveon(FILE * const ifp) {
+    long fliplong;
+    long pos;
+    long magic;
+    long junk;
+    long entries;
+
+    fseek (ifp, 36, SEEK_SET);
+    pm_readlittlelong(ifp, &fliplong);
+    flip = fliplong;
+    fseek (ifp, -4, SEEK_END);
+    pm_readlittlelong(ifp, &pos);
+    fseek (ifp, pos, SEEK_SET);
+    pm_readlittlelong(ifp, &magic);
+    if (magic != 0x64434553) 
+        return; /* SECd */
+    pm_readlittlelong(ifp, &junk);
+    pm_readlittlelong(ifp, &entries);
+    while (entries--) {
+        long off, len, tag;
+        long save;
+        long pent;
+        int i, poff[256][2];
+        char name[64];
+        long sec_;
+
+        pm_readlittlelong(ifp, &off);
+        pm_readlittlelong(ifp, &len);
+        pm_readlittlelong(ifp, &tag);
+            
+        save = ftell(ifp);
+        fseek (ifp, off, SEEK_SET);
+        pm_readlittlelong(ifp, &sec_);
+        if (sec_ != (0x20434553 | (tag << 24))) return;
+        switch (tag) {
+        case 0x47414d49:          /* IMAG */
+            if (data_offset) 
+                break;
+            data_offset = off + 28;
+            fseek (ifp, 12, SEEK_CUR);
+            {
+                long wlong, hlong;
+                pm_readlittlelong(ifp, &wlong);
+                pm_readlittlelong(ifp, &hlong);
+                raw_width  = wlong;
+                raw_height = hlong;
+            }
+            break;
+        case 0x464d4143:          /* CAMF */
+            meta_offset = off + 24;
+            meta_length = len - 28;
+            if (meta_length > 0x20000)
+                meta_length = 0x20000;
+            break;
+        case 0x504f5250:          /* PROP */
+            pm_readlittlelong(ifp, &junk);
+            pm_readlittlelong(ifp, &pent);
+            fseek (ifp, 12, SEEK_CUR);
+            off += pent*8 + 24;
+            if (pent > 256) pent=256;
+            for (i=0; i < pent*2; i++) {
+                long x;
+                pm_readlittlelong(ifp, &x);
+                poff[0][i] = off + x*2;
+            }
+            for (i=0; i < pent; i++) {
+                foveon_gets (poff[i][0], name, 64);
+                if (!strcmp (name, "CAMMANUF"))
+                    foveon_gets (poff[i][1], make, 64);
+                if (!strcmp (name, "CAMMODEL"))
+                    foveon_gets (poff[i][1], model, 64);
+                if (!strcmp (name, "WB_DESC"))
+                    foveon_gets (poff[i][1], model2, 64);
+                if (!strcmp (name, "TIME"))
+                    timestamp = atoi (foveon_gets (poff[i][1], name, 64));
+            }
+        }
+        fseek (ifp, save, SEEK_SET);
+    }
+    is_foveon = 1;
+}
+
+
+
+void  
+foveon_coeff(bool * const useCoeffP,
+             float        coeff[3][4]) {
+
+    static const float foveon[3][3] =
+    { {  1.4032, -0.2231, -0.1016 },
+      { -0.5263,  1.4816,  0.0170 },
+      { -0.0112,  0.0183,  0.9113 } };
+
+    int i, j;
+
+    for (i=0; i < 3; ++i)
+        for (j=0; j < 3; ++j)
+            coeff[i][j] = foveon[i][j];
+    *useCoeffP = 1;
+}
+
+
+
+static void  
+foveon_decoder (unsigned int const huff[1024], 
+                unsigned int const code) {
+
+    struct decode *cur;
+    int len;
+    unsigned int code2;
+
+    cur = free_decode++;
+    if (free_decode > first_decode+2048) {
+        pm_error ("decoder table overflow");
+    }
+    if (code) {
+        unsigned int i;
+        for (i=0; i < 1024; i++) {
+            if (huff[i] == code) {
+                cur->leaf = i;
+                return;
+            }
+        }
+    }
+    if ((len = code >> 27) > 26) 
+        return;
+    code2 = (len+1) << 27 | (code & 0x3ffffff) << 1;
+
+    cur->branch[0] = free_decode;
+    foveon_decoder (huff, code2);
+    cur->branch[1] = free_decode;
+    foveon_decoder (huff, code2+1);
+}
+
+
+
+static void  
+foveon_load_camf() {
+    unsigned int i, val;
+    long key;
+
+    fseek (ifp, meta_offset, SEEK_SET);
+    pm_readlittlelong(ifp, &key);
+    fread (meta_data, 1, meta_length, ifp);
+    for (i=0; i < meta_length; i++) {
+        key = (key * 1597 + 51749) % 244944;
+        assert(have64BitArithmetic);
+        val = key * (INT64) 301593171 >> 24;
+        meta_data[i] ^= ((((key << 8) - val) >> 1) + val) >> 17;
+    }
+}
+
+
+
+void  
+foveon_load_raw() {
+
+    struct decode *dindex;
+    short diff[1024], pred[3];
+    unsigned int huff[1024], bitbuf=0;
+    int row, col, bit=-1, c, i;
+
+    assert(have64BitArithmetic);
+
+    for (i=0; i < 1024; ++i)
+        pm_readlittleshort(ifp, &diff[i]);
+
+    for (i=0; i < 1024; ++i) {
+        long x;
+        pm_readlittlelong(ifp, &x);
+        huff[i] = x;
+    }
+    init_decoder();
+    foveon_decoder (huff, 0);
+
+    for (row=0; row < height; row++) {
+        long junk;
+        memset (pred, 0, sizeof pred);
+        if (!bit) 
+            pm_readlittlelong(ifp, &junk);
+        for (col=bit=0; col < width; col++) {
+            FORC3 {
+                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] += diff[dindex->leaf];
+            }
+            FORC3 image[row*width+col][c] = pred[c];
+        }
+    }
+    foveon_load_camf();
+    maximum = clip_max = 0xffff;
+}
+
+
+
+static int
+sget4(char const s[]) {
+    return s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24;
+}
+
+
+
+static char *  
+foveon_camf_param (const char * const block, 
+                   const char * const param) {
+    unsigned idx, num;
+    char *pos, *cp, *dp;
+
+    for (idx=0; idx < meta_length; idx += sget4(pos+8)) {
+        pos = meta_data + idx;
+        if (strncmp (pos, "CMb", 3)) break;
+        if (pos[3] != 'P') continue;
+        if (strcmp (block, pos+sget4(pos+12))) continue;
+        cp = pos + sget4(pos+16);
+        num = sget4(cp);
+        dp = pos + sget4(cp+4);
+        while (num--) {
+            cp += 8;
+            if (!strcmp (param, dp+sget4(cp)))
+                return dp+sget4(cp+4);
+        }
+    }
+    return NULL;
+}
+
+
+
+static void *  
+foveon_camf_matrix (int                dim[3], 
+                    const char * const name) {
+
+    unsigned i, idx, type, ndim, size, *mat;
+    char *pos, *cp, *dp;
+
+    for (idx=0; idx < meta_length; idx += sget4(pos+8)) {
+        pos = meta_data + idx;
+        if (strncmp (pos, "CMb", 3)) break;
+        if (pos[3] != 'M') continue;
+        if (strcmp (name, pos+sget4(pos+12))) continue;
+        dim[0] = dim[1] = dim[2] = 1;
+        cp = pos + sget4(pos+16);
+        type = sget4(cp);
+        if ((ndim = sget4(cp+4)) > 3) break;
+        dp = pos + sget4(cp+8);
+        for (i=ndim; i--; ) {
+            cp += 12;
+            dim[i] = sget4(cp);
+        }
+        if ((size = dim[0]*dim[1]*dim[2]) > meta_length/4) break;
+        MALLOCARRAY(mat, size);
+        if (mat == NULL)
+            pm_error("Unable to allocate memory for size=%d", size);
+        for (i=0; i < size; i++)
+            if (type && type != 6)
+                mat[i] = sget4(dp + i*4);
+            else
+                mat[i] = sget4(dp + i*2) & 0xffff;
+        return mat;
+    }
+    pm_message ("'%s' matrix not found!", name);
+    return NULL;
+}
+
+
+
+static int  
+foveon_fixed (void *       const ptr, 
+              int          const size, 
+              const char * const name) {
+    void *dp;
+    int dim[3];
+
+    dp = foveon_camf_matrix (dim, name);
+    if (!dp) return 0;
+    memcpy (ptr, dp, size*4);
+    free (dp);
+    return 1;
+}
+
+static float  foveon_avg (unsigned short *pix, int range[2], float cfilt)
+{
+    int i;
+    float val, min=FLT_MAX, max=-FLT_MAX, sum=0;
+
+    for (i=range[0]; i <= range[1]; i++) {
+        sum += val = 
+            (short)pix[i*4] + ((short)pix[i*4]-(short)pix[(i-1)*4]) * cfilt;
+        if (min > val) min = val;
+        if (max < val) max = val;
+    }
+    return (sum - min - max) / (range[1] - range[0] - 1);
+}
+
+static short *foveon_make_curve (double max, double mul, double filt)
+{
+    short *curve;
+    int size;
+    unsigned int i;
+    double x;
+
+    size = 4*M_PI*max / filt;
+    MALLOCARRAY(curve, size+1);
+    if (curve == NULL)
+        pm_error("Out of memory for %d-element curve array", size);
+
+    curve[0] = size;
+    for (i=0; i < size; ++i) {
+        x = i*filt/max/4;
+        curve[i+1] = (cos(x)+1)/2 * tanh(i*filt/mul) * mul + 0.5;
+    }
+    return curve;
+}
+
+static void foveon_make_curves
+(short **curvep, float dq[3], float div[3], float filt)
+{
+    double mul[3], max=0;
+    int c;
+
+    FORC3 mul[c] = dq[c]/div[c];
+    FORC3 if (max < mul[c]) max = mul[c];
+    FORC3 curvep[c] = foveon_make_curve (max, mul[c], filt);
+}
+
+static int  foveon_apply_curve (short *curve, int i)
+{
+    if (abs(i) >= (unsigned short)curve[0]) return 0;
+    return i < 0 ? -(unsigned short)curve[1-i] : (unsigned short)curve[1+i];
+}
+
+void  
+foveon_interpolate(float coeff[3][4]) {
+
+    static const short hood[] = { 
+        -1,-1, -1,0, -1,1, 0,-1, 0,1, 1,-1, 1,0, 1,1 };
+    short *pix, prev[3], *curve[8], (*shrink)[3];
+    float cfilt=0.8, ddft[3][3][2], ppm[3][3][3];
+    float cam_xyz[3][3], correct[3][3], last[3][3], trans[3][3];
+    float chroma_dq[3], color_dq[3], diag[3][3], div[3];
+    float (*black)[3], (*sgain)[3], (*sgrow)[3];
+    float fsum[3], val, frow, num;
+    int row, col, c, i, j, diff, sgx, irow, sum, min, max, limit;
+    int dim[3], dscr[2][2], (*smrow[7])[3], total[4], ipix[3];
+    int work[3][3], smlast, smred, smred_p=0, dev[3];
+    int satlev[3], keep[4], active[4];
+    unsigned *badpix;
+    double dsum=0, trsum[3];
+    char str[128], *cp;
+
+    foveon_fixed (dscr, 4, "DarkShieldColRange");
+    foveon_fixed (ppm[0][0], 27, "PostPolyMatrix");
+    foveon_fixed (ddft[1][0], 12, "DarkDrift");
+    foveon_fixed (&cfilt, 1, "ColumnFilter");
+    foveon_fixed (satlev, 3, "SaturationLevel");
+    foveon_fixed (keep, 4, "KeepImageArea");
+    foveon_fixed (active, 4, "ActiveImageArea");
+    foveon_fixed (chroma_dq, 3, "ChromaDQ");
+    foveon_fixed (color_dq, 3,
+                  foveon_camf_param ("IncludeBlocks", "ColorDQ") ?
+                  "ColorDQ" : "ColorDQCamRGB");
+
+    if (!(cp = foveon_camf_param ("WhiteBalanceIlluminants", model2)))
+    { pm_message ( "Invalid white balance \"%s\"", model2);
+    return; }
+    foveon_fixed (cam_xyz, 9, cp);
+    foveon_fixed (correct, 9,
+                  foveon_camf_param ("WhiteBalanceCorrections", model2));
+    memset (last, 0, sizeof last);
+    for (i=0; i < 3; i++)
+        for (j=0; j < 3; j++)
+            FORC3 last[i][j] += correct[i][c] * cam_xyz[c][j];
+
+    sprintf (str, "%sRGBNeutral", model2);
+    if (foveon_camf_param ("IncludeBlocks", str))
+        foveon_fixed (div, 3, str);
+    else {
+#define LAST(x,y) last[(i+x)%3][(c+y)%3]
+        for (i=0; i < 3; i++)
+            FORC3 diag[c][i] = LAST(1,1)*LAST(2,2) - LAST(1,2)*LAST(2,1);
+#undef LAST
+        FORC3 div[c] = 
+            diag[c][0]*0.3127 + diag[c][1]*0.329 + diag[c][2]*0.3583;
+    }
+    num = 0;
+    FORC3 if (num < div[c]) num = div[c];
+    FORC3 div[c] /= num;
+
+    memset (trans, 0, sizeof trans);
+    for (i=0; i < 3; i++)
+        for (j=0; j < 3; j++)
+            FORC3 trans[i][j] += coeff[i][c] * last[c][j] * div[j];
+    FORC3 trsum[c] = trans[c][0] + trans[c][1] + trans[c][2];
+    dsum = (6*trsum[0] + 11*trsum[1] + 3*trsum[2]) / 20;
+    for (i=0; i < 3; i++)
+        FORC3 last[i][c] = trans[i][c] * dsum / trsum[i];
+    memset (trans, 0, sizeof trans);
+    for (i=0; i < 3; i++)
+        for (j=0; j < 3; j++)
+            FORC3 trans[i][j] += (i==c ? 32 : -1) * last[c][j] / 30;
+
+    foveon_make_curves (curve, color_dq, div, cfilt);
+    FORC3 chroma_dq[c] /= 3;
+    foveon_make_curves (curve+3, chroma_dq, div, cfilt);
+    FORC3 dsum += chroma_dq[c] / div[c];
+    curve[6] = foveon_make_curve (dsum, dsum, cfilt);
+    curve[7] = foveon_make_curve (dsum*2, dsum*2, cfilt);
+
+    sgain = foveon_camf_matrix (dim, "SpatialGain");
+    if (!sgain) return;
+    sgrow = calloc (dim[1], sizeof *sgrow);
+    sgx = (width + dim[1]-2) / (dim[1]-1);
+
+    black = calloc (height, sizeof *black);
+    for (row=0; row < height; row++) {
+        for (i=0; i < 6; i++)
+            ddft[0][0][i] = ddft[1][0][i] +
+                row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]);
+        FORC3 black[row][c] =
+            ( foveon_avg (image[row*width]+c, dscr[0], cfilt) +
+              foveon_avg (image[row*width]+c, dscr[1], cfilt) * 3
+              - ddft[0][c][0] ) / 4 - ddft[0][c][1];
+    }
+    memcpy (black, black+8, sizeof *black*8);
+    memcpy (black+height-11, black+height-22, 11*sizeof *black);
+    memcpy (last, black, sizeof last);
+
+    for (row=1; row < height-1; row++) {
+        FORC3 if (last[1][c] > last[0][c]) {
+            if (last[1][c] > last[2][c])
+                black[row][c] = 
+                    (last[0][c] > last[2][c]) ? last[0][c]:last[2][c];
+        } else
+            if (last[1][c] < last[2][c])
+                black[row][c] = 
+                    (last[0][c] < last[2][c]) ? last[0][c]:last[2][c];
+        memmove (last, last+1, 2*sizeof last[0]);
+        memcpy (last[2], black[row+1], sizeof last[2]);
+    }
+    FORC3 black[row][c] = (last[0][c] + last[1][c])/2;
+    FORC3 black[0][c] = (black[1][c] + black[3][c])/2;
+
+    val = 1 - exp(-1/24.0);
+    memcpy (fsum, black, sizeof fsum);
+    for (row=1; row < height; row++)
+        FORC3 fsum[c] += black[row][c] =
+            (black[row][c] - black[row-1][c])*val + black[row-1][c];
+    memcpy (last[0], black[height-1], sizeof last[0]);
+    FORC3 fsum[c] /= height;
+    for (row = height; row--; )
+        FORC3 last[0][c] = black[row][c] =
+            (black[row][c] - fsum[c] - last[0][c])*val + last[0][c];
+
+    memset (total, 0, sizeof total);
+    for (row=2; row < height; row+=4)
+        for (col=2; col < width; col+=4) {
+            FORC3 total[c] += (short) image[row*width+col][c];
+            total[3]++;
+        }
+    for (row=0; row < height; row++)
+        FORC3 black[row][c] += fsum[c]/2 + total[c]/(total[3]*100.0);
+
+    for (row=0; row < height; row++) {
+        for (i=0; i < 6; i++)
+            ddft[0][0][i] = ddft[1][0][i] +
+                row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]);
+        pix = (short*)image[row*width];
+        memcpy (prev, pix, sizeof prev);
+        frow = row / (height-1.0) * (dim[2]-1);
+        if ((irow = frow) == dim[2]-1) irow--;
+        frow -= irow;
+        for (i=0; i < dim[1]; i++)
+            FORC3 sgrow[i][c] = sgain[ irow   *dim[1]+i][c] * (1-frow) +
+                sgain[(irow+1)*dim[1]+i][c] *    frow;
+        for (col=0; col < width; col++) {
+            FORC3 {
+                diff = pix[c] - prev[c];
+                prev[c] = pix[c];
+                ipix[c] = pix[c] + floor ((diff + (diff*diff >> 14)) * cfilt
+                                          - ddft[0][c][1] 
+                                          - ddft[0][c][0] 
+                                            * ((float) col/width - 0.5)
+                                          - black[row][c] );
+            }
+            FORC3 {
+                work[0][c] = ipix[c] * ipix[c] >> 14;
+                work[2][c] = ipix[c] * work[0][c] >> 14;
+                work[1][2-c] = ipix[(c+1) % 3] * ipix[(c+2) % 3] >> 14;
+            }
+            FORC3 {
+                for (val=i=0; i < 3; i++)
+                    for (  j=0; j < 3; j++)
+                        val += ppm[c][i][j] * work[i][j];
+                ipix[c] = floor ((ipix[c] + floor(val)) *
+                                 ( sgrow[col/sgx  ][c] * (sgx - col%sgx) +
+                                   sgrow[col/sgx+1][c] * (col%sgx) ) / sgx / 
+                                 div[c]);
+                if (ipix[c] > 32000) ipix[c] = 32000;
+                pix[c] = ipix[c];
+            }
+            pix += 4;
+        }
+    }
+    free (black);
+    free (sgrow);
+    free (sgain);
+
+    if ((badpix = foveon_camf_matrix (dim, "BadPixels"))) {
+        for (i=0; i < dim[0]; i++) {
+            col = (badpix[i] >> 8 & 0xfff) - keep[0];
+            row = (badpix[i] >> 20       ) - keep[1];
+            if ((unsigned)(row-1) > height-3 || (unsigned)(col-1) > width-3)
+                continue;
+            memset (fsum, 0, sizeof fsum);
+            for (sum=j=0; j < 8; j++)
+                if (badpix[i] & (1 << j)) {
+                    FORC3 fsum[c] += 
+                        image[(row+hood[j*2])*width+col+hood[j*2+1]][c];
+                    sum++;
+                }
+            if (sum) FORC3 image[row*width+col][c] = fsum[c]/sum;
+        }
+        free (badpix);
+    }
+
+    /* Array for 5x5 Gaussian averaging of red values */
+    smrow[6] = calloc (width*5, sizeof **smrow);
+    if (smrow[6] == NULL)
+        pm_error("Out of memory");
+    for (i=0; i < 5; i++)
+        smrow[i] = smrow[6] + i*width;
+
+    /* Sharpen the reds against these Gaussian averages */
+    for (smlast=-1, row=2; row < height-2; row++) {
+        while (smlast < row+2) {
+            for (i=0; i < 6; i++)
+                smrow[(i+5) % 6] = smrow[i];
+            pix = (short*)image[++smlast*width+2];
+            for (col=2; col < width-2; col++) {
+                smrow[4][col][0] =
+                    (pix[0]*6 + (pix[-4]+pix[4])*4 + pix[-8]+pix[8] + 8) >> 4;
+                pix += 4;
+            }
+        }
+        pix = (short*)image[row*width+2];
+        for (col=2; col < width-2; col++) {
+            smred = ( 6 *  smrow[2][col][0]
+                      + 4 * (smrow[1][col][0] + smrow[3][col][0])
+                      +      smrow[0][col][0] + smrow[4][col][0] + 8 ) >> 4;
+            if (col == 2)
+                smred_p = smred;
+            i = pix[0] + ((pix[0] - ((smred*7 + smred_p) >> 3)) >> 3);
+            if (i > 32000) i = 32000;
+            pix[0] = i;
+            smred_p = smred;
+            pix += 4;
+        }
+    }
+
+    /* Adjust the brighter pixels for better linearity */
+    FORC3 {
+        i = satlev[c] / div[c];
+        if (maximum > i) maximum = i;
+    }
+    clip_max = maximum;
+    limit = maximum * 9 >> 4;
+    for (pix=(short*)image[0]; pix < (short *) image[height*width]; pix+=4) {
+        if (pix[0] <= limit || pix[1] <= limit || pix[2] <= limit)
+            continue;
+        min = max = pix[0];
+        for (c=1; c < 3; c++) {
+            if (min > pix[c]) min = pix[c];
+            if (max < pix[c]) max = pix[c];
+        }
+        i = 0x4000 - ((min - limit) << 14) / limit;
+        i = 0x4000 - (i*i >> 14);
+        i = i*i >> 14;
+        FORC3 pix[c] += (max - pix[c]) * i >> 14;
+    }
+    /*
+      Because photons that miss one detector often hit another,
+      the sum R+G+B is much less noisy than the individual colors.
+      So smooth the hues without smoothing the total.
+    */
+    for (smlast=-1, row=2; row < height-2; row++) {
+        while (smlast < row+2) {
+            for (i=0; i < 6; i++)
+                smrow[(i+5) % 6] = smrow[i];
+            pix = (short*)image[++smlast*width+2];
+            for (col=2; col < width-2; col++) {
+                FORC3 smrow[4][col][c] = (pix[c-4]+2*pix[c]+pix[c+4]+2) >> 2;
+                pix += 4;
+            }
+        }
+        pix = (short*)image[row*width+2];
+        for (col=2; col < width-2; col++) {
+            FORC3 dev[c] = -foveon_apply_curve (curve[7], pix[c] -
+                                                ((smrow[1][col][c] + 
+                                                  2*smrow[2][col][c] + 
+                                                  smrow[3][col][c]) >> 2));
+            sum = (dev[0] + dev[1] + dev[2]) >> 3;
+            FORC3 pix[c] += dev[c] - sum;
+            pix += 4;
+        }
+    }
+    for (smlast=-1, row=2; row < height-2; row++) {
+        while (smlast < row+2) {
+            for (i=0; i < 6; i++)
+                smrow[(i+5) % 6] = smrow[i];
+            pix = (short*)image[++smlast*width+2];
+            for (col=2; col < width-2; col++) {
+                FORC3 smrow[4][col][c] =
+                    (pix[c-8]+pix[c-4]+pix[c]+pix[c+4]+pix[c+8]+2) >> 2;
+                pix += 4;
+            }
+        }
+        pix = (short*)image[row*width+2];
+        for (col=2; col < width-2; col++) {
+            for (total[3]=375, sum=60, c=0; c < 3; c++) {
+                for (total[c]=i=0; i < 5; i++)
+                    total[c] += smrow[i][col][c];
+                total[3] += total[c];
+                sum += pix[c];
+            }
+            if (sum < 0) sum = 0;
+            j = total[3] > 375 ? (sum << 16) / total[3] : sum * 174;
+            FORC3 pix[c] += foveon_apply_curve (curve[6],
+                                                ((j*total[c] + 0x8000) >> 16) 
+                                                - pix[c]);
+            pix += 4;
+        }
+    }
+
+    /* Transform the image to a different colorspace */
+    for (pix=(short*)image[0]; pix < (short *) image[height*width]; pix+=4) {
+        FORC3 pix[c] -= foveon_apply_curve (curve[c], pix[c]);
+        sum = (pix[0]+pix[1]+pix[1]+pix[2]) >> 2;
+        FORC3 pix[c] -= foveon_apply_curve (curve[c], pix[c]-sum);
+        FORC3 {
+            for (dsum=i=0; i < 3; i++)
+                dsum += trans[c][i] * pix[i];
+            if (dsum < 0)  dsum = 0;
+            if (dsum > 24000) dsum = 24000;
+            ipix[c] = dsum + 0.5;
+        }
+        FORC3 pix[c] = ipix[c];
+    }
+
+    /* Smooth the image bottom-to-top and save at 1/4 scale */
+    MALLOCARRAY(shrink, (width/4) * (height/4));
+    if (shrink == NULL)
+        pm_error("Out of memory allocating 1/4 scale array");
+
+    for (row = height/4; row > 0; --row) {
+        for (col=0; col < width/4; ++col) {
+            ipix[0] = ipix[1] = ipix[2] = 0;
+            for (i=0; i < 4; i++)
+                for (j=0; j < 4; j++)
+                    FORC3 ipix[c] += image[(row*4+i)*width+col*4+j][c];
+            FORC3
+                if (row+2 > height/4)
+                    shrink[row*(width/4)+col][c] = ipix[c] >> 4;
+                else
+                    shrink[row*(width/4)+col][c] =
+                        (shrink[(row+1)*(width/4)+col][c]*1840 + 
+                         ipix[c]*141 + 2048) >> 12;
+        }
+    }
+    /* From the 1/4-scale image, smooth right-to-left */
+    for (row=0; row < (height & ~3); ++row) {
+        ipix[0] = ipix[1] = ipix[2] = 0;
+        if ((row & 3) == 0)
+            for (col = width & ~3 ; col--; )
+                FORC3 smrow[0][col][c] = ipix[c] =
+                    (shrink[(row/4)*(width/4)+col/4][c]*1485 + 
+                     ipix[c]*6707 + 4096) >> 13;
+
+        /* Then smooth left-to-right */
+        ipix[0] = ipix[1] = ipix[2] = 0;
+        for (col=0; col < (width & ~3); col++)
+            FORC3 smrow[1][col][c] = ipix[c] =
+                (smrow[0][col][c]*1485 + ipix[c]*6707 + 4096) >> 13;
+
+        /* Smooth top-to-bottom */
+        if (row == 0)
+            memcpy (smrow[2], smrow[1], sizeof **smrow * width);
+        else
+            for (col=0; col < (width & ~3); col++)
+                FORC3 smrow[2][col][c] =
+                    (smrow[2][col][c]*6707 + smrow[1][col][c]*1485 + 4096) 
+                        >> 13;
+
+        /* Adjust the chroma toward the smooth values */
+        for (col=0; col < (width & ~3); col++) {
+            for (i=j=30, c=0; c < 3; c++) {
+                i += smrow[2][col][c];
+                j += image[row*width+col][c];
+            }
+            j = (j << 16) / i;
+            for (sum=c=0; c < 3; c++) {
+                ipix[c] = foveon_apply_curve(
+                    curve[c+3],
+                    ((smrow[2][col][c] * j + 0x8000) >> 16) - 
+                    image[row*width+col][c]);
+                sum += ipix[c];
+            }
+            sum >>= 3;
+            FORC3 {
+                i = image[row*width+col][c] + ipix[c] - sum;
+                if (i < 0) i = 0;
+                image[row*width+col][c] = i;
+            }
+        }
+    }
+    free (shrink);
+    free (smrow[6]);
+    for (i=0; i < 8; i++)
+        free (curve[i]);
+
+    /* Trim off the black border */
+    active[1] -= keep[1];
+    active[3] -= 2;
+    i = active[2] - active[0];
+    for (row = 0; row < active[3]-active[1]; row++)
+        memcpy (image[row*i], image[(row+active[1])*width+active[0]],
+                i * sizeof *image);
+    width = i;
+    height = row;
+}
diff --git a/converter/other/cameratopam/foveon.h b/converter/other/cameratopam/foveon.h
new file mode 100644
index 00000000..57be2244
--- /dev/null
+++ b/converter/other/cameratopam/foveon.h
@@ -0,0 +1,14 @@
+#include "pm.h"
+
+void 
+parse_foveon(FILE * const ifp);
+
+void  
+foveon_interpolate(float coeff[3][4]);
+
+void 
+foveon_load_raw(void);
+
+void  
+foveon_coeff(bool * const useCoeffP,
+             float        coeff[3][4]);
diff --git a/converter/other/cameratopam/global_variables.h b/converter/other/cameratopam/global_variables.h
new file mode 100644
index 00000000..c8732d5a
--- /dev/null
+++ b/converter/other/cameratopam/global_variables.h
@@ -0,0 +1,55 @@
+/* These are unfortunately global variables used by various modules.
+
+   It would be nice to clean up this code and get rid of all of these.
+*/
+#include <stdio.h>
+#include <time.h>
+#include "pm_c_util.h"
+
+extern FILE * ifp;
+extern int flip;
+extern int data_offset;
+extern int raw_width;
+extern int raw_height;
+extern int height;
+extern int width;
+extern char * meta_data;
+extern int meta_offset;
+extern int meta_length;
+extern char make[64];
+extern char model[70];
+extern char model2[64];
+extern time_t timestamp;
+extern int is_foveon;
+extern int is_dng;
+extern int is_canon;
+extern unsigned short (*image)[4];
+extern int maximum;
+extern int clip_max;
+extern short order;
+extern int zero_after_ff;
+extern int shrink;
+extern int iwidth;
+extern unsigned int filters;
+extern int black;
+extern unsigned short white[8][8];
+extern int top_margin;
+extern int left_margin;
+extern int fuji_width;
+extern int tiff_samples;
+extern int tiff_data_compression;
+extern int kodak_data_compression;
+extern int fuji_secondary;
+extern int use_coeff;
+extern int use_gamma;
+extern int xmag;
+extern int ymag;
+extern float cam_mul[4];
+#define camera_red  cam_mul[0]
+#define camera_blue cam_mul[2]
+extern float pre_mul[4];
+extern int colors;
+extern unsigned short curve[0x1000];
+extern float coeff[3][4];
+extern int use_secondary;
+extern bool verbose;
diff --git a/converter/other/cameratopam/identify.c b/converter/other/cameratopam/identify.c
new file mode 100644
index 00000000..a101c8ad
--- /dev/null
+++ b/converter/other/cameratopam/identify.c
@@ -0,0 +1,1183 @@
+#define _BSD_SOURCE   /* Make sure strcasecmp() is in string.h */
+#include <string.h>
+
+#include "pm.h"
+
+#include "global_variables.h"
+#include "util.h"
+#include "foveon.h"
+#include "canon.h"
+#include "dng.h"
+#include "ljpeg.h"
+#include "camera.h"
+
+#include "identify.h"
+
+
+#if HAVE_INT64
+   static bool const have64BitArithmetic = true;
+#else
+   static bool const have64BitArithmetic = false;
+#endif
+
+
+static loadRawFn load_raw;
+
+/* This does the same as the function of the same name in the GNU C library */
+static const char *memmem_internal (const char *haystack, size_t haystacklen,
+                     const char *needle, size_t needlelen)
+{
+  const char *c;
+  for (c = haystack; c <= haystack + haystacklen - needlelen; c++)
+    if (!memcmp (c, needle, needlelen))
+      return c;
+  return NULL;
+}
+
+/*
+   Thanks to Adobe for providing these excellent CAM -> XYZ matrices!
+ */
+static void 
+adobe_coeff()
+{
+  static const struct {
+    const char *prefix;
+    short trans[12];
+  } table[] = {
+    { "Canon EOS D2000C",
+    { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } },
+    { "Canon EOS D30",
+    { 9805,-2689,-1312,-5803,13064,3068,-2438,3075,8775 } },
+    { "Canon EOS D60",
+    { 6188,-1341,-890,-7168,14489,2937,-2640,3228,8483 } },
+    { "Canon EOS 10D",
+    { 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } },
+    { "Canon EOS 20D",
+    { 6599,-537,-891,-8071,15783,2424,-1983,2234,7462 } },
+    { "Canon EOS-1Ds Mark II",
+    { 6517,-602,-867,-8180,15926,2378,-1618,1771,7633 } },
+    { "Canon EOS-1D Mark II",
+    { 6264,-582,-724,-8312,15948,2504,-1744,1919,8664 } },
+    { "Canon EOS-1DS",
+    { 4374,3631,-1743,-7520,15212,2472,-2892,3632,8161 } },
+    { "Canon EOS-1D",
+    { 6906,-278,-1017,-6649,15074,1621,-2848,3897,7611 } },
+    { "Canon EOS",
+    { 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } },
+    { "Canon PowerShot 600",
+    { -3822,10019,1311,4085,-157,3386,-5341,10829,4812,-1969,10969,1126 } },
+    { "Canon PowerShot A50",
+    { -5300,9846,1776,3436,684,3939,-5540,9879,6200,-1404,11175,217 } },
+    { "Canon PowerShot A5",
+    { -4801,9475,1952,2926,1611,4094,-5259,10164,5947,-1554,10883,547 } },
+    { "Canon PowerShot G1",
+    { -4778,9467,2172,4743,-1141,4344,-5146,9908,6077,-1566,11051,557 } },
+    { "Canon PowerShot G2",
+    { 9087,-2693,-1049,-6715,14382,2537,-2291,2819,7790 } },
+    { "Canon PowerShot G3",
+    { 9212,-2781,-1073,-6573,14189,2605,-2300,2844,7664 } },
+    { "Canon PowerShot G5",
+    { 9757,-2872,-933,-5972,13861,2301,-1622,2328,7212 } },
+    { "Canon PowerShot G6",
+    { 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 } },
+    { "Canon PowerShot Pro1",
+    { 10062,-3522,-999,-7643,15117,2730,-765,817,7323 } },
+    { "Canon PowerShot Pro70",
+    { -4155,9818,1529,3939,-25,4522,-5521,9870,6610,-2238,10873,1342 } },
+    { "Canon PowerShot Pro90",
+    { -4963,9896,2235,4642,-987,4294,-5162,10011,5859,-1770,11230,577 } },
+    { "Canon PowerShot S30",
+    { 10566,-3652,-1129,-6552,14662,2006,-2197,2581,7670 } },
+    { "Canon PowerShot S40",
+    { 8510,-2487,-940,-6869,14231,2900,-2318,2829,9013 } },
+    { "Canon PowerShot S45",
+    { 8163,-2333,-955,-6682,14174,2751,-2077,2597,8041 } },
+    { "Canon PowerShot S50",
+    { 8882,-2571,-863,-6348,14234,2288,-1516,2172,6569 } },
+    { "Canon PowerShot S70",
+    { 9976,-3810,-832,-7115,14463,2906,-901,989,7889 } },
+    { "Contax N Digital",
+    { 7777,1285,-1053,-9280,16543,2916,-3677,5679,7060 } },
+    { "EPSON R-D1",
+    { 6827,-1878,-732,-8429,16012,2564,-704,592,7145 } },
+    { "FUJIFILM FinePix E550",
+    { 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 } },
+    { "FUJIFILM FinePix F700",
+    { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } },
+    { "FUJIFILM FinePix S20Pro",
+    { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } },
+    { "FUJIFILM FinePix S2Pro",
+    { 12492,-4690,-1402,-7033,15423,1647,-1507,2111,7697 } },
+    { "FUJIFILM FinePix S3Pro",
+    { 11807,-4612,-1294,-8927,16968,1988,-2120,2741,8006 } },
+    { "FUJIFILM FinePix S5000",
+    { 8754,-2732,-1019,-7204,15069,2276,-1702,2334,6982 } },
+    { "FUJIFILM FinePix S5100",
+    { 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 } },
+    { "FUJIFILM FinePix S7000",
+    { 10190,-3506,-1312,-7153,15051,2238,-2003,2399,7505 } },
+    { "Kodak DCS315C",
+    { 17523,-4827,-2510,756,8546,-137,6113,1649,2250 } },
+    { "Kodak DCS330C",
+    { 20620,-7572,-2801,-103,10073,-396,3551,-233,2220 } },
+    { "KODAK DCS420",
+    { 10868,-1852,-644,-1537,11083,484,2343,628,2216 } },
+    { "KODAK DCS460",
+    { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } },
+    { "KODAK EOSDCS1",
+    { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } },
+    { "KODAK EOSDCS3B",
+    { 9898,-2700,-940,-2478,12219,206,1985,634,1031 } },
+    { "Kodak DCS520C",
+    { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } },
+    { "Kodak DCS560C",
+    { 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } },
+    { "Kodak DCS620C",
+    { 23617,-10175,-3149,-2054,11749,-272,2586,-489,3453 } },
+    { "Kodak DCS620X",
+    { 13095,-6231,154,12221,-21,-2137,895,4602,2258 } },
+    { "Kodak DCS660C",
+    { 18244,-6351,-2739,-791,11193,-521,3711,-129,2802 } },
+    { "Kodak DCS720X",
+    { 11775,-5884,950,9556,1846,-1286,-1019,6221,2728 } },
+    { "Kodak DCS760C",
+    { 16623,-6309,-1411,-4344,13923,323,2285,274,2926 } },
+    { "Kodak DCS Pro SLR",
+    { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } },
+    { "Kodak DCS Pro 14nx",
+    { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } },
+    { "Kodak DCS Pro 14",
+    { 7791,3128,-776,-8588,16458,2039,-2455,4006,6198 } },
+    { "Kodak ProBack645",
+    { 16414,-6060,-1470,-3555,13037,473,2545,122,4948 } },
+    { "Kodak ProBack",
+    { 21179,-8316,-2918,-915,11019,-165,3477,-180,4210 } },
+    { "LEICA DIGILUX 2",
+    { 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } },
+    { "Leaf Valeo",
+    { 8236,1746,-1314,-8251,15953,2428,-3673,5786,5771 } },
+    { "Minolta DiMAGE 5",
+    { 8983,-2942,-963,-6556,14476,2237,-2426,2887,8014 } },
+    { "Minolta DiMAGE 7",
+    { 9144,-2777,-998,-6676,14556,2281,-2470,3019,7744 } },
+    { "Minolta DiMAGE A1",
+    { 9274,-2547,-1167,-8220,16323,1943,-2273,2720,8340 } },
+    { "MINOLTA DiMAGE A200",
+    { 8560,-2487,-986,-8112,15535,2771,-1209,1324,7743 } },
+    { "Minolta DiMAGE A2",
+    { 9097,-2726,-1053,-8073,15506,2762,-966,981,7763 } },
+    { "MINOLTA DYNAX 7D",
+    { 10239,-3104,-1099,-8037,15727,2451,-927,925,6871 } },
+    { "NIKON D100",
+    { 5915,-949,-778,-7516,15364,2282,-1228,1337,6404 } },
+    { "NIKON D1H",
+    { 7577,-2166,-926,-7454,15592,1934,-2377,2808,8606 } },
+    { "NIKON D1X",
+    { 7620,-2173,-966,-7604,15843,1805,-2356,2811,8439 } },
+    { "NIKON D1",
+    { 7559,-2130,-965,-7611,15713,1972,-2478,3042,8290 } },
+    { "NIKON D2H",
+    { 5710,-901,-615,-8594,16617,2024,-2975,4120,6830 } },
+    { "NIKON D70",
+    { 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } },
+    { "NIKON E995", /* copied from E5000 */
+    { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
+    { "NIKON E2500",
+    { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
+    { "NIKON E4500",
+    { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
+    { "NIKON E5000",
+    { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
+    { "NIKON E5400",
+    { 9349,-2987,-1001,-7919,15766,2266,-2098,2680,6839 } },
+    { "NIKON E5700",
+    { -5368,11478,2368,5537,-113,3148,-4969,10021,5782,778,9028,211 } },
+    { "NIKON E8400",
+    { 7842,-2320,-992,-8154,15718,2599,-1098,1342,7560 } },
+    { "NIKON E8700",
+    { 8489,-2583,-1036,-8051,15583,2643,-1307,1407,7354 } },
+    { "NIKON E8800",
+    { 7971,-2314,-913,-8451,15762,2894,-1442,1520,7610 } },
+    { "OLYMPUS C5050",
+    { 10508,-3124,-1273,-6079,14294,1901,-1653,2306,6237 } },
+    { "OLYMPUS C5060",
+    { 10445,-3362,-1307,-7662,15690,2058,-1135,1176,7602 } },
+    { "OLYMPUS C70",
+    { 10793,-3791,-1146,-7498,15177,2488,-1390,1577,7321 } },
+    { "OLYMPUS C80",
+    { 8606,-2509,-1014,-8238,15714,2703,-942,979,7760 } },
+    { "OLYMPUS E-10",
+    { 12745,-4500,-1416,-6062,14542,1580,-1934,2256,6603 } },
+    { "OLYMPUS E-1",
+    { 11846,-4767,-945,-7027,15878,1089,-2699,4122,8311 } },
+    { "OLYMPUS E-20",
+    { 13173,-4732,-1499,-5807,14036,1895,-2045,2452,7142 } },
+    { "OLYMPUS E-300",
+    { 7828,-1761,-348,-5788,14071,1830,-2853,4518,6557 } },
+    { "PENTAX *ist D",
+    { 9651,-2059,-1189,-8881,16512,2487,-1460,1345,10687 } },
+    { "Panasonic DMC-LC1",
+    { 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } },
+    { "SONY DSC-F828",
+    { 7924,-1910,-777,-8226,15459,2998,-1517,2199,6818,-7242,11401,3481 } },
+    { "SONY DSC-V3",
+    { 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 } }
+  };
+  double cc[4][4], cm[4][3], xyz[] = { 1,1,1 };
+  char name[130];
+  int i, j;
+
+  for (i=0; i < 4; i++)
+    for (j=0; j < 4; j++)
+      cc[i][j] = i == j;
+  sprintf (name, "%s %s", make, model);
+  for (i=0; i < sizeof table / sizeof *table; i++)
+    if (!strncmp (name, table[i].prefix, strlen(table[i].prefix))) {
+      for (j=0; j < 12; j++)
+    cm[0][j] = table[i].trans[j];
+      dng_coeff (cc, cm, xyz);
+      break;
+    }
+}
+
+/*
+   Identify which camera created this file, and set global variables
+   accordingly.  Return nonzero if the file cannot be decoded.
+ */
+int
+identify(FILE *       const ifp,
+         bool         const use_secondary,
+         bool         const use_camera_rgb,
+         float        const red_scale,
+         float        const blue_scale,
+         unsigned int const four_color_rgb,
+         const char * const inputFileName,
+         loadRawFn *  const loadRawFnP)
+{
+  char head[32];
+  char * c;
+  unsigned hlen, fsize, i;
+  static const struct {
+    int fsize;
+    char make[12], model[15], withjpeg;
+  } table[] = {
+    {    62464, "Kodak",    "DC20"       ,0 },
+    {   124928, "Kodak",    "DC20"       ,0 },
+    {   311696, "ST Micro", "STV680 VGA" ,0 },  /* SPYz */
+    {   787456, "Creative", "PC-CAM 600" ,0 },
+    {  2465792, "NIKON",    "E950"       ,1 },  /* or E800,E700 */
+    {  2940928, "NIKON",    "E2100"      ,1 },  /* or E2500 */
+    {  4771840, "NIKON",    "E990"       ,1 },  /* or E995 */
+    {  4775936, "NIKON",    "E3700"      ,1 },  /* or Optio 33WR */
+    {  5869568, "NIKON",    "E4300"      ,1 },  /* or DiMAGE Z2 */
+    {  5865472, "NIKON",    "E4500"      ,0 },
+    {  1976352, "CASIO",    "QV-2000UX"  ,0 },
+    {  3217760, "CASIO",    "QV-3*00EX"  ,0 },
+    {  6218368, "CASIO",    "QV-5700"    ,0 },
+    {  7530816, "CASIO",    "QV-R51"     ,1 },
+    {  7684000, "CASIO",    "QV-4000"    ,0 },
+    {  7753344, "CASIO",    "EX-Z55"     ,1 },
+    {  9313536, "CASIO",    "EX-P600"    ,1 },
+    { 10979200, "CASIO",    "EX-P700"    ,1 },
+    {  3178560, "PENTAX",   "Optio S"    ,1 },  /*  8-bit */
+    {  4841984, "PENTAX",   "Optio S"    ,1 },  /* 12-bit */
+    {  6114240, "PENTAX",   "Optio S4"   ,1 },  /* or S4i */
+    { 12582980, "Sinar",    ""           ,0 } };
+  static const char *corp[] =
+    { "Canon", "NIKON", "EPSON", "Kodak", "OLYMPUS", "PENTAX",
+      "MINOLTA", "Minolta", "Konica", "CASIO" };
+  float tmp;
+
+  /*  What format is this file?  Set make[] if we recognize it. */
+
+  raw_height = raw_width = fuji_width = flip = 0;
+  make[0] = model[0] = model2[0] = 0;
+  memset (white, 0, sizeof white);
+  timestamp = tiff_samples = 0;
+  data_offset = meta_length = tiff_data_compression = 0;
+  zero_after_ff = is_dng = fuji_secondary = filters = 0;
+  black = is_foveon = use_coeff = 0;
+  use_gamma = xmag = ymag = 1;
+  for (i=0; i < 4; i++) {
+    cam_mul[i] = 1 & i;
+    pre_mul[i] = 1;
+  }
+  colors = 3;
+  for (i=0; i < 0x1000; i++) curve[i] = i;
+  maximum = 0xfff;
+#ifdef USE_LCMS
+  profile_length = 0;
+#endif
+
+  order = get2(ifp);
+  hlen = get4(ifp);
+  fseek (ifp, 0, SEEK_SET);
+  fread (head, 1, 32, ifp);
+  fseek (ifp, 0, SEEK_END);
+  fsize = ftell(ifp);
+  if ((c = (char*)memmem_internal(head, 32, "MMMMRawT", 8))) {
+    strcpy (make, "Phase One");
+    data_offset = c - head;
+    fseek (ifp, data_offset + 8, SEEK_SET);
+    fseek (ifp, get4(ifp) + 136, SEEK_CUR);
+    raw_width = get4(ifp);
+    fseek (ifp, 12, SEEK_CUR);
+    raw_height = get4(ifp);
+  } else if (order == 0x4949 || order == 0x4d4d) {
+    if (!memcmp (head+6, "HEAPCCDR", 8)) {
+      data_offset = hlen;
+      parse_ciff(ifp, hlen, fsize - hlen);
+    } else {
+      parse_tiff(ifp, 0);
+      if (!strncmp(make,"NIKON",5) && filters == 0)
+    make[0] = 0;
+    }
+  } else if (!memcmp (head, "\0MRM", 4))
+    parse_minolta(ifp);
+    else if (!memcmp (head, "\xff\xd8\xff\xe1", 4) &&
+         !memcmp (head+6, "Exif", 4)) {
+    fseek (ifp, 4, SEEK_SET);
+    fseek (ifp, 4 + get2(ifp), SEEK_SET);
+    if (fgetc(ifp) != 0xff)
+      parse_tiff(ifp, 12);
+  } else if (!memcmp (head, "BM", 2)) {
+    data_offset = 0x1000;
+    order = 0x4949;
+    fseek (ifp, 38, SEEK_SET);
+    if (get4(ifp) == 2834 && get4(ifp) == 2834) {
+      strcpy (model, "BMQ");
+      flip = 3;
+      goto nucore;
+    }
+  } else if (!memcmp (head, "BR", 2)) {
+    strcpy (model, "RAW");
+nucore:
+    strcpy (make, "Nucore");
+    order = 0x4949;
+    fseek (ifp, 10, SEEK_SET);
+    data_offset += get4(ifp);
+    get4(ifp);
+    raw_width  = get4(ifp);
+    raw_height = get4(ifp);
+    if (model[0] == 'B' && raw_width == 2597) {
+      raw_width++;
+      data_offset -= 0x1000;
+    }
+  } else if (!memcmp (head+25, "ARECOYK", 7)) {
+    strcpy (make, "Contax");
+    strcpy (model,"N Digital");
+    fseek (ifp, 60, SEEK_SET);
+    camera_red  = get4(ifp);
+    camera_red /= get4(ifp);
+    camera_blue = get4(ifp);
+    camera_blue = get4(ifp) / camera_blue;
+  } else if (!memcmp (head, "FUJIFILM", 8)) {
+    long data_offset_long;
+    fseek (ifp, 84, SEEK_SET);
+    parse_tiff(ifp, get4(ifp)+12);
+    fseek (ifp, 100, SEEK_SET);
+    pm_readbiglong(ifp, &data_offset_long);
+    data_offset = data_offset_long;
+  } else if (!memcmp (head, "DSC-Image", 9))
+    parse_rollei(ifp);
+  else if (!memcmp (head, "FOVb", 4))
+    parse_foveon(ifp);
+  else
+      for (i=0; i < sizeof table / sizeof *table; i++)
+          if (fsize == table[i].fsize) {
+              strcpy (make,  table[i].make );
+              strcpy (model, table[i].model);
+              if (table[i].withjpeg)
+                  parse_external_jpeg(inputFileName);
+          }
+  parse_mos(ifp, 8);
+  parse_mos(ifp, 3472);
+
+  for (i=0; i < sizeof corp / sizeof *corp; i++)
+    if (strstr (make, corp[i]))     /* Simplify company names */
+    strcpy (make, corp[i]);
+  if (!strncmp (make, "KODAK", 5))
+    make[16] = model[16] = 0;
+  c = make + strlen(make);      /* Remove trailing spaces */
+  while (*--c == ' ') *c = 0;
+  c = model + strlen(model);
+  while (*--c == ' ') *c = 0;
+  i = strlen(make);         /* Remove make from model */
+  if (!strncmp (model, make, i++))
+    memmove (model, model+i, 64-i);
+  make[63] = model[63] = model2[63] = 0;
+
+  if (make[0] == 0) {
+    pm_message ("unrecognized file format.");
+    return 1;
+  }
+
+/*  File format is OK.  Do we know this camera? */
+/*  Start with some useful defaults:           */
+
+  top_margin = left_margin = 0;
+  if ((raw_height | raw_width) < 0)
+       raw_height = raw_width  = 0;
+  height = raw_height;
+  width  = raw_width;
+  if (fuji_width) {
+    width = height + fuji_width;
+    height = width - 1;
+    ymag = 1;
+  }
+  load_raw = NULL;
+  if (is_dng) {
+    strcat (model, " DNG");
+    if (!filters)
+      colors = tiff_samples;
+    if (tiff_data_compression == 1)
+      load_raw = adobe_dng_load_raw_nc;
+    if (tiff_data_compression == 7)
+      load_raw = adobe_dng_load_raw_lj;
+    goto dng_skip;
+  }
+
+/*  We'll try to decode anything from Canon or Nikon. */
+
+  if (!filters) filters = 0x94949494;
+  if ((is_canon = !strcmp(make,"Canon")))
+    load_raw = memcmp (head+6,"HEAPCCDR",8) ?
+        lossless_jpeg_load_raw : canon_compressed_load_raw;
+  if (!strcmp(make,"NIKON"))
+    load_raw = nikon_is_compressed() ?
+    nikon_compressed_load_raw : nikon_load_raw;
+
+/* Set parameters based on camera name (for non-DNG files). */
+
+  if (is_foveon) {
+    if (!have64BitArithmetic)
+      pm_error("This program was built without 64 bit arithmetic "
+               "capability and the Foveon format requires it.");
+    if (height*2 < width) ymag = 2;
+    if (width < height) xmag = 2;
+    filters = 0;
+    load_raw = foveon_load_raw;
+    foveon_coeff(&use_coeff, coeff);
+  } else if (!strcmp(model,"PowerShot 600")) {
+    height = 613;
+    width  = 854;
+    colors = 4;
+    filters = 0xe1e4e1e4;
+    load_raw = canon_600_load_raw;
+  } else if (!strcmp(model,"PowerShot A5") ||
+         !strcmp(model,"PowerShot A5 Zoom")) {
+    height = 773;
+    width  = 960;
+    raw_width = 992;
+    colors = 4;
+    filters = 0x1e4e1e4e;
+    load_raw = canon_a5_load_raw;
+  } else if (!strcmp(model,"PowerShot A50")) {
+    height =  968;
+    width  = 1290;
+    raw_width = 1320;
+    colors = 4;
+    filters = 0x1b4e4b1e;
+    load_raw = canon_a5_load_raw;
+  } else if (!strcmp(model,"PowerShot Pro70")) {
+    height = 1024;
+    width  = 1552;
+    colors = 4;
+    filters = 0x1e4b4e1b;
+    load_raw = canon_a5_load_raw;
+    black = 34;
+  } else if (!strcmp(model,"PowerShot Pro90 IS")) {
+    width  = 1896;
+    colors = 4;
+    filters = 0xb4b4b4b4;
+  } else if (is_canon && raw_width == 2144) {
+    height = 1550;
+    width  = 2088;
+    top_margin  = 8;
+    left_margin = 4;
+    if (!strcmp(model,"PowerShot G1")) {
+      colors = 4;
+      filters = 0xb4b4b4b4;
+    }
+  } else if (is_canon && raw_width == 2224) {
+    height = 1448;
+    width  = 2176;
+    top_margin  = 6;
+    left_margin = 48;
+  } else if (is_canon && raw_width == 2376) {
+    height = 1720;
+    width  = 2312;
+    top_margin  = 6;
+    left_margin = 12;
+  } else if (is_canon && raw_width == 2672) {
+    height = 1960;
+    width  = 2616;
+    top_margin  = 6;
+    left_margin = 12;
+  } else if (is_canon && raw_width == 3152) {
+    height = 2056;
+    width  = 3088;
+    top_margin  = 12;
+    left_margin = 64;
+    maximum = 0xfa0;
+  } else if (is_canon && raw_width == 3160) {
+    height = 2328;
+    width  = 3112;
+    top_margin  = 12;
+    left_margin = 44;
+  } else if (is_canon && raw_width == 3344) {
+    height = 2472;
+    width  = 3288;
+    top_margin  = 6;
+    left_margin = 4;
+  } else if (!strcmp(model,"EOS D2000C")) {
+    filters = 0x61616161;
+    black = curve[200];
+  } else if (!strcmp(model,"EOS-1D")) {
+    raw_height = height = 1662;
+    raw_width  = width  = 2496;
+    data_offset = 288912;
+    filters = 0x61616161;
+  } else if (!strcmp(model,"EOS-1DS")) {
+    raw_height = height = 2718;
+    raw_width  = width  = 4082;
+    data_offset = 289168;
+    filters = 0x61616161;
+  } else if (is_canon && raw_width == 3516) {
+    top_margin  = 14;
+    left_margin = 42;
+    goto canon_cr2;
+  } else if (is_canon && raw_width == 3596) {
+    top_margin  = 12;
+    left_margin = 74;
+    goto canon_cr2;
+  } else if (is_canon && raw_width == 5108) {
+    top_margin  = 13;
+    left_margin = 98;
+    maximum = 0xe80;
+canon_cr2:
+    height = raw_height - top_margin;
+    width  = raw_width - left_margin;
+  } else if (!strcmp(model,"D1")) {
+    camera_red  *= 256/527.0;
+    camera_blue *= 256/317.0;
+  } else if (!strcmp(model,"D1X")) {
+    width  = 4024;
+    ymag = 2;
+  } else if (!strcmp(model,"D100")) {
+    if (tiff_data_compression == 34713 && load_raw == nikon_load_raw)
+      raw_width = (width += 3) + 3;
+  } else if (!strcmp(model,"D2H")) {
+    width  = 2482;
+    left_margin = 6;
+  } else if (!strcmp(model,"D2X")) {
+    width  = 4312;
+    pre_mul[0] = 1.514;
+    pre_mul[2] = 1.727;
+  } else if (fsize == 2465792) {
+    height = 1203;
+    width  = 1616;
+    filters = 0x4b4b4b4b;
+    colors = 4;
+    load_raw = nikon_e950_load_raw;
+    nikon_e950_coeff();
+    pre_mul[0] = 1.18193;
+    pre_mul[2] = 1.16452;
+    pre_mul[3] = 1.17250;
+  } else if (!strcmp(model,"E990")) {
+    if (!timestamp && !nikon_e990()) goto cp_e995;
+    height = 1540;
+    width  = 2064;
+    colors = 4;
+    filters = 0xb4b4b4b4;
+    nikon_e950_coeff();
+    pre_mul[0] = 1.196;
+    pre_mul[1] = 1.246;
+    pre_mul[2] = 1.018;
+  } else if (!strcmp(model,"E995")) {
+cp_e995:
+    strcpy (model, "E995");
+    height = 1540;
+    width  = 2064;
+    colors = 4;
+    filters = 0xe1e1e1e1;
+  } else if (!strcmp(model,"E2100")) {
+    if (!timestamp && !nikon_e2100()) goto cp_e2500;
+    width = 1616;
+    height = 1206;
+    load_raw = nikon_e2100_load_raw;
+    pre_mul[0] = 1.945;
+    pre_mul[2] = 1.040;
+  } else if (!strcmp(model,"E2500")) {
+cp_e2500:
+    strcpy (model, "E2500");
+    width = 1616;
+    height = 1204;
+    colors = 4;
+    filters = 0x4b4b4b4b;
+  } else if (!strcmp(model,"E3700")) {
+    if (!timestamp && pentax_optio33()) goto optio_33wr;
+    height = 1542;
+    width  = 2064;
+    load_raw = nikon_e2100_load_raw;
+    pre_mul[0] = 1.818;
+    pre_mul[2] = 1.618;
+  } else if (!strcmp(model,"Optio 33WR")) {
+optio_33wr:
+    strcpy (make, "PENTAX");
+    strcpy (model,"Optio 33WR");
+    height = 1542;
+    width  = 2064;
+    load_raw = nikon_e2100_load_raw;
+    flip = 1;
+    filters = 0x16161616;
+    pre_mul[0] = 1.331;
+    pre_mul[2] = 1.820;
+  } else if (!strcmp(model,"E4300")) {
+    if (!timestamp && minolta_z2()) goto dimage_z2;
+    height = 1710;
+    width  = 2288;
+    filters = 0x16161616;
+    pre_mul[0] = 508;
+    pre_mul[1] = 256;
+    pre_mul[2] = 322;
+  } else if (!strcmp(model,"DiMAGE Z2")) {
+dimage_z2:
+    strcpy (make, "MINOLTA");
+    strcpy (model,"DiMAGE Z2");
+    height = 1710;
+    width  = 2288;
+    filters = 0x16161616;
+    load_raw = nikon_e2100_load_raw;
+    pre_mul[0] = 508;
+    pre_mul[1] = 256;
+    pre_mul[2] = 450;
+  } else if (!strcmp(model,"E4500")) {
+    height = 1708;
+    width  = 2288;
+    colors = 4;
+    filters = 0xb4b4b4b4;
+  } else if (!strcmp(model,"R-D1")) {
+    tiff_data_compression = 34713;
+    load_raw = nikon_load_raw;
+  } else if (!strcmp(model,"FinePixS2Pro")) {
+    height = 3584;
+    width  = 3583;
+    fuji_width = 2144;
+    filters = 0x61616161;
+    load_raw = fuji_s2_load_raw;
+    black = 128;
+    strcpy (model+7, " S2Pro");
+  } else if (!strcmp(model,"FinePix S3Pro")) {
+    height = 3583;
+    width  = 3584;
+    fuji_width = 2144;
+    if (fsize > 18000000 && use_secondary)
+      data_offset += 4352*2*1444;
+    filters = 0x49494949;
+    load_raw = fuji_s3_load_raw;
+    maximum = 0xffff;
+  } else if (!strcmp(model,"FinePix S5000")) {
+    height = 2499;
+    width  = 2500;
+    fuji_width = 1423;
+    filters = 0x49494949;
+    load_raw = fuji_s5000_load_raw;
+    maximum = 0x3e00;
+  } else if (!strcmp(model,"FinePix S5100") ||
+         !strcmp(model,"FinePix S5500")) {
+    height = 1735;
+    width  = 2304;
+    data_offset += width*10;
+    filters = 0x49494949;
+    load_raw = unpacked_load_raw;
+    maximum = 0xffff;
+  } else if (!strcmp(model,"FinePix E550") ||
+         !strcmp(model,"FinePix F810") ||
+         !strcmp(model,"FinePix S7000")) {
+    height = 3587;
+    width  = 3588;
+    fuji_width = 2047;
+    filters = 0x49494949;
+    load_raw = fuji_s7000_load_raw;
+    maximum = 0x3e00;
+  } else if (!strcmp(model,"FinePix F700") ||
+         !strcmp(model,"FinePix S20Pro")) {
+    height = 2523;
+    width  = 2524;
+    fuji_width = 1440;
+    filters = 0x49494949;
+    load_raw = fuji_f700_load_raw;
+    maximum = 0x3e00;
+  } else if (!strcmp(model,"Digital Camera KD-400Z")) {
+    height = 1712;
+    width  = 2312;
+    raw_width = 2336;
+    data_offset = 4034;
+    fseek (ifp, 2032, SEEK_SET);
+    goto konica_400z;
+  } else if (!strcmp(model,"Digital Camera KD-510Z")) {
+    data_offset = 4032;
+    pre_mul[0] = 1.297;
+    pre_mul[2] = 1.438;
+    fseek (ifp, 2032, SEEK_SET);
+    goto konica_510z;
+  } else if (!strcasecmp(make,"MINOLTA")) {
+    load_raw = unpacked_load_raw;
+    maximum = 0xf7d;
+    if (!strncmp(model,"DiMAGE A",8)) {
+      if (!strcmp(model,"DiMAGE A200")) {
+    filters = 0x49494949;
+    tmp = camera_red;
+    camera_red  = 1 / camera_blue;
+    camera_blue = 1 / tmp;
+      }
+      load_raw = packed_12_load_raw;
+      maximum = model[8] == '1' ? 0xf8b : 0xfff;
+    } else if (!strncmp(model,"ALPHA",5) ||
+           !strncmp(model,"DYNAX",5) ||
+           !strncmp(model,"MAXXUM",6)) {
+      load_raw = packed_12_load_raw;
+      maximum = 0xffb;
+    } else if (!strncmp(model,"DiMAGE G",8)) {
+      if (model[8] == '4') {
+    data_offset = 5056;
+    pre_mul[0] = 1.602;
+    pre_mul[2] = 1.441;
+    fseek (ifp, 2078, SEEK_SET);
+    height = 1716;
+    width  = 2304;
+      } else if (model[8] == '5') {
+    data_offset = 4016;
+    fseek (ifp, 1936, SEEK_SET);
+konica_510z:
+    height = 1956;
+    width  = 2607;
+    raw_width = 2624;
+      } else if (model[8] == '6') {
+    data_offset = 4032;
+    fseek (ifp, 2030, SEEK_SET);
+    height = 2136;
+    width  = 2848;
+      }
+      filters = 0x61616161;
+konica_400z:
+      load_raw = unpacked_load_raw;
+      maximum = 0x3df;
+      order = 0x4d4d;
+      camera_red   = get2(ifp);
+      camera_blue  = get2(ifp);
+      camera_red  /= get2(ifp);
+      camera_blue /= get2(ifp);
+    }
+    if (pre_mul[0] == 1 && pre_mul[2] == 1) {
+      pre_mul[0] = 1.42;
+      pre_mul[2] = 1.25;
+    }
+  } else if (!strcmp(model,"*ist D")) {
+    load_raw = unpacked_load_raw;
+  } else if (!strcmp(model,"*ist DS")) {
+    height--;
+    load_raw = packed_12_load_raw;
+  } else if (!strcmp(model,"Optio S")) {
+    if (fsize == 3178560) {
+      height = 1540;
+      width  = 2064;
+      load_raw = eight_bit_load_raw;
+      camera_red  *= 4;
+      camera_blue *= 4;
+      pre_mul[0] = 1.391;
+      pre_mul[2] = 1.188;
+    } else {
+      height = 1544;
+      width  = 2068;
+      raw_width = 3136;
+      load_raw = packed_12_load_raw;
+      maximum = 0xf7c;
+      pre_mul[0] = 1.137;
+      pre_mul[2] = 1.453;
+    }
+  } else if (!strncmp(model,"Optio S4",8)) {
+    height = 1737;
+    width  = 2324;
+    raw_width = 3520;
+    load_raw = packed_12_load_raw;
+    maximum = 0xf7a;
+    pre_mul[0] = 1.980;
+    pre_mul[2] = 1.570;
+  } else if (!strcmp(model,"STV680 VGA")) {
+    height = 484;
+    width  = 644;
+    load_raw = eight_bit_load_raw;
+    flip = 2;
+    filters = 0x16161616;
+    black = 16;
+    pre_mul[0] = 1.097;
+    pre_mul[2] = 1.128;
+  } else if (!strcmp(make,"Phase One")) {
+    switch (raw_height) {
+      case 2060:
+    strcpy (model, "LightPhase");
+    height = 2048;
+    width  = 3080;
+    top_margin  = 5;
+    left_margin = 22;
+    pre_mul[0] = 1.331;
+    pre_mul[2] = 1.154;
+    break;
+      case 2682:
+    strcpy (model, "H10");
+    height = 2672;
+    width  = 4012;
+    top_margin  = 5;
+    left_margin = 26;
+    break;
+      case 4128:
+    strcpy (model, "H20");
+    height = 4098;
+    width  = 4098;
+    top_margin  = 20;
+    left_margin = 26;
+    pre_mul[0] = 1.963;
+    pre_mul[2] = 1.430;
+    break;
+      case 5488:
+    strcpy (model, "H25");
+    height = 5458;
+    width  = 4098;
+    top_margin  = 20;
+    left_margin = 26;
+    pre_mul[0] = 2.80;
+    pre_mul[2] = 1.20;
+    }
+    filters = top_margin & 1 ? 0x94949494 : 0x49494949;
+    load_raw = phase_one_load_raw;
+    maximum = 0xffff;
+  } else if (!strcmp(model,"Ixpress")) {
+    height = 4084;
+    width  = 4080;
+    filters = 0x49494949;
+    load_raw = ixpress_load_raw;
+    maximum = 0xffff;
+    pre_mul[0] = 1.963;
+    pre_mul[2] = 1.430;
+  } else if (!strcmp(make,"Sinar") && !memcmp(head,"8BPS",4)) {
+    fseek (ifp, 14, SEEK_SET);
+    height = get4(ifp);
+    width  = get4(ifp);
+    filters = 0x61616161;
+    data_offset = 68;
+    load_raw = unpacked_load_raw;
+    maximum = 0xffff;
+  } else if (!strcmp(make,"Leaf")) {
+    if (height > width)
+      filters = 0x16161616;
+    load_raw = unpacked_load_raw;
+    maximum = 0x3fff;
+    strcpy (model, "Valeo");
+    if (raw_width == 2060) {
+      filters = 0;
+      load_raw = leaf_load_raw;
+      maximum = 0xffff;
+      strcpy (model, "Volare");
+    }
+  } else if (!strcmp(model,"DIGILUX 2") || !strcmp(model,"DMC-LC1")) {
+    height = 1928;
+    width  = 2568;
+    data_offset = 1024;
+    load_raw = unpacked_load_raw;
+    maximum = 0xfff0;
+  } else if (!strcmp(model,"E-1")) {
+    filters = 0x61616161;
+    load_raw = unpacked_load_raw;
+    maximum = 0xfff0;
+    black = 1024;
+  } else if (!strcmp(model,"E-10")) {
+    load_raw = unpacked_load_raw;
+    maximum = 0xfff0;
+    black = 2048;
+  } else if (!strncmp(model,"E-20",4)) {
+    load_raw = unpacked_load_raw;
+    maximum = 0xffc0;
+    black = 2560;
+  } else if (!strcmp(model,"E-300")) {
+    width -= 21;
+    load_raw = olympus_e300_load_raw;
+    if (fsize > 15728640) {
+      load_raw = unpacked_load_raw;
+      maximum = 0xfc30;
+    } else
+      black = 62;
+  } else if (!strcmp(make,"OLYMPUS")) {
+    load_raw = olympus_cseries_load_raw;
+    if (!strcmp(model,"C5050Z") ||
+    !strcmp(model,"C8080WZ"))
+      filters = 0x16161616;
+  } else if (!strcmp(model,"N Digital")) {
+    height = 2047;
+    width  = 3072;
+    filters = 0x61616161;
+    data_offset = 0x1a00;
+    load_raw = packed_12_load_raw;
+    maximum = 0xf1e;
+  } else if (!strcmp(model,"DSC-F828")) {
+    width = 3288;
+    left_margin = 5;
+    load_raw = sony_load_raw;
+    filters = 0x9c9c9c9c;
+    colors = 4;
+    black = 491;
+  } else if (!strcmp(model,"DSC-V3")) {
+    width = 3109;
+    left_margin = 59;
+    load_raw = sony_load_raw;
+  } else if (!strcasecmp(make,"KODAK")) {
+    filters = 0x61616161;
+    if (!strcmp(model,"NC2000F")) {
+      width -= 4;
+      left_margin = 1;
+      for (i=176; i < 0x1000; i++)
+    curve[i] = curve[i-1];
+      pre_mul[0] = 1.509;
+      pre_mul[2] = 2.686;
+    } else if (!strcmp(model,"EOSDCS3B")) {
+      width -= 4;
+      left_margin = 2;
+    } else if (!strcmp(model,"EOSDCS1")) {
+      width -= 4;
+      left_margin = 2;
+    } else if (!strcmp(model,"DCS315C")) {
+      black = 8;
+    } else if (!strcmp(model,"DCS330C")) {
+      black = 8;
+    } else if (!strcmp(model,"DCS420")) {
+      width -= 4;
+      left_margin = 2;
+    } else if (!strcmp(model,"DCS460")) {
+      width -= 4;
+      left_margin = 2;
+    } else if (!strcmp(model,"DCS460A")) {
+      width -= 4;
+      left_margin = 2;
+      colors = 1;
+      filters = 0;
+    } else if (!strcmp(model,"DCS520C")) {
+      black = 180;
+    } else if (!strcmp(model,"DCS560C")) {
+      black = 188;
+    } else if (!strcmp(model,"DCS620C")) {
+      black = 180;
+    } else if (!strcmp(model,"DCS620X")) {
+      black = 185;
+    } else if (!strcmp(model,"DCS660C")) {
+      black = 214;
+    } else if (!strcmp(model,"DCS660M")) {
+      black = 214;
+      colors = 1;
+      filters = 0;
+    } else if (!strcmp(model,"DCS760M")) {
+      colors = 1;
+      filters = 0;
+    }
+    switch (tiff_data_compression) {
+    case 0:               /* No compression */
+    case 1:
+        load_raw = kodak_easy_load_raw;
+        break;
+    case 7:               /* Lossless JPEG */
+        load_raw = lossless_jpeg_load_raw;
+    case 32867:
+        break;
+    case 65000:           /* Kodak DCR compression */
+        if (!have64BitArithmetic)
+            pm_error("This program was built without 64 bit arithmetic "
+                     "capability, and Kodak DCR compression requires it.");
+        if (kodak_data_compression == 32803)
+            load_raw = kodak_compressed_load_raw;
+        else {
+            load_raw = kodak_yuv_load_raw;
+            height = (height+1) & -2;
+            width  = (width +1) & -2;
+            filters = 0;
+        }
+        break;
+    default:
+        pm_message ("%s %s uses unrecognized compression method %d.",
+                    make, model, tiff_data_compression);
+        return 1;
+    }
+    if (!strcmp(model,"DC20")) {
+      height = 242;
+      if (fsize < 100000) {
+    width = 249;
+    raw_width = 256;
+      } else {
+    width = 501;
+    raw_width = 512;
+      }
+      data_offset = raw_width + 1;
+      colors = 4;
+      filters = 0x8d8d8d8d;
+      kodak_dc20_coeff (1.0);
+      pre_mul[1] = 1.179;
+      pre_mul[2] = 1.209;
+      pre_mul[3] = 1.036;
+      load_raw = kodak_easy_load_raw;
+    } else if (strstr(model,"DC25")) {
+      strcpy (model, "DC25");
+      height = 242;
+      if (fsize < 100000) {
+    width = 249;
+    raw_width = 256;
+    data_offset = 15681;
+      } else {
+    width = 501;
+    raw_width = 512;
+    data_offset = 15937;
+      }
+      colors = 4;
+      filters = 0xb4b4b4b4;
+      load_raw = kodak_easy_load_raw;
+    } else if (!strcmp(model,"Digital Camera 40")) {
+      strcpy (model, "DC40");
+      height = 512;
+      width = 768;
+      data_offset = 1152;
+      load_raw = kodak_radc_load_raw;
+    } else if (strstr(model,"DC50")) {
+      strcpy (model, "DC50");
+      height = 512;
+      width = 768;
+      data_offset = 19712;
+      load_raw = kodak_radc_load_raw;
+    } else if (strstr(model,"DC120")) {
+      strcpy (model, "DC120");
+      height = 976;
+      width = 848;
+      if (tiff_data_compression == 7)
+    load_raw = kodak_jpeg_load_raw;
+      else
+    load_raw = kodak_dc120_load_raw;
+    }
+  } else if (!strcmp(make,"Rollei")) {
+    switch (raw_width) {
+      case 1316:
+    height = 1030;
+    width  = 1300;
+    top_margin  = 1;
+    left_margin = 6;
+    break;
+      case 2568:
+    height = 1960;
+    width  = 2560;
+    top_margin  = 2;
+    left_margin = 8;
+    }
+    filters = 0x16161616;
+    load_raw = rollei_load_raw;
+    pre_mul[0] = 1.8;
+    pre_mul[2] = 1.3;
+  } else if (!strcmp(model,"PC-CAM 600")) {
+    height = 768;
+    data_offset = width = 1024;
+    filters = 0x49494949;
+    load_raw = eight_bit_load_raw;
+    pre_mul[0] = 1.14;
+    pre_mul[2] = 2.73;
+  } else if (!strcmp(model,"QV-2000UX")) {
+    height = 1208;
+    width  = 1632;
+    data_offset = width * 2;
+    load_raw = eight_bit_load_raw;
+  } else if (!strcmp(model,"QV-3*00EX")) {
+    height = 1546;
+    width  = 2070;
+    raw_width = 2080;
+    load_raw = eight_bit_load_raw;
+  } else if (!strcmp(model,"QV-4000")) {
+    height = 1700;
+    width  = 2260;
+    load_raw = unpacked_load_raw;
+    maximum = 0xffff;
+  } else if (!strcmp(model,"QV-5700")) {
+    height = 1924;
+    width  = 2576;
+    load_raw = casio_qv5700_load_raw;
+  } else if (!strcmp(model,"QV-R51")) {
+    height = 1926;
+    width  = 2576;
+    raw_width = 3904;
+    load_raw = packed_12_load_raw;
+    pre_mul[0] = 1.340;
+    pre_mul[2] = 1.672;
+  } else if (!strcmp(model,"EX-Z55")) {
+    height = 1960;
+    width  = 2570;
+    raw_width = 3904;
+    load_raw = packed_12_load_raw;
+    pre_mul[0] = 1.520;
+    pre_mul[2] = 1.316;
+  } else if (!strcmp(model,"EX-P600")) {
+    height = 2142;
+    width  = 2844;
+    raw_width = 4288;
+    load_raw = packed_12_load_raw;
+    pre_mul[0] = 1.797;
+    pre_mul[2] = 1.219;
+  } else if (!strcmp(model,"EX-P700")) {
+    height = 2318;
+    width  = 3082;
+    raw_width = 4672;
+    load_raw = packed_12_load_raw;
+    pre_mul[0] = 1.758;
+    pre_mul[2] = 1.504;
+  } else if (!strcmp(make,"Nucore")) {
+    filters = 0x61616161;
+    load_raw = unpacked_load_raw;
+    if (width == 2598) {
+      filters = 0x16161616;
+      load_raw = nucore_load_raw;
+      flip = 2;
+    }
+  }
+  if (!use_coeff) adobe_coeff();
+dng_skip:
+  if (!load_raw || !height) {
+    pm_message ("This program cannot handle data from %s %s.",
+                make, model);
+    return 1;
+  }
+#ifdef NO_JPEG
+  if (load_raw == kodak_jpeg_load_raw) {
+    pm_message ("decoder was not linked with libjpeg.");
+    return 1;
+  }
+#endif
+  if (!raw_height) raw_height = height;
+  if (!raw_width ) raw_width  = width;
+  if (use_camera_rgb && colors == 3)
+      use_coeff = 0;
+  if (use_coeff)         /* Apply user-selected color balance */
+    for (i=0; i < colors; i++) {
+      coeff[0][i] *= red_scale;
+      coeff[2][i] *= blue_scale;
+    }
+  if (four_color_rgb && filters && colors == 3) {
+    for (i=0; i < 32; i+=4) {
+      if ((filters >> i & 15) == 9)
+    filters |= 2 << i;
+      if ((filters >> i & 15) == 6)
+    filters |= 8 << i;
+    }
+    colors++;
+    pre_mul[3] = pre_mul[1];
+    if (use_coeff)
+      for (i=0; i < 3; i++)
+    coeff[i][3] = coeff[i][1] /= 2;
+  }
+  fseek (ifp, data_offset, SEEK_SET);
+
+  *loadRawFnP = load_raw;
+
+  return 0;
+}
diff --git a/converter/other/cameratopam/identify.h b/converter/other/cameratopam/identify.h
new file mode 100644
index 00000000..012b807c
--- /dev/null
+++ b/converter/other/cameratopam/identify.h
@@ -0,0 +1,11 @@
+typedef void (*loadRawFn)();
+
+int
+identify(FILE *       const ifp,
+         bool         const use_secondary,
+         bool         const use_camera_rgb,
+         float        const red_scale,
+         float        const blue_scale,
+         unsigned int const four_color_rgb,
+         const char * const inputFileName,
+         loadRawFn *  const loadRawFnP);
diff --git a/converter/other/cameratopam/ljpeg.c b/converter/other/cameratopam/ljpeg.c
new file mode 100644
index 00000000..18423f4f
--- /dev/null
+++ b/converter/other/cameratopam/ljpeg.c
@@ -0,0 +1,141 @@
+#define _BSD_SOURCE    /* Make sure string.h containst strcasecmp() */
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include "pm.h"
+
+#include "global_variables.h"
+#include "util.h"
+#include "decode.h"
+#include "bayer.h"
+
+#include "ljpeg.h"
+
+
+/*
+   Not a full implementation of Lossless JPEG, just
+   enough to decode Canon, Kodak and Adobe DNG images.
+ */
+
+int  
+ljpeg_start (FILE * ifp, struct jhead *jh)
+{
+  int i, tag, len;
+  unsigned char data[256], *dp;
+
+  init_decoder();
+  for (i=0; i < 4; i++)
+    jh->huff[i] = free_decode;
+  fread (data, 2, 1, ifp);
+  if (data[0] != 0xff || data[1] != 0xd8) return 0;
+  do {
+    fread (data, 2, 2, ifp);
+    tag =  data[0] << 8 | data[1];
+    len = (data[2] << 8 | data[3]) - 2;
+    if (tag <= 0xff00 || len > 255) return 0;
+    fread (data, 1, len, ifp);
+    switch (tag) {
+      case 0xffc3:
+    jh->bits = data[0];
+    jh->high = data[1] << 8 | data[2];
+    jh->wide = data[3] << 8 | data[4];
+    jh->clrs = data[5];
+    break;
+      case 0xffc4:
+    for (dp = data; dp < data+len && *dp < 4; ) {
+      jh->huff[*dp] = free_decode;
+      dp = make_decoder (++dp, 0);
+    }
+    }
+  } while (tag != 0xffda);
+  jh->row = calloc (jh->wide*jh->clrs, 2);
+  if (jh->row == NULL)
+      pm_error("Out of memory in ljpeg_start()");
+  for (i=0; i < 4; i++)
+    jh->vpred[i] = 1 << (jh->bits-1);
+  zero_after_ff = 1;
+  getbits(ifp, -1);
+  return 1;
+}
+
+int 
+ljpeg_diff (struct decode *dindex)
+{
+  int len, diff;
+
+  while (dindex->branch[0])
+    dindex = dindex->branch[getbits(ifp, 1)];
+  diff = getbits(ifp, len = dindex->leaf);
+  if ((diff & (1 << (len-1))) == 0)
+    diff -= (1 << len) - 1;
+  return diff;
+}
+
+void
+ljpeg_row (struct jhead *jh)
+{
+  int col, c, diff;
+  unsigned short *outp=jh->row;
+
+  for (col=0; col < jh->wide; col++)
+    for (c=0; c < jh->clrs; c++) {
+      diff = ljpeg_diff (jh->huff[c]);
+      *outp = col ? outp[-jh->clrs]+diff : (jh->vpred[c] += diff);
+      outp++;
+    }
+}
+
+void  
+lossless_jpeg_load_raw(void)
+{
+  int jwide, jrow, jcol, val, jidx, i, row, col;
+  struct jhead jh;
+  int min=INT_MAX;
+
+  if (!ljpeg_start (ifp, &jh)) return;
+  jwide = jh.wide * jh.clrs;
+
+  for (jrow=0; jrow < jh.high; jrow++) {
+    ljpeg_row (&jh);
+    for (jcol=0; jcol < jwide; jcol++) {
+      val = curve[jh.row[jcol]];
+      jidx = jrow*jwide + jcol;
+      if (raw_width == 5108) {
+    i = jidx / (1680*jh.high);
+    if (i < 2) {
+      row = jidx / 1680 % jh.high;
+      col = jidx % 1680 + i*1680;
+    } else {
+      jidx -= 2*1680*jh.high;
+      row = jidx / 1748;
+      col = jidx % 1748 + 2*1680;
+    }
+      } else if (raw_width == 3516) {
+    row = jidx / 1758;
+    col = jidx % 1758;
+    if (row >= raw_height) {
+      row -= raw_height;
+      col += 1758;
+    }
+      } else {
+    row = jidx / raw_width;
+    col = jidx % raw_width;
+      }
+      if ((unsigned) (row-top_margin) >= height) continue;
+      if ((unsigned) (col-left_margin) < width) {
+    BAYER(row-top_margin,col-left_margin) = val;
+    if (min > val) min = val;
+      } else
+    black += val;
+    }
+  }
+  free (jh.row);
+  if (raw_width > width)
+    black /= (raw_width - width) * height;
+  if (!strcasecmp(make,"KODAK"))
+    black = min;
+}
+
+
diff --git a/converter/other/cameratopam/ljpeg.h b/converter/other/cameratopam/ljpeg.h
new file mode 100644
index 00000000..60832a3d
--- /dev/null
+++ b/converter/other/cameratopam/ljpeg.h
@@ -0,0 +1,17 @@
+struct jhead {
+  int bits, high, wide, clrs, vpred[4];
+  struct decode *huff[4];
+  unsigned short *row;
+};
+
+void  
+lossless_jpeg_load_raw(void);
+
+int  
+ljpeg_start (FILE * ifp, struct jhead *jh);
+
+int 
+ljpeg_diff (struct decode *dindex);
+
+void
+ljpeg_row (struct jhead *jh);
diff --git a/converter/other/cameratopam/util.c b/converter/other/cameratopam/util.c
new file mode 100644
index 00000000..b3ccf7e9
--- /dev/null
+++ b/converter/other/cameratopam/util.c
@@ -0,0 +1,83 @@
+#define _XOPEN_SOURCE   /* Make sure unistd.h contains swab() */
+#include <unistd.h>
+#include <stdio.h>
+
+#include "pm.h"
+#include "global_variables.h"
+#include "util.h"
+
+#ifndef LONG_BITS
+#define LONG_BITS (8 * sizeof(long))
+#endif
+/*
+   Get a 2-byte integer, making no assumptions about CPU byte order.
+   Nor should we assume that the compiler evaluates left-to-right.
+ */
+unsigned short
+get2(FILE * const ifp)
+{
+    unsigned char 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(FILE * const ifp)
+{
+    unsigned char 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;
+}
+
+/*
+   Faster than calling get2() multiple times.
+ */
+void
+read_shorts (FILE * const ifp, unsigned short *pixel, int count)
+{
+    fread (pixel, 2, count, ifp);
+    if ((order == 0x4949) == (BYTE_ORDER == BIG_ENDIAN))
+        swab (pixel, pixel, count*2);
+}
+
+/*
+   getbits(-1) initializes the buffer
+   getbits(n) where 0 <= n <= 25 returns an n-bit integer
+ */
+unsigned 
+getbits (FILE * const ifp, int nbits)
+{
+    static unsigned long bitbuf=0;
+    static int vbits=0;
+    unsigned c, ret;
+
+    if (nbits == 0) return 0;
+    if (nbits == -1)
+        ret = bitbuf = vbits = 0;
+    else {
+        ret = bitbuf << (LONG_BITS - vbits) >> (LONG_BITS - nbits);
+        vbits -= nbits;
+    }
+    while (vbits < LONG_BITS - 7) {
+        c = fgetc(ifp);
+        bitbuf = (bitbuf << 8) + c;
+        if (c == 0xff && zero_after_ff)
+            fgetc(ifp);
+        vbits += 8;
+    }
+    return ret;
+}
diff --git a/converter/other/cameratopam/util.h b/converter/other/cameratopam/util.h
new file mode 100644
index 00000000..bf9006d3
--- /dev/null
+++ b/converter/other/cameratopam/util.h
@@ -0,0 +1,16 @@
+#ifndef UTIL_H_INCLUDED
+#define UTIL_H_INCLUDED
+
+unsigned short
+get2(FILE * const ifp);
+
+int
+get4(FILE * const ifp);
+
+void
+read_shorts (FILE * const ifp, unsigned short *pixel, int count);
+
+unsigned int
+getbits (FILE * const ifp, int nbits);
+
+#endif